diff --git a/commands/code-review.md b/commands/code-review.md index 3e5e8ee61..9ec5c6dab 100644 --- a/commands/code-review.md +++ b/commands/code-review.md @@ -1,6 +1,6 @@ --- description: Code review — local uncommitted changes or GitHub PR (pass PR number/URL for PR mode). Use for a step-by-step PRP-style checklist review; for a multi-agent pass use /review-pr, and for the adversarially-verified Workflow pass use /orch-review. -argument-hint: [pr-number | pr-url | blank for local review] +argument-hint: "[pr-number | pr-url | blank for local review]" --- # Code Review diff --git a/commands/cost-report.md b/commands/cost-report.md index 87775981e..7c0b9dd92 100644 --- a/commands/cost-report.md +++ b/commands/cost-report.md @@ -1,6 +1,6 @@ --- description: Generate a local Claude Code cost report from the ECC cost-tracker metrics log. Use for a terminal summary or CSV export of tracked spend; the cost-tracking skill covers the same metrics log for on-demand cost/budget questions asked in conversation. -argument-hint: [csv] +argument-hint: "[csv]" --- # Cost Report diff --git a/commands/orch-review.md b/commands/orch-review.md index 8f942c50f..cb31173dc 100644 --- a/commands/orch-review.md +++ b/commands/orch-review.md @@ -1,6 +1,6 @@ --- description: Run the orch-review native Workflow over a diff (local changes or a GitHub PR) and report blocking vs advisory findings. Surface for the orch-review workflow. Use for a native-Workflow, adversarially-verified multi-dimension review with fan-out and dedup; for the step-by-step checklist pass use /code-review, and for the multi-agent pass use /review-pr. -argument-hint: [pr-number | pr-url | blank for local uncommitted changes] +argument-hint: "[pr-number | pr-url | blank for local uncommitted changes]" --- # /orch-review diff --git a/tests/commands/command-frontmatter.test.js b/tests/commands/command-frontmatter.test.js index 36d4714f3..76e0a9cd9 100644 --- a/tests/commands/command-frontmatter.test.js +++ b/tests/commands/command-frontmatter.test.js @@ -53,6 +53,41 @@ for (const fileName of getCommandFiles()) { }); } +test('argument-hint is never an unquoted YAML flow sequence', () => { + const offenders = []; + + for (const fileName of getCommandFiles()) { + const content = fs.readFileSync(path.join(commandsDir, fileName), 'utf8'); + const frontmatter = parseFrontmatter(content); + if (!frontmatter) { + continue; + } + + for (const line of frontmatter.split(/\r?\n/)) { + const match = line.match(/^argument-hint:\s*(.*)$/); + if (!match) { + continue; + } + + const value = match[1].trim(); + // An unquoted leading '[' makes YAML parse the value as a flow sequence. + // Harnesses that type-check argument-hint as a string then reject the whole + // file, and the slash command disappears from the catalog without a + // user-visible error. Other keys are deliberately not covered: allowed-tools + // is a legitimate list. + if (/^[[{]/.test(value) && !/^["']/.test(value)) { + offenders.push(`${fileName}: ${line.trim()}`); + } + } + } + + assert.deepStrictEqual( + offenders, + [], + 'argument-hint must be quoted when it starts with [ or { so YAML parses it as a string' + ); +}); + if (failed > 0) { console.log(`\nFailed: ${failed}`); process.exit(1);