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,34 @@
1
+ /**
2
+ * template-pin.mjs — the ONE home of the customer-vault template version and
3
+ * the list of docs a healthy customer vault must always contain (HS-KH0Q).
4
+ *
5
+ * Zero dependencies (node builtins only) so BOTH consumers can import it:
6
+ * - `spex-console/src/lib/cli/template-manifest.ts` re-exports it, and
7
+ * - the zero-dependency standalone doctor (`scripts/lib/standalone-doctor.mjs`,
8
+ * the `vault-template` row) reads it without pulling in the console.
9
+ *
10
+ * Bump `TEMPLATE_VERSION` whenever the shipped manifest content changes in a
11
+ * way a customer vault should be able to detect (new universal doc, changed
12
+ * skeleton, etc.). `TEMPLATE_CORE_DESTS` is guarded against drift from the
13
+ * real manifest by `spex-console/src/lib/cli/template-manifest.test.ts`.
14
+ */
15
+
16
+ /** Bumped whenever the shipped manifest content changes. */
17
+ export const TEMPLATE_VERSION = "1.1.0";
18
+
19
+ /**
20
+ * Vault-relative (POSIX) paths every scaffolded customer vault must hold:
21
+ * the foundational docs plus every universal protocol.
22
+ */
23
+ export const TEMPLATE_CORE_DESTS = Object.freeze([
24
+ "specs/north-star.md",
25
+ "specs/principles-index.md",
26
+ "specs/roadmap.md",
27
+ "specs/entity-model.md",
28
+ "specs/protocols/canonical-frontmatter-templates.md",
29
+ "specs/protocols/spec-hierarchy.md",
30
+ "specs/protocols/memory-protocol.md",
31
+ "specs/protocols/session-close-protocol.md",
32
+ "specs/protocols/spec-summary-authoring.md",
33
+ "specs/protocols/destructive-action-checklist.md",
34
+ ]);
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Shared authorship-provenance targeting predicate
3
+ * ([[HS-EPIC-IKIM-authorship-provenance-frontmatter-owner]]).
4
+ *
5
+ * SSOT for "which specs carry the universal `owner` requirement" — imported by
6
+ * both the gate (`validate-vault-provenance.mjs`) and the one-time backfill
7
+ * (`backfill-vault-provenance.mjs`) so they cannot drift. If they drifted, the
8
+ * post-backfill `block` gate could fire on a file the backfill never touched.
9
+ *
10
+ * `owner` is universally required ([[entity-model]] §Authorship provenance). The
11
+ * `contributors` / `assisted_by` lists are intentionally OPTIONAL (auto-stamped,
12
+ * omit-when-empty), so they are NOT part of this predicate. Frozen statuses
13
+ * (closed/superseded/cancelled/duplicate) are skipped — their frontmatter is
14
+ * immutable historical record.
15
+ */
16
+ import fs from "node:fs";
17
+ import path from "node:path";
18
+ import { fileURLToPath } from "node:url";
19
+ import { createRequire } from "node:module";
20
+
21
+ export const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
22
+ const SPECS_DIR = path.join(REPO_ROOT, "specs");
23
+
24
+ // Reuse spex-console's installed js-yaml (transitive via gray-matter) — same pattern
25
+ // as validate-vault-frontmatter.mjs; no deps of our own.
26
+ const spexConsoleRequire = createRequire(path.join(REPO_ROOT, "spex-console", "package.json"));
27
+ export const yaml = spexConsoleRequire("js-yaml");
28
+
29
+ /** Statuses whose frontmatter is frozen historical record — never flagged. */
30
+ export const FROZEN_STATUSES = new Set(["closed", "superseded", "cancelled", "duplicate"]);
31
+
32
+ /** Recursively collect every *.md under specs/ (relative to repo root, posix sep). */
33
+ export function allSpecFiles() {
34
+ /** @type {string[]} */
35
+ const out = [];
36
+ const walk = (dir) => {
37
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
38
+ const abs = path.join(dir, entry.name);
39
+ if (entry.isDirectory()) walk(abs);
40
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
41
+ out.push(path.relative(REPO_ROOT, abs).split(path.sep).join("/"));
42
+ }
43
+ }
44
+ };
45
+ if (fs.existsSync(SPECS_DIR)) walk(SPECS_DIR);
46
+ return out;
47
+ }
48
+
49
+ /** Extract the raw frontmatter block (between the leading `---` fences). */
50
+ export function extractFrontmatter(raw) {
51
+ const m = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
52
+ if (!m) return null;
53
+ return m[1];
54
+ }
55
+
56
+ /**
57
+ * Parse a spec's frontmatter to an object.
58
+ * @returns {object|null} null when there is no frontmatter, the YAML is
59
+ * malformed, or it is not a mapping — all of which this gate skips (malformed
60
+ * YAML is the frontmatter gate's job, not ours).
61
+ */
62
+ export function parseFrontmatter(raw) {
63
+ const fm = extractFrontmatter(raw);
64
+ if (fm === null) return null;
65
+ let data;
66
+ try {
67
+ data = yaml.load(fm);
68
+ } catch {
69
+ return null;
70
+ }
71
+ if (!data || typeof data !== "object") return null;
72
+ return data;
73
+ }
74
+
75
+ /**
76
+ * True when this spec carries the universal `owner` requirement: a typed entity
77
+ * spec whose status is not frozen. Prose-only docs and frozen records are out of
78
+ * scope for both the gate and the backfill.
79
+ */
80
+ export function isTypedNonFrozen(data) {
81
+ if (!data || typeof data !== "object") return false;
82
+ if (typeof data.type !== "string" || !data.type.trim()) return false;
83
+ const status = typeof data.status === "string" ? data.status.trim().toLowerCase() : "";
84
+ if (FROZEN_STATUSES.has(status)) return false;
85
+ return true;
86
+ }
87
+
88
+ /** The trimmed `owner` scalar, or "" when absent/blank. */
89
+ export function ownerOf(data) {
90
+ return typeof data.owner === "string" ? data.owner.trim() : "";
91
+ }
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Write-time `assisted_by` stamp for direct Edit/Write/MultiEdit tool writes
4
+ * ([[HS-EPIC-IKIM-authorship-provenance-frontmatter-owner]] Phase 3).
5
+ *
6
+ * Wired as a Claude Code **PostToolUse** hook (`.claude/settings.json`). The
7
+ * `updateSpec` edit-path stamp only fires for console-UI / MCP PUT writes; an
8
+ * agent that edits a spec file directly via the Edit tool bypasses it. The
9
+ * literal "git pre-commit stamp" can't fix this — the human reviewer runs the
10
+ * commit and the agent has released its claim by then, so neither the committer
11
+ * nor the claim trail names the editing agent. The only place the agent identity
12
+ * is reliably present is *at edit time*, in the agent's own process env — which
13
+ * is exactly when a PostToolUse hook runs.
14
+ *
15
+ * SAFETY (this runs on every spec edit): it only ever APPENDS one `assisted_by`
16
+ * entry, append-only + de-duped, and NO-OPS (touches nothing) on any frontmatter
17
+ * shape it isn't fully confident about. It always exits 0 — a stamp failure must
18
+ * never disrupt the agent's edit. Reads the PostToolUse payload as JSON on stdin.
19
+ *
20
+ * Targeting (typed + non-frozen) and frontmatter parsing are shared with the gate
21
+ * via `provenance-lib.mjs`, so this stamps exactly the specs the gate governs.
22
+ */
23
+ import fs from "node:fs";
24
+ import path from "node:path";
25
+ import { REPO_ROOT, parseFrontmatter, isTypedNonFrozen } from "./provenance-lib.mjs";
26
+ import { getActiveConfig } from "../vendor/heyspex-mcp/vault-resolve.mjs";
27
+
28
+ /** Exit without touching anything — the safe default for every uncertain case. */
29
+ function bail() {
30
+ process.exit(0);
31
+ }
32
+
33
+ /** The acting agent's display name, or null when it can't be identified. */
34
+ function detectAgent() {
35
+ const claude = (
36
+ process.env.CLAUDE_CODE_SESSION_ID ||
37
+ process.env.CLAUDE_SESSION_ID ||
38
+ process.env.CLAUDECODE ||
39
+ ""
40
+ ).trim();
41
+ if (claude) return "Claude Code";
42
+ const cursor = (
43
+ process.env.CURSOR_TRACE_ID ||
44
+ process.env.CURSOR_SESSION_ID ||
45
+ process.env.COMPOSER_SESSION_ID ||
46
+ ""
47
+ ).trim();
48
+ if (cursor) return "Cursor";
49
+ const explicit = process.env.HEYSPEX_AGENT?.trim();
50
+ if (explicit) return explicit;
51
+ return null; // unknown agent — don't stamp a placeholder
52
+ }
53
+
54
+ /** Read all of stdin (the PostToolUse JSON payload). */
55
+ function readStdin() {
56
+ try {
57
+ return fs.readFileSync(0, "utf8");
58
+ } catch {
59
+ return "";
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Append `agent` to the frontmatter's `assisted_by` list via surgical text edit
65
+ * (no YAML reflow). Returns the patched text, or null to signal "no safe edit"
66
+ * (caller no-ops). `current` is the parsed value of `assisted_by` (for dedup).
67
+ */
68
+ function appendAssistedBy(raw, agent, current) {
69
+ // Unexpected shape (present but not a list) → never touch it.
70
+ if (current !== undefined && !Array.isArray(current)) return null;
71
+ // Already present → idempotent no-op. An entry counts as "this agent" whether
72
+ // it is bare ("Claude Code") or model-qualified ("Claude Code:Fable 5") — the
73
+ // `AGENT:MODEL` form ([[entity-model]] §Authorship provenance). Without the
74
+ // prefix check, stamping a bare "Claude Code" onto a spec already crediting
75
+ // "Claude Code:Fable 5" would append a redundant, less-informative duplicate.
76
+ if (
77
+ Array.isArray(current) &&
78
+ current.some((v) => {
79
+ if (typeof v !== "string") return false;
80
+ const t = v.trim();
81
+ return t === agent || t.startsWith(`${agent}:`);
82
+ })
83
+ ) {
84
+ return null;
85
+ }
86
+
87
+ const eol = raw.includes("\r\n") ? "\r\n" : "\n";
88
+ const lines = raw.split(/\r?\n/);
89
+
90
+ // Frontmatter fences (the first must be line 0; parseFrontmatter guaranteed it).
91
+ let f1 = -1;
92
+ let f2 = -1;
93
+ for (let i = 0; i < lines.length; i++) {
94
+ if (lines[i].trim() === "---") {
95
+ if (f1 < 0) f1 = i;
96
+ else {
97
+ f2 = i;
98
+ break;
99
+ }
100
+ }
101
+ }
102
+ if (f1 !== 0 || f2 < 0) return null;
103
+
104
+ // Locate a top-level `assisted_by:` line within the block.
105
+ let abIdx = -1;
106
+ for (let i = f1 + 1; i < f2; i++) {
107
+ if (/^assisted_by\s*:/.test(lines[i])) {
108
+ abIdx = i;
109
+ break;
110
+ }
111
+ }
112
+
113
+ if (abIdx < 0) {
114
+ // No assisted_by yet — insert a block list after owner: / status: / type:.
115
+ let anchor = -1;
116
+ for (const key of ["owner", "status", "type"]) {
117
+ for (let i = f1 + 1; i < f2; i++) {
118
+ if (new RegExp(`^${key}\\s*:`).test(lines[i])) {
119
+ anchor = i;
120
+ break;
121
+ }
122
+ }
123
+ if (anchor >= 0) break;
124
+ }
125
+ if (anchor < 0) return null;
126
+ lines.splice(anchor + 1, 0, "assisted_by:", ` - ${agent}`);
127
+ return lines.join(eol);
128
+ }
129
+
130
+ const abLine = lines[abIdx];
131
+ const inlineMatch = abLine.match(/^(assisted_by\s*:\s*)\[(.*)\]\s*$/);
132
+ if (inlineMatch) {
133
+ // Inline form: assisted_by: [A, B] or assisted_by: []
134
+ const inner = inlineMatch[2].trim();
135
+ const next = inner.length === 0 ? agent : `${inner}, ${agent}`;
136
+ lines[abIdx] = `${inlineMatch[1]}[${next}]`;
137
+ return lines.join(eol);
138
+ }
139
+
140
+ if (/^assisted_by\s*:\s*$/.test(abLine)) {
141
+ // Block form: find the last contiguous `- ` item under it and insert after.
142
+ let lastItem = abIdx;
143
+ let itemIndent = " ";
144
+ for (let i = abIdx + 1; i < f2; i++) {
145
+ const m = lines[i].match(/^(\s+)-\s+/);
146
+ if (m) {
147
+ lastItem = i;
148
+ itemIndent = m[1];
149
+ } else if (lines[i].trim() === "") {
150
+ continue; // tolerate blank lines inside the block
151
+ } else {
152
+ break; // next key — list ended
153
+ }
154
+ }
155
+ if (lastItem === abIdx) {
156
+ // `assisted_by:` with no items yet — add the first.
157
+ lines.splice(abIdx + 1, 0, ` - ${agent}`);
158
+ } else {
159
+ lines.splice(lastItem + 1, 0, `${itemIndent}- ${agent}`);
160
+ }
161
+ return lines.join(eol);
162
+ }
163
+
164
+ // `assisted_by:` in some other shape (e.g. a scalar) → don't touch.
165
+ return null;
166
+ }
167
+
168
+ function main() {
169
+ const payload = readStdin();
170
+ let filePath = "";
171
+ try {
172
+ const obj = JSON.parse(payload);
173
+ filePath = obj?.tool_input?.file_path || obj?.tool_input?.filePath || "";
174
+ } catch {
175
+ return bail();
176
+ }
177
+ if (typeof filePath !== "string" || !filePath.trim()) return bail();
178
+
179
+ const abs = path.resolve(filePath);
180
+ // Must be a markdown spec under a specs/ dir — the code repo's (legacy) or
181
+ // the registry-resolved vault's (the live one since the 2026-08-31 repo split).
182
+ if (!abs.endsWith(".md")) return bail();
183
+ const bases = [REPO_ROOT];
184
+ try {
185
+ const vault = getActiveConfig()?.repoRootAbs;
186
+ if (vault && vault !== REPO_ROOT) bases.push(vault);
187
+ } catch {
188
+ // registry unreadable — the code-repo base still applies
189
+ }
190
+ const underSpecs = bases.some((b) => abs.startsWith(path.join(b, "specs") + path.sep));
191
+ if (!underSpecs) return bail();
192
+
193
+ let raw;
194
+ try {
195
+ raw = fs.readFileSync(abs, "utf8");
196
+ } catch {
197
+ return bail();
198
+ }
199
+
200
+ const data = parseFrontmatter(raw);
201
+ if (data === null) return bail(); // no/ malformed frontmatter — not our concern
202
+ if (!isTypedNonFrozen(data)) return bail(); // prose-only or frozen historical record
203
+
204
+ const agent = detectAgent();
205
+ if (!agent) return bail();
206
+
207
+ const patched = appendAssistedBy(raw, agent, data.assisted_by);
208
+ if (patched === null || patched === raw) return bail();
209
+
210
+ try {
211
+ fs.writeFileSync(abs, patched);
212
+ } catch {
213
+ // Swallow — a stamp must never disrupt the edit.
214
+ }
215
+ process.exit(0);
216
+ }
217
+
218
+ try {
219
+ main();
220
+ } catch {
221
+ process.exit(0);
222
+ }