From 15c2b311edfbbb2e7f2cee8fc07b77dcedcfae93 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 25 Mar 2026 09:46:20 -0400 Subject: [PATCH 1/7] Update start.md and skills/start.md to include instructions for ensuring the base branch is up-to-date before creating a feature branch. Adjusted commands to reflect the use of `dev` and `main` branches appropriately. --- .claude/commands/start.md | 20 +++++++++++++++++++- skills/start.md | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/.claude/commands/start.md b/.claude/commands/start.md index cdac11e..cc4235f 100644 --- a/.claude/commands/start.md +++ b/.claude/commands/start.md @@ -123,6 +123,16 @@ gh issue comment --body "## Agent Implementation Notes ### Step 4 — Create feature branch +Ensure the base branch is up-to-date before branching: + +```bash +git checkout dev && git pull origin dev +``` + +(In trunk mode where `dev` is empty, use `main` instead.) + +Now create the feature branch: + ```bash gh issue develop --name feature/ --checkout ``` @@ -173,6 +183,14 @@ Ask: **"Does this match your understanding? Continue this ticket, or open a fres ### Step 3 — Check out branch +Ensure the base branch is up-to-date before branching: + +```bash +git checkout dev && git pull origin dev +``` + +(In trunk mode where `dev` is empty, use `main` instead.) + Find and check out the existing branch, or create a new one linked to the issue: ```bash @@ -208,4 +226,4 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - `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 --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `` → omit (or create if `false` is `true`). Never apply a label not in `` unless `false` is `true`. Milestone resolution order: per-invocation flag → `` config → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. - + diff --git a/skills/start.md b/skills/start.md index 792e746..d7f1ba1 100644 --- a/skills/start.md +++ b/skills/start.md @@ -126,6 +126,16 @@ gh issue comment --body "## Agent Implementation Notes ### Step 4 — Create feature branch +Ensure the base branch is up-to-date before branching: + +```bash +git checkout {{BRANCH_DEV}} && git pull origin {{BRANCH_DEV}} +``` + +(In trunk mode where `{{BRANCH_DEV}}` is empty, use `{{BRANCH_PROD}}` instead.) + +Now create the feature branch: + ```bash gh issue develop --name feature/ --checkout ``` @@ -176,6 +186,14 @@ Ask: **"Does this match your understanding? Continue this ticket, or open a fres ### Step 3 — Check out branch +Ensure the base branch is up-to-date before branching: + +```bash +git checkout {{BRANCH_DEV}} && git pull origin {{BRANCH_DEV}} +``` + +(In trunk mode where `{{BRANCH_DEV}}` is empty, use `{{BRANCH_PROD}}` instead.) + Find and check out the existing branch, or create a new one linked to the issue: ```bash From 100bddf4e58f8995db6b36267f32b74929725913 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 25 Mar 2026 15:17:24 -0400 Subject: [PATCH 2/7] Add three-branch workflow support to Makefile.agents.mk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce STAGING_BRANCH variable (empty by default) and promote target for projects using feature → dev → staging → main workflow. Add staging branch to protected branch guards in pr, abandon, and merge targets. Update deploy-preview to push STAGING_BRANCH when set. Regenerate skill commands via sync.sh. Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/commands/qa.md | 1 - .claude/commands/release.md | 1 - .claude/commands/setup.md | 3 +-- .claude/commands/ship.md | 1 - .claude/commands/start.md | 6 +++--- .claude/commands/version.md | 3 +-- Makefile | 14 ++++++++++---- Makefile.agents.mk | 31 ++++++++++++++++++++++++++++++- skills/start.md | 4 ++-- 9 files changed, 47 insertions(+), 17 deletions(-) diff --git a/.claude/commands/qa.md b/.claude/commands/qa.md index bab055b..1dbb3d4 100644 --- a/.claude/commands/qa.md +++ b/.claude/commands/qa.md @@ -1,4 +1,3 @@ - You are executing the `/qa` skill. Your argument is: $ARGUMENTS --- diff --git a/.claude/commands/release.md b/.claude/commands/release.md index c43395c..5642a08 100644 --- a/.claude/commands/release.md +++ b/.claude/commands/release.md @@ -1,4 +1,3 @@ - You are executing the `/release` skill. Your argument is: $ARGUMENTS --- diff --git a/.claude/commands/setup.md b/.claude/commands/setup.md index 99d3bee..1a4b5c5 100644 --- a/.claude/commands/setup.md +++ b/.claude/commands/setup.md @@ -1,4 +1,3 @@ - You are executing the `/setup` skill. Your argument is: $ARGUMENTS --- @@ -470,4 +469,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri - Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. - Do not skip any human gate in Phase 2 or Phase 3 — each write requires confirmation. - If the user skips a config value, do not ask again. Move on. - + diff --git a/.claude/commands/ship.md b/.claude/commands/ship.md index bd287f3..53b3351 100644 --- a/.claude/commands/ship.md +++ b/.claude/commands/ship.md @@ -1,4 +1,3 @@ - You are executing the `/ship` skill. Your argument is: $ARGUMENTS --- diff --git a/.claude/commands/start.md b/.claude/commands/start.md index cdac11e..144a6be 100644 --- a/.claude/commands/start.md +++ b/.claude/commands/start.md @@ -141,7 +141,7 @@ Show the user: `On branch feature/` Now write the code. Do NOT commit anything. -When done, say: **"The code is ready for review. Please run `make dev` and test locally. Let me know if it looks good, needs changes, or should be scrapped."** +When done, say: **"The code is ready for review. Please run `make dev` and test locally. Let me know if it looks good, needs changes, or should be scrapped. When you're happy, run `/ship` to commit, push, and open a PR."** - User says looks good → run `/ship` - User requests changes → iterate, repeat this message @@ -195,7 +195,7 @@ gh issue comment --body "Resuming work. --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `` → omit (or create if `false` is `true`). Never apply a label not in `` unless `false` is `true`. Milestone resolution order: per-invocation flag → `` config → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. - + diff --git a/.claude/commands/version.md b/.claude/commands/version.md index be872a0..fa78575 100644 --- a/.claude/commands/version.md +++ b/.claude/commands/version.md @@ -1,4 +1,3 @@ - You are executing the `/version` skill. Your argument is: $ARGUMENTS --- @@ -80,4 +79,4 @@ Report based on mode: - **Trunk mode** (`dev` empty): "Tagged vX.Y.Z. Run `/release` to create the GitHub Release." - **Two-branch mode** (`dev` set, `` empty): "Tagged vX.Y.Z. Run `make deploy-preview` to deploy to preview for testing. When testing is complete, run `/release`." - **Three-branch mode** (both set): "Tagged vX.Y.Z. Run `make deploy-preview` to deploy to staging. When testing is complete, run `/release`." - + diff --git a/Makefile b/Makefile index 3a1989c..487f153 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,9 @@ +# Makefile — CodeCannon project-specific targets +# +# Workflow targets (branch, pr, abandon, merge, promote) come from Makefile.agents.mk. +# This file adds CodeCannon-specific targets: sync, versioning, and deployment. + +INTEGRATION_BRANCH = dev include Makefile.agents.mk .PHONY: check dev sync bump-patch bump-minor bump-major set-version deploy-preview deploy-prod @@ -57,10 +63,10 @@ endif git commit -m "Bump version to $(V)" git tag v$(V) -# Publish the integration branch so consumers can pick up changes via submodule update. +# Push the integration branch for preview/testing. deploy-preview: - git push origin development + git push origin $(INTEGRATION_BRANCH) -# Publish a tagged release to main. +# Publish a tagged release to production. deploy-prod: - git push origin main --tags + git push origin $(PRODUCTION_BRANCH) --tags diff --git a/Makefile.agents.mk b/Makefile.agents.mk index c846c10..0841339 100644 --- a/Makefile.agents.mk +++ b/Makefile.agents.mk @@ -10,13 +10,24 @@ # Configuration: # INTEGRATION_BRANCH — the branch PRs target (default: development) # PRODUCTION_BRANCH — the production branch (default: main) +# STAGING_BRANCH — optional staging/pre-prod branch (default: empty) # FEATURE_PREFIX — prefix for feature branches (default: feature/) +# +# Workflow modes: +# Two-branch (default): feature → INTEGRATION_BRANCH → PRODUCTION_BRANCH +# Three-branch: feature → INTEGRATION_BRANCH → STAGING_BRANCH → PRODUCTION_BRANCH +# +# Set STAGING_BRANCH to enable three-branch mode. When set: +# - `promote` target becomes available (PR: integration → staging) +# - STAGING_BRANCH is protected from direct feature work +# - `deploy-preview` pushes STAGING_BRANCH instead of INTEGRATION_BRANCH INTEGRATION_BRANCH ?= development PRODUCTION_BRANCH ?= main +STAGING_BRANCH ?= FEATURE_PREFIX ?= feature/ -.PHONY: branch pr abandon merge +.PHONY: branch pr abandon merge promote # Create a feature branch from the integration branch. # Usage: make branch name= @@ -33,6 +44,9 @@ pr: if [ "$$branch" = "$(PRODUCTION_BRANCH)" ] || [ "$$branch" = "$(INTEGRATION_BRANCH)" ]; then \ echo "Error: cannot PR from $$branch. Use a feature branch."; exit 1; \ fi; \ + if [ -n "$(STAGING_BRANCH)" ] && [ "$$branch" = "$(STAGING_BRANCH)" ]; then \ + echo "Error: cannot PR from $$branch. Use a feature branch."; exit 1; \ + fi; \ git push -u origin "$$branch" && \ gh pr create --base $(INTEGRATION_BRANCH) --fill @@ -42,6 +56,9 @@ abandon: if [ "$$branch" = "$(PRODUCTION_BRANCH)" ] || [ "$$branch" = "$(INTEGRATION_BRANCH)" ]; then \ echo "Error: cannot abandon $$branch."; exit 1; \ fi; \ + if [ -n "$(STAGING_BRANCH)" ] && [ "$$branch" = "$(STAGING_BRANCH)" ]; then \ + echo "Error: cannot abandon $$branch."; exit 1; \ + fi; \ git checkout . && \ git clean -fd && \ git checkout $(INTEGRATION_BRANCH) && \ @@ -54,6 +71,9 @@ merge: if [ "$$branch" = "$(PRODUCTION_BRANCH)" ] || [ "$$branch" = "$(INTEGRATION_BRANCH)" ]; then \ echo "Error: cannot merge from $$branch. Use a feature branch."; exit 1; \ fi; \ + if [ -n "$(STAGING_BRANCH)" ] && [ "$$branch" = "$(STAGING_BRANCH)" ]; then \ + echo "Error: cannot merge from $$branch. Use a feature branch."; exit 1; \ + fi; \ pr_url=$$(gh pr view --json url -q .url 2>/dev/null); \ if [ -z "$$pr_url" ]; then \ echo "Error: no open PR found for $$branch."; exit 1; \ @@ -66,3 +86,12 @@ merge: git checkout $(INTEGRATION_BRANCH) && \ git pull origin $(INTEGRATION_BRANCH) && \ echo "PR merged into $(INTEGRATION_BRANCH)." + +# Promote integration branch to staging. Creates a PR: INTEGRATION_BRANCH → STAGING_BRANCH. +# Only available in three-branch mode (STAGING_BRANCH is set). +promote: +ifndef STAGING_BRANCH + $(error promote requires STAGING_BRANCH to be set (three-branch mode)) +endif + gh pr create --base $(STAGING_BRANCH) --head $(INTEGRATION_BRANCH) \ + --title "Promote $(INTEGRATION_BRANCH) → $(STAGING_BRANCH)" --fill diff --git a/skills/start.md b/skills/start.md index 792e746..8bd5ae0 100644 --- a/skills/start.md +++ b/skills/start.md @@ -144,7 +144,7 @@ Show the user: `On branch feature/` Now write the code. Do NOT commit anything. -When done, say: **"The code is ready for review. Please run `{{DEV_CMD}}` and test locally. Let me know if it looks good, needs changes, or should be scrapped."** +When done, say: **"The code is ready for review. Please run `{{DEV_CMD}}` and test locally. Let me know if it looks good, needs changes, or should be scrapped. When you're happy, run `/ship` to commit, push, and open a PR."** - User says looks good → run `/ship` - User requests changes → iterate, repeat this message @@ -198,7 +198,7 @@ gh issue comment --body "Resuming work. Date: Wed, 25 Mar 2026 15:21:14 -0400 Subject: [PATCH 3/7] Remove misleading deploy-preview note from header comment The deploy-preview behavior is a consumer Makefile concern, not controlled by Makefile.agents.mk. Co-Authored-By: Claude Opus 4.6 (1M context) --- Makefile.agents.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile.agents.mk b/Makefile.agents.mk index 0841339..35a3ea9 100644 --- a/Makefile.agents.mk +++ b/Makefile.agents.mk @@ -20,7 +20,6 @@ # Set STAGING_BRANCH to enable three-branch mode. When set: # - `promote` target becomes available (PR: integration → staging) # - STAGING_BRANCH is protected from direct feature work -# - `deploy-preview` pushes STAGING_BRANCH instead of INTEGRATION_BRANCH INTEGRATION_BRANCH ?= development PRODUCTION_BRANCH ?= main From 8b16ef2883060afbd5b85955d53e8a191ee2cc35 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 25 Mar 2026 15:29:58 -0400 Subject: [PATCH 4/7] Remove .DS_Store file to clean up repository --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 77b2f85cf327082eede91da81dd4c0a1e45d99ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5T4PZ0YU1`<354Vt6pM>XAgaW8e38!F@?5x&9mqOc&Hbjzz6aL{AOpW zh6eE@qBCLl+nt@C%?H`d5D^bolL1kmh!QkGmO+Pzx^rpAjQ4;XW8Bh`7G*IQUDq9f zexpkEzE3A~MFmai)BdZuZujhI+oGzoEHCREE6LIF;d1@>a^7V%`G(beCTwl8jS+?p zv7dr&z~ziql+!78x4<(McB}d$>V5fow|I0OHf0o>Ul$$_Hx z&VV!E47@WS=R-gfObr{wcyyqPB>=Dobrg)HmJpv{m>M>USb?y*0@anR#9(!YKA2r< z*eI$yu@xU|nK#7?$Lxq7qB(J@=)E)G473^8)oEYu{|kIFokf1z#b?fdGw{b4;9fS$ zhIlEvTen_M?%IGhMiUXcMg|a!TR#Dq$T@Nx7j-?z7)qbN~CAKiieBM=Gk&KdXt G2HpTL^h8Ji From 71cc4758db545601082920bf30ca9830b632e6ba Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 25 Mar 2026 15:30:08 -0400 Subject: [PATCH 5/7] Add .gitignore entries for macOS, editors, and environment files; enhance Makefile with detailed help target --- .gitignore | 13 ++++++++++++- Makefile | 27 ++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4f074fd..d411e17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,13 @@ +# macOS .DS_Store -**/.DS_Store + +# Editors +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# Environment +.env +.env.local diff --git a/Makefile b/Makefile index 487f153..3df6c8a 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,32 @@ INTEGRATION_BRANCH = dev include Makefile.agents.mk -.PHONY: check dev sync bump-patch bump-minor bump-major set-version deploy-preview deploy-prod +.DEFAULT_GOAL := help +.PHONY: help check dev sync bump-patch bump-minor bump-major set-version deploy-preview deploy-prod + +help: + @echo "Usage: make " + @echo "" + @echo "Sync" + @echo " check Validate skill placeholders against config" + @echo " dev Preview sync output (dry run)" + @echo " sync Regenerate adapter output from skills/" + @echo "" + @echo "Git workflow (from Makefile.agents.mk)" + @echo " branch name=X Create feature branch from $(INTEGRATION_BRANCH)" + @echo " pr Push and open PR targeting $(INTEGRATION_BRANCH)" + @echo " merge Merge current PR into $(INTEGRATION_BRANCH)" + @echo " abandon Discard changes and delete current feature branch" + @echo "" + @echo "Versioning" + @echo " bump-patch Bump patch (X.Y.Z+1), commit, and tag" + @echo " bump-minor Bump minor (X.Y+1.0), commit, and tag" + @echo " bump-major Bump major (X+1.0.0), commit, and tag" + @echo " set-version V=X Set explicit version, commit, and tag" + @echo "" + @echo "Deployment" + @echo " deploy-preview Push $(INTEGRATION_BRANCH) for preview/testing" + @echo " deploy-prod Push $(PRODUCTION_BRANCH) with tags" # Validate that all skill placeholders resolve against the config. check: From bc06d797e256d792588042042700c3c714e048e5 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 25 Mar 2026 16:00:59 -0400 Subject: [PATCH 6/7] Use skill descriptions as Claude adapter header instead of generic text Replace the uninformative "You are executing the /X skill. Your argument is: $ARGUMENTS" header with each skill's description from frontmatter. Also fix the release skill description to avoid an unresolved template variable ({{BRANCH_PROD}}) that would show as a literal in previews. Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/commands/qa.md | 5 ++--- .claude/commands/release.md | 5 ++--- .claude/commands/review.md | 4 ++-- .claude/commands/setup.md | 5 ++--- .claude/commands/ship.md | 5 ++--- .claude/commands/start.md | 4 ++-- .claude/commands/status.md | 4 ++-- .claude/commands/version.md | 5 ++--- adapters/claude/header.md | 2 +- skills/release.md | 2 +- 10 files changed, 18 insertions(+), 23 deletions(-) diff --git a/.claude/commands/qa.md b/.claude/commands/qa.md index bab055b..718a7bb 100644 --- a/.claude/commands/qa.md +++ b/.claude/commands/qa.md @@ -1,5 +1,4 @@ - -You are executing the `/qa` skill. Your argument is: $ARGUMENTS +View the QA queue or review a specific issue --- @@ -159,4 +158,4 @@ After applying labels, tell the user what was done: - Never post the comment without showing it to the user first. - Never apply labels without user confirmation (the confirmation in Step 3 is the single gate for both posting the comment and applying labels). - Never merge, deploy, or take any action beyond labeling and commenting. - + diff --git a/.claude/commands/release.md b/.claude/commands/release.md index c43395c..94a42be 100644 --- a/.claude/commands/release.md +++ b/.claude/commands/release.md @@ -1,5 +1,4 @@ - -You are executing the `/release` skill. Your argument is: $ARGUMENTS +Create a GitHub Release and promote the pre-production branch to production --- @@ -358,4 +357,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 ``. Run `make deploy-prod` to ship to production." - + diff --git a/.claude/commands/review.md b/.claude/commands/review.md index d21fb26..e6f32d2 100644 --- a/.claude/commands/review.md +++ b/.claude/commands/review.md @@ -1,4 +1,4 @@ -You are executing the `/review` skill. Your argument is: $ARGUMENTS +Run a standalone code review on a pull request --- @@ -54,4 +54,4 @@ After the review agent completes, relay its verdict to the user: - This command does not commit, push, or merge anything. It only reviews. - It may be called manually at any time, not just from `/ship`. - The review comment is posted to GitHub for the audit trail. - + diff --git a/.claude/commands/setup.md b/.claude/commands/setup.md index 99d3bee..a5180ee 100644 --- a/.claude/commands/setup.md +++ b/.claude/commands/setup.md @@ -1,5 +1,4 @@ - -You are executing the `/setup` skill. Your argument is: $ARGUMENTS +Detect setup state, guide first-time configuration, populate labels, and walk through optional config values --- @@ -470,4 +469,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri - Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. - Do not skip any human gate in Phase 2 or Phase 3 — each write requires confirmation. - If the user skips a config value, do not ask again. Move on. - + diff --git a/.claude/commands/ship.md b/.claude/commands/ship.md index bd287f3..074b96f 100644 --- a/.claude/commands/ship.md +++ b/.claude/commands/ship.md @@ -1,5 +1,4 @@ - -You are executing the `/ship` skill. Your argument is: $ARGUMENTS +Type-check, commit, open PR, review, and merge to the integration branch --- @@ -192,4 +191,4 @@ Report success based on mode: - When `ai` is `"off"`, skip the review agent entirely — merge immediately after checks pass. - In trunk mode, merges target `main`. Otherwise, `/ship` merges only to `dev` — never directly to `` or `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. - + diff --git a/.claude/commands/start.md b/.claude/commands/start.md index cdac11e..a3328f2 100644 --- a/.claude/commands/start.md +++ b/.claude/commands/start.md @@ -1,4 +1,4 @@ -You are executing the `/start` skill. Your argument is: $ARGUMENTS +Start a new feature or bugfix --- @@ -208,4 +208,4 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - `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 --remove-assignee @me`. - Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `` → omit (or create if `false` is `true`). Never apply a label not in `` unless `false` is `true`. Milestone resolution order: per-invocation flag → `` config → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. - + diff --git a/.claude/commands/status.md b/.claude/commands/status.md index 65c177c..8df3878 100644 --- a/.claude/commands/status.md +++ b/.claude/commands/status.md @@ -1,4 +1,4 @@ -You are executing the `/status` skill. Your argument is: $ARGUMENTS +Summarize in-progress and recently completed work from GitHub and git --- @@ -175,4 +175,4 @@ Do not post, comment, write files, or take any action. Output only. - 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. - + diff --git a/.claude/commands/version.md b/.claude/commands/version.md index be872a0..516dd78 100644 --- a/.claude/commands/version.md +++ b/.claude/commands/version.md @@ -1,5 +1,4 @@ - -You are executing the `/version` skill. Your argument is: $ARGUMENTS +Bump the project version, tag, and push — run before deploying to preview --- @@ -80,4 +79,4 @@ Report based on mode: - **Trunk mode** (`dev` empty): "Tagged vX.Y.Z. Run `/release` to create the GitHub Release." - **Two-branch mode** (`dev` set, `` empty): "Tagged vX.Y.Z. Run `make deploy-preview` to deploy to preview for testing. When testing is complete, run `/release`." - **Three-branch mode** (both set): "Tagged vX.Y.Z. Run `make deploy-preview` to deploy to staging. When testing is complete, run `/release`." - + diff --git a/adapters/claude/header.md b/adapters/claude/header.md index ed35b67..696a8e5 100644 --- a/adapters/claude/header.md +++ b/adapters/claude/header.md @@ -1,4 +1,4 @@ -You are executing the `/{skill}` skill. Your argument is: $ARGUMENTS +{description} --- diff --git a/skills/release.md b/skills/release.md index aae011f..09cad71 100644 --- a/skills/release.md +++ b/skills/release.md @@ -1,7 +1,7 @@ --- skill: release type: skill -description: "Create a GitHub Release; in two-branch and three-branch mode, also promotes the pre-production branch to `{{BRANCH_PROD}}`" +description: Create a GitHub Release and promote the pre-production branch to production args: none --- From 6063bfe1866f87485cb5297dd4338cb8e415c053 Mon Sep 17 00:00:00 2001 From: Sebastien Taggart Date: Wed, 25 Mar 2026 20:52:16 -0400 Subject: [PATCH 7/7] Add Codex/Gemini adapters, fix sync.sh and skills for cross-adapter compatibility - Add codex and gemini adapter configs and headers - Fix sync.sh: apply placeholder substitution to frontmatter description - Fix sync.sh: validate frontmatter placeholders in --validate mode - Fix sync.sh: support {{#if KEY}} conditional blocks for mode-aware rendering - Fix review/ship skills: fall back to inline review when sub-agents unsupported - Fix setup skill: check all adapter output dirs, not just .claude/commands - Fix Makefile: set INTEGRATION_BRANCH=dev, use variables for deploy targets, add help - Fix /version: pull before bumping to prevent stale-branch push failures - Fix /start: pull integration branch before creating feature branches - Update .gitignore with standard patterns, remove tracked .DS_Store - Add ROADMAP.md for future work tracking Issue #11 --- .DS_Store | Bin 6148 -> 0 bytes .agents/skills/qa/SKILL.md | 17 ++ .agents/skills/release/SKILL.md | 155 +++++++++++ .agents/skills/review/SKILL.md | 68 +++++ .agents/skills/setup/SKILL.md | 479 +++++++++++++++++++++++++++++++ .agents/skills/ship/SKILL.md | 180 ++++++++++++ .agents/skills/start/SKILL.md | 214 ++++++++++++++ .agents/skills/status/SKILL.md | 183 ++++++++++++ .agents/skills/version/SKILL.md | 81 ++++++ .claude/commands/qa.md | 158 +---------- .claude/commands/release.md | 251 ++--------------- .claude/commands/review.md | 12 +- .claude/commands/setup.md | 11 +- .claude/commands/ship.md | 54 ++-- .claude/commands/start.md | 20 +- .claude/commands/version.md | 13 +- .codecannon.yaml | 5 +- .cursor/rules/qa.mdc | 18 ++ .cursor/rules/release.mdc | 156 +++++++++++ .cursor/rules/review.mdc | 69 +++++ .cursor/rules/setup.mdc | 480 ++++++++++++++++++++++++++++++++ .cursor/rules/ship.mdc | 181 ++++++++++++ .cursor/rules/start.mdc | 215 ++++++++++++++ .cursor/rules/status.mdc | 184 ++++++++++++ .cursor/rules/version.mdc | 82 ++++++ .gemini/skills/qa/SKILL.md | 17 ++ .gemini/skills/release/SKILL.md | 155 +++++++++++ .gemini/skills/review/SKILL.md | 68 +++++ .gemini/skills/setup/SKILL.md | 479 +++++++++++++++++++++++++++++++ .gemini/skills/ship/SKILL.md | 180 ++++++++++++ .gemini/skills/start/SKILL.md | 214 ++++++++++++++ .gemini/skills/status/SKILL.md | 183 ++++++++++++ .gemini/skills/version/SKILL.md | 81 ++++++ .gitignore | 13 +- Makefile | 41 ++- ROADMAP.md | 21 ++ VERSION | 2 +- adapters/codex/config.yaml | 8 + adapters/codex/header.md | 9 + adapters/gemini/config.yaml | 8 + adapters/gemini/header.md | 9 + docs/adapters.md | 16 ++ skills/qa.md | 45 +-- skills/release.md | 113 +++++--- skills/review.md | 10 +- skills/setup.md | 8 +- skills/ship.md | 97 ++++--- skills/start.md | 61 +++- skills/version.md | 30 +- sync.sh | 66 ++++- 50 files changed, 4639 insertions(+), 581 deletions(-) delete mode 100644 .DS_Store create mode 100644 .agents/skills/qa/SKILL.md create mode 100644 .agents/skills/release/SKILL.md create mode 100644 .agents/skills/review/SKILL.md create mode 100644 .agents/skills/setup/SKILL.md create mode 100644 .agents/skills/ship/SKILL.md create mode 100644 .agents/skills/start/SKILL.md create mode 100644 .agents/skills/status/SKILL.md create mode 100644 .agents/skills/version/SKILL.md create mode 100644 .cursor/rules/qa.mdc create mode 100644 .cursor/rules/release.mdc create mode 100644 .cursor/rules/review.mdc create mode 100644 .cursor/rules/setup.mdc create mode 100644 .cursor/rules/ship.mdc create mode 100644 .cursor/rules/start.mdc create mode 100644 .cursor/rules/status.mdc create mode 100644 .cursor/rules/version.mdc create mode 100644 .gemini/skills/qa/SKILL.md create mode 100644 .gemini/skills/release/SKILL.md create mode 100644 .gemini/skills/review/SKILL.md create mode 100644 .gemini/skills/setup/SKILL.md create mode 100644 .gemini/skills/ship/SKILL.md create mode 100644 .gemini/skills/start/SKILL.md create mode 100644 .gemini/skills/status/SKILL.md create mode 100644 .gemini/skills/version/SKILL.md create mode 100644 ROADMAP.md create mode 100644 adapters/codex/config.yaml create mode 100644 adapters/codex/header.md create mode 100644 adapters/gemini/config.yaml create mode 100644 adapters/gemini/header.md diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 77b2f85cf327082eede91da81dd4c0a1e45d99ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5T4PZ0YU1`<354Vt6pM>XAgaW8e38!F@?5x&9mqOc&Hbjzz6aL{AOpW zh6eE@qBCLl+nt@C%?H`d5D^bolL1kmh!QkGmO+Pzx^rpAjQ4;XW8Bh`7G*IQUDq9f zexpkEzE3A~MFmai)BdZuZujhI+oGzoEHCREE6LIF;d1@>a^7V%`G(beCTwl8jS+?p zv7dr&z~ziql+!78x4<(McB}d$>V5fow|I0OHf0o>Ul$$_Hx z&VV!E47@WS=R-gfObr{wcyyqPB>=Dobrg)HmJpv{m>M>USb?y*0@anR#9(!YKA2r< z*eI$yu@xU|nK#7?$Lxq7qB(J@=)E)G473^8)oEYu{|kIFokf1z#b?fdGw{b4;9fS$ zhIlEvTen_M?%IGhMiUXcMg|a!TR#Dq$T@Nx7j-?z7)qbN~CAKiieBM=Gk&KdXt G2HpTL^h8Ji diff --git a/.agents/skills/qa/SKILL.md b/.agents/skills/qa/SKILL.md new file mode 100644 index 0000000..27fc421 --- /dev/null +++ b/.agents/skills/qa/SKILL.md @@ -0,0 +1,17 @@ +--- +name: qa +description: View the QA queue or review a specific issue +--- + +> **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. + +--- + +> **QA workflow is not configured.** `/qa` requires `QA_READY_LABEL` to be set in `.codecannon.yaml` so it can find issues waiting for QA. +> +> To enable: add `QA_READY_LABEL: ready-for-qa` (or your preferred label name) to `.codecannon.yaml` and re-run `CodeCanon/sync.sh`. +> +> Note: In trunk mode, `/ship` does not apply this label automatically — you would need to apply it manually or via a separate workflow. + +Do not proceed. Stop here. + diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md new file mode 100644 index 0000000..4935cee --- /dev/null +++ b/.agents/skills/release/SKILL.md @@ -0,0 +1,155 @@ +--- +name: release +description: Create a GitHub Release; in multi-branch mode, also promotes the pre-production branch to `main` +--- + +> **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 `/release` does + +Creates a GitHub Release and promotes `dev` to `main`. Run this after preview testing is confirmed. + +--- + +## Step 1 — Verify state + +Check the current branch: +```bash +git branch --show-current +``` + +### Verify on `dev` with tag + +If not on `dev`, switch to it: +```bash +git checkout dev && git pull +``` + +Verify a version tag exists on HEAD: +```bash +git describe --exact-match --tags HEAD 2>/dev/null +``` + +If no tag is found, warn: "No version tag found on HEAD. Run `/version` first to tag this release before promoting it." Stop unless the user explicitly says to proceed anyway. + +--- + +## Step 2 — Compute what's being promoted + +Find all merge commits in `dev` not yet in `main`: +```bash +git log main..dev --merges --pretty=format:"%s" +``` + +Parse PR numbers from the merge commit subjects. GitHub's format is: +`Merge pull request #N from branch/name` + +For each PR number found, retrieve the PR body: +```bash +gh pr view --json number,title,body +``` + +Extract `Issue #N` references from PR bodies (look for the pattern `Issue #\d+`). + +Compile: +- List of PRs being promoted (number + title) +- List of open issues linked to those PRs + +--- + +## Step 3 — HUMAN GATE + +Show the user the release summary. Example format: + +``` +Ready to release vX.Y.Z to production. + +PRs included: + #17 — Add /docs directory + #18 — Fix checkout runtime error + +Issues that will close: + #14 — Add /docs directory + #15 — Fix checkout runtime error + +Have you tested all of the above on preview? Type 'release' to confirm. +``` + +Wait for the user to type "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change. + +--- + +## Step 4 — Create PR: `dev` → `main` + +```bash +gh pr create --base main --head dev \ + --title "Release vX.Y.Z" \ + --body "$(cat <<'EOF' +Release vX.Y.Z + +PRs included: +- #17 — Add /docs directory +- #18 — Fix checkout runtime error + +Closes #14 +Closes #15 +EOF +)" +``` + +Note the PR number from the output. + +The `Closes #N` lines will auto-close the linked issues because this PR merges into `main` (the default branch). + +--- + +## Step 5 — Merge + +Do NOT use `make merge` — it refuses PRs targeting `main`. Use `gh pr merge` directly: + +```bash +gh pr merge --merge +``` + +--- + +## Step 6 — Create GitHub Release + +The version tag (from Step 1) and the PR/issue list (from Step 2) are already known. Find the previous tag to build the changelog link: + +```bash +git describe --abbrev=0 ^ 2>/dev/null +``` + +If no previous tag exists, omit the "Full changelog" line. + +Create the release: + +```bash +gh release create \ + --title "" \ + --notes "$(cat <<'EOF' +## Changes + +- # (PR #) +[... one line per PR included in this release ...] + +**Full changelog:** https://github.com///compare/... +EOF +)" +``` + +Format each PR line as `- # (PR #)`. If a PR had no linked issue, omit the `#` prefix and use just the PR title. + +After the command runs, note the release URL from the output. + +--- + +## Step 7 — Report + +Tell the user: + +> "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at ``. Run `make deploy-prod` to ship to production." + diff --git a/.agents/skills/review/SKILL.md b/.agents/skills/review/SKILL.md new file mode 100644 index 0000000..837c7dc --- /dev/null +++ b/.agents/skills/review/SKILL.md @@ -0,0 +1,68 @@ +--- +name: review +description: Run a standalone code review on a pull request +--- + +> **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 `/review` does + +`/review` runs a standalone code review on a PR. It wraps `.claude/review-agent-prompt.md`. + +--- + +## Pre-flight + +If `ai` is `"off"`, say: + +> "Code review is disabled for this project (REVIEW_GATE is set to 'off' in .codecannon.yaml). To enable reviews, set REVIEW_GATE to 'ai' or 'advisory' and re-run CodeCanon/sync.sh." + +Do not proceed. + +--- + +## Step 1 — Identify the PR + +If `$ARGUMENTS` is a number, use it as the PR number. + +If `$ARGUMENTS` is empty, detect the current branch's open PR: +``` +gh pr view --json number --jq '.number' +``` + +If no PR is found, abort and say: "No open PR found for the current branch. Pass a PR number explicitly: `/review `" + +--- + +## Step 2 — Run the review + +Load `.claude/review-agent-prompt.md` and perform the review for the PR number. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: +1. Read the PR diff via `gh pr diff ` +2. Read any files needed for full context +3. Post findings as a PR comment via `gh pr comment ` + +--- + +## Step 3 — Report verdict + +After the review agent completes, relay its verdict to the user: + +- **APPROVE** → "Review passed. Run `/ship` to merge (or it may already be merged if called from `/ship`)." +- **REQUEST CHANGES** → Surface the CRITICAL findings. Say: "Fix the issues above and run `/review` again before merging." + +--- + +## Important + +- This command does not commit, push, or merge anything. It only reviews. +- It may be called manually at any time, not just from `/ship`. +- The review comment is posted to GitHub for the audit trail. + diff --git a/.agents/skills/setup/SKILL.md b/.agents/skills/setup/SKILL.md new file mode 100644 index 0000000..0f3b29c --- /dev/null +++ b/.agents/skills/setup/SKILL.md @@ -0,0 +1,479 @@ +--- +name: setup +description: Detect setup state, guide first-time configuration, populate labels, and walk through optional config values +--- + +> **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. + +--- + +## Detect state + +Before taking any action, determine which state the project is in. Run these checks in order. + +**Check A — Is this the Code Cannon skill library repo itself?** + +```bash +test -f sync.sh && test -d skills +``` + +If both exist at the working directory root → **go to State 1**. + +**Check B — Is there any Code Cannon submodule presence?** + +```bash +test -d CodeCanon +``` + +```bash +test -f .gitmodules && grep -q CodeCanon .gitmodules +``` + +If either is true → **go to State 2**. + +**Check C — State 1 fallback** + +If `.codecannon.yaml` is absent AND neither `CodeCanon/` nor `.gitmodules` exist → **go to State 1**. + +Otherwise → **go to State 2**. + +--- + +## State 1 — Just checking it out + +Do not configure anything. Do not touch any file. + +Tell the user warmly that Code Cannon is designed to live as a submodule inside another project — running `/setup` here in the Code Cannon repo itself isn't the intended path. + +Offer two forward paths and ask which they want: + +**Path A — "I want to understand how Code Cannon works"** + +Explain the three-layer model: +- **Skills** (`skills/*.md`) — portable workflow instructions with `main`-style tokens for project-specific values (see `config.schema.yaml`) +- **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 | +| `/ship` | Checks, commits, opens PR, spawns review agent, merges | +| `/review` | Standalone code review on any PR | +| `/version` | Bumps semver, tags, pushes | +| `/release` | Promotes integration branch to main, closes issues | +| `/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. + +**Path B — "I want to add Code Cannon to my project"** + +Show the exact command sequence: + +```bash +cd /path/to/your-project +git submodule add https://github.com/LightbridgeLab/CodeCanon.git CodeCanon +git submodule update --init +cp CodeCanon/templates/codecannon.yaml .codecannon.yaml +# Edit .codecannon.yaml — set branch names, commands, adapters +CodeCanon/sync.sh +``` + +Do not touch any file. The user runs these commands in their project directory. + +--- + +## State 2 — Partial or broken setup + +Run checks 1–7 in order. Stop at the first failing check and address it. After describing the fix, tell the user to run `/setup` again once they've resolved it. Do not continue past a failing check. + +### Check 1 — CodeCanon/sync.sh present + +```bash +test -f CodeCanon/sync.sh +``` + +If missing: the submodule was added to `.gitmodules` or `CodeCanon/` exists as an empty directory, but it hasn't been initialized. Show: + +```bash +git submodule update --init --recursive +``` + +Offer to run it. If the user agrees, run it. If they decline, tell them to run it manually before continuing. Stop. + +### Check 2 — gh installed + +```bash +which gh +``` + +If not found: "`gh` is required by all Code Cannon skills. Install it with `brew install gh` (macOS) or from https://cli.github.com." Cannot proceed without it. Stop. + +### Check 3 — gh authenticated + +```bash +gh auth status +``` + +If exit code is non-zero: "You're not authenticated with GitHub." Show: + +```bash +gh auth login +``` + +Cannot proceed without it. Stop. + +### Check 4 — Inside a GitHub repository + +```bash +gh repo view --json name +``` + +If exit code is non-zero: warn that most skills require a GitHub remote. Skills can be read and configured, but `/start`, `/ship`, `/review`, `/release`, and `/status` will fail without one. This is not a hard stop — ask if the user wants to continue configuring anyway. + +### Check 5 — .codecannon.yaml present + +```bash +test -f .codecannon.yaml +``` + +If missing: "I'll create `.codecannon.yaml` from the template — you'll want to review the branch names and commands before running sync." + +Show: + +```bash +cp CodeCanon/templates/codecannon.yaml .codecannon.yaml +``` + +Ask permission to run it. If the user agrees, run it. If they decline, tell them to run it manually. + +After creating the file, proceed immediately to Check 5b. + +### Check 5b — Profile selection (only on first setup) + +This check runs only when Check 5 just created `.codecannon.yaml` from the template. If `.codecannon.yaml` already existed before this `/setup` invocation, skip to Check 6. + +Ask the user: + +> "What level of process does this project need?" +> +> **1. Lightweight** — Fast iteration. AI review is advisory, features merge to main, no QA workflow. +> +> **2. Standard** — Integration branch with AI-gated review. QA and milestones available but not required. +> +> **3. Governed** — Full traceability. QA handoff, assigned reviewers, milestones, structured labels. +> +> **4. Custom** — Configure each setting individually. +> +> Pick a number, or describe your workflow and I'll recommend one. + +Wait for response. If the user describes their situation instead of picking a number, recommend the best-fit profile and confirm before applying. + +**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: `, `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. + +**Standard:** +- "What's your production branch name?" (default: `main`) +- "What's your integration branch name?" (default: `development`) +- Write: `BRANCH_PROD: `, `BRANCH_DEV: `, `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. + +**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: `. +- Write: `BRANCH_PROD: `, `BRANCH_DEV: `, `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. + +**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. + +### Check 6 — .codecannon.yaml stale values + +Read `.codecannon.yaml`. Apply the following checks conservatively — only flag a value if you are confident it points to something that does not exist: + +- If `VERSION_READ_CMD` references `package.json` and no `package.json` exists at the project root → flag it +- If `BRANCH_DEV` is set to a non-empty value and that branch does not appear in `git branch -a` → flag it +- If `BRANCH_TEST` is set to a non-empty value and that branch does not appear in `git branch -a` → flag it + +If nothing is confidently broken, do not flag anything. When in doubt, do not flag. + +If anything is flagged: show the specific key names and what they should likely be changed to. Do not modify the file. Tell the user to update these values and run `/setup` again. Stop. + +### Check 7 — Generated skill output present + +Check whether sync.sh has been run by looking for any of the adapter output directories configured in `.codecannon.yaml`: + +```bash +test -d .claude/commands || test -d .cursor/rules || test -d .agents/skills || test -d .gemini/skills +``` + +If none exist: "sync.sh hasn't been run yet — the skill commands don't exist." + +Show: + +```bash +CodeCanon/sync.sh +``` + +Ask permission to run it. If the user agrees, run it. If they decline, tell them to run it manually before continuing. Stop. + +--- + +## State 3 — Everything configured + +All checks pass. Run phases 1–4 in order. + +--- + +### Phase 1 — Health summary + +Print one sentence confirming the setup looks healthy. Read `.codecannon.yaml` and infer the workflow profile for display: + +- `REVIEW_GATE` is `"advisory"` or `"off"` AND `BRANCH_DEV` is empty → **Lightweight** +- `REVIEW_GATE` is `"ai"` AND `BRANCH_DEV` is set AND `QA_READY_LABEL` is empty → **Standard** +- `REVIEW_GATE` is `"ai"` AND `QA_READY_LABEL` is set → **Governed** +- Anything else → **Custom** + +Check whether the configured dev/test branches exist in the remote (skip checks for empty values): + +```bash +git branch -a | grep -q "remotes/origin/" +git branch -a | grep -q "remotes/origin/" +``` + +Display: + +``` +Setup looks healthy. Profile: + + BRANCH_PROD: + BRANCH_DEV: (exists in remote: yes/no/not set) + BRANCH_TEST: (exists in remote: yes/no/not set) + REVIEW_GATE: + CHECK_CMD: + MERGE_CMD: + Adapters: + + Optional config: + DEFAULT_MILESTONE — set / unset + DEFAULT_REVIEWERS — set / unset + TICKET_LABELS — set (N labels) / unset + TICKET_LABEL_CREATION_ALLOWED — set / unset + QA_READY_LABEL — set / unset + PLATFORM_COMPLIANCE_NOTES — set / unset + CONVENTIONS_NOTES — set / unset +``` + +A value counts as "set" if it is present, uncommented, and non-empty in `.codecannon.yaml`. + +--- + +### Phase 2 — Label population + +Run: + +```bash +gh label list --limit 100 --json name,color,description +``` + +If zero labels are found, treat this as a greenfield repository and offer a starter label baseline before asking about `TICKET_LABELS`. + +Show this recommendation: + +``` +No labels were found. For new projects, a practical baseline is: + - bug + - enhancement + - chore + - documentation + - ready-for-qa + - qa-passed + - qa-failed +``` + +Ask: **"Create any missing labels from this baseline now? (yes/no)"** + +Wait for response. + +- **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. + +Display the results as a numbered list: + +``` +Available labels (N found): + 1. bug — Something isn't working + 2. enhancement — New feature or request + 3. good first issue — Good for newcomers + ... +``` + +Ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** + +Wait for the user's response. + +- **yes** → use all labels +- **numbers** (e.g. `1,3,5`) → use only those labels +- **no / skip / anything else** → skip this phase, continue to Phase 3 + +Show the exact change before writing: + +``` +I'll update .codecannon.yaml with: + + TICKET_LABELS: "bug,enhancement,..." + +Proceed? (yes/no) +``` + +Wait for confirmation. Write only on yes. + +--- + +### Phase 3 — Optional config walkthrough (profile-aware) + +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 `/ship` 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')" + +**TICKET_LABEL_CREATION_ALLOWED** (Standard, Governed, and Custom) + +"Controls whether `/start` can create new GitHub labels on the fly when none in the pool fit the task. Defaults to false." + +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: | + - + - + - + +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: | + - + - + +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. + +--- + +### Phase 4 — Team sharing + +After completing or skipping the config walkthrough, say: + +"To share this setup with your team, commit these files. Anyone who clones the project and runs `git submodule update --init` will have all skills ready — no further setup needed." + +Show the exact command: + +```bash +git add .codecannon.yaml .claude/ CodeCanon AGENTS.md +``` + +Add a note: `/start` can be used to create well-formed GitHub issues without writing any code — useful for non-developers tracking work. `/status` generates standup summaries from open issues and PRs — both are valuable outside of a development workflow. + +--- + +## Hard rules + +- Only modify `.codecannon.yaml`. Do not touch any other file (except running `CodeCanon/sync.sh`, which modifies `.claude/commands/` — permitted only with explicit user approval). +- Do not run `sync.sh` without explicit user permission. +- Do not create `.codecannon.yaml` without explicit user permission. +- Do not report a configuration problem unless confident the condition is genuinely broken. Prefer false negatives over false positives on all diagnostic checks. +- Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. +- Do not skip any human gate in Phase 2 or Phase 3 — each write requires confirmation. +- If the user skips a config value, do not ask again. Move on. + diff --git a/.agents/skills/ship/SKILL.md b/.agents/skills/ship/SKILL.md new file mode 100644 index 0000000..56cd05a --- /dev/null +++ b/.agents/skills/ship/SKILL.md @@ -0,0 +1,180 @@ +--- +name: ship +description: Type-check, commit, open PR, review, and merge to the integration branch +--- + +> **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 `/ship` does + +`/ship` is Phase 3 of the workflow: type-check, commit, open PR, spawn review agent, act on verdict. + +--- + +## Step 1 — Verify branch + +Check current branch: +``` +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 ``. `/ship` must be run from a feature branch. Switch to your feature branch first." + +--- + +## Step 2 — Type-check gate + +Run: +``` +make check +``` + +If errors are reported, **stop**. Report the errors to the user and say: + +> "Check failed. Fix the errors above before shipping." + +Do not proceed until `make check` passes cleanly. + +--- + +## Step 3 — Identify linked issue + +Check for a linked issue by inspecting the branch name (should follow `feature/` linked via `gh issue develop`) or by running: +``` +gh pr view --json number,body 2>/dev/null +``` + +If a linked issue number is identifiable, note it for the PR body. If not identifiable, proceed without it but mention this to the user. + +--- + +## Step 4 — Commit + +Stage all changes and commit: +``` +git add -A +git commit -m "" +``` + +Commit message rules: +- Imperative mood ("Add X", "Fix Y", "Remove Z") +- Concise but meaningful — describes what changed and why in one line +- No `.env` files, build artifacts, `node_modules`, or secrets + +--- + +## Step 5 — Push and open PR + +First, push the branch: +``` +git push -u origin HEAD +``` + +Next, check for a CODEOWNERS file: +``` +git ls-files CODEOWNERS .github/CODEOWNERS docs/CODEOWNERS 2>/dev/null +``` + +If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHub will automatically request reviews from code owners." + +PR target branch: `dev` + +Use `Issue #` as the issue reference — the issue stays open until `/release` promotes to `main`. + +Then create the PR with explicit title and body (never use an interactive editor): +``` +gh pr create --base --title "" --body "$(cat <<'EOF' +<description of what changed and why> + +<Closes #N OR Issue #N, based on target above> +EOF +)" +``` + + +**Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. + +Omit the issue line entirely if no linked issue was identified in Step 3. + +--- + +## Step 6 — Review (conditional) + +If `ai` is `"off"`, skip directly to Step 7 (merge without review). + +Otherwise, load `.claude/review-agent-prompt.md` and perform the review for this PR. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: +1. Read the PR diff +2. Read relevant files for context +3. Post findings as a PR comment via `gh pr comment <number>` + +Wait for the review to complete and report its verdict. + +--- + +## Step 7 — Act on verdict + +Merge command (used by all paths below): `make merge` + +--- + +**If `ai` is `"off"` (review skipped):** + +Run the merge command. Apply QA label and report success (see below). + +--- + +**If `ai` is `"advisory"`:** + +Report the review findings to the user. Then merge regardless — treat as APPROVE. + +If the review contained CRITICAL findings, note: + +> "Review flagged issues (see PR comment) but advisory mode is enabled — merged anyway. Review the findings when convenient." + +Apply QA label and report success (see below). + +--- + +**If `ai` is `"ai"` (default):** + +**If APPROVE (no CRITICAL findings):** Run the merge command. Apply QA label and report success (see below). + +**If REQUEST CHANGES (at least one CRITICAL finding):** Report the findings to the user. Do NOT merge. Say: + +> "The review found blocking issues (see above). Fix them and run `/ship` again." + +Return to the coding loop. When fixed, run `/ship` again from Step 1. + +--- + +### After merge — QA label and success report + + +Report success based on mode: +"PR merged. Issues stay open until testing confirms the fix. Run `make deploy-preview` when ready to deploy to preview." + +--- + +## Important constraints + +- Never skip `make check`. A failed check is a hard stop. +- When `ai` is `"ai"`, never merge if the review verdict is REQUEST CHANGES. +- When `ai` is `"advisory"`, always merge after review completes, regardless of verdict. +- When `ai` is `"off"`, skip the review agent entirely — merge immediately after checks pass. +- `/ship` merges only to `dev` — never directly to `main`. +- If `make merge` fails for any reason, report it and stop — do not attempt workarounds. +<!-- generated by CodeCanon/sync.sh | skill: ship | adapter: codex | hash: a7a0480e | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.agents/skills/start/SKILL.md b/.agents/skills/start/SKILL.md new file mode 100644 index 0000000..017c5a5 --- /dev/null +++ b/.agents/skills/start/SKILL.md @@ -0,0 +1,214 @@ +--- +name: start +description: Start a new feature or bugfix +--- + +> **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. + +--- + +## CRITICAL: Order of operations + +**You must complete Steps 1–4 before writing any code.** +Do not open any source file with intent to edit until `git branch --show-current` shows a `feature/*` branch. + +--- + +## Determine case + +If `$ARGUMENTS` is a number (digits only) → go to **Case B: Resume existing issue**. +Otherwise → go to **Case A: New work**. + +--- + +## Parsing $ARGUMENTS (Case A only) + +> Skip this entirely if `$ARGUMENTS` triggered Case B. + +The argument string may contain optional inline flags after the description. Parse as follows: + +1. **Identify flags** — scan for the first token that starts with `--label`, `-l`, `--milestone`, or `-m`. Everything before it is the **description**. Everything from the first flag onward is **flags**. +2. **`--label <value>` / `-l <value>`** — comma-separated label string (e.g. `bug` or `enhancement,ux`). If provided, it **bypasses label auto-selection entirely** for this invocation — use the value verbatim. Labels containing spaces must be quoted (e.g. `--label "good first issue"`). +3. **`--milestone <value>` / `-m <value>`** — milestone name or number (e.g. `Sprint 4` or `12`). Pass the value as-is; GitHub accepts both names and numbers. +4. **Flags may appear in any order** after the description. + +**Label resolution (three-tier, Case A only):** + +After parsing flags, determine the active labels in this order: + +1. **Per-invocation flag** — if `--label <value>` was in `$ARGUMENTS`, use that value verbatim. Skip all remaining steps. +2. **Pool-based selection** — no label pool is configured. Fall through to step 3. +3. **No label / creation** — if the pool is empty or no pool label fits: + - If `false` is `true` (case-insensitive string match): the agent **may** create a new label before applying it: + ```bash + gh label create "<name>" --color "<hex>" --description "<short description>" + ``` + Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. + - If `false` is `false` or unset: omit `--label` entirely. Proceed silently; do not inform the user. + +> **Tip:** Run `/setup` to populate TICKET_LABELS from your repo's existing GitHub labels. + +**Milestone resolution (three-tier, Case A only):** + +After parsing flags, determine the active milestone in this order: + +1. **Per-invocation flag** — if `--milestone <value>` was in `$ARGUMENTS`, use that value. Stop. +3. **Auto-detect** — if no milestone is resolved yet, query open milestones: + ```bash + gh api repos/{owner}/{repo}/milestones --jq '[.[] | select(.state=="open")] | {count: length, milestones: [.[] | {number: .number, title: .title}]}' + ``` + Use `gh repo view --json owner,name` first if the owner/repo are not already known. + - **0 results** → no milestone; proceed without `--milestone`. + - **1 result** → use its title silently. Inform the user inline: `(milestone: <title>)`. + - **2+ results** → show the numbered list, ask once: **"Multiple open milestones — which should this issue go under? (enter a number or title, or 'none')"**. Accept milestone number, title, or "none"/"skip". Wait for response before continuing. + +**Examples:** + +| `$ARGUMENTS` | Description | Labels | Milestone | +|---|---|---|---| +| `Add dark mode toggle to settings page` | `Add dark mode toggle to settings page` | none (no label pool) | auto-detected | +| `Add dark mode --label enhancement` | `Add dark mode` | `enhancement` (verbatim) | auto-detected | +| `Add dark mode --label enhancement,ux --milestone "Sprint 4"` | `Add dark mode` | `enhancement,ux` (verbatim) | `Sprint 4` | + +> Replace vs append: flags **replace** auto-selection entirely, they do not append. This avoids silent label duplication and milestone conflicts. + +--- + +## Case A: New work (text description) + +### Step 1 — Investigate + +Read the relevant code. Propose a concrete implementation approach. Be specific about which files change and how. + +### Step 2 — HUMAN GATE + +Say exactly: **"Does this approach sound right? I'll create a GitHub issue and branch before writing any code."** + +Stop. Wait for the user to confirm. + +- User says yes → continue to Step 3. +- User redirects → revise approach, ask again. +- User abandons → stop. Nothing to clean up. + +### Step 3 — Create GitHub Issue + +Run `gh issue create` with explicit flags (do NOT open an interactive editor): + +```bash +gh issue create \ + --title "<standalone full sentence — must make sense with no context>" \ + --body "<human-readable explanation: what the problem is, why it matters, general approach — written for a non-developer, no code or file paths>" \ + --assignee @me \ + [--label "<resolved labels>"] \ + [--milestone "<resolved milestone>"] +``` + +Resolve labels and milestone using the resolution steps in the Parsing section above: +- **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. +- **Milestone**: use the value from three-tier milestone resolution. If non-empty, add `--milestone "<value>"` to the command. If empty (no flag, no config default, no open milestones), omit `--milestone` entirely. + +**Title rules:** +- ✅ `Fix 'Contact Us' footer link pointing to 404 instead of /contact-us` +- ❌ `Fix broken link` + +After the command runs, note the issue number from the output URL (e.g. `https://github.com/.../issues/42` → issue `42`). + +Show the user: `Created issue #<number>: <title>` + +Then immediately post agent implementation notes as a comment: + +```bash +gh issue comment <number> --body "## Agent Implementation Notes + +<full technical plan: exact files to change, approach, key decisions, edge cases>" +``` + +### Step 4 — Create feature branch + +```bash +gh issue develop <number> --name feature/<short-descriptive-name> --checkout +``` + +Verify the branch was created: + +```bash +git branch --show-current +``` + +Show the user: `On branch feature/<name>` + +**Do not proceed to Step 5 until this shows a `feature/*` branch.** + +### Step 5 — Write the code + +Now write the code. Do NOT commit anything. + +When done, say: **"The code is ready for review. Please run `make dev` and test locally. Let me know if it looks good, needs changes, or should be scrapped."** + +- User says looks good → run `/ship` +- User requests changes → iterate, repeat this message +- User says scrap it → run `make abandon` + +--- + +## Case B: Resume existing issue (numeric argument) + +### Step 1 — Load context + +```bash +gh issue view <number> --comments +``` + +Read the full body and all comments. Note: what was done, what remains, branch status. + +### Step 2 — Summarize and gate + +Tell the user: +- What the issue is about +- What was previously done (from agent notes if present) +- What appears to remain + +Ask: **"Does this match your understanding? Continue this ticket, or open a fresh one?"** + +- Continue → Step 3. +- New ticket → restart as Case A with a new description. + +### Step 3 — Check out branch + +Find and check out the existing branch, or create a new one linked to the issue: + +```bash +gh issue develop <number> --name feature/<short-name> --checkout +``` + +Verify: + +```bash +git branch --show-current +``` + +Post a resumption comment: + +```bash +gh issue comment <number> --body "Resuming work. <brief note on what's being continued.>" +``` + +### Step 4 — Write the code + +Continue from where work left off. Do NOT commit. + +When done, say: **"The code is ready for review. Please run `make dev` and test locally."** + +--- + +## Hard rules + +- 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 `/ship`. +- If already on a feature branch when `/start` is invoked, warn the user before creating another branch. +- `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 labels only when explicitly provided via `--label`. No label pool is configured. +- Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. +<!-- generated by CodeCanon/sync.sh | skill: start | adapter: codex | hash: 1f53b6e5 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.agents/skills/status/SKILL.md b/.agents/skills/status/SKILL.md new file mode 100644 index 0000000..2b16a40 --- /dev/null +++ b/.agents/skills/status/SKILL.md @@ -0,0 +1,183 @@ +--- +name: status +description: Summarize in-progress and recently completed work from GitHub and git +--- + +> **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. + +--- + +## Step 1 — Parse arguments + +First, check whether `$ARGUMENTS` contains `--milestone` or `--sprint` (they are identical aliases). + +**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. + +Examples: +- `--milestone Sprint 4` → milestone name = `Sprint 4` +- `--sprint Sprint 4` → milestone name = `Sprint 4` +- `--milestone Q2 Release` → milestone name = `Q2 Release` +- `--milestone 12` → milestone name = `12` + +**Personal mode** (no `--milestone` / `--sprint` 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. + +No argument → subject = `@me`, lookback = `7`. + +--- + +## Step 2 — Fetch GitHub data (run all three 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 +``` + +**Recently merged PRs (last `<lookback>` days):** +```bash +gh pr list --author <subject> --state merged --limit 20 \ + --json number,title,url,mergedAt,labels,baseRefName +``` +Filter the results to keep only entries where `mergedAt` is within the last `<lookback>` days. + +**Open issues assigned to subject:** +```bash +gh issue list --assignee <subject> --state open \ + --json number,title,url,labels,milestone +``` + +If any `gh` command exits with a non-zero status (including auth errors), report the error message and stop. Do not retry. + +--- + +## Step 3 — Fetch local git context + +Check if the current directory is inside a git repository: +```bash +git rev-parse --is-inside-work-tree 2>/dev/null +``` + +If yes, run: +```bash +git log --oneline --since="<lookback> days ago" +``` + +If not inside a git repo, skip this step and note it was skipped in the output. + +--- + +## Step 4 — Classify items + +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). + +An open issue that IS linked from an open PR body appears under "In progress" alongside that PR, not under "Up next". + +--- + +## Step 5 — Output the summary + +Print a formatted summary. Use this structure: + +``` +## Status for <subject> — last <lookback> days + +### In progress +- #<number> <title> [<labels>] [<milestone>] + PR: <url> + Linked issue: #<number> (if found) + +### Done +- #<number> <title> [<labels>] — merged <date> + PR: <url> + +### Up next +- #<number> <title> [<labels>] [<milestone>] + Issue: <url> + +--- +Local commits (current branch): +<git log output, or "skipped — not in a git repo"> +``` + +Rules: +- 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.` + +Do not post, comment, write files, or take any action. Output only. + +--- + +## Milestone mode (Steps M1–M3) + +Only entered when `--milestone` or `--sprint` is detected in Step 1. + +### Step M1 — Fetch milestone issues + +```bash +gh issue list --milestone "<name>" --state all --limit 200 \ + --json number,title,state,labels,assignees,url +``` + +If this command fails for any reason (milestone not found, auth error, etc.), report the error and stop. + +### Step M2 — Classify issues + +Fetch all open PRs to detect which issues are in progress: + +```bash +gh pr list --state open --json number,title,body,baseRefName +``` + +Group issues into three buckets: + +- **Done** — `state: closed` +- **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 + +### Step M3 — Output the summary + +``` +## Sprint: <name> + +<Y> of <total> issues closed · <Z> in progress · <W> not started + +### In progress (<Z>) +- #<number> <title> [@<assignee>] [<milestone>] + <url> + +### Not started (<W>) +- #<number> <title> [@<assignee>] + +### Done (<Y>) +- #<number> <title> +``` + +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 +- If a section has no items, omit it entirely + +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). +- Never suggest what the developer should work on next. +- 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 CodeCanon/sync.sh | skill: status | adapter: codex | hash: a0b51014 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.agents/skills/version/SKILL.md b/.agents/skills/version/SKILL.md new file mode 100644 index 0000000..557f359 --- /dev/null +++ b/.agents/skills/version/SKILL.md @@ -0,0 +1,81 @@ +--- +name: version +description: Bump the project version, tag, and push — run before deploying to preview +--- + +> **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. + +--- + +## Step 1 — Verify branch + +Run: +```bash +git branch --show-current +``` + +Required branch: `dev` (two-branch mode). + +If not on the required branch, abort and say: "Switch to `<required-branch>` before running `/version`." + +Pull the latest changes before proceeding: +```bash +git pull +``` + +--- + +## Step 2 — Read current version + +```bash +cat VERSION +``` + +--- + +## Step 3 — Ask the user + +Tell the user (filling in the actual computed values): + +> "You are on version X.Y.Z. Do you want to bump: +> - **major** → A.0.0 +> - **minor** → X.B.0 +> - **patch** → X.Y.C +> - or set a specific version?" + +Wait for their response. + +--- + +## Step 4 — Interpret and run + +Map the user's natural language response to a command: + +| User says | Run | +|---|---| +| "patch" / anything mentioning patch | `make bump-patch` | +| "minor" | `make bump-minor` | +| "major" | `make bump-major` | +| A specific version e.g. "2.4.5" | `make set-version V= 2.4.5` | + +These targets update the version manifest, create a git commit, and create a git tag. Do not create commits or tags manually. + +--- + +## Step 5 — Push + +```bash +git push +git push --tags +``` + +Both the version bump commit and the tag must be pushed. + +--- + +## Step 6 — Report + +Tell the user the new version and tag: + +"Tagged vX.Y.Z. Run `make deploy-preview` to deploy to preview for testing. When testing is complete, run `/release`." +<!-- generated by CodeCanon/sync.sh | skill: version | adapter: codex | hash: b719359b | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.claude/commands/qa.md b/.claude/commands/qa.md index bab055b..8e37bfc 100644 --- a/.claude/commands/qa.md +++ b/.claude/commands/qa.md @@ -1,162 +1,12 @@ -<!-- generated by CodeCanon/sync.sh | skill: qa | adapter: claude | hash: 447216d6 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> You are executing the `/qa` skill. Your argument is: $ARGUMENTS --- -<!-- Mode note: The QA label workflow is primarily used in two-branch mode, where `` - is applied by `/ship` after merging to `dev`. In trunk or three-branch mode, this label - is not applied automatically by `/ship` — set `` only if you have a manual or - future-skill-driven QA gate. --> - -## What `/qa` does - -`/qa` has two modes: - -- **No argument** — show all issues awaiting QA. -- **Numeric argument** — walk through QA review for a specific issue. - ---- - -## Pre-flight check - -If `` is empty, say: - -> "The QA label workflow is not configured for this project. `/qa` requires `QA_READY_LABEL` to be set in `.codecannon.yaml` so it can find issues waiting for QA. +> **QA workflow is not configured.** `/qa` requires `QA_READY_LABEL` to be set in `.codecannon.yaml` so it can find issues waiting for QA. > > To enable: add `QA_READY_LABEL: ready-for-qa` (or your preferred label name) to `.codecannon.yaml` and re-run `CodeCanon/sync.sh`. > -> Note: In trunk mode, `/ship` does not apply this label automatically — you would need to apply it manually or via a separate workflow." - -Do not proceed past this point if the label is empty. - ---- - -## Mode A — No argument: QA queue view - -Run: -``` -gh issue list --label "" --state open \ - --json number,title,url,labels,milestone,assignees -``` - -Display as a numbered list. For each issue show: issue number, title, milestone (if set), and URL. - -If the list is empty, say: - -> "No issues are currently waiting for QA." - -Do not take any other action. - ---- - -## Mode B — Numeric argument: Review a specific issue - -The argument is an issue number. Work through the following steps. - -### Step 1 — Load context - -``` -gh issue view <number> --json number,title,body,labels,url,comments -``` - -Display the issue title and body. - -Check whether `` is currently applied to the issue. If it is **not** present, warn: - -> "Issue #N does not have the label. It may not be deployed to preview yet. Continue anyway? (yes/no)" - -Wait. If the user says no, stop. - ---- - -### Step 2 — Review prompt - -Say: - -> "Test this feature on the preview environment. When you're done, describe what you tested, what you found, and your verdict (pass or fail). Include screenshots if you have them — paste image links or drag-and-drop into GitHub directly." - -**Stop. Wait for the QA person's input. Do not proceed until they provide a verdict.** - ---- - -### Step 3 — Post findings as issue comment - -Build a structured comment from their input: - -``` -## QA Review - -**Verdict:** PASS (or FAIL) - -**What was tested:** -<their description> - -**Findings:** -<their findings, or "None — feature works as expected."> - -**Screenshots:** -<image links if provided, or "None attached."> -``` - -Show the comment to the user before posting. Ask: - -> "Post this to issue #N? (yes/no)" - -Wait for confirmation. On yes: -``` -gh issue comment <number> --body "<comment>" -``` - -On no, stop — do not post and do not apply labels. - ---- - -### Step 4 — Apply verdict label - -Based on the verdict: - -**PASS:** -``` -gh issue edit <number> --add-label "" --remove-label "" -``` - -**FAIL:** - -Before applying labels, fetch the issue's current assignees: -``` -gh issue view <number> --json assignees --jq '.assignees[].login' 2>/dev/null -``` - -If one or more assignees are found, prepend the following line to the comment body (above the `## QA Review` heading) in the comment that was already posted: - -> cc @<assignee> — this issue needs attention. - -If there are multiple assignees, include one `cc @<login>` per line. If the command errors or returns no results, omit the line silently. - -``` -gh issue edit <number> --add-label "" --remove-label "" -``` - -- If `` is empty (PASS case), skip `--add-label` but still run `--remove-label ""`. -- If `` is empty (FAIL case), skip `--add-label` but still run `--remove-label ""`. - -If the `gh issue edit` command fails because a label does not exist in the repo, report the error and say: - -> "The label does not exist in this repository. Create it in GitHub Settings → Labels, then re-run `/qa <number>` from Step 4." - -Do not retry automatically. - -After applying labels, tell the user what was done: - -- On PASS: "Issue #N marked as QA passed. The developer can now close it or promote to production." -- On FAIL: "Issue #N marked as QA failed. The developer will see the findings in the issue comments." - ---- - -## Hard rules +> Note: In trunk mode, `/ship` does not apply this label automatically — you would need to apply it manually or via a separate workflow. -- Never close an issue — verdict recording and closure are separate concerns. -- Never post the comment without showing it to the user first. -- Never apply labels without user confirmation (the confirmation in Step 3 is the single gate for both posting the comment and applying labels). -- Never merge, deploy, or take any action beyond labeling and commenting. -<!-- generated by CodeCanon/sync.sh | skill: qa | adapter: claude | hash: 447216d6 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> +Do not proceed. Stop here. +<!-- generated by CodeCanon/sync.sh | skill: qa | adapter: claude | hash: 550854f6 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.claude/commands/release.md b/.claude/commands/release.md index c43395c..edadfd4 100644 --- a/.claude/commands/release.md +++ b/.claude/commands/release.md @@ -1,120 +1,21 @@ -<!-- generated by CodeCanon/sync.sh | skill: release | adapter: claude | hash: b470d522 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> You are executing the `/release` skill. Your argument is: $ARGUMENTS --- ## What `/release` does -Creates a GitHub Release and, in two-branch and three-branch mode, promotes the pre-production branch to `main`. Run this after preview/staging testing is confirmed. +Creates a GitHub Release and promotes `dev` to `main`. Run this after preview testing is confirmed. --- -## Step 1 — Verify state and determine mode +## Step 1 — Verify state Check the current branch: ```bash git branch --show-current ``` -**Determine mode:** -- If `dev` is empty → **Trunk Mode**. Follow the Trunk Mode section below. -- If `dev` is set but `` is empty → **Two-Branch Mode**. Follow the Two-Branch Mode section below. -- If both `dev` and `` are set → **Three-Branch Mode**. Follow the Three-Branch Mode section below. - ---- - -## Trunk Mode - -### Step 1T — Verify on `main` with tag - -If not on `main`, switch to it: -```bash -git checkout main && git pull -``` - -Verify a version tag exists on HEAD (i.e., `/version` was run before this): -```bash -git describe --exact-match --tags HEAD 2>/dev/null -``` - -If no tag is found, warn: "No version tag found on HEAD. Run `/version` first to tag this release before running `/release`." Stop unless the user explicitly says to proceed anyway. - -### Step 2T — Compute release contents - -Find the previous tag to determine the range: -```bash -git describe --abbrev=0 <version-tag>^ 2>/dev/null -``` - -Find all merge commits since the previous tag: -```bash -git log <prev-tag>..HEAD --merges --pretty=format:"%s" -``` - -Parse PR numbers from merge commit subjects (format: `Merge pull request #N from branch/name`). - -For each PR number found, retrieve the PR body: -```bash -gh pr view <N> --json number,title,body -``` - -Extract `Closes #N` references from PR bodies (trunk PRs use `Closes #N`). Compile: -- List of PRs included (number + title) -- List of issues linked via `Closes #N` - -### Step 3T — HUMAN GATE - -Show the user the release summary. Example format: - -``` -Ready to release vX.Y.Z to production. - -PRs included: - #17 — Add /docs directory - #18 — Fix checkout runtime error - -Issues that will be referenced: - #14 — Add /docs directory - #15 — Fix checkout runtime error - -Have you confirmed everything above is ready for production? Type 'release' to confirm. -``` - -Wait for the user to type "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change. - -### Step 4T — Create GitHub Release - -The version tag and PR/issue list are already known. If no previous tag exists, omit the "Full changelog" line. - -```bash -gh release create <version-tag> \ - --title "<version-tag>" \ - --notes "$(cat <<'EOF' -## Changes - -- #<issue> — <PR title> (PR #<pr-number>) -[... one line per PR included in this release ...] - -**Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> -EOF -)" -``` - -Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had no linked issue, use just the PR title. - -After the command runs, note the release URL from the output. - -### Step 5T — Report - -Tell the user: - -> "Released vX.Y.Z. Issues closed on merge. GitHub Release vX.Y.Z created at `<url>`. Run `make deploy-prod` to ship to production." - ---- - -## Two-Branch Mode - -### Step 1 — Verify state +### Verify on `dev` with tag If not on `dev`, switch to it: ```bash @@ -128,7 +29,9 @@ git describe --exact-match --tags HEAD 2>/dev/null If no tag is found, warn: "No version tag found on HEAD. Run `/version` first to tag this release before promoting it." Stop unless the user explicitly says to proceed anyway. -### Step 2 — Compute what's being promoted +--- + +## Step 2 — Compute what's being promoted Find all merge commits in `dev` not yet in `main`: ```bash @@ -149,7 +52,9 @@ Compile: - List of PRs being promoted (number + title) - List of open issues linked to those PRs -### Step 3 — HUMAN GATE +--- + +## Step 3 — HUMAN GATE Show the user the release summary. Example format: @@ -169,7 +74,9 @@ Have you tested all of the above on preview? Type 'release' to confirm. Wait for the user to type "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change. -### Step 4 — Create PR: `dev` → `main` +--- + +## Step 4 — Create PR: `dev` → `main` ```bash gh pr create --base main --head dev \ @@ -191,131 +98,9 @@ Note the PR number from the output. The `Closes #N` lines will auto-close the linked issues because this PR merges into `main` (the default branch). -### Step 5 — Merge - -Do NOT use `make merge` — it refuses PRs targeting `main`. Use `gh pr merge` directly: - -```bash -gh pr merge <pr-number> --merge -``` - -### Step 6 — Create GitHub Release - -The version tag (from Step 1) and the PR/issue list (from Step 2) are already known. Find the previous tag to build the changelog link: - -```bash -git describe --abbrev=0 <version-tag>^ 2>/dev/null -``` - -If no previous tag exists, omit the "Full changelog" line. - -Create the release: - -```bash -gh release create <version-tag> \ - --title "<version-tag>" \ - --notes "$(cat <<'EOF' -## Changes - -- #<issue> — <PR title> (PR #<pr-number>) -[... one line per PR included in this release ...] - -**Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> -EOF -)" -``` - -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. - -After the command runs, note the release URL from the output. - -### Step 7 — Report - -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." - --- -## Three-Branch Mode - -### Step 1 — Verify state - -If not on ``, switch to it: -```bash -git checkout && git pull -``` - -Verify a version tag exists on HEAD: -```bash -git describe --exact-match --tags HEAD 2>/dev/null -``` - -If no tag is found, warn: "No version tag found on HEAD. Run `/version` first to tag this release before promoting it." Stop unless the user explicitly says to proceed anyway. - -### Step 2 — Compute what's being promoted - -Find all merge commits in `` not yet in `main`: -```bash -git log main.. --merges --pretty=format:"%s" -``` - -Parse PR numbers from the merge commit subjects. Note: some merge commits here may be promotion merges from `dev` — these are identifiable by subjects matching "Merge ... from `dev`". Include them in the list but note they are promotion merges; extract the original feature PRs from their PR bodies when possible. - -For each PR number found, retrieve the PR body: -```bash -gh pr view <N> --json number,title,body -``` - -Extract `Issue #N` and `Closes #N` references from PR bodies. - -Compile (best-effort): -- List of PRs being promoted (number + title) -- List of open issues linked to those PRs - -### Step 3 — HUMAN GATE - -Show the user the release summary. Example format: - -``` -Ready to release vX.Y.Z to production. - -PRs included: - #17 — Add /docs directory - #18 — Fix checkout runtime error - -Issues that will close: - #14 — Add /docs directory - #15 — Fix checkout runtime error - -Have you tested all of the above on the environment? Type 'release' to confirm. -``` - -Wait for the user to type "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change. - -### Step 4 — Create PR: `` → `main` - -```bash -gh pr create --base main --head \ - --title "Release vX.Y.Z" \ - --body "$(cat <<'EOF' -Release vX.Y.Z - -PRs included: -- #17 — Add /docs directory -- #18 — Fix checkout runtime error - -Closes #14 -Closes #15 -EOF -)" -``` - -Note the PR number from the output. - -The `Closes #N` lines will auto-close the linked issues because this PR merges into `main` (the default branch). - -### Step 5 — Merge +## Step 5 — Merge Do NOT use `make merge` — it refuses PRs targeting `main`. Use `gh pr merge` directly: @@ -323,7 +108,9 @@ Do NOT use `make merge` — it refuses PRs targeting `main`. Use `gh pr merge` d gh pr merge <pr-number> --merge ``` -### Step 6 — Create GitHub Release +--- + +## Step 6 — Create GitHub Release The version tag (from Step 1) and the PR/issue list (from Step 2) are already known. Find the previous tag to build the changelog link: @@ -353,9 +140,11 @@ Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had After the command runs, note the release URL from the output. -### Step 7 — Report +--- + +## Step 7 — Report 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 CodeCanon/sync.sh | skill: release | adapter: claude | hash: b470d522 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> +<!-- generated by CodeCanon/sync.sh | skill: release | adapter: claude | hash: 985c90d5 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.claude/commands/review.md b/.claude/commands/review.md index d21fb26..84e56f0 100644 --- a/.claude/commands/review.md +++ b/.claude/commands/review.md @@ -31,9 +31,15 @@ If no PR is found, abort and say: "No open PR found for the current branch. Pass --- -## Step 2 — Spawn review agent +## Step 2 — Run the review -Load `.claude/review-agent-prompt.md` and invoke a review agent with the PR number. The review agent will: +Load `.claude/review-agent-prompt.md` and perform the review for the PR number. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: 1. Read the PR diff via `gh pr diff <number>` 2. Read any files needed for full context 3. Post findings as a PR comment via `gh pr comment <number>` @@ -54,4 +60,4 @@ After the review agent completes, relay its verdict to the user: - This command does not commit, push, or merge anything. It only reviews. - It may be called manually at any time, not just from `/ship`. - The review comment is posted to GitHub for the audit trail. -<!-- generated by CodeCanon/sync.sh | skill: review | adapter: claude | hash: 06aee22d | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> +<!-- generated by CodeCanon/sync.sh | skill: review | adapter: claude | hash: b372a312 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.claude/commands/setup.md b/.claude/commands/setup.md index 99d3bee..fd0ccee 100644 --- a/.claude/commands/setup.md +++ b/.claude/commands/setup.md @@ -1,4 +1,3 @@ -<!-- generated by CodeCanon/sync.sh | skill: setup | adapter: claude | hash: 5e26ca1c | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> You are executing the `/setup` skill. Your argument is: $ARGUMENTS --- @@ -206,13 +205,15 @@ If nothing is confidently broken, do not flag anything. When in doubt, do not fl If anything is flagged: show the specific key names and what they should likely be changed to. Do not modify the file. Tell the user to update these values and run `/setup` again. Stop. -### Check 7 — .claude/commands/ present and populated +### Check 7 — Generated skill output present + +Check whether sync.sh has been run by looking for any of the adapter output directories configured in `.codecannon.yaml`: ```bash -test -d .claude/commands +test -d .claude/commands || test -d .cursor/rules || test -d .agents/skills || test -d .gemini/skills ``` -If absent, or the directory exists but is empty: "sync.sh hasn't been run yet — the skill commands don't exist." +If none exist: "sync.sh hasn't been run yet — the skill commands don't exist." Show: @@ -470,4 +471,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri - Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. - Do not skip any human gate in Phase 2 or Phase 3 — each write requires confirmation. - If the user skips a config value, do not ask again. Move on. -<!-- generated by CodeCanon/sync.sh | skill: setup | adapter: claude | hash: bdb21058 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> +<!-- generated by CodeCanon/sync.sh | skill: setup | adapter: claude | hash: d15c8678 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.claude/commands/ship.md b/.claude/commands/ship.md index bd287f3..3820be2 100644 --- a/.claude/commands/ship.md +++ b/.claude/commands/ship.md @@ -1,14 +1,7 @@ -<!-- generated by CodeCanon/sync.sh | skill: ship | adapter: claude | hash: d98aef47 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> You are executing the `/ship` skill. Your argument is: $ARGUMENTS --- -<!-- Mode reference (derived from config at runtime): - Trunk mode: BRANCH_DEV empty, BRANCH_TEST empty → PR targets BRANCH_PROD - Two-branch mode: BRANCH_DEV set, BRANCH_TEST empty → PR targets BRANCH_DEV - Three-branch mode: BRANCH_DEV set, BRANCH_TEST set → PR targets BRANCH_DEV ---> - ## What `/ship` does `/ship` is Phase 3 of the workflow: type-check, commit, open PR, spawn review agent, act on verdict. @@ -22,10 +15,9 @@ Check current branch: git branch --show-current ``` -Determine which branches are protected (i.e., not a feature branch): -- Always: `main` -- If `dev` is non-empty: also `dev` -- If `` is non-empty: also `` +Protected branches (not a feature branch): +- `main` +- `dev` If the current branch matches any of the above, **abort immediately** and say: @@ -88,13 +80,9 @@ git ls-files CODEOWNERS .github/CODEOWNERS docs/CODEOWNERS 2>/dev/null If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHub will automatically request reviews from code owners." -Determine the PR target branch: -- If `dev` is non-empty, target `dev` (two-branch or three-branch mode). -- Otherwise, target `main` (trunk mode). +PR target branch: `dev` -Determine the issue reference format: -- If the PR targets `main` (trunk mode), use `Closes #<number>` — merging to the default branch will auto-close the issue. -- Otherwise, use `Issue #<number>` — the issue stays open until `/release` promotes to `main`. +Use `Issue #<number>` as the issue reference — the issue stays open until `/release` promotes to `main`. Then create the PR with explicit title and body (never use an interactive editor): ``` @@ -106,9 +94,6 @@ EOF )" ``` -If `` is non-empty, add `--reviewer ` to the `gh pr create` command above. If `` is empty, omit the `--reviewer` flag entirely — do not pass `--reviewer ""`. - -If a CODEOWNERS file exists and `` is also set, both apply: CODEOWNERS triggers automatic review requests from GitHub; the `--reviewer` flag adds the explicitly configured handles on top. **Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. @@ -120,20 +105,24 @@ Omit the issue line entirely if no linked issue was identified in Step 3. If `ai` is `"off"`, skip directly to Step 7 (merge without review). -Otherwise, load `.claude/review-agent-prompt.md` and invoke a review agent, passing the PR number. The review agent will: +Otherwise, load `.claude/review-agent-prompt.md` and perform the review for this PR. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: 1. Read the PR diff 2. Read relevant files for context 3. Post findings as a PR comment via `gh pr comment <number>` -Wait for the review agent to complete and report its verdict. +Wait for the review to complete and report its verdict. --- ## Step 7 — Act on verdict -Determine merge command (used by all paths below): -- If in **trunk mode** (`dev` is empty): use `gh pr merge <number> --merge` directly — `make merge` may refuse merges targeting `main`. -- Otherwise: use `make merge`. +Merge command (used by all paths below): `make merge` --- @@ -169,18 +158,9 @@ Return to the coding loop. When fixed, run `/ship` again from Step 1. ### After merge — QA label and success report -Apply the QA label only in **two-branch mode** (`dev` is set AND `` is empty): -- If `` is non-empty AND a linked issue number was identified in Step 3: - ``` - gh issue edit <number> --add-label "" - ``` -- In trunk mode or three-branch mode: skip the QA label entirely. -- If `` is empty or no linked issue was found: skip silently. Report success based on mode: -- **Trunk mode**: "PR merged. Issue #N closed automatically. Run `make deploy-prod` when ready to deploy to production." -- **Two-branch mode**: "PR merged. Issues stay open until testing confirms the fix. Run `make deploy-preview` when ready to deploy to preview." -- **Three-branch mode**: "PR merged to `dev`. Promote to `` when ready for staging." +"PR merged. Issues stay open until testing confirms the fix. Run `make deploy-preview` when ready to deploy to preview." --- @@ -190,6 +170,6 @@ Report success based on mode: - When `ai` is `"ai"`, never merge if the review verdict is REQUEST CHANGES. - When `ai` is `"advisory"`, always merge after review completes, regardless of verdict. - When `ai` is `"off"`, skip the review agent entirely — merge immediately after checks pass. -- In trunk mode, merges target `main`. Otherwise, `/ship` merges only to `dev` — never directly to `` or `main`. +- `/ship` merges only to `dev` — never directly to `main`. - If `make merge` fails for any reason, report it and stop — do not attempt workarounds. -<!-- generated by CodeCanon/sync.sh | skill: ship | adapter: claude | hash: d98aef47 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> +<!-- generated by CodeCanon/sync.sh | skill: ship | adapter: claude | hash: 9b678037 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.claude/commands/start.md b/.claude/commands/start.md index cdac11e..80b781f 100644 --- a/.claude/commands/start.md +++ b/.claude/commands/start.md @@ -24,7 +24,7 @@ The argument string may contain optional inline flags after the description. Par 1. **Identify flags** — scan for the first token that starts with `--label`, `-l`, `--milestone`, or `-m`. Everything before it is the **description**. Everything from the first flag onward is **flags**. 2. **`--label <value>` / `-l <value>`** — comma-separated label string (e.g. `bug` or `enhancement,ux`). If provided, it **bypasses label auto-selection entirely** for this invocation — use the value verbatim. Labels containing spaces must be quoted (e.g. `--label "good first issue"`). -3. **`--milestone <value>` / `-m <value>`** — milestone name or number (e.g. `Sprint 4` or `12`). If provided, it **replaces** `` for this invocation. Pass the value as-is; GitHub accepts both names and numbers. +3. **`--milestone <value>` / `-m <value>`** — milestone name or number (e.g. `Sprint 4` or `12`). Pass the value as-is; GitHub accepts both names and numbers. 4. **Flags may appear in any order** after the description. **Label resolution (three-tier, Case A only):** @@ -32,8 +32,7 @@ The argument string may contain optional inline flags after the description. Par After parsing flags, determine the active labels in this order: 1. **Per-invocation flag** — if `--label <value>` was in `$ARGUMENTS`, use that value verbatim. Skip all remaining steps. -2. **Pool-based selection** — if `` is non-empty, it contains the allowed label pool as a comma-separated list. The agent must select 1–3 labels from this pool that genuinely fit the task description and implementation approach. Do not apply labels mechanically — pick only what fits. If no pool label fits the task, fall through to step 3. - - If any selected label name contains a space (e.g. `good first issue`), quote the entire `--label` value. +2. **Pool-based selection** — no label pool is configured. Fall through to step 3. 3. **No label / creation** — if the pool is empty or no pool label fits: - If `false` is `true` (case-insensitive string match): the agent **may** create a new label before applying it: ```bash @@ -42,15 +41,14 @@ After parsing flags, determine the active labels in this order: Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. - If `false` is `false` or unset: omit `--label` entirely. Proceed silently; do not inform the user. -> **Tip:** If `` is empty, run `/setup` to populate it from your repo's existing GitHub labels. +> **Tip:** Run `/setup` to populate TICKET_LABELS from your repo's existing GitHub labels. **Milestone resolution (three-tier, Case A only):** After parsing flags, determine the active milestone in this order: 1. **Per-invocation flag** — if `--milestone <value>` was in `$ARGUMENTS`, use that value. Stop. -2. **Config default** — if `` is non-empty, use that value. Stop. -3. **Auto-detect** — if both are absent or empty, query open milestones: +3. **Auto-detect** — if no milestone is resolved yet, query open milestones: ```bash gh api repos/{owner}/{repo}/milestones --jq '[.[] | select(.state=="open")] | {count: length, milestones: [.[] | {number: .number, title: .title}]}' ``` @@ -63,10 +61,9 @@ After parsing flags, determine the active milestone in this order: | `$ARGUMENTS` | Description | Labels | Milestone | |---|---|---|---| -| `Add dark mode toggle to settings page` | `Add dark mode toggle to settings page` | auto-selected from `` pool | `` | -| `Add dark mode --label enhancement` | `Add dark mode` | `enhancement` (verbatim, no pool selection) | `` | +| `Add dark mode toggle to settings page` | `Add dark mode toggle to settings page` | none (no label pool) | auto-detected | +| `Add dark mode --label enhancement` | `Add dark mode` | `enhancement` (verbatim) | auto-detected | | `Add dark mode --label enhancement,ux --milestone "Sprint 4"` | `Add dark mode` | `enhancement,ux` (verbatim) | `Sprint 4` | -| `Add dark mode --milestone sprint-4` | `Add dark mode` | auto-selected from `` pool | `sprint-4` | > Replace vs append: flags **replace** auto-selection entirely, they do not append. This avoids silent label duplication and milestone conflicts. @@ -207,5 +204,6 @@ When done, say: **"The code is ready for review. Please run `make dev` and test - If already on a feature branch when `/start` is invoked, warn the user before creating another branch. - `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 `` → omit (or create if `false` is `true`). Never apply a label not in `` unless `false` is `true`. Milestone resolution order: per-invocation flag → `` config → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. -<!-- generated by CodeCanon/sync.sh | skill: start | adapter: claude | hash: 24909c1a | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> +- Apply labels only when explicitly provided via `--label`. No label pool is configured. +- Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. +<!-- generated by CodeCanon/sync.sh | skill: start | adapter: claude | hash: 5e12f95c | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.claude/commands/version.md b/.claude/commands/version.md index be872a0..fb28d19 100644 --- a/.claude/commands/version.md +++ b/.claude/commands/version.md @@ -1,4 +1,3 @@ -<!-- generated by CodeCanon/sync.sh | skill: version | adapter: claude | hash: ab8011ef | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> You are executing the `/version` skill. Your argument is: $ARGUMENTS --- @@ -10,10 +9,7 @@ Run: git branch --show-current ``` -Determine the required branch: -- If `` is non-empty → required branch is `` (three-branch mode). -- Else if `dev` is non-empty → required branch is `dev` (two-branch mode). -- Else → required branch is `main` (trunk mode). +Required branch: `dev` (two-branch mode). If not on the required branch, abort and say: "Switch to `<required-branch>` before running `/version`." @@ -76,8 +72,5 @@ Both the version bump commit and the tag must be pushed. Tell the user the new version and tag: -Report based on mode: -- **Trunk mode** (`dev` empty): "Tagged vX.Y.Z. Run `/release` to create the GitHub Release." -- **Two-branch mode** (`dev` set, `` empty): "Tagged vX.Y.Z. Run `make deploy-preview` to deploy to preview for testing. When testing is complete, run `/release`." -- **Three-branch mode** (both set): "Tagged vX.Y.Z. Run `make deploy-preview` to deploy to staging. When testing is complete, run `/release`." -<!-- generated by CodeCanon/sync.sh | skill: version | adapter: claude | hash: 955517c9 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> +"Tagged vX.Y.Z. Run `make deploy-preview` to deploy to preview for testing. When testing is complete, run `/release`." +<!-- generated by CodeCanon/sync.sh | skill: version | adapter: claude | hash: de0163b3 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.codecannon.yaml b/.codecannon.yaml index 603da97..de64149 100644 --- a/.codecannon.yaml +++ b/.codecannon.yaml @@ -6,9 +6,12 @@ # See config.schema.yaml for documentation on each placeholder. # Which agent adapters to generate skills for. -# Available: claude, cursor +# Available: claude, cursor, codex, gemini adapters: - claude + - cursor + - codex + - gemini # Placeholder values substituted into skill files during sync. # Remove any you don't need; sync.sh will warn about unresolved placeholders. diff --git a/.cursor/rules/qa.mdc b/.cursor/rules/qa.mdc new file mode 100644 index 0000000..ba53587 --- /dev/null +++ b/.cursor/rules/qa.mdc @@ -0,0 +1,18 @@ +--- +description: View the QA queue or review a specific issue +globs: +alwaysApply: false +--- + +> **Cursor:** Trigger this skill via `@qa` in Agent mode. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/ship` must be done manually using the review-agent prompt. + +--- + +> **QA workflow is not configured.** `/qa` requires `QA_READY_LABEL` to be set in `.codecannon.yaml` so it can find issues waiting for QA. +> +> To enable: add `QA_READY_LABEL: ready-for-qa` (or your preferred label name) to `.codecannon.yaml` and re-run `CodeCanon/sync.sh`. +> +> Note: In trunk mode, `/ship` does not apply this label automatically — you would need to apply it manually or via a separate workflow. + +Do not proceed. Stop here. +<!-- generated by CodeCanon/sync.sh | skill: qa | adapter: cursor | hash: c1a9452d | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.cursor/rules/release.mdc b/.cursor/rules/release.mdc new file mode 100644 index 0000000..24ac65f --- /dev/null +++ b/.cursor/rules/release.mdc @@ -0,0 +1,156 @@ +--- +description: Create a GitHub Release; in multi-branch mode, also promotes the pre-production branch to `main` +globs: +alwaysApply: false +--- + +> **Cursor:** Trigger this skill via `@release` in Agent mode. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/ship` must be done manually using the review-agent prompt. + +--- + +## What `/release` does + +Creates a GitHub Release and promotes `dev` to `main`. Run this after preview testing is confirmed. + +--- + +## Step 1 — Verify state + +Check the current branch: +```bash +git branch --show-current +``` + +### Verify on `dev` with tag + +If not on `dev`, switch to it: +```bash +git checkout dev && git pull +``` + +Verify a version tag exists on HEAD: +```bash +git describe --exact-match --tags HEAD 2>/dev/null +``` + +If no tag is found, warn: "No version tag found on HEAD. Run `/version` first to tag this release before promoting it." Stop unless the user explicitly says to proceed anyway. + +--- + +## Step 2 — Compute what's being promoted + +Find all merge commits in `dev` not yet in `main`: +```bash +git log main..dev --merges --pretty=format:"%s" +``` + +Parse PR numbers from the merge commit subjects. GitHub's format is: +`Merge pull request #N from branch/name` + +For each PR number found, retrieve the PR body: +```bash +gh pr view <N> --json number,title,body +``` + +Extract `Issue #N` references from PR bodies (look for the pattern `Issue #\d+`). + +Compile: +- List of PRs being promoted (number + title) +- List of open issues linked to those PRs + +--- + +## Step 3 — HUMAN GATE + +Show the user the release summary. Example format: + +``` +Ready to release vX.Y.Z to production. + +PRs included: + #17 — Add /docs directory + #18 — Fix checkout runtime error + +Issues that will close: + #14 — Add /docs directory + #15 — Fix checkout runtime error + +Have you tested all of the above on preview? Type 'release' to confirm. +``` + +Wait for the user to type "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change. + +--- + +## Step 4 — Create PR: `dev` → `main` + +```bash +gh pr create --base main --head dev \ + --title "Release vX.Y.Z" \ + --body "$(cat <<'EOF' +Release vX.Y.Z + +PRs included: +- #17 — Add /docs directory +- #18 — Fix checkout runtime error + +Closes #14 +Closes #15 +EOF +)" +``` + +Note the PR number from the output. + +The `Closes #N` lines will auto-close the linked issues because this PR merges into `main` (the default branch). + +--- + +## Step 5 — Merge + +Do NOT use `make merge` — it refuses PRs targeting `main`. Use `gh pr merge` directly: + +```bash +gh pr merge <pr-number> --merge +``` + +--- + +## Step 6 — Create GitHub Release + +The version tag (from Step 1) and the PR/issue list (from Step 2) are already known. Find the previous tag to build the changelog link: + +```bash +git describe --abbrev=0 <version-tag>^ 2>/dev/null +``` + +If no previous tag exists, omit the "Full changelog" line. + +Create the release: + +```bash +gh release create <version-tag> \ + --title "<version-tag>" \ + --notes "$(cat <<'EOF' +## Changes + +- #<issue> — <PR title> (PR #<pr-number>) +[... one line per PR included in this release ...] + +**Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> +EOF +)" +``` + +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. + +After the command runs, note the release URL from the output. + +--- + +## Step 7 — Report + +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 CodeCanon/sync.sh | skill: release | adapter: cursor | hash: 825c21ec | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.cursor/rules/review.mdc b/.cursor/rules/review.mdc new file mode 100644 index 0000000..b07db32 --- /dev/null +++ b/.cursor/rules/review.mdc @@ -0,0 +1,69 @@ +--- +description: Run a standalone code review on a pull request +globs: +alwaysApply: false +--- + +> **Cursor:** Trigger this skill via `@review` in Agent mode. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/ship` must be done manually using the review-agent prompt. + +--- + +## What `/review` does + +`/review` runs a standalone code review on a PR. It wraps `.claude/review-agent-prompt.md`. + +--- + +## Pre-flight + +If `ai` is `"off"`, say: + +> "Code review is disabled for this project (REVIEW_GATE is set to 'off' in .codecannon.yaml). To enable reviews, set REVIEW_GATE to 'ai' or 'advisory' and re-run CodeCanon/sync.sh." + +Do not proceed. + +--- + +## Step 1 — Identify the PR + +If `$ARGUMENTS` is a number, use it as the PR number. + +If `$ARGUMENTS` is empty, detect the current branch's open PR: +``` +gh pr view --json number --jq '.number' +``` + +If no PR is found, abort and say: "No open PR found for the current branch. Pass a PR number explicitly: `/review <number>`" + +--- + +## Step 2 — Run the review + +Load `.claude/review-agent-prompt.md` and perform the review for the PR number. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: +1. Read the PR diff via `gh pr diff <number>` +2. Read any files needed for full context +3. Post findings as a PR comment via `gh pr comment <number>` + +--- + +## Step 3 — Report verdict + +After the review agent completes, relay its verdict to the user: + +- **APPROVE** → "Review passed. Run `/ship` to merge (or it may already be merged if called from `/ship`)." +- **REQUEST CHANGES** → Surface the CRITICAL findings. Say: "Fix the issues above and run `/review` again before merging." + +--- + +## Important + +- This command does not commit, push, or merge anything. It only reviews. +- It may be called manually at any time, not just from `/ship`. +- The review comment is posted to GitHub for the audit trail. +<!-- generated by CodeCanon/sync.sh | skill: review | adapter: cursor | hash: 01a11917 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.cursor/rules/setup.mdc b/.cursor/rules/setup.mdc new file mode 100644 index 0000000..7b31a40 --- /dev/null +++ b/.cursor/rules/setup.mdc @@ -0,0 +1,480 @@ +--- +description: Detect setup state, guide first-time configuration, populate labels, and walk through optional config values +globs: +alwaysApply: false +--- + +> **Cursor:** Trigger this skill via `@setup` in Agent mode. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/ship` must be done manually using the review-agent prompt. + +--- + +## Detect state + +Before taking any action, determine which state the project is in. Run these checks in order. + +**Check A — Is this the Code Cannon skill library repo itself?** + +```bash +test -f sync.sh && test -d skills +``` + +If both exist at the working directory root → **go to State 1**. + +**Check B — Is there any Code Cannon submodule presence?** + +```bash +test -d CodeCanon +``` + +```bash +test -f .gitmodules && grep -q CodeCanon .gitmodules +``` + +If either is true → **go to State 2**. + +**Check C — State 1 fallback** + +If `.codecannon.yaml` is absent AND neither `CodeCanon/` nor `.gitmodules` exist → **go to State 1**. + +Otherwise → **go to State 2**. + +--- + +## State 1 — Just checking it out + +Do not configure anything. Do not touch any file. + +Tell the user warmly that Code Cannon is designed to live as a submodule inside another project — running `/setup` here in the Code Cannon repo itself isn't the intended path. + +Offer two forward paths and ask which they want: + +**Path A — "I want to understand how Code Cannon works"** + +Explain the three-layer model: +- **Skills** (`skills/*.md`) — portable workflow instructions with `main`-style tokens for project-specific values (see `config.schema.yaml`) +- **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 | +| `/ship` | Checks, commits, opens PR, spawns review agent, merges | +| `/review` | Standalone code review on any PR | +| `/version` | Bumps semver, tags, pushes | +| `/release` | Promotes integration branch to main, closes issues | +| `/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. + +**Path B — "I want to add Code Cannon to my project"** + +Show the exact command sequence: + +```bash +cd /path/to/your-project +git submodule add https://github.com/LightbridgeLab/CodeCanon.git CodeCanon +git submodule update --init +cp CodeCanon/templates/codecannon.yaml .codecannon.yaml +# Edit .codecannon.yaml — set branch names, commands, adapters +CodeCanon/sync.sh +``` + +Do not touch any file. The user runs these commands in their project directory. + +--- + +## State 2 — Partial or broken setup + +Run checks 1–7 in order. Stop at the first failing check and address it. After describing the fix, tell the user to run `/setup` again once they've resolved it. Do not continue past a failing check. + +### Check 1 — CodeCanon/sync.sh present + +```bash +test -f CodeCanon/sync.sh +``` + +If missing: the submodule was added to `.gitmodules` or `CodeCanon/` exists as an empty directory, but it hasn't been initialized. Show: + +```bash +git submodule update --init --recursive +``` + +Offer to run it. If the user agrees, run it. If they decline, tell them to run it manually before continuing. Stop. + +### Check 2 — gh installed + +```bash +which gh +``` + +If not found: "`gh` is required by all Code Cannon skills. Install it with `brew install gh` (macOS) or from https://cli.github.com." Cannot proceed without it. Stop. + +### Check 3 — gh authenticated + +```bash +gh auth status +``` + +If exit code is non-zero: "You're not authenticated with GitHub." Show: + +```bash +gh auth login +``` + +Cannot proceed without it. Stop. + +### Check 4 — Inside a GitHub repository + +```bash +gh repo view --json name +``` + +If exit code is non-zero: warn that most skills require a GitHub remote. Skills can be read and configured, but `/start`, `/ship`, `/review`, `/release`, and `/status` will fail without one. This is not a hard stop — ask if the user wants to continue configuring anyway. + +### Check 5 — .codecannon.yaml present + +```bash +test -f .codecannon.yaml +``` + +If missing: "I'll create `.codecannon.yaml` from the template — you'll want to review the branch names and commands before running sync." + +Show: + +```bash +cp CodeCanon/templates/codecannon.yaml .codecannon.yaml +``` + +Ask permission to run it. If the user agrees, run it. If they decline, tell them to run it manually. + +After creating the file, proceed immediately to Check 5b. + +### Check 5b — Profile selection (only on first setup) + +This check runs only when Check 5 just created `.codecannon.yaml` from the template. If `.codecannon.yaml` already existed before this `/setup` invocation, skip to Check 6. + +Ask the user: + +> "What level of process does this project need?" +> +> **1. Lightweight** — Fast iteration. AI review is advisory, features merge to main, no QA workflow. +> +> **2. Standard** — Integration branch with AI-gated review. QA and milestones available but not required. +> +> **3. Governed** — Full traceability. QA handoff, assigned reviewers, milestones, structured labels. +> +> **4. Custom** — Configure each setting individually. +> +> Pick a number, or describe your workflow and I'll recommend one. + +Wait for response. If the user describes their situation instead of picking a number, recommend the best-fit profile and confirm before applying. + +**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. + +**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. + +**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. + +**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. + +### Check 6 — .codecannon.yaml stale values + +Read `.codecannon.yaml`. Apply the following checks conservatively — only flag a value if you are confident it points to something that does not exist: + +- If `VERSION_READ_CMD` references `package.json` and no `package.json` exists at the project root → flag it +- If `BRANCH_DEV` is set to a non-empty value and that branch does not appear in `git branch -a` → flag it +- If `BRANCH_TEST` is set to a non-empty value and that branch does not appear in `git branch -a` → flag it + +If nothing is confidently broken, do not flag anything. When in doubt, do not flag. + +If anything is flagged: show the specific key names and what they should likely be changed to. Do not modify the file. Tell the user to update these values and run `/setup` again. Stop. + +### Check 7 — Generated skill output present + +Check whether sync.sh has been run by looking for any of the adapter output directories configured in `.codecannon.yaml`: + +```bash +test -d .claude/commands || test -d .cursor/rules || test -d .agents/skills || test -d .gemini/skills +``` + +If none exist: "sync.sh hasn't been run yet — the skill commands don't exist." + +Show: + +```bash +CodeCanon/sync.sh +``` + +Ask permission to run it. If the user agrees, run it. If they decline, tell them to run it manually before continuing. Stop. + +--- + +## State 3 — Everything configured + +All checks pass. Run phases 1–4 in order. + +--- + +### Phase 1 — Health summary + +Print one sentence confirming the setup looks healthy. Read `.codecannon.yaml` and infer the workflow profile for display: + +- `REVIEW_GATE` is `"advisory"` or `"off"` AND `BRANCH_DEV` is empty → **Lightweight** +- `REVIEW_GATE` is `"ai"` AND `BRANCH_DEV` is set AND `QA_READY_LABEL` is empty → **Standard** +- `REVIEW_GATE` is `"ai"` AND `QA_READY_LABEL` is set → **Governed** +- Anything else → **Custom** + +Check whether the configured dev/test branches exist in the remote (skip checks for empty values): + +```bash +git branch -a | grep -q "remotes/origin/<BRANCH_DEV value>" +git branch -a | grep -q "remotes/origin/<BRANCH_TEST value>" +``` + +Display: + +``` +Setup looks healthy. Profile: <inferred profile> + + BRANCH_PROD: <value> + BRANCH_DEV: <value> (exists in remote: yes/no/not set) + BRANCH_TEST: <value> (exists in remote: yes/no/not set) + REVIEW_GATE: <value> + CHECK_CMD: <value> + MERGE_CMD: <value> + Adapters: <list from config> + + Optional config: + DEFAULT_MILESTONE — set / unset + DEFAULT_REVIEWERS — set / unset + TICKET_LABELS — set (N labels) / unset + TICKET_LABEL_CREATION_ALLOWED — set / unset + QA_READY_LABEL — set / unset + PLATFORM_COMPLIANCE_NOTES — set / unset + CONVENTIONS_NOTES — set / unset +``` + +A value counts as "set" if it is present, uncommented, and non-empty in `.codecannon.yaml`. + +--- + +### Phase 2 — Label population + +Run: + +```bash +gh label list --limit 100 --json name,color,description +``` + +If zero labels are found, treat this as a greenfield repository and offer a starter label baseline before asking about `TICKET_LABELS`. + +Show this recommendation: + +``` +No labels were found. For new projects, a practical baseline is: + - bug + - enhancement + - chore + - documentation + - ready-for-qa + - qa-passed + - qa-failed +``` + +Ask: **"Create any missing labels from this baseline now? (yes/no)"** + +Wait for response. + +- **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. + +Display the results as a numbered list: + +``` +Available labels (N found): + 1. bug — Something isn't working + 2. enhancement — New feature or request + 3. good first issue — Good for newcomers + ... +``` + +Ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** + +Wait for the user's response. + +- **yes** → use all labels +- **numbers** (e.g. `1,3,5`) → use only those labels +- **no / skip / anything else** → skip this phase, continue to Phase 3 + +Show the exact change before writing: + +``` +I'll update .codecannon.yaml with: + + TICKET_LABELS: "bug,enhancement,..." + +Proceed? (yes/no) +``` + +Wait for confirmation. Write only on yes. + +--- + +### Phase 3 — Optional config walkthrough (profile-aware) + +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 `/ship` 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')" + +**TICKET_LABEL_CREATION_ALLOWED** (Standard, Governed, and Custom) + +"Controls whether `/start` can create new GitHub labels on the fly when none in the pool fit the task. Defaults to false." + +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'. +``` + +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. + +--- + +### Phase 4 — Team sharing + +After completing or skipping the config walkthrough, say: + +"To share this setup with your team, commit these files. Anyone who clones the project and runs `git submodule update --init` will have all skills ready — no further setup needed." + +Show the exact command: + +```bash +git add .codecannon.yaml .claude/ CodeCanon AGENTS.md +``` + +Add a note: `/start` can be used to create well-formed GitHub issues without writing any code — useful for non-developers tracking work. `/status` generates standup summaries from open issues and PRs — both are valuable outside of a development workflow. + +--- + +## Hard rules + +- Only modify `.codecannon.yaml`. Do not touch any other file (except running `CodeCanon/sync.sh`, which modifies `.claude/commands/` — permitted only with explicit user approval). +- Do not run `sync.sh` without explicit user permission. +- Do not create `.codecannon.yaml` without explicit user permission. +- Do not report a configuration problem unless confident the condition is genuinely broken. Prefer false negatives over false positives on all diagnostic checks. +- Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. +- Do not skip any human gate in Phase 2 or Phase 3 — each write requires confirmation. +- If the user skips a config value, do not ask again. Move on. +<!-- generated by CodeCanon/sync.sh | skill: setup | adapter: cursor | hash: 93a1a102 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.cursor/rules/ship.mdc b/.cursor/rules/ship.mdc new file mode 100644 index 0000000..01dbec9 --- /dev/null +++ b/.cursor/rules/ship.mdc @@ -0,0 +1,181 @@ +--- +description: Type-check, commit, open PR, review, and merge to the integration branch +globs: +alwaysApply: false +--- + +> **Cursor:** Trigger this skill via `@ship` in Agent mode. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/ship` must be done manually using the review-agent prompt. + +--- + +## What `/ship` does + +`/ship` is Phase 3 of the workflow: type-check, commit, open PR, spawn review agent, act on verdict. + +--- + +## Step 1 — Verify branch + +Check current branch: +``` +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>`. `/ship` must be run from a feature branch. Switch to your feature branch first." + +--- + +## Step 2 — Type-check gate + +Run: +``` +make check +``` + +If errors are reported, **stop**. Report the errors to the user and say: + +> "Check failed. Fix the errors above before shipping." + +Do not proceed until `make check` passes cleanly. + +--- + +## Step 3 — Identify linked issue + +Check for a linked issue by inspecting the branch name (should follow `feature/<name>` linked via `gh issue develop`) or by running: +``` +gh pr view --json number,body 2>/dev/null +``` + +If a linked issue number is identifiable, note it for the PR body. If not identifiable, proceed without it but mention this to the user. + +--- + +## Step 4 — Commit + +Stage all changes and commit: +``` +git add -A +git commit -m "<imperative-mood message>" +``` + +Commit message rules: +- Imperative mood ("Add X", "Fix Y", "Remove Z") +- Concise but meaningful — describes what changed and why in one line +- No `.env` files, build artifacts, `node_modules`, or secrets + +--- + +## Step 5 — Push and open PR + +First, push the branch: +``` +git push -u origin HEAD +``` + +Next, check for a CODEOWNERS file: +``` +git ls-files CODEOWNERS .github/CODEOWNERS docs/CODEOWNERS 2>/dev/null +``` + +If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHub will automatically request reviews from code owners." + +PR target branch: `dev` + +Use `Issue #<number>` as the issue reference — the issue stays open until `/release` promotes to `main`. + +Then create the PR with explicit title and body (never use an interactive editor): +``` +gh pr create --base <target-branch> --title "<title>" --body "$(cat <<'EOF' +<description of what changed and why> + +<Closes #N OR Issue #N, based on target above> +EOF +)" +``` + + +**Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. + +Omit the issue line entirely if no linked issue was identified in Step 3. + +--- + +## Step 6 — Review (conditional) + +If `ai` is `"off"`, skip directly to Step 7 (merge without review). + +Otherwise, load `.claude/review-agent-prompt.md` and perform the review for this PR. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: +1. Read the PR diff +2. Read relevant files for context +3. Post findings as a PR comment via `gh pr comment <number>` + +Wait for the review to complete and report its verdict. + +--- + +## Step 7 — Act on verdict + +Merge command (used by all paths below): `make merge` + +--- + +**If `ai` is `"off"` (review skipped):** + +Run the merge command. Apply QA label and report success (see below). + +--- + +**If `ai` is `"advisory"`:** + +Report the review findings to the user. Then merge regardless — treat as APPROVE. + +If the review contained CRITICAL findings, note: + +> "Review flagged issues (see PR comment) but advisory mode is enabled — merged anyway. Review the findings when convenient." + +Apply QA label and report success (see below). + +--- + +**If `ai` is `"ai"` (default):** + +**If APPROVE (no CRITICAL findings):** Run the merge command. Apply QA label and report success (see below). + +**If REQUEST CHANGES (at least one CRITICAL finding):** Report the findings to the user. Do NOT merge. Say: + +> "The review found blocking issues (see above). Fix them and run `/ship` again." + +Return to the coding loop. When fixed, run `/ship` again from Step 1. + +--- + +### After merge — QA label and success report + + +Report success based on mode: +"PR merged. Issues stay open until testing confirms the fix. Run `make deploy-preview` when ready to deploy to preview." + +--- + +## Important constraints + +- Never skip `make check`. A failed check is a hard stop. +- When `ai` is `"ai"`, never merge if the review verdict is REQUEST CHANGES. +- When `ai` is `"advisory"`, always merge after review completes, regardless of verdict. +- When `ai` is `"off"`, skip the review agent entirely — merge immediately after checks pass. +- `/ship` merges only to `dev` — never directly to `main`. +- If `make merge` fails for any reason, report it and stop — do not attempt workarounds. +<!-- generated by CodeCanon/sync.sh | skill: ship | adapter: cursor | hash: ebeb4102 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.cursor/rules/start.mdc b/.cursor/rules/start.mdc new file mode 100644 index 0000000..fc3d94f --- /dev/null +++ b/.cursor/rules/start.mdc @@ -0,0 +1,215 @@ +--- +description: Start a new feature or bugfix +globs: +alwaysApply: false +--- + +> **Cursor:** Trigger this skill via `@start` in Agent mode. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/ship` must be done manually using the review-agent prompt. + +--- + +## CRITICAL: Order of operations + +**You must complete Steps 1–4 before writing any code.** +Do not open any source file with intent to edit until `git branch --show-current` shows a `feature/*` branch. + +--- + +## Determine case + +If `$ARGUMENTS` is a number (digits only) → go to **Case B: Resume existing issue**. +Otherwise → go to **Case A: New work**. + +--- + +## Parsing $ARGUMENTS (Case A only) + +> Skip this entirely if `$ARGUMENTS` triggered Case B. + +The argument string may contain optional inline flags after the description. Parse as follows: + +1. **Identify flags** — scan for the first token that starts with `--label`, `-l`, `--milestone`, or `-m`. Everything before it is the **description**. Everything from the first flag onward is **flags**. +2. **`--label <value>` / `-l <value>`** — comma-separated label string (e.g. `bug` or `enhancement,ux`). If provided, it **bypasses label auto-selection entirely** for this invocation — use the value verbatim. Labels containing spaces must be quoted (e.g. `--label "good first issue"`). +3. **`--milestone <value>` / `-m <value>`** — milestone name or number (e.g. `Sprint 4` or `12`). Pass the value as-is; GitHub accepts both names and numbers. +4. **Flags may appear in any order** after the description. + +**Label resolution (three-tier, Case A only):** + +After parsing flags, determine the active labels in this order: + +1. **Per-invocation flag** — if `--label <value>` was in `$ARGUMENTS`, use that value verbatim. Skip all remaining steps. +2. **Pool-based selection** — no label pool is configured. Fall through to step 3. +3. **No label / creation** — if the pool is empty or no pool label fits: + - If `false` is `true` (case-insensitive string match): the agent **may** create a new label before applying it: + ```bash + gh label create "<name>" --color "<hex>" --description "<short description>" + ``` + Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. + - If `false` is `false` or unset: omit `--label` entirely. Proceed silently; do not inform the user. + +> **Tip:** Run `/setup` to populate TICKET_LABELS from your repo's existing GitHub labels. + +**Milestone resolution (three-tier, Case A only):** + +After parsing flags, determine the active milestone in this order: + +1. **Per-invocation flag** — if `--milestone <value>` was in `$ARGUMENTS`, use that value. Stop. +3. **Auto-detect** — if no milestone is resolved yet, query open milestones: + ```bash + gh api repos/{owner}/{repo}/milestones --jq '[.[] | select(.state=="open")] | {count: length, milestones: [.[] | {number: .number, title: .title}]}' + ``` + Use `gh repo view --json owner,name` first if the owner/repo are not already known. + - **0 results** → no milestone; proceed without `--milestone`. + - **1 result** → use its title silently. Inform the user inline: `(milestone: <title>)`. + - **2+ results** → show the numbered list, ask once: **"Multiple open milestones — which should this issue go under? (enter a number or title, or 'none')"**. Accept milestone number, title, or "none"/"skip". Wait for response before continuing. + +**Examples:** + +| `$ARGUMENTS` | Description | Labels | Milestone | +|---|---|---|---| +| `Add dark mode toggle to settings page` | `Add dark mode toggle to settings page` | none (no label pool) | auto-detected | +| `Add dark mode --label enhancement` | `Add dark mode` | `enhancement` (verbatim) | auto-detected | +| `Add dark mode --label enhancement,ux --milestone "Sprint 4"` | `Add dark mode` | `enhancement,ux` (verbatim) | `Sprint 4` | + +> Replace vs append: flags **replace** auto-selection entirely, they do not append. This avoids silent label duplication and milestone conflicts. + +--- + +## Case A: New work (text description) + +### Step 1 — Investigate + +Read the relevant code. Propose a concrete implementation approach. Be specific about which files change and how. + +### Step 2 — HUMAN GATE + +Say exactly: **"Does this approach sound right? I'll create a GitHub issue and branch before writing any code."** + +Stop. Wait for the user to confirm. + +- User says yes → continue to Step 3. +- User redirects → revise approach, ask again. +- User abandons → stop. Nothing to clean up. + +### Step 3 — Create GitHub Issue + +Run `gh issue create` with explicit flags (do NOT open an interactive editor): + +```bash +gh issue create \ + --title "<standalone full sentence — must make sense with no context>" \ + --body "<human-readable explanation: what the problem is, why it matters, general approach — written for a non-developer, no code or file paths>" \ + --assignee @me \ + [--label "<resolved labels>"] \ + [--milestone "<resolved milestone>"] +``` + +Resolve labels and milestone using the resolution steps in the Parsing section above: +- **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. +- **Milestone**: use the value from three-tier milestone resolution. If non-empty, add `--milestone "<value>"` to the command. If empty (no flag, no config default, no open milestones), omit `--milestone` entirely. + +**Title rules:** +- ✅ `Fix 'Contact Us' footer link pointing to 404 instead of /contact-us` +- ❌ `Fix broken link` + +After the command runs, note the issue number from the output URL (e.g. `https://github.com/.../issues/42` → issue `42`). + +Show the user: `Created issue #<number>: <title>` + +Then immediately post agent implementation notes as a comment: + +```bash +gh issue comment <number> --body "## Agent Implementation Notes + +<full technical plan: exact files to change, approach, key decisions, edge cases>" +``` + +### Step 4 — Create feature branch + +```bash +gh issue develop <number> --name feature/<short-descriptive-name> --checkout +``` + +Verify the branch was created: + +```bash +git branch --show-current +``` + +Show the user: `On branch feature/<name>` + +**Do not proceed to Step 5 until this shows a `feature/*` branch.** + +### Step 5 — Write the code + +Now write the code. Do NOT commit anything. + +When done, say: **"The code is ready for review. Please run `make dev` and test locally. Let me know if it looks good, needs changes, or should be scrapped."** + +- User says looks good → run `/ship` +- User requests changes → iterate, repeat this message +- User says scrap it → run `make abandon` + +--- + +## Case B: Resume existing issue (numeric argument) + +### Step 1 — Load context + +```bash +gh issue view <number> --comments +``` + +Read the full body and all comments. Note: what was done, what remains, branch status. + +### Step 2 — Summarize and gate + +Tell the user: +- What the issue is about +- What was previously done (from agent notes if present) +- What appears to remain + +Ask: **"Does this match your understanding? Continue this ticket, or open a fresh one?"** + +- Continue → Step 3. +- New ticket → restart as Case A with a new description. + +### Step 3 — Check out branch + +Find and check out the existing branch, or create a new one linked to the issue: + +```bash +gh issue develop <number> --name feature/<short-name> --checkout +``` + +Verify: + +```bash +git branch --show-current +``` + +Post a resumption comment: + +```bash +gh issue comment <number> --body "Resuming work. <brief note on what's being continued.>" +``` + +### Step 4 — Write the code + +Continue from where work left off. Do NOT commit. + +When done, say: **"The code is ready for review. Please run `make dev` and test locally."** + +--- + +## Hard rules + +- 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 `/ship`. +- If already on a feature branch when `/start` is invoked, warn the user before creating another branch. +- `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 labels only when explicitly provided via `--label`. No label pool is configured. +- Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. +<!-- generated by CodeCanon/sync.sh | skill: start | adapter: cursor | hash: 5317dec4 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.cursor/rules/status.mdc b/.cursor/rules/status.mdc new file mode 100644 index 0000000..c9030d7 --- /dev/null +++ b/.cursor/rules/status.mdc @@ -0,0 +1,184 @@ +--- +description: Summarize in-progress and recently completed work from GitHub and git +globs: +alwaysApply: false +--- + +> **Cursor:** Trigger this skill via `@status` in Agent mode. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/ship` must be done manually using the review-agent prompt. + +--- + +## Step 1 — Parse arguments + +First, check whether `$ARGUMENTS` contains `--milestone` or `--sprint` (they are identical aliases). + +**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. + +Examples: +- `--milestone Sprint 4` → milestone name = `Sprint 4` +- `--sprint Sprint 4` → milestone name = `Sprint 4` +- `--milestone Q2 Release` → milestone name = `Q2 Release` +- `--milestone 12` → milestone name = `12` + +**Personal mode** (no `--milestone` / `--sprint` 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. + +No argument → subject = `@me`, lookback = `7`. + +--- + +## Step 2 — Fetch GitHub data (run all three 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 +``` + +**Recently merged PRs (last `<lookback>` days):** +```bash +gh pr list --author <subject> --state merged --limit 20 \ + --json number,title,url,mergedAt,labels,baseRefName +``` +Filter the results to keep only entries where `mergedAt` is within the last `<lookback>` days. + +**Open issues assigned to subject:** +```bash +gh issue list --assignee <subject> --state open \ + --json number,title,url,labels,milestone +``` + +If any `gh` command exits with a non-zero status (including auth errors), report the error message and stop. Do not retry. + +--- + +## Step 3 — Fetch local git context + +Check if the current directory is inside a git repository: +```bash +git rev-parse --is-inside-work-tree 2>/dev/null +``` + +If yes, run: +```bash +git log --oneline --since="<lookback> days ago" +``` + +If not inside a git repo, skip this step and note it was skipped in the output. + +--- + +## Step 4 — Classify items + +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). + +An open issue that IS linked from an open PR body appears under "In progress" alongside that PR, not under "Up next". + +--- + +## Step 5 — Output the summary + +Print a formatted summary. Use this structure: + +``` +## Status for <subject> — last <lookback> days + +### In progress +- #<number> <title> [<labels>] [<milestone>] + PR: <url> + Linked issue: #<number> (if found) + +### Done +- #<number> <title> [<labels>] — merged <date> + PR: <url> + +### Up next +- #<number> <title> [<labels>] [<milestone>] + Issue: <url> + +--- +Local commits (current branch): +<git log output, or "skipped — not in a git repo"> +``` + +Rules: +- 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.` + +Do not post, comment, write files, or take any action. Output only. + +--- + +## Milestone mode (Steps M1–M3) + +Only entered when `--milestone` or `--sprint` is detected in Step 1. + +### Step M1 — Fetch milestone issues + +```bash +gh issue list --milestone "<name>" --state all --limit 200 \ + --json number,title,state,labels,assignees,url +``` + +If this command fails for any reason (milestone not found, auth error, etc.), report the error and stop. + +### Step M2 — Classify issues + +Fetch all open PRs to detect which issues are in progress: + +```bash +gh pr list --state open --json number,title,body,baseRefName +``` + +Group issues into three buckets: + +- **Done** — `state: closed` +- **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 + +### Step M3 — Output the summary + +``` +## Sprint: <name> + +<Y> of <total> issues closed · <Z> in progress · <W> not started + +### In progress (<Z>) +- #<number> <title> [@<assignee>] [<milestone>] + <url> + +### Not started (<W>) +- #<number> <title> [@<assignee>] + +### Done (<Y>) +- #<number> <title> +``` + +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 +- If a section has no items, omit it entirely + +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). +- Never suggest what the developer should work on next. +- 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 CodeCanon/sync.sh | skill: status | adapter: cursor | hash: 53e10253 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.cursor/rules/version.mdc b/.cursor/rules/version.mdc new file mode 100644 index 0000000..51f4fc5 --- /dev/null +++ b/.cursor/rules/version.mdc @@ -0,0 +1,82 @@ +--- +description: Bump the project version, tag, and push — run before deploying to preview +globs: +alwaysApply: false +--- + +> **Cursor:** Trigger this skill via `@version` in Agent mode. State any arguments in your message. Sub-agent spawning is not supported — the automated review step in `/ship` must be done manually using the review-agent prompt. + +--- + +## Step 1 — Verify branch + +Run: +```bash +git branch --show-current +``` + +Required branch: `dev` (two-branch mode). + +If not on the required branch, abort and say: "Switch to `<required-branch>` before running `/version`." + +Pull the latest changes before proceeding: +```bash +git pull +``` + +--- + +## Step 2 — Read current version + +```bash +cat VERSION +``` + +--- + +## Step 3 — Ask the user + +Tell the user (filling in the actual computed values): + +> "You are on version X.Y.Z. Do you want to bump: +> - **major** → A.0.0 +> - **minor** → X.B.0 +> - **patch** → X.Y.C +> - or set a specific version?" + +Wait for their response. + +--- + +## Step 4 — Interpret and run + +Map the user's natural language response to a command: + +| User says | Run | +|---|---| +| "patch" / anything mentioning patch | `make bump-patch` | +| "minor" | `make bump-minor` | +| "major" | `make bump-major` | +| A specific version e.g. "2.4.5" | `make set-version V= 2.4.5` | + +These targets update the version manifest, create a git commit, and create a git tag. Do not create commits or tags manually. + +--- + +## Step 5 — Push + +```bash +git push +git push --tags +``` + +Both the version bump commit and the tag must be pushed. + +--- + +## Step 6 — Report + +Tell the user the new version and tag: + +"Tagged vX.Y.Z. Run `make deploy-preview` to deploy to preview for testing. When testing is complete, run `/release`." +<!-- generated by CodeCanon/sync.sh | skill: version | adapter: cursor | hash: ac8ad867 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.gemini/skills/qa/SKILL.md b/.gemini/skills/qa/SKILL.md new file mode 100644 index 0000000..01ec99a --- /dev/null +++ b/.gemini/skills/qa/SKILL.md @@ -0,0 +1,17 @@ +--- +name: qa +description: View the QA queue or review a specific issue +--- + +> **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 `/ship` must be done manually using the review-agent prompt in a separate session. + +--- + +> **QA workflow is not configured.** `/qa` requires `QA_READY_LABEL` to be set in `.codecannon.yaml` so it can find issues waiting for QA. +> +> To enable: add `QA_READY_LABEL: ready-for-qa` (or your preferred label name) to `.codecannon.yaml` and re-run `CodeCanon/sync.sh`. +> +> Note: In trunk mode, `/ship` does not apply this label automatically — you would need to apply it manually or via a separate workflow. + +Do not proceed. Stop here. +<!-- generated by CodeCanon/sync.sh | skill: qa | adapter: gemini | hash: aaceafdf | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.gemini/skills/release/SKILL.md b/.gemini/skills/release/SKILL.md new file mode 100644 index 0000000..01b6dec --- /dev/null +++ b/.gemini/skills/release/SKILL.md @@ -0,0 +1,155 @@ +--- +name: release +description: Create a GitHub Release; in multi-branch mode, also promotes the pre-production branch to `main` +--- + +> **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 `/ship` must be done manually using the review-agent prompt in a separate session. + +--- + +## What `/release` does + +Creates a GitHub Release and promotes `dev` to `main`. Run this after preview testing is confirmed. + +--- + +## Step 1 — Verify state + +Check the current branch: +```bash +git branch --show-current +``` + +### Verify on `dev` with tag + +If not on `dev`, switch to it: +```bash +git checkout dev && git pull +``` + +Verify a version tag exists on HEAD: +```bash +git describe --exact-match --tags HEAD 2>/dev/null +``` + +If no tag is found, warn: "No version tag found on HEAD. Run `/version` first to tag this release before promoting it." Stop unless the user explicitly says to proceed anyway. + +--- + +## Step 2 — Compute what's being promoted + +Find all merge commits in `dev` not yet in `main`: +```bash +git log main..dev --merges --pretty=format:"%s" +``` + +Parse PR numbers from the merge commit subjects. GitHub's format is: +`Merge pull request #N from branch/name` + +For each PR number found, retrieve the PR body: +```bash +gh pr view <N> --json number,title,body +``` + +Extract `Issue #N` references from PR bodies (look for the pattern `Issue #\d+`). + +Compile: +- List of PRs being promoted (number + title) +- List of open issues linked to those PRs + +--- + +## Step 3 — HUMAN GATE + +Show the user the release summary. Example format: + +``` +Ready to release vX.Y.Z to production. + +PRs included: + #17 — Add /docs directory + #18 — Fix checkout runtime error + +Issues that will close: + #14 — Add /docs directory + #15 — Fix checkout runtime error + +Have you tested all of the above on preview? Type 'release' to confirm. +``` + +Wait for the user to type "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change. + +--- + +## Step 4 — Create PR: `dev` → `main` + +```bash +gh pr create --base main --head dev \ + --title "Release vX.Y.Z" \ + --body "$(cat <<'EOF' +Release vX.Y.Z + +PRs included: +- #17 — Add /docs directory +- #18 — Fix checkout runtime error + +Closes #14 +Closes #15 +EOF +)" +``` + +Note the PR number from the output. + +The `Closes #N` lines will auto-close the linked issues because this PR merges into `main` (the default branch). + +--- + +## Step 5 — Merge + +Do NOT use `make merge` — it refuses PRs targeting `main`. Use `gh pr merge` directly: + +```bash +gh pr merge <pr-number> --merge +``` + +--- + +## Step 6 — Create GitHub Release + +The version tag (from Step 1) and the PR/issue list (from Step 2) are already known. Find the previous tag to build the changelog link: + +```bash +git describe --abbrev=0 <version-tag>^ 2>/dev/null +``` + +If no previous tag exists, omit the "Full changelog" line. + +Create the release: + +```bash +gh release create <version-tag> \ + --title "<version-tag>" \ + --notes "$(cat <<'EOF' +## Changes + +- #<issue> — <PR title> (PR #<pr-number>) +[... one line per PR included in this release ...] + +**Full changelog:** https://github.com/<owner>/<repo>/compare/<previous-tag>...<version-tag> +EOF +)" +``` + +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. + +After the command runs, note the release URL from the output. + +--- + +## Step 7 — Report + +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 CodeCanon/sync.sh | skill: release | adapter: gemini | hash: 650cbe2b | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.gemini/skills/review/SKILL.md b/.gemini/skills/review/SKILL.md new file mode 100644 index 0000000..09381ac --- /dev/null +++ b/.gemini/skills/review/SKILL.md @@ -0,0 +1,68 @@ +--- +name: review +description: Run a standalone code review on a pull request +--- + +> **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 `/ship` must be done manually using the review-agent prompt in a separate session. + +--- + +## What `/review` does + +`/review` runs a standalone code review on a PR. It wraps `.claude/review-agent-prompt.md`. + +--- + +## Pre-flight + +If `ai` is `"off"`, say: + +> "Code review is disabled for this project (REVIEW_GATE is set to 'off' in .codecannon.yaml). To enable reviews, set REVIEW_GATE to 'ai' or 'advisory' and re-run CodeCanon/sync.sh." + +Do not proceed. + +--- + +## Step 1 — Identify the PR + +If `$ARGUMENTS` is a number, use it as the PR number. + +If `$ARGUMENTS` is empty, detect the current branch's open PR: +``` +gh pr view --json number --jq '.number' +``` + +If no PR is found, abort and say: "No open PR found for the current branch. Pass a PR number explicitly: `/review <number>`" + +--- + +## Step 2 — Run the review + +Load `.claude/review-agent-prompt.md` and perform the review for the PR number. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: +1. Read the PR diff via `gh pr diff <number>` +2. Read any files needed for full context +3. Post findings as a PR comment via `gh pr comment <number>` + +--- + +## Step 3 — Report verdict + +After the review agent completes, relay its verdict to the user: + +- **APPROVE** → "Review passed. Run `/ship` to merge (or it may already be merged if called from `/ship`)." +- **REQUEST CHANGES** → Surface the CRITICAL findings. Say: "Fix the issues above and run `/review` again before merging." + +--- + +## Important + +- This command does not commit, push, or merge anything. It only reviews. +- It may be called manually at any time, not just from `/ship`. +- The review comment is posted to GitHub for the audit trail. +<!-- generated by CodeCanon/sync.sh | skill: review | adapter: gemini | hash: 8efa7ca7 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.gemini/skills/setup/SKILL.md b/.gemini/skills/setup/SKILL.md new file mode 100644 index 0000000..7135c68 --- /dev/null +++ b/.gemini/skills/setup/SKILL.md @@ -0,0 +1,479 @@ +--- +name: setup +description: Detect setup state, guide first-time configuration, populate labels, and walk through optional config values +--- + +> **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 `/ship` must be done manually using the review-agent prompt in a separate session. + +--- + +## Detect state + +Before taking any action, determine which state the project is in. Run these checks in order. + +**Check A — Is this the Code Cannon skill library repo itself?** + +```bash +test -f sync.sh && test -d skills +``` + +If both exist at the working directory root → **go to State 1**. + +**Check B — Is there any Code Cannon submodule presence?** + +```bash +test -d CodeCanon +``` + +```bash +test -f .gitmodules && grep -q CodeCanon .gitmodules +``` + +If either is true → **go to State 2**. + +**Check C — State 1 fallback** + +If `.codecannon.yaml` is absent AND neither `CodeCanon/` nor `.gitmodules` exist → **go to State 1**. + +Otherwise → **go to State 2**. + +--- + +## State 1 — Just checking it out + +Do not configure anything. Do not touch any file. + +Tell the user warmly that Code Cannon is designed to live as a submodule inside another project — running `/setup` here in the Code Cannon repo itself isn't the intended path. + +Offer two forward paths and ask which they want: + +**Path A — "I want to understand how Code Cannon works"** + +Explain the three-layer model: +- **Skills** (`skills/*.md`) — portable workflow instructions with `main`-style tokens for project-specific values (see `config.schema.yaml`) +- **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 | +| `/ship` | Checks, commits, opens PR, spawns review agent, merges | +| `/review` | Standalone code review on any PR | +| `/version` | Bumps semver, tags, pushes | +| `/release` | Promotes integration branch to main, closes issues | +| `/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. + +**Path B — "I want to add Code Cannon to my project"** + +Show the exact command sequence: + +```bash +cd /path/to/your-project +git submodule add https://github.com/LightbridgeLab/CodeCanon.git CodeCanon +git submodule update --init +cp CodeCanon/templates/codecannon.yaml .codecannon.yaml +# Edit .codecannon.yaml — set branch names, commands, adapters +CodeCanon/sync.sh +``` + +Do not touch any file. The user runs these commands in their project directory. + +--- + +## State 2 — Partial or broken setup + +Run checks 1–7 in order. Stop at the first failing check and address it. After describing the fix, tell the user to run `/setup` again once they've resolved it. Do not continue past a failing check. + +### Check 1 — CodeCanon/sync.sh present + +```bash +test -f CodeCanon/sync.sh +``` + +If missing: the submodule was added to `.gitmodules` or `CodeCanon/` exists as an empty directory, but it hasn't been initialized. Show: + +```bash +git submodule update --init --recursive +``` + +Offer to run it. If the user agrees, run it. If they decline, tell them to run it manually before continuing. Stop. + +### Check 2 — gh installed + +```bash +which gh +``` + +If not found: "`gh` is required by all Code Cannon skills. Install it with `brew install gh` (macOS) or from https://cli.github.com." Cannot proceed without it. Stop. + +### Check 3 — gh authenticated + +```bash +gh auth status +``` + +If exit code is non-zero: "You're not authenticated with GitHub." Show: + +```bash +gh auth login +``` + +Cannot proceed without it. Stop. + +### Check 4 — Inside a GitHub repository + +```bash +gh repo view --json name +``` + +If exit code is non-zero: warn that most skills require a GitHub remote. Skills can be read and configured, but `/start`, `/ship`, `/review`, `/release`, and `/status` will fail without one. This is not a hard stop — ask if the user wants to continue configuring anyway. + +### Check 5 — .codecannon.yaml present + +```bash +test -f .codecannon.yaml +``` + +If missing: "I'll create `.codecannon.yaml` from the template — you'll want to review the branch names and commands before running sync." + +Show: + +```bash +cp CodeCanon/templates/codecannon.yaml .codecannon.yaml +``` + +Ask permission to run it. If the user agrees, run it. If they decline, tell them to run it manually. + +After creating the file, proceed immediately to Check 5b. + +### Check 5b — Profile selection (only on first setup) + +This check runs only when Check 5 just created `.codecannon.yaml` from the template. If `.codecannon.yaml` already existed before this `/setup` invocation, skip to Check 6. + +Ask the user: + +> "What level of process does this project need?" +> +> **1. Lightweight** — Fast iteration. AI review is advisory, features merge to main, no QA workflow. +> +> **2. Standard** — Integration branch with AI-gated review. QA and milestones available but not required. +> +> **3. Governed** — Full traceability. QA handoff, assigned reviewers, milestones, structured labels. +> +> **4. Custom** — Configure each setting individually. +> +> Pick a number, or describe your workflow and I'll recommend one. + +Wait for response. If the user describes their situation instead of picking a number, recommend the best-fit profile and confirm before applying. + +**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. + +**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. + +**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. + +**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. + +### Check 6 — .codecannon.yaml stale values + +Read `.codecannon.yaml`. Apply the following checks conservatively — only flag a value if you are confident it points to something that does not exist: + +- If `VERSION_READ_CMD` references `package.json` and no `package.json` exists at the project root → flag it +- If `BRANCH_DEV` is set to a non-empty value and that branch does not appear in `git branch -a` → flag it +- If `BRANCH_TEST` is set to a non-empty value and that branch does not appear in `git branch -a` → flag it + +If nothing is confidently broken, do not flag anything. When in doubt, do not flag. + +If anything is flagged: show the specific key names and what they should likely be changed to. Do not modify the file. Tell the user to update these values and run `/setup` again. Stop. + +### Check 7 — Generated skill output present + +Check whether sync.sh has been run by looking for any of the adapter output directories configured in `.codecannon.yaml`: + +```bash +test -d .claude/commands || test -d .cursor/rules || test -d .agents/skills || test -d .gemini/skills +``` + +If none exist: "sync.sh hasn't been run yet — the skill commands don't exist." + +Show: + +```bash +CodeCanon/sync.sh +``` + +Ask permission to run it. If the user agrees, run it. If they decline, tell them to run it manually before continuing. Stop. + +--- + +## State 3 — Everything configured + +All checks pass. Run phases 1–4 in order. + +--- + +### Phase 1 — Health summary + +Print one sentence confirming the setup looks healthy. Read `.codecannon.yaml` and infer the workflow profile for display: + +- `REVIEW_GATE` is `"advisory"` or `"off"` AND `BRANCH_DEV` is empty → **Lightweight** +- `REVIEW_GATE` is `"ai"` AND `BRANCH_DEV` is set AND `QA_READY_LABEL` is empty → **Standard** +- `REVIEW_GATE` is `"ai"` AND `QA_READY_LABEL` is set → **Governed** +- Anything else → **Custom** + +Check whether the configured dev/test branches exist in the remote (skip checks for empty values): + +```bash +git branch -a | grep -q "remotes/origin/<BRANCH_DEV value>" +git branch -a | grep -q "remotes/origin/<BRANCH_TEST value>" +``` + +Display: + +``` +Setup looks healthy. Profile: <inferred profile> + + BRANCH_PROD: <value> + BRANCH_DEV: <value> (exists in remote: yes/no/not set) + BRANCH_TEST: <value> (exists in remote: yes/no/not set) + REVIEW_GATE: <value> + CHECK_CMD: <value> + MERGE_CMD: <value> + Adapters: <list from config> + + Optional config: + DEFAULT_MILESTONE — set / unset + DEFAULT_REVIEWERS — set / unset + TICKET_LABELS — set (N labels) / unset + TICKET_LABEL_CREATION_ALLOWED — set / unset + QA_READY_LABEL — set / unset + PLATFORM_COMPLIANCE_NOTES — set / unset + CONVENTIONS_NOTES — set / unset +``` + +A value counts as "set" if it is present, uncommented, and non-empty in `.codecannon.yaml`. + +--- + +### Phase 2 — Label population + +Run: + +```bash +gh label list --limit 100 --json name,color,description +``` + +If zero labels are found, treat this as a greenfield repository and offer a starter label baseline before asking about `TICKET_LABELS`. + +Show this recommendation: + +``` +No labels were found. For new projects, a practical baseline is: + - bug + - enhancement + - chore + - documentation + - ready-for-qa + - qa-passed + - qa-failed +``` + +Ask: **"Create any missing labels from this baseline now? (yes/no)"** + +Wait for response. + +- **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. + +Display the results as a numbered list: + +``` +Available labels (N found): + 1. bug — Something isn't working + 2. enhancement — New feature or request + 3. good first issue — Good for newcomers + ... +``` + +Ask: **"Write these label names to `.codecannon.yaml` as TICKET_LABELS? (yes / no / list specific numbers)"** + +Wait for the user's response. + +- **yes** → use all labels +- **numbers** (e.g. `1,3,5`) → use only those labels +- **no / skip / anything else** → skip this phase, continue to Phase 3 + +Show the exact change before writing: + +``` +I'll update .codecannon.yaml with: + + TICKET_LABELS: "bug,enhancement,..." + +Proceed? (yes/no) +``` + +Wait for confirmation. Write only on yes. + +--- + +### Phase 3 — Optional config walkthrough (profile-aware) + +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 `/ship` 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')" + +**TICKET_LABEL_CREATION_ALLOWED** (Standard, Governed, and Custom) + +"Controls whether `/start` can create new GitHub labels on the fly when none in the pool fit the task. Defaults to false." + +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'. +``` + +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. + +--- + +### Phase 4 — Team sharing + +After completing or skipping the config walkthrough, say: + +"To share this setup with your team, commit these files. Anyone who clones the project and runs `git submodule update --init` will have all skills ready — no further setup needed." + +Show the exact command: + +```bash +git add .codecannon.yaml .claude/ CodeCanon AGENTS.md +``` + +Add a note: `/start` can be used to create well-formed GitHub issues without writing any code — useful for non-developers tracking work. `/status` generates standup summaries from open issues and PRs — both are valuable outside of a development workflow. + +--- + +## Hard rules + +- Only modify `.codecannon.yaml`. Do not touch any other file (except running `CodeCanon/sync.sh`, which modifies `.claude/commands/` — permitted only with explicit user approval). +- Do not run `sync.sh` without explicit user permission. +- Do not create `.codecannon.yaml` without explicit user permission. +- Do not report a configuration problem unless confident the condition is genuinely broken. Prefer false negatives over false positives on all diagnostic checks. +- Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling. +- Do not skip any human gate in Phase 2 or Phase 3 — each write requires confirmation. +- If the user skips a config value, do not ask again. Move on. +<!-- generated by CodeCanon/sync.sh | skill: setup | adapter: gemini | hash: ef9bd617 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.gemini/skills/ship/SKILL.md b/.gemini/skills/ship/SKILL.md new file mode 100644 index 0000000..af140b4 --- /dev/null +++ b/.gemini/skills/ship/SKILL.md @@ -0,0 +1,180 @@ +--- +name: ship +description: Type-check, commit, open PR, review, and merge to the integration branch +--- + +> **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 `/ship` must be done manually using the review-agent prompt in a separate session. + +--- + +## What `/ship` does + +`/ship` is Phase 3 of the workflow: type-check, commit, open PR, spawn review agent, act on verdict. + +--- + +## Step 1 — Verify branch + +Check current branch: +``` +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>`. `/ship` must be run from a feature branch. Switch to your feature branch first." + +--- + +## Step 2 — Type-check gate + +Run: +``` +make check +``` + +If errors are reported, **stop**. Report the errors to the user and say: + +> "Check failed. Fix the errors above before shipping." + +Do not proceed until `make check` passes cleanly. + +--- + +## Step 3 — Identify linked issue + +Check for a linked issue by inspecting the branch name (should follow `feature/<name>` linked via `gh issue develop`) or by running: +``` +gh pr view --json number,body 2>/dev/null +``` + +If a linked issue number is identifiable, note it for the PR body. If not identifiable, proceed without it but mention this to the user. + +--- + +## Step 4 — Commit + +Stage all changes and commit: +``` +git add -A +git commit -m "<imperative-mood message>" +``` + +Commit message rules: +- Imperative mood ("Add X", "Fix Y", "Remove Z") +- Concise but meaningful — describes what changed and why in one line +- No `.env` files, build artifacts, `node_modules`, or secrets + +--- + +## Step 5 — Push and open PR + +First, push the branch: +``` +git push -u origin HEAD +``` + +Next, check for a CODEOWNERS file: +``` +git ls-files CODEOWNERS .github/CODEOWNERS docs/CODEOWNERS 2>/dev/null +``` + +If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHub will automatically request reviews from code owners." + +PR target branch: `dev` + +Use `Issue #<number>` as the issue reference — the issue stays open until `/release` promotes to `main`. + +Then create the PR with explicit title and body (never use an interactive editor): +``` +gh pr create --base <target-branch> --title "<title>" --body "$(cat <<'EOF' +<description of what changed and why> + +<Closes #N OR Issue #N, based on target above> +EOF +)" +``` + + +**Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. + +Omit the issue line entirely if no linked issue was identified in Step 3. + +--- + +## Step 6 — Review (conditional) + +If `ai` is `"off"`, skip directly to Step 7 (merge without review). + +Otherwise, load `.claude/review-agent-prompt.md` and perform the review for this PR. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: +1. Read the PR diff +2. Read relevant files for context +3. Post findings as a PR comment via `gh pr comment <number>` + +Wait for the review to complete and report its verdict. + +--- + +## Step 7 — Act on verdict + +Merge command (used by all paths below): `make merge` + +--- + +**If `ai` is `"off"` (review skipped):** + +Run the merge command. Apply QA label and report success (see below). + +--- + +**If `ai` is `"advisory"`:** + +Report the review findings to the user. Then merge regardless — treat as APPROVE. + +If the review contained CRITICAL findings, note: + +> "Review flagged issues (see PR comment) but advisory mode is enabled — merged anyway. Review the findings when convenient." + +Apply QA label and report success (see below). + +--- + +**If `ai` is `"ai"` (default):** + +**If APPROVE (no CRITICAL findings):** Run the merge command. Apply QA label and report success (see below). + +**If REQUEST CHANGES (at least one CRITICAL finding):** Report the findings to the user. Do NOT merge. Say: + +> "The review found blocking issues (see above). Fix them and run `/ship` again." + +Return to the coding loop. When fixed, run `/ship` again from Step 1. + +--- + +### After merge — QA label and success report + + +Report success based on mode: +"PR merged. Issues stay open until testing confirms the fix. Run `make deploy-preview` when ready to deploy to preview." + +--- + +## Important constraints + +- Never skip `make check`. A failed check is a hard stop. +- When `ai` is `"ai"`, never merge if the review verdict is REQUEST CHANGES. +- When `ai` is `"advisory"`, always merge after review completes, regardless of verdict. +- When `ai` is `"off"`, skip the review agent entirely — merge immediately after checks pass. +- `/ship` merges only to `dev` — never directly to `main`. +- If `make merge` fails for any reason, report it and stop — do not attempt workarounds. +<!-- generated by CodeCanon/sync.sh | skill: ship | adapter: gemini | hash: 8a7ebcca | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.gemini/skills/start/SKILL.md b/.gemini/skills/start/SKILL.md new file mode 100644 index 0000000..2f1fa08 --- /dev/null +++ b/.gemini/skills/start/SKILL.md @@ -0,0 +1,214 @@ +--- +name: start +description: Start a new feature or bugfix +--- + +> **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 `/ship` must be done manually using the review-agent prompt in a separate session. + +--- + +## CRITICAL: Order of operations + +**You must complete Steps 1–4 before writing any code.** +Do not open any source file with intent to edit until `git branch --show-current` shows a `feature/*` branch. + +--- + +## Determine case + +If `$ARGUMENTS` is a number (digits only) → go to **Case B: Resume existing issue**. +Otherwise → go to **Case A: New work**. + +--- + +## Parsing $ARGUMENTS (Case A only) + +> Skip this entirely if `$ARGUMENTS` triggered Case B. + +The argument string may contain optional inline flags after the description. Parse as follows: + +1. **Identify flags** — scan for the first token that starts with `--label`, `-l`, `--milestone`, or `-m`. Everything before it is the **description**. Everything from the first flag onward is **flags**. +2. **`--label <value>` / `-l <value>`** — comma-separated label string (e.g. `bug` or `enhancement,ux`). If provided, it **bypasses label auto-selection entirely** for this invocation — use the value verbatim. Labels containing spaces must be quoted (e.g. `--label "good first issue"`). +3. **`--milestone <value>` / `-m <value>`** — milestone name or number (e.g. `Sprint 4` or `12`). Pass the value as-is; GitHub accepts both names and numbers. +4. **Flags may appear in any order** after the description. + +**Label resolution (three-tier, Case A only):** + +After parsing flags, determine the active labels in this order: + +1. **Per-invocation flag** — if `--label <value>` was in `$ARGUMENTS`, use that value verbatim. Skip all remaining steps. +2. **Pool-based selection** — no label pool is configured. Fall through to step 3. +3. **No label / creation** — if the pool is empty or no pool label fits: + - If `false` is `true` (case-insensitive string match): the agent **may** create a new label before applying it: + ```bash + gh label create "<name>" --color "<hex>" --description "<short description>" + ``` + Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. + - If `false` is `false` or unset: omit `--label` entirely. Proceed silently; do not inform the user. + +> **Tip:** Run `/setup` to populate TICKET_LABELS from your repo's existing GitHub labels. + +**Milestone resolution (three-tier, Case A only):** + +After parsing flags, determine the active milestone in this order: + +1. **Per-invocation flag** — if `--milestone <value>` was in `$ARGUMENTS`, use that value. Stop. +3. **Auto-detect** — if no milestone is resolved yet, query open milestones: + ```bash + gh api repos/{owner}/{repo}/milestones --jq '[.[] | select(.state=="open")] | {count: length, milestones: [.[] | {number: .number, title: .title}]}' + ``` + Use `gh repo view --json owner,name` first if the owner/repo are not already known. + - **0 results** → no milestone; proceed without `--milestone`. + - **1 result** → use its title silently. Inform the user inline: `(milestone: <title>)`. + - **2+ results** → show the numbered list, ask once: **"Multiple open milestones — which should this issue go under? (enter a number or title, or 'none')"**. Accept milestone number, title, or "none"/"skip". Wait for response before continuing. + +**Examples:** + +| `$ARGUMENTS` | Description | Labels | Milestone | +|---|---|---|---| +| `Add dark mode toggle to settings page` | `Add dark mode toggle to settings page` | none (no label pool) | auto-detected | +| `Add dark mode --label enhancement` | `Add dark mode` | `enhancement` (verbatim) | auto-detected | +| `Add dark mode --label enhancement,ux --milestone "Sprint 4"` | `Add dark mode` | `enhancement,ux` (verbatim) | `Sprint 4` | + +> Replace vs append: flags **replace** auto-selection entirely, they do not append. This avoids silent label duplication and milestone conflicts. + +--- + +## Case A: New work (text description) + +### Step 1 — Investigate + +Read the relevant code. Propose a concrete implementation approach. Be specific about which files change and how. + +### Step 2 — HUMAN GATE + +Say exactly: **"Does this approach sound right? I'll create a GitHub issue and branch before writing any code."** + +Stop. Wait for the user to confirm. + +- User says yes → continue to Step 3. +- User redirects → revise approach, ask again. +- User abandons → stop. Nothing to clean up. + +### Step 3 — Create GitHub Issue + +Run `gh issue create` with explicit flags (do NOT open an interactive editor): + +```bash +gh issue create \ + --title "<standalone full sentence — must make sense with no context>" \ + --body "<human-readable explanation: what the problem is, why it matters, general approach — written for a non-developer, no code or file paths>" \ + --assignee @me \ + [--label "<resolved labels>"] \ + [--milestone "<resolved milestone>"] +``` + +Resolve labels and milestone using the resolution steps in the Parsing section above: +- **Labels**: use the value from three-tier label resolution. If non-empty, add `--label "<value>"` to the command. If empty (no flag, empty pool, creation not allowed), omit `--label` entirely. +- **Milestone**: use the value from three-tier milestone resolution. If non-empty, add `--milestone "<value>"` to the command. If empty (no flag, no config default, no open milestones), omit `--milestone` entirely. + +**Title rules:** +- ✅ `Fix 'Contact Us' footer link pointing to 404 instead of /contact-us` +- ❌ `Fix broken link` + +After the command runs, note the issue number from the output URL (e.g. `https://github.com/.../issues/42` → issue `42`). + +Show the user: `Created issue #<number>: <title>` + +Then immediately post agent implementation notes as a comment: + +```bash +gh issue comment <number> --body "## Agent Implementation Notes + +<full technical plan: exact files to change, approach, key decisions, edge cases>" +``` + +### Step 4 — Create feature branch + +```bash +gh issue develop <number> --name feature/<short-descriptive-name> --checkout +``` + +Verify the branch was created: + +```bash +git branch --show-current +``` + +Show the user: `On branch feature/<name>` + +**Do not proceed to Step 5 until this shows a `feature/*` branch.** + +### Step 5 — Write the code + +Now write the code. Do NOT commit anything. + +When done, say: **"The code is ready for review. Please run `make dev` and test locally. Let me know if it looks good, needs changes, or should be scrapped."** + +- User says looks good → run `/ship` +- User requests changes → iterate, repeat this message +- User says scrap it → run `make abandon` + +--- + +## Case B: Resume existing issue (numeric argument) + +### Step 1 — Load context + +```bash +gh issue view <number> --comments +``` + +Read the full body and all comments. Note: what was done, what remains, branch status. + +### Step 2 — Summarize and gate + +Tell the user: +- What the issue is about +- What was previously done (from agent notes if present) +- What appears to remain + +Ask: **"Does this match your understanding? Continue this ticket, or open a fresh one?"** + +- Continue → Step 3. +- New ticket → restart as Case A with a new description. + +### Step 3 — Check out branch + +Find and check out the existing branch, or create a new one linked to the issue: + +```bash +gh issue develop <number> --name feature/<short-name> --checkout +``` + +Verify: + +```bash +git branch --show-current +``` + +Post a resumption comment: + +```bash +gh issue comment <number> --body "Resuming work. <brief note on what's being continued.>" +``` + +### Step 4 — Write the code + +Continue from where work left off. Do NOT commit. + +When done, say: **"The code is ready for review. Please run `make dev` and test locally."** + +--- + +## Hard rules + +- 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 `/ship`. +- If already on a feature branch when `/start` is invoked, warn the user before creating another branch. +- `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 labels only when explicitly provided via `--label`. No label pool is configured. +- Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. +<!-- generated by CodeCanon/sync.sh | skill: start | adapter: gemini | hash: abdf1658 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.gemini/skills/status/SKILL.md b/.gemini/skills/status/SKILL.md new file mode 100644 index 0000000..688c891 --- /dev/null +++ b/.gemini/skills/status/SKILL.md @@ -0,0 +1,183 @@ +--- +name: status +description: Summarize in-progress and recently completed work from GitHub and git +--- + +> **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 `/ship` must be done manually using the review-agent prompt in a separate session. + +--- + +## Step 1 — Parse arguments + +First, check whether `$ARGUMENTS` contains `--milestone` or `--sprint` (they are identical aliases). + +**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. + +Examples: +- `--milestone Sprint 4` → milestone name = `Sprint 4` +- `--sprint Sprint 4` → milestone name = `Sprint 4` +- `--milestone Q2 Release` → milestone name = `Q2 Release` +- `--milestone 12` → milestone name = `12` + +**Personal mode** (no `--milestone` / `--sprint` 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. + +No argument → subject = `@me`, lookback = `7`. + +--- + +## Step 2 — Fetch GitHub data (run all three 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 +``` + +**Recently merged PRs (last `<lookback>` days):** +```bash +gh pr list --author <subject> --state merged --limit 20 \ + --json number,title,url,mergedAt,labels,baseRefName +``` +Filter the results to keep only entries where `mergedAt` is within the last `<lookback>` days. + +**Open issues assigned to subject:** +```bash +gh issue list --assignee <subject> --state open \ + --json number,title,url,labels,milestone +``` + +If any `gh` command exits with a non-zero status (including auth errors), report the error message and stop. Do not retry. + +--- + +## Step 3 — Fetch local git context + +Check if the current directory is inside a git repository: +```bash +git rev-parse --is-inside-work-tree 2>/dev/null +``` + +If yes, run: +```bash +git log --oneline --since="<lookback> days ago" +``` + +If not inside a git repo, skip this step and note it was skipped in the output. + +--- + +## Step 4 — Classify items + +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). + +An open issue that IS linked from an open PR body appears under "In progress" alongside that PR, not under "Up next". + +--- + +## Step 5 — Output the summary + +Print a formatted summary. Use this structure: + +``` +## Status for <subject> — last <lookback> days + +### In progress +- #<number> <title> [<labels>] [<milestone>] + PR: <url> + Linked issue: #<number> (if found) + +### Done +- #<number> <title> [<labels>] — merged <date> + PR: <url> + +### Up next +- #<number> <title> [<labels>] [<milestone>] + Issue: <url> + +--- +Local commits (current branch): +<git log output, or "skipped — not in a git repo"> +``` + +Rules: +- 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.` + +Do not post, comment, write files, or take any action. Output only. + +--- + +## Milestone mode (Steps M1–M3) + +Only entered when `--milestone` or `--sprint` is detected in Step 1. + +### Step M1 — Fetch milestone issues + +```bash +gh issue list --milestone "<name>" --state all --limit 200 \ + --json number,title,state,labels,assignees,url +``` + +If this command fails for any reason (milestone not found, auth error, etc.), report the error and stop. + +### Step M2 — Classify issues + +Fetch all open PRs to detect which issues are in progress: + +```bash +gh pr list --state open --json number,title,body,baseRefName +``` + +Group issues into three buckets: + +- **Done** — `state: closed` +- **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 + +### Step M3 — Output the summary + +``` +## Sprint: <name> + +<Y> of <total> issues closed · <Z> in progress · <W> not started + +### In progress (<Z>) +- #<number> <title> [@<assignee>] [<milestone>] + <url> + +### Not started (<W>) +- #<number> <title> [@<assignee>] + +### Done (<Y>) +- #<number> <title> +``` + +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 +- If a section has no items, omit it entirely + +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). +- Never suggest what the developer should work on next. +- 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 CodeCanon/sync.sh | skill: status | adapter: gemini | hash: 74df8b14 | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.gemini/skills/version/SKILL.md b/.gemini/skills/version/SKILL.md new file mode 100644 index 0000000..5410a28 --- /dev/null +++ b/.gemini/skills/version/SKILL.md @@ -0,0 +1,81 @@ +--- +name: version +description: Bump the project version, tag, and push — run before deploying to preview +--- + +> **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 `/ship` must be done manually using the review-agent prompt in a separate session. + +--- + +## Step 1 — Verify branch + +Run: +```bash +git branch --show-current +``` + +Required branch: `dev` (two-branch mode). + +If not on the required branch, abort and say: "Switch to `<required-branch>` before running `/version`." + +Pull the latest changes before proceeding: +```bash +git pull +``` + +--- + +## Step 2 — Read current version + +```bash +cat VERSION +``` + +--- + +## Step 3 — Ask the user + +Tell the user (filling in the actual computed values): + +> "You are on version X.Y.Z. Do you want to bump: +> - **major** → A.0.0 +> - **minor** → X.B.0 +> - **patch** → X.Y.C +> - or set a specific version?" + +Wait for their response. + +--- + +## Step 4 — Interpret and run + +Map the user's natural language response to a command: + +| User says | Run | +|---|---| +| "patch" / anything mentioning patch | `make bump-patch` | +| "minor" | `make bump-minor` | +| "major" | `make bump-major` | +| A specific version e.g. "2.4.5" | `make set-version V= 2.4.5` | + +These targets update the version manifest, create a git commit, and create a git tag. Do not create commits or tags manually. + +--- + +## Step 5 — Push + +```bash +git push +git push --tags +``` + +Both the version bump commit and the tag must be pushed. + +--- + +## Step 6 — Report + +Tell the user the new version and tag: + +"Tagged vX.Y.Z. Run `make deploy-preview` to deploy to preview for testing. When testing is complete, run `/release`." +<!-- generated by CodeCanon/sync.sh | skill: version | adapter: gemini | hash: 95d2281d | DO NOT EDIT — run CodeCanon/sync.sh to regenerate --> diff --git a/.gitignore b/.gitignore index 4f074fd..d411e17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,13 @@ +# macOS .DS_Store -**/.DS_Store + +# Editors +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# Environment +.env +.env.local diff --git a/Makefile b/Makefile index 3a1989c..3df6c8a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,37 @@ +# Makefile — CodeCannon project-specific targets +# +# Workflow targets (branch, pr, abandon, merge, promote) come from Makefile.agents.mk. +# This file adds CodeCannon-specific targets: sync, versioning, and deployment. + +INTEGRATION_BRANCH = dev include Makefile.agents.mk -.PHONY: check dev sync bump-patch bump-minor bump-major set-version deploy-preview deploy-prod +.DEFAULT_GOAL := help +.PHONY: help check dev sync bump-patch bump-minor bump-major set-version deploy-preview deploy-prod + +help: + @echo "Usage: make <target>" + @echo "" + @echo "Sync" + @echo " check Validate skill placeholders against config" + @echo " dev Preview sync output (dry run)" + @echo " sync Regenerate adapter output from skills/" + @echo "" + @echo "Git workflow (from Makefile.agents.mk)" + @echo " branch name=X Create feature branch from $(INTEGRATION_BRANCH)" + @echo " pr Push and open PR targeting $(INTEGRATION_BRANCH)" + @echo " merge Merge current PR into $(INTEGRATION_BRANCH)" + @echo " abandon Discard changes and delete current feature branch" + @echo "" + @echo "Versioning" + @echo " bump-patch Bump patch (X.Y.Z+1), commit, and tag" + @echo " bump-minor Bump minor (X.Y+1.0), commit, and tag" + @echo " bump-major Bump major (X+1.0.0), commit, and tag" + @echo " set-version V=X Set explicit version, commit, and tag" + @echo "" + @echo "Deployment" + @echo " deploy-preview Push $(INTEGRATION_BRANCH) for preview/testing" + @echo " deploy-prod Push $(PRODUCTION_BRANCH) with tags" # Validate that all skill placeholders resolve against the config. check: @@ -57,10 +88,10 @@ endif git commit -m "Bump version to $(V)" git tag v$(V) -# Publish the integration branch so consumers can pick up changes via submodule update. +# Push the integration branch for preview/testing. deploy-preview: - git push origin development + git push origin $(INTEGRATION_BRANCH) -# Publish a tagged release to main. +# Publish a tagged release to production. deploy-prod: - git push origin main --tags + git push origin $(PRODUCTION_BRANCH) --tags diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..f05c2e3 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,21 @@ +# 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/ship/review/version/release) 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 `/ship`. 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. + +## 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/VERSION b/VERSION index 0ea3a94..0c62199 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.2.1 diff --git a/adapters/codex/config.yaml b/adapters/codex/config.yaml new file mode 100644 index 0000000..95c7000 --- /dev/null +++ b/adapters/codex/config.yaml @@ -0,0 +1,8 @@ +agent: codex +description: OpenAI Codex CLI agent skills +output_directory: .agents/skills +output_extension: /SKILL.md +notes: | + Sub-agent spawning (used in the /ship review step) is not available in Codex CLI. + That step must be performed manually by pasting the review-agent prompt into a new session. + Skills are triggered by description matching or via the $skill-creator built-in. diff --git a/adapters/codex/header.md b/adapters/codex/header.md new file mode 100644 index 0000000..5345f31 --- /dev/null +++ b/adapters/codex/header.md @@ -0,0 +1,9 @@ +--- +name: {skill} +description: {description} +--- + +> **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. + +--- + diff --git a/adapters/gemini/config.yaml b/adapters/gemini/config.yaml new file mode 100644 index 0000000..547b53a --- /dev/null +++ b/adapters/gemini/config.yaml @@ -0,0 +1,8 @@ +agent: gemini +description: Google Gemini CLI agent skills +output_directory: .gemini/skills +output_extension: /SKILL.md +notes: | + Sub-agent spawning (used in the /ship review step) is not available in Gemini CLI. + That step must be performed manually by pasting the review-agent prompt into a new session. + Skills are triggered by description matching during conversation. diff --git a/adapters/gemini/header.md b/adapters/gemini/header.md new file mode 100644 index 0000000..e87555f --- /dev/null +++ b/adapters/gemini/header.md @@ -0,0 +1,9 @@ +--- +name: {skill} +description: {description} +--- + +> **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 `/ship` must be done manually using the review-agent prompt in a separate session. + +--- + diff --git a/docs/adapters.md b/docs/adapters.md index 84f5d21..3bd843c 100644 --- a/docs/adapters.md +++ b/docs/adapters.md @@ -8,6 +8,8 @@ Adapters translate Code Cannon's generic skill format into agent-specific file f |---|---|---| | `claude` | `.claude/commands/*.md` | Full feature support including sub-agent spawning | | `cursor` | `.cursor/rules/*.mdc` | Agent-requested rules; sub-agent spawning not supported | +| `codex` | `.agents/skills/*/SKILL.md` | Codex CLI skills; sub-agent spawning not supported | +| `gemini` | `.gemini/skills/*/SKILL.md` | Gemini CLI skills; sub-agent spawning not supported | ### Claude Code @@ -19,6 +21,18 @@ The Cursor adapter generates agent-requested rules in `.cursor/rules/`. Users tr **Limitation:** Cursor does not support sub-agent spawning. The review step in `/ship` (which spawns a separate review agent) must be performed manually by pasting the review-agent prompt into a new chat. +### Codex CLI + +The Codex adapter generates agent skills in `.agents/skills/`. Each skill gets its own directory with a `SKILL.md` file containing YAML frontmatter (`name` and `description`). Skills are triggered by description matching during conversation or via the `$skill-creator` built-in. + +**Limitation:** Codex CLI does not support sub-agent spawning. The review step in `/ship` must be performed manually by pasting the review-agent prompt into a new session. + +### Gemini CLI + +The Gemini adapter generates agent skills in `.gemini/skills/`. Each skill gets its own directory with a `SKILL.md` file containing YAML frontmatter (`name` and `description`). Skills are triggered by description matching during conversation. + +**Limitation:** Gemini CLI does not support sub-agent spawning. The review step in `/ship` must be performed manually by pasting the review-agent prompt into a new session. + ## Enabling adapters List the adapters you want in `.codecannon.yaml`: @@ -27,6 +41,8 @@ List the adapters you want in `.codecannon.yaml`: adapters: - claude - cursor + - codex + - gemini ``` Run `sync.sh` to generate files for all listed adapters. You can enable both simultaneously — they write to different directories and don't conflict. diff --git a/skills/qa.md b/skills/qa.md index 9ec718d..46675ad 100644 --- a/skills/qa.md +++ b/skills/qa.md @@ -5,11 +5,16 @@ description: View the QA queue or review a specific issue args: "none | issue number" --- -<!-- Mode note: The QA label workflow is primarily used in two-branch mode, where `{{QA_READY_LABEL}}` - is applied by `/ship` after merging to `{{BRANCH_DEV}}`. In trunk or three-branch mode, this label - is not applied automatically by `/ship` — set `{{QA_READY_LABEL}}` only if you have a manual or - future-skill-driven QA gate. --> +{{#if !QA_READY_LABEL}} +> **QA workflow is not configured.** `/qa` requires `QA_READY_LABEL` to be set in `.codecannon.yaml` so it can find issues waiting for QA. +> +> To enable: add `QA_READY_LABEL: ready-for-qa` (or your preferred label name) to `.codecannon.yaml` and re-run `CodeCanon/sync.sh`. +> +> Note: In trunk mode, `/ship` does not apply this label automatically — you would need to apply it manually or via a separate workflow. +Do not proceed. Stop here. +{{/if}} +{{#if QA_READY_LABEL}} ## What `/qa` does `/qa` has two modes: @@ -19,20 +24,6 @@ args: "none | issue number" --- -## Pre-flight check - -If `{{QA_READY_LABEL}}` is empty, say: - -> "The QA label workflow is not configured for this project. `/qa` requires `QA_READY_LABEL` to be set in `.codecannon.yaml` so it can find issues waiting for QA. -> -> To enable: add `QA_READY_LABEL: ready-for-qa` (or your preferred label name) to `.codecannon.yaml` and re-run `CodeCanon/sync.sh`. -> -> Note: In trunk mode, `/ship` does not apply this label automatically — you would need to apply it manually or via a separate workflow." - -Do not proceed past this point if the label is empty. - ---- - ## Mode A — No argument: QA queue view Run: @@ -118,9 +109,16 @@ On no, stop — do not post and do not apply labels. Based on the verdict: **PASS:** +{{#if QA_PASSED_LABEL}} ``` gh issue edit <number> --add-label "{{QA_PASSED_LABEL}}" --remove-label "{{QA_READY_LABEL}}" ``` +{{/if}} +{{#if !QA_PASSED_LABEL}} +``` +gh issue edit <number> --remove-label "{{QA_READY_LABEL}}" +``` +{{/if}} **FAIL:** @@ -135,12 +133,16 @@ If one or more assignees are found, prepend the following line to the comment bo If there are multiple assignees, include one `cc @<login>` per line. If the command errors or returns no results, omit the line silently. +{{#if QA_FAILED_LABEL}} ``` gh issue edit <number> --add-label "{{QA_FAILED_LABEL}}" --remove-label "{{QA_READY_LABEL}}" ``` - -- If `{{QA_PASSED_LABEL}}` is empty (PASS case), skip `--add-label` but still run `--remove-label "{{QA_READY_LABEL}}"`. -- If `{{QA_FAILED_LABEL}}` is empty (FAIL case), skip `--add-label` but still run `--remove-label "{{QA_READY_LABEL}}"`. +{{/if}} +{{#if !QA_FAILED_LABEL}} +``` +gh issue edit <number> --remove-label "{{QA_READY_LABEL}}" +``` +{{/if}} If the `gh issue edit` command fails because a label does not exist in the repo, report the error and say: @@ -161,3 +163,4 @@ After applying labels, tell the user what was done: - Never post the comment without showing it to the user first. - Never apply labels without user confirmation (the confirmation in Step 3 is the single gate for both posting the comment and applying labels). - Never merge, deploy, or take any action beyond labeling and commenting. +{{/if}} diff --git a/skills/release.md b/skills/release.md index aae011f..d20526a 100644 --- a/skills/release.md +++ b/skills/release.md @@ -1,33 +1,35 @@ --- skill: release type: skill -description: "Create a GitHub Release; in two-branch and three-branch mode, also promotes the pre-production branch to `{{BRANCH_PROD}}`" +description: "Create a GitHub Release; in multi-branch mode, also promotes the pre-production branch to `{{BRANCH_PROD}}`" args: none --- ## What `/release` does -Creates a GitHub Release and, in two-branch and three-branch mode, promotes the pre-production branch to `{{BRANCH_PROD}}`. Run this after preview/staging testing is confirmed. +{{#if !BRANCH_DEV}} +Creates a GitHub Release from `{{BRANCH_PROD}}`. Run this after `/version` has tagged the release. +{{/if}} +{{#if BRANCH_DEV}} +{{#if !BRANCH_TEST}} +Creates a GitHub Release and promotes `{{BRANCH_DEV}}` to `{{BRANCH_PROD}}`. Run this after preview testing is confirmed. +{{/if}} +{{#if BRANCH_TEST}} +Creates a GitHub Release and promotes `{{BRANCH_TEST}}` to `{{BRANCH_PROD}}`. Run this after staging testing is confirmed. +{{/if}} +{{/if}} --- -## Step 1 — Verify state and determine mode +## Step 1 — Verify state Check the current branch: ```bash git branch --show-current ``` -**Determine mode:** -- If `{{BRANCH_DEV}}` is empty → **Trunk Mode**. Follow the Trunk Mode section below. -- If `{{BRANCH_DEV}}` is set but `{{BRANCH_TEST}}` is empty → **Two-Branch Mode**. Follow the Two-Branch Mode section below. -- If both `{{BRANCH_DEV}}` and `{{BRANCH_TEST}}` are set → **Three-Branch Mode**. Follow the Three-Branch Mode section below. - ---- - -## Trunk Mode - -### Step 1T — Verify on `{{BRANCH_PROD}}` with tag +{{#if !BRANCH_DEV}} +### Verify on `{{BRANCH_PROD}}` with tag If not on `{{BRANCH_PROD}}`, switch to it: ```bash @@ -41,7 +43,9 @@ git describe --exact-match --tags HEAD 2>/dev/null If no tag is found, warn: "No version tag found on HEAD. Run `/version` first to tag this release before running `/release`." Stop unless the user explicitly says to proceed anyway. -### Step 2T — Compute release contents +--- + +## Step 2 — Compute release contents Find the previous tag to determine the range: ```bash @@ -64,7 +68,9 @@ Extract `Closes #N` references from PR bodies (trunk PRs use `Closes #N`). Compi - List of PRs included (number + title) - List of issues linked via `Closes #N` -### Step 3T — HUMAN GATE +--- + +## Step 3 — HUMAN GATE Show the user the release summary. Example format: @@ -84,7 +90,9 @@ Have you confirmed everything above is ready for production? Type 'release' to c Wait for the user to type "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change. -### Step 4T — Create GitHub Release +--- + +## Step 4 — Create GitHub Release The version tag and PR/issue list are already known. If no previous tag exists, omit the "Full changelog" line. @@ -106,17 +114,17 @@ Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had After the command runs, note the release URL from the output. -### Step 5T — Report +--- + +## Step 5 — Report Tell the user: > "Released vX.Y.Z. Issues closed on merge. GitHub Release vX.Y.Z created at `<url>`. Run `{{DEPLOY_PROD_CMD}}` to ship to production." - ---- - -## Two-Branch Mode - -### Step 1 — Verify state +{{/if}} +{{#if BRANCH_DEV}} +{{#if !BRANCH_TEST}} +### Verify on `{{BRANCH_DEV}}` with tag If not on `{{BRANCH_DEV}}`, switch to it: ```bash @@ -130,7 +138,9 @@ git describe --exact-match --tags HEAD 2>/dev/null If no tag is found, warn: "No version tag found on HEAD. Run `/version` first to tag this release before promoting it." Stop unless the user explicitly says to proceed anyway. -### Step 2 — Compute what's being promoted +--- + +## Step 2 — Compute what's being promoted Find all merge commits in `{{BRANCH_DEV}}` not yet in `{{BRANCH_PROD}}`: ```bash @@ -151,7 +161,9 @@ Compile: - List of PRs being promoted (number + title) - List of open issues linked to those PRs -### Step 3 — HUMAN GATE +--- + +## Step 3 — HUMAN GATE Show the user the release summary. Example format: @@ -171,7 +183,9 @@ Have you tested all of the above on preview? Type 'release' to confirm. Wait for the user to type "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change. -### Step 4 — Create PR: `{{BRANCH_DEV}}` → `{{BRANCH_PROD}}` +--- + +## Step 4 — Create PR: `{{BRANCH_DEV}}` → `{{BRANCH_PROD}}` ```bash gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_DEV}} \ @@ -193,7 +207,9 @@ Note the PR number from the output. The `Closes #N` lines will auto-close the linked issues because this PR merges into `{{BRANCH_PROD}}` (the default branch). -### Step 5 — Merge +--- + +## Step 5 — Merge Do NOT use `{{MERGE_CMD}}` — it refuses PRs targeting `{{BRANCH_PROD}}`. Use `gh pr merge` directly: @@ -201,7 +217,9 @@ Do NOT use `{{MERGE_CMD}}` — it refuses PRs targeting `{{BRANCH_PROD}}`. Use ` gh pr merge <pr-number> --merge ``` -### Step 6 — Create GitHub Release +--- + +## Step 6 — Create GitHub Release The version tag (from Step 1) and the PR/issue list (from Step 2) are already known. Find the previous tag to build the changelog link: @@ -231,17 +249,16 @@ Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had After the command runs, note the release URL from the output. -### Step 7 — Report +--- + +## Step 7 — Report Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `{{DEPLOY_PROD_CMD}}` to ship to production." - ---- - -## Three-Branch Mode - -### Step 1 — Verify state +{{/if}} +{{#if BRANCH_TEST}} +### Verify on `{{BRANCH_TEST}}` with tag If not on `{{BRANCH_TEST}}`, switch to it: ```bash @@ -255,7 +272,9 @@ git describe --exact-match --tags HEAD 2>/dev/null If no tag is found, warn: "No version tag found on HEAD. Run `/version` first to tag this release before promoting it." Stop unless the user explicitly says to proceed anyway. -### Step 2 — Compute what's being promoted +--- + +## Step 2 — Compute what's being promoted Find all merge commits in `{{BRANCH_TEST}}` not yet in `{{BRANCH_PROD}}`: ```bash @@ -275,7 +294,9 @@ Compile (best-effort): - List of PRs being promoted (number + title) - List of open issues linked to those PRs -### Step 3 — HUMAN GATE +--- + +## Step 3 — HUMAN GATE Show the user the release summary. Example format: @@ -295,7 +316,9 @@ Have you tested all of the above on the {{BRANCH_TEST}} environment? Type 'relea Wait for the user to type "release" or an explicit confirmation. Any other response → stop and ask what they'd like to change. -### Step 4 — Create PR: `{{BRANCH_TEST}}` → `{{BRANCH_PROD}}` +--- + +## Step 4 — Create PR: `{{BRANCH_TEST}}` → `{{BRANCH_PROD}}` ```bash gh pr create --base {{BRANCH_PROD}} --head {{BRANCH_TEST}} \ @@ -317,7 +340,9 @@ Note the PR number from the output. The `Closes #N` lines will auto-close the linked issues because this PR merges into `{{BRANCH_PROD}}` (the default branch). -### Step 5 — Merge +--- + +## Step 5 — Merge Do NOT use `{{MERGE_CMD}}` — it refuses PRs targeting `{{BRANCH_PROD}}`. Use `gh pr merge` directly: @@ -325,7 +350,9 @@ Do NOT use `{{MERGE_CMD}}` — it refuses PRs targeting `{{BRANCH_PROD}}`. Use ` gh pr merge <pr-number> --merge ``` -### Step 6 — Create GitHub Release +--- + +## Step 6 — Create GitHub Release The version tag (from Step 1) and the PR/issue list (from Step 2) are already known. Find the previous tag to build the changelog link: @@ -355,8 +382,12 @@ Format each PR line as `- #<linked-issue> — <PR title> (PR #<N>)`. If a PR had After the command runs, note the release URL from the output. -### Step 7 — Report +--- + +## Step 7 — Report Tell the user: > "Released vX.Y.Z. Issues #N, #M closed automatically. GitHub Release vX.Y.Z created at `<url>`. Run `{{DEPLOY_PROD_CMD}}` to ship to production." +{{/if}} +{{/if}} diff --git a/skills/review.md b/skills/review.md index 245ea7e..e1f4808 100644 --- a/skills/review.md +++ b/skills/review.md @@ -34,9 +34,15 @@ If no PR is found, abort and say: "No open PR found for the current branch. Pass --- -## Step 2 — Spawn review agent +## Step 2 — Run the review -Load `{{REVIEW_AGENT_PROMPT}}` and invoke a review agent with the PR number. The review agent will: +Load `{{REVIEW_AGENT_PROMPT}}` and perform the review for the PR number. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: 1. Read the PR diff via `gh pr diff <number>` 2. Read any files needed for full context 3. Post findings as a PR comment via `gh pr comment <number>` diff --git a/skills/setup.md b/skills/setup.md index 18f8938..7cff646 100644 --- a/skills/setup.md +++ b/skills/setup.md @@ -208,13 +208,15 @@ If nothing is confidently broken, do not flag anything. When in doubt, do not fl If anything is flagged: show the specific key names and what they should likely be changed to. Do not modify the file. Tell the user to update these values and run `/setup` again. Stop. -### Check 7 — .claude/commands/ present and populated +### Check 7 — Generated skill output present + +Check whether sync.sh has been run by looking for any of the adapter output directories configured in `.codecannon.yaml`: ```bash -test -d .claude/commands +test -d .claude/commands || test -d .cursor/rules || test -d .agents/skills || test -d .gemini/skills ``` -If absent, or the directory exists but is empty: "sync.sh hasn't been run yet — the skill commands don't exist." +If none exist: "sync.sh hasn't been run yet — the skill commands don't exist." Show: diff --git a/skills/ship.md b/skills/ship.md index def6ca6..9049720 100644 --- a/skills/ship.md +++ b/skills/ship.md @@ -5,12 +5,6 @@ description: Type-check, commit, open PR, review, and merge to the integration b args: none --- -<!-- Mode reference (derived from config at runtime): - Trunk mode: BRANCH_DEV empty, BRANCH_TEST empty → PR targets BRANCH_PROD - Two-branch mode: BRANCH_DEV set, BRANCH_TEST empty → PR targets BRANCH_DEV - Three-branch mode: BRANCH_DEV set, BRANCH_TEST set → PR targets BRANCH_DEV ---> - ## What `/ship` does `/ship` is Phase 3 of the workflow: type-check, commit, open PR, spawn review agent, act on verdict. @@ -24,10 +18,14 @@ Check current branch: git branch --show-current ``` -Determine which branches are protected (i.e., not a feature branch): -- Always: `{{BRANCH_PROD}}` -- If `{{BRANCH_DEV}}` is non-empty: also `{{BRANCH_DEV}}` -- If `{{BRANCH_TEST}}` is non-empty: also `{{BRANCH_TEST}}` +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: @@ -90,13 +88,16 @@ git ls-files CODEOWNERS .github/CODEOWNERS docs/CODEOWNERS 2>/dev/null If the output is non-empty, inform the user: "CODEOWNERS file detected — GitHub will automatically request reviews from code owners." -Determine the PR target branch: -- If `{{BRANCH_DEV}}` is non-empty, target `{{BRANCH_DEV}}` (two-branch or three-branch mode). -- Otherwise, target `{{BRANCH_PROD}}` (trunk mode). +{{#if BRANCH_DEV}} +PR target branch: `{{BRANCH_DEV}}` + +Use `Issue #<number>` as the issue reference — the issue stays open until `/release` promotes to `{{BRANCH_PROD}}`. +{{/if}} +{{#if !BRANCH_DEV}} +PR target branch: `{{BRANCH_PROD}}` (trunk mode) -Determine the issue reference format: -- If the PR targets `{{BRANCH_PROD}}` (trunk mode), use `Closes #<number>` — merging to the default branch will auto-close the issue. -- Otherwise, use `Issue #<number>` — the issue stays open until `/release` promotes to `{{BRANCH_PROD}}`. +Use `Closes #<number>` as the issue reference — merging to the default branch will auto-close the issue. +{{/if}} Then create the PR with explicit title and body (never use an interactive editor): ``` @@ -108,9 +109,11 @@ EOF )" ``` -If `{{DEFAULT_REVIEWERS}}` is non-empty, add `--reviewer {{DEFAULT_REVIEWERS}}` to the `gh pr create` command above. If `{{DEFAULT_REVIEWERS}}` is empty, omit the `--reviewer` flag entirely — do not pass `--reviewer ""`. +{{#if DEFAULT_REVIEWERS}} +Add `--reviewer {{DEFAULT_REVIEWERS}}` to the `gh pr create` command above. -If a CODEOWNERS file exists and `{{DEFAULT_REVIEWERS}}` is also set, both apply: CODEOWNERS triggers automatic review requests from GitHub; the `--reviewer` flag adds the explicitly configured handles on top. +If a CODEOWNERS file exists, both apply: CODEOWNERS triggers automatic review requests from GitHub; the `--reviewer` flag adds the explicitly configured handles on top. +{{/if}} **Hard rule**: Never auto-select reviewers beyond what is configured in `DEFAULT_REVIEWERS` or declared in CODEOWNERS. Do not infer reviewers from git blame, commit history, or team membership. @@ -122,20 +125,29 @@ Omit the issue line entirely if no linked issue was identified in Step 3. If `{{REVIEW_GATE}}` is `"off"`, skip directly to Step 7 (merge without review). -Otherwise, load `{{REVIEW_AGENT_PROMPT}}` and invoke a review agent, passing the PR number. The review agent will: +Otherwise, load `{{REVIEW_AGENT_PROMPT}}` and perform the review for this PR. + +**If sub-agent spawning is supported** (e.g. Claude Code): invoke a dedicated review agent with the prompt and PR number. + +**If sub-agent spawning is not supported** (e.g. Codex, Cursor, Gemini): perform the review yourself inline — follow the instructions in the review-agent prompt directly. + +The review must: 1. Read the PR diff 2. Read relevant files for context 3. Post findings as a PR comment via `gh pr comment <number>` -Wait for the review agent to complete and report its verdict. +Wait for the review to complete and report its verdict. --- ## Step 7 — Act on verdict -Determine merge command (used by all paths below): -- If in **trunk mode** (`{{BRANCH_DEV}}` is empty): use `gh pr merge <number> --merge` directly — `{{MERGE_CMD}}` may refuse merges targeting `{{BRANCH_PROD}}`. -- Otherwise: use `{{MERGE_CMD}}`. +{{#if BRANCH_DEV}} +Merge command (used by all paths below): `{{MERGE_CMD}}` +{{/if}} +{{#if !BRANCH_DEV}} +Merge command (used by all paths below): `gh pr merge <number> --merge` (trunk mode — `{{MERGE_CMD}}` may refuse merges targeting `{{BRANCH_PROD}}`). +{{/if}} --- @@ -171,18 +183,30 @@ Return to the coding loop. When fixed, run `/ship` again from Step 1. ### After merge — QA label and success report -Apply the QA label only in **two-branch mode** (`{{BRANCH_DEV}}` is set AND `{{BRANCH_TEST}}` is empty): -- If `{{QA_READY_LABEL}}` is non-empty AND a linked issue number was identified in Step 3: - ``` - gh issue edit <number> --add-label "{{QA_READY_LABEL}}" - ``` -- In trunk mode or three-branch mode: skip the QA label entirely. -- If `{{QA_READY_LABEL}}` is empty or no linked issue was found: skip silently. +{{#if QA_READY_LABEL}} +{{#if BRANCH_DEV}} +{{#if !BRANCH_TEST}} +If a linked issue number was identified in Step 3, apply the QA label: +``` +gh issue edit <number> --add-label "{{QA_READY_LABEL}}" +``` +If no linked issue was found, skip silently. +{{/if}} +{{/if}} +{{/if}} Report success based on mode: -- **Trunk mode**: "PR merged. Issue #N closed automatically. Run `{{DEPLOY_PROD_CMD}}` when ready to deploy to production." -- **Two-branch mode**: "PR merged. Issues stay open until testing confirms the fix. Run `{{DEPLOY_PREVIEW_CMD}}` when ready to deploy to preview." -- **Three-branch mode**: "PR merged to `{{BRANCH_DEV}}`. Promote to `{{BRANCH_TEST}}` when ready for staging." +{{#if !BRANCH_DEV}} +"PR merged. Issue #N closed automatically. Run `{{DEPLOY_PROD_CMD}}` when ready to deploy to production." +{{/if}} +{{#if BRANCH_DEV}} +{{#if !BRANCH_TEST}} +"PR merged. Issues stay open until testing confirms the fix. Run `{{DEPLOY_PREVIEW_CMD}}` when ready to deploy to preview." +{{/if}} +{{#if BRANCH_TEST}} +"PR merged to `{{BRANCH_DEV}}`. Promote to `{{BRANCH_TEST}}` when ready for staging." +{{/if}} +{{/if}} --- @@ -192,5 +216,10 @@ Report success based on mode: - When `{{REVIEW_GATE}}` is `"ai"`, never merge if the review verdict is REQUEST CHANGES. - When `{{REVIEW_GATE}}` is `"advisory"`, always merge after review completes, regardless of verdict. - When `{{REVIEW_GATE}}` is `"off"`, skip the review agent entirely — merge immediately after checks pass. -- In trunk mode, merges target `{{BRANCH_PROD}}`. Otherwise, `/ship` merges only to `{{BRANCH_DEV}}` — never directly to `{{BRANCH_TEST}}` or `{{BRANCH_PROD}}`. +{{#if BRANCH_DEV}} +- `/ship` merges only to `{{BRANCH_DEV}}` — never directly to `{{BRANCH_PROD}}`. +{{/if}} +{{#if !BRANCH_DEV}} +- Merges target `{{BRANCH_PROD}}` (trunk mode). +{{/if}} - If `{{MERGE_CMD}}` fails for any reason, report it and stop — do not attempt workarounds. diff --git a/skills/start.md b/skills/start.md index 792e746..50772d1 100644 --- a/skills/start.md +++ b/skills/start.md @@ -27,7 +27,12 @@ The argument string may contain optional inline flags after the description. Par 1. **Identify flags** — scan for the first token that starts with `--label`, `-l`, `--milestone`, or `-m`. Everything before it is the **description**. Everything from the first flag onward is **flags**. 2. **`--label <value>` / `-l <value>`** — comma-separated label string (e.g. `bug` or `enhancement,ux`). If provided, it **bypasses label auto-selection entirely** for this invocation — use the value verbatim. Labels containing spaces must be quoted (e.g. `--label "good first issue"`). -3. **`--milestone <value>` / `-m <value>`** — milestone name or number (e.g. `Sprint 4` or `12`). If provided, it **replaces** `{{DEFAULT_MILESTONE}}` for this invocation. Pass the value as-is; GitHub accepts both names and numbers. +{{#if DEFAULT_MILESTONE}} +3. **`--milestone <value>` / `-m <value>`** — milestone name or number (e.g. `Sprint 4` or `12`). If provided, it **replaces** the default milestone `{{DEFAULT_MILESTONE}}` for this invocation. Pass the value as-is; GitHub accepts both names and numbers. +{{/if}} +{{#if !DEFAULT_MILESTONE}} +3. **`--milestone <value>` / `-m <value>`** — milestone name or number (e.g. `Sprint 4` or `12`). Pass the value as-is; GitHub accepts both names and numbers. +{{/if}} 4. **Flags may appear in any order** after the description. **Label resolution (three-tier, Case A only):** @@ -35,8 +40,13 @@ The argument string may contain optional inline flags after the description. Par After parsing flags, determine the active labels in this order: 1. **Per-invocation flag** — if `--label <value>` was in `$ARGUMENTS`, use that value verbatim. Skip all remaining steps. -2. **Pool-based selection** — if `{{TICKET_LABELS}}` is non-empty, it contains the allowed label pool as a comma-separated list. The agent must select 1–3 labels from this pool that genuinely fit the task description and implementation approach. Do not apply labels mechanically — pick only what fits. If no pool label fits the task, fall through to step 3. +{{#if TICKET_LABELS}} +2. **Pool-based selection** — the allowed label pool is: `{{TICKET_LABELS}}` (comma-separated). Select 1–3 labels from this pool that genuinely fit the task description and implementation approach. Do not apply labels mechanically — pick only what fits. If no pool label fits the task, fall through to step 3. - If any selected label name contains a space (e.g. `good first issue`), quote the entire `--label` value. +{{/if}} +{{#if !TICKET_LABELS}} +2. **Pool-based selection** — no label pool is configured. Fall through to step 3. +{{/if}} 3. **No label / creation** — if the pool is empty or no pool label fits: - If `{{TICKET_LABEL_CREATION_ALLOWED}}` is `true` (case-insensitive string match): the agent **may** create a new label before applying it: ```bash @@ -44,16 +54,20 @@ After parsing flags, determine the active labels in this order: ``` Use judgment — only create a label with clear reuse value. Do not create near-duplicates of existing pool labels. - If `{{TICKET_LABEL_CREATION_ALLOWED}}` is `false` or unset: omit `--label` entirely. Proceed silently; do not inform the user. +{{#if !TICKET_LABELS}} -> **Tip:** If `{{TICKET_LABELS}}` is empty, run `/setup` to populate it from your repo's existing GitHub labels. +> **Tip:** Run `/setup` to populate TICKET_LABELS from your repo's existing GitHub labels. +{{/if}} **Milestone resolution (three-tier, Case A only):** After parsing flags, determine the active milestone in this order: 1. **Per-invocation flag** — if `--milestone <value>` was in `$ARGUMENTS`, use that value. Stop. -2. **Config default** — if `{{DEFAULT_MILESTONE}}` is non-empty, use that value. Stop. -3. **Auto-detect** — if both are absent or empty, query open milestones: +{{#if DEFAULT_MILESTONE}} +2. **Config default** — use `{{DEFAULT_MILESTONE}}`. Stop. +{{/if}} +3. **Auto-detect** — if no milestone is resolved yet, query open milestones: ```bash gh api repos/{owner}/{repo}/milestones --jq '[.[] | select(.state=="open")] | {count: length, milestones: [.[] | {number: .number, title: .title}]}' ``` @@ -64,12 +78,30 @@ After parsing flags, determine the active milestone in this order: **Examples:** +{{#if TICKET_LABELS}} +{{#if DEFAULT_MILESTONE}} | `$ARGUMENTS` | Description | Labels | Milestone | |---|---|---|---| -| `Add dark mode toggle to settings page` | `Add dark mode toggle to settings page` | auto-selected from `{{TICKET_LABELS}}` pool | `{{DEFAULT_MILESTONE}}` | -| `Add dark mode --label enhancement` | `Add dark mode` | `enhancement` (verbatim, no pool selection) | `{{DEFAULT_MILESTONE}}` | +| `Add dark mode toggle to settings page` | `Add dark mode toggle to settings page` | auto-selected from pool | `{{DEFAULT_MILESTONE}}` | +| `Add dark mode --label enhancement` | `Add dark mode` | `enhancement` (verbatim) | `{{DEFAULT_MILESTONE}}` | | `Add dark mode --label enhancement,ux --milestone "Sprint 4"` | `Add dark mode` | `enhancement,ux` (verbatim) | `Sprint 4` | -| `Add dark mode --milestone sprint-4` | `Add dark mode` | auto-selected from `{{TICKET_LABELS}}` pool | `sprint-4` | +| `Add dark mode --milestone sprint-4` | `Add dark mode` | auto-selected from pool | `sprint-4` | +{{/if}} +{{#if !DEFAULT_MILESTONE}} +| `$ARGUMENTS` | Description | Labels | Milestone | +|---|---|---|---| +| `Add dark mode toggle to settings page` | `Add dark mode toggle to settings page` | auto-selected from pool | auto-detected | +| `Add dark mode --label enhancement` | `Add dark mode` | `enhancement` (verbatim) | auto-detected | +| `Add dark mode --label enhancement,ux --milestone "Sprint 4"` | `Add dark mode` | `enhancement,ux` (verbatim) | `Sprint 4` | +{{/if}} +{{/if}} +{{#if !TICKET_LABELS}} +| `$ARGUMENTS` | Description | Labels | Milestone | +|---|---|---|---| +| `Add dark mode toggle to settings page` | `Add dark mode toggle to settings page` | none (no label pool) | auto-detected | +| `Add dark mode --label enhancement` | `Add dark mode` | `enhancement` (verbatim) | auto-detected | +| `Add dark mode --label enhancement,ux --milestone "Sprint 4"` | `Add dark mode` | `enhancement,ux` (verbatim) | `Sprint 4` | +{{/if}} > Replace vs append: flags **replace** auto-selection entirely, they do not append. This avoids silent label duplication and milestone conflicts. @@ -210,4 +242,15 @@ When done, say: **"The code is ready for review. Please run `{{DEV_CMD}}` and te - If already on a feature branch when `/start` is invoked, warn the user before creating another branch. - `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 `{{TICKET_LABELS}}` → omit (or create if `{{TICKET_LABEL_CREATION_ALLOWED}}` is `true`). Never apply a label not in `{{TICKET_LABELS}}` unless `{{TICKET_LABEL_CREATION_ALLOWED}}` is `true`. Milestone resolution order: per-invocation flag → `{{DEFAULT_MILESTONE}}` config → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. +{{#if TICKET_LABELS}} +- Apply resolved labels and milestone to every new issue. Label resolution order: per-invocation flag → pool selection from `{{TICKET_LABELS}}` → omit (or create if `{{TICKET_LABEL_CREATION_ALLOWED}}` is `true`). Never apply a label not in `{{TICKET_LABELS}}` unless `{{TICKET_LABEL_CREATION_ALLOWED}}` is `true`. +{{/if}} +{{#if !TICKET_LABELS}} +- Apply labels only when explicitly provided via `--label`. No label pool is configured. +{{/if}} +{{#if DEFAULT_MILESTONE}} +- Milestone resolution order: per-invocation flag → `{{DEFAULT_MILESTONE}}` config default → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. +{{/if}} +{{#if !DEFAULT_MILESTONE}} +- Milestone resolution order: per-invocation flag → auto-detected from GitHub open milestones. Never prompt for a milestone more than once per invocation. +{{/if}} diff --git a/skills/version.md b/skills/version.md index 35a372f..7446fc4 100644 --- a/skills/version.md +++ b/skills/version.md @@ -12,10 +12,17 @@ Run: git branch --show-current ``` -Determine the required branch: -- If `{{BRANCH_TEST}}` is non-empty → required branch is `{{BRANCH_TEST}}` (three-branch mode). -- Else if `{{BRANCH_DEV}}` is non-empty → required branch is `{{BRANCH_DEV}}` (two-branch mode). -- Else → required branch is `{{BRANCH_PROD}}` (trunk mode). +{{#if BRANCH_TEST}} +Required branch: `{{BRANCH_TEST}}` (three-branch mode). +{{/if}} +{{#if !BRANCH_TEST}} +{{#if BRANCH_DEV}} +Required branch: `{{BRANCH_DEV}}` (two-branch mode). +{{/if}} +{{#if !BRANCH_DEV}} +Required branch: `{{BRANCH_PROD}}` (trunk mode). +{{/if}} +{{/if}} If not on the required branch, abort and say: "Switch to `<required-branch>` before running `/version`." @@ -78,7 +85,14 @@ Both the version bump commit and the tag must be pushed. Tell the user the new version and tag: -Report based on mode: -- **Trunk mode** (`{{BRANCH_DEV}}` empty): "Tagged vX.Y.Z. Run `/release` to create the GitHub Release." -- **Two-branch mode** (`{{BRANCH_DEV}}` set, `{{BRANCH_TEST}}` empty): "Tagged vX.Y.Z. Run `{{DEPLOY_PREVIEW_CMD}}` to deploy to preview for testing. When testing is complete, run `/release`." -- **Three-branch mode** (both set): "Tagged vX.Y.Z. Run `{{DEPLOY_PREVIEW_CMD}}` to deploy to staging. When testing is complete, run `/release`." +{{#if !BRANCH_DEV}} +"Tagged vX.Y.Z. Run `/release` to create the GitHub Release." +{{/if}} +{{#if BRANCH_DEV}} +{{#if !BRANCH_TEST}} +"Tagged vX.Y.Z. Run `{{DEPLOY_PREVIEW_CMD}}` to deploy to preview for testing. When testing is complete, run `/release`." +{{/if}} +{{#if BRANCH_TEST}} +"Tagged vX.Y.Z. Run `{{DEPLOY_PREVIEW_CMD}}` to deploy to staging. When testing is complete, run `/release`." +{{/if}} +{{/if}} diff --git a/sync.sh b/sync.sh index 67c30ff..0fd6f28 100755 --- a/sync.sh +++ b/sync.sh @@ -94,6 +94,55 @@ def parse_frontmatter(text): return fm, body.strip() +# ── Conditional blocks ──────────────────────────────────────────────────────── +# Supports {{#if KEY}} / {{/if}} and {{#if !KEY}} / {{/if}}. +# A key is "truthy" when it exists in values and is non-empty after stripping. +# The directive lines are always removed from the output. +# Nesting is supported (inner blocks are evaluated innermost-first). + +_IF_OPEN = re.compile(r'^\s*\{\{#if\s+(!?)([A-Z_]+)\}\}\s*$') +_IF_CLOSE = re.compile(r'^\s*\{\{/if\}\}\s*$') + + +def apply_conditionals(text, values): + """Process {{#if KEY}} / {{#if !KEY}} ... {{/if}} blocks (innermost-first).""" + changed = True + while changed: + changed = False + lines = text.split('\n') + # Find the first {{/if}} and match it with the nearest preceding {{#if}} + close_idx = None + for i, line in enumerate(lines): + if _IF_CLOSE.match(line): + close_idx = i + break + if close_idx is None: + break + open_idx = None + for i in range(close_idx - 1, -1, -1): + if _IF_OPEN.match(lines[i]): + open_idx = i + break + if open_idx is None: + break # malformed — stop processing + + m = _IF_OPEN.match(lines[open_idx]) + negated = m.group(1) == '!' + key = m.group(2) + truthy = bool(values.get(key, '').strip()) + keep = (not truthy) if negated else truthy + + if keep: + lines = lines[:open_idx] + lines[open_idx + 1:close_idx] + lines[close_idx + 1:] + else: + lines = lines[:open_idx] + lines[close_idx + 1:] + + text = '\n'.join(lines) + changed = True + + return text + + # ── Placeholder substitution ────────────────────────────────────────────────── def apply_placeholders(text, values): @@ -207,13 +256,18 @@ def sync_skill(skill_path, adapter, project_config, project_root, args): no_header = fm.get('no_invocation_header', 'false').lower() == 'true' output_path_override = fm.get('output_path_override', '') - # Apply placeholder substitution + # Evaluate conditional blocks, then substitute placeholders + body = apply_conditionals(body, project_config) body = apply_placeholders(body, project_config) + if fm.get('description'): + fm['description'] = apply_placeholders(fm['description'], project_config) if output_path_override: output_path_override = apply_placeholders(output_path_override, project_config) - # Warn about unresolved placeholders + # Warn about unresolved placeholders (check body and description) unresolved = find_unresolved(body) + if fm.get('description'): + unresolved += find_unresolved(fm['description']) if unresolved: print(f" Warning: {skill_name} has unresolved placeholders: {', '.join(unresolved)}") @@ -275,8 +329,12 @@ def validate_placeholders(skill_files, project_config): errors = [] for skill_path in skill_files: raw = skill_path.read_text() - _, body = parse_frontmatter(raw) - missing = [p for p in find_unresolved(body) if p not in project_config] + fm, body = parse_frontmatter(raw) + # Check body and frontmatter description + text_to_check = body + if fm.get('description'): + text_to_check += '\n' + fm['description'] + missing = [p for p in find_unresolved(text_to_check) if p not in project_config] for p in missing: errors.append(f" {skill_path.name}: {{{{{p}}}}} not defined in config") return errors