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,37 @@
1
+ // heyspex_fleet_queue_* handlers (thin wrappers over fleet-queue-tools.mjs). Extracted from cli.mjs under
2
+ // [[HS-BLNF-6W61-DIAG-cli-mjs-has-grown-into-a-13-665-line]] (no behavior change).
3
+
4
+ import { executeFleetQueueClaim, executeFleetQueueComplete, executeFleetQueueList, executeFleetQueueSubmit, resolveFleetToken } from './fleet-queue-tools.mjs';
5
+ import { consoleFetch } from './console-transport.mjs';
6
+
7
+ export async function handleFleetQueueSubmit(args = {}) {
8
+ const result = await executeFleetQueueSubmit({ consoleFetch, args, fleetToken: resolveFleetToken() });
9
+ return {
10
+ isError: !!result.error,
11
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
12
+ };
13
+ }
14
+
15
+ export async function handleFleetQueueClaim(args = {}) {
16
+ const result = await executeFleetQueueClaim({ consoleFetch, args, fleetToken: resolveFleetToken() });
17
+ return {
18
+ isError: !!result.error,
19
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
20
+ };
21
+ }
22
+
23
+ export async function handleFleetQueueComplete(args = {}) {
24
+ const result = await executeFleetQueueComplete({ consoleFetch, args, fleetToken: resolveFleetToken() });
25
+ return {
26
+ isError: !!result.error,
27
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
28
+ };
29
+ }
30
+
31
+ export async function handleFleetQueueList(args = {}) {
32
+ const result = await executeFleetQueueList({ consoleFetch, args, fleetToken: resolveFleetToken() });
33
+ return {
34
+ isError: !!result.error,
35
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
36
+ };
37
+ }
@@ -66,32 +66,88 @@ function bearerHeaders(fleetToken) {
66
66
  }
67
67
 
68
68
  /**
69
- * HS-FS7X (interim only — full fix is the unstarted [[HS-XOCK-60Z1-EPIC-fleet-work-queue-in-heyspex]]
70
- * child tasks). This route writes to the Neon-backed `/api/fleet/queue`, but
69
+ * HS-FS7X — SUBMIT REFUSES WHILE NO RUNNER DRAINS THE NEON QUEUE.
70
+ *
71
+ * This tool writes to the Neon-backed `/api/fleet/queue`, but
71
72
  * `dev-factory/lib/fleet-queue-runner.mjs` — the only thing that actually
72
73
  * claims and executes queued work — still reads exclusively from the legacy
73
- * file-backed queue (`dev-factory/lib/fleet-queue.mjs`). A submit through this
74
- * tool therefore succeeds and is durably recorded, but nothing will ever pick
75
- * it up. Every success return carries this warning so a caller cannot mistake
76
- * "accepted" for "will run" until the runner is repointed at Neon.
74
+ * file-backed queue (`dev-factory/lib/fleet-queue.mjs`). Work submitted here
75
+ * would be durably recorded and never executed.
76
+ *
77
+ * It previously returned `{ id, position, warning }`. That was not enough: a
78
+ * warning riding along on a SUCCESS shape is still a success —
79
+ * `handleFleetQueueSubmit` sets `isError: !!result.error`, so the MCP client
80
+ * saw a green tool call and an agent that submits and moves on never learned
81
+ * the work was dropped. So the submit now REFUSES before the round trip and
82
+ * writes nothing at all — same shape as the argument refusal in
83
+ * [[HS-IRX0-5KFT-DIAG-record-wip-silently-drops-the-summary]]: refuse at the
84
+ * boundary, because a queue with no consumer cannot refuse on your behalf.
85
+ *
86
+ * Repointing the runner at Neon is a separate BUILD, already owned by
87
+ * [[HS-XOCK-60Z1-EPIC-fleet-work-queue-in-heyspex]] (HS-ZXKF: runner -> queue
88
+ * API; HS-HQP1: make fleet-queue.mjs actually select a backend). When that
89
+ * lands, delete this gate. Meanwhile a machine whose runner genuinely does
90
+ * drain Neon opts back in with HEYSPEX_FLEET_QUEUE_RUNNER_READY=1 — an
91
+ * explicit, per-machine assertion, never something a caller can set per-call.
92
+ */
93
+ export const NEON_QUEUE_RUNNER_READY_ENV = 'HEYSPEX_FLEET_QUEUE_RUNNER_READY';
94
+
95
+ export const NEON_QUEUE_NO_RUNNER_MESSAGE =
96
+ 'Refusing to submit — nothing was submitted. The Neon fleet queue has NO runner reading it: ' +
97
+ 'dev-factory/lib/fleet-queue-runner.mjs still reads only the legacy JSONL file queue ' +
98
+ '(dev-factory/lib/fleet-queue.mjs), so anything accepted here would be durably recorded and ' +
99
+ 'NEVER executed. Track: [[HS-XOCK-60Z1-EPIC-fleet-work-queue-in-heyspex]] (HS-ZXKF repoints the ' +
100
+ 'runner; HS-HQP1 makes the fleet-queue facade select a backend). To run work now, enqueue against ' +
101
+ 'dev-factory/lib/fleet-queue.mjs directly — that is the queue the runner reads. If this ' +
102
+ 'machine’s ' +
103
+ `runner really does drain the Neon queue, set ${NEON_QUEUE_RUNNER_READY_ENV}=1 to re-enable submits.`;
104
+
105
+ /**
106
+ * True only when this machine asserts its runner drains the Neon fleet queue.
107
+ * Defaults to false: the safe answer to "will this run?" is "no".
108
+ *
109
+ * @param {Record<string, string|undefined>} [env]
77
110
  */
78
- export const NEON_QUEUE_NO_RUNNER_WARNING =
79
- 'Neon fleet queue has NO runner reading it yet — dev-factory/lib/fleet-queue-runner.mjs ' +
80
- 'still only reads the legacy JSONL file queue. This item is durably recorded in Neon but ' +
81
- 'will NOT execute until HS-XOCK-60Z1 (fleet-queue Neon port) lands. Track: ' +
82
- '[[HS-XOCK-60Z1-EPIC-fleet-work-queue-in-heyspex]].';
111
+ export function neonQueueRunnerReady(env = process.env) {
112
+ const raw = env?.[NEON_QUEUE_RUNNER_READY_ENV];
113
+ if (typeof raw !== 'string') return false;
114
+ const v = raw.trim().toLowerCase();
115
+ return v === '1' || v === 'true' || v === 'yes' || v === 'on';
116
+ }
83
117
 
84
118
  /**
85
119
  * `heyspex_fleet_queue_submit` — POST /api/fleet/queue. `kind` defaults to
86
120
  * "task" when omitted (the API requires a kind string).
87
121
  *
88
- * @param {{ consoleFetch: Function, args?: Record<string, unknown>, fleetToken?: string|null }} ctx
122
+ * HS-FS7X: refuses outright (writing nothing) unless this machine asserts a
123
+ * runner actually drains the Neon queue — see NEON_QUEUE_NO_RUNNER_MESSAGE.
124
+ *
125
+ * @param {{ consoleFetch: Function, args?: Record<string, unknown>, fleetToken?: string|null, env?: Record<string, string|undefined> }} ctx
89
126
  */
90
- export async function executeFleetQueueSubmit({ consoleFetch, args = {}, fleetToken = null }) {
127
+ export async function executeFleetQueueSubmit({
128
+ consoleFetch,
129
+ args = {},
130
+ fleetToken = null,
131
+ env = process.env,
132
+ }) {
91
133
  const lane = typeof args.lane === 'string' ? args.lane.trim() : '';
92
134
  if (!lane) {
93
135
  return { error: 'INVALID_ARGS', message: 'lane is required' };
94
136
  }
137
+
138
+ // HS-FS7X: refuse BEFORE the round trip. Accepting work into a queue with no
139
+ // consumer and returning `{ id, position }` told the caller it would run.
140
+ if (!neonQueueRunnerReady(env)) {
141
+ return {
142
+ error: 'NO_RUNNER',
143
+ message: NEON_QUEUE_NO_RUNNER_MESSAGE,
144
+ lane,
145
+ submitted: false,
146
+ track: 'HS-XOCK-60Z1',
147
+ override_env: NEON_QUEUE_RUNNER_READY_ENV,
148
+ };
149
+ }
150
+
95
151
  const kind = typeof args.kind === 'string' && args.kind.trim() ? args.kind.trim() : 'task';
96
152
 
97
153
  const body = {
@@ -120,7 +176,6 @@ export async function executeFleetQueueSubmit({ consoleFetch, args = {}, fleetTo
120
176
  id: result.body?.id ?? null,
121
177
  position: result.body?.position ?? null,
122
178
  lane,
123
- warning: NEON_QUEUE_NO_RUNNER_WARNING,
124
179
  };
125
180
  }
126
181
 
@@ -0,0 +1,190 @@
1
+ // Focus pin and focus-area tools (heyspex_pin_get/pin_set, set/get/clear_focus_areas,
2
+ // resolve_focus_intent) plus the Neon focus-pin injector the on-deck brief uses. Extracted from cli.mjs under
3
+ // [[HS-BLNF-6W61-DIAG-cli-mjs-has-grown-into-a-13-665-line]] (no behavior change).
4
+ //
5
+ // ─────────────────────────────────────────────────────────────────────────────
6
+ // Focus-areas CRUD — [[HS-TASK-1GQ5]] / [[HS-EPIC-7WJN]] / [[HS-MIL-AFOC]].
7
+ // Agent-controllable roadmap focus: distinct from the on-deck pin tools above
8
+ // (heyspex_pin_get/set, the `focus_pin` table). These write the `focus_areas`
9
+ // control plane (tag-band) via the console route, which tier-dispatches Neon vs
10
+ // the free-tier `.heyspex/focus-areas.yaml`.
11
+ // ─────────────────────────────────────────────────────────────────────────────
12
+
13
+ import { consoleFetch, controlError, controlOk } from './console-transport.mjs';
14
+ import { detectAgentIdentity, getConfig } from './mcp-config.mjs';
15
+ import { buildDegradedFocusResult, resolveFocusIntentLexical } from './plan-ground.mjs';
16
+
17
+ /**
18
+ * Fetch the Neon layer-1 focus pin (the console-set fuse pin) and inject its
19
+ * spec_id into args as `neon_pin_spec_id`, so the synchronous on-deck resolver
20
+ * can let it win precedence #1 over the file's layer-2 `spec_path`.
21
+ *
22
+ * Why this exists: per [[heyspex-is-saas-cloud-primary-both-tiers]] the focus
23
+ * pin lives in Neon `focus_pin` on a Neon-up machine; the file `pin:` block is
24
+ * dormant. So vendor /hs-next — which reads the file synchronously — was blind to
25
+ * a console-set pin (the documented FCPN paid-tier soak gap). We do the one async
26
+ * read here, at the handler boundary, and thread the result down as a sync option.
27
+ *
28
+ * Best-effort: any unreachable / non-OK (incl. free-tier 402) / empty response
29
+ * leaves args untouched, so the resolver falls back to the file focus. Never
30
+ * throws, never blocks /hs-next. Skipped when `ignore_focus` or an explicit
31
+ * `spec_id` (direct lookup, no focus) is set. The GET response is
32
+ * `{ pin: FocusPinRow | null }` with camelCase keys (`pin.specId`).
33
+ */
34
+ export async function injectNeonFocusPin(args = {}) {
35
+ if (args.ignore_focus === true || args.ignore_focus === 'true') return args;
36
+ if (args.spec_id) return args;
37
+ let next = args;
38
+ try {
39
+ // Both focus layers live in Neon on a Neon-up machine (the file is dormant):
40
+ // layer-1 pin in `focus_pin`, layer-2 selection in `focus_selection`
41
+ // ([[HS-TASK-FCPN]] + [[HS-TASK-FSEL]]). Fetch both, thread the ids into the
42
+ // sync resolver. Responses are `{ pin }` / `{ selection }` with camelCase keys.
43
+ const [pinRes, selRes] = await Promise.all([
44
+ consoleFetch('/api/control/pin'),
45
+ consoleFetch('/api/control/focus-selection'),
46
+ ]);
47
+ const pinId = pinRes && pinRes.ok ? pinRes.body?.pin?.specId : null;
48
+ if (typeof pinId === 'string' && pinId) next = { ...next, neon_pin_spec_id: pinId };
49
+ const selPath = selRes && selRes.ok ? selRes.body?.selection?.specPath : null;
50
+ if (typeof selPath === 'string' && selPath) {
51
+ next = { ...next, neon_selection_spec_path: selPath };
52
+ }
53
+ } catch {
54
+ // Swallow — fall back to the file focus. Focus is a steering nicety, not a
55
+ // correctness gate; an unreachable console must not break /hs-next.
56
+ }
57
+ return next;
58
+ }
59
+
60
+ export async function handlePinGet(_args = {}) {
61
+ const result = await consoleFetch('/api/control/pin');
62
+ if (!result.ok) return controlError(result, 'PIN_GET_FAILED');
63
+ return controlOk(result.body);
64
+ }
65
+
66
+ /**
67
+ * Set or clear the on-deck pin — [[HS-EPIC-NCPS]] focus_pin / [[HS-EPIC-CLMW]]
68
+ * `hs pin set` / `hs pin clear`. PUT /api/control/pin. Omit/null
69
+ * spec_id clears (mirrors the endpoint's null-clears contract).
70
+ */
71
+ export async function handlePinSet(args = {}) {
72
+ const identity = detectAgentIdentity(args.agent, args.session_id);
73
+ const pinnedBy =
74
+ typeof args.pinned_by === 'string' && args.pinned_by.trim()
75
+ ? args.pinned_by.trim()
76
+ : identity.agent;
77
+ const clearing = args.spec_id === undefined || args.spec_id === null;
78
+ const body = clearing
79
+ ? { spec_id: null, pinned_by: pinnedBy }
80
+ : {
81
+ spec_id: args.spec_id,
82
+ pinned_by: pinnedBy,
83
+ ...(args.ttl_ms !== undefined ? { ttl_ms: args.ttl_ms } : {}),
84
+ };
85
+ const result = await consoleFetch('/api/control/pin', { method: 'PUT', body });
86
+ if (!result.ok) return controlError(result, 'PIN_SET_FAILED', { spec_id: args.spec_id ?? null });
87
+ return controlOk(result.body);
88
+ }
89
+
90
+ /** `heyspex_set_focus_areas` → PUT /api/scheduler/focus-areas. */
91
+ export async function handleSetFocusAreas(args = {}) {
92
+ const tags = Array.isArray(args.tags)
93
+ ? args.tags.map((t) => String(t).trim()).filter(Boolean)
94
+ : [];
95
+ if (tags.length === 0) {
96
+ return controlError(
97
+ { ok: false, status: 0, url: '', body: null },
98
+ 'FOCUS_AREAS_INVALID',
99
+ { reason: 'tags must be a non-empty array of known-tag strings' },
100
+ );
101
+ }
102
+ const identity = detectAgentIdentity(args.agent, args.session_id);
103
+ const body = {
104
+ rationale: typeof args.rationale === 'string' ? args.rationale : '',
105
+ tags,
106
+ updated_by: `${identity.agent} · ${identity.sessionId}`,
107
+ ...(typeof args.expires_at === 'string' ? { expires_at: args.expires_at } : {}),
108
+ ...(typeof args.replace === 'boolean' ? { replace: args.replace } : {}),
109
+ };
110
+ const result = await consoleFetch('/api/scheduler/focus-areas', { method: 'PUT', body });
111
+ if (!result.ok) return controlError(result, 'FOCUS_AREAS_SET_FAILED');
112
+ return controlOk(result.body);
113
+ }
114
+
115
+ /** `heyspex_get_focus_areas` → GET /api/scheduler/focus-areas. */
116
+ export async function handleGetFocusAreas(_args = {}) {
117
+ const result = await consoleFetch('/api/scheduler/focus-areas');
118
+ if (!result.ok) return controlError(result, 'FOCUS_AREAS_GET_FAILED');
119
+ return controlOk(result.body);
120
+ }
121
+
122
+ /** `heyspex_clear_focus_areas` → DELETE /api/scheduler/focus-areas. */
123
+ export async function handleClearFocusAreas(_args = {}) {
124
+ const result = await consoleFetch('/api/scheduler/focus-areas', { method: 'DELETE' });
125
+ if (!result.ok) return controlError(result, 'FOCUS_AREAS_CLEAR_FAILED');
126
+ return controlOk(result.body);
127
+ }
128
+
129
+ /**
130
+ * `heyspex_resolve_focus_intent` — Phase-B lexical resolver ([[HS-TASK-JZEJ]] /
131
+ * [[HS-EPIC-OOPW]]). Runs lexical wide-recall through the console search
132
+ * endpoint and returns a wide candidate window ({ spec, tags, why }[]) for the
133
+ * calling agent's LLM to rerank into focus tags — HeySpex hosts no reranker. No
134
+ * embeddings (Gate B = NO,
135
+ * [[retrieval-substrate-lexical-not-embeddings-gate-b-no]]). Retrieval is
136
+ * bounded `GET /api/search` / `GET /api/specs?tag=` pages, never a vault walk
137
+ * ([[HS-YIR1-8KK5-TASK-planning-grounding-retrieves-through]]). An
138
+ * empty/no-match window is returned (not an error) so the caller can fall back
139
+ * to heyspex_list_known_tags + exact-tag NLU.
140
+ *
141
+ * Graceful degradation ([[HS-TASK-09I3]] / focus-intent-routing §6): if the
142
+ * lexical recall primitive is unreachable or throws (console down, 401, 5xx),
143
+ * the resolver core returns a `degraded` marker rather
144
+ * than raising. The try/catch here is belt-and-suspenders — a throw outside the
145
+ * core's own guard must still resolve to a graceful degraded result so the MCP
146
+ * tool never 500s a focus pivot.
147
+ */
148
+ export async function handleResolveFocusIntent(args = {}) {
149
+ const { vaultRoot } = getConfig();
150
+ let result;
151
+ try {
152
+ result = await resolveFocusIntentLexical(vaultRoot, {
153
+ utterance: args.utterance,
154
+ top_k: args.top_k,
155
+ });
156
+ } catch (err) {
157
+ result = buildDegradedFocusResult(args.utterance, err);
158
+ }
159
+ return controlOk(result);
160
+ }
161
+
162
+ /**
163
+ * Persist the /hs-push-next layer-2 focus selection to Neon `focus_selection`
164
+ * via the console endpoint (so the console On-Deck AND vendor /hs-next, both of
165
+ * which read Neon, see it). There is NO `.heyspex/focus.yaml` fallback
166
+ * ([[no-fallbacks-by-default]]; the focus.yaml-fallback incident) — Neon is the
167
+ * only focus store. If the console is unreachable the push itself still
168
+ * succeeds (the spec files were written); the focus pin just isn't set, and we
169
+ * say so on stderr rather than writing a stale file. Returns true iff focus was
170
+ * persisted.
171
+ */
172
+ export async function writeFocusSelectionVendor(vaultRoot, specPath, reason) {
173
+ try {
174
+ const res = await consoleFetch('/api/control/focus-selection', {
175
+ method: 'PUT',
176
+ body: { spec_path: specPath, kind: 'epic', picked_by: 'human', reason: reason ?? null },
177
+ });
178
+ if (res && res.ok) return true;
179
+ console.error(
180
+ `[hs-push] focus not pinned — console returned ${res?.status ?? 'no response'} for PUT /api/control/focus-selection. ` +
181
+ 'Neon is required (no focus.yaml fallback); re-pin once the console is reachable.',
182
+ );
183
+ } catch (err) {
184
+ console.error(
185
+ `[hs-push] focus not pinned — console unreachable (${err?.message ?? err}). ` +
186
+ 'Neon is required (no focus.yaml fallback); re-pin once the console is reachable.',
187
+ );
188
+ }
189
+ return false;
190
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Machine-local folder-trust store I/O — plain ESM.
3
+ *
4
+ * `~/.heyspex/trusted-folders.json` ([[HS-ZBEB-8CMW-TASK-folder-trust-for-spexcode-first-run]])
5
+ * is read by two fronts that cannot share a TypeScript import:
6
+ * - `spex-console/src/lib/cli/folder-trust.ts` (the CLI/`hs chat`/`hs trust` home — TypeScript,
7
+ * run through tsx) — its own decision functions (`resolveFolderTrust`, `recordFolderTrust`,
8
+ * `revokeFolderTrust`, …) stay there; it imports and RE-EXPORTS the I/O functions below
9
+ * rather than re-implementing them.
10
+ * - `vendor/heyspex-mcp/config-settings.mjs` (the settings capability's core — plain ESM, no
11
+ * TypeScript toolchain available) — reads this store directly to answer
12
+ * `heyspex_settings_get("agent_behaviors.spexcode.trusted_folders")`
13
+ * ([[HS-25AJ-8CMW-TASK-folder-trust-revoke-through-the]]).
14
+ *
15
+ * This file is the ONE place this I/O is implemented — never duplicated. Storage contract
16
+ * (realpath-keyed, `{ trusted, decidedAt }` entries) is documented in full in folder-trust.ts's
17
+ * own header; this file only carries the read/write/list primitives, not the trust decision
18
+ * logic itself.
19
+ */
20
+ import fs from 'node:fs';
21
+ import os from 'node:os';
22
+ import path from 'node:path';
23
+
24
+ /** `~/.heyspex/trusted-folders.json`, relative to a home directory. */
25
+ export const TRUST_STORE_REL = path.join('.heyspex', 'trusted-folders.json');
26
+
27
+ /** Resolve the real trust-store path for a given home directory (defaults to `os.homedir()`). */
28
+ export function trustStorePath(home = os.homedir()) {
29
+ return path.join(home, TRUST_STORE_REL);
30
+ }
31
+
32
+ /** Read the trust store from disk. A missing/corrupt/unreadable file reads as an empty store
33
+ * (fail open to "nothing answered yet", never a crash). */
34
+ export function readTrustStore(filePath = trustStorePath()) {
35
+ try {
36
+ const raw = fs.readFileSync(filePath, 'utf8');
37
+ const parsed = JSON.parse(raw);
38
+ return parsed && typeof parsed === 'object' ? parsed : {};
39
+ } catch {
40
+ return {};
41
+ }
42
+ }
43
+
44
+ /** Write the trust store to disk, creating `~/.heyspex/` if needed. */
45
+ export function writeTrustStore(store, filePath = trustStorePath()) {
46
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
47
+ fs.writeFileSync(filePath, `${JSON.stringify(store, null, 2)}\n`, 'utf8');
48
+ }
49
+
50
+ /** List every stored trust decision as a flat array — the read-only listing surface both
51
+ * `hs trust list` and the `agent_behaviors.spexcode.trusted_folders` settings-capability
52
+ * computed key format their output from. */
53
+ export function listTrustedFolders(store) {
54
+ return Object.entries(store).map(([key, entry]) => ({
55
+ path: key,
56
+ trusted: entry.trusted,
57
+ decidedAt: entry.decidedAt,
58
+ }));
59
+ }