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
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import { execFile } from 'node:child_process'
|
|
2
|
-
import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
|
|
3
|
-
import { tmpdir, userInfo } from 'node:os'
|
|
4
|
-
import { join } from 'node:path'
|
|
5
|
-
import { promisify } from 'node:util'
|
|
6
|
-
import { headlessTurnFailureShell, type DispatchResult, type HarnessDeliveryRecord } from './harness.js'
|
|
7
|
-
import { shQuote } from './sh.js'
|
|
8
|
-
|
|
9
|
-
const pexec = promisify(execFile)
|
|
10
|
-
const WAKE_EARLY_EXIT_MS = 5_000
|
|
11
|
-
const OUTCOME_POLL_MS = 25
|
|
12
|
-
|
|
13
|
-
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
|
|
14
|
-
|
|
15
|
-
function accountLoginShell(): string {
|
|
16
|
-
try { return userInfo().shell || '' } catch { return '' }
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// A headless turn owns the pane only while `opencode run` is alive. Returning to a shell keeps the tmux
|
|
20
|
-
// window as the session's durable home without adding a resident controller or an stdin bridge.
|
|
21
|
-
function turnHome(command: string, outcomePath?: string): string {
|
|
22
|
-
const outcomeSetup = outcomePath ? [
|
|
23
|
-
`__spex_outcome_path=${shQuote(outcomePath)}`,
|
|
24
|
-
`__spex_outcome_tmp=${shQuote(`${outcomePath}.tmp`)}`,
|
|
25
|
-
'__spex_outcome() { printf \'%s\\n\' "$1" > "$__spex_outcome_tmp" && mv -f "$__spex_outcome_tmp" "$__spex_outcome_path"; }',
|
|
26
|
-
'__spex_outcome "running:$$" || { printf "[spex opencode-headless] could not create turn outcome marker\\n" >&2; exit 125; }',
|
|
27
|
-
] : []
|
|
28
|
-
const failure = outcomePath ? [
|
|
29
|
-
'__spex_cas_rc=0',
|
|
30
|
-
'if [ "$__spex_rc" -ne 0 ]; then',
|
|
31
|
-
' __spex_outcome "reporting:$$:$__spex_rc" || true',
|
|
32
|
-
` ${headlessTurnFailureShell('opencode-headless', false)}`,
|
|
33
|
-
' __spex_cas_rc=$?',
|
|
34
|
-
'fi',
|
|
35
|
-
'__spex_outcome "exit:$__spex_rc:cas:$__spex_cas_rc" || printf "[spex opencode-headless] could not finalize turn outcome marker\\n" >&2',
|
|
36
|
-
] : [
|
|
37
|
-
`if [ "$__spex_rc" -ne 0 ]; then ${headlessTurnFailureShell('opencode-headless')}; fi`,
|
|
38
|
-
]
|
|
39
|
-
const script = [
|
|
40
|
-
...outcomeSetup,
|
|
41
|
-
command,
|
|
42
|
-
'__spex_rc=$?',
|
|
43
|
-
...failure,
|
|
44
|
-
'[ "$__spex_rc" -eq 0 ] || printf "[spex opencode-headless] turn exited rc=%s\\n" "$__spex_rc" >&2',
|
|
45
|
-
'exec "${SHELL:-/bin/sh}"',
|
|
46
|
-
].join('\n')
|
|
47
|
-
return `bash -lc ${shQuote(script)} spexcode-opencode-headless`
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Launcher profiles carry a base executable plus its configured flags (`opencode --auto`). OpenCode parses
|
|
51
|
-
// `run` as a subcommand, so it must sit between those two halves (`opencode run --auto`), not at the tail.
|
|
52
|
-
function runPrelude(opencodeCmd: string): string[] {
|
|
53
|
-
const accountShell = accountLoginShell()
|
|
54
|
-
return [
|
|
55
|
-
'__spex_login_shell="${SHELL:-}"',
|
|
56
|
-
`[ -n "$__spex_login_shell" ] || __spex_login_shell=${shQuote(accountShell)}`,
|
|
57
|
-
`__spex_cmd=(${opencodeCmd})`,
|
|
58
|
-
'__spex_env=()',
|
|
59
|
-
'while [ "${#__spex_cmd[@]}" -gt 0 ] && [[ "${__spex_cmd[0]}" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; do',
|
|
60
|
-
' __spex_env+=("${__spex_cmd[0]}")',
|
|
61
|
-
' __spex_cmd=("${__spex_cmd[@]:1}")',
|
|
62
|
-
'done',
|
|
63
|
-
// Resolve OpenCode in the same login + interactive environment the user relies on. The launcher-leading
|
|
64
|
-
// assignments are applied after shell startup, so its explicit per-session config still wins.
|
|
65
|
-
'__spex_run() { [ -n "$__spex_login_shell" ] || { printf "[spex opencode-headless] no login shell could be resolved - turn NOT started\\n" >&2; return 126; }; "$__spex_login_shell" -ilc \'exec env "$@"\' spexcode-opencode-headless "${__spex_env[@]}" "${__spex_cmd[0]}" run "${__spex_cmd[@]:1}" "$@"; }',
|
|
66
|
-
]
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function opencodeHeadlessLaunchCommand(opencodeCmd = 'opencode'): string {
|
|
70
|
-
const script = [
|
|
71
|
-
...runPrelude(opencodeCmd),
|
|
72
|
-
'if [ "${1:-}" = "--resume" ]; then',
|
|
73
|
-
' export SPEXCODE_OPENCODE_RESUME_ID="$2"',
|
|
74
|
-
' unset SPEXCODE_OPENCODE_CONTINUE',
|
|
75
|
-
' __spex_run --session "$2"',
|
|
76
|
-
'elif [ "${1:-}" = "--continue" ]; then',
|
|
77
|
-
' unset SPEXCODE_OPENCODE_RESUME_ID',
|
|
78
|
-
' export SPEXCODE_OPENCODE_CONTINUE=1',
|
|
79
|
-
' __spex_run --continue',
|
|
80
|
-
'elif [ -n "${1:-}" ]; then',
|
|
81
|
-
' unset SPEXCODE_OPENCODE_RESUME_ID SPEXCODE_OPENCODE_CONTINUE',
|
|
82
|
-
' __spex_run "$1"',
|
|
83
|
-
'else',
|
|
84
|
-
' __spex_run',
|
|
85
|
-
'fi',
|
|
86
|
-
].join('\n')
|
|
87
|
-
return turnHome(script)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function opencodeHeadlessWakeCommand(
|
|
91
|
-
opencodeCmd: string,
|
|
92
|
-
harnessSessionId: string | null | undefined,
|
|
93
|
-
text: string,
|
|
94
|
-
outcomePath?: string,
|
|
95
|
-
): string {
|
|
96
|
-
const resume = harnessSessionId ? [
|
|
97
|
-
`export SPEXCODE_OPENCODE_RESUME_ID=${shQuote(harnessSessionId)}`,
|
|
98
|
-
'unset SPEXCODE_OPENCODE_CONTINUE',
|
|
99
|
-
`__spex_run --session ${shQuote(harnessSessionId)} ${shQuote(text)}`,
|
|
100
|
-
] : [
|
|
101
|
-
'unset SPEXCODE_OPENCODE_RESUME_ID',
|
|
102
|
-
'export SPEXCODE_OPENCODE_CONTINUE=1',
|
|
103
|
-
`__spex_run --continue ${shQuote(text)}`,
|
|
104
|
-
]
|
|
105
|
-
return turnHome([...runPrelude(opencodeCmd), ...resume].join('\n'), outcomePath)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
type TurnOutcome =
|
|
109
|
-
| { state: 'running'; pid: number }
|
|
110
|
-
| { state: 'reporting'; pid: number; code: number }
|
|
111
|
-
| { state: 'exit'; code: number; casCode: number }
|
|
112
|
-
| { state: 'invalid' }
|
|
113
|
-
|
|
114
|
-
function readTurnOutcome(path: string): TurnOutcome | undefined {
|
|
115
|
-
let value: string
|
|
116
|
-
try { value = readFileSync(path, 'utf8').trim() } catch (error) {
|
|
117
|
-
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined
|
|
118
|
-
throw error
|
|
119
|
-
}
|
|
120
|
-
let match = /^running:(\d+)$/.exec(value)
|
|
121
|
-
if (match) return { state: 'running', pid: Number(match[1]) }
|
|
122
|
-
match = /^reporting:(\d+):(-?\d+)$/.exec(value)
|
|
123
|
-
if (match) return { state: 'reporting', pid: Number(match[1]), code: Number(match[2]) }
|
|
124
|
-
match = /^exit:(-?\d+):cas:(\d+)$/.exec(value)
|
|
125
|
-
if (match) return { state: 'exit', code: Number(match[1]), casCode: Number(match[2]) }
|
|
126
|
-
return { state: 'invalid' }
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function turnExited(rec: HarnessDeliveryRecord, outcome: Extract<TurnOutcome, { state: 'exit' }>): DispatchResult {
|
|
130
|
-
if (outcome.code === 0) return { ok: true }
|
|
131
|
-
const cas = outcome.casCode === 0 ? '' : `; error CAS reporter also exited with code ${outcome.casCode}`
|
|
132
|
-
return {
|
|
133
|
-
ok: false,
|
|
134
|
-
error: `opencode-headless turn exited with code ${outcome.code} during startup for session ${rec.session}${cas} - immediate wake failed`,
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function pidAlive(pid: number): boolean {
|
|
139
|
-
if (!Number.isInteger(pid) || pid <= 0) return false
|
|
140
|
-
try { process.kill(pid, 0); return true } catch (error) {
|
|
141
|
-
return (error as NodeJS.ErrnoException).code === 'EPERM'
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export async function spawnOpenCodeHeadlessTurn(
|
|
146
|
-
rec: HarnessDeliveryRecord,
|
|
147
|
-
text: string,
|
|
148
|
-
opencodeCmd: string,
|
|
149
|
-
socketPath: string,
|
|
150
|
-
): Promise<DispatchResult> {
|
|
151
|
-
if (!rec.worktreePath) return { ok: false, error: `opencode-headless session ${rec.session} has no worktree path - turn NOT started` }
|
|
152
|
-
let outcomeDir: string
|
|
153
|
-
try { outcomeDir = mkdtempSync(join(tmpdir(), 'spexcode-oh-turn-')) } catch (error) {
|
|
154
|
-
return { ok: false, error: `opencode-headless could not prepare turn confirmation for session ${rec.session}: ${(error as Error).message}` }
|
|
155
|
-
}
|
|
156
|
-
const outcomePath = join(outcomeDir, 'exit-code')
|
|
157
|
-
const tmuxSock = process.env.SPEXCODE_TMUX || 'spexcode'
|
|
158
|
-
const args = [
|
|
159
|
-
'-L', tmuxSock, 'respawn-pane', '-k', '-t', rec.session, '-c', rec.worktreePath,
|
|
160
|
-
'-e', `SPEXCODE_SESSION_ID=${rec.session}`,
|
|
161
|
-
'-e', 'CLAUDE_BG_BACKEND=daemon',
|
|
162
|
-
'-e', `CLAUDE_BG_RENDEZVOUS_SOCK=${socketPath}`,
|
|
163
|
-
]
|
|
164
|
-
for (const name of ['SPEXCODE_HOME', 'CODEX_HOME']) {
|
|
165
|
-
const value = process.env[name]
|
|
166
|
-
if (value) args.push('-e', `${name}=${value}`)
|
|
167
|
-
}
|
|
168
|
-
args.push(opencodeHeadlessWakeCommand(opencodeCmd, rec.harnessSessionId, text, outcomePath))
|
|
169
|
-
try {
|
|
170
|
-
await pexec('tmux', args, { timeout: 5_000 })
|
|
171
|
-
const deadline = Date.now() + WAKE_EARLY_EXIT_MS
|
|
172
|
-
for (;;) {
|
|
173
|
-
const outcome = readTurnOutcome(outcomePath)
|
|
174
|
-
if (outcome?.state === 'exit') return turnExited(rec, outcome)
|
|
175
|
-
if (outcome?.state === 'invalid') {
|
|
176
|
-
return { ok: false, error: `opencode-headless turn for session ${rec.session} wrote an invalid exit outcome - immediate wake not confirmed` }
|
|
177
|
-
}
|
|
178
|
-
if (Date.now() >= deadline) break
|
|
179
|
-
await sleep(Math.min(OUTCOME_POLL_MS, deadline - Date.now()))
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const first = readTurnOutcome(outcomePath)
|
|
183
|
-
if (first?.state === 'exit') return turnExited(rec, first)
|
|
184
|
-
if (!first) return { ok: false, error: `opencode-headless turn for session ${rec.session} never confirmed startup - immediate wake failed` }
|
|
185
|
-
if (first.state === 'invalid') return { ok: false, error: `opencode-headless turn for session ${rec.session} wrote an invalid exit outcome - immediate wake not confirmed` }
|
|
186
|
-
if (first.state === 'reporting') {
|
|
187
|
-
return { ok: false, error: `opencode-headless turn exited with code ${first.code} but its error CAS reporter did not finish for session ${rec.session} - immediate wake failed` }
|
|
188
|
-
}
|
|
189
|
-
if (!pidAlive(first.pid)) {
|
|
190
|
-
return { ok: false, error: `opencode-headless turn wrapper died before reporting an outcome for session ${rec.session} - immediate wake failed` }
|
|
191
|
-
}
|
|
192
|
-
await sleep(OUTCOME_POLL_MS)
|
|
193
|
-
const settled = readTurnOutcome(outcomePath)
|
|
194
|
-
if (settled?.state === 'exit') return turnExited(rec, settled)
|
|
195
|
-
if (settled?.state !== 'running' || settled.pid !== first.pid || !pidAlive(settled.pid)) {
|
|
196
|
-
return { ok: false, error: `opencode-headless turn did not remain live through startup for session ${rec.session} - immediate wake failed` }
|
|
197
|
-
}
|
|
198
|
-
return { ok: true }
|
|
199
|
-
} catch (error) {
|
|
200
|
-
const detail = error instanceof Error ? error.message : String(error)
|
|
201
|
-
return { ok: false, error: `opencode-headless could not start a turn for session ${rec.session}: ${detail}` }
|
|
202
|
-
} finally {
|
|
203
|
-
rmSync(outcomeDir, { recursive: true, force: true })
|
|
204
|
-
}
|
|
205
|
-
}
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { createServer, type Server, type Socket } from 'node:net'
|
|
2
|
-
import { spawn, type ChildProcess } from 'node:child_process'
|
|
3
|
-
import { mkdirSync, rmSync } from 'node:fs'
|
|
4
|
-
import { tmpdir } from 'node:os'
|
|
5
|
-
import { join } from 'node:path'
|
|
6
|
-
import { fileURLToPath } from 'node:url'
|
|
7
|
-
import type { DispatchResult, HarnessDeliveryRecord } from './harness.js'
|
|
8
|
-
import { controlRequest, withTimeout } from './headless-controller.js'
|
|
9
|
-
import { shQuote } from './sh.js'
|
|
10
|
-
|
|
11
|
-
type ControlRequest = { type: 'deliver'; text: string; mid?: string }
|
|
12
|
-
type ChildTurn = { process: ChildProcess; exited: Promise<number | null> }
|
|
13
|
-
|
|
14
|
-
const PKG = fileURLToPath(new URL('..', import.meta.url))
|
|
15
|
-
const SPEX = join(PKG, 'bin', 'spex.mjs')
|
|
16
|
-
const CONTROL_TIMEOUT_MS = 30_000
|
|
17
|
-
const START_TIMEOUT_MS = 30_000
|
|
18
|
-
|
|
19
|
-
/** The resident controller socket is distinct from pi's per-turn rendezvous socket. */
|
|
20
|
-
export const piHeadlessSock = (id: string) => join(tmpdir(), `spexcode-ph-${id}.sock`)
|
|
21
|
-
|
|
22
|
-
export function piHeadlessLaunchCommand(id: string, runtimeDir: string, piCmd: string): string {
|
|
23
|
-
return [shQuote(SPEX), 'internal', 'pi-headless-run', shQuote(id), shQuote(runtimeDir), shQuote(piCmd), '--'].join(' ')
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const deliverViaPiHeadless = (rec: HarnessDeliveryRecord, text: string) =>
|
|
27
|
-
controlRequest(piHeadlessSock(rec.session), { type: 'deliver', text, mid: rec.mid }, {
|
|
28
|
-
name: 'pi-headless', session: rec.session, timeoutMs: CONTROL_TIMEOUT_MS,
|
|
29
|
-
rejected: 'pi-headless controller rejected the request',
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
export class PiHeadlessController {
|
|
33
|
-
private server: Server | null = null
|
|
34
|
-
private child: ChildTurn | null = null
|
|
35
|
-
private controlQueue: Promise<void> = Promise.resolve()
|
|
36
|
-
private closing = false
|
|
37
|
-
private readonly socketPath: string
|
|
38
|
-
|
|
39
|
-
constructor(
|
|
40
|
-
private readonly id: string,
|
|
41
|
-
_runtimeDir: string,
|
|
42
|
-
private readonly piCmd: string,
|
|
43
|
-
private readonly cwd = process.cwd(),
|
|
44
|
-
) {
|
|
45
|
-
this.socketPath = piHeadlessSock(id)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async start(initialPrompt?: string): Promise<void> {
|
|
49
|
-
try { rmSync(this.socketPath, { force: true }) } catch { /* stale control socket is replaced at startup */ }
|
|
50
|
-
this.server = createServer((socket) => this.accept(socket))
|
|
51
|
-
await new Promise<void>((resolve, reject) => {
|
|
52
|
-
const onError = (error: Error) => { this.server?.off('listening', onListening); reject(error) }
|
|
53
|
-
const onListening = () => { this.server?.off('error', onError); resolve() }
|
|
54
|
-
this.server!.once('error', onError)
|
|
55
|
-
this.server!.once('listening', onListening)
|
|
56
|
-
this.server!.listen(this.socketPath)
|
|
57
|
-
})
|
|
58
|
-
if (initialPrompt) void this.spawnTurn(initialPrompt, false).catch((error) => {
|
|
59
|
-
console.error(`[spex pi-headless] initial turn failed: ${(error as Error).message}`)
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async close(): Promise<void> {
|
|
64
|
-
if (this.closing) return
|
|
65
|
-
this.closing = true
|
|
66
|
-
const child = this.child
|
|
67
|
-
if (child && child.process.exitCode === null) child.process.kill('SIGTERM')
|
|
68
|
-
await new Promise<void>((resolve) => {
|
|
69
|
-
if (!this.server) return resolve()
|
|
70
|
-
this.server.close(() => resolve())
|
|
71
|
-
})
|
|
72
|
-
// same proof-before-removal rule as every other teardown (harness.ts unlinkSocks): a socket path is keyed
|
|
73
|
-
// by session id alone, so only a listener PROVEN dead is ours to unlink.
|
|
74
|
-
const { rvSock, unlinkSocks } = await import('./harness.js')
|
|
75
|
-
await unlinkSocks(this.socketPath, rvSock(this.id))
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
private accept(socket: Socket): void {
|
|
79
|
-
socket.setEncoding('utf8')
|
|
80
|
-
let buffer = ''
|
|
81
|
-
let handled = false
|
|
82
|
-
socket.on('data', (chunk) => {
|
|
83
|
-
if (handled) return
|
|
84
|
-
buffer += chunk
|
|
85
|
-
const nl = buffer.indexOf('\n')
|
|
86
|
-
if (nl < 0) return
|
|
87
|
-
handled = true
|
|
88
|
-
let request: ControlRequest
|
|
89
|
-
try { request = JSON.parse(buffer.slice(0, nl)) as ControlRequest } catch (error) {
|
|
90
|
-
socket.end(`${JSON.stringify({ ok: false, error: `invalid control request: ${(error as Error).message}` })}\n`)
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
this.controlQueue = this.controlQueue.then(async () => {
|
|
94
|
-
const result = await this.handle(request).catch((error) => ({ ok: false, error: (error as Error).message }))
|
|
95
|
-
socket.end(`${JSON.stringify(result)}\n`)
|
|
96
|
-
})
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private async handle(request: ControlRequest): Promise<DispatchResult> {
|
|
101
|
-
if (request.type !== 'deliver') return { ok: false, error: 'unknown pi-headless control request' }
|
|
102
|
-
if (!request.text) return { ok: false, error: 'empty prompt - nothing to deliver' }
|
|
103
|
-
|
|
104
|
-
// A live extension listener is an in-flight pi turn. Only a proven absent listener may cold-wake a saved
|
|
105
|
-
// session; an inconclusive probe must not start a duplicate turn.
|
|
106
|
-
const { deliverViaSocketOrWake } = await import('./harness.js')
|
|
107
|
-
return deliverViaSocketOrWake(this.id, request.text, request.mid, async () => {
|
|
108
|
-
if (this.child) await withTimeout(this.child.exited, 5_000, `previous pi-headless turn did not exit for session ${this.id}`)
|
|
109
|
-
await this.spawnTurn(request.text, true)
|
|
110
|
-
return { ok: true }
|
|
111
|
-
}, `could not determine whether pi turn ${this.id} is live — prompt NOT delivered`)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
private async spawnTurn(text: string, resume: boolean): Promise<void> {
|
|
115
|
-
if (this.closing) throw new Error('pi-headless controller is closing')
|
|
116
|
-
const mode = resume ? ['--session', this.id] : ['--session-id', this.id]
|
|
117
|
-
// Keep pi's default text mode. `--mode json` is intentionally omitted: it can hang in this runtime.
|
|
118
|
-
const args = ['-p', ...mode, text]
|
|
119
|
-
const command = `exec ${this.piCmd} ${args.map(shQuote).join(' ')}`
|
|
120
|
-
const childProcess = spawn('/bin/sh', ['-lc', command], { cwd: this.cwd, env: process.env, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
121
|
-
let resolveExit!: (code: number | null) => void
|
|
122
|
-
const exited = new Promise<number | null>((resolve) => { resolveExit = resolve })
|
|
123
|
-
const turn: ChildTurn = { process: childProcess, exited }
|
|
124
|
-
this.child = turn
|
|
125
|
-
childProcess.stdout?.pipe(process.stdout)
|
|
126
|
-
childProcess.stderr?.pipe(process.stderr)
|
|
127
|
-
childProcess.once('error', (error) => console.error(`[spex pi-headless] child spawn failed: ${error.message}`))
|
|
128
|
-
childProcess.once('close', (code) => {
|
|
129
|
-
if (this.child === turn) this.child = null
|
|
130
|
-
resolveExit(code)
|
|
131
|
-
if (code !== 0 && !this.closing) void import('./harness.js').then(({ reportHeadlessTurnExit }) => reportHeadlessTurnExit(this.id, 'pi-headless', code, this.cwd))
|
|
132
|
-
})
|
|
133
|
-
await withTimeout(new Promise<void>((resolve, reject) => {
|
|
134
|
-
childProcess.once('spawn', () => resolve())
|
|
135
|
-
childProcess.once('error', reject)
|
|
136
|
-
}), START_TIMEOUT_MS, `pi-headless child did not start for session ${this.id}`)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export async function runPiHeadlessController(id: string, runtimeDir: string, piCmd: string, tail: string[]): Promise<void> {
|
|
141
|
-
// runtimeDir is retained in the command shape for parity with claude-headless and future per-session output.
|
|
142
|
-
mkdirSync(join(runtimeDir, 'sessions', id), { recursive: true })
|
|
143
|
-
const controller = new PiHeadlessController(id, runtimeDir, piCmd)
|
|
144
|
-
const resume = tail[0] === '--session'
|
|
145
|
-
const prompt = resume ? undefined : tail[0] === '--session-id' ? tail.slice(2).join(' ') : tail.join(' ')
|
|
146
|
-
await controller.start(prompt)
|
|
147
|
-
await new Promise<void>((resolve) => {
|
|
148
|
-
const stop = () => void controller.close().finally(resolve)
|
|
149
|
-
process.once('SIGINT', stop)
|
|
150
|
-
process.once('SIGTERM', stop)
|
|
151
|
-
process.once('SIGHUP', stop)
|
|
152
|
-
})
|
|
153
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, existsSync, readdirSync, rmSync } from 'node:fs'
|
|
2
|
-
import { join, dirname } from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import { copyFileIfChanged, writeFileIfChanged } from './file-write.js'
|
|
5
|
-
|
|
6
|
-
// @@@ plugin-harness - the PLUGIN BUNDLE emitter: materialize the whole SpexCode system into ONE self-contained
|
|
7
|
-
// Claude-plugin bundle dropped into the host-agent-scanned folder [[harness-select]] resolved (e.g. `.zcode` /
|
|
8
|
-
// `.claude` → <folder>/plugins/spexcode/). It is the plugin-target counterpart of the native per-harness
|
|
9
|
-
// write [[harness-adapter]] does — chosen INSTEAD of the natives (plugin exclusivity), so [[harness-delivery]]'s
|
|
10
|
-
// materialize prunes every native first, then emits this. The bundle follows the de-facto Claude-plugin schema
|
|
11
|
-
// (a `.claude-plugin/plugin.json` pointing at hooks/skills/commands/agents); the host's discovery order is
|
|
12
|
-
// `.zcode-plugin > .claude-plugin > .codex-plugin` and z-code/Claude both read a `.claude-plugin` directly, so
|
|
13
|
-
// the ONE `.claude-plugin` bundle reaches ZCode, Claude, and (future) Codex from a single emit.
|
|
14
|
-
//
|
|
15
|
-
// The contract is NOT delivered by an always-on CLAUDE.md block here (the bundle never touches the repo's own
|
|
16
|
-
// files) — it maps to a SessionStart hook that emits hookSpecificOutput.additionalContext (the harness-neutral
|
|
17
|
-
// injection Claude/z-code normalize, the superpowers pattern), so a plugin host gets the contract with no
|
|
18
|
-
// --append-system-prompt. The hooks reuse the SAME dispatch.sh wiring as the natives, located via the host's
|
|
19
|
-
// ${CLAUDE_PLUGIN_ROOT} variable; dispatch.sh's first arg is the harness id `plugin`, so its shell mirror
|
|
20
|
-
// (harness.sh) parses payloads as the claude family (z-code/Claude share Claude's tool names + file_path).
|
|
21
|
-
|
|
22
|
-
const PKG = fileURLToPath(new URL('..', import.meta.url)) // installed spec-cli root
|
|
23
|
-
const HOOKS_SRC = join(PKG, 'hooks') // the canonical dispatch.sh + harness.sh source
|
|
24
|
-
const PLUGIN_NAME = 'spexcode'
|
|
25
|
-
// the host substitutes ${CLAUDE_PLUGIN_ROOT} with the bundle's own absolute path before running a hook command
|
|
26
|
-
// (the same variable z-code's hook-compat honours), so dispatch.sh/inject-contract.sh resolve regardless of
|
|
27
|
-
// where the host scanned the bundle from.
|
|
28
|
-
const PLUGIN_ROOT = '${CLAUDE_PLUGIN_ROOT}'
|
|
29
|
-
// the lifecycle events the bundle binds — the Claude/z-code superset; a host that fires fewer (Codex) simply
|
|
30
|
-
// never invokes the extras, so binding all is harmless and one emit serves every host.
|
|
31
|
-
const PLUGIN_EVENTS = ['SessionStart', 'UserPromptSubmit', 'PreToolUse', 'PostToolUse', 'Stop', 'StopFailure', 'Notification'] as const
|
|
32
|
-
|
|
33
|
-
// the gathered surface artifacts this materialize writes into the bundle. contract = the assembled guide+system
|
|
34
|
-
// block (the SAME string the native path folds into CLAUDE.md, here delivered as additionalContext); skills/
|
|
35
|
-
// agents/commands are the already-materialized file CONTENTS (reusing materialize's skillArtifact/agentArtifact),
|
|
36
|
-
// keyed
|
|
37
|
-
// by node name; spex is the baked tsx+cli invocation the dispatcher gate calls; version stamps plugin.json.
|
|
38
|
-
export type PluginBundle = {
|
|
39
|
-
contract: string
|
|
40
|
-
skills: { name: string; content: string }[]
|
|
41
|
-
agents: { name: string; content: string }[]
|
|
42
|
-
commands: { name: string; content: string }[]
|
|
43
|
-
spex: string
|
|
44
|
-
version: string
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// the bundle dir for a plugin folder: <proj>/<folder>/plugins/spexcode — `<folder>` is the host's plugins-scan
|
|
48
|
-
// root ([[harness-select]] requires it explicit), `plugins/<name>` the conventional bundle location under it.
|
|
49
|
-
export function pluginBundleDir(proj: string, folder: string): string {
|
|
50
|
-
return join(proj, folder, 'plugins', PLUGIN_NAME)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// the de-facto Claude-plugin manifest: name (the bundle identity clean() gates on), version, description, and
|
|
54
|
-
// the component pointers Claude/z-code discover (hooks.json + the skills/commands/agents dirs).
|
|
55
|
-
function pluginManifest(version: string): string {
|
|
56
|
-
return JSON.stringify({
|
|
57
|
-
name: PLUGIN_NAME,
|
|
58
|
-
version,
|
|
59
|
-
description: 'SpexCode — spec-driven dev-flow contract, hooks, skills, commands & agents as one self-contained plugin.',
|
|
60
|
-
hooks: './hooks/hooks.json',
|
|
61
|
-
commands: './commands',
|
|
62
|
-
agents: './agents',
|
|
63
|
-
skills: './skills',
|
|
64
|
-
}, null, 2)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// hooks.json in the Claude/z-code-compatible shape { "hooks": { "<Event>": [{ "hooks": [command…] }] } }. Every
|
|
68
|
-
// event → the SHARED dispatch.sh (`plugin` baked as its harness id, SPEX inherited by handlers); SessionStart
|
|
69
|
-
// ALSO runs inject-contract.sh first, so the contract additionalContext lands alongside the normal dispatch.
|
|
70
|
-
function pluginHooksJson(spex: string): string {
|
|
71
|
-
const dispatch = (e: string) => `SPEX='${spex}' bash "${PLUGIN_ROOT}/hooks/dispatch.sh" plugin ${e}`
|
|
72
|
-
const inject = `bash "${PLUGIN_ROOT}/hooks/inject-contract.sh"`
|
|
73
|
-
const hooks: Record<string, unknown> = {}
|
|
74
|
-
for (const e of PLUGIN_EVENTS) {
|
|
75
|
-
const cmds = e === 'SessionStart'
|
|
76
|
-
? [{ type: 'command', command: inject }, { type: 'command', command: dispatch(e) }]
|
|
77
|
-
: [{ type: 'command', command: dispatch(e) }]
|
|
78
|
-
hooks[e] = [{ hooks: cmds }]
|
|
79
|
-
}
|
|
80
|
-
return JSON.stringify({ hooks }, null, 2)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// the SessionStart hook OUTPUT carrying the contract as additionalContext. JSON-encoded HERE, at materialize time,
|
|
84
|
-
// so the runtime hook is a trivial `cat` — never a fragile shell escaping of arbitrary contract prose.
|
|
85
|
-
function contractContextJson(contract: string): string {
|
|
86
|
-
return JSON.stringify({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: contract } }, null, 2)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// the SessionStart contract injector: print the pre-encoded additionalContext payload (the harness-neutral
|
|
90
|
-
// stand-in for --append-system-prompt). A bare cat — all the encoding happened at materialize time.
|
|
91
|
-
const INJECT_SH = `#!/usr/bin/env bash
|
|
92
|
-
# Emit the SpexCode contract as SessionStart additionalContext — the harness-neutral contract injection (the
|
|
93
|
-
# superpowers pattern; Claude/z-code normalize hookSpecificOutput.additionalContext) that replaces a plugin
|
|
94
|
-
# host's missing --append-system-prompt. The JSON was written at materialize time, so this is a trivial cat.
|
|
95
|
-
here="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
|
|
96
|
-
[ -f "$here/contract-context.json" ] && cat "$here/contract-context.json"
|
|
97
|
-
exit 0
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
// emit (or idempotently re-emit) the whole bundle into <folder>/plugins/spexcode.
|
|
101
|
-
export function emitPlugin(proj: string, folder: string, r: PluginBundle): void {
|
|
102
|
-
const bundle = pluginBundleDir(proj, folder)
|
|
103
|
-
const meta = join(bundle, '.claude-plugin')
|
|
104
|
-
const hooksDir = join(bundle, 'hooks')
|
|
105
|
-
mkdirSync(meta, { recursive: true })
|
|
106
|
-
mkdirSync(hooksDir, { recursive: true })
|
|
107
|
-
writeFileIfChanged(join(meta, 'plugin.json'), pluginManifest(r.version))
|
|
108
|
-
// hooks: the SHARED dispatcher + its shell mirror (copied verbatim — the exact native wiring), the contract
|
|
109
|
-
// injector + its pre-encoded payload, and the event→dispatch binding.
|
|
110
|
-
copyFileIfChanged(join(HOOKS_SRC, 'dispatch.sh'), join(hooksDir, 'dispatch.sh'))
|
|
111
|
-
copyFileIfChanged(join(HOOKS_SRC, 'harness.sh'), join(hooksDir, 'harness.sh'))
|
|
112
|
-
writeFileIfChanged(join(hooksDir, 'inject-contract.sh'), INJECT_SH)
|
|
113
|
-
writeFileIfChanged(join(hooksDir, 'contract-context.json'), contractContextJson(r.contract))
|
|
114
|
-
writeFileIfChanged(join(hooksDir, 'hooks.json'), pluginHooksJson(r.spex))
|
|
115
|
-
// skills / agents / commands — the Claude-plugin layout, the SAME materialized contents as the native dirs.
|
|
116
|
-
reconcileBundleDirectory(join(bundle, 'skills'), new Set(r.skills.map((s) => s.name)))
|
|
117
|
-
reconcileBundleDirectory(join(bundle, 'agents'), new Set(r.agents.map((a) => `${a.name}.md`)))
|
|
118
|
-
reconcileBundleDirectory(join(bundle, 'commands'), new Set(r.commands.map((c) => `${c.name}.md`)))
|
|
119
|
-
for (const s of r.skills) writeBundleFile(join(bundle, 'skills', s.name, 'SKILL.md'), s.content)
|
|
120
|
-
for (const a of r.agents) writeBundleFile(join(bundle, 'agents', `${a.name}.md`), a.content)
|
|
121
|
-
for (const c of r.commands) writeBundleFile(join(bundle, 'commands', `${c.name}.md`), c.content)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function reconcileBundleDirectory(dir: string, expectedNames: ReadonlySet<string>): void {
|
|
125
|
-
if (!existsSync(dir)) return
|
|
126
|
-
for (const entry of readdirSync(dir)) {
|
|
127
|
-
if (!expectedNames.has(entry)) rmSync(join(dir, entry), { recursive: true, force: true })
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function writeBundleFile(f: string, content: string): void {
|
|
132
|
-
mkdirSync(dirname(f), { recursive: true })
|
|
133
|
-
writeFileIfChanged(f, content)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// the INVERSE of emitPlugin — prune the bundle when its folder is DESELECTED ([[harness-delivery]] tracks the
|
|
137
|
-
// previously-emitted folders and cleans any the current set dropped, e.g. switching plugin→native or folder A→B).
|
|
138
|
-
// Identity-gated on the bundle's own plugin.json `name`, so it removes ONLY a spexcode bundle, never a folder
|
|
139
|
-
// the user populated with another plugin.
|
|
140
|
-
export function cleanPlugin(proj: string, folder: string): void {
|
|
141
|
-
const bundle = pluginBundleDir(proj, folder)
|
|
142
|
-
const manifest = join(bundle, '.claude-plugin', 'plugin.json')
|
|
143
|
-
if (!existsSync(manifest)) return
|
|
144
|
-
try {
|
|
145
|
-
if (String(JSON.parse(readFileSync(manifest, 'utf8'))?.name) !== PLUGIN_NAME) return
|
|
146
|
-
} catch { return }
|
|
147
|
-
rmSync(bundle, { recursive: true, force: true })
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// the bundle version stamped into plugin.json — the monorepo root `spexcode` package version (the published
|
|
151
|
-
// artifact's version), falling back to the spec-cli package, then 0.0.0.
|
|
152
|
-
export function pluginVersion(): string {
|
|
153
|
-
for (const p of [join(PKG, '..', 'package.json'), join(PKG, 'package.json')]) {
|
|
154
|
-
try { const v = JSON.parse(readFileSync(p, 'utf8'))?.version; if (v) return String(v) } catch { /* keep trying */ }
|
|
155
|
-
}
|
|
156
|
-
return '0.0.0'
|
|
157
|
-
}
|