pi-lens 3.6.2 → 3.6.4
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/CHANGELOG.md +10 -2
- package/package.json +4 -4
- package/tsconfig.json +1 -1
- package/clients/__tests__/file-time.test.js +0 -216
- package/clients/__tests__/file-time.test.ts +0 -276
- package/clients/__tests__/format-service.test.js +0 -245
- package/clients/__tests__/format-service.test.ts +0 -339
- package/clients/__tests__/formatters.test.js +0 -271
- package/clients/__tests__/formatters.test.ts +0 -401
- package/clients/agent-behavior-client.js +0 -110
- package/clients/agent-behavior-client.test.js +0 -94
- package/clients/agent-behavior-client.test.ts +0 -116
- package/clients/amain-types.js +0 -164
- package/clients/architect-client.js +0 -291
- package/clients/ast-grep-client.js +0 -253
- package/clients/ast-grep-parser.js +0 -84
- package/clients/ast-grep-rule-manager.js +0 -89
- package/clients/ast-grep-types.js +0 -9
- package/clients/auto-loop.js +0 -131
- package/clients/biome-client.js +0 -420
- package/clients/biome-client.test.js +0 -144
- package/clients/biome-client.test.ts +0 -163
- package/clients/cache/rule-cache.js +0 -72
- package/clients/cache-manager.js +0 -245
- package/clients/cache-manager.test.js +0 -197
- package/clients/cache-manager.test.ts +0 -299
- package/clients/complexity-client.js +0 -675
- package/clients/complexity-client.test.js +0 -234
- package/clients/complexity-client.test.ts +0 -255
- package/clients/config-validator.js +0 -465
- package/clients/dependency-checker.js +0 -325
- package/clients/dependency-checker.test.js +0 -60
- package/clients/dependency-checker.test.ts +0 -71
- package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
- package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
- package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
- package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
- package/clients/dispatch/debug.log +0 -1
- package/clients/dispatch/dispatcher.edge.test.js +0 -82
- package/clients/dispatch/dispatcher.edge.test.ts +0 -100
- package/clients/dispatch/dispatcher.format.test.js +0 -46
- package/clients/dispatch/dispatcher.format.test.ts +0 -58
- package/clients/dispatch/dispatcher.inline.test.js +0 -74
- package/clients/dispatch/dispatcher.inline.test.ts +0 -93
- package/clients/dispatch/dispatcher.js +0 -381
- package/clients/dispatch/dispatcher.test.js +0 -116
- package/clients/dispatch/dispatcher.test.ts +0 -149
- package/clients/dispatch/integration.js +0 -108
- package/clients/dispatch/plan.js +0 -183
- package/clients/dispatch/runners/architect.js +0 -83
- package/clients/dispatch/runners/architect.test.js +0 -138
- package/clients/dispatch/runners/architect.test.ts +0 -162
- package/clients/dispatch/runners/ast-grep-napi.js +0 -405
- package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
- package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
- package/clients/dispatch/runners/ast-grep.js +0 -157
- package/clients/dispatch/runners/biome.js +0 -55
- package/clients/dispatch/runners/config-validation.js +0 -67
- package/clients/dispatch/runners/go-vet.js +0 -48
- package/clients/dispatch/runners/index.js +0 -47
- package/clients/dispatch/runners/lsp.js +0 -102
- package/clients/dispatch/runners/oxlint.js +0 -67
- package/clients/dispatch/runners/oxlint.test.js +0 -230
- package/clients/dispatch/runners/oxlint.test.ts +0 -303
- package/clients/dispatch/runners/pyright.js +0 -100
- package/clients/dispatch/runners/pyright.test.js +0 -98
- package/clients/dispatch/runners/pyright.test.ts +0 -121
- package/clients/dispatch/runners/python-slop.js +0 -97
- package/clients/dispatch/runners/python-slop.test.js +0 -203
- package/clients/dispatch/runners/python-slop.test.ts +0 -298
- package/clients/dispatch/runners/ruff.js +0 -48
- package/clients/dispatch/runners/rust-clippy.js +0 -102
- package/clients/dispatch/runners/scan_codebase.test.js +0 -89
- package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
- package/clients/dispatch/runners/shellcheck.js +0 -147
- package/clients/dispatch/runners/shellcheck.test.js +0 -98
- package/clients/dispatch/runners/shellcheck.test.ts +0 -129
- package/clients/dispatch/runners/similarity.js +0 -230
- package/clients/dispatch/runners/spellcheck.js +0 -106
- package/clients/dispatch/runners/spellcheck.test.js +0 -158
- package/clients/dispatch/runners/spellcheck.test.ts +0 -214
- package/clients/dispatch/runners/tree-sitter.js +0 -246
- package/clients/dispatch/runners/ts-lsp.js +0 -125
- package/clients/dispatch/runners/ts-slop.js +0 -113
- package/clients/dispatch/runners/type-safety.js +0 -142
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
- package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
- package/clients/dispatch/runners/utils.js +0 -51
- package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
- package/clients/dispatch/types.js +0 -16
- package/clients/dispatch/utils/format-utils.js +0 -44
- package/clients/dogfood.test.js +0 -201
- package/clients/dogfood.test.ts +0 -269
- package/clients/file-kinds.js +0 -177
- package/clients/file-kinds.test.js +0 -169
- package/clients/file-kinds.test.ts +0 -210
- package/clients/file-time.js +0 -152
- package/clients/file-utils.js +0 -40
- package/clients/fix-scanners.js +0 -204
- package/clients/format-service.js +0 -184
- package/clients/formatters.js +0 -488
- package/clients/go-client.js +0 -203
- package/clients/go-client.test.js +0 -127
- package/clients/go-client.test.ts +0 -143
- package/clients/installer/index.js +0 -403
- package/clients/interviewer-templates.js +0 -75
- package/clients/interviewer.js +0 -173
- package/clients/jscpd-client.js +0 -196
- package/clients/jscpd-client.test.js +0 -127
- package/clients/jscpd-client.test.ts +0 -145
- package/clients/knip-client.js +0 -239
- package/clients/knip-client.test.js +0 -112
- package/clients/knip-client.test.ts +0 -128
- package/clients/latency-logger.js +0 -40
- package/clients/lsp/__tests__/client.test.js +0 -310
- package/clients/lsp/__tests__/client.test.ts +0 -412
- package/clients/lsp/__tests__/config.test.js +0 -167
- package/clients/lsp/__tests__/config.test.ts +0 -217
- package/clients/lsp/__tests__/error-recovery.test.js +0 -213
- package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
- package/clients/lsp/__tests__/integration.test.js +0 -127
- package/clients/lsp/__tests__/integration.test.ts +0 -160
- package/clients/lsp/__tests__/launch.test.js +0 -313
- package/clients/lsp/__tests__/launch.test.ts +0 -394
- package/clients/lsp/__tests__/server.test.js +0 -259
- package/clients/lsp/__tests__/server.test.ts +0 -332
- package/clients/lsp/__tests__/service.test.js +0 -438
- package/clients/lsp/__tests__/service.test.ts +0 -530
- package/clients/lsp/client.js +0 -350
- package/clients/lsp/config.js +0 -112
- package/clients/lsp/index.js +0 -318
- package/clients/lsp/installer/index.js +0 -391
- package/clients/lsp/interactive-install.js +0 -221
- package/clients/lsp/language.js +0 -170
- package/clients/lsp/launch.js +0 -329
- package/clients/lsp/lsp/launch.js +0 -116
- package/clients/lsp/lsp/server.js +0 -532
- package/clients/lsp/lsp-index.js +0 -10
- package/clients/lsp/path-utils.js +0 -5
- package/clients/lsp/server.js +0 -725
- package/clients/lsp/test-py-spawn/requirements.txt +0 -1
- package/clients/lsp/test-py-spawn/test.py +0 -3
- package/clients/lsp/test-py-svc/requirements.txt +0 -1
- package/clients/lsp/test-py-svc/test.py +0 -3
- package/clients/lsp/test-python-project/requirements.txt +0 -1
- package/clients/lsp/test-python-project/test.py +0 -5
- package/clients/metrics-client.js +0 -107
- package/clients/metrics-client.test.js +0 -128
- package/clients/metrics-client.test.ts +0 -163
- package/clients/metrics-history.js +0 -367
- package/clients/path-utils.js +0 -142
- package/clients/pipeline.js +0 -272
- package/clients/production-readiness.js +0 -522
- package/clients/project-index.js +0 -255
- package/clients/project-metadata.js +0 -531
- package/clients/ruff-client.js +0 -325
- package/clients/ruff-client.test.js +0 -132
- package/clients/ruff-client.test.ts +0 -153
- package/clients/rules-scanner.js +0 -97
- package/clients/runner-tracker.js +0 -152
- package/clients/rust-client.js +0 -205
- package/clients/rust-client.test.js +0 -108
- package/clients/rust-client.test.ts +0 -130
- package/clients/safe-spawn-async.js +0 -163
- package/clients/safe-spawn.js +0 -241
- package/clients/sanitize.js +0 -291
- package/clients/sanitize.test.js +0 -177
- package/clients/sanitize.test.ts +0 -223
- package/clients/scan-architectural-debt.js +0 -167
- package/clients/scan-utils.js +0 -83
- package/clients/secrets-scanner.js +0 -119
- package/clients/secrets-scanner.test.js +0 -100
- package/clients/secrets-scanner.test.ts +0 -113
- package/clients/sg-runner.js +0 -292
- package/clients/state-matrix.js +0 -160
- package/clients/subprocess-client.js +0 -65
- package/clients/symbol-types.js +0 -5
- package/clients/test-runner-client.js +0 -523
- package/clients/test-runner-client.test.js +0 -192
- package/clients/test-runner-client.test.ts +0 -253
- package/clients/test-utils.js +0 -27
- package/clients/test-utils.ts +0 -36
- package/clients/todo-scanner.js +0 -200
- package/clients/todo-scanner.test.js +0 -301
- package/clients/todo-scanner.test.ts +0 -352
- package/clients/tool-availability.js +0 -207
- package/clients/tree-sitter-client.js +0 -601
- package/clients/tree-sitter-query-loader.js +0 -355
- package/clients/tree-sitter-symbol-extractor.js +0 -289
- package/clients/ts-service.js +0 -129
- package/clients/type-coverage-client.js +0 -127
- package/clients/type-coverage-client.test.js +0 -105
- package/clients/type-coverage-client.test.ts +0 -125
- package/clients/type-safety-client.js +0 -138
- package/clients/types.js +0 -11
- package/clients/typescript-client.codefix.test.js +0 -157
- package/clients/typescript-client.codefix.test.ts +0 -186
- package/clients/typescript-client.js +0 -509
- package/clients/typescript-client.test.js +0 -105
- package/clients/typescript-client.test.ts +0 -126
- package/commands/booboo.js +0 -1007
- package/commands/fix-from-booboo.js +0 -398
- package/commands/fix-simplified.js +0 -618
- package/commands/rate.js +0 -281
- package/commands/rate.test.js +0 -119
- package/commands/rate.test.ts +0 -131
- package/commands/refactor.js +0 -130
|
@@ -1,381 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Declarative Tool Dispatcher for pi-lens
|
|
3
|
-
*
|
|
4
|
-
* Redesigned to handle the full complexity of pi-lens's tool_result handler:
|
|
5
|
-
* - Multiple tools with different semantics (blocking, warning, silent)
|
|
6
|
-
* - Delta mode (baseline tracking)
|
|
7
|
-
* - Autofix handling
|
|
8
|
-
* - Output aggregation and formatting
|
|
9
|
-
*
|
|
10
|
-
* Key abstractions:
|
|
11
|
-
* - RunnerDefinition: A tool that can be run
|
|
12
|
-
* - Diagnostic: Structured issue representation
|
|
13
|
-
* - OutputSemantic: How to display (blocking, warning, silent, etc.)
|
|
14
|
-
* - BaselineStore: Track pre-existing issues for delta mode
|
|
15
|
-
*/
|
|
16
|
-
import { detectFileKind } from "../file-kinds.js";
|
|
17
|
-
import { isTestFile } from "../file-utils.js";
|
|
18
|
-
import { logLatency } from "../latency-logger.js";
|
|
19
|
-
import { normalizeMapKey } from "../path-utils.js";
|
|
20
|
-
import { safeSpawn } from "../safe-spawn.js";
|
|
21
|
-
import { formatDiagnostics } from "./utils/format-utils.js";
|
|
22
|
-
// --- In-Memory Baseline Store ---
|
|
23
|
-
// DEBUG TEST EDIT - Testing dispatch logging
|
|
24
|
-
export function createBaselineStore() {
|
|
25
|
-
const baselines = new Map();
|
|
26
|
-
return {
|
|
27
|
-
get(filePath) {
|
|
28
|
-
return baselines.get(normalizeMapKey(filePath));
|
|
29
|
-
},
|
|
30
|
-
set(filePath, diagnostics) {
|
|
31
|
-
baselines.set(normalizeMapKey(filePath), diagnostics);
|
|
32
|
-
},
|
|
33
|
-
clear() {
|
|
34
|
-
baselines.clear();
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
// --- Runner Registry ---
|
|
39
|
-
const globalRegistry = new Map();
|
|
40
|
-
export function registerRunner(runner) {
|
|
41
|
-
if (globalRegistry.has(runner.id))
|
|
42
|
-
return; // Already registered, skip silently
|
|
43
|
-
globalRegistry.set(runner.id, runner);
|
|
44
|
-
}
|
|
45
|
-
export function getRunner(id) {
|
|
46
|
-
return globalRegistry.get(id);
|
|
47
|
-
}
|
|
48
|
-
export function getRunnersForKind(kind, filePath) {
|
|
49
|
-
if (!kind)
|
|
50
|
-
return [];
|
|
51
|
-
const runners = [];
|
|
52
|
-
const isTest = filePath ? isTestFile(filePath) : false;
|
|
53
|
-
for (const runner of globalRegistry.values()) {
|
|
54
|
-
// Skip runners that shouldn't run on test files
|
|
55
|
-
if (isTest && runner.skipTestFiles)
|
|
56
|
-
continue;
|
|
57
|
-
if (runner.appliesTo.includes(kind) || runner.appliesTo.length === 0) {
|
|
58
|
-
runners.push(runner);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return runners.sort((a, b) => a.priority - b.priority);
|
|
62
|
-
}
|
|
63
|
-
export function listRunners() {
|
|
64
|
-
return Array.from(globalRegistry.values());
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Clear all registered runners. Used primarily for testing.
|
|
68
|
-
*/
|
|
69
|
-
export function clearRunnerRegistry() {
|
|
70
|
-
globalRegistry.clear();
|
|
71
|
-
}
|
|
72
|
-
// --- Tool Availability Cache ---
|
|
73
|
-
const toolCache = new Map();
|
|
74
|
-
function checkToolAvailability(command) {
|
|
75
|
-
if (toolCache.has(command)) {
|
|
76
|
-
return toolCache.get(command);
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
const result = safeSpawn(command, ["--version"], {
|
|
80
|
-
timeout: 5000,
|
|
81
|
-
});
|
|
82
|
-
const available = result.status === 0;
|
|
83
|
-
toolCache.set(command, available);
|
|
84
|
-
return available;
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
toolCache.set(command, false);
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
// --- Dispatch Context Factory ---
|
|
92
|
-
export function createDispatchContext(filePath, cwd, pi, baselines, blockingOnly) {
|
|
93
|
-
// Normalize path for consistent Map key usage on Windows
|
|
94
|
-
const normalizedFilePath = normalizeMapKey(filePath);
|
|
95
|
-
const kind = detectFileKind(normalizedFilePath);
|
|
96
|
-
return {
|
|
97
|
-
filePath: normalizedFilePath,
|
|
98
|
-
cwd,
|
|
99
|
-
kind,
|
|
100
|
-
pi,
|
|
101
|
-
autofix: !!(pi.getFlag("autofix-biome") || pi.getFlag("autofix-ruff")),
|
|
102
|
-
deltaMode: !pi.getFlag("no-delta"),
|
|
103
|
-
baselines: baselines ?? createBaselineStore(),
|
|
104
|
-
blockingOnly,
|
|
105
|
-
async hasTool(command) {
|
|
106
|
-
return checkToolAvailability(command);
|
|
107
|
-
},
|
|
108
|
-
log(message) {
|
|
109
|
-
console.error(`[dispatch] ${message}`);
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
// --- Delta Mode Logic ---
|
|
114
|
-
/**
|
|
115
|
-
* Filter diagnostics to only show NEW issues (delta mode)
|
|
116
|
-
*/
|
|
117
|
-
function filterDelta(after, before, keyFn) {
|
|
118
|
-
const beforeSet = new Set((before ?? []).map(keyFn));
|
|
119
|
-
const afterSet = new Set(after.map(keyFn));
|
|
120
|
-
const fixed = (before ?? []).filter((d) => !afterSet.has(keyFn(d)));
|
|
121
|
-
const newItems = after.filter((d) => !beforeSet.has(keyFn(d)));
|
|
122
|
-
return { new: newItems, fixed };
|
|
123
|
-
}
|
|
124
|
-
const latencyReports = [];
|
|
125
|
-
export function getLatencyReports() {
|
|
126
|
-
return [...latencyReports];
|
|
127
|
-
}
|
|
128
|
-
export function clearLatencyReports() {
|
|
129
|
-
latencyReports.length = 0;
|
|
130
|
-
}
|
|
131
|
-
export function formatLatencyReport(report) {
|
|
132
|
-
const lines = [];
|
|
133
|
-
lines.push(`\n═══════════════════════════════════════════════════════════════`);
|
|
134
|
-
lines.push(`📊 DISPATCH LATENCY REPORT: ${report.filePath.split("/").pop()}`);
|
|
135
|
-
lines.push(` Kind: ${report.fileKind || "unknown"} | Total: ${report.totalDurationMs}ms`);
|
|
136
|
-
lines.push(`───────────────────────────────────────────────────────────────`);
|
|
137
|
-
lines.push(`Runner Duration Status Issues Semantic`);
|
|
138
|
-
lines.push(`───────────────────────────────────────────────────────────────`);
|
|
139
|
-
for (const r of report.runners) {
|
|
140
|
-
const name = r.runnerId.padEnd(30);
|
|
141
|
-
const dur = `${r.durationMs}ms`.padStart(8);
|
|
142
|
-
const status = r.status.padStart(9);
|
|
143
|
-
const issues = String(r.diagnosticCount).padStart(6);
|
|
144
|
-
const sem = r.semantic.padStart(8);
|
|
145
|
-
const slowMarker = r.durationMs > 500 ? " 🔥" : r.durationMs > 100 ? " ⚡" : "";
|
|
146
|
-
lines.push(`${name}${dur}${status}${issues}${sem}${slowMarker}`);
|
|
147
|
-
}
|
|
148
|
-
lines.push(`───────────────────────────────────────────────────────────────`);
|
|
149
|
-
lines.push(`Total: ${report.runners.length} runners | Stopped early: ${report.stoppedEarly}`);
|
|
150
|
-
lines.push(`Diagnostics: ${report.totalDiagnostics} (${report.blockers} blockers, ${report.warnings} warnings)`);
|
|
151
|
-
// Show top 3 slowest
|
|
152
|
-
const sorted = [...report.runners].sort((a, b) => b.durationMs - a.durationMs);
|
|
153
|
-
if (sorted.length > 0 && sorted[0].durationMs > 100) {
|
|
154
|
-
lines.push(`\n🐌 Slowest runners:`);
|
|
155
|
-
for (const r of sorted.slice(0, 3)) {
|
|
156
|
-
if (r.durationMs > 50) {
|
|
157
|
-
lines.push(` ${r.runnerId}: ${r.durationMs}ms (${r.status})`);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
lines.push(`═══════════════════════════════════════════════════════════════`);
|
|
162
|
-
return lines.join("\n");
|
|
163
|
-
}
|
|
164
|
-
// --- Main Dispatch Function ---
|
|
165
|
-
export async function dispatchForFile(ctx, groups) {
|
|
166
|
-
const _overallStart = Date.now();
|
|
167
|
-
const allDiagnostics = [];
|
|
168
|
-
const _fixed = [];
|
|
169
|
-
let stopped = false;
|
|
170
|
-
const runnerLatencies = [];
|
|
171
|
-
// Debug logging goes to latency log only (not console - avoid noise)
|
|
172
|
-
const allRunnerIds = groups.flatMap((g) => g.runnerIds);
|
|
173
|
-
logLatency({
|
|
174
|
-
type: "phase",
|
|
175
|
-
filePath: ctx.filePath,
|
|
176
|
-
phase: "dispatch_start",
|
|
177
|
-
durationMs: 0,
|
|
178
|
-
metadata: {
|
|
179
|
-
groupCount: groups.length,
|
|
180
|
-
kind: ctx.kind,
|
|
181
|
-
runners: allRunnerIds.join(","),
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
for (const group of groups) {
|
|
185
|
-
if (stopped && ctx.pi.getFlag("stop-on-error")) {
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
// Filter runners by kind if specified
|
|
189
|
-
const runnerIds = group.filterKinds
|
|
190
|
-
? group.runnerIds.filter((id) => {
|
|
191
|
-
const runner = getRunner(id);
|
|
192
|
-
return runner && ctx.kind && group.filterKinds?.includes(ctx.kind);
|
|
193
|
-
})
|
|
194
|
-
: group.runnerIds;
|
|
195
|
-
const semantic = group.semantic ?? "warning";
|
|
196
|
-
for (const runnerId of runnerIds) {
|
|
197
|
-
const runnerStart = Date.now();
|
|
198
|
-
const runner = getRunner(runnerId);
|
|
199
|
-
if (!runner) {
|
|
200
|
-
runnerLatencies.push({
|
|
201
|
-
runnerId,
|
|
202
|
-
startTime: runnerStart,
|
|
203
|
-
endTime: Date.now(),
|
|
204
|
-
durationMs: 0,
|
|
205
|
-
status: "skipped",
|
|
206
|
-
diagnosticCount: 0,
|
|
207
|
-
semantic: "unknown",
|
|
208
|
-
});
|
|
209
|
-
logLatency({
|
|
210
|
-
type: "runner",
|
|
211
|
-
filePath: ctx.filePath,
|
|
212
|
-
runnerId,
|
|
213
|
-
durationMs: 0,
|
|
214
|
-
status: "not_registered",
|
|
215
|
-
diagnosticCount: 0,
|
|
216
|
-
semantic: "unknown",
|
|
217
|
-
});
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
// Check preconditions
|
|
221
|
-
if (runner.when && !(await runner.when(ctx))) {
|
|
222
|
-
runnerLatencies.push({
|
|
223
|
-
runnerId,
|
|
224
|
-
startTime: runnerStart,
|
|
225
|
-
endTime: Date.now(),
|
|
226
|
-
durationMs: Date.now() - runnerStart,
|
|
227
|
-
status: "when_skipped",
|
|
228
|
-
diagnosticCount: 0,
|
|
229
|
-
semantic: runner.id,
|
|
230
|
-
});
|
|
231
|
-
logLatency({
|
|
232
|
-
type: "runner",
|
|
233
|
-
filePath: ctx.filePath,
|
|
234
|
-
runnerId,
|
|
235
|
-
durationMs: 0,
|
|
236
|
-
status: "when_skipped",
|
|
237
|
-
diagnosticCount: 0,
|
|
238
|
-
semantic: "when_condition",
|
|
239
|
-
});
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
const result = await runRunner(ctx, runner, semantic);
|
|
243
|
-
const runnerEnd = Date.now();
|
|
244
|
-
const duration = runnerEnd - runnerStart;
|
|
245
|
-
// Track latency for this runner
|
|
246
|
-
runnerLatencies.push({
|
|
247
|
-
runnerId,
|
|
248
|
-
startTime: runnerStart,
|
|
249
|
-
endTime: runnerEnd,
|
|
250
|
-
durationMs: duration,
|
|
251
|
-
status: result.status,
|
|
252
|
-
diagnosticCount: result.diagnostics.length,
|
|
253
|
-
semantic: result.semantic ?? semantic,
|
|
254
|
-
});
|
|
255
|
-
// IMMEDIATE LOG: Each runner result (for debugging)
|
|
256
|
-
logLatency({
|
|
257
|
-
type: "runner",
|
|
258
|
-
filePath: ctx.filePath,
|
|
259
|
-
runnerId,
|
|
260
|
-
durationMs: duration,
|
|
261
|
-
status: result.status,
|
|
262
|
-
diagnosticCount: result.diagnostics.length,
|
|
263
|
-
semantic: result.semantic ?? semantic,
|
|
264
|
-
});
|
|
265
|
-
// Apply delta mode filtering
|
|
266
|
-
let diagnostics = result.diagnostics;
|
|
267
|
-
if (ctx.deltaMode && result.semantic !== "silent") {
|
|
268
|
-
const before = ctx.baselines.get(ctx.filePath);
|
|
269
|
-
if (before) {
|
|
270
|
-
const filtered = filterDelta(diagnostics, before, (d) => d.id);
|
|
271
|
-
diagnostics = filtered.new;
|
|
272
|
-
// TODO: Track fixed diagnostics
|
|
273
|
-
}
|
|
274
|
-
// Update baseline
|
|
275
|
-
ctx.baselines.set(ctx.filePath, [...allDiagnostics, ...diagnostics]);
|
|
276
|
-
}
|
|
277
|
-
allDiagnostics.push(...diagnostics);
|
|
278
|
-
// Check for blockers - use result semantic (not group default) and check individual diagnostics
|
|
279
|
-
const resultSemantic = result.semantic ?? semantic;
|
|
280
|
-
if (resultSemantic === "blocking" && diagnostics.length > 0) {
|
|
281
|
-
stopped = true;
|
|
282
|
-
}
|
|
283
|
-
// Also check if any individual diagnostic is blocking
|
|
284
|
-
if (diagnostics.some((d) => d.semantic === "blocking")) {
|
|
285
|
-
stopped = true;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
// Categorize results
|
|
290
|
-
const blockers = allDiagnostics.filter((d) => d.semantic === "blocking");
|
|
291
|
-
const warnings = allDiagnostics.filter((d) => d.semantic === "warning" || d.semantic === "none");
|
|
292
|
-
const fixedItems = allDiagnostics.filter((d) => d.semantic === "fixed");
|
|
293
|
-
// Format output — only blocking issues shown inline
|
|
294
|
-
// Warnings tracked but not shown (noise) — surfaced via /lens-booboo
|
|
295
|
-
let output = formatDiagnostics(blockers, "blocking");
|
|
296
|
-
output += formatDiagnostics(fixedItems, "fixed");
|
|
297
|
-
// Generate and store latency report
|
|
298
|
-
const overallEnd = Date.now();
|
|
299
|
-
const latencyReport = {
|
|
300
|
-
filePath: ctx.filePath,
|
|
301
|
-
fileKind: ctx.kind,
|
|
302
|
-
overallStartMs: _overallStart,
|
|
303
|
-
overallEndMs: overallEnd,
|
|
304
|
-
totalDurationMs: overallEnd - _overallStart,
|
|
305
|
-
runners: runnerLatencies,
|
|
306
|
-
stoppedEarly: stopped,
|
|
307
|
-
totalDiagnostics: allDiagnostics.length,
|
|
308
|
-
blockers: blockers.length,
|
|
309
|
-
warnings: warnings.length,
|
|
310
|
-
};
|
|
311
|
-
// Store for later analysis
|
|
312
|
-
latencyReports.push(latencyReport);
|
|
313
|
-
// Keep only last 100 reports to prevent memory bloat
|
|
314
|
-
if (latencyReports.length > 100) {
|
|
315
|
-
latencyReports.shift();
|
|
316
|
-
}
|
|
317
|
-
// Runner latencies already logged immediately after execution (line ~329)
|
|
318
|
-
// The runnerLatencies array is stored in latencyReport for aggregate analysis
|
|
319
|
-
// No need to log again here - would create duplicates in the log
|
|
320
|
-
// Log summary to latency log only (not console - avoid noise)
|
|
321
|
-
logLatency({
|
|
322
|
-
type: "tool_result",
|
|
323
|
-
filePath: ctx.filePath,
|
|
324
|
-
durationMs: latencyReport.totalDurationMs,
|
|
325
|
-
result: "dispatch_complete",
|
|
326
|
-
metadata: {
|
|
327
|
-
runners: runnerLatencies.map((r) => ({
|
|
328
|
-
id: r.runnerId,
|
|
329
|
-
duration: r.durationMs,
|
|
330
|
-
status: r.status,
|
|
331
|
-
})),
|
|
332
|
-
totalDiagnostics: allDiagnostics.length,
|
|
333
|
-
blockers: blockers.length,
|
|
334
|
-
},
|
|
335
|
-
});
|
|
336
|
-
return {
|
|
337
|
-
diagnostics: allDiagnostics,
|
|
338
|
-
blockers,
|
|
339
|
-
warnings,
|
|
340
|
-
fixed: fixedItems,
|
|
341
|
-
output,
|
|
342
|
-
hasBlockers: blockers.length > 0,
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
// --- Run Single Runner ---
|
|
346
|
-
async function runRunner(ctx, runner, defaultSemantic) {
|
|
347
|
-
try {
|
|
348
|
-
const result = await runner.run(ctx);
|
|
349
|
-
return {
|
|
350
|
-
...result,
|
|
351
|
-
semantic: result.semantic ?? defaultSemantic,
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
catch (error) {
|
|
355
|
-
ctx.log(`Runner ${runner.id} failed: ${error}`);
|
|
356
|
-
return {
|
|
357
|
-
status: "failed",
|
|
358
|
-
diagnostics: [],
|
|
359
|
-
semantic: defaultSemantic,
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
// --- Simple Integration Helper ---
|
|
364
|
-
export async function dispatchLint(filePath, cwd, pi, baselines) {
|
|
365
|
-
// By default, only run BLOCKING rules for fast feedback on file write
|
|
366
|
-
const ctx = createDispatchContext(filePath, cwd, pi, baselines, true);
|
|
367
|
-
// Get runners for this file kind
|
|
368
|
-
const runners = getRunnersForKind(ctx.kind);
|
|
369
|
-
if (runners.length === 0) {
|
|
370
|
-
return "";
|
|
371
|
-
}
|
|
372
|
-
// Create groups from registered runners (all in fallback mode)
|
|
373
|
-
const groups = [
|
|
374
|
-
{
|
|
375
|
-
mode: "fallback",
|
|
376
|
-
runnerIds: runners.map((r) => r.id),
|
|
377
|
-
},
|
|
378
|
-
];
|
|
379
|
-
const result = await dispatchForFile(ctx, groups);
|
|
380
|
-
return result.output;
|
|
381
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for declarative dispatch system
|
|
3
|
-
*/
|
|
4
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
5
|
-
import { createDispatchContext, getRunner, getRunnersForKind, listRunners, registerRunner, } from "./dispatcher.js";
|
|
6
|
-
// --- Test Runners ---
|
|
7
|
-
const testRunner1 = {
|
|
8
|
-
id: "test-runner-1",
|
|
9
|
-
appliesTo: ["jsts", "python"],
|
|
10
|
-
priority: 10,
|
|
11
|
-
enabledByDefault: true,
|
|
12
|
-
async run() {
|
|
13
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
const testRunner2 = {
|
|
17
|
-
id: "test-runner-2",
|
|
18
|
-
appliesTo: ["python"],
|
|
19
|
-
priority: 20,
|
|
20
|
-
enabledByDefault: true,
|
|
21
|
-
async run() {
|
|
22
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
const testRunnerWithCondition = {
|
|
26
|
-
id: "test-runner-conditional",
|
|
27
|
-
appliesTo: ["jsts"],
|
|
28
|
-
priority: 5,
|
|
29
|
-
enabledByDefault: false,
|
|
30
|
-
when: async (ctx) => ctx.autofix,
|
|
31
|
-
async run() {
|
|
32
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
// --- Tests ---
|
|
36
|
-
describe("Runner Registry", () => {
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
// Note: In a real test suite, we'd reset the registry between tests
|
|
39
|
-
registerRunner(testRunner1);
|
|
40
|
-
registerRunner(testRunner2);
|
|
41
|
-
registerRunner(testRunnerWithCondition);
|
|
42
|
-
});
|
|
43
|
-
it("should register a runner", () => {
|
|
44
|
-
const runner = getRunner("test-runner-1");
|
|
45
|
-
expect(runner).toBeDefined();
|
|
46
|
-
expect(runner?.id).toBe("test-runner-1");
|
|
47
|
-
});
|
|
48
|
-
it("should return undefined for unknown runner", () => {
|
|
49
|
-
const runner = getRunner("unknown-runner");
|
|
50
|
-
expect(runner).toBeUndefined();
|
|
51
|
-
});
|
|
52
|
-
it("should get runners for a specific kind", () => {
|
|
53
|
-
const jstsRunners = getRunnersForKind("jsts");
|
|
54
|
-
expect(jstsRunners.length).toBeGreaterThan(0);
|
|
55
|
-
expect(jstsRunners.some((r) => r.id === "test-runner-1")).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
it("should return runners sorted by priority", () => {
|
|
58
|
-
const jstsRunners = getRunnersForKind("jsts");
|
|
59
|
-
const priorities = jstsRunners.map((r) => r.priority ?? 100);
|
|
60
|
-
for (let i = 1; i < priorities.length; i++) {
|
|
61
|
-
expect(priorities[i - 1]).toBeLessThanOrEqual(priorities[i]);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
it("should list all registered runners", () => {
|
|
65
|
-
const all = listRunners();
|
|
66
|
-
expect(all.length).toBeGreaterThanOrEqual(3);
|
|
67
|
-
});
|
|
68
|
-
it("should reject duplicate registrations", () => {
|
|
69
|
-
// This should log an error but not throw
|
|
70
|
-
expect(() => registerRunner(testRunner1)).not.toThrow();
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
describe("Dispatch Context", () => {
|
|
74
|
-
it("should create a dispatch context", () => {
|
|
75
|
-
const mockPi = { getFlag: (flag) => flag === "autofix" };
|
|
76
|
-
const ctx = createDispatchContext("test.ts", "/project", mockPi);
|
|
77
|
-
// Path is normalized to absolute path (Windows compatibility)
|
|
78
|
-
expect(ctx.filePath).toContain("test.ts");
|
|
79
|
-
expect(ctx.cwd).toBe("/project");
|
|
80
|
-
expect(ctx.autofix).toBe(false);
|
|
81
|
-
expect(ctx.deltaMode).toBe(true);
|
|
82
|
-
});
|
|
83
|
-
it("should detect file kind", () => {
|
|
84
|
-
const mockPi = { getFlag: () => false };
|
|
85
|
-
const ctxTs = createDispatchContext("test.ts", "/project", mockPi);
|
|
86
|
-
expect(ctxTs.kind).toBe("jsts");
|
|
87
|
-
const ctxPy = createDispatchContext("test.py", "/project", mockPi);
|
|
88
|
-
expect(ctxPy.kind).toBe("python");
|
|
89
|
-
const ctxGo = createDispatchContext("test.go", "/project", mockPi);
|
|
90
|
-
expect(ctxGo.kind).toBe("go");
|
|
91
|
-
});
|
|
92
|
-
it("should respect autofix flag", () => {
|
|
93
|
-
const mockPiNoFix = { getFlag: (_f) => false };
|
|
94
|
-
const ctx1 = createDispatchContext("test.ts", "/project", mockPiNoFix);
|
|
95
|
-
expect(ctx1.autofix).toBe(false);
|
|
96
|
-
const mockPiWithFix = { getFlag: (f) => f === "autofix-biome" };
|
|
97
|
-
const ctx2 = createDispatchContext("test.ts", "/project", mockPiWithFix);
|
|
98
|
-
expect(ctx2.autofix).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
describe("Conditional Runners", () => {
|
|
102
|
-
beforeEach(() => {
|
|
103
|
-
registerRunner(testRunnerWithCondition);
|
|
104
|
-
});
|
|
105
|
-
it("should respect when condition", async () => {
|
|
106
|
-
const runner = getRunner("test-runner-conditional");
|
|
107
|
-
expect(runner).toBeDefined();
|
|
108
|
-
const mockPiNoFix = { getFlag: () => false };
|
|
109
|
-
const ctxNoFix = createDispatchContext("test.ts", "/project", mockPiNoFix);
|
|
110
|
-
// When autofix is false, the condition should return false
|
|
111
|
-
if (runner?.when) {
|
|
112
|
-
const shouldRun = await runner.when(ctxNoFix);
|
|
113
|
-
expect(shouldRun).toBe(false);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
});
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for declarative dispatch system
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
6
|
-
import {
|
|
7
|
-
createDispatchContext,
|
|
8
|
-
getRunner,
|
|
9
|
-
getRunnersForKind,
|
|
10
|
-
listRunners,
|
|
11
|
-
registerRunner,
|
|
12
|
-
} from "./dispatcher.js";
|
|
13
|
-
import type { RunnerDefinition, RunnerResult } from "./types.js";
|
|
14
|
-
|
|
15
|
-
// --- Test Runners ---
|
|
16
|
-
|
|
17
|
-
const testRunner1: RunnerDefinition = {
|
|
18
|
-
id: "test-runner-1",
|
|
19
|
-
appliesTo: ["jsts", "python"],
|
|
20
|
-
priority: 10,
|
|
21
|
-
enabledByDefault: true,
|
|
22
|
-
async run(): Promise<RunnerResult> {
|
|
23
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const testRunner2: RunnerDefinition = {
|
|
28
|
-
id: "test-runner-2",
|
|
29
|
-
appliesTo: ["python"],
|
|
30
|
-
priority: 20,
|
|
31
|
-
enabledByDefault: true,
|
|
32
|
-
async run(): Promise<RunnerResult> {
|
|
33
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const testRunnerWithCondition: RunnerDefinition = {
|
|
38
|
-
id: "test-runner-conditional",
|
|
39
|
-
appliesTo: ["jsts"],
|
|
40
|
-
priority: 5,
|
|
41
|
-
enabledByDefault: false,
|
|
42
|
-
when: async (ctx) => ctx.autofix,
|
|
43
|
-
async run(): Promise<RunnerResult> {
|
|
44
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// --- Tests ---
|
|
49
|
-
|
|
50
|
-
describe("Runner Registry", () => {
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
// Note: In a real test suite, we'd reset the registry between tests
|
|
53
|
-
registerRunner(testRunner1);
|
|
54
|
-
registerRunner(testRunner2);
|
|
55
|
-
registerRunner(testRunnerWithCondition);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("should register a runner", () => {
|
|
59
|
-
const runner = getRunner("test-runner-1");
|
|
60
|
-
expect(runner).toBeDefined();
|
|
61
|
-
expect(runner?.id).toBe("test-runner-1");
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("should return undefined for unknown runner", () => {
|
|
65
|
-
const runner = getRunner("unknown-runner");
|
|
66
|
-
expect(runner).toBeUndefined();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("should get runners for a specific kind", () => {
|
|
70
|
-
const jstsRunners = getRunnersForKind("jsts");
|
|
71
|
-
expect(jstsRunners.length).toBeGreaterThan(0);
|
|
72
|
-
expect(jstsRunners.some((r) => r.id === "test-runner-1")).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("should return runners sorted by priority", () => {
|
|
76
|
-
const jstsRunners = getRunnersForKind("jsts");
|
|
77
|
-
const priorities = jstsRunners.map((r) => r.priority ?? 100);
|
|
78
|
-
for (let i = 1; i < priorities.length; i++) {
|
|
79
|
-
expect(priorities[i - 1]).toBeLessThanOrEqual(priorities[i]);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("should list all registered runners", () => {
|
|
84
|
-
const all = listRunners();
|
|
85
|
-
expect(all.length).toBeGreaterThanOrEqual(3);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("should reject duplicate registrations", () => {
|
|
89
|
-
// This should log an error but not throw
|
|
90
|
-
expect(() => registerRunner(testRunner1)).not.toThrow();
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe("Dispatch Context", () => {
|
|
95
|
-
it("should create a dispatch context", () => {
|
|
96
|
-
const mockPi = { getFlag: (flag: string) => flag === "autofix" };
|
|
97
|
-
|
|
98
|
-
const ctx = createDispatchContext("test.ts", "/project", mockPi);
|
|
99
|
-
|
|
100
|
-
// Path is normalized to absolute path (Windows compatibility)
|
|
101
|
-
expect(ctx.filePath).toContain("test.ts");
|
|
102
|
-
expect(ctx.cwd).toBe("/project");
|
|
103
|
-
expect(ctx.autofix).toBe(false);
|
|
104
|
-
expect(ctx.deltaMode).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("should detect file kind", () => {
|
|
108
|
-
const mockPi = { getFlag: () => false };
|
|
109
|
-
|
|
110
|
-
const ctxTs = createDispatchContext("test.ts", "/project", mockPi);
|
|
111
|
-
expect(ctxTs.kind).toBe("jsts");
|
|
112
|
-
|
|
113
|
-
const ctxPy = createDispatchContext("test.py", "/project", mockPi);
|
|
114
|
-
expect(ctxPy.kind).toBe("python");
|
|
115
|
-
|
|
116
|
-
const ctxGo = createDispatchContext("test.go", "/project", mockPi);
|
|
117
|
-
expect(ctxGo.kind).toBe("go");
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("should respect autofix flag", () => {
|
|
121
|
-
const mockPiNoFix = { getFlag: (_f: string) => false };
|
|
122
|
-
const ctx1 = createDispatchContext("test.ts", "/project", mockPiNoFix);
|
|
123
|
-
expect(ctx1.autofix).toBe(false);
|
|
124
|
-
|
|
125
|
-
const mockPiWithFix = { getFlag: (f: string) => f === "autofix-biome" };
|
|
126
|
-
const ctx2 = createDispatchContext("test.ts", "/project", mockPiWithFix);
|
|
127
|
-
expect(ctx2.autofix).toBe(true);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe("Conditional Runners", () => {
|
|
132
|
-
beforeEach(() => {
|
|
133
|
-
registerRunner(testRunnerWithCondition);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("should respect when condition", async () => {
|
|
137
|
-
const runner = getRunner("test-runner-conditional");
|
|
138
|
-
expect(runner).toBeDefined();
|
|
139
|
-
|
|
140
|
-
const mockPiNoFix = { getFlag: () => false };
|
|
141
|
-
const ctxNoFix = createDispatchContext("test.ts", "/project", mockPiNoFix);
|
|
142
|
-
|
|
143
|
-
// When autofix is false, the condition should return false
|
|
144
|
-
if (runner?.when) {
|
|
145
|
-
const shouldRun = await runner.when(ctxNoFix);
|
|
146
|
-
expect(shouldRun).toBe(false);
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
});
|