heyspex 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/bin/hs.mjs +931 -98
- 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 +1085 -103
- package/dist/cli/hs-claim.mjs +936 -63
- package/dist/cli/hs-connect.mjs +9786 -1910
- package/dist/cli/hs-defer.mjs +5267 -3053
- package/dist/cli/hs-dev.mjs +564 -0
- package/dist/cli/hs-env.mjs +385 -299
- package/dist/cli/hs-index-rebuild.mjs +7129 -0
- package/dist/cli/hs-index-reconcile.mjs +7852 -0
- package/dist/cli/hs-init.mjs +777 -138
- package/dist/cli/hs-install.mjs +6583 -485
- package/dist/cli/hs-join.mjs +10137 -2368
- package/dist/cli/hs-vault-pull.mjs +4659 -5882
- package/dist/cli/hs-vault.mjs +3793 -1537
- package/dist/cli/hs-workspace.mjs +2550 -524
- 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 +28 -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 +26 -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 +31 -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 +30 -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/mcp-client-config.md +259 -6
- package/package.json +231 -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/bin/launch-mcp.mjs +95 -0
- package/plugins/heyspex/bin/run-hook.mjs +62 -0
- package/plugins/heyspex/hooks/cursor-hooks.json +227 -0
- package/plugins/heyspex/hooks/hooks.json +361 -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 +410 -0
- package/scripts/claude-hooks/_intent-classifier-semantic.mjs +351 -0
- package/scripts/claude-hooks/_run.mjs +120 -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-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 +575 -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/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 +66 -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 +247 -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 +449 -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/wip-checkpoint.mjs +298 -0
- package/scripts/cursor-hooks/manifest.mjs +235 -0
- package/scripts/dev-server-status.mjs +34 -13
- package/scripts/dev-server-stop.mjs +313 -0
- package/scripts/heyspex-mode.mjs +268 -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 +333 -0
- package/scripts/hs-worktree.mjs +201 -0
- package/scripts/install-claude-code-hooks.mjs +270 -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 +197 -60
- package/scripts/lib/customer-kit.mjs +886 -0
- package/scripts/lib/dev-mode.mjs +244 -28
- package/scripts/lib/heyspex-home.mjs +72 -0
- package/scripts/lib/hs-command.mjs +277 -0
- package/scripts/lib/hs-companion-settings.mjs +472 -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 +11 -21
- package/scripts/lib/hs-source-push.mjs +107 -58
- 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/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 +441 -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/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 +274 -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/git-reflog-audit.mjs +294 -0
- package/spexharness/src/intent-classifier.mjs +194 -0
- package/spexharness/src/intent-directive.mjs +541 -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 +819 -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 +95 -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 +46 -5
- package/vendor/heyspex-mcp/capture-channel.mjs +55 -5
- package/vendor/heyspex-mcp/capture-console-client.mjs +80 -1
- package/vendor/heyspex-mcp/capture-focus.mjs +1035 -29
- package/vendor/heyspex-mcp/capture-outbox.mjs +5 -1
- package/vendor/heyspex-mcp/capture-tools.mjs +1219 -0
- package/vendor/heyspex-mcp/claim-tools.mjs +1124 -0
- package/vendor/heyspex-mcp/cli-verbs.mjs +2076 -0
- package/vendor/heyspex-mcp/cli.mjs +201 -15524
- package/vendor/heyspex-mcp/cloud-tool-surface.mjs +128 -0
- package/vendor/heyspex-mcp/cloud-vault-writes.mjs +412 -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 +657 -16
- package/vendor/heyspex-mcp/console-transport.mjs +166 -0
- package/vendor/heyspex-mcp/credentials.mjs +584 -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 +124 -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 +688 -0
- package/vendor/heyspex-mcp/fleet-handlers.mjs +37 -0
- package/vendor/heyspex-mcp/fleet-queue-tools.mjs +69 -14
- package/vendor/heyspex-mcp/focus-tools.mjs +190 -0
- package/vendor/heyspex-mcp/folder-trust-store.mjs +59 -0
- package/vendor/heyspex-mcp/http-transport.mjs +300 -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 +94 -0
- package/vendor/heyspex-mcp/memory-tools.mjs +523 -0
- package/vendor/heyspex-mcp/mvp-mode.mjs +53 -6
- package/vendor/heyspex-mcp/occupied-keys.mjs +141 -0
- package/vendor/heyspex-mcp/on-deck-brief.mjs +3360 -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 +906 -21
- 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 +325 -0
- package/vendor/heyspex-mcp/procedure-tools.mjs +540 -351
- package/vendor/heyspex-mcp/promote-capture.mjs +386 -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 +660 -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 +29 -9
- 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 +59 -0
- package/vendor/heyspex-mcp/research-focus.mjs +207 -4
- package/vendor/heyspex-mcp/research-handlers.mjs +623 -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/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 +108 -0
- package/vendor/heyspex-mcp/session-tools.mjs +152 -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 +446 -0
- package/vendor/heyspex-mcp/spec-id-lib.mjs +93 -17
- package/vendor/heyspex-mcp/split-research.mjs +301 -56
- package/vendor/heyspex-mcp/stdio-rpc.mjs +22 -0
- package/vendor/heyspex-mcp/submit-for-review.mjs +132 -6
- package/vendor/heyspex-mcp/summary-contract.mjs +105 -0
- package/vendor/heyspex-mcp/summary-write-tools.mjs +224 -0
- package/vendor/heyspex-mcp/symptom-query.mjs +146 -0
- package/vendor/heyspex-mcp/test-helpers.mjs +108 -1
- package/vendor/heyspex-mcp/tool-catalog.mjs +2813 -0
- package/vendor/heyspex-mcp/tool-dispatch.mjs +639 -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 +724 -0
- package/vendor/heyspex-mcp/vault-less-endpoints.mjs +52 -0
- package/vendor/heyspex-mcp/vault-resolve.mjs +366 -68
- package/vendor/heyspex-mcp/verify-reproduction.mjs +270 -30
- package/vendor/heyspex-mcp/whoami.mjs +91 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// spexharness/src/gates/deep-research-cost-gate.mjs
|
|
2
|
+
//
|
|
3
|
+
// Engine port of scripts/claude-hooks/deep-research-cost-gate.mjs — the
|
|
4
|
+
// PreToolUse cost-confirmation + grounding gate over a Workflow/Skill call
|
|
5
|
+
// that launches deep-research (HS-BLH1 grounding extension, epic HS-7HR9).
|
|
6
|
+
// See ./PORTING.md for the general recipe this follows.
|
|
7
|
+
//
|
|
8
|
+
// JUDGMENT CALL — bigger than git-stash-gate.mjs's, read before touching
|
|
9
|
+
// this file: the original script exports NOTHING — no pure decision
|
|
10
|
+
// function, not even its `COST` table or its regex matchers. Its entire
|
|
11
|
+
// decision (inlineScriptTarget / resolveTarget / groundingBlock /
|
|
12
|
+
// isGroundOnlyResearchLaunch / the COST table) is private to the module and
|
|
13
|
+
// tangled with `main()`'s stdin read, stdout write and exit. PORTING.md's
|
|
14
|
+
// step-1 default remedy — extract a pure function INSIDE the original — is
|
|
15
|
+
// exactly what this porting task's own brief forbids for this pass ("do NOT
|
|
16
|
+
// delete or edit anything under scripts/claude-hooks/ ... shells come
|
|
17
|
+
// later"). So, unlike the reference port and unlike git-stash-gate.mjs's
|
|
18
|
+
// port (which could reuse several already-exported primitives), THIS module
|
|
19
|
+
// re-derives the original's decision logic by hand — copied verbatim where
|
|
20
|
+
// the original's own strings/tables are copyable (the COST table, the
|
|
21
|
+
// deny/ask message templates, the grounding-artifact field list), so
|
|
22
|
+
// today's golden fixtures still match. The one real import is
|
|
23
|
+
// `loadResearchFocus` (vendor/heyspex-mcp/research-focus.mjs), which WAS
|
|
24
|
+
// already exported and pure — reused, not re-derived. If the original
|
|
25
|
+
// script is ever changed to export its logic (the "shells come later" plan
|
|
26
|
+
// this brief refers to), this module's private copies should be deleted in
|
|
27
|
+
// favor of importing the real thing — until then, a change to the
|
|
28
|
+
// original's regexes/COST table needs a matching edit here, and vice versa;
|
|
29
|
+
// there is no structural drift-proofing possible while the original stays
|
|
30
|
+
// unexported.
|
|
31
|
+
//
|
|
32
|
+
// Event mapping: PreToolUse (Workflow|Skill) → before_tool.
|
|
33
|
+
// Decision mapping: allow stays allow; the inline-script backstop's `deny`
|
|
34
|
+
// stays `deny`; the grounding block's `deny` stays `deny`; the cost
|
|
35
|
+
// confirmation's `ask` is carried forward as a `rewrite` envelope
|
|
36
|
+
// (`{ ask: true, reason }`) — the same mapping env-vault-guard.mjs's
|
|
37
|
+
// reference port uses for its own `ask` case, since the engine has no
|
|
38
|
+
// synchronous-approval primitive.
|
|
39
|
+
//
|
|
40
|
+
// Env hatches (unchanged): HEYSPEX_DEEP_RESEARCH_GATE_OFF=1 (mutes the whole
|
|
41
|
+
// gate), HEYSPEX_RESEARCH_GROUNDING_GATE_OFF=1 (mutes only the grounding
|
|
42
|
+
// check — the cost ask still fires).
|
|
43
|
+
//
|
|
44
|
+
// `ctx` contract (all optional):
|
|
45
|
+
// ctx.env - env var overrides (falls back to
|
|
46
|
+
// process.env).
|
|
47
|
+
// ctx.cwd - vault root to resolve the
|
|
48
|
+
// research-focus pin and grounding
|
|
49
|
+
// artifact from. Defaults to
|
|
50
|
+
// CLAUDE_PROJECT_DIR, else this repo's
|
|
51
|
+
// root.
|
|
52
|
+
// ctx.loadResearchFocus(root) - override for the research-focus
|
|
53
|
+
// reader. Defaults to the real
|
|
54
|
+
// loadResearchFocus.
|
|
55
|
+
// ctx.readGroundingArtifact(root) - override for the grounding-artifact
|
|
56
|
+
// reader. Tests inject fakes here
|
|
57
|
+
// instead of touching disk.
|
|
58
|
+
|
|
59
|
+
import fs from 'node:fs';
|
|
60
|
+
import path from 'node:path';
|
|
61
|
+
import { fileURLToPath } from 'node:url';
|
|
62
|
+
|
|
63
|
+
import { loadResearchFocus } from '../../../vendor/heyspex-mcp/research-focus.mjs';
|
|
64
|
+
|
|
65
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
66
|
+
const REPO_ROOT = path.resolve(HERE, '..', '..', '..');
|
|
67
|
+
|
|
68
|
+
// Copied verbatim from the original (private consts there) — see the
|
|
69
|
+
// judgment-call note above.
|
|
70
|
+
const GROUNDING_ARTIFACT_REL = path.join('.heyspex', 'research-grounding.json');
|
|
71
|
+
const GROUNDING_FIELDS = ['already_built', 'already_decided', 'owner_reference', 'residual_delta'];
|
|
72
|
+
|
|
73
|
+
const COST = {
|
|
74
|
+
'deep-research': {
|
|
75
|
+
agents: '~97',
|
|
76
|
+
label: 'the BUILT-IN deep-research',
|
|
77
|
+
extra:
|
|
78
|
+
' This is the EXPENSIVE built-in harness and it RATE-LIMITS. Strongly prefer a HeySpex bounded profile instead: deep-research-thorough (~31 agents) or deep-research-quick (~19 agents).',
|
|
79
|
+
},
|
|
80
|
+
'deep-research-thorough': {
|
|
81
|
+
agents: '~31',
|
|
82
|
+
label: 'deep-research-thorough (HeySpex bounded, 3-vote)',
|
|
83
|
+
extra: '',
|
|
84
|
+
},
|
|
85
|
+
'deep-research-medium': {
|
|
86
|
+
agents: '~24',
|
|
87
|
+
label: 'deep-research-medium (HeySpex bounded, 2-vote)',
|
|
88
|
+
extra: '',
|
|
89
|
+
},
|
|
90
|
+
'deep-research-quick': {
|
|
91
|
+
agents: '~19',
|
|
92
|
+
label: 'deep-research-quick (HeySpex bounded, single-vote)',
|
|
93
|
+
extra: '',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Detect the recurring broken launch: a hand-authored inline `script` (often
|
|
98
|
+
// with a `title` like "dynamic workflow: deep-research-thorough") that does
|
|
99
|
+
// not lead with `export const meta` → the harness rejects it. We only trip
|
|
100
|
+
// when there is NO `name`/`scriptPath` (the correct paths) but an inline
|
|
101
|
+
// `script`/`title` clearly targets a deep-research profile. Copied verbatim
|
|
102
|
+
// from the original — see the judgment-call note above.
|
|
103
|
+
function inlineScriptTarget(input) {
|
|
104
|
+
if (typeof input.name === 'string' || typeof input.scriptPath === 'string') return null;
|
|
105
|
+
const hay = `${typeof input.script === 'string' ? input.script : ''}\n${typeof input.title === 'string' ? input.title : ''}`;
|
|
106
|
+
if (!hay.trim()) return null;
|
|
107
|
+
if (/deep-research-thorough/.test(hay)) return 'deep-research-thorough';
|
|
108
|
+
if (/deep-research-medium/.test(hay)) return 'deep-research-medium';
|
|
109
|
+
if (/deep-research-quick/.test(hay)) return 'deep-research-quick';
|
|
110
|
+
if (/deep-research/.test(hay)) return 'deep-research-thorough'; // steer built-in/unmapped to the bounded default
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Resolve which deep-research target a Workflow/Skill call refers to, or
|
|
115
|
+
// null. Copied verbatim from the original.
|
|
116
|
+
function resolveTarget(toolName, input) {
|
|
117
|
+
const candidates = [];
|
|
118
|
+
if (toolName === 'Workflow') {
|
|
119
|
+
if (typeof input.name === 'string') candidates.push(input.name);
|
|
120
|
+
if (typeof input.scriptPath === 'string') candidates.push(input.scriptPath);
|
|
121
|
+
} else if (toolName === 'Skill') {
|
|
122
|
+
if (typeof input.skill === 'string') candidates.push(input.skill);
|
|
123
|
+
}
|
|
124
|
+
for (const c of candidates) {
|
|
125
|
+
if (Object.prototype.hasOwnProperty.call(COST, c)) return c;
|
|
126
|
+
}
|
|
127
|
+
for (const c of candidates) {
|
|
128
|
+
if (/deep-research-thorough/.test(c)) return 'deep-research-thorough';
|
|
129
|
+
if (/deep-research-medium/.test(c)) return 'deep-research-medium';
|
|
130
|
+
if (/deep-research-quick/.test(c)) return 'deep-research-quick';
|
|
131
|
+
if (/deep-research/.test(c)) return 'deep-research';
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// `/hs-res` spends no Anthropic tokens (so no cost ask), but IS still a full
|
|
137
|
+
// external research pass and must still be grounded. Copied verbatim from
|
|
138
|
+
// the original.
|
|
139
|
+
function isGroundOnlyResearchLaunch(toolName, input) {
|
|
140
|
+
if (toolName !== 'Skill') return false;
|
|
141
|
+
return typeof input.skill === 'string' && /^hs-res$/i.test(input.skill.trim());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function defaultReadGroundingArtifact(root) {
|
|
145
|
+
try {
|
|
146
|
+
const full = path.join(root, GROUNDING_ARTIFACT_REL);
|
|
147
|
+
if (!fs.existsSync(full)) return null;
|
|
148
|
+
return JSON.parse(fs.readFileSync(full, 'utf8'));
|
|
149
|
+
} catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Returns a deny-reason string when a promotion-driven deep-research launch
|
|
155
|
+
// is NOT grounded, else null (allow). Copied verbatim from the original
|
|
156
|
+
// (parameterized on `root`/`deps` instead of calling repoRoot() and the
|
|
157
|
+
// module-level readers directly, so ctx can override both for tests).
|
|
158
|
+
function groundingBlock(root, deps) {
|
|
159
|
+
let focus;
|
|
160
|
+
try {
|
|
161
|
+
focus = deps.loadResearchFocus(root);
|
|
162
|
+
} catch {
|
|
163
|
+
return null; // discovery failure → fail open
|
|
164
|
+
}
|
|
165
|
+
const sourceIdeaId = focus?.source_idea_id;
|
|
166
|
+
if (!sourceIdeaId) return null; // no promotion pin → not gated
|
|
167
|
+
|
|
168
|
+
const artifact = deps.readGroundingArtifact(root);
|
|
169
|
+
const grounded =
|
|
170
|
+
artifact &&
|
|
171
|
+
typeof artifact === 'object' &&
|
|
172
|
+
artifact.source_idea_id === sourceIdeaId &&
|
|
173
|
+
GROUNDING_FIELDS.every((k) => typeof artifact[k] === 'string' && artifact[k].trim());
|
|
174
|
+
if (grounded) return null;
|
|
175
|
+
|
|
176
|
+
const why = !artifact
|
|
177
|
+
? 'no grounding artifact was found'
|
|
178
|
+
: artifact.source_idea_id !== sourceIdeaId
|
|
179
|
+
? `the grounding artifact is for a different idea (${artifact.source_idea_id ?? 'unknown'})`
|
|
180
|
+
: 'the grounding artifact is missing already_built / already_decided / owner_reference / residual_delta';
|
|
181
|
+
return (
|
|
182
|
+
`Deep-research launch blocked: this is a promotion-driven research run for ${sourceIdeaId}, but ${why}. ` +
|
|
183
|
+
`Ground FIRST so you research only the residual unknown, not work that already ships or is already decided ` +
|
|
184
|
+
`(the HS-VDPB → HS-LKCO burn re-derived companion features that already exist). Do both routes:\n` +
|
|
185
|
+
` route-1 (plans): heyspex_recall_memories + /hs-ground on the idea.\n` +
|
|
186
|
+
` route-2 (code): check companion/ · spex-console/ · scripts/ · the \`hs\` CLI, PLUS the feature's use case / roadmap / open tasks.\n` +
|
|
187
|
+
` route-3 (owner's working reference): does the owner ALREADY RUN something that does this — a service, container, script, or tool on their own machine? Inspect it and record what it showed. This is the cheapest and highest-authority evidence there is.\n` +
|
|
188
|
+
`Then record the result to ${GROUNDING_ARTIFACT_REL} as ` +
|
|
189
|
+
`{ "source_idea_id": "${sourceIdeaId}", "already_built": "...", "already_decided": "...", "owner_reference": "...", "residual_delta": "...", "recorded_at": "<ISO>" } ` +
|
|
190
|
+
`and relaunch with the RESIDUAL DELTA as the question. If grounding shows it is already built/decided, do NOT launch — record and stop. ` +
|
|
191
|
+
`(Break-glass: set HEYSPEX_RESEARCH_GROUNDING_GATE_OFF=1.)`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** The deep-research cost/grounding gate's id — unique across the built-in registry. */
|
|
196
|
+
export const id = 'deep-research-cost-gate';
|
|
197
|
+
/** Runs pre-tool, same as its PreToolUse Claude Code hook registration. */
|
|
198
|
+
export const event = 'before_tool';
|
|
199
|
+
/** Caller-defined severity label; this gate can deny, so it is "block". */
|
|
200
|
+
export const severity = 'block';
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @param {{ point: string, toolName?: string, toolInput?: Record<string, unknown> }} evt
|
|
204
|
+
* @param {{ cwd?: string, loadResearchFocus?: (root: string) => object | null,
|
|
205
|
+
* readGroundingArtifact?: (root: string) => object | null,
|
|
206
|
+
* env?: Record<string, string | undefined> }} [ctx]
|
|
207
|
+
* @returns {{ decision: 'allow'|'deny'|'rewrite', reason?: string, rewrite?: object }}
|
|
208
|
+
*/
|
|
209
|
+
export function run(evt, ctx = {}) {
|
|
210
|
+
const env = ctx.env ?? process.env;
|
|
211
|
+
if (String(env.HEYSPEX_DEEP_RESEARCH_GATE_OFF ?? '') === '1') {
|
|
212
|
+
return { decision: 'allow' };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const toolName = evt?.toolName;
|
|
216
|
+
if (toolName !== 'Workflow' && toolName !== 'Skill') {
|
|
217
|
+
return { decision: 'allow' };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const input = evt?.toolInput ?? {};
|
|
221
|
+
const root = ctx.cwd ?? process.env.CLAUDE_PROJECT_DIR ?? REPO_ROOT;
|
|
222
|
+
const deps = {
|
|
223
|
+
loadResearchFocus: ctx.loadResearchFocus ?? loadResearchFocus,
|
|
224
|
+
readGroundingArtifact: ctx.readGroundingArtifact ?? defaultReadGroundingArtifact,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Backstop: a hand-authored inline-script deep-research launch (no
|
|
228
|
+
// name/scriptPath) would fail the harness's own validator. Deny it with
|
|
229
|
+
// the exact corrective so the caller relaunches by scriptPath instead.
|
|
230
|
+
if (toolName === 'Workflow') {
|
|
231
|
+
const inlineProfile = inlineScriptTarget(input);
|
|
232
|
+
if (inlineProfile) {
|
|
233
|
+
return {
|
|
234
|
+
decision: 'deny',
|
|
235
|
+
reason:
|
|
236
|
+
`Don't hand-author an inline deep-research workflow script — that trips the ` +
|
|
237
|
+
`"export const meta must be the FIRST statement" validator (the recurring ` +
|
|
238
|
+
`"dynamic workflow: ${inlineProfile}" launch error). Relaunch it via scriptPath so the ` +
|
|
239
|
+
`validated file loads: Workflow({ scriptPath: '.claude/workflows/${inlineProfile}.js', args: '<the research question>' }). ` +
|
|
240
|
+
`Do NOT use the Workflow \`name\` parameter (its loader trips the same validator) and do NOT invoke it via the Skill tool.`,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const target = resolveTarget(toolName, input);
|
|
246
|
+
const groundingOff = String(env.HEYSPEX_RESEARCH_GROUNDING_GATE_OFF ?? '') === '1';
|
|
247
|
+
|
|
248
|
+
// Grounding applies to every external research pass, including the
|
|
249
|
+
// zero-cost /hs-res path — which is grounded but never cost-asked.
|
|
250
|
+
if (!target) {
|
|
251
|
+
if (isGroundOnlyResearchLaunch(toolName, input) && !groundingOff) {
|
|
252
|
+
const groundingDeny = groundingBlock(root, deps);
|
|
253
|
+
if (groundingDeny) return { decision: 'deny', reason: groundingDeny };
|
|
254
|
+
}
|
|
255
|
+
return { decision: 'allow' }; // not a cost-bearing deep-research invocation
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Grounding gate: a promotion-driven deep-research launch must be grounded
|
|
259
|
+
// first. Runs before the cost ask so an ungrounded launch is denied, not
|
|
260
|
+
// merely cost-confirmed. Muted independently of the cost gate.
|
|
261
|
+
if (!groundingOff) {
|
|
262
|
+
const groundingDeny = groundingBlock(root, deps);
|
|
263
|
+
if (groundingDeny) return { decision: 'deny', reason: groundingDeny };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const c = COST[target];
|
|
267
|
+
const reason =
|
|
268
|
+
`Deep-research about to run: ${c.label} — ${c.agents} agent calls ` +
|
|
269
|
+
`(fan-out web search → fetch → adversarially verify → synthesize).${c.extra} ` +
|
|
270
|
+
`Approve to spend those tokens, or cancel and choose the cheaper profile.`;
|
|
271
|
+
return { decision: 'rewrite', reason, rewrite: { ask: true, reason } };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export default { id, event, severity, run };
|