codebyplan 1.5.1 → 1.8.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.
Files changed (205) hide show
  1. package/dist/cli.js +4462 -748
  2. package/package.json +5 -1
  3. package/templates/.gitkeep +0 -0
  4. package/templates/README.md +20 -0
  5. package/templates/agents/cbp-cc-executor.md +213 -0
  6. package/templates/agents/cbp-database-agent.md +229 -0
  7. package/templates/agents/cbp-improve-claude.md +245 -0
  8. package/templates/agents/cbp-improve-round.md +284 -0
  9. package/templates/agents/cbp-mechanical-edits.md +111 -0
  10. package/templates/agents/cbp-research.md +282 -0
  11. package/templates/agents/cbp-round-executor.md +604 -0
  12. package/templates/agents/cbp-security-agent.md +134 -0
  13. package/templates/agents/cbp-task-check.md +213 -0
  14. package/templates/agents/cbp-task-planner.md +582 -0
  15. package/templates/agents/cbp-test-e2e-agent.md +363 -0
  16. package/templates/agents/cbp-testing-qa-agent.md +400 -0
  17. package/templates/context/mcp-docs.md +139 -0
  18. package/templates/hooks/README.md +236 -0
  19. package/templates/hooks/cbp-auto-test-hooks.sh +44 -0
  20. package/templates/hooks/cbp-lint-format-on-edit.sh +159 -0
  21. package/templates/hooks/cbp-maestro-yaml-validate.sh +100 -0
  22. package/templates/hooks/cbp-mcp-migration-guard.sh +32 -0
  23. package/templates/hooks/cbp-mcp-round-sync.sh +79 -0
  24. package/templates/hooks/cbp-mcp-worktree-inject.sh +76 -0
  25. package/templates/hooks/cbp-notify.sh +68 -0
  26. package/templates/hooks/cbp-plugin-dispatch.sh +29 -0
  27. package/templates/hooks/cbp-pre-commit-quality-gate.sh +204 -0
  28. package/templates/hooks/cbp-statusline.sh +347 -0
  29. package/templates/hooks/cbp-subagent-statusline.sh +182 -0
  30. package/templates/hooks/cbp-test-coverage-gate.sh +144 -0
  31. package/templates/hooks/cbp-test-hooks.sh +320 -0
  32. package/templates/hooks/hooks.json +85 -0
  33. package/templates/hooks/validate-context-usage.sh +59 -0
  34. package/templates/hooks/validate-git-commit.sh +78 -0
  35. package/templates/hooks/validate-git-stash-deny.sh +32 -0
  36. package/templates/hooks/validate-structure-lengths.sh +57 -0
  37. package/templates/hooks/validate-structure-lib.sh +104 -0
  38. package/templates/hooks/validate-structure-patterns.sh +54 -0
  39. package/templates/hooks/validate-structure-scope.sh +33 -0
  40. package/templates/hooks/validate-structure-smoke.sh +95 -0
  41. package/templates/hooks/validate-structure-templates.sh +34 -0
  42. package/templates/hooks/validate-structure.sh +69 -0
  43. package/templates/rules/.gitkeep +0 -0
  44. package/templates/rules/README.md +47 -0
  45. package/templates/rules/context-file-loading.md +52 -0
  46. package/templates/rules/scope-vocabulary.md +64 -0
  47. package/templates/rules/todo-backend.md +109 -0
  48. package/templates/settings.project.base.json +55 -0
  49. package/templates/settings.user.base.json +25 -0
  50. package/templates/skills/cbp-build-cc-agent/SKILL.md +139 -0
  51. package/templates/skills/cbp-build-cc-agent/examples/read-only-reviewer.md +32 -0
  52. package/templates/skills/cbp-build-cc-agent/examples/with-hooks.md +41 -0
  53. package/templates/skills/cbp-build-cc-agent/examples/with-skills-preload.md +25 -0
  54. package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +153 -0
  55. package/templates/skills/cbp-build-cc-agent/reference/frontmatter-fields.md +37 -0
  56. package/templates/skills/cbp-build-cc-agent/reference/permission-modes.md +18 -0
  57. package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +67 -0
  58. package/templates/skills/cbp-build-cc-agent/templates/agent.md +66 -0
  59. package/templates/skills/cbp-build-cc-claude-file/SKILL.md +178 -0
  60. package/templates/skills/cbp-build-cc-claude-file/examples/minimal-project.md +33 -0
  61. package/templates/skills/cbp-build-cc-claude-file/examples/monorepo-with-imports.md +39 -0
  62. package/templates/skills/cbp-build-cc-claude-file/reference/imports.md +72 -0
  63. package/templates/skills/cbp-build-cc-claude-file/reference/what-belongs.md +39 -0
  64. package/templates/skills/cbp-build-cc-claude-file/templates/project-claude-md.md +48 -0
  65. package/templates/skills/cbp-build-cc-claude-file/templates/user-claude-md.md +22 -0
  66. package/templates/skills/cbp-build-cc-memory/SKILL.md +201 -0
  67. package/templates/skills/cbp-build-cc-memory/examples/feedback-memory.md +11 -0
  68. package/templates/skills/cbp-build-cc-memory/examples/project-memory.md +11 -0
  69. package/templates/skills/cbp-build-cc-memory/examples/reference-memory.md +13 -0
  70. package/templates/skills/cbp-build-cc-memory/examples/user-memory.md +14 -0
  71. package/templates/skills/cbp-build-cc-memory/reference/memory-types.md +59 -0
  72. package/templates/skills/cbp-build-cc-memory/reference/when-to-save.md +62 -0
  73. package/templates/skills/cbp-build-cc-memory/templates/MEMORY-index.md +4 -0
  74. package/templates/skills/cbp-build-cc-memory/templates/memory-entry.md +15 -0
  75. package/templates/skills/cbp-build-cc-mode/SKILL.md +99 -0
  76. package/templates/skills/cbp-build-cc-rule/SKILL.md +176 -0
  77. package/templates/skills/cbp-build-cc-rule/examples/global-rule.md +19 -0
  78. package/templates/skills/cbp-build-cc-rule/examples/scoped-rule.md +41 -0
  79. package/templates/skills/cbp-build-cc-rule/reference/paths-patterns.md +48 -0
  80. package/templates/skills/cbp-build-cc-rule/templates/rule.md +32 -0
  81. package/templates/skills/cbp-build-cc-settings/SKILL.md +220 -0
  82. package/templates/skills/cbp-build-cc-settings/examples/hooks-config.json +64 -0
  83. package/templates/skills/cbp-build-cc-settings/examples/permissions-config.json +34 -0
  84. package/templates/skills/cbp-build-cc-settings/examples/sandbox-config.json +42 -0
  85. package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +104 -0
  86. package/templates/skills/cbp-build-cc-settings/reference/permission-rules.md +61 -0
  87. package/templates/skills/cbp-build-cc-settings/reference/scope-precedence.md +73 -0
  88. package/templates/skills/cbp-build-cc-settings/reference/settings-fields.md +166 -0
  89. package/templates/skills/cbp-build-cc-settings/templates/settings.json +23 -0
  90. package/templates/skills/cbp-build-cc-settings/templates/settings.local.json +10 -0
  91. package/templates/skills/cbp-build-cc-skill/SKILL.md +154 -0
  92. package/templates/skills/cbp-build-cc-skill/examples/dynamic-context.md +31 -0
  93. package/templates/skills/cbp-build-cc-skill/examples/fork-skill.md +22 -0
  94. package/templates/skills/cbp-build-cc-skill/examples/knowledge-skill.md +25 -0
  95. package/templates/skills/cbp-build-cc-skill/examples/task-skill.md +29 -0
  96. package/templates/skills/cbp-build-cc-skill/reference/cbp-quality.md +157 -0
  97. package/templates/skills/cbp-build-cc-skill/reference/frontmatter-fields.md +35 -0
  98. package/templates/skills/cbp-build-cc-skill/reference/string-substitutions.md +60 -0
  99. package/templates/skills/cbp-build-cc-skill/scripts/validate-skill.sh +90 -0
  100. package/templates/skills/cbp-build-cc-skill/templates/skill.md +51 -0
  101. package/templates/skills/cbp-checkpoint-check/SKILL.md +156 -0
  102. package/templates/skills/cbp-checkpoint-complete/SKILL.md +109 -0
  103. package/templates/skills/cbp-checkpoint-create/SKILL.md +287 -0
  104. package/templates/skills/cbp-checkpoint-end/SKILL.md +241 -0
  105. package/templates/skills/cbp-checkpoint-update/SKILL.md +115 -0
  106. package/templates/skills/cbp-frontend-a11y/SKILL.md +109 -0
  107. package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +130 -0
  108. package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +122 -0
  109. package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +154 -0
  110. package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +111 -0
  111. package/templates/skills/cbp-frontend-design/SKILL.md +145 -0
  112. package/templates/skills/cbp-frontend-design/reference/nextjs-scss.md +118 -0
  113. package/templates/skills/cbp-frontend-design/reference/rn-expo.md +101 -0
  114. package/templates/skills/cbp-frontend-design/reference/tauri-react.md +82 -0
  115. package/templates/skills/cbp-frontend-ui/SKILL.md +262 -0
  116. package/templates/skills/cbp-frontend-ui/reference/ui-label-maps.md +42 -0
  117. package/templates/skills/cbp-frontend-ui/reference/ui-layout-patterns.md +105 -0
  118. package/templates/skills/cbp-frontend-ui/reference/variant-defaults.md +149 -0
  119. package/templates/skills/cbp-frontend-ux/SKILL.md +181 -0
  120. package/templates/skills/cbp-git-branch-feat-create/SKILL.md +115 -0
  121. package/templates/skills/cbp-git-commit/SKILL.md +278 -0
  122. package/templates/skills/cbp-git-worktree-create/SKILL.md +226 -0
  123. package/templates/skills/cbp-git-worktree-remove/SKILL.md +145 -0
  124. package/templates/skills/cbp-merge-main/SKILL.md +228 -0
  125. package/templates/skills/cbp-round-check/SKILL.md +104 -0
  126. package/templates/skills/cbp-round-end/SKILL.md +183 -0
  127. package/templates/skills/cbp-round-end/reference/findings-presentation.md +44 -0
  128. package/templates/skills/cbp-round-end/reference/inline-fallback.md +35 -0
  129. package/templates/skills/cbp-round-execute/SKILL.md +211 -0
  130. package/templates/skills/cbp-round-execute/reference/inline-fallback.md +59 -0
  131. package/templates/skills/cbp-round-input/SKILL.md +165 -0
  132. package/templates/skills/cbp-round-start/SKILL.md +222 -0
  133. package/templates/skills/cbp-round-update/SKILL.md +163 -0
  134. package/templates/skills/cbp-session-end/SKILL.md +187 -0
  135. package/templates/skills/cbp-session-start/SKILL.md +155 -0
  136. package/templates/skills/cbp-ship/SKILL.md +332 -0
  137. package/templates/skills/cbp-ship/reference/changesets-overview.md +120 -0
  138. package/templates/skills/cbp-ship/reference/eas-cli-overview.md +60 -0
  139. package/templates/skills/cbp-ship/reference/gh-cli-overview.md +135 -0
  140. package/templates/skills/cbp-ship/reference/gh-cli-shipment-commands.md +283 -0
  141. package/templates/skills/cbp-ship/reference/npm-publish-monorepo.md +252 -0
  142. package/templates/skills/cbp-ship/reference/npm-publish-oidc-trusted.md +157 -0
  143. package/templates/skills/cbp-ship/reference/npm-publish-overview.md +171 -0
  144. package/templates/skills/cbp-ship/reference/preflight-checklist.md +88 -0
  145. package/templates/skills/cbp-ship/reference/railway-nestjs-deployment.md +169 -0
  146. package/templates/skills/cbp-ship/reference/railway-overview.md +120 -0
  147. package/templates/skills/cbp-ship/reference/railway-troubleshooting.md +168 -0
  148. package/templates/skills/cbp-ship/reference/release-please-overview.md +99 -0
  149. package/templates/skills/cbp-ship/reference/surface-expo-eas.md +155 -0
  150. package/templates/skills/cbp-ship/reference/surface-npm.md +180 -0
  151. package/templates/skills/cbp-ship/reference/surface-railway.md +152 -0
  152. package/templates/skills/cbp-ship/reference/surface-supabase.md +178 -0
  153. package/templates/skills/cbp-ship/reference/surface-tauri.md +138 -0
  154. package/templates/skills/cbp-ship/reference/surface-vercel.md +124 -0
  155. package/templates/skills/cbp-ship/reference/surface-vscode-ext.md +144 -0
  156. package/templates/skills/cbp-ship/reference/surfaces.md +60 -0
  157. package/templates/skills/cbp-ship/reference/testflight-automation.md +215 -0
  158. package/templates/skills/cbp-ship/reference/testflight-internal-vs-external.md +69 -0
  159. package/templates/skills/cbp-ship/reference/testflight-overview.md +98 -0
  160. package/templates/skills/cbp-ship/reference/versioning.md +116 -0
  161. package/templates/skills/cbp-ship/scripts/detect-surfaces.sh +217 -0
  162. package/templates/skills/cbp-ship/scripts/verify-expo-eas.sh +35 -0
  163. package/templates/skills/cbp-ship/scripts/verify-npm.sh +21 -0
  164. package/templates/skills/cbp-ship/scripts/verify-railway.sh +41 -0
  165. package/templates/skills/cbp-ship/scripts/verify-supabase.sh +19 -0
  166. package/templates/skills/cbp-ship/scripts/verify-tauri.sh +24 -0
  167. package/templates/skills/cbp-ship/scripts/verify-vercel.sh +32 -0
  168. package/templates/skills/cbp-ship/scripts/verify-vscode-ext.sh +25 -0
  169. package/templates/skills/cbp-ship/templates/eas.json +66 -0
  170. package/templates/skills/cbp-ship/templates/railway.toml +15 -0
  171. package/templates/skills/cbp-ship/templates/release-please-config.json +17 -0
  172. package/templates/skills/cbp-ship/templates/vercel.json +19 -0
  173. package/templates/skills/cbp-ship/templates/vscodeignore +21 -0
  174. package/templates/skills/cbp-ship/templates/workflow-changesets.yml +41 -0
  175. package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +53 -0
  176. package/templates/skills/cbp-ship/templates/workflow-npm-publish.yml +36 -0
  177. package/templates/skills/cbp-ship/templates/workflow-release-please.yml +21 -0
  178. package/templates/skills/cbp-ship/templates/workflow-tauri-release.yml +69 -0
  179. package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +31 -0
  180. package/templates/skills/cbp-ship-configure/SKILL.md +296 -0
  181. package/templates/skills/cbp-ship-configure/reference/expo-mobile.md +204 -0
  182. package/templates/skills/cbp-ship-configure/reference/npm-package.md +165 -0
  183. package/templates/skills/cbp-ship-configure/reference/railway-backend.md +199 -0
  184. package/templates/skills/cbp-ship-configure/reference/supabase.md +200 -0
  185. package/templates/skills/cbp-ship-configure/reference/tauri-desktop.md +181 -0
  186. package/templates/skills/cbp-ship-configure/reference/vercel.md +117 -0
  187. package/templates/skills/cbp-ship-configure/reference/vscode-ext.md +155 -0
  188. package/templates/skills/cbp-ship-main/SKILL.md +65 -0
  189. package/templates/skills/cbp-supabase-branch-check/SKILL.md +337 -0
  190. package/templates/skills/cbp-supabase-branch-check/reference/dag-steps.md +29 -0
  191. package/templates/skills/cbp-supabase-migrate/SKILL.md +314 -0
  192. package/templates/skills/cbp-supabase-migrate/reference/advisor-triage.md +70 -0
  193. package/templates/skills/cbp-supabase-migrate/reference/cli-fallback.md +87 -0
  194. package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +58 -0
  195. package/templates/skills/cbp-supabase-setup/SKILL.md +239 -0
  196. package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +121 -0
  197. package/templates/skills/cbp-supabase-setup/reference/cli-fallback.md +109 -0
  198. package/templates/skills/cbp-task-check/SKILL.md +166 -0
  199. package/templates/skills/cbp-task-complete/SKILL.md +206 -0
  200. package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +48 -0
  201. package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +56 -0
  202. package/templates/skills/cbp-task-create/SKILL.md +167 -0
  203. package/templates/skills/cbp-task-start/SKILL.md +239 -0
  204. package/templates/skills/cbp-task-testing/SKILL.md +277 -0
  205. package/templates/skills/cbp-todo/SKILL.md +97 -0
@@ -0,0 +1,236 @@
1
+ # Hooks
2
+
3
+ The `codebyplan` npm package ships a small, portable set of Claude Code hooks. They run in your project, use only generic primitives (`git rev-parse`, `${CLAUDE_PROJECT_DIR}`, `${CLAUDE_PLUGIN_ROOT}`), and degrade gracefully (exit 0) when their preconditions aren't met.
4
+
5
+ Hook registration lives in [`hooks/hooks.json`](./hooks.json) — PreToolUse, PostToolUse, and Notification events are wired. (`SessionStart`, `SessionEnd`, `Stop`, and `SubagentStop` are also schema-permitted but unused here.)
6
+
7
+ **`cbp-statusline.sh` is auto-wired via `settings.project.base.json`.** The `statusLine` block is shipped inside `templates/settings.project.base.json` and merged into the consumer's `.claude/settings.json` automatically by `codebyplan claude install` (and on every `codebyplan claude update`). No manual copy-paste is required.
8
+
9
+ ---
10
+
11
+ ## Hooks included
12
+
13
+ ### `cbp-statusline.sh` — auto-registered via `settings.project.base.json`
14
+
15
+ Renders up to 6 structured lines below Claude Code's prompt area. Reads JSON from stdin (Claude Code's `statusLine` stream) and emits ANSI-coloured output. All fields from the Claude Code `statusLine` schema are parsed in a single `jq` invocation.
16
+
17
+ **Render lines:**
18
+
19
+ - **Line 1 — Identity**: single prefix (`wt:NAME` / `session:NAME` / `agent:NAME`, priority order); model display name and id; `effort:LEVEL` (when set); `thinking:on` (only when thinking is enabled); `style:NAME` (when output style is not "default"); `v:VERSION`; `[VIM_MODE]`
20
+ - **Line 2 — Context**: 20-character progress bar (green / yellow / red at 50% / 75%); `used% / ctx_size`; per-call token breakdown — `in:N out:N cache_cr:N cache_rd:N` (from `context_window.current_usage`); `⚠ 200k+` banner when `exceeds_200k_tokens` is true
21
+ - **Line 3 — Cost**: session cost in USD; total wall duration (`dur:`) and API-only duration (`api:`) via human-readable format; lines added/removed
22
+ - **Line 4 — Rate limits**: `5h: PCT% (resets in Xh)` and `7d: PCT% (resets in Xd)` with relative-time helper; colour-coded green / yellow / red at 60% / 80%; emitted only when at least one window has a non-zero `resets_at` epoch
23
+ - **Line 5 — Repo / PR**: `host/owner/name` (from `workspace.repo`); `PR #N <review_state> <url>` (when `pr.number` is present); emitted when at least one segment is present
24
+ - **Line 6 — Worktree**: `name @ branch (was: original_branch)` and truncated path; emitted only when `worktree.name` is present
25
+
26
+ **Env-var toggles** — set any to `1` to suppress that section:
27
+
28
+ | Variable | Suppresses |
29
+ |---|---|
30
+ | `CBP_STATUSLINE_HIDE_IDENTITY` | Line 1 — model / session / agent identity |
31
+ | `CBP_STATUSLINE_HIDE_CONTEXT` | Line 2 — context bar, token breakdown |
32
+ | `CBP_STATUSLINE_HIDE_COST` | Line 3 — cost, duration, lines changed |
33
+ | `CBP_STATUSLINE_HIDE_RATE_LIMITS` | Line 4 — 5h / 7d rate-limit windows |
34
+ | `CBP_STATUSLINE_HIDE_REPO_PR` | Line 5 — repo identity and PR |
35
+ | `CBP_STATUSLINE_HIDE_WORKTREE` | Line 6 — worktree name / branch / path |
36
+ | `CBP_STATUSLINE_NO_COLOR` | Strip all ANSI colour codes (also honoured by `$NO_COLOR`) |
37
+
38
+ **Sample invocation** (pipe mock JSON, run from the hooks directory; `NO_COLOR=1` shown so the comment-output below matches verbatim without ANSI codes):
39
+
40
+ ```bash
41
+ echo '{"model":{"display_name":"Opus","id":"claude-opus-4"},"context_window":{"used_percentage":25,"context_window_size":200000,"current_usage":{"input_tokens":50000,"output_tokens":1000,"cache_creation_input_tokens":0,"cache_read_input_tokens":0}}}' \
42
+ | NO_COLOR=1 bash cbp-statusline.sh
43
+ # Opus (claude-opus-4)
44
+ # ▓▓▓▓▓░░░░░░░░░░░░░░░ 25%/200.0K in:50.0K out:1.0K cache_cr:0 cache_rd:0
45
+ # $0.0000 dur:0s api:0s +0 -0 lines
46
+ ```
47
+
48
+ Pure renderer — no project state read, no commands run. Works on any host.
49
+
50
+ **Blocks vs warns**: neither — it only renders. Cannot fail your workflow.
51
+
52
+ **Opt out**: set `CBP_STATUSLINE_HIDE_*` env vars to suppress individual sections, or remove the `statusLine` block from your `.claude/settings.json`.
53
+
54
+ ---
55
+
56
+ ### `maestro-yaml-validate.sh` — PreToolUse, matcher `Edit|Write|MultiEdit`
57
+
58
+ Validates [Maestro](https://maestro.mobile.dev) flow YAML files (`*/maestro/flows/*.yaml`, `*/maestro/flows/*.yml`) at edit time, against the property allowlist of your locally installed `maestro` CLI version. Catches properties the CLI will reject at flow-run time so you find out at edit time, not after `maestro test` fails.
59
+
60
+ **Blocks vs warns**: blocks (exit 2) when the edited content uses a known-rejected property under a known command (e.g., `timeout` under `assertVisible` in maestro 1.x / 2.x).
61
+
62
+ **Skips when**: the file isn't under `*/maestro/flows/`; or `maestro` isn't on `$PATH` (no enforcement, no failure).
63
+
64
+ **Maintain**: when maestro upgrades, re-validate the `KNOWN_REJECTED` array against [maestro.mobile.dev/api-reference/commands](https://maestro.mobile.dev/api-reference/commands).
65
+
66
+ **Opt out**: disable the plugin's hooks via your settings.json `hooks{}` block override, or disable the plugin entirely.
67
+
68
+ ---
69
+
70
+ ### `test-coverage-gate.sh` — PreToolUse, matcher `Bash` (git commit)
71
+
72
+ When you run `git commit`, this hook scans newly-added (`--diff-filter=A`) `.ts` / `.tsx` / `.js` / `.jsx` files and blocks the commit if any source file lacks a sibling test companion.
73
+
74
+ Recognised test conventions:
75
+
76
+ | Convention | Example |
77
+ | -------------------------- | ---------------------------------- |
78
+ | Sibling | `foo.ts` ↔ `foo.test.ts` |
79
+ | Sibling spec | `foo.ts` ↔ `foo.spec.ts` |
80
+ | Co-located | `foo.ts` ↔ `__tests__/foo.test.ts` |
81
+ | Test staged in same commit | `foo.ts` + `foo.test.ts` both new |
82
+
83
+ **Skipped automatically**: test files themselves; `index.ts` / `index.tsx` barrel exports; `*.d.ts` / `types.ts` / files under `types/`; files under `.claude/`, `docs/`, `supabase/`; `*.config.*`, `*.json`, `*.md`, `*.yaml`, `*.sh`, `*.scss`, `*.css`. (These are skip-only patterns — harmless when your repo doesn't have them.)
84
+
85
+ **Blocks vs warns**: blocks (exit 2) when source files lack tests. Stderr names every offending file and the expected test paths.
86
+
87
+ **Modified files** with no tests are NOT blocked — only newly-added ones. Pre-existing gaps surface via your test-coverage tooling instead.
88
+
89
+ **Opt out**: don't ship a hook override; structure your tests to one of the recognised conventions; or for a one-off, `git commit --no-verify` (host-level bypass — use sparingly).
90
+
91
+ ---
92
+
93
+ ### `pre-commit-quality-gate.sh` — PreToolUse, matcher `Bash` (git commit)
94
+
95
+ Three gates fired during `git commit`. Each scans the staged diff and blocks the commit if its gate fails.
96
+
97
+ **Gate 3 — Greenfield ESLint configs ship clean.** When a newly-added `eslint.config.{mjs,js,cjs,ts}` is staged, the hook walks up to the owning `package.json`, runs `./node_modules/.bin/eslint .` in that package, and blocks if any warning or error is reported. Enforces the convention that brand-new ESLint configs land at zero warnings.
98
+
99
+ **Gate 4 — No skip-only test files.** When a newly-added `*.{test,spec}.{ts,tsx,js,jsx}` is staged, the hook counts `describe(`, `it(`, `test(` declarations vs `.skip(` / `.todo(` declarations. If a file has only skipped / todo tests and no live tests, the commit is blocked. Prevents skip-only masquerading that satisfies `test-coverage-gate.sh` while testing nothing.
100
+
101
+ **Gate 5 — TypeScript compiles in owning package.** _Opt-in only._ Set `CBP_PRECOMMIT_TYPECHECK=1` in your shell to enable. Groups staged `.ts` / `.tsx` files by owning `package.json`, runs `tsc --noEmit -p <pkg>/tsconfig.json` per package, and blocks if any package fails. Off by default because typecheck can take 30–60s on large packages — enable in CI or before shipping.
102
+
103
+ **Blocks vs warns**: all three gates block (exit 2). Stderr names the failing gate, the file, and the exact local command to reproduce.
104
+
105
+ **Skipped automatically**: any gate skips silently when its required binary (`eslint`, `tsc`) is missing.
106
+
107
+ **Opt out**: same as test-coverage-gate — restructure to satisfy the gate, override the hook in your settings, or use `--no-verify` sparingly. Disable Gate 5 simply by not setting `CBP_PRECOMMIT_TYPECHECK`.
108
+
109
+ ---
110
+
111
+ ### `lint-format-on-edit.sh` — PostToolUse, matcher `Edit|Write`
112
+
113
+ After every Edit or Write, formats the changed file with Prettier and runs ESLint `--fix` against it. Auto-fixable issues are applied silently; remaining ESLint errors are surfaced to stderr so Claude sees them inline on the next iteration.
114
+
115
+ Walks up from the edited file to the nearest `package.json` and uses that package's local `node_modules/.bin/prettier` and `node_modules/.bin/eslint`. Falls back to the repo root's binaries if a package-local copy isn't present.
116
+
117
+ **File-type gate**:
118
+
119
+ | Extensions | Format | Lint |
120
+ | ----------------------------------------------------- | ------ | ------ |
121
+ | `.ts` `.tsx` `.js` `.jsx` `.mjs` `.mts` `.cjs` `.cts` | yes | yes |
122
+ | `.scss` `.css` `.json` `.md` `.yml` `.yaml` | yes | no |
123
+ | anything else | exit 0 | exit 0 |
124
+
125
+ **Skipped paths**: `node_modules/`, `.next/`, `dist/`, `build/`, `.git/`, `coverage/`, `.turbo/`.
126
+
127
+ **Blocks vs warns**: never blocks — exit 0 always. Errors are stderr-only so Claude can act on them in-loop.
128
+
129
+ **ESLint config-load failures** (exit 2 from eslint, e.g. preset collision, missing plugin) trigger a one-time loud banner per package per session. Subsequent edits in the same broken package emit a quiet one-line reminder. The marker file lives under `${TMPDIR}/cbp-lint-config-fail/` and clears between sessions.
130
+
131
+ **Requires `${CLAUDE_PROJECT_DIR}` env var** — Claude Code provides this. If unset (older host or unusual launch), the hook exits 0 silently as a no-op.
132
+
133
+ **Opt out**: same patterns as the gate hooks — settings.json override or plugin disable.
134
+
135
+ ---
136
+
137
+ ### `notify.sh` — Notification, matcher `*`
138
+
139
+ Sends a desktop notification when Claude Code emits a notification event (waiting for input, task complete, etc.). Title is `[<project-name>] Claude Code`; body is the notification message; clicking the notification focuses VS Code at the project directory (macOS only).
140
+
141
+ **Cross-platform graceful skip**: exits 0 silently when `terminal-notifier` is not on `$PATH`. Linux, Windows, and macOS hosts without Homebrew see a no-op — no errors, no warnings.
142
+
143
+ **VS Code click action**: only attached when running on macOS (`$OSTYPE` matches `darwin*`) AND the `code` CLI is on `$PATH`. On other hosts the notification still fires but is non-clickable.
144
+
145
+ **Install hint** (macOS): `brew install terminal-notifier`. On other platforms the hook is a no-op — substitute your own notification mechanism via a settings.json override if desired.
146
+
147
+ **Blocks vs warns**: never blocks — exit 0 always. Notification hooks must never block Claude.
148
+
149
+ **Opt out**: settings.json override or plugin disable.
150
+
151
+ ---
152
+
153
+ ### `auto-test-hooks.sh` — PostToolUse, matcher `Edit|Write`
154
+
155
+ Triggers `test-hooks.sh` automatically when any `*/hooks/*.sh` file is edited. Catches accidental breakage of the plugin's own hooks (or your project's `.claude/hooks/` if you author your own) at edit time, before the broken hook runs against future tool calls.
156
+
157
+ Recursion-guarded: edits to `test-hooks.sh` and `auto-test-hooks.sh` themselves don't re-trigger the suite.
158
+
159
+ **Blocks vs warns**: blocks (exit 1) when `test-hooks.sh` reports any failure. Stderr names which check failed.
160
+
161
+ **Skips when**: the edited file isn't under a `*/hooks/` path; or the file is `test-hooks.sh` / `auto-test-hooks.sh` itself.
162
+
163
+ **Opt out**: settings.json override removing this entry. The associated `test-hooks.sh` can still be run manually via `bash ${CLAUDE_PLUGIN_ROOT}/hooks/test-hooks.sh`.
164
+
165
+ ### `cbp-mcp-migration-guard.sh` — PreToolUse, matcher `mcp__codebyplan__(update_task|complete_task|update_checkpoint|create_checkpoint|create_task)`
166
+
167
+ Warns once per session when a legacy `.codebyplan.json` file still exists at the repo root — the pre-CHK-120 single-file config that has since been partitioned into the `.codebyplan/` directory. Fires on CodeByPlan MCP mutation tools so the reminder surfaces during normal planning work.
168
+
169
+ **Blocks vs warns**: never blocks — exits 0 on every path, so the MCP call always proceeds. Advisory only.
170
+
171
+ **Skips when**: no legacy `.codebyplan.json` is present at the repo root; or the reminder has already been emitted once this session.
172
+
173
+ **Opt out**: settings.json override removing this entry.
174
+
175
+ ---
176
+
177
+ ### `validate-git-stash-deny.sh` — PreToolUse, matcher `Bash`
178
+
179
+ Denies any `git stash` command (including `git -C <dir> stash` and `git stash pop/apply`). Git stash is banned because it hides work-in-progress in a place that's easy to lose; the hook points you to safe alternatives (`git diff <ref>`, `git show <ref>:<path>`, `git worktree add`).
180
+
181
+ **Blocks vs warns**: blocks (exit 2) when the `Bash` command contains a `git stash` invocation. Stderr names the alternatives.
182
+
183
+ **Skips when**: the command is any non-`git-stash` Bash call (exit 0).
184
+
185
+ **Opt out**: settings.json override removing this entry, or plugin disable.
186
+
187
+ ---
188
+
189
+ ### `cbp-mcp-worktree-inject.sh` — PreToolUse, matcher `mcp__codebyplan__(update_task|complete_task|complete_round|update_checkpoint)`
190
+
191
+ Auto-injects `caller_worktree_id` into CodeByPlan MCP mutation calls when it's missing, resolving it via `npx codebyplan resolve-worktree` (with `--fallback-from-branch`). Closes the manual worktree-pinning workaround so MCP writes pass the server-side worktree pre-guard without hand-editing every call.
192
+
193
+ **Blocks vs warns**: neither — emits an `updatedInput` to add the field when resolvable, otherwise passes the call through unchanged (graceful passthrough preserves backwards-compat).
194
+
195
+ **Skips when**: the call already carries `caller_worktree_id`, or no worktree can be resolved (both → clean passthrough). A no-op in repos that don't use the CodeByPlan MCP.
196
+
197
+ **Opt out**: settings.json override removing this entry, or plugin disable.
198
+
199
+ ---
200
+
201
+ ### `cbp-mcp-round-sync.sh` — PostToolUse, matcher `mcp__codebyplan__complete_round`
202
+
203
+ After a `complete_round` MCP call succeeds, reconciles the round's `files_changed[]` against `git status`: new files in the diff are added (unapproved), and approval records no longer in the diff are flagged `removed_from_diff` (never deleted — lifecycle preserved).
204
+
205
+ **Blocks vs warns**: never blocks — exit 0 always. A PostToolUse sync hook must never abort Claude.
206
+
207
+ **Skips when**: the CodeByPlan CLI/API isn't reachable, or there's nothing to reconcile (clean exit 0). A no-op in repos that don't use the CodeByPlan MCP.
208
+
209
+ **Opt out**: settings.json override removing this entry, or plugin disable.
210
+
211
+ ---
212
+
213
+ ## Supporting (not registered)
214
+
215
+ ### `test-hooks.sh` — invoked by `auto-test-hooks.sh`
216
+
217
+ Test suite for the plugin's 10 registered hooks. Runs two passes:
218
+
219
+ 1. **Header check** — every registered hook (`lint-format-on-edit`, `test-coverage-gate`, `pre-commit-quality-gate`, `maestro-yaml-validate`, `notify`, `auto-test-hooks`, `mcp-migration-guard`, `validate-git-stash-deny`, `cbp-mcp-worktree-inject`, `cbp-mcp-round-sync`) carries the required `# Hook:` and `# Purpose:` header comments. `statusline` uses its own `# Claude Code Status Line` marker.
220
+ 2. **Functional smoke tests** — each hook is invoked with synthetic stdin matching its fast-path / graceful-degrade input; all must exit 0.
221
+
222
+ Not in `hooks.json` — invoked indirectly via `auto-test-hooks.sh` on hook edits, or directly via `bash ${CLAUDE_PLUGIN_ROOT}/hooks/test-hooks.sh`.
223
+
224
+ ---
225
+
226
+ ## Hooks NOT included and why
227
+
228
+ The CodeByPlan repo ships a larger hook set internally; the following are intentionally **not** in this plugin because they encode CodeByPlan-specific authoring conventions, hardcoded identities, or environment assumptions that don't generalise to plugin consumers:
229
+
230
+ | Hook | Why dropped |
231
+ | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
232
+ | `validate-structure.sh` + `validate-structure-{lib,patterns,templates,scope,lengths,smoke}.sh` (7 files) | Tightly coupled to CodeByPlan's `.claude/` layout conventions (kebab-case, scope markers, `/cbp-*` slash-command notation, research phase 1-8 layout). REPO_ROOT walks up from the script location; in plugin layout that resolves to plugin root, not the user's project. |
233
+ | `validate-context-usage.sh` | Same family as validate-structure — enforces CodeByPlan's `context/` referencing conventions. |
234
+ | `validate-git-commit.sh` | Hardcodes `midevyou <midevyosauhing@gmail.com>` as the only allowed commit author. Would block every plugin user's commits. |
235
+
236
+ If you need any of these for your own workflow, copy the source from the [CodeByPlan repo](https://github.com/midevyou/codebyplan) under `.claude/hooks/` and adapt to your environment.
@@ -0,0 +1,44 @@
1
+ #!/bin/bash
2
+ # @hook: PostToolUse Edit|Write
3
+ # Hook: PostToolUse for Edit|Write on hook files
4
+ # Purpose: Run test-hooks.sh when a plugin hook file is modified.
5
+ #
6
+ # Exit 0 = tests pass (or not a hook file)
7
+ # Exit 1 = tests failed (blocks the edit)
8
+
9
+ set -e
10
+
11
+ HOOKS_DIR="$(dirname "$0")"
12
+
13
+ INPUT=$(cat)
14
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty')
15
+
16
+ if [ -z "$FILE_PATH" ]; then
17
+ exit 0
18
+ fi
19
+
20
+ # Only act on hook files under /.claude/hooks/; avoids matching node_modules/*/hooks/
21
+ if echo "$FILE_PATH" | grep -qE '/\.claude/hooks/[^/]+\.sh$'; then
22
+ BASENAME=$(basename "$FILE_PATH")
23
+ case "$BASENAME" in
24
+ test-hooks.sh|auto-test-hooks.sh)
25
+ exit 0
26
+ ;;
27
+ esac
28
+
29
+ echo ""
30
+ echo "Hook file modified: $FILE_PATH"
31
+ echo "Running automated hook tests..."
32
+
33
+ if bash "$HOOKS_DIR/test-hooks.sh"; then
34
+ echo "All hook tests passed."
35
+ exit 0
36
+ else
37
+ echo ""
38
+ echo "HOOK TESTS FAILED!"
39
+ echo "The hook modification broke existing functionality."
40
+ exit 1
41
+ fi
42
+ fi
43
+
44
+ exit 0
@@ -0,0 +1,159 @@
1
+ #!/bin/bash
2
+ # @hook: PostToolUse Edit|Write
3
+ # Hook: Auto-format and auto-fix lint on edited source files
4
+ # Purpose: Continuous Prettier + ESLint --fix after every Edit/Write.
5
+ # Auto-fixable issues get fixed silently; remaining ESLint errors
6
+ # are piped to stderr so Claude sees them inline.
7
+ #
8
+ # Exit 0 = always (non-blocking; errors shown via stderr, not an Edit block)
9
+ #
10
+ # Performance: uses --cache, direct binary paths (no pnpm-exec startup cost).
11
+ # First edit per package: ~1-3s. Subsequent edits on same package: <500ms.
12
+
13
+ set -e
14
+
15
+ # Use Claude Code's project-dir env var as REPO_ROOT.
16
+ # When unset (rare — older host or unusual launch), exit silently as a no-op.
17
+ REPO_ROOT="${CLAUDE_PROJECT_DIR:-}"
18
+ if [ -z "$REPO_ROOT" ]; then
19
+ exit 0
20
+ fi
21
+
22
+ # Read JSON input from stdin
23
+ INPUT=$(cat)
24
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty')
25
+
26
+ # No path / file gone → silent exit
27
+ [ -z "$FILE_PATH" ] && exit 0
28
+ [ ! -f "$FILE_PATH" ] && exit 0
29
+
30
+ # ── File-type gate ───────────────────────────────────────────────────────
31
+ case "$FILE_PATH" in
32
+ *.ts|*.tsx|*.js|*.jsx|*.mjs|*.mts|*.cjs|*.cts)
33
+ LINT_ENABLED=1
34
+ FORMAT_ENABLED=1
35
+ ;;
36
+ *.scss|*.css|*.json|*.md|*.yml|*.yaml)
37
+ LINT_ENABLED=0
38
+ FORMAT_ENABLED=1
39
+ ;;
40
+ *)
41
+ exit 0
42
+ ;;
43
+ esac
44
+
45
+ # ── Skip ignored directories ─────────────────────────────────────────────
46
+ case "$FILE_PATH" in
47
+ */node_modules/*|*/.next/*|*/dist/*|*/build/*|*/.git/*|*/coverage/*|*/.turbo/*)
48
+ exit 0
49
+ ;;
50
+ esac
51
+
52
+ # ── Skip templates directory (prevents one-direction Prettier drift) ─────
53
+ case "$FILE_PATH" in
54
+ */packages/codebyplan-package/templates/*.md)
55
+ exit 0
56
+ ;;
57
+ esac
58
+
59
+ # ── Find owning workspace package (walk up from file) ────────────────────
60
+ PKG_DIR="$(dirname "$FILE_PATH")"
61
+ while [ "$PKG_DIR" != "$REPO_ROOT" ] && [ "$PKG_DIR" != "/" ] && [ -n "$PKG_DIR" ]; do
62
+ if [ -f "$PKG_DIR/package.json" ]; then
63
+ break
64
+ fi
65
+ PKG_DIR="$(dirname "$PKG_DIR")"
66
+ done
67
+ [ -f "$PKG_DIR/package.json" ] || PKG_DIR="$REPO_ROOT"
68
+
69
+ # ── Prettier: format first (fast, idempotent) ────────────────────────────
70
+ if [ "$FORMAT_ENABLED" = "1" ]; then
71
+ PRETTIER_BIN=""
72
+ if [ -x "$PKG_DIR/node_modules/.bin/prettier" ]; then
73
+ PRETTIER_BIN="$PKG_DIR/node_modules/.bin/prettier"
74
+ elif [ -x "$REPO_ROOT/node_modules/.bin/prettier" ]; then
75
+ PRETTIER_BIN="$REPO_ROOT/node_modules/.bin/prettier"
76
+ fi
77
+ if [ -n "$PRETTIER_BIN" ]; then
78
+ (cd "$PKG_DIR" && "$PRETTIER_BIN" --write --log-level=error "$FILE_PATH" >/dev/null 2>&1) || true
79
+ fi
80
+ fi
81
+
82
+ # ── ESLint --fix + report remaining ──────────────────────────────────────
83
+ if [ "$LINT_ENABLED" = "1" ]; then
84
+ # Only run if the package has an ESLint flat config
85
+ ESLINT_CONFIG=""
86
+ for name in eslint.config.mjs eslint.config.js eslint.config.cjs eslint.config.ts; do
87
+ if [ -f "$PKG_DIR/$name" ]; then
88
+ ESLINT_CONFIG="$PKG_DIR/$name"
89
+ break
90
+ fi
91
+ done
92
+
93
+ if [ -n "$ESLINT_CONFIG" ]; then
94
+ ESLINT_BIN=""
95
+ if [ -x "$PKG_DIR/node_modules/.bin/eslint" ]; then
96
+ ESLINT_BIN="$PKG_DIR/node_modules/.bin/eslint"
97
+ elif [ -x "$REPO_ROOT/node_modules/.bin/eslint" ]; then
98
+ ESLINT_BIN="$REPO_ROOT/node_modules/.bin/eslint"
99
+ fi
100
+
101
+ if [ -n "$ESLINT_BIN" ]; then
102
+ # --fix writes auto-fixable changes AND outputs remaining issues.
103
+ # ESLint exit codes:
104
+ # 0 = clean (no errors, no warnings, or only auto-fixed)
105
+ # 1 = lint errors / warnings remain after --fix (per-file diagnostics)
106
+ # 2 = config-load failure (preset collision, missing plugin, ESM/CJS resolution, etc.)
107
+ # Capture exit code distinctly — config errors must NOT be silently swallowed.
108
+ # Disable set -e for this block so the exit-code capture is reliable
109
+ # (otherwise `VAR=$(failing-cmd)` would terminate the script on lint errors).
110
+ set +e
111
+ LINT_OUTPUT=$(cd "$PKG_DIR" && "$ESLINT_BIN" --fix --cache --no-error-on-unmatched-pattern "$FILE_PATH" 2>&1)
112
+ LINT_EXIT=$?
113
+ set -e
114
+
115
+ case "$LINT_EXIT" in
116
+ 0)
117
+ # Clean — silent
118
+ :
119
+ ;;
120
+ 1)
121
+ # Per-file diagnostics — surface to stderr (Claude reads this)
122
+ if [ -n "$LINT_OUTPUT" ] && echo "$LINT_OUTPUT" | grep -qE '(error|warning)'; then
123
+ echo "[lint-format-on-edit] ESLint issues in ${FILE_PATH#$REPO_ROOT/}:" >&2
124
+ echo "$LINT_OUTPUT" >&2
125
+ fi
126
+ ;;
127
+ 2|*)
128
+ # Config-load failure or unknown error — LOUD banner so config drift
129
+ # cannot persist silently across many rounds. Cache a marker file in
130
+ # /tmp so the loud banner only fires ONCE per package per session —
131
+ # repeat edits in the same broken package emit a one-line reminder.
132
+ MARKER_DIR="${TMPDIR:-/tmp}/cbp-lint-config-fail"
133
+ mkdir -p "$MARKER_DIR" 2>/dev/null
134
+ PKG_HASH=$(echo -n "$PKG_DIR" | shasum | cut -c1-12)
135
+ MARKER_FILE="$MARKER_DIR/$PKG_HASH"
136
+ if [ ! -f "$MARKER_FILE" ]; then
137
+ touch "$MARKER_FILE" 2>/dev/null
138
+ echo "" >&2
139
+ echo "╔════════════════════════════════════════════════════════════════════╗" >&2
140
+ echo "║ ⚠ ESLint CONFIGURATION ERROR — lint-on-edit hook is degraded ║" >&2
141
+ echo "╠════════════════════════════════════════════════════════════════════╣" >&2
142
+ echo "║ Package: ${PKG_DIR#$REPO_ROOT/}" >&2
143
+ echo "║ Exit code: $LINT_EXIT (config-load failure)" >&2
144
+ echo "║ Effect: NO lint coverage in this package until fixed." >&2
145
+ echo "║ This banner fires ONCE per session per package; subsequent" >&2
146
+ echo "║ edits in the same package emit a 1-line reminder only." >&2
147
+ echo "╚════════════════════════════════════════════════════════════════════╝" >&2
148
+ echo "$LINT_OUTPUT" >&2
149
+ echo "" >&2
150
+ else
151
+ echo "[lint-format-on-edit] ESLint config still broken in ${PKG_DIR#$REPO_ROOT/} (banner shown earlier this session)." >&2
152
+ fi
153
+ ;;
154
+ esac
155
+ fi
156
+ fi
157
+ fi
158
+
159
+ exit 0
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env bash
2
+ # @event: PreToolUse
3
+ # @matcher: Edit|Write|MultiEdit
4
+ # Validate maestro flow YAML against the installed CLI's accepted property set.
5
+ # Catches properties the CLI will reject at flow-run time so authors find out at
6
+ # edit time, not after a failed `maestro test` invocation.
7
+ #
8
+ # Source-of-truth: maestro CLI itself. The hook reads the local CLI version and
9
+ # applies a known-rejected-property allowlist for that version.
10
+
11
+ set -euo pipefail
12
+
13
+ INPUT_JSON="$(cat)"
14
+ TOOL_NAME="$(echo "$INPUT_JSON" | jq -r '.tool_name // empty')"
15
+ FILE_PATH="$(echo "$INPUT_JSON" | jq -r '.tool_input.file_path // empty')"
16
+
17
+ # Only enforce on .yaml/.yml files under apps/*/maestro/flows/
18
+ case "$FILE_PATH" in
19
+ */maestro/flows/*.yaml|*/maestro/flows/*.yml) ;;
20
+ *) exit 0 ;;
21
+ esac
22
+
23
+ # Bail if the file doesn't exist yet (Write of a brand new file — content lives in tool_input)
24
+ NEW_CONTENT=""
25
+ if [ "$TOOL_NAME" = "Write" ]; then
26
+ NEW_CONTENT="$(echo "$INPUT_JSON" | jq -r '.tool_input.content // empty')"
27
+ elif [ "$TOOL_NAME" = "Edit" ]; then
28
+ NEW_CONTENT="$(echo "$INPUT_JSON" | jq -r '.tool_input.new_string // empty')"
29
+ elif [ "$TOOL_NAME" = "MultiEdit" ]; then
30
+ NEW_CONTENT="$(echo "$INPUT_JSON" | jq -r '[.tool_input.edits[].new_string] | join("\n")')"
31
+ fi
32
+
33
+ [ -z "$NEW_CONTENT" ] && exit 0
34
+
35
+ # Locate the maestro CLI. Skip if not installed (developer may not have it locally).
36
+ MAESTRO_BIN="$(command -v maestro 2>/dev/null || true)"
37
+ [ -z "$MAESTRO_BIN" ] && exit 0
38
+
39
+ # Capture installed version (best-effort — maestro version output format has shifted).
40
+ MAESTRO_VERSION="$("$MAESTRO_BIN" --version 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")"
41
+
42
+ # Known-rejected property catalog. Keyed by command name; properties listed here
43
+ # fire a hook warning when they appear under that command in the YAML.
44
+ #
45
+ # Maintenance: when maestro upgrades, validate this list against the new CLI's
46
+ # documentation (https://maestro.mobile.dev/api-reference/commands). Add new
47
+ # properties here when the CLI starts accepting them; remove them when the CLI
48
+ # drops them.
49
+ declare -a KNOWN_REJECTED=(
50
+ # property:command_context
51
+ "timeout:assertVisible" # maestro 1.x / 2.x rejects timeout on assertVisible (use waitForAnimationToEnd or maxRetries instead)
52
+ "timeout:assertNotVisible" # same
53
+ "delay:tapOn" # use waitForAnimationToEnd or static wait
54
+ )
55
+
56
+ VIOLATIONS=""
57
+ for entry in "${KNOWN_REJECTED[@]}"; do
58
+ prop="${entry%%:*}"
59
+ cmd="${entry##*:}"
60
+ # Match patterns like:
61
+ # - assertVisible:
62
+ # id: foo
63
+ # timeout: 5000
64
+ # The check is heuristic — it looks for `cmd` followed by a block containing `prop:`.
65
+ # False-positive risk on similarly-named commands; the warning includes the matched line.
66
+ match="$(echo "$NEW_CONTENT" | awk -v c="$cmd" -v p="$prop" '
67
+ /^[[:space:]]*-?[[:space:]]*'"$cmd"':/ { in_block = 1; depth = 0; next }
68
+ in_block && /^[[:space:]]*[a-zA-Z]/ {
69
+ indent = match($0, /[^ ]/) - 1
70
+ if (depth == 0) depth = indent
71
+ if (indent < depth) { in_block = 0; next }
72
+ if ($0 ~ "^[[:space:]]*"p":") {
73
+ printf " line %d (under `%s`): %s\n", NR, c, $0
74
+ }
75
+ }
76
+ ')"
77
+ if [ -n "$match" ]; then
78
+ VIOLATIONS="${VIOLATIONS} - Property \`${prop}\` not accepted under \`${cmd}\` (rejected by maestro ${MAESTRO_VERSION}):\n${match}\n"
79
+ fi
80
+ done
81
+
82
+ if [ -n "$VIOLATIONS" ]; then
83
+ cat >&2 <<EOF
84
+ [hook] maestro-yaml-validate: properties rejected by installed maestro CLI
85
+
86
+ File: $FILE_PATH
87
+ Maestro CLI: $MAESTRO_VERSION
88
+
89
+ Detected:
90
+ $(printf '%b' "$VIOLATIONS")
91
+ The flow will throw 'Unknown Property' when invoked. Either:
92
+ - Remove the unsupported properties (consult https://maestro.mobile.dev/api-reference/commands)
93
+ - Upgrade the CLI if a newer version supports them
94
+ - Update the KNOWN_REJECTED list in this hook if the property is now supported
95
+
96
+ EOF
97
+ exit 2
98
+ fi
99
+
100
+ exit 0
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ # @scope: org-shared
3
+ # @hook: PreToolUse for MCP mutation tools
4
+ # Hook: PreToolUse for MCP mutation tools
5
+ # Purpose: Warn once per session if a legacy .codebyplan.json exists at repo root (post-CHK-120).
6
+ # Warns once per session if legacy .codebyplan.json exists at repo root (post-CHK-120 case).
7
+ # Exits 0 on all paths — never blocks the MCP call.
8
+
9
+ INPUT=$(cat)
10
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
11
+
12
+ case "$TOOL_NAME" in
13
+ *update_task|*complete_task|*update_checkpoint|*create_checkpoint|*create_task) ;;
14
+ *) exit 0 ;;
15
+ esac
16
+
17
+ # Dedup across PreToolUse subprocess invocations: env exports do not survive
18
+ # between hook firings, so use a $PPID-keyed sentinel file in /tmp instead.
19
+ # Sentinel is unlinked when the parent (Claude Code) process exits via /tmp
20
+ # cleanup; harmless if it lingers.
21
+ SENTINEL="/tmp/.cbp-migration-warned-${PPID}"
22
+ [ -f "$SENTINEL" ] && exit 0
23
+ : > "$SENTINEL"
24
+
25
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
26
+ [ -z "$REPO_ROOT" ] && exit 0
27
+ LEGACY_FILE="$REPO_ROOT/.codebyplan.json"
28
+ [ ! -f "$LEGACY_FILE" ] && exit 0
29
+
30
+ echo "cbp: legacy .codebyplan.json detected at repo root. Run: npx codebyplan sync to migrate to the .codebyplan/ directory layout (one-time, automatic). Continuing MCP call." >&2
31
+
32
+ exit 0
@@ -0,0 +1,79 @@
1
+ #!/bin/bash
2
+ # @scope: org-shared
3
+ # Hook: PostToolUse mcp__codebyplan__complete_round
4
+ # Purpose: After complete_round succeeds, delegate git-diff drift merge,
5
+ # staging-status flip, and web-UI flag sync to the codebyplan CLI.
6
+ # Replaces the inline jq merge + curl PATCH with a single CLI call.
7
+ #
8
+ # Delegates to: npx codebyplan round sync-approvals
9
+ # - Git-diff drift merge (in/not-in DB vs git)
10
+ # - Staging-status → user_approved flip
11
+ # - Hook-auto-staged override (lint-format-on-edit.sh detection)
12
+ # - Web-UI flag (app_file_approval_by_user) consumption + reset
13
+ # - Writes both PATCH /api/rounds/${ROUND_ID} and PATCH /api/tasks/${TASK_ID}
14
+ #
15
+ # Flags:
16
+ # --dry-run Pass through to CLI (prints merged payload, no API writes).
17
+ # Used by fixture-based smoke tests.
18
+ #
19
+ # Exit 0 always — non-fatal; errors go to stderr only.
20
+
21
+ DRY_RUN=false
22
+ if [ "${1:-}" = "--dry-run" ]; then
23
+ DRY_RUN=true
24
+ fi
25
+
26
+ INPUT=$(cat)
27
+
28
+ # Only fire on PostToolUse for complete_round
29
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
30
+ [ "$TOOL_NAME" = "mcp__codebyplan__complete_round" ] || exit 0
31
+
32
+ # Check if the tool call succeeded (non-error response)
33
+ # PostToolUse stdin shape: {tool_name, tool_input, tool_response}
34
+ # tool_response may be a JSON string or object — handle both
35
+ TOOL_RESPONSE=$(echo "$INPUT" | jq -r '.tool_response // empty' 2>/dev/null)
36
+ if [ -z "$TOOL_RESPONSE" ]; then
37
+ exit 0
38
+ fi
39
+
40
+ # If tool_response is a string (MCP text content), parse it
41
+ if echo "$TOOL_RESPONSE" | jq -e 'type == "string"' >/dev/null 2>&1; then
42
+ TOOL_RESPONSE=$(echo "$TOOL_RESPONSE" | jq -r '.' 2>/dev/null)
43
+ fi
44
+
45
+ # Detect error responses: look for "error" key or error indicators
46
+ RESPONSE_ERROR=$(echo "$TOOL_RESPONSE" | jq -r '.error // empty' 2>/dev/null)
47
+ if [ -n "$RESPONSE_ERROR" ]; then
48
+ echo "cbp-mcp-round-sync: complete_round returned error, skipping sync" >&2
49
+ exit 0
50
+ fi
51
+
52
+ # Extract round_id from the response
53
+ ROUND_ID=$(echo "$TOOL_RESPONSE" | jq -r '.id // .round_id // empty' 2>/dev/null)
54
+ if [ -z "$ROUND_ID" ]; then
55
+ # Try to extract from tool_input instead
56
+ ROUND_ID=$(echo "$INPUT" | jq -r '.tool_input.round_id // .tool_input.id // empty' 2>/dev/null)
57
+ fi
58
+ if [ -z "$ROUND_ID" ]; then
59
+ echo "cbp-mcp-round-sync: could not extract round_id, skipping sync" >&2
60
+ exit 0
61
+ fi
62
+
63
+ # Extract TASK_ID from tool_response (parent task ref)
64
+ TASK_ID=$(echo "$TOOL_RESPONSE" | jq -r '.task_id // empty' 2>/dev/null)
65
+ if [ -z "$TASK_ID" ]; then
66
+ echo "cbp-mcp-round-sync: could not extract task_id, skipping sync" >&2
67
+ exit 0
68
+ fi
69
+
70
+ # Delegate to the codebyplan CLI (single source of truth for merge semantics)
71
+ CMD_ARGS=("round" "sync-approvals" "--round-id" "$ROUND_ID" "--task-id" "$TASK_ID")
72
+ [ "$DRY_RUN" = "true" ] && CMD_ARGS+=("--dry-run")
73
+
74
+ if npx codebyplan "${CMD_ARGS[@]}" 2>&1; then
75
+ echo "cbp-mcp-round-sync: synced via CLI for round ${ROUND_ID}" >&2
76
+ else
77
+ echo "cbp-mcp-round-sync: CLI sync failed for round ${ROUND_ID} (non-fatal)" >&2
78
+ fi
79
+ exit 0