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.
- package/CHANGELOG.md +16 -0
- package/README.md +1 -1
- package/clients/architect-client.js +50 -20
- package/clients/architect-client.ts +61 -22
- package/clients/ast-grep-client.js +32 -127
- package/clients/ast-grep-client.test.js +2 -14
- package/clients/ast-grep-client.test.ts +2 -16
- package/clients/ast-grep-client.ts +34 -150
- package/clients/auto-loop.js +117 -0
- package/clients/auto-loop.ts +171 -0
- package/clients/biome-client.js +25 -22
- package/clients/biome-client.ts +28 -25
- package/clients/complexity-client.js +111 -74
- package/clients/complexity-client.ts +149 -105
- package/clients/dependency-checker.js +16 -30
- package/clients/dependency-checker.ts +15 -35
- package/clients/fix-scanners.js +195 -0
- package/clients/fix-scanners.ts +297 -0
- package/clients/interviewer-templates.js +75 -0
- package/clients/interviewer-templates.ts +90 -0
- package/clients/interviewer.js +73 -101
- package/clients/interviewer.ts +195 -140
- package/clients/knip-client.js +12 -4
- package/clients/knip-client.ts +21 -16
- package/clients/metrics-history.js +215 -0
- package/clients/metrics-history.ts +300 -0
- package/clients/scan-architectural-debt.js +62 -72
- package/clients/scan-architectural-debt.ts +79 -64
- package/clients/scan-utils.js +98 -0
- package/clients/scan-utils.ts +112 -0
- package/clients/sg-runner.js +138 -0
- package/clients/sg-runner.ts +168 -0
- package/clients/subprocess-client.js +0 -37
- package/clients/subprocess-client.ts +0 -60
- package/clients/ts-service.ts +1 -7
- package/clients/type-safety-client.js +3 -8
- package/clients/type-safety-client.ts +10 -14
- package/clients/typescript-client.js +55 -56
- package/clients/typescript-client.ts +94 -52
- package/default-architect.yaml +87 -0
- package/index.ts +143 -1165
- package/package.json +2 -1
- package/rules/ast-grep-rules/rules/large-class.yml +6 -2
- package/rules/ast-grep-rules/rules/long-method.yml +1 -1
- package/rules/ast-grep-rules/rules/no-single-char-var.yml +2 -2
- 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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
normalized,
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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."
|