pi-lens 2.2.1 → 2.2.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/README.md
CHANGED
|
@@ -16,6 +16,58 @@ pi install git:github.com/apmantza/pi-lens
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
+
## Dependent Tools
|
|
20
|
+
|
|
21
|
+
pi-lens works out of the box for TypeScript/JavaScript. For full language support, install these tools — **all are optional and gracefully skip if not installed**:
|
|
22
|
+
|
|
23
|
+
### JavaScript / TypeScript
|
|
24
|
+
|
|
25
|
+
| Tool | Install | What it does |
|
|
26
|
+
|------|---------|--------------|
|
|
27
|
+
| `@biomejs/biome` | `npm i -D @biomejs/biome` | Linting + formatting |
|
|
28
|
+
| `knip` | `npm i -D knip` | Dead code / unused exports |
|
|
29
|
+
| `jscpd` | `npm i -D jscpd` | Copy-paste detection |
|
|
30
|
+
| `type-coverage` | `npm i -D type-coverage` | TypeScript `any` coverage % |
|
|
31
|
+
|
|
32
|
+
### Python
|
|
33
|
+
|
|
34
|
+
| Tool | Install | What it does |
|
|
35
|
+
|------|---------|--------------|
|
|
36
|
+
| `ruff` | `pip install ruff` | Linting + formatting |
|
|
37
|
+
| `pyright` | `pip install pyright` | Type-checking (catches type errors) |
|
|
38
|
+
|
|
39
|
+
### Go
|
|
40
|
+
|
|
41
|
+
| Tool | Install | What it does |
|
|
42
|
+
|------|---------|--------------|
|
|
43
|
+
| `go` | [golang.org](https://golang.org) | Built-in `go vet` for static analysis |
|
|
44
|
+
|
|
45
|
+
### Rust
|
|
46
|
+
|
|
47
|
+
| Tool | Install | What it does |
|
|
48
|
+
|------|---------|--------------|
|
|
49
|
+
| `rust` + `clippy` | [rustup.rs](https://rustup.rs) | Linting via `cargo clippy` |
|
|
50
|
+
|
|
51
|
+
### All Languages
|
|
52
|
+
|
|
53
|
+
| Tool | Install | What it does |
|
|
54
|
+
|------|---------|--------------|
|
|
55
|
+
| `@ast-grep/cli` | `npm i -D @ast-grep/cli` | Structural pattern matching (80+ rules) |
|
|
56
|
+
|
|
57
|
+
**Example setup for a TypeScript + Python project:**
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# TypeScript tooling
|
|
61
|
+
npm i -D @biomejs/biome knip jscpd type-coverage
|
|
62
|
+
|
|
63
|
+
# Python tooling
|
|
64
|
+
pip install ruff pyright
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
pi-lens automatically detects which tools are available and enables their runners accordingly.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
19
71
|
## What's New (v2.2)
|
|
20
72
|
|
|
21
73
|
### `/lens-rate` — Code Quality Scoring
|
|
@@ -384,21 +436,6 @@ Each rule includes a `message` and `note` that are shown in diagnostics, so the
|
|
|
384
436
|
|
|
385
437
|
---
|
|
386
438
|
|
|
387
|
-
## External dependencies summary
|
|
388
|
-
|
|
389
|
-
| Package | Install | Purpose |
|
|
390
|
-
|---|---|---|
|
|
391
|
-
| `@biomejs/biome` | `npm i -D @biomejs/biome` | JS/TS/CSS/JSON lint + format + autofix |
|
|
392
|
-
| `@ast-grep/cli` | `npm i -D @ast-grep/cli` | 60+ structural pattern rules |
|
|
393
|
-
| `knip` | `npm i -D knip` | Unused exports, types, unlisted deps |
|
|
394
|
-
| `jscpd` | `npm i -D jscpd` | Copy-paste / duplicate code detection |
|
|
395
|
-
| `type-coverage` | `npm i -D type-coverage` | TypeScript `any` coverage percentage |
|
|
396
|
-
| `madge` | `npm i -D madge` | Circular dependency detection |
|
|
397
|
-
| `ruff` | `pip install ruff` | Python lint + format + autofix |
|
|
398
|
-
| `pyright` | `pip install pyright` or `npm i -g pyright` | Python type-checking (optional, graceful skip if not installed) |
|
|
399
|
-
|
|
400
|
-
---
|
|
401
|
-
|
|
402
439
|
## TypeScript LSP — tsconfig detection
|
|
403
440
|
|
|
404
441
|
The LSP walks up from the edited file's directory until it finds a `tsconfig.json`. If found, it uses that project's exact `compilerOptions` (paths, strict settings, lib, etc.). If not found, it falls back to sensible defaults:
|
|
@@ -7,18 +7,44 @@
|
|
|
7
7
|
* Requires: pyright (pip install pyright or npm install -g pyright)
|
|
8
8
|
*/
|
|
9
9
|
import { spawnSync } from "node:child_process";
|
|
10
|
+
import * as fs from "node:fs";
|
|
11
|
+
import * as path from "node:path";
|
|
10
12
|
// Cache pyright availability check
|
|
11
13
|
let pyrightAvailable = null;
|
|
12
|
-
|
|
14
|
+
let pyrightCommand = null;
|
|
15
|
+
/**
|
|
16
|
+
* Find pyright command, checking venv first, then global.
|
|
17
|
+
* Looks in .venv/bin, venv/bin (Unix), .venv/Scripts, venv/Scripts (Windows)
|
|
18
|
+
*/
|
|
19
|
+
function findPyrightCommand(cwd) {
|
|
20
|
+
// Check common venv locations
|
|
21
|
+
const venvPaths = [
|
|
22
|
+
".venv/bin/pyright",
|
|
23
|
+
"venv/bin/pyright",
|
|
24
|
+
".venv/Scripts/pyright.exe",
|
|
25
|
+
"venv/Scripts/pyright.exe",
|
|
26
|
+
];
|
|
27
|
+
for (const venvPath of venvPaths) {
|
|
28
|
+
const fullPath = path.join(cwd, venvPath);
|
|
29
|
+
if (fs.existsSync(fullPath)) {
|
|
30
|
+
return `"${fullPath}"`; // Quote for Windows paths with spaces
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Fall back to global
|
|
34
|
+
return "pyright";
|
|
35
|
+
}
|
|
36
|
+
function isPyrightAvailable(cwd) {
|
|
13
37
|
if (pyrightAvailable !== null)
|
|
14
38
|
return pyrightAvailable;
|
|
15
|
-
|
|
16
|
-
const check = spawnSync(
|
|
39
|
+
const command = findPyrightCommand(cwd || process.cwd());
|
|
40
|
+
const check = spawnSync(command, ["--version"], {
|
|
17
41
|
encoding: "utf-8",
|
|
18
42
|
timeout: 5000,
|
|
19
43
|
shell: true,
|
|
20
44
|
});
|
|
21
45
|
pyrightAvailable = !check.error && check.status === 0;
|
|
46
|
+
if (pyrightAvailable)
|
|
47
|
+
pyrightCommand = command;
|
|
22
48
|
return pyrightAvailable;
|
|
23
49
|
}
|
|
24
50
|
const pyrightRunner = {
|
|
@@ -28,11 +54,11 @@ const pyrightRunner = {
|
|
|
28
54
|
enabledByDefault: true,
|
|
29
55
|
async run(ctx) {
|
|
30
56
|
// Skip if pyright is not installed
|
|
31
|
-
if (!isPyrightAvailable()) {
|
|
57
|
+
if (!isPyrightAvailable(ctx.cwd || process.cwd())) {
|
|
32
58
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
33
59
|
}
|
|
34
|
-
// Run pyright with JSON output (use
|
|
35
|
-
const result = spawnSync(
|
|
60
|
+
// Run pyright with JSON output (use venv-local or global command)
|
|
61
|
+
const result = spawnSync(pyrightCommand, ["--outputjson", ctx.filePath], {
|
|
36
62
|
encoding: "utf-8",
|
|
37
63
|
timeout: 60000,
|
|
38
64
|
shell: true,
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { spawnSync } from "node:child_process";
|
|
11
|
+
import * as fs from "node:fs";
|
|
12
|
+
import * as path from "node:path";
|
|
11
13
|
import type {
|
|
12
14
|
Diagnostic,
|
|
13
15
|
DispatchContext,
|
|
@@ -17,17 +19,44 @@ import type {
|
|
|
17
19
|
|
|
18
20
|
// Cache pyright availability check
|
|
19
21
|
let pyrightAvailable: boolean | null = null;
|
|
22
|
+
let pyrightCommand: string | null = null;
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Find pyright command, checking venv first, then global.
|
|
26
|
+
* Looks in .venv/bin, venv/bin (Unix), .venv/Scripts, venv/Scripts (Windows)
|
|
27
|
+
*/
|
|
28
|
+
function findPyrightCommand(cwd: string): string {
|
|
29
|
+
// Check common venv locations
|
|
30
|
+
const venvPaths = [
|
|
31
|
+
".venv/bin/pyright",
|
|
32
|
+
"venv/bin/pyright",
|
|
33
|
+
".venv/Scripts/pyright.exe",
|
|
34
|
+
"venv/Scripts/pyright.exe",
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
for (const venvPath of venvPaths) {
|
|
38
|
+
const fullPath = path.join(cwd, venvPath);
|
|
39
|
+
if (fs.existsSync(fullPath)) {
|
|
40
|
+
return `"${fullPath}"`; // Quote for Windows paths with spaces
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Fall back to global
|
|
45
|
+
return "pyright";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isPyrightAvailable(cwd?: string): boolean {
|
|
22
49
|
if (pyrightAvailable !== null) return pyrightAvailable;
|
|
23
50
|
|
|
24
|
-
|
|
25
|
-
|
|
51
|
+
const command = findPyrightCommand(cwd || process.cwd());
|
|
52
|
+
|
|
53
|
+
const check = spawnSync(command, ["--version"], {
|
|
26
54
|
encoding: "utf-8",
|
|
27
55
|
timeout: 5000,
|
|
28
56
|
shell: true,
|
|
29
57
|
});
|
|
30
58
|
pyrightAvailable = !check.error && check.status === 0;
|
|
59
|
+
if (pyrightAvailable) pyrightCommand = command;
|
|
31
60
|
return pyrightAvailable;
|
|
32
61
|
}
|
|
33
62
|
|
|
@@ -39,12 +68,12 @@ const pyrightRunner: RunnerDefinition = {
|
|
|
39
68
|
|
|
40
69
|
async run(ctx: DispatchContext): Promise<RunnerResult> {
|
|
41
70
|
// Skip if pyright is not installed
|
|
42
|
-
if (!isPyrightAvailable()) {
|
|
71
|
+
if (!isPyrightAvailable(ctx.cwd || process.cwd())) {
|
|
43
72
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
44
73
|
}
|
|
45
74
|
|
|
46
|
-
// Run pyright with JSON output (use
|
|
47
|
-
const result = spawnSync(
|
|
75
|
+
// Run pyright with JSON output (use venv-local or global command)
|
|
76
|
+
const result = spawnSync(pyrightCommand!, ["--outputjson", ctx.filePath], {
|
|
48
77
|
encoding: "utf-8",
|
|
49
78
|
timeout: 60000,
|
|
50
79
|
shell: true,
|
|
@@ -2,29 +2,62 @@
|
|
|
2
2
|
* Ruff runner for dispatch system
|
|
3
3
|
*
|
|
4
4
|
* Ruff handles both formatting and linting for Python files.
|
|
5
|
+
* Supports venv-local installations.
|
|
5
6
|
*/
|
|
6
7
|
import { spawnSync } from "node:child_process";
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as path from "node:path";
|
|
7
10
|
import { stripAnsi } from "../../sanitize.js";
|
|
11
|
+
// Cache ruff availability check
|
|
12
|
+
let ruffAvailable = null;
|
|
13
|
+
let ruffCommand = null;
|
|
14
|
+
/**
|
|
15
|
+
* Find ruff command, checking venv first, then global.
|
|
16
|
+
*/
|
|
17
|
+
function findRuffCommand(cwd) {
|
|
18
|
+
const venvPaths = [
|
|
19
|
+
".venv/bin/ruff",
|
|
20
|
+
"venv/bin/ruff",
|
|
21
|
+
".venv/Scripts/ruff.exe",
|
|
22
|
+
"venv/Scripts/ruff.exe",
|
|
23
|
+
];
|
|
24
|
+
for (const venvPath of venvPaths) {
|
|
25
|
+
const fullPath = path.join(cwd, venvPath);
|
|
26
|
+
if (fs.existsSync(fullPath)) {
|
|
27
|
+
return `"${fullPath}"`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return "ruff";
|
|
31
|
+
}
|
|
32
|
+
function isRuffAvailable(cwd) {
|
|
33
|
+
if (ruffAvailable !== null)
|
|
34
|
+
return ruffAvailable;
|
|
35
|
+
const command = findRuffCommand(cwd || process.cwd());
|
|
36
|
+
const check = spawnSync(command, ["--version"], {
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
timeout: 5000,
|
|
39
|
+
shell: true,
|
|
40
|
+
});
|
|
41
|
+
ruffAvailable = !check.error && check.status === 0;
|
|
42
|
+
if (ruffAvailable)
|
|
43
|
+
ruffCommand = command;
|
|
44
|
+
return ruffAvailable;
|
|
45
|
+
}
|
|
8
46
|
const ruffRunner = {
|
|
9
47
|
id: "ruff-lint",
|
|
10
48
|
appliesTo: ["python"],
|
|
11
49
|
priority: 10,
|
|
12
50
|
enabledByDefault: true,
|
|
13
51
|
async run(ctx) {
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
encoding: "utf-8",
|
|
17
|
-
timeout: 5000,
|
|
18
|
-
shell: true,
|
|
19
|
-
});
|
|
20
|
-
if (check.error || check.status !== 0) {
|
|
52
|
+
// Skip if ruff is not installed
|
|
53
|
+
if (!isRuffAvailable(ctx.cwd || process.cwd())) {
|
|
21
54
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
22
55
|
}
|
|
23
56
|
// Run ruff check
|
|
24
57
|
const args = ctx.autofix
|
|
25
58
|
? ["check", "--fix", ctx.filePath]
|
|
26
59
|
: ["check", ctx.filePath];
|
|
27
|
-
const result = spawnSync(
|
|
60
|
+
const result = spawnSync(ruffCommand, args, {
|
|
28
61
|
encoding: "utf-8",
|
|
29
62
|
timeout: 30000,
|
|
30
63
|
shell: true,
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* Ruff runner for dispatch system
|
|
3
3
|
*
|
|
4
4
|
* Ruff handles both formatting and linting for Python files.
|
|
5
|
+
* Supports venv-local installations.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
import { spawnSync } from "node:child_process";
|
|
9
|
+
import * as fs from "node:fs";
|
|
10
|
+
import * as path from "node:path";
|
|
8
11
|
import { stripAnsi } from "../../sanitize.js";
|
|
9
12
|
import type {
|
|
10
13
|
Diagnostic,
|
|
@@ -13,6 +16,46 @@ import type {
|
|
|
13
16
|
RunnerResult,
|
|
14
17
|
} from "../types.js";
|
|
15
18
|
|
|
19
|
+
// Cache ruff availability check
|
|
20
|
+
let ruffAvailable: boolean | null = null;
|
|
21
|
+
let ruffCommand: string | null = null;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Find ruff command, checking venv first, then global.
|
|
25
|
+
*/
|
|
26
|
+
function findRuffCommand(cwd: string): string {
|
|
27
|
+
const venvPaths = [
|
|
28
|
+
".venv/bin/ruff",
|
|
29
|
+
"venv/bin/ruff",
|
|
30
|
+
".venv/Scripts/ruff.exe",
|
|
31
|
+
"venv/Scripts/ruff.exe",
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
for (const venvPath of venvPaths) {
|
|
35
|
+
const fullPath = path.join(cwd, venvPath);
|
|
36
|
+
if (fs.existsSync(fullPath)) {
|
|
37
|
+
return `"${fullPath}"`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return "ruff";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isRuffAvailable(cwd?: string): boolean {
|
|
45
|
+
if (ruffAvailable !== null) return ruffAvailable;
|
|
46
|
+
|
|
47
|
+
const command = findRuffCommand(cwd || process.cwd());
|
|
48
|
+
const check = spawnSync(command, ["--version"], {
|
|
49
|
+
encoding: "utf-8",
|
|
50
|
+
timeout: 5000,
|
|
51
|
+
shell: true,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
ruffAvailable = !check.error && check.status === 0;
|
|
55
|
+
if (ruffAvailable) ruffCommand = command;
|
|
56
|
+
return ruffAvailable;
|
|
57
|
+
}
|
|
58
|
+
|
|
16
59
|
const ruffRunner: RunnerDefinition = {
|
|
17
60
|
id: "ruff-lint",
|
|
18
61
|
appliesTo: ["python"],
|
|
@@ -20,14 +63,8 @@ const ruffRunner: RunnerDefinition = {
|
|
|
20
63
|
enabledByDefault: true,
|
|
21
64
|
|
|
22
65
|
async run(ctx: DispatchContext): Promise<RunnerResult> {
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
encoding: "utf-8",
|
|
26
|
-
timeout: 5000,
|
|
27
|
-
shell: true,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
if (check.error || check.status !== 0) {
|
|
66
|
+
// Skip if ruff is not installed
|
|
67
|
+
if (!isRuffAvailable(ctx.cwd || process.cwd())) {
|
|
31
68
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
32
69
|
}
|
|
33
70
|
|
|
@@ -36,7 +73,7 @@ const ruffRunner: RunnerDefinition = {
|
|
|
36
73
|
? ["check", "--fix", ctx.filePath]
|
|
37
74
|
: ["check", ctx.filePath];
|
|
38
75
|
|
|
39
|
-
const result = spawnSync(
|
|
76
|
+
const result = spawnSync(ruffCommand!, args, {
|
|
40
77
|
encoding: "utf-8",
|
|
41
78
|
timeout: 30000,
|
|
42
79
|
shell: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-lens",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Real-time code quality feedback for pi — TypeScript LSP, Biome, ast-grep, Ruff, complexity metrics, duplicate detection. Includes automated fix loop (/lens-booboo-fix) and interactive architectural refactoring (/lens-booboo-refactor) with browser-based interviews.",
|
|
6
6
|
"repository": {
|