codebyplan 1.13.43 → 1.13.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/cli.js +5079 -1556
  2. package/package.json +1 -1
  3. package/templates/agents/cbp-task-check.md +1 -3
  4. package/templates/agents/cbp-task-planner.md +8 -6
  5. package/templates/github-workflows/publish.yml +93 -21
  6. package/templates/hooks/cbp-auto-test-hooks.sh +1 -0
  7. package/templates/hooks/cbp-e2e-spec-patterns.sh +100 -0
  8. package/templates/hooks/cbp-lint-format-on-edit.sh +1 -0
  9. package/templates/hooks/cbp-maestro-yaml-validate.sh +1 -0
  10. package/templates/hooks/cbp-pre-commit-quality-gate.sh +1 -0
  11. package/templates/hooks/cbp-statusline.sh +0 -0
  12. package/templates/hooks/cbp-subagent-statusline.sh +0 -0
  13. package/templates/hooks/cbp-test-coverage-gate.sh +1 -0
  14. package/templates/hooks/cbp-test-hooks.sh +1 -0
  15. package/templates/hooks/hooks.json +4 -0
  16. package/templates/hooks/verify-parity.sh +20 -0
  17. package/templates/rules/parallel-waves.md +8 -3
  18. package/templates/rules/scope-vocabulary.md +4 -3
  19. package/templates/settings.project.base.json +22 -0
  20. package/templates/skills/cbp-build-cc-claude-file/SKILL.md +11 -1
  21. package/templates/skills/cbp-build-cc-claude-file/scripts/validate-claude-file.sh +72 -0
  22. package/templates/skills/cbp-build-cc-mode/SKILL.md +12 -16
  23. package/templates/skills/cbp-build-cc-rule/SKILL.md +11 -1
  24. package/templates/skills/cbp-build-cc-rule/scripts/validate-rule.sh +69 -0
  25. package/templates/skills/cbp-build-cc-settings/SKILL.md +2 -2
  26. package/templates/skills/cbp-build-cc-settings/scripts/validate-settings.sh +67 -0
  27. package/templates/skills/cbp-checkpoint-create/SKILL.md +12 -4
  28. package/templates/skills/cbp-checkpoint-end/SKILL.md +19 -11
  29. package/templates/skills/cbp-git-commit/SKILL.md +10 -12
  30. package/templates/skills/cbp-git-worktree-create/SKILL.md +7 -48
  31. package/templates/skills/cbp-git-worktree-remove/SKILL.md +23 -40
  32. package/templates/skills/cbp-map-architecture/SKILL.md +1 -0
  33. package/templates/skills/cbp-merge-main/SKILL.md +21 -26
  34. package/templates/skills/cbp-refresh-arch-map/SKILL.md +1 -0
  35. package/templates/skills/cbp-round-check/SKILL.md +37 -36
  36. package/templates/skills/cbp-round-execute/SKILL.md +9 -3
  37. package/templates/skills/cbp-session-end/SKILL.md +27 -47
  38. package/templates/skills/cbp-session-start/SKILL.md +35 -51
  39. package/templates/skills/cbp-standalone-task-start/SKILL.md +10 -19
  40. package/templates/skills/cbp-supabase-migrate/SKILL.md +24 -27
  41. package/templates/skills/cbp-task-start/SKILL.md +9 -21
  42. package/templates/skills/cbp-task-testing/SKILL.md +18 -10
@@ -39,11 +39,12 @@ A skill that carries a `model:` line is a **gap** — remove it unless a deliber
39
39
 
40
40
  ### Agents — `model:` + `effort:`
41
41
 
42
- Default `model: sonnet` + `effort: xhigh`. Fifteen of the 16 authoring agents take the default (`cbp-cc-executor`, `cbp-database-agent`, `cbp-improve-claude`, `cbp-improve-round`, `cbp-research`, `cbp-round-executor`, `cbp-security-agent`, `cbp-task-check`, `cbp-task-planner`, `cbp-testing-qa-agent`, `cbp-e2e-playwright`, `cbp-e2e-maestro`, `cbp-e2e-tauri`, `cbp-e2e-vscode`, `cbp-e2e-xcuitest`). The 16th is the one exception:
42
+ Default `model: sonnet` + `effort: xhigh`. Fifteen of the 17 authoring agents take the default (`cbp-cc-executor`, `cbp-database-agent`, `cbp-improve-claude`, `cbp-improve-round`, `cbp-research`, `cbp-round-executor`, `cbp-security-agent`, `cbp-task-check`, `cbp-task-planner`, `cbp-testing-qa-agent`, `cbp-e2e-playwright`, `cbp-e2e-maestro`, `cbp-e2e-tauri`, `cbp-e2e-vscode`, `cbp-e2e-xcuitest`). The other two are exceptions:
43
43
 
44
- | agent | model | effort | reason |
45
- | -------------------- | ----- | ------ | ----------------------------------------------------------------------------------- |
46
- | cbp-mechanical-edits | haiku | low | Mechanical rename/substitution/frontmatter-edit subagent; no judgment, pure dispatch |
44
+ | agent | model | effort | reason |
45
+ | -------------------- | ------ | ------ | ----------------------------------------------------------------------------------- |
46
+ | cbp-mechanical-edits | haiku | low | Mechanical rename/substitution/frontmatter-edit subagent; no judgment, pure dispatch |
47
+ | cbp-map-architecture | sonnet | high | Read-only module analyzer; bounded structured-map generation, lighter than xhigh |
47
48
 
48
49
  `model: inherit` is NOT permitted for agents — pin an explicit alias (`sonnet` / `haiku`) or a full ID.
49
50
 
@@ -53,18 +54,13 @@ A skill's `effort:` applies to the inline-running turn (the orchestrator turn th
53
54
 
54
55
  ## Audit Mode
55
56
 
56
- Invoked with no arguments. Procedure:
57
-
58
- 1. Glob `packages/codebyplan-package/templates/agents/*.md` and `packages/codebyplan-package/templates/skills/*/SKILL.md`.
59
- 2. For each file, read the frontmatter and parse `model:` + `effort:`.
60
- 3. Apply the convention by surface:
61
- - **Skill**: `model:` MUST be ABSENT; `effort:` MUST be present and one of `low`/`medium`/`high`/`xhigh`/`max` (default `xhigh`).
62
- - **Agent**: `model:` MUST be present (per the agent table; default `sonnet`); `effort:` present (default `xhigh`).
63
- 4. Emit one pipe-delimited line per file: `path | current_model/current_effort | expected | status`.
64
- 5. Status values:
65
- - `ok` — matches the convention.
66
- - `gap` — a skill that carries a `model:` line, a skill missing `effort:`, an agent missing/incorrect `model:`, or any missing/invalid `effort:`.
67
- 6. Print a summary: total audited, count `ok`, count `gap`.
57
+ Run `codebyplan claude audit-mode` — it scans `packages/codebyplan-package/templates/agents/*.md` and `packages/codebyplan-package/templates/skills/*/SKILL.md`, applies the convention from the matrix above, and emits a pipe-delimited table:
58
+
59
+ ```
60
+ path | surface | model/effort | expected | status
61
+ ```
62
+
63
+ Plus a summary line: `N audited, N ok, N gaps`. Exits 0 when all files comply; exits 1 on any gap. Pass `--json` for a machine-readable entries array.
68
64
 
69
65
  The audit is read-only.
70
66
 
@@ -153,7 +153,17 @@ ln -s ~/company-standards/security.md .claude/rules/security.md
153
153
 
154
154
  Claude Code resolves symlinks normally. Circular symlinks are detected.
155
155
 
156
- ### Step 8 — Verify loading
156
+ ### Step 8 — Validate
157
+
158
+ Run the validator:
159
+
160
+ ```bash
161
+ bash "${CLAUDE_SKILL_DIR}/scripts/validate-rule.sh" "{scope-path}/{name}.md"
162
+ ```
163
+
164
+ It checks: YAML frontmatter present, `scope:` field present and valid enum value, H1 heading present and its kebab-case form matches the filename, `paths:` field (warns if absent — unconditional rules burn context on every session), line count (warns at 100, errors at 200).
165
+
166
+ ### Step 9 — Verify loading
157
167
 
158
168
  Run `/memory` in a fresh Claude Code session to confirm the rule is listed. If path-scoped, open a file matching the glob to confirm it triggers.
159
169
 
@@ -0,0 +1,69 @@
1
+ #!/bin/bash
2
+ # @scope: org-shared
3
+ # Validate a Claude Code rule file at .claude/rules/{name}.md.
4
+ # Usage: validate-rule.sh <path-to-rule-file>
5
+ # Exit 0 = valid, exit 1 = invalid (errors printed to stderr).
6
+
7
+ set -euo pipefail
8
+
9
+ FILE="${1:?Usage: validate-rule.sh <path-to-rule-file>}"
10
+
11
+ if [ ! -f "$FILE" ]; then
12
+ echo "ERROR: file not found: $FILE" >&2
13
+ exit 1
14
+ fi
15
+
16
+ errors=0
17
+ err() { echo " - $1" >&2; errors=$((errors + 1)); }
18
+
19
+ # Frontmatter must be present
20
+ if ! head -1 "$FILE" | grep -qE '^---[[:space:]]*$'; then
21
+ err "missing YAML frontmatter opener '---'"
22
+ fi
23
+
24
+ # Extract frontmatter block
25
+ fm=$(awk '/^---[[:space:]]*$/{n++; next} n==1{print} n==2{exit}' "$FILE")
26
+
27
+ # scope: field required + must match enum
28
+ if ! grep -qE '^scope:[[:space:]]*' <<< "$fm"; then
29
+ err "missing CBP required field: scope (org-shared|project-shared|repo-only:<repo-name>)"
30
+ else
31
+ scope_val=$(grep -E '^scope:' <<< "$fm" | head -1 | sed -E 's/^scope:[[:space:]]*//; s/[[:space:]]*$//')
32
+ if ! [[ "$scope_val" =~ ^(org-shared|project-shared|repo-only:[a-z0-9]([a-z0-9-]*[a-z0-9])?)$ ]]; then
33
+ err "scope value '$scope_val' is not a valid enum value (org-shared|project-shared|repo-only:<slug>)"
34
+ fi
35
+ fi
36
+
37
+ # H1 heading must be present and its kebab-case form must match the filename (sans .md)
38
+ h1=$(grep -m1 '^# ' "$FILE" | sed 's/^# //' || true)
39
+ if [ -z "$h1" ]; then
40
+ err "missing H1 heading"
41
+ else
42
+ # Convert H1 to kebab-case: lowercase, spaces→hyphens, strip non-alnum chars (except hyphens)
43
+ h1_kebab=$(echo "$h1" | tr '[:upper:]' '[:lower:]' | sed 's/ /-/g; s/_/-/g; s/[^a-z0-9-]//g')
44
+ fname=$(basename "$FILE" .md)
45
+ if [ "$h1_kebab" != "$fname" ]; then
46
+ err "H1 '$h1' (kebab: '$h1_kebab') does not match filename '$(basename "$FILE")'"
47
+ fi
48
+ fi
49
+
50
+ # paths: warning only — unconditional rules load at every session start (use sparingly)
51
+ if ! grep -qE '^paths:[[:space:]]*' <<< "$fm"; then
52
+ echo " WARN: no paths: field — rule loads unconditionally at session start; use sparingly" >&2
53
+ fi
54
+
55
+ # Line count thresholds (matches cbp-build-cc-rule hook: warn ≥ 100, error ≥ 200)
56
+ lines=$(wc -l < "$FILE")
57
+ if [ "$lines" -ge 200 ]; then
58
+ err "rule file is $lines lines (hard limit 200; split on topic)"
59
+ elif [ "$lines" -ge 100 ]; then
60
+ echo " WARN: rule file is $lines lines (recommended max 100; consider splitting)" >&2
61
+ fi
62
+
63
+ if [ "$errors" -gt 0 ]; then
64
+ echo "validation FAILED ($errors issue(s)) for $FILE" >&2
65
+ exit 1
66
+ fi
67
+
68
+ echo "validation OK: $FILE"
69
+ exit 0
@@ -183,8 +183,8 @@ Applied to every session. Full env-var catalogue: the official Claude Code env-v
183
183
  ### Step 9 — Validate and verify
184
184
 
185
185
  ```bash
186
- # Pretty-print and validate JSON
187
- jq . .claude/settings.json
186
+ # Validate structure, $schema, and permission-rule syntax
187
+ bash "${CLAUDE_SKILL_DIR}/scripts/validate-settings.sh" "{scope-path}/settings.json"
188
188
 
189
189
  # Ask Claude Code itself
190
190
  # (inside a session)
@@ -0,0 +1,67 @@
1
+ #!/bin/bash
2
+ # @scope: org-shared
3
+ # Validate a Claude Code settings*.json file.
4
+ # Usage: validate-settings.sh <path-to-settings-file>
5
+ # Exit 0 = valid, exit 1 = invalid (errors printed to stderr).
6
+
7
+ set -euo pipefail
8
+
9
+ FILE="${1:?Usage: validate-settings.sh <path-to-settings-file>}"
10
+
11
+ if [ ! -f "$FILE" ]; then
12
+ echo "ERROR: file not found: $FILE" >&2
13
+ exit 1
14
+ fi
15
+
16
+ errors=0
17
+ err() { echo " - $1" >&2; errors=$((errors + 1)); }
18
+
19
+ # Requires jq
20
+ if ! command -v jq &>/dev/null; then
21
+ echo "ERROR: jq is required but not found in PATH" >&2
22
+ exit 1
23
+ fi
24
+
25
+ # Must be valid JSON
26
+ if ! jq -e . "$FILE" > /dev/null 2>&1; then
27
+ echo "validation FAILED (invalid JSON) for $FILE" >&2
28
+ exit 1
29
+ fi
30
+
31
+ # $schema key recommended
32
+ if ! jq -e '."$schema"' "$FILE" > /dev/null 2>&1; then
33
+ echo " WARN: no \$schema key — add \"https://json.schemastore.org/claude-code-settings.json\" for editor validation" >&2
34
+ fi
35
+
36
+ # Permission entry syntax: Tool or Tool(specifier) — e.g. Bash(git diff *), Read, mcp__foo__bar
37
+ PERM_PATTERN='^[A-Za-z_][A-Za-z0-9_]*(\(.*\))?$'
38
+
39
+ for tier in allow ask deny; do
40
+ while IFS= read -r entry; do
41
+ [ -z "$entry" ] && continue
42
+ if ! [[ "$entry" =~ $PERM_PATTERN ]]; then
43
+ err "permissions.$tier entry '$entry' does not match 'Tool' or 'Tool(specifier)' syntax"
44
+ fi
45
+ done < <(jq -r --arg tier "$tier" '.permissions[$tier][]? // empty' "$FILE" 2>/dev/null || true)
46
+ done
47
+
48
+ # No entry may appear in BOTH allow and ask (allow wins; having it in both is misleading)
49
+ allow_list=$(jq -r '.permissions.allow[]? // empty' "$FILE" 2>/dev/null || true)
50
+ ask_list=$(jq -r '.permissions.ask[]? // empty' "$FILE" 2>/dev/null || true)
51
+
52
+ if [ -n "$allow_list" ] && [ -n "$ask_list" ]; then
53
+ while IFS= read -r entry; do
54
+ [ -z "$entry" ] && continue
55
+ if printf '%s\n' "$ask_list" | grep -qxF "$entry"; then
56
+ err "permission '$entry' appears in both allow and ask (allow wins; remove from ask)"
57
+ fi
58
+ done <<< "$allow_list"
59
+ fi
60
+
61
+ if [ "$errors" -gt 0 ]; then
62
+ echo "validation FAILED ($errors issue(s)) for $FILE" >&2
63
+ exit 1
64
+ fi
65
+
66
+ echo "validation OK: $FILE"
67
+ exit 0
@@ -88,14 +88,22 @@ This is the first identity-stamping point — when claiming, passing `worktree_i
88
88
 
89
89
  Read `.codebyplan/git.json` `branch_config.production` (default `"main"`) as `BASE`. codebyplan repos are main-only — never create or branch from a `development`/integration branch.
90
90
 
91
+ Compute the slug deterministically:
92
+
93
+ ```bash
94
+ SLUG=$(codebyplan slug "{checkpoint title}")
95
+ ```
96
+
97
+ Then create and push the branch:
98
+
91
99
  ```bash
92
100
  git fetch origin "$BASE" 2>/dev/null || true
93
- git checkout -b "feat/CHK-{NNN}-{slug}" "origin/$BASE" 2>/dev/null \
94
- || git checkout -b "feat/CHK-{NNN}-{slug}" "$BASE"
95
- git push -u origin "feat/CHK-{NNN}-{slug}"
101
+ git checkout -b "feat/CHK-{NNN}-$SLUG" "origin/$BASE" 2>/dev/null \
102
+ || git checkout -b "feat/CHK-{NNN}-$SLUG" "$BASE"
103
+ git push -u origin "feat/CHK-{NNN}-$SLUG"
96
104
  ```
97
105
 
98
- Slug: lowercase, dash-joined, punctuation dropped, ≤40 chars. Persist the branch via `codebyplan checkpoint update --id <checkpoint-id> --branch-name "feat/CHK-{NNN}-{slug}"` (CLI write-through; break-glass: MCP `update_checkpoint`). (The dedicated `/cbp-git-branch-feat-create` skill is the canonical config-driven helper if you prefer to delegate.)
106
+ Persist the branch via `codebyplan checkpoint update --id <checkpoint-id> --branch-name "feat/CHK-{NNN}-$SLUG"` (CLI write-through; break-glass: MCP `update_checkpoint`). (The dedicated `/cbp-git-branch-feat-create` skill is the canonical config-driven helper if you prefer to delegate.)
99
107
 
100
108
  **Note — Supabase preview branch**: no Supabase branch is created here. Creation is lazy — it happens on the first DB change when `/cbp-supabase-migrate` runs on this feat branch, which provisions a Supabase branch named identically to the git branch. See `cbp-supabase-migrate` Step 2.3 for the creation protocol.
101
109
 
@@ -166,12 +166,19 @@ Only after both the local and remote git delete above succeed, run a conditional
166
166
 
167
167
  > Lifecycle contract: see [[supabase-branch-lifecycle]].
168
168
 
169
- - Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
170
- - Scan the returned list for an entry whose `name` exactly equals `$BRANCH`.
171
- - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Record the branch name in `SUPABASE_BRANCHES_DELETED[]`.
172
- - If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
173
- - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$BRANCH` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
174
- - Never delete the parent project `rrvtrumtkhrsbhcyrwvf` itself or any persistent/production branch.
169
+ - Resolve the parent project ref and apply the lifecycle guard in one deterministic call:
170
+
171
+ ```bash
172
+ codebyplan supabase teardown-preview "$BRANCH"
173
+ ```
174
+
175
+ Parse its JSON `{ status, parent_ref, project_ref, reason }`. The command never deletes anything — it reads the parent ref from `.codebyplan/shipment.json` (`.shipment.surfaces.supabase.project_ref`) and applies the protected / production / parent-ref guard from [[supabase-branch-lifecycle]].
176
+ - If `status === "rejected"`: STOP the teardown for this branch and surface `reason` — never delete a production / protected / integration branch or one whose preview ref equals the parent.
177
+ - Otherwise (`allowed` or `not_found`), use `parent_ref` for the live existence check — `mcp__supabase__list_branches` with `project_id: <parent_ref>`, then scan for an entry whose `name` exactly equals `$BRANCH`:
178
+ - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Record the branch name in `SUPABASE_BRANCHES_DELETED[]`.
179
+ - If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
180
+ - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$BRANCH` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
181
+ - Never delete the parent project (`parent_ref` from `codebyplan supabase teardown-preview`) itself or any persistent/production branch — the `teardown-preview` guard enforces this.
175
182
 
176
183
  Accumulate all Supabase branch names removed across the loop in `SUPABASE_BRANCHES_DELETED`.
177
184
 
@@ -198,11 +205,12 @@ git push origin --delete "$FEAT_BRANCH"
198
205
 
199
206
  After the feat branch git delete, run the same conditional Supabase teardown for `$FEAT_BRANCH`:
200
207
 
201
- - Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
202
- - Scan for an entry whose `name` exactly equals `$FEAT_BRANCH`.
203
- - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Add `$FEAT_BRANCH` to `SUPABASE_BRANCHES_DELETED[]`.
204
- - If not found: no-op silently idempotent, not-found is success.
205
- - If the `list_branches` call itself fails (network, auth, or a non-success responsedistinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$FEAT_BRANCH` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
208
+ - Run `codebyplan supabase teardown-preview "$FEAT_BRANCH"` and parse its JSON `{ status, parent_ref, project_ref, reason }` (reads the parent ref from `.codebyplan/shipment.json`, applies the lifecycle guard, never deletes).
209
+ - If `status === "rejected"`: STOP the teardown and surface `reason` never delete a production / protected / integration branch or one whose preview ref equals the parent.
210
+ - Otherwise (`allowed` or `not_found`), use `parent_ref` for the live existence check — `mcp__supabase__list_branches` with `project_id: <parent_ref>`, then scan for an entry whose `name` exactly equals `$FEAT_BRANCH`:
211
+ - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Add `$FEAT_BRANCH` to `SUPABASE_BRANCHES_DELETED[]`.
212
+ - If not found: no-op silentlyidempotent, not-found is success.
213
+ - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$FEAT_BRANCH` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
206
214
 
207
215
  ### Step 10: Save Shipment Results and Summary
208
216
 
@@ -108,21 +108,19 @@ REPO_PATH="$(git rev-parse --show-toplevel)"
108
108
 
109
109
  **If `--files` provided:** Use the manual file list.
110
110
 
111
- **If `--scope-task`:** Resolve via local state + intersection.
111
+ **If `--scope-task`:** Resolve via the deterministic CLI.
112
+
113
+ First, read `task.files_changed[].path` (local-first from `.codebyplan/state/checkpoints/*/tasks/*.json`; on miss run `npx codebyplan sync` once, then re-read; MCP `get_current_task` as documented break-glass when the state dir is absent and sync fails) and format as a CSV string. Then compute the intersection:
112
114
 
113
115
  ```bash
114
- # 1. Read task.files_changed[] from local state (glob for active task file)
115
- # On miss: npx codebyplan sync once, then re-read.
116
- # MCP get_current_task as documented break-glass when state dir absent + sync fails.
117
- task_paths=$(cat .codebyplan/state/checkpoints/*/tasks/*.json 2>/dev/null \
118
- | jq -r 'select(.status=="in_progress") | .files_changed[].path' | head -n 200)
119
- # 2. Read staged paths
120
- staged_paths=$(git diff --cached --name-only)
121
- # 3. Compute intersection
122
- intersect=$(comm -12 <(echo "$task_paths" | sort) <(echo "$staged_paths" | sort))
116
+ TASK_FILES_CSV="path1,path2,path3" # CSV string of task.files_changed[].path (local-first)
117
+ SCOPE_RESULT=$(codebyplan commit --scope-task --task-files "$TASK_FILES_CSV")
118
+ # Parse JSON: { files: string[], count: number }
119
+ FILES=$(echo "$SCOPE_RESULT" | jq -r '.files[]')
120
+ COUNT=$(echo "$SCOPE_RESULT" | jq -r '.count')
123
121
  ```
124
122
 
125
- If `intersect` is empty: emit error and STOP.
123
+ If `COUNT === 0` (empty `files[]`): emit error and STOP.
126
124
 
127
125
  ```
128
126
  ## Error: No Task Files Staged
@@ -135,7 +133,7 @@ Options:
135
133
  - Or use --all to commit the foreign-staged files instead.
136
134
  ```
137
135
 
138
- If non-empty: use `intersect` as the file list for Step 5.
136
+ If `COUNT > 0`: use `FILES` as the file list for Step 5.
139
137
 
140
138
  **If `--task`, `--all`, or no scope:** No filtering — all staged files committed.
141
139
 
@@ -159,59 +159,18 @@ If the main repo has no `.codebyplan/e2e.env` yet, provision it after setup by r
159
159
  cd "$WORKTREE_PATH" && git push -u origin "$BRANCH_NAME"
160
160
  ```
161
161
 
162
- ### Step 9: Register Worktree in CodeByPlan
162
+ ### Step 9: Register Worktree and Write `.codebyplan/` Config
163
163
 
164
- Get the repo ID from CLAUDE.md (`Repo ID` in Key References table).
164
+ Run `codebyplan worktree create "$BRANCH_NAME" --path "$WORKTREE_PATH"` and parse the JSON output (`{ worktree_files_written: boolean, mcp_registered: boolean, worktree_id?, warn? }`):
165
165
 
166
- Use MCP `create_worktree` to register the worktree in the CodeByPlan database:
166
+ - If `warn` is present: surface it as a non-blocking warning.
167
+ - Save the returned `worktree_id` for reference (if present).
167
168
 
168
- ```
169
- MCP create_worktree:
170
- repo_id: [repo-id from CLAUDE.md]
171
- name: $BRANCH_NAME
172
- path: $WORKTREE_PATH
173
- status: "active"
174
- ```
175
-
176
- Save the returned `worktree_id` for reference.
177
-
178
- ### Step 10: Write `.codebyplan/` directory
179
-
180
- Create the `.codebyplan/` directory in the worktree root and write per-concern config stubs:
181
-
182
- ```bash
183
- mkdir -p "$WORKTREE_PATH/.codebyplan"
184
- ```
185
-
186
- Write `.codebyplan/repo.json` with the correct `repo_id`:
187
-
188
- ```json
189
- {
190
- "repo_id": "[repo-id from CLAUDE.md]"
191
- }
192
- ```
193
-
194
- Write stubs for the other per-concern files (populated by `npx codebyplan sync` on first run):
195
-
196
- ```bash
197
- # .codebyplan/server.json — server port and type config
198
- echo '{}' > "$WORKTREE_PATH/.codebyplan/server.json"
199
-
200
- # .codebyplan/git.json — branch config
201
- echo '{}' > "$WORKTREE_PATH/.codebyplan/git.json"
202
-
203
- # .codebyplan/shipment.json — surface shipment config
204
- echo '{}' > "$WORKTREE_PATH/.codebyplan/shipment.json"
205
-
206
- # .codebyplan/vendor.json — vendor docs path
207
- echo '{}' > "$WORKTREE_PATH/.codebyplan/vendor.json"
208
- ```
209
-
210
- The `.codebyplan/device.local.json` file is created by `npx codebyplan setup` on the device (gitignored). The `worktree_id` is never COMMITTED; it may be cached per-device in the gitignored `.codebyplan/worktree.local.json` (branch-keyed, re-derivable via `codebyplan resolve-worktree --cache`), otherwise resolved at runtime from the `(device_id, repo path, branch)` tuple via `npx codebyplan resolve-worktree`.
169
+ The CLI atomically writes the `.codebyplan/` directory with per-concern config stubs and registers the worktree in the CodeByPlan database. The `.codebyplan/device.local.json` file is created by `npx codebyplan setup` on the device (gitignored). The `worktree_id` is never COMMITTED; it may be cached per-device in the gitignored `.codebyplan/worktree.local.json` (branch-keyed, re-derivable via `codebyplan resolve-worktree --cache`), otherwise resolved at runtime from the `(device_id, repo path, branch)` tuple via `npx codebyplan resolve-worktree`.
211
170
 
212
171
  No need to mark as `skip-worktree` — the committed files are merge-safe per CHK-108 and CHK-120.
213
172
 
214
- ### Step 11: Show Result
173
+ ### Step 10: Show Result
215
174
 
216
175
  ```
217
176
  ## Worktree Created
@@ -238,4 +197,4 @@ No need to mark as `skip-worktree` — the committed files are merge-safe per CH
238
197
  ## Integration
239
198
 
240
199
  - **Related**: `/cbp-git-worktree-remove` (cleanup and deregister)
241
- - **MCP tools**: `create_worktree`
200
+ - **CLI**: `codebyplan worktree create <name> --path <abs>` (Step 9 — writes `.codebyplan/` config and registers worktree)
@@ -49,31 +49,17 @@ Set:
49
49
  - `WORKTREE_PATH` = resolved path
50
50
  - `BRANCH_NAME` = branch checked out in that worktree
51
51
 
52
- ### Step 4: Look Up Worktree in CodeByPlan
52
+ ### Step 4: Look Up and Deregister Worktree in CodeByPlan
53
53
 
54
- Get the repo ID from CLAUDE.md (`Repo ID` in Key References table).
54
+ Run `codebyplan worktree remove "$BRANCH_NAME"` and parse the JSON output (`{ mcp_deregistered: boolean, warn? }`):
55
55
 
56
- Use MCP `get_worktrees` to find the worktree record:
57
-
58
- ```
59
- MCP get_worktrees:
60
- repo_id: [repo-id from CLAUDE.md]
61
- status: "active"
62
- ```
63
-
64
- Match by `name` = `$BRANCH_NAME` or `path` = `$WORKTREE_PATH`.
65
-
66
- Set `WORKTREE_ID` = matched worktree's `id`.
67
-
68
- If not found in CodeByPlan, note it and continue with local removal.
56
+ - If `warn` is present: surface it as a non-blocking warning. The worktree was not found in CodeByPlan or deregistration failed, but local removal will proceed.
57
+ - If `mcp_deregistered === true`: worktree was successfully deregistered.
58
+ - If `mcp_deregistered === false`: worktree was not registered or deregistration failed; continue with local removal.
69
59
 
70
60
  ### Step 5: Check for Assigned Checkpoints
71
61
 
72
- If `WORKTREE_ID` was found, warn if any checkpoints are assigned to this worktree:
73
-
74
- ```
75
- ⚠ This worktree has [N] checkpoint(s) assigned. They will become unassigned.
76
- ```
62
+ If the worktree was successfully deregistered in Step 4 (`mcp_deregistered === true`), any checkpoints that were assigned to it will become unassigned. This is expected behavior and requires no additional action.
77
63
 
78
64
  ### Step 6: Confirm with User
79
65
 
@@ -92,16 +78,7 @@ Ask:
92
78
  2. Remove worktree and delete branch
93
79
  3. Cancel
94
80
 
95
- ### Step 7: Deregister from CodeByPlan
96
-
97
- If `WORKTREE_ID` was found, delete the worktree record:
98
-
99
- ```
100
- MCP delete_worktree:
101
- worktree_id: [worktree-id]
102
- ```
103
-
104
- ### Step 8: Remove Git Worktree
81
+ ### Step 7: Remove Git Worktree
105
82
 
106
83
  ```bash
107
84
  git worktree remove "$WORKTREE_PATH"
@@ -114,7 +91,7 @@ git worktree remove --force "$WORKTREE_PATH"
114
91
 
115
92
  Only use `--force` if the user confirms.
116
93
 
117
- ### Step 9: Delete Branch (if requested)
94
+ ### Step 8: Delete Branch (if requested)
118
95
 
119
96
  **Protected branch check:** Read the protected set from `.codebyplan/git.json`:
120
97
  ```bash
@@ -135,15 +112,21 @@ After the git branch delete succeeds, run a conditional Supabase preview-branch
135
112
 
136
113
  > Lifecycle contract: see [[supabase-branch-lifecycle]].
137
114
 
138
- - Resolve the parent project ref: read `.codebyplan/shipment.json` `.shipment.surfaces.supabase.project_ref`; if absent or empty, read the first line of `supabase/.temp/project-ref`. Use that resolved ref as the `project_id`.
139
- - Call `mcp__supabase__list_branches` with the resolved `project_id`.
140
- - Scan the returned list for an entry whose `name` exactly equals `$BRANCH_NAME`.
141
- - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Report "Supabase preview branch deleted: `$BRANCH_NAME`".
142
- - If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
143
- - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$BRANCH_NAME` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
144
- - Never delete the branch where `is_default` is true in the `list_branches` response (the production/parent project branch) or any other persistent/long-lived branch.
115
+ - Resolve the parent project ref and apply the lifecycle guard in one deterministic call:
116
+
117
+ ```bash
118
+ codebyplan supabase teardown-preview "$BRANCH_NAME"
119
+ ```
120
+
121
+ Parse its JSON `{ status, parent_ref, project_ref, reason }`. The command never deletes anything — it reads the parent ref from `.codebyplan/shipment.json` (`.shipment.surfaces.supabase.project_ref`) and applies the protected / production / parent-ref guard from [[supabase-branch-lifecycle]].
122
+ - If `status === "rejected"`: STOP the teardown and surface `reason` — never delete a production / protected / integration branch or one whose preview ref equals the parent.
123
+ - Otherwise (`allowed` or `not_found`), use `parent_ref` for the live existence check — `mcp__supabase__list_branches` with `project_id: <parent_ref>`, then scan for an entry whose `name` exactly equals `$BRANCH_NAME`:
124
+ - If found: call `mcp__supabase__delete_branch` with its `branch_id`. Report "Supabase preview branch deleted: `$BRANCH_NAME`".
125
+ - If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
126
+ - If the `list_branches` call itself fails (network, auth, or a non-success response — distinct from a successful lookup that returns no match): emit a non-blocking warning that the Supabase preview branch for `$BRANCH_NAME` may still exist and should be verified in the dashboard. Do not treat an API failure as a not-found success.
127
+ - Never delete the parent project (`parent_ref` from `codebyplan supabase teardown-preview`) itself or any persistent/production branch — the `teardown-preview` guard enforces this.
145
128
 
146
- ### Step 10: Show Result
129
+ ### Step 9: Show Result
147
130
 
148
131
  ```
149
132
  ## Worktree Removed
@@ -159,4 +142,4 @@ After the git branch delete succeeds, run a conditional Supabase preview-branch
159
142
  ## Integration
160
143
 
161
144
  - **Related**: `/cbp-git-worktree-create` (create and register)
162
- - **MCP tools**: `get_worktrees`, `delete_worktree`
145
+ - **CLI**: `codebyplan worktree remove <name>` (Step 4 — deregister from CodeByPlan)
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  scope: org-shared
3
3
  name: cbp-map-architecture
4
+ effort: xhigh
4
5
  description: Orchestrate architecture map generation for one or all modules. Spawns the cbp-map-architecture agent per module, writes per-module .md files to .claude/architecture/, regenerates INDEX.md and graph.md, and stamps each module via the CLI. Idempotent — safe to re-run.
5
6
  argument-hint: '[--module <path>] [--deep <path,...>]'
6
7
  allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task
@@ -69,36 +69,24 @@ Triggered by `/cbp-task-start` (Step 3.6, optional stale-check), `/cbp-task-comp
69
69
 
70
70
  Supabase migrations are version-keyed by their numeric filename prefix. Two files sharing a prefix break `supabase db push`: the schema_migrations table records ONE version per prefix, the second file at the same prefix becomes orphaned, and every subsequent migration stalls — surfacing as the Supabase Preview check failing with `MIGRATIONS_FAILED`. Catch this BEFORE committing the merge, while a clean rollback is one `git merge --abort` away.
71
71
 
72
- 1. Probe both sides of the would-be merge. Use `git ls-files` for the HEAD side so any in-progress `git mv` staged in the index (e.g. a rename produced by step 5 of a prior pass through this section) is reflected — `git ls-tree HEAD` would still see the committed-only state and re-trigger the collision. Use `git ls-tree origin/{BASE}` for the main side since we want the committed remote state, not anything locally staged:
72
+ 1. Probe both sides of the would-be merge via the deterministic CLI:
73
73
 
74
74
  ```bash
75
- git ls-files supabase/migrations/ 2>/dev/null \
76
- | sed 's|.*/||' | sort > /tmp/cbp-merge-our-names.txt
77
- git ls-tree -r --name-only origin/{BASE} supabase/migrations/ 2>/dev/null \
78
- | sed 's|.*/||' | sort > /tmp/cbp-merge-their-names.txt
75
+ PROBE=$(codebyplan migration-collisions --base "$BASE" --json)
76
+ # Parse JSON: { base, collisions: [{ prefix, ours: string[], theirs: string[] }] }
77
+ COLLISIONS=$(echo "$PROBE" | jq '.collisions')
79
78
  ```
80
79
 
81
- 2. A true collision is a numeric prefix that appears on BOTH sides with DIFFERENT filenames. A shared filename (same prefix, same basename i.e. an already-merged migration) is NOT a collision. Compute the unique-to-each-side basenames first, then look for shared prefixes within that unique set:
80
+ 2. If `COLLISIONS` is empty (`[]`), proceed silently to Step 2.
82
81
 
83
- ```bash
84
- # Files unique to each side (same-named files are NOT collisions)
85
- comm -23 /tmp/cbp-merge-our-names.txt /tmp/cbp-merge-their-names.txt > /tmp/cbp-merge-only-ours.txt
86
- comm -13 /tmp/cbp-merge-our-names.txt /tmp/cbp-merge-their-names.txt > /tmp/cbp-merge-only-theirs.txt
87
- # True collision: a prefix in only-ours also appears in only-theirs (same prefix, different basename)
88
- COLLISIONS=$(cat /tmp/cbp-merge-only-ours.txt /tmp/cbp-merge-only-theirs.txt \
89
- | sed 's|_.*||' | sort | uniq -d)
90
- ```
91
-
92
- 3. If `COLLISIONS` is empty, proceed silently to Step 2.
93
-
94
- 4. If `COLLISIONS` is non-empty, for each colliding prefix list both file paths (one from `HEAD`, one from `origin/{BASE}`) and surface via AskUserQuestion:
82
+ 3. If `COLLISIONS` is non-empty, for each colliding prefix in the array (each element has `prefix`, `ours[]`, `theirs[]`) surface via AskUserQuestion:
95
83
 
96
84
  - **Rename HEAD-side (Recommended when a main migration is already applied to a shared remote)** — rename the local file to a fresh, sequential timestamp that respects existing apply-order dependencies (probe `supabase migration list --db-url <preview>` if a preview branch exists, or inspect FK references in surrounding migrations). The orchestrator runs `git mv <old> <new>` itself; the rename lands in the git index and is picked up by the re-probe at step 5.
97
85
  - **Rename main-side (manual, OUT-OF-SKILL)** — only when the main file definitely has not been applied anywhere yet AND the user has write access to `{BASE}`. This skill does NOT touch the main branch: it runs on a feat branch (Step 0 enforces this) and the Key Rules below forbid any push from this skill. The user must, in a separate terminal: `git checkout {BASE} && git mv <old> <new> && git commit -m "fix(migration): rename to resolve collision with feat/..." && git push origin {BASE}`. After that push is confirmed remote-side, re-invoke `/cbp-merge-main` — Step 1 will fetch the updated main tip and Step 1.5 will re-probe with the rename in place.
98
86
  - **Defer to a new task in the active checkpoint** — `git merge --abort` is unnecessary because Step 2 has not started. Create a CHK-bound task per `cbp-round-end` reference `findings-presentation.md` "Infra Issue Absorption Contract — Resolve-in-Current-Scope by Default" and STOP `/cbp-merge-main`. Resume after the task completes.
99
87
  - **Abort merge** — STOP the skill. User decides later.
100
88
 
101
- 5. After any HEAD-side rename action, re-execute Step 1.5 (collisions may chain — fixing one can expose another). The HEAD-side probe at step 1 uses `git ls-files` rather than `git ls-tree HEAD`, so the freshly-staged `git mv` is visible without requiring a commit. Main-side renames require a fresh `/cbp-merge-main` invocation (the user manually fetched and re-ran per option 2 above), not an in-skill loop.
89
+ 4. After any HEAD-side rename action, re-execute Step 1.5 (collisions may chain — fixing one can expose another). The CLI probes the HEAD side via `git ls-files` (so staged renames are visible), matching the documented re-probe behavior. Main-side renames require a fresh `/cbp-merge-main` invocation (the user manually fetched and re-ran per option 2 above), not an in-skill loop.
102
90
 
103
91
  This check is intentionally placed BEFORE Step 2's `git merge`: catching collisions pre-merge means no merge commit to revert, no conflict-resolution work to throw away, no Supabase Preview poll to fail.
104
92
 
@@ -165,27 +153,34 @@ This check is intentionally placed BEFORE Step 2's `git merge`: catching collisi
165
153
 
166
154
  Run a scoped subset of the testing-qa check matrix INLINE (no agent spawn — this skill stays lightweight):
167
155
 
168
- 1. `pnpm -w lint` — always. On non-zero exit, surface stdout/stderr and AskUserQuestion:
156
+ 1. From the repo root, run:
157
+
158
+ ```bash
159
+ codebyplan check --scope merged --json
160
+ ```
161
+
162
+ `--scope merged` runs `gate6`, `lint`, `typecheck`, and `tests` (no `audit` — that is `task` scope only). The runner is **whole-repo + baseline**: it runs `turbo run lint|typecheck|test` across every package and diffs against the committed `.check-baseline.json`, so only NEW per-package failures fail a check (`status: 'fail'` with a non-empty `new_failures`). `gate6` (sibling-identity parity) is ALWAYS hard-fail and never baselined. Capture the JSON result.
163
+
164
+ 2. For each result entry where `status === 'fail'`, surface its `stdout`/`stderr` and present an AskUserQuestion:
169
165
  - **Continue (commit-as-is)** — leave the merge committed; flag QA failure in output.
170
166
  - **Abort merge** — `git reset --hard HEAD~1` to revert just the merge commit. Stop the skill.
171
167
  - **Skip (mark warn)** — leave the merge committed; treat as warn, not fail.
172
168
 
173
- 2. `pnpm exec tsc --noEmit` — only if any merged file matches `*.ts` or `*.tsx`. Same three-option prompt on failure.
174
-
175
- 3. `pnpm test --run` — only if any merged file matches typical source globs (`src/**`, `apps/**/src/**`, `packages/**/src/**`). Same three-option prompt on failure.
176
-
177
- 4. Build a `qa_summary` object:
169
+ 3. Build a `qa_summary` object from the runner's results array:
178
170
 
179
171
  ```
180
172
  {
173
+ "gate6": "pass" | "fail" | "warn" | "skipped",
181
174
  "lint": "pass" | "fail" | "warn" | "skipped",
182
- "tsc": "pass" | "fail" | "warn" | "skipped",
175
+ "typecheck": "pass" | "fail" | "warn" | "skipped",
183
176
  "tests": "pass" | "fail" | "warn" | "skipped",
184
177
  "merged_files_count": N,
185
178
  "user_choice_on_failure": "continue" | "abort" | "skip" | null
186
179
  }
187
180
  ```
188
181
 
182
+ Map runner `status` values: `"pass"` → `"pass"`, `"skipped"` → `"skipped"`, `"fail"` → `"fail"` or `"warn"` per the user's choice above.
183
+
189
184
  Do NOT auto-revert without user consent. User-driven gating is the contract.
190
185
 
191
186
  ### Step 5: Output
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  scope: org-shared
3
3
  name: cbp-refresh-arch-map
4
+ effort: high
4
5
  description: Drift-scoped refresh of the .claude/architecture/ map — re-runs the cbp-map-architecture agent for ONLY the modules whose stamped git SHA has changed, regenerates INDEX.md + graph.md, and re-stamps. Idempotent; no-op when no module has drifted.
5
6
  argument-hint: '[--module <path>]'
6
7
  allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task