codebyplan 1.13.64 → 1.13.66

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 (303) hide show
  1. package/README.md +118 -75
  2. package/dist/__tests__/helpers.d.ts +22 -0
  3. package/dist/__tests__/helpers.d.ts.map +1 -0
  4. package/dist/ci-public.d.ts +20 -0
  5. package/dist/ci-public.d.ts.map +1 -0
  6. package/dist/ci.js +11 -2
  7. package/dist/cli/arch-map.d.ts +33 -0
  8. package/dist/cli/arch-map.d.ts.map +1 -0
  9. package/dist/cli/branch.d.ts +10 -0
  10. package/dist/cli/branch.d.ts.map +1 -0
  11. package/dist/cli/bump.d.ts +12 -0
  12. package/dist/cli/bump.d.ts.map +1 -0
  13. package/dist/cli/cd.d.ts +20 -0
  14. package/dist/cli/cd.d.ts.map +1 -0
  15. package/dist/cli/check.d.ts +30 -0
  16. package/dist/cli/check.d.ts.map +1 -0
  17. package/dist/cli/checkpoint.d.ts +17 -0
  18. package/dist/cli/checkpoint.d.ts.map +1 -0
  19. package/dist/cli/ci.d.ts +31 -0
  20. package/dist/cli/ci.d.ts.map +1 -0
  21. package/dist/cli/claude/__test-helpers__/expect-manifest.d.ts +7 -0
  22. package/dist/cli/claude/__test-helpers__/expect-manifest.d.ts.map +1 -0
  23. package/dist/cli/claude/__test-helpers__/tmp-fixture.d.ts +64 -0
  24. package/dist/cli/claude/__test-helpers__/tmp-fixture.d.ts.map +1 -0
  25. package/dist/cli/claude/audit-mode.d.ts +16 -0
  26. package/dist/cli/claude/audit-mode.d.ts.map +1 -0
  27. package/dist/cli/claude/generate.d.ts +23 -0
  28. package/dist/cli/claude/generate.d.ts.map +1 -0
  29. package/dist/cli/claude/install.d.ts +55 -0
  30. package/dist/cli/claude/install.d.ts.map +1 -0
  31. package/dist/cli/claude/migrate-memory.d.ts +117 -0
  32. package/dist/cli/claude/migrate-memory.d.ts.map +1 -0
  33. package/dist/cli/claude/readme.d.ts +28 -0
  34. package/dist/cli/claude/readme.d.ts.map +1 -0
  35. package/dist/cli/claude/status.d.ts +41 -0
  36. package/dist/cli/claude/status.d.ts.map +1 -0
  37. package/dist/cli/claude/uninstall.d.ts +21 -0
  38. package/dist/cli/claude/uninstall.d.ts.map +1 -0
  39. package/dist/cli/claude/update.d.ts +25 -0
  40. package/dist/cli/claude/update.d.ts.map +1 -0
  41. package/dist/cli/claude/verify-parity.d.ts +25 -0
  42. package/dist/cli/claude/verify-parity.d.ts.map +1 -0
  43. package/dist/cli/cleanup-plan-folders.d.ts +21 -0
  44. package/dist/cli/cleanup-plan-folders.d.ts.map +1 -0
  45. package/dist/cli/commit.d.ts +25 -0
  46. package/dist/cli/commit.d.ts.map +1 -0
  47. package/dist/cli/config.d.ts +50 -0
  48. package/dist/cli/config.d.ts.map +1 -0
  49. package/dist/cli/confirm.d.ts +9 -0
  50. package/dist/cli/confirm.d.ts.map +1 -0
  51. package/dist/cli/create-org.d.ts +9 -0
  52. package/dist/cli/create-org.d.ts.map +1 -0
  53. package/dist/cli/create-project.d.ts +10 -0
  54. package/dist/cli/create-project.d.ts.map +1 -0
  55. package/dist/cli/create-repo.d.ts +14 -0
  56. package/dist/cli/create-repo.d.ts.map +1 -0
  57. package/dist/cli/docs.d.ts +41 -0
  58. package/dist/cli/docs.d.ts.map +1 -0
  59. package/dist/cli/doctor.d.ts +17 -0
  60. package/dist/cli/doctor.d.ts.map +1 -0
  61. package/dist/cli/e2e/verify-round.d.ts +46 -0
  62. package/dist/cli/e2e/verify-round.d.ts.map +1 -0
  63. package/dist/cli/e2e.d.ts +11 -0
  64. package/dist/cli/e2e.d.ts.map +1 -0
  65. package/dist/cli/eslint.d.ts +20 -0
  66. package/dist/cli/eslint.d.ts.map +1 -0
  67. package/dist/cli/export-writer.d.ts +99 -0
  68. package/dist/cli/export-writer.d.ts.map +1 -0
  69. package/dist/cli/handoff.d.ts +13 -0
  70. package/dist/cli/handoff.d.ts.map +1 -0
  71. package/dist/cli/login.d.ts +10 -0
  72. package/dist/cli/login.d.ts.map +1 -0
  73. package/dist/cli/logout.d.ts +2 -0
  74. package/dist/cli/logout.d.ts.map +1 -0
  75. package/dist/cli/lsp.d.ts +16 -0
  76. package/dist/cli/lsp.d.ts.map +1 -0
  77. package/dist/cli/migration-collisions.d.ts +10 -0
  78. package/dist/cli/migration-collisions.d.ts.map +1 -0
  79. package/dist/cli/ports.d.ts +29 -0
  80. package/dist/cli/ports.d.ts.map +1 -0
  81. package/dist/cli/process-exit-signal.d.ts +24 -0
  82. package/dist/cli/process-exit-signal.d.ts.map +1 -0
  83. package/dist/cli/round.d.ts +79 -0
  84. package/dist/cli/round.d.ts.map +1 -0
  85. package/dist/cli/scaffold-publish-workflow.d.ts +16 -0
  86. package/dist/cli/scaffold-publish-workflow.d.ts.map +1 -0
  87. package/dist/cli/session/freshness-gate.d.ts +21 -0
  88. package/dist/cli/session/freshness-gate.d.ts.map +1 -0
  89. package/dist/cli/session/home-ff.d.ts +16 -0
  90. package/dist/cli/session/home-ff.d.ts.map +1 -0
  91. package/dist/cli/session/infra-files.d.ts +21 -0
  92. package/dist/cli/session/infra-files.d.ts.map +1 -0
  93. package/dist/cli/session/start.d.ts +70 -0
  94. package/dist/cli/session/start.d.ts.map +1 -0
  95. package/dist/cli/session.d.ts +17 -0
  96. package/dist/cli/session.d.ts.map +1 -0
  97. package/dist/cli/setup.d.ts +2 -0
  98. package/dist/cli/setup.d.ts.map +1 -0
  99. package/dist/cli/ship.d.ts +16 -0
  100. package/dist/cli/ship.d.ts.map +1 -0
  101. package/dist/cli/slug.d.ts +10 -0
  102. package/dist/cli/slug.d.ts.map +1 -0
  103. package/dist/cli/standalone-task.d.ts +31 -0
  104. package/dist/cli/standalone-task.d.ts.map +1 -0
  105. package/dist/cli/statusline.d.ts +10 -0
  106. package/dist/cli/statusline.d.ts.map +1 -0
  107. package/dist/cli/supabase/new-migration.d.ts +24 -0
  108. package/dist/cli/supabase/new-migration.d.ts.map +1 -0
  109. package/dist/cli/supabase/preview-check.d.ts +30 -0
  110. package/dist/cli/supabase/preview-check.d.ts.map +1 -0
  111. package/dist/cli/supabase/resolve-preview.d.ts +19 -0
  112. package/dist/cli/supabase/resolve-preview.d.ts.map +1 -0
  113. package/dist/cli/supabase/teardown-preview.d.ts +22 -0
  114. package/dist/cli/supabase/teardown-preview.d.ts.map +1 -0
  115. package/dist/cli/sync.d.ts +13 -0
  116. package/dist/cli/sync.d.ts.map +1 -0
  117. package/dist/cli/task.d.ts +17 -0
  118. package/dist/cli/task.d.ts.map +1 -0
  119. package/dist/cli/tech-stack.d.ts +11 -0
  120. package/dist/cli/tech-stack.d.ts.map +1 -0
  121. package/dist/cli/upgrade-auth.d.ts +2 -0
  122. package/dist/cli/upgrade-auth.d.ts.map +1 -0
  123. package/dist/cli/upload-e2e-images.d.ts +18 -0
  124. package/dist/cli/upload-e2e-images.d.ts.map +1 -0
  125. package/dist/cli/validate-waves.d.ts +22 -0
  126. package/dist/cli/validate-waves.d.ts.map +1 -0
  127. package/dist/cli/version-status.d.ts +42 -0
  128. package/dist/cli/version-status.d.ts.map +1 -0
  129. package/dist/cli/watch.d.ts +18 -0
  130. package/dist/cli/watch.d.ts.map +1 -0
  131. package/dist/cli/whoami.d.ts +5 -0
  132. package/dist/cli/whoami.d.ts.map +1 -0
  133. package/dist/cli/worktree/add.d.ts +81 -0
  134. package/dist/cli/worktree/add.d.ts.map +1 -0
  135. package/dist/cli/worktree/create.d.ts +46 -0
  136. package/dist/cli/worktree/create.d.ts.map +1 -0
  137. package/dist/cli/worktree/path.d.ts +19 -0
  138. package/dist/cli/worktree/path.d.ts.map +1 -0
  139. package/dist/cli/worktree/remove.d.ts +60 -0
  140. package/dist/cli/worktree/remove.d.ts.map +1 -0
  141. package/dist/cli/worktree.d.ts +15 -0
  142. package/dist/cli/worktree.d.ts.map +1 -0
  143. package/dist/cli.js +6194 -5429
  144. package/dist/dispatch.d.ts +64 -0
  145. package/dist/dispatch.d.ts.map +1 -0
  146. package/dist/index.d.ts +2 -0
  147. package/dist/index.d.ts.map +1 -0
  148. package/dist/lib/agents-generator.d.ts +42 -0
  149. package/dist/lib/agents-generator.d.ts.map +1 -0
  150. package/dist/lib/api.d.ts +54 -0
  151. package/dist/lib/api.d.ts.map +1 -0
  152. package/dist/lib/atomic-write.d.ts +27 -0
  153. package/dist/lib/atomic-write.d.ts.map +1 -0
  154. package/dist/lib/branch-checkout.d.ts +47 -0
  155. package/dist/lib/branch-checkout.d.ts.map +1 -0
  156. package/dist/lib/branch-port-resolver.d.ts +58 -0
  157. package/dist/lib/branch-port-resolver.d.ts.map +1 -0
  158. package/dist/lib/bump.d.ts +97 -0
  159. package/dist/lib/bump.d.ts.map +1 -0
  160. package/dist/lib/cd-init.d.ts +86 -0
  161. package/dist/lib/cd-init.d.ts.map +1 -0
  162. package/dist/lib/check-baseline.d.ts +91 -0
  163. package/dist/lib/check-baseline.d.ts.map +1 -0
  164. package/dist/lib/check.d.ts +178 -0
  165. package/dist/lib/check.d.ts.map +1 -0
  166. package/dist/lib/ci-init.d.ts +106 -0
  167. package/dist/lib/ci-init.d.ts.map +1 -0
  168. package/dist/lib/ci-resolve.d.ts +60 -0
  169. package/dist/lib/ci-resolve.d.ts.map +1 -0
  170. package/dist/lib/claude-mode-audit.d.ts +41 -0
  171. package/dist/lib/claude-mode-audit.d.ts.map +1 -0
  172. package/dist/lib/claude-plan.d.ts +34 -0
  173. package/dist/lib/claude-plan.d.ts.map +1 -0
  174. package/dist/lib/commit-scope.d.ts +21 -0
  175. package/dist/lib/commit-scope.d.ts.map +1 -0
  176. package/dist/lib/e2e.d.ts +157 -0
  177. package/dist/lib/e2e.d.ts.map +1 -0
  178. package/dist/lib/eslint-generator.d.ts +86 -0
  179. package/dist/lib/eslint-generator.d.ts.map +1 -0
  180. package/dist/lib/file-lock.d.ts +44 -0
  181. package/dist/lib/file-lock.d.ts.map +1 -0
  182. package/dist/lib/flags.d.ts +115 -0
  183. package/dist/lib/flags.d.ts.map +1 -0
  184. package/dist/lib/gh-required-checks.d.ts +76 -0
  185. package/dist/lib/gh-required-checks.d.ts.map +1 -0
  186. package/dist/lib/git-utils.d.ts +46 -0
  187. package/dist/lib/git-utils.d.ts.map +1 -0
  188. package/dist/lib/gitignore-block.d.ts +32 -0
  189. package/dist/lib/gitignore-block.d.ts.map +1 -0
  190. package/dist/lib/gitignore-detect.d.ts +117 -0
  191. package/dist/lib/gitignore-detect.d.ts.map +1 -0
  192. package/dist/lib/handoff-file.d.ts +63 -0
  193. package/dist/lib/handoff-file.d.ts.map +1 -0
  194. package/dist/lib/hash.d.ts +9 -0
  195. package/dist/lib/hash.d.ts.map +1 -0
  196. package/dist/lib/lsp-detect.d.ts +42 -0
  197. package/dist/lib/lsp-detect.d.ts.map +1 -0
  198. package/dist/lib/manifest.d.ts +88 -0
  199. package/dist/lib/manifest.d.ts.map +1 -0
  200. package/dist/lib/mcp-client.d.ts +39 -0
  201. package/dist/lib/mcp-client.d.ts.map +1 -0
  202. package/dist/lib/migrate-branch-model.d.ts +34 -0
  203. package/dist/lib/migrate-branch-model.d.ts.map +1 -0
  204. package/dist/lib/migrate-local-config.d.ts +39 -0
  205. package/dist/lib/migrate-local-config.d.ts.map +1 -0
  206. package/dist/lib/migration-collisions.d.ts +61 -0
  207. package/dist/lib/migration-collisions.d.ts.map +1 -0
  208. package/dist/lib/port-verify.d.ts +73 -0
  209. package/dist/lib/port-verify.d.ts.map +1 -0
  210. package/dist/lib/prompt.d.ts +41 -0
  211. package/dist/lib/prompt.d.ts.map +1 -0
  212. package/dist/lib/readme-generator.d.ts +108 -0
  213. package/dist/lib/readme-generator.d.ts.map +1 -0
  214. package/dist/lib/repo-reader.d.ts +59 -0
  215. package/dist/lib/repo-reader.d.ts.map +1 -0
  216. package/dist/lib/scaffold-cd-workflow.d.ts +48 -0
  217. package/dist/lib/scaffold-cd-workflow.d.ts.map +1 -0
  218. package/dist/lib/scaffold-ci-workflow.d.ts +83 -0
  219. package/dist/lib/scaffold-ci-workflow.d.ts.map +1 -0
  220. package/dist/lib/scaffold-publish-workflow.d.ts +48 -0
  221. package/dist/lib/scaffold-publish-workflow.d.ts.map +1 -0
  222. package/dist/lib/server-detect.d.ts +20 -0
  223. package/dist/lib/server-detect.d.ts.map +1 -0
  224. package/dist/lib/session.d.ts +371 -0
  225. package/dist/lib/session.d.ts.map +1 -0
  226. package/dist/lib/settings-merge.d.ts +268 -0
  227. package/dist/lib/settings-merge.d.ts.map +1 -0
  228. package/dist/lib/ship.d.ts +84 -0
  229. package/dist/lib/ship.d.ts.map +1 -0
  230. package/dist/lib/slug.d.ts +26 -0
  231. package/dist/lib/slug.d.ts.map +1 -0
  232. package/dist/lib/spawn.d.ts +12 -0
  233. package/dist/lib/spawn.d.ts.map +1 -0
  234. package/dist/lib/state-client.d.ts +54 -0
  235. package/dist/lib/state-client.d.ts.map +1 -0
  236. package/dist/lib/state-store.d.ts +153 -0
  237. package/dist/lib/state-store.d.ts.map +1 -0
  238. package/dist/lib/state-sync.d.ts +101 -0
  239. package/dist/lib/state-sync.d.ts.map +1 -0
  240. package/dist/lib/statusline-config.d.ts +55 -0
  241. package/dist/lib/statusline-config.d.ts.map +1 -0
  242. package/dist/lib/structure-generator.d.ts +63 -0
  243. package/dist/lib/structure-generator.d.ts.map +1 -0
  244. package/dist/lib/supabase.d.ts +87 -0
  245. package/dist/lib/supabase.d.ts.map +1 -0
  246. package/dist/lib/sync-approvals.d.ts +108 -0
  247. package/dist/lib/sync-approvals.d.ts.map +1 -0
  248. package/dist/lib/tech-detect.d.ts +77 -0
  249. package/dist/lib/tech-detect.d.ts.map +1 -0
  250. package/dist/lib/template-walker.d.ts +26 -0
  251. package/dist/lib/template-walker.d.ts.map +1 -0
  252. package/dist/lib/templates-dir.d.ts +25 -0
  253. package/dist/lib/templates-dir.d.ts.map +1 -0
  254. package/dist/lib/types.d.ts +843 -0
  255. package/dist/lib/types.d.ts.map +1 -0
  256. package/dist/lib/validate-waves.d.ts +38 -0
  257. package/dist/lib/validate-waves.d.ts.map +1 -0
  258. package/dist/lib/verify-parity.d.ts +154 -0
  259. package/dist/lib/verify-parity.d.ts.map +1 -0
  260. package/dist/lib/version.d.ts +3 -0
  261. package/dist/lib/version.d.ts.map +1 -0
  262. package/dist/lib/watch-daemon.d.ts +114 -0
  263. package/dist/lib/watch-daemon.d.ts.map +1 -0
  264. package/dist/lib/worktree.d.ts +56 -0
  265. package/dist/lib/worktree.d.ts.map +1 -0
  266. package/dist/oauth/browser.d.ts +8 -0
  267. package/dist/oauth/browser.d.ts.map +1 -0
  268. package/dist/oauth/client-registration.d.ts +16 -0
  269. package/dist/oauth/client-registration.d.ts.map +1 -0
  270. package/dist/oauth/device-flow.d.ts +40 -0
  271. package/dist/oauth/device-flow.d.ts.map +1 -0
  272. package/dist/oauth/jwt-decode.d.ts +10 -0
  273. package/dist/oauth/jwt-decode.d.ts.map +1 -0
  274. package/dist/oauth/keychain.d.ts +17 -0
  275. package/dist/oauth/keychain.d.ts.map +1 -0
  276. package/dist/oauth/token-refresh.d.ts +7 -0
  277. package/dist/oauth/token-refresh.d.ts.map +1 -0
  278. package/dist/oauth/types.d.ts +28 -0
  279. package/dist/oauth/types.d.ts.map +1 -0
  280. package/dist/oauth/urls.d.ts +16 -0
  281. package/dist/oauth/urls.d.ts.map +1 -0
  282. package/package.json +5 -6
  283. package/templates/agents/cbp-security-agent.md +0 -1
  284. package/templates/agents/cbp-stripe-agent.md +0 -1
  285. package/templates/agents/cbp-testing-qa-agent.md +0 -1
  286. package/templates/github-workflows/ci.yml +5 -0
  287. package/templates/hooks/cbp-session-id-stamp.sh +67 -0
  288. package/templates/hooks/cbp-skill-context-guard.sh +0 -1
  289. package/templates/hooks/cbp-test-hooks.sh +105 -0
  290. package/templates/hooks/validate-structure-lib.sh +36 -9
  291. package/templates/rules/handoff-file-convention.md +65 -0
  292. package/templates/settings.project.base.json +17 -1
  293. package/templates/skills/cbp-checkpoint-check/SKILL.md +0 -1
  294. package/templates/skills/cbp-checkpoint-complete/SKILL.md +32 -0
  295. package/templates/skills/cbp-clear-continue/SKILL.md +23 -2
  296. package/templates/skills/cbp-clear-prep/SKILL.md +22 -2
  297. package/templates/skills/cbp-finalize/SKILL.md +36 -0
  298. package/templates/skills/cbp-session-end/SKILL.md +23 -25
  299. package/templates/skills/cbp-session-start/SKILL.md +24 -2
  300. package/templates/skills/cbp-setup-cd/SKILL.md +0 -1
  301. package/templates/skills/cbp-setup-ci/SKILL.md +0 -1
  302. package/templates/skills/cbp-standalone-task-complete/SKILL.md +32 -0
  303. package/templates/skills/cbp-stripe/SKILL.md +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebyplan",
3
- "version": "1.13.64",
3
+ "version": "1.13.66",
4
4
  "description": "CLI for CodeByPlan — AI-powered development planning and tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -52,18 +52,17 @@
52
52
  "registry": "https://registry.npmjs.org/"
53
53
  },
54
54
  "engines": {
55
- "node": ">=18"
55
+ "node": ">=22"
56
56
  },
57
57
  "dependencies": {
58
58
  "@napi-rs/keyring": "^1.1.6",
59
- "@supabase/supabase-js": "^2.106.0",
60
- "ws": ">=8.20.1"
59
+ "@supabase/supabase-js": "^2.106.0"
61
60
  },
62
61
  "devDependencies": {
63
62
  "@eslint/js": "^9.18.0",
64
- "@types/node": "^20",
63
+ "@types/node": "^22",
65
64
  "@vitest/eslint-plugin": "^1.1.44",
66
- "esbuild": ">=0.28.1",
65
+ "esbuild": "^0.28.1",
67
66
  "eslint": "^9.18.0",
68
67
  "eslint-config-prettier": "^10.0.1",
69
68
  "eslint-plugin-no-secrets": "^2.2.1",
@@ -1,5 +1,4 @@
1
1
  ---
2
- scope: org-shared
3
2
  name: cbp-security-agent
4
3
  description: Security review specialist. Checks for OWASP top 10 vulnerabilities, hardcoded secrets, SQL injection, XSS, CSRF, and dependency vulnerabilities.
5
4
  tools: Read, Glob, Grep, Bash
@@ -1,5 +1,4 @@
1
1
  ---
2
- scope: org-shared
3
2
  name: cbp-stripe-agent
4
3
  description: Stripe integration specialist. Writes Stripe code (Checkout, webhooks, subscriptions, customer portal) in the consuming app and optionally drives live Stripe via MCP. Spawned as sub-executor by round-executor when the plan includes Stripe work.
5
4
  tools: Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion
@@ -1,5 +1,4 @@
1
1
  ---
2
- scope: org-shared
3
2
  name: cbp-testing-qa-agent
4
3
  description: Combined testing, QA generation, and default checklists. Runs build/lint/types/unit-tests/audit, generates auto QA items, applies default production checklists. Does NOT consume e2e screenshots or frontend-ui findings.
5
4
  tools: Read, Glob, Grep, Bash, AskUserQuestion
@@ -40,6 +40,8 @@ jobs:
40
40
  ci:
41
41
  name: Lint + typecheck + test + build
42
42
  runs-on: ubuntu-latest
43
+ # Cap the job so a hung step can't burn the 6h default and waste runner minutes.
44
+ timeout-minutes: 20
43
45
  steps:
44
46
  - name: Checkout
45
47
  uses: actions/checkout@v4
@@ -85,6 +87,9 @@ jobs:
85
87
  ci-strict:
86
88
  name: Strict whole-repo green{{STRICT_NAME_SUFFIX}}
87
89
  runs-on: ubuntu-latest{{STRICT_CONTINUE_ON_ERROR_LINE}}
90
+ # Whole-repo green is heavier than the soft tier; allow more headroom but
91
+ # still cap it so a hang can't run to the 6h default.
92
+ timeout-minutes: 30
88
93
  steps:
89
94
  - name: Checkout
90
95
  uses: actions/checkout@v4
@@ -0,0 +1,67 @@
1
+ #!/bin/bash
2
+ # @scope: org-shared
3
+ # Hook: SessionStart
4
+ # Purpose: Stamp the Claude harness session UUID into
5
+ # .codebyplan/state/session/session-id.json so the CLI create-log path
6
+ # (and other session-aware tools) can correlate log entries with the
7
+ # running Claude session.
8
+ #
9
+ # Hook-safe: all errors are swallowed, always exits 0. Non-fatal / best-effort.
10
+ # No-op when not inside a codebyplan repo or when the session UUID cannot be
11
+ # derived from the hook payload.
12
+ #
13
+ # UUID derivation order:
14
+ # 1. stdin JSON payload `.session_id` field
15
+ # 2. basename (without extension) of `.transcript_path` field
16
+ # 3. no-op — exit 0 without writing
17
+
18
+ # C0 — require jq; if absent, exit 0 (fail-open).
19
+ if ! command -v jq > /dev/null 2>&1; then
20
+ exit 0
21
+ fi
22
+
23
+ # Resolve the project dir: Claude Code sets CLAUDE_PROJECT_DIR; fall back to pwd.
24
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
25
+
26
+ # No-op when not inside a codebyplan repo (sentinel file absent).
27
+ if [ ! -f "$PROJECT_DIR/.codebyplan/repo.json" ]; then
28
+ exit 0
29
+ fi
30
+
31
+ # Read stdin once into a variable.
32
+ INPUT=$(cat)
33
+
34
+ # Derive the session UUID — try session_id field first.
35
+ SESSION_UUID=$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
36
+
37
+ # Fall back to transcript_path basename (strip directory + extension).
38
+ if [ -z "$SESSION_UUID" ]; then
39
+ TRANSCRIPT=$(printf '%s' "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
40
+ if [ -n "$TRANSCRIPT" ]; then
41
+ BASENAME=$(basename "$TRANSCRIPT")
42
+ SESSION_UUID="${BASENAME%.*}"
43
+ fi
44
+ fi
45
+
46
+ # If still no UUID, nothing to write — exit gracefully.
47
+ if [ -z "$SESSION_UUID" ]; then
48
+ exit 0
49
+ fi
50
+
51
+ # UUID guard — accept only a canonical UUID (8-4-4-4-12 hex) to avoid
52
+ # stamping garbage values (e.g. a literal "null" or an arbitrary path stem).
53
+ UUID_PATTERN='^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'
54
+ if ! printf '%s' "$SESSION_UUID" | grep -qE "$UUID_PATTERN"; then
55
+ exit 0
56
+ fi
57
+
58
+ # Ensure the target directory exists.
59
+ SESSION_DIR="$PROJECT_DIR/.codebyplan/state/session"
60
+ mkdir -p "$SESSION_DIR" 2>/dev/null || true
61
+
62
+ # Stamp the session UUID with an ISO timestamp.
63
+ STAMPED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
64
+ printf '{"claude_session_id":"%s","stamped_at":"%s"}\n' "$SESSION_UUID" "$STAMPED_AT" \
65
+ > "$SESSION_DIR/session-id.json" 2>/dev/null || true
66
+
67
+ exit 0
@@ -1,5 +1,4 @@
1
1
  #!/bin/bash
2
- # @scope: org-shared
3
2
  # Hook: PreToolUse (Skill)
4
3
  # Purpose: Deny heavy close-out skills when context window exceeds the model-aware threshold:
5
4
  # 200K tokens for standard models (CBP_CONTEXT_WARN_TOKENS, default 200000);
@@ -614,6 +614,111 @@ else
614
614
 
615
615
  fi
616
616
 
617
+ # ===== HOOK SMOKE TESTS — cbp-session-id-stamp (CHK-231) =====
618
+ echo "## Hook Smoke Tests — cbp-session-id-stamp (CHK-231)"
619
+
620
+ STAMP_HOOK="$HOOKS_DIR/cbp-session-id-stamp.sh"
621
+
622
+ if [ ! -f "$STAMP_HOOK" ]; then
623
+ test_result "cbp-session-id-stamp.sh present" "passed" "missing"
624
+ else
625
+ test_result "cbp-session-id-stamp.sh present" "passed" "passed"
626
+
627
+ FIRST_LINE=$(head -1 "$STAMP_HOOK")
628
+ if echo "$FIRST_LINE" | grep -q '^#!/'; then
629
+ test_result "cbp-session-id-stamp.sh has shebang" "passed" "passed"
630
+ else
631
+ test_result "cbp-session-id-stamp.sh has shebang" "passed" "missing"
632
+ fi
633
+
634
+ if grep -q '@scope: org-shared' "$STAMP_HOOK"; then
635
+ test_result "cbp-session-id-stamp.sh has @scope: org-shared" "passed" "passed"
636
+ else
637
+ test_result "cbp-session-id-stamp.sh has @scope: org-shared" "passed" "missing"
638
+ fi
639
+
640
+ if bash -n "$STAMP_HOOK" 2>/dev/null; then
641
+ test_result "cbp-session-id-stamp.sh bash -n syntax ok" "passed" "passed"
642
+ else
643
+ test_result "cbp-session-id-stamp.sh bash -n syntax ok" "passed" "failed"
644
+ fi
645
+
646
+ # Case 1: session_id present in payload → session-id.json written with that UUID
647
+ ISO=$(mktemp -d)
648
+ mkdir -p "$ISO/.codebyplan"
649
+ printf '{}' > "$ISO/.codebyplan/repo.json"
650
+ FAKE_UUID="aaaabbbb-cccc-dddd-eeee-ffffffffffff"
651
+ PAYLOAD=$(jq -n --arg s "$FAKE_UUID" '{session_id:$s}')
652
+ EXIT_CODE=$(echo "$PAYLOAD" | CLAUDE_PROJECT_DIR="$ISO" bash "$STAMP_HOOK" >/dev/null 2>&1; echo $?)
653
+ STAMPED_ID=$(jq -r '.claude_session_id // empty' "$ISO/.codebyplan/state/session/session-id.json" 2>/dev/null)
654
+ if [ "$EXIT_CODE" = "0" ] && [ "$STAMPED_ID" = "$FAKE_UUID" ]; then
655
+ test_result "cbp-session-id-stamp.sh session_id present → session-id.json written with UUID" "passed" "passed"
656
+ else
657
+ test_result "cbp-session-id-stamp.sh session_id present → session-id.json written with UUID" "passed" "failed (exit=$EXIT_CODE stamped=$STAMPED_ID)"
658
+ fi
659
+ rm -rf "$ISO"
660
+
661
+ # Case 2: no session_id but transcript_path present (basename is a valid UUID) →
662
+ # UUID derived from transcript basename and written to session-id.json.
663
+ ISO=$(mktemp -d)
664
+ mkdir -p "$ISO/.codebyplan"
665
+ printf '{}' > "$ISO/.codebyplan/repo.json"
666
+ TRANSCRIPT_UUID="11112222-3333-4444-5555-666677778888"
667
+ PAYLOAD=$(jq -n --arg t "/tmp/sessions/${TRANSCRIPT_UUID}.jsonl" '{transcript_path:$t}')
668
+ EXIT_CODE=$(echo "$PAYLOAD" | CLAUDE_PROJECT_DIR="$ISO" bash "$STAMP_HOOK" >/dev/null 2>&1; echo $?)
669
+ STAMPED_ID=$(jq -r '.claude_session_id // empty' "$ISO/.codebyplan/state/session/session-id.json" 2>/dev/null)
670
+ if [ "$EXIT_CODE" = "0" ] && [ "$STAMPED_ID" = "$TRANSCRIPT_UUID" ]; then
671
+ test_result "cbp-session-id-stamp.sh transcript_path fallback → UUID derived from basename" "passed" "passed"
672
+ else
673
+ test_result "cbp-session-id-stamp.sh transcript_path fallback → UUID derived from basename" "passed" "failed (exit=$EXIT_CODE stamped=$STAMPED_ID)"
674
+ fi
675
+ rm -rf "$ISO"
676
+
677
+ # Case 3: neither session_id nor transcript_path → no-op (no file written), exit 0
678
+ ISO=$(mktemp -d)
679
+ mkdir -p "$ISO/.codebyplan"
680
+ printf '{}' > "$ISO/.codebyplan/repo.json"
681
+ PAYLOAD='{"tool_name":"some_tool"}'
682
+ EXIT_CODE=$(echo "$PAYLOAD" | CLAUDE_PROJECT_DIR="$ISO" bash "$STAMP_HOOK" >/dev/null 2>&1; echo $?)
683
+ STAMP_FILE="$ISO/.codebyplan/state/session/session-id.json"
684
+ if [ "$EXIT_CODE" = "0" ] && [ ! -f "$STAMP_FILE" ]; then
685
+ test_result "cbp-session-id-stamp.sh neither present → no-op, exit 0, no file written" "passed" "passed"
686
+ else
687
+ test_result "cbp-session-id-stamp.sh neither present → no-op, exit 0, no file written" "passed" "failed (exit=$EXIT_CODE file_exists=$([ -f "$STAMP_FILE" ] && echo yes || echo no))"
688
+ fi
689
+ rm -rf "$ISO"
690
+
691
+ # Case 4: outside a codebyplan repo (no .codebyplan/repo.json) → no-op, exit 0
692
+ ISO=$(mktemp -d)
693
+ FAKE_UUID="aaaabbbb-cccc-dddd-eeee-ffffffffffff"
694
+ PAYLOAD=$(jq -n --arg s "$FAKE_UUID" '{session_id:$s}')
695
+ EXIT_CODE=$(echo "$PAYLOAD" | CLAUDE_PROJECT_DIR="$ISO" bash "$STAMP_HOOK" >/dev/null 2>&1; echo $?)
696
+ STAMP_FILE="$ISO/.codebyplan/state/session/session-id.json"
697
+ if [ "$EXIT_CODE" = "0" ] && [ ! -f "$STAMP_FILE" ]; then
698
+ test_result "cbp-session-id-stamp.sh no repo.json → no-op, exit 0" "passed" "passed"
699
+ else
700
+ test_result "cbp-session-id-stamp.sh no repo.json → no-op, exit 0" "passed" "failed (exit=$EXIT_CODE file_exists=$([ -f "$STAMP_FILE" ] && echo yes || echo no))"
701
+ fi
702
+ rm -rf "$ISO"
703
+
704
+ # Case 5: session_id present but not a canonical UUID → guard rejects, no file written, exit 0
705
+ ISO=$(mktemp -d)
706
+ mkdir -p "$ISO/.codebyplan"
707
+ printf '{}' > "$ISO/.codebyplan/repo.json"
708
+ PAYLOAD=$(jq -n '{session_id:"not-a-valid-uuid"}')
709
+ EXIT_CODE=$(echo "$PAYLOAD" | CLAUDE_PROJECT_DIR="$ISO" bash "$STAMP_HOOK" >/dev/null 2>&1; echo $?)
710
+ STAMP_FILE="$ISO/.codebyplan/state/session/session-id.json"
711
+ if [ "$EXIT_CODE" = "0" ] && [ ! -f "$STAMP_FILE" ]; then
712
+ test_result "cbp-session-id-stamp.sh invalid UUID → guard rejects, no file written" "passed" "passed"
713
+ else
714
+ test_result "cbp-session-id-stamp.sh invalid UUID → guard rejects, no file written" "passed" "failed (exit=$EXIT_CODE file_exists=$([ -f "$STAMP_FILE" ] && echo yes || echo no))"
715
+ fi
716
+ rm -rf "$ISO"
717
+
718
+ fi
719
+
720
+ echo ""
721
+
617
722
  echo ""
618
723
 
619
724
  # ===== HOOK SMOKE TESTS — cbp-skill-context-guard model-aware tier (CHK-224) =====
@@ -103,17 +103,44 @@ has_scope_comment() {
103
103
  }
104
104
 
105
105
  # has_template_twin <rel_path_under_claude>
106
- # Returns 0 when the codebyplan package ships a template twin for this .claude file
107
- # (i.e. the file is package-MANAGED org-shared is its implicit default and a
108
- # scope marker is NOT required). <rel_path_under_claude> is the path beneath
109
- # .claude/ (e.g. "rules/foo.md", "skills/x/SKILL.md").
110
- # Monorepo: $REPO_ROOT/packages/codebyplan-package/templates/<rel>
111
- # Consumer: $REPO_ROOT/node_modules/codebyplan/templates/<rel>
112
- # Returns non-zero when no twin is found including when no templates dir is
113
- # resolvable at all. Callers treat a non-zero result as "user-created", which is
114
- # the conservative fallback: a scope marker is then required (legacy behavior).
106
+ # Returns 0 when the file is package-MANAGED (a template twin exists) org-shared
107
+ # is its implicit default and a scope marker is NOT required. <rel_path_under_claude>
108
+ # is the path beneath .claude/ (e.g. "rules/foo.md", "skills/x/SKILL.md").
109
+ #
110
+ # Resolution order:
111
+ # 1. The install manifest (.claude/.cbp.manifest.json, with mid/legacy filename
112
+ # fallback). Its files[].dest entries are the authoritative record of what
113
+ # codebyplan installed INTO THIS repo independent of where the package
114
+ # lives on disk (pnpm symlinks, hoisting, monorepo sub-packages). Requires jq.
115
+ # 2. Filesystem probe of the package's templates dir — fallback when there is no
116
+ # manifest/jq, and a backstop so a stale manifest never hides a genuine twin:
117
+ # Monorepo: $REPO_ROOT/packages/codebyplan-package/templates/<rel>
118
+ # Consumer: $REPO_ROOT/node_modules/codebyplan/templates/<rel>
119
+ # Returns non-zero when no twin is found by either path. Callers treat non-zero as
120
+ # "user-created": a scope marker is then required (the conservative fallback).
115
121
  has_template_twin() {
116
122
  local rel="$1"
123
+
124
+ # 1. Authoritative: the install manifest lists every managed file by dest path.
125
+ if command -v jq >/dev/null 2>&1; then
126
+ local manifest
127
+ for manifest in \
128
+ "$REPO_ROOT/.claude/.cbp.manifest.json" \
129
+ "$REPO_ROOT/.claude/.cbp-claude.manifest.json" \
130
+ "$REPO_ROOT/.claude/.codebyplan-claude.manifest.json"; do
131
+ if [ -f "$manifest" ]; then
132
+ if jq -e --arg rel "$rel" 'any(.files[]?; .dest == $rel)' "$manifest" >/dev/null 2>&1; then
133
+ return 0
134
+ fi
135
+ # Manifest found but does not list this file — stop scanning older
136
+ # manifest names and fall through to the filesystem probe (a stale
137
+ # manifest must not hide a genuine template twin).
138
+ break
139
+ fi
140
+ done
141
+ fi
142
+
143
+ # 2. Fallback / backstop: probe the package's templates dir directly.
117
144
  [ -f "$REPO_ROOT/packages/codebyplan-package/templates/$rel" ] && return 0
118
145
  [ -f "$REPO_ROOT/node_modules/codebyplan/templates/$rel" ] && return 0
119
146
  return 1
@@ -0,0 +1,65 @@
1
+ # Handoff File Convention
2
+
3
+ Per-level handoff notes for cross-session context. Files live under
4
+ `.codebyplan/handoff/` and are committed (negation entry `!.codebyplan/handoff/`
5
+ in the managed `.gitignore` block).
6
+
7
+ ## Layout
8
+
9
+ | Level | Path template | Number format |
10
+ |------------|--------------------------------------------------|-------------------|
11
+ | repo | `.codebyplan/handoff/repo.md` | n/a (per-section) |
12
+ | checkpoint | `.codebyplan/handoff/checkpoint/<NNN>.md` | 3-digit zero-pad |
13
+ | task | `.codebyplan/handoff/task/<NNN>-<T>.md` | CHK zero-pad + T |
14
+ | standalone | `.codebyplan/handoff/standalone/<N>.md` | bare number |
15
+
16
+ ## repo.md — per-worktree sections
17
+
18
+ `repo.md` uses `## <worktree-label>` sections so multiple worktrees can write
19
+ to the same file without conflict. Label resolution order:
20
+
21
+ 1. Read `.codebyplan/state/worktrees.json`; find entry whose `branch` matches
22
+ the current git branch; use its `name`.
23
+ 2. Fallback: git branch name with `/` replaced by `-`.
24
+
25
+ Each CLI verb (`write`, `append`, `clear`) operates on the current worktree's
26
+ section only. Merge conflicts are structurally impossible: two worktrees write
27
+ to different `##` sections of the same file.
28
+
29
+ ## Empty = absent / whitespace-only
30
+
31
+ A handoff is considered **empty** when the file is absent OR its content is
32
+ whitespace-only. The CLI deletes the file when it becomes empty:
33
+
34
+ - Non-repo levels: `write --content ""` and `clear` both delete the file.
35
+ - repo level: `write --content ""` and `clear` remove the current worktree's
36
+ `##` section; the file is deleted when no non-empty sections remain.
37
+
38
+ All delete operations swallow ENOENT (idempotent).
39
+
40
+ ## CLI verbs
41
+
42
+ ```
43
+ codebyplan handoff read --level <l> [--number N] [--task T]
44
+ codebyplan handoff write --level <l> [--number N] [--task T] --content "..."
45
+ codebyplan handoff append --level <l> [--number N] [--task T] --content "..."
46
+ codebyplan handoff clear --level <l> [--number N] [--task T]
47
+ codebyplan handoff status [--json]
48
+ ```
49
+
50
+ ## status --json shape (stable contract)
51
+
52
+ ```json
53
+ {
54
+ "nonEmpty": [{ "level": "checkpoint", "identifier": "005", "path": "/abs/path" }],
55
+ "empty": []
56
+ }
57
+ ```
58
+
59
+ `status` is consumed by TASK-3 session-start/end gates to decide whether a
60
+ handoff prompt is shown. TASK-4 web UI reads the same files via the API.
61
+
62
+ ## Content format
63
+
64
+ Freeform markdown. No structured schema is enforced; the files are human-written
65
+ notes surfaced to Claude at session boundaries.
@@ -252,13 +252,29 @@
252
252
  "Bash(codebyplan cd:*)",
253
253
  "Bash(npx codebyplan cd:*)",
254
254
  "Bash(codebyplan cleanup-plan-folders:*)",
255
- "Bash(npx codebyplan cleanup-plan-folders:*)"
255
+ "Bash(npx codebyplan cleanup-plan-folders:*)",
256
+ "Bash(codebyplan handoff:*)",
257
+ "Bash(npx codebyplan handoff:*)"
256
258
  ]
257
259
  },
258
260
  "attribution": {
259
261
  "commit": "",
260
262
  "pr": ""
261
263
  },
264
+ "hooks": {
265
+ "SessionStart": [
266
+ {
267
+ "hooks": [
268
+ {
269
+ "_owner": "codebyplan-claude",
270
+ "_hook_id": "cbp-session-id-stamp.sh",
271
+ "type": "command",
272
+ "command": "bash ./.claude/hooks/cbp-session-id-stamp.sh"
273
+ }
274
+ ]
275
+ }
276
+ ]
277
+ },
262
278
  "showClearContextOnPlanAccept": false,
263
279
  "syntaxHighlightingDisabled": false
264
280
  }
@@ -1,5 +1,4 @@
1
1
  ---
2
- scope: org-shared
3
2
  name: cbp-checkpoint-check
4
3
  description: Full re-evaluation of a checkpoint with before/after comparison
5
4
  argument-hint: [CHK-NNN]
@@ -72,6 +72,38 @@ Complete remaining tasks first, then run `/cbp-checkpoint-complete`.
72
72
  ```
73
73
  Stop here.
74
74
 
75
+ ### Step 2.6: Verify Checkpoint Handoff Is Resolved
76
+
77
+ Run `codebyplan handoff status --json` and parse the output.
78
+
79
+ Check if `nonEmpty[]` contains an entry where `level === 'checkpoint'` AND `identifier === '{NNN}'`
80
+ (the 3-digit zero-padded checkpoint number — e.g. `231`).
81
+
82
+ If a matching entry is found, BLOCK with:
83
+
84
+ ```
85
+ ## Cannot Complete Checkpoint — Handoff Unresolved
86
+
87
+ CHK-[NNN] has unresolved handoff content at `.codebyplan/handoff/checkpoint/[NNN].md`.
88
+
89
+ The handoff file contains scope that has not been resolved. You must clear it before
90
+ completing this checkpoint. Three options — pick exactly one:
91
+
92
+ 1. **Absorb** — address the pending scope now, then clear:
93
+ `codebyplan handoff clear --level checkpoint --number [NNN]`
94
+ 2. **Defer** — create a follow-up checkpoint or standalone task to cover the scope,
95
+ then clear: `codebyplan handoff clear --level checkpoint --number [NNN]`
96
+ 3. **Escalate** — move the content to the repo handoff:
97
+ `codebyplan handoff append --level repo --content "<content>"`
98
+ then clear: `codebyplan handoff clear --level checkpoint --number [NNN]`
99
+
100
+ Never silently delete handoff content — it represents tracked scope.
101
+ ```
102
+
103
+ (STOP)
104
+
105
+ If no matching entry: continue to Step 3.
106
+
75
107
  ### Step 3: Aggregate QA
76
108
 
77
109
  Collect QA results from all tasks and rounds. Build checkpoint-level QA summary:
@@ -1,5 +1,4 @@
1
1
  ---
2
- scope: org-shared
3
2
  name: cbp-clear-continue
4
3
  description: Resume work after /clear by reading .codebyplan/clear/handoff.md and re-invoking the previously-blocked heavy skill. Reports a friendly error if no handoff file exists.
5
4
  model: inherit
@@ -48,6 +47,28 @@ Resuming from handoff:
48
47
  Next action: /<next-action>
49
48
  ```
50
49
 
50
+ ### Step 2.5 — Surface committed handoffs
51
+
52
+ Surface any committed handoff files before proceeding. Non-blocking.
53
+
54
+ 1. Run `codebyplan handoff status --json` → parse `nonEmpty[]`.
55
+ 2. If `nonEmpty` is empty → skip silently.
56
+ 3. For each entry in `nonEmpty`, read the content:
57
+ - `repo` → `codebyplan handoff read --level repo`
58
+ - `checkpoint` → `codebyplan handoff read --level checkpoint --number <identifier>`
59
+ - `task` → split identifier `"<NNN>-<T>"` → `codebyplan handoff read --level task --number <NNN> --task <T>`
60
+ - `standalone` → `codebyplan handoff read --level standalone --number <identifier>`
61
+ 4. Display:
62
+ ```
63
+ Handoff notes from previous session(s):
64
+
65
+ [<level>: <identifier>]
66
+ <content>
67
+
68
+ ---
69
+ ```
70
+ 5. Non-blocking — proceed to Step 3 regardless.
71
+
51
72
  ### Step 3 — Delete the handoff BEFORE re-invoking
52
73
 
53
74
  Delete `.codebyplan/clear/handoff.md` once its contents have been read and displayed:
@@ -80,7 +101,7 @@ the guard will deny again. Follow the same cycle: `/cbp-clear-prep` → `/clear`
80
101
  ## Integration
81
102
 
82
103
  - **Invoked by**: user after `/clear` following `/cbp-clear-prep`
83
- - **Reads**: `.codebyplan/clear/handoff.md`
104
+ - **Reads**: `.codebyplan/clear/handoff.md` (Step 1); `codebyplan handoff status --json` + `codebyplan handoff read` (Step 2.5 — committed handoff surfacing)
84
105
  - **Deletes**: `.codebyplan/clear/handoff.md` (Step 3, before resuming)
85
106
  - **Then invokes**: the skill from `next_action` via Skill tool
86
107
  - **Companion**: `.claude/skills/cbp-clear-prep/SKILL.md` writes the handoff
@@ -1,5 +1,4 @@
1
1
  ---
2
- scope: org-shared
3
2
  name: cbp-clear-prep
4
3
  description: Capture a clear-context handoff when the context window is too large to run a heavy skill. Reads active task/round state, writes .codebyplan/clear/handoff.md, then instructs the user to run /clear and /cbp-clear-continue to resume.
5
4
  argument-hint: "[blocked-skill]"
@@ -86,6 +85,27 @@ next_action: /<skill-name> <args if any>
86
85
  After /clear, run: /cbp-clear-continue
87
86
  ```
88
87
 
88
+ ### Step 4.5 — Flush carryover to committed handoff
89
+
90
+ If `checkpoint_number` from Step 2 is `unknown` → skip silently (no reliable key).
91
+
92
+ Otherwise:
93
+
94
+ 1. Compose a brief carryover note:
95
+ ```markdown
96
+ ## Context-clear carryover — CHK-<N>
97
+
98
+ Captured: <ISO now>
99
+ Blocked skill: <blocked_skill>
100
+ Next action: `<next_action>`
101
+
102
+ <in-flight notes from Step 3>
103
+ ```
104
+ 2. Run `codebyplan handoff write --level checkpoint --number <N> --content "<content>"`.
105
+ 3. Output: `Carryover handoff written to .codebyplan/handoff/checkpoint/<NNN>.md`.
106
+
107
+ Non-blocking — Step 5 continues regardless.
108
+
89
109
  ### Step 5 — Instruct the user
90
110
 
91
111
  Output exactly this summary (fill in the real values):
@@ -116,7 +136,7 @@ The user must run both commands manually.
116
136
  ## Integration
117
137
 
118
138
  - **Invoked when**: `cbp-skill-context-guard` PreToolUse hook emits `permissionDecision: deny`
119
- - **Writes**: `.codebyplan/clear/handoff.md`
139
+ - **Writes**: `.codebyplan/clear/handoff.md` (Step 4 — transient, gitignored); `codebyplan handoff write --level checkpoint --number <N>` (Step 4.5 — committed carryover flush; skipped when checkpoint_number unknown)
120
140
  - **Next**: user runs `/clear`, then `/cbp-clear-continue`
121
141
  - **Companion**: `.claude/skills/cbp-clear-continue/SKILL.md` reads `.codebyplan/clear/handoff.md`
122
142
  - **Guard hook**: `.claude/hooks/cbp-skill-context-guard.sh` — fires when context exceeds the model-aware threshold: `CBP_CONTEXT_WARN_TOKENS` (default 200000) for standard models; `CBP_CONTEXT_WARN_TOKENS_1M` (default 800000) for `[1m]`-context models. No model id found → standard tier (fail-conservative).
@@ -85,6 +85,42 @@ Stop here.
85
85
 
86
86
  `task.context.verify_verdict` must exist and have `verdict: 'READY'` (written by `/cbp-verify` Phase 6 when it runs at task scope — whole-repo `codebyplan check --scope task`, holistic `cbp-verify-reviewer`, and the single batched human walkthrough all passed). If absent or not `READY`, surface "Run `/cbp-verify` first" and stop.
87
87
 
88
+ ### Step 2.6: Verify Task Handoff Is Resolved
89
+
90
+ Run `codebyplan handoff status --json` and parse the output.
91
+
92
+ Check if `nonEmpty[]` contains an entry where `level === 'task'` AND `identifier === '{NNN}-{T}'`
93
+ (the 3-digit zero-padded checkpoint number, hyphen, task number — e.g. `231-3`).
94
+
95
+ > Gate scope is intentional: only **task**-level handoffs block here. Checkpoint-level handoffs
96
+ > (written by `/cbp-session-end` as resume-pointers) are deliberately scoped to
97
+ > `/cbp-checkpoint-complete`, NOT task finalize — do not "fix" this to gate on checkpoint level.
98
+
99
+ If a matching entry is found, BLOCK with:
100
+
101
+ ```
102
+ ## Cannot Complete Task — Handoff Unresolved
103
+
104
+ TASK-[N] has unresolved handoff content at `.codebyplan/handoff/task/[NNN]-[T].md`.
105
+
106
+ The handoff file contains scope that has not been resolved. You must clear it before
107
+ completing this task. Three options — pick exactly one:
108
+
109
+ 1. **Absorb** — address the pending scope in current work, then clear:
110
+ `codebyplan handoff clear --level task --number [NNN] --task [T]`
111
+ 2. **Defer** — create a follow-up task or standalone task to cover the scope,
112
+ then clear: `codebyplan handoff clear --level task --number [NNN] --task [T]`
113
+ 3. **Escalate** — move the content to the repo handoff:
114
+ `codebyplan handoff append --level repo --content "<content>"`
115
+ then clear: `codebyplan handoff clear --level task --number [NNN] --task [T]`
116
+
117
+ Never silently delete handoff content — it represents tracked scope.
118
+ ```
119
+
120
+ (STOP)
121
+
122
+ If no matching entry: continue to Step 3.
123
+
88
124
  ### Step 3: Verify QA and File Approval
89
125
 
90
126
  Load `task.qa` and `task.files_changed`: