pi-lens 1.3.2 → 1.3.3
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 +9 -0
- package/README.md +4 -0
- package/clients/ast-grep-client.ts +1 -1
- package/index.ts +82 -0
- package/package.json +1 -1
- package/rules/ast-grep-rules/rules/large-class.yml +9 -0
- package/rules/ast-grep-rules/rules/long-method.yml +9 -0
- package/rules/ast-grep-rules/rules/long-parameter-list.yml +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to pi-lens will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.4.0] - 2026-03-23
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Design smell rules**: New `long-method`, `long-parameter-list`, and `large-class` rules for structural quality checks.
|
|
9
|
+
- **`/design-review` command**: Analyze files for design smells. Usage: `/design-review [path]`
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- **Improved ast-grep tool descriptions**: Better pattern guidance to prevent overly broad searches.
|
|
13
|
+
|
|
5
14
|
## [1.3.0] - 2026-03-23
|
|
6
15
|
|
|
7
16
|
### Changed
|
package/README.md
CHANGED
|
@@ -70,6 +70,7 @@ Example:
|
|
|
70
70
|
| `/dead-code` | Find unused exports/files/dependencies (requires knip) |
|
|
71
71
|
| `/check-deps` | Circular dependency scan (requires madge) |
|
|
72
72
|
| `/format [file|--all]` | Apply Biome formatting |
|
|
73
|
+
| `/design-review [path]` | Analyze files for design smells (long methods, large classes, etc.) |
|
|
73
74
|
|
|
74
75
|
### On-demand tools
|
|
75
76
|
|
|
@@ -136,6 +137,9 @@ Each rule includes a `message` and `note` that are shown in diagnostics, so the
|
|
|
136
137
|
**Patterns**
|
|
137
138
|
`no-console-log`, `no-alert`, `no-delete-operator`, `no-shadow`, `no-star-imports`, `switch-needs-default`
|
|
138
139
|
|
|
140
|
+
**Design Smells**
|
|
141
|
+
`long-method`, `long-parameter-list`, `large-class`
|
|
142
|
+
|
|
139
143
|
---
|
|
140
144
|
|
|
141
145
|
## External dependencies summary
|
|
@@ -411,7 +411,7 @@ export class AstGrepClient {
|
|
|
411
411
|
return null;
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
-
|
|
414
|
+
getRuleDescription(ruleId: string): RuleDescription | undefined {
|
|
415
415
|
const descriptions = this.loadRuleDescriptions();
|
|
416
416
|
return descriptions.get(ruleId);
|
|
417
417
|
}
|
package/index.ts
CHANGED
|
@@ -187,6 +187,88 @@ export default function (pi: ExtensionAPI) {
|
|
|
187
187
|
},
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
+
pi.registerCommand("design-review", {
|
|
191
|
+
description:
|
|
192
|
+
"Analyze files for design smells (long methods, large classes, deep nesting). Usage: /design-review [path]",
|
|
193
|
+
handler: async (args, ctx) => {
|
|
194
|
+
if (!astGrepClient.isAvailable()) {
|
|
195
|
+
ctx.ui.notify(
|
|
196
|
+
"ast-grep not installed. Run: npm i -D @ast-grep/cli",
|
|
197
|
+
"error",
|
|
198
|
+
);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const targetPath = args.trim() || ctx.cwd || process.cwd();
|
|
203
|
+
ctx.ui.notify("🔍 Analyzing design smells...", "info");
|
|
204
|
+
|
|
205
|
+
const configPath = path.join(
|
|
206
|
+
typeof __dirname !== "undefined" ? __dirname : ".",
|
|
207
|
+
"rules",
|
|
208
|
+
"ast-grep-rules",
|
|
209
|
+
".sgconfig.yml",
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
const result = require("node:child_process").spawnSync("npx", [
|
|
214
|
+
"sg",
|
|
215
|
+
"scan",
|
|
216
|
+
"--config", configPath,
|
|
217
|
+
"--json",
|
|
218
|
+
targetPath,
|
|
219
|
+
], {
|
|
220
|
+
encoding: "utf-8",
|
|
221
|
+
timeout: 30000,
|
|
222
|
+
shell: true,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const output = result.stdout || result.stderr || "";
|
|
226
|
+
if (!output.trim() || result.status !== 1) {
|
|
227
|
+
ctx.ui.notify("✓ No design smells found", "info");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let issues: Array<{line: number; rule: string; message: string}> = [];
|
|
232
|
+
const lines = output.split("\n").filter((l: string) => l.trim());
|
|
233
|
+
|
|
234
|
+
for (const line of lines) {
|
|
235
|
+
try {
|
|
236
|
+
const item = JSON.parse(line);
|
|
237
|
+
const ruleId = item.ruleId || item.name || "unknown";
|
|
238
|
+
const ruleDesc = astGrepClient.getRuleDescription?.(ruleId);
|
|
239
|
+
const message = ruleDesc?.message || item.message || ruleId;
|
|
240
|
+
const lineNum = item.labels?.[0]?.range?.start?.line ||
|
|
241
|
+
item.spans?.[0]?.range?.start?.line || 0;
|
|
242
|
+
|
|
243
|
+
issues.push({
|
|
244
|
+
line: lineNum + 1,
|
|
245
|
+
rule: ruleId,
|
|
246
|
+
message: message,
|
|
247
|
+
});
|
|
248
|
+
} catch {
|
|
249
|
+
// Skip unparseable lines
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (issues.length === 0) {
|
|
254
|
+
ctx.ui.notify("✓ No design smells found", "info");
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let report = `[Design Review] ${issues.length} design smell(s) found:\n`;
|
|
259
|
+
for (const issue of issues.slice(0, 20)) {
|
|
260
|
+
report += ` L${issue.line}: ${issue.rule} — ${issue.message}\n`;
|
|
261
|
+
}
|
|
262
|
+
if (issues.length > 20) {
|
|
263
|
+
report += ` ... and ${issues.length - 20} more\n`;
|
|
264
|
+
}
|
|
265
|
+
ctx.ui.notify(report, "info");
|
|
266
|
+
} catch (err: any) {
|
|
267
|
+
ctx.ui.notify(`Design review failed: ${err.message}`, "error");
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
|
|
190
272
|
pi.registerCommand("format", {
|
|
191
273
|
description:
|
|
192
274
|
"Apply Biome formatting to files. Usage: /format [file-path] or /format --all",
|
package/package.json
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
id: large-class
|
|
2
|
+
language: TypeScript
|
|
3
|
+
message: "Large class detected — consider splitting responsibilities"
|
|
4
|
+
severity: hint
|
|
5
|
+
note: |
|
|
6
|
+
Classes with more than 20 methods are harder to maintain.
|
|
7
|
+
Consider extracting related functionality into separate classes.
|
|
8
|
+
rule:
|
|
9
|
+
kind: class_declaration
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
id: long-method
|
|
2
|
+
language: TypeScript
|
|
3
|
+
message: "Long method detected — consider extracting smaller functions"
|
|
4
|
+
severity: hint
|
|
5
|
+
note: |
|
|
6
|
+
Methods over 50 lines are harder to test and maintain.
|
|
7
|
+
Consider extracting logical chunks into helper functions.
|
|
8
|
+
rule:
|
|
9
|
+
kind: function_declaration
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
id: long-parameter-list
|
|
2
|
+
language: TypeScript
|
|
3
|
+
message: "Function has many parameters — consider using an options object"
|
|
4
|
+
severity: hint
|
|
5
|
+
note: |
|
|
6
|
+
Functions with more than 4 parameters are hard to call and remember.
|
|
7
|
+
Consider grouping related params into an options object or configuration.
|
|
8
|
+
rule:
|
|
9
|
+
kind: formal_parameters
|