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,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* template-pin.mjs — the ONE home of the customer-vault template version and
|
|
3
|
+
* the list of docs a healthy customer vault must always contain (HS-KH0Q).
|
|
4
|
+
*
|
|
5
|
+
* Zero dependencies (node builtins only) so BOTH consumers can import it:
|
|
6
|
+
* - `spex-console/src/lib/cli/template-manifest.ts` re-exports it, and
|
|
7
|
+
* - the zero-dependency standalone doctor (`scripts/lib/standalone-doctor.mjs`,
|
|
8
|
+
* the `vault-template` row) reads it without pulling in the console.
|
|
9
|
+
*
|
|
10
|
+
* Bump `TEMPLATE_VERSION` whenever the shipped manifest content changes in a
|
|
11
|
+
* way a customer vault should be able to detect (new universal doc, changed
|
|
12
|
+
* skeleton, etc.). `TEMPLATE_CORE_DESTS` is guarded against drift from the
|
|
13
|
+
* real manifest by `spex-console/src/lib/cli/template-manifest.test.ts`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Bumped whenever the shipped manifest content changes. */
|
|
17
|
+
export const TEMPLATE_VERSION = "1.1.0";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Vault-relative (POSIX) paths every scaffolded customer vault must hold:
|
|
21
|
+
* the foundational docs plus every universal protocol.
|
|
22
|
+
*/
|
|
23
|
+
export const TEMPLATE_CORE_DESTS = Object.freeze([
|
|
24
|
+
"specs/north-star.md",
|
|
25
|
+
"specs/principles-index.md",
|
|
26
|
+
"specs/roadmap.md",
|
|
27
|
+
"specs/entity-model.md",
|
|
28
|
+
"specs/protocols/canonical-frontmatter-templates.md",
|
|
29
|
+
"specs/protocols/spec-hierarchy.md",
|
|
30
|
+
"specs/protocols/memory-protocol.md",
|
|
31
|
+
"specs/protocols/session-close-protocol.md",
|
|
32
|
+
"specs/protocols/spec-summary-authoring.md",
|
|
33
|
+
"specs/protocols/destructive-action-checklist.md",
|
|
34
|
+
]);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared authorship-provenance targeting predicate
|
|
3
|
+
* ([[HS-EPIC-IKIM-authorship-provenance-frontmatter-owner]]).
|
|
4
|
+
*
|
|
5
|
+
* SSOT for "which specs carry the universal `owner` requirement" — imported by
|
|
6
|
+
* both the gate (`validate-vault-provenance.mjs`) and the one-time backfill
|
|
7
|
+
* (`backfill-vault-provenance.mjs`) so they cannot drift. If they drifted, the
|
|
8
|
+
* post-backfill `block` gate could fire on a file the backfill never touched.
|
|
9
|
+
*
|
|
10
|
+
* `owner` is universally required ([[entity-model]] §Authorship provenance). The
|
|
11
|
+
* `contributors` / `assisted_by` lists are intentionally OPTIONAL (auto-stamped,
|
|
12
|
+
* omit-when-empty), so they are NOT part of this predicate. Frozen statuses
|
|
13
|
+
* (closed/superseded/cancelled/duplicate) are skipped — their frontmatter is
|
|
14
|
+
* immutable historical record.
|
|
15
|
+
*/
|
|
16
|
+
import fs from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { createRequire } from "node:module";
|
|
20
|
+
|
|
21
|
+
export const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
22
|
+
const SPECS_DIR = path.join(REPO_ROOT, "specs");
|
|
23
|
+
|
|
24
|
+
// Reuse spex-console's installed js-yaml (transitive via gray-matter) — same pattern
|
|
25
|
+
// as validate-vault-frontmatter.mjs; no deps of our own.
|
|
26
|
+
const spexConsoleRequire = createRequire(path.join(REPO_ROOT, "spex-console", "package.json"));
|
|
27
|
+
export const yaml = spexConsoleRequire("js-yaml");
|
|
28
|
+
|
|
29
|
+
/** Statuses whose frontmatter is frozen historical record — never flagged. */
|
|
30
|
+
export const FROZEN_STATUSES = new Set(["closed", "superseded", "cancelled", "duplicate"]);
|
|
31
|
+
|
|
32
|
+
/** Recursively collect every *.md under specs/ (relative to repo root, posix sep). */
|
|
33
|
+
export function allSpecFiles() {
|
|
34
|
+
/** @type {string[]} */
|
|
35
|
+
const out = [];
|
|
36
|
+
const walk = (dir) => {
|
|
37
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
38
|
+
const abs = path.join(dir, entry.name);
|
|
39
|
+
if (entry.isDirectory()) walk(abs);
|
|
40
|
+
else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
41
|
+
out.push(path.relative(REPO_ROOT, abs).split(path.sep).join("/"));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
if (fs.existsSync(SPECS_DIR)) walk(SPECS_DIR);
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Extract the raw frontmatter block (between the leading `---` fences). */
|
|
50
|
+
export function extractFrontmatter(raw) {
|
|
51
|
+
const m = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
52
|
+
if (!m) return null;
|
|
53
|
+
return m[1];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Parse a spec's frontmatter to an object.
|
|
58
|
+
* @returns {object|null} null when there is no frontmatter, the YAML is
|
|
59
|
+
* malformed, or it is not a mapping — all of which this gate skips (malformed
|
|
60
|
+
* YAML is the frontmatter gate's job, not ours).
|
|
61
|
+
*/
|
|
62
|
+
export function parseFrontmatter(raw) {
|
|
63
|
+
const fm = extractFrontmatter(raw);
|
|
64
|
+
if (fm === null) return null;
|
|
65
|
+
let data;
|
|
66
|
+
try {
|
|
67
|
+
data = yaml.load(fm);
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (!data || typeof data !== "object") return null;
|
|
72
|
+
return data;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* True when this spec carries the universal `owner` requirement: a typed entity
|
|
77
|
+
* spec whose status is not frozen. Prose-only docs and frozen records are out of
|
|
78
|
+
* scope for both the gate and the backfill.
|
|
79
|
+
*/
|
|
80
|
+
export function isTypedNonFrozen(data) {
|
|
81
|
+
if (!data || typeof data !== "object") return false;
|
|
82
|
+
if (typeof data.type !== "string" || !data.type.trim()) return false;
|
|
83
|
+
const status = typeof data.status === "string" ? data.status.trim().toLowerCase() : "";
|
|
84
|
+
if (FROZEN_STATUSES.has(status)) return false;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The trimmed `owner` scalar, or "" when absent/blank. */
|
|
89
|
+
export function ownerOf(data) {
|
|
90
|
+
return typeof data.owner === "string" ? data.owner.trim() : "";
|
|
91
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Write-time `assisted_by` stamp for direct Edit/Write/MultiEdit tool writes
|
|
4
|
+
* ([[HS-EPIC-IKIM-authorship-provenance-frontmatter-owner]] Phase 3).
|
|
5
|
+
*
|
|
6
|
+
* Wired as a Claude Code **PostToolUse** hook (`.claude/settings.json`). The
|
|
7
|
+
* `updateSpec` edit-path stamp only fires for console-UI / MCP PUT writes; an
|
|
8
|
+
* agent that edits a spec file directly via the Edit tool bypasses it. The
|
|
9
|
+
* literal "git pre-commit stamp" can't fix this — the human reviewer runs the
|
|
10
|
+
* commit and the agent has released its claim by then, so neither the committer
|
|
11
|
+
* nor the claim trail names the editing agent. The only place the agent identity
|
|
12
|
+
* is reliably present is *at edit time*, in the agent's own process env — which
|
|
13
|
+
* is exactly when a PostToolUse hook runs.
|
|
14
|
+
*
|
|
15
|
+
* SAFETY (this runs on every spec edit): it only ever APPENDS one `assisted_by`
|
|
16
|
+
* entry, append-only + de-duped, and NO-OPS (touches nothing) on any frontmatter
|
|
17
|
+
* shape it isn't fully confident about. It always exits 0 — a stamp failure must
|
|
18
|
+
* never disrupt the agent's edit. Reads the PostToolUse payload as JSON on stdin.
|
|
19
|
+
*
|
|
20
|
+
* Targeting (typed + non-frozen) and frontmatter parsing are shared with the gate
|
|
21
|
+
* via `provenance-lib.mjs`, so this stamps exactly the specs the gate governs.
|
|
22
|
+
*/
|
|
23
|
+
import fs from "node:fs";
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
import { REPO_ROOT, parseFrontmatter, isTypedNonFrozen } from "./provenance-lib.mjs";
|
|
26
|
+
import { getActiveConfig } from "../vendor/heyspex-mcp/vault-resolve.mjs";
|
|
27
|
+
|
|
28
|
+
/** Exit without touching anything — the safe default for every uncertain case. */
|
|
29
|
+
function bail() {
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The acting agent's display name, or null when it can't be identified. */
|
|
34
|
+
function detectAgent() {
|
|
35
|
+
const claude = (
|
|
36
|
+
process.env.CLAUDE_CODE_SESSION_ID ||
|
|
37
|
+
process.env.CLAUDE_SESSION_ID ||
|
|
38
|
+
process.env.CLAUDECODE ||
|
|
39
|
+
""
|
|
40
|
+
).trim();
|
|
41
|
+
if (claude) return "Claude Code";
|
|
42
|
+
const cursor = (
|
|
43
|
+
process.env.CURSOR_TRACE_ID ||
|
|
44
|
+
process.env.CURSOR_SESSION_ID ||
|
|
45
|
+
process.env.COMPOSER_SESSION_ID ||
|
|
46
|
+
""
|
|
47
|
+
).trim();
|
|
48
|
+
if (cursor) return "Cursor";
|
|
49
|
+
const explicit = process.env.HEYSPEX_AGENT?.trim();
|
|
50
|
+
if (explicit) return explicit;
|
|
51
|
+
return null; // unknown agent — don't stamp a placeholder
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Read all of stdin (the PostToolUse JSON payload). */
|
|
55
|
+
function readStdin() {
|
|
56
|
+
try {
|
|
57
|
+
return fs.readFileSync(0, "utf8");
|
|
58
|
+
} catch {
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Append `agent` to the frontmatter's `assisted_by` list via surgical text edit
|
|
65
|
+
* (no YAML reflow). Returns the patched text, or null to signal "no safe edit"
|
|
66
|
+
* (caller no-ops). `current` is the parsed value of `assisted_by` (for dedup).
|
|
67
|
+
*/
|
|
68
|
+
function appendAssistedBy(raw, agent, current) {
|
|
69
|
+
// Unexpected shape (present but not a list) → never touch it.
|
|
70
|
+
if (current !== undefined && !Array.isArray(current)) return null;
|
|
71
|
+
// Already present → idempotent no-op. An entry counts as "this agent" whether
|
|
72
|
+
// it is bare ("Claude Code") or model-qualified ("Claude Code:Fable 5") — the
|
|
73
|
+
// `AGENT:MODEL` form ([[entity-model]] §Authorship provenance). Without the
|
|
74
|
+
// prefix check, stamping a bare "Claude Code" onto a spec already crediting
|
|
75
|
+
// "Claude Code:Fable 5" would append a redundant, less-informative duplicate.
|
|
76
|
+
if (
|
|
77
|
+
Array.isArray(current) &&
|
|
78
|
+
current.some((v) => {
|
|
79
|
+
if (typeof v !== "string") return false;
|
|
80
|
+
const t = v.trim();
|
|
81
|
+
return t === agent || t.startsWith(`${agent}:`);
|
|
82
|
+
})
|
|
83
|
+
) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const eol = raw.includes("\r\n") ? "\r\n" : "\n";
|
|
88
|
+
const lines = raw.split(/\r?\n/);
|
|
89
|
+
|
|
90
|
+
// Frontmatter fences (the first must be line 0; parseFrontmatter guaranteed it).
|
|
91
|
+
let f1 = -1;
|
|
92
|
+
let f2 = -1;
|
|
93
|
+
for (let i = 0; i < lines.length; i++) {
|
|
94
|
+
if (lines[i].trim() === "---") {
|
|
95
|
+
if (f1 < 0) f1 = i;
|
|
96
|
+
else {
|
|
97
|
+
f2 = i;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (f1 !== 0 || f2 < 0) return null;
|
|
103
|
+
|
|
104
|
+
// Locate a top-level `assisted_by:` line within the block.
|
|
105
|
+
let abIdx = -1;
|
|
106
|
+
for (let i = f1 + 1; i < f2; i++) {
|
|
107
|
+
if (/^assisted_by\s*:/.test(lines[i])) {
|
|
108
|
+
abIdx = i;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (abIdx < 0) {
|
|
114
|
+
// No assisted_by yet — insert a block list after owner: / status: / type:.
|
|
115
|
+
let anchor = -1;
|
|
116
|
+
for (const key of ["owner", "status", "type"]) {
|
|
117
|
+
for (let i = f1 + 1; i < f2; i++) {
|
|
118
|
+
if (new RegExp(`^${key}\\s*:`).test(lines[i])) {
|
|
119
|
+
anchor = i;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (anchor >= 0) break;
|
|
124
|
+
}
|
|
125
|
+
if (anchor < 0) return null;
|
|
126
|
+
lines.splice(anchor + 1, 0, "assisted_by:", ` - ${agent}`);
|
|
127
|
+
return lines.join(eol);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const abLine = lines[abIdx];
|
|
131
|
+
const inlineMatch = abLine.match(/^(assisted_by\s*:\s*)\[(.*)\]\s*$/);
|
|
132
|
+
if (inlineMatch) {
|
|
133
|
+
// Inline form: assisted_by: [A, B] or assisted_by: []
|
|
134
|
+
const inner = inlineMatch[2].trim();
|
|
135
|
+
const next = inner.length === 0 ? agent : `${inner}, ${agent}`;
|
|
136
|
+
lines[abIdx] = `${inlineMatch[1]}[${next}]`;
|
|
137
|
+
return lines.join(eol);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (/^assisted_by\s*:\s*$/.test(abLine)) {
|
|
141
|
+
// Block form: find the last contiguous `- ` item under it and insert after.
|
|
142
|
+
let lastItem = abIdx;
|
|
143
|
+
let itemIndent = " ";
|
|
144
|
+
for (let i = abIdx + 1; i < f2; i++) {
|
|
145
|
+
const m = lines[i].match(/^(\s+)-\s+/);
|
|
146
|
+
if (m) {
|
|
147
|
+
lastItem = i;
|
|
148
|
+
itemIndent = m[1];
|
|
149
|
+
} else if (lines[i].trim() === "") {
|
|
150
|
+
continue; // tolerate blank lines inside the block
|
|
151
|
+
} else {
|
|
152
|
+
break; // next key — list ended
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (lastItem === abIdx) {
|
|
156
|
+
// `assisted_by:` with no items yet — add the first.
|
|
157
|
+
lines.splice(abIdx + 1, 0, ` - ${agent}`);
|
|
158
|
+
} else {
|
|
159
|
+
lines.splice(lastItem + 1, 0, `${itemIndent}- ${agent}`);
|
|
160
|
+
}
|
|
161
|
+
return lines.join(eol);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// `assisted_by:` in some other shape (e.g. a scalar) → don't touch.
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function main() {
|
|
169
|
+
const payload = readStdin();
|
|
170
|
+
let filePath = "";
|
|
171
|
+
try {
|
|
172
|
+
const obj = JSON.parse(payload);
|
|
173
|
+
filePath = obj?.tool_input?.file_path || obj?.tool_input?.filePath || "";
|
|
174
|
+
} catch {
|
|
175
|
+
return bail();
|
|
176
|
+
}
|
|
177
|
+
if (typeof filePath !== "string" || !filePath.trim()) return bail();
|
|
178
|
+
|
|
179
|
+
const abs = path.resolve(filePath);
|
|
180
|
+
// Must be a markdown spec under a specs/ dir — the code repo's (legacy) or
|
|
181
|
+
// the registry-resolved vault's (the live one since the 2026-08-31 repo split).
|
|
182
|
+
if (!abs.endsWith(".md")) return bail();
|
|
183
|
+
const bases = [REPO_ROOT];
|
|
184
|
+
try {
|
|
185
|
+
const vault = getActiveConfig()?.repoRootAbs;
|
|
186
|
+
if (vault && vault !== REPO_ROOT) bases.push(vault);
|
|
187
|
+
} catch {
|
|
188
|
+
// registry unreadable — the code-repo base still applies
|
|
189
|
+
}
|
|
190
|
+
const underSpecs = bases.some((b) => abs.startsWith(path.join(b, "specs") + path.sep));
|
|
191
|
+
if (!underSpecs) return bail();
|
|
192
|
+
|
|
193
|
+
let raw;
|
|
194
|
+
try {
|
|
195
|
+
raw = fs.readFileSync(abs, "utf8");
|
|
196
|
+
} catch {
|
|
197
|
+
return bail();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const data = parseFrontmatter(raw);
|
|
201
|
+
if (data === null) return bail(); // no/ malformed frontmatter — not our concern
|
|
202
|
+
if (!isTypedNonFrozen(data)) return bail(); // prose-only or frozen historical record
|
|
203
|
+
|
|
204
|
+
const agent = detectAgent();
|
|
205
|
+
if (!agent) return bail();
|
|
206
|
+
|
|
207
|
+
const patched = appendAssistedBy(raw, agent, data.assisted_by);
|
|
208
|
+
if (patched === null || patched === raw) return bail();
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
fs.writeFileSync(abs, patched);
|
|
212
|
+
} catch {
|
|
213
|
+
// Swallow — a stamp must never disrupt the edit.
|
|
214
|
+
}
|
|
215
|
+
process.exit(0);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
main();
|
|
220
|
+
} catch {
|
|
221
|
+
process.exit(0);
|
|
222
|
+
}
|