codebyplan 1.11.2 → 1.12.0
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.
- package/dist/cli.js +553 -347
- package/package.json +1 -1
- package/templates/hooks/cbp-statusline.mjs +44 -0
- package/templates/hooks/cbp-statusline.py +24 -2
- package/templates/hooks/cbp-statusline.sh +22 -2
- package/templates/rules/README.md +8 -1
- package/templates/rules/supabase-branch-lifecycle.md +99 -0
- package/templates/settings.project.base.json +1 -2
- package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +1 -2
- package/templates/skills/cbp-checkpoint-create/SKILL.md +2 -0
- package/templates/skills/cbp-checkpoint-end/SKILL.md +27 -5
- package/templates/skills/cbp-git-worktree-remove/SKILL.md +17 -1
- package/templates/skills/cbp-session-start/SKILL.md +27 -2
- package/templates/skills/cbp-ship-main/SKILL.md +13 -0
- package/templates/skills/cbp-supabase-branch-check/SKILL.md +12 -5
- package/templates/skills/cbp-supabase-migrate/SKILL.md +139 -9
- package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +1 -1
- package/templates/skills/cbp-supabase-setup/SKILL.md +13 -7
- package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +2 -2
- package/templates/skills/cbp-task-start/SKILL.md +2 -0
package/package.json
CHANGED
|
@@ -118,6 +118,7 @@ function main() {
|
|
|
118
118
|
rate_limits: true,
|
|
119
119
|
repo_pr: true,
|
|
120
120
|
worktree: true,
|
|
121
|
+
infra_drift: true,
|
|
121
122
|
no_color: false,
|
|
122
123
|
};
|
|
123
124
|
try {
|
|
@@ -136,6 +137,7 @@ function main() {
|
|
|
136
137
|
"rate_limits",
|
|
137
138
|
"repo_pr",
|
|
138
139
|
"worktree",
|
|
140
|
+
"infra_drift",
|
|
139
141
|
]) {
|
|
140
142
|
if (typeof parsed.lines[k] === "boolean") cfg[k] = parsed.lines[k];
|
|
141
143
|
}
|
|
@@ -374,6 +376,48 @@ function main() {
|
|
|
374
376
|
}
|
|
375
377
|
}
|
|
376
378
|
|
|
379
|
+
// ============================================================
|
|
380
|
+
// LINE 7 — Infra drift (monorepo feat branches behind main)
|
|
381
|
+
// ============================================================
|
|
382
|
+
// Only the codebyplan monorepo (templates/ present) on a feat branch can carry
|
|
383
|
+
// stale .claude/ infra. No fetch — counts against the cached origin/main only.
|
|
384
|
+
if (shouldShow("INFRA_DRIFT", cfg.infra_drift)) {
|
|
385
|
+
if (
|
|
386
|
+
BRANCH.startsWith("feat/") &&
|
|
387
|
+
fs.existsSync(
|
|
388
|
+
path.join(root, "packages", "codebyplan-package", "templates")
|
|
389
|
+
)
|
|
390
|
+
) {
|
|
391
|
+
let behind = 0;
|
|
392
|
+
try {
|
|
393
|
+
behind = parseInt(
|
|
394
|
+
execFileSync(
|
|
395
|
+
"git",
|
|
396
|
+
[
|
|
397
|
+
"-C",
|
|
398
|
+
root,
|
|
399
|
+
"rev-list",
|
|
400
|
+
"--count",
|
|
401
|
+
"HEAD..origin/main",
|
|
402
|
+
"--",
|
|
403
|
+
".claude",
|
|
404
|
+
"packages/codebyplan-package/templates",
|
|
405
|
+
],
|
|
406
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
|
|
407
|
+
).trim(),
|
|
408
|
+
10
|
|
409
|
+
);
|
|
410
|
+
} catch {
|
|
411
|
+
behind = 0;
|
|
412
|
+
}
|
|
413
|
+
if (Number.isFinite(behind) && behind > 0) {
|
|
414
|
+
out.push(
|
|
415
|
+
`${C.YELLOW}⚠ infra ${behind} behind${C.RST} ${C.DIM}→ /cbp-refresh-infra${C.RST}`
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
377
421
|
process.stdout.write(out.length ? out.join("\n") + "\n" : "");
|
|
378
422
|
}
|
|
379
423
|
|
|
@@ -80,7 +80,8 @@ def main():
|
|
|
80
80
|
# ---- Config: line toggles + no_color -------------------------------------
|
|
81
81
|
cfg = {
|
|
82
82
|
"identity": True, "context": True, "cost": True,
|
|
83
|
-
"rate_limits": True, "repo_pr": True, "worktree": True,
|
|
83
|
+
"rate_limits": True, "repo_pr": True, "worktree": True,
|
|
84
|
+
"infra_drift": True, "no_color": False,
|
|
84
85
|
}
|
|
85
86
|
try:
|
|
86
87
|
with open(os.path.join(root, ".codebyplan", "statusline.json"), "r", encoding="utf-8") as fh:
|
|
@@ -90,7 +91,7 @@ def main():
|
|
|
90
91
|
cfg["no_color"] = parsed["no_color"]
|
|
91
92
|
lines = parsed.get("lines")
|
|
92
93
|
if isinstance(lines, dict):
|
|
93
|
-
for k in ["identity", "context", "cost", "rate_limits", "repo_pr", "worktree"]:
|
|
94
|
+
for k in ["identity", "context", "cost", "rate_limits", "repo_pr", "worktree", "infra_drift"]:
|
|
94
95
|
if isinstance(lines.get(k), bool):
|
|
95
96
|
cfg[k] = lines[k]
|
|
96
97
|
except Exception:
|
|
@@ -321,6 +322,27 @@ def main():
|
|
|
321
322
|
l6 += " %s%s%s" % (DIM, wt_path_disp, RST)
|
|
322
323
|
out.append(l6)
|
|
323
324
|
|
|
325
|
+
# ===== LINE 7 — Infra drift (monorepo feat branches behind main) =====
|
|
326
|
+
# Only the codebyplan monorepo (templates/ present) on a feat branch can carry
|
|
327
|
+
# stale .claude/ infra. No fetch — counts against the cached origin/main only.
|
|
328
|
+
if should_show("INFRA_DRIFT", cfg["infra_drift"]):
|
|
329
|
+
if branch.startswith("feat/") and os.path.isdir(
|
|
330
|
+
os.path.join(root, "packages", "codebyplan-package", "templates")
|
|
331
|
+
):
|
|
332
|
+
behind = 0
|
|
333
|
+
try:
|
|
334
|
+
res = subprocess.run(
|
|
335
|
+
["git", "-C", root, "rev-list", "--count", "HEAD..origin/main",
|
|
336
|
+
"--", ".claude", "packages/codebyplan-package/templates"],
|
|
337
|
+
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True,
|
|
338
|
+
)
|
|
339
|
+
if res.returncode == 0:
|
|
340
|
+
behind = int(res.stdout.strip() or "0")
|
|
341
|
+
except Exception:
|
|
342
|
+
behind = 0
|
|
343
|
+
if behind > 0:
|
|
344
|
+
out.append("%s⚠ infra %d behind%s %s→ /cbp-refresh-infra%s" % (YELLOW, behind, RST, DIM, RST))
|
|
345
|
+
|
|
324
346
|
sys.stdout.write(("\n".join(out) + "\n") if out else "")
|
|
325
347
|
|
|
326
348
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
#
|
|
16
16
|
# DISPLAY OPTIONS (team-shared, committed)
|
|
17
17
|
# .codebyplan/statusline.json -> { "lines": {identity,context,cost,rate_limits,
|
|
18
|
-
# repo_pr,worktree}, "no_color": bool }
|
|
18
|
+
# repo_pr,worktree,infra_drift}, "no_color": bool }
|
|
19
19
|
#
|
|
20
20
|
# ENV-VAR OVERRIDES (env > config > default)
|
|
21
21
|
# CBP_STATUSLINE_HIDE_IDENTITY=1 suppress line 1 (folder, branch, model, effort, …)
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
# CBP_STATUSLINE_HIDE_RATE_LIMITS=1 suppress line 4 (5h / 7d rate limits)
|
|
25
25
|
# CBP_STATUSLINE_HIDE_REPO_PR=1 suppress line 5 (repo host/owner/name, PR)
|
|
26
26
|
# CBP_STATUSLINE_HIDE_WORKTREE=1 suppress line 6 (worktree name/branch/path)
|
|
27
|
+
# CBP_STATUSLINE_HIDE_INFRA_DRIFT=1 suppress line 7 (.claude infra commits behind main)
|
|
27
28
|
# CBP_STATUSLINE_NO_COLOR=1 strip all ANSI colour codes (also honoured by $NO_COLOR)
|
|
28
29
|
#
|
|
29
30
|
# TEST SEAMS (no effect in normal use)
|
|
@@ -104,7 +105,7 @@ eval "$(echo "$INPUT" | jq -r '
|
|
|
104
105
|
|
|
105
106
|
# ---- Config: line toggles + no_color from .codebyplan/statusline.json --------
|
|
106
107
|
CFG_IDENTITY=true; CFG_CONTEXT=true; CFG_COST=true
|
|
107
|
-
CFG_RATE_LIMITS=true; CFG_REPO_PR=true; CFG_WORKTREE=true; CFG_NO_COLOR=false
|
|
108
|
+
CFG_RATE_LIMITS=true; CFG_REPO_PR=true; CFG_WORKTREE=true; CFG_INFRA_DRIFT=true; CFG_NO_COLOR=false
|
|
108
109
|
CBP_CFG="$CBP_ROOT/.codebyplan/statusline.json"
|
|
109
110
|
if [ -f "$CBP_CFG" ] && command -v jq >/dev/null 2>&1; then
|
|
110
111
|
# Use `!= false` / `== true` (NOT jq `//`): the `//` operator treats an explicit
|
|
@@ -117,6 +118,7 @@ if [ -f "$CBP_CFG" ] && command -v jq >/dev/null 2>&1; then
|
|
|
117
118
|
"CFG_RATE_LIMITS=\(.lines.rate_limits != false)",
|
|
118
119
|
"CFG_REPO_PR=\(.lines.repo_pr != false)",
|
|
119
120
|
"CFG_WORKTREE=\(.lines.worktree != false)",
|
|
121
|
+
"CFG_INFRA_DRIFT=\(.lines.infra_drift != false)",
|
|
120
122
|
"CFG_NO_COLOR=\(.no_color == true)"
|
|
121
123
|
' "$CBP_CFG" 2>/dev/null)"
|
|
122
124
|
fi
|
|
@@ -401,3 +403,21 @@ if should_show WORKTREE "$CFG_WORKTREE"; then
|
|
|
401
403
|
printf "%b\n" "$L6"
|
|
402
404
|
fi
|
|
403
405
|
fi
|
|
406
|
+
|
|
407
|
+
# ============================================================
|
|
408
|
+
# LINE 7 — Infra drift (monorepo feat branches behind main)
|
|
409
|
+
# ============================================================
|
|
410
|
+
# Only the codebyplan monorepo (templates/ present) on a feat branch can carry
|
|
411
|
+
# stale .claude/ infra. No fetch — counts against the cached origin/main only.
|
|
412
|
+
if should_show INFRA_DRIFT "$CFG_INFRA_DRIFT"; then
|
|
413
|
+
case "$BRANCH" in
|
|
414
|
+
feat/*)
|
|
415
|
+
if [ -d "$CBP_ROOT/packages/codebyplan-package/templates" ]; then
|
|
416
|
+
BEHIND="$(git -C "$CBP_ROOT" rev-list --count HEAD..origin/main -- .claude packages/codebyplan-package/templates 2>/dev/null)"
|
|
417
|
+
if [ -n "$BEHIND" ] && [ "$BEHIND" -gt 0 ] 2>/dev/null; then
|
|
418
|
+
printf "%b\n" "${YELLOW}⚠ infra ${BEHIND} behind${RST} ${DIM}→ /cbp-refresh-infra${RST}"
|
|
419
|
+
fi
|
|
420
|
+
fi
|
|
421
|
+
;;
|
|
422
|
+
esac
|
|
423
|
+
fi
|
|
@@ -34,7 +34,14 @@ The `install`/`update`/`uninstall` flow handles these files identically to how i
|
|
|
34
34
|
|
|
35
35
|
## Current status
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Four rules are shipped:
|
|
38
|
+
|
|
39
|
+
| Rule file | Summary |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `scope-vocabulary.md` | Canonical scope-marker enum (`org-shared` / `project-shared` / `repo-only:<name>`) enforced by three validators |
|
|
42
|
+
| `context-file-loading.md` | Context-file load contract — who loads what, when, and how missing files are handled |
|
|
43
|
+
| `todo-backend.md` | Todos queue contract, six DB-layer workflow invariants, and writer obligations for MCP mutators |
|
|
44
|
+
| `supabase-branch-lifecycle.md` | Supabase preview-branch lifecycle mirrors the git feat-branch lifecycle — lazy create on first DB change, delete wherever the git branch is removed |
|
|
38
45
|
|
|
39
46
|
## Contributing a rule
|
|
40
47
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
scope: org-shared
|
|
3
|
+
description: Supabase preview-branch lifecycle mirrors the git feat-branch lifecycle
|
|
4
|
+
paths:
|
|
5
|
+
- "supabase/migrations/**"
|
|
6
|
+
- ".codebyplan/shipment.json"
|
|
7
|
+
- ".codebyplan/git.json"
|
|
8
|
+
- ".claude/skills/cbp-supabase-migrate/**"
|
|
9
|
+
- ".claude/skills/cbp-supabase-branch-check/**"
|
|
10
|
+
- ".claude/skills/cbp-checkpoint-end/**"
|
|
11
|
+
- ".claude/skills/cbp-git-worktree-remove/**"
|
|
12
|
+
- ".claude/skills/cbp-ship-main/**"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Supabase Branch Lifecycle
|
|
16
|
+
|
|
17
|
+
When a checkpoint or standalone task does DB work on its own feat branch, that branch gets
|
|
18
|
+
a Supabase preview branch whose lifecycle mirrors the git branch — created on first DB
|
|
19
|
+
change, deleted when the git branch is removed.
|
|
20
|
+
|
|
21
|
+
## Contract
|
|
22
|
+
|
|
23
|
+
Each feat branch that touches the database owns exactly one Supabase preview branch, named
|
|
24
|
+
identically to the git branch. The git branch is the source of truth; the Supabase branch
|
|
25
|
+
follows it.
|
|
26
|
+
|
|
27
|
+
## Create (lazy)
|
|
28
|
+
|
|
29
|
+
The Supabase branch is created by `cbp-supabase-migrate` (Step 2.3) on the **first DB
|
|
30
|
+
change** for the branch — never eagerly at git-branch creation time. Creation is guarded
|
|
31
|
+
by the skill's db_paths detection so the step fires only when the invocation actually
|
|
32
|
+
touches the database.
|
|
33
|
+
|
|
34
|
+
The branch is named **verbatim** after the git branch, slashes included (e.g.
|
|
35
|
+
`feat/CHK-144-supabase-branch-lifecycle`). This identical naming is the linchpin: the
|
|
36
|
+
GitHub branching integration reconciles to the same branch on PR open rather than creating
|
|
37
|
+
a duplicate.
|
|
38
|
+
|
|
39
|
+
After creation, the branch name and resulting `project_ref` are recorded on the parent
|
|
40
|
+
checkpoint or standalone task context (via `mcp__codebyplan__update_checkpoint` /
|
|
41
|
+
`mcp__codebyplan__update_task`) and may also be noted in `.codebyplan/shipment.json` so
|
|
42
|
+
cleanup skills can discover and remove it without querying the API.
|
|
43
|
+
|
|
44
|
+
## Delete
|
|
45
|
+
|
|
46
|
+
The Supabase branch is removed wherever the git branch is deleted:
|
|
47
|
+
|
|
48
|
+
| Skill | Trigger |
|
|
49
|
+
|---|---|
|
|
50
|
+
| `cbp-checkpoint-end` | stale-branch cleanup + current feat-branch delete on ship |
|
|
51
|
+
| `cbp-git-worktree-remove` | worktree teardown removes the coupled Supabase branch |
|
|
52
|
+
| `cbp-ship-main` | `branch_deleted` event after PR merge |
|
|
53
|
+
|
|
54
|
+
Deletion is **existence-checked and idempotent** — a not-found response is treated as
|
|
55
|
+
success. This tolerates the GitHub integration auto-deleting the preview branch on PR
|
|
56
|
+
close before the skill runs.
|
|
57
|
+
|
|
58
|
+
These skills use `delete_branch` (MCP tool), never `merge_branch`. Production migrations
|
|
59
|
+
reach main via the existing merge path (GitHub rebuild / `last_shipped_migration_version`);
|
|
60
|
+
there is no separate Supabase branch merge step.
|
|
61
|
+
|
|
62
|
+
## Exclusions
|
|
63
|
+
|
|
64
|
+
This lifecycle applies **only to feat branches**. It MUST NOT fire for:
|
|
65
|
+
|
|
66
|
+
- The main/production branch (`branch_config.production`, default `"main"`)
|
|
67
|
+
- Any branch listed in `branch_config.protected[]`
|
|
68
|
+
- A standalone task running directly on the production branch
|
|
69
|
+
- The integration branch (`branch_config.integration`) when non-null — it gets a persistent branch via `cbp-supabase-setup`, not a lazy ephemeral one.
|
|
70
|
+
|
|
71
|
+
`cbp-supabase-migrate` Step 2.3 Guard 1 enforces this by reading `.codebyplan/git.json`
|
|
72
|
+
and skipping provisioning when the current branch matches `$PRODUCTION`, `$PROTECTED`, or
|
|
73
|
+
a non-null `$INTEGRATION`.
|
|
74
|
+
|
|
75
|
+
## Guards
|
|
76
|
+
|
|
77
|
+
Skills that delete Supabase branches MUST:
|
|
78
|
+
|
|
79
|
+
1. Verify the branch name matches the feat branch being removed (never delete by
|
|
80
|
+
`project_ref` alone).
|
|
81
|
+
2. Never delete the parent/production project (`rrvtrumtkhrsbhcyrwvf`) itself.
|
|
82
|
+
3. Never delete a persistent/long-lived branch that does not correspond to the git branch
|
|
83
|
+
being torn down.
|
|
84
|
+
|
|
85
|
+
## PR Gate (retained)
|
|
86
|
+
|
|
87
|
+
`cbp-supabase-branch-check` runs as a required PR gate, independent of the create/delete
|
|
88
|
+
coupling. By-name resolution works whether the branch was created by `cbp-supabase-migrate`
|
|
89
|
+
or auto-created by the GitHub integration — both paths use the same branch name.
|
|
90
|
+
|
|
91
|
+
## Skill Map
|
|
92
|
+
|
|
93
|
+
| Role | Skill |
|
|
94
|
+
|---|---|
|
|
95
|
+
| Create (lazy) | `cbp-supabase-migrate` (Step 2.3) |
|
|
96
|
+
| Delete | `cbp-checkpoint-end`, `cbp-git-worktree-remove`, `cbp-ship-main` |
|
|
97
|
+
| PR gate | `cbp-supabase-branch-check` |
|
|
98
|
+
|
|
99
|
+
Each skill in the Skill Map above carries an inline back-reference to this rule at its create or teardown step.
|
|
@@ -47,8 +47,7 @@ These values are part of the CBP baseline and should not be weakened:
|
|
|
47
47
|
"Bash(git push --force:*)",
|
|
48
48
|
"Bash(git push -f:*)",
|
|
49
49
|
"Bash(git checkout -- :*)",
|
|
50
|
-
"Bash(git add
|
|
51
|
-
"Bash(git add -A:*)",
|
|
50
|
+
"Bash(git add:*)",
|
|
52
51
|
"Bash(rm -rf:*)",
|
|
53
52
|
"Bash(pnpm add:*)",
|
|
54
53
|
"Bash(pnpm install:*)",
|
|
@@ -95,6 +95,8 @@ git push -u origin "feat/CHK-{NNN}-{slug}"
|
|
|
95
95
|
|
|
96
96
|
Slug: lowercase, dash-joined, punctuation dropped, ≤40 chars. Persist the branch via MCP `update_checkpoint(checkpoint_id, branch_name: "feat/CHK-{NNN}-{slug}")`. (The dedicated `/cbp-git-branch-feat-create` skill is the canonical config-driven helper if you prefer to delegate.)
|
|
97
97
|
|
|
98
|
+
**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.
|
|
99
|
+
|
|
98
100
|
### Step 10: Show Result + Auto-Trigger Plan
|
|
99
101
|
|
|
100
102
|
```
|
|
@@ -141,13 +141,25 @@ C) Keep all
|
|
|
141
141
|
|
|
142
142
|
Only delete with explicit user confirmation. Delete both local and remote:
|
|
143
143
|
```bash
|
|
144
|
-
git branch -d "$BRANCH"
|
|
145
|
-
git push origin --delete "$BRANCH"
|
|
144
|
+
git branch -d "$BRANCH" && git push origin --delete "$BRANCH"
|
|
146
145
|
```
|
|
147
146
|
|
|
147
|
+
Only after both the local and remote git delete above succeed, run a conditional Supabase preview-branch teardown for that branch name (do not suppress the delete output — a failed local delete must stay visible):
|
|
148
|
+
|
|
149
|
+
> Lifecycle contract: see [[supabase-branch-lifecycle]].
|
|
150
|
+
|
|
151
|
+
- Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
|
|
152
|
+
- Scan the returned list for an entry whose `name` exactly equals `$BRANCH`.
|
|
153
|
+
- If found: call `mcp__supabase__delete_branch` with its `branch_id`. Record the branch name in `SUPABASE_BRANCHES_DELETED[]`.
|
|
154
|
+
- If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
|
|
155
|
+
- 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.
|
|
156
|
+
- Never delete the parent project `rrvtrumtkhrsbhcyrwvf` itself or any persistent/production branch.
|
|
157
|
+
|
|
158
|
+
Accumulate all Supabase branch names removed across the loop in `SUPABASE_BRANCHES_DELETED`.
|
|
159
|
+
|
|
148
160
|
### Step 9: Current Feat Branch Cleanup
|
|
149
161
|
|
|
150
|
-
|
|
162
|
+
Before asking about the current feat branch, present a partial summary of what has shipped so far — runtime surfaces (from `/cbp-ship` Step 7), stale branches cleaned (Step 8), and the `SUPABASE_BRANCHES_DELETED` accumulated so far — then ask about the current feat branch via AskUserQuestion (the complete shipment record is finalised in Step 10):
|
|
151
163
|
|
|
152
164
|
```
|
|
153
165
|
The current feat branch [BRANCH] has been fully merged.
|
|
@@ -166,6 +178,14 @@ git branch -d "$FEAT_BRANCH"
|
|
|
166
178
|
git push origin --delete "$FEAT_BRANCH"
|
|
167
179
|
```
|
|
168
180
|
|
|
181
|
+
After the feat branch git delete, run the same conditional Supabase teardown for `$FEAT_BRANCH`:
|
|
182
|
+
|
|
183
|
+
- Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
|
|
184
|
+
- Scan for an entry whose `name` exactly equals `$FEAT_BRANCH`.
|
|
185
|
+
- If found: call `mcp__supabase__delete_branch` with its `branch_id`. Add `$FEAT_BRANCH` to `SUPABASE_BRANCHES_DELETED[]`.
|
|
186
|
+
- If not found: no-op silently — idempotent, not-found is success.
|
|
187
|
+
- 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.
|
|
188
|
+
|
|
169
189
|
### Step 10: Save Shipment Results and Summary
|
|
170
190
|
|
|
171
191
|
Update checkpoint context via MCP `update_checkpoint`. The `shipment` block contains both branch promotion AND runtime surface results (from `/cbp-ship` Step 7):
|
|
@@ -177,8 +197,9 @@ context.shipment: {
|
|
|
177
197
|
feat_to_base: { pr_url, merged: true/false },
|
|
178
198
|
surfaces: [...], // populated by /cbp-ship — per-surface deploy results
|
|
179
199
|
skipped: [...], // populated by /cbp-ship — surfaces explicitly skipped
|
|
180
|
-
stale_branches_cleaned: [list of deleted branches],
|
|
181
|
-
feat_branch_deleted: true/false
|
|
200
|
+
stale_branches_cleaned: [list of deleted git branches],
|
|
201
|
+
feat_branch_deleted: true/false,
|
|
202
|
+
supabase_branches_deleted: [list of Supabase preview branch names removed in Steps 8–9]
|
|
182
203
|
}
|
|
183
204
|
```
|
|
184
205
|
|
|
@@ -198,6 +219,7 @@ Present summary:
|
|
|
198
219
|
### Branch Operations
|
|
199
220
|
- Stale branches deleted: [N] ([list])
|
|
200
221
|
- Feat branch: [deleted/kept]
|
|
222
|
+
- Supabase preview branches deleted: [N] ([list from supabase_branches_deleted], or "none")
|
|
201
223
|
|
|
202
224
|
### Before/After Branch State
|
|
203
225
|
- Before: [list of feat/* branches]
|
|
@@ -116,7 +116,12 @@ Only use `--force` if the user confirms.
|
|
|
116
116
|
|
|
117
117
|
### Step 9: Delete Branch (if requested)
|
|
118
118
|
|
|
119
|
-
**Protected branch check:**
|
|
119
|
+
**Protected branch check:** Read the protected set from `.codebyplan/git.json`:
|
|
120
|
+
```bash
|
|
121
|
+
PRODUCTION=$(jq -r '.branch_config.production // "main"' .codebyplan/git.json)
|
|
122
|
+
PROTECTED=$(jq -r '.branch_config.protected[]? // empty' .codebyplan/git.json)
|
|
123
|
+
```
|
|
124
|
+
If `$BRANCH_NAME` equals `$PRODUCTION` or appears in `$PROTECTED` — refuse deletion and stop.
|
|
120
125
|
|
|
121
126
|
**Checkpoint verification:** Before deleting a feat branch, verify that the associated checkpoint has completed via `/cbp-checkpoint-end`. If the checkpoint is still active, warn the user that unshipped work may be lost.
|
|
122
127
|
|
|
@@ -126,6 +131,17 @@ git branch -d "$BRANCH_NAME" && git push origin --delete "$BRANCH_NAME"
|
|
|
126
131
|
|
|
127
132
|
Use `-d` (not `-D`) to prevent deleting unmerged work. If it fails because the branch is not fully merged, inform the user and ask if they want to force delete with `-D`.
|
|
128
133
|
|
|
134
|
+
After the git branch delete succeeds, run a conditional Supabase preview-branch teardown for `$BRANCH_NAME`:
|
|
135
|
+
|
|
136
|
+
> Lifecycle contract: see [[supabase-branch-lifecycle]].
|
|
137
|
+
|
|
138
|
+
- Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
|
|
139
|
+
- Scan the returned list for an entry whose `name` exactly equals `$BRANCH_NAME`.
|
|
140
|
+
- If found: call `mcp__supabase__delete_branch` with its `branch_id`. Report "Supabase preview branch deleted: `$BRANCH_NAME`".
|
|
141
|
+
- If not found: no-op silently — the GitHub integration may have already removed it on PR close; not-found is success, NOT an error.
|
|
142
|
+
- 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.
|
|
143
|
+
- Never delete the parent project `rrvtrumtkhrsbhcyrwvf` itself or any persistent/production branch.
|
|
144
|
+
|
|
129
145
|
### Step 10: Show Result
|
|
130
146
|
|
|
131
147
|
```
|
|
@@ -12,7 +12,7 @@ Activate the session, open a fresh session log, and surface the previous log's p
|
|
|
12
12
|
|
|
13
13
|
## Instructions
|
|
14
14
|
|
|
15
|
-
Run Steps 0 through 5.8 silently (no intermediate output) — except Step 1.4 may surface a one-line fast-forward note or warning, and Step 5.7 may surface an approval gate. (Step numbers are organizational labels; execution order is 0 → 1 → 1.4 → 2 → 3 → 4 → 4.5 → 5 → 5.7 → 5.8 → 6 → 7.) Produce ONE output block at Step 6, then auto-trigger or stop per Step 7.
|
|
15
|
+
Run Steps 0 through 5.8 silently (no intermediate output) — except Step 1.4 may surface a one-line fast-forward note or warning, Step 1.5 may surface a one-line infra-drift nudge, and Step 5.7 may surface an approval gate. (Step numbers are organizational labels; execution order is 0 → 1 → 1.4 → 1.5 → 2 → 3 → 4 → 4.5 → 5 → 5.7 → 5.8 → 6 → 7.) Produce ONE output block at Step 6, then auto-trigger or stop per Step 7.
|
|
16
16
|
|
|
17
17
|
### Step 0: MCP Health Check
|
|
18
18
|
|
|
@@ -77,6 +77,31 @@ CURRENT="$(git rev-parse --abbrev-ref HEAD)"
|
|
|
77
77
|
|
|
78
78
|
Never rebase, reset, force-push, or stash. A non-fast-forwardable home branch is a signal to reconcile manually, not to overwrite.
|
|
79
79
|
|
|
80
|
+
### Step 1.5: Infra Drift Check
|
|
81
|
+
|
|
82
|
+
Surface — never block — when this worktree's CBP tooling has fallen behind. Runs after Step 1.4 and may add one line to the Step 6 output. Two mutually-exclusive concepts, keyed on repo type (`$PRODUCTION` is the branch resolved in Step 1.4):
|
|
83
|
+
|
|
84
|
+
- **Monorepo (concept A)** — both `packages/codebyplan-package/templates/` and `scripts/infra-drift.mjs` exist. Step 1.4 skips the fetch on a feat branch, so refresh `origin/$PRODUCTION` best-effort first, then run the reporter:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git fetch origin "$PRODUCTION" 2>/dev/null || true
|
|
88
|
+
node scripts/infra-drift.mjs 2>/dev/null || true
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The script self-guards (feat branch + behind > 0) and emits at most one `⚠ .claude/ infra is N behind — run /cbp-refresh-infra` line. Hold any output for Step 6.
|
|
92
|
+
|
|
93
|
+
- **Consumer (concept B)** — no `templates/`, but an install manifest exists (`.claude/.cbp.manifest.json`, falling back to `.cbp-claude.manifest.json` then `.codebyplan-claude.manifest.json`). Compare its `version` to the latest published `codebyplan`, offline-safe:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
LATEST="$(npm view codebyplan version 2>/dev/null)"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
When `$LATEST` is non-empty and newer than the manifest `version`, hold one line for Step 6: `⚠ codebyplan {installed} → {LATEST} — run npx codebyplan@latest claude update`. Any failure (offline, npm absent) → silent.
|
|
100
|
+
|
|
101
|
+
- **Neither** → skip silently.
|
|
102
|
+
|
|
103
|
+
Concept B never fires in the monorepo — the `templates/` guard routes the source repo to concept A only (its manifest `version` is intentionally stale). Fully non-blocking; every failure path falls through with no output.
|
|
104
|
+
|
|
80
105
|
### Step 2: Check Dev Server
|
|
81
106
|
|
|
82
107
|
**Skip if `server_type` is `"none"`.**
|
|
@@ -198,7 +223,7 @@ Three-branch gate using `owned_count` and `total_count` from Step 5.8:
|
|
|
198
223
|
|
|
199
224
|
- **Triggered by**: user invocation, `/clear` recovery
|
|
200
225
|
- **Resolves**: `npx codebyplan resolve-worktree --json` (worktree id + distress signal; non-tuple-miss distress is non-blocking at session-start)
|
|
201
|
-
- **Reads**: `.codebyplan/repo.json`, `.codebyplan/git.json` (`branch_config.production` for the Step 1.4 home-branch fast-forward), MCP `get_session_logs` (worktree-filtered, limit 1 — single call shared by Step 4 and Step 4.5), MCP `health_check`, MCP `get_current_task`, MCP `get_rounds`, MCP `get_checkpoints` (two calls: `{ repo_id, status: 'active' }` for the Step 5.8 ownership partition; `{ repo_id }` unfiltered for the Step 4.5 freshness probe, which may resolve a non-active checkpoint), MCP `get_tasks` / `get_rounds` for the Step 4.5 freshness probe
|
|
226
|
+
- **Reads**: `.codebyplan/repo.json`, `.codebyplan/git.json` (`branch_config.production` for the Step 1.4 home-branch fast-forward), MCP `get_session_logs` (worktree-filtered, limit 1 — single call shared by Step 4 and Step 4.5), MCP `health_check`, MCP `get_current_task`, MCP `get_rounds`, MCP `get_checkpoints` (two calls: `{ repo_id, status: 'active' }` for the Step 5.8 ownership partition; `{ repo_id }` unfiltered for the Step 4.5 freshness probe, which may resolve a non-active checkpoint), MCP `get_tasks` / `get_rounds` for the Step 4.5 freshness probe; `scripts/infra-drift.mjs` + a best-effort `git fetch` (Step 1.5 monorepo drift) or the install manifest + `npm view codebyplan version` (Step 1.5 consumer drift)
|
|
202
227
|
- **Writes**: MCP `create_session_log` (new, possibly empty), MCP `update_session_state` (activate)
|
|
203
228
|
- **Spawns**: none
|
|
204
229
|
- **Triggers**: `/cbp-git-commit` (conditional, on user approval), `handoff.command` (on fresh handoff hit at Step 4.5), `/cbp-todo` (auto fall-through when owned_count >= 1 or total_count === 0; STOPS with no trigger when total_count >= 1 AND owned_count === 0)
|
|
@@ -52,6 +52,19 @@ Pass `--dry-run` through if the skill was invoked with a dry-run arg.
|
|
|
52
52
|
|
|
53
53
|
Parse JSON from Step 3. Report `pr_url`, `merge_commit`, `branch_deleted`. If `checks_failed: true`, surface `checks_failure_reason` and stop.
|
|
54
54
|
|
|
55
|
+
If `branch_deleted === true`, run a conditional Supabase preview-branch teardown for the feat branch that was just merged:
|
|
56
|
+
|
|
57
|
+
> Lifecycle contract: see [[supabase-branch-lifecycle]].
|
|
58
|
+
|
|
59
|
+
- Read `FEAT_BRANCH` from the `feat_branch` field in the Step 3 JSON output — NOT from `git branch --show-current`. By the time Step 4 runs, `codebyplan ship` has already checked out the base branch (unless `--keep-feat` was passed), so the live branch is the base, not the feat branch.
|
|
60
|
+
- Call `mcp__supabase__list_branches` with `project_id: rrvtrumtkhrsbhcyrwvf`.
|
|
61
|
+
- Scan the returned list for an entry whose `name` exactly equals `$FEAT_BRANCH`.
|
|
62
|
+
- If found: call `mcp__supabase__delete_branch` with its `branch_id`. Report the Supabase delete outcome alongside `pr_url` / `merge_commit` / `branch_deleted`.
|
|
63
|
+
- If not found: no-op silently — the GitHub integration may have already removed the preview branch on PR close; not-found is success, NOT an error.
|
|
64
|
+
- 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.
|
|
65
|
+
- Never delete the parent project `rrvtrumtkhrsbhcyrwvf` itself or any persistent/production branch.
|
|
66
|
+
- This coordinates safely with `/cbp-checkpoint-end` — the existence-checked delete makes any second attempt a harmless no-op.
|
|
67
|
+
|
|
55
68
|
## Key Rules
|
|
56
69
|
|
|
57
70
|
- **Read branch names from config** — never hardcode "main"
|
|
@@ -62,10 +62,10 @@ Infer `TARGET` when absent:
|
|
|
62
62
|
|
|
63
63
|
## Step 1 — Read DB Paths Config
|
|
64
64
|
|
|
65
|
-
Read `.codebyplan.json` to obtain the configured DB path globs:
|
|
65
|
+
Read `.codebyplan/shipment.json` to obtain the configured DB path globs:
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
DB_PATHS=$(jq -r '.shipment.surfaces.supabase.db_paths[]? // empty' .codebyplan.json 2>/dev/null)
|
|
68
|
+
DB_PATHS=$(jq -r '.shipment.surfaces.supabase.db_paths[]? // empty' .codebyplan/shipment.json 2>/dev/null)
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
If `DB_PATHS` is empty, fall back to defaults:
|
|
@@ -80,11 +80,11 @@ Store each pattern as a separate entry for matching in Step 2.
|
|
|
80
80
|
|
|
81
81
|
## Step 2 — Detect DB-Path Changes
|
|
82
82
|
|
|
83
|
-
Resolve the BASE branch from `TARGET`. Read `.codebyplan.json`:
|
|
83
|
+
Resolve the BASE branch from `TARGET`. Read `.codebyplan/git.json`:
|
|
84
84
|
|
|
85
85
|
```bash
|
|
86
|
-
INTEGRATION=$(jq -r '.branch_config.integration // "development"' .codebyplan.json)
|
|
87
|
-
PRODUCTION=$(jq -r '.branch_config.production // "main"' .codebyplan.json)
|
|
86
|
+
INTEGRATION=$(jq -r '.branch_config.integration // "development"' .codebyplan/git.json)
|
|
87
|
+
PRODUCTION=$(jq -r '.branch_config.production // "main"' .codebyplan/git.json)
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
Set `BASE`:
|
|
@@ -156,6 +156,13 @@ Store as `PROJECT_REF`.
|
|
|
156
156
|
|
|
157
157
|
## Step 4 — Handle Missing project_ref
|
|
158
158
|
|
|
159
|
+
> **Note — Hybrid branch creation**: The preview branch may pre-exist as a CBP-created
|
|
160
|
+
> branch (named identically to the git branch, provisioned lazily by `cbp-supabase-migrate`
|
|
161
|
+
> on first DB change) rather than being auto-created by the GitHub integration on PR open.
|
|
162
|
+
> The by-name resolution in Step 3 works identically for both creation paths because both
|
|
163
|
+
> name the branch verbatim after the git branch. See [[supabase-branch-lifecycle]] for the
|
|
164
|
+
> full lifecycle contract.
|
|
165
|
+
|
|
159
166
|
If `PROJECT_REF` is empty after Step 3:
|
|
160
167
|
|
|
161
168
|
- `MODE=pre_pr_create`:
|