@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
|
@@ -26,13 +26,14 @@ import {
|
|
|
26
26
|
isPlanFresh,
|
|
27
27
|
type RefactorPlan,
|
|
28
28
|
} from "./refactor-plans.ts";
|
|
29
|
-
import
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
import {
|
|
30
|
+
PUBLIC_REFACTOR_OPERATION_NAMES,
|
|
31
|
+
type PublicSourceRange,
|
|
32
|
+
type RefactorApplyWorkflowInput,
|
|
33
|
+
type RefactorApplyWorkflowOutcome,
|
|
34
|
+
type RefactorOperationInput,
|
|
35
|
+
type RefactorPlanWorkflowInput,
|
|
36
|
+
type RefactorPlanWorkflowOutcome,
|
|
36
37
|
} from "./refactor-types.ts";
|
|
37
38
|
import { resolveTargetWorkflow, type TargetWorkflowDeps } from "./target-workflow.ts";
|
|
38
39
|
import { reportProgress, throwIfAborted, type WorkflowControl } from "./workflow-control.ts";
|
|
@@ -208,13 +209,11 @@ function parseOperation(input: RefactorOperationInput):
|
|
|
208
209
|
| {
|
|
209
210
|
kind: "ok";
|
|
210
211
|
operation: RefactorOperation;
|
|
211
|
-
newName
|
|
212
|
+
newName?: string;
|
|
212
213
|
range?: PublicSourceRange;
|
|
213
214
|
}
|
|
214
215
|
| { kind: "invalid-input"; message: string } {
|
|
215
|
-
const keys =
|
|
216
|
-
(key) => key in input,
|
|
217
|
-
);
|
|
216
|
+
const keys = PUBLIC_REFACTOR_OPERATION_NAMES.filter((key) => key in input);
|
|
218
217
|
if (keys.length !== 1) {
|
|
219
218
|
return { kind: "invalid-input", message: "Select exactly one refactor operation." };
|
|
220
219
|
}
|
|
@@ -233,11 +232,17 @@ function parseOperation(input: RefactorOperationInput):
|
|
|
233
232
|
range: input.extract_function.range,
|
|
234
233
|
};
|
|
235
234
|
}
|
|
235
|
+
if ("extract_variable" in input) {
|
|
236
|
+
return {
|
|
237
|
+
kind: "ok",
|
|
238
|
+
operation: "extract_variable",
|
|
239
|
+
newName: input.extract_variable.newName,
|
|
240
|
+
range: input.extract_variable.range,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
236
243
|
return {
|
|
237
244
|
kind: "ok",
|
|
238
|
-
operation: "
|
|
239
|
-
newName: input.extract_variable.newName,
|
|
240
|
-
range: input.extract_variable.range,
|
|
245
|
+
operation: "update_imports" in input ? "update_imports" : "delete_dead_code",
|
|
241
246
|
};
|
|
242
247
|
}
|
|
243
248
|
|
|
@@ -265,13 +270,13 @@ async function planWithProvider(
|
|
|
265
270
|
file: string;
|
|
266
271
|
position: { line: number; character: number };
|
|
267
272
|
range?: SourceRange;
|
|
268
|
-
newName
|
|
273
|
+
newName?: string;
|
|
269
274
|
},
|
|
270
275
|
control?: WorkflowControl,
|
|
271
276
|
): Promise<RefactorResult> {
|
|
272
277
|
if (!provider) return { kind: "unavailable", reason: "No semantic provider is active." };
|
|
273
278
|
if (provider.refactor) return provider.refactor(request, control);
|
|
274
|
-
if (request.operation === "rename_symbol" && provider.rename) {
|
|
279
|
+
if (request.operation === "rename_symbol" && request.newName !== undefined && provider.rename) {
|
|
275
280
|
return provider.rename(request.file, request.position, request.newName, control);
|
|
276
281
|
}
|
|
277
282
|
return {
|
|
@@ -45,7 +45,6 @@ export function registerLspSessionLifecycle(
|
|
|
45
45
|
await state.providerLease?.release();
|
|
46
46
|
state.providerLease = null;
|
|
47
47
|
state.controller = null;
|
|
48
|
-
state.lspActive = false;
|
|
49
48
|
state.sentinelSnapshot = new Map();
|
|
50
49
|
|
|
51
50
|
const lease = await acquireWorkspaceProviderHost(ctx.cwd, {
|
|
@@ -53,7 +52,6 @@ export function registerLspSessionLifecycle(
|
|
|
53
52
|
});
|
|
54
53
|
state.providerLease = lease;
|
|
55
54
|
state.controller = lease.lspController;
|
|
56
|
-
state.lspActive = lease.lspController?.kind === "ready";
|
|
57
55
|
state.sentinelSnapshot = lease.sentinelSnapshot;
|
|
58
56
|
await onStarted?.(ctx);
|
|
59
57
|
startLifecycleSubscription();
|
|
@@ -64,7 +62,6 @@ export function registerLspSessionLifecycle(
|
|
|
64
62
|
await state.providerLease?.release();
|
|
65
63
|
state.providerLease = null;
|
|
66
64
|
state.controller = null;
|
|
67
|
-
state.lspActive = false;
|
|
68
65
|
state.sentinelSnapshot = new Map();
|
|
69
66
|
dispatchStateChange();
|
|
70
67
|
});
|
|
@@ -24,7 +24,6 @@ import {
|
|
|
24
24
|
isLikelyStaleDiagnostic,
|
|
25
25
|
isProjectConfigFileName,
|
|
26
26
|
raceRequestControl,
|
|
27
|
-
syncWorkspaceSentinelSnapshot,
|
|
28
27
|
type WorkspaceLspRuntime,
|
|
29
28
|
} from "@mrclrchtr/supi-lsp/api";
|
|
30
29
|
import { mergeDiagnosticEvidence } from "../../diagnostics/evidence.ts";
|
|
@@ -56,7 +55,7 @@ export async function refreshLspMaintenance(
|
|
|
56
55
|
sentinelSnapshot: Map<string, number>,
|
|
57
56
|
options: LspMaintenanceOptions = {},
|
|
58
57
|
): Promise<WorkspaceLspMaintenanceResult> {
|
|
59
|
-
const { snapshot } = await synchronizeSentinels(runtime,
|
|
58
|
+
const { snapshot } = await synchronizeSentinels(runtime, sentinelSnapshot, options);
|
|
60
59
|
let diagnosticEvidence = emptyEvidence();
|
|
61
60
|
let failureReason: string | undefined;
|
|
62
61
|
|
|
@@ -115,7 +114,7 @@ export async function refreshFileLspMaintenance(options: {
|
|
|
115
114
|
const { runtime, cwd, sentinelSnapshot, filePath, control } = options;
|
|
116
115
|
// A cancelled caller stops before any maintenance work starts.
|
|
117
116
|
throwIfCodeRequestInterrupted(control);
|
|
118
|
-
const { snapshot } = await synchronizeSentinels(runtime,
|
|
117
|
+
const { snapshot } = await synchronizeSentinels(runtime, sentinelSnapshot);
|
|
119
118
|
const target = nodePath.resolve(filePath);
|
|
120
119
|
const stale = confirmedOutstandingDiagnostics(runtime, cwd).some(
|
|
121
120
|
(entry) =>
|
|
@@ -149,12 +148,11 @@ export async function refreshFileLspMaintenance(options: {
|
|
|
149
148
|
*/
|
|
150
149
|
async function synchronizeSentinels(
|
|
151
150
|
runtime: WorkspaceLspRuntime,
|
|
152
|
-
cwd: string,
|
|
153
151
|
sentinelSnapshot: Map<string, number>,
|
|
154
152
|
options: LspMaintenanceOptions = {},
|
|
155
153
|
) {
|
|
156
154
|
const primed = sentinelSnapshot.size > 0;
|
|
157
|
-
const state = syncWorkspaceSentinelSnapshot(
|
|
155
|
+
const state = runtime.syncWorkspaceSentinelSnapshot(sentinelSnapshot, {
|
|
158
156
|
includeSourceFiles: true,
|
|
159
157
|
});
|
|
160
158
|
|
|
@@ -193,6 +191,7 @@ async function trackCreatedSourceFiles(
|
|
|
193
191
|
if (change.type !== FileChangeType.Created) continue;
|
|
194
192
|
const filePath = uriToFile(change.uri);
|
|
195
193
|
if (scope && !isWithinOrEqual(scope, filePath)) continue;
|
|
194
|
+
if (!runtime.isSupportedSourceFile(filePath)) continue;
|
|
196
195
|
// Best-effort: a file no client can serve stays untracked and is
|
|
197
196
|
// simply absent from evidence until a later explicit request.
|
|
198
197
|
await runtime.trackFile(filePath);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// LSP settings registration for the code-intelligence umbrella extension.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// `lsp.servers.<language>.enabled: false` is the only supported opt-out.
|
|
3
|
+
// Per-language disable via `lsp.servers.<language>.enabled: false` is the
|
|
4
|
+
// supported opt-out.
|
|
6
5
|
//
|
|
7
6
|
// Registered fields:
|
|
8
7
|
// - exclude: stringList
|
|
@@ -21,11 +20,9 @@ import {
|
|
|
21
20
|
} from "@mrclrchtr/supi-core/settings";
|
|
22
21
|
import { type LspSettings, loadConfig } from "@mrclrchtr/supi-lsp/api";
|
|
23
22
|
|
|
24
|
-
const LSP_DEFAULTS
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
exclude: [],
|
|
28
|
-
};
|
|
23
|
+
const LSP_DEFAULTS = {
|
|
24
|
+
exclude: [] as string[],
|
|
25
|
+
} satisfies LspSettings;
|
|
29
26
|
|
|
30
27
|
/** Discover configured servers from the defaults + effective LSP config. */
|
|
31
28
|
function getConfiguredServers(cwd: string): string[] {
|
|
@@ -153,7 +150,7 @@ export function registerLspSettings(pi: ExtensionAPI): void {
|
|
|
153
150
|
kind: "stringList" as const,
|
|
154
151
|
key: "exclude",
|
|
155
152
|
label: "Exclude Patterns",
|
|
156
|
-
description: "Gitignore patterns
|
|
153
|
+
description: "Gitignore patterns for automatic LSP workspace work (comma-separated)",
|
|
157
154
|
},
|
|
158
155
|
{
|
|
159
156
|
kind: "custom" as const,
|
|
@@ -22,7 +22,6 @@ export interface LspAdapterState {
|
|
|
22
22
|
controller: LspRuntimeController | null;
|
|
23
23
|
providerLease: WorkspaceProviderHostLease | null;
|
|
24
24
|
inspector: LspInspectorState;
|
|
25
|
-
lspActive: boolean;
|
|
26
25
|
/** Snapshot of workspace sentinel files (package.json, tsconfig, lockfiles) for change detection. */
|
|
27
26
|
sentinelSnapshot: Map<string, number>;
|
|
28
27
|
/** Fired for runtime lifecycle changes that require a footer render. */
|
|
@@ -34,7 +33,6 @@ export function createLspAdapterState(): LspAdapterState {
|
|
|
34
33
|
controller: null,
|
|
35
34
|
providerLease: null,
|
|
36
35
|
inspector: { handle: null, close: null },
|
|
37
|
-
lspActive: false,
|
|
38
36
|
sentinelSnapshot: new Map(),
|
|
39
37
|
stateChanges: new EventTarget(),
|
|
40
38
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { realpathSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { getDefaultWorkspaceRuntime } from "@mrclrchtr/supi-code-runtime/api";
|
|
4
|
-
import { LspRuntimeController
|
|
4
|
+
import { LspRuntimeController } from "@mrclrchtr/supi-lsp/api";
|
|
5
5
|
import { TreeSitterRuntimeController } from "@mrclrchtr/supi-tree-sitter/api";
|
|
6
6
|
|
|
7
7
|
const HOSTS = Symbol.for("supi-code-intelligence/workspace-provider-hosts");
|
|
@@ -77,7 +77,7 @@ class WorkspaceProviderHost {
|
|
|
77
77
|
lspController: projectTrusted ? this.#lsp : null,
|
|
78
78
|
sentinelSnapshot:
|
|
79
79
|
projectTrusted && this.#lsp?.kind === "ready"
|
|
80
|
-
?
|
|
80
|
+
? (this.#lsp.workspaceRuntime?.scanWorkspaceSentinels() ?? new Map())
|
|
81
81
|
: new Map(),
|
|
82
82
|
release: async () => {
|
|
83
83
|
if (released) return;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { HealthDiagnosticObservation } from "../../session/health-types.ts";
|
|
2
|
+
|
|
3
|
+
/** Render the tsconfig coverage result for a single-file health request. */
|
|
4
|
+
export function renderFileScopeStatus(
|
|
5
|
+
lines: string[],
|
|
6
|
+
observation: Extract<
|
|
7
|
+
HealthDiagnosticObservation,
|
|
8
|
+
{ kind: "completed" | "partial" | "unavailable" }
|
|
9
|
+
>,
|
|
10
|
+
cwd: string,
|
|
11
|
+
): void {
|
|
12
|
+
if (observation.scope.kind !== "file" || !observation.scopeStatus) return;
|
|
13
|
+
|
|
14
|
+
const decision = observation.scopeStatus;
|
|
15
|
+
switch (decision.status) {
|
|
16
|
+
case "included":
|
|
17
|
+
lines.push(
|
|
18
|
+
`**Tsconfig**: covered by ${formatConfigPath(decision.configPath, cwd)} — part of workspace diagnostics.`,
|
|
19
|
+
);
|
|
20
|
+
return;
|
|
21
|
+
case "excluded":
|
|
22
|
+
lines.push(
|
|
23
|
+
`**Tsconfig**: NOT covered by ${formatConfigPath(decision.configPath, cwd)} — not part of workspace diagnostics.`,
|
|
24
|
+
);
|
|
25
|
+
return;
|
|
26
|
+
case "no-config":
|
|
27
|
+
lines.push("**Tsconfig**: no tsconfig.json or jsconfig.json found — nothing filtered.");
|
|
28
|
+
return;
|
|
29
|
+
case "out-of-tree":
|
|
30
|
+
lines.push("**Tsconfig**: outside the project root — not part of workspace diagnostics.");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Format a config path relative to the current workspace. */
|
|
35
|
+
function formatConfigPath(configPath: string | null, cwd: string): string {
|
|
36
|
+
if (!configPath) return "no config";
|
|
37
|
+
return `\`${makeRelative(cwd, configPath)}\``;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Format a workspace path without exposing an absolute root. */
|
|
41
|
+
export function makeRelative(cwd: string, file: string): string {
|
|
42
|
+
return file.startsWith(cwd) ? file.slice(cwd.length + 1) : file;
|
|
43
|
+
}
|
|
@@ -11,9 +11,15 @@ import {
|
|
|
11
11
|
formatProjectServerRouteSummary,
|
|
12
12
|
formatProjectServerStatusReason,
|
|
13
13
|
} from "../../analysis/health/server-status.ts";
|
|
14
|
+
import { makeRelative, renderFileScopeStatus } from "./file-scope-markdown.ts";
|
|
15
|
+
import {
|
|
16
|
+
formatProcessCrashRecovery,
|
|
17
|
+
formatRefreshElapsed,
|
|
18
|
+
formatStaleDiagnosticRestarts as formatStaleDiagnosticRestartsText,
|
|
19
|
+
isFileReadinessPending,
|
|
20
|
+
} from "./refresh-outcome.ts";
|
|
14
21
|
import type {
|
|
15
22
|
HealthData,
|
|
16
|
-
HealthDiagnosticObservation,
|
|
17
23
|
HealthDiagnosticScope,
|
|
18
24
|
HealthRefreshAttempt,
|
|
19
25
|
HealthResultAssembly,
|
|
@@ -48,19 +54,25 @@ function renderRefreshStatus(
|
|
|
48
54
|
if (!hasDiagnostics) return;
|
|
49
55
|
|
|
50
56
|
switch (data.refresh.kind) {
|
|
51
|
-
case "completed":
|
|
57
|
+
case "completed": {
|
|
58
|
+
const fileReadinessPending = isFileReadinessPending(data.refresh, data.semanticState);
|
|
52
59
|
lines.push(
|
|
53
|
-
`**${refreshAttemptLabel(data.refresh)}**: ${asSentence(completedRefreshText(data.refresh))}`,
|
|
60
|
+
`**${refreshAttemptLabel(data.refresh)}**: ${asSentence(completedRefreshText(data.refresh, fileReadinessPending))}`,
|
|
54
61
|
);
|
|
55
62
|
lines.push(`**Stale assessment**: ${asSentence(staleAssessmentText(data.refresh))}`);
|
|
56
63
|
lines.push("");
|
|
57
64
|
return;
|
|
65
|
+
}
|
|
58
66
|
case "failed": {
|
|
59
67
|
const evidence = data.refresh.diagnosticEvidence
|
|
60
68
|
? `; ${formatDiagnosticEvidence(data.refresh.diagnosticEvidence)}`
|
|
61
69
|
: "";
|
|
70
|
+
const processCrashRecovery = data.refresh.processCrashRecovery
|
|
71
|
+
? formatProcessCrashRecovery(data.refresh.processCrashRecovery)
|
|
72
|
+
: null;
|
|
73
|
+
const staleRestart = formatStaleDiagnosticRestarts(data.refresh);
|
|
62
74
|
lines.push(
|
|
63
|
-
`**${refreshAttemptLabel(data.refresh)}**: failed — ${data.refresh.reason}${evidence}`,
|
|
75
|
+
`**${refreshAttemptLabel(data.refresh)}**: failed — ${data.refresh.reason}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${evidence}`,
|
|
64
76
|
);
|
|
65
77
|
lines.push("");
|
|
66
78
|
return;
|
|
@@ -84,14 +96,27 @@ function renderRefreshStatus(
|
|
|
84
96
|
|
|
85
97
|
function completedRefreshText(
|
|
86
98
|
attempt: Extract<HealthRefreshAttempt, { kind: "completed" }>,
|
|
99
|
+
fileReadinessPending = attempt.fileReadiness === "pending",
|
|
87
100
|
): string {
|
|
88
|
-
const
|
|
89
|
-
|
|
101
|
+
const processCrashRecovery = formatProcessCrashRecovery(attempt.processCrashRecovery);
|
|
102
|
+
const noOp =
|
|
103
|
+
!fileReadinessPending &&
|
|
104
|
+
attempt.attemptedActiveClients === 0 &&
|
|
105
|
+
attempt.restartedClients === 0 &&
|
|
106
|
+
processCrashRecovery === null;
|
|
107
|
+
const base = fileReadinessPending
|
|
108
|
+
? "readiness pending — LSP may still be warming; retry shortly"
|
|
109
|
+
: noOp
|
|
90
110
|
? "completed no-op — no active clients were targeted"
|
|
91
|
-
: `completed — ${attempt.attemptedActiveClients} active client${plural(attempt.attemptedActiveClients)} targeted
|
|
111
|
+
: `completed — ${attempt.attemptedActiveClients} active client${plural(attempt.attemptedActiveClients)} targeted`;
|
|
112
|
+
const staleRestart = noOp ? null : formatStaleDiagnosticRestarts(attempt);
|
|
113
|
+
const outcome = [staleRestart, processCrashRecovery].filter(
|
|
114
|
+
(value): value is string => value !== null,
|
|
115
|
+
);
|
|
116
|
+
const withOutcome = outcome.length > 0 ? `${base}; ${outcome.join("; ")}` : base;
|
|
92
117
|
return attempt.operationScope === "workspace-runtime"
|
|
93
|
-
? `${
|
|
94
|
-
:
|
|
118
|
+
? `${withOutcome}; ${formatDiagnosticEvidence(attempt.diagnosticEvidence)}`
|
|
119
|
+
: withOutcome;
|
|
95
120
|
}
|
|
96
121
|
|
|
97
122
|
function refreshAttemptLabel(attempt: Pick<HealthRefreshAttempt, "operationScope">): string {
|
|
@@ -117,7 +142,7 @@ function renderLastAttempt(lines: string[], attempt: HealthRefreshAttempt, cwd:
|
|
|
117
142
|
const outcome = refreshAttemptOutcome(attempt);
|
|
118
143
|
const label = refreshAttemptLabel(attempt);
|
|
119
144
|
lines.push(
|
|
120
|
-
`**Previous ${label.toLowerCase()}**: ${asSentence(outcome)} Started ${
|
|
145
|
+
`**Previous ${label.toLowerCase()}**: ${asSentence(outcome)} Started ${formatRefreshElapsed(Date.now() - attempt.attemptedAt)}; requested ${formatDiagnosticScope(attempt.requestedDiagnosticScope, cwd)}; operation scope: ${refreshOperationScopeText(attempt)}.`,
|
|
121
146
|
);
|
|
122
147
|
}
|
|
123
148
|
|
|
@@ -125,24 +150,35 @@ function renderRetainedAttempt(lines: string[], attempt: HealthRefreshAttempt, c
|
|
|
125
150
|
const label =
|
|
126
151
|
attempt.operationScope === "file-runtime" ? "File LSP maintenance" : "Diagnostic refresh";
|
|
127
152
|
lines.push(
|
|
128
|
-
`**${label}**: not requested for this call. Previous attempt ${asSentence(refreshAttemptOutcome(attempt))} Started ${
|
|
153
|
+
`**${label}**: not requested for this call. Previous attempt ${asSentence(refreshAttemptOutcome(attempt))} Started ${formatRefreshElapsed(Date.now() - attempt.attemptedAt)}; requested ${formatDiagnosticScope(attempt.requestedDiagnosticScope, cwd)}; operation scope: ${refreshOperationScopeText(attempt)}.`,
|
|
129
154
|
);
|
|
130
155
|
}
|
|
131
156
|
|
|
132
157
|
function refreshAttemptOutcome(attempt: HealthRefreshAttempt): string {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
158
|
+
if (attempt.kind === "completed") return completedRefreshText(attempt);
|
|
159
|
+
const processCrashRecovery = attempt.processCrashRecovery
|
|
160
|
+
? formatProcessCrashRecovery(attempt.processCrashRecovery)
|
|
161
|
+
: null;
|
|
162
|
+
const staleRestart = formatStaleDiagnosticRestarts(attempt);
|
|
163
|
+
const evidence = attempt.diagnosticEvidence
|
|
164
|
+
? `; ${formatDiagnosticEvidence(attempt.diagnosticEvidence)}`
|
|
165
|
+
: "";
|
|
166
|
+
return `failed — ${attempt.reason}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${evidence}`;
|
|
136
167
|
}
|
|
137
168
|
|
|
138
169
|
function refreshOperationScopeText(attempt: HealthRefreshAttempt): string {
|
|
139
170
|
return attempt.operationScope === "file-runtime" ? "file runtime" : "workspace runtime";
|
|
140
171
|
}
|
|
141
172
|
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
173
|
+
function formatStaleDiagnosticRestarts(attempt: {
|
|
174
|
+
kind?: HealthRefreshAttempt["kind"];
|
|
175
|
+
operationScope: HealthRefreshAttempt["operationScope"];
|
|
176
|
+
restartedClients?: number;
|
|
177
|
+
}): string | null {
|
|
178
|
+
if (attempt.operationScope !== "workspace-runtime" || attempt.restartedClients === undefined) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return formatStaleDiagnosticRestartsText(attempt.restartedClients);
|
|
146
182
|
}
|
|
147
183
|
|
|
148
184
|
function renderCapabilityWarningsSection(
|
|
@@ -193,49 +229,6 @@ function renderDiagnosticsSection(lines: string[], data: HealthData, cwd: string
|
|
|
193
229
|
lines.push("");
|
|
194
230
|
}
|
|
195
231
|
|
|
196
|
-
/**
|
|
197
|
-
* Render the tsconfig coverage verdict for a single-file health request.
|
|
198
|
-
*
|
|
199
|
-
* Written in plain vocabulary (no basis terms) so a reader without tsconfig
|
|
200
|
-
* background can act on it: the sentence names the deciding config and states
|
|
201
|
-
* whether the file's errors are part of workspace diagnostics. The structured
|
|
202
|
-
* decision basis stays in the diagnostics.scope debug event.
|
|
203
|
-
*/
|
|
204
|
-
function renderFileScopeStatus(
|
|
205
|
-
lines: string[],
|
|
206
|
-
observation: Extract<
|
|
207
|
-
HealthDiagnosticObservation,
|
|
208
|
-
{ kind: "completed" | "partial" | "unavailable" }
|
|
209
|
-
>,
|
|
210
|
-
cwd: string,
|
|
211
|
-
): void {
|
|
212
|
-
if (observation.scope.kind !== "file" || !observation.scopeStatus) return;
|
|
213
|
-
|
|
214
|
-
const decision = observation.scopeStatus;
|
|
215
|
-
switch (decision.status) {
|
|
216
|
-
case "included":
|
|
217
|
-
lines.push(
|
|
218
|
-
`**Tsconfig**: covered by ${formatConfigPath(decision.configPath, cwd)} — part of workspace diagnostics.`,
|
|
219
|
-
);
|
|
220
|
-
return;
|
|
221
|
-
case "excluded":
|
|
222
|
-
lines.push(
|
|
223
|
-
`**Tsconfig**: NOT covered by ${formatConfigPath(decision.configPath, cwd)} — not part of workspace diagnostics.`,
|
|
224
|
-
);
|
|
225
|
-
return;
|
|
226
|
-
case "no-config":
|
|
227
|
-
lines.push("**Tsconfig**: no tsconfig.json or jsconfig.json found — nothing filtered.");
|
|
228
|
-
return;
|
|
229
|
-
case "out-of-tree":
|
|
230
|
-
lines.push("**Tsconfig**: outside the project root — not part of workspace diagnostics.");
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function formatConfigPath(configPath: string | null, cwd: string): string {
|
|
235
|
-
if (!configPath) return "no config";
|
|
236
|
-
return `\`${makeRelative(cwd, configPath)}\``;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
232
|
function formatDiagnosticEvidence(evidence: {
|
|
240
233
|
requested: number;
|
|
241
234
|
confirmed: number;
|
|
@@ -250,7 +243,14 @@ function renderDiagnosticObservation(lines: string[], data: HealthData, cwd: str
|
|
|
250
243
|
const observation = data.diagnostics;
|
|
251
244
|
if (observation.kind === "not-requested") return;
|
|
252
245
|
if (observation.kind === "unavailable") {
|
|
253
|
-
|
|
246
|
+
if (
|
|
247
|
+
observation.scope.kind === "file" &&
|
|
248
|
+
isFileReadinessPending(currentRefreshAttempt(data), data.semanticState)
|
|
249
|
+
) {
|
|
250
|
+
lines.push("Diagnostics pending — LSP may still be warming; retry shortly.");
|
|
251
|
+
} else {
|
|
252
|
+
lines.push(`Diagnostics unavailable — ${asSentence(observation.reason)}`);
|
|
253
|
+
}
|
|
254
254
|
return;
|
|
255
255
|
}
|
|
256
256
|
if (observation.kind === "partial") {
|
|
@@ -269,6 +269,10 @@ function renderDiagnosticObservation(lines: string[], data: HealthData, cwd: str
|
|
|
269
269
|
renderDiagnosticEntries(lines, data, cwd);
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
function currentRefreshAttempt(data: HealthData): HealthRefreshAttempt | null {
|
|
273
|
+
return data.refresh.kind === "completed" || data.refresh.kind === "failed" ? data.refresh : null;
|
|
274
|
+
}
|
|
275
|
+
|
|
272
276
|
function asSentence(text: string): string {
|
|
273
277
|
return /[.!?]$/.test(text) ? text : `${text}.`;
|
|
274
278
|
}
|
|
@@ -371,7 +375,3 @@ function formatDiagnosticScope(scope: HealthDiagnosticScope, cwd: string): strin
|
|
|
371
375
|
function plural(count: number): string {
|
|
372
376
|
return count === 1 ? "" : "s";
|
|
373
377
|
}
|
|
374
|
-
|
|
375
|
-
function makeRelative(cwd: string, file: string): string {
|
|
376
|
-
return file.startsWith(cwd) ? file.slice(cwd.length + 1) : file;
|
|
377
|
-
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ProcessCrashRecoverySummary } from "@mrclrchtr/supi-lsp/api";
|
|
2
|
+
import type { SemanticHealthState } from "../../session/health-types.ts";
|
|
3
|
+
|
|
4
|
+
/** Report whether a file health result is waiting for semantic readiness. */
|
|
5
|
+
export function isFileReadinessPending(
|
|
6
|
+
attempt: { operationScope?: string; fileReadiness?: string } | null | undefined,
|
|
7
|
+
semanticState: SemanticHealthState | null | undefined,
|
|
8
|
+
): boolean {
|
|
9
|
+
if (attempt?.operationScope !== undefined) {
|
|
10
|
+
return (
|
|
11
|
+
attempt.operationScope === "file-runtime" &&
|
|
12
|
+
(attempt.fileReadiness === "pending" ||
|
|
13
|
+
(attempt.fileReadiness === undefined && semanticState?.kind === "pending"))
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
return semanticState?.kind === "pending";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Format the outcome of stale-diagnostic client restarts. */
|
|
20
|
+
export function formatStaleDiagnosticRestarts(restartedClients: number): string {
|
|
21
|
+
return `stale diagnostic restarts: ${restartedClients} client${plural(restartedClients)} restarted`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Format a non-empty process-crash route recovery outcome. */
|
|
25
|
+
export function formatProcessCrashRecovery(
|
|
26
|
+
summary: ProcessCrashRecoverySummary | null | undefined,
|
|
27
|
+
): string | null {
|
|
28
|
+
if (
|
|
29
|
+
!summary ||
|
|
30
|
+
(summary.attemptedRoutes === 0 && summary.recoveredRoutes === 0 && summary.failedRoutes === 0)
|
|
31
|
+
) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return `process-crash recovery: ${summary.recoveredRoutes} route${plural(summary.recoveredRoutes)} recovered (${summary.attemptedRoutes} attempted, ${summary.failedRoutes} failed)`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Format the age of a retained refresh attempt for a status line. */
|
|
38
|
+
export function formatRefreshElapsed(milliseconds: number): string {
|
|
39
|
+
const seconds = Math.max(0, Math.round(milliseconds / 1_000));
|
|
40
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
41
|
+
return `${Math.round(seconds / 60)}m ago`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function plural(count: number): string {
|
|
45
|
+
return count === 1 ? "" : "s";
|
|
46
|
+
}
|