pi-lens 2.0.7 → 2.0.8
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/clients/ast-grep-client.test.ts +146 -116
- package/clients/ast-grep-client.ts +645 -551
- package/clients/biome-client.test.ts +154 -137
- package/clients/biome-client.ts +397 -337
- package/clients/complexity-client.test.ts +188 -200
- package/clients/complexity-client.ts +815 -667
- package/clients/dependency-checker.test.ts +55 -55
- package/clients/dependency-checker.ts +358 -333
- package/clients/go-client.test.ts +121 -111
- package/clients/go-client.ts +218 -216
- package/clients/jscpd-client.test.ts +132 -132
- package/clients/jscpd-client.ts +155 -118
- package/clients/knip-client.test.ts +123 -133
- package/clients/knip-client.ts +231 -218
- package/clients/metrics-client.test.ts +171 -167
- package/clients/metrics-client.ts +283 -252
- package/clients/ruff-client.test.ts +128 -117
- package/clients/ruff-client.ts +300 -269
- package/clients/rust-client.test.ts +104 -85
- package/clients/rust-client.ts +241 -234
- package/clients/subprocess-client.ts +1 -1
- package/clients/test-runner-client.test.ts +248 -215
- package/clients/test-runner-client.ts +728 -608
- package/clients/test-utils.ts +10 -3
- package/clients/todo-scanner.test.ts +288 -202
- package/clients/todo-scanner.ts +225 -187
- package/clients/type-coverage-client.test.ts +119 -119
- package/clients/type-coverage-client.ts +142 -115
- package/clients/types.ts +28 -28
- package/clients/typescript-client.test.ts +99 -93
- package/clients/typescript-client.ts +527 -502
- package/index.ts +662 -212
- package/package.json +1 -1
- package/tsconfig.json +12 -12
package/clients/rust-client.ts
CHANGED
|
@@ -8,259 +8,266 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { spawnSync } from "node:child_process";
|
|
11
|
-
import * as path from "node:path";
|
|
12
11
|
import * as fs from "node:fs";
|
|
12
|
+
import * as path from "node:path";
|
|
13
13
|
|
|
14
14
|
// --- Types ---
|
|
15
15
|
|
|
16
16
|
export interface RustDiagnostic {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
line: number;
|
|
18
|
+
column: number;
|
|
19
|
+
endLine: number;
|
|
20
|
+
endColumn: number;
|
|
21
|
+
severity: "error" | "warning" | "note" | "help";
|
|
22
|
+
message: string;
|
|
23
|
+
code?: string;
|
|
24
|
+
file: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
interface CargoMessage {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
reason: "compiler-artifact" | "compiler-message" | "build-script-executed";
|
|
29
|
+
message?: {
|
|
30
|
+
level: string;
|
|
31
|
+
code?: string;
|
|
32
|
+
message: string;
|
|
33
|
+
spans?: Array<{
|
|
34
|
+
line_start: number;
|
|
35
|
+
line_end: number;
|
|
36
|
+
column_start: number;
|
|
37
|
+
column_end: number;
|
|
38
|
+
file_name: string;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// --- Common install paths ---
|
|
44
44
|
|
|
45
45
|
const CARGO_WINDOWS_PATHS = [
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
path.join(process.env.USERPROFILE || "", ".cargo", "bin", "cargo.exe"),
|
|
47
|
+
"C:\\cargo\\bin\\cargo.exe",
|
|
48
|
+
"cargo.exe", // PATH
|
|
49
49
|
];
|
|
50
50
|
|
|
51
51
|
const CARGO_UNIX_PATHS = [
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
path.join(process.env.HOME || "", ".cargo", "bin", "cargo"),
|
|
53
|
+
"/usr/local/cargo/bin/cargo",
|
|
54
|
+
"/usr/bin/cargo",
|
|
55
|
+
"cargo", // PATH
|
|
56
56
|
];
|
|
57
57
|
|
|
58
58
|
// --- Client ---
|
|
59
59
|
|
|
60
60
|
export class RustClient {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
61
|
+
private cargoAvailable: boolean | null = null;
|
|
62
|
+
private cargoPath: string | null = null;
|
|
63
|
+
private log: (msg: string) => void;
|
|
64
|
+
|
|
65
|
+
constructor(verbose = false) {
|
|
66
|
+
this.log = verbose
|
|
67
|
+
? (msg: string) => console.log(`[rust] ${msg}`)
|
|
68
|
+
: () => {};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Find cargo executable path
|
|
73
|
+
*/
|
|
74
|
+
private findCargoPath(): string | null {
|
|
75
|
+
if (this.cargoPath) return this.cargoPath;
|
|
76
|
+
|
|
77
|
+
const paths =
|
|
78
|
+
process.platform === "win32" ? CARGO_WINDOWS_PATHS : CARGO_UNIX_PATHS;
|
|
79
|
+
|
|
80
|
+
for (const p of paths) {
|
|
81
|
+
try {
|
|
82
|
+
if (p.includes("\\") || p.includes("/")) {
|
|
83
|
+
if (fs.existsSync(p)) {
|
|
84
|
+
this.cargoPath = p;
|
|
85
|
+
return p;
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
const result = spawnSync(p, ["--version"], {
|
|
89
|
+
encoding: "utf-8",
|
|
90
|
+
timeout: 3000,
|
|
91
|
+
shell: true,
|
|
92
|
+
});
|
|
93
|
+
if (!result.error && result.status === 0) {
|
|
94
|
+
this.cargoPath = p;
|
|
95
|
+
return p;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch (err) { void err; }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if cargo is installed
|
|
106
|
+
*/
|
|
107
|
+
isAvailable(): boolean {
|
|
108
|
+
if (this.cargoAvailable !== null) return this.cargoAvailable;
|
|
109
|
+
this.cargoAvailable = this.findCargoPath() !== null;
|
|
110
|
+
if (this.cargoAvailable) {
|
|
111
|
+
this.log(`Cargo found: ${this.cargoPath}`);
|
|
112
|
+
}
|
|
113
|
+
return this.cargoAvailable;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Check if a file is a Rust file
|
|
118
|
+
*/
|
|
119
|
+
isRustFile(filePath: string): boolean {
|
|
120
|
+
return path.extname(filePath).toLowerCase() === ".rs";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Run cargo check on the project
|
|
125
|
+
*/
|
|
126
|
+
checkFile(filePath: string, cwd: string): RustDiagnostic[] {
|
|
127
|
+
const cargoExe = this.findCargoPath();
|
|
128
|
+
if (!cargoExe) return [];
|
|
129
|
+
|
|
130
|
+
const absolutePath = path.resolve(filePath);
|
|
131
|
+
if (!fs.existsSync(absolutePath)) return [];
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const cargoCmd = cargoExe.includes(" ") ? `"${cargoExe}"` : cargoExe;
|
|
135
|
+
const result = spawnSync(
|
|
136
|
+
cargoCmd,
|
|
137
|
+
["check", "--message-format", "json"],
|
|
138
|
+
{
|
|
139
|
+
encoding: "utf-8",
|
|
140
|
+
timeout: 60000,
|
|
141
|
+
cwd,
|
|
142
|
+
shell: true,
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const output = result.stdout || "";
|
|
147
|
+
return this.parseJsonOutput(output, absolutePath);
|
|
148
|
+
} catch (err: any) {
|
|
149
|
+
this.log(`Check error: ${err.message}`);
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Run clippy for additional lints
|
|
156
|
+
*/
|
|
157
|
+
clippyCheck(cwd: string): RustDiagnostic[] {
|
|
158
|
+
if (!this.isAvailable()) return [];
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const result = spawnSync(
|
|
162
|
+
"cargo",
|
|
163
|
+
["clippy", "--message-format", "json"],
|
|
164
|
+
{
|
|
165
|
+
encoding: "utf-8",
|
|
166
|
+
timeout: 60000,
|
|
167
|
+
cwd,
|
|
168
|
+
shell: true,
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const output = result.stdout || "";
|
|
173
|
+
return this.parseJsonOutput(output, "");
|
|
174
|
+
} catch (err: any) {
|
|
175
|
+
this.log(`Clippy error: ${err.message}`);
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Format diagnostics for LLM consumption
|
|
182
|
+
*/
|
|
183
|
+
formatDiagnostics(diags: RustDiagnostic[], maxItems = 10): string {
|
|
184
|
+
if (diags.length === 0) return "";
|
|
185
|
+
|
|
186
|
+
const errors = diags.filter((d) => d.severity === "error");
|
|
187
|
+
const warnings = diags.filter((d) => d.severity === "warning");
|
|
188
|
+
|
|
189
|
+
let output = `[Rust] ${diags.length} issue(s)`;
|
|
190
|
+
if (errors.length) output += ` — ${errors.length} error(s)`;
|
|
191
|
+
if (warnings.length) output += ` — ${warnings.length} warning(s)`;
|
|
192
|
+
output += ":\n";
|
|
193
|
+
|
|
194
|
+
for (const d of diags.slice(0, maxItems)) {
|
|
195
|
+
const loc = `L${d.line}:${d.column}`;
|
|
196
|
+
const code = d.code ? ` [${d.code}]` : "";
|
|
197
|
+
output += ` [${d.severity}] ${loc} ${d.message.slice(0, 200)}${code}\n`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (diags.length > maxItems) {
|
|
201
|
+
output += ` ... and ${diags.length - maxItems} more\n`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return output;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// --- Internal ---
|
|
208
|
+
|
|
209
|
+
private parseJsonOutput(
|
|
210
|
+
output: string,
|
|
211
|
+
filterFile: string,
|
|
212
|
+
): RustDiagnostic[] {
|
|
213
|
+
if (!output.trim()) return [];
|
|
214
|
+
|
|
215
|
+
const diags: RustDiagnostic[] = [];
|
|
216
|
+
const lines = output.split("\n").filter((l) => l.trim());
|
|
217
|
+
|
|
218
|
+
for (const line of lines) {
|
|
219
|
+
try {
|
|
220
|
+
const msg: CargoMessage = JSON.parse(line);
|
|
221
|
+
|
|
222
|
+
if (msg.reason === "compiler-message" && msg.message) {
|
|
223
|
+
const { level, message, spans, code } = msg.message;
|
|
224
|
+
|
|
225
|
+
// Only include errors and warnings
|
|
226
|
+
if (level !== "error" && level !== "warning" && level !== "note") {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Get location from spans
|
|
231
|
+
if (spans && spans.length > 0) {
|
|
232
|
+
for (const span of spans) {
|
|
233
|
+
const file = span.file_name;
|
|
234
|
+
|
|
235
|
+
// Filter to specific file if provided
|
|
236
|
+
if (
|
|
237
|
+
filterFile &&
|
|
238
|
+
path.resolve(file) !== path.resolve(filterFile)
|
|
239
|
+
) {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
diags.push({
|
|
244
|
+
line: span.line_start,
|
|
245
|
+
column: span.column_start - 1,
|
|
246
|
+
endLine: span.line_end,
|
|
247
|
+
endColumn: span.column_end - 1,
|
|
248
|
+
severity: level as RustDiagnostic["severity"],
|
|
249
|
+
message: message.slice(0, 300),
|
|
250
|
+
code,
|
|
251
|
+
file: path.resolve(file),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
// No span info, add as general diagnostic
|
|
256
|
+
diags.push({
|
|
257
|
+
line: 1,
|
|
258
|
+
column: 0,
|
|
259
|
+
endLine: 1,
|
|
260
|
+
endColumn: 0,
|
|
261
|
+
severity: level as RustDiagnostic["severity"],
|
|
262
|
+
message: message.slice(0, 300),
|
|
263
|
+
code,
|
|
264
|
+
file: filterFile || "",
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} catch (err) { void err; } // Skip non-JSON lines
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return diags;
|
|
272
|
+
}
|
|
266
273
|
}
|