pi-lens 2.0.37 → 2.0.39

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +1 -1
  3. package/clients/architect-client.js +50 -20
  4. package/clients/architect-client.ts +61 -22
  5. package/clients/ast-grep-client.js +32 -127
  6. package/clients/ast-grep-client.test.js +2 -14
  7. package/clients/ast-grep-client.test.ts +2 -16
  8. package/clients/ast-grep-client.ts +34 -150
  9. package/clients/auto-loop.js +117 -0
  10. package/clients/auto-loop.ts +171 -0
  11. package/clients/biome-client.js +25 -22
  12. package/clients/biome-client.ts +28 -25
  13. package/clients/complexity-client.js +111 -74
  14. package/clients/complexity-client.ts +149 -105
  15. package/clients/dependency-checker.js +16 -30
  16. package/clients/dependency-checker.ts +15 -35
  17. package/clients/fix-scanners.js +195 -0
  18. package/clients/fix-scanners.ts +297 -0
  19. package/clients/interviewer-templates.js +75 -0
  20. package/clients/interviewer-templates.ts +90 -0
  21. package/clients/interviewer.js +73 -101
  22. package/clients/interviewer.ts +195 -140
  23. package/clients/knip-client.js +12 -4
  24. package/clients/knip-client.ts +21 -16
  25. package/clients/metrics-history.js +215 -0
  26. package/clients/metrics-history.ts +300 -0
  27. package/clients/scan-architectural-debt.js +62 -72
  28. package/clients/scan-architectural-debt.ts +79 -64
  29. package/clients/scan-utils.js +98 -0
  30. package/clients/scan-utils.ts +112 -0
  31. package/clients/sg-runner.js +138 -0
  32. package/clients/sg-runner.ts +168 -0
  33. package/clients/subprocess-client.js +0 -37
  34. package/clients/subprocess-client.ts +0 -60
  35. package/clients/ts-service.ts +1 -7
  36. package/clients/type-safety-client.js +3 -8
  37. package/clients/type-safety-client.ts +10 -14
  38. package/clients/typescript-client.js +55 -56
  39. package/clients/typescript-client.ts +94 -52
  40. package/default-architect.yaml +87 -0
  41. package/index.ts +143 -1165
  42. package/package.json +2 -1
  43. package/rules/ast-grep-rules/rules/large-class.yml +6 -2
  44. package/rules/ast-grep-rules/rules/long-method.yml +1 -1
  45. package/rules/ast-grep-rules/rules/no-single-char-var.yml +2 -2
  46. package/rules/ast-grep-rules/rules/switch-without-default.yml +0 -12
@@ -323,31 +323,53 @@ export class TypeScriptClient {
323
323
  };
324
324
  }
325
325
 
326
+ private withPosition<T>(
327
+ filePath: string,
328
+ line: number,
329
+ character: number,
330
+ cb: (
331
+ normalized: string,
332
+ position: number,
333
+ ls: ts.LanguageService,
334
+ ) => T | undefined,
335
+ ): T | [] {
336
+ const resolved = this.resolvePosition(filePath, line, character);
337
+ if (!resolved) return [];
338
+ const { normalized, position, ls } = resolved;
339
+ return cb(normalized, position, ls) ?? [];
340
+ }
341
+
326
342
  /**
327
343
  * Go to definition
328
344
  */
329
345
  getDefinition(filePath: string, line: number, character: number): Location[] {
330
- const resolved = this.resolvePosition(filePath, line, character);
331
- if (!resolved) return [];
332
- const { normalized, position, ls } = resolved;
333
- const definitions = ls.getDefinitionAtPosition(normalized, position);
334
- if (!definitions) return [];
335
-
336
- return definitions.map((def) => {
337
- if (def.textSpan) {
338
- const defFile = def.fileName || normalized;
339
- const defContent = this.fileContents.get(defFile) || "";
340
- if (defContent) {
341
- const lines = defContent.substring(0, def.textSpan.start).split("\n");
342
- return {
343
- file: defFile,
344
- line: lines.length - 1,
345
- character: lines[lines.length - 1].length,
346
- };
347
- }
348
- }
349
- return { file: def.fileName, line: 0, character: 0 };
350
- });
346
+ return this.withPosition(
347
+ filePath,
348
+ line,
349
+ character,
350
+ (normalized, position, ls) => {
351
+ const definitions = ls.getDefinitionAtPosition(normalized, position);
352
+ if (!definitions) return undefined;
353
+
354
+ return definitions.map((def) => {
355
+ if (def.textSpan) {
356
+ const defFile = def.fileName || normalized;
357
+ const defContent = this.fileContents.get(defFile) || "";
358
+ if (defContent) {
359
+ const lines = defContent
360
+ .substring(0, def.textSpan.start)
361
+ .split("\n");
362
+ return {
363
+ file: defFile,
364
+ line: lines.length - 1,
365
+ character: lines[lines.length - 1].length,
366
+ };
367
+ }
368
+ }
369
+ return { file: def.fileName, line: 0, character: 0 };
370
+ });
371
+ },
372
+ ) as Location[];
351
373
  }
352
374
 
353
375
  /**
@@ -358,24 +380,32 @@ export class TypeScriptClient {
358
380
  line: number,
359
381
  character: number,
360
382
  ): Location[] {
361
- const resolved = this.resolvePosition(filePath, line, character);
362
- if (!resolved) return [];
363
- const { normalized, position, ls } = resolved;
364
- const defs = ls.getTypeDefinitionAtPosition(normalized, position);
365
- if (!defs) return [];
366
- return this.toLocations(defs, normalized);
383
+ return this.withPosition(
384
+ filePath,
385
+ line,
386
+ character,
387
+ (normalized, position, ls) => {
388
+ const defs = ls.getTypeDefinitionAtPosition(normalized, position);
389
+ if (!defs) return undefined;
390
+ return this.toLocations(defs, normalized);
391
+ },
392
+ ) as Location[];
367
393
  }
368
394
 
369
395
  /**
370
396
  * Find references
371
397
  */
372
398
  getReferences(filePath: string, line: number, character: number): Location[] {
373
- const resolved = this.resolvePosition(filePath, line, character);
374
- if (!resolved) return [];
375
- const { normalized, position, ls } = resolved;
376
- const references = ls.getReferencesAtPosition(normalized, position);
377
- if (!references) return [];
378
- return this.toLocations(references);
399
+ return this.withPosition(
400
+ filePath,
401
+ line,
402
+ character,
403
+ (normalized, position, ls) => {
404
+ const references = ls.getReferencesAtPosition(normalized, position);
405
+ if (!references) return undefined;
406
+ return this.toLocations(references);
407
+ },
408
+ ) as Location[];
379
409
  }
380
410
 
381
411
  /** Map TS definition/reference entries to Location objects. */
@@ -442,16 +472,24 @@ export class TypeScriptClient {
442
472
  line: number,
443
473
  character: number,
444
474
  ): CompletionItem[] {
445
- const resolved = this.resolvePosition(filePath, line, character);
446
- if (!resolved) return [];
447
- const { normalized, position, ls } = resolved;
448
- const completions = ls.getCompletionsAtPosition(normalized, position, {});
449
- if (!completions) return [];
450
- return completions.entries.slice(0, 50).map((entry) => ({
451
- name: entry.name,
452
- kind: this.completionKind(entry.kind),
453
- sortText: entry.sortText,
454
- }));
475
+ return this.withPosition(
476
+ filePath,
477
+ line,
478
+ character,
479
+ (normalized, position, ls) => {
480
+ const completions = ls.getCompletionsAtPosition(
481
+ normalized,
482
+ position,
483
+ {},
484
+ );
485
+ if (!completions) return undefined;
486
+ return completions.entries.slice(0, 50).map((entry) => ({
487
+ name: entry.name,
488
+ kind: this.completionKind(entry.kind),
489
+ sortText: entry.sortText,
490
+ }));
491
+ },
492
+ ) as CompletionItem[];
455
493
  }
456
494
 
457
495
  /**
@@ -462,15 +500,19 @@ export class TypeScriptClient {
462
500
  line: number,
463
501
  character: number,
464
502
  ): Location[] {
465
- const resolved = this.resolvePosition(filePath, line, character);
466
- if (!resolved) return [];
467
- const { normalized, position, ls } = resolved;
468
- const implementations = ls.getImplementationAtPosition(
469
- normalized,
470
- position,
471
- );
472
- if (!implementations) return [];
473
- return this.toLocations(implementations);
503
+ return this.withPosition(
504
+ filePath,
505
+ line,
506
+ character,
507
+ (normalized, position, ls) => {
508
+ const implementations = ls.getImplementationAtPosition(
509
+ normalized,
510
+ position,
511
+ );
512
+ if (!implementations) return undefined;
513
+ return this.toLocations(implementations);
514
+ },
515
+ ) as Location[];
474
516
  }
475
517
 
476
518
  /**
@@ -0,0 +1,87 @@
1
+ # .pi-lens/architect.yaml
2
+ # Architectural rules for your project
3
+ # Copy to your project root as .pi-lens/architect.yaml and customize.
4
+ #
5
+ # Structure is language-agnostic — regex patterns in must_not are language-specific.
6
+
7
+ version: "1.1"
8
+
9
+ # =============================================================================
10
+ # LANGUAGE-AGNOSTIC RULES (The "Universal Truths")
11
+ # =============================================================================
12
+
13
+ rules:
14
+ # --- CI & Environment Safety ---
15
+ - pattern: "**/*.{ts,tsx,js,jsx,py,go,rs,java,cpp}"
16
+ must_not:
17
+ - pattern: "[a-zA-Z]:\\\\(?:Users|Program Files|Windows|Temp)\\\\"
18
+ message: "No absolute Windows paths — breaks CI and cross-platform builds."
19
+ - pattern: "\\/(?:home|Users|usr|etc|var)\\/[a-zA-Z0-9_\\-]+\\/"
20
+ message: "Potential absolute Unix path detected — use relative paths or path.join()."
21
+ - pattern: 'https?://localhost:[0-9]+'
22
+ message: "No hardcoded localhost URLs — use environment variables or a config service."
23
+
24
+ # --- Complexity & Technical Debt ---
25
+ - pattern: "**/*.{ts,tsx,js,jsx,py,go,rs}"
26
+ max_lines: 3000
27
+ must_not:
28
+ - pattern: '(?:\s{8,}.*\n){5,}'
29
+ message: "Deep nesting detected (4+ levels). Refactor into smaller functions to reduce cognitive load."
30
+ - pattern: '(?:(?:\/\/|#).*\n){10,}'
31
+ message: "Large block of commented-out code detected. This is dead code — delete it and rely on Git history."
32
+
33
+ # --- Reliability & Fragility ---
34
+ - pattern: "**/*.{ts,tsx,js,jsx,py,go,rs}"
35
+ must_not:
36
+ - pattern: '(?:catch|except)\s*\(?.*?\)?\s*\{\s*\}'
37
+ message: "No empty catch/except blocks. Swallowing errors makes debugging impossible — at least log the error."
38
+ - pattern: '\b(password|secret|api_?key|token|private_?key)\b\s*[:=]\s*["\'][^"\']{8,}["\']'
39
+ message: "No hardcoded secrets — use environment variables or a secrets manager."
40
+
41
+ # =============================================================================
42
+ # JS/TS-SPECIFIC RULES (Node.js & Frontend)
43
+ # =============================================================================
44
+
45
+ # --- Type Safety ---
46
+ - pattern: "**/*.{ts,tsx}"
47
+ must_not:
48
+ - pattern: ':\s*any\b|as\s+any\b'
49
+ message: "No 'any' types — use 'unknown' or define a proper interface to maintain type safety."
50
+ must:
51
+ - "Use strict TypeScript mode"
52
+
53
+ # --- Configuration & IO ---
54
+ - pattern: "**/services/**/*.ts|**/domain/**/*.ts"
55
+ must_not:
56
+ - pattern: 'process\.env'
57
+ message: "Domain/Service logic must not read env vars directly — inject config via constructor or a config service."
58
+ - pattern: 'console\.(log|warn|error)'
59
+ message: "Core logic should use a structured logger, not console.log."
60
+
61
+ # --- Async Patterns ---
62
+ - pattern: "**/*.{ts,tsx,js,jsx}"
63
+ must_not:
64
+ - pattern: '\.then\('
65
+ message: "Prefer async/await over .then() chains for better readability and error handling."
66
+
67
+ # =============================================================================
68
+ # PYTHON-SPECIFIC RULES
69
+ # =============================================================================
70
+
71
+ # --- Clean Python ---
72
+ - pattern: "**/*.py"
73
+ must_not:
74
+ - pattern: 'print\('
75
+ message: "No print() statements in production code — use the 'logging' module."
76
+ - pattern: 'global\s+[a-zA-Z_]'
77
+ message: "No 'global' keyword — global state makes code untestable and unpredictable."
78
+ - pattern: 'def\s+[a-zA-Z0-9_]+\(.*?=\s*\[\]'
79
+ message: "No mutable default arguments (e.g., x=[]). Use 'x=None' and initialize inside the function."
80
+
81
+ # --- Python Reliability ---
82
+ - pattern: "**/*.py"
83
+ must_not:
84
+ - pattern: 'except:\s*pass|except\s+Exception:\s*pass'
85
+ message: "Do not use bare 'except: pass'. Explicitly catch specific exceptions and log them."
86
+ - pattern: 'eval\(|exec\('
87
+ message: "No eval() or exec() — these are security risks and architectural anti-patterns."