heyspex 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (352) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/README.md +21 -0
  3. package/bin/hs.mjs +1089 -104
  4. package/bin/spexcode.mjs +262 -0
  5. package/dev-factory/engine/worktree.mjs +740 -0
  6. package/dev-factory/lib/code-repo-root.mjs +67 -0
  7. package/dev-factory/lib/context-budget.mjs +84 -0
  8. package/dev-factory/lib/epic-detect.mjs +190 -0
  9. package/dev-factory/lib/fleet-queue-file.mjs +846 -0
  10. package/dev-factory/lib/fleet-queue.mjs +426 -0
  11. package/dev-factory/lib/fleet-state.mjs +14 -18
  12. package/dev-factory/lib/model-catalog.mjs +338 -0
  13. package/dev-factory/lib/seat-context.mjs +199 -0
  14. package/dev-factory/lib/spec-io.mjs +93 -0
  15. package/dev-factory/lib/sufficiency-score.mjs +8 -2
  16. package/dev-factory/lib/worktree-allocator.mjs +744 -0
  17. package/dev-factory/model-catalog.json +222 -0
  18. package/dist/cli/hs-check.mjs +1127 -103
  19. package/dist/cli/hs-claim.mjs +981 -63
  20. package/dist/cli/hs-connect.mjs +10059 -1729
  21. package/dist/cli/hs-defer.mjs +5507 -3072
  22. package/dist/cli/hs-dev.mjs +576 -0
  23. package/dist/cli/hs-env.mjs +416 -302
  24. package/dist/cli/hs-index-rebuild.mjs +7334 -0
  25. package/dist/cli/hs-index-reconcile.mjs +8138 -0
  26. package/dist/cli/hs-init.mjs +2840 -149
  27. package/dist/cli/hs-install.mjs +7214 -594
  28. package/dist/cli/hs-join.mjs +10640 -2421
  29. package/dist/cli/hs-vault-pull.mjs +8157 -6370
  30. package/dist/cli/hs-vault-push.mjs +6433 -0
  31. package/dist/cli/hs-vault.mjs +4029 -1555
  32. package/dist/cli/hs-workspace.mjs +2702 -538
  33. package/dist/cli-templates/customer-vault/specs/entity-model.md +594 -601
  34. package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +110 -177
  35. package/dist/cli-templates/customer-vault/specs/protocols/destructive-action-checklist.md +122 -0
  36. package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +13 -15
  37. package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +91 -0
  38. package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +150 -168
  39. package/dist/cli-templates/customer-vault/specs/protocols/spec-summary-authoring.md +83 -0
  40. package/dist/cli-templates/harness-commands/claude/hs-auto.md +36 -0
  41. package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -0
  42. package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -0
  43. package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -0
  44. package/dist/cli-templates/harness-commands/claude/hs-dev.md +20 -0
  45. package/dist/cli-templates/harness-commands/claude/hs-doctor.md +32 -0
  46. package/dist/cli-templates/harness-commands/claude/hs-ground.md +30 -0
  47. package/dist/cli-templates/harness-commands/claude/hs-handoff.md +33 -0
  48. package/dist/cli-templates/harness-commands/claude/hs-log.md +27 -0
  49. package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -0
  50. package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -0
  51. package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -0
  52. package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -0
  53. package/dist/cli-templates/harness-commands/claude/hs-res.md +63 -0
  54. package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -0
  55. package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -0
  56. package/dist/cli-templates/harness-commands/claude/hs-view.md +28 -0
  57. package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -0
  58. package/dist/cli-templates/harness-commands/claude/prototype.md +91 -0
  59. package/dist/cli-templates/harness-commands/cursor/hs-auto.md +40 -0
  60. package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +31 -0
  61. package/dist/cli-templates/harness-commands/cursor/hs-capture.md +65 -0
  62. package/dist/cli-templates/harness-commands/cursor/hs-defer.md +32 -0
  63. package/dist/cli-templates/harness-commands/cursor/hs-dev.md +24 -0
  64. package/dist/cli-templates/harness-commands/cursor/hs-doctor.md +36 -0
  65. package/dist/cli-templates/harness-commands/cursor/hs-ground.md +33 -0
  66. package/dist/cli-templates/harness-commands/cursor/hs-handoff.md +37 -0
  67. package/dist/cli-templates/harness-commands/cursor/hs-log.md +31 -0
  68. package/dist/cli-templates/harness-commands/cursor/hs-next.md +32 -0
  69. package/dist/cli-templates/harness-commands/cursor/hs-promote.md +73 -0
  70. package/dist/cli-templates/harness-commands/cursor/hs-push.md +38 -0
  71. package/dist/cli-templates/harness-commands/cursor/hs-qc.md +30 -0
  72. package/dist/cli-templates/harness-commands/cursor/hs-res.md +67 -0
  73. package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +26 -0
  74. package/dist/cli-templates/harness-commands/cursor/hs-review.md +26 -0
  75. package/dist/cli-templates/harness-commands/cursor/hs-view.md +32 -0
  76. package/dist/cli-templates/harness-commands/cursor/kill-ports.md +35 -0
  77. package/dist/cli-templates/harness-commands/cursor/prototype.md +95 -0
  78. package/docs/copilot-plugin-install.md +162 -0
  79. package/docs/driving-heyspex-from-copilot.md +165 -0
  80. package/docs/mcp-client-config.md +259 -6
  81. package/package.json +249 -124
  82. package/plugins/heyspex/.claude-plugin/plugin.json +12 -0
  83. package/plugins/heyspex/.cursor-plugin/plugin.json +15 -0
  84. package/plugins/heyspex/.mcp.json +12 -0
  85. package/plugins/heyspex/README.md +309 -0
  86. package/plugins/heyspex/bin/launch-mcp.mjs +95 -0
  87. package/plugins/heyspex/bin/run-hook.mjs +80 -0
  88. package/plugins/heyspex/hooks/cursor-hooks.json +308 -0
  89. package/plugins/heyspex/hooks/hooks.json +418 -0
  90. package/plugins/heyspex/mcp.json +13 -0
  91. package/scripts/check-mcp-freshness.mjs +17 -9
  92. package/scripts/claude-hooks/_env-vault-events.mjs +68 -0
  93. package/scripts/claude-hooks/_guardrail-events.mjs +96 -0
  94. package/scripts/claude-hooks/_guardrail-loader.mjs +494 -0
  95. package/scripts/claude-hooks/_intent-classifier-router.mjs +456 -0
  96. package/scripts/claude-hooks/_intent-classifier-semantic.mjs +356 -0
  97. package/scripts/claude-hooks/_run.mjs +413 -0
  98. package/scripts/claude-hooks/_transcript-text.mjs +60 -0
  99. package/scripts/claude-hooks/_vault-retrieval.mjs +322 -0
  100. package/scripts/claude-hooks/agent-spawn-delegation-gate.mjs +133 -0
  101. package/scripts/claude-hooks/capture-execution-gate.mjs +51 -0
  102. package/scripts/claude-hooks/capture-workflow-gate.mjs +11 -0
  103. package/scripts/claude-hooks/check-auto-memory-budget.mjs +396 -0
  104. package/scripts/claude-hooks/check-claude-md-budget.mjs +327 -0
  105. package/scripts/claude-hooks/check-mcp-freshness-hook.mjs +14 -0
  106. package/scripts/claude-hooks/claim-before-code-gate.mjs +11 -0
  107. package/scripts/claude-hooks/claim-liveness-shared.mjs +577 -0
  108. package/scripts/claude-hooks/close-terminal-on-ship.mjs +64 -0
  109. package/scripts/claude-hooks/coo-denylist-gate.mjs +11 -0
  110. package/scripts/claude-hooks/coo-guard-check.mjs +11 -0
  111. package/scripts/claude-hooks/deep-research-cost-gate.mjs +11 -0
  112. package/scripts/claude-hooks/dev-server-task-reminder.mjs +15 -0
  113. package/scripts/claude-hooks/diagnose-capture-reminder.mjs +11 -0
  114. package/scripts/claude-hooks/directive-obligation-gate.mjs +11 -0
  115. package/scripts/claude-hooks/emit-claim-status.mjs +41 -0
  116. package/scripts/claude-hooks/enforce-session-sequence.mjs +11 -0
  117. package/scripts/claude-hooks/enforce-vault-naming.mjs +11 -0
  118. package/scripts/claude-hooks/env-vault-guard.mjs +11 -0
  119. package/scripts/claude-hooks/epic-milestone-phrasing-gate.mjs +11 -0
  120. package/scripts/claude-hooks/git-identity-guard.mjs +11 -0
  121. package/scripts/claude-hooks/git-reflog-audit.mjs +15 -0
  122. package/scripts/claude-hooks/git-stash-gate.mjs +11 -0
  123. package/scripts/claude-hooks/guard-auto-memory-writes.mjs +137 -0
  124. package/scripts/claude-hooks/id-display-format-gate.mjs +11 -0
  125. package/scripts/claude-hooks/inject-communication-style.mjs +86 -0
  126. package/scripts/claude-hooks/inject-coo-seat.mjs +78 -0
  127. package/scripts/claude-hooks/inject-dev-server-awareness.mjs +11 -0
  128. package/scripts/claude-hooks/inject-doctrine.mjs +13 -0
  129. package/scripts/claude-hooks/inject-vault-context.mjs +14 -0
  130. package/scripts/claude-hooks/intent-classifier-trigger.mjs +279 -0
  131. package/scripts/claude-hooks/log-session-activity.mjs +341 -0
  132. package/scripts/claude-hooks/naughty-list-gate.mjs +11 -0
  133. package/scripts/claude-hooks/notify-status-flip.mjs +72 -0
  134. package/scripts/claude-hooks/plan-exit-ingest-gate.mjs +170 -0
  135. package/scripts/claude-hooks/plan-ingest-close-reminder.mjs +66 -0
  136. package/scripts/claude-hooks/plan-review-gate.mjs +11 -0
  137. package/scripts/claude-hooks/preflight-reminder.mjs +262 -0
  138. package/scripts/claude-hooks/protected-action-intent.mjs +221 -0
  139. package/scripts/claude-hooks/research-method-guard.mjs +398 -0
  140. package/scripts/claude-hooks/research-protocol-gate.mjs +11 -0
  141. package/scripts/claude-hooks/session-end.mjs +451 -0
  142. package/scripts/claude-hooks/settings-write-gate.mjs +11 -0
  143. package/scripts/claude-hooks/settlement-check-gate.mjs +39 -0
  144. package/scripts/claude-hooks/status-flip-detect.mjs +17 -0
  145. package/scripts/claude-hooks/terminal-close-shared.mjs +426 -0
  146. package/scripts/claude-hooks/terminal-close-watcher.mjs +122 -0
  147. package/scripts/claude-hooks/vault-mirror-catchup.mjs +11 -0
  148. package/scripts/claude-hooks/vault-write-tier-guard.mjs +11 -0
  149. package/scripts/claude-hooks/wip-checkpoint.mjs +302 -0
  150. package/scripts/cursor-hooks/manifest.mjs +281 -0
  151. package/scripts/dev-server-status.mjs +34 -13
  152. package/scripts/dev-server-stop.mjs +313 -0
  153. package/scripts/heyspex-mode.mjs +288 -0
  154. package/scripts/hs-auto-resolve.mjs +240 -0
  155. package/scripts/hs-auto-source.mjs +398 -0
  156. package/scripts/hs-auto.mjs +134 -0
  157. package/scripts/hs-capture-cli.mjs +326 -0
  158. package/scripts/hs-classifier.mjs +136 -0
  159. package/scripts/hs-companion.mjs +88 -0
  160. package/scripts/hs-gateway.mjs +115 -0
  161. package/scripts/hs-mode.mjs +125 -0
  162. package/scripts/hs-next.mjs +441 -0
  163. package/scripts/hs-settings.mjs +338 -0
  164. package/scripts/hs-view.mjs +496 -0
  165. package/scripts/hs-worktree.mjs +201 -0
  166. package/scripts/install-claude-code-hooks.mjs +272 -0
  167. package/scripts/install-cursor-hooks.mjs +471 -0
  168. package/scripts/lib/agent-env-markers.mjs +38 -0
  169. package/scripts/lib/console-url-resolver.mjs +199 -61
  170. package/scripts/lib/customer-kit.mjs +1203 -0
  171. package/scripts/lib/dev-mode.mjs +249 -32
  172. package/scripts/lib/directive-obligations.mjs +349 -0
  173. package/scripts/lib/heyspex-home.mjs +72 -0
  174. package/scripts/lib/hook-dispositions.mjs +210 -0
  175. package/scripts/lib/hook-vault-header.mjs +30 -0
  176. package/scripts/lib/hs-command.mjs +277 -0
  177. package/scripts/lib/hs-companion-settings.mjs +472 -0
  178. package/scripts/lib/hs-source-branch.mjs +196 -0
  179. package/scripts/lib/hs-source-commit.mjs +59 -5
  180. package/scripts/lib/hs-source-identity.mjs +526 -0
  181. package/scripts/lib/hs-source-log.mjs +13 -22
  182. package/scripts/lib/hs-source-preflight.mjs +18 -4
  183. package/scripts/lib/hs-source-push.mjs +386 -307
  184. package/scripts/lib/hs-worktree-args.mjs +115 -0
  185. package/scripts/lib/shared-path-helpers.mjs +134 -0
  186. package/scripts/lib/standalone-doctor.mjs +540 -3
  187. package/scripts/lib/subagent-git-lock.mjs +109 -0
  188. package/scripts/lib/template-pin.mjs +34 -0
  189. package/scripts/lib/window-placement.mjs +109 -0
  190. package/scripts/provenance-lib.mjs +91 -0
  191. package/scripts/stamp-provenance-on-edit.mjs +222 -0
  192. package/scripts/wip-ledger-health.mjs +653 -0
  193. package/spexharness/doctrine/baseline/SPEXCODE-DOCTRINE.baseline.json +29 -0
  194. package/spexharness/src/adapters/audit.mjs +197 -0
  195. package/spexharness/src/adapters/claude-code.mjs +449 -0
  196. package/spexharness/src/adapters/copilot.mjs +236 -0
  197. package/spexharness/src/adapters/detect-harness.mjs +34 -0
  198. package/spexharness/src/chat/tui/ansi.mjs +448 -0
  199. package/spexharness/src/chat/tui/palette.mjs +22 -0
  200. package/spexharness/src/chat/tui/select.mjs +208 -0
  201. package/spexharness/src/config-root.mjs +54 -0
  202. package/spexharness/src/context-budget.mjs +141 -0
  203. package/spexharness/src/dev-server-control.mjs +389 -0
  204. package/spexharness/src/doctrine/behaviour.mjs +371 -0
  205. package/spexharness/src/doctrine/corpus.mjs +733 -0
  206. package/spexharness/src/doctrine/inject.mjs +338 -0
  207. package/spexharness/src/doctrine/merge.mjs +184 -0
  208. package/spexharness/src/doctrine/sources.mjs +252 -0
  209. package/spexharness/src/doctrine/synthesize.mjs +401 -0
  210. package/spexharness/src/doctrine/vault-source.mjs +574 -0
  211. package/spexharness/src/doctrine/writer.mjs +176 -0
  212. package/spexharness/src/events.mjs +84 -0
  213. package/spexharness/src/gates/_shell-write-targets.mjs +77 -0
  214. package/spexharness/src/gates/capture-workflow-gate.mjs +202 -0
  215. package/spexharness/src/gates/claim-before-code-gate.decide.mjs +33 -0
  216. package/spexharness/src/gates/claim-before-code-gate.mjs +129 -0
  217. package/spexharness/src/gates/deep-research-cost-gate.mjs +275 -0
  218. package/spexharness/src/gates/diagnose-capture-reminder.mjs +112 -0
  219. package/spexharness/src/gates/directive-obligation-gate.decide.mjs +132 -0
  220. package/spexharness/src/gates/directive-obligation-gate.mjs +183 -0
  221. package/spexharness/src/gates/enforce-session-sequence.mjs +895 -0
  222. package/spexharness/src/gates/enforce-vault-naming.mjs +215 -0
  223. package/spexharness/src/gates/epic-milestone-phrasing-gate.mjs +233 -0
  224. package/spexharness/src/gates/git-identity-guard.decide.mjs +436 -0
  225. package/spexharness/src/gates/git-identity-guard.mjs +61 -0
  226. package/spexharness/src/gates/git-reflog-audit.mjs +151 -0
  227. package/spexharness/src/gates/git-stash-gate.decide.mjs +361 -0
  228. package/spexharness/src/gates/git-stash-gate.mjs +159 -0
  229. package/spexharness/src/gates/id-display-format-gate.mjs +190 -0
  230. package/spexharness/src/gates/inject-doctrine-gate.mjs +89 -0
  231. package/spexharness/src/gates/inject-vault-context.mjs +239 -0
  232. package/spexharness/src/gates/naughty-list-gate.mjs +213 -0
  233. package/spexharness/src/gates/plan-review-gate.mjs +233 -0
  234. package/spexharness/src/gates/preflight-reminder-gate.mjs +265 -0
  235. package/spexharness/src/gates/research-protocol-gate.mjs +175 -0
  236. package/spexharness/src/gates/settings-write-gate.decide.mjs +96 -0
  237. package/spexharness/src/gates/settings-write-gate.mjs +104 -0
  238. package/spexharness/src/gates/vault-mirror-catchup.mjs +124 -0
  239. package/spexharness/src/gates/vault-write-tier-guard.decide.mjs +125 -0
  240. package/spexharness/src/gates/vault-write-tier-guard.mjs +42 -0
  241. package/spexharness/src/git-reflog-audit.mjs +294 -0
  242. package/spexharness/src/intent-classifier.mjs +196 -0
  243. package/spexharness/src/intent-directive.mjs +626 -0
  244. package/spexharness/src/profile/learn.mjs +551 -0
  245. package/spexharness/src/profile/proposals.mjs +357 -0
  246. package/spexharness/src/profile/vault-inclusion.mjs +386 -0
  247. package/spexharness/src/redact.mjs +151 -0
  248. package/spexharness/src/rule-table.mjs +849 -0
  249. package/spexharness/src/seat-context.mjs +122 -0
  250. package/spexharness/src/session-bootstrap.mjs +512 -0
  251. package/spexharness/src/session-record.mjs +815 -0
  252. package/spexharness/src/token-inventory.mjs +207 -0
  253. package/vendor/heyspex-mcp/account-switch.mjs +96 -0
  254. package/vendor/heyspex-mcp/alignment-gate.mjs +315 -1
  255. package/vendor/heyspex-mcp/blocker-resolver.mjs +37 -2
  256. package/vendor/heyspex-mcp/breakdown-ladder.mjs +119 -4
  257. package/vendor/heyspex-mcp/breakdown-source.mjs +271 -0
  258. package/vendor/heyspex-mcp/breakdown-writer.mjs +23 -3
  259. package/vendor/heyspex-mcp/capture-buffer-patches.mjs +77 -8
  260. package/vendor/heyspex-mcp/capture-channel.mjs +55 -5
  261. package/vendor/heyspex-mcp/capture-console-client.mjs +117 -3
  262. package/vendor/heyspex-mcp/capture-focus.mjs +1061 -31
  263. package/vendor/heyspex-mcp/capture-outbox.mjs +15 -5
  264. package/vendor/heyspex-mcp/capture-sources.mjs +195 -0
  265. package/vendor/heyspex-mcp/capture-tools.mjs +1264 -0
  266. package/vendor/heyspex-mcp/claim-tools.mjs +1147 -0
  267. package/vendor/heyspex-mcp/cli-verbs.mjs +2074 -0
  268. package/vendor/heyspex-mcp/cli.mjs +207 -15524
  269. package/vendor/heyspex-mcp/close-branch-report.mjs +102 -0
  270. package/vendor/heyspex-mcp/cloud-tool-surface.mjs +128 -0
  271. package/vendor/heyspex-mcp/cloud-vault-writes.mjs +602 -0
  272. package/vendor/heyspex-mcp/config-seed.yaml +15 -0
  273. package/vendor/heyspex-mcp/config-settings.mjs +290 -7
  274. package/vendor/heyspex-mcp/console-fetch.mjs +822 -17
  275. package/vendor/heyspex-mcp/console-transport.mjs +172 -0
  276. package/vendor/heyspex-mcp/credentials.mjs +585 -0
  277. package/vendor/heyspex-mcp/defer-spec.mjs +44 -4
  278. package/vendor/heyspex-mcp/deferred-reclaim.mjs +29 -3
  279. package/vendor/heyspex-mcp/design-handlers.mjs +31 -0
  280. package/vendor/heyspex-mcp/design-tools.mjs +4 -1
  281. package/vendor/heyspex-mcp/dev-factory-bridge.mjs +4 -4
  282. package/vendor/heyspex-mcp/dev-mode-core.mjs +45 -0
  283. package/vendor/heyspex-mcp/dev-tools.mjs +131 -0
  284. package/vendor/heyspex-mcp/diagnose-handlers.mjs +54 -0
  285. package/vendor/heyspex-mcp/events-tools.mjs +207 -0
  286. package/vendor/heyspex-mcp/filing-tools.mjs +723 -0
  287. package/vendor/heyspex-mcp/fleet-handlers.mjs +37 -0
  288. package/vendor/heyspex-mcp/fleet-queue-tools.mjs +71 -15
  289. package/vendor/heyspex-mcp/focus-tools.mjs +190 -0
  290. package/vendor/heyspex-mcp/folder-trust-store.mjs +60 -0
  291. package/vendor/heyspex-mcp/http-transport.mjs +302 -0
  292. package/vendor/heyspex-mcp/list-tools.mjs +257 -0
  293. package/vendor/heyspex-mcp/login-handoff.mjs +15 -1
  294. package/vendor/heyspex-mcp/mcp-config.mjs +107 -0
  295. package/vendor/heyspex-mcp/memory-tools.mjs +523 -0
  296. package/vendor/heyspex-mcp/mvp-mode.mjs +53 -6
  297. package/vendor/heyspex-mcp/obligation-backstop.mjs +103 -0
  298. package/vendor/heyspex-mcp/occupied-keys.mjs +142 -0
  299. package/vendor/heyspex-mcp/on-deck-brief.mjs +3377 -0
  300. package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -0
  301. package/vendor/heyspex-mcp/package-root.mjs +32 -0
  302. package/vendor/heyspex-mcp/peer-presence.mjs +8 -1
  303. package/vendor/heyspex-mcp/plan-ground.mjs +621 -652
  304. package/vendor/heyspex-mcp/plan-push.mjs +908 -22
  305. package/vendor/heyspex-mcp/plan-review.mjs +124 -30
  306. package/vendor/heyspex-mcp/preflight-gate.mjs +0 -0
  307. package/vendor/heyspex-mcp/procedure-handlers.mjs +327 -0
  308. package/vendor/heyspex-mcp/procedure-tools.mjs +1181 -970
  309. package/vendor/heyspex-mcp/promote-capture.mjs +388 -69
  310. package/vendor/heyspex-mcp/promote-diagnose.mjs +74 -0
  311. package/vendor/heyspex-mcp/promote-research-to-diagnose.mjs +9 -2
  312. package/vendor/heyspex-mcp/promote-research.mjs +666 -53
  313. package/vendor/heyspex-mcp/promotion-helpers.mjs +251 -0
  314. package/vendor/heyspex-mcp/prompts.mjs +1460 -0
  315. package/vendor/heyspex-mcp/read-path.mjs +191 -15
  316. package/vendor/heyspex-mcp/receipt-shape.mjs +146 -0
  317. package/vendor/heyspex-mcp/recipe-handlers.mjs +117 -0
  318. package/vendor/heyspex-mcp/regen-tasks.mjs +210 -3
  319. package/vendor/heyspex-mcp/register-spec.mjs +266 -54
  320. package/vendor/heyspex-mcp/request-context.mjs +83 -0
  321. package/vendor/heyspex-mcp/research-focus.mjs +207 -4
  322. package/vendor/heyspex-mcp/research-handlers.mjs +625 -0
  323. package/vendor/heyspex-mcp/research-tools.mjs +4 -4
  324. package/vendor/heyspex-mcp/response-budget.mjs +348 -0
  325. package/vendor/heyspex-mcp/review-focus.mjs +25 -1
  326. package/vendor/heyspex-mcp/roadmap-default.mjs +70 -0
  327. package/vendor/heyspex-mcp/search-specs.mjs +280 -0
  328. package/vendor/heyspex-mcp/seat-block-ledger.mjs +113 -0
  329. package/vendor/heyspex-mcp/seat-identity.mjs +2 -6
  330. package/vendor/heyspex-mcp/session-owner.mjs +109 -0
  331. package/vendor/heyspex-mcp/session-tools.mjs +162 -0
  332. package/vendor/heyspex-mcp/settings-registry.mjs +232 -0
  333. package/vendor/heyspex-mcp/settings-tools.mjs +85 -12
  334. package/vendor/heyspex-mcp/spec-edit-tools.mjs +453 -0
  335. package/vendor/heyspex-mcp/spec-id-lib.mjs +93 -17
  336. package/vendor/heyspex-mcp/split-research.mjs +304 -56
  337. package/vendor/heyspex-mcp/stdio-rpc.mjs +22 -0
  338. package/vendor/heyspex-mcp/submit-for-review.mjs +150 -7
  339. package/vendor/heyspex-mcp/summary-contract.mjs +105 -0
  340. package/vendor/heyspex-mcp/summary-write-tools.mjs +228 -0
  341. package/vendor/heyspex-mcp/symptom-query.mjs +146 -0
  342. package/vendor/heyspex-mcp/test-helpers.mjs +123 -1
  343. package/vendor/heyspex-mcp/tool-catalog.mjs +2819 -0
  344. package/vendor/heyspex-mcp/tool-dispatch.mjs +657 -0
  345. package/vendor/heyspex-mcp/tool-helpers.mjs +325 -0
  346. package/vendor/heyspex-mcp/validate-linkage.mjs +414 -27
  347. package/vendor/heyspex-mcp/vault-io.mjs +720 -0
  348. package/vendor/heyspex-mcp/vault-less-endpoints.mjs +52 -0
  349. package/vendor/heyspex-mcp/vault-resolve.mjs +554 -68
  350. package/vendor/heyspex-mcp/verify-reproduction.mjs +270 -30
  351. package/vendor/heyspex-mcp/whoami.mjs +92 -0
  352. package/vendor/heyspex-mcp/write-outbox.mjs +512 -0
@@ -12,13 +12,56 @@
12
12
  * close-out gate while ON).
13
13
  */
14
14
 
15
- import { readFileSync } from 'node:fs';
15
+ import { existsSync, readFileSync } from 'node:fs';
16
16
  import { join } from 'node:path';
17
+ import { PACKAGE_ROOT } from './package-root.mjs';
18
+ import { resolveCodeRoot } from '../../scripts/lib/shared-path-helpers.mjs';
17
19
 
18
20
  export function configYamlPath(vaultRoot) {
19
21
  return join(vaultRoot, '.heyspex', 'config.yaml');
20
22
  }
21
23
 
24
+ // The code checkout (this package's root). Since the repo split (2026-08-31)
25
+ // `.heyspex/config.yaml` is git-tracked HERE and ignored in the vault repo
26
+ // (`.heyspex/*` in its .gitignore), so a vault-root-only read found nothing,
27
+ // reported MVP mode OFF, and every task became a full review unit an agent
28
+ // could not hand off (409 CLOSE_NOT_PERMITTED on every submit). Same
29
+ // precedence as console-transport.mjs / dev-tools.mjs: HS_PKG_ROOT wins.
30
+ // Resolved per call (not once at import) so a caller that sets HS_PKG_ROOT
31
+ // after this module loads — a test pointing the fallback at its own fixture
32
+ // checkout — is honored; a real process sets it before boot, so the value is
33
+ // identical there.
34
+ function checkoutRoot() {
35
+ return resolveCodeRoot({ packageRoot: PACKAGE_ROOT });
36
+ }
37
+
38
+ /**
39
+ * Candidate config.yaml paths, first hit wins: the vault root, then the code
40
+ * checkout — but the fallback applies only to a directory that IS a HeySpex
41
+ * vault and merely lacks the file, i.e. the post-split vault checkout.
42
+ *
43
+ * "Is a vault" means the `heyspex.yaml` marker `hs init` writes at the vault
44
+ * root (adr-0009 / the vault-marker contract in CLAUDE.md). Deliberately
45
+ * NARROWER than `isVaultRoot()` in vault-resolve.mjs (marker OR `specs/`) and
46
+ * narrower than an earlier `.heyspex/` container check: a scratch or fixture
47
+ * directory routinely grows `specs/` and a `.heyspex/` container (that is what
48
+ * `writeReviewFocus`, `record_wip`, etc. create) without ever being a vault
49
+ * checkout, and under the looser tests it silently inherited THIS repo's real
50
+ * config — turning MVP mode on and the review grain to milestone inside a
51
+ * fixture whose assertions describe an unconfigured vault. The marker is
52
+ * written by `hs init` and present in every real vault checkout, so a real
53
+ * user's fallback is unchanged; a marker-less directory keeps the original
54
+ * "file missing ⇒ MVP off" contract.
55
+ */
56
+ export function configYamlCandidates(vaultRoot) {
57
+ const out = [];
58
+ if (vaultRoot) out.push(configYamlPath(vaultRoot));
59
+ const isVault = Boolean(vaultRoot) && existsSync(join(vaultRoot, 'heyspex.yaml'));
60
+ const fallback = configYamlPath(checkoutRoot());
61
+ if (isVault && !out.includes(fallback)) out.push(fallback);
62
+ return out;
63
+ }
64
+
22
65
  function stripInlineComment(value) {
23
66
  // Drop a trailing ` # comment` that isn't inside quotes. The config uses
24
67
  // unquoted scalars, so a simple split on ` #` is sufficient and safe.
@@ -89,12 +132,16 @@ export function parseMvpModeBlock(raw) {
89
132
  * falsy) — same contract as the console's `readMvpModeState`.
90
133
  */
91
134
  export function readMvpModeState(vaultRoot) {
92
- let raw;
93
- try {
94
- raw = readFileSync(configYamlPath(vaultRoot), 'utf-8');
95
- } catch {
96
- return null;
135
+ let raw = null;
136
+ for (const candidate of configYamlCandidates(vaultRoot)) {
137
+ try {
138
+ raw = readFileSync(candidate, 'utf-8');
139
+ break;
140
+ } catch {
141
+ raw = null;
142
+ }
97
143
  }
144
+ if (raw === null) return null;
98
145
  const block = parseMvpModeBlock(raw);
99
146
  if (!block || !block.enabled) return null;
100
147
  return block;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * MCP-side backstop for the directive-obligation gate ([[HS-20SW-DRSM-DIAG-heyspex-capture-on-idea-and-workflow]]).
3
+ *
4
+ * The PreToolUse hook (spexharness/src/gates/directive-obligation-gate.mjs) is the primary enforcement,
5
+ * but hooks do not load on every surface (a bare MCP client, a seat with no harness). This module lets
6
+ * the tools themselves refuse implementation work while a capture directive is outstanding. It reads
7
+ * the SAME state file the hook writes, through scripts/lib/directive-obligations.mjs (never a second
8
+ * parser): `.heyspex/directive-obligations.json` under the project root.
9
+ *
10
+ * FAIL OPEN everywhere: a missing / unreadable state file, no obligation, any thrown error -> never refuse.
11
+ * Break-glass (owner only, env): HEYSPEX_OBLIGATION_BACKSTOP_OFF=1.
12
+ */
13
+ import path from 'node:path';
14
+
15
+ import { outstanding, readObligations } from '../../scripts/lib/directive-obligations.mjs';
16
+
17
+ export const OBLIGATION_BACKSTOP_OFF_ENV = 'HEYSPEX_OBLIGATION_BACKSTOP_OFF';
18
+ export const CAPTURE_OBLIGATION_ERROR = 'CAPTURE_OBLIGATION_OUTSTANDING';
19
+ /** With no resolvable session id, only an obligation armed this recently counts. */
20
+ export const NO_SESSION_WINDOW_MS = 30 * 60 * 1000;
21
+ /** heyspex_file_spec types that are implementation filings (research/diagnose/memory/capture stay open). */
22
+ export const IMPLEMENTATION_TYPES = Object.freeze(['task', 'epic', 'milestone']);
23
+
24
+ /** The ids the MCP identity layer returns when it could not resolve a real session. */
25
+ function isUnresolvedSession(sessionId) {
26
+ const s = String(sessionId ?? '').trim();
27
+ return !s || s === 'local';
28
+ }
29
+
30
+ /** Project roots to look in, in order: the hook's own (CLAUDE_PROJECT_DIR), the cwd, then the vault root. */
31
+ export function candidateRoots({ roots, vaultRoot, env = process.env } = {}) {
32
+ if (Array.isArray(roots) && roots.length) return roots;
33
+ const out = [];
34
+ const fromEnv = String(env.CLAUDE_PROJECT_DIR ?? '').trim();
35
+ if (fromEnv) out.push(fromEnv);
36
+ out.push(process.cwd());
37
+ if (vaultRoot) out.push(vaultRoot);
38
+ return [...new Set(out.map((r) => path.resolve(r)))];
39
+ }
40
+
41
+ function summarize(ob, reason) {
42
+ return { outstanding: true, reason, armedAt: ob.issued_at ?? null, directiveKey: ob.id ?? ob.prompt_sha ?? null };
43
+ }
44
+
45
+ const NONE = (reason) => ({ outstanding: false, reason, armedAt: null, directiveKey: null });
46
+
47
+ /**
48
+ * @param {{ sessionId?: string, now?: number, roots?: string[], vaultRoot?: string, env?: object }} [opts]
49
+ * @returns {{ outstanding: boolean, reason: string, armedAt: string|null, directiveKey: string|null }}
50
+ */
51
+ export function checkOutstandingCaptureObligation({ sessionId, now = Date.now(), roots, vaultRoot, env = process.env } = {}) {
52
+ try {
53
+ if (String(env[OBLIGATION_BACKSTOP_OFF_ENV] ?? '') === '1') return NONE('backstop off (env)');
54
+ const known = !isUnresolvedSession(sessionId);
55
+ for (const root of candidateRoots({ roots, vaultRoot, env })) {
56
+ const full = readObligations(root, known ? String(sessionId).trim() : undefined);
57
+ if (!full) continue;
58
+ if (known) {
59
+ const open = outstanding(full, String(sessionId).trim(), now);
60
+ if (open.length) return summarize(open[0], 'capture directive issued for this session and not yet satisfied');
61
+ continue;
62
+ }
63
+ // No session id: any unsatisfied obligation armed in this project within the window.
64
+ const records = [full, ...Object.entries(full.others ?? {}).map(([sid, o]) => ({ ...o, session_id: sid }))];
65
+ for (const rec of records) {
66
+ if (!rec?.session_id) continue;
67
+ const open = outstanding(rec, rec.session_id, now).filter((o) => now - Date.parse(o.issued_at) <= NO_SESSION_WINDOW_MS);
68
+ if (open.length) return summarize(open[0], 'capture directive armed in this project within 30 minutes and not yet satisfied (session unknown)');
69
+ }
70
+ }
71
+ return NONE('no outstanding capture obligation');
72
+ } catch {
73
+ return NONE('obligation state unreadable (fail open)');
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Tool-level verdict. Returns null (allow) or { error, message, tool, armed_at, directive_key }.
79
+ * Refuses heyspex_claim_spec always, and heyspex_file_spec only for task/epic/milestone filings.
80
+ */
81
+ export function evaluateObligationBackstop(toolName, args = {}, opts = {}) {
82
+ try {
83
+ if (toolName !== 'heyspex_claim_spec' && toolName !== 'heyspex_file_spec') return null;
84
+ if (toolName === 'heyspex_file_spec') {
85
+ const type = String(args?.type ?? '').trim().toLowerCase();
86
+ if (!IMPLEMENTATION_TYPES.includes(type)) return null;
87
+ }
88
+ const r = checkOutstandingCaptureObligation(opts);
89
+ if (!r.outstanding) return null;
90
+ return {
91
+ error: CAPTURE_OBLIGATION_ERROR,
92
+ message:
93
+ `${toolName} refused: the capture_on_idea directive was issued (${r.armedAt ?? 'earlier'}) and the capture has not happened. ` +
94
+ 'Call heyspex_update_capture for the armed capture (with the owner\'s exact words), or have the owner say "discard". ' +
95
+ `Then retry. (Owner-only break-glass: ${OBLIGATION_BACKSTOP_OFF_ENV}=1.)`,
96
+ tool: toolName,
97
+ armed_at: r.armedAt,
98
+ directive_key: r.directiveKey,
99
+ };
100
+ } catch {
101
+ return null;
102
+ }
103
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * occupied-keys.mjs — tier-aware vault KEY occupancy for the id minters
3
+ * ([[HS-FJWL]]).
4
+ *
5
+ * `collectOccupiedKeysEitherKind` (spec-id-lib.mjs) is a `readdirSync` walk of
6
+ * `specs/**` basenames. That is complete on the DISK tier and blind on the
7
+ * CLOUD tier: since the cutover to cloud reliance a filed spec gets a Neon row
8
+ * and NO local `.md` (`landed.onDisk: false`), so the walk answers from a local
9
+ * clone that is not empty, just STALE — a large, plausible-looking set that
10
+ * silently omits every cloud-only spec. A blind scan is tolerable for FAMILY
11
+ * keys (they are a "pick something fresh" default, not a uniqueness gate —
12
+ * spec-id-lib.mjs `collectOccupiedFamilyKeysFromVault`) and unsound for FILE
13
+ * keys, which under Naming V3 ARE the unique per-file identity that `id:` keys
14
+ * onto. Re-issuing one mints a duplicate id.
15
+ *
16
+ * Shape follows the existing `*ViaTier` family and composes on
17
+ * {@link readViaSelector} — {@link collectOccupiedCaptureIdsViaTier}
18
+ * (capture-tools.mjs) is the direct precedent.
19
+ *
20
+ * CONSOLE READ STRATEGY — full enumeration, not a per-candidate existence
21
+ * probe. `GET /api/specs/resolve?q=<key>` would be one request per mint, but it
22
+ * answers `404 { error: "not_found" }` BOTH for "this key is free" and for "you
23
+ * are not authorized for this vault" (`resolveCallerVaultScope` →
24
+ * `not_found`, spex-console/src/app/api/specs/resolve/route.ts), so a
25
+ * signed-out session would read every key as free and mint duplicates — the
26
+ * exact failure this module exists to prevent. It also cannot see family keys
27
+ * in era-1 (`HS-TASK-<family>-…`) or era-3 (`HS-<fileKey>-<family>-TYPE-…`)
28
+ * positions, because its prefix tier only matches a LEADING `<token>-`.
29
+ * Enumerating `GET /api/specs?fields=id,basename` instead applies the SAME
30
+ * basename parsers the disk walk uses, so both namespaces and all three eras
31
+ * are covered identically, and an unauthorized answer is detectable (it comes
32
+ * back as an empty page — see the empty-set refusal below).
33
+ */
34
+
35
+ import { consoleFetch as defaultConsoleFetch } from './console-transport.mjs';
36
+ import { walkSpecListPages } from './list-tools.mjs';
37
+ import { readViaSelector } from './read-path.mjs';
38
+ import {
39
+ bareFileKeyOf,
40
+ collectOccupiedKeysEitherKind,
41
+ fileKeyFromBasename,
42
+ parseSpecId,
43
+ } from './spec-id-lib.mjs';
44
+
45
+ /** The projection the cloud occupancy walk asks for — two fields, ~60 bytes/row. */
46
+ export const OCCUPIED_KEYS_LIST_PATH = '/api/specs?fields=id,basename';
47
+
48
+ /** `routeLabel` for the selector's unreachable-console error. */
49
+ const ROUTE_LABEL = `GET ${OCCUPIED_KEYS_LIST_PATH}`;
50
+
51
+ /**
52
+ * Cross-namespace occupancy (FILE keys ∪ FAMILY keys) from `GET /api/specs`
53
+ * rows — the cloud twin of {@link collectOccupiedKeysEitherKind}'s union.
54
+ *
55
+ * Reads the same two atoms the disk walk reads, from the same parsers:
56
+ * - `basename` → `fileKeyFromBasename` (era-3 leading token / era-1-2
57
+ * after-TYPE token) and `parseSpecId(...).familyKey` (positional in every
58
+ * era).
59
+ * - `id` → a bare era-3 `HS-<fileKey>` (`bareFileKeyOf`), plus the positional
60
+ * atoms of a full id. The disk walk has only filenames to go on; list rows
61
+ * carry the stored `id:` too, so this is a strict superset — never a
62
+ * smaller set than the disk scan would return for the same specs.
63
+ *
64
+ * @param {Array<{ id?: string|null, basename?: string|null }>} rows
65
+ * @returns {Set<string>} uppercase keys
66
+ */
67
+ export function occupiedKeysFromSpecRows(rows) {
68
+ const occupied = new Set();
69
+ const addFrom = (value) => {
70
+ if (typeof value !== 'string' || !value.trim()) return;
71
+ const bare = bareFileKeyOf(value);
72
+ if (bare) occupied.add(bare);
73
+ const fileKey = fileKeyFromBasename(value);
74
+ if (fileKey) occupied.add(fileKey.toUpperCase());
75
+ const parsed = parseSpecId(value);
76
+ if (parsed?.familyKey) occupied.add(parsed.familyKey.toUpperCase());
77
+ if (parsed?.fileKey) occupied.add(parsed.fileKey.toUpperCase());
78
+ };
79
+ for (const row of Array.isArray(rows) ? rows : []) {
80
+ addFrom(row?.basename);
81
+ addFrom(row?.id);
82
+ }
83
+ return occupied;
84
+ }
85
+
86
+ /**
87
+ * Tier-aware {@link collectOccupiedKeysEitherKind}.
88
+ *
89
+ * - `disk`: byte-identical to today's scan; `neonRead` never runs, so a
90
+ * disk-tier mint makes ZERO console calls.
91
+ * - `neon`: walks `GET /api/specs?fields=id,basename` to exhaustion. The route
92
+ * caps a page at 500 rows and hands back `next_cursor`
93
+ * ([[HS-0R2X-8KK5-EPIC]]); {@link walkSpecListPages} follows it, because a
94
+ * caller that reads page 1 only would UNDER-COUNT occupancy, which is the
95
+ * same duplicate-id bug wearing a different hat.
96
+ *
97
+ * FAILS LOUDLY, deliberately, and in TWO ways the read path does not:
98
+ *
99
+ * 1. An unreachable/erroring console throws
100
+ * {@link ConsoleUnreachableCloudModeError} via the selector — no silent
101
+ * disk fallback. That is the selector's own contract, and it is the right
102
+ * one here for a stronger reason than it is for a read: handing out a
103
+ * possibly-duplicate id is worse than refusing to mint. A read that falls
104
+ * back to a stale local copy shows stale data; a MINT that falls back to a
105
+ * stale local copy writes a colliding id into the vault of record.
106
+ * 2. An EMPTY occupancy set from the cloud is refused rather than returned.
107
+ * `GET /api/specs` answers `200 { specs: [], vaultScope: { authorized:
108
+ * false } }` when the caller is not authorized for the vault
109
+ * (spex-console/src/app/api/specs/route.ts), and an empty set is exactly
110
+ * what makes every candidate key look free. A real vault that could be
111
+ * minted into is never empty — the spec being minted is filed INTO an
112
+ * existing vault — so "zero rows" is always either a signed-out session or
113
+ * a wrong-vault scope, never a legitimate answer.
114
+ *
115
+ * @param {string} vaultRoot
116
+ * @param {{ tier: 'disk'|'neon', consoleUrl?: string, consoleFetch?: Function, walk?: Function }} opts
117
+ * @returns {Promise<Set<string>>}
118
+ */
119
+ export async function collectOccupiedKeysEitherKindViaTier(
120
+ vaultRoot,
121
+ { tier, consoleUrl, consoleFetch = defaultConsoleFetch, walk = walkSpecListPages } = {},
122
+ ) {
123
+ return readViaSelector({
124
+ allowDiskFallback: false,
125
+ tier,
126
+ diskRead: () => collectOccupiedKeysEitherKind(vaultRoot),
127
+ neonRead: async () => {
128
+ const rows = await walk(consoleFetch, OCCUPIED_KEYS_LIST_PATH);
129
+ if (!Array.isArray(rows) || rows.length === 0) {
130
+ throw new Error(
131
+ `${ROUTE_LABEL} returned 0 specs — refusing to mint against an empty occupancy set. ` +
132
+ `An unauthorized or wrong-vault caller gets \`200 { specs: [], vaultScope: ` +
133
+ `{ authorized: false } }\` from this route, which is indistinguishable from ` +
134
+ `"every key is free"; minting on it would hand out a duplicate file key.`,
135
+ );
136
+ }
137
+ return occupiedKeysFromSpecRows(rows);
138
+ },
139
+ routeLabel: ROUTE_LABEL,
140
+ consoleUrl,
141
+ });
142
+ }