spexcode 0.6.2 → 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 +3 -1
- 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} +36 -32
- 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 +1 -1
- 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/{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/{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-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 -728
- package/spec-cli/src/attach.ts +0 -43
- package/spec-cli/src/claude-headless.ts +0 -321
- package/spec-cli/src/cli.ts +0 -1258
- package/spec-cli/src/client.ts +0 -414
- 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 -165
- package/spec-cli/src/doctor.ts +0 -516
- package/spec-cli/src/execution-trace.ts +0 -444
- 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 -358
- package/spec-cli/src/gateway.ts +0 -461
- package/spec-cli/src/git.ts +0 -2556
- package/spec-cli/src/graph.ts +0 -271
- package/spec-cli/src/graphCache.ts +0 -710
- 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 -3025
- package/spec-cli/src/headless-controller.ts +0 -63
- package/spec-cli/src/help.ts +0 -492
- package/spec-cli/src/host-resources.ts +0 -762
- 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 -138
- package/spec-cli/src/index.ts +0 -818
- 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 -640
- package/spec-cli/src/lint.ts +0 -488
- package/spec-cli/src/localIssues.ts +0 -538
- package/spec-cli/src/materialize.ts +0 -417
- 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 -434
- 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-execution.ts +0 -68
- package/spec-cli/src/session-files.ts +0 -121
- package/spec-cli/src/session-follow.ts +0 -182
- package/spec-cli/src/session-reparent.ts +0 -33
- package/spec-cli/src/session-timeline.ts +0 -247
- package/spec-cli/src/session-web.ts +0 -135
- package/spec-cli/src/sessions.ts +0 -3883
- 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-dashboard/dist/assets/App-B6IbFbGu.js +0 -2
- package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
- package/spec-dashboard/dist/assets/Dashboard-_sDk7Rcg.js +0 -27
- package/spec-dashboard/dist/assets/EvalsPage-BT357Lyt.js +0 -2
- package/spec-dashboard/dist/assets/IssuesPage-BG9rNOgV.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-D9yPMR3A.js +0 -2
- package/spec-dashboard/dist/assets/Modal-B3EcgeA3.js +0 -1
- package/spec-dashboard/dist/assets/PageScroll-CNZOugWV.js +0 -1
- package/spec-dashboard/dist/assets/ProjectsPage-C2CRHmvs.js +0 -1
- package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
- package/spec-dashboard/dist/assets/SessionInterface-DOEjuT3l.js +0 -40
- package/spec-dashboard/dist/assets/SessionWindow-1nYgZ4CL.js +0 -1
- package/spec-dashboard/dist/assets/Settings-7WeTJn0V.js +0 -1
- package/spec-dashboard/dist/assets/Thread-CEbzAFQn.js +0 -13
- package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
- package/spec-dashboard/dist/assets/data-SlvxNA2r.js +0 -1
- package/spec-dashboard/dist/assets/index-DLVeOHL-.js +0 -41
- package/spec-dashboard/dist/assets/index-DYLGfPjx.css +0 -1
- package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
- package/spec-dashboard/dist/assets/launch-yxCYr64x.js +0 -276
- package/spec-dashboard/dist/assets/sessionSurface--7l69h4F.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 -894
- package/spec-eval/src/evaltab.ts +0 -264
- package/spec-eval/src/filing.ts +0 -39
- package/spec-eval/src/freshness.ts +0 -836
- 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 -2220
- 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/comment-altitude/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/core/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/reproduce-before-fix/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
package/spec-cli/src/doctor.ts
DELETED
|
@@ -1,516 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, accessSync, constants } from 'node:fs'
|
|
2
|
-
import { join, dirname, basename } from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
import { execFileSync } from 'node:child_process'
|
|
5
|
-
import { homedir } from 'node:os'
|
|
6
|
-
import { loadSystemConfig, loadSkillConfig, loadSpecs } from './specs.js'
|
|
7
|
-
import { runtimeRoot, treeSlotDir, envSessionId, readAliasedRawRecord, mainCheckout, readJsonConfig } from './layout.js'
|
|
8
|
-
import { loadConfig } from './lint.js'
|
|
9
|
-
import { trackedSourceFiles } from './source-files.js'
|
|
10
|
-
import { gitBinary } from './git.js'
|
|
11
|
-
|
|
12
|
-
// this file lives at <pkgRoot>/src/self.ts, so `..` is the package root — the same derivation init.ts/
|
|
13
|
-
// materialize.ts use (never a hardcoded repo path), so the git-hook template lookup survives a relocated install.
|
|
14
|
-
const PKG_ROOT = fileURLToPath(new URL('..', import.meta.url))
|
|
15
|
-
|
|
16
|
-
// run a git query in `dir`, swallowing git's own stderr — a non-repo returns null (the absence IS the signal).
|
|
17
|
-
function git(dir: string, args: string[]): string | null {
|
|
18
|
-
try { return execFileSync(gitBinary(process.env), ['-C', dir, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() }
|
|
19
|
-
catch { return null }
|
|
20
|
-
}
|
|
21
|
-
function repoRoot(dir: string): string | null { return git(dir, ['rev-parse', '--show-toplevel']) }
|
|
22
|
-
function commonHooksDir(dir: string): string | null {
|
|
23
|
-
const common = git(dir, ['rev-parse', '--path-format=absolute', '--git-common-dir'])
|
|
24
|
-
return common ? join(common, 'hooks') : null
|
|
25
|
-
}
|
|
26
|
-
const read = (f: string): string => { try { return readFileSync(f, 'utf8') } catch { return '' } }
|
|
27
|
-
|
|
28
|
-
type AltitudeConfig = {
|
|
29
|
-
lineBudget: number
|
|
30
|
-
charBudget: number
|
|
31
|
-
sizeable: number
|
|
32
|
-
dense: number
|
|
33
|
-
steps: number
|
|
34
|
-
identifierExtensions: string[]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
type BreadthConfig = {
|
|
38
|
-
maxChildren: number
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export type HealthFinding = {
|
|
42
|
-
check: string
|
|
43
|
-
spec: string
|
|
44
|
-
summary: string
|
|
45
|
-
evidence: string[]
|
|
46
|
-
repair: string
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const DEFAULT_ALTITUDE: AltitudeConfig = {
|
|
50
|
-
lineBudget: 50,
|
|
51
|
-
charBudget: 4200,
|
|
52
|
-
sizeable: 35,
|
|
53
|
-
dense: 1.3,
|
|
54
|
-
steps: 3,
|
|
55
|
-
identifierExtensions: [],
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const DEFAULT_BREADTH: BreadthConfig = {
|
|
59
|
-
maxChildren: 8,
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function loadAltitudeConfig(root: string): AltitudeConfig {
|
|
63
|
-
const configured = readJsonConfig(join(root, 'spexcode.json'))?.doctor?.altitude ?? {}
|
|
64
|
-
const merged = { ...DEFAULT_ALTITUDE, ...configured }
|
|
65
|
-
return {
|
|
66
|
-
...merged,
|
|
67
|
-
identifierExtensions: (merged.identifierExtensions ?? []).map((ext: string) => ext.replace(/^\.+/, '')),
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function loadBreadthConfig(root: string): BreadthConfig {
|
|
72
|
-
const configured = readJsonConfig(join(root, 'spexcode.json'))?.doctor?.breadth ?? {}
|
|
73
|
-
return { ...DEFAULT_BREADTH, ...configured }
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Filename rows are lint coverage's exact tracked candidates. Compatibility extensions lower to wildcard
|
|
77
|
-
// rows before the one identifier matcher is compiled.
|
|
78
|
-
function identifierFilenameCandidates(sourceFiles: string[], compatibilityExtensions: string[]): string[] {
|
|
79
|
-
return [...new Set([
|
|
80
|
-
...sourceFiles.map((path) => basename(path)),
|
|
81
|
-
...compatibilityExtensions.map((ext) => `*.${ext}`),
|
|
82
|
-
])]
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function identRe(filenameCandidates: string[]): RegExp {
|
|
86
|
-
const escape = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
87
|
-
const filenames = filenameCandidates
|
|
88
|
-
.map((candidate) => candidate.startsWith('*.')
|
|
89
|
-
? `[\\w-]+\\.${escape(candidate.slice(2))}`
|
|
90
|
-
: escape(candidate))
|
|
91
|
-
.sort((a, b) => b.length - a.length)
|
|
92
|
-
const signals = [
|
|
93
|
-
'[a-z][A-Za-z0-9]*[A-Z][A-Za-z0-9]*',
|
|
94
|
-
'\\b[a-z]+_[a-z0-9_]+\\b',
|
|
95
|
-
'\\b\\w+\\(',
|
|
96
|
-
'`[^`]+`',
|
|
97
|
-
'\\/[\\w./-]+\\.\\w+',
|
|
98
|
-
]
|
|
99
|
-
if (filenames.length) signals.push(`(?<![\\w./-])(?:${filenames.join('|')})(?![\\w.-])`)
|
|
100
|
-
return new RegExp(signals.join('|'), 'g')
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const STEP_LINE = /^\s*(\d+[.)]\s|[-*]\s*(first|then|next|finally)\b)|(^|[,;]\s*)(first|then|next|finally),/i
|
|
104
|
-
|
|
105
|
-
function altitudeEvidence(body: string, cfg: AltitudeConfig, ident: RegExp): string[] {
|
|
106
|
-
const lines = body.split('\n')
|
|
107
|
-
const nonBlank = lines.filter((line) => line.trim()).length
|
|
108
|
-
let inFence = false
|
|
109
|
-
let signals = 0
|
|
110
|
-
let steps = 0
|
|
111
|
-
for (const line of lines) {
|
|
112
|
-
if (/^\s*```/.test(line)) { inFence = !inFence; continue }
|
|
113
|
-
if (inFence || !line.trim()) continue
|
|
114
|
-
signals += line.match(ident)?.length ?? 0
|
|
115
|
-
if (STEP_LINE.test(line)) steps++
|
|
116
|
-
}
|
|
117
|
-
const density = signals / Math.max(1, nonBlank)
|
|
118
|
-
const evidence: string[] = []
|
|
119
|
-
if (nonBlank > cfg.lineBudget || body.length > cfg.charBudget)
|
|
120
|
-
evidence.push(`${nonBlank} non-blank lines / ${body.length} chars over budget (${cfg.lineBudget}/${cfg.charBudget})`)
|
|
121
|
-
if (nonBlank > cfg.sizeable && density > cfg.dense)
|
|
122
|
-
evidence.push(`code-identifier density ${density.toFixed(2)}/line over ${cfg.dense}`)
|
|
123
|
-
if (nonBlank > cfg.sizeable && steps >= cfg.steps)
|
|
124
|
-
evidence.push(`${steps} step-by-step how-to lines`)
|
|
125
|
-
return evidence
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export async function specHealthDiagnosis(root: string): Promise<HealthFinding[]> {
|
|
129
|
-
const lint = loadConfig(root)
|
|
130
|
-
const altitude = loadAltitudeConfig(root)
|
|
131
|
-
const breadth = loadBreadthConfig(root)
|
|
132
|
-
const governed = trackedSourceFiles(root, lint.governedRoots, lint)
|
|
133
|
-
const ident = identRe(identifierFilenameCandidates(governed, altitude.identifierExtensions))
|
|
134
|
-
const specs = await loadSpecs(root)
|
|
135
|
-
const childCount = new Map<string, number>()
|
|
136
|
-
for (const spec of specs) if (spec.parent) childCount.set(spec.parent, (childCount.get(spec.parent) ?? 0) + 1)
|
|
137
|
-
const findings: HealthFinding[] = []
|
|
138
|
-
for (const spec of specs) {
|
|
139
|
-
const evidence = altitudeEvidence(spec.body, altitude, ident)
|
|
140
|
-
if (evidence.length) findings.push({
|
|
141
|
-
check: 'altitude',
|
|
142
|
-
spec: spec.id,
|
|
143
|
-
summary: 'body reads like mechanics rather than a contract',
|
|
144
|
-
evidence,
|
|
145
|
-
repair: `rewrite '${spec.id}' around observable intent and invariants; the tidy workflow can perform the semantic review`,
|
|
146
|
-
})
|
|
147
|
-
const children = childCount.get(spec.id) ?? 0
|
|
148
|
-
if (children >= breadth.maxChildren) findings.push({
|
|
149
|
-
check: 'breadth',
|
|
150
|
-
spec: spec.id,
|
|
151
|
-
summary: 'tree fan-out may be missing a natural grouping layer',
|
|
152
|
-
evidence: [`${children} direct child nodes (>= ${breadth.maxChildren})`],
|
|
153
|
-
repair: `review '${spec.id}' with the regroup workflow; group only along a real seam and leave genuine peers flat`,
|
|
154
|
-
})
|
|
155
|
-
}
|
|
156
|
-
return findings
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function healthReport(findings: HealthFinding[], adopted: boolean): string[] {
|
|
160
|
-
const lines = ['Spec health diagnosis (opt-in advisory; never part of spex spec lint)']
|
|
161
|
-
if (!adopted) {
|
|
162
|
-
lines.push(' status : unavailable — adopt the repository with `spex init`')
|
|
163
|
-
return lines
|
|
164
|
-
}
|
|
165
|
-
const checks = ['altitude', 'breadth', ...new Set(findings.map((finding) => finding.check).filter((check) => check !== 'altitude' && check !== 'breadth'))]
|
|
166
|
-
for (const check of checks) {
|
|
167
|
-
const rows = findings.filter((finding) => finding.check === check)
|
|
168
|
-
lines.push(` ${check.padEnd(16)}: ${rows.length ? `${rows.length} finding(s)` : 'healthy'}`)
|
|
169
|
-
for (const finding of rows) {
|
|
170
|
-
lines.push(` ${finding.spec.padEnd(16)}: ${finding.summary}`)
|
|
171
|
-
for (const evidence of finding.evidence) lines.push(` evidence : ${evidence}`)
|
|
172
|
-
lines.push(` repair : ${finding.repair}`)
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return lines
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function contractText(): { names: string[]; body: string } {
|
|
179
|
-
let cfgs: { name: string; body: string }[] = []
|
|
180
|
-
try { cfgs = loadSystemConfig().map((c) => ({ name: c.name, body: c.body })) } catch { /* tree-less cwd → empty */ }
|
|
181
|
-
return { names: cfgs.map((c) => c.name), body: cfgs.map((c) => c.body.trim()).filter(Boolean).join('\n\n') }
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// compare an installed git hook to the shipped template: spexcode (current) | stale (older SpexCode, re-run
|
|
185
|
-
// `spex init`) | foreign (a real conflict) | missing. The stale/foreign split keeps a slightly-behind repo
|
|
186
|
-
// from reading as a CONFLICT.
|
|
187
|
-
type HookState = 'spexcode' | 'stale' | 'foreign' | 'missing'
|
|
188
|
-
function hookState(hooksDir: string | null, name: string): HookState {
|
|
189
|
-
if (!hooksDir) return 'missing'
|
|
190
|
-
const installed = join(hooksDir, name)
|
|
191
|
-
if (!existsSync(installed)) return 'missing'
|
|
192
|
-
const body = read(installed)
|
|
193
|
-
try { if (body === readFileSync(join(PKG_ROOT, 'templates', 'hooks', name), 'utf8')) return 'spexcode' } catch { /* fall through */ }
|
|
194
|
-
return /SpexCode/.test(body) ? 'stale' : 'foreign'
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// does `bin` resolve to an executable on the inherited PATH? Returns the resolving path, or null. This is the
|
|
198
|
-
// precondition behind a dozen confusing symptoms — a session shell whose PATH misses the global bin dir gets
|
|
199
|
-
// `command not found` for spex/codex/claude (npm's global prefix landing off-PATH).
|
|
200
|
-
function resolveOnPath(bin: string): string | null {
|
|
201
|
-
for (const d of (process.env.PATH || '').split(':')) {
|
|
202
|
-
if (!d) continue
|
|
203
|
-
const p = join(d, bin)
|
|
204
|
-
try { accessSync(p, constants.X_OK); return p } catch { /* not here */ }
|
|
205
|
-
}
|
|
206
|
-
return null
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// parse the materialized manifest (TAB lines: event<TAB>order<TAB>block<TAB>script) → the unique handler
|
|
210
|
-
// scripts, repo-relative. The dispatcher runs each as bash "<proj>/<script>".
|
|
211
|
-
function manifestScripts(text: string): string[] {
|
|
212
|
-
const out = new Set<string>()
|
|
213
|
-
for (const line of text.split('\n')) {
|
|
214
|
-
const f = line.split('\t')
|
|
215
|
-
if (f.length >= 4 && f[3]) out.add(f[3])
|
|
216
|
-
}
|
|
217
|
-
return [...out]
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// a plugin bundle dir found under a harness's plugins root, carrying a spexcode stamp.
|
|
221
|
-
type Bundle = { dir: string; name: string; scope: string; hooksToDispatch: boolean; skillsDir: string }
|
|
222
|
-
|
|
223
|
-
// the bundle's declared name, from plugin.json (root or the .claude-plugin/ convention), or null when none.
|
|
224
|
-
function pluginName(dir: string): string | null {
|
|
225
|
-
for (const p of [join(dir, 'plugin.json'), join(dir, '.claude-plugin', 'plugin.json')]) {
|
|
226
|
-
if (!existsSync(p)) continue
|
|
227
|
-
try { const n = (JSON.parse(readFileSync(p, 'utf8')) as { name?: unknown }).name; if (typeof n === 'string') return n } catch { /* malformed → treat as nameless */ }
|
|
228
|
-
}
|
|
229
|
-
return null
|
|
230
|
-
}
|
|
231
|
-
// does this bundle wire a hooks shim that routes to OUR dispatch.sh? (hooks/hooks.json or a root hooks.json)
|
|
232
|
-
function bundleHooksToDispatch(dir: string): boolean {
|
|
233
|
-
return [join(dir, 'hooks', 'hooks.json'), join(dir, 'hooks.json')].some((p) => /dispatch\.sh/.test(read(p)))
|
|
234
|
-
}
|
|
235
|
-
// scan one plugins root's immediate children for SpexCode-stamped bundles (name=="spexcode", a dispatch.sh
|
|
236
|
-
// shim, or one of our own skill names) — three stamps, any one suffices. A non-dir / missing root → [].
|
|
237
|
-
function scanBundles(root: string, scope: string, ourSkills: string[]): Bundle[] {
|
|
238
|
-
let names: string[] = []
|
|
239
|
-
try { names = readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name) } catch { return [] }
|
|
240
|
-
const out: Bundle[] = []
|
|
241
|
-
for (const n of names) {
|
|
242
|
-
const dir = join(root, n)
|
|
243
|
-
const skillsDir = join(dir, 'skills')
|
|
244
|
-
const pname = pluginName(dir)
|
|
245
|
-
const hooksToDispatch = bundleHooksToDispatch(dir)
|
|
246
|
-
const hasOurSkill = ourSkills.some((s) => existsSync(join(skillsDir, s, 'SKILL.md')) || existsSync(join(skillsDir, s)))
|
|
247
|
-
if (pname === 'spexcode' || hooksToDispatch || hasOurSkill) out.push({ dir, name: pname ?? n, scope, hooksToDispatch, skillsDir })
|
|
248
|
-
}
|
|
249
|
-
return out
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// per-harness double-delivery report. For each harness it derives the plugins roots FROM THE ADAPTER's own
|
|
253
|
-
// shim path (`<cfgdir>/plugins` in-tree + `~/<cfgdir>/plugins` global — no hardcoded `.claude`, so a new
|
|
254
|
-
// harness scans for free), finds the spexcode bundles, then counts three channels against the loose native
|
|
255
|
-
// delivery. Returns the printable lines + whether ANY channel >1 (a live double-delivery).
|
|
256
|
-
async function doubleDeliveryReport(base: string): Promise<{ lines: string[]; conflict: boolean }> {
|
|
257
|
-
const { HARNESSES } = await import('./harness.js')
|
|
258
|
-
const ourSkills = (() => { try { return loadSkillConfig().map((c) => c.name) } catch { return [] as string[] } })()
|
|
259
|
-
const L: string[] = []
|
|
260
|
-
const line = (k: string, v: string) => L.push(` ${k.padEnd(16)}: ${v}`)
|
|
261
|
-
let conflict = false
|
|
262
|
-
const rel = (f: string) => f.startsWith(base + '/') ? f.slice(base.length + 1) : f
|
|
263
|
-
|
|
264
|
-
for (const h of HARNESSES) {
|
|
265
|
-
const shimFile = h.shimFile(base)
|
|
266
|
-
const looseDispatch = /dispatch\.sh/.test(read(shimFile))
|
|
267
|
-
const looseSkillDir = h.skillDir(base)
|
|
268
|
-
const looseContract = h.contractFiles(base).some((f) => /<!--\s*spexcode:start\s*-->/.test(read(f)))
|
|
269
|
-
const looseSkill = (s: string) => !!looseSkillDir && existsSync(join(looseSkillDir, s, 'SKILL.md'))
|
|
270
|
-
const looseDelivery = looseDispatch || looseContract || ourSkills.some(looseSkill)
|
|
271
|
-
|
|
272
|
-
const cfgDir = dirname(shimFile) // <proj>/.claude (codex: <main>/.codex)
|
|
273
|
-
const roots: [string, string][] = [[join(cfgDir, 'plugins'), 'workspace'], [join(homedir(), basename(cfgDir), 'plugins'), 'global']]
|
|
274
|
-
const bundles = roots.flatMap(([r, scope]) => scanBundles(r, scope, ourSkills))
|
|
275
|
-
|
|
276
|
-
// channel 1 — hooks → dispatch.sh (loose shim + any bundle hooks shim)
|
|
277
|
-
const hookSrc = [
|
|
278
|
-
...(looseDispatch ? [`loose ${rel(shimFile)}`] : []),
|
|
279
|
-
...bundles.filter((b) => b.hooksToDispatch).map((b) => `plugin "${b.name}" (${b.scope})`),
|
|
280
|
-
]
|
|
281
|
-
// channel 2 — same-named skill in loose skillDir AND a bundle's skills dir
|
|
282
|
-
const skillHits: string[] = []
|
|
283
|
-
for (const s of ourSkills) {
|
|
284
|
-
let c = looseSkill(s) ? 1 : 0
|
|
285
|
-
c += bundles.filter((b) => existsSync(join(b.skillsDir, s, 'SKILL.md')) || existsSync(join(b.skillsDir, s))).length
|
|
286
|
-
if (c > 1) skillHits.push(`${s} ×${c}`)
|
|
287
|
-
}
|
|
288
|
-
// channel 3 — total delivery sources reaching this harness (loose + each spexcode bundle)
|
|
289
|
-
const sources = (looseDelivery ? 1 : 0) + bundles.length
|
|
290
|
-
|
|
291
|
-
const hConflict = hookSrc.length > 1 || skillHits.length > 0 || sources > 1
|
|
292
|
-
if (hConflict) conflict = true
|
|
293
|
-
|
|
294
|
-
L.push(`${h.id}${hConflict ? ' — DOUBLE-DELIVERY CONFLICT' : ''}`)
|
|
295
|
-
line('delivery srcs', `${sources}${sources > 1 ? ' (>1 → CONFLICT)' : sources === 1 ? ' (single — ok)' : ' (none)'}`)
|
|
296
|
-
line(' loose', looseDelivery ? `present (${[looseDispatch && 'shim→dispatch', looseContract && 'contract', ourSkills.some(looseSkill) && 'skills'].filter(Boolean).join('+')})` : 'absent')
|
|
297
|
-
for (const b of bundles) line(' plugin', `"${b.name}" (${b.scope}) — ${b.dir}`)
|
|
298
|
-
line('hooks→dispatch', `${hookSrc.length}${hookSrc.length > 1 ? ' (>1 → CONFLICT): ' + hookSrc.join(', ') : hookSrc.length === 1 ? ' (single — ok)' : ' (none wired)'}`)
|
|
299
|
-
line('skill shadowing', skillHits.length ? `CONFLICT: ${skillHits.join(', ')}` : 'none')
|
|
300
|
-
L.push('')
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (conflict) {
|
|
304
|
-
L.push('Repair — SpexCode is reaching this agent through MORE THAN ONE discovery channel. Keep exactly one:')
|
|
305
|
-
L.push(' • remove the independently-installed plugin bundle (delete its dir, or `claude plugin uninstall spexcode`); or')
|
|
306
|
-
L.push(' • if you WANT the plugin, stop the native delivery: set spexcode.json to')
|
|
307
|
-
L.push(' `"harnesses": [{"plugin":".claude"}]`, then run `spex materialize` to prune the loose shim/contract/skills; or')
|
|
308
|
-
L.push(' • remove SpexCode\'s generated delivery with `spex uninstall`.')
|
|
309
|
-
} else {
|
|
310
|
-
L.push('No double-delivery: each harness is reached by at most one spexcode-stamped channel.')
|
|
311
|
-
}
|
|
312
|
-
return { lines: L, conflict }
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// ping the backend (the resolved apiBase) with a short timeout so doctor never hangs on a dead/wrong backend.
|
|
316
|
-
async function backendReachable(): Promise<{ base: string; up: boolean }> {
|
|
317
|
-
let base = 'http://127.0.0.1:8787'
|
|
318
|
-
try { base = await (await import('./sessions.js')).apiBase() } catch { /* keep default */ }
|
|
319
|
-
const ctrl = new AbortController()
|
|
320
|
-
const t = setTimeout(() => ctrl.abort(), 800)
|
|
321
|
-
try { return { base, up: (await fetch(`${base}/api/sessions`, { signal: ctrl.signal })).ok } }
|
|
322
|
-
catch { return { base, up: false } }
|
|
323
|
-
finally { clearTimeout(t) }
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// the headline: a per-layer report of whether the SpexCode workflow truly reaches THIS agent.
|
|
327
|
-
async function doctor(): Promise<number> {
|
|
328
|
-
const cwd = process.cwd()
|
|
329
|
-
const root = repoRoot(cwd)
|
|
330
|
-
const base = root ?? cwd // the contract files (CLAUDE.md/AGENTS.md) + hook shims live at the worktree ROOT — anchor every probe here
|
|
331
|
-
const adopted = existsSync(join(base, '.spec')) && existsSync(join(base, 'spexcode.json'))
|
|
332
|
-
// managed = THIS agent's own session is a GOVERNED record in the global store (the dashboard launcher set
|
|
333
|
-
// governed:true). That governed flag is the source of truth the old worktree `.session` presence only implied
|
|
334
|
-
// (see [[state]]); resolve the agent's id from its env and read the record — a self-launched BYOA agent has none.
|
|
335
|
-
const ownId = envSessionId()
|
|
336
|
-
const managed = !!(ownId && readAliasedRawRecord(ownId)?.governed)
|
|
337
|
-
const hooksDir = commonHooksDir(cwd)
|
|
338
|
-
const { names, body } = contractText()
|
|
339
|
-
|
|
340
|
-
const { HARNESSES } = await import('./harness.js')
|
|
341
|
-
type H = typeof HARNESSES[number]
|
|
342
|
-
// which harness is the RUNNING agent? the one whose session env var is set in this process.
|
|
343
|
-
const runningHarness: H | undefined = HARNESSES.find((h) => process.env[h.sessionEnvVar])
|
|
344
|
-
|
|
345
|
-
const L: string[] = []
|
|
346
|
-
const line = (k: string, v: string) => L.push(` ${k.padEnd(16)}: ${v}`)
|
|
347
|
-
L.push('spex doctor — how the SpexCode workflow reaches this agent\n')
|
|
348
|
-
|
|
349
|
-
L.push('Agent')
|
|
350
|
-
line('detected', runningHarness ? `${runningHarness.id} (${runningHarness.sessionEnvVar}=${process.env[runningHarness.sessionEnvVar]})` : 'none detected (no harness session env var set)')
|
|
351
|
-
L.push('\nRepo')
|
|
352
|
-
line('spex-adopted', adopted ? 'yes (.spec/ + spexcode.json)' : root ? 'no — run `spex init`' : 'not a git repo')
|
|
353
|
-
line('root', base)
|
|
354
|
-
line('mode', managed ? 'managed worktree (backend-launched session)' : 'standalone repo (bring-your-own-agent)')
|
|
355
|
-
// the issues-workflow switch ([[local-issues]]): its only home is the `issues.enabled` settings key
|
|
356
|
-
// (v0.3.0 — the on|off|status CLI verbs are gone), so doctor is where its state is READ. A legacy
|
|
357
|
-
// pre-rename `proposals.enabled` key is no longer consulted at runtime (no fallback) — flag it here so
|
|
358
|
-
// an old settings file gets repaired instead of silently drifting from what its author believes.
|
|
359
|
-
if (adopted) {
|
|
360
|
-
const { issuesEnabled } = await import('./localIssues.js')
|
|
361
|
-
// probe the SAME files the switch actually reads — the trunk checkout's settings pair, not this
|
|
362
|
-
// worktree's (readConfig resolves to mainCheckout, so a legacy key only matters there).
|
|
363
|
-
const cfgHome = (() => { try { return mainCheckout() } catch { return base } })()
|
|
364
|
-
const legacy = ['spexcode.json', 'spexcode.local.json'].filter((f) => {
|
|
365
|
-
try { return 'proposals' in JSON.parse(readFileSync(join(cfgHome, f), 'utf8')) } catch { return false }
|
|
366
|
-
})
|
|
367
|
-
line('issues workflow', `${issuesEnabled() ? 'ON' : 'OFF'} (spexcode.json issues.enabled)`)
|
|
368
|
-
if (legacy.length) line(' LEGACY key', `\`proposals\` found in ${legacy.map((f) => join(cfgHome, f)).join(', ')} — no longer read; rename it to "issues": { "enabled": … }`)
|
|
369
|
-
const projectConfig = readJsonConfig(join(base, 'spexcode.json'))
|
|
370
|
-
if (Object.prototype.hasOwnProperty.call(projectConfig.lint ?? {}, 'maxChildren'))
|
|
371
|
-
line('RETIRED key', '`lint.maxChildren` is no longer read — move the value to `doctor.breadth.maxChildren`')
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const health = adopted ? await specHealthDiagnosis(base) : []
|
|
375
|
-
L.push('\n' + healthReport(health, adopted).join('\n'))
|
|
376
|
-
|
|
377
|
-
// --- preconditions: nothing downstream fires without these ---
|
|
378
|
-
L.push('\nPreconditions (without these nothing downstream fires)')
|
|
379
|
-
for (const bin of ['spex', 'claude', 'codex']) {
|
|
380
|
-
const at = resolveOnPath(bin)
|
|
381
|
-
line(`PATH ${bin}`, at ? `resolves (${at})` : 'NOT on PATH — a session shell will hit `command not found`')
|
|
382
|
-
}
|
|
383
|
-
const codexHome = process.env.CODEX_HOME || join(homedir(), '.codex')
|
|
384
|
-
const codexCfg = read(join(codexHome, 'config.toml'))
|
|
385
|
-
const codexAuth = existsSync(join(codexHome, 'auth.json'))
|
|
386
|
-
line('codex auth', (/base_url|model_provider/.test(codexCfg) && codexAuth) ? `present (${codexHome})` : `incomplete (${codexHome}: config provider/base_url ${/base_url|model_provider/.test(codexCfg) ? 'ok' : 'MISSING'}, auth.json ${codexAuth ? 'ok' : 'MISSING'})`)
|
|
387
|
-
|
|
388
|
-
// --- git-hook floor: enforces for ANY agent/harness ---
|
|
389
|
-
const preCommit = hookState(hooksDir, 'pre-commit')
|
|
390
|
-
const prepare = hookState(hooksDir, 'prepare-commit-msg')
|
|
391
|
-
const hb = (s: HookState) => s === 'spexcode' ? 'installed (SpexCode)' : s === 'stale' ? 'OUTDATED (older SpexCode — re-run `spex init`)' : s === 'foreign' ? 'PRESENT but not SpexCode\'s (a foreign hook holds the slot)' : 'MISSING'
|
|
392
|
-
L.push('\nLayer 1 — git-hook floor (enforces for ANY agent)')
|
|
393
|
-
line('pre-commit', hb(preCommit))
|
|
394
|
-
line('prepare-cmsg', hb(prepare))
|
|
395
|
-
line('hooks dir', hooksDir ?? '—')
|
|
396
|
-
|
|
397
|
-
// --- contract: the surface:system block landed in each harness's contract file ---
|
|
398
|
-
L.push('\nLayer 2 — contract (surface:system text)')
|
|
399
|
-
line('nodes', names.length ? names.join(', ') : 'none — contract is empty')
|
|
400
|
-
for (const h of HARNESSES) {
|
|
401
|
-
const present = h.contractFiles(base).every((f) => /<!--\s*spexcode:start\s*-->/.test(read(f)))
|
|
402
|
-
line(`in ${h.id}`, present ? `block present (${h.contractFiles(base).map((f) => f.replace(base + '/', '')).join(', ')})` : 'NOT landed — run `spex doctor contract` / materialize')
|
|
403
|
-
}
|
|
404
|
-
line('view', 'spex doctor --contract')
|
|
405
|
-
|
|
406
|
-
// --- hooks: the shim → dispatch, the manifest, and EVERY handler readable in the worktree ---
|
|
407
|
-
L.push('\nLayer 3 — hooks (shim → dispatch · manifest · handler-existence)')
|
|
408
|
-
for (const h of HARNESSES) {
|
|
409
|
-
const shim = read(h.shimFile(base))
|
|
410
|
-
line(`${h.id} shim`, /dispatch\.sh/.test(shim) ? `wired (${h.shimFile(base).replace(base + '/', '')})` : 'NOT wired (no dispatch shim)')
|
|
411
|
-
}
|
|
412
|
-
// manifest resolution mirrors dispatch.sh: this tree's materialize slot first, then the legacy global file
|
|
413
|
-
// (a pre-slot tree's migration-window fallback) — so the doctor reads exactly what a dispatch would.
|
|
414
|
-
let manifestText = ''
|
|
415
|
-
let manifestHome = 'tree slot'
|
|
416
|
-
try { manifestText = read(join(treeSlotDir(base), 'hooks-manifest')) } catch { /* non-git / no store */ }
|
|
417
|
-
if (!manifestText) {
|
|
418
|
-
try { manifestText = read(join(runtimeRoot(base), 'hooks-manifest')); manifestHome = 'legacy global file (pre-slot materialize — re-run `spex materialize`)' } catch { /* neither */ }
|
|
419
|
-
}
|
|
420
|
-
if (!manifestText) {
|
|
421
|
-
line('manifest', 'MISSING from the global store — materialize never ran (hooks fire but find no manifest)')
|
|
422
|
-
} else {
|
|
423
|
-
const scripts = manifestScripts(manifestText)
|
|
424
|
-
const missing = scripts.filter((s) => !existsSync(join(base, s)))
|
|
425
|
-
line('manifest', `${scripts.length} handler(s) in the ${manifestHome}`)
|
|
426
|
-
line('handlers', missing.length === 0 ? 'all readable in the worktree' : `${missing.length} MISSING in the worktree → those hooks SILENTLY NO-OP:`)
|
|
427
|
-
for (const m of missing) L.push(` ✗ ${m}`)
|
|
428
|
-
}
|
|
429
|
-
// codex trust
|
|
430
|
-
const trustPresent = codexCfg.includes(`# spexcode:trust:${base}`)
|
|
431
|
-
line('codex trust', trustPresent ? 'trusted_hash block present in ~/.codex/config.toml' : 'absent (codex self-launch would prompt for trust)')
|
|
432
|
-
|
|
433
|
-
// --- backend: orchestration; absent is normal for BYOA ---
|
|
434
|
-
const { base: backendBase, up } = await backendReachable()
|
|
435
|
-
L.push('\nLayer 4 — session orchestration (backend-only: dispatch · queue · comms)')
|
|
436
|
-
line('backend', up ? `reachable at ${backendBase}` : `not reachable at ${backendBase}`)
|
|
437
|
-
|
|
438
|
-
// --- double-delivery: the same agent reached through two discovery channels (loose + a plugin bundle) ---
|
|
439
|
-
const dd = await doubleDeliveryReport(base)
|
|
440
|
-
L.push('\nLayer 5 — double-delivery (one harness, two discovery channels)')
|
|
441
|
-
L.push(...dd.lines)
|
|
442
|
-
|
|
443
|
-
// --- verdict ---
|
|
444
|
-
L.push('\nCoverage verdict')
|
|
445
|
-
line('preconditions', resolveOnPath('spex') ? 'spex resolves' : 'BLOCKED — spex not on PATH (fix first)')
|
|
446
|
-
line('layer 1', preCommit === 'spexcode' ? 'ENFORCED' : preCommit === 'stale' ? 'ENFORCED (older — update with `spex init`)' : preCommit === 'foreign' ? 'CONFLICT (a non-SpexCode hook holds the slot)' : 'ABSENT')
|
|
447
|
-
line('layer 2', body.length === 0 ? 'ABSENT (no contract)' : 'see per-harness above')
|
|
448
|
-
line('layer 3', manifestText ? 'see handler-existence above' : 'ABSENT (no manifest — agent ungoverned)')
|
|
449
|
-
line('layer 4', up ? 'present' : managed ? 'EXPECTED but backend down' : 'absent (normal for bring-your-own-agent)')
|
|
450
|
-
line('layer 5', dd.conflict ? 'CONFLICT (double-delivery — see Layer 5; `spex doctor --conflicts`)' : 'clean (single channel)')
|
|
451
|
-
|
|
452
|
-
// --- footprint: every artifact Spex wrote here, + any slot held by something not ours ---
|
|
453
|
-
L.push('\nFootprint (what Spex wrote into this environment)')
|
|
454
|
-
const foot: string[] = []
|
|
455
|
-
const ours = (s: HookState) => s === 'spexcode' || s === 'stale'
|
|
456
|
-
if (ours(preCommit)) foot.push(`${join(hooksDir!, 'pre-commit')} (SpexCode${preCommit === 'stale' ? ', outdated' : ''})`)
|
|
457
|
-
if (ours(prepare)) foot.push(`${join(hooksDir!, 'prepare-commit-msg')} (SpexCode${prepare === 'stale' ? ', outdated' : ''})`)
|
|
458
|
-
for (const h of HARNESSES) {
|
|
459
|
-
if (/dispatch\.sh/.test(read(h.shimFile(base)))) foot.push(`${h.shimFile(base)} (${h.id} hook shim)`)
|
|
460
|
-
for (const f of h.contractFiles(base)) if (/<!--\s*spexcode:start\s*-->/.test(read(f))) foot.push(`${f} (${h.id} contract block)`)
|
|
461
|
-
}
|
|
462
|
-
if (trustPresent) foot.push(`${join(codexHome, 'config.toml')} (codex trust block)`)
|
|
463
|
-
L.push(foot.length ? foot.map((f) => ` ${f}`).join('\n') : ' (nothing — Spex has written no files into this environment)')
|
|
464
|
-
const collisions = [preCommit, prepare].filter((s) => s === 'foreign').length
|
|
465
|
-
line('\ncollisions', collisions ? `${collisions} hook slot(s) held by a non-SpexCode hook` : 'none')
|
|
466
|
-
|
|
467
|
-
console.log(L.join('\n'))
|
|
468
|
-
return 0
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// print the layer-2 contract so any agent/harness can be handed exactly what materialize folds in.
|
|
472
|
-
function contract(): number {
|
|
473
|
-
const { body } = contractText()
|
|
474
|
-
if (!body) { console.error('spex doctor: no surface:system nodes in this .spec tree — the contract is empty.'); return 0 }
|
|
475
|
-
console.log(body)
|
|
476
|
-
return 0
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// the focused double-delivery check: JUST Layer 5, exit non-zero when a conflict is live so it gates a script
|
|
480
|
-
// / eval. Anchors at the repo root like doctor (the shims + contract + skills live there).
|
|
481
|
-
async function conflicts(): Promise<number> {
|
|
482
|
-
const cwd = process.cwd()
|
|
483
|
-
const base = repoRoot(cwd) ?? cwd
|
|
484
|
-
const { lines, conflict } = await doubleDeliveryReport(base)
|
|
485
|
-
console.log(['spex doctor --conflicts — does SpexCode reach this agent through more than one discovery channel?\n', ...lines].join('\n'))
|
|
486
|
-
return conflict ? 1 : 0
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function migrationRemoved(): number {
|
|
490
|
-
console.error('spex: `spex doctor --migrate` was removed in v0.4.0 — migrate this tree with a 0.3.x SpexCode release, then reinstall the current release. Nothing was changed.')
|
|
491
|
-
return 2
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
function usage(): number {
|
|
495
|
-
console.error(`spex doctor — diagnose spec health and how the SpexCode workflow reaches your agent
|
|
496
|
-
(bare) spec-health findings + delivery report: preconditions · git-hook floor · contract · hooks(+handlers) · backend · footprint
|
|
497
|
-
--contract print the surface:system contract text (hand it to any agent)
|
|
498
|
-
--conflicts detect double-delivery — the same agent reached via loose native delivery AND a plugin bundle (exits non-zero on conflict)`)
|
|
499
|
-
return 0
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
export async function runDoctor(args: string[]): Promise<number> {
|
|
503
|
-
// contract/conflicts are FLAGS, not subcommands ([[cli-surface]] §4: another representation of the same
|
|
504
|
-
// diagnosis read, not a distinct action). The old positional spellings signpost — report, never run.
|
|
505
|
-
if (args.includes('--migrate')) return migrationRemoved()
|
|
506
|
-
if (args.includes('--contract')) return contract()
|
|
507
|
-
if (args.includes('--conflicts')) return await conflicts()
|
|
508
|
-
switch (args[0]) {
|
|
509
|
-
case undefined: return await doctor()
|
|
510
|
-
case 'contract': case 'conflicts':
|
|
511
|
-
console.error(`spex: \`spex doctor ${args[0]}\` was removed in v0.3.0 — use: spex doctor --${args[0]}`)
|
|
512
|
-
return 2
|
|
513
|
-
case 'help': case '--help': case '-h': return usage()
|
|
514
|
-
default: console.error(`spex doctor: unknown subcommand "${args[0]}"`); usage(); return 2
|
|
515
|
-
}
|
|
516
|
-
}
|