heyspex 0.1.0 → 0.2.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 (322) hide show
  1. package/README.md +21 -0
  2. package/bin/hs.mjs +931 -98
  3. package/bin/spexcode.mjs +262 -0
  4. package/dev-factory/engine/worktree.mjs +740 -0
  5. package/dev-factory/lib/code-repo-root.mjs +67 -0
  6. package/dev-factory/lib/context-budget.mjs +84 -0
  7. package/dev-factory/lib/epic-detect.mjs +190 -0
  8. package/dev-factory/lib/fleet-queue-file.mjs +846 -0
  9. package/dev-factory/lib/fleet-queue.mjs +426 -0
  10. package/dev-factory/lib/fleet-state.mjs +14 -18
  11. package/dev-factory/lib/model-catalog.mjs +338 -0
  12. package/dev-factory/lib/seat-context.mjs +199 -0
  13. package/dev-factory/lib/spec-io.mjs +93 -0
  14. package/dev-factory/lib/sufficiency-score.mjs +8 -2
  15. package/dev-factory/lib/worktree-allocator.mjs +744 -0
  16. package/dev-factory/model-catalog.json +222 -0
  17. package/dist/cli/hs-check.mjs +1085 -103
  18. package/dist/cli/hs-claim.mjs +936 -63
  19. package/dist/cli/hs-connect.mjs +9786 -1910
  20. package/dist/cli/hs-defer.mjs +5267 -3053
  21. package/dist/cli/hs-dev.mjs +564 -0
  22. package/dist/cli/hs-env.mjs +385 -299
  23. package/dist/cli/hs-index-rebuild.mjs +7129 -0
  24. package/dist/cli/hs-index-reconcile.mjs +7852 -0
  25. package/dist/cli/hs-init.mjs +777 -138
  26. package/dist/cli/hs-install.mjs +6583 -485
  27. package/dist/cli/hs-join.mjs +10137 -2368
  28. package/dist/cli/hs-vault-pull.mjs +4659 -5882
  29. package/dist/cli/hs-vault.mjs +3793 -1537
  30. package/dist/cli/hs-workspace.mjs +2550 -524
  31. package/dist/cli-templates/customer-vault/specs/entity-model.md +594 -601
  32. package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +110 -177
  33. package/dist/cli-templates/customer-vault/specs/protocols/destructive-action-checklist.md +122 -0
  34. package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +13 -15
  35. package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +91 -0
  36. package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +150 -168
  37. package/dist/cli-templates/customer-vault/specs/protocols/spec-summary-authoring.md +83 -0
  38. package/dist/cli-templates/harness-commands/claude/hs-auto.md +36 -0
  39. package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -0
  40. package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -0
  41. package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -0
  42. package/dist/cli-templates/harness-commands/claude/hs-dev.md +20 -0
  43. package/dist/cli-templates/harness-commands/claude/hs-doctor.md +32 -0
  44. package/dist/cli-templates/harness-commands/claude/hs-ground.md +28 -0
  45. package/dist/cli-templates/harness-commands/claude/hs-handoff.md +33 -0
  46. package/dist/cli-templates/harness-commands/claude/hs-log.md +27 -0
  47. package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -0
  48. package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -0
  49. package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -0
  50. package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -0
  51. package/dist/cli-templates/harness-commands/claude/hs-res.md +63 -0
  52. package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -0
  53. package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -0
  54. package/dist/cli-templates/harness-commands/claude/hs-view.md +26 -0
  55. package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -0
  56. package/dist/cli-templates/harness-commands/claude/prototype.md +91 -0
  57. package/dist/cli-templates/harness-commands/cursor/hs-auto.md +40 -0
  58. package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +31 -0
  59. package/dist/cli-templates/harness-commands/cursor/hs-capture.md +65 -0
  60. package/dist/cli-templates/harness-commands/cursor/hs-defer.md +32 -0
  61. package/dist/cli-templates/harness-commands/cursor/hs-dev.md +24 -0
  62. package/dist/cli-templates/harness-commands/cursor/hs-doctor.md +36 -0
  63. package/dist/cli-templates/harness-commands/cursor/hs-ground.md +31 -0
  64. package/dist/cli-templates/harness-commands/cursor/hs-handoff.md +37 -0
  65. package/dist/cli-templates/harness-commands/cursor/hs-log.md +31 -0
  66. package/dist/cli-templates/harness-commands/cursor/hs-next.md +32 -0
  67. package/dist/cli-templates/harness-commands/cursor/hs-promote.md +73 -0
  68. package/dist/cli-templates/harness-commands/cursor/hs-push.md +38 -0
  69. package/dist/cli-templates/harness-commands/cursor/hs-qc.md +30 -0
  70. package/dist/cli-templates/harness-commands/cursor/hs-res.md +67 -0
  71. package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +26 -0
  72. package/dist/cli-templates/harness-commands/cursor/hs-review.md +26 -0
  73. package/dist/cli-templates/harness-commands/cursor/hs-view.md +30 -0
  74. package/dist/cli-templates/harness-commands/cursor/kill-ports.md +35 -0
  75. package/dist/cli-templates/harness-commands/cursor/prototype.md +95 -0
  76. package/docs/mcp-client-config.md +259 -6
  77. package/package.json +231 -124
  78. package/plugins/heyspex/.claude-plugin/plugin.json +12 -0
  79. package/plugins/heyspex/.cursor-plugin/plugin.json +15 -0
  80. package/plugins/heyspex/.mcp.json +12 -0
  81. package/plugins/heyspex/bin/launch-mcp.mjs +95 -0
  82. package/plugins/heyspex/bin/run-hook.mjs +62 -0
  83. package/plugins/heyspex/hooks/cursor-hooks.json +227 -0
  84. package/plugins/heyspex/hooks/hooks.json +361 -0
  85. package/plugins/heyspex/mcp.json +13 -0
  86. package/scripts/check-mcp-freshness.mjs +17 -9
  87. package/scripts/claude-hooks/_env-vault-events.mjs +68 -0
  88. package/scripts/claude-hooks/_guardrail-events.mjs +96 -0
  89. package/scripts/claude-hooks/_guardrail-loader.mjs +494 -0
  90. package/scripts/claude-hooks/_intent-classifier-router.mjs +410 -0
  91. package/scripts/claude-hooks/_intent-classifier-semantic.mjs +351 -0
  92. package/scripts/claude-hooks/_run.mjs +120 -0
  93. package/scripts/claude-hooks/_transcript-text.mjs +60 -0
  94. package/scripts/claude-hooks/_vault-retrieval.mjs +322 -0
  95. package/scripts/claude-hooks/agent-spawn-delegation-gate.mjs +133 -0
  96. package/scripts/claude-hooks/capture-workflow-gate.mjs +11 -0
  97. package/scripts/claude-hooks/check-auto-memory-budget.mjs +396 -0
  98. package/scripts/claude-hooks/check-claude-md-budget.mjs +327 -0
  99. package/scripts/claude-hooks/check-mcp-freshness-hook.mjs +14 -0
  100. package/scripts/claude-hooks/claim-before-code-gate.mjs +11 -0
  101. package/scripts/claude-hooks/claim-liveness-shared.mjs +575 -0
  102. package/scripts/claude-hooks/close-terminal-on-ship.mjs +64 -0
  103. package/scripts/claude-hooks/coo-denylist-gate.mjs +11 -0
  104. package/scripts/claude-hooks/coo-guard-check.mjs +11 -0
  105. package/scripts/claude-hooks/deep-research-cost-gate.mjs +11 -0
  106. package/scripts/claude-hooks/dev-server-task-reminder.mjs +15 -0
  107. package/scripts/claude-hooks/emit-claim-status.mjs +41 -0
  108. package/scripts/claude-hooks/enforce-session-sequence.mjs +11 -0
  109. package/scripts/claude-hooks/enforce-vault-naming.mjs +11 -0
  110. package/scripts/claude-hooks/env-vault-guard.mjs +11 -0
  111. package/scripts/claude-hooks/epic-milestone-phrasing-gate.mjs +11 -0
  112. package/scripts/claude-hooks/git-identity-guard.mjs +11 -0
  113. package/scripts/claude-hooks/git-reflog-audit.mjs +15 -0
  114. package/scripts/claude-hooks/git-stash-gate.mjs +11 -0
  115. package/scripts/claude-hooks/guard-auto-memory-writes.mjs +137 -0
  116. package/scripts/claude-hooks/id-display-format-gate.mjs +11 -0
  117. package/scripts/claude-hooks/inject-communication-style.mjs +66 -0
  118. package/scripts/claude-hooks/inject-coo-seat.mjs +78 -0
  119. package/scripts/claude-hooks/inject-dev-server-awareness.mjs +11 -0
  120. package/scripts/claude-hooks/inject-doctrine.mjs +13 -0
  121. package/scripts/claude-hooks/inject-vault-context.mjs +14 -0
  122. package/scripts/claude-hooks/intent-classifier-trigger.mjs +247 -0
  123. package/scripts/claude-hooks/log-session-activity.mjs +341 -0
  124. package/scripts/claude-hooks/naughty-list-gate.mjs +11 -0
  125. package/scripts/claude-hooks/notify-status-flip.mjs +72 -0
  126. package/scripts/claude-hooks/plan-exit-ingest-gate.mjs +170 -0
  127. package/scripts/claude-hooks/plan-ingest-close-reminder.mjs +66 -0
  128. package/scripts/claude-hooks/plan-review-gate.mjs +11 -0
  129. package/scripts/claude-hooks/preflight-reminder.mjs +262 -0
  130. package/scripts/claude-hooks/protected-action-intent.mjs +221 -0
  131. package/scripts/claude-hooks/research-method-guard.mjs +398 -0
  132. package/scripts/claude-hooks/research-protocol-gate.mjs +11 -0
  133. package/scripts/claude-hooks/session-end.mjs +449 -0
  134. package/scripts/claude-hooks/settings-write-gate.mjs +11 -0
  135. package/scripts/claude-hooks/settlement-check-gate.mjs +39 -0
  136. package/scripts/claude-hooks/status-flip-detect.mjs +17 -0
  137. package/scripts/claude-hooks/terminal-close-shared.mjs +426 -0
  138. package/scripts/claude-hooks/terminal-close-watcher.mjs +122 -0
  139. package/scripts/claude-hooks/wip-checkpoint.mjs +298 -0
  140. package/scripts/cursor-hooks/manifest.mjs +235 -0
  141. package/scripts/dev-server-status.mjs +34 -13
  142. package/scripts/dev-server-stop.mjs +313 -0
  143. package/scripts/heyspex-mode.mjs +268 -0
  144. package/scripts/hs-auto-resolve.mjs +240 -0
  145. package/scripts/hs-auto-source.mjs +398 -0
  146. package/scripts/hs-auto.mjs +134 -0
  147. package/scripts/hs-capture-cli.mjs +326 -0
  148. package/scripts/hs-classifier.mjs +136 -0
  149. package/scripts/hs-companion.mjs +88 -0
  150. package/scripts/hs-gateway.mjs +115 -0
  151. package/scripts/hs-mode.mjs +125 -0
  152. package/scripts/hs-next.mjs +441 -0
  153. package/scripts/hs-settings.mjs +338 -0
  154. package/scripts/hs-view.mjs +333 -0
  155. package/scripts/hs-worktree.mjs +201 -0
  156. package/scripts/install-claude-code-hooks.mjs +270 -0
  157. package/scripts/install-cursor-hooks.mjs +471 -0
  158. package/scripts/lib/agent-env-markers.mjs +38 -0
  159. package/scripts/lib/console-url-resolver.mjs +197 -60
  160. package/scripts/lib/customer-kit.mjs +886 -0
  161. package/scripts/lib/dev-mode.mjs +244 -28
  162. package/scripts/lib/heyspex-home.mjs +72 -0
  163. package/scripts/lib/hs-command.mjs +277 -0
  164. package/scripts/lib/hs-companion-settings.mjs +472 -0
  165. package/scripts/lib/hs-source-commit.mjs +59 -5
  166. package/scripts/lib/hs-source-identity.mjs +526 -0
  167. package/scripts/lib/hs-source-log.mjs +11 -21
  168. package/scripts/lib/hs-source-push.mjs +107 -58
  169. package/scripts/lib/hs-worktree-args.mjs +115 -0
  170. package/scripts/lib/shared-path-helpers.mjs +134 -0
  171. package/scripts/lib/standalone-doctor.mjs +540 -3
  172. package/scripts/lib/subagent-git-lock.mjs +109 -0
  173. package/scripts/lib/template-pin.mjs +34 -0
  174. package/scripts/provenance-lib.mjs +91 -0
  175. package/scripts/stamp-provenance-on-edit.mjs +222 -0
  176. package/scripts/wip-ledger-health.mjs +653 -0
  177. package/spexharness/doctrine/baseline/SPEXCODE-DOCTRINE.baseline.json +29 -0
  178. package/spexharness/src/adapters/audit.mjs +197 -0
  179. package/spexharness/src/adapters/claude-code.mjs +441 -0
  180. package/spexharness/src/chat/tui/ansi.mjs +448 -0
  181. package/spexharness/src/chat/tui/palette.mjs +22 -0
  182. package/spexharness/src/chat/tui/select.mjs +208 -0
  183. package/spexharness/src/config-root.mjs +54 -0
  184. package/spexharness/src/context-budget.mjs +141 -0
  185. package/spexharness/src/dev-server-control.mjs +389 -0
  186. package/spexharness/src/doctrine/behaviour.mjs +371 -0
  187. package/spexharness/src/doctrine/corpus.mjs +733 -0
  188. package/spexharness/src/doctrine/inject.mjs +338 -0
  189. package/spexharness/src/doctrine/merge.mjs +184 -0
  190. package/spexharness/src/doctrine/sources.mjs +252 -0
  191. package/spexharness/src/doctrine/synthesize.mjs +401 -0
  192. package/spexharness/src/doctrine/vault-source.mjs +574 -0
  193. package/spexharness/src/doctrine/writer.mjs +176 -0
  194. package/spexharness/src/events.mjs +84 -0
  195. package/spexharness/src/gates/capture-workflow-gate.mjs +202 -0
  196. package/spexharness/src/gates/claim-before-code-gate.decide.mjs +33 -0
  197. package/spexharness/src/gates/claim-before-code-gate.mjs +129 -0
  198. package/spexharness/src/gates/deep-research-cost-gate.mjs +274 -0
  199. package/spexharness/src/gates/enforce-session-sequence.mjs +895 -0
  200. package/spexharness/src/gates/enforce-vault-naming.mjs +215 -0
  201. package/spexharness/src/gates/epic-milestone-phrasing-gate.mjs +233 -0
  202. package/spexharness/src/gates/git-identity-guard.decide.mjs +436 -0
  203. package/spexharness/src/gates/git-identity-guard.mjs +61 -0
  204. package/spexharness/src/gates/git-reflog-audit.mjs +151 -0
  205. package/spexharness/src/gates/git-stash-gate.decide.mjs +361 -0
  206. package/spexharness/src/gates/git-stash-gate.mjs +159 -0
  207. package/spexharness/src/gates/id-display-format-gate.mjs +190 -0
  208. package/spexharness/src/gates/inject-doctrine-gate.mjs +89 -0
  209. package/spexharness/src/gates/inject-vault-context.mjs +239 -0
  210. package/spexharness/src/gates/naughty-list-gate.mjs +213 -0
  211. package/spexharness/src/gates/plan-review-gate.mjs +233 -0
  212. package/spexharness/src/gates/preflight-reminder-gate.mjs +265 -0
  213. package/spexharness/src/gates/research-protocol-gate.mjs +175 -0
  214. package/spexharness/src/gates/settings-write-gate.decide.mjs +96 -0
  215. package/spexharness/src/gates/settings-write-gate.mjs +104 -0
  216. package/spexharness/src/git-reflog-audit.mjs +294 -0
  217. package/spexharness/src/intent-classifier.mjs +194 -0
  218. package/spexharness/src/intent-directive.mjs +541 -0
  219. package/spexharness/src/profile/learn.mjs +551 -0
  220. package/spexharness/src/profile/proposals.mjs +357 -0
  221. package/spexharness/src/profile/vault-inclusion.mjs +386 -0
  222. package/spexharness/src/redact.mjs +151 -0
  223. package/spexharness/src/rule-table.mjs +819 -0
  224. package/spexharness/src/seat-context.mjs +122 -0
  225. package/spexharness/src/session-bootstrap.mjs +512 -0
  226. package/spexharness/src/session-record.mjs +815 -0
  227. package/spexharness/src/token-inventory.mjs +207 -0
  228. package/vendor/heyspex-mcp/account-switch.mjs +95 -0
  229. package/vendor/heyspex-mcp/alignment-gate.mjs +315 -1
  230. package/vendor/heyspex-mcp/blocker-resolver.mjs +37 -2
  231. package/vendor/heyspex-mcp/breakdown-ladder.mjs +119 -4
  232. package/vendor/heyspex-mcp/breakdown-source.mjs +271 -0
  233. package/vendor/heyspex-mcp/breakdown-writer.mjs +23 -3
  234. package/vendor/heyspex-mcp/capture-buffer-patches.mjs +46 -5
  235. package/vendor/heyspex-mcp/capture-channel.mjs +55 -5
  236. package/vendor/heyspex-mcp/capture-console-client.mjs +80 -1
  237. package/vendor/heyspex-mcp/capture-focus.mjs +1035 -29
  238. package/vendor/heyspex-mcp/capture-outbox.mjs +5 -1
  239. package/vendor/heyspex-mcp/capture-tools.mjs +1219 -0
  240. package/vendor/heyspex-mcp/claim-tools.mjs +1124 -0
  241. package/vendor/heyspex-mcp/cli-verbs.mjs +2076 -0
  242. package/vendor/heyspex-mcp/cli.mjs +201 -15524
  243. package/vendor/heyspex-mcp/cloud-tool-surface.mjs +128 -0
  244. package/vendor/heyspex-mcp/cloud-vault-writes.mjs +412 -0
  245. package/vendor/heyspex-mcp/config-seed.yaml +15 -0
  246. package/vendor/heyspex-mcp/config-settings.mjs +290 -7
  247. package/vendor/heyspex-mcp/console-fetch.mjs +657 -16
  248. package/vendor/heyspex-mcp/console-transport.mjs +166 -0
  249. package/vendor/heyspex-mcp/credentials.mjs +584 -0
  250. package/vendor/heyspex-mcp/defer-spec.mjs +44 -4
  251. package/vendor/heyspex-mcp/deferred-reclaim.mjs +29 -3
  252. package/vendor/heyspex-mcp/design-handlers.mjs +31 -0
  253. package/vendor/heyspex-mcp/design-tools.mjs +4 -1
  254. package/vendor/heyspex-mcp/dev-factory-bridge.mjs +4 -4
  255. package/vendor/heyspex-mcp/dev-mode-core.mjs +45 -0
  256. package/vendor/heyspex-mcp/dev-tools.mjs +124 -0
  257. package/vendor/heyspex-mcp/diagnose-handlers.mjs +54 -0
  258. package/vendor/heyspex-mcp/events-tools.mjs +207 -0
  259. package/vendor/heyspex-mcp/filing-tools.mjs +688 -0
  260. package/vendor/heyspex-mcp/fleet-handlers.mjs +37 -0
  261. package/vendor/heyspex-mcp/fleet-queue-tools.mjs +69 -14
  262. package/vendor/heyspex-mcp/focus-tools.mjs +190 -0
  263. package/vendor/heyspex-mcp/folder-trust-store.mjs +59 -0
  264. package/vendor/heyspex-mcp/http-transport.mjs +300 -0
  265. package/vendor/heyspex-mcp/list-tools.mjs +257 -0
  266. package/vendor/heyspex-mcp/login-handoff.mjs +15 -1
  267. package/vendor/heyspex-mcp/mcp-config.mjs +94 -0
  268. package/vendor/heyspex-mcp/memory-tools.mjs +523 -0
  269. package/vendor/heyspex-mcp/mvp-mode.mjs +53 -6
  270. package/vendor/heyspex-mcp/occupied-keys.mjs +141 -0
  271. package/vendor/heyspex-mcp/on-deck-brief.mjs +3360 -0
  272. package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -0
  273. package/vendor/heyspex-mcp/package-root.mjs +32 -0
  274. package/vendor/heyspex-mcp/peer-presence.mjs +8 -1
  275. package/vendor/heyspex-mcp/plan-ground.mjs +621 -652
  276. package/vendor/heyspex-mcp/plan-push.mjs +906 -21
  277. package/vendor/heyspex-mcp/plan-review.mjs +124 -30
  278. package/vendor/heyspex-mcp/preflight-gate.mjs +0 -0
  279. package/vendor/heyspex-mcp/procedure-handlers.mjs +325 -0
  280. package/vendor/heyspex-mcp/procedure-tools.mjs +540 -351
  281. package/vendor/heyspex-mcp/promote-capture.mjs +386 -69
  282. package/vendor/heyspex-mcp/promote-diagnose.mjs +74 -0
  283. package/vendor/heyspex-mcp/promote-research-to-diagnose.mjs +9 -2
  284. package/vendor/heyspex-mcp/promote-research.mjs +660 -53
  285. package/vendor/heyspex-mcp/promotion-helpers.mjs +251 -0
  286. package/vendor/heyspex-mcp/prompts.mjs +1460 -0
  287. package/vendor/heyspex-mcp/read-path.mjs +29 -9
  288. package/vendor/heyspex-mcp/receipt-shape.mjs +146 -0
  289. package/vendor/heyspex-mcp/recipe-handlers.mjs +117 -0
  290. package/vendor/heyspex-mcp/regen-tasks.mjs +210 -3
  291. package/vendor/heyspex-mcp/register-spec.mjs +266 -54
  292. package/vendor/heyspex-mcp/request-context.mjs +59 -0
  293. package/vendor/heyspex-mcp/research-focus.mjs +207 -4
  294. package/vendor/heyspex-mcp/research-handlers.mjs +623 -0
  295. package/vendor/heyspex-mcp/research-tools.mjs +4 -4
  296. package/vendor/heyspex-mcp/response-budget.mjs +348 -0
  297. package/vendor/heyspex-mcp/review-focus.mjs +25 -1
  298. package/vendor/heyspex-mcp/search-specs.mjs +280 -0
  299. package/vendor/heyspex-mcp/seat-block-ledger.mjs +113 -0
  300. package/vendor/heyspex-mcp/seat-identity.mjs +2 -6
  301. package/vendor/heyspex-mcp/session-owner.mjs +108 -0
  302. package/vendor/heyspex-mcp/session-tools.mjs +152 -0
  303. package/vendor/heyspex-mcp/settings-registry.mjs +232 -0
  304. package/vendor/heyspex-mcp/settings-tools.mjs +85 -12
  305. package/vendor/heyspex-mcp/spec-edit-tools.mjs +446 -0
  306. package/vendor/heyspex-mcp/spec-id-lib.mjs +93 -17
  307. package/vendor/heyspex-mcp/split-research.mjs +301 -56
  308. package/vendor/heyspex-mcp/stdio-rpc.mjs +22 -0
  309. package/vendor/heyspex-mcp/submit-for-review.mjs +132 -6
  310. package/vendor/heyspex-mcp/summary-contract.mjs +105 -0
  311. package/vendor/heyspex-mcp/summary-write-tools.mjs +224 -0
  312. package/vendor/heyspex-mcp/symptom-query.mjs +146 -0
  313. package/vendor/heyspex-mcp/test-helpers.mjs +108 -1
  314. package/vendor/heyspex-mcp/tool-catalog.mjs +2813 -0
  315. package/vendor/heyspex-mcp/tool-dispatch.mjs +639 -0
  316. package/vendor/heyspex-mcp/tool-helpers.mjs +325 -0
  317. package/vendor/heyspex-mcp/validate-linkage.mjs +414 -27
  318. package/vendor/heyspex-mcp/vault-io.mjs +724 -0
  319. package/vendor/heyspex-mcp/vault-less-endpoints.mjs +52 -0
  320. package/vendor/heyspex-mcp/vault-resolve.mjs +366 -68
  321. package/vendor/heyspex-mcp/verify-reproduction.mjs +270 -30
  322. package/vendor/heyspex-mcp/whoami.mjs +91 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * symptom-query.mjs — turn a SYMPTOM SENTENCE into the small set of bounded
3
+ * queries `GET /api/search` can actually answer. Pure: no imports, no I/O, no
4
+ * console, so both the MCP tool and the console's own route suite can drive it.
5
+ *
6
+ * WHY THIS IS NOT "just pass the sentence through"
7
+ * ------------------------------------------------
8
+ * The console's ranked path is `websearch_to_tsquery('english', $q)`, and
9
+ * unquoted words in a websearch query are AND-ed. A real bug report —
10
+ *
11
+ * "row-level security policies exist but are not enforced at runtime;
12
+ * the database connection bypasses tenant isolation"
13
+ *
14
+ * — is ~12 content words AND-ed together, which matches NOTHING in any vault,
15
+ * then falls through to the trigram near-miss on `name`, which also matches
16
+ * nothing. The endpoint answers honestly ("searched, zero hits") and the agent
17
+ * concludes the symptom was never filed, files a duplicate, and the two
18
+ * existing specs stay invisible. That is [[HS-YFLS]] AC2.
19
+ *
20
+ * So a symptom search is a handful of ANGLES, not one query:
21
+ *
22
+ * phrase — the sentence verbatim (AND). Rarely hits; when it does, it is
23
+ * the highest-precision answer there is, so it is asked first.
24
+ * bigrams — the adjacent salient pairs, quoted, OR-ed. "tenant isolation"
25
+ * and "row-level security" are CONCEPTS; a quoted phrase makes
26
+ * `ts_rank_cd` reward the document that contains the concept over
27
+ * one that merely mentions both words a page apart.
28
+ * terms — every salient term, OR-ed. The recall floor. A document that
29
+ * matches eight of the twelve terms outranks one that matches two
30
+ * without any extra scoring on our side, because `ts_rank_cd`
31
+ * already counts matched lexemes and their density.
32
+ *
33
+ * Nothing here is a reranker and nothing is semantic — the substrate is
34
+ * lexical by decision ([[retrieval-substrate-lexical-not-embeddings-gate-b-no]]).
35
+ */
36
+
37
+ /**
38
+ * Words that carry no retrieval signal in a bug report. Deliberately larger
39
+ * than a generic English stoplist: the vocabulary of a SYMPTOM ("happens",
40
+ * "should", "instead", "error", "issue") is noise when every spec in the vault
41
+ * is about something going wrong.
42
+ */
43
+ const STOPWORDS = new Set([
44
+ 'a', 'an', 'the', 'and', 'or', 'but', 'not', 'nor', 'for', 'to', 'in', 'on', 'at', 'of',
45
+ 'with', 'without', 'from', 'by', 'as', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
46
+ 'do', 'does', 'did', 'has', 'have', 'had', 'can', 'cannot', 'could', 'will', 'would',
47
+ 'should', 'shall', 'may', 'might', 'must', 'this', 'that', 'these', 'those', 'it', 'its',
48
+ 'we', 'our', 'you', 'your', 'they', 'them', 'their', 'there', 'here', 'when', 'where',
49
+ 'what', 'which', 'who', 'whom', 'how', 'why', 'all', 'any', 'some', 'only', 'also',
50
+ 'still', 'then', 'than', 'so', 'if', 'else', 'into', 'onto', 'out', 'up', 'down', 'over',
51
+ 'under', 'again', 'more', 'most', 'much', 'very', 'just', 'now', 'get', 'gets', 'got',
52
+ // symptom filler — true of every bug, so it discriminates between none of them
53
+ 'bug', 'issue', 'problem', 'error', 'fail', 'fails', 'failed', 'failing', 'broken',
54
+ 'wrong', 'happens', 'happening', 'instead', 'expected', 'actual', 'repro', 'steps',
55
+ 'seems', 'looks', 'like', 'never', 'always', 'sometimes', 'exist', 'exists',
56
+ ]);
57
+
58
+ /** Minimum length for a token to count as salient (drops `is`, `an`, `ok`, `x`). */
59
+ const MIN_TERM_LENGTH = 3;
60
+
61
+ /** Hard ceiling on salient terms — a 400-word paste must not become a 400-term OR. */
62
+ export const DEFAULT_MAX_TERMS = 12;
63
+
64
+ /** Hard ceiling on quoted bigram phrases. */
65
+ export const DEFAULT_MAX_BIGRAMS = 6;
66
+
67
+ /**
68
+ * Split a symptom sentence into ordered, de-duplicated content terms.
69
+ * Hyphens and underscores are KEPT inside a word (`row-level`, `vault_id`,
70
+ * `HS-WRUQ`) because those are the highest-signal tokens a spec vault has;
71
+ * everything else becomes a separator.
72
+ */
73
+ export function extractSymptomTerms(text, { maxTerms = DEFAULT_MAX_TERMS } = {}) {
74
+ if (typeof text !== 'string' || !text.trim()) return [];
75
+ const raw = text
76
+ .toLowerCase()
77
+ .replace(/[^a-z0-9_-]+/g, ' ')
78
+ .split(' ')
79
+ .map((t) => t.replace(/^[-_]+|[-_]+$/g, ''))
80
+ .filter(Boolean);
81
+
82
+ const ordered = [];
83
+ const seen = new Set();
84
+ for (const token of raw) {
85
+ if (token.length < MIN_TERM_LENGTH) continue;
86
+ if (STOPWORDS.has(token)) continue;
87
+ if (/^\d+$/.test(token)) continue;
88
+ if (seen.has(token)) continue;
89
+ seen.add(token);
90
+ ordered.push(token);
91
+ }
92
+ if (ordered.length <= maxTerms) return ordered;
93
+
94
+ // Over the cap: keep the most SALIENT terms, then restore reading order so
95
+ // the bigram angle below still pairs words that were adjacent in the report.
96
+ // Saliency is length plus a bonus for a compound (`row-level`, `vault_id`) —
97
+ // a crude rarity proxy, but one that needs no corpus statistics and cannot
98
+ // go stale.
99
+ const salience = (t) => t.length + (/[-_]/.test(t) ? 4 : 0);
100
+ const keep = new Set(
101
+ [...ordered]
102
+ .sort((a, b) => salience(b) - salience(a) || ordered.indexOf(a) - ordered.indexOf(b))
103
+ .slice(0, maxTerms),
104
+ );
105
+ return ordered.filter((t) => keep.has(t));
106
+ }
107
+
108
+ /** `["tenant","isolation","neon"]` → `['"tenant isolation"', '"isolation neon"']`. */
109
+ export function buildBigrams(terms, { maxBigrams = DEFAULT_MAX_BIGRAMS } = {}) {
110
+ const pairs = [];
111
+ for (let i = 0; i + 1 < terms.length && pairs.length < maxBigrams; i += 1) {
112
+ pairs.push(`${terms[i]} ${terms[i + 1]}`);
113
+ }
114
+ return pairs;
115
+ }
116
+
117
+ /**
118
+ * The angles to ask `GET /api/search`, most precise first.
119
+ *
120
+ * Returns `{ terms, queries: [{ label, q }] }`. Queries are de-duplicated by
121
+ * `q`, so a one-word symptom collapses to a single request rather than three
122
+ * identical ones. An input with no salient terms at all (punctuation, pure
123
+ * stopwords) yields `queries: []` — the caller must treat that as "nothing
124
+ * searchable was asked", never as "nothing is filed".
125
+ */
126
+ export function buildSymptomQueries(text, { maxTerms = DEFAULT_MAX_TERMS, maxBigrams = DEFAULT_MAX_BIGRAMS } = {}) {
127
+ const phrase = typeof text === 'string' ? text.trim().replace(/\s+/g, ' ') : '';
128
+ const terms = extractSymptomTerms(phrase, { maxTerms });
129
+ const queries = [];
130
+ const pushed = new Set();
131
+ const push = (label, q) => {
132
+ if (!q || pushed.has(q)) return;
133
+ pushed.add(q);
134
+ queries.push({ label, q });
135
+ };
136
+
137
+ if (terms.length > 0) {
138
+ // Precision first: the sentence as written, AND-ed by websearch_to_tsquery.
139
+ if (phrase) push('phrase', phrase);
140
+ const bigrams = buildBigrams(terms, { maxBigrams });
141
+ if (bigrams.length > 0) push('bigrams', bigrams.map((b) => `"${b}"`).join(' or '));
142
+ push('terms', terms.join(' or '));
143
+ }
144
+
145
+ return { phrase, terms, queries };
146
+ }
@@ -2,9 +2,105 @@
2
2
  * Shared test helpers for the vendor MCP round-trip suites.
3
3
  */
4
4
 
5
- import { cpSync, existsSync, mkdirSync } from 'node:fs';
5
+ import { randomUUID } from 'node:crypto';
6
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
7
+ import { tmpdir } from 'node:os';
6
8
  import { join } from 'node:path';
7
9
 
10
+ import { resetActiveConfigCache } from './vault-resolve.mjs';
11
+
12
+ /**
13
+ * A hermetic `{ account, vault }` scope for tests that drive the REAL console
14
+ * transport ([[HS-MVG4-WW9R-TASK-mcp-and-hs-name-account-vault-on-every]]).
15
+ *
16
+ * Since invariant 2 the transport refuses LOCALLY (no fetch) when the cwd
17
+ * resolves to an unregistered root or the machine has no configured account.
18
+ * A test that hits a stand-in console through `consoleFetch` with the default
19
+ * header resolvers therefore depends on the machine it runs on — registered
20
+ * here, unregistered on CI — unless it pins both facts. This helper pins them:
21
+ *
22
+ * - a temp HOME (`HOME` + `USERPROFILE`, what `os.homedir()` reads) holding
23
+ * `~/.heyspex/registry.json` with the roots the test names,
24
+ * - `HS_ACCOUNT_ID` (tier 1 of session-owner.mjs) as the configured account,
25
+ * - optionally `HEYSPEX_VAULT_ROOT` pointed at a registered root.
26
+ *
27
+ * `registerRoot(root)` adds a row for a root a test mints later (its own temp
28
+ * vault) and resets the memoized resolver so the next request sees it. Call
29
+ * `restore()` in `after`/`afterEach`. Nothing here touches the real profile.
30
+ *
31
+ * Row ids are UUIDs (what the console mints): the wire gate treats a non-UUID
32
+ * registry id as a legacy slug and looks for the repo's heyspex.yaml
33
+ * `vault_id` instead — a slug row with no marker is refused, by design.
34
+ *
35
+ * @param {{ accountId?: string, vaultId?: string, repoRootAbs?: string, activeVaultId?: string | null }} [opts]
36
+ * `repoRootAbs`: register this root as `vaultId` and set `HEYSPEX_VAULT_ROOT` to it.
37
+ * `activeVaultId`: the registry's active pointer (defaults to `vaultId` when a root is registered).
38
+ */
39
+ export function installScopedConsoleEnv({
40
+ accountId = 'acct_test',
41
+ vaultId = randomUUID(),
42
+ repoRootAbs,
43
+ activeVaultId,
44
+ } = {}) {
45
+ const ENV_KEYS = ['HOME', 'USERPROFILE', 'HS_ACCOUNT_ID', 'HEYSPEX_VAULT_ROOT'];
46
+ const saved = Object.fromEntries(ENV_KEYS.map((k) => [k, process.env[k]]));
47
+ const home = mkdtempSync(join(tmpdir(), 'hs-scoped-home-'));
48
+ const registryDir = join(home, '.heyspex');
49
+ mkdirSync(registryDir, { recursive: true });
50
+ const registryPath = join(registryDir, 'registry.json');
51
+ // Registry schema v2 (HS-RXLG): the pointer is the signed-in account's row.
52
+ const registry = {
53
+ schema_version: 2,
54
+ current_account_id: accountId,
55
+ accounts: {
56
+ [accountId]: {
57
+ active_vault_id: activeVaultId ?? (repoRootAbs ? vaultId : null),
58
+ active_workspace_id: null,
59
+ },
60
+ },
61
+ workspaces: [],
62
+ vaults: [],
63
+ };
64
+ const flush = () => {
65
+ writeFileSync(registryPath, JSON.stringify(registry, null, 2));
66
+ resetActiveConfigCache();
67
+ };
68
+
69
+ const registerRoot = (root, id) => {
70
+ const rowId = id ?? randomUUID();
71
+ registry.vaults = registry.vaults.filter((v) => v.id !== rowId && v.repo_root_abs !== root);
72
+ registry.vaults.push({ id: rowId, repo_root_abs: root, account_id: accountId });
73
+ flush();
74
+ return rowId;
75
+ };
76
+
77
+ process.env.HOME = home;
78
+ process.env.USERPROFILE = home;
79
+ process.env.HS_ACCOUNT_ID = accountId;
80
+ if (repoRootAbs) {
81
+ registerRoot(repoRootAbs, vaultId);
82
+ process.env.HEYSPEX_VAULT_ROOT = repoRootAbs;
83
+ } else {
84
+ flush();
85
+ }
86
+
87
+ return {
88
+ home,
89
+ accountId,
90
+ vaultId,
91
+ registryPath,
92
+ registerRoot,
93
+ restore() {
94
+ for (const k of ENV_KEYS) {
95
+ if (saved[k] === undefined) delete process.env[k];
96
+ else process.env[k] = saved[k];
97
+ }
98
+ resetActiveConfigCache();
99
+ rmSync(home, { recursive: true, force: true });
100
+ },
101
+ };
102
+ }
103
+
8
104
  /**
9
105
  * Mirror into a temp vault every real-repo file that
10
106
  * `scripts/validate-vault-ids.mjs` needs to run in isolation.
@@ -19,6 +115,13 @@ import { join } from 'node:path';
19
115
  * id-suffix-shape rule, compiled from protocol frontmatter), so that file must be
20
116
  * mirrored too or the engine import dangles.
21
117
  *
118
+ * Post repo-split, `scripts/lib/resolve-validate-root.mjs` (copied wholesale as
119
+ * part of `scripts/`) also imports `vendor/heyspex-mcp/vault-resolve.mjs` by a
120
+ * relative path OUTSIDE `scripts/` — that file must be mirrored too, or the
121
+ * copied validator dangles with MODULE_NOT_FOUND the moment it computes its
122
+ * own default root. `vault-resolve.mjs` itself has no further local-file
123
+ * imports (node builtins only), so this is a single extra copy, not a chain.
124
+ *
22
125
  * Single source of truth: the next dependency the validator grows is a one-line
23
126
  * add here, not a silent breakage in every round-trip test.
24
127
  *
@@ -28,6 +131,10 @@ import { join } from 'node:path';
28
131
  export function mirrorValidatorDeps(repoRoot, root) {
29
132
  cpSync(join(repoRoot, 'scripts'), join(root, 'scripts'), { recursive: true });
30
133
 
134
+ const vaultResolveRel = join('vendor', 'heyspex-mcp', 'vault-resolve.mjs');
135
+ mkdirSync(join(root, 'vendor', 'heyspex-mcp'), { recursive: true });
136
+ cpSync(join(repoRoot, vaultResolveRel), join(root, vaultResolveRel));
137
+
31
138
  const indexerRel = join('spex-console', 'src', 'lib', 'indexer');
32
139
  mkdirSync(join(root, indexerRel), { recursive: true });
33
140
  for (const file of [