@shanepadgett/tau-agent 0.33.1 → 0.34.0
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/docs/context.md +29 -20
- package/docs/extending-tau-agent.md +63 -24
- package/extensions/appshot/index.ts +112 -111
- package/extensions/aside/README.md +7 -0
- package/extensions/aside/index.ts +168 -0
- package/extensions/aside/panel.ts +83 -0
- package/extensions/branch/index.ts +38 -21
- package/extensions/cache-diagnostics/index.ts +70 -58
- package/extensions/checkpoint/README.md +9 -0
- package/extensions/checkpoint/checkpoint-budget.ts +79 -0
- package/extensions/checkpoint/checkpoint.ts +277 -0
- package/extensions/checkpoint/index.ts +102 -0
- package/extensions/checkpoint/messages.ts +169 -0
- package/extensions/checkpoint/prompt.ts +24 -0
- package/extensions/checkpoint/settings.ts +27 -0
- package/extensions/commit/commit-effort.ts +3 -3
- package/extensions/context/README.md +12 -8
- package/extensions/context/definitions.ts +178 -55
- package/extensions/context/index.ts +192 -160
- package/extensions/context/panel.ts +57 -37
- package/extensions/context/sync.ts +139 -89
- package/extensions/context/validation.ts +37 -25
- package/extensions/effort/README.md +1 -1
- package/extensions/effort/index.ts +3 -3
- package/extensions/effort/state.ts +3 -18
- package/extensions/explore/guidance.ts +6 -47
- package/extensions/explore/index.ts +38 -53
- package/extensions/explore/{ast/read → read}/hook.ts +12 -7
- package/extensions/explore/{ast/tools → tools}/ast-search.ts +15 -19
- package/extensions/explore/{ast/tools → tools}/context.ts +16 -23
- package/extensions/explore/{ast/tools → tools}/deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/discover.ts +15 -17
- package/extensions/explore/{ast/tools → tools}/impact.ts +20 -23
- package/extensions/explore/{ast/tools → tools}/outline.ts +21 -24
- package/extensions/explore/{ast/tools → tools}/relationships.ts +23 -32
- package/extensions/explore/{ast/tools → tools}/render.ts +1 -1
- package/extensions/explore/{ast/tools → tools}/reverse-deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/show.ts +16 -19
- package/extensions/explore/tools/target-params.ts +9 -0
- package/extensions/footer/index.ts +75 -56
- package/extensions/handoff/index.ts +8 -30
- package/extensions/image-gen/index.ts +46 -40
- package/extensions/patch/executor.ts +190 -135
- package/extensions/patch/matcher.ts +82 -48
- package/extensions/patch/parser.ts +149 -105
- package/extensions/patch/render.ts +32 -39
- package/extensions/patch/summary.ts +30 -26
- package/extensions/ready/README.md +7 -0
- package/extensions/ready/index.ts +58 -0
- package/extensions/ready/model.ts +128 -0
- package/extensions/ready/packs.ts +272 -0
- package/extensions/ready/render.ts +445 -0
- package/extensions/ready/scan.ts +760 -0
- package/extensions/reference/panel.ts +28 -16
- package/extensions/review/index.ts +63 -51
- package/extensions/review/panel.ts +5 -34
- package/extensions/runtime-context/context.ts +30 -26
- package/extensions/script-runner/README.md +2 -2
- package/extensions/script-runner/index.ts +122 -82
- package/extensions/silent-command-runner/index.ts +38 -23
- package/extensions/subagent/agents/context-sync.md +185 -33
- package/extensions/subagent/agents/scout.md +2 -2
- package/extensions/subagent/agents.ts +137 -84
- package/extensions/subagent/cmux-dashboard.ts +39 -30
- package/extensions/subagent/index.ts +79 -59
- package/extensions/subagent/resume.ts +1 -1
- package/extensions/subagent/run.ts +304 -193
- package/extensions/subagent/runtime.ts +781 -343
- package/extensions/tau/index.ts +47 -38
- package/extensions/tau-help/help.md +14 -6
- package/extensions/tool-loader/README.md +4 -2
- package/extensions/tool-loader/index.ts +77 -52
- package/extensions/web/index.ts +6 -3
- package/extensions/web/webfetch.ts +114 -86
- package/package.json +3 -3
- package/schemas/tau.schema.json +20 -33
- package/shared/bounded-text-result.ts +1 -0
- package/shared/checkpoint-visibility.ts +9 -0
- package/shared/events.ts +28 -10
- package/shared/isolated-session.ts +71 -29
- package/shared/model-effort.ts +9 -19
- package/shared/model-fallback/index.ts +66 -41
- package/shared/settings/load.ts +45 -38
- package/shared/temporary-output-store.ts +16 -10
- package/{extensions/explore → src}/ast/adapter.ts +11 -2
- package/{extensions/explore → src}/ast/engine.ts +54 -38
- package/{extensions/explore → src}/ast/format/ast-search.ts +15 -6
- package/{extensions/explore → src}/ast/format/context.ts +3 -6
- package/src/ast/format/deps.ts +94 -0
- package/{extensions/explore → src}/ast/format/discover.ts +2 -2
- package/{extensions/explore → src}/ast/format/impact.ts +9 -11
- package/{extensions/explore → src}/ast/format/outline.ts +2 -2
- package/{extensions/explore → src}/ast/format/relationships.ts +11 -7
- package/{extensions/explore → src}/ast/format/show.ts +1 -1
- package/src/ast/format/targeted-result.ts +20 -0
- package/src/ast/grammars/c_sharp.wasm +0 -0
- package/{extensions/explore → src}/ast/grammars/manifest.json +9 -12
- package/{extensions/explore → src}/ast/grammars/manifest.ts +0 -4
- package/{extensions/explore → src}/ast/graph/file-graph.ts +210 -98
- package/{extensions/explore → src}/ast/graph/relationships.ts +285 -72
- package/{extensions/explore → src}/ast/ir.ts +6 -0
- package/src/ast/languages/csharp-file-deps.ts +90 -0
- package/{extensions/explore → src}/ast/languages/csharp.ts +9 -40
- package/src/ast/languages/dotted-index.ts +131 -0
- package/{extensions/explore → src}/ast/languages/file-dep-util.ts +22 -13
- package/src/ast/languages/fixtures/sample.cs +53 -0
- package/src/ast/languages/fixtures/sample.cs.expected.json +156 -0
- package/src/ast/languages/fixtures/sample.go +37 -0
- package/src/ast/languages/fixtures/sample.go.expected.json +96 -0
- package/src/ast/languages/fixtures/sample.java +40 -0
- package/src/ast/languages/fixtures/sample.java.expected.json +126 -0
- package/src/ast/languages/fixtures/sample.kt +31 -0
- package/src/ast/languages/fixtures/sample.kt.expected.json +134 -0
- package/src/ast/languages/fixtures/sample.md +23 -0
- package/src/ast/languages/fixtures/sample.md.expected.json +44 -0
- package/src/ast/languages/fixtures/sample.rs +54 -0
- package/src/ast/languages/fixtures/sample.rs.expected.json +178 -0
- package/src/ast/languages/fixtures/sample.swift +45 -0
- package/src/ast/languages/fixtures/sample.swift.expected.json +147 -0
- package/src/ast/languages/fixtures/sample.ts +45 -0
- package/src/ast/languages/fixtures/sample.ts.expected.json +141 -0
- package/src/ast/languages/fixtures/sample.tsx +36 -0
- package/src/ast/languages/fixtures/sample.tsx.expected.json +59 -0
- package/{extensions/explore → src}/ast/languages/go-file-deps.ts +6 -0
- package/{extensions/explore → src}/ast/languages/go.ts +21 -3
- package/{extensions/explore → src}/ast/languages/java.ts +5 -38
- package/src/ast/languages/jvm-file-deps.ts +135 -0
- package/{extensions/explore → src}/ast/languages/kotlin.ts +5 -39
- package/{extensions/explore → src}/ast/languages/rust-file-deps.ts +3 -3
- package/{extensions/explore → src}/ast/languages/rust.ts +21 -13
- package/{extensions/explore → src}/ast/languages/swift-file-deps.ts +22 -15
- package/{extensions/explore → src}/ast/languages/swift.ts +9 -39
- package/{extensions/explore → src}/ast/languages/tree.ts +47 -0
- package/{extensions/explore → src}/ast/languages/typescript-file-deps.ts +108 -0
- package/{extensions/explore → src}/ast/languages/typescript-package-surface.ts +4 -3
- package/{extensions/explore → src}/ast/languages/typescript.ts +3 -5
- package/{extensions/explore → src}/ast/markdown.ts +72 -47
- package/{extensions/explore → src}/ast/queries/ast-search.ts +164 -146
- package/{extensions/explore → src}/ast/queries/composite-target.ts +1 -1
- package/{extensions/explore → src}/ast/queries/context.ts +116 -80
- package/{extensions/explore → src}/ast/queries/discover.ts +154 -105
- package/src/ast/queries/impact.ts +291 -0
- package/{extensions/explore → src}/ast/queries/outline.ts +4 -2
- package/{extensions/explore → src}/ast/queries/show.ts +46 -42
- package/{extensions/explore → src}/ast/query.ts +1 -1
- package/{extensions/explore → src}/ast/registry.ts +1 -2
- package/{extensions/explore → src}/ast/scan.ts +1 -1
- package/src/ast/session.ts +50 -0
- package/{extensions/explore → src}/ast/slice.ts +8 -0
- package/{extensions/explore → src/ast}/traverse.ts +32 -20
- package/src/file-injection/index.ts +547 -0
- package/src/image-generation/client.ts +50 -31
- package/src/image-generation/index.ts +177 -134
- package/src/index.ts +2 -0
- package/src/tool-loading/index.ts +58 -0
- package/extensions/context/evidence.ts +0 -517
- package/extensions/context/projection.ts +0 -141
- package/extensions/context/state.ts +0 -30
- package/extensions/context/write-scope.ts +0 -109
- package/extensions/explore/ast/format/deps.ts +0 -86
- package/extensions/explore/ast/grammars/odin.wasm +0 -0
- package/extensions/explore/ast/languages/csharp-file-deps.ts +0 -189
- package/extensions/explore/ast/languages/fixtures/sample.cs +0 -17
- package/extensions/explore/ast/languages/fixtures/sample.java +0 -21
- package/extensions/explore/ast/languages/fixtures/sample.kt +0 -15
- package/extensions/explore/ast/languages/fixtures/sample.odin +0 -28
- package/extensions/explore/ast/languages/fixtures/sample.rs +0 -31
- package/extensions/explore/ast/languages/fixtures/sample.swift +0 -25
- package/extensions/explore/ast/languages/jvm-file-deps.ts +0 -217
- package/extensions/explore/ast/languages/odin-file-deps.ts +0 -175
- package/extensions/explore/ast/languages/odin.ts +0 -407
- package/extensions/explore/ast/queries/impact.ts +0 -250
- package/extensions/explore/outline-injection.ts +0 -151
- package/extensions/explore/read/autoread.ts +0 -118
- package/extensions/working-memory/README.md +0 -17
- package/extensions/working-memory/checkpoint.ts +0 -242
- package/extensions/working-memory/index.ts +0 -341
- package/extensions/working-memory/memory.ts +0 -235
- package/extensions/working-memory/render.ts +0 -185
- package/extensions/working-memory/settings.ts +0 -38
- package/extensions/working-memory/state.ts +0 -158
- package/shared/autoread.ts +0 -234
- package/shared/context-messages.ts +0 -19
- package/shared/outline-injection.ts +0 -56
- /package/{extensions/explore → src}/ast/format/composite.ts +0 -0
- /package/{extensions/explore → src}/ast/grammars/kotlin.wasm +0 -0
- /package/{extensions/explore → src}/ast/grammars/swift.wasm +0 -0
- /package/{extensions/explore → src}/ast/identity.ts +0 -0
- /package/{extensions/explore → src}/ast/package-surface.ts +0 -0
- /package/{extensions/explore/ast/read/policy.ts → src/ast/read-policy.ts} +0 -0
|
@@ -182,13 +182,19 @@ async function snapsFromCalleeSites(
|
|
|
182
182
|
): Promise<DeclSnap[]> {
|
|
183
183
|
const out: DeclSnap[] = [];
|
|
184
184
|
const seen = new Set<string>();
|
|
185
|
-
const seenNames = new Set<string>();
|
|
186
185
|
for (const site of sites) {
|
|
187
186
|
signal.throwIfAborted();
|
|
188
|
-
if (site.
|
|
189
|
-
|
|
190
|
-
|
|
187
|
+
if (site.target === undefined) continue;
|
|
188
|
+
const resolution = await resolveTarget(
|
|
189
|
+
engine,
|
|
190
|
+
scopeDir,
|
|
191
|
+
{ path: site.target.path, name: site.name, line: site.target.startLine },
|
|
192
|
+
signal,
|
|
193
|
+
);
|
|
191
194
|
if (resolution.kind !== "resolved") continue;
|
|
195
|
+
if (site.kind === "construct" ? !isTypeLike(resolution.decl.kind) : !isCallableLike(resolution.decl.kind)) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
192
198
|
const item: DeclSnap = {
|
|
193
199
|
path: resolution.path,
|
|
194
200
|
decl: resolution.decl,
|
|
@@ -261,9 +267,13 @@ function packInto(
|
|
|
261
267
|
}
|
|
262
268
|
}
|
|
263
269
|
|
|
264
|
-
|
|
270
|
+
/**
|
|
271
|
+
* Type-like targets print signature plus members (the `methods` group covers the
|
|
272
|
+
* bodies); callable targets print their own body.
|
|
273
|
+
*/
|
|
274
|
+
function packTarget(state: PackState, snap: DeclSnap, targetKind: DeclKind): void {
|
|
265
275
|
const left = state.budget - state.used;
|
|
266
|
-
const preferBody = !bodyUnavailable(snap);
|
|
276
|
+
const preferBody = !bodyUnavailable(snap) && !isTypeLike(targetKind);
|
|
267
277
|
if (preferBody) {
|
|
268
278
|
const body = entryFrom(snap, "body", []);
|
|
269
279
|
if (body.tokens <= left && body.text.length > 0) {
|
|
@@ -273,7 +283,7 @@ function packTarget(state: PackState, snap: DeclSnap): void {
|
|
|
273
283
|
return;
|
|
274
284
|
}
|
|
275
285
|
}
|
|
276
|
-
const flags: ContextFlag[] = ["target_omitted"];
|
|
286
|
+
const flags: ContextFlag[] = isTypeLike(targetKind) ? [] : ["target_omitted"];
|
|
277
287
|
if (bodyUnavailable(snap)) flags.push("body_unavailable");
|
|
278
288
|
const signature = entryFrom(snap, "signature", flags);
|
|
279
289
|
if (signature.tokens <= left && signature.text.length > 0) {
|
|
@@ -289,6 +299,102 @@ function packTarget(state: PackState, snap: DeclSnap): void {
|
|
|
289
299
|
* Budgeted symbol pack. Bodies/signatures via show view extraction.
|
|
290
300
|
* Depth-2 is a capped second resolve+query pass — no relationship depth param.
|
|
291
301
|
*/
|
|
302
|
+
async function collectDepth2(
|
|
303
|
+
args: ContextQueryArgs,
|
|
304
|
+
state: PackState,
|
|
305
|
+
snaps: DeclSnap[],
|
|
306
|
+
mode: "callees" | "callers",
|
|
307
|
+
scopeDir: string,
|
|
308
|
+
followUps: { count: number },
|
|
309
|
+
): Promise<DeclSnap[]> {
|
|
310
|
+
const { engine, signal } = args;
|
|
311
|
+
const out: DeclSnap[] = [];
|
|
312
|
+
for (const snap of snaps) {
|
|
313
|
+
if (followUps.count >= CONTEXT_FOLLOWUP_N || state.used >= state.budget) break;
|
|
314
|
+
signal.throwIfAborted();
|
|
315
|
+
followUps.count += 1;
|
|
316
|
+
const sites = await relSites(args, mode, {
|
|
317
|
+
path: snap.path,
|
|
318
|
+
name: snap.decl.name,
|
|
319
|
+
qualifiedName: snap.decl.qualifiedName,
|
|
320
|
+
kind: snap.decl.kind,
|
|
321
|
+
startLine: snap.decl.startLine,
|
|
322
|
+
});
|
|
323
|
+
const items =
|
|
324
|
+
mode === "callees"
|
|
325
|
+
? await snapsFromCalleeSites(engine, scopeDir, sites, signal)
|
|
326
|
+
: await snapsFromSites(engine, sites, signal, (d) => isCallableLike(d.kind));
|
|
327
|
+
for (const item of items) {
|
|
328
|
+
if (!state.seen.has(snapKey(item))) out.push(item);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return out;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function packCallableContext(
|
|
335
|
+
args: ContextQueryArgs,
|
|
336
|
+
state: PackState,
|
|
337
|
+
target: CompositeTarget,
|
|
338
|
+
scopeDir: string,
|
|
339
|
+
): Promise<void> {
|
|
340
|
+
const { engine, signal } = args;
|
|
341
|
+
const calleeSites = await relSites(args, "callees", target);
|
|
342
|
+
const calleeSnaps = await snapsFromCalleeSites(engine, scopeDir, calleeSites, signal);
|
|
343
|
+
packInto(state, "callees", "direct callees", calleeSnaps, true);
|
|
344
|
+
|
|
345
|
+
const callerSites = await relSites(args, "callers", target);
|
|
346
|
+
const callerSnaps = await snapsFromSites(engine, callerSites, signal, (d) => isCallableLike(d.kind));
|
|
347
|
+
packInto(state, "callers", "direct callers", callerSnaps, false);
|
|
348
|
+
|
|
349
|
+
const followUps = { count: 0 };
|
|
350
|
+
const depth2Callees = await collectDepth2(args, state, calleeSnaps, "callees", scopeDir, followUps);
|
|
351
|
+
packInto(state, "calleesDepth2", "depth-2 callees", depth2Callees, false);
|
|
352
|
+
const depth2Callers = await collectDepth2(args, state, callerSnaps, "callers", scopeDir, followUps);
|
|
353
|
+
packInto(state, "callersDepth2", "depth-2 callers", depth2Callers, false);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function packTypeContext(
|
|
357
|
+
args: ContextQueryArgs,
|
|
358
|
+
state: PackState,
|
|
359
|
+
target: CompositeTarget,
|
|
360
|
+
decl: Decl,
|
|
361
|
+
ir: FileIr,
|
|
362
|
+
source: string,
|
|
363
|
+
): Promise<void> {
|
|
364
|
+
const { engine, signal } = args;
|
|
365
|
+
const implSites = await relSites(args, "implementations", target);
|
|
366
|
+
const implSnaps = await snapsFromSites(engine, implSites, signal, (d) => isTypeLike(d.kind));
|
|
367
|
+
packInto(state, "implementors", "implementors", implSnaps, true);
|
|
368
|
+
|
|
369
|
+
const methods: DeclSnap[] = [];
|
|
370
|
+
for (const child of decl.children) {
|
|
371
|
+
if (!isCallableLike(child.kind)) continue;
|
|
372
|
+
if (methods.length >= CONTEXT_METHOD_CAP) break;
|
|
373
|
+
methods.push({ path: target.path, decl: child, ir, source });
|
|
374
|
+
}
|
|
375
|
+
packInto(state, "methods", "methods", methods, true);
|
|
376
|
+
|
|
377
|
+
const dependents: DeclSnap[] = [];
|
|
378
|
+
for (const method of methods) {
|
|
379
|
+
if (state.used >= state.budget) break;
|
|
380
|
+
signal.throwIfAborted();
|
|
381
|
+
const sites = await relSites(args, "callers", {
|
|
382
|
+
path: method.path,
|
|
383
|
+
name: method.decl.name,
|
|
384
|
+
qualifiedName: method.decl.qualifiedName,
|
|
385
|
+
kind: method.decl.kind,
|
|
386
|
+
startLine: method.decl.startLine,
|
|
387
|
+
});
|
|
388
|
+
// Method-name matching collects unrelated same-name callers; ambiguous
|
|
389
|
+
// entries would spend budget correct ones need.
|
|
390
|
+
const certain = sites.filter((site) => site.certainty !== "ambiguous");
|
|
391
|
+
for (const item of await snapsFromSites(engine, certain, signal, (d) => isCallableLike(d.kind))) {
|
|
392
|
+
if (!state.seen.has(snapKey(item))) dependents.push(item);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
packInto(state, "dependents", "dependents", dependents, false);
|
|
396
|
+
}
|
|
397
|
+
|
|
292
398
|
export async function queryContext(args: ContextQueryArgs): Promise<ContextResult> {
|
|
293
399
|
const { engine, signal } = args;
|
|
294
400
|
if (args.budget < 1) {
|
|
@@ -317,82 +423,12 @@ export async function queryContext(args: ContextQueryArgs): Promise<ContextResul
|
|
|
317
423
|
groups: [],
|
|
318
424
|
};
|
|
319
425
|
|
|
320
|
-
packTarget(state, targetSnap);
|
|
426
|
+
packTarget(state, targetSnap, kind);
|
|
321
427
|
|
|
322
428
|
if (isCallableLike(kind)) {
|
|
323
|
-
|
|
324
|
-
const calleeSnaps = await snapsFromCalleeSites(engine, scopeDir, calleeSites, signal);
|
|
325
|
-
packInto(state, "callees", "direct callees", calleeSnaps, true);
|
|
326
|
-
|
|
327
|
-
const callerSites = await relSites(args, "callers", target);
|
|
328
|
-
const callerSnaps = await snapsFromSites(engine, callerSites, signal, (d) => isCallableLike(d.kind));
|
|
329
|
-
packInto(state, "callers", "direct callers", callerSnaps, false);
|
|
330
|
-
|
|
331
|
-
let followUps = 0;
|
|
332
|
-
const depth2Callees: DeclSnap[] = [];
|
|
333
|
-
for (const snap of calleeSnaps) {
|
|
334
|
-
if (followUps >= CONTEXT_FOLLOWUP_N || state.used >= state.budget) break;
|
|
335
|
-
signal.throwIfAborted();
|
|
336
|
-
followUps += 1;
|
|
337
|
-
const sites = await relSites(args, "callees", {
|
|
338
|
-
path: snap.path,
|
|
339
|
-
name: snap.decl.name,
|
|
340
|
-
qualifiedName: snap.decl.qualifiedName,
|
|
341
|
-
kind: snap.decl.kind,
|
|
342
|
-
startLine: snap.decl.startLine,
|
|
343
|
-
});
|
|
344
|
-
for (const item of await snapsFromCalleeSites(engine, scopeDir, sites, signal)) {
|
|
345
|
-
if (!state.seen.has(snapKey(item))) depth2Callees.push(item);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
packInto(state, "calleesDepth2", "depth-2 callees", depth2Callees, false);
|
|
349
|
-
|
|
350
|
-
const depth2Callers: DeclSnap[] = [];
|
|
351
|
-
for (const snap of callerSnaps) {
|
|
352
|
-
if (followUps >= CONTEXT_FOLLOWUP_N || state.used >= state.budget) break;
|
|
353
|
-
signal.throwIfAborted();
|
|
354
|
-
followUps += 1;
|
|
355
|
-
const sites = await relSites(args, "callers", {
|
|
356
|
-
path: snap.path,
|
|
357
|
-
name: snap.decl.name,
|
|
358
|
-
qualifiedName: snap.decl.qualifiedName,
|
|
359
|
-
kind: snap.decl.kind,
|
|
360
|
-
startLine: snap.decl.startLine,
|
|
361
|
-
});
|
|
362
|
-
for (const item of await snapsFromSites(engine, sites, signal, (d) => isCallableLike(d.kind))) {
|
|
363
|
-
if (!state.seen.has(snapKey(item))) depth2Callers.push(item);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
packInto(state, "callersDepth2", "depth-2 callers", depth2Callers, false);
|
|
429
|
+
await packCallableContext(args, state, target, scopeDir);
|
|
367
430
|
} else {
|
|
368
|
-
|
|
369
|
-
const implSnaps = await snapsFromSites(engine, implSites, signal, (d) => isTypeLike(d.kind));
|
|
370
|
-
packInto(state, "implementors", "implementors", implSnaps, true);
|
|
371
|
-
|
|
372
|
-
const methods: DeclSnap[] = [];
|
|
373
|
-
for (const child of decl.children) {
|
|
374
|
-
if (!isCallableLike(child.kind)) continue;
|
|
375
|
-
if (methods.length >= CONTEXT_METHOD_CAP) break;
|
|
376
|
-
methods.push({ path: target.path, decl: child, ir, source });
|
|
377
|
-
}
|
|
378
|
-
packInto(state, "methods", "methods", methods, true);
|
|
379
|
-
|
|
380
|
-
const dependents: DeclSnap[] = [];
|
|
381
|
-
for (const method of methods) {
|
|
382
|
-
if (state.used >= state.budget) break;
|
|
383
|
-
signal.throwIfAborted();
|
|
384
|
-
const sites = await relSites(args, "callers", {
|
|
385
|
-
path: method.path,
|
|
386
|
-
name: method.decl.name,
|
|
387
|
-
qualifiedName: method.decl.qualifiedName,
|
|
388
|
-
kind: method.decl.kind,
|
|
389
|
-
startLine: method.decl.startLine,
|
|
390
|
-
});
|
|
391
|
-
for (const item of await snapsFromSites(engine, sites, signal, (d) => isCallableLike(d.kind))) {
|
|
392
|
-
if (!state.seen.has(snapKey(item))) dependents.push(item);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
packInto(state, "dependents", "dependents", dependents, false);
|
|
431
|
+
await packTypeContext(args, state, target, decl, ir, source);
|
|
396
432
|
}
|
|
397
433
|
|
|
398
434
|
return {
|
|
@@ -4,7 +4,7 @@ import type { Decl, DeclKind, Visibility } from "../ir.ts";
|
|
|
4
4
|
import { packageDeclKey, type PackageSurfaceHost } from "../package-surface.ts";
|
|
5
5
|
import { scanSources, type ScanOutcome } from "../scan.ts";
|
|
6
6
|
import { docsText, signatureText } from "../slice.ts";
|
|
7
|
-
import { pathResolutionError, resolveExplorePath } from "
|
|
7
|
+
import { pathResolutionError, resolveExplorePath } from "../traverse.ts";
|
|
8
8
|
|
|
9
9
|
export type DiscoverSurface = "all" | "public" | "private" | "sourceExport" | "packageSurface";
|
|
10
10
|
|
|
@@ -54,10 +54,14 @@ function nameFields(decl: Decl): [string, string] {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function matchesNameQuery(kind: "exactName" | "prefixName" | "substringName", needle: string, decl: Decl): boolean {
|
|
57
|
+
// Every language in the sweep capitalizes types and lowercases functions, so
|
|
58
|
+
// non-exact matching folds case; exactName stays exact.
|
|
59
|
+
const folded = needle.toLowerCase();
|
|
57
60
|
for (const field of nameFields(decl)) {
|
|
58
61
|
if (kind === "exactName" && field === needle) return true;
|
|
59
|
-
|
|
60
|
-
if (kind === "
|
|
62
|
+
const lower = field.toLowerCase();
|
|
63
|
+
if (kind === "prefixName" && lower.startsWith(folded)) return true;
|
|
64
|
+
if (kind === "substringName" && lower.includes(folded)) return true;
|
|
61
65
|
}
|
|
62
66
|
return false;
|
|
63
67
|
}
|
|
@@ -87,6 +91,20 @@ function fuzzyScore(query: string, text: string): number | undefined {
|
|
|
87
91
|
return dist === undefined ? undefined : 1000 + dist;
|
|
88
92
|
}
|
|
89
93
|
|
|
94
|
+
function levenshteinRow(prev: number[], cur: number[], b: string, ca: number, maxDist: number): number {
|
|
95
|
+
let rowMin = cur[0] ?? maxDist + 1;
|
|
96
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
97
|
+
const cost = ca === b.charCodeAt(j - 1) ? 0 : 1;
|
|
98
|
+
const del = (prev[j] ?? maxDist + 1) + 1;
|
|
99
|
+
const ins = (cur[j - 1] ?? maxDist + 1) + 1;
|
|
100
|
+
const sub = (prev[j - 1] ?? maxDist + 1) + cost;
|
|
101
|
+
const v = Math.min(del, ins, sub);
|
|
102
|
+
cur[j] = v;
|
|
103
|
+
if (v < rowMin) rowMin = v;
|
|
104
|
+
}
|
|
105
|
+
return rowMin;
|
|
106
|
+
}
|
|
107
|
+
|
|
90
108
|
function boundedLevenshtein(a: string, b: string, maxDist: number): number | undefined {
|
|
91
109
|
if (Math.abs(a.length - b.length) > maxDist) return undefined;
|
|
92
110
|
const prev = Array.from({ length: b.length + 1 }, () => 0);
|
|
@@ -94,17 +112,7 @@ function boundedLevenshtein(a: string, b: string, maxDist: number): number | und
|
|
|
94
112
|
for (let j = 0; j <= b.length; j += 1) prev[j] = j;
|
|
95
113
|
for (let i = 1; i <= a.length; i += 1) {
|
|
96
114
|
cur[0] = i;
|
|
97
|
-
|
|
98
|
-
const ca = a.charCodeAt(i - 1);
|
|
99
|
-
for (let j = 1; j <= b.length; j += 1) {
|
|
100
|
-
const cost = ca === b.charCodeAt(j - 1) ? 0 : 1;
|
|
101
|
-
const del = (prev[j] ?? maxDist + 1) + 1;
|
|
102
|
-
const ins = (cur[j - 1] ?? maxDist + 1) + 1;
|
|
103
|
-
const sub = (prev[j - 1] ?? maxDist + 1) + cost;
|
|
104
|
-
const v = Math.min(del, ins, sub);
|
|
105
|
-
cur[j] = v;
|
|
106
|
-
if (v < rowMin) rowMin = v;
|
|
107
|
-
}
|
|
115
|
+
const rowMin = levenshteinRow(prev, cur, b, a.charCodeAt(i - 1), maxDist);
|
|
108
116
|
if (rowMin > maxDist) return undefined;
|
|
109
117
|
for (let j = 0; j <= b.length; j += 1) prev[j] = cur[j] ?? 0;
|
|
110
118
|
}
|
|
@@ -113,11 +121,14 @@ function boundedLevenshtein(a: string, b: string, maxDist: number): number | und
|
|
|
113
121
|
}
|
|
114
122
|
|
|
115
123
|
function bestFuzzyScore(query: string, decl: Decl): number | undefined {
|
|
124
|
+
const folded = query.toLowerCase();
|
|
116
125
|
let best: number | undefined;
|
|
117
126
|
for (const field of nameFields(decl)) {
|
|
118
|
-
const score = fuzzyScore(
|
|
127
|
+
const score = fuzzyScore(folded, field.toLowerCase());
|
|
119
128
|
if (score === undefined) continue;
|
|
120
|
-
|
|
129
|
+
// Same-case hits still sort ahead of case-folded ones inside a tier.
|
|
130
|
+
const adjusted = field.includes(query) ? score : score + 0.5;
|
|
131
|
+
if (best === undefined || adjusted < best) best = adjusted;
|
|
121
132
|
}
|
|
122
133
|
return best;
|
|
123
134
|
}
|
|
@@ -201,6 +212,57 @@ function newBucket(query: DiscoverQuery): MatchBucket {
|
|
|
201
212
|
};
|
|
202
213
|
}
|
|
203
214
|
|
|
215
|
+
function consumeWork(bucket: MatchBucket, maxWork: number): boolean {
|
|
216
|
+
bucket.workUsed += 1;
|
|
217
|
+
if (bucket.workUsed <= maxWork) return true;
|
|
218
|
+
bucket.workLimitReached = true;
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function considerFuzzy(
|
|
223
|
+
bucket: MatchBucket,
|
|
224
|
+
query: Extract<DiscoverQuery, { kind: "fuzzyName" }>,
|
|
225
|
+
path: string,
|
|
226
|
+
decl: Decl,
|
|
227
|
+
source: string,
|
|
228
|
+
access: string | undefined,
|
|
229
|
+
): void {
|
|
230
|
+
if (!consumeWork(bucket, query.maxWork)) return;
|
|
231
|
+
const score = bestFuzzyScore(query.name, decl);
|
|
232
|
+
if (score === undefined) return;
|
|
233
|
+
const candidate = toCandidate(path, decl, source, access);
|
|
234
|
+
const worst = bucket.scored[bucket.scored.length - 1];
|
|
235
|
+
if (bucket.scored.length >= query.maxCandidates && worst !== undefined && score >= worst.score) {
|
|
236
|
+
bucket.candidateLimitReached = true;
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const before = bucket.scored.length;
|
|
240
|
+
insertScored(bucket.scored, { score, candidate }, query.maxCandidates);
|
|
241
|
+
if (before === query.maxCandidates) bucket.candidateLimitReached = true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function considerDocumentation(
|
|
245
|
+
bucket: MatchBucket,
|
|
246
|
+
query: Extract<DiscoverQuery, { kind: "documentation" }>,
|
|
247
|
+
path: string,
|
|
248
|
+
decl: Decl,
|
|
249
|
+
source: string,
|
|
250
|
+
access: string | undefined,
|
|
251
|
+
): void {
|
|
252
|
+
if (decl.docStartOffset === undefined || decl.docEndOffset === undefined) return;
|
|
253
|
+
if (!consumeWork(bucket, query.maxWork)) return;
|
|
254
|
+
const docs = docsText(decl, source);
|
|
255
|
+
if (docs === undefined) return;
|
|
256
|
+
const foldedDocs = docs.toLowerCase();
|
|
257
|
+
if (!query.terms.every((term) => foldedDocs.includes(term.toLowerCase()))) return;
|
|
258
|
+
if (bucket.scored.length >= query.maxCandidates) {
|
|
259
|
+
bucket.candidateLimitReached = true;
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
bucket.scored.push({ score: bucket.scored.length, candidate: toCandidate(path, decl, source, access) });
|
|
263
|
+
if (bucket.scored.length >= query.maxCandidates) bucket.candidateLimitReached = true;
|
|
264
|
+
}
|
|
265
|
+
|
|
204
266
|
function considerDecl(
|
|
205
267
|
bucket: MatchBucket,
|
|
206
268
|
query: DiscoverQuery,
|
|
@@ -231,41 +293,11 @@ function considerDecl(
|
|
|
231
293
|
bucket.plain.push(toCandidate(path, decl, source, access));
|
|
232
294
|
return;
|
|
233
295
|
}
|
|
234
|
-
case "fuzzyName":
|
|
235
|
-
bucket
|
|
236
|
-
if (bucket.workUsed > query.maxWork) {
|
|
237
|
-
bucket.workLimitReached = true;
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
const score = bestFuzzyScore(query.name, decl);
|
|
241
|
-
if (score === undefined) return;
|
|
242
|
-
const candidate = toCandidate(path, decl, source, access);
|
|
243
|
-
const worst = bucket.scored[bucket.scored.length - 1];
|
|
244
|
-
if (bucket.scored.length >= query.maxCandidates && worst !== undefined && score >= worst.score) {
|
|
245
|
-
bucket.candidateLimitReached = true;
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
const before = bucket.scored.length;
|
|
249
|
-
insertScored(bucket.scored, { score, candidate }, query.maxCandidates);
|
|
250
|
-
if (before === query.maxCandidates) bucket.candidateLimitReached = true;
|
|
296
|
+
case "fuzzyName":
|
|
297
|
+
considerFuzzy(bucket, query, path, decl, source, access);
|
|
251
298
|
return;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (decl.docStartOffset === undefined || decl.docEndOffset === undefined) return;
|
|
255
|
-
bucket.workUsed += 1;
|
|
256
|
-
if (bucket.workUsed > query.maxWork) {
|
|
257
|
-
bucket.workLimitReached = true;
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
const docs = docsText(decl, source);
|
|
261
|
-
if (docs === undefined || !query.terms.every((term) => docs.includes(term))) return;
|
|
262
|
-
if (bucket.scored.length >= query.maxCandidates) {
|
|
263
|
-
bucket.candidateLimitReached = true;
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
bucket.scored.push({ score: bucket.scored.length, candidate: toCandidate(path, decl, source, access) });
|
|
267
|
-
if (bucket.scored.length >= query.maxCandidates) bucket.candidateLimitReached = true;
|
|
268
|
-
}
|
|
299
|
+
case "documentation":
|
|
300
|
+
considerDocumentation(bucket, query, path, decl, source, access);
|
|
269
301
|
}
|
|
270
302
|
}
|
|
271
303
|
|
|
@@ -338,68 +370,68 @@ function packageSurfaceHost(engine: ExploreEngine): PackageSurfaceHost {
|
|
|
338
370
|
};
|
|
339
371
|
}
|
|
340
372
|
|
|
341
|
-
|
|
342
|
-
export async function discover(
|
|
373
|
+
async function discoverPackageSurface(
|
|
343
374
|
engine: ExploreEngine,
|
|
344
|
-
|
|
375
|
+
absolutePath: string,
|
|
345
376
|
query: DiscoverQuery,
|
|
346
|
-
surface: DiscoverSurface,
|
|
347
377
|
resultLimit: number,
|
|
348
378
|
signal: AbortSignal,
|
|
349
379
|
): Promise<DiscoverResult> {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
for (const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (graph === undefined) continue;
|
|
372
|
-
anyPackage = true;
|
|
373
|
-
filesVisited += graph.filesVisited;
|
|
374
|
-
for (const key of graph.declKeys) packageKeys.add(key);
|
|
375
|
-
for (const [key, access] of graph.accessByDecl) packageAccess.set(key, access);
|
|
376
|
-
for (const path of graph.paths) paths.add(path);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (!anyPackage) {
|
|
380
|
-
throw new Error("packageSurface: no package found for a supporting language near the scope path");
|
|
381
|
-
}
|
|
380
|
+
const resolvers = engine.registry.packageSurfaceResolvers();
|
|
381
|
+
if (resolvers.length === 0) {
|
|
382
|
+
throw new Error("packageSurface capability is not available");
|
|
383
|
+
}
|
|
384
|
+
const host = packageSurfaceHost(engine);
|
|
385
|
+
const packageKeys = new Set<string>();
|
|
386
|
+
const packageAccess = new Map<string, string>();
|
|
387
|
+
const paths = new Set<string>();
|
|
388
|
+
let filesVisited = 0;
|
|
389
|
+
let anyPackage = false;
|
|
390
|
+
|
|
391
|
+
for (const resolve of resolvers) {
|
|
392
|
+
signal.throwIfAborted();
|
|
393
|
+
const graph = await resolve(absolutePath, host, signal);
|
|
394
|
+
if (graph === undefined) continue;
|
|
395
|
+
anyPackage = true;
|
|
396
|
+
filesVisited += graph.filesVisited;
|
|
397
|
+
for (const key of graph.declKeys) packageKeys.add(key);
|
|
398
|
+
for (const [key, access] of graph.accessByDecl) packageAccess.set(key, access);
|
|
399
|
+
for (const path of graph.paths) paths.add(path);
|
|
400
|
+
}
|
|
382
401
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const file = await engine.sourceForFile(path);
|
|
387
|
-
walkFileDecls(bucket, query, file, "packageSurface", packageKeys, packageAccess);
|
|
388
|
-
}
|
|
402
|
+
if (!anyPackage) {
|
|
403
|
+
throw new Error("packageSurface: no package found for a supporting language near the scope path");
|
|
404
|
+
}
|
|
389
405
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
sourceBytes: 0,
|
|
397
|
-
elapsedMs: 0,
|
|
398
|
-
filesEmitted: paths.size,
|
|
399
|
-
},
|
|
400
|
-
};
|
|
406
|
+
const bucket = newBucket(query);
|
|
407
|
+
for (const path of paths) {
|
|
408
|
+
signal.throwIfAborted();
|
|
409
|
+
if (bucket.workLimitReached) break;
|
|
410
|
+
const file = await engine.sourceForFile(path);
|
|
411
|
+
walkFileDecls(bucket, query, file, "packageSurface", packageKeys, packageAccess);
|
|
401
412
|
}
|
|
402
413
|
|
|
414
|
+
return {
|
|
415
|
+
...finalizeBucket(bucket, resultLimit),
|
|
416
|
+
scan: {
|
|
417
|
+
limit: undefined,
|
|
418
|
+
filesVisited,
|
|
419
|
+
sourceBytes: 0,
|
|
420
|
+
elapsedMs: 0,
|
|
421
|
+
filesEmitted: paths.size,
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function discoverViaScan(
|
|
427
|
+
engine: ExploreEngine,
|
|
428
|
+
absolutePath: string,
|
|
429
|
+
query: DiscoverQuery,
|
|
430
|
+
surface: DiscoverSurface,
|
|
431
|
+
resultLimit: number,
|
|
432
|
+
signal: AbortSignal,
|
|
433
|
+
): Promise<DiscoverResult> {
|
|
434
|
+
const bucket = newBucket(query);
|
|
403
435
|
const scan = scanSources({ engine, cwd: engine.cwd, root: absolutePath, signal });
|
|
404
436
|
let step = await scan.next();
|
|
405
437
|
let filesEmitted = 0;
|
|
@@ -421,7 +453,24 @@ export async function discover(
|
|
|
421
453
|
filesEmitted,
|
|
422
454
|
};
|
|
423
455
|
if (outcome.limit === "cancelled") throw new Error("discover cancelled");
|
|
456
|
+
return { ...finalizeBucket(bucket, resultLimit), scan: outcome };
|
|
457
|
+
}
|
|
424
458
|
|
|
425
|
-
|
|
426
|
-
|
|
459
|
+
/** Directory-scoped declaration discovery. Language rules stay on adapters. */
|
|
460
|
+
export async function discover(
|
|
461
|
+
engine: ExploreEngine,
|
|
462
|
+
pathInput: string,
|
|
463
|
+
query: DiscoverQuery,
|
|
464
|
+
surface: DiscoverSurface,
|
|
465
|
+
resultLimit: number,
|
|
466
|
+
signal: AbortSignal,
|
|
467
|
+
): Promise<DiscoverResult> {
|
|
468
|
+
signal.throwIfAborted();
|
|
469
|
+
const absolutePath = resolveExplorePath(engine.cwd, pathInput);
|
|
470
|
+
await pathIsDirectory(absolutePath, pathInput);
|
|
471
|
+
|
|
472
|
+
if (surface === "packageSurface") {
|
|
473
|
+
return discoverPackageSurface(engine, absolutePath, query, resultLimit, signal);
|
|
474
|
+
}
|
|
475
|
+
return discoverViaScan(engine, absolutePath, query, surface, resultLimit, signal);
|
|
427
476
|
}
|