@toolu/opencode 6.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 (331) hide show
  1. package/generated/GENERATED-NOTES.md +16 -0
  2. package/generated/agents/toolu--architect.md +52 -0
  3. package/generated/agents/toolu--deep-explore.md +65 -0
  4. package/generated/agents/toolu--implementer.md +50 -0
  5. package/generated/agents/toolu--quick-task.md +54 -0
  6. package/generated/agents/toolu--research-agent.md +101 -0
  7. package/generated/commands/toolu--commit.md +8 -0
  8. package/generated/commands/toolu--review-and-commit.md +8 -0
  9. package/generated/opencode.toolu.json +116 -0
  10. package/generated/skills/toolu--brainstorm/SKILL.md +57 -0
  11. package/generated/skills/toolu--brainstorm/references/design-questions.md +36 -0
  12. package/generated/skills/toolu--commit--skill/SKILL.md +11 -0
  13. package/generated/skills/toolu--debug/SKILL.md +54 -0
  14. package/generated/skills/toolu--deep-research/SKILL.md +51 -0
  15. package/generated/skills/toolu--execution/SKILL.md +77 -0
  16. package/generated/skills/toolu--orchestrator/SKILL.md +145 -0
  17. package/generated/skills/toolu--orchestrator/references/delegation-cost.md +104 -0
  18. package/generated/skills/toolu--orchestrator/references/model-routing.md +112 -0
  19. package/generated/skills/toolu--plan/SKILL.md +56 -0
  20. package/generated/skills/toolu--plan/references/ledger.md +18 -0
  21. package/generated/skills/toolu--plan-review/SKILL.md +48 -0
  22. package/generated/skills/toolu--review-and-commit--skill/SKILL.md +10 -0
  23. package/generated/skills/toolu--setup/SKILL.md +24 -0
  24. package/generated/skills/toolu--setup/scripts/setup.sh +206 -0
  25. package/generated/skills/toolu--spec/SKILL.md +67 -0
  26. package/generated/skills/toolu--spec-review/SKILL.md +50 -0
  27. package/generated/skills/toolu--test/SKILL.md +49 -0
  28. package/package.json +51 -0
  29. package/plugins/agent-browser/.claude-plugin/plugin.json +18 -0
  30. package/plugins/agent-browser/.codex-plugin/plugin.json +7 -0
  31. package/plugins/agent-browser/README.md +18 -0
  32. package/plugins/agent-browser/hooks/hooks.json +15 -0
  33. package/plugins/agent-browser/hooks/session-start.sh +42 -0
  34. package/plugins/agent-browser/skills/agent-browser/SKILL.md +77 -0
  35. package/plugins/agent-browser/skills/agent-browser/scripts/agent-browser.sh +80 -0
  36. package/plugins/ast-grep/.claude-plugin/plugin.json +18 -0
  37. package/plugins/ast-grep/.codex-plugin/plugin.json +7 -0
  38. package/plugins/ast-grep/README.md +21 -0
  39. package/plugins/ast-grep/hooks/hooks.json +15 -0
  40. package/plugins/ast-grep/hooks/post-tools.d/byte-savings.sh +67 -0
  41. package/plugins/ast-grep/hooks/pre-tools.d/search-nudge.sh +170 -0
  42. package/plugins/ast-grep/hooks/register.sh +68 -0
  43. package/plugins/ast-grep/scripts/byte-savings-report.sh +28 -0
  44. package/plugins/ast-grep/skills/ast-grep/SKILL.md +127 -0
  45. package/plugins/ast-grep/skills/ast-grep/references/ast-grep-advanced.md +44 -0
  46. package/plugins/ast-grep/skills/ast-grep/scripts/ast-grep.sh +103 -0
  47. package/plugins/context7/.claude-plugin/plugin.json +18 -0
  48. package/plugins/context7/.codex-plugin/plugin.json +7 -0
  49. package/plugins/context7/README.md +19 -0
  50. package/plugins/context7/hooks/hooks.json +15 -0
  51. package/plugins/context7/hooks/session-start.sh +42 -0
  52. package/plugins/context7/skills/context7/SKILL.md +64 -0
  53. package/plugins/context7/skills/context7/scripts/search.sh +151 -0
  54. package/plugins/exa-search/.claude-plugin/plugin.json +18 -0
  55. package/plugins/exa-search/.codex-plugin/plugin.json +7 -0
  56. package/plugins/exa-search/README.md +19 -0
  57. package/plugins/exa-search/hooks/hooks.json +15 -0
  58. package/plugins/exa-search/hooks/session-start.sh +34 -0
  59. package/plugins/exa-search/skills/exa-search/SKILL.md +91 -0
  60. package/plugins/exa-search/skills/exa-search/scripts/search.sh +221 -0
  61. package/plugins/jev/.claude-plugin/plugin.json +19 -0
  62. package/plugins/jev/.codex-plugin/plugin.json +7 -0
  63. package/plugins/jev/README.md +61 -0
  64. package/plugins/jev/hooks/hooks.json +25 -0
  65. package/plugins/jev/hooks/lib/common.sh +49 -0
  66. package/plugins/jev/hooks/session-start.sh +45 -0
  67. package/plugins/jev/hooks/user-prompt-submit.sh +41 -0
  68. package/plugins/jev/skills/jev/SKILL.md +81 -0
  69. package/plugins/jev/skills/jev/evals/README.md +98 -0
  70. package/plugins/jev/skills/jev/references/problem-solving.md +198 -0
  71. package/plugins/jev/skills/jev/scripts/jev.sh +405 -0
  72. package/plugins/jira/.claude-plugin/plugin.json +18 -0
  73. package/plugins/jira/.codex-plugin/plugin.json +7 -0
  74. package/plugins/jira/README.md +42 -0
  75. package/plugins/jira/hooks/hooks.json +15 -0
  76. package/plugins/jira/hooks/session-start.sh +34 -0
  77. package/plugins/jira/skills/jira/SKILL.md +123 -0
  78. package/plugins/jira/skills/jira/scripts/jira.sh +70 -0
  79. package/plugins/jira/skills/jira/scripts/lib/adf.sh +17 -0
  80. package/plugins/jira/skills/jira/scripts/lib/attachment.sh +94 -0
  81. package/plugins/jira/skills/jira/scripts/lib/board.sh +58 -0
  82. package/plugins/jira/skills/jira/scripts/lib/http.sh +184 -0
  83. package/plugins/jira/skills/jira/scripts/lib/issue.sh +159 -0
  84. package/plugins/jira/skills/jira/scripts/lib/paginate.sh +45 -0
  85. package/plugins/jira/skills/jira/scripts/lib/plan-parse.sh +70 -0
  86. package/plugins/jira/skills/jira/scripts/lib/plan-run.sh +115 -0
  87. package/plugins/jira/skills/jira/scripts/lib/plan-store.sh +168 -0
  88. package/plugins/jira/skills/jira/scripts/lib/plan.sh +108 -0
  89. package/plugins/jira/skills/jira/scripts/lib/project.sh +42 -0
  90. package/plugins/jira/skills/jira/scripts/lib/raw.sh +15 -0
  91. package/plugins/jira/skills/jira/scripts/lib/search.sh +41 -0
  92. package/plugins/jira/skills/jira/scripts/lib/sprint.sh +88 -0
  93. package/plugins/jira/skills/jira/scripts/lib/user.sh +45 -0
  94. package/plugins/jira/skills/jira/scripts/lib/worklog.sh +56 -0
  95. package/plugins/pr-babysit/.claude-plugin/plugin.json +25 -0
  96. package/plugins/pr-babysit/.codex-plugin/plugin.json +7 -0
  97. package/plugins/pr-babysit/README.md +38 -0
  98. package/plugins/pr-babysit/commands/babysit.md +13 -0
  99. package/plugins/pr-babysit/hooks/check-toolu.sh +20 -0
  100. package/plugins/pr-babysit/hooks/hooks.json +15 -0
  101. package/plugins/pr-babysit/scripts/babysit-tick.sh +133 -0
  102. package/plugins/pr-babysit/scripts/collect-pr.sh +224 -0
  103. package/plugins/pr-babysit/scripts/lib/common.sh +146 -0
  104. package/plugins/pr-babysit/scripts/lib/gh.sh +150 -0
  105. package/plugins/pr-babysit/scripts/lib/lock.sh +84 -0
  106. package/plugins/pr-babysit/scripts/lib/normalize.sh +98 -0
  107. package/plugins/pr-babysit/scripts/lib/state.sh +37 -0
  108. package/plugins/pr-babysit/scripts/parse-verdict.sh +171 -0
  109. package/plugins/pr-babysit/scripts/record.sh +69 -0
  110. package/plugins/pr-babysit/scripts/reduce-state.sh +245 -0
  111. package/plugins/pr-babysit/scripts/reply-thread.sh +90 -0
  112. package/plugins/pr-babysit/scripts/resolve-thread.sh +71 -0
  113. package/plugins/pr-babysit/skills/babysit/SKILL.md +31 -0
  114. package/plugins/pr-babysit/skills/babysit/references/helper.md +209 -0
  115. package/plugins/pr-babysit/workflows/babysit.md +722 -0
  116. package/plugins/python-quality/.claude-plugin/plugin.json +24 -0
  117. package/plugins/python-quality/.codex-plugin/plugin.json +6 -0
  118. package/plugins/python-quality/README.md +23 -0
  119. package/plugins/python-quality/hooks/check-toolu.sh +20 -0
  120. package/plugins/python-quality/hooks/concerns/00-preamble.sh +85 -0
  121. package/plugins/python-quality/hooks/concerns/10-size-file.sh +6 -0
  122. package/plugins/python-quality/hooks/concerns/20-tests.sh +37 -0
  123. package/plugins/python-quality/hooks/concerns/30-suppression.sh +33 -0
  124. package/plugins/python-quality/hooks/concerns/50-size-fn.sh +49 -0
  125. package/plugins/python-quality/hooks/concerns/70-no-mocks.sh +76 -0
  126. package/plugins/python-quality/hooks/concerns/90-docs.sh +52 -0
  127. package/plugins/python-quality/hooks/concerns/99-finalize.sh +36 -0
  128. package/plugins/python-quality/hooks/hooks.json +19 -0
  129. package/plugins/python-quality/hooks/register.sh +65 -0
  130. package/plugins/rust-quality/.claude-plugin/plugin.json +24 -0
  131. package/plugins/rust-quality/.codex-plugin/plugin.json +6 -0
  132. package/plugins/rust-quality/README.md +24 -0
  133. package/plugins/rust-quality/hooks/check-toolu.sh +20 -0
  134. package/plugins/rust-quality/hooks/concerns/00-preamble.sh +93 -0
  135. package/plugins/rust-quality/hooks/concerns/10-size-file.sh +10 -0
  136. package/plugins/rust-quality/hooks/concerns/20-tests.sh +84 -0
  137. package/plugins/rust-quality/hooks/concerns/30-suppression.sh +26 -0
  138. package/plugins/rust-quality/hooks/concerns/40-unsafe.sh +39 -0
  139. package/plugins/rust-quality/hooks/concerns/50-size-fn.sh +38 -0
  140. package/plugins/rust-quality/hooks/concerns/55-size-impl.sh +28 -0
  141. package/plugins/rust-quality/hooks/concerns/60-error-handling.sh +149 -0
  142. package/plugins/rust-quality/hooks/concerns/70-no-mocks.sh +139 -0
  143. package/plugins/rust-quality/hooks/concerns/90-docs.sh +32 -0
  144. package/plugins/rust-quality/hooks/concerns/99-finalize.sh +36 -0
  145. package/plugins/rust-quality/hooks/hooks.json +19 -0
  146. package/plugins/rust-quality/hooks/register.sh +65 -0
  147. package/plugins/statusline/.claude-plugin/plugin.json +19 -0
  148. package/plugins/statusline/.codex-plugin/plugin.json +7 -0
  149. package/plugins/statusline/README.md +108 -0
  150. package/plugins/statusline/commands/setup.md +30 -0
  151. package/plugins/statusline/hooks/hooks.json +15 -0
  152. package/plugins/statusline/hooks/session-start.sh +45 -0
  153. package/plugins/statusline/scripts/collect-status.sh +147 -0
  154. package/plugins/statusline/scripts/setup.sh +93 -0
  155. package/plugins/statusline/scripts/status.sh +55 -0
  156. package/plugins/statusline/skills/status/SKILL.md +13 -0
  157. package/plugins/statusline/statusline.sh +177 -0
  158. package/plugins/toolu/.claude-plugin/plugin.json +19 -0
  159. package/plugins/toolu/.codex-plugin/plugin.json +7 -0
  160. package/plugins/toolu/README.md +35 -0
  161. package/plugins/toolu/agents/.gitkeep +0 -0
  162. package/plugins/toolu/agents/architect.md +52 -0
  163. package/plugins/toolu/agents/deep-explore.md +65 -0
  164. package/plugins/toolu/agents/implementer.md +50 -0
  165. package/plugins/toolu/agents/quick-task.md +54 -0
  166. package/plugins/toolu/agents/research-agent.md +107 -0
  167. package/plugins/toolu/assets/agents/architect.toml +9 -0
  168. package/plugins/toolu/assets/agents/deep-explore.toml +9 -0
  169. package/plugins/toolu/assets/agents/implementer.toml +9 -0
  170. package/plugins/toolu/assets/agents/quick-task.toml +9 -0
  171. package/plugins/toolu/assets/agents/research-agent.toml +9 -0
  172. package/plugins/toolu/commands/.gitkeep +0 -0
  173. package/plugins/toolu/commands/commit.md +5 -0
  174. package/plugins/toolu/commands/review-and-commit.md +5 -0
  175. package/plugins/toolu/hooks/docs/gates.md +157 -0
  176. package/plugins/toolu/hooks/docs/model-routing.md +14 -0
  177. package/plugins/toolu/hooks/docs/post-compaction.md +3 -0
  178. package/plugins/toolu/hooks/docs/push-review.md +123 -0
  179. package/plugins/toolu/hooks/docs/session-start-python.md +3 -0
  180. package/plugins/toolu/hooks/docs/session-start-rust.md +4 -0
  181. package/plugins/toolu/hooks/docs/session-start-ts.md +3 -0
  182. package/plugins/toolu/hooks/docs/session-start.md +16 -0
  183. package/plugins/toolu/hooks/hooks.json +76 -0
  184. package/plugins/toolu/hooks/lib/config.sh +355 -0
  185. package/plugins/toolu/hooks/lib/detect.sh +929 -0
  186. package/plugins/toolu/hooks/lib/diff-sha.sh +32 -0
  187. package/plugins/toolu/hooks/lib/dispatch.sh +365 -0
  188. package/plugins/toolu/hooks/lib/docs-sync-config.sh +81 -0
  189. package/plugins/toolu/hooks/lib/edit-records.sh +157 -0
  190. package/plugins/toolu/hooks/lib/gate-file.sh +201 -0
  191. package/plugins/toolu/hooks/lib/gate-mode.sh +244 -0
  192. package/plugins/toolu/hooks/lib/host.sh +205 -0
  193. package/plugins/toolu/hooks/lib/permissions.sh +125 -0
  194. package/plugins/toolu/hooks/lib/plan-ledger-parse.sh +219 -0
  195. package/plugins/toolu/hooks/lib/plan-ledger-preflight.sh +137 -0
  196. package/plugins/toolu/hooks/lib/plan-ledger.sh +749 -0
  197. package/plugins/toolu/hooks/lib/push-waiver.sh +116 -0
  198. package/plugins/toolu/hooks/lib/quality-config.sh +184 -0
  199. package/plugins/toolu/hooks/lib/registry.sh +72 -0
  200. package/plugins/toolu/hooks/lib/state-sweeper.sh +194 -0
  201. package/plugins/toolu/hooks/lib/telemetry.sh +94 -0
  202. package/plugins/toolu/hooks/lib/verdict.sh +477 -0
  203. package/plugins/toolu/hooks/post-tools/mod.sh +42 -0
  204. package/plugins/toolu/hooks/post-tools/modules/gate-status.sh +140 -0
  205. package/plugins/toolu/hooks/post-tools/modules/push-waiver.sh +97 -0
  206. package/plugins/toolu/hooks/pre-compact.sh +18 -0
  207. package/plugins/toolu/hooks/pre-tools/agent-tier.sh +111 -0
  208. package/plugins/toolu/hooks/pre-tools/mod.sh +37 -0
  209. package/plugins/toolu/hooks/pre-tools/modules/bash-commands.sh +187 -0
  210. package/plugins/toolu/hooks/pre-tools/modules/code-edit-rules.sh +78 -0
  211. package/plugins/toolu/hooks/pre-tools/modules/commit-gate.sh +70 -0
  212. package/plugins/toolu/hooks/pre-tools/modules/docs-sync.sh +137 -0
  213. package/plugins/toolu/hooks/pre-tools/modules/mcp-blocker.sh +151 -0
  214. package/plugins/toolu/hooks/pre-tools/modules/plan-ledger.sh +201 -0
  215. package/plugins/toolu/hooks/pre-tools/modules/protected-files.sh +177 -0
  216. package/plugins/toolu/hooks/pre-tools/modules/push-review.sh +273 -0
  217. package/plugins/toolu/hooks/pre-tools/modules/quality-gate.sh +87 -0
  218. package/plugins/toolu/hooks/session-start.sh +379 -0
  219. package/plugins/toolu/hooks/user-prompt-submit.sh +197 -0
  220. package/plugins/toolu/scripts/context-budget.sh +111 -0
  221. package/plugins/toolu/scripts/debug-log.sh +98 -0
  222. package/plugins/toolu/scripts/debug-stack.sh +108 -0
  223. package/plugins/toolu/scripts/debug-testfail.sh +97 -0
  224. package/plugins/toolu/settings/.gitkeep +0 -0
  225. package/plugins/toolu/settings/README.md +127 -0
  226. package/plugins/toolu/settings/bash-allowlist.txt +12 -0
  227. package/plugins/toolu/settings/bash-denylist.txt +25 -0
  228. package/plugins/toolu/settings/code-edit-rules.json +67 -0
  229. package/plugins/toolu/settings/commit-prefixes.txt +12 -0
  230. package/plugins/toolu/settings/mcp-blocklist.txt +8 -0
  231. package/plugins/toolu/settings/permissions.fragment.json +36 -0
  232. package/plugins/toolu/settings/protected-files.txt +30 -0
  233. package/plugins/toolu/settings/rust-unsafe-exemptions.txt +5 -0
  234. package/plugins/toolu/settings/toolu.config.example.json +130 -0
  235. package/plugins/toolu/skills/.gitkeep +0 -0
  236. package/plugins/toolu/skills/brainstorm/SKILL.md +57 -0
  237. package/plugins/toolu/skills/brainstorm/references/design-questions.md +36 -0
  238. package/plugins/toolu/skills/commit/SKILL.md +11 -0
  239. package/plugins/toolu/skills/debug/SKILL.md +54 -0
  240. package/plugins/toolu/skills/debug/evals/README.md +17 -0
  241. package/plugins/toolu/skills/debug/evals/cases.json +24 -0
  242. package/plugins/toolu/skills/debug/evals/results.json +34 -0
  243. package/plugins/toolu/skills/deep-research/SKILL.md +51 -0
  244. package/plugins/toolu/skills/execution/SKILL.md +77 -0
  245. package/plugins/toolu/skills/orchestrator/SKILL.md +145 -0
  246. package/plugins/toolu/skills/orchestrator/references/delegation-cost.md +104 -0
  247. package/plugins/toolu/skills/orchestrator/references/model-routing.md +112 -0
  248. package/plugins/toolu/skills/plan/SKILL.md +56 -0
  249. package/plugins/toolu/skills/plan/references/ledger.md +18 -0
  250. package/plugins/toolu/skills/plan-review/SKILL.md +48 -0
  251. package/plugins/toolu/skills/review-and-commit/SKILL.md +10 -0
  252. package/plugins/toolu/skills/setup/SKILL.md +24 -0
  253. package/plugins/toolu/skills/setup/scripts/setup.sh +206 -0
  254. package/plugins/toolu/skills/spec/SKILL.md +67 -0
  255. package/plugins/toolu/skills/spec-review/SKILL.md +50 -0
  256. package/plugins/toolu/skills/test/SKILL.md +49 -0
  257. package/plugins/toolu/workflows/commit.md +21 -0
  258. package/plugins/toolu/workflows/host-mapping.md +22 -0
  259. package/plugins/toolu/workflows/review-and-commit.md +54 -0
  260. package/plugins/toolu/workflows/semantic-judgments.md +35 -0
  261. package/plugins/toolu-review/.claude-plugin/plugin.json +18 -0
  262. package/plugins/toolu-review/.codex-plugin/plugin.json +7 -0
  263. package/plugins/toolu-review/README.md +17 -0
  264. package/plugins/toolu-review/hooks/hooks.json +15 -0
  265. package/plugins/toolu-review/hooks/session-start.sh +34 -0
  266. package/plugins/toolu-review/skills/review/SKILL.md +90 -0
  267. package/plugins/toolu-review/skills/review/scripts/write-state.sh +189 -0
  268. package/plugins/ts-quality/.claude-plugin/plugin.json +24 -0
  269. package/plugins/ts-quality/.codex-plugin/plugin.json +6 -0
  270. package/plugins/ts-quality/README.md +24 -0
  271. package/plugins/ts-quality/hooks/check-toolu.sh +20 -0
  272. package/plugins/ts-quality/hooks/concerns/00-preamble.sh +123 -0
  273. package/plugins/ts-quality/hooks/concerns/10-imports.sh +33 -0
  274. package/plugins/ts-quality/hooks/concerns/15-type-as.sh +9 -0
  275. package/plugins/ts-quality/hooks/concerns/20-tests.sh +26 -0
  276. package/plugins/ts-quality/hooks/concerns/25-size-file.sh +40 -0
  277. package/plugins/ts-quality/hooks/concerns/30-size-fn.sh +53 -0
  278. package/plugins/ts-quality/hooks/concerns/35-react-hooks.sh +7 -0
  279. package/plugins/ts-quality/hooks/concerns/40-factory.sh +5 -0
  280. package/plugins/ts-quality/hooks/concerns/45-typeguard.sh +6 -0
  281. package/plugins/ts-quality/hooks/concerns/50-type-dup.sh +16 -0
  282. package/plugins/ts-quality/hooks/concerns/55-naming.sh +11 -0
  283. package/plugins/ts-quality/hooks/concerns/60-console.sh +6 -0
  284. package/plugins/ts-quality/hooks/concerns/65-suppression.sh +12 -0
  285. package/plugins/ts-quality/hooks/concerns/70-ui-confirm.sh +8 -0
  286. package/plugins/ts-quality/hooks/concerns/72-ui-radix.sh +12 -0
  287. package/plugins/ts-quality/hooks/concerns/74-react-props.sh +6 -0
  288. package/plugins/ts-quality/hooks/concerns/76-toast.sh +10 -0
  289. package/plugins/ts-quality/hooks/concerns/78-error-ast.sh +242 -0
  290. package/plugins/ts-quality/hooks/concerns/80-throw-literal.sh +23 -0
  291. package/plugins/ts-quality/hooks/concerns/85-no-mocks.sh +109 -0
  292. package/plugins/ts-quality/hooks/concerns/90-duplication.sh +30 -0
  293. package/plugins/ts-quality/hooks/concerns/92-docs.sh +36 -0
  294. package/plugins/ts-quality/hooks/concerns/94-handler.sh +15 -0
  295. package/plugins/ts-quality/hooks/concerns/99-finalize.sh +46 -0
  296. package/plugins/ts-quality/hooks/hooks.json +19 -0
  297. package/plugins/ts-quality/hooks/register.sh +65 -0
  298. package/src/adapter/__tests__/evaluate.test.ts +82 -0
  299. package/src/adapter/__tests__/permission-map.test.ts +91 -0
  300. package/src/adapter/evaluate.ts +54 -0
  301. package/src/adapter/permission-map.ts +177 -0
  302. package/src/bootstrap/__tests__/bootstrap.test.ts +148 -0
  303. package/src/bootstrap/entrypoint.ts +18 -0
  304. package/src/bootstrap/index.ts +5 -0
  305. package/src/bootstrap/readiness.ts +59 -0
  306. package/src/bootstrap/result.ts +21 -0
  307. package/src/bootstrap/runtime.ts +99 -0
  308. package/src/bootstrap/test-helpers.ts +25 -0
  309. package/src/host/__tests__/host.test.ts +37 -0
  310. package/src/host/detect.ts +46 -0
  311. package/src/host/index.ts +10 -0
  312. package/src/host/roots.ts +59 -0
  313. package/src/host/types.ts +14 -0
  314. package/src/inventory/__tests__/inventory.test.ts +69 -0
  315. package/src/inventory/index.ts +9 -0
  316. package/src/inventory/manifest.ts +46 -0
  317. package/src/inventory/scan.ts +66 -0
  318. package/src/inventory/selection.ts +92 -0
  319. package/src/inventory/types.ts +22 -0
  320. package/src/lifecycle/__tests__/lifecycle.test.ts +7 -0
  321. package/src/lifecycle/index.ts +2 -0
  322. package/src/lifecycle/table.ts +42 -0
  323. package/src/plugin/__tests__/plugin-stub.test.ts +6 -0
  324. package/src/plugin/plugin-stub.ts +7 -0
  325. package/src/plugin/toolu.ts +142 -0
  326. package/src/preflight/__tests__/preflight.test.ts +20 -0
  327. package/src/preflight/check.ts +75 -0
  328. package/src/preflight/index.ts +2 -0
  329. package/src/select/__tests__/select.test.ts +52 -0
  330. package/src/select/index.ts +2 -0
  331. package/src/select/resolve.ts +87 -0
@@ -0,0 +1,379 @@
1
+ #!/usr/bin/env bash
2
+ # SessionStart hook
3
+ # Event-aware: tailors context for startup, resume, clear, compact.
4
+ # Project-agnostic: detects project name, language, and package manager.
5
+
6
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
7
+
8
+ # Disable bash 5.2+ patsub_replacement so `&` in ${var//pat/repl} values is
9
+ # literal. No-op (option unknown) on older bash, including macOS /bin/bash 3.2.
10
+ shopt -u patsub_replacement 2>/dev/null || true
11
+
12
+ # shellcheck source=lib/detect.sh
13
+ . "$HOOK_DIR/lib/detect.sh"
14
+ # shellcheck source=lib/config.sh
15
+ . "$HOOK_DIR/lib/config.sh"
16
+ # shellcheck source=lib/registry.sh
17
+ . "$HOOK_DIR/lib/registry.sh"
18
+ # shellcheck source=lib/state-sweeper.sh
19
+ . "$HOOK_DIR/lib/state-sweeper.sh"
20
+ # shellcheck source=lib/permissions.sh
21
+ . "$HOOK_DIR/lib/permissions.sh"
22
+ # shellcheck source=lib/gate-mode.sh
23
+ . "$HOOK_DIR/lib/gate-mode.sh"
24
+
25
+ # Codex has no installed_plugins.json equivalent on the hot path. Snapshot the
26
+ # CLI's installed set once at SessionStart; all later checks consume this file.
27
+ toolu_snapshot_codex_plugins
28
+ toolu_registry_prune_inactive
29
+
30
+ # Transitional orphan sweep (safe to delete this block once users have migrated,
31
+ # ~v1.7+): the statusline moved to the standalone `statusline` plugin
32
+ # (~/.claude/statusline/statusline.sh, wired by its own SessionStart hook). Runs
33
+ # every session but no-ops once the symlink is gone. Remove the stale symlink
34
+ # toolu used to own at
35
+ # $config/toolu/statusline.sh so an un-migrated settings.json fails loudly
36
+ # (missing file) instead of dangling into a cleaned plugin cache. Only ever
37
+ # removes OUR symlink — a real file a user placed there is left untouched.
38
+ # Deliberately runs BEFORE the `toolu_enabled` opt-out below: a user who
39
+ # disables session-start context should still not be left with a dangling symlink.
40
+ _config_root="$(toolu_config_root)"
41
+ _old_sl="$_config_root/toolu/statusline.sh"
42
+ [ -L "$_old_sl" ] && rm -f "$_old_sl"
43
+
44
+ if ! toolu_enabled hooks session-start; then
45
+ cat > /dev/null 2>&1 || true
46
+ exit 0
47
+ fi
48
+
49
+ PROJECT_ROOT="$(detect_project_root)"
50
+ [ -z "$PROJECT_ROOT" ] && PROJECT_ROOT="$(pwd)"
51
+ PROJECT_NAME="$(detect_project_name)"
52
+ NODE_PM="$(detect_node_pm)"
53
+ HAS_RUST="$(detect_rust)"
54
+ HAS_TS="$(detect_ts)"
55
+ HAS_PYTHON="$(detect_python)"
56
+
57
+ # ── Parse stdin to detect event type ────────────────────────────────────────
58
+ input=$(cat 2>/dev/null || echo "{}")
59
+ event="startup"
60
+ if command -v jq >/dev/null 2>&1; then
61
+ # Claude Code sends the SessionStart event type in `source`
62
+ # (startup | resume | clear | compact); keep legacy fields as fallbacks.
63
+ event=$(jq -r '.source // .session_event // .event // "startup"' <<< "$input" 2>/dev/null || echo "startup")
64
+ fi
65
+ [[ -z "$event" || "$event" == "null" ]] && event="startup"
66
+
67
+ # ── Render the main session doc with project tokens substituted ─────────────
68
+ render_doc() {
69
+ local src="$1"
70
+ [ -f "$src" ] || { echo ""; return 0; }
71
+ local content
72
+ content=$(cat "$src")
73
+ # Substitute placeholders with bash-native replacement — immune to sed
74
+ # metacharacters (|, &, \) in project names or package manager values.
75
+ # Replacement is deliberately UNQUOTED: quoting inside ${} inserts literal
76
+ # quote characters on bash 3.2 (stock macOS). Literal-& safety on bash 5.2+
77
+ # comes from `shopt -u patsub_replacement` at the top of this script.
78
+ local name="${PROJECT_NAME:-this project}"
79
+ local pm="${NODE_PM:-your package manager}"
80
+ content="${content//\{\{project_name\}\}/$name}"
81
+ content="${content//\{\{node_pm\}\}/$pm}"
82
+ # Model-tier placeholders: substituted from the resolved routing table so a
83
+ # user who remaps a tier in toolu.config.json sees THEIR aliases in the
84
+ # injected block, not the built-in defaults.
85
+ content="${content//\{\{model_mechanical\}\}/$MODEL_MECHANICAL}"
86
+ content="${content//\{\{model_exploration\}\}/$MODEL_EXPLORATION}"
87
+ content="${content//\{\{model_implementation\}\}/$MODEL_IMPLEMENTATION}"
88
+ content="${content//\{\{model_review\}\}/$MODEL_REVIEW}"
89
+ content="${content//\{\{model_synthesis\}\}/$MODEL_SYNTHESIS}"
90
+ content="${content//\{\{model_architecture\}\}/$MODEL_ARCHITECTURE}"
91
+ content="${content//\{\{effort_mechanical\}\}/$EFFORT_MECHANICAL}"
92
+ content="${content//\{\{effort_exploration\}\}/$EFFORT_EXPLORATION}"
93
+ content="${content//\{\{effort_implementation\}\}/$EFFORT_IMPLEMENTATION}"
94
+ content="${content//\{\{effort_review\}\}/$EFFORT_REVIEW}"
95
+ content="${content//\{\{effort_synthesis\}\}/$EFFORT_SYNTHESIS}"
96
+ content="${content//\{\{effort_architecture\}\}/$EFFORT_ARCHITECTURE}"
97
+ printf '%s' "$content"
98
+ }
99
+
100
+ # Resolve the model-routing table once per session. Config-driven and stable
101
+ # within a session, so it is safe for the cached SessionStart prefix.
102
+ EFFORT_MECHANICAL="" EFFORT_EXPLORATION="" EFFORT_IMPLEMENTATION=""
103
+ EFFORT_REVIEW="" EFFORT_SYNTHESIS="" EFFORT_ARCHITECTURE=""
104
+ if [ "$(toolu_host)" = codex ]; then
105
+ IFS=$'\t' read -r MODEL_MECHANICAL _effort <<<"$(toolu_codex_model mechanical)"
106
+ EFFORT_MECHANICAL=", effort \`$_effort\`"
107
+ IFS=$'\t' read -r MODEL_EXPLORATION _effort <<<"$(toolu_codex_model exploration)"
108
+ EFFORT_EXPLORATION=", effort \`$_effort\`"
109
+ IFS=$'\t' read -r MODEL_IMPLEMENTATION _effort <<<"$(toolu_codex_model implementation)"
110
+ EFFORT_IMPLEMENTATION=", effort \`$_effort\`"
111
+ IFS=$'\t' read -r MODEL_REVIEW _effort <<<"$(toolu_codex_model review)"
112
+ EFFORT_REVIEW=", effort \`$_effort\`"
113
+ IFS=$'\t' read -r MODEL_SYNTHESIS _effort <<<"$(toolu_codex_model synthesis)"
114
+ EFFORT_SYNTHESIS=", effort \`$_effort\`"
115
+ IFS=$'\t' read -r MODEL_ARCHITECTURE _effort <<<"$(toolu_codex_model architecture)"
116
+ EFFORT_ARCHITECTURE=", effort \`$_effort\`"
117
+ else
118
+ MODEL_MECHANICAL=$(toolu_model mechanical)
119
+ MODEL_EXPLORATION=$(toolu_model exploration)
120
+ MODEL_IMPLEMENTATION=$(toolu_model implementation)
121
+ MODEL_REVIEW=$(toolu_model review)
122
+ MODEL_SYNTHESIS=$(toolu_model synthesis)
123
+ MODEL_ARCHITECTURE=$(toolu_model architecture)
124
+ fi
125
+
126
+ # ── Git context (branch only) ───────────────────────────────────────────────
127
+ # Cache discipline: this string lands in the once-cached SessionStart prefix, so
128
+ # it must depend only on stable inputs. The branch is stable within a session;
129
+ # the working-tree dirty count is NOT — it goes stale immediately and differs on
130
+ # every startup/resume/compact, perturbing the prefix for no lasting value. Live
131
+ # working state is already on the statusline and one `git status` away.
132
+ git_ctx=""
133
+ if command -v git >/dev/null 2>&1; then
134
+ branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
135
+ [[ -n "$branch" ]] && git_ctx="Branch: $branch"
136
+ fi
137
+
138
+ # Note: the failing-quality-gate reminder is deliberately NOT injected here. It
139
+ # is volatile (flips as you work) and already surfaced live by the statusline
140
+ # and per-prompt by user-prompt-submit.sh — re-injecting it into the cached
141
+ # prefix would duplicate context and perturb the cache on every gate flip.
142
+
143
+ # ── Build context based on event type ───────────────────────────────────────
144
+ parts=()
145
+ title=""
146
+
147
+ main_doc=$(render_doc "$HOOK_DIR/docs/session-start.md")
148
+
149
+ case "$event" in
150
+ startup)
151
+ title="Toolu is on!"
152
+ [ -n "$main_doc" ] && parts+=("$main_doc")
153
+ ;;
154
+ resume)
155
+ title="Session resumed"
156
+ [ -n "$main_doc" ] && parts+=("$main_doc")
157
+ ;;
158
+ clear)
159
+ title="Context cleared"
160
+ [ -n "$main_doc" ] && parts+=("$main_doc")
161
+ ;;
162
+ compact)
163
+ title="Context compacted"
164
+ compaction=$(cat "$HOOK_DIR/docs/post-compaction.md" 2>/dev/null || echo "")
165
+ [[ -n "$compaction" ]] && parts+=("$compaction")
166
+ [ -n "$main_doc" ] && parts+=("$main_doc")
167
+ ;;
168
+ esac
169
+
170
+ # Model routing — which tier handles which class of delegated work. Injected on
171
+ # every event (it must survive a compact, since post-compaction delegation is
172
+ # exactly where the agent otherwise falls back to one model for everything).
173
+ # Opt out with `"models": {"enabled": false}`.
174
+ if toolu_enabled models enabled; then
175
+ routing_doc=$(render_doc "$HOOK_DIR/docs/model-routing.md")
176
+ [ -n "$routing_doc" ] && parts+=("$routing_doc")
177
+ fi
178
+
179
+ # Per-toolchain snippets — opt-in via $TOOLU_VERBOSE to save tokens.
180
+ # Default off: the session-start.md core already covers project rules.
181
+ # Set TOOLU_VERBOSE=1 to re-enable per-toolchain blocks. Any value other
182
+ # than "0" / unset / empty enables (so `=true`, `=on`, `=1` all work, but a
183
+ # user who sets `=0` to disable is not surprised).
184
+ if [ "${TOOLU_VERBOSE:-0}" != "0" ]; then
185
+ if [ "$HAS_TS" = "ts" ]; then
186
+ ts_doc=$(render_doc "$HOOK_DIR/docs/session-start-ts.md")
187
+ [ -n "$ts_doc" ] && parts+=("$ts_doc")
188
+ fi
189
+ if [ "$HAS_RUST" = "rust" ]; then
190
+ rust_doc=$(render_doc "$HOOK_DIR/docs/session-start-rust.md")
191
+ [ -n "$rust_doc" ] && parts+=("$rust_doc")
192
+ fi
193
+ if [ "$HAS_PYTHON" = "python" ]; then
194
+ python_doc=$(render_doc "$HOOK_DIR/docs/session-start-python.md")
195
+ [ -n "$python_doc" ] && parts+=("$python_doc")
196
+ fi
197
+ fi
198
+
199
+ # Append project line only when name was detected.
200
+ if [ -n "$PROJECT_NAME" ]; then
201
+ parts+=("Project: $PROJECT_NAME")
202
+ fi
203
+
204
+ # ── Housekeeping: reclaim spent state, and settle host permissions once ─────
205
+ # Both are best-effort and self-silencing; neither may keep a session from
206
+ # starting. The sweeper never speaks unless something went wrong; the
207
+ # permission writer speaks exactly once per repo, ever.
208
+ toolu_sweep_state "$PROJECT_ROOT"
209
+
210
+ _perm_summary=$(toolu_permissions_autowrite "$PROJECT_ROOT" 2>/dev/null || true)
211
+ [ -n "$_perm_summary" ] && parts+=("$_perm_summary")
212
+
213
+ # ── One-time notice: gates no longer prompt ─────────────────────────────────
214
+ # Existing users had push-review and the denylist asking. The default is now
215
+ # advise (quality gate still blocks commit/push), and a behavior change
216
+ # nobody was told about reads as a bug — so say it once per machine, to
217
+ # anyone who has not already made a choice about it.
218
+ _gate_notice_dir="$_config_root/toolu"
219
+ _gate_notice="$_gate_notice_dir/.gate-preset-notice-v6"
220
+ # Skip only when the user already pinned delivery (a preset, or a per-gate
221
+ # mode). A sweep/TTL-only `gates` object is not a delivery choice — those
222
+ # users still need to hear that balanced no longer prompts.
223
+ _gate_delivery_pinned=$(jq -r '
224
+ if (.gates | type) != "object" then "no"
225
+ elif .gates.preset? != null then "yes"
226
+ elif ([.gates | to_entries[] | select((.value | type) == "object" and (.value | has("mode")))] | length) > 0 then "yes"
227
+ else "no"
228
+ end
229
+ ' <<< "$TOOLU_CFG_JSON" 2>/dev/null || echo "no")
230
+ if [ ! -f "$_gate_notice" ] && [ "$_TOOLU_HAS_JQ" = "1" ] \
231
+ && [ "$_gate_delivery_pinned" != "yes" ]; then
232
+ parts+=("toolu gates no longer prompt: the \`balanced\` preset advises on push-review and denylist hits, and the quality gate still blocks only \`git commit\`/\`git push\`. Pin a prompt with \`gates.<name>.mode: ask\`, or the old hard denies with \`{\"gates\":{\"preset\":\"strict\"}}\`.")
233
+ mkdir -p "$_gate_notice_dir" 2>/dev/null && : > "$_gate_notice" 2>/dev/null
234
+ fi
235
+
236
+ # Warn when optional tools referenced by docs/skills are missing — keeps the
237
+ # session start honest about which capabilities are actually available.
238
+ HAS_ASTGREP="$(detect_ast_grep)"
239
+ missing_tools=()
240
+ if [ "$HAS_ASTGREP" != "ast-grep" ] && toolu_enabled skills ast-grep; then
241
+ missing_tools+=("ast-grep (structural code search)")
242
+ fi
243
+ if [ "${#missing_tools[@]}" -gt 0 ]; then
244
+ warn="WARN: optional tools missing — features that depend on them are disabled:"
245
+ for t in "${missing_tools[@]}"; do
246
+ warn+="
247
+ • $t"
248
+ done
249
+ parts+=("$warn")
250
+ fi
251
+
252
+ # ── Mandatory proactive tool use ────────────────────────────────────────────
253
+ # When the comemory / ast-grep / exa-search / context7 plugins are INSTALLED
254
+ # and their underlying tool is available (binary on PATH, or published CLI
255
+ # wrapper), front-load a hard, proactive mandate into session context. The
256
+ # skills are ALWAYS-ACTIVE, but their bodies only load on trigger and the agent
257
+ # tends to wait to be asked — this injection makes the requirement unmissable
258
+ # from turn one. Aggressive by design; the per-skill opt-out (toolu_enabled
259
+ # skills <key> = false) is the escape hatch for a user who wants it off.
260
+ mandates=()
261
+ if [ "$HAS_ASTGREP" = "ast-grep" ] && toolu_enabled skills ast-grep && toolu_plugin_active ast-grep@toolu; then
262
+ mandates+=("ast-grep (structural search) — for ANY search by code shape (signatures, call sites, impls, trait/interface usage, patterns) you MUST reach for \`ast-grep run --pattern …\` FIRST. Grep/ripgrep/sed are a FALLBACK ONLY — use them for plain-text literals in non-code files, or when a query genuinely cannot be expressed structurally. Never reach for them first on code.")
263
+ fi
264
+ # exa-search / context7 publish their CLI wrappers at a stable config-dir
265
+ # path via their own SessionStart hooks — there is no binary on PATH to
266
+ # detect, so the wrapper file itself is the presence signal. The symlink
267
+ # persists across sessions; only the very first session of a fresh install
268
+ # can race the publishing hook, and the mandate simply appears one session
269
+ # later. exa-search additionally gates on EXA_API_KEY: the wrapper hard-fails
270
+ # without it, and mandating a broken tool is worse than staying quiet.
271
+ exa_wrapper="$_config_root/exa-search/search.sh"
272
+ if [ -n "${EXA_API_KEY:-}" ] && [ -x "$exa_wrapper" ] && toolu_enabled skills exa-search && toolu_plugin_active exa-search@toolu; then
273
+ mandates+=("exa-search (web search) — for ANY web search, code-example hunt, URL crawl, or topic research you MUST reach for \`\"$exa_wrapper\"\` FIRST (commands: search / crawl / similar — see the exa-search skill). Native web tools are a FALLBACK ONLY — use them when the wrapper errors or a URL needs your logged-in session.")
274
+ fi
275
+ ctx7_wrapper="$_config_root/context7/search.sh"
276
+ if [ -x "$ctx7_wrapper" ] && toolu_enabled skills context7 && toolu_plugin_active context7@toolu; then
277
+ mandates+=("context7 (library docs) — for ANY third-party library/framework question (API usage, current docs, code examples, version behavior) you MUST query \`\"$ctx7_wrapper\"\` FIRST (\`search <library>\` to resolve the ID, then \`docs <id> <query>\`) BEFORE answering from memory or searching the web. Web search is a FALLBACK ONLY when context7 lacks coverage.")
278
+ fi
279
+ if [ "${#mandates[@]}" -gt 0 ]; then
280
+ mwarn="MANDATORY — proactive plugin use (installed + available; NOT optional, do NOT ask permission):"
281
+ for m in "${mandates[@]}"; do
282
+ mwarn+="
283
+ • $m"
284
+ done
285
+ mwarn+="
286
+ • Propagation: these mandates bind EVERY agent in this session, not just the main thread. When you delegate via Task/subagents (nested ones too), carry the same requirement into the subagent's prompt — recall/save via comemory, ast-grep first with Grep/sed as fallback. Delegation never exempts the work."
287
+ parts+=("$mwarn")
288
+ fi
289
+
290
+ # Verify required plugin dependencies declared in plugin.json `dependencies`
291
+ # (Claude Code's official schema: array of "name" strings or
292
+ # {name, marketplace?, version?} objects). Auto-install only fires when the
293
+ # dep's marketplace is already configured; we surface the exact
294
+ # `/plugin install …` command so the user can fix in one paste.
295
+ #
296
+ # Scope: the check only fires when we can locate the toolu plugin manifest
297
+ # (via CLAUDE_PLUGIN_ROOT, set by Claude Code when this hook runs from the
298
+ # installed plugin, or via the in-repo path when working inside the toolu
299
+ # checkout itself). Outside both — e.g., a hook run by another project that
300
+ # inherits these helpers — the block silently no-ops. Intentional: repo-A
301
+ # should not WARN about plugin deps declared in repo-B's manifest.
302
+ plugin_manifest=""
303
+ plugin_root=$(toolu_plugin_root)
304
+ if [ "$(toolu_host)" = codex ] && [ -n "$plugin_root" ] && [ -f "$plugin_root/.codex-plugin/plugin.json" ]; then
305
+ plugin_manifest="$plugin_root/.codex-plugin/plugin.json"
306
+ elif [ -n "$plugin_root" ] && [ -f "$plugin_root/.claude-plugin/plugin.json" ]; then
307
+ plugin_manifest="$plugin_root/.claude-plugin/plugin.json"
308
+ elif [ -f "$PROJECT_ROOT/plugins/toolu/.claude-plugin/plugin.json" ]; then
309
+ plugin_manifest="$PROJECT_ROOT/plugins/toolu/.claude-plugin/plugin.json"
310
+ fi
311
+ if [[ -n "$plugin_manifest" && -f "$plugin_manifest" ]] && command -v jq >/dev/null 2>&1; then
312
+ dependency_manifest="$plugin_manifest"
313
+ # Codex's published manifest schema has no dependency field. The Claude
314
+ # manifest remains the shared dependency declaration inside each dual-host
315
+ # plugin, so Codex reads it when its native manifest has no dependencies.
316
+ if [ "$(toolu_host)" = codex ] && ! jq -e '.dependencies | type == "array"' \
317
+ "$dependency_manifest" >/dev/null 2>&1 && \
318
+ [ -f "$plugin_root/.claude-plugin/plugin.json" ]; then
319
+ dependency_manifest="$plugin_root/.claude-plugin/plugin.json"
320
+ fi
321
+ missing_plugins=()
322
+ indeterminate=0
323
+ while IFS= read -r req_spec; do
324
+ [ -z "$req_spec" ] && continue
325
+ installed=$(detect_plugin_installed "$req_spec")
326
+ rc=$?
327
+ if [ "$rc" -eq 2 ]; then
328
+ # Registry/jq unavailable on this box — suppress all WARNs in this
329
+ # block so we don't spam every required plugin as "missing" on a
330
+ # machine where the registry was moved or jq was uninstalled.
331
+ indeterminate=1
332
+ break
333
+ fi
334
+ if [ -z "$installed" ]; then
335
+ missing_plugins+=("$(toolu_plugin_install_command "$req_spec")")
336
+ fi
337
+ done < <(jq -r '
338
+ (.dependencies // [])[]
339
+ | if type == "string" then .
340
+ elif (type == "object" and (.name | type) == "string")
341
+ then (if .marketplace then "\(.name)@\(.marketplace)" else .name end)
342
+ else empty end
343
+ ' "$dependency_manifest" 2>/dev/null)
344
+ if [ "$indeterminate" -eq 0 ] && [ "${#missing_plugins[@]}" -gt 0 ]; then
345
+ pwarn="WARN: required plugins missing — dependent workflows will fail. Install:"
346
+ for cmd in "${missing_plugins[@]}"; do
347
+ pwarn+="
348
+ • $cmd"
349
+ done
350
+ parts+=("$pwarn")
351
+ fi
352
+ fi
353
+
354
+ # Append git context (branch only — see the cache-discipline note above).
355
+ [[ -n "$git_ctx" ]] && parts+=("$git_ctx")
356
+
357
+ # ── Join and output ─────────────────────────────────────────────────────────
358
+ full_context=""
359
+ for part in "${parts[@]}"; do
360
+ if [[ -z "$full_context" ]]; then
361
+ full_context="$part"
362
+ else
363
+ full_context="$full_context
364
+
365
+ $part"
366
+ fi
367
+ done
368
+
369
+ if command -v jq >/dev/null 2>&1; then
370
+ jq -n --arg ctx "$full_context" --arg title "$title" '{
371
+ "hookSpecificOutput": {
372
+ "hookEventName": "SessionStart",
373
+ "additionalContext": $ctx
374
+ },
375
+ "systemMessage": $title
376
+ }'
377
+ fi
378
+
379
+ exit 0
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env bash
2
+ # UserPromptSubmit hook
3
+ # Validates prompts, injects optional per-project context, git context, intent hints.
4
+ # Project-agnostic: no project literals. Per-project hints opt in via the
5
+ # host-native project directory's context.sh.
6
+
7
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
8
+
9
+ # shellcheck source=lib/detect.sh
10
+ . "$HOOK_DIR/lib/detect.sh"
11
+ # shellcheck source=lib/config.sh
12
+ . "$HOOK_DIR/lib/config.sh"
13
+
14
+ if ! toolu_enabled hooks user-prompt-submit; then
15
+ cat > /dev/null 2>&1 || true
16
+ exit 0
17
+ fi
18
+
19
+ input=$(cat)
20
+ prompt=""
21
+ if command -v jq >/dev/null 2>&1; then
22
+ prompt=$(jq -r '.prompt // ""' <<< "$input" 2>/dev/null || echo "")
23
+ fi
24
+
25
+ [[ -z "$prompt" ]] && exit 0
26
+
27
+ PROJECT_ROOT="$(detect_project_root)"
28
+ [ -z "$PROJECT_ROOT" ] && PROJECT_ROOT="$(pwd)"
29
+ GATE_FILE="$(toolu_project_state_root "$PROJECT_ROOT")/quality-gate-status.json"
30
+ prompt_lower=$(printf '%s' "$prompt" | tr '[:upper:]' '[:lower:]')
31
+
32
+ # ── Skip trivial prompts (confirmations, short replies) ───────────────────────
33
+ if [[ "$prompt_lower" =~ ^(y|n|yes|no|ok|sure|thanks|thank\ you|go\ ahead|looks\ good|lgtm|correct|exactly|right|done|nah|nope|yep|yup|continue)[\.\!\?]?$ ]]; then
34
+ exit 0
35
+ fi
36
+
37
+ # ── Skip slash commands — skills handle their own context ─────────────────────
38
+ if [[ "$prompt_lower" =~ ^/ ]]; then
39
+ exit 0
40
+ fi
41
+
42
+ # ── Block vague one-word prompts ──────────────────────────────────────────────
43
+ if [[ "$prompt_lower" =~ ^(fix|help|debug|check|look|see|run|do|try)[[:space:]]*$ ]]; then
44
+ if command -v jq >/dev/null 2>&1; then
45
+ jq -n '{"decision": "block", "reason": "Prompt too vague - specify what file/feature/error needs attention"}'
46
+ fi
47
+ exit 0
48
+ fi
49
+
50
+ # Word-boundary helpers — bash extended regex has no `\b`. WB/WE wrap each
51
+ # alternation so e.g. `impl` does NOT match `implement` and `move` does NOT
52
+ # match `remove`. Also dropped overly short tokens that no boundary trick
53
+ # can rescue: `impl` (vs implement), `ast` (vs fast/past/last), `drop`
54
+ # (vs dropdown). Inflected forms (tested, fixing) won't match — users
55
+ # typically write the base verb in a directive prompt, and silence is
56
+ # safer than the wrong hint.
57
+ WB='(^|[^a-z])'
58
+ WE='([^a-z]|$)'
59
+
60
+ # ── Quality gate warning (not block) ─────────────────────────────────────────
61
+ quality_gate_hint=""
62
+ if [[ -f "$GATE_FILE" ]] && command -v jq >/dev/null 2>&1; then
63
+ gate_json=$(cat "$GATE_FILE" 2>/dev/null)
64
+ gate_status=$(jq -r '.status // ""' <<< "$gate_json" 2>/dev/null)
65
+ if [[ "$gate_status" == "failing" ]]; then
66
+ # Word-boundary wrapped: `prefix` must not suppress via the `fix` substring.
67
+ if ! [[ "$prompt_lower" =~ ${WB}(fix|resolve|error|warning|test|lint|check|type)${WE} ]]; then
68
+ reason=$(jq -r '.reason // "Unknown quality failure"' <<< "$gate_json" 2>/dev/null)
69
+ quality_gate_hint="Quality gate failing: $reason. Prefer fixing before unrelated work."
70
+ fi
71
+ fi
72
+ fi
73
+
74
+ # ── Build context parts ──────────────────────────────────────────────────────
75
+ # Token-budget rule: every part below must be opt-in via prompt content.
76
+ # We do NOT re-inject SessionStart material (branch and gates)
77
+ # on every prompt — that duplicates context already in the session.
78
+
79
+ HAS_ASTGREP="$(detect_ast_grep)"
80
+ if ! toolu_enabled skills ast-grep; then
81
+ HAS_ASTGREP=""
82
+ fi
83
+
84
+
85
+ # 2. Intent hint — at most ONE. Most-specific pattern wins.
86
+ intent=""
87
+ if [[ "$prompt_lower" =~ ${WB}(pattern|struct|trait|interface|all\ functions|all\ methods|every\ function|every\ method|syntax|code\ structure|signature|return\ type|where\ clause|lifetime|closure|macro|decorator|annotation)${WE} ]]; then
88
+ if [ "$HAS_ASTGREP" = "ast-grep" ]; then
89
+ intent="Structural pattern: use \`ast-grep run --pattern\` (not Grep)."
90
+ else
91
+ intent="WARN: ast-grep not installed — install via brew/cargo for structural matching."
92
+ fi
93
+ elif [[ "$prompt_lower" =~ ${WB}(rename|move|extract|split)${WE} ]]; then
94
+ intent="Rename: find all refs (ast-grep + Grep on configs) before rewriting."
95
+ elif [[ "$prompt_lower" =~ ${WB}(test|spec|coverage)${WE} ]]; then
96
+ intent="Tests: real-world data only, NO mocks."
97
+ elif [[ "$prompt_lower" =~ ${WB}(fix|debug|error|bug|issue)${WE} ]]; then
98
+ intent="Fix in code. Never suppress with disable comments."
99
+ elif [[ "$prompt_lower" =~ ${WB}(delete|remove|clean\ up)${WE} ]]; then
100
+ intent="Verify no deps before removing."
101
+ elif [[ "$prompt_lower" =~ ${WB}(review|audit)${WE} ]]; then
102
+ intent="Review: forbidden syntax, quality gates, test coverage."
103
+ fi
104
+
105
+ # 2b. Scale nudge — independent of the single intent hint above (a task can be
106
+ # both a "fix" AND large). Fires only on words that indicate real SCALE, and it
107
+ # suggests considering a split rather than instructing a fan-out.
108
+ #
109
+ # The trigger list used to include `refactor`, `audit` and `across`, which are
110
+ # everyday words: "refactor this function", "audit this helper" and "rename it
111
+ # across two files" are all single-thread work, and each one was being told to
112
+ # delegate and parallelize. Spawning agents for small tasks is slower than doing
113
+ # them, so a nudge that fires on ordinary phrasing costs time rather than saving
114
+ # it. What remains names a scope no single sweep covers.
115
+ #
116
+ # WB/WE-wrapped like the other hints to avoid substring false positives.
117
+ orchestrate=""
118
+ if [[ "$prompt_lower" =~ ${WB}(migrate|codebase-wide|throughout|end-to-end)${WE} ]]; then
119
+ orchestrate="Possibly large task — if it splits into genuinely independent units, consider decomposing it; if it is really one thread of work, just do it. The orchestrator skill has the test for which."
120
+ fi
121
+
122
+ # 2c. Brainstorm nudge — independent of intent and scale. These terms signal a
123
+ # design conversation, but the nudge remains advisory: bounded or mechanical
124
+ # requests should continue directly without launching a workflow.
125
+ brainstorm=""
126
+ if [[ "$prompt_lower" =~ ${WB}(brainstorms?|designs?|scopes?|approach(es)?|architectures?|trade-?offs?|redesigns?|overhauls?)${WE} ]] ||
127
+ [[ "$prompt_lower" =~ ${WB}new[[:space:]]+(feature|workflow|system)${WE} ]]; then
128
+ brainstorm="Scope may be unresolved — consider the \`brainstorm\` skill for material design choices; skip it when the request is already bounded or mechanical."
129
+ fi
130
+
131
+ # 2d. Research nudge — independent of the single intent hint (a prompt can be
132
+ # both a "fix" AND need external lookup). Fires on EXTERNAL-knowledge signals
133
+ # (live docs, latest releases, third-party APIs) so the work is delegated to the
134
+ # research-agent subagent — it isolates the token cost and routes
135
+ # exa-search/context7 with a native fallback. Deliberately tight and
136
+ # external-leaning: local-code questions should not be routed to web research.
137
+ # WB/WE-wrapped. Gated by the
138
+ # `agents.research-agent` toggle (default on).
139
+ research=""
140
+ if toolu_enabled agents research-agent &&
141
+ [[ "$prompt_lower" =~ ${WB}(latest|docs\ for|library\ docs|api\ reference|api\ docs|changelog|release\ notes|best\ practices?|look\ up|search\ the\ web|web\ search|how\ to\ use)${WE} ]]; then
142
+ research="External research — delegate to the research-agent subagent (routes exa-search/context7, native fallback) to keep main context lean."
143
+ fi
144
+
145
+ # 2e. Jira discoverability nudge — independent of the single intent hint. Names
146
+ # the jira skill and warns off the Atlassian MCP so the skill wins
147
+ # tool-selection when the user mentions Jira (today the model often reaches for
148
+ # the Atlassian MCP instead). Fires on the words jira/atlassian, an
149
+ # *.atlassian.net/browse/<KEY> link, or an issue key (ABC-123) GATED by a
150
+ # jira-context word so bare key-shaped tokens (UTF-8, GPT-4, ISO-8601) don't
151
+ # trigger it. The URL and issue-key matches use the ORIGINAL $prompt: the key is
152
+ # case-sensitive ([A-Z]) and $prompt_lower has already been lowercased.
153
+ jira_nudge=""
154
+ if [[ "$prompt_lower" =~ ${WB}(jira|atlassian)${WE} ]] ||
155
+ [[ "$prompt" =~ atlassian[.]net/browse/[A-Z][A-Z0-9]+-[0-9]+ ]] ||
156
+ { [[ "$prompt" =~ [A-Z][A-Z0-9]+-[0-9]+ ]] &&
157
+ [[ "$prompt_lower" =~ ${WB}(ticket|issue|board|sprint|epic|backlog)${WE} ]]; }; then
158
+ jira_nudge="Jira mentioned — use the \`jira\` skill (REST wrapper over jira.sh), NOT the Atlassian MCP."
159
+ fi
160
+
161
+ # 3. Per-project context hook — opt-in. Project may emit any string.
162
+ project_ctx=""
163
+ PROJECT_CONTEXT="$PROJECT_ROOT/$(toolu_project_dirname)/context.sh"
164
+ if [ -n "$PROJECT_ROOT" ] && [ -f "$PROJECT_CONTEXT" ]; then
165
+ # shellcheck disable=SC1091 # path is project-specific; sourced only if present
166
+ project_ctx=$(PROMPT="$prompt" bash "$PROJECT_CONTEXT" 2>/dev/null || true)
167
+ fi
168
+
169
+ # ── Combine and output ───────────────────────────────────────────────────────
170
+ parts=()
171
+ [[ -n "$intent" ]] && parts+=("$intent")
172
+ [[ -n "$orchestrate" ]] && parts+=("$orchestrate")
173
+ [[ -n "$brainstorm" ]] && parts+=("$brainstorm")
174
+ [[ -n "$research" ]] && parts+=("$research")
175
+ [[ -n "$jira_nudge" ]] && parts+=("$jira_nudge")
176
+ [[ -n "$project_ctx" ]] && parts+=("$project_ctx")
177
+ [[ -n "$quality_gate_hint" ]] && parts+=("$quality_gate_hint")
178
+
179
+ # Join parts with " | "
180
+ context=""
181
+ if [[ ${#parts[@]} -gt 0 ]]; then
182
+ context="${parts[0]}"
183
+ for part in "${parts[@]:1}"; do
184
+ context="$context | $part"
185
+ done
186
+ fi
187
+
188
+ if command -v jq >/dev/null 2>&1; then
189
+ jq -n --arg ctx "$context" '{
190
+ "hookSpecificOutput": {
191
+ "hookEventName": "UserPromptSubmit",
192
+ "additionalContext": $ctx
193
+ }
194
+ }'
195
+ fi
196
+
197
+ exit 0