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.
- package/.claude-plugin/marketplace.json +14 -0
- package/README.md +21 -0
- package/bin/hs.mjs +1089 -104
- package/bin/spexcode.mjs +262 -0
- package/dev-factory/engine/worktree.mjs +740 -0
- package/dev-factory/lib/code-repo-root.mjs +67 -0
- package/dev-factory/lib/context-budget.mjs +84 -0
- package/dev-factory/lib/epic-detect.mjs +190 -0
- package/dev-factory/lib/fleet-queue-file.mjs +846 -0
- package/dev-factory/lib/fleet-queue.mjs +426 -0
- package/dev-factory/lib/fleet-state.mjs +14 -18
- package/dev-factory/lib/model-catalog.mjs +338 -0
- package/dev-factory/lib/seat-context.mjs +199 -0
- package/dev-factory/lib/spec-io.mjs +93 -0
- package/dev-factory/lib/sufficiency-score.mjs +8 -2
- package/dev-factory/lib/worktree-allocator.mjs +744 -0
- package/dev-factory/model-catalog.json +222 -0
- package/dist/cli/hs-check.mjs +1127 -103
- package/dist/cli/hs-claim.mjs +981 -63
- package/dist/cli/hs-connect.mjs +10059 -1729
- package/dist/cli/hs-defer.mjs +5507 -3072
- package/dist/cli/hs-dev.mjs +576 -0
- package/dist/cli/hs-env.mjs +416 -302
- package/dist/cli/hs-index-rebuild.mjs +7334 -0
- package/dist/cli/hs-index-reconcile.mjs +8138 -0
- package/dist/cli/hs-init.mjs +2840 -149
- package/dist/cli/hs-install.mjs +7214 -594
- package/dist/cli/hs-join.mjs +10640 -2421
- package/dist/cli/hs-vault-pull.mjs +8157 -6370
- package/dist/cli/hs-vault-push.mjs +6433 -0
- package/dist/cli/hs-vault.mjs +4029 -1555
- package/dist/cli/hs-workspace.mjs +2702 -538
- package/dist/cli-templates/customer-vault/specs/entity-model.md +594 -601
- package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +110 -177
- package/dist/cli-templates/customer-vault/specs/protocols/destructive-action-checklist.md +122 -0
- package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +13 -15
- package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +91 -0
- package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +150 -168
- package/dist/cli-templates/customer-vault/specs/protocols/spec-summary-authoring.md +83 -0
- package/dist/cli-templates/harness-commands/claude/hs-auto.md +36 -0
- package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -0
- package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -0
- package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -0
- package/dist/cli-templates/harness-commands/claude/hs-dev.md +20 -0
- package/dist/cli-templates/harness-commands/claude/hs-doctor.md +32 -0
- package/dist/cli-templates/harness-commands/claude/hs-ground.md +30 -0
- package/dist/cli-templates/harness-commands/claude/hs-handoff.md +33 -0
- package/dist/cli-templates/harness-commands/claude/hs-log.md +27 -0
- package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -0
- package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -0
- package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -0
- package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -0
- package/dist/cli-templates/harness-commands/claude/hs-res.md +63 -0
- package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -0
- package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -0
- package/dist/cli-templates/harness-commands/claude/hs-view.md +28 -0
- package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -0
- package/dist/cli-templates/harness-commands/claude/prototype.md +91 -0
- package/dist/cli-templates/harness-commands/cursor/hs-auto.md +40 -0
- package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-capture.md +65 -0
- package/dist/cli-templates/harness-commands/cursor/hs-defer.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/hs-dev.md +24 -0
- package/dist/cli-templates/harness-commands/cursor/hs-doctor.md +36 -0
- package/dist/cli-templates/harness-commands/cursor/hs-ground.md +33 -0
- package/dist/cli-templates/harness-commands/cursor/hs-handoff.md +37 -0
- package/dist/cli-templates/harness-commands/cursor/hs-log.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-next.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/hs-promote.md +73 -0
- package/dist/cli-templates/harness-commands/cursor/hs-push.md +38 -0
- package/dist/cli-templates/harness-commands/cursor/hs-qc.md +30 -0
- package/dist/cli-templates/harness-commands/cursor/hs-res.md +67 -0
- package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +26 -0
- package/dist/cli-templates/harness-commands/cursor/hs-review.md +26 -0
- package/dist/cli-templates/harness-commands/cursor/hs-view.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/kill-ports.md +35 -0
- package/dist/cli-templates/harness-commands/cursor/prototype.md +95 -0
- package/docs/copilot-plugin-install.md +162 -0
- package/docs/driving-heyspex-from-copilot.md +165 -0
- package/docs/mcp-client-config.md +259 -6
- package/package.json +249 -124
- package/plugins/heyspex/.claude-plugin/plugin.json +12 -0
- package/plugins/heyspex/.cursor-plugin/plugin.json +15 -0
- package/plugins/heyspex/.mcp.json +12 -0
- package/plugins/heyspex/README.md +309 -0
- package/plugins/heyspex/bin/launch-mcp.mjs +95 -0
- package/plugins/heyspex/bin/run-hook.mjs +80 -0
- package/plugins/heyspex/hooks/cursor-hooks.json +308 -0
- package/plugins/heyspex/hooks/hooks.json +418 -0
- package/plugins/heyspex/mcp.json +13 -0
- package/scripts/check-mcp-freshness.mjs +17 -9
- package/scripts/claude-hooks/_env-vault-events.mjs +68 -0
- package/scripts/claude-hooks/_guardrail-events.mjs +96 -0
- package/scripts/claude-hooks/_guardrail-loader.mjs +494 -0
- package/scripts/claude-hooks/_intent-classifier-router.mjs +456 -0
- package/scripts/claude-hooks/_intent-classifier-semantic.mjs +356 -0
- package/scripts/claude-hooks/_run.mjs +413 -0
- package/scripts/claude-hooks/_transcript-text.mjs +60 -0
- package/scripts/claude-hooks/_vault-retrieval.mjs +322 -0
- package/scripts/claude-hooks/agent-spawn-delegation-gate.mjs +133 -0
- package/scripts/claude-hooks/capture-execution-gate.mjs +51 -0
- package/scripts/claude-hooks/capture-workflow-gate.mjs +11 -0
- package/scripts/claude-hooks/check-auto-memory-budget.mjs +396 -0
- package/scripts/claude-hooks/check-claude-md-budget.mjs +327 -0
- package/scripts/claude-hooks/check-mcp-freshness-hook.mjs +14 -0
- package/scripts/claude-hooks/claim-before-code-gate.mjs +11 -0
- package/scripts/claude-hooks/claim-liveness-shared.mjs +577 -0
- package/scripts/claude-hooks/close-terminal-on-ship.mjs +64 -0
- package/scripts/claude-hooks/coo-denylist-gate.mjs +11 -0
- package/scripts/claude-hooks/coo-guard-check.mjs +11 -0
- package/scripts/claude-hooks/deep-research-cost-gate.mjs +11 -0
- package/scripts/claude-hooks/dev-server-task-reminder.mjs +15 -0
- package/scripts/claude-hooks/diagnose-capture-reminder.mjs +11 -0
- package/scripts/claude-hooks/directive-obligation-gate.mjs +11 -0
- package/scripts/claude-hooks/emit-claim-status.mjs +41 -0
- package/scripts/claude-hooks/enforce-session-sequence.mjs +11 -0
- package/scripts/claude-hooks/enforce-vault-naming.mjs +11 -0
- package/scripts/claude-hooks/env-vault-guard.mjs +11 -0
- package/scripts/claude-hooks/epic-milestone-phrasing-gate.mjs +11 -0
- package/scripts/claude-hooks/git-identity-guard.mjs +11 -0
- package/scripts/claude-hooks/git-reflog-audit.mjs +15 -0
- package/scripts/claude-hooks/git-stash-gate.mjs +11 -0
- package/scripts/claude-hooks/guard-auto-memory-writes.mjs +137 -0
- package/scripts/claude-hooks/id-display-format-gate.mjs +11 -0
- package/scripts/claude-hooks/inject-communication-style.mjs +86 -0
- package/scripts/claude-hooks/inject-coo-seat.mjs +78 -0
- package/scripts/claude-hooks/inject-dev-server-awareness.mjs +11 -0
- package/scripts/claude-hooks/inject-doctrine.mjs +13 -0
- package/scripts/claude-hooks/inject-vault-context.mjs +14 -0
- package/scripts/claude-hooks/intent-classifier-trigger.mjs +279 -0
- package/scripts/claude-hooks/log-session-activity.mjs +341 -0
- package/scripts/claude-hooks/naughty-list-gate.mjs +11 -0
- package/scripts/claude-hooks/notify-status-flip.mjs +72 -0
- package/scripts/claude-hooks/plan-exit-ingest-gate.mjs +170 -0
- package/scripts/claude-hooks/plan-ingest-close-reminder.mjs +66 -0
- package/scripts/claude-hooks/plan-review-gate.mjs +11 -0
- package/scripts/claude-hooks/preflight-reminder.mjs +262 -0
- package/scripts/claude-hooks/protected-action-intent.mjs +221 -0
- package/scripts/claude-hooks/research-method-guard.mjs +398 -0
- package/scripts/claude-hooks/research-protocol-gate.mjs +11 -0
- package/scripts/claude-hooks/session-end.mjs +451 -0
- package/scripts/claude-hooks/settings-write-gate.mjs +11 -0
- package/scripts/claude-hooks/settlement-check-gate.mjs +39 -0
- package/scripts/claude-hooks/status-flip-detect.mjs +17 -0
- package/scripts/claude-hooks/terminal-close-shared.mjs +426 -0
- package/scripts/claude-hooks/terminal-close-watcher.mjs +122 -0
- package/scripts/claude-hooks/vault-mirror-catchup.mjs +11 -0
- package/scripts/claude-hooks/vault-write-tier-guard.mjs +11 -0
- package/scripts/claude-hooks/wip-checkpoint.mjs +302 -0
- package/scripts/cursor-hooks/manifest.mjs +281 -0
- package/scripts/dev-server-status.mjs +34 -13
- package/scripts/dev-server-stop.mjs +313 -0
- package/scripts/heyspex-mode.mjs +288 -0
- package/scripts/hs-auto-resolve.mjs +240 -0
- package/scripts/hs-auto-source.mjs +398 -0
- package/scripts/hs-auto.mjs +134 -0
- package/scripts/hs-capture-cli.mjs +326 -0
- package/scripts/hs-classifier.mjs +136 -0
- package/scripts/hs-companion.mjs +88 -0
- package/scripts/hs-gateway.mjs +115 -0
- package/scripts/hs-mode.mjs +125 -0
- package/scripts/hs-next.mjs +441 -0
- package/scripts/hs-settings.mjs +338 -0
- package/scripts/hs-view.mjs +496 -0
- package/scripts/hs-worktree.mjs +201 -0
- package/scripts/install-claude-code-hooks.mjs +272 -0
- package/scripts/install-cursor-hooks.mjs +471 -0
- package/scripts/lib/agent-env-markers.mjs +38 -0
- package/scripts/lib/console-url-resolver.mjs +199 -61
- package/scripts/lib/customer-kit.mjs +1203 -0
- package/scripts/lib/dev-mode.mjs +249 -32
- package/scripts/lib/directive-obligations.mjs +349 -0
- package/scripts/lib/heyspex-home.mjs +72 -0
- package/scripts/lib/hook-dispositions.mjs +210 -0
- package/scripts/lib/hook-vault-header.mjs +30 -0
- package/scripts/lib/hs-command.mjs +277 -0
- package/scripts/lib/hs-companion-settings.mjs +472 -0
- package/scripts/lib/hs-source-branch.mjs +196 -0
- package/scripts/lib/hs-source-commit.mjs +59 -5
- package/scripts/lib/hs-source-identity.mjs +526 -0
- package/scripts/lib/hs-source-log.mjs +13 -22
- package/scripts/lib/hs-source-preflight.mjs +18 -4
- package/scripts/lib/hs-source-push.mjs +386 -307
- package/scripts/lib/hs-worktree-args.mjs +115 -0
- package/scripts/lib/shared-path-helpers.mjs +134 -0
- package/scripts/lib/standalone-doctor.mjs +540 -3
- package/scripts/lib/subagent-git-lock.mjs +109 -0
- package/scripts/lib/template-pin.mjs +34 -0
- package/scripts/lib/window-placement.mjs +109 -0
- package/scripts/provenance-lib.mjs +91 -0
- package/scripts/stamp-provenance-on-edit.mjs +222 -0
- package/scripts/wip-ledger-health.mjs +653 -0
- package/spexharness/doctrine/baseline/SPEXCODE-DOCTRINE.baseline.json +29 -0
- package/spexharness/src/adapters/audit.mjs +197 -0
- package/spexharness/src/adapters/claude-code.mjs +449 -0
- package/spexharness/src/adapters/copilot.mjs +236 -0
- package/spexharness/src/adapters/detect-harness.mjs +34 -0
- package/spexharness/src/chat/tui/ansi.mjs +448 -0
- package/spexharness/src/chat/tui/palette.mjs +22 -0
- package/spexharness/src/chat/tui/select.mjs +208 -0
- package/spexharness/src/config-root.mjs +54 -0
- package/spexharness/src/context-budget.mjs +141 -0
- package/spexharness/src/dev-server-control.mjs +389 -0
- package/spexharness/src/doctrine/behaviour.mjs +371 -0
- package/spexharness/src/doctrine/corpus.mjs +733 -0
- package/spexharness/src/doctrine/inject.mjs +338 -0
- package/spexharness/src/doctrine/merge.mjs +184 -0
- package/spexharness/src/doctrine/sources.mjs +252 -0
- package/spexharness/src/doctrine/synthesize.mjs +401 -0
- package/spexharness/src/doctrine/vault-source.mjs +574 -0
- package/spexharness/src/doctrine/writer.mjs +176 -0
- package/spexharness/src/events.mjs +84 -0
- package/spexharness/src/gates/_shell-write-targets.mjs +77 -0
- package/spexharness/src/gates/capture-workflow-gate.mjs +202 -0
- package/spexharness/src/gates/claim-before-code-gate.decide.mjs +33 -0
- package/spexharness/src/gates/claim-before-code-gate.mjs +129 -0
- package/spexharness/src/gates/deep-research-cost-gate.mjs +275 -0
- package/spexharness/src/gates/diagnose-capture-reminder.mjs +112 -0
- package/spexharness/src/gates/directive-obligation-gate.decide.mjs +132 -0
- package/spexharness/src/gates/directive-obligation-gate.mjs +183 -0
- package/spexharness/src/gates/enforce-session-sequence.mjs +895 -0
- package/spexharness/src/gates/enforce-vault-naming.mjs +215 -0
- package/spexharness/src/gates/epic-milestone-phrasing-gate.mjs +233 -0
- package/spexharness/src/gates/git-identity-guard.decide.mjs +436 -0
- package/spexharness/src/gates/git-identity-guard.mjs +61 -0
- package/spexharness/src/gates/git-reflog-audit.mjs +151 -0
- package/spexharness/src/gates/git-stash-gate.decide.mjs +361 -0
- package/spexharness/src/gates/git-stash-gate.mjs +159 -0
- package/spexharness/src/gates/id-display-format-gate.mjs +190 -0
- package/spexharness/src/gates/inject-doctrine-gate.mjs +89 -0
- package/spexharness/src/gates/inject-vault-context.mjs +239 -0
- package/spexharness/src/gates/naughty-list-gate.mjs +213 -0
- package/spexharness/src/gates/plan-review-gate.mjs +233 -0
- package/spexharness/src/gates/preflight-reminder-gate.mjs +265 -0
- package/spexharness/src/gates/research-protocol-gate.mjs +175 -0
- package/spexharness/src/gates/settings-write-gate.decide.mjs +96 -0
- package/spexharness/src/gates/settings-write-gate.mjs +104 -0
- package/spexharness/src/gates/vault-mirror-catchup.mjs +124 -0
- package/spexharness/src/gates/vault-write-tier-guard.decide.mjs +125 -0
- package/spexharness/src/gates/vault-write-tier-guard.mjs +42 -0
- package/spexharness/src/git-reflog-audit.mjs +294 -0
- package/spexharness/src/intent-classifier.mjs +196 -0
- package/spexharness/src/intent-directive.mjs +626 -0
- package/spexharness/src/profile/learn.mjs +551 -0
- package/spexharness/src/profile/proposals.mjs +357 -0
- package/spexharness/src/profile/vault-inclusion.mjs +386 -0
- package/spexharness/src/redact.mjs +151 -0
- package/spexharness/src/rule-table.mjs +849 -0
- package/spexharness/src/seat-context.mjs +122 -0
- package/spexharness/src/session-bootstrap.mjs +512 -0
- package/spexharness/src/session-record.mjs +815 -0
- package/spexharness/src/token-inventory.mjs +207 -0
- package/vendor/heyspex-mcp/account-switch.mjs +96 -0
- package/vendor/heyspex-mcp/alignment-gate.mjs +315 -1
- package/vendor/heyspex-mcp/blocker-resolver.mjs +37 -2
- package/vendor/heyspex-mcp/breakdown-ladder.mjs +119 -4
- package/vendor/heyspex-mcp/breakdown-source.mjs +271 -0
- package/vendor/heyspex-mcp/breakdown-writer.mjs +23 -3
- package/vendor/heyspex-mcp/capture-buffer-patches.mjs +77 -8
- package/vendor/heyspex-mcp/capture-channel.mjs +55 -5
- package/vendor/heyspex-mcp/capture-console-client.mjs +117 -3
- package/vendor/heyspex-mcp/capture-focus.mjs +1061 -31
- package/vendor/heyspex-mcp/capture-outbox.mjs +15 -5
- package/vendor/heyspex-mcp/capture-sources.mjs +195 -0
- package/vendor/heyspex-mcp/capture-tools.mjs +1264 -0
- package/vendor/heyspex-mcp/claim-tools.mjs +1147 -0
- package/vendor/heyspex-mcp/cli-verbs.mjs +2074 -0
- package/vendor/heyspex-mcp/cli.mjs +207 -15524
- package/vendor/heyspex-mcp/close-branch-report.mjs +102 -0
- package/vendor/heyspex-mcp/cloud-tool-surface.mjs +128 -0
- package/vendor/heyspex-mcp/cloud-vault-writes.mjs +602 -0
- package/vendor/heyspex-mcp/config-seed.yaml +15 -0
- package/vendor/heyspex-mcp/config-settings.mjs +290 -7
- package/vendor/heyspex-mcp/console-fetch.mjs +822 -17
- package/vendor/heyspex-mcp/console-transport.mjs +172 -0
- package/vendor/heyspex-mcp/credentials.mjs +585 -0
- package/vendor/heyspex-mcp/defer-spec.mjs +44 -4
- package/vendor/heyspex-mcp/deferred-reclaim.mjs +29 -3
- package/vendor/heyspex-mcp/design-handlers.mjs +31 -0
- package/vendor/heyspex-mcp/design-tools.mjs +4 -1
- package/vendor/heyspex-mcp/dev-factory-bridge.mjs +4 -4
- package/vendor/heyspex-mcp/dev-mode-core.mjs +45 -0
- package/vendor/heyspex-mcp/dev-tools.mjs +131 -0
- package/vendor/heyspex-mcp/diagnose-handlers.mjs +54 -0
- package/vendor/heyspex-mcp/events-tools.mjs +207 -0
- package/vendor/heyspex-mcp/filing-tools.mjs +723 -0
- package/vendor/heyspex-mcp/fleet-handlers.mjs +37 -0
- package/vendor/heyspex-mcp/fleet-queue-tools.mjs +71 -15
- package/vendor/heyspex-mcp/focus-tools.mjs +190 -0
- package/vendor/heyspex-mcp/folder-trust-store.mjs +60 -0
- package/vendor/heyspex-mcp/http-transport.mjs +302 -0
- package/vendor/heyspex-mcp/list-tools.mjs +257 -0
- package/vendor/heyspex-mcp/login-handoff.mjs +15 -1
- package/vendor/heyspex-mcp/mcp-config.mjs +107 -0
- package/vendor/heyspex-mcp/memory-tools.mjs +523 -0
- package/vendor/heyspex-mcp/mvp-mode.mjs +53 -6
- package/vendor/heyspex-mcp/obligation-backstop.mjs +103 -0
- package/vendor/heyspex-mcp/occupied-keys.mjs +142 -0
- package/vendor/heyspex-mcp/on-deck-brief.mjs +3377 -0
- package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -0
- package/vendor/heyspex-mcp/package-root.mjs +32 -0
- package/vendor/heyspex-mcp/peer-presence.mjs +8 -1
- package/vendor/heyspex-mcp/plan-ground.mjs +621 -652
- package/vendor/heyspex-mcp/plan-push.mjs +908 -22
- package/vendor/heyspex-mcp/plan-review.mjs +124 -30
- package/vendor/heyspex-mcp/preflight-gate.mjs +0 -0
- package/vendor/heyspex-mcp/procedure-handlers.mjs +327 -0
- package/vendor/heyspex-mcp/procedure-tools.mjs +1181 -970
- package/vendor/heyspex-mcp/promote-capture.mjs +388 -69
- package/vendor/heyspex-mcp/promote-diagnose.mjs +74 -0
- package/vendor/heyspex-mcp/promote-research-to-diagnose.mjs +9 -2
- package/vendor/heyspex-mcp/promote-research.mjs +666 -53
- package/vendor/heyspex-mcp/promotion-helpers.mjs +251 -0
- package/vendor/heyspex-mcp/prompts.mjs +1460 -0
- package/vendor/heyspex-mcp/read-path.mjs +191 -15
- package/vendor/heyspex-mcp/receipt-shape.mjs +146 -0
- package/vendor/heyspex-mcp/recipe-handlers.mjs +117 -0
- package/vendor/heyspex-mcp/regen-tasks.mjs +210 -3
- package/vendor/heyspex-mcp/register-spec.mjs +266 -54
- package/vendor/heyspex-mcp/request-context.mjs +83 -0
- package/vendor/heyspex-mcp/research-focus.mjs +207 -4
- package/vendor/heyspex-mcp/research-handlers.mjs +625 -0
- package/vendor/heyspex-mcp/research-tools.mjs +4 -4
- package/vendor/heyspex-mcp/response-budget.mjs +348 -0
- package/vendor/heyspex-mcp/review-focus.mjs +25 -1
- package/vendor/heyspex-mcp/roadmap-default.mjs +70 -0
- package/vendor/heyspex-mcp/search-specs.mjs +280 -0
- package/vendor/heyspex-mcp/seat-block-ledger.mjs +113 -0
- package/vendor/heyspex-mcp/seat-identity.mjs +2 -6
- package/vendor/heyspex-mcp/session-owner.mjs +109 -0
- package/vendor/heyspex-mcp/session-tools.mjs +162 -0
- package/vendor/heyspex-mcp/settings-registry.mjs +232 -0
- package/vendor/heyspex-mcp/settings-tools.mjs +85 -12
- package/vendor/heyspex-mcp/spec-edit-tools.mjs +453 -0
- package/vendor/heyspex-mcp/spec-id-lib.mjs +93 -17
- package/vendor/heyspex-mcp/split-research.mjs +304 -56
- package/vendor/heyspex-mcp/stdio-rpc.mjs +22 -0
- package/vendor/heyspex-mcp/submit-for-review.mjs +150 -7
- package/vendor/heyspex-mcp/summary-contract.mjs +105 -0
- package/vendor/heyspex-mcp/summary-write-tools.mjs +228 -0
- package/vendor/heyspex-mcp/symptom-query.mjs +146 -0
- package/vendor/heyspex-mcp/test-helpers.mjs +123 -1
- package/vendor/heyspex-mcp/tool-catalog.mjs +2819 -0
- package/vendor/heyspex-mcp/tool-dispatch.mjs +657 -0
- package/vendor/heyspex-mcp/tool-helpers.mjs +325 -0
- package/vendor/heyspex-mcp/validate-linkage.mjs +414 -27
- package/vendor/heyspex-mcp/vault-io.mjs +720 -0
- package/vendor/heyspex-mcp/vault-less-endpoints.mjs +52 -0
- package/vendor/heyspex-mcp/vault-resolve.mjs +554 -68
- package/vendor/heyspex-mcp/verify-reproduction.mjs +270 -30
- package/vendor/heyspex-mcp/whoami.mjs +92 -0
- 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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
+
}
|