heyspex 0.1.0 → 0.2.1

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 (352) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/README.md +21 -0
  3. package/bin/hs.mjs +1089 -104
  4. package/bin/spexcode.mjs +262 -0
  5. package/dev-factory/engine/worktree.mjs +740 -0
  6. package/dev-factory/lib/code-repo-root.mjs +67 -0
  7. package/dev-factory/lib/context-budget.mjs +84 -0
  8. package/dev-factory/lib/epic-detect.mjs +190 -0
  9. package/dev-factory/lib/fleet-queue-file.mjs +846 -0
  10. package/dev-factory/lib/fleet-queue.mjs +426 -0
  11. package/dev-factory/lib/fleet-state.mjs +14 -18
  12. package/dev-factory/lib/model-catalog.mjs +338 -0
  13. package/dev-factory/lib/seat-context.mjs +199 -0
  14. package/dev-factory/lib/spec-io.mjs +93 -0
  15. package/dev-factory/lib/sufficiency-score.mjs +8 -2
  16. package/dev-factory/lib/worktree-allocator.mjs +744 -0
  17. package/dev-factory/model-catalog.json +222 -0
  18. package/dist/cli/hs-check.mjs +1127 -103
  19. package/dist/cli/hs-claim.mjs +981 -63
  20. package/dist/cli/hs-connect.mjs +10059 -1729
  21. package/dist/cli/hs-defer.mjs +5507 -3072
  22. package/dist/cli/hs-dev.mjs +576 -0
  23. package/dist/cli/hs-env.mjs +416 -302
  24. package/dist/cli/hs-index-rebuild.mjs +7334 -0
  25. package/dist/cli/hs-index-reconcile.mjs +8138 -0
  26. package/dist/cli/hs-init.mjs +2840 -149
  27. package/dist/cli/hs-install.mjs +7214 -594
  28. package/dist/cli/hs-join.mjs +10640 -2421
  29. package/dist/cli/hs-vault-pull.mjs +8157 -6370
  30. package/dist/cli/hs-vault-push.mjs +6433 -0
  31. package/dist/cli/hs-vault.mjs +4029 -1555
  32. package/dist/cli/hs-workspace.mjs +2702 -538
  33. package/dist/cli-templates/customer-vault/specs/entity-model.md +594 -601
  34. package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +110 -177
  35. package/dist/cli-templates/customer-vault/specs/protocols/destructive-action-checklist.md +122 -0
  36. package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +13 -15
  37. package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +91 -0
  38. package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +150 -168
  39. package/dist/cli-templates/customer-vault/specs/protocols/spec-summary-authoring.md +83 -0
  40. package/dist/cli-templates/harness-commands/claude/hs-auto.md +36 -0
  41. package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -0
  42. package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -0
  43. package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -0
  44. package/dist/cli-templates/harness-commands/claude/hs-dev.md +20 -0
  45. package/dist/cli-templates/harness-commands/claude/hs-doctor.md +32 -0
  46. package/dist/cli-templates/harness-commands/claude/hs-ground.md +30 -0
  47. package/dist/cli-templates/harness-commands/claude/hs-handoff.md +33 -0
  48. package/dist/cli-templates/harness-commands/claude/hs-log.md +27 -0
  49. package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -0
  50. package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -0
  51. package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -0
  52. package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -0
  53. package/dist/cli-templates/harness-commands/claude/hs-res.md +63 -0
  54. package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -0
  55. package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -0
  56. package/dist/cli-templates/harness-commands/claude/hs-view.md +28 -0
  57. package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -0
  58. package/dist/cli-templates/harness-commands/claude/prototype.md +91 -0
  59. package/dist/cli-templates/harness-commands/cursor/hs-auto.md +40 -0
  60. package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +31 -0
  61. package/dist/cli-templates/harness-commands/cursor/hs-capture.md +65 -0
  62. package/dist/cli-templates/harness-commands/cursor/hs-defer.md +32 -0
  63. package/dist/cli-templates/harness-commands/cursor/hs-dev.md +24 -0
  64. package/dist/cli-templates/harness-commands/cursor/hs-doctor.md +36 -0
  65. package/dist/cli-templates/harness-commands/cursor/hs-ground.md +33 -0
  66. package/dist/cli-templates/harness-commands/cursor/hs-handoff.md +37 -0
  67. package/dist/cli-templates/harness-commands/cursor/hs-log.md +31 -0
  68. package/dist/cli-templates/harness-commands/cursor/hs-next.md +32 -0
  69. package/dist/cli-templates/harness-commands/cursor/hs-promote.md +73 -0
  70. package/dist/cli-templates/harness-commands/cursor/hs-push.md +38 -0
  71. package/dist/cli-templates/harness-commands/cursor/hs-qc.md +30 -0
  72. package/dist/cli-templates/harness-commands/cursor/hs-res.md +67 -0
  73. package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +26 -0
  74. package/dist/cli-templates/harness-commands/cursor/hs-review.md +26 -0
  75. package/dist/cli-templates/harness-commands/cursor/hs-view.md +32 -0
  76. package/dist/cli-templates/harness-commands/cursor/kill-ports.md +35 -0
  77. package/dist/cli-templates/harness-commands/cursor/prototype.md +95 -0
  78. package/docs/copilot-plugin-install.md +162 -0
  79. package/docs/driving-heyspex-from-copilot.md +165 -0
  80. package/docs/mcp-client-config.md +259 -6
  81. package/package.json +249 -124
  82. package/plugins/heyspex/.claude-plugin/plugin.json +12 -0
  83. package/plugins/heyspex/.cursor-plugin/plugin.json +15 -0
  84. package/plugins/heyspex/.mcp.json +12 -0
  85. package/plugins/heyspex/README.md +309 -0
  86. package/plugins/heyspex/bin/launch-mcp.mjs +95 -0
  87. package/plugins/heyspex/bin/run-hook.mjs +80 -0
  88. package/plugins/heyspex/hooks/cursor-hooks.json +308 -0
  89. package/plugins/heyspex/hooks/hooks.json +418 -0
  90. package/plugins/heyspex/mcp.json +13 -0
  91. package/scripts/check-mcp-freshness.mjs +17 -9
  92. package/scripts/claude-hooks/_env-vault-events.mjs +68 -0
  93. package/scripts/claude-hooks/_guardrail-events.mjs +96 -0
  94. package/scripts/claude-hooks/_guardrail-loader.mjs +494 -0
  95. package/scripts/claude-hooks/_intent-classifier-router.mjs +456 -0
  96. package/scripts/claude-hooks/_intent-classifier-semantic.mjs +356 -0
  97. package/scripts/claude-hooks/_run.mjs +413 -0
  98. package/scripts/claude-hooks/_transcript-text.mjs +60 -0
  99. package/scripts/claude-hooks/_vault-retrieval.mjs +322 -0
  100. package/scripts/claude-hooks/agent-spawn-delegation-gate.mjs +133 -0
  101. package/scripts/claude-hooks/capture-execution-gate.mjs +51 -0
  102. package/scripts/claude-hooks/capture-workflow-gate.mjs +11 -0
  103. package/scripts/claude-hooks/check-auto-memory-budget.mjs +396 -0
  104. package/scripts/claude-hooks/check-claude-md-budget.mjs +327 -0
  105. package/scripts/claude-hooks/check-mcp-freshness-hook.mjs +14 -0
  106. package/scripts/claude-hooks/claim-before-code-gate.mjs +11 -0
  107. package/scripts/claude-hooks/claim-liveness-shared.mjs +577 -0
  108. package/scripts/claude-hooks/close-terminal-on-ship.mjs +64 -0
  109. package/scripts/claude-hooks/coo-denylist-gate.mjs +11 -0
  110. package/scripts/claude-hooks/coo-guard-check.mjs +11 -0
  111. package/scripts/claude-hooks/deep-research-cost-gate.mjs +11 -0
  112. package/scripts/claude-hooks/dev-server-task-reminder.mjs +15 -0
  113. package/scripts/claude-hooks/diagnose-capture-reminder.mjs +11 -0
  114. package/scripts/claude-hooks/directive-obligation-gate.mjs +11 -0
  115. package/scripts/claude-hooks/emit-claim-status.mjs +41 -0
  116. package/scripts/claude-hooks/enforce-session-sequence.mjs +11 -0
  117. package/scripts/claude-hooks/enforce-vault-naming.mjs +11 -0
  118. package/scripts/claude-hooks/env-vault-guard.mjs +11 -0
  119. package/scripts/claude-hooks/epic-milestone-phrasing-gate.mjs +11 -0
  120. package/scripts/claude-hooks/git-identity-guard.mjs +11 -0
  121. package/scripts/claude-hooks/git-reflog-audit.mjs +15 -0
  122. package/scripts/claude-hooks/git-stash-gate.mjs +11 -0
  123. package/scripts/claude-hooks/guard-auto-memory-writes.mjs +137 -0
  124. package/scripts/claude-hooks/id-display-format-gate.mjs +11 -0
  125. package/scripts/claude-hooks/inject-communication-style.mjs +86 -0
  126. package/scripts/claude-hooks/inject-coo-seat.mjs +78 -0
  127. package/scripts/claude-hooks/inject-dev-server-awareness.mjs +11 -0
  128. package/scripts/claude-hooks/inject-doctrine.mjs +13 -0
  129. package/scripts/claude-hooks/inject-vault-context.mjs +14 -0
  130. package/scripts/claude-hooks/intent-classifier-trigger.mjs +279 -0
  131. package/scripts/claude-hooks/log-session-activity.mjs +341 -0
  132. package/scripts/claude-hooks/naughty-list-gate.mjs +11 -0
  133. package/scripts/claude-hooks/notify-status-flip.mjs +72 -0
  134. package/scripts/claude-hooks/plan-exit-ingest-gate.mjs +170 -0
  135. package/scripts/claude-hooks/plan-ingest-close-reminder.mjs +66 -0
  136. package/scripts/claude-hooks/plan-review-gate.mjs +11 -0
  137. package/scripts/claude-hooks/preflight-reminder.mjs +262 -0
  138. package/scripts/claude-hooks/protected-action-intent.mjs +221 -0
  139. package/scripts/claude-hooks/research-method-guard.mjs +398 -0
  140. package/scripts/claude-hooks/research-protocol-gate.mjs +11 -0
  141. package/scripts/claude-hooks/session-end.mjs +451 -0
  142. package/scripts/claude-hooks/settings-write-gate.mjs +11 -0
  143. package/scripts/claude-hooks/settlement-check-gate.mjs +39 -0
  144. package/scripts/claude-hooks/status-flip-detect.mjs +17 -0
  145. package/scripts/claude-hooks/terminal-close-shared.mjs +426 -0
  146. package/scripts/claude-hooks/terminal-close-watcher.mjs +122 -0
  147. package/scripts/claude-hooks/vault-mirror-catchup.mjs +11 -0
  148. package/scripts/claude-hooks/vault-write-tier-guard.mjs +11 -0
  149. package/scripts/claude-hooks/wip-checkpoint.mjs +302 -0
  150. package/scripts/cursor-hooks/manifest.mjs +281 -0
  151. package/scripts/dev-server-status.mjs +34 -13
  152. package/scripts/dev-server-stop.mjs +313 -0
  153. package/scripts/heyspex-mode.mjs +288 -0
  154. package/scripts/hs-auto-resolve.mjs +240 -0
  155. package/scripts/hs-auto-source.mjs +398 -0
  156. package/scripts/hs-auto.mjs +134 -0
  157. package/scripts/hs-capture-cli.mjs +326 -0
  158. package/scripts/hs-classifier.mjs +136 -0
  159. package/scripts/hs-companion.mjs +88 -0
  160. package/scripts/hs-gateway.mjs +115 -0
  161. package/scripts/hs-mode.mjs +125 -0
  162. package/scripts/hs-next.mjs +441 -0
  163. package/scripts/hs-settings.mjs +338 -0
  164. package/scripts/hs-view.mjs +496 -0
  165. package/scripts/hs-worktree.mjs +201 -0
  166. package/scripts/install-claude-code-hooks.mjs +272 -0
  167. package/scripts/install-cursor-hooks.mjs +471 -0
  168. package/scripts/lib/agent-env-markers.mjs +38 -0
  169. package/scripts/lib/console-url-resolver.mjs +199 -61
  170. package/scripts/lib/customer-kit.mjs +1203 -0
  171. package/scripts/lib/dev-mode.mjs +249 -32
  172. package/scripts/lib/directive-obligations.mjs +349 -0
  173. package/scripts/lib/heyspex-home.mjs +72 -0
  174. package/scripts/lib/hook-dispositions.mjs +210 -0
  175. package/scripts/lib/hook-vault-header.mjs +30 -0
  176. package/scripts/lib/hs-command.mjs +277 -0
  177. package/scripts/lib/hs-companion-settings.mjs +472 -0
  178. package/scripts/lib/hs-source-branch.mjs +196 -0
  179. package/scripts/lib/hs-source-commit.mjs +59 -5
  180. package/scripts/lib/hs-source-identity.mjs +526 -0
  181. package/scripts/lib/hs-source-log.mjs +13 -22
  182. package/scripts/lib/hs-source-preflight.mjs +18 -4
  183. package/scripts/lib/hs-source-push.mjs +386 -307
  184. package/scripts/lib/hs-worktree-args.mjs +115 -0
  185. package/scripts/lib/shared-path-helpers.mjs +134 -0
  186. package/scripts/lib/standalone-doctor.mjs +540 -3
  187. package/scripts/lib/subagent-git-lock.mjs +109 -0
  188. package/scripts/lib/template-pin.mjs +34 -0
  189. package/scripts/lib/window-placement.mjs +109 -0
  190. package/scripts/provenance-lib.mjs +91 -0
  191. package/scripts/stamp-provenance-on-edit.mjs +222 -0
  192. package/scripts/wip-ledger-health.mjs +653 -0
  193. package/spexharness/doctrine/baseline/SPEXCODE-DOCTRINE.baseline.json +29 -0
  194. package/spexharness/src/adapters/audit.mjs +197 -0
  195. package/spexharness/src/adapters/claude-code.mjs +449 -0
  196. package/spexharness/src/adapters/copilot.mjs +236 -0
  197. package/spexharness/src/adapters/detect-harness.mjs +34 -0
  198. package/spexharness/src/chat/tui/ansi.mjs +448 -0
  199. package/spexharness/src/chat/tui/palette.mjs +22 -0
  200. package/spexharness/src/chat/tui/select.mjs +208 -0
  201. package/spexharness/src/config-root.mjs +54 -0
  202. package/spexharness/src/context-budget.mjs +141 -0
  203. package/spexharness/src/dev-server-control.mjs +389 -0
  204. package/spexharness/src/doctrine/behaviour.mjs +371 -0
  205. package/spexharness/src/doctrine/corpus.mjs +733 -0
  206. package/spexharness/src/doctrine/inject.mjs +338 -0
  207. package/spexharness/src/doctrine/merge.mjs +184 -0
  208. package/spexharness/src/doctrine/sources.mjs +252 -0
  209. package/spexharness/src/doctrine/synthesize.mjs +401 -0
  210. package/spexharness/src/doctrine/vault-source.mjs +574 -0
  211. package/spexharness/src/doctrine/writer.mjs +176 -0
  212. package/spexharness/src/events.mjs +84 -0
  213. package/spexharness/src/gates/_shell-write-targets.mjs +77 -0
  214. package/spexharness/src/gates/capture-workflow-gate.mjs +202 -0
  215. package/spexharness/src/gates/claim-before-code-gate.decide.mjs +33 -0
  216. package/spexharness/src/gates/claim-before-code-gate.mjs +129 -0
  217. package/spexharness/src/gates/deep-research-cost-gate.mjs +275 -0
  218. package/spexharness/src/gates/diagnose-capture-reminder.mjs +112 -0
  219. package/spexharness/src/gates/directive-obligation-gate.decide.mjs +132 -0
  220. package/spexharness/src/gates/directive-obligation-gate.mjs +183 -0
  221. package/spexharness/src/gates/enforce-session-sequence.mjs +895 -0
  222. package/spexharness/src/gates/enforce-vault-naming.mjs +215 -0
  223. package/spexharness/src/gates/epic-milestone-phrasing-gate.mjs +233 -0
  224. package/spexharness/src/gates/git-identity-guard.decide.mjs +436 -0
  225. package/spexharness/src/gates/git-identity-guard.mjs +61 -0
  226. package/spexharness/src/gates/git-reflog-audit.mjs +151 -0
  227. package/spexharness/src/gates/git-stash-gate.decide.mjs +361 -0
  228. package/spexharness/src/gates/git-stash-gate.mjs +159 -0
  229. package/spexharness/src/gates/id-display-format-gate.mjs +190 -0
  230. package/spexharness/src/gates/inject-doctrine-gate.mjs +89 -0
  231. package/spexharness/src/gates/inject-vault-context.mjs +239 -0
  232. package/spexharness/src/gates/naughty-list-gate.mjs +213 -0
  233. package/spexharness/src/gates/plan-review-gate.mjs +233 -0
  234. package/spexharness/src/gates/preflight-reminder-gate.mjs +265 -0
  235. package/spexharness/src/gates/research-protocol-gate.mjs +175 -0
  236. package/spexharness/src/gates/settings-write-gate.decide.mjs +96 -0
  237. package/spexharness/src/gates/settings-write-gate.mjs +104 -0
  238. package/spexharness/src/gates/vault-mirror-catchup.mjs +124 -0
  239. package/spexharness/src/gates/vault-write-tier-guard.decide.mjs +125 -0
  240. package/spexharness/src/gates/vault-write-tier-guard.mjs +42 -0
  241. package/spexharness/src/git-reflog-audit.mjs +294 -0
  242. package/spexharness/src/intent-classifier.mjs +196 -0
  243. package/spexharness/src/intent-directive.mjs +626 -0
  244. package/spexharness/src/profile/learn.mjs +551 -0
  245. package/spexharness/src/profile/proposals.mjs +357 -0
  246. package/spexharness/src/profile/vault-inclusion.mjs +386 -0
  247. package/spexharness/src/redact.mjs +151 -0
  248. package/spexharness/src/rule-table.mjs +849 -0
  249. package/spexharness/src/seat-context.mjs +122 -0
  250. package/spexharness/src/session-bootstrap.mjs +512 -0
  251. package/spexharness/src/session-record.mjs +815 -0
  252. package/spexharness/src/token-inventory.mjs +207 -0
  253. package/vendor/heyspex-mcp/account-switch.mjs +96 -0
  254. package/vendor/heyspex-mcp/alignment-gate.mjs +315 -1
  255. package/vendor/heyspex-mcp/blocker-resolver.mjs +37 -2
  256. package/vendor/heyspex-mcp/breakdown-ladder.mjs +119 -4
  257. package/vendor/heyspex-mcp/breakdown-source.mjs +271 -0
  258. package/vendor/heyspex-mcp/breakdown-writer.mjs +23 -3
  259. package/vendor/heyspex-mcp/capture-buffer-patches.mjs +77 -8
  260. package/vendor/heyspex-mcp/capture-channel.mjs +55 -5
  261. package/vendor/heyspex-mcp/capture-console-client.mjs +117 -3
  262. package/vendor/heyspex-mcp/capture-focus.mjs +1061 -31
  263. package/vendor/heyspex-mcp/capture-outbox.mjs +15 -5
  264. package/vendor/heyspex-mcp/capture-sources.mjs +195 -0
  265. package/vendor/heyspex-mcp/capture-tools.mjs +1264 -0
  266. package/vendor/heyspex-mcp/claim-tools.mjs +1147 -0
  267. package/vendor/heyspex-mcp/cli-verbs.mjs +2074 -0
  268. package/vendor/heyspex-mcp/cli.mjs +207 -15524
  269. package/vendor/heyspex-mcp/close-branch-report.mjs +102 -0
  270. package/vendor/heyspex-mcp/cloud-tool-surface.mjs +128 -0
  271. package/vendor/heyspex-mcp/cloud-vault-writes.mjs +602 -0
  272. package/vendor/heyspex-mcp/config-seed.yaml +15 -0
  273. package/vendor/heyspex-mcp/config-settings.mjs +290 -7
  274. package/vendor/heyspex-mcp/console-fetch.mjs +822 -17
  275. package/vendor/heyspex-mcp/console-transport.mjs +172 -0
  276. package/vendor/heyspex-mcp/credentials.mjs +585 -0
  277. package/vendor/heyspex-mcp/defer-spec.mjs +44 -4
  278. package/vendor/heyspex-mcp/deferred-reclaim.mjs +29 -3
  279. package/vendor/heyspex-mcp/design-handlers.mjs +31 -0
  280. package/vendor/heyspex-mcp/design-tools.mjs +4 -1
  281. package/vendor/heyspex-mcp/dev-factory-bridge.mjs +4 -4
  282. package/vendor/heyspex-mcp/dev-mode-core.mjs +45 -0
  283. package/vendor/heyspex-mcp/dev-tools.mjs +131 -0
  284. package/vendor/heyspex-mcp/diagnose-handlers.mjs +54 -0
  285. package/vendor/heyspex-mcp/events-tools.mjs +207 -0
  286. package/vendor/heyspex-mcp/filing-tools.mjs +723 -0
  287. package/vendor/heyspex-mcp/fleet-handlers.mjs +37 -0
  288. package/vendor/heyspex-mcp/fleet-queue-tools.mjs +71 -15
  289. package/vendor/heyspex-mcp/focus-tools.mjs +190 -0
  290. package/vendor/heyspex-mcp/folder-trust-store.mjs +60 -0
  291. package/vendor/heyspex-mcp/http-transport.mjs +302 -0
  292. package/vendor/heyspex-mcp/list-tools.mjs +257 -0
  293. package/vendor/heyspex-mcp/login-handoff.mjs +15 -1
  294. package/vendor/heyspex-mcp/mcp-config.mjs +107 -0
  295. package/vendor/heyspex-mcp/memory-tools.mjs +523 -0
  296. package/vendor/heyspex-mcp/mvp-mode.mjs +53 -6
  297. package/vendor/heyspex-mcp/obligation-backstop.mjs +103 -0
  298. package/vendor/heyspex-mcp/occupied-keys.mjs +142 -0
  299. package/vendor/heyspex-mcp/on-deck-brief.mjs +3377 -0
  300. package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -0
  301. package/vendor/heyspex-mcp/package-root.mjs +32 -0
  302. package/vendor/heyspex-mcp/peer-presence.mjs +8 -1
  303. package/vendor/heyspex-mcp/plan-ground.mjs +621 -652
  304. package/vendor/heyspex-mcp/plan-push.mjs +908 -22
  305. package/vendor/heyspex-mcp/plan-review.mjs +124 -30
  306. package/vendor/heyspex-mcp/preflight-gate.mjs +0 -0
  307. package/vendor/heyspex-mcp/procedure-handlers.mjs +327 -0
  308. package/vendor/heyspex-mcp/procedure-tools.mjs +1181 -970
  309. package/vendor/heyspex-mcp/promote-capture.mjs +388 -69
  310. package/vendor/heyspex-mcp/promote-diagnose.mjs +74 -0
  311. package/vendor/heyspex-mcp/promote-research-to-diagnose.mjs +9 -2
  312. package/vendor/heyspex-mcp/promote-research.mjs +666 -53
  313. package/vendor/heyspex-mcp/promotion-helpers.mjs +251 -0
  314. package/vendor/heyspex-mcp/prompts.mjs +1460 -0
  315. package/vendor/heyspex-mcp/read-path.mjs +191 -15
  316. package/vendor/heyspex-mcp/receipt-shape.mjs +146 -0
  317. package/vendor/heyspex-mcp/recipe-handlers.mjs +117 -0
  318. package/vendor/heyspex-mcp/regen-tasks.mjs +210 -3
  319. package/vendor/heyspex-mcp/register-spec.mjs +266 -54
  320. package/vendor/heyspex-mcp/request-context.mjs +83 -0
  321. package/vendor/heyspex-mcp/research-focus.mjs +207 -4
  322. package/vendor/heyspex-mcp/research-handlers.mjs +625 -0
  323. package/vendor/heyspex-mcp/research-tools.mjs +4 -4
  324. package/vendor/heyspex-mcp/response-budget.mjs +348 -0
  325. package/vendor/heyspex-mcp/review-focus.mjs +25 -1
  326. package/vendor/heyspex-mcp/roadmap-default.mjs +70 -0
  327. package/vendor/heyspex-mcp/search-specs.mjs +280 -0
  328. package/vendor/heyspex-mcp/seat-block-ledger.mjs +113 -0
  329. package/vendor/heyspex-mcp/seat-identity.mjs +2 -6
  330. package/vendor/heyspex-mcp/session-owner.mjs +109 -0
  331. package/vendor/heyspex-mcp/session-tools.mjs +162 -0
  332. package/vendor/heyspex-mcp/settings-registry.mjs +232 -0
  333. package/vendor/heyspex-mcp/settings-tools.mjs +85 -12
  334. package/vendor/heyspex-mcp/spec-edit-tools.mjs +453 -0
  335. package/vendor/heyspex-mcp/spec-id-lib.mjs +93 -17
  336. package/vendor/heyspex-mcp/split-research.mjs +304 -56
  337. package/vendor/heyspex-mcp/stdio-rpc.mjs +22 -0
  338. package/vendor/heyspex-mcp/submit-for-review.mjs +150 -7
  339. package/vendor/heyspex-mcp/summary-contract.mjs +105 -0
  340. package/vendor/heyspex-mcp/summary-write-tools.mjs +228 -0
  341. package/vendor/heyspex-mcp/symptom-query.mjs +146 -0
  342. package/vendor/heyspex-mcp/test-helpers.mjs +123 -1
  343. package/vendor/heyspex-mcp/tool-catalog.mjs +2819 -0
  344. package/vendor/heyspex-mcp/tool-dispatch.mjs +657 -0
  345. package/vendor/heyspex-mcp/tool-helpers.mjs +325 -0
  346. package/vendor/heyspex-mcp/validate-linkage.mjs +414 -27
  347. package/vendor/heyspex-mcp/vault-io.mjs +720 -0
  348. package/vendor/heyspex-mcp/vault-less-endpoints.mjs +52 -0
  349. package/vendor/heyspex-mcp/vault-resolve.mjs +554 -68
  350. package/vendor/heyspex-mcp/verify-reproduction.mjs +270 -30
  351. package/vendor/heyspex-mcp/whoami.mjs +92 -0
  352. package/vendor/heyspex-mcp/write-outbox.mjs +512 -0
@@ -0,0 +1,356 @@
1
+ #!/usr/bin/env node
2
+ // HeySpex Intent Classifier — SEMANTIC layer.
3
+ //
4
+ // The Intent Classifier has two layers ([[HS-UJVA-EPIC-KKMO-autonomous-skill-routing-context]]):
5
+ // 1. LEXICAL (fast path) — the per-rule regex patterns in the guardrail registry.
6
+ // A regex hit fires instantly and never waits on this module.
7
+ // 2. SEMANTIC — on a regex MISS, resolve intent WITHOUT a second, separate model.
8
+ // DEFAULT (mode: self_check, [[HS-M4PN-TASK]]): the router deterministically
9
+ // injects a SELF-CHECK instruction and lets the agent already running the session
10
+ // judge intent as part of its own turn — no network, no fleet machine, works for
11
+ // every user. The self-check block is built in _intent-classifier-router.mjs;
12
+ // this module is the OPT-IN model fallback for anyone who still wants it.
13
+ // Why the semantic layer exists at all (regex alone is not enough):
14
+ // [[intent-classifier-injected-content]] — 9/10 paraphrased improvement-idea
15
+ // phrasings missed before the patterns were broadened, and paraphrase drift
16
+ // never ends.
17
+ //
18
+ // OPT-IN MODEL FALLBACK (mode: model): classifyIntent() below classifies the prompt
19
+ // with a fast LOCAL model (Ollama) and returns { behavior, confidence } so the caller
20
+ // can fire above a threshold. This is the ORIGINAL mechanism, now DEMOTED to an
21
+ // explicit opt-in — it is NEVER on the default path, so no reachable endpoint is
22
+ // required for the semantic layer to function ([[HS-M4PN-TASK]] AC2).
23
+ //
24
+ // HARD FAIL-SAFE CONTRACT (hook house rules): this module NEVER throws, NEVER
25
+ // hangs past its timeout, and on ANY problem (endpoint down, timeout, bad JSON,
26
+ // unexpected shape) returns { behavior: null, confidence: 0 } so the calling hook
27
+ // proceeds exactly as if the semantic layer did not exist. Dependency-free:
28
+ // Node stdlib + global fetch only (hooks run on a fresh clone with no npm install).
29
+ //
30
+ // Config (.heyspex/config.yaml → agent_behaviors.intent_classifier.*), all optional.
31
+ // The file is read from the VAULT root (the loader's `defaultConfigRoot()`), not this
32
+ // checkout — [[HS-4C76-9W86-DIAG-heyspex-config-yaml-has-no-single-home]]:
33
+ // semantic_enabled: true|false (default true — turns Layer 2 on/off entirely)
34
+ // mode: self_check|model (default self_check — deterministic injection;
35
+ // `model` opts into the Ollama fallback below)
36
+ // endpoint: Ollama base URL (default http://192.168.1.252:11434 — mode:model only)
37
+ // model: model name (default qwen3:8b — mode:model only)
38
+ // timeout_ms: request budget (default 2500 — the hook's overall budget is 5s)
39
+ // min_confidence: fire threshold (default 0.7 — consumed by the calling hook)
40
+ // Env overrides (kill switch first):
41
+ // HEYSPEX_INTENT_SEMANTIC_OFF=1 hard off (disables Layer 2 entirely)
42
+ // HEYSPEX_INTENT_SEMANTIC_MODE=self_check|model
43
+ // HEYSPEX_INTENT_ENDPOINT / HEYSPEX_INTENT_MODEL /
44
+ // HEYSPEX_INTENT_TIMEOUT_MS / HEYSPEX_INTENT_MIN_CONFIDENCE
45
+ //
46
+ // Self-test without a session (exercises the opt-in model fallback):
47
+ // node scripts/claude-hooks/_intent-classifier-semantic.mjs --ping
48
+ // node scripts/claude-hooks/_intent-classifier-semantic.mjs --classify "some prompt"
49
+
50
+ import fs from 'node:fs';
51
+ import path from 'node:path';
52
+ import { fileURLToPath } from 'node:url';
53
+
54
+ import { readConfigLeafRaw } from './_guardrail-loader.mjs';
55
+ import { defaultConfigRoot } from '../../spexharness/src/config-root.mjs';
56
+
57
+ // The CODE checkout — where `.heyspex/intent-classifier-history.jsonl` (an event
58
+ // log, a code-repo artifact) is appended. NOT where config.yaml is read from: the
59
+ // config reads below default to the vault root via `defaultConfigRoot()`
60
+ // ([[HS-4C76-9W86-DIAG-heyspex-config-yaml-has-no-single-home]]).
61
+ export const DEFAULT_REPO_ROOT = path.resolve(
62
+ path.dirname(fileURLToPath(import.meta.url)),
63
+ '..',
64
+ '..',
65
+ );
66
+
67
+ // Semantic-layer modes ([[HS-M4PN-TASK]]):
68
+ // 'self_check' — DEFAULT. On a regex miss the router injects a deterministic
69
+ // self-check instruction; the session's own agent judges intent.
70
+ // No network, no fleet machine.
71
+ // 'model' — OPT-IN fallback. classifyIntent() calls the local Ollama endpoint.
72
+ export const SEMANTIC_MODES = Object.freeze(['self_check', 'model']);
73
+
74
+ export const DEFAULTS = Object.freeze({
75
+ semanticEnabled: true,
76
+ semanticMode: 'self_check',
77
+ endpoint: 'http://192.168.1.252:11434',
78
+ model: 'qwen3:8b',
79
+ timeoutMs: 2500,
80
+ minConfidence: 0.7,
81
+ });
82
+
83
+ // The behavior catalog the classifier chooses from. Descriptions are the actual
84
+ // classification instructions, so keep them about the USER'S INTENT, not the
85
+ // system's response. Any hook can pass its own list; this shared catalog keeps
86
+ // capture-trigger and research-trigger consistent if both adopt the semantic layer.
87
+ export const KNOWN_BEHAVIORS = Object.freeze([
88
+ {
89
+ key: 'capture_on_idea',
90
+ description:
91
+ 'The user is sharing a raw idea, suggestion, observation, or improvement thought — a brain-dump about something that could exist or be better — rather than giving a direct work order, asking a question about existing work, or continuing work already in progress.',
92
+ },
93
+ {
94
+ key: 'research_before_decision',
95
+ description:
96
+ 'The user is declining to make a design or architecture decision themselves and is asking for research, best practices, or an expert recommendation instead (e.g. "not a decision I should have to make", "don\'t reinvent the wheel").',
97
+ },
98
+ {
99
+ key: 'settings_intent',
100
+ description:
101
+ 'The user wants to READ or CHANGE a HeySpex workflow setting / agent behavior — enable or disable a behavior (auto-capture, the research trigger, the intent classifier), turn something on or off, set/lower/raise a threshold or value (confidence, timeout), or ask what a setting is currently. About the configuration itself, NOT a request to do the underlying work. (e.g. "turn off auto-capture", "lower the classifier confidence to 0.6", "is auto-capture on", "make it stop capturing everything").',
102
+ },
103
+ {
104
+ key: 'defer_intent',
105
+ description:
106
+ 'The user wants to DEFER, POSTPONE, or SCHEDULE a follow-up to a later time — "come back to this later", "remind me in a week", "park/table this until X is done", "schedule this for next week", "not now — later, after the migration". About WHEN to revisit something, not a request to do it now.',
107
+ },
108
+ {
109
+ key: 'handoff_intent',
110
+ description:
111
+ 'The user asks for a hand-off prompt, doc, notes or resume context for the NEXT agent or session ("give me a handoff prompt for the next agent", "hand this off", "what should the next session know"). About producing the hand-off artifact, NOT about loading one (a lane code like GQ5R or `hs handoff load`) and not ordinary work.',
112
+ },
113
+ ]);
114
+
115
+ function num(raw, fallback) {
116
+ const n = Number(raw);
117
+ return Number.isFinite(n) && n > 0 ? n : fallback;
118
+ }
119
+
120
+ function clamp01(n) {
121
+ const v = Number(n);
122
+ if (!Number.isFinite(v)) return 0;
123
+ return Math.max(0, Math.min(1, v));
124
+ }
125
+
126
+ /**
127
+ * Resolve the semantic-layer config: env override → .heyspex/config.yaml →
128
+ * shipped defaults. FAIL-SAFE: any unreadable value falls back to its default.
129
+ * `repoRoot` is the directory holding `.heyspex/config.yaml` — defaults to the
130
+ * vault root; an explicit argument (tests, fixtures) always wins.
131
+ */
132
+ export function readIntentClassifierConfig(repoRoot = defaultConfigRoot()) {
133
+ const cfg = { ...DEFAULTS };
134
+ try {
135
+ if (process.env.HEYSPEX_INTENT_SEMANTIC_OFF === '1') cfg.semanticEnabled = false;
136
+ else {
137
+ const enabled = readConfigLeafRaw('agent_behaviors.intent_classifier.semantic_enabled', repoRoot);
138
+ if (enabled !== undefined) cfg.semanticEnabled = enabled.trim().toLowerCase() !== 'false';
139
+ }
140
+ // Mode: env override → config → default 'self_check'. Any unrecognized value
141
+ // falls back to the deterministic default rather than silently enabling the model.
142
+ const modeRaw =
143
+ process.env.HEYSPEX_INTENT_SEMANTIC_MODE ||
144
+ unquote(readConfigLeafRaw('agent_behaviors.intent_classifier.mode', repoRoot));
145
+ if (typeof modeRaw === 'string') {
146
+ const m = modeRaw.trim().toLowerCase();
147
+ if (SEMANTIC_MODES.includes(m)) cfg.semanticMode = m;
148
+ }
149
+ const endpoint =
150
+ process.env.HEYSPEX_INTENT_ENDPOINT ||
151
+ unquote(readConfigLeafRaw('agent_behaviors.intent_classifier.endpoint', repoRoot));
152
+ if (endpoint && /^https?:\/\//.test(endpoint)) cfg.endpoint = endpoint.replace(/\/+$/, '');
153
+ const model =
154
+ process.env.HEYSPEX_INTENT_MODEL ||
155
+ unquote(readConfigLeafRaw('agent_behaviors.intent_classifier.model', repoRoot));
156
+ if (model) cfg.model = model;
157
+ cfg.timeoutMs = num(
158
+ process.env.HEYSPEX_INTENT_TIMEOUT_MS ??
159
+ readConfigLeafRaw('agent_behaviors.intent_classifier.timeout_ms', repoRoot),
160
+ DEFAULTS.timeoutMs,
161
+ );
162
+ const minConf = Number(
163
+ process.env.HEYSPEX_INTENT_MIN_CONFIDENCE ??
164
+ readConfigLeafRaw('agent_behaviors.intent_classifier.min_confidence', repoRoot),
165
+ );
166
+ if (Number.isFinite(minConf) && minConf > 0 && minConf <= 1) cfg.minConfidence = minConf;
167
+ } catch {
168
+ return { ...DEFAULTS };
169
+ }
170
+ return cfg;
171
+ }
172
+
173
+ function unquote(raw) {
174
+ if (typeof raw !== 'string') return undefined;
175
+ const v = raw.trim();
176
+ const m = v.match(/^"(.*)"$/) || v.match(/^'(.*)'$/);
177
+ return m ? m[1] : v || undefined;
178
+ }
179
+
180
+ function buildSystemPrompt(behaviors) {
181
+ const rows = behaviors
182
+ .map((b) => `- ${b.key}: ${b.description}`)
183
+ .join('\n');
184
+ return (
185
+ 'You classify ONE user message from a coding-agent session into exactly one behavior key, or "none" ' +
186
+ 'when no behavior applies. Behaviors:\n' +
187
+ rows +
188
+ '\n- none: a direct work order, a question about existing work, a bug report to fix now, small talk, or anything else.\n' +
189
+ 'Reply with STRICT JSON only, no prose: {"behavior":"<one key above or none>","confidence":<number 0..1>}'
190
+ );
191
+ }
192
+
193
+ /**
194
+ * Classify a prompt into one of the given behaviors via the local Ollama endpoint.
195
+ *
196
+ * @param {string} prompt the user's message
197
+ * @param {{key:string, description:string}[]} behaviors candidate behaviors (defaults to KNOWN_BEHAVIORS)
198
+ * @param {object} [opts] { endpoint, model, timeoutMs, repoRoot } — omitted values come from config
199
+ * @returns {Promise<{behavior: string|null, confidence: number, latencyMs?: number, error?: string}>}
200
+ * behavior is null when nothing matched OR anything failed (fail-safe).
201
+ */
202
+ export async function classifyIntent(prompt, behaviors = KNOWN_BEHAVIORS, opts = {}) {
203
+ const none = { behavior: null, confidence: 0 };
204
+ try {
205
+ if (typeof prompt !== 'string' || !prompt.trim()) return none;
206
+ const list = Array.isArray(behaviors) && behaviors.length ? behaviors : KNOWN_BEHAVIORS;
207
+ const cfg = readIntentClassifierConfig(opts.repoRoot ?? defaultConfigRoot());
208
+ const endpoint = opts.endpoint ?? cfg.endpoint;
209
+ const model = opts.model ?? cfg.model;
210
+ const timeoutMs = num(opts.timeoutMs, cfg.timeoutMs);
211
+
212
+ // Very short prompts carry no classifiable intent; very long ones are truncated
213
+ // to keep the local model inside the latency budget.
214
+ const text = prompt.trim().slice(0, 2000);
215
+ if (text.length < 12) return none;
216
+
217
+ // Optional call counter (proof/observability): when HEYSPEX_INTENT_CALL_LOG names
218
+ // a file, append one byte per model call so a test can assert exactly ≤1 call per
219
+ // prompt. Fail-safe: never let instrumentation break the classifier.
220
+ if (process.env.HEYSPEX_INTENT_CALL_LOG) {
221
+ try {
222
+ fs.appendFileSync(process.env.HEYSPEX_INTENT_CALL_LOG, `${new Date().toISOString()} ${model}\n`);
223
+ } catch {
224
+ /* ignore */
225
+ }
226
+ }
227
+
228
+ const controller = new AbortController();
229
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
230
+ const t0 = Date.now();
231
+ let res;
232
+ try {
233
+ res = await fetch(`${endpoint}/api/chat`, {
234
+ method: 'POST',
235
+ headers: { 'content-type': 'application/json' },
236
+ signal: controller.signal,
237
+ body: JSON.stringify({
238
+ model,
239
+ stream: false,
240
+ format: 'json',
241
+ think: false, // qwen3: skip the reasoning preamble; harmless no-op elsewhere
242
+ keep_alive: '30m', // keep the classifier warm between prompts
243
+ options: { temperature: 0, num_predict: 120 },
244
+ messages: [
245
+ { role: 'system', content: buildSystemPrompt(list) },
246
+ { role: 'user', content: text },
247
+ ],
248
+ }),
249
+ });
250
+ } finally {
251
+ clearTimeout(timer);
252
+ }
253
+ const latencyMs = Date.now() - t0;
254
+ if (!res || !res.ok) return { ...none, latencyMs, error: `http_${res?.status ?? 'no_response'}` };
255
+
256
+ const payload = await res.json().catch(() => null);
257
+ const content = payload?.message?.content;
258
+ if (typeof content !== 'string' || !content.trim()) return { ...none, latencyMs, error: 'empty_response' };
259
+
260
+ let parsed;
261
+ try {
262
+ parsed = JSON.parse(content);
263
+ } catch {
264
+ // Defensive: some models wrap JSON in text — try the first {...} span.
265
+ const m = content.match(/\{[\s\S]*\}/);
266
+ if (!m) return { ...none, latencyMs, error: 'unparseable_response' };
267
+ try {
268
+ parsed = JSON.parse(m[0]);
269
+ } catch {
270
+ return { ...none, latencyMs, error: 'unparseable_response' };
271
+ }
272
+ }
273
+
274
+ const rawBehavior = typeof parsed?.behavior === 'string' ? parsed.behavior.trim() : '';
275
+ const confidence = clamp01(parsed?.confidence);
276
+ const valid = list.some((b) => b.key === rawBehavior);
277
+ if (!valid || rawBehavior === 'none') return { behavior: null, confidence, latencyMs };
278
+ return { behavior: rawBehavior, confidence, latencyMs };
279
+ } catch (err) {
280
+ const error = err?.name === 'AbortError' ? 'timeout' : 'unreachable';
281
+ return { ...none, error };
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Append one classifier-decision row to .heyspex/intent-classifier-history.jsonl.
287
+ * Called by the hooks for FIRES (either layer), semantic NEAR-MISSES (matched
288
+ * behavior below threshold), and semantic ERRORS — never for the ordinary
289
+ * regex-miss/no-match case, so the log stays small. FAIL-SAFE: never throws.
290
+ */
291
+ export function appendClassifierEvent(row, repoRoot = DEFAULT_REPO_ROOT) {
292
+ try {
293
+ const full = {
294
+ timestamp: new Date().toISOString(),
295
+ event_kind: 'intent_classifier.decision',
296
+ ...row,
297
+ };
298
+ fs.appendFileSync(
299
+ path.join(repoRoot, '.heyspex', 'intent-classifier-history.jsonl'),
300
+ JSON.stringify(full) + '\n',
301
+ );
302
+ return true;
303
+ } catch {
304
+ return false;
305
+ }
306
+ }
307
+
308
+ // ── self-test CLI (no session required) ─────────────────────────────────────
309
+
310
+ async function ping() {
311
+ const cfg = readIntentClassifierConfig();
312
+ const out = { config: cfg, reachable: false, version: null, classify: null };
313
+ try {
314
+ const controller = new AbortController();
315
+ const timer = setTimeout(() => controller.abort(), cfg.timeoutMs);
316
+ const res = await fetch(`${cfg.endpoint}/api/version`, { signal: controller.signal });
317
+ clearTimeout(timer);
318
+ if (res.ok) {
319
+ out.reachable = true;
320
+ out.version = (await res.json().catch(() => null))?.version ?? null;
321
+ }
322
+ } catch {
323
+ /* stays unreachable */
324
+ }
325
+ if (out.reachable) {
326
+ out.classify = await classifyIntent(
327
+ "it'd be smart to let the app notice ideas on its own",
328
+ KNOWN_BEHAVIORS,
329
+ );
330
+ }
331
+ process.stdout.write(JSON.stringify(out, null, 2) + '\n');
332
+ process.exit(out.reachable && out.classify?.behavior === 'capture_on_idea' ? 0 : 1);
333
+ }
334
+
335
+ async function classifyCli(text) {
336
+ const result = await classifyIntent(text, KNOWN_BEHAVIORS);
337
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
338
+ process.exit(0);
339
+ }
340
+
341
+ const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
342
+ if (isMain) {
343
+ if (process.argv.includes('--ping')) {
344
+ ping();
345
+ } else {
346
+ const idx = process.argv.indexOf('--classify');
347
+ if (idx !== -1 && process.argv[idx + 1]) {
348
+ classifyCli(process.argv[idx + 1]);
349
+ } else if (process.argv.length > 2) {
350
+ process.stdout.write(
351
+ 'Usage: node _intent-classifier-semantic.mjs --ping | --classify "<text>"\n',
352
+ );
353
+ process.exit(2);
354
+ }
355
+ }
356
+ }