@mrclrchtr/supi-code-intelligence 6.3.0 → 6.4.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/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- 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-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +10 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +4 -32
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sources.ts +108 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +165 -14
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +17 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +32 -2
- 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-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/package.json +5 -5
- package/src/session/health-refresh.ts +15 -10
- package/src/session/health-types.ts +5 -0
- package/src/session/health-workflow.ts +7 -3
- package/src/session/session.ts +12 -5
- package/src/substrate/lsp/maintenance.ts +53 -47
- package/src/substrate/lsp/source-tracking.ts +247 -0
- package/src/tool/code_find/guidance.ts +3 -3
- package/src/tool/code_graph/guidance.ts +2 -2
- package/src/tool/code_health/guidance.ts +3 -3
- package/src/tool/code_health/markdown.ts +21 -5
- package/src/tool/code_health/refresh-outcome.ts +11 -0
- package/src/tool/code_health/refresh-status.ts +25 -4
- package/src/tool/code_health/tui.ts +4 -0
- package/src/tool/code_inspect/guidance.ts +5 -3
- package/src/tool/code_orientation/guidance.ts +3 -4
- package/src/tool/code_refactor_apply/guidance.ts +1 -1
- package/src/tool/code_refactor_plan/guidance.ts +3 -5
- package/src/tool/code_resolve/guidance.ts +3 -3
- package/src/tool/guidance.ts +11 -9
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
type CodeRequestControl,
|
|
5
|
+
throwIfCodeRequestInterrupted,
|
|
6
|
+
} from "@mrclrchtr/supi-code-runtime/api";
|
|
7
|
+
import { isWithinOrEqual } from "@mrclrchtr/supi-core/api";
|
|
8
|
+
import type {
|
|
9
|
+
BulkTrackFileOutcome,
|
|
10
|
+
WorkspaceLspRuntime,
|
|
11
|
+
WorkspaceSourceInventory,
|
|
12
|
+
} from "@mrclrchtr/supi-lsp/api";
|
|
13
|
+
import { isMissingFileError, MAX_BULK_TRACK_FILES } from "@mrclrchtr/supi-lsp/api";
|
|
14
|
+
|
|
15
|
+
/** Typed session state for sentinel maintenance and automatic source tracking. */
|
|
16
|
+
export interface LspMaintenanceState {
|
|
17
|
+
readonly sentinelSnapshot: Map<string, number>;
|
|
18
|
+
readonly sourceBaseline: ReadonlySet<string> | null;
|
|
19
|
+
readonly createdSourceQueue: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Source discovery and tracking facts from one broad diagnostic refresh. */
|
|
23
|
+
export interface SourceTrackingReport {
|
|
24
|
+
readonly status: WorkspaceSourceInventory["status"];
|
|
25
|
+
readonly reason: WorkspaceSourceInventory["reason"];
|
|
26
|
+
readonly observedFileCount: number;
|
|
27
|
+
readonly discovered: readonly string[];
|
|
28
|
+
readonly tracked: readonly string[];
|
|
29
|
+
readonly unsupported: readonly string[];
|
|
30
|
+
readonly unavailable: readonly string[];
|
|
31
|
+
readonly deferred: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Create empty source state with an optional lifecycle-seeded sentinel snapshot. */
|
|
35
|
+
export function createLspMaintenanceState(
|
|
36
|
+
sentinelSnapshot: Map<string, number> = new Map(),
|
|
37
|
+
): LspMaintenanceState {
|
|
38
|
+
return {
|
|
39
|
+
sentinelSnapshot,
|
|
40
|
+
sourceBaseline: null,
|
|
41
|
+
createdSourceQueue: [],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Replace the sentinel portion of maintenance state without source state. */
|
|
46
|
+
export function withSentinelSnapshot(
|
|
47
|
+
state: LspMaintenanceState,
|
|
48
|
+
sentinelSnapshot: Map<string, number>,
|
|
49
|
+
): LspMaintenanceState {
|
|
50
|
+
return { ...state, sentinelSnapshot };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Discover source additions and process a bounded queue for one refresh. */
|
|
54
|
+
export async function trackCreatedSources(options: {
|
|
55
|
+
readonly runtime: WorkspaceLspRuntime;
|
|
56
|
+
readonly cwd: string;
|
|
57
|
+
readonly state: LspMaintenanceState;
|
|
58
|
+
readonly scope?: string | null;
|
|
59
|
+
readonly control?: CodeRequestControl;
|
|
60
|
+
}): Promise<{ state: LspMaintenanceState; report: SourceTrackingReport }> {
|
|
61
|
+
const inventory = await options.runtime.scanWorkspaceSources(options.control);
|
|
62
|
+
throwIfCodeRequestInterrupted(options.control);
|
|
63
|
+
if (inventory.status === "limited") {
|
|
64
|
+
return {
|
|
65
|
+
state: options.state,
|
|
66
|
+
report: createReport({
|
|
67
|
+
cwd: options.cwd,
|
|
68
|
+
inventory,
|
|
69
|
+
discovered: [],
|
|
70
|
+
tracked: [],
|
|
71
|
+
unsupported: [],
|
|
72
|
+
unavailable: [],
|
|
73
|
+
deferred: options.state.createdSourceQueue,
|
|
74
|
+
control: options.control,
|
|
75
|
+
}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const discovered = discoverCreatedPaths(
|
|
80
|
+
options.state.sourceBaseline,
|
|
81
|
+
inventory.files,
|
|
82
|
+
options.control,
|
|
83
|
+
);
|
|
84
|
+
const queue = removeMissingPaths(
|
|
85
|
+
options.state.sourceBaseline === null
|
|
86
|
+
? []
|
|
87
|
+
: deduplicatePaths([...options.state.createdSourceQueue, ...discovered], options.control),
|
|
88
|
+
options.control,
|
|
89
|
+
);
|
|
90
|
+
const selected = selectScopedPaths(queue, options.cwd, options.scope, options.control).slice(
|
|
91
|
+
0,
|
|
92
|
+
MAX_BULK_TRACK_FILES,
|
|
93
|
+
);
|
|
94
|
+
const batch =
|
|
95
|
+
selected.length === 0
|
|
96
|
+
? { outcomes: [] as const }
|
|
97
|
+
: await options.runtime.bulkTrackFiles(selected, options.control);
|
|
98
|
+
const nextQueue = retainQueuePaths(queue, selected, batch.outcomes, options.control);
|
|
99
|
+
const nextState: LspMaintenanceState = {
|
|
100
|
+
sentinelSnapshot: options.state.sentinelSnapshot,
|
|
101
|
+
sourceBaseline: new Set(inventory.files),
|
|
102
|
+
createdSourceQueue: nextQueue,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const tracked = pathsForOutcome(batch.outcomes, "tracked");
|
|
106
|
+
const unsupported = pathsForOutcome(batch.outcomes, "unsupported");
|
|
107
|
+
const unavailable = pathsForOutcome(batch.outcomes, "unavailable");
|
|
108
|
+
return {
|
|
109
|
+
state: nextState,
|
|
110
|
+
report: createReport({
|
|
111
|
+
cwd: options.cwd,
|
|
112
|
+
inventory,
|
|
113
|
+
discovered,
|
|
114
|
+
tracked,
|
|
115
|
+
unsupported,
|
|
116
|
+
unavailable,
|
|
117
|
+
deferred: nextQueue,
|
|
118
|
+
control: options.control,
|
|
119
|
+
}),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function discoverCreatedPaths(
|
|
124
|
+
baseline: ReadonlySet<string> | null,
|
|
125
|
+
current: readonly string[],
|
|
126
|
+
control?: CodeRequestControl,
|
|
127
|
+
): string[] {
|
|
128
|
+
if (baseline === null) return [];
|
|
129
|
+
const discovered: string[] = [];
|
|
130
|
+
for (const filePath of current) {
|
|
131
|
+
throwIfCodeRequestInterrupted(control);
|
|
132
|
+
if (!baseline.has(filePath)) discovered.push(filePath);
|
|
133
|
+
}
|
|
134
|
+
return discovered;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function selectScopedPaths(
|
|
138
|
+
queue: readonly string[],
|
|
139
|
+
cwd: string,
|
|
140
|
+
scope: string | null | undefined,
|
|
141
|
+
control?: CodeRequestControl,
|
|
142
|
+
): string[] {
|
|
143
|
+
const resolvedScope = scope === null || scope === undefined ? null : path.resolve(cwd, scope);
|
|
144
|
+
const selected: string[] = [];
|
|
145
|
+
for (const filePath of queue) {
|
|
146
|
+
throwIfCodeRequestInterrupted(control);
|
|
147
|
+
if (resolvedScope === null || isWithinOrEqual(resolvedScope, filePath)) selected.push(filePath);
|
|
148
|
+
}
|
|
149
|
+
return selected;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function retainQueuePaths(
|
|
153
|
+
queue: readonly string[],
|
|
154
|
+
selected: readonly string[],
|
|
155
|
+
outcomes: readonly BulkTrackFileOutcome[],
|
|
156
|
+
control?: CodeRequestControl,
|
|
157
|
+
): string[] {
|
|
158
|
+
const selectedSet = new Set(selected);
|
|
159
|
+
const outcomeByPath = new Map(outcomes.map((outcome) => [outcome.file, outcome] as const));
|
|
160
|
+
const unstarted: string[] = [];
|
|
161
|
+
const unavailable: string[] = [];
|
|
162
|
+
|
|
163
|
+
for (const filePath of queue) {
|
|
164
|
+
throwIfCodeRequestInterrupted(control);
|
|
165
|
+
if (!selectedSet.has(filePath)) {
|
|
166
|
+
unstarted.push(filePath);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const outcome = outcomeByPath.get(filePath);
|
|
170
|
+
if (!outcome) {
|
|
171
|
+
unstarted.push(filePath);
|
|
172
|
+
} else if (outcome.kind === "unavailable") {
|
|
173
|
+
unavailable.push(filePath);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return [...unstarted, ...unavailable];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function pathsForOutcome(
|
|
180
|
+
outcomes: readonly BulkTrackFileOutcome[],
|
|
181
|
+
kind: BulkTrackFileOutcome["kind"],
|
|
182
|
+
): string[] {
|
|
183
|
+
return outcomes.filter((outcome) => outcome.kind === kind).map((outcome) => outcome.file);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function createReport(options: {
|
|
187
|
+
cwd: string;
|
|
188
|
+
inventory: WorkspaceSourceInventory;
|
|
189
|
+
discovered: readonly string[];
|
|
190
|
+
tracked: readonly string[];
|
|
191
|
+
unsupported: readonly string[];
|
|
192
|
+
unavailable: readonly string[];
|
|
193
|
+
deferred: readonly string[];
|
|
194
|
+
control?: CodeRequestControl;
|
|
195
|
+
}): SourceTrackingReport {
|
|
196
|
+
return {
|
|
197
|
+
status: options.inventory.status,
|
|
198
|
+
reason: options.inventory.reason,
|
|
199
|
+
observedFileCount: options.inventory.observedFileCount,
|
|
200
|
+
discovered: displayPaths(options.cwd, options.discovered, options.control),
|
|
201
|
+
tracked: displayPaths(options.cwd, options.tracked, options.control),
|
|
202
|
+
unsupported: displayPaths(options.cwd, options.unsupported, options.control),
|
|
203
|
+
unavailable: displayPaths(options.cwd, options.unavailable, options.control),
|
|
204
|
+
deferred: options.deferred.length,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function displayPaths(
|
|
209
|
+
cwd: string,
|
|
210
|
+
filePaths: readonly string[],
|
|
211
|
+
control?: CodeRequestControl,
|
|
212
|
+
): string[] {
|
|
213
|
+
const displayed: string[] = [];
|
|
214
|
+
for (const filePath of filePaths) {
|
|
215
|
+
throwIfCodeRequestInterrupted(control);
|
|
216
|
+
const relativePath = path.relative(cwd, filePath);
|
|
217
|
+
displayed.push((relativePath || path.basename(filePath)).replaceAll(path.sep, "/"));
|
|
218
|
+
}
|
|
219
|
+
return displayed;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function deduplicatePaths(filePaths: readonly string[], control?: CodeRequestControl): string[] {
|
|
223
|
+
const unique = new Set<string>();
|
|
224
|
+
for (const filePath of filePaths) {
|
|
225
|
+
throwIfCodeRequestInterrupted(control);
|
|
226
|
+
unique.add(filePath);
|
|
227
|
+
}
|
|
228
|
+
return [...unique];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function removeMissingPaths(filePaths: readonly string[], control?: CodeRequestControl): string[] {
|
|
232
|
+
const existing: string[] = [];
|
|
233
|
+
for (const filePath of filePaths) {
|
|
234
|
+
throwIfCodeRequestInterrupted(control);
|
|
235
|
+
if (!isMissingFile(filePath)) existing.push(filePath);
|
|
236
|
+
}
|
|
237
|
+
return existing;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function isMissingFile(filePath: string): boolean {
|
|
241
|
+
try {
|
|
242
|
+
statSync(filePath);
|
|
243
|
+
return false;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
return isMissingFileError(error);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
|
|
2
|
+
"Search code structure or workspace symbols for structural or semantic matches. It never silently falls back to another mode or to text search.";
|
|
3
3
|
|
|
4
|
-
export const promptSnippet = "
|
|
4
|
+
export const promptSnippet = "search code structure or workspace symbols";
|
|
5
5
|
|
|
6
6
|
export const promptGuidelines = [
|
|
7
|
-
"Use code_find for structural or semantic search
|
|
7
|
+
"Use code_find for structural or semantic search, grep for text search, and code_graph for symbol relationships.",
|
|
8
8
|
];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"
|
|
2
|
+
"Find symbol references and implementations, and list calls from a target's enclosing scope. Callee results match source shape, not symbol identity.";
|
|
3
3
|
|
|
4
|
-
export const promptSnippet = "
|
|
4
|
+
export const promptSnippet = "trace code relationships";
|
|
5
5
|
|
|
6
6
|
export const promptGuidelines: string[] = [];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"Report live diagnostics
|
|
2
|
+
"Report live diagnostics and language-server health. Diagnostic snapshots do not prove that the whole workspace is clean; server inventory and route-status counts are always workspace-wide.";
|
|
3
3
|
|
|
4
|
-
export const promptSnippet = "live
|
|
4
|
+
export const promptSnippet = "check live diagnostics and language-server health";
|
|
5
5
|
|
|
6
6
|
export const promptGuidelines = [
|
|
7
|
-
"Use code_health with refresh:true before relying on
|
|
7
|
+
"Use code_health with `refresh: true` before relying on diagnostics that can be stale.",
|
|
8
8
|
];
|
|
@@ -15,6 +15,7 @@ import { makeRelative, renderFileScopeStatus } from "./file-scope-markdown.ts";
|
|
|
15
15
|
import {
|
|
16
16
|
formatProcessCrashRecovery,
|
|
17
17
|
formatRefreshElapsed,
|
|
18
|
+
formatSourceTracking,
|
|
18
19
|
formatStaleDiagnosticRestarts as formatStaleDiagnosticRestartsText,
|
|
19
20
|
isFileReadinessPending,
|
|
20
21
|
} from "./refresh-outcome.ts";
|
|
@@ -57,8 +58,9 @@ function renderRefreshStatus(
|
|
|
57
58
|
case "completed": {
|
|
58
59
|
const fileReadinessPending = isFileReadinessPending(data.refresh, data.semanticState);
|
|
59
60
|
lines.push(
|
|
60
|
-
`**${refreshAttemptLabel(data.refresh)}**: ${asSentence(completedRefreshText(data.refresh, fileReadinessPending))}`,
|
|
61
|
+
`**${refreshAttemptLabel(data.refresh)}**: ${asSentence(completedRefreshText(data.refresh, fileReadinessPending, false))}`,
|
|
61
62
|
);
|
|
63
|
+
renderSourceTracking(lines, data.refresh);
|
|
62
64
|
lines.push(`**Stale assessment**: ${asSentence(staleAssessmentText(data.refresh))}`);
|
|
63
65
|
lines.push("");
|
|
64
66
|
return;
|
|
@@ -74,6 +76,7 @@ function renderRefreshStatus(
|
|
|
74
76
|
lines.push(
|
|
75
77
|
`**${refreshAttemptLabel(data.refresh)}**: failed — ${data.refresh.reason}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${evidence}`,
|
|
76
78
|
);
|
|
79
|
+
renderSourceTracking(lines, data.refresh);
|
|
77
80
|
lines.push("");
|
|
78
81
|
return;
|
|
79
82
|
}
|
|
@@ -97,6 +100,7 @@ function renderRefreshStatus(
|
|
|
97
100
|
function completedRefreshText(
|
|
98
101
|
attempt: Extract<HealthRefreshAttempt, { kind: "completed" }>,
|
|
99
102
|
fileReadinessPending = attempt.fileReadiness === "pending",
|
|
103
|
+
includeSourceTracking = true,
|
|
100
104
|
): string {
|
|
101
105
|
const processCrashRecovery = formatProcessCrashRecovery(attempt.processCrashRecovery);
|
|
102
106
|
const noOp =
|
|
@@ -110,15 +114,26 @@ function completedRefreshText(
|
|
|
110
114
|
? "completed no-op — no active clients were targeted"
|
|
111
115
|
: `completed — ${attempt.attemptedActiveClients} active client${plural(attempt.attemptedActiveClients)} targeted`;
|
|
112
116
|
const staleRestart = noOp ? null : formatStaleDiagnosticRestarts(attempt);
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
const sourceTracking = formatSourceTracking(attempt.sourceTracking);
|
|
118
|
+
const outcome = [
|
|
119
|
+
includeSourceTracking ? sourceTracking : null,
|
|
120
|
+
staleRestart,
|
|
121
|
+
processCrashRecovery,
|
|
122
|
+
].filter((value): value is string => value !== null);
|
|
116
123
|
const withOutcome = outcome.length > 0 ? `${base}; ${outcome.join("; ")}` : base;
|
|
117
124
|
return attempt.operationScope === "workspace-runtime"
|
|
118
125
|
? `${withOutcome}; ${formatDiagnosticEvidence(attempt.diagnosticEvidence)}`
|
|
119
126
|
: withOutcome;
|
|
120
127
|
}
|
|
121
128
|
|
|
129
|
+
function renderSourceTracking(
|
|
130
|
+
lines: string[],
|
|
131
|
+
attempt: Pick<HealthRefreshAttempt, "sourceTracking">,
|
|
132
|
+
): void {
|
|
133
|
+
const sourceTracking = formatSourceTracking(attempt.sourceTracking);
|
|
134
|
+
if (sourceTracking) lines.push(`**Source discovery**: ${asSentence(sourceTracking)}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
122
137
|
function refreshAttemptLabel(attempt: Pick<HealthRefreshAttempt, "operationScope">): string {
|
|
123
138
|
return attempt.operationScope === "file-runtime"
|
|
124
139
|
? "File LSP maintenance attempt"
|
|
@@ -160,10 +175,11 @@ function refreshAttemptOutcome(attempt: HealthRefreshAttempt): string {
|
|
|
160
175
|
? formatProcessCrashRecovery(attempt.processCrashRecovery)
|
|
161
176
|
: null;
|
|
162
177
|
const staleRestart = formatStaleDiagnosticRestarts(attempt);
|
|
178
|
+
const sourceTracking = formatSourceTracking(attempt.sourceTracking);
|
|
163
179
|
const evidence = attempt.diagnosticEvidence
|
|
164
180
|
? `; ${formatDiagnosticEvidence(attempt.diagnosticEvidence)}`
|
|
165
181
|
: "";
|
|
166
|
-
return `failed — ${attempt.reason}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${evidence}`;
|
|
182
|
+
return `failed — ${attempt.reason}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${sourceTracking ? `; ${sourceTracking}` : ""}${evidence}`;
|
|
167
183
|
}
|
|
168
184
|
|
|
169
185
|
function refreshOperationScopeText(attempt: HealthRefreshAttempt): string {
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
ProcessCrashRecoveryReport,
|
|
4
4
|
} from "@mrclrchtr/supi-lsp/api";
|
|
5
5
|
import type { SemanticHealthState } from "../../session/health-types.ts";
|
|
6
|
+
import type { SourceTrackingReport } from "../../substrate/lsp/source-tracking.ts";
|
|
6
7
|
|
|
7
8
|
/** Report whether a file health result is waiting for semantic readiness. */
|
|
8
9
|
export function isFileReadinessPending(
|
|
@@ -64,6 +65,16 @@ function formatProcessCrashRecoveryEntry(entry: ProcessCrashRecoveryEntry): stri
|
|
|
64
65
|
return `${entry.name} @ ${entry.root}: ${outcome}${action}${failure}`;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
/** Format source discovery and bounded tracking counts separately from diagnostics. */
|
|
69
|
+
export function formatSourceTracking(
|
|
70
|
+
report: SourceTrackingReport | null | undefined,
|
|
71
|
+
): string | null {
|
|
72
|
+
if (!report) return null;
|
|
73
|
+
const inventory =
|
|
74
|
+
report.status === "complete" ? "complete" : `limited (${report.reason ?? "unknown reason"})`;
|
|
75
|
+
return `source discovery: ${inventory}; ${report.observedFileCount} source files observed, ${report.discovered.length} discovered, ${report.tracked.length} tracked, ${report.unsupported.length} unsupported, ${report.unavailable.length} unavailable, ${report.deferred} deferred`;
|
|
76
|
+
}
|
|
77
|
+
|
|
67
78
|
/** Format the age of a retained refresh attempt for a status line. */
|
|
68
79
|
export function formatRefreshElapsed(milliseconds: number): string {
|
|
69
80
|
const seconds = Math.max(0, Math.round(milliseconds / 1_000));
|
|
@@ -14,6 +14,14 @@ import { readSemanticHealthState } from "./semantic-state.ts";
|
|
|
14
14
|
|
|
15
15
|
/** Render current and retained health refresh status for the TUI. */
|
|
16
16
|
|
|
17
|
+
/** Read source discovery counts from structured health details. */
|
|
18
|
+
export function readSourceTrackingStatus(source: Record<string, unknown> | null): string | null {
|
|
19
|
+
if (!source || (source.status !== "complete" && source.status !== "limited")) return null;
|
|
20
|
+
const reason =
|
|
21
|
+
source.status === "limited" && typeof source.reason === "string" ? ` (${source.reason})` : "";
|
|
22
|
+
return `${source.status}${reason}: ${readNumber(source.observedFileCount)} observed, ${readArrayLength(source.discovered)} discovered, ${readArrayLength(source.tracked)} tracked, ${readArrayLength(source.unsupported)} unsupported, ${readArrayLength(source.unavailable)} unavailable, ${readNumber(source.deferred)} deferred`;
|
|
23
|
+
}
|
|
24
|
+
|
|
17
25
|
export function readRefreshStatus(data: Record<string, unknown> | null): string | null {
|
|
18
26
|
const refresh = readRecord(data?.refresh);
|
|
19
27
|
if (!refresh) return null;
|
|
@@ -88,19 +96,21 @@ export function readCompactRefreshStatus(data: Record<string, unknown> | null):
|
|
|
88
96
|
const refresh = readRecord(data?.refresh);
|
|
89
97
|
if (!refresh || (refresh.kind !== "completed" && refresh.kind !== "failed")) return null;
|
|
90
98
|
|
|
99
|
+
const sourceTracking = formatSourceTracking(readRecord(refresh.sourceTracking));
|
|
91
100
|
const processCrashRecovery = formatCompactProcessCrashRecovery(
|
|
92
101
|
readProcessCrashRecovery(refresh.processCrashRecovery),
|
|
93
102
|
);
|
|
94
103
|
if (refresh.kind === "failed") {
|
|
95
104
|
const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
|
|
96
105
|
return (
|
|
97
|
-
[staleRestart, processCrashRecovery]
|
|
106
|
+
[sourceTracking, staleRestart, processCrashRecovery]
|
|
98
107
|
.filter((value): value is string => value !== null)
|
|
99
108
|
.join("; ") || null
|
|
100
109
|
);
|
|
101
110
|
}
|
|
102
111
|
const fileReadinessPending = isPendingFileReadiness(data);
|
|
103
112
|
if (
|
|
113
|
+
!sourceTracking &&
|
|
104
114
|
!processCrashRecovery &&
|
|
105
115
|
readNumber(refresh.restartedClients) === 0 &&
|
|
106
116
|
!fileReadinessPending
|
|
@@ -110,7 +120,7 @@ export function readCompactRefreshStatus(data: Record<string, unknown> | null):
|
|
|
110
120
|
|
|
111
121
|
const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
|
|
112
122
|
const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
|
|
113
|
-
return [staleRestart, processCrashRecovery, readiness]
|
|
123
|
+
return [sourceTracking, staleRestart, processCrashRecovery, readiness]
|
|
114
124
|
.filter((value): value is string => value !== null)
|
|
115
125
|
.join("; ");
|
|
116
126
|
}
|
|
@@ -123,6 +133,7 @@ export function readPreviousRefreshStatus(data: Record<string, unknown> | null):
|
|
|
123
133
|
const attempt = readRecord(refresh.lastAttempt);
|
|
124
134
|
if (!attempt) return null;
|
|
125
135
|
const evidence = formatCompactDiagnosticEvidence(readRecord(attempt.diagnosticEvidence));
|
|
136
|
+
const sourceTracking = formatSourceTracking(readRecord(attempt.sourceTracking));
|
|
126
137
|
const processCrashRecovery = formatCompactProcessCrashRecovery(
|
|
127
138
|
readProcessCrashRecovery(attempt.processCrashRecovery),
|
|
128
139
|
);
|
|
@@ -131,7 +142,7 @@ export function readPreviousRefreshStatus(data: Record<string, unknown> | null):
|
|
|
131
142
|
attempt.kind === "completed" &&
|
|
132
143
|
isFileReadinessPending(attempt, readSemanticHealthState(data?.semanticState));
|
|
133
144
|
const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
|
|
134
|
-
const outcome = [readiness, staleRestart, processCrashRecovery, evidence]
|
|
145
|
+
const outcome = [readiness, sourceTracking, staleRestart, processCrashRecovery, evidence]
|
|
135
146
|
.filter((value): value is string => value !== null)
|
|
136
147
|
.join("; ");
|
|
137
148
|
if (attempt.kind === "failed") {
|
|
@@ -170,6 +181,7 @@ function formatLastRefreshSuffix(refresh: Record<string, unknown>): string {
|
|
|
170
181
|
const attempt = readRecord(refresh.lastAttempt);
|
|
171
182
|
if (!attempt) return "";
|
|
172
183
|
const evidence = formatDiagnosticEvidence(readRecord(attempt.diagnosticEvidence));
|
|
184
|
+
const sourceTracking = formatSourceTracking(readRecord(attempt.sourceTracking));
|
|
173
185
|
const staleRestart = formatStaleDiagnosticRestartsForRecord(attempt);
|
|
174
186
|
const processCrashRecovery = formatProcessCrashRecovery(
|
|
175
187
|
readProcessCrashRecovery(attempt.processCrashRecovery),
|
|
@@ -177,7 +189,7 @@ function formatLastRefreshSuffix(refresh: Record<string, unknown>): string {
|
|
|
177
189
|
const fileReadinessPending =
|
|
178
190
|
attempt.kind === "completed" && isFileReadinessPending(attempt, null);
|
|
179
191
|
const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
|
|
180
|
-
const outcome = [readiness, staleRestart, processCrashRecovery, evidence]
|
|
192
|
+
const outcome = [readiness, sourceTracking, staleRestart, processCrashRecovery, evidence]
|
|
181
193
|
.filter((value): value is string => value !== null)
|
|
182
194
|
.join("; ");
|
|
183
195
|
if (attempt.kind === "failed") {
|
|
@@ -191,6 +203,15 @@ function formatLastRefreshSuffix(refresh: Record<string, unknown>): string {
|
|
|
191
203
|
return "";
|
|
192
204
|
}
|
|
193
205
|
|
|
206
|
+
function formatSourceTracking(source: Record<string, unknown> | null): string | null {
|
|
207
|
+
const status = readSourceTrackingStatus(source);
|
|
208
|
+
return status ? `source discovery ${status}` : null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function readArrayLength(value: unknown): number {
|
|
212
|
+
return Array.isArray(value) ? value.length : 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
194
215
|
function formatCompactDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
|
|
195
216
|
const counts = readEvidenceCounts(evidence);
|
|
196
217
|
if (!counts) return null;
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
readCompactRefreshStatus,
|
|
30
30
|
readPreviousRefreshStatus,
|
|
31
31
|
readRefreshStatus,
|
|
32
|
+
readSourceTrackingStatus,
|
|
32
33
|
} from "./refresh-status.ts";
|
|
33
34
|
import { formatSemanticHealthState, readSemanticHealthState } from "./semantic-state.ts";
|
|
34
35
|
|
|
@@ -192,6 +193,8 @@ function buildStatusBar(data: Record<string, unknown> | null, theme: Theme): Tex
|
|
|
192
193
|
const semanticStatus = readSemanticStatus(data);
|
|
193
194
|
const structuralStatus = readString(data, "structuralStatus");
|
|
194
195
|
const refreshStatus = readRefreshStatus(data);
|
|
196
|
+
const refresh = readRecord(data?.refresh);
|
|
197
|
+
const sourceTracking = readSourceTrackingStatus(readRecord(refresh?.sourceTracking));
|
|
195
198
|
|
|
196
199
|
const lspColor = semanticStatus.startsWith("ready") ? "success" : "warning";
|
|
197
200
|
const structuralColor = structuralStatus === "ready" ? "success" : "muted";
|
|
@@ -204,6 +207,7 @@ function buildStatusBar(data: Record<string, unknown> | null, theme: Theme): Tex
|
|
|
204
207
|
lines.push(`Tree-sitter: ${theme.fg(structuralColor, structuralStatus)}`);
|
|
205
208
|
}
|
|
206
209
|
if (refreshStatus) lines.push(`Diagnostics: ${theme.fg("dim", refreshStatus)}`);
|
|
210
|
+
if (sourceTracking) lines.push(`Source discovery: ${theme.fg("dim", sourceTracking)}`);
|
|
207
211
|
|
|
208
212
|
return new Text(lines.join(" "), 0, 0);
|
|
209
213
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"Inspect one
|
|
2
|
+
"Inspect one source location for syntax, its enclosing declaration, hover information, definitions, and nearby diagnostics. Use it for point-local facts, not broad code context.";
|
|
3
3
|
|
|
4
|
-
export const promptSnippet = "
|
|
4
|
+
export const promptSnippet = "inspect a source location";
|
|
5
5
|
|
|
6
|
-
export const promptGuidelines
|
|
6
|
+
export const promptGuidelines = [
|
|
7
|
+
"Use code_inspect for diagnostics near a source location; use code_health for broader diagnostics or language-server status.",
|
|
8
|
+
];
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"
|
|
2
|
+
"Return observed workspace or focused code context to choose what source to inspect next. Omit `focus` for workspace context. A directory focus can surface local instruction files.";
|
|
3
3
|
|
|
4
|
-
export const promptSnippet = "workspace
|
|
4
|
+
export const promptSnippet = "orient around workspace or code context";
|
|
5
5
|
|
|
6
6
|
export const promptGuidelines = [
|
|
7
|
-
"Use code_orientation before broad
|
|
8
|
-
"Use code_graph for relationships and code_health for provider or diagnostic state.",
|
|
7
|
+
"Use code_orientation before broad source reading when workspace or path context can narrow the files.",
|
|
9
8
|
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"Apply
|
|
2
|
+
"Apply a fresh stored refactor plan to change its files. It does not create or regenerate a plan.";
|
|
3
3
|
|
|
4
4
|
export const promptSnippet = "apply a stored refactor plan";
|
|
5
5
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"Preview
|
|
2
|
+
"Preview a semantic refactor without changing files or falling back to text edits.";
|
|
3
3
|
|
|
4
|
-
export const promptSnippet = "preview a
|
|
4
|
+
export const promptSnippet = "preview a semantic refactor";
|
|
5
5
|
|
|
6
|
-
export const promptGuidelines = [
|
|
7
|
-
"Use code_refactor_plan for preview only, then call code_refactor_apply with its planId.",
|
|
8
|
-
];
|
|
6
|
+
export const promptGuidelines: string[] = [];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"Resolve
|
|
2
|
+
"Resolve a real symbol anchor or semantic query to target handles, or list a file's declarations as a target group. Symbol lookup does not fall back to text search.";
|
|
3
3
|
|
|
4
|
-
export const promptSnippet = "resolve
|
|
4
|
+
export const promptSnippet = "resolve symbols or file declarations";
|
|
5
5
|
|
|
6
6
|
export const promptGuidelines = [
|
|
7
|
-
"Use code_resolve when a symbol query
|
|
7
|
+
"Use code_resolve when a symbol query can match multiple symbols or when a later tool requires a target handle.",
|
|
8
8
|
];
|
package/src/tool/guidance.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// Parameter mechanics (formats, enum semantics, cross-field rules) live in
|
|
7
|
-
// schemas.ts / per-tool spec.ts and are not repeated here.
|
|
1
|
+
/**
|
|
2
|
+
* Each tool owns its model-facing text. This module assembles the canonical
|
|
3
|
+
* surface map used by registration. Parameter mechanics stay in specs and
|
|
4
|
+
* shared schemas.
|
|
5
|
+
*/
|
|
8
6
|
|
|
9
7
|
import type { CodeIntelligenceToolName } from "../types/index.ts";
|
|
10
8
|
import {
|
|
@@ -48,9 +46,13 @@ import {
|
|
|
48
46
|
promptSnippet as resolveSnippet,
|
|
49
47
|
} from "./code_resolve/guidance.ts";
|
|
50
48
|
|
|
49
|
+
/** Model-facing fields registered for one public code-intelligence tool. */
|
|
51
50
|
export interface CodeIntelligenceToolPromptSurface {
|
|
51
|
+
/** Selection contract in the active provider tool definition. */
|
|
52
52
|
description: string;
|
|
53
|
+
/** One-line capability phrase in the default tool list. */
|
|
53
54
|
promptSnippet: string;
|
|
55
|
+
/** Optional active-tool routing or ordering reminders. */
|
|
54
56
|
promptGuidelines: string[];
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -59,7 +61,7 @@ export type CodeIntelligenceToolPromptSurfaceMap = Record<
|
|
|
59
61
|
CodeIntelligenceToolPromptSurface
|
|
60
62
|
>;
|
|
61
63
|
|
|
62
|
-
export const CODE_INTELLIGENCE_TOOL_PROMPT_SURFACES
|
|
64
|
+
export const CODE_INTELLIGENCE_TOOL_PROMPT_SURFACES = {
|
|
63
65
|
code_resolve: {
|
|
64
66
|
description: resolveDescription,
|
|
65
67
|
promptSnippet: resolveSnippet,
|
|
@@ -100,4 +102,4 @@ export const CODE_INTELLIGENCE_TOOL_PROMPT_SURFACES: CodeIntelligenceToolPromptS
|
|
|
100
102
|
promptSnippet: applySnippet,
|
|
101
103
|
promptGuidelines: applyGuidelines,
|
|
102
104
|
},
|
|
103
|
-
};
|
|
105
|
+
} satisfies CodeIntelligenceToolPromptSurfaceMap;
|