@mrclrchtr/supi-code-intelligence 6.4.0 → 7.0.1
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 +164 -82
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/README.md +35 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +6 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +43 -35
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +83 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +108 -92
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +44 -82
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +3 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +9 -68
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +45 -58
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +230 -331
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +201 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +129 -70
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-typescript.ts +395 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +8 -12
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +685 -183
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +60 -60
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-request-enrollment.ts +75 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-barrier.ts +443 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-errors.ts +30 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-file.ts +15 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +290 -37
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +72 -16
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +30 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +10 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +608 -45
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +19 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/package.json +1 -1
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +1 -2
- package/src/analysis/health/file-scope.ts +4 -1
- package/src/analysis/health/server-status.ts +1 -1
- package/src/analysis/relations/callees.ts +17 -8
- package/src/analysis/relations/implementations.ts +1 -2
- package/src/analysis/relations/references.ts +8 -4
- package/src/analysis/relations/types.ts +6 -30
- package/src/analysis/target/anchored.ts +9 -1
- package/src/analysis/target/symbol.ts +29 -11
- package/src/analysis/target/types.ts +14 -6
- package/src/api.ts +2 -0
- package/src/session/graph/collect.ts +18 -3
- package/src/session/health-refresh.ts +12 -1
- package/src/session/health-types.ts +18 -6
- package/src/session/health-workflow.ts +21 -19
- package/src/session/orientation-types.ts +5 -6
- package/src/session/orientation-workflow.ts +10 -2
- package/src/session/target-workflow.ts +9 -6
- package/src/substrate/lsp/source-tracking.ts +11 -6
- package/src/tool/code_find/guidance.ts +2 -2
- package/src/tool/code_find/spec.ts +5 -4
- package/src/tool/code_find/tui.ts +7 -1
- package/src/tool/code_graph/details.ts +32 -0
- package/src/tool/code_graph/display.ts +54 -0
- package/src/tool/code_graph/format.ts +60 -0
- package/src/tool/code_graph/guidance.ts +1 -1
- package/src/tool/code_graph/markdown.ts +76 -198
- package/src/tool/code_graph/read-next.ts +65 -0
- package/src/tool/code_graph/result.ts +118 -83
- package/src/tool/code_graph/tui.ts +216 -54
- package/src/tool/code_health/guidance.ts +2 -4
- package/src/tool/code_health/markdown.ts +75 -58
- package/src/tool/code_health/refresh-outcome.ts +41 -1
- package/src/tool/code_health/refresh-status.ts +114 -48
- package/src/tool/code_health/scope.ts +78 -0
- package/src/tool/code_health/spec.ts +1 -1
- package/src/tool/code_health/tui.ts +25 -38
- package/src/tool/code_inspect/tui.ts +7 -1
- package/src/tool/code_orientation/result.ts +31 -8
- package/src/tool/code_orientation/tui.ts +26 -5
- package/src/tool/code_refactor_apply/guidance.ts +1 -1
- package/src/tool/code_refactor_apply/tui.ts +7 -3
- package/src/tool/code_refactor_plan/guidance.ts +1 -1
- package/src/tool/code_refactor_plan/tui.ts +7 -1
- package/src/tool/code_resolve/guidance.ts +2 -4
- package/src/tool/code_resolve/result.ts +13 -10
- package/src/tool/code_resolve/tui.ts +18 -1
- package/src/tool/infra/truncate.ts +39 -13
- package/src/tool/result/candidate-row.ts +27 -0
- package/src/tool/result/errors.ts +21 -6
- package/src/tool/result/types.ts +7 -3
- package/src/tool/schemas.ts +5 -0
- package/src/types/execution.ts +2 -0
- package/src/types/index.ts +2 -0
- package/src/ui/tui/candidate-legacy-body.ts +69 -0
- package/src/ui/tui/candidate-selection.ts +366 -0
- package/src/ui/tui/common.ts +26 -31
- package/src/ui/tui/execution-error.ts +92 -0
|
@@ -5,6 +5,7 @@ import type { EvidenceListMetadata } from "../analysis/evidence.ts";
|
|
|
5
5
|
import type { InstructionFilesMetadata } from "../analysis/instruction-files.ts";
|
|
6
6
|
import type { CodeProvider } from "../analysis/provider.ts";
|
|
7
7
|
import type { ReadNextItem } from "../analysis/read-next.ts";
|
|
8
|
+
import type { TargetCandidateCompleteness } from "../analysis/target/types.ts";
|
|
8
9
|
import type { OrientationTargetInput, TargetSymbolKind } from "./target-input.ts";
|
|
9
10
|
import type { AnchorKind, TargetStoreEntry } from "./target-store.ts";
|
|
10
11
|
|
|
@@ -118,16 +119,14 @@ export interface OrientationResultData {
|
|
|
118
119
|
|
|
119
120
|
export type OrientationWorkflowOutcome =
|
|
120
121
|
| { readonly kind: "completed"; readonly data: OrientationResultData }
|
|
121
|
-
| {
|
|
122
|
+
| ({
|
|
122
123
|
readonly kind: "disambiguation";
|
|
123
124
|
readonly candidates: readonly OrientationCandidate[];
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
| {
|
|
125
|
+
} & TargetCandidateCompleteness)
|
|
126
|
+
| ({
|
|
127
127
|
readonly kind: "kind-mismatch";
|
|
128
128
|
readonly requestedKind: TargetSymbolKind;
|
|
129
129
|
readonly candidates: readonly OrientationCandidate[];
|
|
130
|
-
|
|
131
|
-
}
|
|
130
|
+
} & TargetCandidateCompleteness)
|
|
132
131
|
| { readonly kind: "invalid-input"; readonly message: string }
|
|
133
132
|
| { readonly kind: "unavailable"; readonly reason: string };
|
|
@@ -131,10 +131,18 @@ async function orientTarget(options: {
|
|
|
131
131
|
? {
|
|
132
132
|
kind: "kind-mismatch",
|
|
133
133
|
requestedKind: target.requestedKind,
|
|
134
|
-
omittedCount: target.omittedCount,
|
|
135
134
|
candidates,
|
|
135
|
+
totalCount: target.totalCount,
|
|
136
|
+
omittedCount: target.omittedCount,
|
|
137
|
+
partialReason: target.partialReason,
|
|
136
138
|
}
|
|
137
|
-
: {
|
|
139
|
+
: {
|
|
140
|
+
kind: "disambiguation",
|
|
141
|
+
candidates,
|
|
142
|
+
totalCount: target.totalCount,
|
|
143
|
+
omittedCount: target.omittedCount,
|
|
144
|
+
partialReason: target.partialReason,
|
|
145
|
+
};
|
|
138
146
|
}
|
|
139
147
|
if (target.kind !== "resolved") return target;
|
|
140
148
|
throwIfAborted(control);
|
|
@@ -19,6 +19,7 @@ import { resolveSymbolTarget } from "../analysis/target/symbol.ts";
|
|
|
19
19
|
import type {
|
|
20
20
|
ResolvedTargetData,
|
|
21
21
|
ResolvedTargetGroupData,
|
|
22
|
+
TargetCandidateCompleteness,
|
|
22
23
|
TargetOutcome,
|
|
23
24
|
} from "../analysis/target/types.ts";
|
|
24
25
|
import type { CapabilityAdapter } from "./capability-adapter.ts";
|
|
@@ -59,17 +60,15 @@ export type TargetWorkflowOutcome =
|
|
|
59
60
|
omittedCount: number;
|
|
60
61
|
unknownNestingCount: number;
|
|
61
62
|
}
|
|
62
|
-
| {
|
|
63
|
+
| ({
|
|
63
64
|
kind: "disambiguation";
|
|
64
65
|
candidates: ReadonlyArray<TargetWorkflowCandidate>;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
| {
|
|
66
|
+
} & TargetCandidateCompleteness)
|
|
67
|
+
| ({
|
|
68
68
|
kind: "kind-mismatch";
|
|
69
69
|
requestedKind: TargetSymbolKind;
|
|
70
70
|
candidates: ReadonlyArray<TargetWorkflowCandidate>;
|
|
71
|
-
|
|
72
|
-
}
|
|
71
|
+
} & TargetCandidateCompleteness)
|
|
73
72
|
| { kind: "invalid-input"; message: string }
|
|
74
73
|
| { kind: "unavailable"; reason: string };
|
|
75
74
|
|
|
@@ -318,12 +317,16 @@ function toWorkflowOutcome(
|
|
|
318
317
|
kind: "kind-mismatch",
|
|
319
318
|
requestedKind: outcome.requestedKind,
|
|
320
319
|
candidates,
|
|
320
|
+
totalCount: outcome.totalCount,
|
|
321
321
|
omittedCount: outcome.omittedCount,
|
|
322
|
+
partialReason: outcome.partialReason,
|
|
322
323
|
}
|
|
323
324
|
: {
|
|
324
325
|
kind: "disambiguation",
|
|
325
326
|
candidates,
|
|
327
|
+
totalCount: outcome.totalCount,
|
|
326
328
|
omittedCount: outcome.omittedCount,
|
|
329
|
+
partialReason: outcome.partialReason,
|
|
327
330
|
};
|
|
328
331
|
}
|
|
329
332
|
|
|
@@ -26,7 +26,12 @@ export interface SourceTrackingReport {
|
|
|
26
26
|
readonly observedFileCount: number;
|
|
27
27
|
readonly discovered: readonly string[];
|
|
28
28
|
readonly tracked: readonly string[];
|
|
29
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Candidates skipped by automatic policy or tracking preconditions. This can
|
|
31
|
+
* include policy exclusions, unsupported automatic routes, and paths that
|
|
32
|
+
* are no longer regular; it is not a server capability result.
|
|
33
|
+
*/
|
|
34
|
+
readonly skipped: readonly string[];
|
|
30
35
|
readonly unavailable: readonly string[];
|
|
31
36
|
readonly deferred: number;
|
|
32
37
|
}
|
|
@@ -68,7 +73,7 @@ export async function trackCreatedSources(options: {
|
|
|
68
73
|
inventory,
|
|
69
74
|
discovered: [],
|
|
70
75
|
tracked: [],
|
|
71
|
-
|
|
76
|
+
skipped: [],
|
|
72
77
|
unavailable: [],
|
|
73
78
|
deferred: options.state.createdSourceQueue,
|
|
74
79
|
control: options.control,
|
|
@@ -103,7 +108,7 @@ export async function trackCreatedSources(options: {
|
|
|
103
108
|
};
|
|
104
109
|
|
|
105
110
|
const tracked = pathsForOutcome(batch.outcomes, "tracked");
|
|
106
|
-
const
|
|
111
|
+
const skipped = pathsForOutcome(batch.outcomes, "skipped");
|
|
107
112
|
const unavailable = pathsForOutcome(batch.outcomes, "unavailable");
|
|
108
113
|
return {
|
|
109
114
|
state: nextState,
|
|
@@ -112,7 +117,7 @@ export async function trackCreatedSources(options: {
|
|
|
112
117
|
inventory,
|
|
113
118
|
discovered,
|
|
114
119
|
tracked,
|
|
115
|
-
|
|
120
|
+
skipped,
|
|
116
121
|
unavailable,
|
|
117
122
|
deferred: nextQueue,
|
|
118
123
|
control: options.control,
|
|
@@ -188,7 +193,7 @@ function createReport(options: {
|
|
|
188
193
|
inventory: WorkspaceSourceInventory;
|
|
189
194
|
discovered: readonly string[];
|
|
190
195
|
tracked: readonly string[];
|
|
191
|
-
|
|
196
|
+
skipped: readonly string[];
|
|
192
197
|
unavailable: readonly string[];
|
|
193
198
|
deferred: readonly string[];
|
|
194
199
|
control?: CodeRequestControl;
|
|
@@ -199,7 +204,7 @@ function createReport(options: {
|
|
|
199
204
|
observedFileCount: options.inventory.observedFileCount,
|
|
200
205
|
discovered: displayPaths(options.cwd, options.discovered, options.control),
|
|
201
206
|
tracked: displayPaths(options.cwd, options.tracked, options.control),
|
|
202
|
-
|
|
207
|
+
skipped: displayPaths(options.cwd, options.skipped, options.control),
|
|
203
208
|
unavailable: displayPaths(options.cwd, options.unavailable, options.control),
|
|
204
209
|
deferred: options.deferred.length,
|
|
205
210
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"Search
|
|
2
|
+
"Search workspace symbols in semantic mode, or declaration, import, export, and call names in AST mode. Use PI grep for literal or regular-expression file-content search. The selected mode does not switch to another search method.";
|
|
3
3
|
|
|
4
4
|
export const promptSnippet = "search code structure or workspace symbols";
|
|
5
5
|
|
|
6
6
|
export const promptGuidelines = [
|
|
7
|
-
"Use code_find for
|
|
7
|
+
"Use code_find for broad code-aware discovery, code_resolve for target identity, and code_graph for relationships.",
|
|
8
8
|
];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
3
|
import type { CodeIntelToolExecCtx } from "../../types/index.ts";
|
|
4
|
-
import { FindScopeParam, MaxResultsParam
|
|
4
|
+
import { FindQueryParam, FindScopeParam, MaxResultsParam } from "../schemas.ts";
|
|
5
5
|
import { CODE_FIND_AST_KINDS } from "./ast-kinds.ts";
|
|
6
6
|
import { executeFindTool } from "./execute.ts";
|
|
7
7
|
import { CODE_FIND_MODES } from "./modes.ts";
|
|
@@ -16,14 +16,15 @@ export const codeFindSpec = {
|
|
|
16
16
|
label: CODE_FIND_TOOL_LABEL,
|
|
17
17
|
parameters: Type.Object(
|
|
18
18
|
{
|
|
19
|
-
query:
|
|
19
|
+
query: FindQueryParam,
|
|
20
20
|
scope: Type.Optional(FindScopeParam),
|
|
21
21
|
mode: StringEnum(CODE_FIND_MODES, {
|
|
22
|
-
description:
|
|
22
|
+
description:
|
|
23
|
+
"Required code-aware search mode. AST mode requires `kind`; semantic mode rejects `kind`.",
|
|
23
24
|
}),
|
|
24
25
|
kind: Type.Optional(
|
|
25
26
|
StringEnum(CODE_FIND_AST_KINDS, {
|
|
26
|
-
description: 'AST kind for mode:"ast".',
|
|
27
|
+
description: 'AST structure kind; required only for mode:"ast".',
|
|
27
28
|
}),
|
|
28
29
|
),
|
|
29
30
|
maxResults: Type.Optional(MaxResultsParam),
|
|
@@ -37,5 +37,11 @@ export function renderFindResult(
|
|
|
37
37
|
theme: Theme,
|
|
38
38
|
context: ToolRendererContext | undefined,
|
|
39
39
|
): ReturnType<typeof renderSimpleResult> {
|
|
40
|
-
return renderSimpleResult(
|
|
40
|
+
return renderSimpleResult(
|
|
41
|
+
result,
|
|
42
|
+
options,
|
|
43
|
+
theme,
|
|
44
|
+
{ progress: "Searching…", failure: "code_find failed" },
|
|
45
|
+
context,
|
|
46
|
+
);
|
|
41
47
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { EvidenceListMetadata } from "../../analysis/evidence.ts";
|
|
2
|
+
import type { ReadNextItem } from "../../analysis/read-next.ts";
|
|
3
|
+
import type { CalleeScope } from "../../analysis/relations/types.ts";
|
|
4
|
+
import type { GraphRelationKind } from "../../session/graph-types.ts";
|
|
5
|
+
|
|
6
|
+
/** File heading for the next count rows in the relation's bounded display section. */
|
|
7
|
+
export interface GraphFileGroup {
|
|
8
|
+
file: string;
|
|
9
|
+
count: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Relation status and context kept for the human transcript. Counts live in evidenceLists. */
|
|
13
|
+
export interface GraphSectionDetails {
|
|
14
|
+
rel: GraphRelationKind;
|
|
15
|
+
source: "semantic" | "structural";
|
|
16
|
+
status: "complete" | "partial" | "unavailable";
|
|
17
|
+
message: string | null;
|
|
18
|
+
externalCount: number;
|
|
19
|
+
enclosingScope: CalleeScope | null;
|
|
20
|
+
depth: "direct" | "deep" | null;
|
|
21
|
+
/** Absent in older results whose display rows already contain full paths. */
|
|
22
|
+
fileGroups?: GraphFileGroup[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Persisted graph facts. The TUI does not read or parse agent Markdown. */
|
|
26
|
+
export interface GraphDetails {
|
|
27
|
+
targetName: string;
|
|
28
|
+
targetFile: string;
|
|
29
|
+
sections: GraphSectionDetails[];
|
|
30
|
+
evidenceLists: EvidenceListMetadata[];
|
|
31
|
+
readNext: ReadNextItem[];
|
|
32
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { CallEntry, ReferenceEntry } from "../../analysis/relations/types.ts";
|
|
2
|
+
import { toDisplayPath } from "../../analysis/search/paths.ts";
|
|
3
|
+
import { createToolDisplaySection } from "../result/display.ts";
|
|
4
|
+
import type { ToolDisplaySection } from "../result/types.ts";
|
|
5
|
+
import type { GraphFileGroup } from "./details.ts";
|
|
6
|
+
import { compactGraphLabel } from "./format.ts";
|
|
7
|
+
import type { AssembledGraphSection } from "./result.ts";
|
|
8
|
+
|
|
9
|
+
/** Bound sites before grouping them. Each file group partitions the display rows. */
|
|
10
|
+
export function graphDisplaySection(
|
|
11
|
+
section: AssembledGraphSection,
|
|
12
|
+
cwd: string,
|
|
13
|
+
): {
|
|
14
|
+
display: ToolDisplaySection;
|
|
15
|
+
fileGroups: GraphFileGroup[];
|
|
16
|
+
} {
|
|
17
|
+
if (section.kind === "unavailable") {
|
|
18
|
+
return {
|
|
19
|
+
display: createToolDisplaySection({
|
|
20
|
+
key: `graph.${section.rel}`,
|
|
21
|
+
title: section.rel,
|
|
22
|
+
items: [],
|
|
23
|
+
format: () => "",
|
|
24
|
+
}),
|
|
25
|
+
fileGroups: [],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const items = section.evidence.items as readonly (CallEntry | ReferenceEntry)[];
|
|
29
|
+
const display = createToolDisplaySection({
|
|
30
|
+
key: `graph.${section.rel}`,
|
|
31
|
+
title: section.rel,
|
|
32
|
+
items,
|
|
33
|
+
totalCount: section.evidence.metadata.totalCount,
|
|
34
|
+
omittedCount: section.evidence.metadata.omittedCount,
|
|
35
|
+
partialReason: section.evidence.metadata.partialReason,
|
|
36
|
+
format: (item) => {
|
|
37
|
+
const site = `L${item.line}:${item.character}`;
|
|
38
|
+
if (section.rel !== "callees") return site;
|
|
39
|
+
const call = item as CallEntry;
|
|
40
|
+
return `${site} — ${compactGraphLabel(call.displayName ?? call.name)}`;
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const groups = new Map<string, string[]>();
|
|
44
|
+
for (let index = 0; index < display.shownCount; index++) {
|
|
45
|
+
const file = toDisplayPath(cwd, items[index].file);
|
|
46
|
+
const rows = groups.get(file) ?? [];
|
|
47
|
+
rows.push(display.lines[index]);
|
|
48
|
+
groups.set(file, rows);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
display: { ...display, lines: [...groups.values()].flat() },
|
|
52
|
+
fileGroups: [...groups].map(([file, rows]) => ({ file, count: rows.length })),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { EvidenceListMetadata } from "../../analysis/evidence.ts";
|
|
2
|
+
|
|
3
|
+
/** Format graph counts and all known limits in one place. */
|
|
4
|
+
export function formatGraphEvidence(
|
|
5
|
+
metadata: Omit<EvidenceListMetadata, "partialReason"> & { partialReason: string | null },
|
|
6
|
+
noun: "locations" | "call sites" | "candidates",
|
|
7
|
+
externalCount = 0,
|
|
8
|
+
): string {
|
|
9
|
+
const { shownCount, totalCount, omittedCount, partialReason, invalidLocationCount } = metadata;
|
|
10
|
+
const count = totalCount ?? shownCount;
|
|
11
|
+
const label = count === 1 ? singularGraphNoun(noun) : noun;
|
|
12
|
+
let text =
|
|
13
|
+
totalCount === null
|
|
14
|
+
? `${shownCount} ${label} shown${omittedCount ? `; ${omittedCount} collected omitted` : ""}; more may exist — ${partialReason}`
|
|
15
|
+
: omittedCount
|
|
16
|
+
? `${shownCount} of ${totalCount} ${label} shown; ${omittedCount} omitted`
|
|
17
|
+
: `${totalCount} ${label}`;
|
|
18
|
+
if (externalCount > 0)
|
|
19
|
+
text += `; ${externalCount} external location${externalCount === 1 ? "" : "s"}`;
|
|
20
|
+
if (invalidLocationCount) {
|
|
21
|
+
const invalidNoun = invalidLocationCount === 1 ? "location" : "locations";
|
|
22
|
+
text += `; ${invalidLocationCount} invalid provider ${invalidNoun} omitted (invalid-provider-location)`;
|
|
23
|
+
}
|
|
24
|
+
return text;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function singularGraphNoun(noun: "locations" | "call sites" | "candidates"): string {
|
|
28
|
+
switch (noun) {
|
|
29
|
+
case "locations":
|
|
30
|
+
return "location";
|
|
31
|
+
case "call sites":
|
|
32
|
+
return "call site";
|
|
33
|
+
case "candidates":
|
|
34
|
+
return "candidate";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Format a 1-based source-line range for either graph presentation. */
|
|
39
|
+
export function formatGraphRange(scope: { startLine: number; endLine: number }): string {
|
|
40
|
+
return scope.startLine === scope.endLine
|
|
41
|
+
? `L${scope.startLine}`
|
|
42
|
+
: `L${scope.startLine}–L${scope.endLine}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Bound a source label while keeping both ends of a chained expression. */
|
|
46
|
+
export function compactGraphLabel(value: string, limit = 100): string {
|
|
47
|
+
const text = value.replace(/\s+/g, " ").trim();
|
|
48
|
+
if (text.length <= limit) return text;
|
|
49
|
+
const tail = Math.floor(limit / 3);
|
|
50
|
+
return `${text.slice(0, limit - tail - 1)}…${text.slice(-tail)}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Quote source text that can contain Markdown backticks. */
|
|
54
|
+
export function graphCodeSpan(text: string): string {
|
|
55
|
+
const runs = text.match(/`+/g) ?? [];
|
|
56
|
+
const delimiter = "`".repeat(Math.max(0, ...runs.map((run) => run.length)) + 1);
|
|
57
|
+
return text.startsWith("`") || text.endsWith("`")
|
|
58
|
+
? `${delimiter} ${text} ${delimiter}`
|
|
59
|
+
: `${delimiter}${text}${delimiter}`;
|
|
60
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"Find
|
|
2
|
+
"Find semantic references and implementations, or non-symbol-aware structural calls written inside a target's enclosing scope.";
|
|
3
3
|
|
|
4
4
|
export const promptSnippet = "trace code relationships";
|
|
5
5
|
|
|
@@ -1,222 +1,100 @@
|
|
|
1
1
|
/** Markdown adapter for assembled code_graph evidence. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
type EvidenceList,
|
|
5
|
-
type EvidenceListMetadata,
|
|
6
|
-
renderEvidenceListMetadataDisclosure,
|
|
7
|
-
} from "../../analysis/evidence.ts";
|
|
8
3
|
import { renderReadNextSection } from "../../analysis/read-next.ts";
|
|
9
|
-
import {
|
|
10
|
-
compactLineRanges,
|
|
11
|
-
formatAssembledReferenceList,
|
|
12
|
-
} from "../../analysis/references/semantic-refs.ts";
|
|
13
|
-
import type {
|
|
14
|
-
CallEntry,
|
|
15
|
-
CalleeScope,
|
|
16
|
-
ImplementationEntry,
|
|
17
|
-
ReferenceEntry,
|
|
18
|
-
} from "../../analysis/relations/types.ts";
|
|
4
|
+
import type { CallEntry, CalleeScope } from "../../analysis/relations/types.ts";
|
|
19
5
|
import { toDisplayPath } from "../../analysis/search/paths.ts";
|
|
20
|
-
import
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
import { assembledReadNext } from "../result/assembly.ts";
|
|
7
|
+
import {
|
|
8
|
+
compactGraphLabel,
|
|
9
|
+
formatGraphEvidence,
|
|
10
|
+
formatGraphRange,
|
|
11
|
+
graphCodeSpan,
|
|
12
|
+
} from "./format.ts";
|
|
13
|
+
import type { AssembledGraphSection, GraphResultAssembly } from "./result.ts";
|
|
14
|
+
|
|
15
|
+
/** Render one target and one summary per requested relation. */
|
|
23
16
|
export function renderGraphResult(assembly: GraphResultAssembly): string {
|
|
24
17
|
const lines = [
|
|
25
|
-
|
|
26
|
-
"",
|
|
27
|
-
`_File: \`${assembly.resolvedDisplayFile}\`_`,
|
|
18
|
+
`Target: ${graphCodeSpan(assembly.displayName)} — ${graphCodeSpan(assembly.resolvedDisplayFile)}`,
|
|
28
19
|
"",
|
|
29
20
|
];
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (available.length > 0) {
|
|
33
|
-
lines.push(
|
|
34
|
-
available
|
|
35
|
-
.map(
|
|
36
|
-
(section) =>
|
|
37
|
-
`**${section.rel}**: ${sectionCount(section)} result${sectionCount(section) === 1 ? "" : "s"}`,
|
|
38
|
-
)
|
|
39
|
-
.join(" | "),
|
|
40
|
-
"",
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const unavailable = assembly.sections.filter((section) => section.kind === "unavailable");
|
|
45
|
-
if (unavailable.length > 0) {
|
|
46
|
-
lines.push(
|
|
47
|
-
`_Unavailable: ${unavailable.map((section) => `\`${section.rel}\``).join(", ")}_`,
|
|
48
|
-
"",
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
for (const section of assembly.sections) {
|
|
53
|
-
if (section.kind === "unavailable") {
|
|
54
|
-
lines.push(`**${section.rel}**: ${section.message}`, "");
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
switch (section.rel) {
|
|
59
|
-
case "references": {
|
|
60
|
-
const rendered = renderReferencesResult({
|
|
61
|
-
symbolName: assembly.displayName,
|
|
62
|
-
references: section.evidence,
|
|
63
|
-
externalCount: section.data.externalCount,
|
|
64
|
-
confidence: section.data.confidence,
|
|
65
|
-
cwd: assembly.cwd,
|
|
66
|
-
});
|
|
67
|
-
lines.push(rendered.content, "");
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
case "callees": {
|
|
71
|
-
const rendered = renderCallsResult(
|
|
72
|
-
section.data.enclosingScope,
|
|
73
|
-
section.evidence,
|
|
74
|
-
assembly.resolvedDisplayFile,
|
|
75
|
-
section.data.depth,
|
|
76
|
-
);
|
|
77
|
-
lines.push(rendered.content, "");
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
case "implements": {
|
|
81
|
-
const rendered = renderImplementationsResult({
|
|
82
|
-
implementations: section.evidence,
|
|
83
|
-
externalCount: section.data.externalCount,
|
|
84
|
-
cwd: assembly.cwd,
|
|
85
|
-
targetName: assembly.displayName,
|
|
86
|
-
});
|
|
87
|
-
lines.push(rendered.content, "");
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
lines.push(...renderReadNextSection(available.flatMap((section) => section.readNext)));
|
|
21
|
+
for (const section of assembly.sections) lines.push(...renderSection(section, assembly), "");
|
|
22
|
+
lines.push(...renderReadNextSection(assembledReadNext(assembly.assembled)));
|
|
94
23
|
return lines.join("\n");
|
|
95
24
|
}
|
|
96
25
|
|
|
97
|
-
function
|
|
98
|
-
section
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function renderReferencesResult(options: {
|
|
104
|
-
symbolName: string;
|
|
105
|
-
references: EvidenceList<ReferenceEntry>;
|
|
106
|
-
externalCount: number;
|
|
107
|
-
confidence: string;
|
|
108
|
-
cwd: string;
|
|
109
|
-
}): { content: string; evidenceList: EvidenceListMetadata | null } {
|
|
110
|
-
const total = options.references.metadata.totalCount ?? options.references.metadata.shownCount;
|
|
111
|
-
const lines = [
|
|
112
|
-
`# References of \`${options.symbolName}\``,
|
|
113
|
-
"",
|
|
114
|
-
`**${total} reference${total === 1 ? "" : "s"}** (${options.confidence})`,
|
|
115
|
-
];
|
|
116
|
-
if (options.externalCount > 0) {
|
|
117
|
-
lines.push(
|
|
118
|
-
`_+${options.externalCount} external reference${options.externalCount === 1 ? "" : "s"}_`,
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
const invalidDisclosure = renderInvalidProviderLocations(options.references.metadata);
|
|
122
|
-
if (invalidDisclosure) lines.push(invalidDisclosure);
|
|
123
|
-
lines.push("");
|
|
124
|
-
|
|
125
|
-
const displayEvidence = {
|
|
126
|
-
key: options.references.key,
|
|
127
|
-
items: options.references.items.map((reference) => ({
|
|
128
|
-
file: toDisplayPath(options.cwd, reference.file),
|
|
129
|
-
line: reference.line,
|
|
130
|
-
})),
|
|
131
|
-
metadata: options.references.metadata,
|
|
132
|
-
};
|
|
133
|
-
const evidenceList = formatAssembledReferenceList(lines, displayEvidence);
|
|
134
|
-
return { content: lines.join("\n"), evidenceList };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function renderCallsResult(
|
|
138
|
-
enclosingScope: CalleeScope,
|
|
139
|
-
calls: EvidenceList<CallEntry>,
|
|
140
|
-
relPath: string,
|
|
141
|
-
depth: "direct" | "deep" = "direct",
|
|
142
|
-
): { content: string; evidenceList: EvidenceListMetadata } {
|
|
143
|
-
const lines: string[] = [];
|
|
144
|
-
const depthLabel = depth === "deep" ? "Deep structural calls" : "Direct structural calls";
|
|
145
|
-
const depthNote =
|
|
146
|
-
depth === "deep"
|
|
147
|
-
? "_Deep: includes calls from nested function/method/callback scopes within the enclosing scope._"
|
|
148
|
-
: "_Structural only: call expressions are reported by source shape, not symbol identity; calls inside nested function/method/callback scopes are excluded from this enclosing scope._";
|
|
149
|
-
const total = calls.metadata.totalCount ?? calls.metadata.shownCount;
|
|
150
|
-
|
|
151
|
-
lines.push(`# ${depthLabel} from \`${enclosingScope.name}\``, "");
|
|
26
|
+
function renderSection(section: AssembledGraphSection, assembly: GraphResultAssembly): string[] {
|
|
27
|
+
const source = section.rel === "callees" ? "structural" : "semantic";
|
|
28
|
+
const depth = section.kind === "ok" && section.rel === "callees" ? `, ${section.data.depth}` : "";
|
|
29
|
+
const lines = [`## ${section.rel} (${source}${depth})`];
|
|
30
|
+
if (section.kind === "unavailable") return [...lines, `Unavailable — ${section.message}`];
|
|
152
31
|
lines.push(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
32
|
+
formatGraphEvidence(
|
|
33
|
+
section.evidence.metadata,
|
|
34
|
+
section.rel === "callees" ? "call sites" : "locations",
|
|
35
|
+
section.rel === "callees" ? 0 : section.data.externalCount,
|
|
36
|
+
),
|
|
156
37
|
"",
|
|
157
38
|
);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
function renderImplementationsResult(options: {
|
|
166
|
-
implementations: EvidenceList<ImplementationEntry>;
|
|
167
|
-
externalCount: number;
|
|
168
|
-
cwd: string;
|
|
169
|
-
targetName?: string;
|
|
170
|
-
}): { content: string; evidenceList: EvidenceListMetadata } {
|
|
171
|
-
const lines: string[] = [
|
|
172
|
-
options.targetName
|
|
173
|
-
? `# Implementations of \`${options.targetName}\` (semantic)`
|
|
174
|
-
: "# Implementations (semantic)",
|
|
39
|
+
if (section.rel !== "callees") return [...lines, ...formatLocations(section, assembly.cwd)];
|
|
40
|
+
return [
|
|
41
|
+
...lines,
|
|
42
|
+
formatScope(section.data.enclosingScope, assembly.displayName),
|
|
43
|
+
section.data.depth === "direct"
|
|
44
|
+
? "Nested scopes excluded; calls are source expressions, not resolved symbols."
|
|
45
|
+
: "Nested scopes included; calls are source expressions, not resolved symbols.",
|
|
175
46
|
"",
|
|
47
|
+
...formatCalls(section.evidence.items, assembly),
|
|
176
48
|
];
|
|
177
|
-
const total =
|
|
178
|
-
options.implementations.metadata.totalCount ?? options.implementations.metadata.shownCount;
|
|
179
|
-
if (total > 0) {
|
|
180
|
-
lines.push(`**${total} implementation${total === 1 ? "" : "s"}** in the project`, "");
|
|
181
|
-
for (const [file, locations] of groupByFile(options.implementations.items, options.cwd)) {
|
|
182
|
-
lines.push(`### ${file}`, `- ${compactLineRanges(locations)}`, "");
|
|
183
|
-
}
|
|
184
|
-
const disclosure = renderEvidenceListMetadataDisclosure(options.implementations.metadata);
|
|
185
|
-
if (disclosure) lines.push(disclosure);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (options.externalCount > 0) {
|
|
189
|
-
lines.push(
|
|
190
|
-
`_+${options.externalCount} external location${options.externalCount === 1 ? "" : "s"} (outside this project)_`,
|
|
191
|
-
"",
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
const invalidDisclosure = renderInvalidProviderLocations(options.implementations.metadata);
|
|
195
|
-
if (invalidDisclosure) lines.push(invalidDisclosure, "");
|
|
196
|
-
|
|
197
|
-
return { content: lines.join("\n"), evidenceList: options.implementations.metadata };
|
|
198
49
|
}
|
|
199
50
|
|
|
200
|
-
function
|
|
201
|
-
|
|
202
|
-
return `
|
|
51
|
+
function formatScope(scope: CalleeScope, targetName: string): string {
|
|
52
|
+
const name = scope.name === targetName ? "" : `${graphCodeSpan(scope.name)} — `;
|
|
53
|
+
return `Scope: ${name}${formatGraphRange(scope)}`;
|
|
203
54
|
}
|
|
204
55
|
|
|
205
|
-
function
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
56
|
+
function formatLocations(
|
|
57
|
+
section: Extract<AssembledGraphSection, { kind: "ok"; rel: "references" | "implements" }>,
|
|
58
|
+
cwd: string,
|
|
59
|
+
): string[] {
|
|
60
|
+
const groups = new Map<string, string[]>();
|
|
61
|
+
for (const location of section.evidence.items) {
|
|
62
|
+
const file = toDisplayPath(cwd, location.file);
|
|
63
|
+
const sites = groups.get(file) ?? [];
|
|
64
|
+
sites.push(`L${location.line}:${location.character}`);
|
|
65
|
+
groups.set(file, sites);
|
|
212
66
|
}
|
|
213
|
-
return
|
|
67
|
+
return [...groups].map(([file, sites]) => `- ${graphCodeSpan(file)}: ${sites.join(", ")}`);
|
|
214
68
|
}
|
|
215
69
|
|
|
216
|
-
/**
|
|
217
|
-
function
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
70
|
+
/** Group by the full expression, never by a shortened display label. */
|
|
71
|
+
function formatCalls(calls: readonly CallEntry[], assembly: GraphResultAssembly): string[] {
|
|
72
|
+
const groups = new Map<string, { name: string; file: string; sites: string[] }>();
|
|
73
|
+
for (const call of calls) {
|
|
74
|
+
const file = toDisplayPath(assembly.cwd, call.file);
|
|
75
|
+
const key = JSON.stringify([file, call.name]);
|
|
76
|
+
const group = groups.get(key) ?? { name: call.displayName ?? call.name, file, sites: [] };
|
|
77
|
+
group.sites.push(`L${call.line}:${call.character}`);
|
|
78
|
+
groups.set(key, group);
|
|
79
|
+
}
|
|
80
|
+
const previews = [...groups.values()].map((group) => ({
|
|
81
|
+
...group,
|
|
82
|
+
label: compactGraphLabel(group.name),
|
|
83
|
+
}));
|
|
84
|
+
const collisions = new Map<string, { count: number; seen: number }>();
|
|
85
|
+
for (const { file, label } of previews) {
|
|
86
|
+
const key = JSON.stringify([file, label]);
|
|
87
|
+
const count = (collisions.get(key)?.count ?? 0) + 1;
|
|
88
|
+
collisions.set(key, { count, seen: 0 });
|
|
89
|
+
}
|
|
90
|
+
return previews.map(({ label, file, sites }) => {
|
|
91
|
+
const collision = collisions.get(JSON.stringify([file, label]));
|
|
92
|
+
let qualifier = "";
|
|
93
|
+
if (collision && collision.count > 1) {
|
|
94
|
+
collision.seen++;
|
|
95
|
+
qualifier = ` (expression ${collision.seen}/${collision.count})`;
|
|
96
|
+
}
|
|
97
|
+
const prefix = file === assembly.resolvedDisplayFile ? "" : `${graphCodeSpan(file)}: `;
|
|
98
|
+
return `- ${graphCodeSpan(label)}${qualifier} — ${prefix}${sites.join(", ")}`;
|
|
99
|
+
});
|
|
222
100
|
}
|