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,262 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * bin/spexcode.mjs — `spexcode`, the named PATH launcher for SpexCode
4
+ * (formerly SpexHarness — historical name only), HeySpex's own agent
5
+ * harness. Running `spexcode` opens a SpexCode session: it forwards to the
6
+ * chat front (`hs chat`, i.e. `bin/hs.mjs`'s `chat` verb dispatching to
7
+ * `spex-console/bin/hs-chat.ts`).
8
+ *
9
+ * Epic: [[HS-MSDL-8CMW-EPIC-spexcode-ships-as-a-named-path-launcher]]
10
+ * Research: [[HS-TJKO-8CMW-RES-spexcode-powershell-launcher-seat-model]]
11
+ *
12
+ * This file is a THIN forwarder plus a `--seat` guard — no chat-loop code,
13
+ * no model logic. Every real behavior lives in `hs chat`
14
+ * (spex-console/src/lib/cli/chat.ts, spexharness/src/chat/*.mjs).
15
+ *
16
+ * Two responsibilities, both pure-testable (see the exported functions
17
+ * below, exercised without spawning a real process or touching the real
18
+ * `dev-factory/seats.json`):
19
+ *
20
+ * 1. HS-FWTK — before forwarding, if `--seat <id>` / `--seat=<id>` is
21
+ * present, refuse the seat when its `dev-factory/seats.json`
22
+ * `fill.backends[]` has no entry whose `kind` is `SPEXCODE_BACKEND_KIND`
23
+ * ("spexcode") — naming the seat and the exact registry entry to change
24
+ * — and exit 2 without spawning anything.
25
+ * 2. HS-HF24 — otherwise, spawn `node bin/hs.mjs chat <...args>` with every
26
+ * argument forwarded unchanged, `stdio: "inherit"`, SIGINT forwarded to
27
+ * the child so its own graceful-shutdown path
28
+ * (spexharness/src/chat/chat-sigint.mjs) runs, and exit with the
29
+ * child's exit code (or 1 on a spawn error).
30
+ */
31
+ import { spawn } from "node:child_process";
32
+ import { readFileSync, realpathSync } from "node:fs";
33
+ import path from "node:path";
34
+ import process from "node:process";
35
+ import { fileURLToPath } from "node:url";
36
+
37
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
38
+
39
+ /** `bin/hs.mjs`, resolved relative to THIS file (not the caller's cwd) so
40
+ * `spexcode` works from any directory once it's on PATH. */
41
+ const HS_PATH = path.join(__dirname, "hs.mjs");
42
+
43
+ /** `dev-factory/seats.json`, resolved relative to this file's location
44
+ * (`bin/` is one level under the repo root). */
45
+ const SEATS_JSON_PATH = path.resolve(__dirname, "..", "dev-factory", "seats.json");
46
+
47
+ /**
48
+ * The `fill.backends[].kind` string a seat must carry for `spexcode` to
49
+ * launch it. NOT one of the coarse categories already in use elsewhere in
50
+ * `seats.json` (`cloud-api`, `ide-session`, `local`, `human`, `routed`) —
51
+ * per research §"Seats & Backend Routing", "SpexCode is one more backend
52
+ * kind alongside Claude Code, Cursor, OpenCode, and CommandCode", so it gets
53
+ * its own kind value rather than overloading an existing category. The
54
+ * `coo-phase-1` seat's `fill.backends[]` carries a `"spexcode/hs-chat"`
55
+ * entry with this kind (see dev-factory/seats.json) so bare `spexcode` and
56
+ * `spexcode --seat coo-phase-1` are never refused.
57
+ */
58
+ export const SPEXCODE_BACKEND_KIND = "spexcode";
59
+
60
+ /** Every argument `spexcode` receives is forwarded unchanged to `hs chat`. */
61
+ export function buildForwardArgs(argv) {
62
+ return ["chat", ...argv];
63
+ }
64
+
65
+ /**
66
+ * Parse `--seat X` or `--seat=X` out of argv. Returns `null` when no `--seat`
67
+ * flag is present (or `--seat` is the last token with no value — `hs chat`'s
68
+ * own parser reports that usage error once forwarded).
69
+ */
70
+ export function parseSeatArg(argv) {
71
+ for (let i = 0; i < argv.length; i += 1) {
72
+ const token = argv[i];
73
+ if (token === "--seat") {
74
+ const value = argv[i + 1];
75
+ return value !== undefined ? value : null;
76
+ }
77
+ if (token.startsWith("--seat=")) {
78
+ return token.slice("--seat=".length);
79
+ }
80
+ }
81
+ return null;
82
+ }
83
+
84
+ /** Find a seat by id in a parsed `seats.json` object. `null` when absent. */
85
+ export function findSeat(seatsRegistry, seatId) {
86
+ const seats = Array.isArray(seatsRegistry?.seats) ? seatsRegistry.seats : [];
87
+ return seats.find((seat) => seat && seat.id === seatId) ?? null;
88
+ }
89
+
90
+ /** True when `seat.fill.backends[]` has an entry with kind `"spexcode"`. */
91
+ export function seatHasSpexCodeBackend(seat) {
92
+ const backends = Array.isArray(seat?.fill?.backends) ? seat.fill.backends : [];
93
+ return backends.some((backend) => backend && backend.kind === SPEXCODE_BACKEND_KIND);
94
+ }
95
+
96
+ /**
97
+ * Decide whether `argv` (as given to `spexcode`, before the `chat` verb is
98
+ * prepended) may forward to `hs chat`. Pure — takes the already-parsed
99
+ * `seats.json` object so tests never touch the filesystem.
100
+ *
101
+ * @param {readonly string[]} argv
102
+ * @param {{seats?: Array}} seatsRegistry
103
+ * @returns {{allowed: true} | {allowed: false, message: string}}
104
+ */
105
+ export function resolveSeatGuard(argv, seatsRegistry) {
106
+ const seatId = parseSeatArg(argv);
107
+ if (seatId === null) return { allowed: true };
108
+
109
+ const seat = findSeat(seatsRegistry, seatId);
110
+ if (seat && seatHasSpexCodeBackend(seat)) {
111
+ return { allowed: true };
112
+ }
113
+
114
+ const reason = seat
115
+ ? `has no fill.backends[] entry with kind "${SPEXCODE_BACKEND_KIND}"`
116
+ : "is not a known seat in dev-factory/seats.json";
117
+
118
+ return {
119
+ allowed: false,
120
+ message:
121
+ `spexcode: refusing --seat "${seatId}" — seat "${seatId}" ${reason}. ` +
122
+ `Fix: add a backend with kind "${SPEXCODE_BACKEND_KIND}" to dev-factory/seats.json's ` +
123
+ `seats[${seatId}].fill.backends (see the "coo-phase-1" seat's "spexcode/hs-chat" entry ` +
124
+ `for an example), then retry. Nothing was started.`,
125
+ };
126
+ }
127
+
128
+ /** Load + JSON.parse `dev-factory/seats.json`. Isolated so tests can stub it. */
129
+ export function loadSeatsRegistry(seatsJsonPath = SEATS_JSON_PATH) {
130
+ return JSON.parse(readFileSync(seatsJsonPath, "utf8"));
131
+ }
132
+
133
+ /**
134
+ * Spawn `node hs.mjs chat <...argv>`, inheriting stdio, forwarding SIGINT to
135
+ * the child so its own graceful-shutdown path runs, and resolving with the
136
+ * child's exit code (or `1` on a spawn error or an unclean signal exit).
137
+ * `spawnFn` is injectable so tests never spawn a real process.
138
+ *
139
+ * @param {readonly string[]} argv
140
+ * @param {{hsPath?: string, spawnFn?: typeof spawn}} [opts]
141
+ * @returns {Promise<number>}
142
+ */
143
+ export function runForward(argv, { hsPath = HS_PATH, spawnFn = spawn } = {}) {
144
+ return new Promise((resolve) => {
145
+ const child = spawnFn(process.execPath, [hsPath, ...buildForwardArgs(argv)], {
146
+ stdio: "inherit",
147
+ shell: false,
148
+ });
149
+
150
+ const forwardSigint = () => {
151
+ child.kill("SIGINT");
152
+ };
153
+ process.on("SIGINT", forwardSigint);
154
+
155
+ const cleanup = () => {
156
+ process.removeListener("SIGINT", forwardSigint);
157
+ };
158
+
159
+ child.on("error", () => {
160
+ cleanup();
161
+ resolve(1);
162
+ });
163
+
164
+ child.on("exit", (code, signal) => {
165
+ cleanup();
166
+ if (typeof code === "number") {
167
+ resolve(code);
168
+ return;
169
+ }
170
+ resolve(signal ? 1 : 0);
171
+ });
172
+ });
173
+ }
174
+
175
+ /**
176
+ * The full orchestration — seat guard, then forward — with every dependency
177
+ * injectable so tests exercise it end to end without touching the real
178
+ * filesystem or spawning a real process.
179
+ *
180
+ * @param {readonly string[]} argv
181
+ * @param {{spawnFn?: typeof spawn, hsPath?: string, loadRegistry?: typeof loadSeatsRegistry, stderr?: {write: Function}}} [deps]
182
+ * @returns {Promise<number>}
183
+ */
184
+ export async function runSpexcode(argv, deps = {}) {
185
+ const {
186
+ spawnFn = spawn,
187
+ hsPath = HS_PATH,
188
+ loadRegistry = loadSeatsRegistry,
189
+ stderr = process.stderr,
190
+ } = deps;
191
+
192
+ const seatId = parseSeatArg(argv);
193
+ if (seatId !== null) {
194
+ let registry;
195
+ try {
196
+ registry = loadRegistry();
197
+ } catch (err) {
198
+ stderr.write(
199
+ `spexcode: could not read dev-factory/seats.json to check --seat "${seatId}": ` +
200
+ `${err && err.message ? err.message : err}\n`,
201
+ );
202
+ return 2;
203
+ }
204
+
205
+ const guard = resolveSeatGuard(argv, registry);
206
+ if (!guard.allowed) {
207
+ stderr.write(guard.message + "\n");
208
+ return 2;
209
+ }
210
+ }
211
+
212
+ return runForward(argv, { hsPath, spawnFn });
213
+ }
214
+
215
+ async function main() {
216
+ const argv = process.argv.slice(2);
217
+ process.exitCode = await runSpexcode(argv);
218
+ }
219
+
220
+ /**
221
+ * Same fix as bin/hs.mjs's `isSameFile`/`isMain` ([[HS-561M]]): a naive
222
+ * `pathToFileURL(process.argv[1]).href === import.meta.url` idiom breaks
223
+ * under `npm link`. The global shim invokes
224
+ * `node "<npm-prefix>/node_modules/heyspex/bin/spexcode.mjs"`; that path is
225
+ * a symlink (npm link's install shape), and Node's default module
226
+ * resolution realpaths it for `import.meta.url` but leaves `process.argv[1]`
227
+ * as the ORIGINAL (symlinked) path the shim passed. The two never match,
228
+ * `isMain` comes out false, and the globally-installed `spexcode` silently
229
+ * prints nothing and exits 0 for every invocation. Fix: realpath BOTH sides
230
+ * before comparing (case-insensitively on Windows, where drive-letter casing
231
+ * can differ between how a shim built the path and how realpath reports it).
232
+ */
233
+ function isSameFile(a, b) {
234
+ const normalize = (p) => {
235
+ let real;
236
+ try {
237
+ real = realpathSync.native(p);
238
+ } catch {
239
+ real = path.resolve(p);
240
+ }
241
+ return process.platform === "win32" ? real.toLowerCase() : real;
242
+ };
243
+ return normalize(a) === normalize(b);
244
+ }
245
+
246
+ const isMain = (() => {
247
+ if (!process.argv[1]) return false;
248
+ try {
249
+ if (isSameFile(process.argv[1], fileURLToPath(import.meta.url))) return true;
250
+ } catch {
251
+ // fall through to the basename fallback below
252
+ }
253
+ // Belt-and-suspenders fallback for install shapes realpath can't reconcile:
254
+ // recognize this file by its own known bin name rather than failing closed
255
+ // into silent no-output.
256
+ const base = path.basename(process.argv[1]).toLowerCase();
257
+ return base === "spexcode.mjs" || base === "spexcode";
258
+ })();
259
+
260
+ if (isMain) {
261
+ main();
262
+ }