@specship/specship-win32-arm64 0.24.0 → 0.28.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 (341) hide show
  1. package/bin/specship.cmd +31 -1
  2. package/lib/dist/activation/cheatsheet.d.ts +17 -1
  3. package/lib/dist/activation/cheatsheet.d.ts.map +1 -1
  4. package/lib/dist/activation/cheatsheet.js +47 -3
  5. package/lib/dist/activation/cheatsheet.js.map +1 -1
  6. package/lib/dist/activation/hook-output.d.ts +80 -0
  7. package/lib/dist/activation/hook-output.d.ts.map +1 -0
  8. package/lib/dist/activation/hook-output.js +103 -0
  9. package/lib/dist/activation/hook-output.js.map +1 -0
  10. package/lib/dist/activation/search-intercept.d.ts +60 -29
  11. package/lib/dist/activation/search-intercept.d.ts.map +1 -1
  12. package/lib/dist/activation/search-intercept.js +159 -55
  13. package/lib/dist/activation/search-intercept.js.map +1 -1
  14. package/lib/dist/activation/steering.d.ts +11 -13
  15. package/lib/dist/activation/steering.d.ts.map +1 -1
  16. package/lib/dist/activation/steering.js +25 -10
  17. package/lib/dist/activation/steering.js.map +1 -1
  18. package/lib/dist/behaviour/behaviour-surface.d.ts +47 -0
  19. package/lib/dist/behaviour/behaviour-surface.d.ts.map +1 -1
  20. package/lib/dist/behaviour/behaviour-surface.js +108 -2
  21. package/lib/dist/behaviour/behaviour-surface.js.map +1 -1
  22. package/lib/dist/bin/node-version-check.d.ts +22 -7
  23. package/lib/dist/bin/node-version-check.d.ts.map +1 -1
  24. package/lib/dist/bin/node-version-check.js +39 -9
  25. package/lib/dist/bin/node-version-check.js.map +1 -1
  26. package/lib/dist/bin/specship.js +989 -116
  27. package/lib/dist/bin/specship.js.map +1 -1
  28. package/lib/dist/commands/specship/learn.md +27 -3
  29. package/lib/dist/commands/specship/spec.md +85 -15
  30. package/lib/dist/db/spec-queries.d.ts +73 -3
  31. package/lib/dist/db/spec-queries.d.ts.map +1 -1
  32. package/lib/dist/db/spec-queries.js +139 -5
  33. package/lib/dist/db/spec-queries.js.map +1 -1
  34. package/lib/dist/db/sqlite-adapter.d.ts +7 -5
  35. package/lib/dist/db/sqlite-adapter.d.ts.map +1 -1
  36. package/lib/dist/db/sqlite-adapter.js +11 -9
  37. package/lib/dist/db/sqlite-adapter.js.map +1 -1
  38. package/lib/dist/enforce/changed-files.d.ts +39 -0
  39. package/lib/dist/enforce/changed-files.d.ts.map +1 -0
  40. package/lib/dist/enforce/changed-files.js +114 -0
  41. package/lib/dist/enforce/changed-files.js.map +1 -0
  42. package/lib/dist/enforce/enforce.d.ts +53 -2
  43. package/lib/dist/enforce/enforce.d.ts.map +1 -1
  44. package/lib/dist/enforce/enforce.js +95 -8
  45. package/lib/dist/enforce/enforce.js.map +1 -1
  46. package/lib/dist/enforce/mutation.d.ts +46 -0
  47. package/lib/dist/enforce/mutation.d.ts.map +1 -0
  48. package/lib/dist/enforce/mutation.js +104 -0
  49. package/lib/dist/enforce/mutation.js.map +1 -0
  50. package/lib/dist/enforce/suppressions.d.ts +33 -0
  51. package/lib/dist/enforce/suppressions.d.ts.map +1 -0
  52. package/lib/dist/enforce/suppressions.js +159 -0
  53. package/lib/dist/enforce/suppressions.js.map +1 -0
  54. package/lib/dist/extraction/grammars.d.ts +10 -0
  55. package/lib/dist/extraction/grammars.d.ts.map +1 -1
  56. package/lib/dist/extraction/grammars.js +32 -3
  57. package/lib/dist/extraction/grammars.js.map +1 -1
  58. package/lib/dist/extraction/index.d.ts +7 -0
  59. package/lib/dist/extraction/index.d.ts.map +1 -1
  60. package/lib/dist/extraction/index.js.map +1 -1
  61. package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts +7 -0
  62. package/lib/dist/extraction/specs/markdown-spec-extractor.d.ts.map +1 -1
  63. package/lib/dist/extraction/specs/markdown-spec-extractor.js +97 -10
  64. package/lib/dist/extraction/specs/markdown-spec-extractor.js.map +1 -1
  65. package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm.gz +0 -0
  66. package/lib/dist/extraction/wasm/tree-sitter-scala.wasm.gz +0 -0
  67. package/lib/dist/graph/spec-coverage.d.ts +142 -0
  68. package/lib/dist/graph/spec-coverage.d.ts.map +1 -0
  69. package/lib/dist/graph/spec-coverage.js +276 -0
  70. package/lib/dist/graph/spec-coverage.js.map +1 -0
  71. package/lib/dist/health/smoke-check.js +2 -2
  72. package/lib/dist/health/smoke-check.js.map +1 -1
  73. package/lib/dist/index.d.ts +75 -7
  74. package/lib/dist/index.d.ts.map +1 -1
  75. package/lib/dist/index.js +191 -23
  76. package/lib/dist/index.js.map +1 -1
  77. package/lib/dist/installer/gemini-commands.d.ts +107 -0
  78. package/lib/dist/installer/gemini-commands.d.ts.map +1 -0
  79. package/lib/dist/installer/gemini-commands.js +264 -0
  80. package/lib/dist/installer/gemini-commands.js.map +1 -0
  81. package/lib/dist/installer/index.d.ts +55 -8
  82. package/lib/dist/installer/index.d.ts.map +1 -1
  83. package/lib/dist/installer/index.js +72 -53
  84. package/lib/dist/installer/index.js.map +1 -1
  85. package/lib/dist/installer/init-offer.d.ts +14 -9
  86. package/lib/dist/installer/init-offer.d.ts.map +1 -1
  87. package/lib/dist/installer/init-offer.js +13 -9
  88. package/lib/dist/installer/init-offer.js.map +1 -1
  89. package/lib/dist/installer/install-defaults.d.ts +81 -0
  90. package/lib/dist/installer/install-defaults.d.ts.map +1 -0
  91. package/lib/dist/installer/install-defaults.js +95 -0
  92. package/lib/dist/installer/install-defaults.js.map +1 -0
  93. package/lib/dist/installer/instructions-template.d.ts +22 -0
  94. package/lib/dist/installer/instructions-template.d.ts.map +1 -1
  95. package/lib/dist/installer/instructions-template.js +37 -1
  96. package/lib/dist/installer/instructions-template.js.map +1 -1
  97. package/lib/dist/installer/targets/claude.d.ts +18 -0
  98. package/lib/dist/installer/targets/claude.d.ts.map +1 -1
  99. package/lib/dist/installer/targets/claude.js +70 -23
  100. package/lib/dist/installer/targets/claude.js.map +1 -1
  101. package/lib/dist/installer/targets/gemini.d.ts +85 -12
  102. package/lib/dist/installer/targets/gemini.d.ts.map +1 -1
  103. package/lib/dist/installer/targets/gemini.js +527 -30
  104. package/lib/dist/installer/targets/gemini.js.map +1 -1
  105. package/lib/dist/installer/targets/shared.d.ts +13 -0
  106. package/lib/dist/installer/targets/shared.d.ts.map +1 -1
  107. package/lib/dist/installer/targets/shared.js +22 -4
  108. package/lib/dist/installer/targets/shared.js.map +1 -1
  109. package/lib/dist/installer/targets/types.d.ts +12 -0
  110. package/lib/dist/installer/targets/types.d.ts.map +1 -1
  111. package/lib/dist/jira/auto-publish.d.ts +71 -4
  112. package/lib/dist/jira/auto-publish.d.ts.map +1 -1
  113. package/lib/dist/jira/auto-publish.js +186 -17
  114. package/lib/dist/jira/auto-publish.js.map +1 -1
  115. package/lib/dist/jira/reconcile.d.ts +24 -1
  116. package/lib/dist/jira/reconcile.d.ts.map +1 -1
  117. package/lib/dist/jira/reconcile.js +46 -2
  118. package/lib/dist/jira/reconcile.js.map +1 -1
  119. package/lib/dist/jira/repo-config.d.ts +9 -0
  120. package/lib/dist/jira/repo-config.d.ts.map +1 -1
  121. package/lib/dist/jira/repo-config.js +11 -0
  122. package/lib/dist/jira/repo-config.js.map +1 -1
  123. package/lib/dist/jira/types.d.ts +7 -0
  124. package/lib/dist/jira/types.d.ts.map +1 -1
  125. package/lib/dist/jira/types.js.map +1 -1
  126. package/lib/dist/knowledge/enforce.d.ts +75 -0
  127. package/lib/dist/knowledge/enforce.d.ts.map +1 -0
  128. package/lib/dist/knowledge/enforce.js +250 -0
  129. package/lib/dist/knowledge/enforce.js.map +1 -0
  130. package/lib/dist/knowledge/facts.d.ts +33 -0
  131. package/lib/dist/knowledge/facts.d.ts.map +1 -0
  132. package/lib/dist/knowledge/facts.js +117 -0
  133. package/lib/dist/knowledge/facts.js.map +1 -0
  134. package/lib/dist/knowledge/proposals.d.ts +88 -0
  135. package/lib/dist/knowledge/proposals.d.ts.map +1 -0
  136. package/lib/dist/knowledge/proposals.js +360 -0
  137. package/lib/dist/knowledge/proposals.js.map +1 -0
  138. package/lib/dist/knowledge/stale.d.ts +64 -0
  139. package/lib/dist/knowledge/stale.d.ts.map +1 -0
  140. package/lib/dist/knowledge/stale.js +174 -0
  141. package/lib/dist/knowledge/stale.js.map +1 -0
  142. package/lib/dist/mcp/fitness-tool.d.ts +5 -6
  143. package/lib/dist/mcp/fitness-tool.d.ts.map +1 -1
  144. package/lib/dist/mcp/fitness-tool.js +0 -16
  145. package/lib/dist/mcp/fitness-tool.js.map +1 -1
  146. package/lib/dist/mcp/gemini-session.d.ts +50 -0
  147. package/lib/dist/mcp/gemini-session.d.ts.map +1 -0
  148. package/lib/dist/mcp/gemini-session.js +159 -0
  149. package/lib/dist/mcp/gemini-session.js.map +1 -0
  150. package/lib/dist/mcp/health-tool.d.ts +26 -0
  151. package/lib/dist/mcp/health-tool.d.ts.map +1 -0
  152. package/lib/dist/mcp/health-tool.js +50 -0
  153. package/lib/dist/mcp/health-tool.js.map +1 -0
  154. package/lib/dist/mcp/jira-tools.d.ts.map +1 -1
  155. package/lib/dist/mcp/jira-tools.js +25 -0
  156. package/lib/dist/mcp/jira-tools.js.map +1 -1
  157. package/lib/dist/mcp/maintainability-tool.d.ts +5 -7
  158. package/lib/dist/mcp/maintainability-tool.d.ts.map +1 -1
  159. package/lib/dist/mcp/maintainability-tool.js +0 -16
  160. package/lib/dist/mcp/maintainability-tool.js.map +1 -1
  161. package/lib/dist/mcp/model-context.d.ts +31 -7
  162. package/lib/dist/mcp/model-context.d.ts.map +1 -1
  163. package/lib/dist/mcp/model-context.js +70 -21
  164. package/lib/dist/mcp/model-context.js.map +1 -1
  165. package/lib/dist/mcp/proxy.d.ts.map +1 -1
  166. package/lib/dist/mcp/proxy.js +5 -2
  167. package/lib/dist/mcp/proxy.js.map +1 -1
  168. package/lib/dist/mcp/server-instructions.d.ts +16 -15
  169. package/lib/dist/mcp/server-instructions.d.ts.map +1 -1
  170. package/lib/dist/mcp/server-instructions.js +72 -5
  171. package/lib/dist/mcp/server-instructions.js.map +1 -1
  172. package/lib/dist/mcp/session.js +1 -1
  173. package/lib/dist/mcp/session.js.map +1 -1
  174. package/lib/dist/mcp/spec-tools.d.ts +9 -1
  175. package/lib/dist/mcp/spec-tools.d.ts.map +1 -1
  176. package/lib/dist/mcp/spec-tools.js +282 -49
  177. package/lib/dist/mcp/spec-tools.js.map +1 -1
  178. package/lib/dist/mcp/tools.d.ts +49 -9
  179. package/lib/dist/mcp/tools.d.ts.map +1 -1
  180. package/lib/dist/mcp/tools.js +210 -58
  181. package/lib/dist/mcp/tools.js.map +1 -1
  182. package/lib/dist/pulse/pulse.d.ts +93 -0
  183. package/lib/dist/pulse/pulse.d.ts.map +1 -0
  184. package/lib/dist/pulse/pulse.js +401 -0
  185. package/lib/dist/pulse/pulse.js.map +1 -0
  186. package/lib/dist/reflect/apply.d.ts.map +1 -1
  187. package/lib/dist/reflect/apply.js +38 -3
  188. package/lib/dist/reflect/apply.js.map +1 -1
  189. package/lib/dist/reflect/auto-sweep.d.ts +26 -0
  190. package/lib/dist/reflect/auto-sweep.d.ts.map +1 -0
  191. package/lib/dist/reflect/auto-sweep.js +90 -0
  192. package/lib/dist/reflect/auto-sweep.js.map +1 -0
  193. package/lib/dist/reflect/cli.d.ts +62 -0
  194. package/lib/dist/reflect/cli.d.ts.map +1 -0
  195. package/lib/dist/reflect/cli.js +163 -0
  196. package/lib/dist/reflect/cli.js.map +1 -0
  197. package/lib/dist/reflect/miner.d.ts.map +1 -1
  198. package/lib/dist/reflect/miner.js +119 -0
  199. package/lib/dist/reflect/miner.js.map +1 -1
  200. package/lib/dist/reflect/sweep.d.ts +1 -0
  201. package/lib/dist/reflect/sweep.d.ts.map +1 -1
  202. package/lib/dist/reflect/sweep.js +62 -13
  203. package/lib/dist/reflect/sweep.js.map +1 -1
  204. package/lib/dist/reflect/targets.d.ts +2 -0
  205. package/lib/dist/reflect/targets.d.ts.map +1 -1
  206. package/lib/dist/reflect/targets.js +33 -0
  207. package/lib/dist/reflect/targets.js.map +1 -1
  208. package/lib/dist/reflect/types.d.ts +19 -2
  209. package/lib/dist/reflect/types.d.ts.map +1 -1
  210. package/lib/dist/resolution/spec-link-resolver.d.ts +44 -2
  211. package/lib/dist/resolution/spec-link-resolver.d.ts.map +1 -1
  212. package/lib/dist/resolution/spec-link-resolver.js +132 -0
  213. package/lib/dist/resolution/spec-link-resolver.js.map +1 -1
  214. package/lib/dist/resolution/test-title-links.d.ts +62 -0
  215. package/lib/dist/resolution/test-title-links.d.ts.map +1 -0
  216. package/lib/dist/resolution/test-title-links.js +97 -0
  217. package/lib/dist/resolution/test-title-links.js.map +1 -0
  218. package/lib/dist/search/query-parser.d.ts.map +1 -1
  219. package/lib/dist/search/query-parser.js +6 -1
  220. package/lib/dist/search/query-parser.js.map +1 -1
  221. package/lib/dist/server/project-registry.js +1 -1
  222. package/lib/dist/server/routes/claude.js +24 -43
  223. package/lib/dist/server/routes/graph.js +53 -7
  224. package/lib/dist/server/routes/knowledge.js +118 -0
  225. package/lib/dist/server/routes/mcp.js +4 -3
  226. package/lib/dist/server/routes/projects.js +4 -4
  227. package/lib/dist/server/routes/spec.js +49 -7
  228. package/lib/dist/server/server.js +31 -17
  229. package/lib/dist/skills/spec-author/SKILL.md +164 -0
  230. package/lib/dist/skills/spec-author/references/format.md +210 -0
  231. package/lib/dist/skills/spec-author/references/gap-questions.md +152 -0
  232. package/lib/dist/skills/spec-author/references/quality-rubric.md +125 -0
  233. package/lib/dist/skills/spec-author/references/review-checklist.md +175 -0
  234. package/lib/dist/spec-lint/lint.d.ts +82 -0
  235. package/lib/dist/spec-lint/lint.d.ts.map +1 -0
  236. package/lib/dist/spec-lint/lint.js +491 -0
  237. package/lib/dist/spec-lint/lint.js.map +1 -0
  238. package/lib/dist/spec-quality/criterion-ids.d.ts +13 -0
  239. package/lib/dist/spec-quality/criterion-ids.d.ts.map +1 -0
  240. package/lib/dist/spec-quality/criterion-ids.js +38 -0
  241. package/lib/dist/spec-quality/criterion-ids.js.map +1 -0
  242. package/lib/dist/spec-quality/evidence-kind.d.ts +54 -0
  243. package/lib/dist/spec-quality/evidence-kind.d.ts.map +1 -0
  244. package/lib/dist/spec-quality/evidence-kind.js +183 -0
  245. package/lib/dist/spec-quality/evidence-kind.js.map +1 -0
  246. package/lib/dist/spec-quality/link-persist.d.ts +65 -0
  247. package/lib/dist/spec-quality/link-persist.d.ts.map +1 -0
  248. package/lib/dist/spec-quality/link-persist.js +249 -0
  249. package/lib/dist/spec-quality/link-persist.js.map +1 -0
  250. package/lib/dist/spec-quality/plan-scope.d.ts +38 -0
  251. package/lib/dist/spec-quality/plan-scope.d.ts.map +1 -0
  252. package/lib/dist/spec-quality/plan-scope.js +129 -0
  253. package/lib/dist/spec-quality/plan-scope.js.map +1 -0
  254. package/lib/dist/spec-quality/postcheck.d.ts +85 -0
  255. package/lib/dist/spec-quality/postcheck.d.ts.map +1 -0
  256. package/lib/dist/spec-quality/postcheck.js +390 -0
  257. package/lib/dist/spec-quality/postcheck.js.map +1 -0
  258. package/lib/dist/spec-quality/readiness.d.ts +72 -0
  259. package/lib/dist/spec-quality/readiness.d.ts.map +1 -0
  260. package/lib/dist/spec-quality/readiness.js +226 -0
  261. package/lib/dist/spec-quality/readiness.js.map +1 -0
  262. package/lib/dist/statusline/index.js.map +1 -1
  263. package/lib/dist/statusline/render.d.ts +1 -1
  264. package/lib/dist/statusline/render.d.ts.map +1 -1
  265. package/lib/dist/statusline/render.js +3 -1
  266. package/lib/dist/statusline/render.js.map +1 -1
  267. package/lib/dist/ui/assets/index-BzuUWUAE.js +50 -0
  268. package/lib/dist/ui/index.html +1 -1
  269. package/lib/dist/verify/report-ingest.d.ts +88 -0
  270. package/lib/dist/verify/report-ingest.d.ts.map +1 -0
  271. package/lib/dist/verify/report-ingest.js +269 -0
  272. package/lib/dist/verify/report-ingest.js.map +1 -0
  273. package/lib/dist/workflows/agents/claude.d.ts +16 -0
  274. package/lib/dist/workflows/agents/claude.d.ts.map +1 -0
  275. package/lib/dist/workflows/agents/claude.js +82 -0
  276. package/lib/dist/workflows/agents/claude.js.map +1 -0
  277. package/lib/dist/workflows/agents/gemini.d.ts +21 -0
  278. package/lib/dist/workflows/agents/gemini.d.ts.map +1 -0
  279. package/lib/dist/workflows/agents/gemini.js +124 -0
  280. package/lib/dist/workflows/agents/gemini.js.map +1 -0
  281. package/lib/dist/workflows/agents/index.d.ts +28 -0
  282. package/lib/dist/workflows/agents/index.d.ts.map +1 -0
  283. package/lib/dist/workflows/agents/index.js +104 -0
  284. package/lib/dist/workflows/agents/index.js.map +1 -0
  285. package/lib/dist/workflows/agents/types.d.ts +55 -0
  286. package/lib/dist/workflows/agents/types.d.ts.map +1 -0
  287. package/lib/dist/workflows/agents/types.js +46 -0
  288. package/lib/dist/workflows/agents/types.js.map +1 -0
  289. package/lib/dist/workflows/batch.d.ts +63 -0
  290. package/lib/dist/workflows/batch.d.ts.map +1 -0
  291. package/lib/dist/workflows/batch.js +151 -0
  292. package/lib/dist/workflows/batch.js.map +1 -0
  293. package/lib/dist/workflows/defaults/claude-design-implement.yaml +3 -4
  294. package/lib/dist/workflows/defaults/spec-author.yaml +11 -9
  295. package/lib/dist/workflows/defaults/spec-fix.yaml +103 -12
  296. package/lib/dist/workflows/defaults/spec-implement-mixed.yaml +21 -193
  297. package/lib/dist/workflows/defaults/spec-implement.yaml +701 -41
  298. package/lib/dist/workflows/defaults/spec-verify.yaml +60 -9
  299. package/lib/dist/workflows/discovery.d.ts +13 -0
  300. package/lib/dist/workflows/discovery.d.ts.map +1 -1
  301. package/lib/dist/workflows/discovery.js +79 -17
  302. package/lib/dist/workflows/discovery.js.map +1 -1
  303. package/lib/dist/workflows/executor.d.ts +18 -0
  304. package/lib/dist/workflows/executor.d.ts.map +1 -1
  305. package/lib/dist/workflows/executor.js +130 -33
  306. package/lib/dist/workflows/executor.js.map +1 -1
  307. package/lib/dist/workflows/gc.d.ts +50 -0
  308. package/lib/dist/workflows/gc.d.ts.map +1 -0
  309. package/lib/dist/workflows/gc.js +120 -0
  310. package/lib/dist/workflows/gc.js.map +1 -0
  311. package/lib/dist/workflows/merge-queue.d.ts +64 -0
  312. package/lib/dist/workflows/merge-queue.d.ts.map +1 -0
  313. package/lib/dist/workflows/merge-queue.js +162 -0
  314. package/lib/dist/workflows/merge-queue.js.map +1 -0
  315. package/lib/dist/workflows/runners/approval.d.ts +1 -1
  316. package/lib/dist/workflows/runners/approval.d.ts.map +1 -1
  317. package/lib/dist/workflows/runners/approval.js +11 -2
  318. package/lib/dist/workflows/runners/approval.js.map +1 -1
  319. package/lib/dist/workflows/runners/bash.d.ts +15 -0
  320. package/lib/dist/workflows/runners/bash.d.ts.map +1 -1
  321. package/lib/dist/workflows/runners/bash.js +50 -1
  322. package/lib/dist/workflows/runners/bash.js.map +1 -1
  323. package/lib/dist/workflows/runners/prompt.d.ts +16 -41
  324. package/lib/dist/workflows/runners/prompt.d.ts.map +1 -1
  325. package/lib/dist/workflows/runners/prompt.js +55 -120
  326. package/lib/dist/workflows/runners/prompt.js.map +1 -1
  327. package/lib/dist/workflows/runners/types.d.ts +12 -0
  328. package/lib/dist/workflows/runners/types.d.ts.map +1 -1
  329. package/lib/dist/workflows/schemas/workflow.d.ts +17 -0
  330. package/lib/dist/workflows/schemas/workflow.d.ts.map +1 -1
  331. package/lib/dist/workflows/schemas/workflow.js +27 -0
  332. package/lib/dist/workflows/schemas/workflow.js.map +1 -1
  333. package/lib/node_modules/.package-lock.json +1 -1
  334. package/lib/package.json +8 -4
  335. package/package.json +5 -3
  336. package/lib/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  337. package/lib/dist/extraction/wasm/tree-sitter-scala.wasm +0 -0
  338. package/lib/dist/server/ingest/impact-query.js +0 -343
  339. package/lib/dist/server/routes/events.js +0 -152
  340. package/lib/dist/ui/assets/index-CjjaftDw.js +0 -50
  341. package/node.exe +0 -4
package/bin/specship.cmd CHANGED
@@ -1 +1,31 @@
1
- @"%~dp0..\node.exe" --liftoff-only "%~dp0..\lib\dist\bin\specship.js" %*
1
+ @echo off
2
+ setlocal
3
+ set "NODE="
4
+ for /f "delims=" %%i in ('where node 2^>nul') do if not defined NODE set "NODE=%%i"
5
+ if not defined NODE (
6
+ echo specship: Node.js is required ^(^>^= 22.5.0, ^< 25.0.0^) but no 'node' was found on PATH.>&2
7
+ echo specship: install it from https://nodejs.org>&2
8
+ exit /b 1
9
+ )
10
+ set "NODEVER="
11
+ rem Query the version through PATH (`node`, not "%NODE%") so a Program Files
12
+ rem path with spaces doesn't hit cmd's nested-quote parsing — `where node`
13
+ rem already proved PATH resolves to this same executable.
14
+ for /f "delims=" %%v in ('node -v 2^>nul') do set "NODEVER=%%v"
15
+ set "NMAJ="
16
+ set "NMIN="
17
+ for /f "tokens=1,2 delims=v." %%a in ("%NODEVER%") do (
18
+ set "NMAJ=%%a"
19
+ set "NMIN=%%b"
20
+ )
21
+ if not defined NMAJ goto :badnode
22
+ if %NMAJ% LSS 22 goto :badnode
23
+ if %NMAJ% GEQ 25 goto :badnode
24
+ if %NMAJ% EQU 22 if %NMIN% LSS 5 goto :badnode
25
+ rem --liftoff-only: avoid the V8 turboshaft WASM Zone OOM (issues #293/#298).
26
+ "%NODE%" --liftoff-only "%~dp0..\lib\dist\bin\specship.js" %*
27
+ exit /b %ERRORLEVEL%
28
+ :badnode
29
+ echo specship: Node.js ^>^= 22.5.0 and ^< 25.0.0 is required (found %NODEVER% at %NODE%).>&2
30
+ echo specship: install a supported version from https://nodejs.org>&2
31
+ exit /b 1
@@ -27,7 +27,23 @@ export declare const CHEATSHEET_TEXT: string;
27
27
  * out via `SPECSHIP_NO_CHEATSHEET` (REQ-CHEAT-004.A1). An unset or empty
28
28
  * variable prints normally (REQ-CHEAT-004.A2).
29
29
  */
30
- export declare function buildCheatsheetPayload(env?: NodeJS.ProcessEnv): {
30
+ export declare function buildCheatsheetPayload(env?: NodeJS.ProcessEnv, extras?: CheatsheetExtras): {
31
31
  systemMessage: string;
32
32
  } | null;
33
+ /** Optional learned-state extras the CLI resolves before rendering. */
34
+ export interface CheatsheetExtras {
35
+ /** Raw content of .specship/conventions.md (null/empty → no section). */
36
+ conventions?: string | null;
37
+ /** Count of open reflection proposals (0/undefined → no line). */
38
+ openProposals?: number;
39
+ }
40
+ /** Max convention bullets injected per session (REQ-ALEARN-002.A2). */
41
+ export declare const CONVENTIONS_BUDGET_LINES = 8;
42
+ /**
43
+ * Render the confirmed conventions as a budget-bounded cheatsheet section
44
+ * (REQ-ALEARN-002). Rules are the `## <title>` lines of the SpecShip-written
45
+ * marker blocks plus their first body line; over budget, the rest collapse
46
+ * into a "+N more" pointer. No blocks → empty string (nothing injected).
47
+ */
48
+ export declare function renderConventionsSection(content: string, budget: number): string;
33
49
  //# sourceMappingURL=cheatsheet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cheatsheet.d.ts","sourceRoot":"","sources":["../../src/activation/cheatsheet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,QAqBhB,CAAC;AAEb;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAGlC"}
1
+ {"version":3,"file":"cheatsheet.d.ts","sourceRoot":"","sources":["../../src/activation/cheatsheet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,QAqBhB,CAAC;AAEb;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,MAAM,GAAE,gBAAqB,GAC5B;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAkBlC;AAED,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,uEAAuE;AACvE,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAmBhF"}
@@ -15,8 +15,9 @@
15
15
  * and the `SPECSHIP_NO_CHEATSHEET` opt-out below, not by this module.
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.CHEATSHEET_TEXT = void 0;
18
+ exports.CONVENTIONS_BUDGET_LINES = exports.CHEATSHEET_TEXT = void 0;
19
19
  exports.buildCheatsheetPayload = buildCheatsheetPayload;
20
+ exports.renderConventionsSection = renderConventionsSection;
20
21
  /**
21
22
  * The cheat-sheet rendered to the user. Names only user-facing commands, tools,
22
23
  * and env vars — no internal file paths, symbol names, or benchmark figures
@@ -52,9 +53,52 @@ exports.CHEATSHEET_TEXT = [
52
53
  * out via `SPECSHIP_NO_CHEATSHEET` (REQ-CHEAT-004.A1). An unset or empty
53
54
  * variable prints normally (REQ-CHEAT-004.A2).
54
55
  */
55
- function buildCheatsheetPayload(env = process.env) {
56
+ function buildCheatsheetPayload(env = process.env, extras = {}) {
56
57
  if (env.SPECSHIP_NO_CHEATSHEET)
57
58
  return null;
58
- return { systemMessage: exports.CHEATSHEET_TEXT };
59
+ const parts = [exports.CHEATSHEET_TEXT];
60
+ // Learned project conventions (REQ-ALEARN-002): absent/empty file → output
61
+ // is byte-identical to the base cheatsheet (A1).
62
+ if (extras.conventions) {
63
+ const section = renderConventionsSection(extras.conventions, exports.CONVENTIONS_BUDGET_LINES);
64
+ if (section)
65
+ parts.push('', section);
66
+ }
67
+ // Open learned proposals awaiting review (REQ-ALEARN-004.A2): one line,
68
+ // only when the count is positive.
69
+ if (extras.openProposals && extras.openProposals > 0) {
70
+ parts.push('', `🧠 ${extras.openProposals} learned proposal(s) awaiting review — run \`specship reflect\` to review them.`);
71
+ }
72
+ return { systemMessage: parts.join('\n') };
73
+ }
74
+ /** Max convention bullets injected per session (REQ-ALEARN-002.A2). */
75
+ exports.CONVENTIONS_BUDGET_LINES = 8;
76
+ /**
77
+ * Render the confirmed conventions as a budget-bounded cheatsheet section
78
+ * (REQ-ALEARN-002). Rules are the `## <title>` lines of the SpecShip-written
79
+ * marker blocks plus their first body line; over budget, the rest collapse
80
+ * into a "+N more" pointer. No blocks → empty string (nothing injected).
81
+ */
82
+ function renderConventionsSection(content, budget) {
83
+ const rules = [];
84
+ const re = /<!-- SPECSHIP_LEARNING:[^>]+ -->\n## ([^\n]+)\n+([^\n<]*)/g;
85
+ let m;
86
+ while ((m = re.exec(content)) !== null) {
87
+ const title = (m[1] ?? '').replace(/^Convention:\s*/i, '').trim();
88
+ const body = (m[2] ?? '').trim();
89
+ // The body line is the rule; the title is a summary of it. Prefer the
90
+ // body (the user's own words), fall back to the title.
91
+ rules.push(body || title);
92
+ }
93
+ if (rules.length === 0)
94
+ return '';
95
+ const shown = rules.slice(0, budget);
96
+ const lines = ['Project conventions (learned — .specship/conventions.md):'];
97
+ for (const r of shown)
98
+ lines.push(`• ${r.length > 100 ? r.slice(0, 97) + '…' : r}`);
99
+ if (rules.length > shown.length) {
100
+ lines.push(` +${rules.length - shown.length} more — see .specship/conventions.md`);
101
+ }
102
+ return lines.join('\n');
59
103
  }
60
104
  //# sourceMappingURL=cheatsheet.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cheatsheet.js","sourceRoot":"","sources":["../../src/activation/cheatsheet.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAsCH,wDAKC;AAzCD;;;;;GAKG;AACU,QAAA,eAAe,GAAG;IAC7B,2EAA2E;IAC3E,EAAE;IACF,kCAAkC;IAClC,+EAA+E;IAC/E,sFAAsF;IACtF,+EAA+E;IAC/E,qFAAqF;IACrF,EAAE;IACF,+BAA+B;IAC/B,mFAAmF;IACnF,mFAAmF;IACnF,8EAA8E;IAC9E,EAAE;IACF,+EAA+E;IAC/E,kFAAkF;IAClF,kFAAkF;IAClF,sDAAsD;IACtD,uFAAuF;IACvF,EAAE;IACF,oFAAoF;CACrF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;;;;GAMG;AACH,SAAgB,sBAAsB,CACpC,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,GAAG,CAAC,sBAAsB;QAAE,OAAO,IAAI,CAAC;IAC5C,OAAO,EAAE,aAAa,EAAE,uBAAe,EAAE,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"cheatsheet.js","sourceRoot":"","sources":["../../src/activation/cheatsheet.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Shared helpers for hook commands (`steer-nudge`, `spec-nudge`,
3
+ * `search-intercept`) that read a hook payload from stdin and emit a
4
+ * `hookSpecificOutput.additionalContext` line.
5
+ *
6
+ * The `hook_event_name` field is echoed back from the payload rather than
7
+ * hard-coded so the same CLI command serves Claude Code's event names
8
+ * (`UserPromptSubmit`, `PreToolUse`) AND Gemini CLI's (`BeforeAgent`,
9
+ * `BeforeTool`) — the installer wires them to the same binaries and the
10
+ * payload tells the command which host called it (REQ-GEMINI-010).
11
+ */
12
+ export interface HookPayload {
13
+ prompt: string;
14
+ cwd: string | null;
15
+ sessionId: string | null;
16
+ transcriptPath: string | null;
17
+ hookEventName: string | null;
18
+ }
19
+ export declare function parseHookPayload(raw: string): HookPayload;
20
+ /**
21
+ * Build a `hookSpecificOutput.additionalContext` JSON line. `eventName` is
22
+ * echoed from the payload when present (so Gemini's `BeforeAgent` /
23
+ * `BeforeTool` names round-trip) and falls back to `fallbackEventName` only
24
+ * when the payload carries none.
25
+ */
26
+ export declare function buildHookAdditionalContext(additionalContext: string, eventName: string | null, fallbackEventName: string): string;
27
+ /**
28
+ * A blocking decision for a host whose before-tool hook reads a TOP-LEVEL
29
+ * `decision` (Gemini CLI 0.28.0 `BeforeToolHookOutput.isBlockingDecision`
30
+ * checks `this.decision === 'block' || 'deny'`; nested under
31
+ * `hookSpecificOutput` it is ignored). Only emitted for hosts whose traits
32
+ * report `supportsBeforeToolDeny`.
33
+ */
34
+ export declare function buildHookDenyDecision(reason: string): string;
35
+ /**
36
+ * Host-shaped traits derived from a hook payload — the one place that peeks
37
+ * at `hook_event_name` to decide whether we're serving Claude Code or Gemini
38
+ * CLI. Shared code (`search-intercept`, `bin/specship`) consumes the traits
39
+ * and never event-name-sniffs itself, keeping it host-agnostic per the
40
+ * project's agent-agnostic rule.
41
+ *
42
+ * - `specshipToolMarkers` — substrings naming a specship tool_use record.
43
+ * Claude exposes our server as `mcp__specship__<tool>`; Gemini shows
44
+ * the model the bare tool name (`specship_explore`), qualifying it as
45
+ * `specship__<tool>` only on a name collision.
46
+ * - `transcriptFormat` — Claude writes JSONL (one record per line); Gemini's
47
+ * `ChatRecordingService` writes one pretty-printed JSON doc with
48
+ * `messages[].toolCalls[].name`.
49
+ * - `supportsBeforeToolDeny` — whether the host reads a top-level
50
+ * `{decision:'deny'}` on its BeforeTool-equivalent. Gemini 0.28.0+
51
+ * `BeforeToolHookOutput.isBlockingDecision` does; Claude's PreToolUse
52
+ * uses a different shape so we never emit deny for Claude.
53
+ */
54
+ export interface HostTraits {
55
+ specshipToolMarkers: string[];
56
+ transcriptFormat: 'jsonl' | 'single-json';
57
+ supportsBeforeToolDeny: boolean;
58
+ /**
59
+ * Prefix the host puts in front of a SpecShip tool name when the model
60
+ * calls it (AGENTADAPT-DOC, REQ-AGENTADAPT-001): agent-facing text renders
61
+ * `${toolPrefix}specship_explore` so each host sees its own tool name.
62
+ */
63
+ toolPrefix: string;
64
+ /** Event names to answer with when the payload names none (REQ-AGENTADAPT-002). */
65
+ fallbackPromptEvent: string;
66
+ fallbackToolEvent: string;
67
+ /** The host's own built-in search tools a strict interceptor may redirect. */
68
+ denyEligibleTools: readonly string[];
69
+ /** The host's shell tool, whose `command` the interceptor classifies. */
70
+ shellTool: string | null;
71
+ }
72
+ /**
73
+ * Claude Code — also the legacy default for payloads that name no event, so
74
+ * every pre-adapter install keeps its exact behaviour (REQ-AGENTADAPT-002.A2).
75
+ */
76
+ export declare const CLAUDE_CODE_TRAITS: HostTraits;
77
+ /** Gemini CLI (verified against 0.28.0). */
78
+ export declare const GEMINI_CLI_TRAITS: HostTraits;
79
+ export declare function deriveHostTraits(payload: HookPayload): HostTraits;
80
+ //# sourceMappingURL=hook-output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-output.d.ts","sourceRoot":"","sources":["../../src/activation/hook-output.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CA4BzD;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,iBAAiB,EAAE,MAAM,GACxB,MAAM,CAMR;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,UAAU;IACzB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gBAAgB,EAAE,OAAO,GAAG,aAAa,CAAC;IAC1C,sBAAsB,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,yEAAyE;IACzE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,UAShC,CAAC;AAEF,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,EAAE,UAS/B,CAAC;AAIF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAKjE"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * Shared helpers for hook commands (`steer-nudge`, `spec-nudge`,
4
+ * `search-intercept`) that read a hook payload from stdin and emit a
5
+ * `hookSpecificOutput.additionalContext` line.
6
+ *
7
+ * The `hook_event_name` field is echoed back from the payload rather than
8
+ * hard-coded so the same CLI command serves Claude Code's event names
9
+ * (`UserPromptSubmit`, `PreToolUse`) AND Gemini CLI's (`BeforeAgent`,
10
+ * `BeforeTool`) — the installer wires them to the same binaries and the
11
+ * payload tells the command which host called it (REQ-GEMINI-010).
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.GEMINI_CLI_TRAITS = exports.CLAUDE_CODE_TRAITS = void 0;
15
+ exports.parseHookPayload = parseHookPayload;
16
+ exports.buildHookAdditionalContext = buildHookAdditionalContext;
17
+ exports.buildHookDenyDecision = buildHookDenyDecision;
18
+ exports.deriveHostTraits = deriveHostTraits;
19
+ function parseHookPayload(raw) {
20
+ const empty = {
21
+ prompt: '',
22
+ cwd: null,
23
+ sessionId: null,
24
+ transcriptPath: null,
25
+ hookEventName: null,
26
+ };
27
+ const trimmed = raw.trim();
28
+ if (!trimmed)
29
+ return empty;
30
+ let parsed;
31
+ try {
32
+ parsed = JSON.parse(trimmed);
33
+ }
34
+ catch {
35
+ // Not JSON — treat the raw text as the prompt body.
36
+ return { ...empty, prompt: trimmed };
37
+ }
38
+ if (!parsed || typeof parsed !== 'object')
39
+ return empty;
40
+ const p = parsed;
41
+ const str = (v) => typeof v === 'string' && v.length > 0 ? v : null;
42
+ return {
43
+ prompt: typeof p.prompt === 'string' ? p.prompt : '',
44
+ cwd: str(p.cwd),
45
+ sessionId: str(p.session_id),
46
+ transcriptPath: str(p.transcript_path),
47
+ hookEventName: str(p.hook_event_name),
48
+ };
49
+ }
50
+ /**
51
+ * Build a `hookSpecificOutput.additionalContext` JSON line. `eventName` is
52
+ * echoed from the payload when present (so Gemini's `BeforeAgent` /
53
+ * `BeforeTool` names round-trip) and falls back to `fallbackEventName` only
54
+ * when the payload carries none.
55
+ */
56
+ function buildHookAdditionalContext(additionalContext, eventName, fallbackEventName) {
57
+ const hookEventName = eventName ?? fallbackEventName;
58
+ return (JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext } }) +
59
+ '\n');
60
+ }
61
+ /**
62
+ * A blocking decision for a host whose before-tool hook reads a TOP-LEVEL
63
+ * `decision` (Gemini CLI 0.28.0 `BeforeToolHookOutput.isBlockingDecision`
64
+ * checks `this.decision === 'block' || 'deny'`; nested under
65
+ * `hookSpecificOutput` it is ignored). Only emitted for hosts whose traits
66
+ * report `supportsBeforeToolDeny`.
67
+ */
68
+ function buildHookDenyDecision(reason) {
69
+ return JSON.stringify({ decision: 'deny', reason }) + '\n';
70
+ }
71
+ /**
72
+ * Claude Code — also the legacy default for payloads that name no event, so
73
+ * every pre-adapter install keeps its exact behaviour (REQ-AGENTADAPT-002.A2).
74
+ */
75
+ exports.CLAUDE_CODE_TRAITS = {
76
+ specshipToolMarkers: ['"name":"mcp__specship__'],
77
+ transcriptFormat: 'jsonl',
78
+ supportsBeforeToolDeny: false,
79
+ toolPrefix: 'mcp__specship__',
80
+ fallbackPromptEvent: 'UserPromptSubmit',
81
+ fallbackToolEvent: 'PreToolUse',
82
+ denyEligibleTools: [],
83
+ shellTool: null,
84
+ };
85
+ /** Gemini CLI (verified against 0.28.0). */
86
+ exports.GEMINI_CLI_TRAITS = {
87
+ specshipToolMarkers: ['"name":"specship_', '"name": "specship_'],
88
+ transcriptFormat: 'single-json',
89
+ supportsBeforeToolDeny: true,
90
+ toolPrefix: '',
91
+ fallbackPromptEvent: 'BeforeAgent',
92
+ fallbackToolEvent: 'BeforeTool',
93
+ denyEligibleTools: ['grep_search', 'glob'],
94
+ shellTool: 'run_shell_command',
95
+ };
96
+ const GEMINI_EVENTS = new Set(['BeforeAgent', 'BeforeTool', 'AfterTool']);
97
+ function deriveHostTraits(payload) {
98
+ if (payload.hookEventName && GEMINI_EVENTS.has(payload.hookEventName)) {
99
+ return exports.GEMINI_CLI_TRAITS;
100
+ }
101
+ return exports.CLAUDE_CODE_TRAITS;
102
+ }
103
+ //# sourceMappingURL=hook-output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-output.js","sourceRoot":"","sources":["../../src/activation/hook-output.ts"],"names":[],"mappings":"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"}
@@ -1,23 +1,31 @@
1
1
  /**
2
- * Point-of-use search interceptor (STEER-HOOK-DOC, REQ-STEER-004/005).
2
+ * Point-of-use search interceptor (STEER-HOOK-DOC, REQ-STEER-004/005,
3
+ * REQ-GEMINI-011).
3
4
  *
4
5
  * The prompt-level nudge (steering.ts) fires once, at prompt submit, and its
5
6
  * pull decays across a long turn — the observed failure is a search-shaped
6
7
  * tool call made many tool-calls after the nudge. This module is the decision
7
- * logic for the `PreToolUse` hook the installer writes on search tools
8
- * (`specship search-intercept`): deliver the same guidance at the moment of
9
- * the wrong choice.
8
+ * logic for the hook the installer writes on each host's search tools
9
+ * (Claude: PreToolUse on Grep/Glob; Gemini: BeforeTool on grep_search / glob /
10
+ * run_shell_command): deliver the same guidance at the moment of the wrong
11
+ * choice.
10
12
  *
11
- * Two properties are non-negotiable:
13
+ * Two modes exist:
12
14
  *
13
- * - ADVISORY (REQ-STEER-004): the emitted text is `additionalContext`,
14
- * never a permission decision. A search the index cannot answer is a
15
- * legitimate search; a wrong denial costs the agent a turn — the very
16
- * failure this feature exists to reduce.
17
- * - SELF-SILENCING (REQ-STEER-005): at most one line per session, and only
18
- * when the session has made zero specship tool calls. Firing on every
19
- * search would make the hook noise, and noise is what taught the agent to
20
- * ignore the prompt-level nudge.
15
+ * - ADVISORY (default, both hosts, REQ-STEER-004/005): emit a one-line
16
+ * `additionalContext` redirect. At most once per session, silent once the
17
+ * session has used specship. The search still runs.
18
+ * - STRICT (Gemini-only opt-in, REQ-GEMINI-011): for a built-in search tool
19
+ * (`grep_search`, `glob`) in an indexed project, emit a top-level
20
+ * `{decision:'deny'}` with a reason naming `specship_explore`. A
21
+ * search-shaped `run_shell_command` NEVER denies — Gemini's hook runs
22
+ * after the user's shell confirmation, so denying it would make the user
23
+ * approve a command and then see it blocked. Parse-failure / unindexed
24
+ * project / already-using-specship → ALLOW.
25
+ *
26
+ * Shared code stays agent-agnostic: this file never sniffs `hook_event_name`
27
+ * or tool-name prefixes. The caller (bin/specship) hands in host traits
28
+ * derived in the hook I/O adapter (`activation/hook-output.ts`).
21
29
  */
22
30
  /**
23
31
  * The one redirect line. Explicitly advisory ("your search still runs") so
@@ -26,10 +34,19 @@
26
34
  export declare const SEARCH_INTERCEPT_TEXT: string;
27
35
  /** Per-session tracking file, kept inside the project's own `.specship/`. */
28
36
  export declare function interceptStatePath(projectRoot: string): string;
37
+ /**
38
+ * Classify a shell command string for intercept purposes. We ONLY look at the
39
+ * first pipeline segment: `npm test | grep FAIL` uses grep as a FILTER on
40
+ * another command's output and must not be nudged, while `rg "renderScene"
41
+ * src/` is the lookup we care about. Read-shaped programs (`cat`, `head`,
42
+ * `tail`, `sed -n`, …) are never classified `search` — reading a config or
43
+ * log is legitimate.
44
+ */
45
+ export declare function classifyShellCommand(cmd: string): 'search' | 'other';
29
46
  export interface SearchInterceptInput {
30
47
  /** Project the intercepted call belongs to (the hook payload's `cwd`). */
31
48
  cwd: string;
32
- /** Claude Code session id from the hook payload; null → untrackable, stay silent. */
49
+ /** Host session id from the hook payload; null → untrackable, stay silent. */
33
50
  sessionId: string | null;
34
51
  /** Session transcript path from the hook payload, when present. */
35
52
  transcriptPath: string | null;
@@ -37,23 +54,37 @@ export interface SearchInterceptInput {
37
54
  homedir?: string;
38
55
  /** Injectable clock for tests. */
39
56
  now?: Date;
57
+ /** Transcript substrings signaling specship tool_use. Default: Claude form. */
58
+ specshipToolMarkers?: string[];
59
+ /** Transcript file format. Default: Claude JSONL. */
60
+ transcriptFormat?: 'jsonl' | 'single-json';
61
+ /** Strict mode (host supports a BeforeTool deny AND user opted in). */
62
+ strict?: boolean;
63
+ /** Whether the intercepted tool is a built-in search eligible for deny. */
64
+ denyEligible?: boolean;
65
+ /** When the intercepted tool is a shell runner, the raw command string. */
66
+ shellCommand?: string | null;
67
+ /** Search pattern/needle to echo in the deny reason, when known. */
68
+ searchPattern?: string | null;
40
69
  }
70
+ export type SearchInterceptResult = {
71
+ additionalContext: string;
72
+ } | {
73
+ decision: 'deny';
74
+ reason: string;
75
+ } | null;
41
76
  /**
42
- * Decide whether to emit the redirect line for one intercepted search call.
43
- *
44
- * Silence gates, in order (all REQ-STEER-004.A3 / REQ-STEER-005):
45
- * 1. `SPECSHIP_NO_STEERING=1` (env > project > install settings) — same
46
- * opt-out as the prompt nudge.
47
- * 2. No `.specship/` directory at cwd — uninitialized projects get zero
48
- * noise.
49
- * 3. No session id — nothing to key the once-per-session guarantee on, so
50
- * silence beats repeat-firing.
51
- * 4. The session is already recorded — it fired before, or was seen using
52
- * specship.
53
- * 5. The transcript shows specship use — the agent doesn't need redirecting;
54
- * record the session so later calls skip the scan.
77
+ * Decide what to emit for one intercepted search call.
55
78
  *
56
- * Otherwise: record the session and return the line (fires exactly once).
79
+ * Order:
80
+ * 1. `SPECSHIP_NO_STEERING=1` → ALLOW (silent).
81
+ * 2. No `.specship/` at cwd → ALLOW.
82
+ * 3. Shell runner: classify; `other` → ALLOW; `search` → advisory path
83
+ * only (never deny, REQ-GEMINI-011.A3).
84
+ * 4. Strict + denyEligible: parse transcript; `used` or `parse-failure` or
85
+ * no transcript path → ALLOW; `not-used` → DENY with reason.
86
+ * 5. Otherwise advisory: once-per-session, silent in sessions already using
87
+ * specship (REQ-STEER-005).
57
88
  */
58
- export declare function buildSearchIntercept(input: SearchInterceptInput): string | null;
89
+ export declare function buildSearchIntercept(input: SearchInterceptInput): SearchInterceptResult;
59
90
  //# sourceMappingURL=search-intercept.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-intercept.d.ts","sourceRoot":"","sources":["../../src/activation/search-intercept.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAIX,CAAC;AAExB,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9D;AA8ED,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mEAAmE;IACnE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,GAAG,IAAI,CAwB/E"}
1
+ {"version":3,"file":"search-intercept.d.ts","sourceRoot":"","sources":["../../src/activation/search-intercept.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAOH;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAIX,CAAC;AAExB,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9D;AA0GD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAUpE;AAqBD,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mEAAmE;IACnE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,GAAG,CAAC,EAAE,IAAI,CAAC;IAEX,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;IAC3C,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,iBAAiB,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpC,IAAI,CAAC;AAYT;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB,GAAG,qBAAqB,CA4DvF"}