diff --git a/skills/openclaw-feedback/SKILL.md b/skills/openclaw-feedback/SKILL.md index 280284e66f3..435c68b6903 100644 --- a/skills/openclaw-feedback/SKILL.md +++ b/skills/openclaw-feedback/SKILL.md @@ -46,6 +46,7 @@ Use this skill only for `openclaw/openclaw`. - Show the full sanitized draft before asking to submit. - Ask permission in plain English before adding `--submit`. - Only after approval, use `--submit` for public bug or feature issues. +- If submission succeeds, include the created GitHub issue URL in the final reply to the user. ## Do Not @@ -99,7 +100,8 @@ Use this skill only for `openclaw/openclaw`. 10. After showing the draft, ask in plain English: `If this draft looks right, I can submit it to GitHub now.` 11. Do not mention CLI flags like `--submit` in the user-facing approval question. 12. Only if the user clearly approves, rerun or continue with `--submit` for public bug or feature issues. -13. For security, keep the report private and route the user to `security@openclaw.ai`. +13. If the issue is created successfully, include the created GitHub URL in the final reply. +14. For security, keep the report private and route the user to `security@openclaw.ai`. ## Common Commands @@ -131,6 +133,8 @@ Use `--additional-information` for details that do not fit neatly into `--repro` Prefer deriving these from the conversation when they are already clear instead of asking the user to restate them. +When passing multiline text into the CLI as a single quoted argument, encode line breaks as literal `\n` so `openclaw report` can render them back as real line breaks in the final issue body. + ## Private Security Reports If the request is a security issue: @@ -179,7 +183,7 @@ I need a few bug-report details: what steps reproduce it, what you expected, wha If those fields are not already clear from the diagnosis session, ask for them. Otherwise infer them and run: ```bash -openclaw report bug --summary "Gateway times out behind mitmproxy" --repro "..." --expected "..." --actual "..." --impact "..." --probe gateway +openclaw report bug --summary "Gateway times out behind mitmproxy" --repro "1. Start gateway\n2. Send request\n3. Observe timeout" --expected "..." --actual "..." --impact "..." --probe gateway ``` Then show the draft itself, not internal report metadata. Only add `--submit` after explicit approval. @@ -190,6 +194,8 @@ After showing the draft, ask: If this draft looks right, I can submit it to GitHub now. ``` +If submission succeeds, include the created issue URL in the final reply. + ### Example: regression bug with loose extra context User says: `This worked last week, but after updating it started timing out.` @@ -206,7 +212,7 @@ I need the repro steps, expected behavior, actual behavior, and impact. I can al If the missing facts are not already clear from the conversation or diagnosis session, ask for them. Otherwise infer them and run: ```bash -openclaw report bug --summary "Regression after update" --repro "..." --expected "..." --actual "..." --impact "..." --previous-version "2026.3.14" --additional-information "Worked last week; now every call times out behind the same proxy setup." --probe model +openclaw report bug --summary "Regression after update" --repro "1. Start gateway\n2. Send request\n3. Observe failure" --expected "..." --actual "..." --impact "..." --previous-version "2026.3.14" --additional-information "Worked last week; now every call times out behind the same proxy setup." --probe model ``` Then show the draft and ask: @@ -215,6 +221,8 @@ Then show the draft and ask: If this draft looks right, I can submit it to GitHub now. ``` +If submission succeeds, include the created issue URL in the final reply. + ### Example: feature request User says: `Please add a way to export a report draft without submitting it.` @@ -231,6 +239,8 @@ Then show the draft and ask: If this draft looks right, I can submit it to GitHub now. ``` +If submission succeeds, include the created issue URL in the final reply. + ### Example: private security report User says: `I found a token leak in logs.` diff --git a/src/commands/report.test.ts b/src/commands/report.test.ts index cf07309d3f0..0d256821b34 100644 --- a/src/commands/report.test.ts +++ b/src/commands/report.test.ts @@ -202,6 +202,22 @@ describe("reportCommand", () => { expect(payload.submissionEligible).toBe(false); }); + it("renders literal escaped newline sequences as real line breaks in report sections", async () => { + const payload = await buildReportPayload({ + kind: "bug", + options: { + summary: "Gateway timeout", + repro: "1. Start gateway\\n2. Send request\\n3. Observe failure", + expected: "Model responds", + actual: "Timeout", + impact: "Blocks requests", + }, + }); + + expect(payload.body).toContain("1. Start gateway\n2. Send request\n3. Observe failure"); + expect(payload.body).not.toContain("\\n2. Send request"); + }); + it("writes markdown output that matches the generated body", async () => { const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-report-test-")); const outputPath = path.join(tmpDir, "bug.md"); @@ -446,6 +462,25 @@ describe("reportCommand", () => { expect(payload.submission.url).toBe("https://github.com/openclaw/openclaw/issues/999"); }); + it("prints the created issue url in human output after successful submission", async () => { + await reportCommand({ + kind: "bug", + options: { + summary: "Gateway timeout", + repro: "1. Start gateway", + expected: "Model responds", + actual: "Timeout", + impact: "Blocks requests", + submit: true, + yes: true, + nonInteractive: true, + }, + runtime, + }); + + expect(runtime.log).toHaveBeenCalledWith("Created: https://github.com/openclaw/openclaw/issues/999"); + }); + it("keeps draft generation working when config and secret resolution degrade", async () => { readBestEffortConfig.mockRejectedValueOnce(new Error("config missing")); resolveCommandSecretRefsViaGateway.mockRejectedValueOnce(new Error("secret refs unavailable")); diff --git a/src/commands/report.ts b/src/commands/report.ts index e1b16e366cc..cda7274e26c 100644 --- a/src/commands/report.ts +++ b/src/commands/report.ts @@ -137,8 +137,15 @@ export type ReportPayload = { const PUBLIC_REPO = "openclaw/openclaw"; const REPORT_GENERATED_NOTE = "_Generated via `openclaw report`._"; +function normalizeEscapedMultilineText(value: string | undefined): string | undefined { + if (value === undefined) { + return undefined; + } + return value.replace(/\\r\\n/g, "\n").replace(/\\n/g, "\n").replace(/\r\n/g, "\n"); +} + function sanitizeWhitespace(value: string | undefined): string | undefined { - const trimmed = value?.trim(); + const trimmed = normalizeEscapedMultilineText(value)?.trim(); return trimmed ? trimmed : undefined; } @@ -165,8 +172,9 @@ export function sanitizeReportText(value: string | undefined): { if (!value) { return { text: "", redactionsApplied: [] }; } - const redactions = detectRedactions(value); - let text = redactSecrets(value); + const normalized = normalizeEscapedMultilineText(value) ?? ""; + const redactions = detectRedactions(normalized); + let text = redactSecrets(normalized); text = text.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[email-redacted]"); text = text.replace(/\b(?:\+?1[-.\s]?)?(?:\(?\d{3}\)?[-.\s]?){2}\d{4}\b/g, "[phone-redacted]"); text = text.replace(/\/Users\/[^/\s]+/g, "/Users/user");