@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.
Files changed (34) hide show
  1. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  2. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  5. package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
  6. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +10 -0
  7. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +4 -32
  8. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sources.ts +108 -0
  9. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +165 -14
  10. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +17 -1
  11. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +32 -2
  12. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  13. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  14. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
  15. package/package.json +5 -5
  16. package/src/session/health-refresh.ts +15 -10
  17. package/src/session/health-types.ts +5 -0
  18. package/src/session/health-workflow.ts +7 -3
  19. package/src/session/session.ts +12 -5
  20. package/src/substrate/lsp/maintenance.ts +53 -47
  21. package/src/substrate/lsp/source-tracking.ts +247 -0
  22. package/src/tool/code_find/guidance.ts +3 -3
  23. package/src/tool/code_graph/guidance.ts +2 -2
  24. package/src/tool/code_health/guidance.ts +3 -3
  25. package/src/tool/code_health/markdown.ts +21 -5
  26. package/src/tool/code_health/refresh-outcome.ts +11 -0
  27. package/src/tool/code_health/refresh-status.ts +25 -4
  28. package/src/tool/code_health/tui.ts +4 -0
  29. package/src/tool/code_inspect/guidance.ts +5 -3
  30. package/src/tool/code_orientation/guidance.ts +3 -4
  31. package/src/tool/code_refactor_apply/guidance.ts +1 -1
  32. package/src/tool/code_refactor_plan/guidance.ts +3 -5
  33. package/src/tool/code_resolve/guidance.ts +3 -3
  34. 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
- 'Search source shape with mode:"ast" or LSP workspace symbols with mode:"semantic". Modes never silently fall back. Unscoped AST scans cover visible supported files, skipping hidden and generated/dependency directories.';
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 = "explicit structural or semantic code search";
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 evidence; use PI grep for literal/regex source search and code_graph references for symbol-identity relationships.",
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
- "Collect references, structural callees, and implementations for one target. Callees match source shape, not symbol identity.";
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 = "provider-backed and structural relation evidence";
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 as observations, language-server route status, and final semantic health state. Tracked-file snapshots do not prove workspace completeness; server inventory and route issue counts are workspace-wide. Server-only calls are passive.";
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 workspace health observations";
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 potentially stale diagnostics; explicit refresh can recover crashed routes with tracked files in scope.",
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 outcome = [staleRestart, processCrashRecovery].filter(
114
- (value): value is string => value !== null,
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 exact point for syntax, the narrowest enclosing declaration, hover, definition, and nearby diagnostics. Use for point facts, not broad Orientation.";
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 = "factual point inspection";
4
+ export const promptSnippet = "inspect a source location";
5
5
 
6
- export const promptGuidelines: string[] = [];
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
- "Orient around the workspace or one path, module, or target focus before surgical work. Omit focus for workspace Orientation. Workspace Orientation includes manifest and dependency inventories. For one-file work, use focused path Orientation. Directory focus may surface local instruction files.";
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/path/module/symbol Orientation";
4
+ export const promptSnippet = "orient around workspace or code context";
5
5
 
6
6
  export const promptGuidelines = [
7
- "Use code_orientation before broad file reading when you need direct workspace, package, directory, file, or symbol facts.",
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 one fresh stored refactor plan by planId after safety checks; mutates its files.";
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 one semantic rename, extraction, import cleanup, or dead-code deletion and return a planId without mutating files.";
2
+ "Preview a semantic refactor without changing files or falling back to text edits.";
3
3
 
4
- export const promptSnippet = "preview a precise semantic refactor";
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 one provider-backed anchor or semantic symbol query to session handles, or enumerate a file's declarations as a bounded Target group. Anchors must identify real symbols";
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 a precise target or enumerate a file’s target group";
4
+ export const promptSnippet = "resolve symbols or file declarations";
5
5
 
6
6
  export const promptGuidelines = [
7
- "Use code_resolve when a symbol query may be ambiguous, when later calls should share a stable target handle, or when a known file’s declarations should be enumerated.",
7
+ "Use code_resolve when a symbol query can match multiple symbols or when a later tool requires a target handle.",
8
8
  ];
@@ -1,10 +1,8 @@
1
- // Model-facing prompt surfaces for the eight public code-intelligence tools.
2
- //
3
- // Ownership map (docs/pi/tool-guidance.md): each model-facing fact lives in
4
- // exactly one home — the per-tool guidance module. This aggregator assembles
5
- // the canonical surface map consumed by registration.
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: CodeIntelligenceToolPromptSurfaceMap = {
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;