From c1d1ed738cb7bdcdda0040ed5d17a9314065269e Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Sat, 11 Apr 2026 16:01:04 -0400 Subject: [PATCH 1/5] Enhance /status with health indicators, stale detection, team mode, and quick wins --- .agents/skills/status/SKILL.md | 155 +++++++++++++++++++++++++++++---- .claude/commands/status.md | 155 +++++++++++++++++++++++++++++---- .codecannon.yaml | 1 + .cursor/rules/status.mdc | 155 +++++++++++++++++++++++++++++---- .gemini/skills/status/SKILL.md | 155 +++++++++++++++++++++++++++++---- config.schema.yaml | 11 +++ docs/skills/status.md | 49 ++++++++--- skills/status.md | 155 +++++++++++++++++++++++++++++---- 8 files changed, 751 insertions(+), 85 deletions(-) diff --git a/.agents/skills/status/SKILL.md b/.agents/skills/status/SKILL.md index 1be68e0..0c97559 100644 --- a/.agents/skills/status/SKILL.md +++ b/.agents/skills/status/SKILL.md @@ -9,9 +9,9 @@ description: Code Cannon: Summarize in-progress and recently completed work from ## Step 1 — Parse arguments -First, check whether `$ARGUMENTS` contains `--milestone` or `--sprint` (they are identical aliases). +First, check whether `$ARGUMENTS` contains `--milestone`, `--sprint`, or `--team`. -**Milestone mode:** If either flag is present, extract everything after `--milestone` or `--sprint` as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–5. +**Milestone mode:** If `--milestone` or `--sprint` is present, extract everything after the flag as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–6. Examples: - `--milestone Sprint 4` → milestone name = `Sprint 4` @@ -19,7 +19,9 @@ Examples: - `--milestone Q2 Release` → milestone name = `Q2 Release` - `--milestone 12` → milestone name = `12` -**Personal mode** (no `--milestone` / `--sprint` flag): determine: +**Team mode:** If `--team` is present, enter team mode (Steps T1–T3 below) and skip Steps 2–6. `--team` is mutually exclusive with `--milestone`/`--sprint` and username arguments. If both are present, report the conflict and stop. + +**Personal mode** (no `--milestone` / `--sprint` / `--team` flag): determine: - **subject**: default `@me`. If the argument starts with `@` or is a plain word that is not a number, treat it as a GitHub username. Strip the leading `@` for `gh` commands that do not accept it (e.g. `gh pr list --author alice`); keep it for display. - **lookback**: default `7`. If the argument is a number (digits only), use it as the lookback window in days. @@ -28,14 +30,14 @@ No argument → subject = `@me`, lookback = `7`. --- -## Step 2 — Fetch GitHub data (run all three in parallel) +## Step 2 — Fetch GitHub data (run all in parallel) Run these commands concurrently: **Open PRs authored by subject:** ```bash gh pr list --author --state open \ - --json number,title,url,labels,milestone,baseRefName,body + --json number,title,url,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft ``` **Recently merged PRs (last `` days):** @@ -48,8 +50,15 @@ Filter the results to keep only entries where `mergedAt` is within the last ` --state open \ - --json number,title,url,labels,milestone + --json number,title,url,labels,milestone,updatedAt +``` + +**PRs requesting your review** (only when subject is `@me`): +```bash +gh pr list --search "review-requested:@me" --state open \ + --json number,title,url,author,updatedAt ``` +Skip this query when viewing another user's status. If any `gh` command exits with a non-zero status (including auth errors), report the error message and stop. Do not retry. @@ -78,9 +87,38 @@ Using the data from Steps 2 and 3, classify each item: - **In progress** — open PRs. For each, attempt to identify a linked issue number from the PR body (look for `#N`, `closes #N`, `fixes #N`, `issue #N`). If found, cross-reference with open issues. - **Done** — merged PRs within the lookback window. - **Up next** — open issues that are NOT associated with any open PR (i.e. no open PR body references their issue number). +- **Needs your review** — PRs from the review-requested query (only when subject is `@me`). An open issue that IS linked from an open PR body appears under "In progress" alongside that PR, not under "Up next". +### 4a — Derive health badges + +For each open PR, derive the following badges: + +**Draft status:** +- If `isDraft` is `true` → `[draft]` + +**CI check status** (from `statusCheckRollup`): +- All checks have `status: COMPLETED` and `conclusion: SUCCESS` → `✅ checks passing` +- Any check has `conclusion: FAILURE` → `❌ checks failing` +- Checks are still running or have other states → `⏳ checks pending` +- No checks configured → omit badge + +**Review decision** (from `reviewDecision`): +- `APPROVED` → `✅ approved` +- `CHANGES_REQUESTED` → `🔄 changes requested` +- `REVIEW_REQUIRED` or empty → `⏳ awaiting review` + +**Merge conflict** (from `mergeable`): +- `CONFLICTING` → `⚠️ conflicts` +- `MERGEABLE` or `UNKNOWN` → omit badge + +### 4b — Flag stale items + +For each open PR and open issue, check `updatedAt`. If the item has not been updated within `14` days (default: 14; disabled when set to 0), flag it as stale. Record the last-updated date and the number of days since the last update. + +A stale item gets an inline `⚠️ stale (d)` badge appended after any other badges. + --- ## Step 5 — Output the summary @@ -90,29 +128,43 @@ Print a formatted summary. Use this structure: ``` ## Status for — last days + in progress · done · up next[ · need your review] + ### In progress -- # [<labels>] [<milestone>] - PR: <url> +- #<number> <title> [<labels>] [<milestone>] [draft] + PR: <url> · <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>] Linked issue: #<number> (if found) ### Done - #<number> <title> [<labels>] — merged <date> PR: <url> +### Needs your review +- #<number> <title> (by @<author>) + PR: <url> + ### Up next -- #<number> <title> [<labels>] [<milestone>] +- #<number> <title> [<labels>] [<milestone>][ · <stale badge>] Issue: <url> +### ⚠️ Stale +- #<number> <title> — last updated <date> (<N> days ago) + --- Local commits (current branch): <git log output, or "skipped — not in a git repo"> ``` Rules: +- **Summary counts line**: show immediately after the heading. Omit zero-count segments (e.g., if nothing is done, skip that segment). "need your review" only appears when subject is `@me` and the count is > 0. +- **Health badges**: show on the second line of each "In progress" item, after the PR URL, separated by ` · `. Omit individual badges that don't apply (e.g., no conflict badge if mergeable). +- **Draft badge**: show `[draft]` inline in the first line of draft PRs, before any other badges. +- **Stale section**: a dedicated section at the bottom (before "Local commits") listing all stale items from any section, with their last-updated date and age. This gives a consolidated view. Individual items also get the inline `⚠️ stale (<N>d)` badge in their own sections. +- **"Needs your review" section**: only shown when subject is `@me` and there are PRs requesting review. Placed between "Done" and "Up next". - Omit any section that has no items — do not show an empty heading. - Show labels only if present; show milestone only if present. - Dates use `YYYY-MM-DD` format. -- If all three GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` +- If all GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` Do not post, comment, write files, or take any action. Output only. @@ -160,6 +212,7 @@ Evaluate the following conditions **in order**. Use the **first** match: | 1 | On a `feature/*` branch with uncommitted changes (`git status --porcelain` is non-empty) | `What's next: You have uncommitted changes on \`<branch>\`. When ready, run \`/submit-for-review\`.` | | 2 | On a `feature/*` branch with an open PR that has `reviewDecision: APPROVED` and all status checks are `COMPLETED` | `What's next: PR #<number> is approved and checks pass. Consider running \`/deploy\`.` | | 3 | On a `feature/*` branch with an open PR (any other review/check state) | `What's next: PR #<number> (<title>) is open and awaiting review.` | +| 3.5 | Subject is `@me` and there are PRs requesting your review (from Step 2 query) | Append to the current suggestion (or show standalone if no higher priority matched): `You also have <N> PR(s) awaiting your review.` | | 4 | On a `feature/*` branch with no open PR and clean working tree | `What's next: No open PR for \`<branch>\`. Run \`/submit-for-review\` to open one.` | | 5 | On the integration branch (`dev`, `develop`, or `main` when no integration branch exists) with unreleased commits (rev-list count > 0 since last tag) | `What's next: <N> commit(s) on \`<branch>\` since \`<tag>\`. Run \`/deploy\` when ready to release.` | | 6 | No open PRs, no open issues assigned to subject | `What's next: Nothing in progress. Run \`/start\` to begin new work.` | @@ -195,10 +248,11 @@ If this command fails for any reason (milestone not found, auth error, etc.), re ### Step M2 — Classify issues -Fetch all open PRs to detect which issues are in progress: +Fetch all open PRs to detect which issues are in progress (with health fields): ```bash -gh pr list --state open --json number,title,body,baseRefName +gh pr list --state open \ + --json number,title,body,baseRefName,reviewDecision,statusCheckRollup,mergeable,isDraft ``` Group issues into three buckets: @@ -207,6 +261,8 @@ Group issues into three buckets: - **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#<number>`, `closes #<number>`, `fixes #<number>`, `issue #<number>`) - **Not started** — `state: open` AND no open PR body references the issue number +For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). + ### Step M3 — Output the summary ``` @@ -215,8 +271,8 @@ Group issues into three buckets: <Y> of <total> issues closed · <Z> in progress · <W> not started ### In progress (<Z>) -- #<number> <title> [@<assignee>] [<milestone>] - <url> +- #<number> <title> [@<assignee>] [<milestone>][ [draft]] + <url> · <check badge> · <review badge>[ · <conflict badge>] ### Not started (<W>) - #<number> <title> [@<assignee>] @@ -229,16 +285,85 @@ Rules: - Show "In progress" first, then "Not started", then "Done" - Show assignee only if present; omit if unassigned - Show URLs only for in-progress items; omit URLs for closed issues +- Show health badges on in-progress items (same derivation as Step 4a) - If a section has no items, omit it entirely Do not post, comment, write files, or take any action. Output only. --- +## Team mode (Steps T1–T3) + +Only entered when `--team` is detected in Step 1. + +### Step T1 — Fetch all open work (run both in parallel) + +```bash +gh pr list --state open --limit 100 \ + --json number,title,url,author,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft +``` + +```bash +gh issue list --state open --limit 200 \ + --json number,title,url,assignees,labels,milestone,updatedAt +``` + +If either command fails, report the error and stop. + +### Step T2 — Group and classify + +Group items by person: +- PRs are grouped by `author.login` +- Issues are grouped by assignee (first assignee if multiple). Issues with no assignee go into an "Unassigned" group. + +Within each person's group, classify items the same way as personal mode (Step 4): +- **In progress** — open PRs (and linked issues) +- **Up next** — open issues not linked from any open PR + +Derive health badges (Step 4a) and flag stale items (Step 4b) for all items. + +### Step T3 — Output the team summary + +``` +## Team status + +<N> open PRs · <N> open issues · <N> people + +### @<person> (<N> in progress, <N> up next) +- #<number> <title> — PR <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>][ [draft]] + <url> +- #<number> <title> [up next][ · <stale badge>] + +### @<person> (<N> in progress, <N> up next) +... + +### Unassigned (<N>) +- #<number> <title> + <url> + +### ⚠️ Stale +- #<number> <title> (@<person>) — last updated <date> (<N> days ago) +``` + +Rules: +- Sort people alphabetically by username +- Within each person, show in-progress items first, then up-next items +- Show health badges on PR items (same format as personal mode) +- Show `[draft]` on draft PRs +- Tag up-next items with `[up next]` for visual distinction +- "Unassigned" section appears at the bottom, only if there are unassigned issues +- "Stale" section consolidates all stale items across all people +- Omit any section or group with no items +- No "What's next" section in team mode + +Do not post, comment, write files, or take any action. Output only. + +--- + ## Hard rules - Never write to GitHub (no comments, labels, issue updates, or PR changes). - If `gh` is unauthenticated or any fetch fails, report the error and stop immediately. - Do not retry failed commands. - Strip the leading `@` from the subject when passing to `gh` flags that do not accept it. -<!-- generated by CodeCannon/sync.sh | skill: status | adapter: codex | hash: b942c6b6 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: status | adapter: codex | hash: 03a99a92 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/status.md b/.claude/commands/status.md index cab7d0b..f7d3554 100644 --- a/.claude/commands/status.md +++ b/.claude/commands/status.md @@ -4,9 +4,9 @@ Code Cannon: Summarize in-progress and recently completed work from GitHub and g ## Step 1 — Parse arguments -First, check whether `$ARGUMENTS` contains `--milestone` or `--sprint` (they are identical aliases). +First, check whether `$ARGUMENTS` contains `--milestone`, `--sprint`, or `--team`. -**Milestone mode:** If either flag is present, extract everything after `--milestone` or `--sprint` as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–5. +**Milestone mode:** If `--milestone` or `--sprint` is present, extract everything after the flag as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–6. Examples: - `--milestone Sprint 4` → milestone name = `Sprint 4` @@ -14,7 +14,9 @@ Examples: - `--milestone Q2 Release` → milestone name = `Q2 Release` - `--milestone 12` → milestone name = `12` -**Personal mode** (no `--milestone` / `--sprint` flag): determine: +**Team mode:** If `--team` is present, enter team mode (Steps T1–T3 below) and skip Steps 2–6. `--team` is mutually exclusive with `--milestone`/`--sprint` and username arguments. If both are present, report the conflict and stop. + +**Personal mode** (no `--milestone` / `--sprint` / `--team` flag): determine: - **subject**: default `@me`. If the argument starts with `@` or is a plain word that is not a number, treat it as a GitHub username. Strip the leading `@` for `gh` commands that do not accept it (e.g. `gh pr list --author alice`); keep it for display. - **lookback**: default `7`. If the argument is a number (digits only), use it as the lookback window in days. @@ -23,14 +25,14 @@ No argument → subject = `@me`, lookback = `7`. --- -## Step 2 — Fetch GitHub data (run all three in parallel) +## Step 2 — Fetch GitHub data (run all in parallel) Run these commands concurrently: **Open PRs authored by subject:** ```bash gh pr list --author <subject> --state open \ - --json number,title,url,labels,milestone,baseRefName,body + --json number,title,url,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft ``` **Recently merged PRs (last `<lookback>` days):** @@ -43,8 +45,15 @@ Filter the results to keep only entries where `mergedAt` is within the last `<lo **Open issues assigned to subject:** ```bash gh issue list --assignee <subject> --state open \ - --json number,title,url,labels,milestone + --json number,title,url,labels,milestone,updatedAt +``` + +**PRs requesting your review** (only when subject is `@me`): +```bash +gh pr list --search "review-requested:@me" --state open \ + --json number,title,url,author,updatedAt ``` +Skip this query when viewing another user's status. If any `gh` command exits with a non-zero status (including auth errors), report the error message and stop. Do not retry. @@ -73,9 +82,38 @@ Using the data from Steps 2 and 3, classify each item: - **In progress** — open PRs. For each, attempt to identify a linked issue number from the PR body (look for `#N`, `closes #N`, `fixes #N`, `issue #N`). If found, cross-reference with open issues. - **Done** — merged PRs within the lookback window. - **Up next** — open issues that are NOT associated with any open PR (i.e. no open PR body references their issue number). +- **Needs your review** — PRs from the review-requested query (only when subject is `@me`). An open issue that IS linked from an open PR body appears under "In progress" alongside that PR, not under "Up next". +### 4a — Derive health badges + +For each open PR, derive the following badges: + +**Draft status:** +- If `isDraft` is `true` → `[draft]` + +**CI check status** (from `statusCheckRollup`): +- All checks have `status: COMPLETED` and `conclusion: SUCCESS` → `✅ checks passing` +- Any check has `conclusion: FAILURE` → `❌ checks failing` +- Checks are still running or have other states → `⏳ checks pending` +- No checks configured → omit badge + +**Review decision** (from `reviewDecision`): +- `APPROVED` → `✅ approved` +- `CHANGES_REQUESTED` → `🔄 changes requested` +- `REVIEW_REQUIRED` or empty → `⏳ awaiting review` + +**Merge conflict** (from `mergeable`): +- `CONFLICTING` → `⚠️ conflicts` +- `MERGEABLE` or `UNKNOWN` → omit badge + +### 4b — Flag stale items + +For each open PR and open issue, check `updatedAt`. If the item has not been updated within `14` days (default: 14; disabled when set to 0), flag it as stale. Record the last-updated date and the number of days since the last update. + +A stale item gets an inline `⚠️ stale (<N>d)` badge appended after any other badges. + --- ## Step 5 — Output the summary @@ -85,29 +123,43 @@ Print a formatted summary. Use this structure: ``` ## Status for <subject> — last <lookback> days +<N> in progress · <N> done · <N> up next[ · <N> need your review] + ### In progress -- #<number> <title> [<labels>] [<milestone>] - PR: <url> +- #<number> <title> [<labels>] [<milestone>] [draft] + PR: <url> · <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>] Linked issue: #<number> (if found) ### Done - #<number> <title> [<labels>] — merged <date> PR: <url> +### Needs your review +- #<number> <title> (by @<author>) + PR: <url> + ### Up next -- #<number> <title> [<labels>] [<milestone>] +- #<number> <title> [<labels>] [<milestone>][ · <stale badge>] Issue: <url> +### ⚠️ Stale +- #<number> <title> — last updated <date> (<N> days ago) + --- Local commits (current branch): <git log output, or "skipped — not in a git repo"> ``` Rules: +- **Summary counts line**: show immediately after the heading. Omit zero-count segments (e.g., if nothing is done, skip that segment). "need your review" only appears when subject is `@me` and the count is > 0. +- **Health badges**: show on the second line of each "In progress" item, after the PR URL, separated by ` · `. Omit individual badges that don't apply (e.g., no conflict badge if mergeable). +- **Draft badge**: show `[draft]` inline in the first line of draft PRs, before any other badges. +- **Stale section**: a dedicated section at the bottom (before "Local commits") listing all stale items from any section, with their last-updated date and age. This gives a consolidated view. Individual items also get the inline `⚠️ stale (<N>d)` badge in their own sections. +- **"Needs your review" section**: only shown when subject is `@me` and there are PRs requesting review. Placed between "Done" and "Up next". - Omit any section that has no items — do not show an empty heading. - Show labels only if present; show milestone only if present. - Dates use `YYYY-MM-DD` format. -- If all three GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` +- If all GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` Do not post, comment, write files, or take any action. Output only. @@ -155,6 +207,7 @@ Evaluate the following conditions **in order**. Use the **first** match: | 1 | On a `feature/*` branch with uncommitted changes (`git status --porcelain` is non-empty) | `What's next: You have uncommitted changes on \`<branch>\`. When ready, run \`/submit-for-review\`.` | | 2 | On a `feature/*` branch with an open PR that has `reviewDecision: APPROVED` and all status checks are `COMPLETED` | `What's next: PR #<number> is approved and checks pass. Consider running \`/deploy\`.` | | 3 | On a `feature/*` branch with an open PR (any other review/check state) | `What's next: PR #<number> (<title>) is open and awaiting review.` | +| 3.5 | Subject is `@me` and there are PRs requesting your review (from Step 2 query) | Append to the current suggestion (or show standalone if no higher priority matched): `You also have <N> PR(s) awaiting your review.` | | 4 | On a `feature/*` branch with no open PR and clean working tree | `What's next: No open PR for \`<branch>\`. Run \`/submit-for-review\` to open one.` | | 5 | On the integration branch (`dev`, `develop`, or `main` when no integration branch exists) with unreleased commits (rev-list count > 0 since last tag) | `What's next: <N> commit(s) on \`<branch>\` since \`<tag>\`. Run \`/deploy\` when ready to release.` | | 6 | No open PRs, no open issues assigned to subject | `What's next: Nothing in progress. Run \`/start\` to begin new work.` | @@ -190,10 +243,11 @@ If this command fails for any reason (milestone not found, auth error, etc.), re ### Step M2 — Classify issues -Fetch all open PRs to detect which issues are in progress: +Fetch all open PRs to detect which issues are in progress (with health fields): ```bash -gh pr list --state open --json number,title,body,baseRefName +gh pr list --state open \ + --json number,title,body,baseRefName,reviewDecision,statusCheckRollup,mergeable,isDraft ``` Group issues into three buckets: @@ -202,6 +256,8 @@ Group issues into three buckets: - **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#<number>`, `closes #<number>`, `fixes #<number>`, `issue #<number>`) - **Not started** — `state: open` AND no open PR body references the issue number +For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). + ### Step M3 — Output the summary ``` @@ -210,8 +266,8 @@ Group issues into three buckets: <Y> of <total> issues closed · <Z> in progress · <W> not started ### In progress (<Z>) -- #<number> <title> [@<assignee>] [<milestone>] - <url> +- #<number> <title> [@<assignee>] [<milestone>][ [draft]] + <url> · <check badge> · <review badge>[ · <conflict badge>] ### Not started (<W>) - #<number> <title> [@<assignee>] @@ -224,16 +280,85 @@ Rules: - Show "In progress" first, then "Not started", then "Done" - Show assignee only if present; omit if unassigned - Show URLs only for in-progress items; omit URLs for closed issues +- Show health badges on in-progress items (same derivation as Step 4a) - If a section has no items, omit it entirely Do not post, comment, write files, or take any action. Output only. --- +## Team mode (Steps T1–T3) + +Only entered when `--team` is detected in Step 1. + +### Step T1 — Fetch all open work (run both in parallel) + +```bash +gh pr list --state open --limit 100 \ + --json number,title,url,author,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft +``` + +```bash +gh issue list --state open --limit 200 \ + --json number,title,url,assignees,labels,milestone,updatedAt +``` + +If either command fails, report the error and stop. + +### Step T2 — Group and classify + +Group items by person: +- PRs are grouped by `author.login` +- Issues are grouped by assignee (first assignee if multiple). Issues with no assignee go into an "Unassigned" group. + +Within each person's group, classify items the same way as personal mode (Step 4): +- **In progress** — open PRs (and linked issues) +- **Up next** — open issues not linked from any open PR + +Derive health badges (Step 4a) and flag stale items (Step 4b) for all items. + +### Step T3 — Output the team summary + +``` +## Team status + +<N> open PRs · <N> open issues · <N> people + +### @<person> (<N> in progress, <N> up next) +- #<number> <title> — PR <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>][ [draft]] + <url> +- #<number> <title> [up next][ · <stale badge>] + +### @<person> (<N> in progress, <N> up next) +... + +### Unassigned (<N>) +- #<number> <title> + <url> + +### ⚠️ Stale +- #<number> <title> (@<person>) — last updated <date> (<N> days ago) +``` + +Rules: +- Sort people alphabetically by username +- Within each person, show in-progress items first, then up-next items +- Show health badges on PR items (same format as personal mode) +- Show `[draft]` on draft PRs +- Tag up-next items with `[up next]` for visual distinction +- "Unassigned" section appears at the bottom, only if there are unassigned issues +- "Stale" section consolidates all stale items across all people +- Omit any section or group with no items +- No "What's next" section in team mode + +Do not post, comment, write files, or take any action. Output only. + +--- + ## Hard rules - Never write to GitHub (no comments, labels, issue updates, or PR changes). - If `gh` is unauthenticated or any fetch fails, report the error and stop immediately. - Do not retry failed commands. - Strip the leading `@` from the subject when passing to `gh` flags that do not accept it. -<!-- generated by CodeCannon/sync.sh | skill: status | adapter: claude | hash: eb38c883 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: status | adapter: claude | hash: ab4b0450 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.codecannon.yaml b/.codecannon.yaml index 12fbf0e..bfed680 100644 --- a/.codecannon.yaml +++ b/.codecannon.yaml @@ -39,5 +39,6 @@ config: QA_READY_LABEL: "ready-for-qa" QA_PASSED_LABEL: "qa-passed" QA_FAILED_LABEL: "qa-failed" + STALE_DAYS: "14" PLATFORM_COMPLIANCE_NOTES: "<!-- No platform-specific compliance rules defined. Edit .codecannon.yaml to add them. -->" CONVENTIONS_NOTES: "<!-- No project conventions defined. Edit .codecannon.yaml to add them. -->" diff --git a/.cursor/rules/status.mdc b/.cursor/rules/status.mdc index 084b206..373745b 100644 --- a/.cursor/rules/status.mdc +++ b/.cursor/rules/status.mdc @@ -10,9 +10,9 @@ alwaysApply: false ## Step 1 — Parse arguments -First, check whether `$ARGUMENTS` contains `--milestone` or `--sprint` (they are identical aliases). +First, check whether `$ARGUMENTS` contains `--milestone`, `--sprint`, or `--team`. -**Milestone mode:** If either flag is present, extract everything after `--milestone` or `--sprint` as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–5. +**Milestone mode:** If `--milestone` or `--sprint` is present, extract everything after the flag as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–6. Examples: - `--milestone Sprint 4` → milestone name = `Sprint 4` @@ -20,7 +20,9 @@ Examples: - `--milestone Q2 Release` → milestone name = `Q2 Release` - `--milestone 12` → milestone name = `12` -**Personal mode** (no `--milestone` / `--sprint` flag): determine: +**Team mode:** If `--team` is present, enter team mode (Steps T1–T3 below) and skip Steps 2–6. `--team` is mutually exclusive with `--milestone`/`--sprint` and username arguments. If both are present, report the conflict and stop. + +**Personal mode** (no `--milestone` / `--sprint` / `--team` flag): determine: - **subject**: default `@me`. If the argument starts with `@` or is a plain word that is not a number, treat it as a GitHub username. Strip the leading `@` for `gh` commands that do not accept it (e.g. `gh pr list --author alice`); keep it for display. - **lookback**: default `7`. If the argument is a number (digits only), use it as the lookback window in days. @@ -29,14 +31,14 @@ No argument → subject = `@me`, lookback = `7`. --- -## Step 2 — Fetch GitHub data (run all three in parallel) +## Step 2 — Fetch GitHub data (run all in parallel) Run these commands concurrently: **Open PRs authored by subject:** ```bash gh pr list --author <subject> --state open \ - --json number,title,url,labels,milestone,baseRefName,body + --json number,title,url,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft ``` **Recently merged PRs (last `<lookback>` days):** @@ -49,8 +51,15 @@ Filter the results to keep only entries where `mergedAt` is within the last `<lo **Open issues assigned to subject:** ```bash gh issue list --assignee <subject> --state open \ - --json number,title,url,labels,milestone + --json number,title,url,labels,milestone,updatedAt +``` + +**PRs requesting your review** (only when subject is `@me`): +```bash +gh pr list --search "review-requested:@me" --state open \ + --json number,title,url,author,updatedAt ``` +Skip this query when viewing another user's status. If any `gh` command exits with a non-zero status (including auth errors), report the error message and stop. Do not retry. @@ -79,9 +88,38 @@ Using the data from Steps 2 and 3, classify each item: - **In progress** — open PRs. For each, attempt to identify a linked issue number from the PR body (look for `#N`, `closes #N`, `fixes #N`, `issue #N`). If found, cross-reference with open issues. - **Done** — merged PRs within the lookback window. - **Up next** — open issues that are NOT associated with any open PR (i.e. no open PR body references their issue number). +- **Needs your review** — PRs from the review-requested query (only when subject is `@me`). An open issue that IS linked from an open PR body appears under "In progress" alongside that PR, not under "Up next". +### 4a — Derive health badges + +For each open PR, derive the following badges: + +**Draft status:** +- If `isDraft` is `true` → `[draft]` + +**CI check status** (from `statusCheckRollup`): +- All checks have `status: COMPLETED` and `conclusion: SUCCESS` → `✅ checks passing` +- Any check has `conclusion: FAILURE` → `❌ checks failing` +- Checks are still running or have other states → `⏳ checks pending` +- No checks configured → omit badge + +**Review decision** (from `reviewDecision`): +- `APPROVED` → `✅ approved` +- `CHANGES_REQUESTED` → `🔄 changes requested` +- `REVIEW_REQUIRED` or empty → `⏳ awaiting review` + +**Merge conflict** (from `mergeable`): +- `CONFLICTING` → `⚠️ conflicts` +- `MERGEABLE` or `UNKNOWN` → omit badge + +### 4b — Flag stale items + +For each open PR and open issue, check `updatedAt`. If the item has not been updated within `14` days (default: 14; disabled when set to 0), flag it as stale. Record the last-updated date and the number of days since the last update. + +A stale item gets an inline `⚠️ stale (<N>d)` badge appended after any other badges. + --- ## Step 5 — Output the summary @@ -91,29 +129,43 @@ Print a formatted summary. Use this structure: ``` ## Status for <subject> — last <lookback> days +<N> in progress · <N> done · <N> up next[ · <N> need your review] + ### In progress -- #<number> <title> [<labels>] [<milestone>] - PR: <url> +- #<number> <title> [<labels>] [<milestone>] [draft] + PR: <url> · <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>] Linked issue: #<number> (if found) ### Done - #<number> <title> [<labels>] — merged <date> PR: <url> +### Needs your review +- #<number> <title> (by @<author>) + PR: <url> + ### Up next -- #<number> <title> [<labels>] [<milestone>] +- #<number> <title> [<labels>] [<milestone>][ · <stale badge>] Issue: <url> +### ⚠️ Stale +- #<number> <title> — last updated <date> (<N> days ago) + --- Local commits (current branch): <git log output, or "skipped — not in a git repo"> ``` Rules: +- **Summary counts line**: show immediately after the heading. Omit zero-count segments (e.g., if nothing is done, skip that segment). "need your review" only appears when subject is `@me` and the count is > 0. +- **Health badges**: show on the second line of each "In progress" item, after the PR URL, separated by ` · `. Omit individual badges that don't apply (e.g., no conflict badge if mergeable). +- **Draft badge**: show `[draft]` inline in the first line of draft PRs, before any other badges. +- **Stale section**: a dedicated section at the bottom (before "Local commits") listing all stale items from any section, with their last-updated date and age. This gives a consolidated view. Individual items also get the inline `⚠️ stale (<N>d)` badge in their own sections. +- **"Needs your review" section**: only shown when subject is `@me` and there are PRs requesting review. Placed between "Done" and "Up next". - Omit any section that has no items — do not show an empty heading. - Show labels only if present; show milestone only if present. - Dates use `YYYY-MM-DD` format. -- If all three GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` +- If all GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` Do not post, comment, write files, or take any action. Output only. @@ -161,6 +213,7 @@ Evaluate the following conditions **in order**. Use the **first** match: | 1 | On a `feature/*` branch with uncommitted changes (`git status --porcelain` is non-empty) | `What's next: You have uncommitted changes on \`<branch>\`. When ready, run \`/submit-for-review\`.` | | 2 | On a `feature/*` branch with an open PR that has `reviewDecision: APPROVED` and all status checks are `COMPLETED` | `What's next: PR #<number> is approved and checks pass. Consider running \`/deploy\`.` | | 3 | On a `feature/*` branch with an open PR (any other review/check state) | `What's next: PR #<number> (<title>) is open and awaiting review.` | +| 3.5 | Subject is `@me` and there are PRs requesting your review (from Step 2 query) | Append to the current suggestion (or show standalone if no higher priority matched): `You also have <N> PR(s) awaiting your review.` | | 4 | On a `feature/*` branch with no open PR and clean working tree | `What's next: No open PR for \`<branch>\`. Run \`/submit-for-review\` to open one.` | | 5 | On the integration branch (`dev`, `develop`, or `main` when no integration branch exists) with unreleased commits (rev-list count > 0 since last tag) | `What's next: <N> commit(s) on \`<branch>\` since \`<tag>\`. Run \`/deploy\` when ready to release.` | | 6 | No open PRs, no open issues assigned to subject | `What's next: Nothing in progress. Run \`/start\` to begin new work.` | @@ -196,10 +249,11 @@ If this command fails for any reason (milestone not found, auth error, etc.), re ### Step M2 — Classify issues -Fetch all open PRs to detect which issues are in progress: +Fetch all open PRs to detect which issues are in progress (with health fields): ```bash -gh pr list --state open --json number,title,body,baseRefName +gh pr list --state open \ + --json number,title,body,baseRefName,reviewDecision,statusCheckRollup,mergeable,isDraft ``` Group issues into three buckets: @@ -208,6 +262,8 @@ Group issues into three buckets: - **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#<number>`, `closes #<number>`, `fixes #<number>`, `issue #<number>`) - **Not started** — `state: open` AND no open PR body references the issue number +For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). + ### Step M3 — Output the summary ``` @@ -216,8 +272,8 @@ Group issues into three buckets: <Y> of <total> issues closed · <Z> in progress · <W> not started ### In progress (<Z>) -- #<number> <title> [@<assignee>] [<milestone>] - <url> +- #<number> <title> [@<assignee>] [<milestone>][ [draft]] + <url> · <check badge> · <review badge>[ · <conflict badge>] ### Not started (<W>) - #<number> <title> [@<assignee>] @@ -230,16 +286,85 @@ Rules: - Show "In progress" first, then "Not started", then "Done" - Show assignee only if present; omit if unassigned - Show URLs only for in-progress items; omit URLs for closed issues +- Show health badges on in-progress items (same derivation as Step 4a) - If a section has no items, omit it entirely Do not post, comment, write files, or take any action. Output only. --- +## Team mode (Steps T1–T3) + +Only entered when `--team` is detected in Step 1. + +### Step T1 — Fetch all open work (run both in parallel) + +```bash +gh pr list --state open --limit 100 \ + --json number,title,url,author,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft +``` + +```bash +gh issue list --state open --limit 200 \ + --json number,title,url,assignees,labels,milestone,updatedAt +``` + +If either command fails, report the error and stop. + +### Step T2 — Group and classify + +Group items by person: +- PRs are grouped by `author.login` +- Issues are grouped by assignee (first assignee if multiple). Issues with no assignee go into an "Unassigned" group. + +Within each person's group, classify items the same way as personal mode (Step 4): +- **In progress** — open PRs (and linked issues) +- **Up next** — open issues not linked from any open PR + +Derive health badges (Step 4a) and flag stale items (Step 4b) for all items. + +### Step T3 — Output the team summary + +``` +## Team status + +<N> open PRs · <N> open issues · <N> people + +### @<person> (<N> in progress, <N> up next) +- #<number> <title> — PR <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>][ [draft]] + <url> +- #<number> <title> [up next][ · <stale badge>] + +### @<person> (<N> in progress, <N> up next) +... + +### Unassigned (<N>) +- #<number> <title> + <url> + +### ⚠️ Stale +- #<number> <title> (@<person>) — last updated <date> (<N> days ago) +``` + +Rules: +- Sort people alphabetically by username +- Within each person, show in-progress items first, then up-next items +- Show health badges on PR items (same format as personal mode) +- Show `[draft]` on draft PRs +- Tag up-next items with `[up next]` for visual distinction +- "Unassigned" section appears at the bottom, only if there are unassigned issues +- "Stale" section consolidates all stale items across all people +- Omit any section or group with no items +- No "What's next" section in team mode + +Do not post, comment, write files, or take any action. Output only. + +--- + ## Hard rules - Never write to GitHub (no comments, labels, issue updates, or PR changes). - If `gh` is unauthenticated or any fetch fails, report the error and stop immediately. - Do not retry failed commands. - Strip the leading `@` from the subject when passing to `gh` flags that do not accept it. -<!-- generated by CodeCannon/sync.sh | skill: status | adapter: cursor | hash: 22821ad0 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: status | adapter: cursor | hash: 6c6a0c34 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/status/SKILL.md b/.gemini/skills/status/SKILL.md index 0fbfbfc..3b4fb5c 100644 --- a/.gemini/skills/status/SKILL.md +++ b/.gemini/skills/status/SKILL.md @@ -9,9 +9,9 @@ description: Code Cannon: Summarize in-progress and recently completed work from ## Step 1 — Parse arguments -First, check whether `$ARGUMENTS` contains `--milestone` or `--sprint` (they are identical aliases). +First, check whether `$ARGUMENTS` contains `--milestone`, `--sprint`, or `--team`. -**Milestone mode:** If either flag is present, extract everything after `--milestone` or `--sprint` as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–5. +**Milestone mode:** If `--milestone` or `--sprint` is present, extract everything after the flag as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–6. Examples: - `--milestone Sprint 4` → milestone name = `Sprint 4` @@ -19,7 +19,9 @@ Examples: - `--milestone Q2 Release` → milestone name = `Q2 Release` - `--milestone 12` → milestone name = `12` -**Personal mode** (no `--milestone` / `--sprint` flag): determine: +**Team mode:** If `--team` is present, enter team mode (Steps T1–T3 below) and skip Steps 2–6. `--team` is mutually exclusive with `--milestone`/`--sprint` and username arguments. If both are present, report the conflict and stop. + +**Personal mode** (no `--milestone` / `--sprint` / `--team` flag): determine: - **subject**: default `@me`. If the argument starts with `@` or is a plain word that is not a number, treat it as a GitHub username. Strip the leading `@` for `gh` commands that do not accept it (e.g. `gh pr list --author alice`); keep it for display. - **lookback**: default `7`. If the argument is a number (digits only), use it as the lookback window in days. @@ -28,14 +30,14 @@ No argument → subject = `@me`, lookback = `7`. --- -## Step 2 — Fetch GitHub data (run all three in parallel) +## Step 2 — Fetch GitHub data (run all in parallel) Run these commands concurrently: **Open PRs authored by subject:** ```bash gh pr list --author <subject> --state open \ - --json number,title,url,labels,milestone,baseRefName,body + --json number,title,url,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft ``` **Recently merged PRs (last `<lookback>` days):** @@ -48,8 +50,15 @@ Filter the results to keep only entries where `mergedAt` is within the last `<lo **Open issues assigned to subject:** ```bash gh issue list --assignee <subject> --state open \ - --json number,title,url,labels,milestone + --json number,title,url,labels,milestone,updatedAt +``` + +**PRs requesting your review** (only when subject is `@me`): +```bash +gh pr list --search "review-requested:@me" --state open \ + --json number,title,url,author,updatedAt ``` +Skip this query when viewing another user's status. If any `gh` command exits with a non-zero status (including auth errors), report the error message and stop. Do not retry. @@ -78,9 +87,38 @@ Using the data from Steps 2 and 3, classify each item: - **In progress** — open PRs. For each, attempt to identify a linked issue number from the PR body (look for `#N`, `closes #N`, `fixes #N`, `issue #N`). If found, cross-reference with open issues. - **Done** — merged PRs within the lookback window. - **Up next** — open issues that are NOT associated with any open PR (i.e. no open PR body references their issue number). +- **Needs your review** — PRs from the review-requested query (only when subject is `@me`). An open issue that IS linked from an open PR body appears under "In progress" alongside that PR, not under "Up next". +### 4a — Derive health badges + +For each open PR, derive the following badges: + +**Draft status:** +- If `isDraft` is `true` → `[draft]` + +**CI check status** (from `statusCheckRollup`): +- All checks have `status: COMPLETED` and `conclusion: SUCCESS` → `✅ checks passing` +- Any check has `conclusion: FAILURE` → `❌ checks failing` +- Checks are still running or have other states → `⏳ checks pending` +- No checks configured → omit badge + +**Review decision** (from `reviewDecision`): +- `APPROVED` → `✅ approved` +- `CHANGES_REQUESTED` → `🔄 changes requested` +- `REVIEW_REQUIRED` or empty → `⏳ awaiting review` + +**Merge conflict** (from `mergeable`): +- `CONFLICTING` → `⚠️ conflicts` +- `MERGEABLE` or `UNKNOWN` → omit badge + +### 4b — Flag stale items + +For each open PR and open issue, check `updatedAt`. If the item has not been updated within `14` days (default: 14; disabled when set to 0), flag it as stale. Record the last-updated date and the number of days since the last update. + +A stale item gets an inline `⚠️ stale (<N>d)` badge appended after any other badges. + --- ## Step 5 — Output the summary @@ -90,29 +128,43 @@ Print a formatted summary. Use this structure: ``` ## Status for <subject> — last <lookback> days +<N> in progress · <N> done · <N> up next[ · <N> need your review] + ### In progress -- #<number> <title> [<labels>] [<milestone>] - PR: <url> +- #<number> <title> [<labels>] [<milestone>] [draft] + PR: <url> · <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>] Linked issue: #<number> (if found) ### Done - #<number> <title> [<labels>] — merged <date> PR: <url> +### Needs your review +- #<number> <title> (by @<author>) + PR: <url> + ### Up next -- #<number> <title> [<labels>] [<milestone>] +- #<number> <title> [<labels>] [<milestone>][ · <stale badge>] Issue: <url> +### ⚠️ Stale +- #<number> <title> — last updated <date> (<N> days ago) + --- Local commits (current branch): <git log output, or "skipped — not in a git repo"> ``` Rules: +- **Summary counts line**: show immediately after the heading. Omit zero-count segments (e.g., if nothing is done, skip that segment). "need your review" only appears when subject is `@me` and the count is > 0. +- **Health badges**: show on the second line of each "In progress" item, after the PR URL, separated by ` · `. Omit individual badges that don't apply (e.g., no conflict badge if mergeable). +- **Draft badge**: show `[draft]` inline in the first line of draft PRs, before any other badges. +- **Stale section**: a dedicated section at the bottom (before "Local commits") listing all stale items from any section, with their last-updated date and age. This gives a consolidated view. Individual items also get the inline `⚠️ stale (<N>d)` badge in their own sections. +- **"Needs your review" section**: only shown when subject is `@me` and there are PRs requesting review. Placed between "Done" and "Up next". - Omit any section that has no items — do not show an empty heading. - Show labels only if present; show milestone only if present. - Dates use `YYYY-MM-DD` format. -- If all three GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` +- If all GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` Do not post, comment, write files, or take any action. Output only. @@ -160,6 +212,7 @@ Evaluate the following conditions **in order**. Use the **first** match: | 1 | On a `feature/*` branch with uncommitted changes (`git status --porcelain` is non-empty) | `What's next: You have uncommitted changes on \`<branch>\`. When ready, run \`/submit-for-review\`.` | | 2 | On a `feature/*` branch with an open PR that has `reviewDecision: APPROVED` and all status checks are `COMPLETED` | `What's next: PR #<number> is approved and checks pass. Consider running \`/deploy\`.` | | 3 | On a `feature/*` branch with an open PR (any other review/check state) | `What's next: PR #<number> (<title>) is open and awaiting review.` | +| 3.5 | Subject is `@me` and there are PRs requesting your review (from Step 2 query) | Append to the current suggestion (or show standalone if no higher priority matched): `You also have <N> PR(s) awaiting your review.` | | 4 | On a `feature/*` branch with no open PR and clean working tree | `What's next: No open PR for \`<branch>\`. Run \`/submit-for-review\` to open one.` | | 5 | On the integration branch (`dev`, `develop`, or `main` when no integration branch exists) with unreleased commits (rev-list count > 0 since last tag) | `What's next: <N> commit(s) on \`<branch>\` since \`<tag>\`. Run \`/deploy\` when ready to release.` | | 6 | No open PRs, no open issues assigned to subject | `What's next: Nothing in progress. Run \`/start\` to begin new work.` | @@ -195,10 +248,11 @@ If this command fails for any reason (milestone not found, auth error, etc.), re ### Step M2 — Classify issues -Fetch all open PRs to detect which issues are in progress: +Fetch all open PRs to detect which issues are in progress (with health fields): ```bash -gh pr list --state open --json number,title,body,baseRefName +gh pr list --state open \ + --json number,title,body,baseRefName,reviewDecision,statusCheckRollup,mergeable,isDraft ``` Group issues into three buckets: @@ -207,6 +261,8 @@ Group issues into three buckets: - **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#<number>`, `closes #<number>`, `fixes #<number>`, `issue #<number>`) - **Not started** — `state: open` AND no open PR body references the issue number +For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). + ### Step M3 — Output the summary ``` @@ -215,8 +271,8 @@ Group issues into three buckets: <Y> of <total> issues closed · <Z> in progress · <W> not started ### In progress (<Z>) -- #<number> <title> [@<assignee>] [<milestone>] - <url> +- #<number> <title> [@<assignee>] [<milestone>][ [draft]] + <url> · <check badge> · <review badge>[ · <conflict badge>] ### Not started (<W>) - #<number> <title> [@<assignee>] @@ -229,16 +285,85 @@ Rules: - Show "In progress" first, then "Not started", then "Done" - Show assignee only if present; omit if unassigned - Show URLs only for in-progress items; omit URLs for closed issues +- Show health badges on in-progress items (same derivation as Step 4a) - If a section has no items, omit it entirely Do not post, comment, write files, or take any action. Output only. --- +## Team mode (Steps T1–T3) + +Only entered when `--team` is detected in Step 1. + +### Step T1 — Fetch all open work (run both in parallel) + +```bash +gh pr list --state open --limit 100 \ + --json number,title,url,author,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft +``` + +```bash +gh issue list --state open --limit 200 \ + --json number,title,url,assignees,labels,milestone,updatedAt +``` + +If either command fails, report the error and stop. + +### Step T2 — Group and classify + +Group items by person: +- PRs are grouped by `author.login` +- Issues are grouped by assignee (first assignee if multiple). Issues with no assignee go into an "Unassigned" group. + +Within each person's group, classify items the same way as personal mode (Step 4): +- **In progress** — open PRs (and linked issues) +- **Up next** — open issues not linked from any open PR + +Derive health badges (Step 4a) and flag stale items (Step 4b) for all items. + +### Step T3 — Output the team summary + +``` +## Team status + +<N> open PRs · <N> open issues · <N> people + +### @<person> (<N> in progress, <N> up next) +- #<number> <title> — PR <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>][ [draft]] + <url> +- #<number> <title> [up next][ · <stale badge>] + +### @<person> (<N> in progress, <N> up next) +... + +### Unassigned (<N>) +- #<number> <title> + <url> + +### ⚠️ Stale +- #<number> <title> (@<person>) — last updated <date> (<N> days ago) +``` + +Rules: +- Sort people alphabetically by username +- Within each person, show in-progress items first, then up-next items +- Show health badges on PR items (same format as personal mode) +- Show `[draft]` on draft PRs +- Tag up-next items with `[up next]` for visual distinction +- "Unassigned" section appears at the bottom, only if there are unassigned issues +- "Stale" section consolidates all stale items across all people +- Omit any section or group with no items +- No "What's next" section in team mode + +Do not post, comment, write files, or take any action. Output only. + +--- + ## Hard rules - Never write to GitHub (no comments, labels, issue updates, or PR changes). - If `gh` is unauthenticated or any fetch fails, report the error and stop immediately. - Do not retry failed commands. - Strip the leading `@` from the subject when passing to `gh` flags that do not accept it. -<!-- generated by CodeCannon/sync.sh | skill: status | adapter: gemini | hash: 62cf34b7 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: status | adapter: gemini | hash: c4b55ece | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/config.schema.yaml b/config.schema.yaml index a564a5a..7f20a21 100644 --- a/config.schema.yaml +++ b/config.schema.yaml @@ -179,6 +179,17 @@ placeholders: # behave differently across GitHub tiers and org settings. Project board placement is explicitly # out of scope for /start and remains a manual step or future extension. + # ── Status skill ─────────────────────────────────────────────────────────── + + STALE_DAYS: + description: > + Number of days after which an open PR or issue is flagged as stale in /status output. + Items whose updatedAt is older than this threshold get a ⚠️ stale badge and appear in a + consolidated "Stale" section. Set to 0 to disable stale detection entirely. + default: "14" + category: status + used_in: [status] + # ── Review agent content ─────────────────────────────────────────────────── PLATFORM_COMPLIANCE_NOTES: diff --git a/docs/skills/status.md b/docs/skills/status.md index 83967b1..94532f3 100644 --- a/docs/skills/status.md +++ b/docs/skills/status.md @@ -1,14 +1,14 @@ # /status -Read-only snapshot of open PRs, recently merged work, and open issues. +Read-only snapshot of open PRs, recently merged work, and open issues — with health indicators, stale detection, and team-wide views. **Source prompt:** [`../../skills/status.md`](../../skills/status.md) ## What it does -`/status` generates a standup-style summary from GitHub data. It classifies work into three buckets — in progress, done, and up next — by cross-referencing open PRs, merged PRs, and open issues. +`/status` generates a standup-style summary from GitHub data. It classifies work into buckets — in progress, done, up next, and needs your review — by cross-referencing open PRs, merged PRs, and open issues. Each open PR shows health badges for CI checks, review decision, draft status, and merge conflicts. -It also supports a milestone mode for sprint-level views. +It supports three modes: personal (default), milestone, and team. ## Usage @@ -19,34 +19,58 @@ It also supports a milestone mode for sprint-level views. /status @alice 14 # alice's work, last 14 days /status --milestone "Sprint 4" # all issues in Sprint 4 /status --sprint "Sprint 4" # same (alias) +/status --team # all open work across the team ``` ## Personal mode (default) -Fetches three things in parallel: +Fetches four things in parallel: -- **Open PRs** authored by the subject +- **Open PRs** authored by the subject (with health fields: checks, review decision, mergeable, draft status) - **Recently merged PRs** within the lookback window - **Open issues** assigned to the subject +- **PRs requesting your review** (only when subject is `@me`) Then classifies items: -- **In progress** — open PRs, with linked issues identified from PR bodies +- **In progress** — open PRs, with linked issues identified from PR bodies. Each shows health badges. - **Done** — merged PRs within the lookback window +- **Needs your review** — PRs where your review has been requested (only for `@me`) - **Up next** — open issues with no associated open PR Open issues that are linked from an open PR appear under "In progress", not "Up next". +A summary counts line appears at the top: `3 in progress · 2 done · 4 up next · 1 need your review` + +### Health badges + +Each open PR in "In progress" shows: + +- **Draft**: `[draft]` if the PR is a draft +- **CI checks**: `✅ checks passing`, `❌ checks failing`, or `⏳ checks pending` +- **Review decision**: `✅ approved`, `🔄 changes requested`, or `⏳ awaiting review` +- **Merge conflicts**: `⚠️ conflicts` if the PR cannot be cleanly merged + +### Stale detection + +Items not updated within the configured threshold (`STALE_DAYS`, default 14) get a `⚠️ stale (<N>d)` badge and appear in a consolidated "Stale" section at the bottom. + ## Milestone mode When `--milestone` or `--sprint` is passed, `/status` fetches all issues in that milestone and classifies them by cross-referencing with open PRs: - **Done** — closed issues -- **In progress** — open issues referenced in an open PR body +- **In progress** — open issues referenced in an open PR body (with health badges from the linked PR) - **Not started** — open issues with no associated open PR Shows a progress summary: "X of Y issues closed, Z in progress, W not started." +## Team mode + +When `--team` is passed, `/status` fetches all open PRs and issues across the entire repo (no author/assignee filter) and groups them by person. Each person's section shows their in-progress items (with health badges) and up-next items. Unassigned issues appear in their own group. Stale items are consolidated at the bottom. + +Team mode is designed for standups and team leads who need the full picture in one command. + ## What's next (personal mode only) After the status summary, `/status` appends a single actionable suggestion based on local git state and the GitHub data already fetched. It evaluates conditions in priority order and shows the first match: @@ -54,12 +78,13 @@ After the status summary, `/status` appends a single actionable suggestion based - **Feature branch with uncommitted changes** → "Run `/submit-for-review`" - **Feature branch with approved PR and passing checks** → "Run `/deploy`" - **Feature branch with open PR** → "Awaiting review" +- **PRs requesting your review** → "You also have N PR(s) awaiting your review" (appended to above) - **Feature branch with no PR** → "Run `/submit-for-review` to open one" - **Integration branch with unreleased commits since last tag** → "Run `/deploy`" - **Nothing in progress** → "Run `/start`" - **Open issues in backlog** → "Run `/start N` to pick one up" -This section is omitted in milestone mode and when no condition matches. +This section is omitted in milestone mode, team mode, and when no condition matches. ## Why it's built this way @@ -67,8 +92,12 @@ This section is omitted in milestone mode and when no condition matches. **Cross-referenced classification.** Rather than relying on labels or project board columns, `/status` infers work state from the relationship between PRs and issues. An open issue with a linked open PR is "in progress" — no manual status tracking needed. -**Useful outside development.** `/status` generates standup summaries from GitHub data. It's valuable for project managers, team leads, or anyone who wants to see what's happening without reading code. +**Health at a glance.** A PR with failing checks or requested changes needs immediate attention but without badges looks identical to one awaiting first review. Health indicators surface this without clicking through GitHub. + +**Useful outside development.** `/status` generates standup summaries from GitHub data. It's valuable for project managers, team leads, or anyone who wants to see what's happening without reading code. Team mode extends this to the whole repo. ## Config keys used -None. `/status` uses only `gh` CLI commands and git log. It works with any Code Cannon configuration. +| Key | Default | Description | +|-----|---------|-------------| +| `STALE_DAYS` | `14` | Days after which open PRs/issues are flagged as stale. Set to `0` to disable. | diff --git a/skills/status.md b/skills/status.md index 9e462c5..1f2f0e2 100644 --- a/skills/status.md +++ b/skills/status.md @@ -1,14 +1,14 @@ --- skill: status description: "Code Cannon: Summarize in-progress and recently completed work from GitHub and git" -args: "optional: lookback days (e.g. 14), a GitHub username (@alice), or --milestone <name>" +args: "optional: lookback days (e.g. 14), a GitHub username (@alice), --milestone <name>, or --team" --- ## Step 1 — Parse arguments -First, check whether `$ARGUMENTS` contains `--milestone` or `--sprint` (they are identical aliases). +First, check whether `$ARGUMENTS` contains `--milestone`, `--sprint`, or `--team`. -**Milestone mode:** If either flag is present, extract everything after `--milestone` or `--sprint` as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–5. +**Milestone mode:** If `--milestone` or `--sprint` is present, extract everything after the flag as the milestone name (trim leading/trailing whitespace; preserve internal spaces). Ignore any other arguments. Enter milestone mode (Steps M1–M3 below) and skip Steps 2–6. Examples: - `--milestone Sprint 4` → milestone name = `Sprint 4` @@ -16,7 +16,9 @@ Examples: - `--milestone Q2 Release` → milestone name = `Q2 Release` - `--milestone 12` → milestone name = `12` -**Personal mode** (no `--milestone` / `--sprint` flag): determine: +**Team mode:** If `--team` is present, enter team mode (Steps T1–T3 below) and skip Steps 2–6. `--team` is mutually exclusive with `--milestone`/`--sprint` and username arguments. If both are present, report the conflict and stop. + +**Personal mode** (no `--milestone` / `--sprint` / `--team` flag): determine: - **subject**: default `@me`. If the argument starts with `@` or is a plain word that is not a number, treat it as a GitHub username. Strip the leading `@` for `gh` commands that do not accept it (e.g. `gh pr list --author alice`); keep it for display. - **lookback**: default `7`. If the argument is a number (digits only), use it as the lookback window in days. @@ -25,14 +27,14 @@ No argument → subject = `@me`, lookback = `7`. --- -## Step 2 — Fetch GitHub data (run all three in parallel) +## Step 2 — Fetch GitHub data (run all in parallel) Run these commands concurrently: **Open PRs authored by subject:** ```bash gh pr list --author <subject> --state open \ - --json number,title,url,labels,milestone,baseRefName,body + --json number,title,url,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft ``` **Recently merged PRs (last `<lookback>` days):** @@ -45,8 +47,15 @@ Filter the results to keep only entries where `mergedAt` is within the last `<lo **Open issues assigned to subject:** ```bash gh issue list --assignee <subject> --state open \ - --json number,title,url,labels,milestone + --json number,title,url,labels,milestone,updatedAt +``` + +**PRs requesting your review** (only when subject is `@me`): +```bash +gh pr list --search "review-requested:@me" --state open \ + --json number,title,url,author,updatedAt ``` +Skip this query when viewing another user's status. If any `gh` command exits with a non-zero status (including auth errors), report the error message and stop. Do not retry. @@ -75,9 +84,38 @@ Using the data from Steps 2 and 3, classify each item: - **In progress** — open PRs. For each, attempt to identify a linked issue number from the PR body (look for `#N`, `closes #N`, `fixes #N`, `issue #N`). If found, cross-reference with open issues. - **Done** — merged PRs within the lookback window. - **Up next** — open issues that are NOT associated with any open PR (i.e. no open PR body references their issue number). +- **Needs your review** — PRs from the review-requested query (only when subject is `@me`). An open issue that IS linked from an open PR body appears under "In progress" alongside that PR, not under "Up next". +### 4a — Derive health badges + +For each open PR, derive the following badges: + +**Draft status:** +- If `isDraft` is `true` → `[draft]` + +**CI check status** (from `statusCheckRollup`): +- All checks have `status: COMPLETED` and `conclusion: SUCCESS` → `✅ checks passing` +- Any check has `conclusion: FAILURE` → `❌ checks failing` +- Checks are still running or have other states → `⏳ checks pending` +- No checks configured → omit badge + +**Review decision** (from `reviewDecision`): +- `APPROVED` → `✅ approved` +- `CHANGES_REQUESTED` → `🔄 changes requested` +- `REVIEW_REQUIRED` or empty → `⏳ awaiting review` + +**Merge conflict** (from `mergeable`): +- `CONFLICTING` → `⚠️ conflicts` +- `MERGEABLE` or `UNKNOWN` → omit badge + +### 4b — Flag stale items + +For each open PR and open issue, check `updatedAt`. If the item has not been updated within `{{STALE_DAYS}}` days (default: 14; disabled when set to 0), flag it as stale. Record the last-updated date and the number of days since the last update. + +A stale item gets an inline `⚠️ stale (<N>d)` badge appended after any other badges. + --- ## Step 5 — Output the summary @@ -87,29 +125,43 @@ Print a formatted summary. Use this structure: ``` ## Status for <subject> — last <lookback> days +<N> in progress · <N> done · <N> up next[ · <N> need your review] + ### In progress -- #<number> <title> [<labels>] [<milestone>] - PR: <url> +- #<number> <title> [<labels>] [<milestone>] [draft] + PR: <url> · <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>] Linked issue: #<number> (if found) ### Done - #<number> <title> [<labels>] — merged <date> PR: <url> +### Needs your review +- #<number> <title> (by @<author>) + PR: <url> + ### Up next -- #<number> <title> [<labels>] [<milestone>] +- #<number> <title> [<labels>] [<milestone>][ · <stale badge>] Issue: <url> +### ⚠️ Stale +- #<number> <title> — last updated <date> (<N> days ago) + --- Local commits (current branch): <git log output, or "skipped — not in a git repo"> ``` Rules: +- **Summary counts line**: show immediately after the heading. Omit zero-count segments (e.g., if nothing is done, skip that segment). "need your review" only appears when subject is `@me` and the count is > 0. +- **Health badges**: show on the second line of each "In progress" item, after the PR URL, separated by ` · `. Omit individual badges that don't apply (e.g., no conflict badge if mergeable). +- **Draft badge**: show `[draft]` inline in the first line of draft PRs, before any other badges. +- **Stale section**: a dedicated section at the bottom (before "Local commits") listing all stale items from any section, with their last-updated date and age. This gives a consolidated view. Individual items also get the inline `⚠️ stale (<N>d)` badge in their own sections. +- **"Needs your review" section**: only shown when subject is `@me` and there are PRs requesting review. Placed between "Done" and "Up next". - Omit any section that has no items — do not show an empty heading. - Show labels only if present; show milestone only if present. - Dates use `YYYY-MM-DD` format. -- If all three GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` +- If all GitHub sections are empty, print: `Nothing found for <subject> in the last <lookback> days.` Do not post, comment, write files, or take any action. Output only. @@ -157,6 +209,7 @@ Evaluate the following conditions **in order**. Use the **first** match: | 1 | On a `feature/*` branch with uncommitted changes (`git status --porcelain` is non-empty) | `What's next: You have uncommitted changes on \`<branch>\`. When ready, run \`/submit-for-review\`.` | | 2 | On a `feature/*` branch with an open PR that has `reviewDecision: APPROVED` and all status checks are `COMPLETED` | `What's next: PR #<number> is approved and checks pass. Consider running \`/deploy\`.` | | 3 | On a `feature/*` branch with an open PR (any other review/check state) | `What's next: PR #<number> (<title>) is open and awaiting review.` | +| 3.5 | Subject is `@me` and there are PRs requesting your review (from Step 2 query) | Append to the current suggestion (or show standalone if no higher priority matched): `You also have <N> PR(s) awaiting your review.` | | 4 | On a `feature/*` branch with no open PR and clean working tree | `What's next: No open PR for \`<branch>\`. Run \`/submit-for-review\` to open one.` | | 5 | On the integration branch (`dev`, `develop`, or `main` when no integration branch exists) with unreleased commits (rev-list count > 0 since last tag) | `What's next: <N> commit(s) on \`<branch>\` since \`<tag>\`. Run \`/deploy\` when ready to release.` | | 6 | No open PRs, no open issues assigned to subject | `What's next: Nothing in progress. Run \`/start\` to begin new work.` | @@ -192,10 +245,11 @@ If this command fails for any reason (milestone not found, auth error, etc.), re ### Step M2 — Classify issues -Fetch all open PRs to detect which issues are in progress: +Fetch all open PRs to detect which issues are in progress (with health fields): ```bash -gh pr list --state open --json number,title,body,baseRefName +gh pr list --state open \ + --json number,title,body,baseRefName,reviewDecision,statusCheckRollup,mergeable,isDraft ``` Group issues into three buckets: @@ -204,6 +258,8 @@ Group issues into three buckets: - **In progress** — `state: open` AND the issue number appears in any open PR body (look for `#<number>`, `closes #<number>`, `fixes #<number>`, `issue #<number>`) - **Not started** — `state: open` AND no open PR body references the issue number +For in-progress issues, derive health badges from the linked PR using the same rules as Step 4a (check status, review decision, draft, conflict). + ### Step M3 — Output the summary ``` @@ -212,8 +268,8 @@ Group issues into three buckets: <Y> of <total> issues closed · <Z> in progress · <W> not started ### In progress (<Z>) -- #<number> <title> [@<assignee>] [<milestone>] - <url> +- #<number> <title> [@<assignee>] [<milestone>][ [draft]] + <url> · <check badge> · <review badge>[ · <conflict badge>] ### Not started (<W>) - #<number> <title> [@<assignee>] @@ -226,12 +282,81 @@ Rules: - Show "In progress" first, then "Not started", then "Done" - Show assignee only if present; omit if unassigned - Show URLs only for in-progress items; omit URLs for closed issues +- Show health badges on in-progress items (same derivation as Step 4a) - If a section has no items, omit it entirely Do not post, comment, write files, or take any action. Output only. --- +## Team mode (Steps T1–T3) + +Only entered when `--team` is detected in Step 1. + +### Step T1 — Fetch all open work (run both in parallel) + +```bash +gh pr list --state open --limit 100 \ + --json number,title,url,author,labels,milestone,baseRefName,body,reviewDecision,statusCheckRollup,updatedAt,mergeable,isDraft +``` + +```bash +gh issue list --state open --limit 200 \ + --json number,title,url,assignees,labels,milestone,updatedAt +``` + +If either command fails, report the error and stop. + +### Step T2 — Group and classify + +Group items by person: +- PRs are grouped by `author.login` +- Issues are grouped by assignee (first assignee if multiple). Issues with no assignee go into an "Unassigned" group. + +Within each person's group, classify items the same way as personal mode (Step 4): +- **In progress** — open PRs (and linked issues) +- **Up next** — open issues not linked from any open PR + +Derive health badges (Step 4a) and flag stale items (Step 4b) for all items. + +### Step T3 — Output the team summary + +``` +## Team status + +<N> open PRs · <N> open issues · <N> people + +### @<person> (<N> in progress, <N> up next) +- #<number> <title> — PR <check badge> · <review badge>[ · <conflict badge>][ · <stale badge>][ [draft]] + <url> +- #<number> <title> [up next][ · <stale badge>] + +### @<person> (<N> in progress, <N> up next) +... + +### Unassigned (<N>) +- #<number> <title> + <url> + +### ⚠️ Stale +- #<number> <title> (@<person>) — last updated <date> (<N> days ago) +``` + +Rules: +- Sort people alphabetically by username +- Within each person, show in-progress items first, then up-next items +- Show health badges on PR items (same format as personal mode) +- Show `[draft]` on draft PRs +- Tag up-next items with `[up next]` for visual distinction +- "Unassigned" section appears at the bottom, only if there are unassigned issues +- "Stale" section consolidates all stale items across all people +- Omit any section or group with no items +- No "What's next" section in team mode + +Do not post, comment, write files, or take any action. Output only. + +--- + ## Hard rules - Never write to GitHub (no comments, labels, issue updates, or PR changes). From ffac0f97ce6be6e187a77f5ee49d05cfd6367ad9 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart <sebastien.taggart@gmail.com> Date: Sat, 11 Apr 2026 17:37:23 -0400 Subject: [PATCH 2/5] Trim /setup skill and remove ROADMAP.md --- ROADMAP.md | 35 ----------- skills/setup.md | 158 +++++++++--------------------------------------- 2 files changed, 27 insertions(+), 166 deletions(-) delete mode 100644 ROADMAP.md diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 4aee4fd..0000000 --- a/ROADMAP.md +++ /dev/null @@ -1,35 +0,0 @@ -# Roadmap - -Ideas and future work. Not prioritized — just captured so they don't get lost. - -## Swarm mode / multi-agent workflows - -The current skill set (start/submit-for-review/review/deploy) assumes **deep mode**: one issue, one branch, one coherent PR. This works well for disciplined, sequential work. - -In practice, developers often run multiple agents simultaneously on unrelated tasks — "swarm mode." This resists the ticket-per-change structure because the agents share a working directory and branch. Worktrees could isolate agents, but only Claude Code supports them natively; Cursor, Codex, and Gemini don't. - -Possible additions: - -- **`/checkpoint` skill** — commit and push WIP without the full ship ceremony. Gives save points during swarm mode without pretending each save is a reviewable unit. -- **Worktree launcher** — a `make agent name=<task>` target that creates a worktree, launches an agent with `/start`, and registers cleanup after `/submit-for-review`. Orchestration layer outside the skills themselves. -- **Conflict detection** — warn when multiple agents are modifying overlapping files on the same branch. - -Decision for now: better discipline (one agent per issue, sequential) is the right path. Revisit when the pain of sequential work outweighs the cost of coordination tooling. - -## `/status` enhancements - -### PR health indicators - -Show whether checks are passing or failing on open PRs, and surface unresolved review comments. A PR with "changes requested" is very different from one awaiting first review — the status output should make that obvious at a glance. - -### Stale work detection - -Flag PRs or issues that haven't been touched in a configurable number of days. Helps surface forgotten branches and abandoned work before they rot. - -### `/status --team` - -Aggregate view across all open PRs and assigned issues in the repo, not scoped to a single user. Useful for team leads and standups where you want the full picture in one command. - -## Three-branch Makefile targets - -GitHub issue #6. `Makefile.agents.mk` lacks `STAGING_BRANCH` support — no `promote` target, no staging guard rails. The skills handle three-branch mode but the Makefile doesn't. diff --git a/skills/setup.md b/skills/setup.md index dc20c5d..7984217 100644 --- a/skills/setup.md +++ b/skills/setup.md @@ -52,18 +52,7 @@ Explain the three-layer model: - **Config** (`.codecannon.yaml`) — a project's values that fill those tokens at sync time - **Sync** (`sync.sh`) — reads the config, substitutes values, and writes generated command files for each adapter (Claude Code → `.claude/commands/`, Cursor → `.cursor/rules/`) -List the available skills: - -| Skill | What it does | -|---|---| -| `/start` | Creates a GitHub issue, feature branch, and writes code | -| `/submit-for-review` | Checks, commits, opens PR, spawns review agent, merges | -| `/review` | Standalone code review on any PR | -| `/deploy` | Bumps version, creates GitHub Release, promotes to production | -| `/status` | Snapshot of open PRs and issues for the team | -| `/setup` | This skill — configures Code Cannon in a project | - -Point to README.md for full documentation. Do not touch any file. +Point to README.md for the full skill list and documentation. Do not touch any file. **Path B — "I want to add Code Cannon to my project"** @@ -170,30 +159,21 @@ Wait for response. If the user describes their situation instead of picking a nu **Apply profile values to `.codecannon.yaml`:** -After the user selects a profile, ask follow-up questions and write values. Show every change before writing and ask "Apply these values to `.codecannon.yaml`? (yes/no)". Write only on yes. - -**Lightweight:** -- "What's your production branch name?" (default: `main`) -- Write: `BRANCH_PROD: <answer>`, `REVIEW_GATE: "advisory"`. Leave `BRANCH_DEV`, `BRANCH_TEST`, `DEFAULT_REVIEWERS`, `TICKET_LABELS`, and all QA labels commented out. -- No further questions. Say: "Lightweight profile applied. Check the workflow commands (`CHECK_CMD`, deploy commands, etc.) and run `/setup` again to finish configuration." Stop. +After the user selects a profile, ask the applicable follow-up questions from this list: +- "What's your production branch name?" (default: `main`) — ask for all profiles except Custom +- "What's your integration branch name?" (default: `development`) — ask for Standard and Governed only +- "Do you need a separate test/staging branch?" (default: `staging`) — ask for Governed only; if yes, ask for the branch name -**Standard:** -- "What's your production branch name?" (default: `main`) -- "What's your integration branch name?" (default: `development`) -- Write: `BRANCH_PROD: <answer>`, `BRANCH_DEV: <answer>`, `REVIEW_GATE: "ai"`. Leave `BRANCH_TEST` and QA labels commented out. -- Say: "Standard profile applied. Check the workflow commands and run `/setup` again to finish configuration." Stop. +Show every change before writing and ask "Apply these values to `.codecannon.yaml`? (yes/no)". Write only on yes. -**Governed:** -- "What's your production branch name?" (default: `main`) -- "What's your integration branch name?" (default: `development`) -- "Do you need a separate test/staging branch between integration and production? (yes/no)" - - If yes: "What's the test/staging branch name?" (default: `staging`) - - Write `BRANCH_TEST: <answer>`. -- Write: `BRANCH_PROD: <answer>`, `BRANCH_DEV: <answer>`, `REVIEW_GATE: "ai"`, `QA_READY_LABEL: "ready-for-qa"`, `QA_PASSED_LABEL: "qa-passed"`, `QA_FAILED_LABEL: "qa-failed"`. -- Say: "Governed profile applied. Check the workflow commands and run `/setup` again to finish configuration." Stop. +| Profile | Values to write | Values left commented out | +|---|---|---| +| **Lightweight** | `BRANCH_PROD`, `REVIEW_GATE: "advisory"` | `BRANCH_DEV`, `BRANCH_TEST`, `DEFAULT_REVIEWERS`, `TICKET_LABELS`, all QA labels | +| **Standard** | `BRANCH_PROD`, `BRANCH_DEV`, `REVIEW_GATE: "ai"` | `BRANCH_TEST`, QA labels | +| **Governed** | `BRANCH_PROD`, `BRANCH_DEV`, `REVIEW_GATE: "ai"`, `QA_READY_LABEL: "ready-for-qa"`, `QA_PASSED_LABEL: "qa-passed"`, `QA_FAILED_LABEL: "qa-failed"`, and `BRANCH_TEST` if applicable | — | +| **Custom** | Nothing — tell the user to review the file manually | — | -**Custom:** -- Say: "Open `.codecannon.yaml` and check the values marked with comments — especially `BRANCH_PROD`, `BRANCH_DEV`, `REVIEW_GATE`, `CHECK_CMD`, and the deploy commands. Then run `/setup` again." Stop. +After writing, say: "<Profile> profile applied. Check the workflow commands and run `/setup` again to finish configuration." Stop. ### Check 6 — .codecannon.yaml stale values @@ -306,7 +286,9 @@ Wait for response. - **yes** → create missing labels only (do not recreate existing labels). Use sensible colors and short descriptions. - **no / skip / anything else** → continue without creating labels. -After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again and continue with the numbered list flow below. +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`). If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 3; otherwise leave it unset so `/start` auto-detects." Display the results as a numbered list: @@ -344,107 +326,21 @@ Wait for confirmation. Write only on yes. First, infer the current profile using the same rules as Phase 1. -The walkthrough adapts based on profile. Walk through each applicable unset optional config value in the order shown below. Skip any value that is already set. For each unset value, explain what it does in one sentence, show an example, and ask if they want to set it. If the user says "skip" or provides nothing useful, move on immediately without modifying the file. Do not ask again. - -**Which values to walk through per profile:** - -- **Lightweight:** `PLATFORM_COMPLIANCE_NOTES` → `CONVENTIONS_NOTES` only. Skip DEFAULT_MILESTONE, DEFAULT_REVIEWERS, TICKET_LABEL_CREATION_ALLOWED, and QA labels — the Lightweight profile intentionally leaves these unset. -- **Standard:** `DEFAULT_REVIEWERS` → `TICKET_LABEL_CREATION_ALLOWED` → `PLATFORM_COMPLIANCE_NOTES` → `CONVENTIONS_NOTES`. Skip DEFAULT_MILESTONE and QA labels unless the user asks about them. -- **Governed:** All values: `DEFAULT_MILESTONE` → `DEFAULT_REVIEWERS` → `TICKET_LABEL_CREATION_ALLOWED` → `PLATFORM_COMPLIANCE_NOTES` → `CONVENTIONS_NOTES`. -- **Custom:** Same as Governed (walk through everything). - ---- - -### Greenfield GitHub baseline guidance (for PM/BA setup) - -Before the value walkthrough, provide this mini guide when either condition is true: -- `TICKET_LABELS` is unset, or -- fewer than 5 labels exist in the repository. - -Keep it short and practical: - -1. Explain that `/start` works best with a clear issue-label pool (`TICKET_LABELS`) and `/qa` needs explicit QA lifecycle labels. -2. Recommend this baseline label set for new projects: - - Work intake: `bug`, `enhancement`, `chore`, `documentation` - - QA lifecycle: `ready-for-qa`, `qa-passed`, `qa-failed` - - Optional planning: one lightweight priority scheme (for example `priority:high`, `priority:medium`, `priority:low`) -3. Explain milestone guidance: - - If the team runs planned iterations, set `DEFAULT_MILESTONE` (example: `Sprint 12` or `Release 2026.04`). - - If not, leave it unset so `/start` auto-detects open milestones and prompts only when needed. -4. End with: "Want me to help apply this baseline now during setup? (yes/no)" - -If user says no, continue immediately with the normal walkthrough. - ---- - -**DEFAULT_MILESTONE** (Governed and Custom only) - -"Sets the default milestone applied to every issue `/start` creates — skip if you're not using milestones or prefer auto-detect." - -Example: `DEFAULT_MILESTONE: "Sprint 4"` - -Ask: "Which milestone should new issues go under, if any? (name, number, or 'skip')" - -**DEFAULT_REVIEWERS** (Standard, Governed, and Custom) - -"Comma-separated GitHub handles or team slugs that `/submit-for-review` adds as PR reviewers — leave unset to rely on CODEOWNERS or manual assignment." - -Example: `DEFAULT_REVIEWERS: "@alice,@bob"` - -Ask: "Who should be auto-assigned as PR reviewers? (handles, team slugs, or 'skip')" +The walkthrough adapts based on profile. Walk through each applicable unset value in the order shown. Skip any value already set. If the user says "skip", move on without modifying the file and do not ask again. -**TICKET_LABEL_CREATION_ALLOWED** (Standard, Governed, and Custom) +**Common pattern for simple values** (DEFAULT_MILESTONE, DEFAULT_REVIEWERS, TICKET_LABEL_CREATION_ALLOWED): explain in one sentence, show the example value, ask. If the user provides a value, show the exact YAML change and ask "Write this to `.codecannon.yaml`? (yes/no)". Write only on yes. -"Controls whether `/start` can create new GitHub labels on the fly when none in the pool fit the task. Defaults to false." +**Common pattern for drafted values** (PLATFORM_COMPLIANCE_NOTES, CONVENTIONS_NOTES): ask what technologies/conventions apply, draft 2–4 concise checkable rules, show the draft, iterate until the user approves or skips. On approval, show the exact YAML change, confirm, write only on yes. -Example: `TICKET_LABEL_CREATION_ALLOWED: "true"` - -Ask: "Allow `/start` to create new labels when none fit? (true / false / skip)" - -**PLATFORM_COMPLIANCE_NOTES** (all profiles) - -"Platform-specific rules injected into the review agent — this is how the review agent catches issues specific to your infrastructure. Skip if you're not sure yet." - -Ask: "What backend or infrastructure does this project use? (e.g. Postgres, Redis, Next.js, a specific ORM or framework — or 'skip')" - -Wait for response. If skip → move on. - -Based on their answer, draft 2–4 compliance rules that are commonly violated for those technologies and checkable by a review agent. Show the draft: - -``` -Here's a draft for PLATFORM_COMPLIANCE_NOTES: - - PLATFORM_COMPLIANCE_NOTES: | - - <rule 1> - - <rule 2> - - <rule 3> - -Does this look right? Edit, add more, or say 'looks good'. -``` - -Iterate until the user approves or says skip. On approval, show the exact yaml change and ask "Write this to `.codecannon.yaml`? (yes/no)". Write only on yes. Confirm with one line after writing. - -**CONVENTIONS_NOTES** (all profiles) - -"Non-obvious team conventions injected into the review agent — rules that differ from common defaults and that you'd want a reviewer to flag. Skip if you're not sure yet." - -Ask: "What are the most commonly violated or non-obvious code conventions on this project that you'd want a reviewer to catch? (or 'skip')" - -Wait for response. If skip → move on. - -Shape their answer into concise, checkable rules. Show the draft: - -``` -Here's a draft for CONVENTIONS_NOTES: - - CONVENTIONS_NOTES: | - - <rule 1> - - <rule 2> - -Does this look right? Edit, add more, or say 'looks good'. -``` +| Key | Profiles | Description | Example / Prompt | +|---|---|---|---| +| `DEFAULT_MILESTONE` | Governed, Custom | Default milestone for `/start` issues | `"Sprint 4"` — "Which milestone, if any?" | +| `DEFAULT_REVIEWERS` | Standard, Governed, Custom | PR reviewers for `/submit-for-review` | `"@alice,@bob"` — "Who should review PRs?" | +| `TICKET_LABEL_CREATION_ALLOWED` | Standard, Governed, Custom | Allow `/start` to create new labels on the fly | `"true"` — "Allow label creation? (true/false)" | +| `PLATFORM_COMPLIANCE_NOTES` | All | Platform-specific rules for the review agent | "What backend/infra? (Postgres, Next.js, etc.)" | +| `CONVENTIONS_NOTES` | All | Non-obvious team conventions for the review agent | "What conventions should a reviewer catch?" | -Iterate until the user approves or says skip. On approval, show the exact yaml change and ask "Write this to `.codecannon.yaml`? (yes/no)". Write only on yes. Confirm with one line after writing. +**Lightweight** skips DEFAULT_MILESTONE, DEFAULT_REVIEWERS, and TICKET_LABEL_CREATION_ALLOWED — those are intentionally unset. --- From ae16f011762fc307b79b4ec9850c632e1b46ded2 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart <sebastien.taggart@gmail.com> Date: Sat, 11 Apr 2026 17:39:51 -0400 Subject: [PATCH 3/5] Restore priority label suggestion and default value note in setup skill --- skills/setup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/setup.md b/skills/setup.md index 7984217..d4d59ea 100644 --- a/skills/setup.md +++ b/skills/setup.md @@ -288,7 +288,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`). If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 3; 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 3; otherwise leave it unset so `/start` auto-detects." Display the results as a numbered list: @@ -336,7 +336,7 @@ The walkthrough adapts based on profile. Walk through each applicable unset valu |---|---|---|---| | `DEFAULT_MILESTONE` | Governed, Custom | Default milestone for `/start` issues | `"Sprint 4"` — "Which milestone, if any?" | | `DEFAULT_REVIEWERS` | Standard, Governed, Custom | PR reviewers for `/submit-for-review` | `"@alice,@bob"` — "Who should review PRs?" | -| `TICKET_LABEL_CREATION_ALLOWED` | Standard, Governed, Custom | Allow `/start` to create new labels on the fly | `"true"` — "Allow label creation? (true/false)" | +| `TICKET_LABEL_CREATION_ALLOWED` | Standard, Governed, Custom | Allow `/start` to create new labels on the fly (defaults to `false`) | `"true"` — "Allow label creation? (true/false)" | | `PLATFORM_COMPLIANCE_NOTES` | All | Platform-specific rules for the review agent | "What backend/infra? (Postgres, Next.js, etc.)" | | `CONVENTIONS_NOTES` | All | Non-obvious team conventions for the review agent | "What conventions should a reviewer catch?" | From 8a5eca2a9ec6b2a161563b936f1f4f4ec3db15d7 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart <sebastien.taggart@gmail.com> Date: Sat, 11 Apr 2026 17:47:57 -0400 Subject: [PATCH 4/5] Add checkpoint skill, fix temp file collisions, and support starting on existing branches --- .agents/skills/checkpoint/SKILL.md | 93 ++++++++++++++++++++++ .agents/skills/deploy/SKILL.md | 18 +++-- .agents/skills/start/SKILL.md | 49 ++++++++++-- .agents/skills/submit-for-review/SKILL.md | 22 ++++-- .claude/commands/checkpoint.md | 88 +++++++++++++++++++++ .claude/commands/deploy.md | 18 +++-- .claude/commands/start.md | 49 ++++++++++-- .claude/commands/submit-for-review.md | 22 ++++-- .cursor/rules/checkpoint.mdc | 94 ++++++++++++++++++++++ .cursor/rules/deploy.mdc | 18 +++-- .cursor/rules/start.mdc | 49 ++++++++++-- .cursor/rules/submit-for-review.mdc | 22 ++++-- .gemini/skills/checkpoint/SKILL.md | 93 ++++++++++++++++++++++ .gemini/skills/deploy/SKILL.md | 18 +++-- .gemini/skills/start/SKILL.md | 49 ++++++++++-- .gemini/skills/submit-for-review/SKILL.md | 22 ++++-- skills/checkpoint.md | 95 +++++++++++++++++++++++ skills/deploy.md | 44 ++++++++--- skills/start.md | 47 +++++++++-- skills/submit-for-review.md | 20 +++-- 20 files changed, 837 insertions(+), 93 deletions(-) create mode 100644 .agents/skills/checkpoint/SKILL.md create mode 100644 .claude/commands/checkpoint.md create mode 100644 .cursor/rules/checkpoint.mdc create mode 100644 .gemini/skills/checkpoint/SKILL.md create mode 100644 skills/checkpoint.md diff --git a/.agents/skills/checkpoint/SKILL.md b/.agents/skills/checkpoint/SKILL.md new file mode 100644 index 0000000..cab107f --- /dev/null +++ b/.agents/skills/checkpoint/SKILL.md @@ -0,0 +1,93 @@ +--- +name: checkpoint +description: Code Cannon: Commit and push WIP without review or merge +--- + +> **Codex CLI:** This skill is triggered by description matching. State any arguments in your message. Sub-agent spawning is not supported — perform any review steps inline. + +--- + +## What `/checkpoint` does + +`/checkpoint` is a lightweight save point — commit and push work-in-progress to the remote without triggering type-checks, reviews, PR creation, or merge. Use it to persist progress during long coding sessions or before context-switching. + +--- + +## Step 1 — Verify branch + +```bash +git branch --show-current +``` + +Protected branches (not a feature branch): +- `main` +- `dev` + +If the current branch matches any of the above, **abort immediately** and say: + +> "You are on `<branch>`. `/checkpoint` must be run from a feature branch." + +--- + +## Step 2 — Check for changes + +```bash +git status --porcelain +``` + +If the output is empty (no staged, unstaged, or untracked changes), say: + +> "Nothing to checkpoint — working tree is clean." + +Stop. Do not create an empty commit. + +--- + +## Step 3 — Stage and commit + +```bash +git add -A +git commit -m "WIP: <message>" +``` + +**Commit message rules:** +- Always prefix with `WIP: ` so these commits are visually distinct in `git log`. +- If `$ARGUMENTS` is provided, use it as `<message>` (e.g. `WIP: add auth middleware`). +- If `$ARGUMENTS` is empty, auto-generate from the diff summary (e.g. `WIP: update auth.ts, add login tests`). Keep it under 72 characters. +- Do not commit `.env` files, secrets, or build artifacts. If `git status` shows such files, add them to `.gitignore` or exclude them from staging before committing. + +--- + +## Step 4 — Push + +```bash +git push -u origin HEAD +``` + +If the push fails because the remote branch does not exist yet, the `-u` flag handles creation. If it fails for another reason, report the error and stop. + +--- + +## Step 5 — Report + +Count the files in the commit: + +```bash +git diff --stat HEAD~1 +``` + +Say: + +> "Checkpoint saved. N file(s) committed and pushed to `<branch>`. Run `/submit-for-review` when ready to open a PR." + +--- + +## Hard rules + +- Never create a PR. +- Never trigger a review. +- Never merge. +- Never apply labels or modify issues. +- Never run `make check` — checkpoints are not quality gates. +- Do not prompt for confirmation — `/checkpoint` is a quick save, not a ceremony. +<!-- generated by CodeCannon/sync.sh | skill: checkpoint | adapter: codex | hash: a5c7187c | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.agents/skills/deploy/SKILL.md b/.agents/skills/deploy/SKILL.md index 7d00d24..5ae2bbb 100644 --- a/.agents/skills/deploy/SKILL.md +++ b/.agents/skills/deploy/SKILL.md @@ -166,7 +166,15 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ## Step 6 — Create PR: `dev` → `main` -Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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 (not Bash) to create `<tmpdir>/release_pr_body.md`: ```markdown Release vX.Y.Z @@ -184,7 +192,7 @@ Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash gh pr create --base main --head dev \ --title "Release vX.Y.Z" \ - --body-file /tmp/cc_release_pr_body.md + --body-file <tmpdir>/release_pr_body.md ``` Note the PR number from the output. @@ -215,7 +223,7 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/release_notes.md` (same temp directory from Step 6): ```markdown ## Changes @@ -231,7 +239,7 @@ Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes-file /tmp/cc_release_notes.md + --notes-file <tmpdir>/release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -245,4 +253,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: codex | hash: 6d8dd3bb | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: codex | hash: 1a6fd564 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.agents/skills/start/SKILL.md b/.agents/skills/start/SKILL.md index 837fb80..27c02ed 100644 --- a/.agents/skills/start/SKILL.md +++ b/.agents/skills/start/SKILL.md @@ -68,6 +68,35 @@ After parsing flags, determine the active milestone in this order: --- +## Pre-check: Existing feature branch + +Before entering Case A or Case B, check the current branch: + +```bash +git branch --show-current +``` + +If already on a `feature/*` branch and `$ARGUMENTS` is **not** a number (i.e. this is new work, not a resume): + +Say: + +> **"You're already on `feature/<name>`. Would you like to create a GitHub issue linked to this branch and start coding? Or switch to the base branch first? (yes to continue here / no to abort)"** + +Stop. Wait for the user to respond. + +- **Yes** → proceed to **Case A**, but **skip Step 4** (branch creation) entirely. The current branch is used as-is. In Step 3, after creating the issue, link it to the existing branch by running: + ```bash + gh issue develop <number> --base <base-branch> --name <current-branch-name> + ``` + This links the issue to the branch in GitHub without creating or checking out a new branch. If `gh issue develop` fails because the branch already exists on the remote, that is fine — the link may already be established. Continue to Step 5. +- **No / abort** → stop. Tell the user to switch to the base branch and run `/start` again. + +If already on a `feature/*` branch and `$ARGUMENTS` **is** a number → proceed to **Case B** normally (it handles branch checkout itself). + +If on any other branch → proceed to Case A or Case B as determined by the `$ARGUMENTS` check above. + +--- + ## Case A: New work (text description) ### Step 1 — Investigate @@ -95,7 +124,15 @@ The friendly text question is required regardless of harness mode. If your harne Create the issue in two steps — **this exact sequence is mandatory**: -**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. +**Step 3a — Create a temp directory and write the body file.** Run: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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>/issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. **Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: @@ -105,7 +142,7 @@ gh issue create \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] \ - --body-file /tmp/cc_issue_body.md + --body-file <tmpdir>/issue_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -153,7 +190,7 @@ Show the user: `Created issue #<number>: <title>` Then immediately post agent implementation notes as a comment. -Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/issue_comment.md` (same temp directory from Step 3a): ```markdown ## Agent Implementation Notes @@ -162,7 +199,7 @@ Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: Then post it (do NOT use `--body` or heredocs — same rule as Step 3): ```bash -gh issue comment <number> --body-file /tmp/cc_issue_comment.md +gh issue comment <number> --body-file <tmpdir>/issue_comment.md ``` ### Step 4 — Create feature branch @@ -271,9 +308,9 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - Do not write or edit any source file before `git branch --show-current` shows `feature/*`. - Do not use `make branch` — always use `gh issue develop` so the branch is linked to the issue in GitHub. - Do not commit during `/start` — commits happen in `/submit-for-review`. -- If already on a feature branch when `/start` is invoked, warn the user before creating another branch. +- If already on a feature branch when `/start` is invoked with new work (Case A), prompt the user to either continue on the current branch (skipping branch creation) or abort. See **Pre-check: Existing feature branch** above. - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: codex | hash: 7ba07088 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: codex | hash: a1701c1d | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.agents/skills/submit-for-review/SKILL.md b/.agents/skills/submit-for-review/SKILL.md index d711c85..5cab5f5 100644 --- a/.agents/skills/submit-for-review/SKILL.md +++ b/.agents/skills/submit-for-review/SKILL.md @@ -111,7 +111,15 @@ Use `Issue #<number>` as the issue reference — the issue stays open until `/de Then create the PR in two steps — **this exact sequence is mandatory**: -First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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. ```markdown <description of what changed and why> @@ -122,7 +130,7 @@ First, use your file-writing tool (Write in Claude Code, equivalent in other age Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): ``` -gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +gh pr create --base <target-branch> --title "<title>" --body-file <tmpdir>/pr_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -226,7 +234,7 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): ```markdown ## Resolution @@ -239,7 +247,7 @@ See #<PR-number> for full technical details. Then post it (do NOT use `--body` or heredocs): ``` -gh issue comment <number> --body-file /tmp/cc_resolution_comment.md +gh issue comment <number> --body-file <tmpdir>/resolution_comment.md ``` **Resolution writing rules:** @@ -275,7 +283,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 `/tmp/cc_followup_body.md` for each finding: +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. @@ -291,7 +299,7 @@ Then create the issue (do NOT use `--body` or heredocs): gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ [--label "<pool-selected labels>"] \ - --body-file /tmp/cc_followup_body.md + --body-file <tmpdir>/followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. @@ -317,4 +325,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: codex | hash: d09ab40d | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: codex | hash: 7da4e9fb | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/checkpoint.md b/.claude/commands/checkpoint.md new file mode 100644 index 0000000..c7e8473 --- /dev/null +++ b/.claude/commands/checkpoint.md @@ -0,0 +1,88 @@ +Code Cannon: Commit and push WIP without review or merge + +--- + +## What `/checkpoint` does + +`/checkpoint` is a lightweight save point — commit and push work-in-progress to the remote without triggering type-checks, reviews, PR creation, or merge. Use it to persist progress during long coding sessions or before context-switching. + +--- + +## Step 1 — Verify branch + +```bash +git branch --show-current +``` + +Protected branches (not a feature branch): +- `main` +- `dev` + +If the current branch matches any of the above, **abort immediately** and say: + +> "You are on `<branch>`. `/checkpoint` must be run from a feature branch." + +--- + +## Step 2 — Check for changes + +```bash +git status --porcelain +``` + +If the output is empty (no staged, unstaged, or untracked changes), say: + +> "Nothing to checkpoint — working tree is clean." + +Stop. Do not create an empty commit. + +--- + +## Step 3 — Stage and commit + +```bash +git add -A +git commit -m "WIP: <message>" +``` + +**Commit message rules:** +- Always prefix with `WIP: ` so these commits are visually distinct in `git log`. +- If `$ARGUMENTS` is provided, use it as `<message>` (e.g. `WIP: add auth middleware`). +- If `$ARGUMENTS` is empty, auto-generate from the diff summary (e.g. `WIP: update auth.ts, add login tests`). Keep it under 72 characters. +- Do not commit `.env` files, secrets, or build artifacts. If `git status` shows such files, add them to `.gitignore` or exclude them from staging before committing. + +--- + +## Step 4 — Push + +```bash +git push -u origin HEAD +``` + +If the push fails because the remote branch does not exist yet, the `-u` flag handles creation. If it fails for another reason, report the error and stop. + +--- + +## Step 5 — Report + +Count the files in the commit: + +```bash +git diff --stat HEAD~1 +``` + +Say: + +> "Checkpoint saved. N file(s) committed and pushed to `<branch>`. Run `/submit-for-review` when ready to open a PR." + +--- + +## Hard rules + +- Never create a PR. +- Never trigger a review. +- Never merge. +- Never apply labels or modify issues. +- Never run `make check` — checkpoints are not quality gates. +- Do not prompt for confirmation — `/checkpoint` is a quick save, not a ceremony. +<!-- generated by CodeCannon/sync.sh | skill: checkpoint | adapter: claude | hash: cc7e9e00 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/deploy.md b/.claude/commands/deploy.md index 962a484..9a5a2cb 100644 --- a/.claude/commands/deploy.md +++ b/.claude/commands/deploy.md @@ -161,7 +161,15 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ## Step 6 — Create PR: `dev` → `main` -Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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 (not Bash) to create `<tmpdir>/release_pr_body.md`: ```markdown Release vX.Y.Z @@ -179,7 +187,7 @@ Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash gh pr create --base main --head dev \ --title "Release vX.Y.Z" \ - --body-file /tmp/cc_release_pr_body.md + --body-file <tmpdir>/release_pr_body.md ``` Note the PR number from the output. @@ -210,7 +218,7 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/release_notes.md` (same temp directory from Step 6): ```markdown ## Changes @@ -226,7 +234,7 @@ Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes-file /tmp/cc_release_notes.md + --notes-file <tmpdir>/release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -240,4 +248,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: claude | hash: 88439028 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: claude | hash: 1fa58dba | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/start.md b/.claude/commands/start.md index 2458d44..b769146 100644 --- a/.claude/commands/start.md +++ b/.claude/commands/start.md @@ -63,6 +63,35 @@ After parsing flags, determine the active milestone in this order: --- +## Pre-check: Existing feature branch + +Before entering Case A or Case B, check the current branch: + +```bash +git branch --show-current +``` + +If already on a `feature/*` branch and `$ARGUMENTS` is **not** a number (i.e. this is new work, not a resume): + +Say: + +> **"You're already on `feature/<name>`. Would you like to create a GitHub issue linked to this branch and start coding? Or switch to the base branch first? (yes to continue here / no to abort)"** + +Stop. Wait for the user to respond. + +- **Yes** → proceed to **Case A**, but **skip Step 4** (branch creation) entirely. The current branch is used as-is. In Step 3, after creating the issue, link it to the existing branch by running: + ```bash + gh issue develop <number> --base <base-branch> --name <current-branch-name> + ``` + This links the issue to the branch in GitHub without creating or checking out a new branch. If `gh issue develop` fails because the branch already exists on the remote, that is fine — the link may already be established. Continue to Step 5. +- **No / abort** → stop. Tell the user to switch to the base branch and run `/start` again. + +If already on a `feature/*` branch and `$ARGUMENTS` **is** a number → proceed to **Case B** normally (it handles branch checkout itself). + +If on any other branch → proceed to Case A or Case B as determined by the `$ARGUMENTS` check above. + +--- + ## Case A: New work (text description) ### Step 1 — Investigate @@ -90,7 +119,15 @@ The friendly text question is required regardless of harness mode. If your harne Create the issue in two steps — **this exact sequence is mandatory**: -**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. +**Step 3a — Create a temp directory and write the body file.** Run: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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>/issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. **Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: @@ -100,7 +137,7 @@ gh issue create \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] \ - --body-file /tmp/cc_issue_body.md + --body-file <tmpdir>/issue_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -148,7 +185,7 @@ Show the user: `Created issue #<number>: <title>` Then immediately post agent implementation notes as a comment. -Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/issue_comment.md` (same temp directory from Step 3a): ```markdown ## Agent Implementation Notes @@ -157,7 +194,7 @@ Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: Then post it (do NOT use `--body` or heredocs — same rule as Step 3): ```bash -gh issue comment <number> --body-file /tmp/cc_issue_comment.md +gh issue comment <number> --body-file <tmpdir>/issue_comment.md ``` ### Step 4 — Create feature branch @@ -266,9 +303,9 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - Do not write or edit any source file before `git branch --show-current` shows `feature/*`. - Do not use `make branch` — always use `gh issue develop` so the branch is linked to the issue in GitHub. - Do not commit during `/start` — commits happen in `/submit-for-review`. -- If already on a feature branch when `/start` is invoked, warn the user before creating another branch. +- If already on a feature branch when `/start` is invoked with new work (Case A), prompt the user to either continue on the current branch (skipping branch creation) or abort. See **Pre-check: Existing feature branch** above. - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: claude | hash: d1144b46 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: claude | hash: 3761be9e | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.claude/commands/submit-for-review.md b/.claude/commands/submit-for-review.md index 98a6d71..6ae24c6 100644 --- a/.claude/commands/submit-for-review.md +++ b/.claude/commands/submit-for-review.md @@ -106,7 +106,15 @@ Use `Issue #<number>` as the issue reference — the issue stays open until `/de Then create the PR in two steps — **this exact sequence is mandatory**: -First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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. ```markdown <description of what changed and why> @@ -117,7 +125,7 @@ First, use your file-writing tool (Write in Claude Code, equivalent in other age Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): ``` -gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +gh pr create --base <target-branch> --title "<title>" --body-file <tmpdir>/pr_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -221,7 +229,7 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): ```markdown ## Resolution @@ -234,7 +242,7 @@ See #<PR-number> for full technical details. Then post it (do NOT use `--body` or heredocs): ``` -gh issue comment <number> --body-file /tmp/cc_resolution_comment.md +gh issue comment <number> --body-file <tmpdir>/resolution_comment.md ``` **Resolution writing rules:** @@ -270,7 +278,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 `/tmp/cc_followup_body.md` for each finding: +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. @@ -286,7 +294,7 @@ Then create the issue (do NOT use `--body` or heredocs): gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ [--label "<pool-selected labels>"] \ - --body-file /tmp/cc_followup_body.md + --body-file <tmpdir>/followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. @@ -312,4 +320,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: claude | hash: 7cc1a5b2 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: claude | hash: f9e7a62e | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/checkpoint.mdc b/.cursor/rules/checkpoint.mdc new file mode 100644 index 0000000..5ec2e26 --- /dev/null +++ b/.cursor/rules/checkpoint.mdc @@ -0,0 +1,94 @@ +--- +description: Code Cannon: Commit and push WIP without review or merge +globs: +alwaysApply: false +--- + +> **Cursor:** Trigger this skill via `@checkpoint` in Agent mode. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/submit-for-review` must be done manually using the review-agent prompt. + +--- + +## What `/checkpoint` does + +`/checkpoint` is a lightweight save point — commit and push work-in-progress to the remote without triggering type-checks, reviews, PR creation, or merge. Use it to persist progress during long coding sessions or before context-switching. + +--- + +## Step 1 — Verify branch + +```bash +git branch --show-current +``` + +Protected branches (not a feature branch): +- `main` +- `dev` + +If the current branch matches any of the above, **abort immediately** and say: + +> "You are on `<branch>`. `/checkpoint` must be run from a feature branch." + +--- + +## Step 2 — Check for changes + +```bash +git status --porcelain +``` + +If the output is empty (no staged, unstaged, or untracked changes), say: + +> "Nothing to checkpoint — working tree is clean." + +Stop. Do not create an empty commit. + +--- + +## Step 3 — Stage and commit + +```bash +git add -A +git commit -m "WIP: <message>" +``` + +**Commit message rules:** +- Always prefix with `WIP: ` so these commits are visually distinct in `git log`. +- If `$ARGUMENTS` is provided, use it as `<message>` (e.g. `WIP: add auth middleware`). +- If `$ARGUMENTS` is empty, auto-generate from the diff summary (e.g. `WIP: update auth.ts, add login tests`). Keep it under 72 characters. +- Do not commit `.env` files, secrets, or build artifacts. If `git status` shows such files, add them to `.gitignore` or exclude them from staging before committing. + +--- + +## Step 4 — Push + +```bash +git push -u origin HEAD +``` + +If the push fails because the remote branch does not exist yet, the `-u` flag handles creation. If it fails for another reason, report the error and stop. + +--- + +## Step 5 — Report + +Count the files in the commit: + +```bash +git diff --stat HEAD~1 +``` + +Say: + +> "Checkpoint saved. N file(s) committed and pushed to `<branch>`. Run `/submit-for-review` when ready to open a PR." + +--- + +## Hard rules + +- Never create a PR. +- Never trigger a review. +- Never merge. +- Never apply labels or modify issues. +- Never run `make check` — checkpoints are not quality gates. +- Do not prompt for confirmation — `/checkpoint` is a quick save, not a ceremony. +<!-- generated by CodeCannon/sync.sh | skill: checkpoint | adapter: cursor | hash: 1b224590 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/deploy.mdc b/.cursor/rules/deploy.mdc index d4e7d70..664a54a 100644 --- a/.cursor/rules/deploy.mdc +++ b/.cursor/rules/deploy.mdc @@ -167,7 +167,15 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ## Step 6 — Create PR: `dev` → `main` -Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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 (not Bash) to create `<tmpdir>/release_pr_body.md`: ```markdown Release vX.Y.Z @@ -185,7 +193,7 @@ Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash gh pr create --base main --head dev \ --title "Release vX.Y.Z" \ - --body-file /tmp/cc_release_pr_body.md + --body-file <tmpdir>/release_pr_body.md ``` Note the PR number from the output. @@ -216,7 +224,7 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/release_notes.md` (same temp directory from Step 6): ```markdown ## Changes @@ -232,7 +240,7 @@ Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes-file /tmp/cc_release_notes.md + --notes-file <tmpdir>/release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -246,4 +254,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: cursor | hash: a7a8fe00 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: cursor | hash: 31e92299 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/start.mdc b/.cursor/rules/start.mdc index 3adac90..58440b6 100644 --- a/.cursor/rules/start.mdc +++ b/.cursor/rules/start.mdc @@ -69,6 +69,35 @@ After parsing flags, determine the active milestone in this order: --- +## Pre-check: Existing feature branch + +Before entering Case A or Case B, check the current branch: + +```bash +git branch --show-current +``` + +If already on a `feature/*` branch and `$ARGUMENTS` is **not** a number (i.e. this is new work, not a resume): + +Say: + +> **"You're already on `feature/<name>`. Would you like to create a GitHub issue linked to this branch and start coding? Or switch to the base branch first? (yes to continue here / no to abort)"** + +Stop. Wait for the user to respond. + +- **Yes** → proceed to **Case A**, but **skip Step 4** (branch creation) entirely. The current branch is used as-is. In Step 3, after creating the issue, link it to the existing branch by running: + ```bash + gh issue develop <number> --base <base-branch> --name <current-branch-name> + ``` + This links the issue to the branch in GitHub without creating or checking out a new branch. If `gh issue develop` fails because the branch already exists on the remote, that is fine — the link may already be established. Continue to Step 5. +- **No / abort** → stop. Tell the user to switch to the base branch and run `/start` again. + +If already on a `feature/*` branch and `$ARGUMENTS` **is** a number → proceed to **Case B** normally (it handles branch checkout itself). + +If on any other branch → proceed to Case A or Case B as determined by the `$ARGUMENTS` check above. + +--- + ## Case A: New work (text description) ### Step 1 — Investigate @@ -96,7 +125,15 @@ The friendly text question is required regardless of harness mode. If your harne Create the issue in two steps — **this exact sequence is mandatory**: -**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. +**Step 3a — Create a temp directory and write the body file.** Run: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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>/issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. **Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: @@ -106,7 +143,7 @@ gh issue create \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] \ - --body-file /tmp/cc_issue_body.md + --body-file <tmpdir>/issue_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -154,7 +191,7 @@ Show the user: `Created issue #<number>: <title>` Then immediately post agent implementation notes as a comment. -Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/issue_comment.md` (same temp directory from Step 3a): ```markdown ## Agent Implementation Notes @@ -163,7 +200,7 @@ Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: Then post it (do NOT use `--body` or heredocs — same rule as Step 3): ```bash -gh issue comment <number> --body-file /tmp/cc_issue_comment.md +gh issue comment <number> --body-file <tmpdir>/issue_comment.md ``` ### Step 4 — Create feature branch @@ -272,9 +309,9 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - Do not write or edit any source file before `git branch --show-current` shows `feature/*`. - Do not use `make branch` — always use `gh issue develop` so the branch is linked to the issue in GitHub. - Do not commit during `/start` — commits happen in `/submit-for-review`. -- If already on a feature branch when `/start` is invoked, warn the user before creating another branch. +- If already on a feature branch when `/start` is invoked with new work (Case A), prompt the user to either continue on the current branch (skipping branch creation) or abort. See **Pre-check: Existing feature branch** above. - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: cursor | hash: 99965523 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: cursor | hash: 1c0ca80d | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.cursor/rules/submit-for-review.mdc b/.cursor/rules/submit-for-review.mdc index 89107be..085ebad 100644 --- a/.cursor/rules/submit-for-review.mdc +++ b/.cursor/rules/submit-for-review.mdc @@ -112,7 +112,15 @@ Use `Issue #<number>` as the issue reference — the issue stays open until `/de Then create the PR in two steps — **this exact sequence is mandatory**: -First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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. ```markdown <description of what changed and why> @@ -123,7 +131,7 @@ First, use your file-writing tool (Write in Claude Code, equivalent in other age Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): ``` -gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +gh pr create --base <target-branch> --title "<title>" --body-file <tmpdir>/pr_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -227,7 +235,7 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): ```markdown ## Resolution @@ -240,7 +248,7 @@ See #<PR-number> for full technical details. Then post it (do NOT use `--body` or heredocs): ``` -gh issue comment <number> --body-file /tmp/cc_resolution_comment.md +gh issue comment <number> --body-file <tmpdir>/resolution_comment.md ``` **Resolution writing rules:** @@ -276,7 +284,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 `/tmp/cc_followup_body.md` for each finding: +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. @@ -292,7 +300,7 @@ Then create the issue (do NOT use `--body` or heredocs): gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ [--label "<pool-selected labels>"] \ - --body-file /tmp/cc_followup_body.md + --body-file <tmpdir>/followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. @@ -318,4 +326,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: cursor | hash: 141fa52f | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: cursor | hash: f7cf15c3 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/checkpoint/SKILL.md b/.gemini/skills/checkpoint/SKILL.md new file mode 100644 index 0000000..beb2bf8 --- /dev/null +++ b/.gemini/skills/checkpoint/SKILL.md @@ -0,0 +1,93 @@ +--- +name: checkpoint +description: Code Cannon: Commit and push WIP without review or merge +--- + +> **Gemini CLI:** This skill is triggered by description matching. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/submit-for-review` must be done manually using the review-agent prompt in a separate session. + +--- + +## What `/checkpoint` does + +`/checkpoint` is a lightweight save point — commit and push work-in-progress to the remote without triggering type-checks, reviews, PR creation, or merge. Use it to persist progress during long coding sessions or before context-switching. + +--- + +## Step 1 — Verify branch + +```bash +git branch --show-current +``` + +Protected branches (not a feature branch): +- `main` +- `dev` + +If the current branch matches any of the above, **abort immediately** and say: + +> "You are on `<branch>`. `/checkpoint` must be run from a feature branch." + +--- + +## Step 2 — Check for changes + +```bash +git status --porcelain +``` + +If the output is empty (no staged, unstaged, or untracked changes), say: + +> "Nothing to checkpoint — working tree is clean." + +Stop. Do not create an empty commit. + +--- + +## Step 3 — Stage and commit + +```bash +git add -A +git commit -m "WIP: <message>" +``` + +**Commit message rules:** +- Always prefix with `WIP: ` so these commits are visually distinct in `git log`. +- If `$ARGUMENTS` is provided, use it as `<message>` (e.g. `WIP: add auth middleware`). +- If `$ARGUMENTS` is empty, auto-generate from the diff summary (e.g. `WIP: update auth.ts, add login tests`). Keep it under 72 characters. +- Do not commit `.env` files, secrets, or build artifacts. If `git status` shows such files, add them to `.gitignore` or exclude them from staging before committing. + +--- + +## Step 4 — Push + +```bash +git push -u origin HEAD +``` + +If the push fails because the remote branch does not exist yet, the `-u` flag handles creation. If it fails for another reason, report the error and stop. + +--- + +## Step 5 — Report + +Count the files in the commit: + +```bash +git diff --stat HEAD~1 +``` + +Say: + +> "Checkpoint saved. N file(s) committed and pushed to `<branch>`. Run `/submit-for-review` when ready to open a PR." + +--- + +## Hard rules + +- Never create a PR. +- Never trigger a review. +- Never merge. +- Never apply labels or modify issues. +- Never run `make check` — checkpoints are not quality gates. +- Do not prompt for confirmation — `/checkpoint` is a quick save, not a ceremony. +<!-- generated by CodeCannon/sync.sh | skill: checkpoint | adapter: gemini | hash: 1a1dbbb0 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/deploy/SKILL.md b/.gemini/skills/deploy/SKILL.md index aaf65c2..c50671f 100644 --- a/.gemini/skills/deploy/SKILL.md +++ b/.gemini/skills/deploy/SKILL.md @@ -166,7 +166,15 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo ## Step 6 — Create PR: `dev` → `main` -Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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 (not Bash) to create `<tmpdir>/release_pr_body.md`: ```markdown Release vX.Y.Z @@ -184,7 +192,7 @@ Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash gh pr create --base main --head dev \ --title "Release vX.Y.Z" \ - --body-file /tmp/cc_release_pr_body.md + --body-file <tmpdir>/release_pr_body.md ``` Note the PR number from the output. @@ -215,7 +223,7 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/release_notes.md` (same temp directory from Step 6): ```markdown ## Changes @@ -231,7 +239,7 @@ Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes-file /tmp/cc_release_notes.md + --notes-file <tmpdir>/release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -245,4 +253,4 @@ After the command runs, note the release URL from the output. Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `make deploy-prod` to ship to production." -<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: gemini | hash: 1b252e14 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: deploy | adapter: gemini | hash: 7e8845cd | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/start/SKILL.md b/.gemini/skills/start/SKILL.md index bd2678f..640d717 100644 --- a/.gemini/skills/start/SKILL.md +++ b/.gemini/skills/start/SKILL.md @@ -68,6 +68,35 @@ After parsing flags, determine the active milestone in this order: --- +## Pre-check: Existing feature branch + +Before entering Case A or Case B, check the current branch: + +```bash +git branch --show-current +``` + +If already on a `feature/*` branch and `$ARGUMENTS` is **not** a number (i.e. this is new work, not a resume): + +Say: + +> **"You're already on `feature/<name>`. Would you like to create a GitHub issue linked to this branch and start coding? Or switch to the base branch first? (yes to continue here / no to abort)"** + +Stop. Wait for the user to respond. + +- **Yes** → proceed to **Case A**, but **skip Step 4** (branch creation) entirely. The current branch is used as-is. In Step 3, after creating the issue, link it to the existing branch by running: + ```bash + gh issue develop <number> --base <base-branch> --name <current-branch-name> + ``` + This links the issue to the branch in GitHub without creating or checking out a new branch. If `gh issue develop` fails because the branch already exists on the remote, that is fine — the link may already be established. Continue to Step 5. +- **No / abort** → stop. Tell the user to switch to the base branch and run `/start` again. + +If already on a `feature/*` branch and `$ARGUMENTS` **is** a number → proceed to **Case B** normally (it handles branch checkout itself). + +If on any other branch → proceed to Case A or Case B as determined by the `$ARGUMENTS` check above. + +--- + ## Case A: New work (text description) ### Step 1 — Investigate @@ -95,7 +124,15 @@ The friendly text question is required regardless of harness mode. If your harne Create the issue in two steps — **this exact sequence is mandatory**: -**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. +**Step 3a — Create a temp directory and write the body file.** Run: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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>/issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. **Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: @@ -105,7 +142,7 @@ gh issue create \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] \ - --body-file /tmp/cc_issue_body.md + --body-file <tmpdir>/issue_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -153,7 +190,7 @@ Show the user: `Created issue #<number>: <title>` Then immediately post agent implementation notes as a comment. -Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/issue_comment.md` (same temp directory from Step 3a): ```markdown ## Agent Implementation Notes @@ -162,7 +199,7 @@ Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: Then post it (do NOT use `--body` or heredocs — same rule as Step 3): ```bash -gh issue comment <number> --body-file /tmp/cc_issue_comment.md +gh issue comment <number> --body-file <tmpdir>/issue_comment.md ``` ### Step 4 — Create feature branch @@ -271,9 +308,9 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - Do not write or edit any source file before `git branch --show-current` shows `feature/*`. - Do not use `make branch` — always use `gh issue develop` so the branch is linked to the issue in GitHub. - Do not commit during `/start` — commits happen in `/submit-for-review`. -- If already on a feature branch when `/start` is invoked, warn the user before creating another branch. +- If already on a feature branch when `/start` is invoked with new work (Case A), prompt the user to either continue on the current branch (skipping branch creation) or abort. See **Pre-check: Existing feature branch** above. - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `bug, documentation, enhancement, chore` → omit `--label` entirely. Never apply a label outside `bug, documentation, enhancement, chore`. - Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCannon/sync.sh | skill: start | adapter: gemini | hash: 250dcb74 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: start | adapter: gemini | hash: 6a465756 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/.gemini/skills/submit-for-review/SKILL.md b/.gemini/skills/submit-for-review/SKILL.md index ec193b8..5a58f16 100644 --- a/.gemini/skills/submit-for-review/SKILL.md +++ b/.gemini/skills/submit-for-review/SKILL.md @@ -111,7 +111,15 @@ Use `Issue #<number>` as the issue reference — the issue stays open until `/de Then create the PR in two steps — **this exact sequence is mandatory**: -First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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. ```markdown <description of what changed and why> @@ -122,7 +130,7 @@ First, use your file-writing tool (Write in Claude Code, equivalent in other age Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): ``` -gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +gh pr create --base <target-branch> --title "<title>" --body-file <tmpdir>/pr_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -226,7 +234,7 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): ```markdown ## Resolution @@ -239,7 +247,7 @@ See #<PR-number> for full technical details. Then post it (do NOT use `--body` or heredocs): ``` -gh issue comment <number> --body-file /tmp/cc_resolution_comment.md +gh issue comment <number> --body-file <tmpdir>/resolution_comment.md ``` **Resolution writing rules:** @@ -275,7 +283,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 `/tmp/cc_followup_body.md` for each finding: +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. @@ -291,7 +299,7 @@ Then create the issue (do NOT use `--body` or heredocs): gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ [--label "<pool-selected labels>"] \ - --body-file /tmp/cc_followup_body.md + --body-file <tmpdir>/followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `bug, documentation, enhancement, chore` that genuinely fit the finding. If `bug, documentation, enhancement, chore` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. @@ -317,4 +325,4 @@ If a single `gh issue create` call fails, report the failure for that finding an - `/submit-for-review` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - The follow-up issue offer in Step 9 runs only after a successful merge and only when the review produced non-blocking findings. Never prompt the user for follow-ups when the review blocked the merge — those findings should be fixed, not ticketed. -<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: gemini | hash: ba3271f2 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> +<!-- generated by CodeCannon/sync.sh | skill: submit-for-review | adapter: gemini | hash: 9915fca9 | DO NOT EDIT — run CodeCannon/sync.sh to regenerate --> diff --git a/skills/checkpoint.md b/skills/checkpoint.md new file mode 100644 index 0000000..7cbe0ff --- /dev/null +++ b/skills/checkpoint.md @@ -0,0 +1,95 @@ +--- +skill: checkpoint +type: skill +description: "Code Cannon: Commit and push WIP without review or merge" +args: "optional commit message" +--- + +## What `/checkpoint` does + +`/checkpoint` is a lightweight save point — commit and push work-in-progress to the remote without triggering type-checks, reviews, PR creation, or merge. Use it to persist progress during long coding sessions or before context-switching. + +--- + +## Step 1 — Verify branch + +```bash +git branch --show-current +``` + +Protected branches (not a feature branch): +- `{{BRANCH_PROD}}` +{{#if BRANCH_DEV}} +- `{{BRANCH_DEV}}` +{{/if}} +{{#if BRANCH_TEST}} +- `{{BRANCH_TEST}}` +{{/if}} + +If the current branch matches any of the above, **abort immediately** and say: + +> "You are on `<branch>`. `/checkpoint` must be run from a feature branch." + +--- + +## Step 2 — Check for changes + +```bash +git status --porcelain +``` + +If the output is empty (no staged, unstaged, or untracked changes), say: + +> "Nothing to checkpoint — working tree is clean." + +Stop. Do not create an empty commit. + +--- + +## Step 3 — Stage and commit + +```bash +git add -A +git commit -m "WIP: <message>" +``` + +**Commit message rules:** +- Always prefix with `WIP: ` so these commits are visually distinct in `git log`. +- If `$ARGUMENTS` is provided, use it as `<message>` (e.g. `WIP: add auth middleware`). +- If `$ARGUMENTS` is empty, auto-generate from the diff summary (e.g. `WIP: update auth.ts, add login tests`). Keep it under 72 characters. +- Do not commit `.env` files, secrets, or build artifacts. If `git status` shows such files, add them to `.gitignore` or exclude them from staging before committing. + +--- + +## Step 4 — Push + +```bash +git push -u origin HEAD +``` + +If the push fails because the remote branch does not exist yet, the `-u` flag handles creation. If it fails for another reason, report the error and stop. + +--- + +## Step 5 — Report + +Count the files in the commit: + +```bash +git diff --stat HEAD~1 +``` + +Say: + +> "Checkpoint saved. N file(s) committed and pushed to `<branch>`. Run `/submit-for-review` when ready to open a PR." + +--- + +## Hard rules + +- Never create a PR. +- Never trigger a review. +- Never merge. +- Never apply labels or modify issues. +- Never run `{{CHECK_CMD}}` — checkpoints are not quality gates. +- Do not prompt for confirmation — `/checkpoint` is a quick save, not a ceremony. diff --git a/skills/deploy.md b/skills/deploy.md index cbf7cdc..ffdcd4b 100644 --- a/skills/deploy.md +++ b/skills/deploy.md @@ -239,7 +239,15 @@ Wait for the user to type "release" or an explicit confirmation. Any other respo The version tag and PR/issue list are already known. If no previous tag exists, omit the "Full changelog" line. -Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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 (not Bash) to create `<tmpdir>/release_notes.md`: ```markdown ## Changes @@ -255,7 +263,7 @@ Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes-file /tmp/cc_release_notes.md + --notes-file <tmpdir>/release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, use just the PR title. @@ -274,7 +282,15 @@ Tell the user: {{#if !BRANCH_TEST}} ## Step 6 — Create PR: `{{BRANCH_DEV}}` → `{{BRANCH_PROD}}` -Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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 (not Bash) to create `<tmpdir>/release_pr_body.md`: ```markdown Release vX.Y.Z @@ -292,7 +308,7 @@ Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_DEV}} \ --title "Release vX.Y.Z" \ - --body-file /tmp/cc_release_pr_body.md + --body-file <tmpdir>/release_pr_body.md ``` Note the PR number from the output. @@ -323,7 +339,7 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/release_notes.md` (same temp directory from Step 6): ```markdown ## Changes @@ -339,7 +355,7 @@ Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes-file /tmp/cc_release_notes.md + --notes-file <tmpdir>/release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. @@ -357,7 +373,15 @@ Tell the user: {{#if BRANCH_TEST}} ## Step 6 — Create PR: `{{BRANCH_TEST}}` → `{{BRANCH_PROD}}` -Use your file-writing tool (not Bash) to create `/tmp/cc_release_pr_body.md`: +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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 (not Bash) to create `<tmpdir>/release_pr_body.md`: ```markdown Release vX.Y.Z @@ -375,7 +399,7 @@ Then create the PR (do NOT use `--body`, `--body-file -`, or heredocs): ```bash gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_TEST}} \ --title "Release vX.Y.Z" \ - --body-file /tmp/cc_release_pr_body.md + --body-file <tmpdir>/release_pr_body.md ``` Note the PR number from the output. @@ -406,7 +430,7 @@ git describe --abbrev=0 <version-tag>^ 2>/dev/null If no previous tag exists, omit the "Full changelog" line. -Use your file-writing tool (not Bash) to create `/tmp/cc_release_notes.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/release_notes.md` (same temp directory from Step 6): ```markdown ## Changes @@ -422,7 +446,7 @@ Then create the release (do NOT use `--notes`, `--notes-file -`, or heredocs): ```bash gh release create <version-tag> \ --title "<version-tag>" \ - --notes-file /tmp/cc_release_notes.md + --notes-file <tmpdir>/release_notes.md ``` Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, omit the `#<issue>` prefix and use just the PR title. diff --git a/skills/start.md b/skills/start.md index 3d43eed..b404998 100644 --- a/skills/start.md +++ b/skills/start.md @@ -111,6 +111,35 @@ After parsing flags, determine the active milestone in this order: --- +## Pre-check: Existing feature branch + +Before entering Case A or Case B, check the current branch: + +```bash +git branch --show-current +``` + +If already on a `feature/*` branch and `$ARGUMENTS` is **not** a number (i.e. this is new work, not a resume): + +Say: + +> **"You're already on `feature/<name>`. Would you like to create a GitHub issue linked to this branch and start coding? Or switch to the base branch first? (yes to continue here / no to abort)"** + +Stop. Wait for the user to respond. + +- **Yes** → proceed to **Case A**, but **skip Step 4** (branch creation) entirely. The current branch is used as-is. In Step 3, after creating the issue, link it to the existing branch by running: + ```bash + gh issue develop <number> --base <base-branch> --name <current-branch-name> + ``` + This links the issue to the branch in GitHub without creating or checking out a new branch. If `gh issue develop` fails because the branch already exists on the remote, that is fine — the link may already be established. Continue to Step 5. +- **No / abort** → stop. Tell the user to switch to the base branch and run `/start` again. + +If already on a `feature/*` branch and `$ARGUMENTS` **is** a number → proceed to **Case B** normally (it handles branch checkout itself). + +If on any other branch → proceed to Case A or Case B as determined by the `$ARGUMENTS` check above. + +--- + ## Case A: New work (text description) ### Step 1 — Investigate @@ -138,7 +167,15 @@ The friendly text question is required regardless of harness mode. If your harne Create the issue in two steps — **this exact sequence is mandatory**: -**Step 3a — Write the body to a temp file.** Use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. +**Step 3a — Create a temp directory and write the body file.** Run: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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>/issue_body.md` with the structured markdown body (see sections below). Do NOT use Bash/shell to write this file. Do NOT use heredocs, `cat`, or `echo`. The file-writing tool bypasses shell parsing entirely. **Step 3b — Run `gh issue create`** with `--body-file` pointing to the temp file: @@ -148,7 +185,7 @@ gh issue create \ --assignee @me \ [--label "<resolved labels>"] \ [--milestone "<resolved milestone>"] \ - --body-file /tmp/cc_issue_body.md + --body-file <tmpdir>/issue_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -196,7 +233,7 @@ Show the user: `Created issue #<number>: <title>` Then immediately post agent implementation notes as a comment. -Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/issue_comment.md` (same temp directory from Step 3a): ```markdown ## Agent Implementation Notes @@ -205,7 +242,7 @@ Use your file-writing tool (not Bash) to create `/tmp/cc_issue_comment.md`: Then post it (do NOT use `--body` or heredocs — same rule as Step 3): ```bash -gh issue comment <number> --body-file /tmp/cc_issue_comment.md +gh issue comment <number> --body-file <tmpdir>/issue_comment.md ``` ### Step 4 — Create feature branch @@ -346,7 +383,7 @@ When done, say: **"The code is ready for review. Please run `{{DEV_CMD}}` and te - Do not write or edit any source file before `git branch --show-current` shows `feature/*`. - Do not use `make branch` — always use `gh issue develop` so the branch is linked to the issue in GitHub. - Do not commit during `/start` — commits happen in `/submit-for-review`. -- If already on a feature branch when `/start` is invoked, warn the user before creating another branch. +- If already on a feature branch when `/start` is invoked with new work (Case A), prompt the user to either continue on the current branch (skipping branch creation) or abort. See **Pre-check: Existing feature branch** above. - `gh issue create` must use `--title` and `--body` flags. Never open an interactive editor. - The issue is assigned to `@me` at creation. If you are creating a ticket on someone else's behalf, remove the assignee after creation with `gh issue edit <number> --remove-assignee @me`. {{#if TICKET_LABELS}} diff --git a/skills/submit-for-review.md b/skills/submit-for-review.md index c1bdf67..5659c6c 100644 --- a/skills/submit-for-review.md +++ b/skills/submit-for-review.md @@ -128,7 +128,15 @@ Use `Closes #<number>` as the issue reference — merging to the default branch Then create the PR in two steps — **this exact sequence is mandatory**: -First, use your file-writing tool (Write in Claude Code, equivalent in other agents) to create `/tmp/cc_pr_body.md`. Do NOT use Bash/shell to write this file. +First, create a temp directory for this invocation: + +```bash +mkdir -p /tmp/CodeCannon && mktemp -d /tmp/CodeCannon/XXXXXX +``` + +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. ```markdown <description of what changed and why> @@ -139,7 +147,7 @@ First, use your file-writing tool (Write in Claude Code, equivalent in other age Then create the PR (do NOT use `--body`, `--body-file -`, heredocs, or `$(cat ...)`): ``` -gh pr create --base <target-branch> --title "<title>" --body-file /tmp/cc_pr_body.md +gh pr create --base <target-branch> --title "<title>" --body-file <tmpdir>/pr_body.md ``` > **IMPORTANT — never pass body content inline in the `gh` command.** Do not use `--body`, `--body-file -`, heredocs (`<<EOF` or `<<'EOF'`), or `$(cat ...)`. All of these embed markdown in a Bash command, which triggers permission prompts that cannot be permanently allowed (the shell parser flags `#` headings, quoted delimiters, and substitutions). The two-step pattern above — file-writing tool then `--body-file <path>` — is the only approach that works without prompts across Claude Code, Gemini CLI, Cursor, and Codex. @@ -256,7 +264,7 @@ If no linked issue was found, skip silently. Read the issue body (from Step 3 or via `gh issue view <number>`) to recall the original problem description. Then post a comment summarizing what was done: -Use your file-writing tool (not Bash) to create `/tmp/cc_resolution_comment.md`: +Use your file-writing tool (not Bash) to create `<tmpdir>/resolution_comment.md` (same temp directory from Step 6): ```markdown ## Resolution @@ -269,7 +277,7 @@ See #<PR-number> for full technical details. Then post it (do NOT use `--body` or heredocs): ``` -gh issue comment <number> --body-file /tmp/cc_resolution_comment.md +gh issue comment <number> --body-file <tmpdir>/resolution_comment.md ``` **Resolution writing rules:** @@ -315,7 +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 `/tmp/cc_followup_body.md` for each finding: +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. @@ -331,7 +339,7 @@ Then create the issue (do NOT use `--body` or heredocs): gh issue create \ --title "<finding text with [WARNING]/[NOTE]/[CRITICAL] prefix stripped, trimmed to a standalone sentence>" \ [--label "<pool-selected labels>"] \ - --body-file /tmp/cc_followup_body.md + --body-file <tmpdir>/followup_body.md ``` Label resolution for each follow-up issue: use the pool-based selection tier from `/start` — pick 1–3 labels from `{{TICKET_LABELS}}` that genuinely fit the finding. If `{{TICKET_LABELS}}` is empty or no pool label fits, omit `--label`. Do not attempt per-invocation flag resolution (there is no flag here) and never create new labels from follow-ups, even if label creation is enabled for the project. From 7466099dab0ebd7f3d7266c0e6f2bcb9466515e0 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart <sebastien.taggart@gmail.com> Date: Sat, 11 Apr 2026 17:57:08 -0400 Subject: [PATCH 5/5] Bump version to 0.4.2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 267577d..2b7c5ae 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.1 +0.4.2