spexcode 0.6.1 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -149
- package/bin/spex.mjs +4 -0
- package/node_modules/@spexcode/spec-cli/bin/spex.mjs +76 -0
- package/node_modules/@spexcode/spec-cli/dist/attach.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/attach.js +48 -0
- package/node_modules/@spexcode/spec-cli/dist/claude-headless.d.ts +33 -0
- package/node_modules/@spexcode/spec-cli/dist/claude-headless.js +307 -0
- package/node_modules/@spexcode/spec-cli/dist/cli.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/cli.js +1859 -0
- package/node_modules/@spexcode/spec-cli/dist/client.d.ts +111 -0
- package/node_modules/@spexcode/spec-cli/dist/client.js +476 -0
- package/node_modules/@spexcode/spec-cli/dist/cockpit.d.ts +7 -0
- package/node_modules/@spexcode/spec-cli/dist/cockpit.js +26 -0
- package/node_modules/@spexcode/spec-cli/dist/codex-headless.d.ts +1 -0
- package/{spec-cli/src/codex-headless.ts → node_modules/@spexcode/spec-cli/dist/codex-headless.js} +3 -9
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +53 -0
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +686 -0
- package/node_modules/@spexcode/spec-cli/dist/commit-surgery.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/commit-surgery.js +90 -0
- package/node_modules/@spexcode/spec-cli/dist/contract-filter.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/contract-filter.js +219 -0
- package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/dashboard-assets.js +50 -0
- package/node_modules/@spexcode/spec-cli/dist/delivery-queue.d.ts +23 -0
- package/node_modules/@spexcode/spec-cli/dist/delivery-queue.js +179 -0
- package/node_modules/@spexcode/spec-cli/dist/doctor.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/doctor.js +547 -0
- package/node_modules/@spexcode/spec-cli/dist/eval-host.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/eval-host.js +19 -0
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +26 -0
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +475 -0
- package/node_modules/@spexcode/spec-cli/dist/file-write.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/file-write.js +26 -0
- package/node_modules/@spexcode/spec-cli/dist/flat.d.ts +89 -0
- package/node_modules/@spexcode/spec-cli/dist/flat.js +1076 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-auth.d.ts +56 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-auth.js +168 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-hub.d.ts +28 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +361 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway.d.ts +37 -0
- package/node_modules/@spexcode/spec-cli/dist/gateway.js +503 -0
- package/node_modules/@spexcode/spec-cli/dist/graphCache.d.ts +27 -0
- package/node_modules/@spexcode/spec-cli/dist/graphCache.js +716 -0
- package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +35 -0
- package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +85 -0
- package/node_modules/@spexcode/spec-cli/dist/graphStream.js +1195 -0
- package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.d.ts +64 -0
- package/node_modules/@spexcode/spec-cli/dist/guidance-catalog.js +166 -0
- package/node_modules/@spexcode/spec-cli/dist/guide.d.ts +8 -0
- package/{spec-cli/src/guide.ts → node_modules/@spexcode/spec-cli/dist/guide.js} +115 -22
- package/node_modules/@spexcode/spec-cli/dist/harness-select.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/harness-select.js +62 -0
- package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +325 -0
- package/node_modules/@spexcode/spec-cli/dist/harness.js +3026 -0
- package/node_modules/@spexcode/spec-cli/dist/headless-controller.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/headless-controller.js +47 -0
- package/node_modules/@spexcode/spec-cli/dist/help.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/help.js +574 -0
- package/node_modules/@spexcode/spec-cli/dist/hook-prompts.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +122 -0
- package/node_modules/@spexcode/spec-cli/dist/hooks.d.ts +1 -0
- package/{spec-cli/src/hooks.ts → node_modules/@spexcode/spec-cli/dist/hooks.js} +15 -15
- package/node_modules/@spexcode/spec-cli/dist/host-resources.d.ts +97 -0
- package/node_modules/@spexcode/spec-cli/dist/host-resources.js +795 -0
- package/node_modules/@spexcode/spec-cli/dist/host.d.ts +88 -0
- package/node_modules/@spexcode/spec-cli/dist/host.js +663 -0
- package/node_modules/@spexcode/spec-cli/dist/index.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/index.js +899 -0
- package/node_modules/@spexcode/spec-cli/dist/init.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/init.js +284 -0
- package/node_modules/@spexcode/spec-cli/dist/issues-cli.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/issues-cli.js +334 -0
- package/node_modules/@spexcode/spec-cli/dist/issues.d.ts +69 -0
- package/node_modules/@spexcode/spec-cli/dist/issues.js +189 -0
- package/node_modules/@spexcode/spec-cli/dist/lint.d.ts +34 -0
- package/node_modules/@spexcode/spec-cli/dist/lint.js +476 -0
- package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +7 -0
- package/{spec-cli/src/listen.ts → node_modules/@spexcode/spec-cli/dist/listen.js} +13 -17
- package/node_modules/@spexcode/spec-cli/dist/localIssues.d.ts +65 -0
- package/node_modules/@spexcode/spec-cli/dist/localIssues.js +582 -0
- package/node_modules/@spexcode/spec-cli/dist/login-page.d.ts +5 -0
- package/{spec-cli/src/login-page.ts → node_modules/@spexcode/spec-cli/dist/login-page.js} +7 -7
- package/node_modules/@spexcode/spec-cli/dist/loop-in.d.ts +23 -0
- package/{spec-cli/src/loop-in.ts → node_modules/@spexcode/spec-cli/dist/loop-in.js} +35 -45
- package/node_modules/@spexcode/spec-cli/dist/machine-peer.d.ts +69 -0
- package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +603 -0
- package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +18 -0
- package/node_modules/@spexcode/spec-cli/dist/materialize.js +468 -0
- package/node_modules/@spexcode/spec-cli/dist/mentions.d.ts +50 -0
- package/node_modules/@spexcode/spec-cli/dist/mentions.js +117 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode-headless.d.ts +4 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode-headless.js +208 -0
- package/node_modules/@spexcode/spec-cli/dist/opencode.d.ts +3 -0
- package/{spec-cli/src/opencode.ts → node_modules/@spexcode/spec-cli/dist/opencode.js} +9 -13
- package/node_modules/@spexcode/spec-cli/dist/pi-harness.d.ts +3 -0
- package/{spec-cli/src/pi-harness.ts → node_modules/@spexcode/spec-cli/dist/pi-harness.js} +45 -40
- package/node_modules/@spexcode/spec-cli/dist/pi-headless.d.ts +25 -0
- package/node_modules/@spexcode/spec-cli/dist/pi-headless.js +196 -0
- package/node_modules/@spexcode/spec-cli/dist/plugin-harness.d.ts +21 -0
- package/node_modules/@spexcode/spec-cli/dist/plugin-harness.js +146 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-bridge.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +404 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-helper.d.mts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-helper.mjs +101 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.d.mts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/pty-native-helper.mjs +23 -0
- package/node_modules/@spexcode/spec-cli/dist/public-graph.d.ts +46 -0
- package/node_modules/@spexcode/spec-cli/dist/public-graph.js +56 -0
- package/node_modules/@spexcode/spec-cli/dist/ranker.d.ts +15 -0
- package/node_modules/@spexcode/spec-cli/dist/ranker.js +188 -0
- package/node_modules/@spexcode/spec-cli/dist/reaper.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/reaper.js +81 -0
- package/node_modules/@spexcode/spec-cli/dist/reviews.d.ts +189 -0
- package/node_modules/@spexcode/spec-cli/dist/reviews.js +350 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-guard.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-guard.js +37 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +21 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +84 -0
- package/node_modules/@spexcode/spec-cli/dist/search.bench.d.mts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/search.bench.mjs +75 -0
- package/node_modules/@spexcode/spec-cli/dist/search.d.ts +20 -0
- package/node_modules/@spexcode/spec-cli/dist/search.js +61 -0
- package/node_modules/@spexcode/spec-cli/dist/session-cursors.d.ts +14 -0
- package/node_modules/@spexcode/spec-cli/dist/session-cursors.js +82 -0
- package/node_modules/@spexcode/spec-cli/dist/session-declarations.d.ts +13 -0
- package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +126 -0
- package/node_modules/@spexcode/spec-cli/dist/session-execution.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/session-execution.js +70 -0
- package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +28 -0
- package/node_modules/@spexcode/spec-cli/dist/session-files.js +130 -0
- package/node_modules/@spexcode/spec-cli/dist/session-follow.d.ts +31 -0
- package/node_modules/@spexcode/spec-cli/dist/session-follow.js +179 -0
- package/node_modules/@spexcode/spec-cli/dist/session-reparent.d.ts +10 -0
- package/node_modules/@spexcode/spec-cli/dist/session-reparent.js +33 -0
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +51 -0
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +233 -0
- package/node_modules/@spexcode/spec-cli/dist/session-web.d.ts +24 -0
- package/node_modules/@spexcode/spec-cli/dist/session-web.js +141 -0
- package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +412 -0
- package/node_modules/@spexcode/spec-cli/dist/sessions.js +4239 -0
- package/node_modules/@spexcode/spec-cli/dist/sh.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/sh.js +3 -0
- package/node_modules/@spexcode/spec-cli/dist/shim-runtime.d.ts +1 -0
- package/{spec-cli/src/shim-runtime.ts → node_modules/@spexcode/spec-cli/dist/shim-runtime.js} +3 -4
- package/node_modules/@spexcode/spec-cli/dist/slash-commands.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/slash-commands.js +307 -0
- package/node_modules/@spexcode/spec-cli/dist/source-files.d.ts +9 -0
- package/node_modules/@spexcode/spec-cli/dist/source-files.js +113 -0
- package/node_modules/@spexcode/spec-cli/dist/supervise.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/supervise.js +308 -0
- package/node_modules/@spexcode/spec-cli/dist/tree.d.ts +25 -0
- package/node_modules/@spexcode/spec-cli/dist/tree.js +98 -0
- package/node_modules/@spexcode/spec-cli/dist/tsx-bin.d.ts +3 -0
- package/node_modules/@spexcode/spec-cli/dist/tsx-bin.js +41 -0
- package/node_modules/@spexcode/spec-cli/dist/uninstall.d.ts +3 -0
- package/node_modules/@spexcode/spec-cli/dist/uninstall.js +177 -0
- package/node_modules/@spexcode/spec-cli/dist/uploads.d.ts +36 -0
- package/node_modules/@spexcode/spec-cli/dist/uploads.js +243 -0
- package/node_modules/@spexcode/spec-cli/dist/worktree-sources.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/worktree-sources.js +47 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/dispatch.sh +5 -5
- package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/harness.sh +27 -7
- package/node_modules/@spexcode/spec-cli/package.json +47 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +8 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/comment-altitude/spec.md +9 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec-first.sh +1 -1
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec-of-file.sh +1 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/spec.md +22 -0
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/spec.md +2 -2
- package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +31 -29
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +15 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/forge-link/spec.md +10 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +13 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +12 -0
- package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +94 -0
- package/node_modules/@spexcode/spec-core/dist/anchors.js +730 -0
- package/node_modules/@spexcode/spec-core/dist/git.d.ts +166 -0
- package/node_modules/@spexcode/spec-core/dist/git.js +2736 -0
- package/node_modules/@spexcode/spec-core/dist/graph-delta.d.ts +44 -0
- package/node_modules/@spexcode/spec-core/dist/graph-delta.js +72 -0
- package/node_modules/@spexcode/spec-core/dist/graph.d.ts +34 -0
- package/node_modules/@spexcode/spec-core/dist/graph.js +237 -0
- package/node_modules/@spexcode/spec-core/dist/graphDelta.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/graphDelta.js +13 -0
- package/node_modules/@spexcode/spec-core/dist/harness-identity.d.ts +31 -0
- package/node_modules/@spexcode/spec-core/dist/harness-identity.js +20 -0
- package/node_modules/@spexcode/spec-core/dist/identity-presets.d.ts +152 -0
- package/node_modules/@spexcode/spec-core/dist/identity-presets.js +132 -0
- package/node_modules/@spexcode/spec-core/dist/index.d.ts +45 -0
- package/node_modules/@spexcode/spec-core/dist/index.js +19 -0
- package/node_modules/@spexcode/spec-core/dist/layout.d.ts +183 -0
- package/node_modules/@spexcode/spec-core/dist/layout.js +548 -0
- package/node_modules/@spexcode/spec-core/dist/process-identity.d.ts +37 -0
- package/node_modules/@spexcode/spec-core/dist/process-identity.js +214 -0
- package/node_modules/@spexcode/spec-core/dist/project-identity.d.ts +12 -0
- package/node_modules/@spexcode/spec-core/dist/project-identity.js +71 -0
- package/node_modules/@spexcode/spec-core/dist/project-store.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/project-store.js +14 -0
- package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +2 -0
- package/node_modules/@spexcode/spec-core/dist/resilience.js +40 -0
- package/node_modules/@spexcode/spec-core/dist/review/index.d.ts +3 -0
- package/node_modules/@spexcode/spec-core/dist/review/index.js +5 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +77 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +308 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +66 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +180 -0
- package/node_modules/@spexcode/spec-core/dist/review/session.d.ts +4 -0
- package/node_modules/@spexcode/spec-core/dist/review/session.js +8 -0
- package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.d.ts +15 -0
- package/node_modules/@spexcode/spec-core/dist/reviewSnapshot.js +12 -0
- package/node_modules/@spexcode/spec-core/dist/root-lru.d.ts +4 -0
- package/{spec-cli/src/root-lru.ts → node_modules/@spexcode/spec-core/dist/root-lru.js} +26 -30
- package/node_modules/@spexcode/spec-core/dist/specs.d.ts +117 -0
- package/node_modules/@spexcode/spec-core/dist/specs.js +489 -0
- package/node_modules/@spexcode/spec-core/package.json +28 -0
- package/node_modules/@spexcode/spec-eval/dist/cache.d.ts +10 -0
- package/node_modules/@spexcode/spec-eval/dist/cache.js +50 -0
- package/node_modules/@spexcode/spec-eval/dist/cli.d.ts +22 -0
- package/node_modules/@spexcode/spec-eval/dist/cli.js +981 -0
- package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +98 -0
- package/node_modules/@spexcode/spec-eval/dist/evaltab.js +176 -0
- package/node_modules/@spexcode/spec-eval/dist/filing.d.ts +16 -0
- package/node_modules/@spexcode/spec-eval/dist/filing.js +36 -0
- package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +48 -0
- package/node_modules/@spexcode/spec-eval/dist/freshness.js +799 -0
- package/node_modules/@spexcode/spec-eval/dist/host.d.ts +46 -0
- package/node_modules/@spexcode/spec-eval/dist/host.js +53 -0
- package/node_modules/@spexcode/spec-eval/dist/humanok.d.ts +11 -0
- package/node_modules/@spexcode/spec-eval/dist/humanok.js +27 -0
- package/node_modules/@spexcode/spec-eval/dist/index.d.ts +4 -0
- package/node_modules/@spexcode/spec-eval/dist/index.js +4 -0
- package/node_modules/@spexcode/spec-eval/dist/remarks.d.ts +31 -0
- package/node_modules/@spexcode/spec-eval/dist/remarks.js +1 -0
- package/node_modules/@spexcode/spec-eval/dist/scenariofresh.d.ts +13 -0
- package/node_modules/@spexcode/spec-eval/dist/scenariofresh.js +303 -0
- package/node_modules/@spexcode/spec-eval/dist/scenarios.d.ts +99 -0
- package/node_modules/@spexcode/spec-eval/dist/scenarios.js +650 -0
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +307 -0
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +1883 -0
- package/node_modules/@spexcode/spec-eval/dist/sidecar.d.ts +55 -0
- package/node_modules/@spexcode/spec-eval/dist/sidecar.js +82 -0
- package/node_modules/@spexcode/spec-eval/dist/timeline.d.ts +25 -0
- package/node_modules/@spexcode/spec-eval/dist/timeline.js +65 -0
- package/node_modules/@spexcode/spec-eval/dist/ui-path.d.ts +1 -0
- package/node_modules/@spexcode/spec-eval/dist/ui-path.js +2 -0
- package/node_modules/@spexcode/spec-eval/package.json +36 -0
- package/node_modules/@spexcode/spec-forge/dist/cache.d.ts +29 -0
- package/node_modules/@spexcode/spec-forge/dist/cache.js +59 -0
- package/node_modules/@spexcode/spec-forge/dist/cli.d.ts +1 -0
- package/node_modules/@spexcode/spec-forge/dist/cli.js +100 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/github.d.ts +2 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/github.js +116 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.d.ts +7 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers/gitlab.js +144 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers.d.ts +10 -0
- package/node_modules/@spexcode/spec-forge/dist/drivers.js +83 -0
- package/node_modules/@spexcode/spec-forge/dist/index.d.ts +4 -0
- package/node_modules/@spexcode/spec-forge/dist/index.js +4 -0
- package/node_modules/@spexcode/spec-forge/dist/links.d.ts +15 -0
- package/node_modules/@spexcode/spec-forge/dist/links.js +65 -0
- package/node_modules/@spexcode/spec-forge/dist/needs-eval.d.ts +10 -0
- package/node_modules/@spexcode/spec-forge/dist/needs-eval.js +20 -0
- package/node_modules/@spexcode/spec-forge/dist/port.d.ts +53 -0
- package/node_modules/@spexcode/spec-forge/dist/port.js +1 -0
- package/node_modules/@spexcode/spec-forge/dist/resident.d.ts +7 -0
- package/node_modules/@spexcode/spec-forge/dist/resident.js +43 -0
- package/node_modules/@spexcode/spec-forge/package.json +32 -0
- package/package.json +33 -19
- package/spec-cli/bin/spex.mjs +0 -69
- package/spec-cli/src/anchors.ts +0 -706
- package/spec-cli/src/attach.ts +0 -43
- package/spec-cli/src/claude-headless.ts +0 -321
- package/spec-cli/src/cli.ts +0 -1115
- package/spec-cli/src/client.ts +0 -315
- package/spec-cli/src/cockpit.ts +0 -43
- package/spec-cli/src/codex-runtime-generations.ts +0 -646
- package/spec-cli/src/commit-surgery.ts +0 -68
- package/spec-cli/src/contract-filter.ts +0 -202
- package/spec-cli/src/delivery-queue.ts +0 -107
- package/spec-cli/src/doctor.ts +0 -516
- package/spec-cli/src/file-write.ts +0 -22
- package/spec-cli/src/gateway-auth.ts +0 -181
- package/spec-cli/src/gateway-hub.ts +0 -356
- package/spec-cli/src/gateway.ts +0 -368
- package/spec-cli/src/git.ts +0 -2180
- package/spec-cli/src/graph.ts +0 -221
- package/spec-cli/src/graphCache.ts +0 -641
- package/spec-cli/src/graphDelta.ts +0 -77
- package/spec-cli/src/graphStream.ts +0 -1019
- package/spec-cli/src/harness-select.ts +0 -77
- package/spec-cli/src/harness.ts +0 -2747
- package/spec-cli/src/headless-controller.ts +0 -63
- package/spec-cli/src/help.ts +0 -480
- package/spec-cli/src/host-resources.ts +0 -707
- package/spec-cli/src/host.ts +0 -611
- package/spec-cli/src/identity-presets.d.ts +0 -13
- package/spec-cli/src/identity-presets.js +0 -128
- package/spec-cli/src/index.ts +0 -730
- package/spec-cli/src/init.ts +0 -258
- package/spec-cli/src/issues-cli.ts +0 -276
- package/spec-cli/src/issues.ts +0 -237
- package/spec-cli/src/layout.ts +0 -557
- package/spec-cli/src/lint.ts +0 -458
- package/spec-cli/src/localIssues.ts +0 -538
- package/spec-cli/src/materialize.ts +0 -412
- package/spec-cli/src/mentions.ts +0 -81
- package/spec-cli/src/opencode-headless.ts +0 -205
- package/spec-cli/src/pi-headless.ts +0 -153
- package/spec-cli/src/plugin-harness.ts +0 -157
- package/spec-cli/src/process-identity.ts +0 -207
- package/spec-cli/src/project-identity.ts +0 -73
- package/spec-cli/src/project-store.ts +0 -17
- package/spec-cli/src/pty-bridge.ts +0 -396
- package/spec-cli/src/pty-helper.mjs +0 -96
- package/spec-cli/src/pty-native-helper.mjs +0 -22
- package/spec-cli/src/ranker.ts +0 -191
- package/spec-cli/src/reaper.ts +0 -125
- package/spec-cli/src/resilience.ts +0 -41
- package/spec-cli/src/reviewSnapshot.ts +0 -28
- package/spec-cli/src/reviews.ts +0 -405
- package/spec-cli/src/runtime-guard.ts +0 -35
- package/spec-cli/src/runtime-ownership.ts +0 -88
- package/spec-cli/src/search.bench.mjs +0 -80
- package/spec-cli/src/search.ts +0 -63
- package/spec-cli/src/session-cursors.ts +0 -91
- package/spec-cli/src/session-follow.ts +0 -182
- package/spec-cli/src/session-timeline.ts +0 -98
- package/spec-cli/src/sessions.ts +0 -3403
- package/spec-cli/src/sh.ts +0 -3
- package/spec-cli/src/slash-commands.ts +0 -318
- package/spec-cli/src/source-files.ts +0 -104
- package/spec-cli/src/specs.ts +0 -498
- package/spec-cli/src/supervise.ts +0 -245
- package/spec-cli/src/tree.ts +0 -118
- package/spec-cli/src/tsx-bin.ts +0 -18
- package/spec-cli/src/uninstall.ts +0 -171
- package/spec-cli/src/uploads.ts +0 -253
- package/spec-cli/src/worktree-sources.ts +0 -42
- package/spec-cli/templates/spec/project/.plugins/commands/supervisor/spec.md +0 -8
- package/spec-cli/templates/spec/project/.plugins/core/comment-altitude/spec.md +0 -28
- package/spec-cli/templates/spec/project/.plugins/core/spec.md +0 -23
- package/spec-cli/templates/spec/project/.plugins/prompts/atomic-landing/spec.md +0 -32
- package/spec-cli/templates/spec/project/.plugins/prompts/forge-link/spec.md +0 -9
- package/spec-cli/templates/spec/project/.plugins/prompts/memory-hygiene/spec.md +0 -15
- package/spec-cli/templates/spec/project/.plugins/prompts/reproduce-before-fix/spec.md +0 -18
- package/spec-dashboard/dist/assets/App-F9uaAVcH.js +0 -2
- package/spec-dashboard/dist/assets/Dashboard-Ba_jhxp1.js +0 -27
- package/spec-dashboard/dist/assets/Dashboard-C5ap-Sga.css +0 -1
- package/spec-dashboard/dist/assets/EvalsPage-FixoOg_n.js +0 -2
- package/spec-dashboard/dist/assets/IssuesPage-CuKLFhH3.js +0 -1
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/spec-dashboard/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/spec-dashboard/dist/assets/MobileApp-CHgEHORJ.js +0 -2
- package/spec-dashboard/dist/assets/Modal-CQgYymmr.js +0 -1
- package/spec-dashboard/dist/assets/PageScroll-hT7UTLvD.js +0 -1
- package/spec-dashboard/dist/assets/ProjectsPage-CtXxakF9.js +0 -1
- package/spec-dashboard/dist/assets/SessionInterface-6GBZ9nXN.css +0 -32
- package/spec-dashboard/dist/assets/SessionInterface-Bpie-9fs.js +0 -39
- package/spec-dashboard/dist/assets/SessionWindow-CixDi4PI.js +0 -1
- package/spec-dashboard/dist/assets/Settings-C2MsucfE.js +0 -1
- package/spec-dashboard/dist/assets/Thread-C6Go8HRh.js +0 -13
- package/spec-dashboard/dist/assets/bindings-BC9vqpYU.js +0 -1
- package/spec-dashboard/dist/assets/data-B-RQmit6.js +0 -1
- package/spec-dashboard/dist/assets/index-CsI8DElI.css +0 -1
- package/spec-dashboard/dist/assets/index-DrVao0Ep.js +0 -41
- package/spec-dashboard/dist/assets/launch-BBH02b1v.js +0 -276
- package/spec-dashboard/dist/assets/launch-Cp579UoJ.css +0 -1
- package/spec-dashboard/dist/assets/terminalFont-C6oGNKrf.js +0 -1
- package/spec-dashboard/dist/index.html +0 -29
- package/spec-dashboard/src/reviewFilters.js +0 -322
- package/spec-dashboard/src/reviewQuery.js +0 -174
- package/spec-dashboard/src/session.js +0 -199
- package/spec-eval/src/cache.ts +0 -57
- package/spec-eval/src/cli.ts +0 -889
- package/spec-eval/src/evaltab.ts +0 -262
- package/spec-eval/src/filing.ts +0 -39
- package/spec-eval/src/freshness.ts +0 -557
- package/spec-eval/src/humanok.ts +0 -28
- package/spec-eval/src/scenariofresh.ts +0 -244
- package/spec-eval/src/scenarios.ts +0 -672
- package/spec-eval/src/sessioneval.ts +0 -2226
- package/spec-eval/src/sidecar.ts +0 -93
- package/spec-eval/src/timeline.ts +0 -54
- package/spec-forge/src/__fixtures__/github-forge.json +0 -63
- package/spec-forge/src/cache.ts +0 -66
- package/spec-forge/src/cli.ts +0 -99
- package/spec-forge/src/drivers/github.ts +0 -132
- package/spec-forge/src/drivers/gitlab.ts +0 -153
- package/spec-forge/src/drivers.ts +0 -73
- package/spec-forge/src/links.ts +0 -72
- package/spec-forge/src/needs-eval.ts +0 -29
- package/spec-forge/src/port.ts +0 -36
- package/spec-forge/src/resident.ts +0 -46
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/README.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/hooks/compat/mark-active-sed-v0.fixture +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/commit-msg +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-checkout +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/post-merge +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/pre-commit +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/prepare-commit-msg +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/hooks/reference-transaction +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/extract/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/regroup/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/rename/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/commands/tidy/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/idle.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/idle/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/mark-active.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/mark-active/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/fail.sh +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/session-fail/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-first/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/core/spec-of-file/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/prompts/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/digest.mjs +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/distill/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/skills/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/.plugins/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-cli}/templates/spec/project/spec.md +0 -0
- /package/{spec-cli → node_modules/@spexcode/spec-core}/templates/spexcode.json +0 -0
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { gitRequiredA, gitObjectFormat, isGitObjectId, batchRevisionOids, batchBlobTexts, combinedDiffOwnedChanges, driftPathWindow, readImmutableHunkFacts, persistImmutableHunkFacts, withEventLedgerBuild } from './git.js';
|
|
4
|
+
const RS = '\x1e';
|
|
5
|
+
export function parseCodeEntry(raw) {
|
|
6
|
+
const i = raw.indexOf('#');
|
|
7
|
+
if (i < 0)
|
|
8
|
+
return { path: raw.trim(), anchor: null };
|
|
9
|
+
return { path: raw.slice(0, i).trim(), anchor: raw.slice(i + 1).trim() || null };
|
|
10
|
+
}
|
|
11
|
+
export function relationClaimsPath(claim, file) {
|
|
12
|
+
if (claim === file)
|
|
13
|
+
return true;
|
|
14
|
+
if (file.startsWith(claim.replace(/\/+$/, '') + '/'))
|
|
15
|
+
return true;
|
|
16
|
+
if (!claim.includes('*'))
|
|
17
|
+
return false;
|
|
18
|
+
const pattern = claim.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*');
|
|
19
|
+
return new RegExp(`^${pattern}$`).test(file);
|
|
20
|
+
}
|
|
21
|
+
export function parseRelation(raws, relation) {
|
|
22
|
+
const order = [];
|
|
23
|
+
const byPath = new Map();
|
|
24
|
+
const problems = [];
|
|
25
|
+
for (const raw of raws) {
|
|
26
|
+
const { path, anchor } = parseCodeEntry(raw);
|
|
27
|
+
let e = byPath.get(path);
|
|
28
|
+
if (!e) {
|
|
29
|
+
e = { bare: false, selectors: [] };
|
|
30
|
+
byPath.set(path, e);
|
|
31
|
+
order.push(path);
|
|
32
|
+
}
|
|
33
|
+
if (anchor === null) {
|
|
34
|
+
if (e.bare)
|
|
35
|
+
problems.push(`${relation}: lists '${path}' twice — drop the duplicate entry`);
|
|
36
|
+
e.bare = true;
|
|
37
|
+
}
|
|
38
|
+
else if (e.selectors.includes(anchor)) {
|
|
39
|
+
problems.push(`${relation}: lists selector '${path}#${anchor}' twice — drop the duplicate`);
|
|
40
|
+
}
|
|
41
|
+
else
|
|
42
|
+
e.selectors.push(anchor);
|
|
43
|
+
}
|
|
44
|
+
for (const path of order) {
|
|
45
|
+
const e = byPath.get(path);
|
|
46
|
+
if (e.bare && e.selectors.length)
|
|
47
|
+
problems.push(`${relation}: mixes bare '${path}' with '${path}#…' selectors — one base path is either whole-file or selector-scoped, never both; drop one form`);
|
|
48
|
+
if (e.selectors.length && path.includes('*'))
|
|
49
|
+
problems.push(`${relation}: '${path}#${e.selectors[0]}' puts a selector on a glob — a selector scopes ONE real file`);
|
|
50
|
+
}
|
|
51
|
+
return { entries: order.map((p) => ({ path: p, selectors: byPath.get(p).selectors })), problems };
|
|
52
|
+
}
|
|
53
|
+
// ---- extractor: ts-ast (the designated extractor for the JS family) ----
|
|
54
|
+
// Parse-only via the HOST project's own typescript, so the parse matches what the project itself compiles
|
|
55
|
+
// with. If it cannot resolve, ready() returns a loud unverified verdict and lint skips these anchors
|
|
56
|
+
// without crashing (no bundled compiler, regex fallback, or fake pass for JS).
|
|
57
|
+
const JS_EXTS = new Set(['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs', 'mts', 'cts']);
|
|
58
|
+
const TS_AST_MEMO_SCHEMA = 'ts-ast-memo-v1';
|
|
59
|
+
export function tsAstExtractor(root) {
|
|
60
|
+
let ts; // undefined = unprobed; null = unresolvable
|
|
61
|
+
let tsModulePath = '';
|
|
62
|
+
let readiness;
|
|
63
|
+
const probe = () => {
|
|
64
|
+
if (ts !== undefined)
|
|
65
|
+
return;
|
|
66
|
+
try {
|
|
67
|
+
const require = createRequire(join(root, 'package.json'));
|
|
68
|
+
tsModulePath = require.resolve('typescript');
|
|
69
|
+
ts = require('typescript');
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
ts = null;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
id: 'ts-ast',
|
|
77
|
+
claims: (ext) => JS_EXTS.has(ext),
|
|
78
|
+
ready() {
|
|
79
|
+
if (readiness !== undefined)
|
|
80
|
+
return readiness;
|
|
81
|
+
probe();
|
|
82
|
+
if (!ts)
|
|
83
|
+
return (readiness = `typescript is not resolvable from the governed repository (${root}) — JS-family anchors were skipped and remain unverified; run 'npm i -D typescript@5', or remove the #anchor`);
|
|
84
|
+
// resolvability is not usability: typescript@7 (the Go rewrite) may resolve yet not expose the JS
|
|
85
|
+
// compiler API this extractor drives. Probe the ACTUAL surface with a tiny parse. Once a candidate
|
|
86
|
+
// resolves, incompatibility is loud rather than silently changing parser versions.
|
|
87
|
+
try {
|
|
88
|
+
const sf = ts.createSourceFile('probe.ts', 'const x = 1', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
89
|
+
if (!sf?.statements?.length || sf.parseDiagnostics?.length)
|
|
90
|
+
throw new Error('probe parse failed');
|
|
91
|
+
readiness = true;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
readiness = `host typescript (v${ts?.version ?? 'unknown'}) resolves but its createSourceFile API is unusable (a TS7/Go build?) — pin 'npm i -D typescript@5', or remove the #anchor`;
|
|
95
|
+
}
|
|
96
|
+
return readiness;
|
|
97
|
+
},
|
|
98
|
+
extract(content, filename) {
|
|
99
|
+
probe();
|
|
100
|
+
if (!ts)
|
|
101
|
+
throw new Error('ts-ast extractor is not ready (typescript unresolvable)');
|
|
102
|
+
const kind = /\.(tsx)$/.test(filename) ? ts.ScriptKind.TSX
|
|
103
|
+
: /\.(jsx)$/.test(filename) ? ts.ScriptKind.JSX
|
|
104
|
+
: /\.(ts|mts|cts)$/.test(filename) ? ts.ScriptKind.TS
|
|
105
|
+
: ts.ScriptKind.JS;
|
|
106
|
+
const sf = ts.createSourceFile(filename, content, ts.ScriptTarget.Latest, false, kind);
|
|
107
|
+
// parse-only gate: a file that does not parse yields GARBAGE units (a shell script's `x=$(...)`
|
|
108
|
+
// parses as a const) — throw so the caller renders an honest "cannot parse" verdict instead.
|
|
109
|
+
if (sf.parseDiagnostics?.length)
|
|
110
|
+
throw new Error(`${filename} does not parse as ${ts.ScriptKind[kind]} (${sf.parseDiagnostics.length} syntax error(s))`);
|
|
111
|
+
const line = (pos) => sf.getLineAndCharacterOfPosition(pos).line + 1;
|
|
112
|
+
const units = [];
|
|
113
|
+
const push = (name, ukind, node, typeOnly = false) => units.push({ name, kind: ukind, start: line(node.getStart(sf)), end: line(node.end), ...(typeOnly ? { typeOnly } : {}) });
|
|
114
|
+
for (const st of sf.statements) {
|
|
115
|
+
if (ts.isFunctionDeclaration(st))
|
|
116
|
+
push(st.name ? st.name.text : '(default)', 'function', st);
|
|
117
|
+
else if (ts.isClassDeclaration(st)) {
|
|
118
|
+
const cname = st.name ? st.name.text : '(default)';
|
|
119
|
+
push(cname, 'class', st);
|
|
120
|
+
for (const m of st.members) {
|
|
121
|
+
if ((ts.isMethodDeclaration(m) || ts.isConstructorDeclaration(m) || ts.isGetAccessorDeclaration(m) || ts.isSetAccessorDeclaration(m)) && m.body) {
|
|
122
|
+
const mname = ts.isConstructorDeclaration(m) ? 'constructor' : (m.name && ts.isIdentifier(m.name) ? m.name.text : '(computed)');
|
|
123
|
+
push(`${cname}.${mname}`, 'method', m);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (ts.isVariableStatement(st)) {
|
|
128
|
+
for (const d of st.declarationList.declarations) {
|
|
129
|
+
if (!ts.isIdentifier(d.name))
|
|
130
|
+
continue; // destructuring — not anchorable by one name
|
|
131
|
+
const fn = d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer));
|
|
132
|
+
// range = the whole statement (multi-declarator lines co-move; each name shares the range)
|
|
133
|
+
units.push({ name: d.name.text, kind: fn ? 'const-fn' : 'const-data', start: line(st.getStart(sf)), end: line(st.end) });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (ts.isEnumDeclaration(st))
|
|
137
|
+
push(st.name.text, 'enum', st);
|
|
138
|
+
else if (ts.isInterfaceDeclaration(st))
|
|
139
|
+
push(st.name.text, 'interface', st, true);
|
|
140
|
+
else if (ts.isTypeAliasDeclaration(st))
|
|
141
|
+
push(st.name.text, 'type', st, true);
|
|
142
|
+
}
|
|
143
|
+
return units;
|
|
144
|
+
},
|
|
145
|
+
memoKey(filename) {
|
|
146
|
+
probe();
|
|
147
|
+
const host = ts ? `${tsModulePath}\0${ts.version ?? 'unknown'}` : `unresolved\0${root}`;
|
|
148
|
+
return `${TS_AST_MEMO_SCHEMA}\0${host}\0target:Latest\0parents:false\0${filename}`;
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const balance = (s) => { let n = 0; for (const ch of s) {
|
|
153
|
+
if ('([{'.includes(ch))
|
|
154
|
+
n++;
|
|
155
|
+
else if (')]}'.includes(ch))
|
|
156
|
+
n--;
|
|
157
|
+
} return n; };
|
|
158
|
+
// split a declarator-list head on top-level commas: `COLS = 220, ROWS = 50` -> [COLS, ROWS]
|
|
159
|
+
function declNames(head) {
|
|
160
|
+
let d = 0, seg = '';
|
|
161
|
+
const segs = [];
|
|
162
|
+
for (const ch of head) {
|
|
163
|
+
if ('([{<'.includes(ch))
|
|
164
|
+
d++;
|
|
165
|
+
else if (')]}>'.includes(ch))
|
|
166
|
+
d--;
|
|
167
|
+
if (ch === ',' && d === 0) {
|
|
168
|
+
segs.push(seg);
|
|
169
|
+
seg = '';
|
|
170
|
+
}
|
|
171
|
+
else
|
|
172
|
+
seg += ch;
|
|
173
|
+
}
|
|
174
|
+
segs.push(seg);
|
|
175
|
+
return segs.map((s) => s.match(/^\s*([A-Za-z_$][\w$]*)\s*(?::|=|$)/)?.[1]).filter((x) => !!x);
|
|
176
|
+
}
|
|
177
|
+
const indentation = (line) => {
|
|
178
|
+
let n = 0;
|
|
179
|
+
for (const ch of line) {
|
|
180
|
+
if (ch === ' ')
|
|
181
|
+
n++;
|
|
182
|
+
else if (ch === '\t')
|
|
183
|
+
n += 8 - (n % 8);
|
|
184
|
+
else
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
return n;
|
|
188
|
+
};
|
|
189
|
+
function extractIndentScoped(content, spec) {
|
|
190
|
+
const lines = content.split('\n');
|
|
191
|
+
const units = [];
|
|
192
|
+
const scopes = [];
|
|
193
|
+
for (let i = 0; i < lines.length; i++) {
|
|
194
|
+
const line = lines[i];
|
|
195
|
+
if (!line.trim() || /^\s*#/.test(line))
|
|
196
|
+
continue;
|
|
197
|
+
const indent = indentation(line);
|
|
198
|
+
while (scopes.length && scopes[scopes.length - 1].indent >= indent)
|
|
199
|
+
scopes.pop();
|
|
200
|
+
for (const d of spec.decls) {
|
|
201
|
+
const m = line.match(d.re);
|
|
202
|
+
if (!m)
|
|
203
|
+
continue;
|
|
204
|
+
const local = m[1];
|
|
205
|
+
const name = [...scopes.map((s) => s.name), local].join('.');
|
|
206
|
+
const parent = scopes[scopes.length - 1];
|
|
207
|
+
const kind = d.memberOf && parent?.kind === d.memberOf.parentKind ? d.memberOf.kind : d.kind;
|
|
208
|
+
let start = i + 1;
|
|
209
|
+
if (spec.indentScopes?.decorator) {
|
|
210
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
211
|
+
if (indentation(lines[j]) !== indent || !spec.indentScopes.decorator.test(lines[j]))
|
|
212
|
+
break;
|
|
213
|
+
start = j + 1;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
units.push({ name, kind, start, end: i + 1, declaration: i, indent, ...(d.typeOnly ? { typeOnly: true } : {}) });
|
|
217
|
+
if (d.scopeOpener)
|
|
218
|
+
scopes.push({ indent, name: local, kind: d.kind });
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
for (const unit of units) {
|
|
223
|
+
let boundary = null;
|
|
224
|
+
for (let i = unit.declaration + 1; i < lines.length; i++) {
|
|
225
|
+
const line = lines[i];
|
|
226
|
+
if (!line.trim())
|
|
227
|
+
continue;
|
|
228
|
+
const indent = indentation(line);
|
|
229
|
+
if (/^\s*#/.test(line)) {
|
|
230
|
+
if (indent <= unit.indent && boundary === null)
|
|
231
|
+
boundary = i;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (indent <= unit.indent) {
|
|
235
|
+
boundary ??= i;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
boundary = null;
|
|
239
|
+
}
|
|
240
|
+
unit.end = Math.max(unit.start, (boundary ?? lines.length));
|
|
241
|
+
}
|
|
242
|
+
return units.map(({ declaration: _declaration, indent: _indent, ...unit }) => unit);
|
|
243
|
+
}
|
|
244
|
+
export function heuristicExtractor(spec) {
|
|
245
|
+
return {
|
|
246
|
+
id: spec.id,
|
|
247
|
+
claims: (ext) => spec.extensions.includes(ext),
|
|
248
|
+
ready: () => true,
|
|
249
|
+
extract(content) {
|
|
250
|
+
if (spec.indentScopes)
|
|
251
|
+
return extractIndentScoped(content, spec);
|
|
252
|
+
const lines = content.split('\n');
|
|
253
|
+
const units = [];
|
|
254
|
+
let cls = null, depth = 0;
|
|
255
|
+
for (let i = 0; i < lines.length; i++) {
|
|
256
|
+
const l = lines[i];
|
|
257
|
+
if (cls) {
|
|
258
|
+
const m = spec.member && l.match(spec.member.re);
|
|
259
|
+
if (m && !spec.member.blacklist?.test(m[1]))
|
|
260
|
+
units.push({ name: `${cls}.${m[1]}`, kind: 'method', start: i + 1, end: i + 1 });
|
|
261
|
+
depth += balance(l);
|
|
262
|
+
if (depth <= 0)
|
|
263
|
+
cls = null;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
for (const d of spec.decls) {
|
|
267
|
+
const m = l.match(d.re);
|
|
268
|
+
if (!m)
|
|
269
|
+
continue;
|
|
270
|
+
for (const name of d.declList ? declNames(m[1]) : [m[1]])
|
|
271
|
+
units.push({ name, kind: d.kind, start: i + 1, end: i + 1, ...(d.typeOnly ? { typeOnly: true } : {}) });
|
|
272
|
+
if (d.classOpener) {
|
|
273
|
+
cls = m[1];
|
|
274
|
+
depth = balance(l);
|
|
275
|
+
}
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// R5b ranges: a unit ends before the next column-0 boundary line; a method is also capped by the
|
|
280
|
+
// next unit's start (methods sit inside their class's indentation, below boundary's radar).
|
|
281
|
+
const bset = [];
|
|
282
|
+
for (let i = 0; i < lines.length; i++)
|
|
283
|
+
if (spec.boundary.test(lines[i]))
|
|
284
|
+
bset.push(i + 1);
|
|
285
|
+
const starts = units.map((u) => u.start).sort((a, b) => a - b);
|
|
286
|
+
for (const u of units) {
|
|
287
|
+
const nb = bset.find((b) => b > u.start);
|
|
288
|
+
let end = nb ?? lines.length + 1;
|
|
289
|
+
if (u.kind === 'method') {
|
|
290
|
+
const ns = starts.find((x) => x > u.start);
|
|
291
|
+
if (ns && ns < end)
|
|
292
|
+
end = ns;
|
|
293
|
+
}
|
|
294
|
+
u.end = Math.max(u.start, end - 1);
|
|
295
|
+
}
|
|
296
|
+
return units;
|
|
297
|
+
},
|
|
298
|
+
memoKey(filename) {
|
|
299
|
+
const regex = (r) => r ? `${r.source}/${r.flags}` : null;
|
|
300
|
+
return JSON.stringify({
|
|
301
|
+
schema: 'heuristic-memo-v1', filename, id: spec.id, extensions: spec.extensions,
|
|
302
|
+
decls: spec.decls.map((d) => ({
|
|
303
|
+
re: regex(d.re), kind: d.kind, typeOnly: !!d.typeOnly, classOpener: !!d.classOpener,
|
|
304
|
+
declList: !!d.declList, scopeOpener: !!d.scopeOpener, memberOf: d.memberOf ?? null,
|
|
305
|
+
})),
|
|
306
|
+
member: spec.member ? { re: regex(spec.member.re), blacklist: regex(spec.member.blacklist) } : null,
|
|
307
|
+
indentScopes: spec.indentScopes ? { decorator: regex(spec.indentScopes.decorator) } : null,
|
|
308
|
+
boundary: regex(spec.boundary),
|
|
309
|
+
});
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
// The validated JS-family reference row (R5b: name precision 99.7% / recall 100% / range 98.9% on the
|
|
314
|
+
// 41-file oracle) — NOT registered for JS (ts-ast is designated); kept as the engine's reference shape
|
|
315
|
+
// and the benchmark's scoring subject.
|
|
316
|
+
export const JS_LANG_R5B = {
|
|
317
|
+
id: 'heuristic-js',
|
|
318
|
+
extensions: [...JS_EXTS],
|
|
319
|
+
decls: [
|
|
320
|
+
{ re: /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function\*?\s+([A-Za-z_$][\w$]*)/, kind: 'function' },
|
|
321
|
+
{ re: /^(?:export\s+)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/, kind: 'class', classOpener: true },
|
|
322
|
+
{ re: /^(?:export\s+)?(?:declare\s+)?enum\s+([A-Za-z_$][\w$]*)/, kind: 'enum' },
|
|
323
|
+
{ re: /^(?:export\s+)?(?:declare\s+)?interface\s+([A-Za-z_$][\w$]*)/, kind: 'interface', typeOnly: true },
|
|
324
|
+
{ re: /^(?:export\s+)?(?:declare\s+)?type\s+([A-Za-z_$][\w$]*)/, kind: 'type', typeOnly: true },
|
|
325
|
+
{ re: /^(?:export\s+)?(?:const|let|var)\s+(.+)$/, kind: 'const', declList: true },
|
|
326
|
+
],
|
|
327
|
+
member: {
|
|
328
|
+
re: /^\s+(?:(?:public|private|protected|static|readonly|async|get|set)\s+)*([A-Za-z_$][\w$]*)\s*(?:<[^>]*>)?\(/,
|
|
329
|
+
blacklist: /^(if|for|while|switch|return|catch|new|await|typeof|throw|else|do)$/,
|
|
330
|
+
},
|
|
331
|
+
boundary: /^(?:[A-Za-z_$]|\/\/|\/\*)/,
|
|
332
|
+
};
|
|
333
|
+
// Python is a LangSpec DATA row over the same generic engine: declaration names come from patterns;
|
|
334
|
+
// significant indentation supplies lexical qualification and ranges. It is intentionally structural,
|
|
335
|
+
// not a Python runtime or full grammar (the user-facing boundary is documented by [[code-anchor]]).
|
|
336
|
+
const PY_ID = String.raw `[\p{ID_Start}_][\p{ID_Continue}_]*`;
|
|
337
|
+
export const PYTHON_LANG = {
|
|
338
|
+
id: 'heuristic-python',
|
|
339
|
+
extensions: ['py', 'pyi'],
|
|
340
|
+
decls: [
|
|
341
|
+
{
|
|
342
|
+
re: new RegExp(`^\\s*(?:async\\s+)?def\\s+(${PY_ID})\\s*\\(`, 'u'),
|
|
343
|
+
kind: 'function',
|
|
344
|
+
scopeOpener: true,
|
|
345
|
+
memberOf: { parentKind: 'class', kind: 'method' },
|
|
346
|
+
},
|
|
347
|
+
{ re: new RegExp(`^\\s*class\\s+(${PY_ID})(?:\\s*\\(|\\s*:)`, 'u'), kind: 'class', scopeOpener: true },
|
|
348
|
+
],
|
|
349
|
+
indentScopes: { decorator: /^\s*@/ },
|
|
350
|
+
boundary: /^\S/,
|
|
351
|
+
};
|
|
352
|
+
// ---- registry: extension -> its ONE designated extractor ----
|
|
353
|
+
// The registry's shape is the Extractor INTERFACE, not any engine: a future language row may be a
|
|
354
|
+
// heuristicExtractor(LangSpec) or a web-tree-sitter extractor carrying its own wasm-grammar/query
|
|
355
|
+
// config — whatever the implementation needs rides inside its own factory, never in the registry.
|
|
356
|
+
export function extractors(root) {
|
|
357
|
+
return [tsAstExtractor(root), ...[PYTHON_LANG].map(heuristicExtractor)];
|
|
358
|
+
}
|
|
359
|
+
// first claiming extractor IS the designation (the registry order defines it); null = no anchor support
|
|
360
|
+
// for this language yet (lint ERRORS — the remedy is a LangSpec data row, or dropping the anchor).
|
|
361
|
+
export function extractorFor(regs, ext) {
|
|
362
|
+
return regs.find((x) => x.claims(ext)) ?? null;
|
|
363
|
+
}
|
|
364
|
+
export const extOf = (path) => {
|
|
365
|
+
const base = path.slice(path.lastIndexOf('/') + 1);
|
|
366
|
+
const dot = base.lastIndexOf('.');
|
|
367
|
+
return dot > 0 ? base.slice(dot + 1) : '';
|
|
368
|
+
};
|
|
369
|
+
export function resolveSelectors(units, selectors) {
|
|
370
|
+
return selectors.map((selector) => {
|
|
371
|
+
const r = resolveAnchor(units, selector);
|
|
372
|
+
if ('dead' in r)
|
|
373
|
+
return { selector, dead: true };
|
|
374
|
+
if ('ambiguous' in r)
|
|
375
|
+
return { selector, ambiguous: r.ambiguous };
|
|
376
|
+
return { selector, ok: r.ok };
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
export function resolveAnchor(units, symbol) {
|
|
380
|
+
const hits = units.filter((u) => u.name === symbol);
|
|
381
|
+
if (!hits.length)
|
|
382
|
+
return { dead: true };
|
|
383
|
+
if (hits.length > 1)
|
|
384
|
+
return { ambiguous: hits.length };
|
|
385
|
+
return { ok: hits[0] };
|
|
386
|
+
}
|
|
387
|
+
// The one selector/range intersection used by historical commits and a pinned worktree overlay.
|
|
388
|
+
export function selectorsHitRanges(units, symbols, ranges) {
|
|
389
|
+
return symbols.filter((symbol) => {
|
|
390
|
+
const matching = units.filter((unit) => unit.name === symbol);
|
|
391
|
+
return matching.length > 0 && ranges.some(([start, end]) => matching.some((unit) => start <= unit.end && unit.start <= end));
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
// Ordinary zero-context ranges. The side chooses the immutable image whose units the caller resolved.
|
|
395
|
+
export function diffHunkRanges(patch, side = 'new') {
|
|
396
|
+
const ranges = [];
|
|
397
|
+
for (const match of patch.matchAll(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/gm)) {
|
|
398
|
+
const offset = side === 'old' ? 1 : 3;
|
|
399
|
+
const start = +match[offset];
|
|
400
|
+
const count = match[offset + 1] === undefined ? 1 : +match[offset + 1];
|
|
401
|
+
if (count > 0)
|
|
402
|
+
ranges.push([start, start + count - 1]);
|
|
403
|
+
}
|
|
404
|
+
return ranges;
|
|
405
|
+
}
|
|
406
|
+
const fileRevisionUnitMemo = new Map();
|
|
407
|
+
const MEMO_MAX = 4096;
|
|
408
|
+
const fileRevisionMemoKey = (objectFormat, oid, x, path) => `${objectFormat}\0${oid}\0${x.memoKey(path)}`;
|
|
409
|
+
async function unitsAtFileRevision(commit, path, x, objectFormat, oid, text) {
|
|
410
|
+
if (!oid)
|
|
411
|
+
return { absent: true };
|
|
412
|
+
const key = fileRevisionMemoKey(objectFormat, oid, x, path);
|
|
413
|
+
const hit = fileRevisionUnitMemo.get(key);
|
|
414
|
+
if (hit)
|
|
415
|
+
return hit;
|
|
416
|
+
if (text === undefined)
|
|
417
|
+
throw new Error(`git cat-file --batch omitted object ${oid} for ${commit}:${path}`);
|
|
418
|
+
let result;
|
|
419
|
+
try {
|
|
420
|
+
result = { units: x.extract(text, path) };
|
|
421
|
+
}
|
|
422
|
+
catch (e) {
|
|
423
|
+
result = { unparseable: e?.message ?? String(e) };
|
|
424
|
+
}
|
|
425
|
+
if (fileRevisionUnitMemo.size >= MEMO_MAX)
|
|
426
|
+
fileRevisionUnitMemo.clear();
|
|
427
|
+
fileRevisionUnitMemo.set(key, result);
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
// @@@ RANGE_SEMANTICS - hunk ranges must not depend on ambient Git state, because a range that moves with
|
|
431
|
+
// config or attributes is neither a sound anchor verdict nor a reusable fact. Outside the commit, three
|
|
432
|
+
// classes decide them: presentation (`.gitattributes` `-diff` prints `Binary files … differ` with no `@@` at
|
|
433
|
+
// all — measured: one hunk bare, ZERO under `src/x.py -diff`, so an attribute edit silently disabled an
|
|
434
|
+
// anchored contract's drift; a textconv or external driver replaces the text compared), the ALGORITHM and
|
|
435
|
+
// its heuristics (`diff.algorithm`, `diff.indentHeuristic` — both repo config), and hunk COALESCING
|
|
436
|
+
// (`diff.interHunkContext`). All are pinned here, on the two readers this node owns.
|
|
437
|
+
//
|
|
438
|
+
// The algorithm's VALUE is a product choice, not a formality, because the algorithms genuinely disagree:
|
|
439
|
+
// on `c a c a` -> `c c a a a`, myers authors new lines 4-5 and deletes old line 2, while histogram authors
|
|
440
|
+
// new lines 2-3 and deletes old line 3 — so a unit on line 3 is a HIT under histogram and a MISS under
|
|
441
|
+
// myers, on one commit. histogram is pinned because it aligns the change with the unit that actually moved,
|
|
442
|
+
// which for a BLOCKING gate is the conservative direction; myers (Git's default) would silently miss that
|
|
443
|
+
// drift. So this pin is not merely determinism: it settles which reading a verdict means.
|
|
444
|
+
// `--no-color` is not cosmetic here: under an ambient `color.ui=always` Git prefixes every `@@` with an
|
|
445
|
+
// ANSI escape, so a `/^@@/` parse finds NO hunks and every anchored path reads as "nothing changed" — a
|
|
446
|
+
// silent zero-drift verdict for the whole corpus, and one that would then be memoized. `-M -l0` pins rename
|
|
447
|
+
// detection with no candidate limit, matching the identity stream this engine's events come from, so a large
|
|
448
|
+
// rename set cannot quietly change which pairs are compared.
|
|
449
|
+
const RANGE_SEMANTICS = [
|
|
450
|
+
'--text', '--no-textconv', '--no-ext-diff', '--no-color',
|
|
451
|
+
'--diff-algorithm=histogram', '--no-indent-heuristic', '--inter-hunk-context=0',
|
|
452
|
+
'-M', '-l0',
|
|
453
|
+
];
|
|
454
|
+
const hunkMemo = new Map();
|
|
455
|
+
// @@@ the reusable fact is a diff of ORDERED IMAGES, not of a commit id - a commit id is not immutable
|
|
456
|
+
// interpretation: `refs/replace` can swap the object, and a graft or an unshallow can change its parents, so
|
|
457
|
+
// one (commit,path) can name two different diffs inside one process. The inputs that actually decide the
|
|
458
|
+
// hunks are the result image and each parent image, in order, each identified by its resolved blob oid and
|
|
459
|
+
// historical path. Keying on those makes the key move exactly when an input does, and the oids are already
|
|
460
|
+
// resolved by this read's one `cat-file --batch-check`, so completeness costs no extra child and no new state.
|
|
461
|
+
const ABSENT_IMAGE = '-';
|
|
462
|
+
const HUNK_FACT_SCHEMA = 'anchor-range-histogram-v1';
|
|
463
|
+
const hunkMemoKey = (images) => `${HUNK_FACT_SCHEMA}\0${images.join('\0')}`;
|
|
464
|
+
function rememberHunks(key, ranges) {
|
|
465
|
+
if (hunkMemo.size >= MEMO_MAX)
|
|
466
|
+
hunkMemo.clear();
|
|
467
|
+
hunkMemo.set(key, ranges);
|
|
468
|
+
return ranges;
|
|
469
|
+
}
|
|
470
|
+
async function hunksAt(root, event, key) {
|
|
471
|
+
const paths = [...new Set([event.historicalPath, ...event.parents.map((parent) => parent.historicalPath)])];
|
|
472
|
+
const hit = hunkMemo.get(key);
|
|
473
|
+
if (hit)
|
|
474
|
+
return hit;
|
|
475
|
+
const merge = event.parents.length > 1;
|
|
476
|
+
const out = await gitRequiredA(['-C', root, '-c', 'core.quotePath=false', 'show', '--cc', '--combined-all-paths', '--unified=0', ...RANGE_SEMANTICS, '--format=', event.commit,
|
|
477
|
+
...(merge ? [] : ['--', ...paths])], `cannot derive anchor hunks for ${event.commit}:${event.historicalPath}`);
|
|
478
|
+
let ranges = { after: [], before: [[]] };
|
|
479
|
+
if (/^@@@/m.test(out)) {
|
|
480
|
+
const owned = combinedDiffOwnedChanges(out).get(event.historicalPath);
|
|
481
|
+
if (!owned)
|
|
482
|
+
throw new Error(`combined diff for ${event.commit} did not expose owned ranges for '${event.historicalPath}'`);
|
|
483
|
+
ranges = owned;
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
for (const m of out.matchAll(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/gm)) {
|
|
487
|
+
const oldStart = +m[1], oldCount = m[2] === undefined ? 1 : +m[2];
|
|
488
|
+
const newStart = +m[3], newCount = m[4] === undefined ? 1 : +m[4];
|
|
489
|
+
if (oldCount > 0)
|
|
490
|
+
ranges.before[0].push([oldStart, oldStart + oldCount - 1]);
|
|
491
|
+
if (newCount > 0)
|
|
492
|
+
ranges.after.push([newStart, newStart + newCount - 1]);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return rememberHunks(key, ranges);
|
|
496
|
+
}
|
|
497
|
+
// Ordinary commits whose two images keep one path share a single query. Renames and merges retain distinct
|
|
498
|
+
// image paths, so they stay on hunksAt's exact event-shaped query. Commits ride argv here, so a build-wide
|
|
499
|
+
// batch is split to stay clear of the kernel's exec argument limit; the records are order-independent.
|
|
500
|
+
//
|
|
501
|
+
// @@@ the demand set is the MISSES - a hunk is reusable by the identity of the IMAGES it diffed, never by the
|
|
502
|
+
// commit id (see hunkMemoKey): under that identity, plus this seam's pinned interpretation, the fact cannot be
|
|
503
|
+
// reinterpreted inside one process, so the batch may ask git only for what it has not read. Asking for the
|
|
504
|
+
// whole window again is what made a repeated read cost the CORPUS instead of the movement: a re-lint after one
|
|
505
|
+
// trunk commit or one dirty edit re-forked one `log --patch` per anchored path (22 here) to re-derive
|
|
506
|
+
// byte-identical answers.
|
|
507
|
+
const HUNK_COMMIT_CHUNK = 400;
|
|
508
|
+
async function hunksAtMany(root, path, entries) {
|
|
509
|
+
const result = new Map();
|
|
510
|
+
const ordinary = [];
|
|
511
|
+
for (const [commit, key] of entries) {
|
|
512
|
+
const hit = hunkMemo.get(key);
|
|
513
|
+
if (hit)
|
|
514
|
+
result.set(commit, hit);
|
|
515
|
+
else
|
|
516
|
+
ordinary.push(commit);
|
|
517
|
+
}
|
|
518
|
+
if (!ordinary.length)
|
|
519
|
+
return result;
|
|
520
|
+
for (let cursor = 0; cursor < ordinary.length; cursor += HUNK_COMMIT_CHUNK)
|
|
521
|
+
await hunkRecordsInto(root, ordinary.slice(cursor, cursor + HUNK_COMMIT_CHUNK), path, entries, result);
|
|
522
|
+
return result;
|
|
523
|
+
}
|
|
524
|
+
async function hunkRecordsInto(root, ordinary, path, keys, result) {
|
|
525
|
+
const out = await gitRequiredA(['-C', root, '-c', 'core.quotePath=false', 'log', '--no-walk', '--no-merges', '--patch', '--unified=0', ...RANGE_SEMANTICS,
|
|
526
|
+
`--format=${RS}%H`, ...ordinary, '--', path], `cannot derive anchor hunks for ${path}`);
|
|
527
|
+
for (const rec of out.split(RS)) {
|
|
528
|
+
const normalized = rec.replace(/^\n/, '');
|
|
529
|
+
if (!normalized)
|
|
530
|
+
continue;
|
|
531
|
+
const newline = normalized.indexOf('\n');
|
|
532
|
+
const hash = (newline < 0 ? normalized : normalized.slice(0, newline)).trim();
|
|
533
|
+
if (!isGitObjectId(root, hash))
|
|
534
|
+
throw new Error(`anchor hunk query returned malformed object id '${hash || 'empty'}'`);
|
|
535
|
+
const patch = newline < 0 ? '' : normalized.slice(newline + 1);
|
|
536
|
+
const ranges = { after: [], before: [[]] };
|
|
537
|
+
for (const m of patch.matchAll(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/gm)) {
|
|
538
|
+
const oldStart = +m[1], oldCount = m[2] === undefined ? 1 : +m[2];
|
|
539
|
+
const newStart = +m[3], newCount = m[4] === undefined ? 1 : +m[4];
|
|
540
|
+
if (oldCount > 0)
|
|
541
|
+
ranges.before[0].push([oldStart, oldStart + oldCount - 1]);
|
|
542
|
+
if (newCount > 0)
|
|
543
|
+
ranges.after.push([newStart, newStart + newCount - 1]);
|
|
544
|
+
}
|
|
545
|
+
const key = keys.get(hash);
|
|
546
|
+
result.set(hash, key === undefined ? ranges : rememberHunks(key, ranges));
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
// the drift window of an anchored file: every commit to `path` not reachable from the spec's version
|
|
550
|
+
// and not covered by a valid Spec-OK ack — the SAME set driftFor counts, exposed as commits so the
|
|
551
|
+
// anchor engine can probe each one. Ordinary file commits and merge-authored dense-combined paths are
|
|
552
|
+
// indexed separately so clean merge transport is never charged twice.
|
|
553
|
+
export function windowEvents(idx, sinceHash, path, nodeId) {
|
|
554
|
+
if (!sinceHash)
|
|
555
|
+
return [];
|
|
556
|
+
return driftPathWindow(idx, sinceHash, path, nodeId) ?? [];
|
|
557
|
+
}
|
|
558
|
+
const anchorRevisionKey = ({ commit, path }) => `${commit}\0${path}`;
|
|
559
|
+
// One lint can judge several selectors with overlapping windows. Their Git images and ordinary hunks are
|
|
560
|
+
// reusable under the identity of those images, so the batch owns them once and each query keeps its own
|
|
561
|
+
// selector verdict.
|
|
562
|
+
async function runAnchorQueries(root, queries, regs, stopAtFirstHit) {
|
|
563
|
+
if (!queries.length)
|
|
564
|
+
return [];
|
|
565
|
+
return withEventLedgerBuild(root, () => runAnchorQueriesInLedger(root, queries, regs, stopAtFirstHit));
|
|
566
|
+
}
|
|
567
|
+
async function runAnchorQueriesInLedger(root, queries, regs, stopAtFirstHit) {
|
|
568
|
+
const objectFormat = gitObjectFormat(root);
|
|
569
|
+
const revisions = new Map();
|
|
570
|
+
const ordinaryEvents = [];
|
|
571
|
+
for (const { win } of queries)
|
|
572
|
+
for (const event of win) {
|
|
573
|
+
const refs = [{ commit: event.commit, path: event.historicalPath }, ...event.parents.map(({ commit, historicalPath }) => ({ commit, path: historicalPath }))];
|
|
574
|
+
for (const ref of refs)
|
|
575
|
+
revisions.set(anchorRevisionKey(ref), ref);
|
|
576
|
+
if (event.parents.length > 1 || event.parents.some((parent) => parent.historicalPath !== event.historicalPath))
|
|
577
|
+
continue;
|
|
578
|
+
ordinaryEvents.push({ path: event.historicalPath, commit: event.commit, event });
|
|
579
|
+
}
|
|
580
|
+
const refs = [...revisions.values()];
|
|
581
|
+
const oids = await batchRevisionOids(root, refs.map(({ commit, path }) => `${commit}:${path}`));
|
|
582
|
+
// Every image this read will diff is now resolved, so the reusable hunk fact can be named by the identity
|
|
583
|
+
// that actually decides it: the result image and each parent image, in order, oid + historical path.
|
|
584
|
+
const oidByRef = new Map();
|
|
585
|
+
for (let index = 0; index < refs.length; index++)
|
|
586
|
+
oidByRef.set(anchorRevisionKey(refs[index]), oids[index]);
|
|
587
|
+
const imageOf = (commit, path) => `${oidByRef.get(anchorRevisionKey({ commit, path })) ?? ABSENT_IMAGE}:${path}`;
|
|
588
|
+
const imageIdentity = (event) => hunkMemoKey([
|
|
589
|
+
imageOf(event.commit, event.historicalPath),
|
|
590
|
+
...event.parents.map(({ commit, historicalPath }) => imageOf(commit, historicalPath)),
|
|
591
|
+
]);
|
|
592
|
+
const hunkKeys = new Set();
|
|
593
|
+
const ordinaryByPath = new Map();
|
|
594
|
+
for (const { path, commit, event } of ordinaryEvents) {
|
|
595
|
+
const entries = ordinaryByPath.get(path) ?? new Map();
|
|
596
|
+
const key = imageIdentity(event);
|
|
597
|
+
hunkKeys.add(key);
|
|
598
|
+
entries.set(commit, key);
|
|
599
|
+
ordinaryByPath.set(path, entries);
|
|
600
|
+
}
|
|
601
|
+
for (const { win } of queries)
|
|
602
|
+
for (const event of win)
|
|
603
|
+
hunkKeys.add(imageIdentity(event));
|
|
604
|
+
const durableHunks = readImmutableHunkFacts(root, hunkKeys);
|
|
605
|
+
for (const [key, ranges] of durableHunks)
|
|
606
|
+
rememberHunks(key, ranges);
|
|
607
|
+
const newHunks = new Map();
|
|
608
|
+
const ordinaryHunks = new Map();
|
|
609
|
+
for (const [path, entries] of ordinaryByPath) {
|
|
610
|
+
const rangesByCommit = await hunksAtMany(root, path, entries);
|
|
611
|
+
ordinaryHunks.set(path, rangesByCommit);
|
|
612
|
+
for (const [commit, key] of entries) {
|
|
613
|
+
const ranges = rangesByCommit.get(commit);
|
|
614
|
+
if (ranges && !durableHunks.has(key))
|
|
615
|
+
newHunks.set(key, ranges);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
// The image's own memo answers before any bytes move, so the read asks git only for the revisions this
|
|
619
|
+
// process has not parsed yet — the retention half of the same rule the hunk demand set follows.
|
|
620
|
+
const units = new Map();
|
|
621
|
+
const primeUnits = async (keys) => {
|
|
622
|
+
const pending = [];
|
|
623
|
+
const wanted = new Set();
|
|
624
|
+
for (const key of keys) {
|
|
625
|
+
if (units.has(key))
|
|
626
|
+
continue;
|
|
627
|
+
const ref = revisions.get(key), oid = oidByRef.get(key) ?? null;
|
|
628
|
+
const x = extractorFor(regs, extOf(ref.path));
|
|
629
|
+
const ready = x?.ready();
|
|
630
|
+
if (!x || ready !== true) {
|
|
631
|
+
units.set(key, { unparseable: !x ? `no designated extractor for ${ref.path}` : String(ready) });
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
if (!oid) {
|
|
635
|
+
units.set(key, { absent: true });
|
|
636
|
+
continue;
|
|
637
|
+
}
|
|
638
|
+
const hit = fileRevisionUnitMemo.get(fileRevisionMemoKey(objectFormat, oid, x, ref.path));
|
|
639
|
+
if (hit) {
|
|
640
|
+
units.set(key, hit);
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
pending.push({ key, ref, oid, x });
|
|
644
|
+
wanted.add(oid);
|
|
645
|
+
}
|
|
646
|
+
if (!pending.length)
|
|
647
|
+
return;
|
|
648
|
+
const blobs = await batchBlobTexts(root, [...wanted]);
|
|
649
|
+
for (const { key, ref, oid, x } of pending)
|
|
650
|
+
units.set(key, await unitsAtFileRevision(ref.commit, ref.path, x, objectFormat, oid, blobs.get(oid)));
|
|
651
|
+
};
|
|
652
|
+
const intersects = (ranges, candidates) => ranges.some(([start, end]) => candidates.some((unit) => start <= unit.end && unit.start <= end));
|
|
653
|
+
// @@@ the scan advances in DOUBLING corpus-wide rounds - an existence read may stop at its window's first
|
|
654
|
+
// hit, but which event that is cannot be known before the images are parsed, so the demand set has to be
|
|
655
|
+
// discovered rather than declared. Each round still asks the WHOLE unsettled corpus at once (never one
|
|
656
|
+
// reading at a time, which is what would re-fork the batch per unit), and doubling bounds the rounds by hit
|
|
657
|
+
// DEPTH, not by how many readings are asked. An enumeration read takes its whole remaining window in one
|
|
658
|
+
// slice, so it keeps the single-round shape it has always had.
|
|
659
|
+
const runs = queries.map(() => ({ hits: new Map(), cursor: 0, settled: false }));
|
|
660
|
+
for (let chunk = 1;; chunk *= 2) {
|
|
661
|
+
const slices = [];
|
|
662
|
+
for (let index = 0; index < queries.length; index++) {
|
|
663
|
+
const run = runs[index], { win, symbols } = queries[index];
|
|
664
|
+
if (run.settled || run.cursor >= win.length)
|
|
665
|
+
continue;
|
|
666
|
+
slices.push({ run, symbols, events: win.slice(run.cursor, run.cursor + (stopAtFirstHit ? chunk : win.length - run.cursor)) });
|
|
667
|
+
}
|
|
668
|
+
if (!slices.length)
|
|
669
|
+
break;
|
|
670
|
+
const keys = new Set();
|
|
671
|
+
for (const { events } of slices)
|
|
672
|
+
for (const event of events) {
|
|
673
|
+
keys.add(anchorRevisionKey({ commit: event.commit, path: event.historicalPath }));
|
|
674
|
+
for (const parent of event.parents)
|
|
675
|
+
keys.add(anchorRevisionKey({ commit: parent.commit, path: parent.historicalPath }));
|
|
676
|
+
}
|
|
677
|
+
await primeUnits(keys);
|
|
678
|
+
for (const { run, symbols, events } of slices) {
|
|
679
|
+
for (const event of events) {
|
|
680
|
+
run.cursor++;
|
|
681
|
+
const after = units.get(anchorRevisionKey({ commit: event.commit, path: event.historicalPath }));
|
|
682
|
+
const before = event.parents.map(({ commit, historicalPath }) => units.get(anchorRevisionKey({ commit, path: historicalPath })));
|
|
683
|
+
const key = imageIdentity(event);
|
|
684
|
+
const ranges = ordinaryHunks.get(event.historicalPath)?.get(event.commit)
|
|
685
|
+
?? await hunksAt(root, event, key);
|
|
686
|
+
if (!durableHunks.has(key))
|
|
687
|
+
newHunks.set(key, ranges);
|
|
688
|
+
if (event.parents.length && ranges.before.length !== before.length)
|
|
689
|
+
throw new Error(`anchor diff for ${event.commit}:${event.historicalPath} has ${ranges.before.length} parent ranges for ${before.length} parents`);
|
|
690
|
+
const hit = run.hits.get(event.commit) ?? { selectors: new Set() };
|
|
691
|
+
const broken = [after, ...before].find((image) => 'unparseable' in image);
|
|
692
|
+
if (broken && 'unparseable' in broken) {
|
|
693
|
+
for (const symbol of symbols)
|
|
694
|
+
hit.selectors.add(symbol);
|
|
695
|
+
hit.unparseable = broken.unparseable;
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
for (const symbol of symbols) {
|
|
699
|
+
const afterUnits = 'units' in after ? after.units.filter((unit) => unit.name === symbol) : [];
|
|
700
|
+
const authoredAfter = intersects(ranges.after, afterUnits);
|
|
701
|
+
const authoredBefore = before.length > 0 && before.every((image, parent) => 'units' in image && intersects(ranges.before[parent], image.units.filter((unit) => unit.name === symbol)));
|
|
702
|
+
if (authoredAfter || authoredBefore)
|
|
703
|
+
hit.selectors.add(symbol);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (hit.selectors.size)
|
|
707
|
+
run.hits.set(event.commit, hit);
|
|
708
|
+
if (stopAtFirstHit && run.hits.size) {
|
|
709
|
+
run.settled = true;
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
await persistImmutableHunkFacts(root, newHunks);
|
|
716
|
+
return runs.map((run) => [...run.hits].map(([commit, hit]) => ({ commit, selectors: [...hit.selectors], ...(hit.unparseable ? { unparseable: hit.unparseable } : {}) })));
|
|
717
|
+
}
|
|
718
|
+
// The two consumers ask DIFFERENT questions of this engine, so they enter through different doors rather
|
|
719
|
+
// than through one call with a mode flag. Enumeration must scan every window event because its answer IS the
|
|
720
|
+
// per-commit, per-selector list; existence is discharged by the first hit, and scanning past it computes rows
|
|
721
|
+
// nobody reads.
|
|
722
|
+
export async function anchorHitQueries(root, queries, regs) {
|
|
723
|
+
return runAnchorQueries(root, queries, regs, false);
|
|
724
|
+
}
|
|
725
|
+
export async function anchorHitExists(root, queries, regs) {
|
|
726
|
+
return (await runAnchorQueries(root, queries, regs, true)).map((hits) => hits.length > 0);
|
|
727
|
+
}
|
|
728
|
+
export async function anchorHitCommits(root, win, symbols, regs) {
|
|
729
|
+
return (await anchorHitQueries(root, [{ win, symbols }], regs))[0];
|
|
730
|
+
}
|