spexcode 0.6.6 → 0.6.8
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 +12 -7
- package/node_modules/@spexcode/session-application/dist/index.d.ts +17 -0
- package/node_modules/@spexcode/session-application/dist/index.js +35 -0
- package/node_modules/@spexcode/session-application/dist/migration.d.ts +51 -0
- package/node_modules/@spexcode/session-application/dist/migration.js +694 -0
- package/node_modules/@spexcode/session-application/dist/production.d.ts +109 -0
- package/node_modules/@spexcode/session-application/dist/production.js +438 -0
- package/node_modules/@spexcode/session-application/dist/schema.d.ts +2 -0
- package/node_modules/@spexcode/session-application/dist/schema.js +39 -0
- package/node_modules/@spexcode/session-application/package.json +29 -0
- package/node_modules/@spexcode/session-events/dist/errors.d.ts +6 -0
- package/node_modules/@spexcode/session-events/dist/errors.js +11 -0
- package/node_modules/@spexcode/session-events/dist/index.d.ts +46 -0
- package/node_modules/@spexcode/session-events/dist/index.js +245 -0
- package/node_modules/@spexcode/session-events/dist/schema.d.ts +3 -0
- package/node_modules/@spexcode/session-events/dist/schema.js +44 -0
- package/node_modules/@spexcode/session-events/package.json +24 -0
- package/node_modules/@spexcode/session-protocol/dist/canonical.d.ts +36 -0
- package/node_modules/@spexcode/session-protocol/dist/canonical.js +139 -0
- package/node_modules/@spexcode/session-protocol/dist/engine.d.ts +22 -0
- package/node_modules/@spexcode/session-protocol/dist/engine.js +478 -0
- package/node_modules/@spexcode/session-protocol/dist/errors.d.ts +8 -0
- package/node_modules/@spexcode/session-protocol/dist/errors.js +39 -0
- package/node_modules/@spexcode/session-protocol/dist/index.d.ts +60 -0
- package/node_modules/@spexcode/session-protocol/dist/index.js +4 -0
- package/node_modules/@spexcode/session-protocol/dist/schema.d.ts +36 -0
- package/node_modules/@spexcode/session-protocol/dist/schema.js +192 -0
- package/node_modules/@spexcode/{session-core → session-protocol}/package.json +4 -8
- package/node_modules/@spexcode/session-runtime/dist/errors.d.ts +6 -0
- package/node_modules/@spexcode/session-runtime/dist/errors.js +11 -0
- package/node_modules/@spexcode/session-runtime/dist/index.d.ts +33 -0
- package/node_modules/@spexcode/session-runtime/dist/index.js +193 -0
- package/node_modules/@spexcode/session-runtime/dist/schema.d.ts +2 -0
- package/node_modules/@spexcode/session-runtime/dist/schema.js +37 -0
- package/node_modules/@spexcode/session-runtime/package.json +24 -0
- package/node_modules/@spexcode/session-selflaunch/bin/spex-session.mjs +4 -0
- package/node_modules/@spexcode/session-selflaunch/dist/cli.d.ts +25 -0
- package/node_modules/@spexcode/session-selflaunch/dist/cli.js +156 -0
- package/node_modules/@spexcode/session-selflaunch/dist/index.d.ts +28 -0
- package/node_modules/@spexcode/session-selflaunch/dist/index.js +25 -0
- package/node_modules/@spexcode/session-selflaunch/dist/locality.d.ts +20 -0
- package/node_modules/@spexcode/session-selflaunch/dist/locality.js +83 -0
- package/node_modules/@spexcode/session-selflaunch/dist/path.d.ts +13 -0
- package/node_modules/@spexcode/session-selflaunch/dist/path.js +45 -0
- package/node_modules/@spexcode/session-selflaunch/package.json +35 -0
- package/node_modules/@spexcode/session-topology/dist/errors.d.ts +6 -0
- package/node_modules/@spexcode/session-topology/dist/errors.js +11 -0
- package/node_modules/@spexcode/session-topology/dist/index.d.ts +24 -0
- package/node_modules/@spexcode/session-topology/dist/index.js +173 -0
- package/node_modules/@spexcode/session-topology/dist/schema.d.ts +3 -0
- package/node_modules/@spexcode/session-topology/dist/schema.js +27 -0
- package/node_modules/@spexcode/session-topology/package.json +24 -0
- package/node_modules/@spexcode/spec-cli/bin/spex.mjs +56 -19
- package/node_modules/@spexcode/spec-cli/dist/cli.js +102 -59
- package/node_modules/@spexcode/spec-cli/dist/client.d.ts +1 -3
- package/node_modules/@spexcode/spec-cli/dist/client.js +49 -30
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.d.ts +11 -0
- package/node_modules/@spexcode/spec-cli/dist/codex-runtime-generations.js +46 -9
- package/node_modules/@spexcode/spec-cli/dist/delivery-lock.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/delivery-lock.js +58 -0
- package/node_modules/@spexcode/spec-cli/dist/doctor.js +53 -11
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.d.ts +1 -0
- package/node_modules/@spexcode/spec-cli/dist/execution-trace.js +2 -2
- package/node_modules/@spexcode/spec-cli/dist/gateway-hub.js +2 -1
- package/node_modules/@spexcode/spec-cli/dist/gateway.js +6 -3
- package/node_modules/@spexcode/spec-cli/dist/graphCache.js +32 -2
- package/node_modules/@spexcode/spec-cli/dist/graphSnapshot.js +57 -2
- package/node_modules/@spexcode/spec-cli/dist/graphStream.d.ts +2 -0
- package/node_modules/@spexcode/spec-cli/dist/graphStream.js +83 -3
- package/node_modules/@spexcode/spec-cli/dist/guide.js +20 -7
- package/node_modules/@spexcode/spec-cli/dist/harness-select.js +16 -3
- package/node_modules/@spexcode/spec-cli/dist/harness.d.ts +15 -3
- package/node_modules/@spexcode/spec-cli/dist/harness.js +331 -50
- package/node_modules/@spexcode/spec-cli/dist/help.js +11 -8
- package/node_modules/@spexcode/spec-cli/dist/hook-prompts.js +8 -0
- package/node_modules/@spexcode/spec-cli/dist/host-resources.js +29 -8
- package/node_modules/@spexcode/spec-cli/dist/host.d.ts +7 -0
- package/node_modules/@spexcode/spec-cli/dist/host.js +93 -0
- package/node_modules/@spexcode/spec-cli/dist/index.js +324 -22
- package/node_modules/@spexcode/spec-cli/dist/init.js +1 -1
- package/node_modules/@spexcode/spec-cli/dist/lint.js +70 -35
- package/node_modules/@spexcode/spec-cli/dist/listen.d.ts +3 -2
- package/node_modules/@spexcode/spec-cli/dist/listen.js +14 -2
- package/node_modules/@spexcode/spec-cli/dist/machine-peer.js +1 -1
- package/node_modules/@spexcode/spec-cli/dist/materialize.d.ts +2 -2
- package/node_modules/@spexcode/spec-cli/dist/materialize.js +176 -35
- package/node_modules/@spexcode/spec-cli/dist/pty-bridge.js +14 -14
- package/node_modules/@spexcode/spec-cli/dist/reviews.js +12 -7
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.d.ts +11 -0
- package/node_modules/@spexcode/spec-cli/dist/runtime-ownership.js +79 -1
- package/node_modules/@spexcode/spec-cli/dist/session-application.d.ts +23 -0
- package/node_modules/@spexcode/spec-cli/dist/session-application.js +189 -0
- package/node_modules/@spexcode/spec-cli/dist/session-declarations.js +13 -1
- package/node_modules/@spexcode/spec-cli/dist/session-files.d.ts +6 -0
- package/node_modules/@spexcode/spec-cli/dist/session-files.js +13 -1
- package/node_modules/@spexcode/spec-cli/dist/session-follow.js +39 -22
- package/node_modules/@spexcode/{session-core/dist/record-lock.d.ts → spec-cli/dist/session-record-lock.d.ts} +0 -4
- package/node_modules/@spexcode/spec-cli/dist/session-record-lock.js +94 -0
- package/node_modules/@spexcode/spec-cli/dist/session-runtime-adapter.d.ts +44 -0
- package/node_modules/@spexcode/spec-cli/dist/session-runtime-adapter.js +37 -0
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.d.ts +25 -2
- package/node_modules/@spexcode/spec-cli/dist/session-timeline.js +68 -11
- package/node_modules/@spexcode/spec-cli/dist/session-web.js +4 -4
- package/node_modules/@spexcode/spec-cli/dist/sessions.d.ts +108 -15
- package/node_modules/@spexcode/spec-cli/dist/sessions.js +1465 -744
- package/node_modules/@spexcode/spec-cli/dist/source-list.d.ts +13 -0
- package/node_modules/@spexcode/spec-cli/dist/source-list.js +99 -0
- package/node_modules/@spexcode/spec-cli/dist/source-read.d.ts +16 -0
- package/node_modules/@spexcode/spec-cli/dist/source-read.js +84 -0
- package/node_modules/@spexcode/spec-cli/dist/spec-attachments.d.ts +7 -0
- package/node_modules/@spexcode/spec-cli/dist/spec-attachments.js +89 -0
- package/node_modules/@spexcode/spec-cli/dist/spec-body-edit.d.ts +23 -0
- package/node_modules/@spexcode/spec-cli/dist/spec-body-edit.js +138 -0
- package/node_modules/@spexcode/spec-cli/dist/supervise.js +15 -6
- package/node_modules/@spexcode/spec-cli/dist/transcript-reader.d.ts +36 -0
- package/node_modules/@spexcode/spec-cli/dist/transcript-reader.js +251 -0
- package/node_modules/@spexcode/spec-cli/hooks/dispatch.sh +19 -31
- package/node_modules/@spexcode/spec-cli/hooks/harness.sh +6 -6
- package/node_modules/@spexcode/spec-cli/package.json +6 -6
- package/node_modules/@spexcode/spec-cli/templates/hooks/post-checkout +4 -2
- package/node_modules/@spexcode/spec-cli/templates/hooks/post-merge +2 -1
- package/node_modules/@spexcode/spec-cli/templates/hooks/pre-commit +5 -3
- package/node_modules/@spexcode/spec-cli/templates/hooks/reference-transaction +5 -3
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/commands/spec.md +2 -7
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/idle/idle.sh +4 -10
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/idle/spec.md +1 -1
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/mark-active/mark-active.sh +22 -24
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/mark-active/spec.md +10 -2
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-fail/fail.sh +8 -7
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-fail/spec.md +3 -1
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-listen/session-listen.sh +133 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/session-listen/spec.md +36 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/spec.md +2 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/stop-gate/spec.md +1 -1
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/core/stop-gate/stop-gate.sh +17 -20
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/skills/merge/spec.md +33 -0
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/skills/spec.md +2 -6
- package/node_modules/@spexcode/spec-cli/templates/spec/project/.plugins/spec.md +7 -0
- package/node_modules/@spexcode/spec-core/dist/anchors.d.ts +13 -2
- package/node_modules/@spexcode/spec-core/dist/anchors.js +311 -15
- package/node_modules/@spexcode/spec-core/dist/git.d.ts +4 -0
- package/node_modules/@spexcode/spec-core/dist/git.js +9 -9
- package/node_modules/@spexcode/spec-core/dist/layout.d.ts +12 -0
- package/node_modules/@spexcode/spec-core/dist/layout.js +4 -2
- package/node_modules/@spexcode/spec-core/dist/resilience.d.ts +1 -0
- package/node_modules/@spexcode/spec-core/dist/resilience.js +8 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.d.ts +1 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewFilters.js +7 -3
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.d.ts +1 -0
- package/node_modules/@spexcode/spec-core/dist/review/reviewQuery.js +4 -0
- package/node_modules/@spexcode/spec-core/dist/specs.d.ts +2 -0
- package/node_modules/@spexcode/spec-core/dist/specs.js +30 -0
- package/node_modules/@spexcode/spec-core/package.json +4 -1
- package/node_modules/@spexcode/spec-eval/dist/cli.js +1 -1
- package/node_modules/@spexcode/spec-eval/dist/evaltab.d.ts +1 -0
- package/node_modules/@spexcode/spec-eval/dist/evaltab.js +6 -1
- package/node_modules/@spexcode/spec-eval/dist/freshness.d.ts +1 -1
- package/node_modules/@spexcode/spec-eval/dist/freshness.js +12 -12
- package/node_modules/@spexcode/spec-eval/dist/host.d.ts +2 -1
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.d.ts +2 -1
- package/node_modules/@spexcode/spec-eval/dist/sessioneval.js +20 -3
- package/node_modules/@spexcode/spec-eval/package.json +2 -2
- package/node_modules/@spexcode/spec-forge/package.json +2 -2
- package/node_modules/@vscode/tree-sitter-wasm/LICENSE +21 -0
- package/node_modules/@vscode/tree-sitter-wasm/README.md +36 -0
- package/node_modules/@vscode/tree-sitter-wasm/SECURITY.md +41 -0
- package/node_modules/@vscode/tree-sitter-wasm/cgmanifest.json +16 -0
- package/node_modules/@vscode/tree-sitter-wasm/package.json +42 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-bash.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-c-sharp.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-cpp.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-css.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-go.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ini.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-java.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-javascript.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-php.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-powershell.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-python.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-regex.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-ruby.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-rust.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-tsx.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-typescript.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.js +4075 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter.wasm +0 -0
- package/node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter.d.ts +1027 -0
- package/package.json +9 -3
- package/node_modules/@spexcode/session-core/dist/delivery-queue.d.ts +0 -23
- package/node_modules/@spexcode/session-core/dist/delivery-queue.js +0 -195
- package/node_modules/@spexcode/session-core/dist/index.d.ts +0 -5
- package/node_modules/@spexcode/session-core/dist/index.js +0 -5
- package/node_modules/@spexcode/session-core/dist/internal.d.ts +0 -3
- package/node_modules/@spexcode/session-core/dist/internal.js +0 -3
- package/node_modules/@spexcode/session-core/dist/message.d.ts +0 -22
- package/node_modules/@spexcode/session-core/dist/message.js +0 -52
- package/node_modules/@spexcode/session-core/dist/record-lock.js +0 -152
- package/node_modules/@spexcode/session-core/dist/runtime-session.d.ts +0 -50
- package/node_modules/@spexcode/session-core/dist/runtime-session.js +0 -286
- package/node_modules/@spexcode/session-core/dist/session-cursors.d.ts +0 -14
- package/node_modules/@spexcode/session-core/dist/session-cursors.js +0 -82
- package/node_modules/@spexcode/session-core/dist/session-timeline.d.ts +0 -46
- package/node_modules/@spexcode/session-core/dist/session-timeline.js +0 -216
- package/node_modules/@spexcode/spec-cli/hooks/compat/mark-active-0.5.2-eef1.fixture +0 -53
- package/node_modules/@spexcode/spec-cli/hooks/compat/mark-active-sed-v0.fixture +0 -46
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
1
|
+
import { dirname, join } from 'node:path';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { gitRequiredA, gitObjectFormat, isGitObjectId, batchRevisionOids, batchBlobTexts, combinedDiffOwnedChanges, driftPathWindow, readImmutableHunkFacts, persistImmutableHunkFacts, withEventLedgerBuild } from './git.js';
|
|
4
4
|
const RS = '\x1e';
|
|
@@ -50,7 +50,9 @@ export function parseRelation(raws, relation) {
|
|
|
50
50
|
}
|
|
51
51
|
return { entries: order.map((p) => ({ path: p, selectors: byPath.get(p).selectors })), problems };
|
|
52
52
|
}
|
|
53
|
-
// ---- extractor: ts-ast
|
|
53
|
+
// ---- legacy extractor: ts-ast ----
|
|
54
|
+
// Kept as a public compatibility helper only. The production language registry below routes every shipped
|
|
55
|
+
// language through Tree-sitter WASM and never selects this host-dependent path.
|
|
54
56
|
// Parse-only via the HOST project's own typescript, so the parse matches what the project itself compiles
|
|
55
57
|
// with. If it cannot resolve, ready() returns a loud unverified verdict and lint skips these anchors
|
|
56
58
|
// without crashing (no bundled compiler, regex fallback, or fake pass for JS).
|
|
@@ -310,9 +312,7 @@ export function heuristicExtractor(spec) {
|
|
|
310
312
|
},
|
|
311
313
|
};
|
|
312
314
|
}
|
|
313
|
-
//
|
|
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.
|
|
315
|
+
// Historical JS-family reference row retained for compatibility callers; not a production registry row.
|
|
316
316
|
export const JS_LANG_R5B = {
|
|
317
317
|
id: 'heuristic-js',
|
|
318
318
|
extensions: [...JS_EXTS],
|
|
@@ -330,9 +330,7 @@ export const JS_LANG_R5B = {
|
|
|
330
330
|
},
|
|
331
331
|
boundary: /^(?:[A-Za-z_$]|\/\/|\/\*)/,
|
|
332
332
|
};
|
|
333
|
-
// Python
|
|
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]]).
|
|
333
|
+
// Historical Python row retained for compatibility callers; Tree-sitter owns shipped Python anchors.
|
|
336
334
|
const PY_ID = String.raw `[\p{ID_Start}_][\p{ID_Continue}_]*`;
|
|
337
335
|
export const PYTHON_LANG = {
|
|
338
336
|
id: 'heuristic-python',
|
|
@@ -349,15 +347,313 @@ export const PYTHON_LANG = {
|
|
|
349
347
|
indentScopes: { decorator: /^\s*@/ },
|
|
350
348
|
boundary: /^\S/,
|
|
351
349
|
};
|
|
350
|
+
let treeSitterModulePromise;
|
|
351
|
+
let treeSitterInitPromise;
|
|
352
|
+
const treeSitterLanguagePromises = new Map();
|
|
353
|
+
function treeSitterRuntimePath() {
|
|
354
|
+
const require = createRequire(import.meta.url);
|
|
355
|
+
return require.resolve('@vscode/tree-sitter-wasm');
|
|
356
|
+
}
|
|
357
|
+
async function treeSitterModule() {
|
|
358
|
+
if (!treeSitterModulePromise)
|
|
359
|
+
treeSitterModulePromise = import(treeSitterRuntimePath()).then((m) => m.default ?? m);
|
|
360
|
+
return treeSitterModulePromise;
|
|
361
|
+
}
|
|
362
|
+
async function treeSitterLanguage(row) {
|
|
363
|
+
const existing = treeSitterLanguagePromises.get(row.grammar);
|
|
364
|
+
if (existing)
|
|
365
|
+
return existing;
|
|
366
|
+
const pending = (async () => {
|
|
367
|
+
const mod = await treeSitterModule();
|
|
368
|
+
if (!treeSitterInitPromise) {
|
|
369
|
+
const runtime = dirname(treeSitterRuntimePath());
|
|
370
|
+
treeSitterInitPromise = mod.Parser.init({ locateFile: (file) => join(runtime, file) }).then(() => mod);
|
|
371
|
+
}
|
|
372
|
+
await treeSitterInitPromise;
|
|
373
|
+
return mod.Language.load(join(dirname(treeSitterRuntimePath()), `tree-sitter-${row.grammar}.wasm`));
|
|
374
|
+
})();
|
|
375
|
+
treeSitterLanguagePromises.set(row.grammar, pending);
|
|
376
|
+
return pending;
|
|
377
|
+
}
|
|
378
|
+
const nodeField = (node, field) => node?.childForFieldName?.(field) ?? null;
|
|
379
|
+
const nodeChildren = (node) => (node?.namedChildren ?? []).filter(Boolean);
|
|
380
|
+
const nodeAllChildren = (node) => (node?.children ?? []).filter(Boolean);
|
|
381
|
+
const nodeText = (node) => node?.text ?? '';
|
|
382
|
+
const treeHasSyntaxError = (root) => {
|
|
383
|
+
if (root?.hasError)
|
|
384
|
+
return true;
|
|
385
|
+
const pending = [root];
|
|
386
|
+
while (pending.length) {
|
|
387
|
+
const node = pending.pop();
|
|
388
|
+
if (node.isError || node.isMissing)
|
|
389
|
+
return true;
|
|
390
|
+
pending.push(...nodeAllChildren(node));
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
};
|
|
394
|
+
const nodeLineRange = (node, start = node.startPosition.row + 1, end = node.endPosition.row + 1) => ({ start, end });
|
|
395
|
+
const simpleName = (node) => nodeText(node).replace(/^\s+|\s+$/g, '');
|
|
396
|
+
const lastTypeName = (text) => {
|
|
397
|
+
const clean = text.replace(/^\s*\(/, '').replace(/\)\s*$/, '').replace(/^\s*[*&]+/, '');
|
|
398
|
+
const base = clean.split(/[.[\]<>\s]/, 1)[0];
|
|
399
|
+
return (base.split('.').pop() ?? base).replace(/[^\p{L}\p{N}_$]/gu, '');
|
|
400
|
+
};
|
|
401
|
+
function tsTreeUnits(root) {
|
|
402
|
+
const units = [];
|
|
403
|
+
const add = (name, kind, node, typeOnly = false, start) => {
|
|
404
|
+
if (!name)
|
|
405
|
+
return;
|
|
406
|
+
units.push({ name, kind, ...nodeLineRange(node, start), ...(typeOnly ? { typeOnly: true } : {}) });
|
|
407
|
+
};
|
|
408
|
+
const methodUnits = (className, body) => {
|
|
409
|
+
for (const member of nodeChildren(body)) {
|
|
410
|
+
if (member.type !== 'method_definition' || !nodeField(member, 'body'))
|
|
411
|
+
continue;
|
|
412
|
+
const name = nodeText(nodeField(member, 'name')) || nodeText(member.namedChildren?.[0]) || '(computed)';
|
|
413
|
+
add(`${className}.${name === 'constructor' ? 'constructor' : name}`, 'method', member);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
for (const raw of nodeChildren(root)) {
|
|
417
|
+
const top = raw.type === 'export_statement' ? nodeChildren(raw) : [raw];
|
|
418
|
+
for (const node of top) {
|
|
419
|
+
if (node.type === 'function_declaration')
|
|
420
|
+
add(nodeText(nodeField(node, 'name')), 'function', node);
|
|
421
|
+
else if (node.type === 'class_declaration') {
|
|
422
|
+
const name = nodeText(nodeField(node, 'name'));
|
|
423
|
+
add(name, 'class', node);
|
|
424
|
+
methodUnits(name, nodeField(node, 'body'));
|
|
425
|
+
}
|
|
426
|
+
else if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
|
|
427
|
+
for (const decl of nodeChildren(node).filter((child) => child.type === 'variable_declarator')) {
|
|
428
|
+
const name = nodeText(nodeField(decl, 'name'));
|
|
429
|
+
if (!/^[A-Za-z_$][\w$]*$/u.test(name))
|
|
430
|
+
continue;
|
|
431
|
+
const value = nodeField(decl, 'value');
|
|
432
|
+
add(name, value?.type === 'arrow_function' || value?.type === 'function' ? 'const-fn' : 'const-data', node);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
else if (node.type === 'enum_declaration')
|
|
436
|
+
add(nodeText(nodeField(node, 'name')), 'enum', node);
|
|
437
|
+
else if (node.type === 'interface_declaration')
|
|
438
|
+
add(nodeText(nodeField(node, 'name')), 'interface', node, true);
|
|
439
|
+
else if (node.type === 'type_alias_declaration')
|
|
440
|
+
add(nodeText(nodeField(node, 'name')), 'type', node, true);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return units;
|
|
444
|
+
}
|
|
445
|
+
function pythonTreeUnits(root) {
|
|
446
|
+
const units = [];
|
|
447
|
+
const definition = (node) => node.type === 'decorated_definition'
|
|
448
|
+
? nodeChildren(node).find((child) => child.type === 'function_definition' || child.type === 'class_definition') ?? null
|
|
449
|
+
: node;
|
|
450
|
+
const visitContainer = (container, scopes) => {
|
|
451
|
+
for (const child of nodeChildren(container)) {
|
|
452
|
+
const inner = definition(child);
|
|
453
|
+
if (!inner || (inner.type !== 'function_definition' && inner.type !== 'class_definition')) {
|
|
454
|
+
visitContainer(child, scopes);
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
const name = nodeText(nodeField(inner, 'name'));
|
|
458
|
+
const qualified = [...scopes.map((scope) => scope.name), name].filter(Boolean).join('.');
|
|
459
|
+
const wrapperStart = child.type === 'decorated_definition' ? child.startPosition.row + 1 : undefined;
|
|
460
|
+
const kind = inner.type === 'class_definition' ? 'class' : (scopes.at(-1)?.classLike ? 'method' : 'function');
|
|
461
|
+
units.push({ name: qualified, kind, ...nodeLineRange(inner, wrapperStart) });
|
|
462
|
+
const body = nodeField(inner, 'body');
|
|
463
|
+
if (body)
|
|
464
|
+
visitContainer(body, [...scopes, { name, classLike: inner.type === 'class_definition' }]);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
visitContainer(root, []);
|
|
468
|
+
return units;
|
|
469
|
+
}
|
|
470
|
+
function goTreeUnits(root) {
|
|
471
|
+
const units = [];
|
|
472
|
+
for (const node of root.descendantsOfType?.(['const_declaration', 'function_declaration', 'method_declaration', 'type_declaration']) ?? []) {
|
|
473
|
+
if (node.type === 'const_declaration') {
|
|
474
|
+
for (const spec of nodeChildren(node).filter((child) => child.type === 'const_spec')) {
|
|
475
|
+
const name = nodeText(nodeField(spec, 'name')) || nodeText(nodeChildren(spec)[0]);
|
|
476
|
+
if (name)
|
|
477
|
+
units.push({ name, kind: 'const-data', ...nodeLineRange(spec) });
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
else if (node.type === 'function_declaration') {
|
|
481
|
+
units.push({ name: nodeText(nodeField(node, 'name')), kind: 'function', ...nodeLineRange(node) });
|
|
482
|
+
}
|
|
483
|
+
else if (node.type === 'method_declaration') {
|
|
484
|
+
const receiver = nodeField(node, 'receiver');
|
|
485
|
+
const parameter = nodeChildren(receiver).find((child) => child.type === 'parameter_declaration');
|
|
486
|
+
const receiverType = nodeField(parameter, 'type') ?? nodeChildren(parameter)[1];
|
|
487
|
+
const receiverName = lastTypeName(nodeText(receiverType));
|
|
488
|
+
const methodName = nodeText(nodeField(node, 'name'));
|
|
489
|
+
units.push({ name: receiverName ? `${receiverName}.${methodName}` : methodName, kind: 'method', ...nodeLineRange(node) });
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
for (const spec of nodeChildren(node).filter((child) => child.type === 'type_spec')) {
|
|
493
|
+
const name = nodeText(nodeField(spec, 'name')) || nodeText(nodeChildren(spec)[0]);
|
|
494
|
+
const shape = nodeChildren(spec).find((child) => ['struct_type', 'interface_type'].includes(child.type));
|
|
495
|
+
const kind = shape?.type === 'struct_type' ? 'struct' : shape?.type === 'interface_type' ? 'interface' : 'type';
|
|
496
|
+
if (name)
|
|
497
|
+
units.push({ name, kind, ...nodeLineRange(spec) });
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return units;
|
|
502
|
+
}
|
|
503
|
+
function rustImplName(node) {
|
|
504
|
+
const header = nodeText(node).split('{', 1)[0];
|
|
505
|
+
const forMatch = header.match(/\bfor\s+([A-Za-z_][\w:]*)/u);
|
|
506
|
+
if (forMatch)
|
|
507
|
+
return forMatch[1].split('::').pop() ?? forMatch[1];
|
|
508
|
+
const names = nodeChildren(node).filter((child) => child.type === 'type_identifier' || child.type === 'scoped_type_identifier');
|
|
509
|
+
return lastTypeName(nodeText(names[0]));
|
|
510
|
+
}
|
|
511
|
+
function rustTreeUnits(root) {
|
|
512
|
+
const units = [];
|
|
513
|
+
for (const node of root.descendantsOfType?.(['const_item', 'function_item', 'struct_item', 'enum_item', 'trait_item']) ?? []) {
|
|
514
|
+
const name = nodeText(nodeField(node, 'name')) || nodeText(nodeChildren(node).find((child) => child.type === 'type_identifier'));
|
|
515
|
+
if (!name)
|
|
516
|
+
continue;
|
|
517
|
+
const parent = node.parent;
|
|
518
|
+
let impl = parent;
|
|
519
|
+
while (impl && impl.type !== 'impl_item' && impl.type !== 'source_file')
|
|
520
|
+
impl = impl.parent;
|
|
521
|
+
const implName = impl?.type === 'impl_item' ? rustImplName(impl) : '';
|
|
522
|
+
const kind = node.type === 'function_item' ? (implName ? 'method' : 'function')
|
|
523
|
+
: node.type === 'const_item' ? 'const-data'
|
|
524
|
+
: node.type === 'struct_item' ? 'struct'
|
|
525
|
+
: node.type === 'enum_item' ? 'enum' : 'trait';
|
|
526
|
+
units.push({ name: implName && node.type === 'function_item' ? `${implName}.${name}` : name, kind, ...nodeLineRange(node) });
|
|
527
|
+
}
|
|
528
|
+
return units;
|
|
529
|
+
}
|
|
530
|
+
function javaTreeUnits(root) {
|
|
531
|
+
const units = [];
|
|
532
|
+
const visitClass = (node, scopes) => {
|
|
533
|
+
const name = nodeText(nodeField(node, 'name')) || nodeText(nodeChildren(node).find((child) => child.type === 'identifier'));
|
|
534
|
+
if (!name)
|
|
535
|
+
return;
|
|
536
|
+
const qualified = [...scopes, name].join('.');
|
|
537
|
+
const kind = node.type === 'class_declaration' ? 'class' : node.type === 'interface_declaration' ? 'interface' : 'enum';
|
|
538
|
+
units.push({ name: qualified, kind, ...nodeLineRange(node) });
|
|
539
|
+
const body = nodeField(node, 'body');
|
|
540
|
+
for (const child of nodeChildren(body)) {
|
|
541
|
+
if (child.type === 'field_declaration' && /\bfinal\b/u.test(nodeText(child))) {
|
|
542
|
+
for (const variable of nodeChildren(child).filter((part) => part.type === 'variable_declarator')) {
|
|
543
|
+
const field = nodeText(nodeField(variable, 'name')) || nodeText(nodeChildren(variable).find((part) => part.type === 'identifier'));
|
|
544
|
+
if (field)
|
|
545
|
+
units.push({ name: `${qualified}.${field}`, kind: 'const-data', ...nodeLineRange(child) });
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
else if (child.type === 'method_declaration' || child.type === 'constructor_declaration') {
|
|
549
|
+
const method = nodeText(nodeField(child, 'name')) || nodeText(nodeChildren(child).find((part) => part.type === 'identifier'));
|
|
550
|
+
if (method)
|
|
551
|
+
units.push({ name: `${qualified}.${child.type === 'constructor_declaration' ? 'constructor' : method}`, kind: 'method', ...nodeLineRange(child) });
|
|
552
|
+
}
|
|
553
|
+
else if (child.type.endsWith('_declaration') && ['class_declaration', 'interface_declaration', 'enum_declaration'].includes(child.type))
|
|
554
|
+
visitClass(child, [...scopes, name]);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
const walk = (node) => {
|
|
558
|
+
for (const child of nodeChildren(node)) {
|
|
559
|
+
if (['class_declaration', 'interface_declaration', 'enum_declaration'].includes(child.type))
|
|
560
|
+
visitClass(child, []);
|
|
561
|
+
else
|
|
562
|
+
walk(child);
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
walk(root);
|
|
566
|
+
return units;
|
|
567
|
+
}
|
|
568
|
+
function rubyTreeUnits(root) {
|
|
569
|
+
const units = [];
|
|
570
|
+
const rubyName = (name) => name.replaceAll('::', '.');
|
|
571
|
+
const nameOf = (node) => rubyName(nodeText(nodeField(node, 'name')) || nodeText(nodeChildren(node).find((child) => ['constant', 'identifier'].includes(child.type))));
|
|
572
|
+
const visit = (container, scopes) => {
|
|
573
|
+
for (const child of nodeChildren(container)) {
|
|
574
|
+
if (child.type === 'class' || child.type === 'module') {
|
|
575
|
+
const name = nameOf(child);
|
|
576
|
+
if (!name)
|
|
577
|
+
continue;
|
|
578
|
+
const qualified = [...scopes.map((scope) => scope.name), name].join('.');
|
|
579
|
+
units.push({ name: qualified, kind: child.type === 'class' ? 'class' : 'module', ...nodeLineRange(child) });
|
|
580
|
+
visit(nodeField(child, 'body') ?? child, [...scopes, { name, kind: child.type }]);
|
|
581
|
+
}
|
|
582
|
+
else if (child.type === 'assignment' && nodeField(child, 'left')?.type === 'constant') {
|
|
583
|
+
const name = rubyName(nodeText(nodeField(child, 'left')));
|
|
584
|
+
if (name)
|
|
585
|
+
units.push({ name: [...scopes.map((scope) => scope.name), name].join('.'), kind: 'const-data', ...nodeLineRange(child) });
|
|
586
|
+
}
|
|
587
|
+
else if (child.type === 'method' || child.type === 'singleton_method') {
|
|
588
|
+
const name = nameOf(child);
|
|
589
|
+
if (!name)
|
|
590
|
+
continue;
|
|
591
|
+
const receiver = child.type === 'singleton_method' ? rubyName(nodeText(nodeField(child, 'object'))) : '';
|
|
592
|
+
const qualified = receiver && receiver !== 'self' ? `${receiver}.${name}` : [...scopes.map((scope) => scope.name), name].join('.');
|
|
593
|
+
units.push({ name: qualified, kind: child.type === 'singleton_method' || scopes.length ? 'method' : 'function', ...nodeLineRange(child) });
|
|
594
|
+
}
|
|
595
|
+
else
|
|
596
|
+
visit(child, scopes);
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
visit(root, []);
|
|
600
|
+
return units;
|
|
601
|
+
}
|
|
602
|
+
export const TREE_SITTER_ROWS = [
|
|
603
|
+
{ id: 'tree-sitter-typescript', extensions: ['ts', 'mts', 'cts'], grammar: 'typescript', schema: 'ts-v1', units: tsTreeUnits },
|
|
604
|
+
{ id: 'tree-sitter-tsx', extensions: ['tsx'], grammar: 'tsx', schema: 'tsx-v1', units: tsTreeUnits },
|
|
605
|
+
{ id: 'tree-sitter-javascript', extensions: ['js', 'jsx', 'mjs', 'cjs'], grammar: 'javascript', schema: 'js-v1', units: tsTreeUnits },
|
|
606
|
+
{ id: 'tree-sitter-python', extensions: ['py', 'pyi'], grammar: 'python', schema: 'python-v1', units: pythonTreeUnits },
|
|
607
|
+
{ id: 'tree-sitter-go', extensions: ['go'], grammar: 'go', schema: 'go-v1', units: goTreeUnits },
|
|
608
|
+
{ id: 'tree-sitter-rust', extensions: ['rs'], grammar: 'rust', schema: 'rust-v1', units: rustTreeUnits },
|
|
609
|
+
{ id: 'tree-sitter-java', extensions: ['java'], grammar: 'java', schema: 'java-v1', units: javaTreeUnits },
|
|
610
|
+
{ id: 'tree-sitter-ruby', extensions: ['rb'], grammar: 'ruby', schema: 'ruby-v1', units: rubyTreeUnits },
|
|
611
|
+
];
|
|
612
|
+
export function treeSitterExtractor(row) {
|
|
613
|
+
let readiness;
|
|
614
|
+
const getLanguage = () => treeSitterLanguage(row);
|
|
615
|
+
return {
|
|
616
|
+
id: row.id,
|
|
617
|
+
claims: (ext) => row.extensions.includes(ext),
|
|
618
|
+
async ready() {
|
|
619
|
+
if (readiness !== undefined)
|
|
620
|
+
return readiness;
|
|
621
|
+
try {
|
|
622
|
+
await getLanguage();
|
|
623
|
+
readiness = true;
|
|
624
|
+
}
|
|
625
|
+
catch (error) {
|
|
626
|
+
readiness = `Tree-sitter extractor '${row.id}' cannot load grammar '${row.grammar}': ${error?.message ?? String(error)} — reinstall SpexCode or remove the #anchor`;
|
|
627
|
+
}
|
|
628
|
+
return readiness;
|
|
629
|
+
},
|
|
630
|
+
async extract(content, filename) {
|
|
631
|
+
const language = await getLanguage();
|
|
632
|
+
const mod = await treeSitterModule();
|
|
633
|
+
const parser = new mod.Parser().setLanguage(language);
|
|
634
|
+
let tree;
|
|
635
|
+
try {
|
|
636
|
+
tree = parser.parse(content);
|
|
637
|
+
if (!tree?.rootNode || treeHasSyntaxError(tree.rootNode))
|
|
638
|
+
throw new Error(`${filename} has Tree-sitter syntax errors`);
|
|
639
|
+
return row.units(tree.rootNode);
|
|
640
|
+
}
|
|
641
|
+
finally {
|
|
642
|
+
tree?.delete?.();
|
|
643
|
+
parser.delete?.();
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
memoKey(filename) { return JSON.stringify({ schema: 'tree-sitter-wasm-v1', row: row.id, grammar: row.grammar, rowSchema: row.schema, filename }); },
|
|
647
|
+
};
|
|
648
|
+
}
|
|
352
649
|
// ---- registry: extension -> its ONE designated extractor ----
|
|
353
|
-
// The registry
|
|
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.
|
|
650
|
+
// The registry is one Tree-sitter language adapter: every shipped extension is a grammar DATA row.
|
|
356
651
|
export function extractors(root) {
|
|
357
|
-
|
|
652
|
+
void root;
|
|
653
|
+
return TREE_SITTER_ROWS.map(treeSitterExtractor);
|
|
358
654
|
}
|
|
359
655
|
// 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
|
|
656
|
+
// for this language yet (lint ERRORS — the remedy is a Tree-sitter language row, or dropping the anchor).
|
|
361
657
|
export function extractorFor(regs, ext) {
|
|
362
658
|
return regs.find((x) => x.claims(ext)) ?? null;
|
|
363
659
|
}
|
|
@@ -417,7 +713,7 @@ async function unitsAtFileRevision(commit, path, x, objectFormat, oid, text) {
|
|
|
417
713
|
throw new Error(`git cat-file --batch omitted object ${oid} for ${commit}:${path}`);
|
|
418
714
|
let result;
|
|
419
715
|
try {
|
|
420
|
-
result = { units: x.extract(text, path) };
|
|
716
|
+
result = { units: await x.extract(text, path) };
|
|
421
717
|
}
|
|
422
718
|
catch (e) {
|
|
423
719
|
result = { unparseable: e?.message ?? String(e) };
|
|
@@ -626,7 +922,7 @@ async function runAnchorQueriesInLedger(root, queries, regs, stopAtFirstHit) {
|
|
|
626
922
|
continue;
|
|
627
923
|
const ref = revisions.get(key), oid = oidByRef.get(key) ?? null;
|
|
628
924
|
const x = extractorFor(regs, extOf(ref.path));
|
|
629
|
-
const ready = x
|
|
925
|
+
const ready = x ? await x.ready() : undefined;
|
|
630
926
|
if (!x || ready !== true) {
|
|
631
927
|
units.set(key, { unparseable: !x ? `no designated extractor for ${ref.path}` : String(ready) });
|
|
632
928
|
continue;
|
|
@@ -153,6 +153,10 @@ export type ReviewDiffFile = {
|
|
|
153
153
|
additions: number;
|
|
154
154
|
deletions: number;
|
|
155
155
|
};
|
|
156
|
+
export declare function parseStatPath(token: string): {
|
|
157
|
+
from: string;
|
|
158
|
+
to: string;
|
|
159
|
+
};
|
|
156
160
|
export declare function mergeBaseDiff(wtPath: string, mainRef?: string, headRef?: string): Promise<ReviewDiffFile[]>;
|
|
157
161
|
export declare function mergeConflicts(wtPath: string, mainRef?: string, headRef?: string): Promise<boolean>;
|
|
158
162
|
type WorktreeSpecDemand = {
|
|
@@ -230,15 +230,15 @@ export async function batchRevisionOids(root, revisions, options = {}) {
|
|
|
230
230
|
});
|
|
231
231
|
}
|
|
232
232
|
export async function batchBlobTexts(root, oids) {
|
|
233
|
-
const
|
|
233
|
+
const objectIds = [...new Set(oids.filter(Boolean))];
|
|
234
234
|
const files = new Map();
|
|
235
|
-
if (!
|
|
235
|
+
if (!objectIds.length)
|
|
236
236
|
return files;
|
|
237
|
-
for (const oid of
|
|
237
|
+
for (const oid of objectIds)
|
|
238
238
|
if (!isGitObjectId(root, oid))
|
|
239
239
|
throw new Error(`invalid object id '${oid}'`);
|
|
240
|
-
for (let cursor = 0; cursor <
|
|
241
|
-
const chunk =
|
|
240
|
+
for (let cursor = 0; cursor < objectIds.length; cursor += BATCH_BLOB_CHUNK) {
|
|
241
|
+
const chunk = objectIds.slice(cursor, cursor + BATCH_BLOB_CHUNK);
|
|
242
242
|
const out = await batchBuffer(['-C', root, 'cat-file', '--batch'], chunk.join('\n') + '\n', BATCH_BLOB_MAX_BUFFER);
|
|
243
243
|
let offset = 0;
|
|
244
244
|
for (const oid of chunk) {
|
|
@@ -1609,12 +1609,12 @@ function canonicalPathProjector(renamesByFrom, topology) {
|
|
|
1609
1609
|
continue;
|
|
1610
1610
|
}
|
|
1611
1611
|
seen.add(candidate);
|
|
1612
|
-
const
|
|
1612
|
+
const applicableByIdentity = new Map();
|
|
1613
1613
|
for (const rename of renamesByFrom.get(candidate) ?? []) {
|
|
1614
1614
|
if (!precedes(rename.hash, event))
|
|
1615
|
-
|
|
1615
|
+
applicableByIdentity.set(`${rename.hash}\0${rename.to}`, rename);
|
|
1616
1616
|
}
|
|
1617
|
-
const applicable = [...
|
|
1617
|
+
const applicable = [...applicableByIdentity.values()];
|
|
1618
1618
|
if (!applicable.length) {
|
|
1619
1619
|
resolved.add(candidate);
|
|
1620
1620
|
continue;
|
|
@@ -2531,7 +2531,7 @@ function parseNameStatus(out) {
|
|
|
2531
2531
|
return rows;
|
|
2532
2532
|
}
|
|
2533
2533
|
const DIFF_STATUS = { A: 'added', M: 'modified', D: 'deleted', R: 'renamed', C: 'copied', T: 'type-changed' };
|
|
2534
|
-
function parseStatPath(token) {
|
|
2534
|
+
export function parseStatPath(token) {
|
|
2535
2535
|
const b = token.indexOf('{'), arrow = token.indexOf(' => ', b), close = token.indexOf('}', arrow);
|
|
2536
2536
|
if (b >= 0 && arrow > b && close > arrow) {
|
|
2537
2537
|
const pre = token.slice(0, b), post = token.slice(close + 1);
|
|
@@ -111,8 +111,10 @@ export type RawRecord = {
|
|
|
111
111
|
createdAt: number;
|
|
112
112
|
harness?: string;
|
|
113
113
|
harness_session_id?: string;
|
|
114
|
+
runtime_start_token?: string;
|
|
114
115
|
stopped?: boolean;
|
|
115
116
|
archived?: boolean;
|
|
117
|
+
closed_at?: string;
|
|
116
118
|
cold_proof?: string;
|
|
117
119
|
adapter_recovery?: string;
|
|
118
120
|
launcher?: string;
|
|
@@ -125,6 +127,15 @@ export type RawRecord = {
|
|
|
125
127
|
runtime_revision?: string;
|
|
126
128
|
runtime_metadata?: Record<string, string>;
|
|
127
129
|
base?: string;
|
|
130
|
+
diff_comments?: Array<{
|
|
131
|
+
id: string;
|
|
132
|
+
file_path: string;
|
|
133
|
+
line_start: number;
|
|
134
|
+
line_end: number;
|
|
135
|
+
body: string;
|
|
136
|
+
diff_identity: string;
|
|
137
|
+
sent_at: string | null;
|
|
138
|
+
}>;
|
|
128
139
|
launch_readiness_pending?: '' | RawLaunchReadinessPending;
|
|
129
140
|
};
|
|
130
141
|
export declare const SESSION_LIFECYCLES: readonly ["active", "idle", "awaiting", "parked", "error", "asking", "queued"];
|
|
@@ -139,6 +150,7 @@ export type RawLaunchReadinessOriginal = {
|
|
|
139
150
|
note: string | null;
|
|
140
151
|
stopped: boolean;
|
|
141
152
|
archived: boolean;
|
|
153
|
+
closed_at?: string | null;
|
|
142
154
|
cold_proof: string | null;
|
|
143
155
|
adapter_recovery: string | null;
|
|
144
156
|
};
|
|
@@ -136,7 +136,7 @@ export function mainRoot(proj) {
|
|
|
136
136
|
}
|
|
137
137
|
// @@@ global per-session store - Fork A: NO SpexCode files live in the worktree any more, so the worktree's
|
|
138
138
|
// spec/code tree is pristine (zero per-session pollution). Every per-session runtime artifact — the
|
|
139
|
-
//
|
|
139
|
+
// runtime envelope (runtime.json) AND the launcher products (prompt, launch, launch.sh) AND the recorded comms AND
|
|
140
140
|
// the spec-discipline sentinels — lives in a per-USER GLOBAL store, keyed by the harness `session_id` so two
|
|
141
141
|
// agents in one folder never clobber, and grouped PER PROJECT (mirroring Claude's ~/.claude/projects/<enc>/)
|
|
142
142
|
// so the board enumerates ONE directory. This is the single seam that knows where the store sits; sessions.ts
|
|
@@ -169,7 +169,7 @@ export function treeSlotDir(wt) {
|
|
|
169
169
|
// all keyed by session_id under <home>/projects/<enc>/sessions/.
|
|
170
170
|
export function sessionsRoot() { return join(runtimeRoot(), 'sessions'); }
|
|
171
171
|
export function sessionStoreDir(id) { return join(sessionsRoot(), id); }
|
|
172
|
-
export function sessionRecordPath(id) { return join(sessionStoreDir(id), '
|
|
172
|
+
export function sessionRecordPath(id) { return join(sessionStoreDir(id), 'runtime.json'); }
|
|
173
173
|
export function sessionArtifactPath(id, name) { return join(sessionStoreDir(id), name); }
|
|
174
174
|
export const SESSION_LIFECYCLES = ['active', 'idle', 'awaiting', 'parked', 'error', 'asking', 'queued'];
|
|
175
175
|
export const SESSION_PROPOSALS = ['merge', 'nothing', 'close'];
|
|
@@ -190,6 +190,7 @@ export function rawLaunchReadinessOriginal(raw) {
|
|
|
190
190
|
|| !(original.proposal === null || original.proposal === '' || isSessionProposal(original.proposal))
|
|
191
191
|
|| !(typeof original.note === 'string' || original.note === null)
|
|
192
192
|
|| typeof original.stopped !== 'boolean' || typeof original.archived !== 'boolean'
|
|
193
|
+
|| !(typeof original.closed_at === 'string' || original.closed_at === null || original.closed_at === undefined)
|
|
193
194
|
|| !(typeof original.cold_proof === 'string' || original.cold_proof === null)
|
|
194
195
|
|| !(typeof original.adapter_recovery === 'string' || original.adapter_recovery === null)) {
|
|
195
196
|
throw new Error(`session '${raw.session_id}' has an invalid launch_readiness_pending fence`);
|
|
@@ -281,6 +282,7 @@ export function projectPublicRecordEntry(id, entry) {
|
|
|
281
282
|
note: original.note || null,
|
|
282
283
|
stopped: original.stopped,
|
|
283
284
|
archived: original.archived,
|
|
285
|
+
closed_at: original.closed_at ?? undefined,
|
|
284
286
|
cold_proof: original.cold_proof ?? undefined,
|
|
285
287
|
adapter_recovery: original.adapter_recovery ?? undefined,
|
|
286
288
|
launch_readiness_pending: '',
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
2
3
|
function describe(e) {
|
|
3
4
|
return e instanceof Error ? e.message : String(e);
|
|
4
5
|
}
|
|
6
|
+
// Delayed close deletion lives below this directory and is never a live worktree input.
|
|
7
|
+
export function isTrashWorktreePath(path) {
|
|
8
|
+
const normalized = resolve(path);
|
|
9
|
+
const parts = normalized.split(/[\\/]+/);
|
|
10
|
+
const worktrees = parts.lastIndexOf('.worktrees');
|
|
11
|
+
return worktrees >= 0 && parts[worktrees + 1] === '.trash';
|
|
12
|
+
}
|
|
5
13
|
// run a per-worktree DETAIL read; on a throw, branch on whether the worktree DIRECTORY still exists.
|
|
6
14
|
// dir gone → the worktree was genuinely removed mid-read → return null so the caller omits it.
|
|
7
15
|
// dir present → a flaky detail read (ENOENT race on a sibling file, or a git index/ref lock under a
|
|
@@ -11,6 +11,7 @@ export const EVAL_FILTER_KIND = Object.freeze({
|
|
|
11
11
|
RESULT: 'result',
|
|
12
12
|
BLIND: 'blind',
|
|
13
13
|
UNMEASURED: 'unmeasured',
|
|
14
|
+
DEFERRED: 'deferred',
|
|
14
15
|
DANGLING: 'dangling',
|
|
15
16
|
});
|
|
16
17
|
export const evidenceList = (reading) => reading?.evidence?.length ? reading.evidence
|
|
@@ -146,9 +147,11 @@ export function issueFilterModel(items, raw = {}, context = {}) {
|
|
|
146
147
|
const evalIsResult = (entry) => entry.filterKind === EVAL_FILTER_KIND.RESULT;
|
|
147
148
|
const verdictOf = (entry) => evalIsResult(entry)
|
|
148
149
|
? (entry.verdict?.status || 'unscored')
|
|
149
|
-
: entry.filterKind === EVAL_FILTER_KIND.
|
|
150
|
-
? '
|
|
151
|
-
:
|
|
150
|
+
: entry.filterKind === EVAL_FILTER_KIND.DEFERRED
|
|
151
|
+
? 'deferred'
|
|
152
|
+
: entry.filterKind === EVAL_FILTER_KIND.BLIND || entry.filterKind === EVAL_FILTER_KIND.UNMEASURED
|
|
153
|
+
? 'unmeasured'
|
|
154
|
+
: 'unscored';
|
|
152
155
|
const reviewStateOf = (entry) => (evalIsResult(entry) && entry.fresh && entry.humanOk ? 'reviewed' : 'current');
|
|
153
156
|
// the ONE freshness axis of the Eval adapter — the facet's option values and the verdict sections' split
|
|
154
157
|
// read it, so a chip and its Freshness menu can never disagree about what "stale" counts.
|
|
@@ -194,6 +197,7 @@ const EVAL_CONFIG = {
|
|
|
194
197
|
{ value: 'fail', label: 'reviewList.verdict.fail', split: true },
|
|
195
198
|
{ value: 'pass', label: 'reviewList.verdict.pass', split: true },
|
|
196
199
|
{ value: 'unmeasured', label: 'reviewList.verdict.unmeasured' },
|
|
200
|
+
{ value: 'deferred', label: 'reviewList.verdict.deferred' },
|
|
197
201
|
],
|
|
198
202
|
},
|
|
199
203
|
facets: [
|
|
@@ -31,6 +31,7 @@ export const ISSUE_QUERY_DEFAULT: "is:issue state:open";
|
|
|
31
31
|
export const EVAL_QUERY_DEFAULT: "is:eval";
|
|
32
32
|
export function scopedEvalQuery(sessionId: any): any;
|
|
33
33
|
export function nodeEvalQuery(nodeId: any): any;
|
|
34
|
+
export function nodeIssueQuery(nodeId: any): any;
|
|
34
35
|
export function quoteValue(v: any): string;
|
|
35
36
|
export function tokenize(text: any): ({
|
|
36
37
|
ws: boolean;
|
|
@@ -8,6 +8,10 @@ export const EVAL_QUERY_DEFAULT = 'is:eval';
|
|
|
8
8
|
export const scopedEvalQuery = (sessionId) => setToken(EVAL_QUERY_DEFAULT, 'scope', sessionId);
|
|
9
9
|
// the aggregate score/count doors' address ([[eval-score-badge]]): the default view, node-filtered.
|
|
10
10
|
export const nodeEvalQuery = (nodeId) => setToken(EVAL_QUERY_DEFAULT, 'node', nodeId);
|
|
11
|
+
// a node's OPEN issues ([[context-dock]]): the issue list's default view, node-filtered. Same shape and
|
|
12
|
+
// same reason as the eval twin — a surface that wants "this node's issues" asks for a text, never for a
|
|
13
|
+
// second filter path, so the panel and the list it links to are literally the same query.
|
|
14
|
+
export const nodeIssueQuery = (nodeId) => setToken(ISSUE_QUERY_DEFAULT, 'node', nodeId);
|
|
11
15
|
const KEY_RE = /^([A-Za-z][A-Za-z0-9-]*):(.*)$/s;
|
|
12
16
|
const unquote = (v) => (v.length >= 2 && v.startsWith('"') && v.endsWith('"') ? v.slice(1, -1) : v);
|
|
13
17
|
export const quoteValue = (v) => (/\s/.test(String(v)) ? `"${v}"` : String(v));
|
|
@@ -9,6 +9,7 @@ export type SpecParts = {
|
|
|
9
9
|
expandedSpec: string;
|
|
10
10
|
};
|
|
11
11
|
declare function parseParts(body: string): SpecParts | null;
|
|
12
|
+
export declare function bodyMentions(body: string): string[];
|
|
12
13
|
export type DerivedStatus = 'pending' | 'active' | 'merged' | 'drift';
|
|
13
14
|
export declare function deriveStatus(d: {
|
|
14
15
|
version: number;
|
|
@@ -43,6 +44,7 @@ export declare function specContent(id: string): {
|
|
|
43
44
|
body: string;
|
|
44
45
|
parts: ReturnType<typeof parseParts>;
|
|
45
46
|
} | null;
|
|
47
|
+
export declare function specDir(id: string): string | null;
|
|
46
48
|
export type LoadSpecsOptions = {
|
|
47
49
|
tip?: string;
|
|
48
50
|
history?: HistoryIndex | null;
|
|
@@ -68,6 +68,29 @@ function parseParts(body) {
|
|
|
68
68
|
const t = (a) => a.join('\n').trim();
|
|
69
69
|
return { rawSource: t(acc.rawSource), expandedSpec: t(acc.expandedSpec) };
|
|
70
70
|
}
|
|
71
|
+
// the `[[id]]` reference grammar as a spec BODY writes it. Its consumer is the lint rule that rejects a
|
|
72
|
+
// dangling reference — the one place a `[[name]]` has to be judged against the node universe. It is a
|
|
73
|
+
// parser and not a projection: the loader once shipped the surviving ids as a `mentions` edge on every
|
|
74
|
+
// node, and that edge is gone, because a prose mention is a fact about the GRAPH and never was a fact
|
|
75
|
+
// about the node the reader has open ([[context-dock]]). Prose only: a fenced block or an inline
|
|
76
|
+
// `code span` is sample text (`[[node]]`, `[[<id>]]` placeholders live there), not a reference. Distinct,
|
|
77
|
+
// in first-appearance order; whether a name resolves to a real node is the caller's judgement.
|
|
78
|
+
const MENTION_RE = /\[\[(\.?[\p{L}\p{N}_-]+)\]\]/gu;
|
|
79
|
+
export function bodyMentions(body) {
|
|
80
|
+
const out = new Set();
|
|
81
|
+
let inFence = false;
|
|
82
|
+
for (const rawLine of body.split('\n')) {
|
|
83
|
+
if (/^\s*```/.test(rawLine)) {
|
|
84
|
+
inFence = !inFence;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (inFence)
|
|
88
|
+
continue;
|
|
89
|
+
for (const m of rawLine.replace(/`[^`]*`/g, '').matchAll(MENTION_RE))
|
|
90
|
+
out.add(m[1]);
|
|
91
|
+
}
|
|
92
|
+
return [...out];
|
|
93
|
+
}
|
|
71
94
|
export function deriveStatus(d) {
|
|
72
95
|
if (d.fmStatus === 'pending' && !d.hasCode && d.drift === 0)
|
|
73
96
|
return 'pending';
|
|
@@ -228,6 +251,13 @@ export function specContent(id) {
|
|
|
228
251
|
const r = raws().find((x) => x.id === id);
|
|
229
252
|
return r ? { body: r.body.trim(), parts: parseParts(r.body) } : null;
|
|
230
253
|
}
|
|
254
|
+
// Where a node LIVES, repo-relative — its own folder, not its spec.md. A node's folder is the unit (the
|
|
255
|
+
// same rule the plugin instances are built on), so anything that wants to see what a node carries besides
|
|
256
|
+
// its body asks the spec tree's own reader rather than re-deriving a path from an id.
|
|
257
|
+
export function specDir(id) {
|
|
258
|
+
const r = raws().find((x) => x.id === id);
|
|
259
|
+
return r ? r.relPath.replace(/\/spec\.md$/, '') : null;
|
|
260
|
+
}
|
|
231
261
|
export async function loadSpecs(root = ROOT, options = {}) {
|
|
232
262
|
// The default pair shares one immutable-event snapshot; explicit sides let callers skip or supply either
|
|
233
263
|
// projection. Every node below is then a pure in-memory lookup.
|