From 060f218e24c7718516bd3b02f8c16cc1ef88e50c Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Sat, 11 Apr 2026 10:06:57 -0400 Subject: [PATCH 1/2] Use Write tool + --body-file for start.md gh commands (test approach) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Heredocs in Bash commands — quoted, unquoted, or via $(cat) — all trigger Claude Code permission prompts due to shell metacharacter detection (#, $, quotes). Moving body content to a temp file via the Write tool bypasses shell parsing entirely. The gh command becomes a clean prefix match for Bash(gh:*). Testing in start.md first before applying to other skills. Ref #82 Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/start.md | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/skills/start.md b/skills/start.md index aefb3b8..a52d979 100644 --- a/skills/start.md +++ b/skills/start.md @@ -136,7 +136,13 @@ The friendly text question is required regardless of harness mode. If your harne ### Step 3 — Create GitHub Issue -Run `gh issue create` with explicit flags (do NOT open an interactive editor): +Run `gh issue create` with explicit flags (do NOT open an interactive editor). + +First, write the issue body to a temp file using the **Write tool** (not Bash): + +**Write** `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). + +Then create the issue: ```bash gh issue create \ @@ -144,12 +150,10 @@ gh issue create \ --assignee @me \ [--label ""] \ [--milestone ""] \ - --body-file - <<'EOF' - -EOF + --body-file /tmp/cc_issue_body.md ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc avoids this — the matcher sees a clean `gh …` invocation and `Bash(gh:*)` matches. +> **Why Write tool + `--body-file`?** Passing multi-line markdown content inline in a Bash command — whether via `$(...)`, quoted heredocs (`<<'EOF'`), or unquoted heredocs (`<"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. @@ -192,14 +196,18 @@ After the command runs, note the issue number from the output URL (e.g. `https:/ Show the user: `Created issue #: ` -Then immediately post agent implementation notes as a comment: +Then immediately post agent implementation notes as a comment. -```bash -gh issue comment <number> --body-file - <<'EOF' +**Write** `/tmp/cc_issue_comment.md`: +```markdown ## Agent Implementation Notes <full technical plan: exact files to change, approach, key decisions, edge cases> -EOF +``` + +Then post it: +```bash +gh issue comment <number> --body-file /tmp/cc_issue_comment.md ``` ### Step 4 — Create feature branch From e5e8b3eb0ffba41758003f3ee9d757ebbcec325e Mon Sep 17 00:00:00 2001 From: Sebastien Taggart <sebastien.taggart@gmail.com> Date: Sat, 11 Apr 2026 15:35:11 -0400 Subject: [PATCH 2/2] Use Write tool + --body-file for all gh commands with multi-line bodies Replace heredoc patterns (--body-file - <<EOF, --notes-file - <<EOF) with a two-step approach: write body to /tmp/cc_*.md via the file-writing tool, then pass --body-file /tmp/cc_*.md to gh. This avoids all shell metacharacter issues that trigger Claude Code permission prompts ($, #, quoted delimiters). Updated skills: start.md, submit-for-review.md, deploy.md Regenerated all 12 adapter files via sync.sh. Closes #82 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --- .agents/skills/deploy/SKILL.md | 34 ++++++---- .agents/skills/start/SKILL.md | 26 +++++--- .agents/skills/submit-for-review/SKILL.md | 49 +++++++++----- .claude/commands/deploy.md | 34 ++++++---- .claude/commands/start.md | 26 +++++--- .claude/commands/submit-for-review.md | 49 +++++++++----- .cursor/rules/deploy.mdc | 34 ++++++---- .cursor/rules/start.mdc | 26 +++++--- .cursor/rules/submit-for-review.mdc | 49 +++++++++----- .gemini/skills/deploy/SKILL.md | 34 ++++++---- .gemini/skills/start/SKILL.md | 26 +++++--- .gemini/skills/submit-for-review/SKILL.md | 49 +++++++++----- skills/deploy.md | 80 +++++++++++++++-------- skills/start.md | 14 ++-- skills/submit-for-review.md | 47 +++++++++---- 15 files changed, 380 insertions(+), 197 deletions(-) diff --git a/.agents/skills/deploy/SKILL.md b/.agents/skills/deploy/SKILL.md index 037b39a..7d00d24 100644 --- a/.agents/skills/deploy/SKILL.md +++ b/.agents/skills/deploy/SKILL.md @@ -166,10 +166,9 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ## Step 6 — Create PR: `dev` → `main` -```bash -gh pr create --base main --head dev \ - --title "Release vX.Y.Z" \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: + +```markdown Release vX.Y.Z PRs included: @@ -178,7 +177,14 @@ PRs included: Closes #14 Closes #15 -EOF +``` + +Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): + +```bash +gh pr create --base main --head dev \ + --title "Release vX.Y.Z" \ + --body-file /tmp/cc_release_pr_body.md ``` Note the PR number from the output. @@ -209,19 +215,23 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Create the release: +Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: -```bash -gh release create <version-tag> \ - --title "<version-tag>" \ - --notes-file - <<'EOF' +```markdown ## Changes - #<issue> — <PR title> (PR #<pr-number>) [... one line per PR included in this release ...] **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> -EOF +``` + +Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): + +```bash +gh release create <version-tag> \ + --title "<version-tag>" \ + --notes-file /tmp/cc_release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -235,4 +245,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: codex | hash: 0e47902b | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: codex | hash: 6d8dd3bb | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.agents/skills/start/SKILL.md b/.agents/skills/start/SKILL.md index 47881d4..837fb80 100644 --- a/.agents/skills/start/SKILL.md +++ b/.agents/skills/start/SKILL.md @@ -93,7 +93,11 @@ The friendly text question is required regardless of harness mode. If your harne ### Step 3 — Create GitHub Issue -Run `gh issue create` with explicit flags (do NOT open an interactive editor): +Create the issue in two steps — **this exact sequence is mandatory**: + +**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. + +**Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: ```bash gh issue create \ @@ -101,12 +105,10 @@ gh issue create \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] \ - --body-file - <<'EOF' -<structured markdown body — see sections below> -EOF + --body-file /tmp/cc_issue_body.md ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc avoids this — the matcher sees a clean `gh …` invocation and `Bash(gh:*)` matches. +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. Resolve labels and milestone using the resolution steps in the Parsing section above: - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. @@ -149,14 +151,18 @@ After the command runs, note the issue number from the output URL (e.g. `https:/ Show the user: `Created issue #<number>: <title>` -Then immediately post agent implementation notes as a comment: +Then immediately post agent implementation notes as a comment. -```bash -gh issue comment <number> --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: +```markdown ## Agent Implementation Notes <full technical plan: exact files to change, approach, key decisions, edge cases> -EOF +``` + +Then post it (do NOT use `--body` or heredocs — same rule as Step 3): +```bash +gh issue comment <number> --body-file /tmp/cc_issue_comment.md ``` ### Step 4 — Create feature branch @@ -270,4 +276,4 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: codex | hash: 5603cf1a | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: codex | hash: 7ba07088 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.agents/skills/submit-for-review/SKILL.md b/.agents/skills/submit-for-review/SKILL.md index dd35ec9..d711c85 100644 --- a/.agents/skills/submit-for-review/SKILL.md +++ b/.agents/skills/submit-for-review/SKILL.md @@ -109,16 +109,23 @@ Use `Issue #<number>` as the issue reference — the issue stays open until `/de > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. -Then create the PR with explicit title and body (never use an interactive editor): -``` -gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' +Then create the PR in two steps — **this exact sequence is mandatory**: + +First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. + +```markdown <description of what changed and why> <Closes #N OR Issue #N, based on target above> -EOF ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). +Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): + +``` +gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +``` + +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. Add `--reviewer` to the `gh pr create` command above using the handles from `@sebastientaggart`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. @@ -219,14 +226,20 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -``` -gh issue comment <number> --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: + +```markdown ## Resolution <1-3 sentences explaining what was done to fix the problem, written in plain language for a non-technical audience — no code, no file paths, no jargon. Focus on what changed from the user's perspective and why it solves the problem described in the issue.> See #<PR-number> for full technical details. -EOF +``` + +Then post it (do NOT use `--body` or heredocs): + +``` +gh issue comment <number> --body-file /tmp/cc_resolution_comment.md ``` **Resolution writing rules:** @@ -262,17 +275,23 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is **Create the selected issues.** For each selected finding, run `gh issue create` with explicit flags: -``` -gh issue create \ - --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - [--label "<pool-selected labels>"] \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_followup_body.md` for each finding: + +```markdown Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. -EOF +``` + +Then create the issue (do NOT use `--body` or heredocs): + +``` +gh issue create \ + --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ + [--label "<pool-selected labels>"] \ + --body-file /tmp/cc_followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. @@ -298,4 +317,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: codex | hash: ba60d5aa | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: codex | hash: d09ab40d | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/deploy.md b/.claude/commands/deploy.md index 4c919ad..962a484 100644 --- a/.claude/commands/deploy.md +++ b/.claude/commands/deploy.md @@ -161,10 +161,9 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ## Step 6 — Create PR: `dev` → `main` -```bash -gh pr create --base main --head dev \ - --title "Release vX.Y.Z" \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: + +```markdown Release vX.Y.Z PRs included: @@ -173,7 +172,14 @@ PRs included: Closes #14 Closes #15 -EOF +``` + +Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): + +```bash +gh pr create --base main --head dev \ + --title "Release vX.Y.Z" \ + --body-file /tmp/cc_release_pr_body.md ``` Note the PR number from the output. @@ -204,19 +210,23 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Create the release: +Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: -```bash -gh release create <version-tag> \ - --title "<version-tag>" \ - --notes-file - <<'EOF' +```markdown ## Changes - #<issue> — <PR title> (PR #<pr-number>) [... one line per PR included in this release ...] **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> -EOF +``` + +Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): + +```bash +gh release create <version-tag> \ + --title "<version-tag>" \ + --notes-file /tmp/cc_release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -230,4 +240,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: claude | hash: b1efee29 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: claude | hash: 88439028 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/start.md b/.claude/commands/start.md index edbe3e6..2458d44 100644 --- a/.claude/commands/start.md +++ b/.claude/commands/start.md @@ -88,7 +88,11 @@ The friendly text question is required regardless of harness mode. If your harne ### Step 3 — Create GitHub Issue -Run `gh issue create` with explicit flags (do NOT open an interactive editor): +Create the issue in two steps — **this exact sequence is mandatory**: + +**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. + +**Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: ```bash gh issue create \ @@ -96,12 +100,10 @@ gh issue create \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] \ - --body-file - <<'EOF' -<structured markdown body — see sections below> -EOF + --body-file /tmp/cc_issue_body.md ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc avoids this — the matcher sees a clean `gh …` invocation and `Bash(gh:*)` matches. +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. Resolve labels and milestone using the resolution steps in the Parsing section above: - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. @@ -144,14 +146,18 @@ After the command runs, note the issue number from the output URL (e.g. `https:/ Show the user: `Created issue #<number>: <title>` -Then immediately post agent implementation notes as a comment: +Then immediately post agent implementation notes as a comment. -```bash -gh issue comment <number> --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: +```markdown ## Agent Implementation Notes <full technical plan: exact files to change, approach, key decisions, edge cases> -EOF +``` + +Then post it (do NOT use `--body` or heredocs — same rule as Step 3): +```bash +gh issue comment <number> --body-file /tmp/cc_issue_comment.md ``` ### Step 4 — Create feature branch @@ -265,4 +271,4 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: claude | hash: 3c1bac69 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: claude | hash: d1144b46 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/submit-for-review.md b/.claude/commands/submit-for-review.md index 95d9218..98a6d71 100644 --- a/.claude/commands/submit-for-review.md +++ b/.claude/commands/submit-for-review.md @@ -104,16 +104,23 @@ Use `Issue #<number>` as the issue reference — the issue stays open until `/de > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. -Then create the PR with explicit title and body (never use an interactive editor): -``` -gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' +Then create the PR in two steps — **this exact sequence is mandatory**: + +First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. + +```markdown <description of what changed and why> <Closes #N OR Issue #N, based on target above> -EOF ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). +Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): + +``` +gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +``` + +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. Add `--reviewer` to the `gh pr create` command above using the handles from `@sebastientaggart`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. @@ -214,14 +221,20 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -``` -gh issue comment <number> --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: + +```markdown ## Resolution <1-3 sentences explaining what was done to fix the problem, written in plain language for a non-technical audience — no code, no file paths, no jargon. Focus on what changed from the user's perspective and why it solves the problem described in the issue.> See #<PR-number> for full technical details. -EOF +``` + +Then post it (do NOT use `--body` or heredocs): + +``` +gh issue comment <number> --body-file /tmp/cc_resolution_comment.md ``` **Resolution writing rules:** @@ -257,17 +270,23 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is **Create the selected issues.** For each selected finding, run `gh issue create` with explicit flags: -``` -gh issue create \ - --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - [--label "<pool-selected labels>"] \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_followup_body.md` for each finding: + +```markdown Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. -EOF +``` + +Then create the issue (do NOT use `--body` or heredocs): + +``` +gh issue create \ + --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ + [--label "<pool-selected labels>"] \ + --body-file /tmp/cc_followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. @@ -293,4 +312,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: claude | hash: 28da1f65 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: claude | hash: 7cc1a5b2 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/deploy.mdc b/.cursor/rules/deploy.mdc index a34de2c..d4e7d70 100644 --- a/.cursor/rules/deploy.mdc +++ b/.cursor/rules/deploy.mdc @@ -167,10 +167,9 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ## Step 6 — Create PR: `dev` → `main` -```bash -gh pr create --base main --head dev \ - --title "Release vX.Y.Z" \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: + +```markdown Release vX.Y.Z PRs included: @@ -179,7 +178,14 @@ PRs included: Closes #14 Closes #15 -EOF +``` + +Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): + +```bash +gh pr create --base main --head dev \ + --title "Release vX.Y.Z" \ + --body-file /tmp/cc_release_pr_body.md ``` Note the PR number from the output. @@ -210,19 +216,23 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Create the release: +Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: -```bash -gh release create <version-tag> \ - --title "<version-tag>" \ - --notes-file - <<'EOF' +```markdown ## Changes - #<issue> — <PR title> (PR #<pr-number>) [... one line per PR included in this release ...] **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> -EOF +``` + +Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): + +```bash +gh release create <version-tag> \ + --title "<version-tag>" \ + --notes-file /tmp/cc_release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -236,4 +246,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: cursor | hash: 96c1037f | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: cursor | hash: a7a8fe00 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/start.mdc b/.cursor/rules/start.mdc index 58a770b..3adac90 100644 --- a/.cursor/rules/start.mdc +++ b/.cursor/rules/start.mdc @@ -94,7 +94,11 @@ The friendly text question is required regardless of harness mode. If your harne ### Step 3 — Create GitHub Issue -Run `gh issue create` with explicit flags (do NOT open an interactive editor): +Create the issue in two steps — **this exact sequence is mandatory**: + +**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. + +**Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: ```bash gh issue create \ @@ -102,12 +106,10 @@ gh issue create \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] \ - --body-file - <<'EOF' -<structured markdown body — see sections below> -EOF + --body-file /tmp/cc_issue_body.md ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc avoids this — the matcher sees a clean `gh …` invocation and `Bash(gh:*)` matches. +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. Resolve labels and milestone using the resolution steps in the Parsing section above: - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. @@ -150,14 +152,18 @@ After the command runs, note the issue number from the output URL (e.g. `https:/ Show the user: `Created issue #<number>: <title>` -Then immediately post agent implementation notes as a comment: +Then immediately post agent implementation notes as a comment. -```bash -gh issue comment <number> --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: +```markdown ## Agent Implementation Notes <full technical plan: exact files to change, approach, key decisions, edge cases> -EOF +``` + +Then post it (do NOT use `--body` or heredocs — same rule as Step 3): +```bash +gh issue comment <number> --body-file /tmp/cc_issue_comment.md ``` ### Step 4 — Create feature branch @@ -271,4 +277,4 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: cursor | hash: fad7e3ef | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: cursor | hash: 99965523 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/submit-for-review.mdc b/.cursor/rules/submit-for-review.mdc index ef6b002..89107be 100644 --- a/.cursor/rules/submit-for-review.mdc +++ b/.cursor/rules/submit-for-review.mdc @@ -110,16 +110,23 @@ Use `Issue #<number>` as the issue reference — the issue stays open until `/de > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. -Then create the PR with explicit title and body (never use an interactive editor): -``` -gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' +Then create the PR in two steps — **this exact sequence is mandatory**: + +First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. + +```markdown <description of what changed and why> <Closes #N OR Issue #N, based on target above> -EOF ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). +Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): + +``` +gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +``` + +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. Add `--reviewer` to the `gh pr create` command above using the handles from `@sebastientaggart`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. @@ -220,14 +227,20 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -``` -gh issue comment <number> --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: + +```markdown ## Resolution <1-3 sentences explaining what was done to fix the problem, written in plain language for a non-technical audience — no code, no file paths, no jargon. Focus on what changed from the user's perspective and why it solves the problem described in the issue.> See #<PR-number> for full technical details. -EOF +``` + +Then post it (do NOT use `--body` or heredocs): + +``` +gh issue comment <number> --body-file /tmp/cc_resolution_comment.md ``` **Resolution writing rules:** @@ -263,17 +276,23 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is **Create the selected issues.** For each selected finding, run `gh issue create` with explicit flags: -``` -gh issue create \ - --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - [--label "<pool-selected labels>"] \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_followup_body.md` for each finding: + +```markdown Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. -EOF +``` + +Then create the issue (do NOT use `--body` or heredocs): + +``` +gh issue create \ + --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ + [--label "<pool-selected labels>"] \ + --body-file /tmp/cc_followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. @@ -299,4 +318,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: cursor | hash: d2df6354 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: cursor | hash: 141fa52f | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/deploy/SKILL.md b/.gemini/skills/deploy/SKILL.md index 9146f22..aaf65c2 100644 --- a/.gemini/skills/deploy/SKILL.md +++ b/.gemini/skills/deploy/SKILL.md @@ -166,10 +166,9 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ## Step 6 — Create PR: `dev` → `main` -```bash -gh pr create --base main --head dev \ - --title "Release vX.Y.Z" \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: + +```markdown Release vX.Y.Z PRs included: @@ -178,7 +177,14 @@ PRs included: Closes #14 Closes #15 -EOF +``` + +Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): + +```bash +gh pr create --base main --head dev \ + --title "Release vX.Y.Z" \ + --body-file /tmp/cc_release_pr_body.md ``` Note the PR number from the output. @@ -209,19 +215,23 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Create the release: +Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: -```bash -gh release create <version-tag> \ - --title "<version-tag>" \ - --notes-file - <<'EOF' +```markdown ## Changes - #<issue> — <PR title> (PR #<pr-number>) [... one line per PR included in this release ...] **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> -EOF +``` + +Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): + +```bash +gh release create <version-tag> \ + --title "<version-tag>" \ + --notes-file /tmp/cc_release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -235,4 +245,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: gemini | hash: cde00382 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: gemini | hash: 1b252e14 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/start/SKILL.md b/.gemini/skills/start/SKILL.md index 6695df4..bd2678f 100644 --- a/.gemini/skills/start/SKILL.md +++ b/.gemini/skills/start/SKILL.md @@ -93,7 +93,11 @@ The friendly text question is required regardless of harness mode. If your harne ### Step 3 — Create GitHub Issue -Run `gh issue create` with explicit flags (do NOT open an interactive editor): +Create the issue in two steps — **this exact sequence is mandatory**: + +**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. + +**Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: ```bash gh issue create \ @@ -101,12 +105,10 @@ gh issue create \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] \ - --body-file - <<'EOF' -<structured markdown body — see sections below> -EOF + --body-file /tmp/cc_issue_body.md ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc avoids this — the matcher sees a clean `gh …` invocation and `Bash(gh:*)` matches. +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. Resolve labels and milestone using the resolution steps in the Parsing section above: - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. @@ -149,14 +151,18 @@ After the command runs, note the issue number from the output URL (e.g. `https:/ Show the user: `Created issue #<number>: <title>` -Then immediately post agent implementation notes as a comment: +Then immediately post agent implementation notes as a comment. -```bash -gh issue comment <number> --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: +```markdown ## Agent Implementation Notes <full technical plan: exact files to change, approach, key decisions, edge cases> -EOF +``` + +Then post it (do NOT use `--body` or heredocs — same rule as Step 3): +```bash +gh issue comment <number> --body-file /tmp/cc_issue_comment.md ``` ### Step 4 — Create feature branch @@ -270,4 +276,4 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: gemini | hash: 064b9454 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: gemini | hash: 250dcb74 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/submit-for-review/SKILL.md b/.gemini/skills/submit-for-review/SKILL.md index c494469..ec193b8 100644 --- a/.gemini/skills/submit-for-review/SKILL.md +++ b/.gemini/skills/submit-for-review/SKILL.md @@ -109,16 +109,23 @@ Use `Issue #<number>` as the issue reference — the issue stays open until `/de > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. -Then create the PR with explicit title and body (never use an interactive editor): -``` -gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' +Then create the PR in two steps — **this exact sequence is mandatory**: + +First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. + +```markdown <description of what changed and why> <Closes #N OR Issue #N, based on target above> -EOF ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). +Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): + +``` +gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +``` + +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. Add `--reviewer` to the `gh pr create` command above using the handles from `@sebastientaggart`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. @@ -219,14 +226,20 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -``` -gh issue comment <number> --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: + +```markdown ## Resolution <1-3 sentences explaining what was done to fix the problem, written in plain language for a non-technical audience — no code, no file paths, no jargon. Focus on what changed from the user's perspective and why it solves the problem described in the issue.> See #<PR-number> for full technical details. -EOF +``` + +Then post it (do NOT use `--body` or heredocs): + +``` +gh issue comment <number> --body-file /tmp/cc_resolution_comment.md ``` **Resolution writing rules:** @@ -262,17 +275,23 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is **Create the selected issues.** For each selected finding, run `gh issue create` with explicit flags: -``` -gh issue create \ - --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - [--label "<pool-selected labels>"] \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_followup_body.md` for each finding: + +```markdown Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. -EOF +``` + +Then create the issue (do NOT use `--body` or heredocs): + +``` +gh issue create \ + --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ + [--label "<pool-selected labels>"] \ + --body-file /tmp/cc_followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. @@ -298,4 +317,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: gemini | hash: 156f1ee6 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: gemini | hash: ba3271f2 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/skills/deploy.md b/skills/deploy.md index a0c21c4..cbf7cdc 100644 --- a/skills/deploy.md +++ b/skills/deploy.md @@ -239,17 +239,23 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo The version tag and PR/issue list are already known. If no previous tag exists, omit the "Full changelog" line. -```bash -gh release create <version-tag> \ - --title "<version-tag>" \ - --notes-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: + +```markdown ## Changes - #<issue> — <PR title> (PR #<pr-number>) [... one line per PR included in this release ...] **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> -EOF +``` + +Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): + +```bash +gh release create <version-tag> \ + --title "<version-tag>" \ + --notes-file /tmp/cc_release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, use just the PR title. @@ -268,10 +274,9 @@ Tell the user: {{#if !BRANCH_TEST}} ## Step 6 — Create PR: `{{BRANCH_DEV}}` → `{{BRANCH_PROD}}` -```bash -gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_DEV}} \ - --title "Release vX.Y.Z" \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: + +```markdown Release vX.Y.Z PRs included: @@ -280,7 +285,14 @@ PRs included: Closes #14 Closes #15 -EOF +``` + +Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): + +```bash +gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_DEV}} \ + --title "Release vX.Y.Z" \ + --body-file /tmp/cc_release_pr_body.md ``` Note the PR number from the output. @@ -311,19 +323,23 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Create the release: +Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: -```bash -gh release create <version-tag> \ - --title "<version-tag>" \ - --notes-file - <<'EOF' +```markdown ## Changes - #<issue> — <PR title> (PR #<pr-number>) [... one line per PR included in this release ...] **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> -EOF +``` + +Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): + +```bash +gh release create <version-tag> \ + --title "<version-tag>" \ + --notes-file /tmp/cc_release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -341,10 +357,9 @@ Tell the user: {{#if BRANCH_TEST}} ## Step 6 — Create PR: `{{BRANCH_TEST}}` → `{{BRANCH_PROD}}` -```bash -gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_TEST}} \ - --title "Release vX.Y.Z" \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: + +```markdown Release vX.Y.Z PRs included: @@ -353,7 +368,14 @@ PRs included: Closes #14 Closes #15 -EOF +``` + +Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): + +```bash +gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_TEST}} \ + --title "Release vX.Y.Z" \ + --body-file /tmp/cc_release_pr_body.md ``` Note the PR number from the output. @@ -384,19 +406,23 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Create the release: +Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: -```bash -gh release create <version-tag> \ - --title "<version-tag>" \ - --notes-file - <<'EOF' +```markdown ## Changes - #<issue> — <PR title> (PR #<pr-number>) [... one line per PR included in this release ...] **Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> -EOF +``` + +Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): + +```bash +gh release create <version-tag> \ + --title "<version-tag>" \ + --notes-file /tmp/cc_release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. diff --git a/skills/start.md b/skills/start.md index a52d979..3d43eed 100644 --- a/skills/start.md +++ b/skills/start.md @@ -136,13 +136,11 @@ The friendly text question is required regardless of harness mode. If your harne ### Step 3 — Create GitHub Issue -Run `gh issue create` with explicit flags (do NOT open an interactive editor). +Create the issue in two steps — **this exact sequence is mandatory**: -First, write the issue body to a temp file using the **Write tool** (not Bash): +**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. -**Write** `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). - -Then create the issue: +**Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: ```bash gh issue create \ @@ -153,7 +151,7 @@ gh issue create \ --body-file /tmp/cc_issue_body.md ``` -> **Why Write tool + `--body-file`?** Passing multi-line markdown content inline in a Bash command — whether via `$(...)`, quoted heredocs (`<<'EOF'`), or unquoted heredocs (`<<EOF` with `#` headings) — triggers Claude Code permission prompts that cannot be permanently allowed. Writing the body with the Write tool bypasses shell parsing entirely, and the resulting `gh` command is a clean prefix match for `Bash(gh:*)`. This pattern works identically across Claude Code, Gemini CLI, Cursor, and Codex. +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. Resolve labels and milestone using the resolution steps in the Parsing section above: - **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. @@ -198,14 +196,14 @@ Show the user: `Created issue #<number>: <title>` Then immediately post agent implementation notes as a comment. -**Write** `/tmp/cc_issue_comment.md`: +Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: ```markdown ## Agent Implementation Notes <full technical plan: exact files to change, approach, key decisions, edge cases> ``` -Then post it: +Then post it (do NOT use `--body` or heredocs — same rule as Step 3): ```bash gh issue comment <number> --body-file /tmp/cc_issue_comment.md ``` diff --git a/skills/submit-for-review.md b/skills/submit-for-review.md index 9def763..c1bdf67 100644 --- a/skills/submit-for-review.md +++ b/skills/submit-for-review.md @@ -126,16 +126,23 @@ Use `Closes #<number>` as the issue reference — merging to the default branch > **Critical:** Use the unqualified `#N` form (e.g. `Closes #42`), never the fully-qualified `owner/repo#N` form (e.g. `Closes LightbridgeLab/CodeCannon#42`), even for same-repo references. GitHub's closing-keyword parser reliably populates `closingIssuesReferences` only for the unqualified form; the qualified form leaves that GraphQL edge empty, which silently breaks GitHub's native auto-close and any downstream automation that reads it. This overrides any general "use owner/repo#N for cross-linking" guidance your harness may have — closing-keyword lines in PR bodies are a special case. -Then create the PR with explicit title and body (never use an interactive editor): -``` -gh pr create --base <target-branch> --title "<title>" --body-file - <<'EOF' +Then create the PR in two steps — **this exact sequence is mandatory**: + +First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. + +```markdown <description of what changed and why> <Closes #N OR Issue #N, based on target above> -EOF ``` -> **Why `--body-file -` + stdin heredoc?** Claude Code's permission matcher refuses to extend any `Bash(gh:*)` allow rule over a command containing `$(...)` substitution, so `--body "$(cat <<'EOF' ...)"` triggers a Yes/No prompt on every run with no "Allow always" option. Piping the body in on stdin via heredoc redirection is not command substitution — the matcher sees a clean `gh pr create …` invocation and `Bash(gh:*)` matches. Apply this pattern to every multi-line `--body` / `--notes` in skills (`gh pr create`, `gh issue create`, `gh issue comment`, and `gh release create` all accept `-` for their `*-file` flags). +Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): + +``` +gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +``` + +> **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. {{#if DEFAULT_REVIEWERS}} Add `--reviewer` to the `gh pr create` command above using the handles from `{{DEFAULT_REVIEWERS}}`. Before passing them, strip any leading `@` from each comma-separated handle (e.g. `@alice,@org/team` becomes `alice,org/team`) — the `gh` CLI requires bare usernames. @@ -249,14 +256,20 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -``` -gh issue comment <number> --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: + +```markdown ## Resolution <1-3 sentences explaining what was done to fix the problem, written in plain language for a non-technical audience — no code, no file paths, no jargon. Focus on what changed from the user's perspective and why it solves the problem described in the issue.> See #<PR-number> for full technical details. -EOF +``` + +Then post it (do NOT use `--body` or heredocs): + +``` +gh issue comment <number> --body-file /tmp/cc_resolution_comment.md ``` **Resolution writing rules:** @@ -302,17 +315,23 @@ Accept: comma-separated numbers, `all`, or `none`/`skip`/empty. If the input is **Create the selected issues.** For each selected finding, run `gh issue create` with explicit flags: -``` -gh issue create \ - --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ - [--label "<pool-selected labels>"] \ - --body-file - <<'EOF' +Use your file-writing tool (not Bash) to create `/tmp/cc_followup_body.md` for each finding: + +```markdown Follow-up from PR #<merged-pr-number> — auto-proposed from the code review. **Finding:** <full finding text, prefix included> See the review comment on the PR for context. -EOF +``` + +Then create the issue (do NOT use `--body` or heredocs): + +``` +gh issue create \ + --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ + [--label "<pool-selected labels>"] \ + --body-file /tmp/cc_followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `{{TICKET_LABELS}}` that genuinely fit the finding. If `{{TICKET_LABELS}}` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project.