@mrclrchtr/supi-code-intelligence 6.1.0 → 6.2.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 (114) hide show
  1. package/README.md +1 -1
  2. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
  5. package/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
  6. package/node_modules/@mrclrchtr/supi-lsp/README.md +21 -3
  7. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  8. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  9. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
  10. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
  11. package/node_modules/@mrclrchtr/supi-lsp/package.json +4 -5
  12. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +12 -7
  13. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +1 -1
  14. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +0 -5
  15. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +6 -3
  16. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +2 -1
  17. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +5 -2
  18. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +2 -1
  19. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +17 -5
  20. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +40 -18
  21. package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +164 -30
  22. package/node_modules/@mrclrchtr/supi-lsp/src/config/lsp-settings.ts +9 -87
  23. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +0 -1
  24. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +4 -5
  25. package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +0 -29
  26. package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +7 -21
  27. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-severity.ts +6 -0
  28. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-summary.ts +11 -14
  29. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +0 -5
  30. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/stale-diagnostics.ts +0 -1
  31. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +29 -49
  32. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +1 -1
  33. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +4 -8
  34. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-helpers.ts +1 -9
  35. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +6 -3
  36. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +9 -1
  37. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +26 -41
  38. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +275 -99
  39. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +4 -1
  40. package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +59 -28
  41. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +27 -19
  42. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +26 -0
  43. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registration.ts +0 -17
  44. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +83 -19
  45. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +5 -2
  46. package/node_modules/@mrclrchtr/supi-lsp/src/session/scanner.ts +24 -21
  47. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +29 -3
  48. package/node_modules/@mrclrchtr/supi-lsp/src/summary.ts +19 -40
  49. package/node_modules/@mrclrchtr/supi-lsp/src/utils.ts +13 -19
  50. package/node_modules/@mrclrchtr/supi-lsp/src/workspace-path-policy.ts +287 -0
  51. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +6 -18
  52. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  53. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  54. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
  55. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
  56. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -5
  57. package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +1 -11
  58. package/node_modules/@mrclrchtr/supi-tree-sitter/src/coordinates.ts +19 -0
  59. package/node_modules/@mrclrchtr/supi-tree-sitter/src/language.ts +5 -23
  60. package/node_modules/@mrclrchtr/supi-tree-sitter/src/operation-support.ts +3 -21
  61. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +5 -9
  62. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +10 -20
  63. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +7 -1
  64. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +9 -5
  65. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +134 -9
  66. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-name.ts +96 -0
  67. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +11 -12
  68. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +39 -56
  69. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +9 -7
  70. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/js-binding-pattern.ts +26 -0
  71. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +2 -21
  72. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-html-sql.ts +144 -12
  73. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline.ts +50 -37
  74. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/scope.ts +138 -0
  75. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +0 -7
  76. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +1 -14
  77. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +0 -10
  78. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +6 -8
  79. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +34 -10
  80. package/package.json +5 -5
  81. package/src/analysis/capability/capability-warnings.ts +4 -22
  82. package/src/analysis/search/ast-scan-timing.ts +4 -2
  83. package/src/analysis/search/ast-scan.ts +1 -1
  84. package/src/api.ts +1 -0
  85. package/src/extension.ts +4 -2
  86. package/src/session/health-refresh.ts +24 -2
  87. package/src/session/health-types.ts +19 -0
  88. package/src/session/input/health-refactor.ts +16 -14
  89. package/src/session/refactor-types.ts +14 -1
  90. package/src/session/refactor-workflow.ts +21 -16
  91. package/src/substrate/lsp/lifecycle.ts +0 -3
  92. package/src/substrate/lsp/maintenance.ts +4 -5
  93. package/src/substrate/lsp/settings.ts +6 -9
  94. package/src/substrate/lsp/state.ts +0 -2
  95. package/src/substrate/workspace-provider-host.ts +2 -2
  96. package/src/tool/code_health/file-scope-markdown.ts +43 -0
  97. package/src/tool/code_health/markdown.ts +66 -66
  98. package/src/tool/code_health/refresh-outcome.ts +46 -0
  99. package/src/tool/code_health/refresh-status.ts +166 -23
  100. package/src/tool/code_health/result.ts +1 -0
  101. package/src/tool/code_health/tui.ts +18 -5
  102. package/src/tool/code_refactor_apply/guidance.ts +3 -2
  103. package/src/tool/code_refactor_plan/guidance.ts +1 -1
  104. package/src/tool/register.ts +5 -2
  105. package/src/tool/schemas.ts +21 -11
  106. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-actions.ts +0 -59
  107. package/node_modules/@mrclrchtr/supi-lsp/src/pattern-matcher.ts +0 -24
  108. package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-kotlin-wasm.mjs +0 -132
  109. package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-sql-wasm.mjs +0 -158
  110. package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/vendor-wasm.mjs +0 -167
  111. package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -42
  112. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-registration.ts +0 -37
  113. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/service-registry.ts +0 -30
  114. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/structure.ts +0 -8
@@ -1,3 +1,11 @@
1
+ import type { ProcessCrashRecoverySummary } from "@mrclrchtr/supi-lsp/api";
2
+ import {
3
+ formatProcessCrashRecovery,
4
+ formatStaleDiagnosticRestarts,
5
+ isFileReadinessPending,
6
+ } from "./refresh-outcome.ts";
7
+ import { readSemanticHealthState } from "./semantic-state.ts";
8
+
1
9
  /** Render current and retained health refresh status for the TUI. */
2
10
 
3
11
  export function readRefreshStatus(data: Record<string, unknown> | null): string | null {
@@ -5,25 +13,10 @@ export function readRefreshStatus(data: Record<string, unknown> | null): string
5
13
  if (!refresh) return null;
6
14
 
7
15
  switch (refresh.kind) {
8
- case "completed": {
9
- const attempted = readNumber(refresh.attemptedActiveClients);
10
- const restarted = readNumber(refresh.restartedClients);
11
- const stale = readRecord(refresh.staleAssessment);
12
- const noOp = attempted === 0 && restarted === 0;
13
- const label = refreshAttemptLabel(refresh);
14
- const base = noOp
15
- ? `${label} completed no-op`
16
- : `${label} completed: ${attempted} clients targeted, ${restarted} restarted`;
17
- const evidence = formatDiagnosticEvidence(readRecord(refresh.diagnosticEvidence));
18
- const withEvidence = evidence ? `${base}; ${evidence}` : base;
19
- return stale?.suspected === true
20
- ? `${withEvidence}; stale pattern suspected in ${readNumber(stale.matchedFileCount)} files`
21
- : withEvidence;
22
- }
23
- case "failed": {
24
- const evidence = formatDiagnosticEvidence(readRecord(refresh.diagnosticEvidence));
25
- return `${refreshAttemptLabel(refresh)} failed${typeof refresh.reason === "string" ? `: ${refresh.reason}` : ""}${evidence ? `; ${evidence}` : ""}`;
26
- }
16
+ case "completed":
17
+ return formatCompletedRefreshStatus(refresh, isPendingFileReadiness(data));
18
+ case "failed":
19
+ return formatFailedRefreshStatus(refresh);
27
20
  case "not-attempted":
28
21
  return `refresh attempt not started${formatLastRefreshSuffix(refresh)}`;
29
22
  case "not-requested":
@@ -33,6 +26,89 @@ export function readRefreshStatus(data: Record<string, unknown> | null): string
33
26
  }
34
27
  }
35
28
 
29
+ function formatCompletedRefreshStatus(
30
+ refresh: Record<string, unknown>,
31
+ fileReadinessPending: boolean,
32
+ ): string {
33
+ const attempted = readNumber(refresh.attemptedActiveClients);
34
+ const restarted = readNumber(refresh.restartedClients);
35
+ const processCrashRecovery = formatProcessCrashRecovery(
36
+ readProcessCrashRecovery(refresh.processCrashRecovery),
37
+ );
38
+ const noOp =
39
+ !fileReadinessPending && attempted === 0 && restarted === 0 && processCrashRecovery === null;
40
+ const label = refreshAttemptLabel(refresh);
41
+ const base = fileReadinessPending
42
+ ? `${label} waiting — LSP may still be warming; retry shortly`
43
+ : noOp
44
+ ? `${label} completed no-op`
45
+ : `${label} completed: ${attempted} clients targeted`;
46
+ const outcomes = formatRefreshOutcomes(refresh, restarted, processCrashRecovery, noOp);
47
+ const withOutcome = outcomes.length > 0 ? `${base}; ${outcomes.join("; ")}` : base;
48
+ const evidence = formatDiagnosticEvidence(readRecord(refresh.diagnosticEvidence));
49
+ const withEvidence = evidence ? `${withOutcome}; ${evidence}` : withOutcome;
50
+ const stale = readRecord(refresh.staleAssessment);
51
+ return stale?.suspected === true
52
+ ? `${withEvidence}; stale pattern suspected in ${readNumber(stale.matchedFileCount)} files`
53
+ : withEvidence;
54
+ }
55
+
56
+ function formatFailedRefreshStatus(refresh: Record<string, unknown>): string {
57
+ const processCrashRecovery = formatProcessCrashRecovery(
58
+ readProcessCrashRecovery(refresh.processCrashRecovery),
59
+ );
60
+ const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
61
+ const evidence = formatDiagnosticEvidence(readRecord(refresh.diagnosticEvidence));
62
+ const reason = typeof refresh.reason === "string" ? `: ${refresh.reason}` : "";
63
+ return `${refreshAttemptLabel(refresh)} failed${reason}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${evidence ? `; ${evidence}` : ""}`;
64
+ }
65
+
66
+ function formatRefreshOutcomes(
67
+ refresh: Record<string, unknown>,
68
+ restarted: number,
69
+ processCrashRecovery: string | null,
70
+ noOp: boolean,
71
+ ): string[] {
72
+ const outcomes: string[] = [];
73
+ if (refresh.operationScope === "workspace-runtime" && !noOp) {
74
+ outcomes.push(formatStaleDiagnosticRestarts(restarted));
75
+ }
76
+ if (processCrashRecovery) outcomes.push(processCrashRecovery);
77
+ return outcomes;
78
+ }
79
+
80
+ /** Render only the material current recovery outcome for compact TUI chrome. */
81
+ export function readCompactRefreshStatus(data: Record<string, unknown> | null): string | null {
82
+ const refresh = readRecord(data?.refresh);
83
+ if (!refresh || (refresh.kind !== "completed" && refresh.kind !== "failed")) return null;
84
+
85
+ const processCrashRecovery = formatProcessCrashRecovery(
86
+ readProcessCrashRecovery(refresh.processCrashRecovery),
87
+ );
88
+ if (refresh.kind === "failed") {
89
+ const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
90
+ return (
91
+ [staleRestart, processCrashRecovery]
92
+ .filter((value): value is string => value !== null)
93
+ .join("; ") || null
94
+ );
95
+ }
96
+ const fileReadinessPending = isPendingFileReadiness(data);
97
+ if (
98
+ !processCrashRecovery &&
99
+ readNumber(refresh.restartedClients) === 0 &&
100
+ !fileReadinessPending
101
+ ) {
102
+ return null;
103
+ }
104
+
105
+ const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
106
+ const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
107
+ return [staleRestart, processCrashRecovery, readiness]
108
+ .filter((value): value is string => value !== null)
109
+ .join("; ");
110
+ }
111
+
36
112
  export function readPreviousRefreshStatus(data: Record<string, unknown> | null): string | null {
37
113
  const refresh = readRecord(data?.refresh);
38
114
  if (!refresh || (refresh.kind !== "not-requested" && refresh.kind !== "not-attempted")) {
@@ -41,11 +117,23 @@ export function readPreviousRefreshStatus(data: Record<string, unknown> | null):
41
117
  const attempt = readRecord(refresh.lastAttempt);
42
118
  if (!attempt) return null;
43
119
  const evidence = formatCompactDiagnosticEvidence(readRecord(attempt.diagnosticEvidence));
120
+ const processCrashRecovery = formatProcessCrashRecovery(
121
+ readProcessCrashRecovery(attempt.processCrashRecovery),
122
+ );
123
+ const staleRestart = formatStaleDiagnosticRestartsForRecord(attempt);
124
+ const fileReadinessPending =
125
+ attempt.kind === "completed" &&
126
+ isFileReadinessPending(attempt, readSemanticHealthState(data?.semanticState));
127
+ const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
128
+ const outcome = [readiness, staleRestart, processCrashRecovery, evidence]
129
+ .filter((value): value is string => value !== null)
130
+ .join("; ");
44
131
  if (attempt.kind === "failed") {
45
- return `last ${refreshAttemptLabel(attempt)} failed${evidence ? ` (${evidence})` : ""}`;
132
+ return `last ${refreshAttemptLabel(attempt)} failed${outcome ? ` (${outcome})` : ""}`;
46
133
  }
47
134
  if (attempt.kind === "completed") {
48
- return `last ${refreshAttemptLabel(attempt)} completed${evidence ? ` (${evidence})` : ""}`;
135
+ const status = fileReadinessPending ? "waiting" : "completed";
136
+ return `last ${refreshAttemptLabel(attempt)} ${status}${outcome ? ` (${outcome})` : ""}`;
49
137
  }
50
138
  return null;
51
139
  }
@@ -54,16 +142,45 @@ function refreshAttemptLabel(attempt: Record<string, unknown>): string {
54
142
  return attempt.operationScope === "file-runtime" ? "file maintenance attempt" : "refresh attempt";
55
143
  }
56
144
 
145
+ function formatStaleDiagnosticRestartsForRecord(attempt: Record<string, unknown>): string | null {
146
+ if (
147
+ attempt.operationScope !== "workspace-runtime" ||
148
+ typeof attempt.restartedClients !== "number"
149
+ ) {
150
+ return null;
151
+ }
152
+ const processCrashRecovery = formatProcessCrashRecovery(
153
+ readProcessCrashRecovery(attempt.processCrashRecovery),
154
+ );
155
+ const noOp =
156
+ attempt.kind === "completed" &&
157
+ readNumber(attempt.attemptedActiveClients) === 0 &&
158
+ attempt.restartedClients === 0 &&
159
+ processCrashRecovery === null;
160
+ return noOp ? null : formatStaleDiagnosticRestarts(attempt.restartedClients);
161
+ }
162
+
57
163
  function formatLastRefreshSuffix(refresh: Record<string, unknown>): string {
58
164
  const attempt = readRecord(refresh.lastAttempt);
59
165
  if (!attempt) return "";
60
166
  const evidence = formatDiagnosticEvidence(readRecord(attempt.diagnosticEvidence));
167
+ const staleRestart = formatStaleDiagnosticRestartsForRecord(attempt);
168
+ const processCrashRecovery = formatProcessCrashRecovery(
169
+ readProcessCrashRecovery(attempt.processCrashRecovery),
170
+ );
171
+ const fileReadinessPending =
172
+ attempt.kind === "completed" && isFileReadinessPending(attempt, null);
173
+ const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
174
+ const outcome = [readiness, staleRestart, processCrashRecovery, evidence]
175
+ .filter((value): value is string => value !== null)
176
+ .join("; ");
61
177
  if (attempt.kind === "failed") {
62
178
  const reason = typeof attempt.reason === "string" ? `: ${attempt.reason}` : "";
63
- return `; last ${refreshAttemptLabel(attempt)} failed${reason}${evidence ? `; ${evidence}` : ""}`;
179
+ return `; last ${refreshAttemptLabel(attempt)} failed${reason}${outcome ? `; ${outcome}` : ""}`;
64
180
  }
65
181
  if (attempt.kind === "completed") {
66
- return `; last ${refreshAttemptLabel(attempt)} completed${evidence ? `; ${evidence}` : ""}`;
182
+ const status = fileReadinessPending ? "waiting" : "completed";
183
+ return `; last ${refreshAttemptLabel(attempt)} ${status}${outcome ? `; ${outcome}` : ""}`;
67
184
  }
68
185
  return "";
69
186
  }
@@ -80,6 +197,32 @@ function formatDiagnosticEvidence(evidence: Record<string, unknown> | null): str
80
197
  return `${counts[0]} requested, ${counts[1]} confirmed, ${counts[2]} unconfirmed, ${counts[3]} failed, ${counts[4]} removed`;
81
198
  }
82
199
 
200
+ export function isPendingFileReadiness(data: Record<string, unknown> | null): boolean {
201
+ const observation = readRecord(data?.diagnosticObservation);
202
+ const scope = readRecord(observation?.scope);
203
+ if (scope?.kind !== "file") return false;
204
+
205
+ const refresh = readRecord(data?.refresh);
206
+ const attempt = refresh && typeof refresh.operationScope === "string" ? refresh : null;
207
+ return isFileReadinessPending(attempt, readSemanticHealthState(data?.semanticState));
208
+ }
209
+
210
+ function readProcessCrashRecovery(value: unknown): ProcessCrashRecoverySummary | null {
211
+ const summary = readRecord(value);
212
+ if (!summary) return null;
213
+ const attemptedRoutes = summary.attemptedRoutes;
214
+ const recoveredRoutes = summary.recoveredRoutes;
215
+ const failedRoutes = summary.failedRoutes;
216
+ if (
217
+ !isEvidenceCount(attemptedRoutes) ||
218
+ !isEvidenceCount(recoveredRoutes) ||
219
+ !isEvidenceCount(failedRoutes)
220
+ ) {
221
+ return null;
222
+ }
223
+ return { attemptedRoutes, recoveredRoutes, failedRoutes };
224
+ }
225
+
83
226
  function readEvidenceCounts(evidence: Record<string, unknown> | null): number[] | null {
84
227
  if (!evidence) return null;
85
228
  const counts = [
@@ -19,6 +19,7 @@ export type {
19
19
  HealthDiagnosticEntry,
20
20
  HealthDiagnosticObservation,
21
21
  HealthDiagnosticScope,
22
+ HealthFileReadiness,
22
23
  HealthRefreshAttempt,
23
24
  HealthRefreshState,
24
25
  HealthSection,
@@ -24,7 +24,12 @@ import {
24
24
  type ToolResult,
25
25
  } from "../../ui/tui/common.ts";
26
26
  import type { CodeHealthToolParams } from "./execute.ts";
27
- import { readPreviousRefreshStatus, readRefreshStatus } from "./refresh-status.ts";
27
+ import {
28
+ isPendingFileReadiness,
29
+ readCompactRefreshStatus,
30
+ readPreviousRefreshStatus,
31
+ readRefreshStatus,
32
+ } from "./refresh-status.ts";
28
33
  import { formatSemanticHealthState, readSemanticHealthState } from "./semantic-state.ts";
29
34
 
30
35
  /** ── renderCall ────────────────────────────────────────────────── */
@@ -125,6 +130,7 @@ function buildCompactSummary(data: Record<string, unknown> | null, theme: Theme)
125
130
  const semanticRequested = sections.some(
126
131
  (section) => section.key === "diagnostics" || section.key === "servers",
127
132
  );
133
+ const fileReadinessPending = isPendingFileReadiness(data);
128
134
  const segments = sections.map((section) =>
129
135
  formatSectionSummary(
130
136
  section,
@@ -132,6 +138,7 @@ function buildCompactSummary(data: Record<string, unknown> | null, theme: Theme)
132
138
  ? formatCompactDiagnosticEvidence(readDiagnosticEvidence(data))
133
139
  : null,
134
140
  theme,
141
+ fileReadinessPending,
135
142
  ),
136
143
  );
137
144
  if (semanticRequested) {
@@ -142,8 +149,8 @@ function buildCompactSummary(data: Record<string, unknown> | null, theme: Theme)
142
149
  `${theme.fg("dim", "lsp")} ${theme.fg(statusColor, semanticStatus)}${routeSummary ? ` ${theme.fg("warning", routeSummary)}` : ""}`,
143
150
  );
144
151
  }
145
- const previousRefresh = readPreviousRefreshStatus(data);
146
- if (previousRefresh) segments.push(theme.fg("dim", previousRefresh));
152
+ const refreshStatus = readCompactRefreshStatus(data) ?? readPreviousRefreshStatus(data);
153
+ if (refreshStatus) segments.push(theme.fg("dim", refreshStatus));
147
154
  const capabilityWarningCount = readCapabilityWarnings(data).length;
148
155
  if (capabilityWarningCount > 0) {
149
156
  const label = capabilityWarningCount === 1 ? "capability warning" : "capability warnings";
@@ -158,11 +165,14 @@ function formatSectionSummary(
158
165
  section: HealthSectionSummary,
159
166
  coverage: string | null,
160
167
  theme: Theme,
168
+ fileReadinessPending: boolean,
161
169
  ): string {
162
170
  const label = sectionLabel(section.key);
163
171
  const coverageSuffix = coverage ? ` (${coverage})` : "";
164
172
  if (section.status === "unavailable" || !section.available) {
165
- return `${theme.fg("dim", label)} ${theme.fg("warning", "unavailable")}${coverageSuffix}`;
173
+ const status =
174
+ section.key === "diagnostics" && fileReadinessPending ? "pending" : "unavailable";
175
+ return `${theme.fg("dim", label)} ${theme.fg("warning", status)}${coverageSuffix}`;
166
176
  }
167
177
  if (section.status === "partial") {
168
178
  return `${theme.fg("dim", label)} ${theme.fg("warning", "partial")}${coverageSuffix}`;
@@ -307,7 +317,10 @@ function buildDiagnosticSummary(data: Record<string, unknown> | null, theme: The
307
317
  const section = readHealthSections(data).find((entry) => entry.key === "diagnostics");
308
318
  if (!section) return new Text("", 0, 0);
309
319
  if (section.status === "unavailable" || !section.available) {
310
- return new Text(theme.fg("warning", "Diagnostics unavailable"), 0, 0);
320
+ const label = isPendingFileReadiness(data)
321
+ ? "Diagnostics pending — LSP may still be warming; retry shortly"
322
+ : "Diagnostics unavailable";
323
+ return new Text(theme.fg("warning", label), 0, 0);
311
324
  }
312
325
  const trackedFiles = diagnosticScopeKind(data) === "tracked-files";
313
326
  if (section.status === "partial") {
@@ -1,5 +1,6 @@
1
- export const toolDescription = "Apply one stored refactor plan by planId.";
1
+ export const toolDescription =
2
+ "Apply one fresh stored refactor plan by planId after safety checks; mutates its files.";
2
3
 
3
- export const promptSnippet = "apply a fresh stored refactor plan";
4
+ export const promptSnippet = "apply a stored refactor plan";
4
5
 
5
6
  export const promptGuidelines: string[] = [];
@@ -1,5 +1,5 @@
1
1
  export const toolDescription =
2
- "Preview one semantic rename or extraction and return a planId without mutating files.";
2
+ "Preview one semantic rename, extraction, import cleanup, or dead-code deletion and return a planId without mutating files.";
3
3
 
4
4
  export const promptSnippet = "preview a precise semantic refactor";
5
5
 
@@ -1,7 +1,10 @@
1
1
  import { createHmac, randomBytes } from "node:crypto";
2
2
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
3
- import { recordDebugEvent, startDebugTimer } from "@mrclrchtr/supi-core/debug";
4
- import { truncateIdentity } from "@mrclrchtr/supi-lsp/debug-telemetry";
3
+ import {
4
+ recordDebugEvent,
5
+ startDebugTimer,
6
+ truncateDebugIdentity as truncateIdentity,
7
+ } from "@mrclrchtr/supi-core/debug";
5
8
  import type { WorkspaceCodeIntelligenceSession } from "../session/session.ts";
6
9
  import {
7
10
  CODE_INTELLIGENCE_TOOL_PROMPT_SURFACES,
@@ -1,5 +1,9 @@
1
1
  import { StringEnum } from "@earendil-works/pi-ai";
2
2
  import { type TSchema, Type } from "typebox";
3
+ import {
4
+ PUBLIC_REFACTOR_OPERATION_NAMES,
5
+ type PublicRefactorOperationName,
6
+ } from "../session/refactor-types.ts";
3
7
  import { TARGET_SYMBOL_KINDS } from "../session/target-input.ts";
4
8
 
5
9
  export const ScopeParam = Type.String({
@@ -127,18 +131,24 @@ const RangeParam = Type.Object(
127
131
 
128
132
  const NewNameParam = Type.String({ description: "New symbol name.", minLength: 1 });
129
133
 
134
+ const RefactorOperationProperties = {
135
+ rename_symbol: Type.Object({ newName: NewNameParam }, { additionalProperties: false }),
136
+ extract_function: Type.Object(
137
+ { newName: NewNameParam, range: RangeParam },
138
+ { additionalProperties: false },
139
+ ),
140
+ extract_variable: Type.Object(
141
+ { newName: NewNameParam, range: RangeParam },
142
+ { additionalProperties: false },
143
+ ),
144
+ update_imports: Type.Object({}, { additionalProperties: false }),
145
+ delete_dead_code: Type.Object({}, { additionalProperties: false }),
146
+ } satisfies Record<PublicRefactorOperationName, TSchema>;
147
+
130
148
  export const RefactorOperationParam = exactOneSelector(
131
- {
132
- rename_symbol: Type.Object({ newName: NewNameParam }, { additionalProperties: false }),
133
- extract_function: Type.Object(
134
- { newName: NewNameParam, range: RangeParam },
135
- { additionalProperties: false },
136
- ),
137
- extract_variable: Type.Object(
138
- { newName: NewNameParam, range: RangeParam },
139
- { additionalProperties: false },
140
- ),
141
- },
149
+ Object.fromEntries(
150
+ PUBLIC_REFACTOR_OPERATION_NAMES.map((name) => [name, RefactorOperationProperties[name]]),
151
+ ),
142
152
  "Exactly one refactor operation.",
143
153
  );
144
154
 
@@ -1,59 +0,0 @@
1
- // Server capability action label helpers — maps LSP capabilities to readable action labels.
2
-
3
- import type { ServerCapabilities } from "../config/types.ts";
4
-
5
- interface LspServerSupportedActionSpec {
6
- label: string;
7
- isSupported: (capabilities: ServerCapabilities | null | undefined) => boolean;
8
- }
9
-
10
- const LSP_SERVER_SUPPORTED_ACTION_SPECS: readonly LspServerSupportedActionSpec[] = [
11
- {
12
- label: "diagnostics [optional file]",
13
- isSupported: () => true,
14
- },
15
- {
16
- label: "hover(file,line,char)",
17
- isSupported: (capabilities) => Boolean(capabilities?.hoverProvider),
18
- },
19
- {
20
- label: "definition(file,line,char)",
21
- isSupported: (capabilities) => Boolean(capabilities?.definitionProvider),
22
- },
23
- {
24
- label: "references(file,line,char)",
25
- isSupported: (capabilities) => Boolean(capabilities?.referencesProvider),
26
- },
27
- {
28
- label: "implementation(file,line,char)",
29
- isSupported: (capabilities) => Boolean(capabilities?.implementationProvider),
30
- },
31
- {
32
- label: "symbols(file)",
33
- isSupported: (capabilities) => Boolean(capabilities?.documentSymbolProvider),
34
- },
35
- {
36
- label: "workspace_symbols(query)",
37
- isSupported: (capabilities) => Boolean(capabilities?.workspaceSymbolProvider),
38
- },
39
- {
40
- label: "rename(file,line,char,newName)",
41
- isSupported: (capabilities) => Boolean(capabilities?.renameProvider),
42
- },
43
- {
44
- label: "code_actions(file,line,char)",
45
- isSupported: (capabilities) => Boolean(capabilities?.codeActionProvider),
46
- },
47
- ] as const;
48
-
49
- /**
50
- * Get the list of supported LSP server actions based on capabilities.
51
- */
52
- export function getSupportedLspServerActions(
53
- capabilities: ServerCapabilities | null | undefined,
54
- ): string[] {
55
- if (!capabilities) return [];
56
- return LSP_SERVER_SUPPORTED_ACTION_SPECS.filter((spec) => spec.isSupported(capabilities)).map(
57
- (spec) => spec.label,
58
- );
59
- }
@@ -1,24 +0,0 @@
1
- import ignore from "ignore";
2
-
3
- /** Gitignore-style glob pattern matching for path exclusion.
4
- *
5
- * Delegates to the {@link https://github.com/kaelzhang/node-ignore | ignore} package,
6
- * which provides battle-tested .gitignore semantics used by ESLint, Prettier, and others.
7
- *
8
- * Supports full gitignore syntax: literal names, `*` / `?` wildcards, `**` recursive globs,
9
- * leading `/` anchored patterns, trailing `/` directory-only patterns, and `!` negation.
10
- *
11
- * **Note:** Patterns that start with `#` are treated as comments unless escaped with `\#`.
12
- */
13
-
14
- /**
15
- * Check whether a project-relative file path matches a gitignore-style glob pattern.
16
- *
17
- * @param filePath - Relative file path with forward slashes (e.g. `"src/__tests__/bar.test.ts"`)
18
- * @param pattern - Gitignore-style glob pattern (e.g. `"__tests__/"`, `"*.test.ts"`, `"/build"`)
19
- * @returns `true` if the file path matches the pattern
20
- */
21
- export function isGlobMatch(filePath: string, pattern: string): boolean {
22
- if (!filePath || !pattern) return false;
23
- return ignore().add(pattern).ignores(filePath);
24
- }
@@ -1,132 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawnSync } from "node:child_process";
4
- import { createHash } from "node:crypto";
5
- import * as fs from "node:fs";
6
- import { createRequire } from "node:module";
7
- import * as os from "node:os";
8
- import * as path from "node:path";
9
- import { fileURLToPath } from "node:url";
10
-
11
- const require = createRequire(import.meta.url);
12
- const scriptDir = path.dirname(fileURLToPath(import.meta.url));
13
- const packageRoot = path.resolve(scriptDir, "..");
14
- const wasmDir = path.join(packageRoot, "resources", "grammars", "kotlin");
15
- const wasmPath = path.join(wasmDir, "tree-sitter-kotlin.wasm");
16
- const metadataPath = path.join(wasmDir, "tree-sitter-kotlin.wasm.json");
17
- const checkMode = process.argv.includes("--check");
18
-
19
- function readPackage(packageName) {
20
- const packageJsonPath = require.resolve(`${packageName}/package.json`);
21
- return {
22
- dir: path.dirname(packageJsonPath),
23
- json: JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")),
24
- };
25
- }
26
-
27
- function sha256(filePath) {
28
- return createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
29
- }
30
-
31
- function readMetadata() {
32
- return JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
33
- }
34
-
35
- function expectedReleaseAsset(version) {
36
- return `https://github.com/fwcd/tree-sitter-kotlin/releases/download/${version}/tree-sitter-kotlin.wasm`;
37
- }
38
-
39
- function assertKotlinWasmCurrent() {
40
- const kotlinPackage = readPackage("tree-sitter-kotlin");
41
- const cliPackage = readPackage("tree-sitter-cli");
42
- const metadata = readMetadata();
43
- const actualSha = sha256(wasmPath);
44
- const errors = [];
45
-
46
- if (metadata.source?.npmPackage !== "tree-sitter-kotlin") {
47
- errors.push("metadata source package must be tree-sitter-kotlin");
48
- }
49
- if (metadata.source?.version !== kotlinPackage.json.version) {
50
- errors.push(
51
- `metadata pins tree-sitter-kotlin ${metadata.source?.version}, but installed package is ${kotlinPackage.json.version}`,
52
- );
53
- }
54
- if (metadata.source?.releaseAsset !== expectedReleaseAsset(kotlinPackage.json.version)) {
55
- errors.push(
56
- "metadata release asset URL does not match the installed tree-sitter-kotlin version",
57
- );
58
- }
59
- if (metadata.generatedWith?.treeSitterCli !== cliPackage.json.version) {
60
- errors.push(
61
- `metadata pins tree-sitter-cli ${metadata.generatedWith?.treeSitterCli}, but installed CLI is ${cliPackage.json.version}`,
62
- );
63
- }
64
- if (metadata.sha256 !== actualSha) {
65
- errors.push(`metadata sha256 ${metadata.sha256} does not match vendored file ${actualSha}`);
66
- }
67
-
68
- if (errors.length > 0) {
69
- throw new Error(
70
- `Vendored Kotlin Tree-sitter WASM is stale:\n- ${errors.join("\n- ")}\nRun: pnpm --filter @mrclrchtr/supi-tree-sitter generate:kotlin-wasm`,
71
- );
72
- }
73
-
74
- process.stdout.write(
75
- `Kotlin Tree-sitter WASM is current (${kotlinPackage.json.version}, ${actualSha}).\n`,
76
- );
77
- }
78
-
79
- function generateKotlinWasm() {
80
- const kotlinPackage = readPackage("tree-sitter-kotlin");
81
- const cliPackage = readPackage("tree-sitter-cli");
82
- const cliPath = path.join(cliPackage.dir, "cli.js");
83
- const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "supi-kotlin-wasm-"));
84
- const grammarDir = path.join(tempRoot, "tree-sitter-kotlin");
85
-
86
- try {
87
- fs.cpSync(kotlinPackage.dir, grammarDir, { recursive: true });
88
- const result = spawnSync(process.execPath, [cliPath, "build", "--wasm"], {
89
- cwd: grammarDir,
90
- stdio: "inherit",
91
- });
92
-
93
- if (result.status !== 0) {
94
- throw new Error(
95
- "tree-sitter build --wasm failed. Install Docker or Emscripten, then rerun generate:kotlin-wasm.",
96
- );
97
- }
98
-
99
- const generatedWasmPath = path.join(grammarDir, "tree-sitter-kotlin.wasm");
100
- if (!fs.existsSync(generatedWasmPath)) {
101
- throw new Error(`Expected generated WASM at ${generatedWasmPath}`);
102
- }
103
-
104
- fs.mkdirSync(wasmDir, { recursive: true });
105
- fs.copyFileSync(generatedWasmPath, wasmPath);
106
-
107
- const checksum = sha256(wasmPath);
108
- const metadata = {
109
- source: {
110
- npmPackage: "tree-sitter-kotlin",
111
- version: kotlinPackage.json.version,
112
- repository: "https://github.com/fwcd/tree-sitter-kotlin",
113
- releaseAsset: expectedReleaseAsset(kotlinPackage.json.version),
114
- },
115
- generatedWith: {
116
- treeSitterCli: cliPackage.json.version,
117
- },
118
- sha256: checksum,
119
- };
120
- fs.writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`);
121
- process.stdout.write(`Generated ${wasmPath}\n`);
122
- process.stdout.write(`SHA256 ${checksum}\n`);
123
- } finally {
124
- fs.rmSync(tempRoot, { recursive: true, force: true });
125
- }
126
- }
127
-
128
- if (checkMode) {
129
- assertKotlinWasmCurrent();
130
- } else {
131
- generateKotlinWasm();
132
- }