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
|
@@ -31,9 +31,11 @@ import {
|
|
|
31
31
|
renameSync,
|
|
32
32
|
rmSync,
|
|
33
33
|
} from 'node:fs';
|
|
34
|
-
import { basename, join } from 'node:path';
|
|
34
|
+
import { basename, join, isAbsolute, resolve, sep } from 'node:path';
|
|
35
35
|
|
|
36
36
|
import { buildCaptureBufferPatches } from './capture-buffer-patches.mjs';
|
|
37
|
+
import { assertPathUnderRoot } from '../../scripts/lib/shared-path-helpers.mjs';
|
|
38
|
+
|
|
37
39
|
|
|
38
40
|
/**
|
|
39
41
|
* Capture args that carry content worth preserving, in chronological write order.
|
|
@@ -87,12 +89,15 @@ export function stashCaptureArgs(vaultRoot, args = {}) {
|
|
|
87
89
|
for (const kind of OUTBOX_KINDS) {
|
|
88
90
|
const raw = args[kind];
|
|
89
91
|
if (raw !== undefined && String(raw).trim()) {
|
|
90
|
-
|
|
92
|
+
// user_quote is stashed verbatim (no trim) so an offline replay keeps the
|
|
93
|
+
// exact words (HS-TGHU).
|
|
94
|
+
entries.push({ kind, text: kind === 'user_quote' ? String(raw) : String(raw).trim() });
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
if (entries.length === 0) return [];
|
|
94
98
|
|
|
95
99
|
const dir = captureOutboxDir(vaultRoot);
|
|
100
|
+
assertPathUnderRoot(vaultRoot, dir, 'capture-outbox write');
|
|
96
101
|
mkdirSync(dir, { recursive: true });
|
|
97
102
|
const stampBase = new Date().toISOString().replace(/[:.]/g, '-');
|
|
98
103
|
const written = [];
|
|
@@ -119,7 +124,10 @@ export function listOutbox(vaultRoot) {
|
|
|
119
124
|
const kind = m ? m[1] : 'user_quote';
|
|
120
125
|
let text = '';
|
|
121
126
|
try {
|
|
122
|
-
text
|
|
127
|
+
// stash writes text + one newline: strip exactly that one so a verbatim
|
|
128
|
+
// quote's own trailing whitespace survives the round trip.
|
|
129
|
+
text = readFileSync(join(dir, f), 'utf8').replace(/\n$/, '');
|
|
130
|
+
if (kind !== 'user_quote') text = text.replace(/\n+$/, '');
|
|
123
131
|
} catch {
|
|
124
132
|
/* skip unreadable */
|
|
125
133
|
}
|
|
@@ -156,6 +164,7 @@ export function removeOutboxEntry(path) {
|
|
|
156
164
|
export function quarantineOutboxEntry(vaultRoot, entryPath, metadata = {}) {
|
|
157
165
|
try {
|
|
158
166
|
const qdir = captureOutboxQuarantineDir(vaultRoot);
|
|
167
|
+
assertPathUnderRoot(vaultRoot, qdir, 'capture-outbox write');
|
|
159
168
|
mkdirSync(qdir, { recursive: true });
|
|
160
169
|
const name = basename(entryPath);
|
|
161
170
|
const dest = join(qdir, name);
|
|
@@ -231,8 +240,9 @@ export function quarantineCount(vaultRoot) {
|
|
|
231
240
|
* @returns {{path: string, value: unknown} | null}
|
|
232
241
|
*/
|
|
233
242
|
export function outboxEntryToPatch(entry, iso, currentBuffer = {}) {
|
|
234
|
-
const text =
|
|
235
|
-
|
|
243
|
+
const text =
|
|
244
|
+
entry.kind === 'user_quote' ? String(entry.text ?? '') : String(entry.text ?? '').trim();
|
|
245
|
+
if (!text.trim()) return null;
|
|
236
246
|
if (!OUTBOX_KINDS.includes(entry.kind)) return null;
|
|
237
247
|
const patches = buildCaptureBufferPatches({ [entry.kind]: text }, currentBuffer, iso);
|
|
238
248
|
return patches[0] ?? null;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sources` argument of `heyspex_update_capture`: links, original text
|
|
3
|
+
* documents and binary files saved WITH a capture
|
|
4
|
+
* ([[HS-9K12-ST07-TASK-heyspex-update-capture-sources-argument]], epic
|
|
5
|
+
* [[HS-WF23-ST07-EPIC-verbatim-words-and-source-files]]; owner 2026-10-10:
|
|
6
|
+
* "files/original docs etc saved as source").
|
|
7
|
+
*
|
|
8
|
+
* Entry forms (exactly one content key per entry):
|
|
9
|
+
* { url, title? } -> link source (`## Source Material`, grain link)
|
|
10
|
+
* { name, text, mime?, origin? } -> original TEXT document, stored byte-for-byte
|
|
11
|
+
* { name, content_base64, mime?, origin? } -> binary file
|
|
12
|
+
* { path, name?, mime? } -> the MCP reads the file at `path` itself (so the
|
|
13
|
+
* agent never has to pass the content through its context)
|
|
14
|
+
*
|
|
15
|
+
* Caps mirror the console (`spex-console/src/lib/capture-source-files.ts`),
|
|
16
|
+
* which re-validates server-side: 256 KiB per file, 1 MiB total, 20 files.
|
|
17
|
+
* Oversize / malformed entries are REFUSED by name; nothing is truncated.
|
|
18
|
+
*/
|
|
19
|
+
import { createHash } from 'node:crypto';
|
|
20
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
21
|
+
import { basename } from 'node:path';
|
|
22
|
+
|
|
23
|
+
export const SOURCE_FILE_MAX_BYTES = 256 * 1024;
|
|
24
|
+
export const SOURCE_FILES_MAX_TOTAL_BYTES = 1024 * 1024;
|
|
25
|
+
export const SOURCE_FILES_MAX_COUNT = 20;
|
|
26
|
+
const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
27
|
+
|
|
28
|
+
/** Reduce a name to one safe path segment (mirrors the console). */
|
|
29
|
+
export function sanitizeSourceFileName(raw) {
|
|
30
|
+
const segs = String(raw).split(/[\\/]/);
|
|
31
|
+
// eslint-disable-next-line no-control-regex
|
|
32
|
+
return (segs[segs.length - 1] ?? '').replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, 200);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function defaultMime(name, encoding) {
|
|
36
|
+
if (encoding === 'base64') return 'application/octet-stream';
|
|
37
|
+
return /\.(md|markdown)$/i.test(name) ? 'text/markdown' : 'text/plain';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function fileFromBuffer(buf, { name, mime, origin, iso, encoding, content }) {
|
|
41
|
+
if (buf.length === 0) return { error: `source "${name}" is empty` };
|
|
42
|
+
if (buf.length > SOURCE_FILE_MAX_BYTES) {
|
|
43
|
+
return {
|
|
44
|
+
error: `source "${name}" is ${buf.length} bytes; the inline source-file cap is ${SOURCE_FILE_MAX_BYTES} bytes. Attach a smaller file or link it by url.`,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
file: {
|
|
49
|
+
name,
|
|
50
|
+
mime: mime || defaultMime(name, encoding),
|
|
51
|
+
bytes: buf.length,
|
|
52
|
+
sha256: createHash('sha256').update(buf).digest('hex'),
|
|
53
|
+
at: iso,
|
|
54
|
+
encoding,
|
|
55
|
+
content,
|
|
56
|
+
...(origin ? { origin } : {}),
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Decode bytes as UTF-8 text only when that is lossless; otherwise treat as binary. */
|
|
62
|
+
function bufferToStored(buf) {
|
|
63
|
+
if (!buf.includes(0)) {
|
|
64
|
+
const text = buf.toString('utf8');
|
|
65
|
+
if (Buffer.from(text, 'utf8').equals(buf)) return { encoding: 'utf8', content: text };
|
|
66
|
+
}
|
|
67
|
+
return { encoding: 'base64', content: buf.toString('base64') };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Validate + normalize the `sources` argument.
|
|
72
|
+
* @returns {{ ok: true, links: object[], files: object[] } | { ok: false, error: object }}
|
|
73
|
+
*/
|
|
74
|
+
export function normalizeCaptureSources(sources, iso, deps = {}) {
|
|
75
|
+
const read = deps.readFile ?? ((p) => readFileSync(p));
|
|
76
|
+
const size = deps.fileSize ?? ((p) => statSync(p).size);
|
|
77
|
+
const refuse = (message, index) => ({
|
|
78
|
+
ok: false,
|
|
79
|
+
error: {
|
|
80
|
+
error: 'INVALID_SOURCES',
|
|
81
|
+
message: `${message} NOTHING WAS WRITTEN.`,
|
|
82
|
+
field: `sources[${index}]`,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
if (!Array.isArray(sources)) return refuse('sources must be an array.', 0);
|
|
86
|
+
if (sources.length > SOURCE_FILES_MAX_COUNT) {
|
|
87
|
+
return refuse(`sources cannot exceed ${SOURCE_FILES_MAX_COUNT} entries.`, 0);
|
|
88
|
+
}
|
|
89
|
+
const links = [];
|
|
90
|
+
const files = [];
|
|
91
|
+
let total = 0;
|
|
92
|
+
for (let i = 0; i < sources.length; i += 1) {
|
|
93
|
+
const s = sources[i];
|
|
94
|
+
if (!s || typeof s !== 'object') return refuse(`sources[${i}] must be an object.`, i);
|
|
95
|
+
if (typeof s.url === 'string' && s.url.trim()) {
|
|
96
|
+
if (!/^https?:\/\//i.test(s.url.trim())) {
|
|
97
|
+
return refuse(`sources[${i}].url must be an http(s) URL.`, i);
|
|
98
|
+
}
|
|
99
|
+
links.push({
|
|
100
|
+
url: s.url.trim(),
|
|
101
|
+
...(typeof s.title === 'string' && s.title.trim() ? { title: s.title.trim() } : {}),
|
|
102
|
+
added_at: iso,
|
|
103
|
+
grain: 'link',
|
|
104
|
+
});
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
let name = typeof s.name === 'string' ? sanitizeSourceFileName(s.name) : '';
|
|
108
|
+
const mime = typeof s.mime === 'string' && s.mime.trim() ? s.mime.trim().slice(0, 100) : '';
|
|
109
|
+
const origin =
|
|
110
|
+
typeof s.origin === 'string' && s.origin.trim() ? s.origin.trim().slice(0, 500) : '';
|
|
111
|
+
let r;
|
|
112
|
+
if (typeof s.path === 'string' && s.path.trim()) {
|
|
113
|
+
const p = s.path.trim();
|
|
114
|
+
let sz;
|
|
115
|
+
try {
|
|
116
|
+
sz = size(p);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
return refuse(`sources[${i}].path could not be read (${e.code || e.message}).`, i);
|
|
119
|
+
}
|
|
120
|
+
if (sz > SOURCE_FILE_MAX_BYTES) {
|
|
121
|
+
return refuse(
|
|
122
|
+
`sources[${i}] (${basename(p)}) is ${sz} bytes; the inline source-file cap is ${SOURCE_FILE_MAX_BYTES} bytes.`,
|
|
123
|
+
i,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
let buf;
|
|
127
|
+
try {
|
|
128
|
+
buf = read(p);
|
|
129
|
+
} catch (e) {
|
|
130
|
+
return refuse(`sources[${i}].path could not be read (${e.code || e.message}).`, i);
|
|
131
|
+
}
|
|
132
|
+
name = name || sanitizeSourceFileName(basename(p));
|
|
133
|
+
if (!name) return refuse(`sources[${i}] needs a name.`, i);
|
|
134
|
+
r = fileFromBuffer(buf, { name, mime, origin: origin || p, iso, ...bufferToStored(buf) });
|
|
135
|
+
} else if (typeof s.text === 'string') {
|
|
136
|
+
if (!name) return refuse(`sources[${i}].name is required for a text document.`, i);
|
|
137
|
+
r = fileFromBuffer(Buffer.from(s.text, 'utf8'), {
|
|
138
|
+
name,
|
|
139
|
+
mime,
|
|
140
|
+
origin,
|
|
141
|
+
iso,
|
|
142
|
+
encoding: 'utf8',
|
|
143
|
+
content: s.text,
|
|
144
|
+
});
|
|
145
|
+
} else if (typeof s.content_base64 === 'string') {
|
|
146
|
+
if (!name) return refuse(`sources[${i}].name is required for a binary file.`, i);
|
|
147
|
+
const b64 = s.content_base64.replace(/\s+/g, '');
|
|
148
|
+
if (b64.length % 4 !== 0 || !BASE64_RE.test(b64)) {
|
|
149
|
+
return refuse(`sources[${i}].content_base64 is not valid base64.`, i);
|
|
150
|
+
}
|
|
151
|
+
r = fileFromBuffer(Buffer.from(b64, 'base64'), {
|
|
152
|
+
name,
|
|
153
|
+
mime,
|
|
154
|
+
origin,
|
|
155
|
+
iso,
|
|
156
|
+
encoding: 'base64',
|
|
157
|
+
content: b64,
|
|
158
|
+
});
|
|
159
|
+
} else {
|
|
160
|
+
return refuse(`sources[${i}] needs one of url, path, text (+name) or content_base64 (+name).`, i);
|
|
161
|
+
}
|
|
162
|
+
if (r.error) return refuse(r.error, i);
|
|
163
|
+
total += r.file.bytes;
|
|
164
|
+
files.push(r.file);
|
|
165
|
+
}
|
|
166
|
+
if (total > SOURCE_FILES_MAX_TOTAL_BYTES) {
|
|
167
|
+
return refuse(
|
|
168
|
+
`sources total ${total} bytes exceeds the ${SOURCE_FILES_MAX_TOTAL_BYTES} byte per-capture cap.`,
|
|
169
|
+
0,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
return { ok: true, links, files };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Patches for validated sources: prior-plus-new full array (old consoles) + append hint (new consoles). */
|
|
176
|
+
export function buildSourcesPatches(normalized, currentBuffer = {}) {
|
|
177
|
+
const patches = [];
|
|
178
|
+
if (normalized.files.length > 0) {
|
|
179
|
+
const prior = Array.isArray(currentBuffer?.sourceFiles) ? currentBuffer.sourceFiles : [];
|
|
180
|
+
const fresh = normalized.files.filter(
|
|
181
|
+
(f) => !prior.some((p) => p && p.name === f.name && p.sha256 === f.sha256),
|
|
182
|
+
);
|
|
183
|
+
if (fresh.length > 0) {
|
|
184
|
+
patches.push({ path: 'sourceFiles', value: [...prior, ...fresh], op: 'append', items: fresh });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (normalized.links.length > 0) {
|
|
188
|
+
const prior = Array.isArray(currentBuffer?.sources) ? currentBuffer.sources : [];
|
|
189
|
+
const fresh = normalized.links.filter((l) => !prior.some((p) => p && p.url === l.url));
|
|
190
|
+
if (fresh.length > 0) {
|
|
191
|
+
patches.push({ path: 'sources', value: [...prior, ...fresh], op: 'append', items: fresh });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return patches;
|
|
195
|
+
}
|