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 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
- private getRuleDescription(ruleId: string): RuleDescription | undefined {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-lens",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "Real-time code feedback for pi — TypeScript LSP, Biome, ast-grep, Ruff, TODO scanner, dead code, duplicate detection, type coverage",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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