diff --git a/.agents/skills/setup/SKILL.md b/.agents/skills/setup/SKILL.md index fb2ab27..6e45964 100644 --- a/.agents/skills/setup/SKILL.md +++ b/.agents/skills/setup/SKILL.md @@ -237,30 +237,7 @@ git show-ref --quiet --verify refs/remotes/origin/ git show-ref --quiet --verify refs/remotes/origin/ ``` -Display: - -``` -Setup looks healthy. Profile: - - BRANCH_PROD: - BRANCH_DEV: (exists in remote: yes/no/not set) - BRANCH_TEST: (exists in remote: yes/no/not set) - REVIEW_GATE: - CHECK_CMD: - MERGE_CMD: - Adapters: - - Optional config: - DEFAULT_MILESTONE — set / unset - DEFAULT_REVIEWERS — set / unset - TICKET_LABELS — set (N labels) / unset - TICKET_LABEL_CREATION_ALLOWED — set / unset - QA_READY_LABEL — set / unset - PLATFORM_COMPLIANCE_NOTES — set / unset - CONVENTIONS_NOTES — set / unset - SENSITIVE_AREAS_GATE — "true" (default) / "false" - SENSITIVE_AREAS_CATEGORIES — set (custom list) / unset (default 5-category list) -``` +Confirm the setup is healthy and show a scannable summary — lay it out however reads clearly. Include the inferred profile, the core workflow values (`BRANCH_PROD`; `BRANCH_DEV` and `BRANCH_TEST`, each with whether it exists in the remote; `REVIEW_GATE`; `CHECK_CMD`; `MERGE_CMD`; and the configured adapters), and then each optional config value reported as set or unset: `DEFAULT_MILESTONE`, `DEFAULT_REVIEWERS`, `TICKET_LABELS` (with label count when set), `TICKET_LABEL_CREATION_ALLOWED`, the QA labels, `PLATFORM_COMPLIANCE_NOTES`, `CONVENTIONS_NOTES`, `SENSITIVE_AREAS_GATE` (`"true"` default / `"false"`), and `SENSITIVE_AREAS_CATEGORIES` (custom list / default 5-category list). A value counts as "set" if it is present, uncommented, and non-empty in `.codecannon.yaml`. @@ -268,29 +245,13 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca ### Phase 2 — Permission audit -Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read `CodeCannon/permissions.yaml` to get the list of required command prefixes. +Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read the `commands:` list in `CodeCannon/permissions.yaml` for the required command prefixes. Use **only** the `commands:` key — commands under `validate_only:` (e.g. `cd`) are deliberately never emitted as allow rules, so they must not be reported as missing. -**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each command prefix in `permissions.yaml`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers the `git` prefix). +**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each prefix in `commands:`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers `git`). If all prefixes are covered, display `Agent permissions: all skill commands pre-approved` and continue to Phase 3. -If any prefixes are missing, show: - -``` -Agent permissions: some skill commands may prompt for approval. - - Missing allow rules: - - Bash(cd:*) - - Bash(make:*) - ... - - To pre-approve these, add them to .claude/settings.local.json (git-ignored) - or .claude/settings.json (shared with team). See docs/index.md for a full example. - - This is optional — you can approve commands individually when prompted instead. -``` - -Do not modify any settings file. This is advisory only. +If any are missing, report them as `Bash(:*)` allow rules the user can optionally add to `.claude/settings.local.json` (git-ignored) or `.claude/settings.json` (shared with team) — pointing at `docs/index.md` for a full example — and note that commands can also be approved individually when prompted. Do not modify any settings file. This is advisory only. **Other agents (Cursor, Codex, Gemini):** Skip this phase silently — Cursor doesn't prompt, and Codex/Gemini permission systems vary. The docs cover these agents separately. @@ -319,22 +280,7 @@ Wait for response. git config --get user.signingkey ``` -**If a signing key is found**, show the proposed change and confirm: - -``` -I'll enable commit and tag signing for this repo: - - git config commit.gpgsign true - git config tag.gpgsign true - - Signing key: - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. - -Continue to Phase 4. +**If a signing key is found**, show the proposed change — enabling `commit.gpgsign` and `tag.gpgsign` for this repo, and naming the signing key — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. Otherwise continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -345,19 +291,7 @@ git config --get gpg.format - If `ssh` → suggest: `git config user.signingkey ~/.ssh/id_ed25519.pub` (adjust path to the user's key). Ask the user for their SSH public key path. - If `gpg` or unset → suggest: run `gpg --list-secret-keys --keyid-format=long` to find a key ID. Ask the user for their GPG key ID. -Once the user provides a key value, show the proposed changes and confirm: - -``` -I'll configure signing for this repo: - - git config user.signingkey - git config commit.gpgsign true - git config tag.gpgsign true - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. +Once the user provides a key value, show the proposed changes — setting `user.signingkey` to the provided key and enabling `commit.gpgsign` and `tag.gpgsign` — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. If the user has no signing key and doesn't know how to create one, point them to GitHub's signing key documentation and stop: "Set up a signing key first, then run `/setup` again to enable commit signing." @@ -371,26 +305,13 @@ Run: gh label list --limit 100 --json name,color,description ``` -If zero labels are found, treat this as a greenfield repository and offer a starter label baseline before asking about `TICKET_LABELS`. - -Show this recommendation: - -``` -No labels were found. For new projects, a practical baseline is: - - bug - - enhancement - - chore - - documentation - - ready-for-qa - - qa-passed - - qa-failed -``` - -Ask: **"Create any missing labels from this baseline now? (yes/no)"** - -Wait for response. +If zero labels are found, treat this as a greenfield repository. Present the starter baseline — `bug`, `enhancement`, `chore`, `documentation`, `ready-for-qa`, `qa-passed`, `qa-failed` — and ask: **"Create any missing labels from this baseline now? (yes/no)"** -- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed`. The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. +- **yes** → run the label-create script with exactly those seven names: + ```bash + python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed + ``` + The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. - **no / skip / anything else** → continue without creating labels. #### Configured-label audit @@ -408,21 +329,11 @@ The script reads `.codecannon.yaml`, collects the names referenced by `TICKET_LA - **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py ...` with the missing names from the audit output. - **no / skip** → continue, but at the end of Phase 4 print a one-line summary: "Skipped creating: \. `/submit-for-review` will warn and continue if it needs to apply a missing label; `/qa` and `/start` may degrade similarly." -After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. +After this step (or if labels were non-zero initially), re-run the same `gh label list` fetch to pick up any labels just created. If `TICKET_LABELS` is unset or fewer than 5 labels exist, add a note: "`/start` works best with a clear issue-label pool (`TICKET_LABELS`), and `/qa` needs explicit QA lifecycle labels (`ready-for-qa`, `qa-passed`, `qa-failed`). Consider a lightweight priority scheme (e.g. `priority:high`, `priority:medium`, `priority:low`) if the team needs triage support. If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 5; otherwise leave it unset so `/start` auto-detects." -Display the results as a numbered list: - -``` -Available labels (N found): - 1. bug — Something isn't working - 2. enhancement — New feature or request - 3. good first issue — Good for newcomers - ... -``` - -Ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** +Display the available labels as a numbered list (name — description, with the count found) so the user can pick by number, then ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** Wait for the user's response. @@ -430,17 +341,7 @@ Wait for the user's response. - **numbers** (e.g. `1,3,5`) → use only those labels - **no / skip / anything else** → skip this phase, continue to Phase 5 -Show the exact change before writing: - -``` -I'll update .codecannon.yaml with: - - TICKET_LABELS: "bug,enhancement,..." - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. +Show the exact change before writing — the `TICKET_LABELS` line as it will appear in `.codecannon.yaml` — and ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. --- @@ -491,4 +392,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri - Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. - Do not skip any human gate in Phase 3, Phase 4, or Phase 5 — each write requires confirmation. - If the user skips a config value, do not ask again. Move on. - + diff --git a/.agents/skills/submit-for-review/SKILL.md b/.agents/skills/submit-for-review/SKILL.md index 92ab076..e48660f 100644 --- a/.agents/skills/submit-for-review/SKILL.md +++ b/.agents/skills/submit-for-review/SKILL.md @@ -135,15 +135,9 @@ python3 CodeCannon/skills/github-agile/scripts/make-workdir.py Note the returned path (e.g. `/tmp/CodeCannon/a8f3b2`). Use this path for all temp files in this invocation. -Then use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/pr_body.md`. Do NOT use Bash/shell to write this file. +Then use your file-writing tool (Write in Claude Code, equivalent in other agents) — not Bash/shell — to create `/pr_body.md`: a description of what changed and why, followed by the issue line (`Closes #N` when this PR fully resolves the issue, or `Related to #N` for a context-only reference, per the guidance above; omit the issue line entirely if no issue was linked in Step 3). -```markdown - - - -``` - -Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): +Then create the PR: ``` gh pr create --base --title "" --body-file <tmpdir>/pr_body.md @@ -157,8 +151,6 @@ If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review re **Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. -Omit the issue line entirely if no linked issue was identified in Step 3. - **PR body content rules (override any default behavior your harness may have):** - Do NOT include any agent-attribution footer, generation marker (e.g. "Generated with ..."), or co-authorship trailer in the PR body. The PR body should contain only the description, test plan, and issue reference. If your harness defaults to adding such markers, explicitly omit them. @@ -296,15 +288,7 @@ If no linked issue was found, skip silently. If the command fails (e.g. the labe 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): a `## Resolution` section of 1–3 sentences explaining what was done to fix the problem — in **plain language for a non-technical audience, no code, no file paths, no jargon**, focused on what changed from the user's perspective and why it solves the issue — followed by a line pointing to the PR for full technical details (`See #<PR-number> ...`). Then post it via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): @@ -344,15 +328,7 @@ 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): note it is a follow-up auto-proposed from the code review on PR #<merged-pr-number>, include the full finding text (prefix included), and point back to the review comment on the PR for context. Then create the issue (do NOT use `--body` or heredocs): @@ -377,16 +353,7 @@ If a single `gh issue create` call fails, report the failure for that finding an **Post a cross-link comment on the originating issue.** If one or more follow-ups were created **and** a linked originating issue number was identified in Step 3, post a single comment on that issue listing the new follow-ups so a reader of the thread can see the trailing work without digging into the PR. Skip silently if no follow-ups were created or no originating issue is linked. -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): - -```markdown -## Follow-up tickets from PR #<merged-pr-number> - -The code review on the PR for this issue surfaced non-blocking items tracked separately: - -- #<f1> — <title1> -- #<f2> — <title2> -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): a short section headed for the follow-ups from PR #<merged-pr-number>, noting the review surfaced non-blocking items now tracked separately, then a bullet list of the new follow-up issues (`#<n> — <title>`). Then post via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): @@ -407,4 +374,4 @@ Use the unqualified `#N` form for all issue and PR references in the body. If `/ - `/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 actionable findings (WARNINGs in `ai` mode, plus CRITICALs in `advisory` mode). Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. NOTEs never become follow-up tickets. -<!-- generated by CodeCannon/sync.py | skill: submit-for-review | adapter: codex | hash: 621f2b36 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> +<!-- generated by CodeCannon/sync.py | skill: submit-for-review | adapter: codex | hash: 7a5f7fcd | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> diff --git a/.claude/commands/setup.md b/.claude/commands/setup.md index 741750e..ec3d9e0 100644 --- a/.claude/commands/setup.md +++ b/.claude/commands/setup.md @@ -232,30 +232,7 @@ git show-ref --quiet --verify refs/remotes/origin/<BRANCH_DEV value> git show-ref --quiet --verify refs/remotes/origin/<BRANCH_TEST value> ``` -Display: - -``` -Setup looks healthy. Profile: <inferred profile> - - BRANCH_PROD: <value> - BRANCH_DEV: <value> (exists in remote: yes/no/not set) - BRANCH_TEST: <value> (exists in remote: yes/no/not set) - REVIEW_GATE: <value> - CHECK_CMD: <value> - MERGE_CMD: <value> - Adapters: <list from config> - - Optional config: - DEFAULT_MILESTONE — set / unset - DEFAULT_REVIEWERS — set / unset - TICKET_LABELS — set (N labels) / unset - TICKET_LABEL_CREATION_ALLOWED — set / unset - QA_READY_LABEL — set / unset - PLATFORM_COMPLIANCE_NOTES — set / unset - CONVENTIONS_NOTES — set / unset - SENSITIVE_AREAS_GATE — "true" (default) / "false" - SENSITIVE_AREAS_CATEGORIES — set (custom list) / unset (default 5-category list) -``` +Confirm the setup is healthy and show a scannable summary — lay it out however reads clearly. Include the inferred profile, the core workflow values (`BRANCH_PROD`; `BRANCH_DEV` and `BRANCH_TEST`, each with whether it exists in the remote; `REVIEW_GATE`; `CHECK_CMD`; `MERGE_CMD`; and the configured adapters), and then each optional config value reported as set or unset: `DEFAULT_MILESTONE`, `DEFAULT_REVIEWERS`, `TICKET_LABELS` (with label count when set), `TICKET_LABEL_CREATION_ALLOWED`, the QA labels, `PLATFORM_COMPLIANCE_NOTES`, `CONVENTIONS_NOTES`, `SENSITIVE_AREAS_GATE` (`"true"` default / `"false"`), and `SENSITIVE_AREAS_CATEGORIES` (custom list / default 5-category list). A value counts as "set" if it is present, uncommented, and non-empty in `.codecannon.yaml`. @@ -263,29 +240,13 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca ### Phase 2 — Permission audit -Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read `CodeCannon/permissions.yaml` to get the list of required command prefixes. +Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read the `commands:` list in `CodeCannon/permissions.yaml` for the required command prefixes. Use **only** the `commands:` key — commands under `validate_only:` (e.g. `cd`) are deliberately never emitted as allow rules, so they must not be reported as missing. -**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each command prefix in `permissions.yaml`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers the `git` prefix). +**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each prefix in `commands:`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers `git`). If all prefixes are covered, display `Agent permissions: all skill commands pre-approved` and continue to Phase 3. -If any prefixes are missing, show: - -``` -Agent permissions: some skill commands may prompt for approval. - - Missing allow rules: - - Bash(cd:*) - - Bash(make:*) - ... - - To pre-approve these, add them to .claude/settings.local.json (git-ignored) - or .claude/settings.json (shared with team). See docs/index.md for a full example. - - This is optional — you can approve commands individually when prompted instead. -``` - -Do not modify any settings file. This is advisory only. +If any are missing, report them as `Bash(<cmd>:*)` allow rules the user can optionally add to `.claude/settings.local.json` (git-ignored) or `.claude/settings.json` (shared with team) — pointing at `docs/index.md` for a full example — and note that commands can also be approved individually when prompted. Do not modify any settings file. This is advisory only. **Other agents (Cursor, Codex, Gemini):** Skip this phase silently — Cursor doesn't prompt, and Codex/Gemini permission systems vary. The docs cover these agents separately. @@ -314,22 +275,7 @@ Wait for response. git config --get user.signingkey ``` -**If a signing key is found**, show the proposed change and confirm: - -``` -I'll enable commit and tag signing for this repo: - - git config commit.gpgsign true - git config tag.gpgsign true - - Signing key: <truncated-key> - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. - -Continue to Phase 4. +**If a signing key is found**, show the proposed change — enabling `commit.gpgsign` and `tag.gpgsign` for this repo, and naming the signing key — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. Otherwise continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -340,19 +286,7 @@ git config --get gpg.format - If `ssh` → suggest: `git config user.signingkey ~/.ssh/id_ed25519.pub` (adjust path to the user's key). Ask the user for their SSH public key path. - If `gpg` or unset → suggest: run `gpg --list-secret-keys --keyid-format=long` to find a key ID. Ask the user for their GPG key ID. -Once the user provides a key value, show the proposed changes and confirm: - -``` -I'll configure signing for this repo: - - git config user.signingkey <provided-key> - git config commit.gpgsign true - git config tag.gpgsign true - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. +Once the user provides a key value, show the proposed changes — setting `user.signingkey` to the provided key and enabling `commit.gpgsign` and `tag.gpgsign` — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. If the user has no signing key and doesn't know how to create one, point them to GitHub's signing key documentation and stop: "Set up a signing key first, then run `/setup` again to enable commit signing." @@ -366,26 +300,13 @@ Run: gh label list --limit 100 --json name,color,description ``` -If zero labels are found, treat this as a greenfield repository and offer a starter label baseline before asking about `TICKET_LABELS`. - -Show this recommendation: - -``` -No labels were found. For new projects, a practical baseline is: - - bug - - enhancement - - chore - - documentation - - ready-for-qa - - qa-passed - - qa-failed -``` - -Ask: **"Create any missing labels from this baseline now? (yes/no)"** - -Wait for response. +If zero labels are found, treat this as a greenfield repository. Present the starter baseline — `bug`, `enhancement`, `chore`, `documentation`, `ready-for-qa`, `qa-passed`, `qa-failed` — and ask: **"Create any missing labels from this baseline now? (yes/no)"** -- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed`. The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. +- **yes** → run the label-create script with exactly those seven names: + ```bash + python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed + ``` + The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. - **no / skip / anything else** → continue without creating labels. #### Configured-label audit @@ -403,21 +324,11 @@ The script reads `.codecannon.yaml`, collects the names referenced by `TICKET_LA - **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py <name1> <name2> ...` with the missing names from the audit output. - **no / skip** → continue, but at the end of Phase 4 print a one-line summary: "Skipped creating: \<list\>. `/submit-for-review` will warn and continue if it needs to apply a missing label; `/qa` and `/start` may degrade similarly." -After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. +After this step (or if labels were non-zero initially), re-run the same `gh label list` fetch to pick up any labels just created. If `TICKET_LABELS` is unset or fewer than 5 labels exist, add a note: "`/start` works best with a clear issue-label pool (`TICKET_LABELS`), and `/qa` needs explicit QA lifecycle labels (`ready-for-qa`, `qa-passed`, `qa-failed`). Consider a lightweight priority scheme (e.g. `priority:high`, `priority:medium`, `priority:low`) if the team needs triage support. If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 5; otherwise leave it unset so `/start` auto-detects." -Display the results as a numbered list: - -``` -Available labels (N found): - 1. bug — Something isn't working - 2. enhancement — New feature or request - 3. good first issue — Good for newcomers - ... -``` - -Ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** +Display the available labels as a numbered list (name — description, with the count found) so the user can pick by number, then ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** Wait for the user's response. @@ -425,17 +336,7 @@ Wait for the user's response. - **numbers** (e.g. `1,3,5`) → use only those labels - **no / skip / anything else** → skip this phase, continue to Phase 5 -Show the exact change before writing: - -``` -I'll update .codecannon.yaml with: - - TICKET_LABELS: "bug,enhancement,..." - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. +Show the exact change before writing — the `TICKET_LABELS` line as it will appear in `.codecannon.yaml` — and ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. --- @@ -486,4 +387,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri - Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. - Do not skip any human gate in Phase 3, Phase 4, or Phase 5 — each write requires confirmation. - If the user skips a config value, do not ask again. Move on. -<!-- generated by CodeCannon/sync.py | skill: setup | adapter: claude | hash: ab9bf4d2 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> +<!-- generated by CodeCannon/sync.py | skill: setup | adapter: claude | hash: 8c83ac68 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> diff --git a/.claude/commands/submit-for-review.md b/.claude/commands/submit-for-review.md index 439163c..2899cd9 100644 --- a/.claude/commands/submit-for-review.md +++ b/.claude/commands/submit-for-review.md @@ -130,15 +130,9 @@ python3 CodeCannon/skills/github-agile/scripts/make-workdir.py Note the returned path (e.g. `/tmp/CodeCannon/a8f3b2`). Use this path for all temp files in this invocation. -Then use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `<tmpdir>/pr_body.md`. Do NOT use Bash/shell to write this file. +Then use your file-writing tool (Write in Claude Code, equivalent in other agents) — not Bash/shell — to create `<tmpdir>/pr_body.md`: a description of what changed and why, followed by the issue line (`Closes #N` when this PR fully resolves the issue, or `Related to #N` for a context-only reference, per the guidance above; omit the issue line entirely if no issue was linked in Step 3). -```markdown -<description of what changed and why> - -<Closes #N (this PR fully resolves the issue) OR Related to #N (context-only reference), per the guidance above> -``` - -Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): +Then create the PR: ``` gh pr create --base <target-branch> --title "<title>" --body-file <tmpdir>/pr_body.md @@ -152,8 +146,6 @@ If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review re **Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. -Omit the issue line entirely if no linked issue was identified in Step 3. - **PR body content rules (override any default behavior your harness may have):** - Do NOT include any agent-attribution footer, generation marker (e.g. "Generated with ..."), or co-authorship trailer in the PR body. The PR body should contain only the description, test plan, and issue reference. If your harness defaults to adding such markers, explicitly omit them. @@ -291,15 +283,7 @@ If no linked issue was found, skip silently. If the command fails (e.g. the labe 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): a `## Resolution` section of 1–3 sentences explaining what was done to fix the problem — in **plain language for a non-technical audience, no code, no file paths, no jargon**, focused on what changed from the user's perspective and why it solves the issue — followed by a line pointing to the PR for full technical details (`See #<PR-number> ...`). Then post it via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): @@ -339,15 +323,7 @@ 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): note it is a follow-up auto-proposed from the code review on PR #<merged-pr-number>, include the full finding text (prefix included), and point back to the review comment on the PR for context. Then create the issue (do NOT use `--body` or heredocs): @@ -372,16 +348,7 @@ If a single `gh issue create` call fails, report the failure for that finding an **Post a cross-link comment on the originating issue.** If one or more follow-ups were created **and** a linked originating issue number was identified in Step 3, post a single comment on that issue listing the new follow-ups so a reader of the thread can see the trailing work without digging into the PR. Skip silently if no follow-ups were created or no originating issue is linked. -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): - -```markdown -## Follow-up tickets from PR #<merged-pr-number> - -The code review on the PR for this issue surfaced non-blocking items tracked separately: - -- #<f1> — <title1> -- #<f2> — <title2> -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): a short section headed for the follow-ups from PR #<merged-pr-number>, noting the review surfaced non-blocking items now tracked separately, then a bullet list of the new follow-up issues (`#<n> — <title>`). Then post via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): @@ -402,4 +369,4 @@ Use the unqualified `#N` form for all issue and PR references in the body. If `/ - `/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 actionable findings (WARNINGs in `ai` mode, plus CRITICALs in `advisory` mode). Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. NOTEs never become follow-up tickets. -<!-- generated by CodeCannon/sync.py | skill: submit-for-review | adapter: claude | hash: be0b8ed1 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> +<!-- generated by CodeCannon/sync.py | skill: submit-for-review | adapter: claude | hash: 757a0fb2 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> diff --git a/.cursor/rules/setup.mdc b/.cursor/rules/setup.mdc index d6243d3..1e14f8f 100644 --- a/.cursor/rules/setup.mdc +++ b/.cursor/rules/setup.mdc @@ -238,30 +238,7 @@ git show-ref --quiet --verify refs/remotes/origin/<BRANCH_DEV value> git show-ref --quiet --verify refs/remotes/origin/<BRANCH_TEST value> ``` -Display: - -``` -Setup looks healthy. Profile: <inferred profile> - - BRANCH_PROD: <value> - BRANCH_DEV: <value> (exists in remote: yes/no/not set) - BRANCH_TEST: <value> (exists in remote: yes/no/not set) - REVIEW_GATE: <value> - CHECK_CMD: <value> - MERGE_CMD: <value> - Adapters: <list from config> - - Optional config: - DEFAULT_MILESTONE — set / unset - DEFAULT_REVIEWERS — set / unset - TICKET_LABELS — set (N labels) / unset - TICKET_LABEL_CREATION_ALLOWED — set / unset - QA_READY_LABEL — set / unset - PLATFORM_COMPLIANCE_NOTES — set / unset - CONVENTIONS_NOTES — set / unset - SENSITIVE_AREAS_GATE — "true" (default) / "false" - SENSITIVE_AREAS_CATEGORIES — set (custom list) / unset (default 5-category list) -``` +Confirm the setup is healthy and show a scannable summary — lay it out however reads clearly. Include the inferred profile, the core workflow values (`BRANCH_PROD`; `BRANCH_DEV` and `BRANCH_TEST`, each with whether it exists in the remote; `REVIEW_GATE`; `CHECK_CMD`; `MERGE_CMD`; and the configured adapters), and then each optional config value reported as set or unset: `DEFAULT_MILESTONE`, `DEFAULT_REVIEWERS`, `TICKET_LABELS` (with label count when set), `TICKET_LABEL_CREATION_ALLOWED`, the QA labels, `PLATFORM_COMPLIANCE_NOTES`, `CONVENTIONS_NOTES`, `SENSITIVE_AREAS_GATE` (`"true"` default / `"false"`), and `SENSITIVE_AREAS_CATEGORIES` (custom list / default 5-category list). A value counts as "set" if it is present, uncommented, and non-empty in `.codecannon.yaml`. @@ -269,29 +246,13 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca ### Phase 2 — Permission audit -Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read `CodeCannon/permissions.yaml` to get the list of required command prefixes. +Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read the `commands:` list in `CodeCannon/permissions.yaml` for the required command prefixes. Use **only** the `commands:` key — commands under `validate_only:` (e.g. `cd`) are deliberately never emitted as allow rules, so they must not be reported as missing. -**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each command prefix in `permissions.yaml`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers the `git` prefix). +**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each prefix in `commands:`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers `git`). If all prefixes are covered, display `Agent permissions: all skill commands pre-approved` and continue to Phase 3. -If any prefixes are missing, show: - -``` -Agent permissions: some skill commands may prompt for approval. - - Missing allow rules: - - Bash(cd:*) - - Bash(make:*) - ... - - To pre-approve these, add them to .claude/settings.local.json (git-ignored) - or .claude/settings.json (shared with team). See docs/index.md for a full example. - - This is optional — you can approve commands individually when prompted instead. -``` - -Do not modify any settings file. This is advisory only. +If any are missing, report them as `Bash(<cmd>:*)` allow rules the user can optionally add to `.claude/settings.local.json` (git-ignored) or `.claude/settings.json` (shared with team) — pointing at `docs/index.md` for a full example — and note that commands can also be approved individually when prompted. Do not modify any settings file. This is advisory only. **Other agents (Cursor, Codex, Gemini):** Skip this phase silently — Cursor doesn't prompt, and Codex/Gemini permission systems vary. The docs cover these agents separately. @@ -320,22 +281,7 @@ Wait for response. git config --get user.signingkey ``` -**If a signing key is found**, show the proposed change and confirm: - -``` -I'll enable commit and tag signing for this repo: - - git config commit.gpgsign true - git config tag.gpgsign true - - Signing key: <truncated-key> - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. - -Continue to Phase 4. +**If a signing key is found**, show the proposed change — enabling `commit.gpgsign` and `tag.gpgsign` for this repo, and naming the signing key — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. Otherwise continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -346,19 +292,7 @@ git config --get gpg.format - If `ssh` → suggest: `git config user.signingkey ~/.ssh/id_ed25519.pub` (adjust path to the user's key). Ask the user for their SSH public key path. - If `gpg` or unset → suggest: run `gpg --list-secret-keys --keyid-format=long` to find a key ID. Ask the user for their GPG key ID. -Once the user provides a key value, show the proposed changes and confirm: - -``` -I'll configure signing for this repo: - - git config user.signingkey <provided-key> - git config commit.gpgsign true - git config tag.gpgsign true - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. +Once the user provides a key value, show the proposed changes — setting `user.signingkey` to the provided key and enabling `commit.gpgsign` and `tag.gpgsign` — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. If the user has no signing key and doesn't know how to create one, point them to GitHub's signing key documentation and stop: "Set up a signing key first, then run `/setup` again to enable commit signing." @@ -372,26 +306,13 @@ Run: gh label list --limit 100 --json name,color,description ``` -If zero labels are found, treat this as a greenfield repository and offer a starter label baseline before asking about `TICKET_LABELS`. - -Show this recommendation: - -``` -No labels were found. For new projects, a practical baseline is: - - bug - - enhancement - - chore - - documentation - - ready-for-qa - - qa-passed - - qa-failed -``` - -Ask: **"Create any missing labels from this baseline now? (yes/no)"** - -Wait for response. +If zero labels are found, treat this as a greenfield repository. Present the starter baseline — `bug`, `enhancement`, `chore`, `documentation`, `ready-for-qa`, `qa-passed`, `qa-failed` — and ask: **"Create any missing labels from this baseline now? (yes/no)"** -- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed`. The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. +- **yes** → run the label-create script with exactly those seven names: + ```bash + python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed + ``` + The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. - **no / skip / anything else** → continue without creating labels. #### Configured-label audit @@ -409,21 +330,11 @@ The script reads `.codecannon.yaml`, collects the names referenced by `TICKET_LA - **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py <name1> <name2> ...` with the missing names from the audit output. - **no / skip** → continue, but at the end of Phase 4 print a one-line summary: "Skipped creating: \<list\>. `/submit-for-review` will warn and continue if it needs to apply a missing label; `/qa` and `/start` may degrade similarly." -After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. +After this step (or if labels were non-zero initially), re-run the same `gh label list` fetch to pick up any labels just created. If `TICKET_LABELS` is unset or fewer than 5 labels exist, add a note: "`/start` works best with a clear issue-label pool (`TICKET_LABELS`), and `/qa` needs explicit QA lifecycle labels (`ready-for-qa`, `qa-passed`, `qa-failed`). Consider a lightweight priority scheme (e.g. `priority:high`, `priority:medium`, `priority:low`) if the team needs triage support. If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 5; otherwise leave it unset so `/start` auto-detects." -Display the results as a numbered list: - -``` -Available labels (N found): - 1. bug — Something isn't working - 2. enhancement — New feature or request - 3. good first issue — Good for newcomers - ... -``` - -Ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** +Display the available labels as a numbered list (name — description, with the count found) so the user can pick by number, then ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** Wait for the user's response. @@ -431,17 +342,7 @@ Wait for the user's response. - **numbers** (e.g. `1,3,5`) → use only those labels - **no / skip / anything else** → skip this phase, continue to Phase 5 -Show the exact change before writing: - -``` -I'll update .codecannon.yaml with: - - TICKET_LABELS: "bug,enhancement,..." - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. +Show the exact change before writing — the `TICKET_LABELS` line as it will appear in `.codecannon.yaml` — and ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. --- @@ -492,4 +393,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri - Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. - Do not skip any human gate in Phase 3, Phase 4, or Phase 5 — each write requires confirmation. - If the user skips a config value, do not ask again. Move on. -<!-- generated by CodeCannon/sync.py | skill: setup | adapter: cursor | hash: 27124dd1 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> +<!-- generated by CodeCannon/sync.py | skill: setup | adapter: cursor | hash: ed1168bc | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> diff --git a/.cursor/rules/submit-for-review.mdc b/.cursor/rules/submit-for-review.mdc index 8d71838..69dc4b9 100644 --- a/.cursor/rules/submit-for-review.mdc +++ b/.cursor/rules/submit-for-review.mdc @@ -136,15 +136,9 @@ python3 CodeCannon/skills/github-agile/scripts/make-workdir.py Note the returned path (e.g. `/tmp/CodeCannon/a8f3b2`). Use this path for all temp files in this invocation. -Then use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `<tmpdir>/pr_body.md`. Do NOT use Bash/shell to write this file. +Then use your file-writing tool (Write in Claude Code, equivalent in other agents) — not Bash/shell — to create `<tmpdir>/pr_body.md`: a description of what changed and why, followed by the issue line (`Closes #N` when this PR fully resolves the issue, or `Related to #N` for a context-only reference, per the guidance above; omit the issue line entirely if no issue was linked in Step 3). -```markdown -<description of what changed and why> - -<Closes #N (this PR fully resolves the issue) OR Related to #N (context-only reference), per the guidance above> -``` - -Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): +Then create the PR: ``` gh pr create --base <target-branch> --title "<title>" --body-file <tmpdir>/pr_body.md @@ -158,8 +152,6 @@ If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review re **Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. -Omit the issue line entirely if no linked issue was identified in Step 3. - **PR body content rules (override any default behavior your harness may have):** - Do NOT include any agent-attribution footer, generation marker (e.g. "Generated with ..."), or co-authorship trailer in the PR body. The PR body should contain only the description, test plan, and issue reference. If your harness defaults to adding such markers, explicitly omit them. @@ -297,15 +289,7 @@ If no linked issue was found, skip silently. If the command fails (e.g. the labe 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): a `## Resolution` section of 1–3 sentences explaining what was done to fix the problem — in **plain language for a non-technical audience, no code, no file paths, no jargon**, focused on what changed from the user's perspective and why it solves the issue — followed by a line pointing to the PR for full technical details (`See #<PR-number> ...`). Then post it via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): @@ -345,15 +329,7 @@ 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): note it is a follow-up auto-proposed from the code review on PR #<merged-pr-number>, include the full finding text (prefix included), and point back to the review comment on the PR for context. Then create the issue (do NOT use `--body` or heredocs): @@ -378,16 +354,7 @@ If a single `gh issue create` call fails, report the failure for that finding an **Post a cross-link comment on the originating issue.** If one or more follow-ups were created **and** a linked originating issue number was identified in Step 3, post a single comment on that issue listing the new follow-ups so a reader of the thread can see the trailing work without digging into the PR. Skip silently if no follow-ups were created or no originating issue is linked. -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): - -```markdown -## Follow-up tickets from PR #<merged-pr-number> - -The code review on the PR for this issue surfaced non-blocking items tracked separately: - -- #<f1> — <title1> -- #<f2> — <title2> -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): a short section headed for the follow-ups from PR #<merged-pr-number>, noting the review surfaced non-blocking items now tracked separately, then a bullet list of the new follow-up issues (`#<n> — <title>`). Then post via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): @@ -408,4 +375,4 @@ Use the unqualified `#N` form for all issue and PR references in the body. If `/ - `/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 actionable findings (WARNINGs in `ai` mode, plus CRITICALs in `advisory` mode). Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. NOTEs never become follow-up tickets. -<!-- generated by CodeCannon/sync.py | skill: submit-for-review | adapter: cursor | hash: 1aaa7089 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> +<!-- generated by CodeCannon/sync.py | skill: submit-for-review | adapter: cursor | hash: d6de4406 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> diff --git a/.gemini/skills/setup/SKILL.md b/.gemini/skills/setup/SKILL.md index 0fb8a5c..010ea67 100644 --- a/.gemini/skills/setup/SKILL.md +++ b/.gemini/skills/setup/SKILL.md @@ -237,30 +237,7 @@ git show-ref --quiet --verify refs/remotes/origin/<BRANCH_DEV value> git show-ref --quiet --verify refs/remotes/origin/<BRANCH_TEST value> ``` -Display: - -``` -Setup looks healthy. Profile: <inferred profile> - - BRANCH_PROD: <value> - BRANCH_DEV: <value> (exists in remote: yes/no/not set) - BRANCH_TEST: <value> (exists in remote: yes/no/not set) - REVIEW_GATE: <value> - CHECK_CMD: <value> - MERGE_CMD: <value> - Adapters: <list from config> - - Optional config: - DEFAULT_MILESTONE — set / unset - DEFAULT_REVIEWERS — set / unset - TICKET_LABELS — set (N labels) / unset - TICKET_LABEL_CREATION_ALLOWED — set / unset - QA_READY_LABEL — set / unset - PLATFORM_COMPLIANCE_NOTES — set / unset - CONVENTIONS_NOTES — set / unset - SENSITIVE_AREAS_GATE — "true" (default) / "false" - SENSITIVE_AREAS_CATEGORIES — set (custom list) / unset (default 5-category list) -``` +Confirm the setup is healthy and show a scannable summary — lay it out however reads clearly. Include the inferred profile, the core workflow values (`BRANCH_PROD`; `BRANCH_DEV` and `BRANCH_TEST`, each with whether it exists in the remote; `REVIEW_GATE`; `CHECK_CMD`; `MERGE_CMD`; and the configured adapters), and then each optional config value reported as set or unset: `DEFAULT_MILESTONE`, `DEFAULT_REVIEWERS`, `TICKET_LABELS` (with label count when set), `TICKET_LABEL_CREATION_ALLOWED`, the QA labels, `PLATFORM_COMPLIANCE_NOTES`, `CONVENTIONS_NOTES`, `SENSITIVE_AREAS_GATE` (`"true"` default / `"false"`), and `SENSITIVE_AREAS_CATEGORIES` (custom list / default 5-category list). A value counts as "set" if it is present, uncommented, and non-empty in `.codecannon.yaml`. @@ -268,29 +245,13 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca ### Phase 2 — Permission audit -Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read `CodeCannon/permissions.yaml` to get the list of required command prefixes. +Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read the `commands:` list in `CodeCannon/permissions.yaml` for the required command prefixes. Use **only** the `commands:` key — commands under `validate_only:` (e.g. `cd`) are deliberately never emitted as allow rules, so they must not be reported as missing. -**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each command prefix in `permissions.yaml`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers the `git` prefix). +**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each prefix in `commands:`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers `git`). If all prefixes are covered, display `Agent permissions: all skill commands pre-approved` and continue to Phase 3. -If any prefixes are missing, show: - -``` -Agent permissions: some skill commands may prompt for approval. - - Missing allow rules: - - Bash(cd:*) - - Bash(make:*) - ... - - To pre-approve these, add them to .claude/settings.local.json (git-ignored) - or .claude/settings.json (shared with team). See docs/index.md for a full example. - - This is optional — you can approve commands individually when prompted instead. -``` - -Do not modify any settings file. This is advisory only. +If any are missing, report them as `Bash(<cmd>:*)` allow rules the user can optionally add to `.claude/settings.local.json` (git-ignored) or `.claude/settings.json` (shared with team) — pointing at `docs/index.md` for a full example — and note that commands can also be approved individually when prompted. Do not modify any settings file. This is advisory only. **Other agents (Cursor, Codex, Gemini):** Skip this phase silently — Cursor doesn't prompt, and Codex/Gemini permission systems vary. The docs cover these agents separately. @@ -319,22 +280,7 @@ Wait for response. git config --get user.signingkey ``` -**If a signing key is found**, show the proposed change and confirm: - -``` -I'll enable commit and tag signing for this repo: - - git config commit.gpgsign true - git config tag.gpgsign true - - Signing key: <truncated-key> - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. - -Continue to Phase 4. +**If a signing key is found**, show the proposed change — enabling `commit.gpgsign` and `tag.gpgsign` for this repo, and naming the signing key — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. Otherwise continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -345,19 +291,7 @@ git config --get gpg.format - If `ssh` → suggest: `git config user.signingkey ~/.ssh/id_ed25519.pub` (adjust path to the user's key). Ask the user for their SSH public key path. - If `gpg` or unset → suggest: run `gpg --list-secret-keys --keyid-format=long` to find a key ID. Ask the user for their GPG key ID. -Once the user provides a key value, show the proposed changes and confirm: - -``` -I'll configure signing for this repo: - - git config user.signingkey <provided-key> - git config commit.gpgsign true - git config tag.gpgsign true - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. +Once the user provides a key value, show the proposed changes — setting `user.signingkey` to the provided key and enabling `commit.gpgsign` and `tag.gpgsign` — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. If the user has no signing key and doesn't know how to create one, point them to GitHub's signing key documentation and stop: "Set up a signing key first, then run `/setup` again to enable commit signing." @@ -371,26 +305,13 @@ Run: gh label list --limit 100 --json name,color,description ``` -If zero labels are found, treat this as a greenfield repository and offer a starter label baseline before asking about `TICKET_LABELS`. - -Show this recommendation: - -``` -No labels were found. For new projects, a practical baseline is: - - bug - - enhancement - - chore - - documentation - - ready-for-qa - - qa-passed - - qa-failed -``` - -Ask: **"Create any missing labels from this baseline now? (yes/no)"** - -Wait for response. +If zero labels are found, treat this as a greenfield repository. Present the starter baseline — `bug`, `enhancement`, `chore`, `documentation`, `ready-for-qa`, `qa-passed`, `qa-failed` — and ask: **"Create any missing labels from this baseline now? (yes/no)"** -- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed`. The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. +- **yes** → run the label-create script with exactly those seven names: + ```bash + python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed + ``` + The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. - **no / skip / anything else** → continue without creating labels. #### Configured-label audit @@ -408,21 +329,11 @@ The script reads `.codecannon.yaml`, collects the names referenced by `TICKET_LA - **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py <name1> <name2> ...` with the missing names from the audit output. - **no / skip** → continue, but at the end of Phase 4 print a one-line summary: "Skipped creating: \<list\>. `/submit-for-review` will warn and continue if it needs to apply a missing label; `/qa` and `/start` may degrade similarly." -After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. +After this step (or if labels were non-zero initially), re-run the same `gh label list` fetch to pick up any labels just created. If `TICKET_LABELS` is unset or fewer than 5 labels exist, add a note: "`/start` works best with a clear issue-label pool (`TICKET_LABELS`), and `/qa` needs explicit QA lifecycle labels (`ready-for-qa`, `qa-passed`, `qa-failed`). Consider a lightweight priority scheme (e.g. `priority:high`, `priority:medium`, `priority:low`) if the team needs triage support. If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 5; otherwise leave it unset so `/start` auto-detects." -Display the results as a numbered list: - -``` -Available labels (N found): - 1. bug — Something isn't working - 2. enhancement — New feature or request - 3. good first issue — Good for newcomers - ... -``` - -Ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** +Display the available labels as a numbered list (name — description, with the count found) so the user can pick by number, then ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** Wait for the user's response. @@ -430,17 +341,7 @@ Wait for the user's response. - **numbers** (e.g. `1,3,5`) → use only those labels - **no / skip / anything else** → skip this phase, continue to Phase 5 -Show the exact change before writing: - -``` -I'll update .codecannon.yaml with: - - TICKET_LABELS: "bug,enhancement,..." - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. +Show the exact change before writing — the `TICKET_LABELS` line as it will appear in `.codecannon.yaml` — and ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. --- @@ -491,4 +392,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri - Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. - Do not skip any human gate in Phase 3, Phase 4, or Phase 5 — each write requires confirmation. - If the user skips a config value, do not ask again. Move on. -<!-- generated by CodeCannon/sync.py | skill: setup | adapter: gemini | hash: 38f9e584 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> +<!-- generated by CodeCannon/sync.py | skill: setup | adapter: gemini | hash: dc967ad7 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> diff --git a/.gemini/skills/submit-for-review/SKILL.md b/.gemini/skills/submit-for-review/SKILL.md index 493e9df..3bc9f8e 100644 --- a/.gemini/skills/submit-for-review/SKILL.md +++ b/.gemini/skills/submit-for-review/SKILL.md @@ -135,15 +135,9 @@ python3 CodeCannon/skills/github-agile/scripts/make-workdir.py Note the returned path (e.g. `/tmp/CodeCannon/a8f3b2`). Use this path for all temp files in this invocation. -Then use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `<tmpdir>/pr_body.md`. Do NOT use Bash/shell to write this file. +Then use your file-writing tool (Write in Claude Code, equivalent in other agents) — not Bash/shell — to create `<tmpdir>/pr_body.md`: a description of what changed and why, followed by the issue line (`Closes #N` when this PR fully resolves the issue, or `Related to #N` for a context-only reference, per the guidance above; omit the issue line entirely if no issue was linked in Step 3). -```markdown -<description of what changed and why> - -<Closes #N (this PR fully resolves the issue) OR Related to #N (context-only reference), per the guidance above> -``` - -Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): +Then create the PR: ``` gh pr create --base <target-branch> --title "<title>" --body-file <tmpdir>/pr_body.md @@ -157,8 +151,6 @@ If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review re **Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. -Omit the issue line entirely if no linked issue was identified in Step 3. - **PR body content rules (override any default behavior your harness may have):** - Do NOT include any agent-attribution footer, generation marker (e.g. "Generated with ..."), or co-authorship trailer in the PR body. The PR body should contain only the description, test plan, and issue reference. If your harness defaults to adding such markers, explicitly omit them. @@ -296,15 +288,7 @@ If no linked issue was found, skip silently. If the command fails (e.g. the labe 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): a `## Resolution` section of 1–3 sentences explaining what was done to fix the problem — in **plain language for a non-technical audience, no code, no file paths, no jargon**, focused on what changed from the user's perspective and why it solves the issue — followed by a line pointing to the PR for full technical details (`See #<PR-number> ...`). Then post it via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): @@ -344,15 +328,7 @@ 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): note it is a follow-up auto-proposed from the code review on PR #<merged-pr-number>, include the full finding text (prefix included), and point back to the review comment on the PR for context. Then create the issue (do NOT use `--body` or heredocs): @@ -377,16 +353,7 @@ If a single `gh issue create` call fails, report the failure for that finding an **Post a cross-link comment on the originating issue.** If one or more follow-ups were created **and** a linked originating issue number was identified in Step 3, post a single comment on that issue listing the new follow-ups so a reader of the thread can see the trailing work without digging into the PR. Skip silently if no follow-ups were created or no originating issue is linked. -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): - -```markdown -## Follow-up tickets from PR #<merged-pr-number> - -The code review on the PR for this issue surfaced non-blocking items tracked separately: - -- #<f1> — <title1> -- #<f2> — <title2> -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): a short section headed for the follow-ups from PR #<merged-pr-number>, noting the review surfaced non-blocking items now tracked separately, then a bullet list of the new follow-up issues (`#<n> — <title>`). Then post via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): @@ -407,4 +374,4 @@ Use the unqualified `#N` form for all issue and PR references in the body. If `/ - `/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 actionable findings (WARNINGs in `ai` mode, plus CRITICALs in `advisory` mode). Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. NOTEs never become follow-up tickets. -<!-- generated by CodeCannon/sync.py | skill: submit-for-review | adapter: gemini | hash: 4620184f | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> +<!-- generated by CodeCannon/sync.py | skill: submit-for-review | adapter: gemini | hash: dd7b55b2 | DO NOT EDIT — run CodeCannon/sync.py to regenerate --> diff --git a/docs/index.md b/docs/index.md index f81e24b..2977948 100644 --- a/docs/index.md +++ b/docs/index.md @@ -123,7 +123,6 @@ Code Cannon skills are agent-agnostic, but each agent has its own quirks. This s "permissions": { "defaultMode": "acceptEdits", "allow": [ - "Bash(cd:*)", "Bash(git:*)", "Bash(gh:*)", "Bash(make:*)", @@ -144,7 +143,7 @@ Code Cannon skills are agent-agnostic, but each agent has its own quirks. This s } ``` -`defaultMode: "acceptEdits"` auto-approves file edits and common filesystem ops. `allow` rules pre-approve matching bash commands (wildcards supported). `deny` rules always win — dangerous operations still prompt. Adjust the `allow` list to match your project's tooling. +`defaultMode: "acceptEdits"` auto-approves file edits and common filesystem ops. `allow` rules pre-approve matching bash commands (wildcards supported). `deny` rules always win — dangerous operations still prompt. Adjust the `allow` list to match your project's tooling. (`Bash(cd:*)` is intentionally omitted — Code Cannon skills use single, statically-analyzable commands, and blessing `cd` would re-invite the compound `cd … && …` shape the allowlist is designed to avoid; `/setup`'s permission audit will not ask for it.) ### Cursor diff --git a/permissions.yaml b/permissions.yaml index ca0a0b0..e3ec8f4 100644 --- a/permissions.yaml +++ b/permissions.yaml @@ -15,8 +15,8 @@ # (no `&&`/`|`/`;` chains, no `$(...)`, no redirections) — sync.py --validate # enforces this. Push irreducibly-complex commands into scripts/ instead. +# Emitted as allow rules AND validated in skill code blocks. commands: - - cd - cp - gh - git @@ -28,3 +28,11 @@ commands: - python3 - test - which + +# Validated in skill code blocks but deliberately NOT emitted as allow rules. +# Blessing these would re-invite the compound shapes #202 removed (e.g. `cd … && …`). +# Keeping the exclusion here — rather than as a special case in code — means the +# emitter, the shape/prefix validators, and the /setup permission audit all agree +# without any of them hardcoding a command name. +validate_only: + - cd diff --git a/skills/github-agile/setup.md b/skills/github-agile/setup.md index 9bdf095..fa636db 100644 --- a/skills/github-agile/setup.md +++ b/skills/github-agile/setup.md @@ -235,30 +235,7 @@ git show-ref --quiet --verify refs/remotes/origin/<BRANCH_DEV value> git show-ref --quiet --verify refs/remotes/origin/<BRANCH_TEST value> ``` -Display: - -``` -Setup looks healthy. Profile: <inferred profile> - - BRANCH_PROD: <value> - BRANCH_DEV: <value> (exists in remote: yes/no/not set) - BRANCH_TEST: <value> (exists in remote: yes/no/not set) - REVIEW_GATE: <value> - CHECK_CMD: <value> - MERGE_CMD: <value> - Adapters: <list from config> - - Optional config: - DEFAULT_MILESTONE — set / unset - DEFAULT_REVIEWERS — set / unset - TICKET_LABELS — set (N labels) / unset - TICKET_LABEL_CREATION_ALLOWED — set / unset - QA_READY_LABEL — set / unset - PLATFORM_COMPLIANCE_NOTES — set / unset - CONVENTIONS_NOTES — set / unset - SENSITIVE_AREAS_GATE — "true" (default) / "false" - SENSITIVE_AREAS_CATEGORIES — set (custom list) / unset (default 5-category list) -``` +Confirm the setup is healthy and show a scannable summary — lay it out however reads clearly. Include the inferred profile, the core workflow values (`BRANCH_PROD`; `BRANCH_DEV` and `BRANCH_TEST`, each with whether it exists in the remote; `REVIEW_GATE`; `CHECK_CMD`; `MERGE_CMD`; and the configured adapters), and then each optional config value reported as set or unset: `DEFAULT_MILESTONE`, `DEFAULT_REVIEWERS`, `TICKET_LABELS` (with label count when set), `TICKET_LABEL_CREATION_ALLOWED`, the QA labels, `PLATFORM_COMPLIANCE_NOTES`, `CONVENTIONS_NOTES`, `SENSITIVE_AREAS_GATE` (`"true"` default / `"false"`), and `SENSITIVE_AREAS_CATEGORIES` (custom list / default 5-category list). A value counts as "set" if it is present, uncommented, and non-empty in `.codecannon.yaml`. @@ -266,29 +243,13 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca ### Phase 2 — Permission audit -Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read `CodeCannon/permissions.yaml` to get the list of required command prefixes. +Check whether the agent's permission configuration covers the shell commands Code Cannon skills use. Read the `commands:` list in `CodeCannon/permissions.yaml` for the required command prefixes. Use **only** the `commands:` key — commands under `validate_only:` (e.g. `cd`) are deliberately never emitted as allow rules, so they must not be reported as missing. -**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each command prefix in `permissions.yaml`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers the `git` prefix). +**Claude Code:** Read `.claude/settings.local.json` (if it exists) and `.claude/settings.json` (if it exists). Collect all `Bash(...)` entries from the `permissions.allow` arrays in both files. For each prefix in `commands:`, check whether an allow rule covers it (e.g. `Bash(git:*)` or `Bash(git *)` covers `git`). If all prefixes are covered, display `Agent permissions: all skill commands pre-approved` and continue to Phase 3. -If any prefixes are missing, show: - -``` -Agent permissions: some skill commands may prompt for approval. - - Missing allow rules: - - Bash(cd:*) - - Bash(make:*) - ... - - To pre-approve these, add them to .claude/settings.local.json (git-ignored) - or .claude/settings.json (shared with team). See docs/index.md for a full example. - - This is optional — you can approve commands individually when prompted instead. -``` - -Do not modify any settings file. This is advisory only. +If any are missing, report them as `Bash(<cmd>:*)` allow rules the user can optionally add to `.claude/settings.local.json` (git-ignored) or `.claude/settings.json` (shared with team) — pointing at `docs/index.md` for a full example — and note that commands can also be approved individually when prompted. Do not modify any settings file. This is advisory only. **Other agents (Cursor, Codex, Gemini):** Skip this phase silently — Cursor doesn't prompt, and Codex/Gemini permission systems vary. The docs cover these agents separately. @@ -317,22 +278,7 @@ Wait for response. git config --get user.signingkey ``` -**If a signing key is found**, show the proposed change and confirm: - -``` -I'll enable commit and tag signing for this repo: - - git config commit.gpgsign true - git config tag.gpgsign true - - Signing key: <truncated-key> - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. - -Continue to Phase 4. +**If a signing key is found**, show the proposed change — enabling `commit.gpgsign` and `tag.gpgsign` for this repo, and naming the signing key — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. Otherwise continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -343,19 +289,7 @@ git config --get gpg.format - If `ssh` → suggest: `git config user.signingkey ~/.ssh/id_ed25519.pub` (adjust path to the user's key). Ask the user for their SSH public key path. - If `gpg` or unset → suggest: run `gpg --list-secret-keys --keyid-format=long` to find a key ID. Ask the user for their GPG key ID. -Once the user provides a key value, show the proposed changes and confirm: - -``` -I'll configure signing for this repo: - - git config user.signingkey <provided-key> - git config commit.gpgsign true - git config tag.gpgsign true - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. If no, skip to Phase 4. +Once the user provides a key value, show the proposed changes — setting `user.signingkey` to the provided key and enabling `commit.gpgsign` and `tag.gpgsign` — then ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. If no, skip to Phase 4. If the user has no signing key and doesn't know how to create one, point them to GitHub's signing key documentation and stop: "Set up a signing key first, then run `/setup` again to enable commit signing." @@ -369,26 +303,13 @@ Run: gh label list --limit 100 --json name,color,description ``` -If zero labels are found, treat this as a greenfield repository and offer a starter label baseline before asking about `TICKET_LABELS`. - -Show this recommendation: - -``` -No labels were found. For new projects, a practical baseline is: - - bug - - enhancement - - chore - - documentation - - ready-for-qa - - qa-passed - - qa-failed -``` - -Ask: **"Create any missing labels from this baseline now? (yes/no)"** - -Wait for response. +If zero labels are found, treat this as a greenfield repository. Present the starter baseline — `bug`, `enhancement`, `chore`, `documentation`, `ready-for-qa`, `qa-passed`, `qa-failed` — and ask: **"Create any missing labels from this baseline now? (yes/no)"** -- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed`. The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. +- **yes** → run the label-create script with exactly those seven names: + ```bash + python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed + ``` + The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists. - **no / skip / anything else** → continue without creating labels. #### Configured-label audit @@ -406,21 +327,11 @@ The script reads `.codecannon.yaml`, collects the names referenced by `TICKET_LA - **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py <name1> <name2> ...` with the missing names from the audit output. - **no / skip** → continue, but at the end of Phase 4 print a one-line summary: "Skipped creating: \<list\>. `/submit-for-review` will warn and continue if it needs to apply a missing label; `/qa` and `/start` may degrade similarly." -After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. +After this step (or if labels were non-zero initially), re-run the same `gh label list` fetch to pick up any labels just created. If `TICKET_LABELS` is unset or fewer than 5 labels exist, add a note: "`/start` works best with a clear issue-label pool (`TICKET_LABELS`), and `/qa` needs explicit QA lifecycle labels (`ready-for-qa`, `qa-passed`, `qa-failed`). Consider a lightweight priority scheme (e.g. `priority:high`, `priority:medium`, `priority:low`) if the team needs triage support. If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 5; otherwise leave it unset so `/start` auto-detects." -Display the results as a numbered list: - -``` -Available labels (N found): - 1. bug — Something isn't working - 2. enhancement — New feature or request - 3. good first issue — Good for newcomers - ... -``` - -Ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** +Display the available labels as a numbered list (name — description, with the count found) so the user can pick by number, then ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** Wait for the user's response. @@ -428,17 +339,7 @@ Wait for the user's response. - **numbers** (e.g. `1,3,5`) → use only those labels - **no / skip / anything else** → skip this phase, continue to Phase 5 -Show the exact change before writing: - -``` -I'll update .codecannon.yaml with: - - TICKET_LABELS: "bug,enhancement,..." - -Proceed? (yes/no) -``` - -Wait for confirmation. Write only on yes. +Show the exact change before writing — the `TICKET_LABELS` line as it will appear in `.codecannon.yaml` — and ask "Proceed? (yes/no)". Wait for confirmation. Write only on yes. --- diff --git a/skills/github-agile/submit-for-review.md b/skills/github-agile/submit-for-review.md index 753b312..bc21ea9 100644 --- a/skills/github-agile/submit-for-review.md +++ b/skills/github-agile/submit-for-review.md @@ -153,15 +153,9 @@ python3 CodeCannon/skills/github-agile/scripts/make-workdir.py Note the returned path (e.g. `/tmp/CodeCannon/a8f3b2`). Use this path for all temp files in this invocation. -Then use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `<tmpdir>/pr_body.md`. Do NOT use Bash/shell to write this file. +Then use your file-writing tool (Write in Claude Code, equivalent in other agents) — not Bash/shell — to create `<tmpdir>/pr_body.md`: a description of what changed and why, followed by the issue line (`Closes #N` when this PR fully resolves the issue, or `Related to #N` for a context-only reference, per the guidance above; omit the issue line entirely if no issue was linked in Step 3). -```markdown -<description of what changed and why> - -<Closes #N (this PR fully resolves the issue) OR Related to #N (context-only reference), per the guidance above> -``` - -Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): +Then create the PR: ``` gh pr create --base <target-branch> --title "<title>" --body-file <tmpdir>/pr_body.md @@ -177,8 +171,6 @@ If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review re **Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. -Omit the issue line entirely if no linked issue was identified in Step 3. - **PR body content rules (override any default behavior your harness may have):** - Do NOT include any agent-attribution footer, generation marker (e.g. "Generated with ..."), or co-authorship trailer in the PR body. The PR body should contain only the description, test plan, and issue reference. If your harness defaults to adding such markers, explicitly omit them. @@ -327,15 +319,7 @@ If no linked issue was found, skip silently. If the command fails (e.g. the labe 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): a `## Resolution` section of 1–3 sentences explaining what was done to fix the problem — in **plain language for a non-technical audience, no code, no file paths, no jargon**, focused on what changed from the user's perspective and why it solves the issue — followed by a line pointing to the PR for full technical details (`See #<PR-number> ...`). Then post it via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): @@ -385,15 +369,7 @@ 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: -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): - -```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. -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_body.md` for each finding (same temp directory from Step 6): note it is a follow-up auto-proposed from the code review on PR #<merged-pr-number>, include the full finding text (prefix included), and point back to the review comment on the PR for context. Then create the issue (do NOT use `--body` or heredocs): @@ -418,16 +394,7 @@ If a single `gh issue create` call fails, report the failure for that finding an **Post a cross-link comment on the originating issue.** If one or more follow-ups were created **and** a linked originating issue number was identified in Step 3, post a single comment on that issue listing the new follow-ups so a reader of the thread can see the trailing work without digging into the PR. Skip silently if no follow-ups were created or no originating issue is linked. -Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): - -```markdown -## Follow-up tickets from PR #<merged-pr-number> - -The code review on the PR for this issue surfaced non-blocking items tracked separately: - -- #<f1> — <title1> -- #<f2> — <title2> -``` +Use your file-writing tool (not Bash) to create `<tmpdir>/followup_link_comment.md` (same temp directory from Step 6): a short section headed for the follow-ups from PR #<merged-pr-number>, noting the review surfaced non-blocking items now tracked separately, then a bullet list of the new follow-up issues (`#<n> — <title>`). Then post via the comment-posting script (do NOT use `gh issue comment` with `--body` or heredocs): diff --git a/sync.py b/sync.py index 2e23da9..cb3caa1 100755 --- a/sync.py +++ b/sync.py @@ -508,6 +508,14 @@ def validate_placeholders(skill_files, project_config): return errors +def _validated_commands(perms): + """Commands permitted to appear in skill code blocks: those emitted as allow + rules (`commands:`) plus validate-only commands (`validate_only:`, e.g. `cd`) + that are intentionally never emitted. Both are legal in skills and must pass + validation; only `commands:` becomes a harness allow rule.""" + return list(perms.get('commands', [])) + list(perms.get('validate_only', [])) + + def validate_permissions(skill_files): """Check that command prefixes in skill code blocks are listed in permissions.yaml.""" perms_path = CODECANNON_DIR / 'permissions.yaml' @@ -515,7 +523,7 @@ def validate_permissions(skill_files): return [" permissions.yaml not found"] perms = parse_yaml_simple(perms_path.read_text()) - allowed = set(perms.get('commands', [])) + allowed = set(_validated_commands(perms)) if not allowed: return [" permissions.yaml has no commands listed"] @@ -572,7 +580,7 @@ def validate_command_shapes(skill_files): perms_path = CODECANNON_DIR / 'permissions.yaml' allowed = set() if perms_path.exists(): - allowed = set(parse_yaml_simple(perms_path.read_text()).get('commands', [])) + allowed = set(_validated_commands(parse_yaml_simple(perms_path.read_text()))) block_re = re.compile(r'```[a-z]*\n(.*?)```', re.DOTALL) errors = [] @@ -600,9 +608,11 @@ def validate_command_shapes(skill_files): def _allow_rules_from_permissions(): - """Turn permissions.yaml's command list into harness allow rules. `cd` is - intentionally excluded: the skills no longer use it, and blessing it would - re-invite the compound `cd … && …` shape this work removes. + """Turn permissions.yaml's `commands:` list into harness allow rules. Commands + under `validate_only:` (e.g. `cd`) are intentionally not emitted — blessing + them would re-invite the compound `cd … && …` shape this work removes. That + exclusion lives in the data (the `commands:` / `validate_only:` split), so no + command name is special-cased here. The rules are broad prefixes (e.g. `Bash(git:*)`, `Bash(gh:*)`), which auto-approve destructive subcommands too (`git push --force`, etc.). That @@ -613,7 +623,7 @@ def _allow_rules_from_permissions(): if not perms_path.exists(): return [] cmds = parse_yaml_simple(perms_path.read_text()).get('commands', []) - return [f"Bash({c}:*)" for c in cmds if c != 'cd'] + return [f"Bash({c}:*)" for c in cmds] def generate_permissions(adapter, project_root, args): diff --git a/tests/test_sync.py b/tests/test_sync.py index 099e3c3..dff943d 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -1011,6 +1011,49 @@ def test_preserves_existing_settings(self): self.assertEqual(settings["other"], "keep") +class TestPermissionCommandSplit(unittest.TestCase): + """Regression tests for #208: `commands:` vs `validate_only:` in permissions.yaml. + + `validate_only` commands (e.g. `cd`) must stay legal in skill code blocks + (validated) while never being emitted as harness allow rules. This split is + what stops the /setup permission audit from reporting `Bash(cd:*)` as a + missing rule on every run — the audit reads `commands:`, which no longer + contains `cd`. + """ + + def test_real_permissions_split(self): + perms = sync.parse_yaml_simple( + (REPO_ROOT / "permissions.yaml").read_text()) + # cd lives under validate_only, not commands. + self.assertNotIn("cd", perms.get("commands", [])) + self.assertIn("cd", perms.get("validate_only", [])) + + def test_cd_excluded_from_allow_rules(self): + rules = sync._allow_rules_from_permissions() + self.assertNotIn("Bash(cd:*)", rules) + # A normal command still becomes a rule. + self.assertIn("Bash(git:*)", rules) + + def test_validate_only_commands_still_validated(self): + # The union that gates skill code blocks must still include cd, so a + # skill legitimately using `cd` is not flagged as an unknown command. + perms = sync.parse_yaml_simple( + (REPO_ROOT / "permissions.yaml").read_text()) + validated = sync._validated_commands(perms) + self.assertIn("cd", validated) + self.assertIn("git", validated) + + def test_validated_commands_unions_both_keys(self): + # Logic test against a synthetic fixture, independent of the real file. + perms = {"commands": ["git", "make"], "validate_only": ["cd"]} + self.assertEqual( + set(sync._validated_commands(perms)), {"git", "make", "cd"}) + + def test_validated_commands_tolerates_missing_validate_only(self): + self.assertEqual( + sync._validated_commands({"commands": ["git"]}), ["git"]) + + # ═══════════════════════════════════════════════════════════════════════════════ # MAIN / CLI INTEGRATION # ═══════════════════════════════════════════════════════════════════════════════