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).