spexcode 0.6.1 → 0.6.5
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 +99 -149
- package/bin/spex.mjs +4 -0
- package/node_modules/@spexcode/spec-cli/bin/spex.mjs +76 -0
- package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
- package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +33 -0
- package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +307 -0
- package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/cli.js +1859 -0
- package/node_modules/@spexcode/spec-cli/dist/client.d.ts +111 -0
- package/node_modules/@spexcode/spec-cli/dist/client.js +476 -0
- package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
- package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
- package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
- package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +53 -0
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +686 -0
- package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
- package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
- package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
- package/node_modules/@spexcode/spec-cli/dist/delivery-queue.d.ts +23 -0
- package/node_modules/@spexcode/spec-cli/dist/delivery-queue.js +179 -0
- package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/doctor.js +547 -0
- package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
- package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
- package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
- package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +361 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +37 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway.js +503 -0
- package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
- package/node_modules/@spexcode/spec-cli/dist/graphCache.js +716 -0
- package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +35 -0
- package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
- package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
- package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
- package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
- package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
- package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +115 -22
- package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
- package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +325 -0
- package/node_modules/@spexcode/spec-cli/dist/harness.js +3026 -0
- package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
- package/node_modules/@spexcode/spec-cli/dist/help.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/help.js +574 -0
- package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
- package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
- package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
- package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
- package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
- package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
- package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
- package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
- package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
- package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
- package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
- package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
- package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
- package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
- package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +7 -0
- package/{spec-cli/src/listen.ts → node_modules/@spexcode/spec-cli/dist/listen.js} +13 -17
- package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
- package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
- package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
- package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
- package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
- package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
- package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
- package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
- package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
- package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
- package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +4 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +208 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
- package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
- package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
- package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
- package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
- package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
- package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
- package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
- package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
- package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
- package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
- package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
- package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
- package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
- package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
- package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
- package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
- package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
- package/node_modules/@spexcode/spec-cli/dist/session-cursors.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/session-cursors.js +82 -0
- package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
- package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
- package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
- package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
- package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
- package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
- package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
- package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +51 -0
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +233 -0
- package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
- package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
- package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +412 -0
- package/node_modules/@spexcode/spec-cli/dist/sessions.js +4239 -0
- package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
- package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
- package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
- package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
- package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
- package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
- package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
- package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
- package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
- package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
- package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
- package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
- package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
- package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
- package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
- package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +27 -7
- package/node_modules/@spexcode/spec-cli/package.json +47 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/comment-altitude/spec.md +9 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/spec.md +22 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +15 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/forge-link/spec.md +10 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +13 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +12 -0
- package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
- package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
- package/node_modules/@spexcode/spec-core/dist/git.d.ts +166 -0
- package/node_modules/@spexcode/spec-core/dist/git.js +2736 -0
- package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
- package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
- package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
- package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
- package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
- package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
- package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
- package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
- package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
- package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
- package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
- package/node_modules/@spexcode/spec-core/dist/layout.d.ts +183 -0
- package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
- package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
- package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
- package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
- package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
- package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
- package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
- package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
- package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
- package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
- package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
- package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
- package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
- package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
- package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
- package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
- package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
- package/node_modules/@spexcode/spec-core/package.json +28 -0
- package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
- package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
- package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
- package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
- package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
- package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
- package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
- package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
- package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
- package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
- package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
- package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
- package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
- package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
- package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
- package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
- package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
- package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
- package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
- package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
- package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
- package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
- package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
- package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
- package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
- package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
- package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
- package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
- package/node_modules/@spexcode/spec-eval/package.json +36 -0
- package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
- package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
- package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
- package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
- package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
- package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
- package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
- package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
- package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
- package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
- package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
- package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
- package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
- package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
- package/node_modules/@spexcode/spec-forge/package.json +32 -0
- package/package.json +33 -19
- package/spec-cli/bin/spex.mjs +0 -69
- package/spec-cli/src/anchors.ts +0 -706
- package/spec-cli/src/attach.ts +0 -43
- package/spec-cli/src/claude-headless.ts +0 -321
- package/spec-cli/src/cli.ts +0 -1115
- package/spec-cli/src/client.ts +0 -315
- package/spec-cli/src/cockpit.ts +0 -43
- package/spec-cli/src/codex-runtime-generations.ts +0 -646
- package/spec-cli/src/commit-surgery.ts +0 -68
- package/spec-cli/src/contract-filter.ts +0 -202
- package/spec-cli/src/delivery-queue.ts +0 -107
- package/spec-cli/src/doctor.ts +0 -516
- package/spec-cli/src/file-write.ts +0 -22
- package/spec-cli/src/gateway-auth.ts +0 -181
- package/spec-cli/src/gateway-hub.ts +0 -356
- package/spec-cli/src/gateway.ts +0 -368
- package/spec-cli/src/git.ts +0 -2180
- package/spec-cli/src/graph.ts +0 -221
- package/spec-cli/src/graphCache.ts +0 -641
- package/spec-cli/src/graphDelta.ts +0 -77
- package/spec-cli/src/graphStream.ts +0 -1019
- package/spec-cli/src/harness-select.ts +0 -77
- package/spec-cli/src/harness.ts +0 -2747
- package/spec-cli/src/headless-controller.ts +0 -63
- package/spec-cli/src/help.ts +0 -480
- package/spec-cli/src/host-resources.ts +0 -707
- package/spec-cli/src/host.ts +0 -611
- package/spec-cli/src/identity-presets.d.ts +0 -13
- package/spec-cli/src/identity-presets.js +0 -128
- package/spec-cli/src/index.ts +0 -730
- package/spec-cli/src/init.ts +0 -258
- package/spec-cli/src/issues-cli.ts +0 -276
- package/spec-cli/src/issues.ts +0 -237
- package/spec-cli/src/layout.ts +0 -557
- package/spec-cli/src/lint.ts +0 -458
- package/spec-cli/src/localIssues.ts +0 -538
- package/spec-cli/src/materialize.ts +0 -412
- package/spec-cli/src/mentions.ts +0 -81
- package/spec-cli/src/opencode-headless.ts +0 -205
- package/spec-cli/src/pi-headless.ts +0 -153
- package/spec-cli/src/plugin-harness.ts +0 -157
- package/spec-cli/src/process-identity.ts +0 -207
- package/spec-cli/src/project-identity.ts +0 -73
- package/spec-cli/src/project-store.ts +0 -17
- package/spec-cli/src/pty-bridge.ts +0 -396
- package/spec-cli/src/pty-helper.mjs +0 -96
- package/spec-cli/src/pty-native-helper.mjs +0 -22
- package/spec-cli/src/ranker.ts +0 -191
- package/spec-cli/src/reaper.ts +0 -125
- package/spec-cli/src/resilience.ts +0 -41
- package/spec-cli/src/reviewSnapshot.ts +0 -28
- package/spec-cli/src/reviews.ts +0 -405
- package/spec-cli/src/runtime-guard.ts +0 -35
- package/spec-cli/src/runtime-ownership.ts +0 -88
- package/spec-cli/src/search.bench.mjs +0 -80
- package/spec-cli/src/search.ts +0 -63
- package/spec-cli/src/session-cursors.ts +0 -91
- package/spec-cli/src/session-follow.ts +0 -182
- package/spec-cli/src/session-timeline.ts +0 -98
- package/spec-cli/src/sessions.ts +0 -3403
- package/spec-cli/src/sh.ts +0 -3
- package/spec-cli/src/slash-commands.ts +0 -318
- package/spec-cli/src/source-files.ts +0 -104
- package/spec-cli/src/specs.ts +0 -498
- package/spec-cli/src/supervise.ts +0 -245
- package/spec-cli/src/tree.ts +0 -118
- package/spec-cli/src/tsx-bin.ts +0 -18
- package/spec-cli/src/uninstall.ts +0 -171
- package/spec-cli/src/uploads.ts +0 -253
- package/spec-cli/src/worktree-sources.ts +0 -42
- package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
- package/spec-cli/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -28
- package/spec-cli/templates/spec/project/.plugins/core/spec.md +0 -23
- package/spec-cli/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -32
- package/spec-cli/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -9
- package/spec-cli/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -15
- package/spec-cli/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -18
- package/spec-dashboard/dist/assets/App-F9uaAVcH.js +0 -2
- package/spec-dashboard/dist/assets/Dashboard-Ba_jhxp1.js +0 -27
- package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
- package/spec-dashboard/dist/assets/EvalsPage-FixoOg_n.js +0 -2
- package/spec-dashboard/dist/assets/IssuesPage-CuKLFhH3.js +0 -1
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/spec-dashboard/dist/assets/MobileApp-CHgEHORJ.js +0 -2
- package/spec-dashboard/dist/assets/Modal-CQgYymmr.js +0 -1
- package/spec-dashboard/dist/assets/PageScroll-hT7UTLvD.js +0 -1
- package/spec-dashboard/dist/assets/ProjectsPage-CtXxakF9.js +0 -1
- package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
- package/spec-dashboard/dist/assets/SessionInterface-Bpie-9fs.js +0 -39
- package/spec-dashboard/dist/assets/SessionWindow-CixDi4PI.js +0 -1
- package/spec-dashboard/dist/assets/Settings-C2MsucfE.js +0 -1
- package/spec-dashboard/dist/assets/Thread-C6Go8HRh.js +0 -13
- package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
- package/spec-dashboard/dist/assets/data-B-RQmit6.js +0 -1
- package/spec-dashboard/dist/assets/index-CsI8DElI.css +0 -1
- package/spec-dashboard/dist/assets/index-DrVao0Ep.js +0 -41
- package/spec-dashboard/dist/assets/launch-BBH02b1v.js +0 -276
- package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
- package/spec-dashboard/dist/assets/terminalFont-C6oGNKrf.js +0 -1
- package/spec-dashboard/dist/index.html +0 -29
- package/spec-dashboard/src/reviewFilters.js +0 -322
- package/spec-dashboard/src/reviewQuery.js +0 -174
- package/spec-dashboard/src/session.js +0 -199
- package/spec-eval/src/cache.ts +0 -57
- package/spec-eval/src/cli.ts +0 -889
- package/spec-eval/src/evaltab.ts +0 -262
- package/spec-eval/src/filing.ts +0 -39
- package/spec-eval/src/freshness.ts +0 -557
- package/spec-eval/src/humanok.ts +0 -28
- package/spec-eval/src/scenariofresh.ts +0 -244
- package/spec-eval/src/scenarios.ts +0 -672
- package/spec-eval/src/sessioneval.ts +0 -2226
- package/spec-eval/src/sidecar.ts +0 -93
- package/spec-eval/src/timeline.ts +0 -54
- package/spec-forge/src/__fixtures__/github-forge.json +0 -63
- package/spec-forge/src/cache.ts +0 -66
- package/spec-forge/src/cli.ts +0 -99
- package/spec-forge/src/drivers/github.ts +0 -132
- package/spec-forge/src/drivers/gitlab.ts +0 -153
- package/spec-forge/src/drivers.ts +0 -73
- package/spec-forge/src/links.ts +0 -72
- package/spec-forge/src/needs-eval.ts +0 -29
- package/spec-forge/src/port.ts +0 -36
- package/spec-forge/src/resident.ts +0 -46
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
import { writeFileSync, mkdirSync, readFileSync, existsSync, readdirSync, renameSync, rmSync, rmdirSync } from 'node:fs';
|
|
2
|
+
import { join, dirname, relative } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import { loadSystemConfig, loadSkillConfig, loadAgentConfig, loadConfig } from '@spexcode/spec-core';
|
|
6
|
+
import { compileManifest } from './hooks.js';
|
|
7
|
+
import { writeManagedBlock, removeManagedBlock, HARNESSES } from './harness.js';
|
|
8
|
+
import { git, gitBinary } from '@spexcode/spec-core';
|
|
9
|
+
import { runtimeRoot, treeSlotDir, mainCheckout, readConfig } from '@spexcode/spec-core';
|
|
10
|
+
import { resolveHarnessTargets, partitionHarnesses } from './harness-select.js';
|
|
11
|
+
import { emitPlugin, cleanPlugin, pluginBundleDir, pluginVersion } from './plugin-harness.js';
|
|
12
|
+
import { clearContractFilterPayload, contractFilterPlanted, plantContractFilter, removeContractFilter, settleIndexStat } from './contract-filter.js';
|
|
13
|
+
import { writeFileIfChanged } from './file-write.js';
|
|
14
|
+
const PKG = fileURLToPath(new URL('..', import.meta.url)); // installed spec-cli root
|
|
15
|
+
const DISPATCH = join(PKG, 'hooks', 'dispatch.sh');
|
|
16
|
+
// the ONE spex entry: the launcher (bin/spex.mjs), never a raw source entry - the launcher runs compiled
|
|
17
|
+
// CLI code and keeps the source-workspace mid-merge guard (one line + exit 75), so every hook callback
|
|
18
|
+
// inherits both.
|
|
19
|
+
const SPEX = join(PKG, 'bin', 'spex.mjs');
|
|
20
|
+
// the manifest + content-hash marker + plugin-folder ledger land in the materialized TREE's own slot of the
|
|
21
|
+
// GLOBAL per-project store (layout.treeSlotDir — trees/<enc-worktree>), NOT the worktree and NOT one shared
|
|
22
|
+
// per-project file: each is a pure function of ONE tree's .plugins, and the old single slot let the last-
|
|
23
|
+
// materialized tree's hook set reach every other tree's dispatch ([[hook-dispatch]]). The worktree keeps
|
|
24
|
+
// zero SpexCode-materialized runtime; only the harness-discovered contract files + shims (which the harness
|
|
25
|
+
// must find in-tree) are written under proj below.
|
|
26
|
+
// the deterministic content fingerprint of the config roots + THE TOOLCHAIN ITSELF (`hp_config_hash` in the
|
|
27
|
+
// shell mirror, harness.sh). Stamped as a freshness record after every materialize; it folds in
|
|
28
|
+
// hp_toolchain_version (the toolchain-side content hash), so a stale stamp is diagnosable after a toolchain
|
|
29
|
+
// update ([[harness-delivery]]).
|
|
30
|
+
export function contentHash(proj) {
|
|
31
|
+
try {
|
|
32
|
+
const harnessSh = join(PKG, 'hooks', 'harness.sh');
|
|
33
|
+
const gitDir = dirname(gitBinary(process.env));
|
|
34
|
+
const env = { ...process.env, PATH: `${gitDir}:${process.env.PATH || ''}` };
|
|
35
|
+
return execFileSync('bash', ['-c', `cd "${proj}" && . "${harnessSh}" && hp_config_hash`], { env }).toString().trim();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function retiredAxisNotice(cfg) {
|
|
42
|
+
if (!cfg.render?.trim() && !cfg.private)
|
|
43
|
+
return;
|
|
44
|
+
const field = cfg.render?.trim() ? `"render": "${cfg.render.trim()}"` : '"private": true';
|
|
45
|
+
console.error(`spexcode: the render vote is retired — ${field} is ignored. Materialized artifacts are never tracked:\n` +
|
|
46
|
+
` tree-local ignore rules live in a filtered working .gitignore, and a host-tracked contract is covered by the\n` +
|
|
47
|
+
` clean/smudge filter, and a clone without spex runs \`spex materialize\` in its setup step. Remove the\n` +
|
|
48
|
+
` field from spexcode.json / spexcode.local.json to retire this notice (see \`spex guide footprint\`).`);
|
|
49
|
+
}
|
|
50
|
+
function gitCommonDirOf(proj) {
|
|
51
|
+
return git(['-C', proj, 'rev-parse', '--path-format=absolute', '--git-common-dir']).trim();
|
|
52
|
+
}
|
|
53
|
+
function infoExcludePath(proj) {
|
|
54
|
+
return join(gitCommonDirOf(proj), 'info', 'exclude');
|
|
55
|
+
}
|
|
56
|
+
function isTracked(proj, file) {
|
|
57
|
+
try {
|
|
58
|
+
git(['-C', proj, 'ls-files', '--error-unmatch', file]);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function clearSkipWorktree(proj, file) {
|
|
66
|
+
if (!isTracked(proj, file))
|
|
67
|
+
return;
|
|
68
|
+
try {
|
|
69
|
+
git(['-C', proj, 'update-index', '--no-skip-worktree', file]);
|
|
70
|
+
}
|
|
71
|
+
catch { }
|
|
72
|
+
}
|
|
73
|
+
function registeredTrees(proj) {
|
|
74
|
+
const rows = git(['-C', mainCheckout(proj), 'worktree', 'list', '--porcelain', '-z']).split('\0');
|
|
75
|
+
return rows.filter((row) => row.startsWith('worktree ')).map((row) => row.slice('worktree '.length));
|
|
76
|
+
}
|
|
77
|
+
function selectionBody(selected, plugin = false) {
|
|
78
|
+
return [...new Set([...selected.map((h) => h.dispatchId), ...(plugin ? ['plugin'] : [])])].sort().join('\n') + '\n';
|
|
79
|
+
}
|
|
80
|
+
function publishSelection(path, body) {
|
|
81
|
+
try {
|
|
82
|
+
if (readFileSync(path, 'utf8') === body)
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (error?.code !== 'ENOENT')
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
const prepared = `${path}.${process.pid}.tmp`;
|
|
90
|
+
writeFileSync(prepared, body);
|
|
91
|
+
renameSync(prepared, path);
|
|
92
|
+
}
|
|
93
|
+
function managedBlockPattern(comment) {
|
|
94
|
+
const [open, close] = comment;
|
|
95
|
+
const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
96
|
+
return new RegExp(`\\n*${escape(`${open}spexcode:start${close}`)}[\\s\\S]*?${escape(`${open}spexcode:end${close}`)}\\n*`);
|
|
97
|
+
}
|
|
98
|
+
export function stripSpexcodeBlock(text, comment = ['<!-- ', ' -->']) {
|
|
99
|
+
const sentinel = managedBlockPattern(comment);
|
|
100
|
+
const m = sentinel.exec(text);
|
|
101
|
+
if (!m)
|
|
102
|
+
return text;
|
|
103
|
+
// mirror removeManagedBlock exactly: our block + its surrounding blanks collapse to one '\n', and only a
|
|
104
|
+
// block sitting at the TOP of the file drops the leading newline (a host file beginning with its own
|
|
105
|
+
// blank lines keeps them — clean(smudge(x)) == x).
|
|
106
|
+
const replaced = text.replace(sentinel, '\n');
|
|
107
|
+
return m.index === 0 ? replaced.replace(/^\n+/, '') : replaced;
|
|
108
|
+
}
|
|
109
|
+
function hostContentOf(file) {
|
|
110
|
+
if (!existsSync(file))
|
|
111
|
+
return '';
|
|
112
|
+
return stripSpexcodeBlock(readFileSync(file, 'utf8'));
|
|
113
|
+
}
|
|
114
|
+
// the identity stamp on every generated skill/agent file — what lets the erase phase forget a product whose
|
|
115
|
+
// NODE was renamed or deleted (the name-scoped sweep can only reconstruct paths the LIVE config still names).
|
|
116
|
+
export const GENERATED_MARK = '<!-- spexcode:generated -->';
|
|
117
|
+
function pruneGeneratedSkills(dir, keep) {
|
|
118
|
+
if (!dir || !existsSync(dir))
|
|
119
|
+
return false;
|
|
120
|
+
let changed = false;
|
|
121
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
122
|
+
if (!e.isDirectory())
|
|
123
|
+
continue;
|
|
124
|
+
const f = join(dir, e.name, 'SKILL.md');
|
|
125
|
+
try {
|
|
126
|
+
if (!keep.has(e.name) && existsSync(f) && readFileSync(f, 'utf8').includes(GENERATED_MARK)) {
|
|
127
|
+
rmSync(join(dir, e.name), { recursive: true, force: true });
|
|
128
|
+
changed = true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch { /* unreadable → not provably ours */ }
|
|
132
|
+
}
|
|
133
|
+
return changed;
|
|
134
|
+
}
|
|
135
|
+
function pruneGeneratedAgents(dir, keep) {
|
|
136
|
+
if (!dir || !existsSync(dir))
|
|
137
|
+
return false;
|
|
138
|
+
let changed = false;
|
|
139
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
140
|
+
if (!e.isFile() || !e.name.endsWith('.md'))
|
|
141
|
+
continue;
|
|
142
|
+
const f = join(dir, e.name);
|
|
143
|
+
try {
|
|
144
|
+
if (!keep.has(e.name.slice(0, -3)) && readFileSync(f, 'utf8').includes(GENERATED_MARK)) {
|
|
145
|
+
rmSync(f, { force: true });
|
|
146
|
+
changed = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch { /* unreadable → not provably ours */ }
|
|
150
|
+
}
|
|
151
|
+
return changed;
|
|
152
|
+
}
|
|
153
|
+
function cleanupHarnessDirs(proj) {
|
|
154
|
+
const roots = new Set([proj, mainCheckout(proj)]);
|
|
155
|
+
const dirs = HARNESSES.flatMap((h) => {
|
|
156
|
+
const anchor = h.worktreeHookAnchor(proj);
|
|
157
|
+
return [h.skillDir(proj), h.agentDir(proj), dirname(h.shimFile(proj)), anchor ? dirname(anchor) : null];
|
|
158
|
+
}).filter((d) => !!d);
|
|
159
|
+
for (const d of [...new Set([...dirs, ...dirs.map((dir) => dirname(dir))])]
|
|
160
|
+
.filter((dir) => !roots.has(dir)).sort((a, b) => b.length - a.length)) {
|
|
161
|
+
try {
|
|
162
|
+
rmdirSync(d);
|
|
163
|
+
}
|
|
164
|
+
catch { }
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function reconcileTree(proj, targets, tracked) {
|
|
168
|
+
let removed = false;
|
|
169
|
+
const contractFiles = new Set(HARNESSES.flatMap((h) => h.contractFiles(proj)));
|
|
170
|
+
for (const file of contractFiles) {
|
|
171
|
+
if (targets.contracts.has(file) || !existsSync(file))
|
|
172
|
+
continue;
|
|
173
|
+
const text = readFileSync(file, 'utf8');
|
|
174
|
+
if (!text.includes('<!-- spexcode:start -->'))
|
|
175
|
+
continue;
|
|
176
|
+
removeManagedBlock(file, ['<!-- ', ' -->'], !tracked(file));
|
|
177
|
+
removed = true;
|
|
178
|
+
}
|
|
179
|
+
const treeShims = new Set(HARNESSES.filter((h) => h.shimScope === 'tree').map((h) => h.shimFile(proj)));
|
|
180
|
+
const anchors = new Set(HARNESSES.map((h) => h.worktreeHookAnchor(proj)).filter((path) => !!path));
|
|
181
|
+
for (const file of [...treeShims, ...anchors]) {
|
|
182
|
+
if (targets.treeShims.has(file) || targets.anchors.has(file) || !existsSync(file))
|
|
183
|
+
continue;
|
|
184
|
+
if (readFileSync(file, 'utf8').includes('dispatch.sh')) {
|
|
185
|
+
rmSync(file, { force: true });
|
|
186
|
+
removed = true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
for (const dir of new Set(HARNESSES.map((h) => h.skillDir(proj)).filter((path) => !!path)))
|
|
190
|
+
removed = pruneGeneratedSkills(dir, targets.skills.get(dir) ?? new Set()) || removed;
|
|
191
|
+
for (const dir of new Set(HARNESSES.map((h) => h.agentDir(proj)).filter((path) => !!path)))
|
|
192
|
+
removed = pruneGeneratedAgents(dir, targets.agents.get(dir) ?? new Set()) || removed;
|
|
193
|
+
if (removed)
|
|
194
|
+
cleanupHarnessDirs(proj);
|
|
195
|
+
}
|
|
196
|
+
function eraseTree(proj, arts, preserveProject) {
|
|
197
|
+
for (const h of HARNESSES) {
|
|
198
|
+
// h.clean = the adapter's surgical inverse: contract block (sentinels, deleteIfEmpty), the dispatch.sh-
|
|
199
|
+
// stamped shim + worktree anchor, the trust block, and the arts-named skill/agent files.
|
|
200
|
+
h.clean(proj, arts, preserveProject);
|
|
201
|
+
for (const f of h.contractFiles(proj))
|
|
202
|
+
clearSkipWorktree(proj, f);
|
|
203
|
+
pruneGeneratedSkills(h.skillDir(proj), new Set());
|
|
204
|
+
pruneGeneratedAgents(h.agentDir(proj), new Set());
|
|
205
|
+
}
|
|
206
|
+
// same authorship rule as the contract files: deleteIfEmpty only when .gitignore is UNTRACKED (wholly-ours
|
|
207
|
+
// generated file); a HOST-TRACKED .gitignore that carried nothing but our block is stripped, never deleted.
|
|
208
|
+
removeManagedBlock(join(proj, '.gitignore'), ['# ', ''], !isTracked(proj, '.gitignore'));
|
|
209
|
+
clearContractFilterPayload(proj, [...HARNESSES.flatMap((h) => h.contractFiles(proj)), join(proj, '.gitignore')]);
|
|
210
|
+
// the block-strip left tracked contract files stat-dirty (under a filter git NEVER content-verifies them,
|
|
211
|
+
// and even unfiltered the phantom-`M` lingers) — settle the index stat, content-guarded so a user's real
|
|
212
|
+
// unstaged edit is never staged ([[content-filter]] edge 2).
|
|
213
|
+
try {
|
|
214
|
+
settleIndexStat(proj, HARNESSES.flatMap((h) => h.contractFiles(proj)));
|
|
215
|
+
}
|
|
216
|
+
catch { /* not a git repo */ }
|
|
217
|
+
// leaving nothing behind: drop the now-EMPTY dirs the assert phase mkdir'ed (.claude/.codex/.opencode/.pi
|
|
218
|
+
// and their skills/agents/plugins/extensions subdirs). Each dir AND its parent are swept deepest-first,
|
|
219
|
+
// because a harness may nest its shim a level below its home (opencode's .opencode/plugins/, pi's
|
|
220
|
+
// .pi/extensions/) — but never the checkout roots themselves. rmdirSync is NON-recursive, so a dir holding
|
|
221
|
+
// any user file survives untouched; `.git/spexcode/` is deliberately NOT swept (shared per-clone home).
|
|
222
|
+
cleanupHarnessDirs(proj);
|
|
223
|
+
}
|
|
224
|
+
export function dematerialize(proj = process.cwd(), arts = { skills: [], agents: [] }) {
|
|
225
|
+
const trees = registeredTrees(proj);
|
|
226
|
+
const current = git(['-C', proj, 'rev-parse', '--show-toplevel']).trim();
|
|
227
|
+
for (const tree of trees) {
|
|
228
|
+
if (!existsSync(tree))
|
|
229
|
+
throw new Error(`cannot dematerialize project while registered worktree ${tree} is inaccessible — repair or remove/prune it first`);
|
|
230
|
+
git(['-C', tree, 'rev-parse', '--show-toplevel']);
|
|
231
|
+
}
|
|
232
|
+
for (const tree of trees) {
|
|
233
|
+
// Only the caller's live spec may widen the name sweep. Siblings are identity-stamp-only: the
|
|
234
|
+
// same name there may be user-owned or may not exist in this tree's divergent spec at all.
|
|
235
|
+
eraseTree(tree, tree === current ? arts : { skills: [], agents: [] }, false);
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
removeManagedBlock(infoExcludePath(proj), ['# ', ''], false);
|
|
239
|
+
}
|
|
240
|
+
catch { /* not a git repo */ }
|
|
241
|
+
removeContractFilter(proj, [...HARNESSES.flatMap((h) => h.contractFiles(proj)), join(proj, '.gitignore')], true);
|
|
242
|
+
}
|
|
243
|
+
// the whole pay-per-change materialize. proj defaults to cwd. Its receipt is populated at each successful
|
|
244
|
+
// write so callers report the actual selected footprint instead of maintaining a second artifact inventory.
|
|
245
|
+
export function materialize(proj = process.cwd()) {
|
|
246
|
+
const rt = treeSlotDir(proj); // this tree's slot in the global store, not the worktree
|
|
247
|
+
mkdirSync(rt, { recursive: true });
|
|
248
|
+
const planted = [];
|
|
249
|
+
const record = (kind, path) => {
|
|
250
|
+
if (!planted.some((a) => a.kind === kind && a.path === path))
|
|
251
|
+
planted.push({ kind, path });
|
|
252
|
+
};
|
|
253
|
+
// (1) hook manifest (persistent — the dispatcher reads it; regenerated only here, on change).
|
|
254
|
+
const manifest = join(rt, 'hooks-manifest');
|
|
255
|
+
writeFileIfChanged(manifest, compileManifest());
|
|
256
|
+
record('hook manifest', manifest);
|
|
257
|
+
// (2) the contract = the surface:system plugin bodies (in name order), written WHOLE into EACH harness's
|
|
258
|
+
// contract file(s) + (3) each harness's thin shim → dispatch.sh + (4) its trust. All owned by the adapter.
|
|
259
|
+
// ONE source, no per-project escape hatch: the contract IS the surface:system plugin bodies. A project's
|
|
260
|
+
// own hand-written prose is not folded in — repo-local notes belong in the harness file's own
|
|
261
|
+
// block-outside region (untracked, per-clone), and anything that must reach EVERY agent is a plugin node.
|
|
262
|
+
const contract = loadSystemConfig().map((c) => c.body.trim()).filter(Boolean).join('\n\n');
|
|
263
|
+
// WHICH harnesses to deliver into ([[harness-select]]): this tree's explicit spexcode.json `harnesses` set.
|
|
264
|
+
// resolveHarnessTargets FAILS LOUD on an illegal set (plugin+native, plugin w/o folder).
|
|
265
|
+
const cfg = readConfig(proj);
|
|
266
|
+
const targets = resolveHarnessTargets(cfg.harnesses);
|
|
267
|
+
retiredAxisNotice(cfg); // [[residence]] — the vote axis is retired
|
|
268
|
+
const { selected, plugins } = partitionHarnesses(targets);
|
|
269
|
+
const skillNodes = loadSkillConfig();
|
|
270
|
+
const agentNodes = loadAgentConfig();
|
|
271
|
+
const commandNodes = loadConfig();
|
|
272
|
+
// a skill node → the agentskills.io SKILL.md primitive: `name`+`description` frontmatter (the load-trigger)
|
|
273
|
+
// over the body instructions, closed by the GENERATED_MARK identity stamp (what the erase phase keys on).
|
|
274
|
+
// One pure artifact builder shared by every harness — divergence is only its skillDir.
|
|
275
|
+
const skillArtifact = (sk) => `---\nname: ${sk.name}\ndescription: ${JSON.stringify(sk.desc)}\n---\n\n${sk.body}\n\n${GENERATED_MARK}\n`;
|
|
276
|
+
// an agent node → a coding-agent sub-agent definition (the same primitive .claude/agents/*.md ships): the
|
|
277
|
+
// node's `desc` is the on-demand load-trigger, its `tools` the harness tool allowlist, its body the agent's
|
|
278
|
+
// system prompt. Same stamp, same reason.
|
|
279
|
+
const agentArtifact = (ag) => `---\nname: ${ag.name}\ndescription: ${ag.desc}\ntools: ${ag.tools.join(', ')}\n---\n\n${ag.body}\n\n${GENERATED_MARK}\n`;
|
|
280
|
+
// a command node → a host `/`-menu command file: plugin-only (the native path serves command presets via
|
|
281
|
+
// the dashboard /api/slash-commands instead).
|
|
282
|
+
const commandArtifact = (cm) => (cm.desc ? `---\ndescription: ${JSON.stringify(cm.desc)}\n---\n\n` : '') + `${cm.body}\n`;
|
|
283
|
+
// materialized artifacts and machine facts both land in the same per-clone exclude; contract files are kept separate
|
|
284
|
+
// because their residence is the live three-state kind detection below, not a static entry.
|
|
285
|
+
const artifactPaths = [];
|
|
286
|
+
const machinePaths = [];
|
|
287
|
+
const contractTargets = new Map();
|
|
288
|
+
const treeShimTargets = new Map();
|
|
289
|
+
const anchorTargets = new Map();
|
|
290
|
+
const skillTargets = new Map();
|
|
291
|
+
const agentTargets = new Map();
|
|
292
|
+
const skillsByDir = new Map();
|
|
293
|
+
const agentsByDir = new Map();
|
|
294
|
+
const addTarget = (targets, path, content) => {
|
|
295
|
+
const prior = targets.get(path);
|
|
296
|
+
if (prior !== undefined && prior !== content)
|
|
297
|
+
throw new Error(`conflicting materialize targets for ${path}`);
|
|
298
|
+
targets.set(path, content);
|
|
299
|
+
};
|
|
300
|
+
for (const h of selected) {
|
|
301
|
+
if (contract)
|
|
302
|
+
for (const f of h.contractFiles(proj))
|
|
303
|
+
addTarget(contractTargets, f, contract);
|
|
304
|
+
const shim = h.shim(DISPATCH, SPEX);
|
|
305
|
+
if (h.shimScope === 'tree') {
|
|
306
|
+
addTarget(treeShimTargets, h.shimFile(proj), shim.content);
|
|
307
|
+
}
|
|
308
|
+
// a linked-worktree ANCHOR copy of the shim, when the harness needs one (codex: the shim lives at the main
|
|
309
|
+
// checkout, so the worktree gets no `.codex/` unless we place one). One adapter line; null otherwise.
|
|
310
|
+
const anchor = h.worktreeHookAnchor(proj);
|
|
311
|
+
if (anchor)
|
|
312
|
+
addTarget(anchorTargets, anchor, shim.content);
|
|
313
|
+
}
|
|
314
|
+
for (const sk of skillNodes)
|
|
315
|
+
for (const h of selected) {
|
|
316
|
+
const dir = h.skillDir(proj);
|
|
317
|
+
if (!dir)
|
|
318
|
+
continue;
|
|
319
|
+
addTarget(skillTargets, join(dir, sk.name, 'SKILL.md'), skillArtifact(sk));
|
|
320
|
+
const names = skillsByDir.get(dir) ?? new Set();
|
|
321
|
+
names.add(sk.name);
|
|
322
|
+
skillsByDir.set(dir, names);
|
|
323
|
+
}
|
|
324
|
+
for (const ag of agentNodes)
|
|
325
|
+
for (const h of selected) {
|
|
326
|
+
const dir = h.agentDir(proj);
|
|
327
|
+
if (!dir)
|
|
328
|
+
continue;
|
|
329
|
+
addTarget(agentTargets, join(dir, `${ag.name}.md`), agentArtifact(ag));
|
|
330
|
+
const names = agentsByDir.get(dir) ?? new Set();
|
|
331
|
+
names.add(ag.name);
|
|
332
|
+
agentsByDir.set(dir, names);
|
|
333
|
+
}
|
|
334
|
+
const tracked = new Map();
|
|
335
|
+
const isTrackedHere = (file) => {
|
|
336
|
+
const known = tracked.get(file);
|
|
337
|
+
if (known !== undefined)
|
|
338
|
+
return known;
|
|
339
|
+
const value = isTracked(proj, file);
|
|
340
|
+
tracked.set(file, value);
|
|
341
|
+
return value;
|
|
342
|
+
};
|
|
343
|
+
reconcileTree(proj, {
|
|
344
|
+
contracts: new Set(contractTargets.keys()), treeShims: new Set(treeShimTargets.keys()), anchors: new Set(anchorTargets.keys()),
|
|
345
|
+
skills: skillsByDir, agents: agentsByDir,
|
|
346
|
+
}, isTrackedHere);
|
|
347
|
+
const changedMaterialized = new Set();
|
|
348
|
+
for (const [file, content] of contractTargets) {
|
|
349
|
+
if (writeManagedBlock(file, content))
|
|
350
|
+
changedMaterialized.add(file);
|
|
351
|
+
record('contract', file);
|
|
352
|
+
}
|
|
353
|
+
const contractPaths = [...contractTargets.keys()];
|
|
354
|
+
for (const [file, content] of treeShimTargets) {
|
|
355
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
356
|
+
writeFileIfChanged(file, content);
|
|
357
|
+
record('shim', file);
|
|
358
|
+
machinePaths.push(file);
|
|
359
|
+
}
|
|
360
|
+
for (const [file, content] of anchorTargets) {
|
|
361
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
362
|
+
writeFileIfChanged(file, content);
|
|
363
|
+
record('shim', file);
|
|
364
|
+
machinePaths.push(file);
|
|
365
|
+
}
|
|
366
|
+
const selectedByDispatch = new Map(selected.map((h) => [h.dispatchId, h]));
|
|
367
|
+
for (const h of selectedByDispatch.values()) {
|
|
368
|
+
const shim = h.shim(DISPATCH, SPEX);
|
|
369
|
+
if (h.shimScope === 'project') {
|
|
370
|
+
const file = h.shimFile(proj);
|
|
371
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
372
|
+
writeFileIfChanged(file, shim.content);
|
|
373
|
+
record('shim', file);
|
|
374
|
+
}
|
|
375
|
+
for (const file of h.writeTrust(proj, shim.cmd))
|
|
376
|
+
record('trust', file);
|
|
377
|
+
}
|
|
378
|
+
for (const [file, content] of skillTargets) {
|
|
379
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
380
|
+
writeFileIfChanged(file, content);
|
|
381
|
+
artifactPaths.push(file);
|
|
382
|
+
record('skill', file);
|
|
383
|
+
}
|
|
384
|
+
for (const [file, content] of agentTargets) {
|
|
385
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
386
|
+
writeFileIfChanged(file, content);
|
|
387
|
+
artifactPaths.push(file);
|
|
388
|
+
record('agent', file);
|
|
389
|
+
}
|
|
390
|
+
// (8) the PLUGIN target ([[plugin-harness]]): materialize the whole system into one self-contained Claude-plugin
|
|
391
|
+
// bundle per selected folder. A plugin is EXCLUSIVE (`selected` is empty then). Pruning a DESELECTED
|
|
392
|
+
// folder needs the PREVIOUS folder set, which the live config no longer names — the one landing point
|
|
393
|
+
// the identity-stamped erase cannot enumerate (a folder is an arbitrary path) — so a tiny ledger in the
|
|
394
|
+
// global store records the folders emitted last run; any prev folder absent from the current set is
|
|
395
|
+
// clean()ed, then the current folders are emitted and the ledger rewritten.
|
|
396
|
+
const ledger = join(rt, 'plugin-folders');
|
|
397
|
+
const prevFolders = existsSync(ledger) ? readFileSync(ledger, 'utf8').split('\n').map((l) => l.trim()).filter(Boolean) : [];
|
|
398
|
+
const curFolders = plugins.map((p) => p.folder);
|
|
399
|
+
for (const f of prevFolders)
|
|
400
|
+
if (!curFolders.includes(f))
|
|
401
|
+
cleanPlugin(proj, f);
|
|
402
|
+
if (plugins.length) {
|
|
403
|
+
const bundle = {
|
|
404
|
+
contract,
|
|
405
|
+
skills: skillNodes.map((s) => ({ name: s.name, content: skillArtifact(s) })),
|
|
406
|
+
agents: agentNodes.map((a) => ({ name: a.name, content: agentArtifact(a) })),
|
|
407
|
+
commands: commandNodes.map((c) => ({ name: c.name, content: commandArtifact(c) })),
|
|
408
|
+
spex: SPEX,
|
|
409
|
+
version: pluginVersion(),
|
|
410
|
+
};
|
|
411
|
+
for (const p of plugins) {
|
|
412
|
+
emitPlugin(proj, p.folder, bundle);
|
|
413
|
+
record('plugin bundle', pluginBundleDir(proj, p.folder));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
writeFileIfChanged(ledger, curFolders.join('\n'));
|
|
417
|
+
// (9) ignore + mixed text. Only checkout-invariant residue and project-shared shims belong in the COMMON
|
|
418
|
+
// info/exclude; selection-dependent paths live in this tree's filtered working .gitignore.
|
|
419
|
+
const mc = mainCheckout(proj);
|
|
420
|
+
const bundlePaths = curFolders.map((f) => pluginBundleDir(proj, f));
|
|
421
|
+
const commonEntries = [
|
|
422
|
+
...[...new Set(HARNESSES.filter((h) => h.shimScope === 'project' && existsSync(h.shimFile(proj)) &&
|
|
423
|
+
readFileSync(h.shimFile(proj), 'utf8').includes('dispatch.sh')).map((h) => relative(mc, h.shimFile(proj))))]
|
|
424
|
+
.filter((p) => !p.startsWith('..')),
|
|
425
|
+
'spexcode.local.json', '.worktrees/', '.session',
|
|
426
|
+
];
|
|
427
|
+
const entries = (list) => [...new Set(list)].sort().join('\n');
|
|
428
|
+
// Contract residence stays a live fact. Selection-dependent untracked products are ignored by this tree's
|
|
429
|
+
// working .gitignore, whose own managed block is filtered when the host tracks/owns that file.
|
|
430
|
+
const filterContracts = [];
|
|
431
|
+
const oursContracts = [];
|
|
432
|
+
for (const f of contractPaths) {
|
|
433
|
+
if (isTrackedHere(f) || hostContentOf(f).trim())
|
|
434
|
+
filterContracts.push(f);
|
|
435
|
+
else
|
|
436
|
+
oursContracts.push(f);
|
|
437
|
+
}
|
|
438
|
+
const localEntries = [...machinePaths, ...bundlePaths, ...artifactPaths, ...oursContracts]
|
|
439
|
+
.map((p) => relative(proj, p)).filter((p) => !p.startsWith('..'));
|
|
440
|
+
const ignoreFile = join(proj, '.gitignore');
|
|
441
|
+
const ignoreTracked = isTrackedHere(ignoreFile);
|
|
442
|
+
const ignoreHost = existsSync(ignoreFile) ? stripSpexcodeBlock(readFileSync(ignoreFile, 'utf8'), ['# ', '']) : '';
|
|
443
|
+
if (!ignoreTracked && !ignoreHost.trim())
|
|
444
|
+
localEntries.push('.gitignore');
|
|
445
|
+
const ignoreBody = entries(localEntries);
|
|
446
|
+
if (writeManagedBlock(ignoreFile, ignoreBody, ['# ', '']))
|
|
447
|
+
changedMaterialized.add(ignoreFile);
|
|
448
|
+
const payloads = filterContracts.map((file) => ({ file: relative(proj, file), content: contract }));
|
|
449
|
+
if (ignoreTracked || ignoreHost.trim())
|
|
450
|
+
payloads.push({ file: '.gitignore', content: ignoreBody });
|
|
451
|
+
const bindings = [
|
|
452
|
+
...[...new Set(HARNESSES.flatMap((h) => h.contractFiles(proj).map((file) => relative(proj, file))))]
|
|
453
|
+
.map((file) => ({ file, start: '<!-- spexcode:start -->', end: '<!-- spexcode:end -->' })),
|
|
454
|
+
{ file: '.gitignore', start: '# spexcode:start', end: '# spexcode:end' },
|
|
455
|
+
];
|
|
456
|
+
if (payloads.length)
|
|
457
|
+
plantContractFilter(proj, payloads, bindings, [...changedMaterialized]);
|
|
458
|
+
else if (contractFilterPlanted(proj))
|
|
459
|
+
removeContractFilter(proj, [...HARNESSES.flatMap((h) => h.contractFiles(proj)), join(proj, '.gitignore')]);
|
|
460
|
+
// (5) finish diagnostics, then atomically publish the allowlist LAST. Dispatch consumes only that
|
|
461
|
+
// final receipt; a killed writer leaves the preceding successful selection intact.
|
|
462
|
+
const h = contentHash(proj);
|
|
463
|
+
writeFileIfChanged(join(rt, 'content-hash'), h);
|
|
464
|
+
writeFileIfChanged(join(runtimeRoot(proj), 'harness-selection-v1'), '');
|
|
465
|
+
writeManagedBlock(infoExcludePath(proj), entries(commonEntries), ['# ', '']);
|
|
466
|
+
publishSelection(join(rt, 'harnesses'), selectionBody(selected, plugins.length > 0));
|
|
467
|
+
return { contentHash: h, planted };
|
|
468
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export declare function stripRefSigil(token: string): string;
|
|
2
|
+
export declare function parseMentions(text: string): {
|
|
3
|
+
sessions: string[];
|
|
4
|
+
nodes: string[];
|
|
5
|
+
};
|
|
6
|
+
export type DispatchOutcome = {
|
|
7
|
+
token: string;
|
|
8
|
+
result: 'spawned' | 'failed';
|
|
9
|
+
detail?: string;
|
|
10
|
+
note?: string;
|
|
11
|
+
};
|
|
12
|
+
export type NewDispatchContext = {
|
|
13
|
+
threadId: string;
|
|
14
|
+
node: string | null;
|
|
15
|
+
author: string;
|
|
16
|
+
status?: string | null;
|
|
17
|
+
} | {
|
|
18
|
+
sessionId: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function spawnParent(author: string, sessions: {
|
|
21
|
+
id: string;
|
|
22
|
+
}[]): string | null;
|
|
23
|
+
export declare function newWorkerPrompt(threadId: string, node: string | null, author: string, text: string, status?: string | null): string;
|
|
24
|
+
export declare function commandWorkerPrompt(sessionId: string, text: string): string;
|
|
25
|
+
export declare function dispatchNewMentions(text: string, ctx: NewDispatchContext): Promise<DispatchOutcome[]>;
|
|
26
|
+
export declare const summarizeDispatch: (outcomes: DispatchOutcome[]) => string;
|
|
27
|
+
type Session = {
|
|
28
|
+
id: string;
|
|
29
|
+
node: string | null;
|
|
30
|
+
name: string | null;
|
|
31
|
+
title: string | null;
|
|
32
|
+
liveness: string;
|
|
33
|
+
};
|
|
34
|
+
export type LoopIn = {
|
|
35
|
+
originator: string;
|
|
36
|
+
};
|
|
37
|
+
export type LoopInPick = {
|
|
38
|
+
kind: 'deliver';
|
|
39
|
+
originator: string;
|
|
40
|
+
session: Session;
|
|
41
|
+
} | {
|
|
42
|
+
kind: 'none';
|
|
43
|
+
};
|
|
44
|
+
export declare function pickLoopIn(chain: (string | null)[], replier: string, sessions: Session[]): LoopInPick;
|
|
45
|
+
export declare function notifyOriginator(chain: (string | null)[], replier: string, text: string, ctx: {
|
|
46
|
+
threadId: string;
|
|
47
|
+
node: string | null;
|
|
48
|
+
}): Promise<LoopIn | null>;
|
|
49
|
+
export declare const summarizeLoopIn: (loopIn?: LoopIn | null) => string;
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// @@@ mentions - the shared in-text grammar: `[[node]]` and `@session` are references;
|
|
2
|
+
// the exact @new token is the durable worker-creation action. Its dispatcher stays here
|
|
3
|
+
// so every composer reaches the same creation owner after its own write has committed.
|
|
4
|
+
const SESSION_RE = /(?:^|\s)@([\p{L}\p{N}_-]+(?::[\p{L}\p{N}_.-]+)?)/gu;
|
|
5
|
+
const NODE_RE = /\[\[([^\]\s]+)\]\]/g;
|
|
6
|
+
const uniq = (xs) => [...new Set(xs)];
|
|
7
|
+
// In free text a sigil separates a reference from prose. A CLI argument is already a
|
|
8
|
+
// reference, so it tolerates either sigil without widening its normal matching grammar.
|
|
9
|
+
export function stripRefSigil(token) {
|
|
10
|
+
const wrapped = /^\[\[(.*)\]\]$/.exec(token);
|
|
11
|
+
if (wrapped)
|
|
12
|
+
return wrapped[1];
|
|
13
|
+
return token.startsWith('@') ? token.slice(1) : token;
|
|
14
|
+
}
|
|
15
|
+
export function parseMentions(text) {
|
|
16
|
+
const sessions = [];
|
|
17
|
+
const nodes = [];
|
|
18
|
+
for (const m of text.matchAll(SESSION_RE))
|
|
19
|
+
sessions.push(m[1]);
|
|
20
|
+
for (const m of text.matchAll(NODE_RE))
|
|
21
|
+
nodes.push(m[1]);
|
|
22
|
+
return { sessions: uniq(sessions), nodes: uniq(nodes) };
|
|
23
|
+
}
|
|
24
|
+
export function spawnParent(author, sessions) {
|
|
25
|
+
return sessions.some((session) => session.id === author) ? author : null;
|
|
26
|
+
}
|
|
27
|
+
export function newWorkerPrompt(threadId, node, author, text, status) {
|
|
28
|
+
const scope = node ? ` on node [[${node}]]` : '';
|
|
29
|
+
const settled = status && status !== 'open'
|
|
30
|
+
? `NOTE: this thread is already resolved (status: ${status}). Verify main first; if it already satisfies the thread, report that finding instead of re-implementing.\n\n`
|
|
31
|
+
: '';
|
|
32
|
+
return `Issue thread "${threadId}"${scope} @-mentioned @new (by ${author}) for a fresh look:\n\n ${text.trim()}\n\n` +
|
|
33
|
+
settled +
|
|
34
|
+
`Read the thread (\`spex issue ls --all\`, find ${threadId}) and act on it${node ? `; the relevant node is ${node}` : ''}.`;
|
|
35
|
+
}
|
|
36
|
+
export function commandWorkerPrompt(sessionId, text) {
|
|
37
|
+
return `Session "${sessionId}" opened a child worker from its Command Box:\n\n${text.trim()}`;
|
|
38
|
+
}
|
|
39
|
+
// Writes call this only after their durable append/commit succeeds. @session deliberately remains absent:
|
|
40
|
+
// sending to an existing session is still the explicit `spex session send` operation.
|
|
41
|
+
export async function dispatchNewMentions(text, ctx) {
|
|
42
|
+
const tokens = parseMentions(text).sessions;
|
|
43
|
+
const requested = tokens.flatMap((token) => {
|
|
44
|
+
const match = /^new(?::([\p{L}\p{N}_.-]+))?$/u.exec(token);
|
|
45
|
+
return match ? [{ token, launcher: match[1] }] : [];
|
|
46
|
+
});
|
|
47
|
+
if (!requested.length)
|
|
48
|
+
return [];
|
|
49
|
+
const { listSessions, sessionCreateRequest } = await import('./sessions.js');
|
|
50
|
+
const sessions = await listSessions();
|
|
51
|
+
const command = 'sessionId' in ctx;
|
|
52
|
+
const author = command ? ctx.sessionId : ctx.author;
|
|
53
|
+
const outcomes = [];
|
|
54
|
+
for (const request of requested) {
|
|
55
|
+
const settled = !command && ctx.status && ctx.status !== 'open' ? ctx.status : undefined;
|
|
56
|
+
try {
|
|
57
|
+
const created = await sessionCreateRequest({
|
|
58
|
+
prompt: command
|
|
59
|
+
? commandWorkerPrompt(ctx.sessionId, text)
|
|
60
|
+
: newWorkerPrompt(ctx.threadId, ctx.node, ctx.author, text, ctx.status),
|
|
61
|
+
parent: spawnParent(author, sessions),
|
|
62
|
+
launcher: request.launcher,
|
|
63
|
+
});
|
|
64
|
+
if (created.status !== 201)
|
|
65
|
+
throw new Error(`${created.code || 'session_create_failed'}: ${created.error}`);
|
|
66
|
+
outcomes.push({ token: request.token, result: 'spawned', detail: created.session.id, ...(settled ? { note: `thread ${settled}` } : {}) });
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
outcomes.push({ token: request.token, result: 'failed', detail: error instanceof Error ? error.message : String(error) });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return outcomes;
|
|
73
|
+
}
|
|
74
|
+
export const summarizeDispatch = (outcomes) => outcomes.length ? '@ ' + outcomes.map((outcome) => outcome.result === 'spawned'
|
|
75
|
+
? `${outcome.token}->${outcome.detail}${outcome.note ? ` (${outcome.note})` : ''}`
|
|
76
|
+
: `${outcome.token}->failed (${outcome.detail})`).join(' | ') : '';
|
|
77
|
+
function resolveOnlineSession(token, sessions) {
|
|
78
|
+
const online = sessions.filter((session) => session.liveness === 'online');
|
|
79
|
+
const text = token.toLowerCase();
|
|
80
|
+
const label = (session) => (session.name || session.title || '').toLowerCase();
|
|
81
|
+
return online.find((session) => session.id === token)
|
|
82
|
+
|| online.find((session) => session.id.startsWith(token))
|
|
83
|
+
|| online.find((session) => label(session) === text)
|
|
84
|
+
|| online.find((session) => label(session).startsWith(text) && text.length >= 2)
|
|
85
|
+
|| null;
|
|
86
|
+
}
|
|
87
|
+
// A committed reply can notify its originator as a courtesy. This is not a mention
|
|
88
|
+
// dispatch or assignment, and it never creates a worker or retries an offline session.
|
|
89
|
+
export function pickLoopIn(chain, replier, sessions) {
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
for (const originator of chain) {
|
|
92
|
+
if (!originator || originator === replier || seen.has(originator))
|
|
93
|
+
continue;
|
|
94
|
+
seen.add(originator);
|
|
95
|
+
const session = resolveOnlineSession(originator, sessions);
|
|
96
|
+
if (session)
|
|
97
|
+
return { kind: 'deliver', originator, session };
|
|
98
|
+
}
|
|
99
|
+
return { kind: 'none' };
|
|
100
|
+
}
|
|
101
|
+
function originatorPrompt(threadId, node, replier, text) {
|
|
102
|
+
const re = node ? ` (re: ${node})` : '';
|
|
103
|
+
return `A new reply landed on a thread you originated - "${threadId}"${re}, from ${replier}:\n\n ${text.trim()}\n\n` +
|
|
104
|
+
`This is a courtesy heads-up, not an assignment. Look if it concerns you; ` +
|
|
105
|
+
`\`spex issue ls --all\` lists threads and \`spex issue reply ${threadId} --body -\` replies.`;
|
|
106
|
+
}
|
|
107
|
+
export async function notifyOriginator(chain, replier, text, ctx) {
|
|
108
|
+
if (!chain.some((candidate) => candidate && candidate !== replier))
|
|
109
|
+
return null;
|
|
110
|
+
const { sendText, listSessions } = await import('./sessions.js');
|
|
111
|
+
const pick = pickLoopIn(chain, replier, await listSessions());
|
|
112
|
+
if (pick.kind !== 'deliver')
|
|
113
|
+
return null;
|
|
114
|
+
const result = await sendText(pick.session.id, originatorPrompt(ctx.threadId, ctx.node, replier, text), 'issues');
|
|
115
|
+
return result.ok ? { originator: pick.originator } : null;
|
|
116
|
+
}
|
|
117
|
+
export const summarizeLoopIn = (loopIn) => loopIn ? `looped in originator @${loopIn.originator} (online)` : '';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type DispatchResult, type HarnessDeliveryRecord } from './harness.js';
|
|
2
|
+
export declare function opencodeHeadlessLaunchCommand(opencodeCmd?: string): string;
|
|
3
|
+
export declare function opencodeHeadlessWakeCommand(opencodeCmd: string, harnessSessionId: string | null | undefined, text: string, outcomePath?: string): string;
|
|
4
|
+
export declare function spawnOpenCodeHeadlessTurn(rec: HarnessDeliveryRecord, text: string, opencodeCmd: string, socketPath: string): Promise<DispatchResult>;
|