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,37 @@
|
|
|
1
|
+
// heyspex_fleet_queue_* handlers (thin wrappers over fleet-queue-tools.mjs). Extracted from cli.mjs under
|
|
2
|
+
// [[HS-BLNF-6W61-DIAG-cli-mjs-has-grown-into-a-13-665-line]] (no behavior change).
|
|
3
|
+
|
|
4
|
+
import { executeFleetQueueClaim, executeFleetQueueComplete, executeFleetQueueList, executeFleetQueueSubmit, resolveFleetToken } from './fleet-queue-tools.mjs';
|
|
5
|
+
import { consoleFetch } from './console-transport.mjs';
|
|
6
|
+
|
|
7
|
+
export async function handleFleetQueueSubmit(args = {}) {
|
|
8
|
+
const result = await executeFleetQueueSubmit({ consoleFetch, args, fleetToken: resolveFleetToken() });
|
|
9
|
+
return {
|
|
10
|
+
isError: !!result.error,
|
|
11
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function handleFleetQueueClaim(args = {}) {
|
|
16
|
+
const result = await executeFleetQueueClaim({ consoleFetch, args, fleetToken: resolveFleetToken() });
|
|
17
|
+
return {
|
|
18
|
+
isError: !!result.error,
|
|
19
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function handleFleetQueueComplete(args = {}) {
|
|
24
|
+
const result = await executeFleetQueueComplete({ consoleFetch, args, fleetToken: resolveFleetToken() });
|
|
25
|
+
return {
|
|
26
|
+
isError: !!result.error,
|
|
27
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function handleFleetQueueList(args = {}) {
|
|
32
|
+
const result = await executeFleetQueueList({ consoleFetch, args, fleetToken: resolveFleetToken() });
|
|
33
|
+
return {
|
|
34
|
+
isError: !!result.error,
|
|
35
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -66,32 +66,88 @@ function bearerHeaders(fleetToken) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* HS-FS7X
|
|
70
|
-
*
|
|
69
|
+
* HS-FS7X — SUBMIT REFUSES WHILE NO RUNNER DRAINS THE NEON QUEUE.
|
|
70
|
+
*
|
|
71
|
+
* This tool writes to the Neon-backed `/api/fleet/queue`, but
|
|
71
72
|
* `dev-factory/lib/fleet-queue-runner.mjs` — the only thing that actually
|
|
72
73
|
* claims and executes queued work — still reads exclusively from the legacy
|
|
73
|
-
* file-backed queue (`dev-factory/lib/fleet-queue.mjs`).
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
74
|
+
* file-backed queue (`dev-factory/lib/fleet-queue.mjs`). Work submitted here
|
|
75
|
+
* would be durably recorded and never executed.
|
|
76
|
+
*
|
|
77
|
+
* It previously returned `{ id, position, warning }`. That was not enough: a
|
|
78
|
+
* warning riding along on a SUCCESS shape is still a success —
|
|
79
|
+
* `handleFleetQueueSubmit` sets `isError: !!result.error`, so the MCP client
|
|
80
|
+
* saw a green tool call and an agent that submits and moves on never learned
|
|
81
|
+
* the work was dropped. So the submit now REFUSES before the round trip and
|
|
82
|
+
* writes nothing at all — same shape as the argument refusal in
|
|
83
|
+
* [[HS-IRX0-5KFT-DIAG-record-wip-silently-drops-the-summary]]: refuse at the
|
|
84
|
+
* boundary, because a queue with no consumer cannot refuse on your behalf.
|
|
85
|
+
*
|
|
86
|
+
* Repointing the runner at Neon is a separate BUILD, already owned by
|
|
87
|
+
* [[HS-XOCK-60Z1-EPIC-fleet-work-queue-in-heyspex]] (HS-ZXKF: runner -> queue
|
|
88
|
+
* API; HS-HQP1: make fleet-queue.mjs actually select a backend). When that
|
|
89
|
+
* lands, delete this gate. Meanwhile a machine whose runner genuinely does
|
|
90
|
+
* drain Neon opts back in with HEYSPEX_FLEET_QUEUE_RUNNER_READY=1 — an
|
|
91
|
+
* explicit, per-machine assertion, never something a caller can set per-call.
|
|
92
|
+
*/
|
|
93
|
+
export const NEON_QUEUE_RUNNER_READY_ENV = 'HEYSPEX_FLEET_QUEUE_RUNNER_READY';
|
|
94
|
+
|
|
95
|
+
export const NEON_QUEUE_NO_RUNNER_MESSAGE =
|
|
96
|
+
'Refusing to submit — nothing was submitted. The Neon fleet queue has NO runner reading it: ' +
|
|
97
|
+
'dev-factory/lib/fleet-queue-runner.mjs still reads only the legacy JSONL file queue ' +
|
|
98
|
+
'(dev-factory/lib/fleet-queue.mjs), so anything accepted here would be durably recorded and ' +
|
|
99
|
+
'NEVER executed. Track: [[HS-XOCK-60Z1-EPIC-fleet-work-queue-in-heyspex]] (HS-ZXKF repoints the ' +
|
|
100
|
+
'runner; HS-HQP1 makes the fleet-queue facade select a backend). To run work now, enqueue against ' +
|
|
101
|
+
'dev-factory/lib/fleet-queue.mjs directly — that is the queue the runner reads. If this ' +
|
|
102
|
+
'machine’s ' +
|
|
103
|
+
`runner really does drain the Neon queue, set ${NEON_QUEUE_RUNNER_READY_ENV}=1 to re-enable submits.`;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* True only when this machine asserts its runner drains the Neon fleet queue.
|
|
107
|
+
* Defaults to false: the safe answer to "will this run?" is "no".
|
|
108
|
+
*
|
|
109
|
+
* @param {Record<string, string|undefined>} [env]
|
|
77
110
|
*/
|
|
78
|
-
export
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
'
|
|
111
|
+
export function neonQueueRunnerReady(env = process.env) {
|
|
112
|
+
const raw = env?.[NEON_QUEUE_RUNNER_READY_ENV];
|
|
113
|
+
if (typeof raw !== 'string') return false;
|
|
114
|
+
const v = raw.trim().toLowerCase();
|
|
115
|
+
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
116
|
+
}
|
|
83
117
|
|
|
84
118
|
/**
|
|
85
119
|
* `heyspex_fleet_queue_submit` — POST /api/fleet/queue. `kind` defaults to
|
|
86
120
|
* "task" when omitted (the API requires a kind string).
|
|
87
121
|
*
|
|
88
|
-
*
|
|
122
|
+
* HS-FS7X: refuses outright (writing nothing) unless this machine asserts a
|
|
123
|
+
* runner actually drains the Neon queue — see NEON_QUEUE_NO_RUNNER_MESSAGE.
|
|
124
|
+
*
|
|
125
|
+
* @param {{ consoleFetch: Function, args?: Record<string, unknown>, fleetToken?: string|null, env?: Record<string, string|undefined> }} ctx
|
|
89
126
|
*/
|
|
90
|
-
export async function executeFleetQueueSubmit({
|
|
127
|
+
export async function executeFleetQueueSubmit({
|
|
128
|
+
consoleFetch,
|
|
129
|
+
args = {},
|
|
130
|
+
fleetToken = null,
|
|
131
|
+
env = process.env,
|
|
132
|
+
}) {
|
|
91
133
|
const lane = typeof args.lane === 'string' ? args.lane.trim() : '';
|
|
92
134
|
if (!lane) {
|
|
93
135
|
return { error: 'INVALID_ARGS', message: 'lane is required' };
|
|
94
136
|
}
|
|
137
|
+
|
|
138
|
+
// HS-FS7X: refuse BEFORE the round trip. Accepting work into a queue with no
|
|
139
|
+
// consumer and returning `{ id, position }` told the caller it would run.
|
|
140
|
+
if (!neonQueueRunnerReady(env)) {
|
|
141
|
+
return {
|
|
142
|
+
error: 'NO_RUNNER',
|
|
143
|
+
message: NEON_QUEUE_NO_RUNNER_MESSAGE,
|
|
144
|
+
lane,
|
|
145
|
+
submitted: false,
|
|
146
|
+
track: 'HS-XOCK-60Z1',
|
|
147
|
+
override_env: NEON_QUEUE_RUNNER_READY_ENV,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
95
151
|
const kind = typeof args.kind === 'string' && args.kind.trim() ? args.kind.trim() : 'task';
|
|
96
152
|
|
|
97
153
|
const body = {
|
|
@@ -120,7 +176,6 @@ export async function executeFleetQueueSubmit({ consoleFetch, args = {}, fleetTo
|
|
|
120
176
|
id: result.body?.id ?? null,
|
|
121
177
|
position: result.body?.position ?? null,
|
|
122
178
|
lane,
|
|
123
|
-
warning: NEON_QUEUE_NO_RUNNER_WARNING,
|
|
124
179
|
};
|
|
125
180
|
}
|
|
126
181
|
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Focus pin and focus-area tools (heyspex_pin_get/pin_set, set/get/clear_focus_areas,
|
|
2
|
+
// resolve_focus_intent) plus the Neon focus-pin injector the on-deck brief uses. Extracted from cli.mjs under
|
|
3
|
+
// [[HS-BLNF-6W61-DIAG-cli-mjs-has-grown-into-a-13-665-line]] (no behavior change).
|
|
4
|
+
//
|
|
5
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
6
|
+
// Focus-areas CRUD — [[HS-TASK-1GQ5]] / [[HS-EPIC-7WJN]] / [[HS-MIL-AFOC]].
|
|
7
|
+
// Agent-controllable roadmap focus: distinct from the on-deck pin tools above
|
|
8
|
+
// (heyspex_pin_get/set, the `focus_pin` table). These write the `focus_areas`
|
|
9
|
+
// control plane (tag-band) via the console route, which tier-dispatches Neon vs
|
|
10
|
+
// the free-tier `.heyspex/focus-areas.yaml`.
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
import { consoleFetch, controlError, controlOk } from './console-transport.mjs';
|
|
14
|
+
import { detectAgentIdentity, getConfig } from './mcp-config.mjs';
|
|
15
|
+
import { buildDegradedFocusResult, resolveFocusIntentLexical } from './plan-ground.mjs';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Fetch the Neon layer-1 focus pin (the console-set fuse pin) and inject its
|
|
19
|
+
* spec_id into args as `neon_pin_spec_id`, so the synchronous on-deck resolver
|
|
20
|
+
* can let it win precedence #1 over the file's layer-2 `spec_path`.
|
|
21
|
+
*
|
|
22
|
+
* Why this exists: per [[heyspex-is-saas-cloud-primary-both-tiers]] the focus
|
|
23
|
+
* pin lives in Neon `focus_pin` on a Neon-up machine; the file `pin:` block is
|
|
24
|
+
* dormant. So vendor /hs-next — which reads the file synchronously — was blind to
|
|
25
|
+
* a console-set pin (the documented FCPN paid-tier soak gap). We do the one async
|
|
26
|
+
* read here, at the handler boundary, and thread the result down as a sync option.
|
|
27
|
+
*
|
|
28
|
+
* Best-effort: any unreachable / non-OK (incl. free-tier 402) / empty response
|
|
29
|
+
* leaves args untouched, so the resolver falls back to the file focus. Never
|
|
30
|
+
* throws, never blocks /hs-next. Skipped when `ignore_focus` or an explicit
|
|
31
|
+
* `spec_id` (direct lookup, no focus) is set. The GET response is
|
|
32
|
+
* `{ pin: FocusPinRow | null }` with camelCase keys (`pin.specId`).
|
|
33
|
+
*/
|
|
34
|
+
export async function injectNeonFocusPin(args = {}) {
|
|
35
|
+
if (args.ignore_focus === true || args.ignore_focus === 'true') return args;
|
|
36
|
+
if (args.spec_id) return args;
|
|
37
|
+
let next = args;
|
|
38
|
+
try {
|
|
39
|
+
// Both focus layers live in Neon on a Neon-up machine (the file is dormant):
|
|
40
|
+
// layer-1 pin in `focus_pin`, layer-2 selection in `focus_selection`
|
|
41
|
+
// ([[HS-TASK-FCPN]] + [[HS-TASK-FSEL]]). Fetch both, thread the ids into the
|
|
42
|
+
// sync resolver. Responses are `{ pin }` / `{ selection }` with camelCase keys.
|
|
43
|
+
const [pinRes, selRes] = await Promise.all([
|
|
44
|
+
consoleFetch('/api/control/pin'),
|
|
45
|
+
consoleFetch('/api/control/focus-selection'),
|
|
46
|
+
]);
|
|
47
|
+
const pinId = pinRes && pinRes.ok ? pinRes.body?.pin?.specId : null;
|
|
48
|
+
if (typeof pinId === 'string' && pinId) next = { ...next, neon_pin_spec_id: pinId };
|
|
49
|
+
const selPath = selRes && selRes.ok ? selRes.body?.selection?.specPath : null;
|
|
50
|
+
if (typeof selPath === 'string' && selPath) {
|
|
51
|
+
next = { ...next, neon_selection_spec_path: selPath };
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Swallow — fall back to the file focus. Focus is a steering nicety, not a
|
|
55
|
+
// correctness gate; an unreachable console must not break /hs-next.
|
|
56
|
+
}
|
|
57
|
+
return next;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function handlePinGet(_args = {}) {
|
|
61
|
+
const result = await consoleFetch('/api/control/pin');
|
|
62
|
+
if (!result.ok) return controlError(result, 'PIN_GET_FAILED');
|
|
63
|
+
return controlOk(result.body);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Set or clear the on-deck pin — [[HS-EPIC-NCPS]] focus_pin / [[HS-EPIC-CLMW]]
|
|
68
|
+
* `hs pin set` / `hs pin clear`. PUT /api/control/pin. Omit/null
|
|
69
|
+
* spec_id clears (mirrors the endpoint's null-clears contract).
|
|
70
|
+
*/
|
|
71
|
+
export async function handlePinSet(args = {}) {
|
|
72
|
+
const identity = detectAgentIdentity(args.agent, args.session_id);
|
|
73
|
+
const pinnedBy =
|
|
74
|
+
typeof args.pinned_by === 'string' && args.pinned_by.trim()
|
|
75
|
+
? args.pinned_by.trim()
|
|
76
|
+
: identity.agent;
|
|
77
|
+
const clearing = args.spec_id === undefined || args.spec_id === null;
|
|
78
|
+
const body = clearing
|
|
79
|
+
? { spec_id: null, pinned_by: pinnedBy }
|
|
80
|
+
: {
|
|
81
|
+
spec_id: args.spec_id,
|
|
82
|
+
pinned_by: pinnedBy,
|
|
83
|
+
...(args.ttl_ms !== undefined ? { ttl_ms: args.ttl_ms } : {}),
|
|
84
|
+
};
|
|
85
|
+
const result = await consoleFetch('/api/control/pin', { method: 'PUT', body });
|
|
86
|
+
if (!result.ok) return controlError(result, 'PIN_SET_FAILED', { spec_id: args.spec_id ?? null });
|
|
87
|
+
return controlOk(result.body);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** `heyspex_set_focus_areas` → PUT /api/scheduler/focus-areas. */
|
|
91
|
+
export async function handleSetFocusAreas(args = {}) {
|
|
92
|
+
const tags = Array.isArray(args.tags)
|
|
93
|
+
? args.tags.map((t) => String(t).trim()).filter(Boolean)
|
|
94
|
+
: [];
|
|
95
|
+
if (tags.length === 0) {
|
|
96
|
+
return controlError(
|
|
97
|
+
{ ok: false, status: 0, url: '', body: null },
|
|
98
|
+
'FOCUS_AREAS_INVALID',
|
|
99
|
+
{ reason: 'tags must be a non-empty array of known-tag strings' },
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const identity = detectAgentIdentity(args.agent, args.session_id);
|
|
103
|
+
const body = {
|
|
104
|
+
rationale: typeof args.rationale === 'string' ? args.rationale : '',
|
|
105
|
+
tags,
|
|
106
|
+
updated_by: `${identity.agent} · ${identity.sessionId}`,
|
|
107
|
+
...(typeof args.expires_at === 'string' ? { expires_at: args.expires_at } : {}),
|
|
108
|
+
...(typeof args.replace === 'boolean' ? { replace: args.replace } : {}),
|
|
109
|
+
};
|
|
110
|
+
const result = await consoleFetch('/api/scheduler/focus-areas', { method: 'PUT', body });
|
|
111
|
+
if (!result.ok) return controlError(result, 'FOCUS_AREAS_SET_FAILED');
|
|
112
|
+
return controlOk(result.body);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** `heyspex_get_focus_areas` → GET /api/scheduler/focus-areas. */
|
|
116
|
+
export async function handleGetFocusAreas(_args = {}) {
|
|
117
|
+
const result = await consoleFetch('/api/scheduler/focus-areas');
|
|
118
|
+
if (!result.ok) return controlError(result, 'FOCUS_AREAS_GET_FAILED');
|
|
119
|
+
return controlOk(result.body);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** `heyspex_clear_focus_areas` → DELETE /api/scheduler/focus-areas. */
|
|
123
|
+
export async function handleClearFocusAreas(_args = {}) {
|
|
124
|
+
const result = await consoleFetch('/api/scheduler/focus-areas', { method: 'DELETE' });
|
|
125
|
+
if (!result.ok) return controlError(result, 'FOCUS_AREAS_CLEAR_FAILED');
|
|
126
|
+
return controlOk(result.body);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* `heyspex_resolve_focus_intent` — Phase-B lexical resolver ([[HS-TASK-JZEJ]] /
|
|
131
|
+
* [[HS-EPIC-OOPW]]). Runs lexical wide-recall through the console search
|
|
132
|
+
* endpoint and returns a wide candidate window ({ spec, tags, why }[]) for the
|
|
133
|
+
* calling agent's LLM to rerank into focus tags — HeySpex hosts no reranker. No
|
|
134
|
+
* embeddings (Gate B = NO,
|
|
135
|
+
* [[retrieval-substrate-lexical-not-embeddings-gate-b-no]]). Retrieval is
|
|
136
|
+
* bounded `GET /api/search` / `GET /api/specs?tag=` pages, never a vault walk
|
|
137
|
+
* ([[HS-YIR1-8KK5-TASK-planning-grounding-retrieves-through]]). An
|
|
138
|
+
* empty/no-match window is returned (not an error) so the caller can fall back
|
|
139
|
+
* to heyspex_list_known_tags + exact-tag NLU.
|
|
140
|
+
*
|
|
141
|
+
* Graceful degradation ([[HS-TASK-09I3]] / focus-intent-routing §6): if the
|
|
142
|
+
* lexical recall primitive is unreachable or throws (console down, 401, 5xx),
|
|
143
|
+
* the resolver core returns a `degraded` marker rather
|
|
144
|
+
* than raising. The try/catch here is belt-and-suspenders — a throw outside the
|
|
145
|
+
* core's own guard must still resolve to a graceful degraded result so the MCP
|
|
146
|
+
* tool never 500s a focus pivot.
|
|
147
|
+
*/
|
|
148
|
+
export async function handleResolveFocusIntent(args = {}) {
|
|
149
|
+
const { vaultRoot } = getConfig();
|
|
150
|
+
let result;
|
|
151
|
+
try {
|
|
152
|
+
result = await resolveFocusIntentLexical(vaultRoot, {
|
|
153
|
+
utterance: args.utterance,
|
|
154
|
+
top_k: args.top_k,
|
|
155
|
+
});
|
|
156
|
+
} catch (err) {
|
|
157
|
+
result = buildDegradedFocusResult(args.utterance, err);
|
|
158
|
+
}
|
|
159
|
+
return controlOk(result);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Persist the /hs-push-next layer-2 focus selection to Neon `focus_selection`
|
|
164
|
+
* via the console endpoint (so the console On-Deck AND vendor /hs-next, both of
|
|
165
|
+
* which read Neon, see it). There is NO `.heyspex/focus.yaml` fallback
|
|
166
|
+
* ([[no-fallbacks-by-default]]; the focus.yaml-fallback incident) — Neon is the
|
|
167
|
+
* only focus store. If the console is unreachable the push itself still
|
|
168
|
+
* succeeds (the spec files were written); the focus pin just isn't set, and we
|
|
169
|
+
* say so on stderr rather than writing a stale file. Returns true iff focus was
|
|
170
|
+
* persisted.
|
|
171
|
+
*/
|
|
172
|
+
export async function writeFocusSelectionVendor(vaultRoot, specPath, reason) {
|
|
173
|
+
try {
|
|
174
|
+
const res = await consoleFetch('/api/control/focus-selection', {
|
|
175
|
+
method: 'PUT',
|
|
176
|
+
body: { spec_path: specPath, kind: 'epic', picked_by: 'human', reason: reason ?? null },
|
|
177
|
+
});
|
|
178
|
+
if (res && res.ok) return true;
|
|
179
|
+
console.error(
|
|
180
|
+
`[hs-push] focus not pinned — console returned ${res?.status ?? 'no response'} for PUT /api/control/focus-selection. ` +
|
|
181
|
+
'Neon is required (no focus.yaml fallback); re-pin once the console is reachable.',
|
|
182
|
+
);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.error(
|
|
185
|
+
`[hs-push] focus not pinned — console unreachable (${err?.message ?? err}). ` +
|
|
186
|
+
'Neon is required (no focus.yaml fallback); re-pin once the console is reachable.',
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Machine-local folder-trust store I/O — plain ESM.
|
|
3
|
+
*
|
|
4
|
+
* `~/.heyspex/trusted-folders.json` ([[HS-ZBEB-8CMW-TASK-folder-trust-for-spexcode-first-run]])
|
|
5
|
+
* is read by two fronts that cannot share a TypeScript import:
|
|
6
|
+
* - `spex-console/src/lib/cli/folder-trust.ts` (the CLI/`hs chat`/`hs trust` home — TypeScript,
|
|
7
|
+
* run through tsx) — its own decision functions (`resolveFolderTrust`, `recordFolderTrust`,
|
|
8
|
+
* `revokeFolderTrust`, …) stay there; it imports and RE-EXPORTS the I/O functions below
|
|
9
|
+
* rather than re-implementing them.
|
|
10
|
+
* - `vendor/heyspex-mcp/config-settings.mjs` (the settings capability's core — plain ESM, no
|
|
11
|
+
* TypeScript toolchain available) — reads this store directly to answer
|
|
12
|
+
* `heyspex_settings_get("agent_behaviors.spexcode.trusted_folders")`
|
|
13
|
+
* ([[HS-25AJ-8CMW-TASK-folder-trust-revoke-through-the]]).
|
|
14
|
+
*
|
|
15
|
+
* This file is the ONE place this I/O is implemented — never duplicated. Storage contract
|
|
16
|
+
* (realpath-keyed, `{ trusted, decidedAt }` entries) is documented in full in folder-trust.ts's
|
|
17
|
+
* own header; this file only carries the read/write/list primitives, not the trust decision
|
|
18
|
+
* logic itself.
|
|
19
|
+
*/
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import os from 'node:os';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
|
|
24
|
+
/** `~/.heyspex/trusted-folders.json`, relative to a home directory. */
|
|
25
|
+
export const TRUST_STORE_REL = path.join('.heyspex', 'trusted-folders.json');
|
|
26
|
+
|
|
27
|
+
/** Resolve the real trust-store path for a given home directory (defaults to `os.homedir()`). */
|
|
28
|
+
export function trustStorePath(home = os.homedir()) {
|
|
29
|
+
return path.join(home, TRUST_STORE_REL);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Read the trust store from disk. A missing/corrupt/unreadable file reads as an empty store
|
|
33
|
+
* (fail open to "nothing answered yet", never a crash). */
|
|
34
|
+
export function readTrustStore(filePath = trustStorePath()) {
|
|
35
|
+
try {
|
|
36
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
37
|
+
const parsed = JSON.parse(raw);
|
|
38
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
39
|
+
} catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Write the trust store to disk, creating `~/.heyspex/` if needed. */
|
|
45
|
+
export function writeTrustStore(store, filePath = trustStorePath()) {
|
|
46
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
47
|
+
fs.writeFileSync(filePath, `${JSON.stringify(store, null, 2)}\n`, 'utf8');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** List every stored trust decision as a flat array — the read-only listing surface both
|
|
51
|
+
* `hs trust list` and the `agent_behaviors.spexcode.trusted_folders` settings-capability
|
|
52
|
+
* computed key format their output from. */
|
|
53
|
+
export function listTrustedFolders(store) {
|
|
54
|
+
return Object.entries(store).map(([key, entry]) => ({
|
|
55
|
+
path: key,
|
|
56
|
+
trusted: entry.trusted,
|
|
57
|
+
decidedAt: entry.decidedAt,
|
|
58
|
+
}));
|
|
59
|
+
}
|