diff --git a/.agents/skills/setup/SKILL.md b/.agents/skills/setup/SKILL.md index d8db8d6..936f3d2 100644 --- a/.agents/skills/setup/SKILL.md +++ b/.agents/skills/setup/SKILL.md @@ -258,7 +258,37 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca --- -### Phase 2 — Commit signing +### 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. + +**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). + +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. + +**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. + +--- + +### Phase 3 — Commit signing Check whether commit signing is already configured at any level (local or global): @@ -266,13 +296,13 @@ Check whether commit signing is already configured at any level (local or global git config --get commit.gpgsign ``` -If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 3. +If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 4. If not `true`, ask: **"Does this project require signed commits? (yes/no)"** Wait for response. -- **no / skip** → continue to Phase 3. +- **no / skip** → continue to Phase 4. - **yes** → proceed with signing setup. **Verify a signing key exists:** @@ -294,9 +324,9 @@ I'll enable commit and tag signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +Wait for confirmation. Write only on yes. If no, skip to Phase 4. -Continue to Phase 3. +Continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -319,13 +349,13 @@ I'll configure signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +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." --- -### Phase 3 — Label population +### Phase 4 — Label population Run: @@ -357,7 +387,7 @@ Wait for response. After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. -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 4; otherwise leave it unset so `/start` auto-detects." +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: @@ -375,7 +405,7 @@ Wait for the user's response. - **yes** → use all labels - **numbers** (e.g. `1,3,5`) → use only those labels -- **no / skip / anything else** → skip this phase, continue to Phase 4 +- **no / skip / anything else** → skip this phase, continue to Phase 5 Show the exact change before writing: @@ -391,7 +421,7 @@ Wait for confirmation. Write only on yes. --- -### Phase 4 — Optional config walkthrough (profile-aware) +### Phase 5 — Optional config walkthrough (profile-aware) First, infer the current profile using the same rules as Phase 1. @@ -413,7 +443,7 @@ The walkthrough adapts based on profile. Walk through each applicable unset valu --- -### Phase 5 — Team sharing +### Phase 6 — Team sharing After completing or skipping the config walkthrough, say: @@ -431,11 +461,11 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri ## Hard rules -- Only modify `.codecannon.yaml` and local git config (Phase 2 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). +- Only modify `.codecannon.yaml` and local git config (Phase 3 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). - Do not run `sync.py` without explicit user permission. - Do not create `.codecannon.yaml` without explicit user permission. - Do not report a configuration problem unless confident the condition is genuinely broken. Prefer false negatives over false positives on all diagnostic checks. - 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 2, Phase 3, or Phase 4 — each write requires confirmation. +- 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 eea5728..392fa58 100644 --- a/.agents/skills/submit-for-review/SKILL.md +++ b/.agents/skills/submit-for-review/SKILL.md @@ -32,16 +32,16 @@ If the current branch matches any of the above, **abort immediately** and say: ## Step 2 — Type-check gate -First, move to the repository root so the command resolves against the correct Makefile / project config: +First, find the repository root: ``` -cd "$(git rev-parse --show-toplevel)" +git rev-parse --show-toplevel ``` -Then verify the make target exists before running it. Extract the target name from `make check` (e.g. `make check` → `check`) and run: +Then `cd` to the returned path and verify the make target exists. Extract the target name from `make check` (e.g. `make check` → `check`) and run: ``` -make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: @@ -188,10 +188,10 @@ Wait for the review to complete and report its verdict. ## Step 8 — Act on verdict -Before merging, verify the merge target exists. Move to the repo root, extract the target name from `make merge` (e.g. `make merge` → `merge`), and run: +Before merging, verify the merge target exists. Find the repo root with `git rev-parse --show-toplevel`, then extract the target name from `make merge` (e.g. `make merge` → `merge`) and run: ``` -cd "$(git rev-parse --show-toplevel)" && make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: @@ -350,4 +350,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. - + diff --git a/.claude/commands/setup.md b/.claude/commands/setup.md index 4c219e1..0adb239 100644 --- a/.claude/commands/setup.md +++ b/.claude/commands/setup.md @@ -253,7 +253,37 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca --- -### Phase 2 — Commit signing +### 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. + +**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). + +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. + +**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. + +--- + +### Phase 3 — Commit signing Check whether commit signing is already configured at any level (local or global): @@ -261,13 +291,13 @@ Check whether commit signing is already configured at any level (local or global git config --get commit.gpgsign ``` -If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 3. +If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 4. If not `true`, ask: **"Does this project require signed commits? (yes/no)"** Wait for response. -- **no / skip** → continue to Phase 3. +- **no / skip** → continue to Phase 4. - **yes** → proceed with signing setup. **Verify a signing key exists:** @@ -289,9 +319,9 @@ I'll enable commit and tag signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +Wait for confirmation. Write only on yes. If no, skip to Phase 4. -Continue to Phase 3. +Continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -314,13 +344,13 @@ I'll configure signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +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." --- -### Phase 3 — Label population +### Phase 4 — Label population Run: @@ -352,7 +382,7 @@ Wait for response. After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. -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 4; otherwise leave it unset so `/start` auto-detects." +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: @@ -370,7 +400,7 @@ Wait for the user's response. - **yes** → use all labels - **numbers** (e.g. `1,3,5`) → use only those labels -- **no / skip / anything else** → skip this phase, continue to Phase 4 +- **no / skip / anything else** → skip this phase, continue to Phase 5 Show the exact change before writing: @@ -386,7 +416,7 @@ Wait for confirmation. Write only on yes. --- -### Phase 4 — Optional config walkthrough (profile-aware) +### Phase 5 — Optional config walkthrough (profile-aware) First, infer the current profile using the same rules as Phase 1. @@ -408,7 +438,7 @@ The walkthrough adapts based on profile. Walk through each applicable unset valu --- -### Phase 5 — Team sharing +### Phase 6 — Team sharing After completing or skipping the config walkthrough, say: @@ -426,11 +456,11 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri ## Hard rules -- Only modify `.codecannon.yaml` and local git config (Phase 2 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). +- Only modify `.codecannon.yaml` and local git config (Phase 3 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). - Do not run `sync.py` without explicit user permission. - Do not create `.codecannon.yaml` without explicit user permission. - Do not report a configuration problem unless confident the condition is genuinely broken. Prefer false negatives over false positives on all diagnostic checks. - 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 2, Phase 3, or Phase 4 — each write requires confirmation. +- 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/.claude/commands/submit-for-review.md b/.claude/commands/submit-for-review.md index b9fabb0..d21f08e 100644 --- a/.claude/commands/submit-for-review.md +++ b/.claude/commands/submit-for-review.md @@ -27,16 +27,16 @@ If the current branch matches any of the above, **abort immediately** and say: ## Step 2 — Type-check gate -First, move to the repository root so the command resolves against the correct Makefile / project config: +First, find the repository root: ``` -cd "$(git rev-parse --show-toplevel)" +git rev-parse --show-toplevel ``` -Then verify the make target exists before running it. Extract the target name from `make check` (e.g. `make check` → `check`) and run: +Then `cd` to the returned path and verify the make target exists. Extract the target name from `make check` (e.g. `make check` → `check`) and run: ``` -make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: @@ -183,10 +183,10 @@ Wait for the review to complete and report its verdict. ## Step 8 — Act on verdict -Before merging, verify the merge target exists. Move to the repo root, extract the target name from `make merge` (e.g. `make merge` → `merge`), and run: +Before merging, verify the merge target exists. Find the repo root with `git rev-parse --show-toplevel`, then extract the target name from `make merge` (e.g. `make merge` → `merge`) and run: ``` -cd "$(git rev-parse --show-toplevel)" && make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: @@ -345,4 +345,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. - + diff --git a/.claude/settings.local.json.example b/.claude/settings.local.json.example index 4e6cacb..c1ca260 100644 --- a/.claude/settings.local.json.example +++ b/.claude/settings.local.json.example @@ -2,6 +2,7 @@ "permissions": { "allow": [ "Bash(cat:*)", + "Bash(cd:*)", "Bash(cp:*)", "Bash(gh:*)", "Bash(git:*)", diff --git a/.cursor/rules/setup.mdc b/.cursor/rules/setup.mdc index 8162698..8977e7d 100644 --- a/.cursor/rules/setup.mdc +++ b/.cursor/rules/setup.mdc @@ -259,7 +259,37 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca --- -### Phase 2 — Commit signing +### 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. + +**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). + +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. + +**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. + +--- + +### Phase 3 — Commit signing Check whether commit signing is already configured at any level (local or global): @@ -267,13 +297,13 @@ Check whether commit signing is already configured at any level (local or global git config --get commit.gpgsign ``` -If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 3. +If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 4. If not `true`, ask: **"Does this project require signed commits? (yes/no)"** Wait for response. -- **no / skip** → continue to Phase 3. +- **no / skip** → continue to Phase 4. - **yes** → proceed with signing setup. **Verify a signing key exists:** @@ -295,9 +325,9 @@ I'll enable commit and tag signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +Wait for confirmation. Write only on yes. If no, skip to Phase 4. -Continue to Phase 3. +Continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -320,13 +350,13 @@ I'll configure signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +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." --- -### Phase 3 — Label population +### Phase 4 — Label population Run: @@ -358,7 +388,7 @@ Wait for response. After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. -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 4; otherwise leave it unset so `/start` auto-detects." +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: @@ -376,7 +406,7 @@ Wait for the user's response. - **yes** → use all labels - **numbers** (e.g. `1,3,5`) → use only those labels -- **no / skip / anything else** → skip this phase, continue to Phase 4 +- **no / skip / anything else** → skip this phase, continue to Phase 5 Show the exact change before writing: @@ -392,7 +422,7 @@ Wait for confirmation. Write only on yes. --- -### Phase 4 — Optional config walkthrough (profile-aware) +### Phase 5 — Optional config walkthrough (profile-aware) First, infer the current profile using the same rules as Phase 1. @@ -414,7 +444,7 @@ The walkthrough adapts based on profile. Walk through each applicable unset valu --- -### Phase 5 — Team sharing +### Phase 6 — Team sharing After completing or skipping the config walkthrough, say: @@ -432,11 +462,11 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri ## Hard rules -- Only modify `.codecannon.yaml` and local git config (Phase 2 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). +- Only modify `.codecannon.yaml` and local git config (Phase 3 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). - Do not run `sync.py` without explicit user permission. - Do not create `.codecannon.yaml` without explicit user permission. - Do not report a configuration problem unless confident the condition is genuinely broken. Prefer false negatives over false positives on all diagnostic checks. - 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 2, Phase 3, or Phase 4 — each write requires confirmation. +- 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/.cursor/rules/submit-for-review.mdc b/.cursor/rules/submit-for-review.mdc index edae937..be6468f 100644 --- a/.cursor/rules/submit-for-review.mdc +++ b/.cursor/rules/submit-for-review.mdc @@ -33,16 +33,16 @@ If the current branch matches any of the above, **abort immediately** and say: ## Step 2 — Type-check gate -First, move to the repository root so the command resolves against the correct Makefile / project config: +First, find the repository root: ``` -cd "$(git rev-parse --show-toplevel)" +git rev-parse --show-toplevel ``` -Then verify the make target exists before running it. Extract the target name from `make check` (e.g. `make check` → `check`) and run: +Then `cd` to the returned path and verify the make target exists. Extract the target name from `make check` (e.g. `make check` → `check`) and run: ``` -make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: @@ -189,10 +189,10 @@ Wait for the review to complete and report its verdict. ## Step 8 — Act on verdict -Before merging, verify the merge target exists. Move to the repo root, extract the target name from `make merge` (e.g. `make merge` → `merge`), and run: +Before merging, verify the merge target exists. Find the repo root with `git rev-parse --show-toplevel`, then extract the target name from `make merge` (e.g. `make merge` → `merge`) and run: ``` -cd "$(git rev-parse --show-toplevel)" && make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: @@ -351,4 +351,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. - + diff --git a/.gemini/skills/setup/SKILL.md b/.gemini/skills/setup/SKILL.md index d9686c5..4ab317b 100644 --- a/.gemini/skills/setup/SKILL.md +++ b/.gemini/skills/setup/SKILL.md @@ -258,7 +258,37 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca --- -### Phase 2 — Commit signing +### 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. + +**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). + +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. + +**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. + +--- + +### Phase 3 — Commit signing Check whether commit signing is already configured at any level (local or global): @@ -266,13 +296,13 @@ Check whether commit signing is already configured at any level (local or global git config --get commit.gpgsign ``` -If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 3. +If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 4. If not `true`, ask: **"Does this project require signed commits? (yes/no)"** Wait for response. -- **no / skip** → continue to Phase 3. +- **no / skip** → continue to Phase 4. - **yes** → proceed with signing setup. **Verify a signing key exists:** @@ -294,9 +324,9 @@ I'll enable commit and tag signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +Wait for confirmation. Write only on yes. If no, skip to Phase 4. -Continue to Phase 3. +Continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -319,13 +349,13 @@ I'll configure signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +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." --- -### Phase 3 — Label population +### Phase 4 — Label population Run: @@ -357,7 +387,7 @@ Wait for response. After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. -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 4; otherwise leave it unset so `/start` auto-detects." +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: @@ -375,7 +405,7 @@ Wait for the user's response. - **yes** → use all labels - **numbers** (e.g. `1,3,5`) → use only those labels -- **no / skip / anything else** → skip this phase, continue to Phase 4 +- **no / skip / anything else** → skip this phase, continue to Phase 5 Show the exact change before writing: @@ -391,7 +421,7 @@ Wait for confirmation. Write only on yes. --- -### Phase 4 — Optional config walkthrough (profile-aware) +### Phase 5 — Optional config walkthrough (profile-aware) First, infer the current profile using the same rules as Phase 1. @@ -413,7 +443,7 @@ The walkthrough adapts based on profile. Walk through each applicable unset valu --- -### Phase 5 — Team sharing +### Phase 6 — Team sharing After completing or skipping the config walkthrough, say: @@ -431,11 +461,11 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri ## Hard rules -- Only modify `.codecannon.yaml` and local git config (Phase 2 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). +- Only modify `.codecannon.yaml` and local git config (Phase 3 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). - Do not run `sync.py` without explicit user permission. - Do not create `.codecannon.yaml` without explicit user permission. - Do not report a configuration problem unless confident the condition is genuinely broken. Prefer false negatives over false positives on all diagnostic checks. - 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 2, Phase 3, or Phase 4 — each write requires confirmation. +- 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/.gemini/skills/submit-for-review/SKILL.md b/.gemini/skills/submit-for-review/SKILL.md index e4bc76f..b1e258f 100644 --- a/.gemini/skills/submit-for-review/SKILL.md +++ b/.gemini/skills/submit-for-review/SKILL.md @@ -32,16 +32,16 @@ If the current branch matches any of the above, **abort immediately** and say: ## Step 2 — Type-check gate -First, move to the repository root so the command resolves against the correct Makefile / project config: +First, find the repository root: ``` -cd "$(git rev-parse --show-toplevel)" +git rev-parse --show-toplevel ``` -Then verify the make target exists before running it. Extract the target name from `make check` (e.g. `make check` → `check`) and run: +Then `cd` to the returned path and verify the make target exists. Extract the target name from `make check` (e.g. `make check` → `check`) and run: ``` -make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: @@ -188,10 +188,10 @@ Wait for the review to complete and report its verdict. ## Step 8 — Act on verdict -Before merging, verify the merge target exists. Move to the repo root, extract the target name from `make merge` (e.g. `make merge` → `merge`), and run: +Before merging, verify the merge target exists. Find the repo root with `git rev-parse --show-toplevel`, then extract the target name from `make merge` (e.g. `make merge` → `merge`) and run: ``` -cd "$(git rev-parse --show-toplevel)" && make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: @@ -350,4 +350,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. - + diff --git a/docs/index.md b/docs/index.md index b120926..4565208 100644 --- a/docs/index.md +++ b/docs/index.md @@ -110,28 +110,29 @@ Code Cannon skills are agent-agnostic, but each agent has its own quirks. This s ### Claude Code -**Reduce approval prompts.** Claude Code prompts for permission on shell commands that contain command substitutions (`$(...)`) or certain patterns. Pre-approve the commands Code Cannon uses by adding a `permissions` block to `.claude/settings.json` (project-level, committed) or `.claude/settings.local.json` (personal, git-ignored): +**Reduce approval prompts.** Claude Code prompts for permission on unfamiliar shell commands. Pre-approve the commands Code Cannon uses by adding a `permissions` block to `.claude/settings.json` (project-level, committed) or `.claude/settings.local.json` (personal, git-ignored): ```json { "permissions": { "defaultMode": "acceptEdits", "allow": [ - "Bash(git *)", - "Bash(gh *)", - "Bash(make *)", - "Bash(python3 *)", - "Bash(./sync.py *)", - "Bash(mkdir *)", - "Bash(mktemp *)" + "Bash(cd:*)", + "Bash(git:*)", + "Bash(gh:*)", + "Bash(make:*)", + "Bash(python3:*)", + "Bash(./sync.py:*)", + "Bash(mkdir:*)", + "Bash(mktemp:*)" ], "deny": [ "Bash(git push --force)", - "Bash(git push --force *)", + "Bash(git push --force:*)", "Bash(git reset --hard)", - "Bash(git reset --hard *)", - "Bash(rm -rf *)", - "Bash(sudo *)" + "Bash(git reset --hard:*)", + "Bash(rm -rf:*)", + "Bash(sudo:*)" ] } } diff --git a/permissions.yaml b/permissions.yaml new file mode 100644 index 0000000..9ac731c --- /dev/null +++ b/permissions.yaml @@ -0,0 +1,21 @@ +# CodeCannon permissions reference +# Hand-maintained list of shell command prefixes used across all skills. +# The /setup skill reads this file to check whether the agent's permission +# config covers these commands and suggest additions if not. +# +# When adding new shell commands to skills, add the prefix here too. +# sync.py --validate checks that every prefix in skill code blocks appears +# in this file. + +commands: + - cd + - cp + - gh + - git + - gpg + - grep + - make + - mkdir + - mktemp + - test + - which diff --git a/skills/setup.md b/skills/setup.md index f242aac..e6fded8 100644 --- a/skills/setup.md +++ b/skills/setup.md @@ -256,7 +256,37 @@ A value counts as "set" if it is present, uncommented, and non-empty in `.codeca --- -### Phase 2 — Commit signing +### 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. + +**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). + +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. + +**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. + +--- + +### Phase 3 — Commit signing Check whether commit signing is already configured at any level (local or global): @@ -264,13 +294,13 @@ Check whether commit signing is already configured at any level (local or global git config --get commit.gpgsign ``` -If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 3. +If the output is `true`, display `Commit signing: enabled` in the health summary area and skip to Phase 4. If not `true`, ask: **"Does this project require signed commits? (yes/no)"** Wait for response. -- **no / skip** → continue to Phase 3. +- **no / skip** → continue to Phase 4. - **yes** → proceed with signing setup. **Verify a signing key exists:** @@ -292,9 +322,9 @@ I'll enable commit and tag signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +Wait for confirmation. Write only on yes. If no, skip to Phase 4. -Continue to Phase 3. +Continue to Phase 4. **If no signing key is found**, detect the signing format: @@ -317,13 +347,13 @@ I'll configure signing for this repo: Proceed? (yes/no) ``` -Wait for confirmation. Write only on yes. If no, skip to Phase 3. +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." --- -### Phase 3 — Label population +### Phase 4 — Label population Run: @@ -355,7 +385,7 @@ Wait for response. After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again. -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 4; otherwise leave it unset so `/start` auto-detects." +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: @@ -373,7 +403,7 @@ Wait for the user's response. - **yes** → use all labels - **numbers** (e.g. `1,3,5`) → use only those labels -- **no / skip / anything else** → skip this phase, continue to Phase 4 +- **no / skip / anything else** → skip this phase, continue to Phase 5 Show the exact change before writing: @@ -389,7 +419,7 @@ Wait for confirmation. Write only on yes. --- -### Phase 4 — Optional config walkthrough (profile-aware) +### Phase 5 — Optional config walkthrough (profile-aware) First, infer the current profile using the same rules as Phase 1. @@ -411,7 +441,7 @@ The walkthrough adapts based on profile. Walk through each applicable unset valu --- -### Phase 5 — Team sharing +### Phase 6 — Team sharing After completing or skipping the config walkthrough, say: @@ -429,10 +459,10 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri ## Hard rules -- Only modify `.codecannon.yaml` and local git config (Phase 2 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). +- Only modify `.codecannon.yaml` and local git config (Phase 3 signing setup). Do not touch any other file (except running `CodeCannon/sync.py`, which modifies `.claude/commands/` — permitted only with explicit user approval). - Do not run `sync.py` without explicit user permission. - Do not create `.codecannon.yaml` without explicit user permission. - Do not report a configuration problem unless confident the condition is genuinely broken. Prefer false negatives over false positives on all diagnostic checks. - 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 2, Phase 3, or Phase 4 — each write requires confirmation. +- 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/skills/submit-for-review.md b/skills/submit-for-review.md index 6da4edc..7681ef8 100644 --- a/skills/submit-for-review.md +++ b/skills/submit-for-review.md @@ -35,16 +35,16 @@ If the current branch matches any of the above, **abort immediately** and say: ## Step 2 — Type-check gate -First, move to the repository root so the command resolves against the correct Makefile / project config: +First, find the repository root: ``` -cd "$(git rev-parse --show-toplevel)" +git rev-parse --show-toplevel ``` -Then verify the make target exists before running it. Extract the target name from `{{CHECK_CMD}}` (e.g. `make check` → `check`) and run: +Then `cd` to the returned path and verify the make target exists. Extract the target name from `{{CHECK_CMD}}` (e.g. `make check` → `check`) and run: ``` -make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: @@ -207,10 +207,10 @@ Wait for the review to complete and report its verdict. ## Step 8 — Act on verdict -Before merging, verify the merge target exists. Move to the repo root, extract the target name from `{{MERGE_CMD}}` (e.g. `make merge` → `merge`), and run: +Before merging, verify the merge target exists. Find the repo root with `git rev-parse --show-toplevel`, then extract the target name from `{{MERGE_CMD}}` (e.g. `make merge` → `merge`) and run: ``` -cd "$(git rev-parse --show-toplevel)" && make -n 2>/dev/null +cd && make -n 2>/dev/null ``` If `make -n` exits non-zero, **stop** and say: diff --git a/sync.py b/sync.py index 7f35d11..b9fed67 100755 --- a/sync.py +++ b/sync.py @@ -369,6 +369,44 @@ def validate_placeholders(skill_files, project_config): return errors +def validate_permissions(skill_files): + """Check that command prefixes in skill code blocks are listed in permissions.yaml.""" + perms_path = CODECANNON_DIR / 'permissions.yaml' + if not perms_path.exists(): + return [" permissions.yaml not found"] + + perms = parse_yaml_simple(perms_path.read_text()) + allowed = set(perms.get('commands', [])) + if not allowed: + return [" permissions.yaml has no commands listed"] + + code_block_re = re.compile(r'```bash\n(.*?)```', re.DOTALL) + errors = [] + seen = set() + + for skill_path in skill_files: + text = skill_path.read_text() + for block in code_block_re.findall(text): + for line in block.strip().splitlines(): + line = line.strip() + if not line or line.startswith('#') or line.startswith('<'): + continue + token = line.split()[0] + # Only check tokens that look like shell commands + if not (token[0].islower() or token.startswith('./')): + continue + # Normalize ./CodeCannon/sync.py → CodeCannon/sync.py + if token.startswith('./'): + token = token[2:] + # For path-like commands (CodeCannon/sync.py), check the full path + # For simple commands (git, make), check the prefix + if token not in allowed and token not in seen: + seen.add(token) + errors.append(f" {skill_path.name}: command '{token}' not in permissions.yaml") + + return errors + + def self_update_or_exit(): """Update the CodeCannon checkout via git pull --ff-only. Only runs on main.""" try: @@ -450,16 +488,29 @@ def main(): else: skill_files = all_skill_files - # --validate: pre-flight placeholder check, no writes + # --validate: pre-flight placeholder check + permissions check, no writes if args.validate: + failed = False errors = validate_placeholders(skill_files, project_config) if errors: - print("Validation failed — undefined placeholders:\n") + print("Placeholder validation failed — undefined placeholders:\n") for e in errors: print(e) - sys.exit(1) + failed = True + else: + print("Placeholder validation passed — all placeholders are defined.") + + perm_errors = validate_permissions(all_skill_files) + if perm_errors: + print("\nPermission validation failed — commands not in permissions.yaml:\n") + for e in perm_errors: + print(e) + failed = True else: - print("Validation passed — all placeholders are defined.") + print("Permission validation passed — all command prefixes are listed.") + + if failed: + sys.exit(1) return if args.dry_run: