@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.
- package/README.md +1 -1
- 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-core/src/debug-identity.ts +11 -0
- package/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
- package/node_modules/@mrclrchtr/supi-lsp/README.md +21 -3
- 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/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
- package/node_modules/@mrclrchtr/supi-lsp/package.json +4 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +12 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +0 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +6 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +2 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +2 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +17 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +40 -18
- package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +164 -30
- package/node_modules/@mrclrchtr/supi-lsp/src/config/lsp-settings.ts +9 -87
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +0 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +4 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +0 -29
- package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +7 -21
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-severity.ts +6 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-summary.ts +11 -14
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +0 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/stale-diagnostics.ts +0 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +29 -49
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +4 -8
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-helpers.ts +1 -9
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +6 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +9 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +26 -41
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +275 -99
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +4 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +59 -28
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +27 -19
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +26 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registration.ts +0 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +83 -19
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +5 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/session/scanner.ts +24 -21
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +29 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/summary.ts +19 -40
- package/node_modules/@mrclrchtr/supi-lsp/src/utils.ts +13 -19
- package/node_modules/@mrclrchtr/supi-lsp/src/workspace-path-policy.ts +287 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +6 -18
- 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/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -5
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +1 -11
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/coordinates.ts +19 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/language.ts +5 -23
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/operation-support.ts +3 -21
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +5 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +10 -20
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +7 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +9 -5
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +134 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-name.ts +96 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +11 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +39 -56
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +9 -7
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/js-binding-pattern.ts +26 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +2 -21
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-html-sql.ts +144 -12
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline.ts +50 -37
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/scope.ts +138 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +0 -7
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +1 -14
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +0 -10
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +6 -8
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +34 -10
- package/package.json +5 -5
- package/src/analysis/capability/capability-warnings.ts +4 -22
- package/src/analysis/search/ast-scan-timing.ts +4 -2
- package/src/analysis/search/ast-scan.ts +1 -1
- package/src/api.ts +1 -0
- package/src/extension.ts +4 -2
- package/src/session/health-refresh.ts +24 -2
- package/src/session/health-types.ts +19 -0
- package/src/session/input/health-refactor.ts +16 -14
- package/src/session/refactor-types.ts +14 -1
- package/src/session/refactor-workflow.ts +21 -16
- package/src/substrate/lsp/lifecycle.ts +0 -3
- package/src/substrate/lsp/maintenance.ts +4 -5
- package/src/substrate/lsp/settings.ts +6 -9
- package/src/substrate/lsp/state.ts +0 -2
- package/src/substrate/workspace-provider-host.ts +2 -2
- package/src/tool/code_health/file-scope-markdown.ts +43 -0
- package/src/tool/code_health/markdown.ts +66 -66
- package/src/tool/code_health/refresh-outcome.ts +46 -0
- package/src/tool/code_health/refresh-status.ts +166 -23
- package/src/tool/code_health/result.ts +1 -0
- package/src/tool/code_health/tui.ts +18 -5
- package/src/tool/code_refactor_apply/guidance.ts +3 -2
- package/src/tool/code_refactor_plan/guidance.ts +1 -1
- package/src/tool/register.ts +5 -2
- package/src/tool/schemas.ts +21 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-actions.ts +0 -59
- package/node_modules/@mrclrchtr/supi-lsp/src/pattern-matcher.ts +0 -24
- package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-kotlin-wasm.mjs +0 -132
- package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-sql-wasm.mjs +0 -158
- package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/vendor-wasm.mjs +0 -167
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -42
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-registration.ts +0 -37
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/service-registry.ts +0 -30
- 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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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${
|
|
132
|
+
return `last ${refreshAttemptLabel(attempt)} failed${outcome ? ` (${outcome})` : ""}`;
|
|
46
133
|
}
|
|
47
134
|
if (attempt.kind === "completed") {
|
|
48
|
-
|
|
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}${
|
|
179
|
+
return `; last ${refreshAttemptLabel(attempt)} failed${reason}${outcome ? `; ${outcome}` : ""}`;
|
|
64
180
|
}
|
|
65
181
|
if (attempt.kind === "completed") {
|
|
66
|
-
|
|
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 = [
|
|
@@ -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 {
|
|
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
|
|
146
|
-
if (
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
package/src/tool/register.ts
CHANGED
|
@@ -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 {
|
|
4
|
-
|
|
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,
|
package/src/tool/schemas.ts
CHANGED
|
@@ -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
|
-
|
|
133
|
-
|
|
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
|
-
}
|