pi-lens 2.2.2 → 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.
@@ -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
- function isPyrightAvailable() {
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
- // Check if pyright CLI is available (do NOT auto-install via npx)
16
- const check = spawnSync("pyright", ["--version"], {
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 direct command, not npx)
35
- const result = spawnSync("pyright", ["--outputjson", ctx.filePath], {
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
- function isPyrightAvailable(): boolean {
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
- // Check if pyright CLI is available (do NOT auto-install via npx)
25
- const check = spawnSync("pyright", ["--version"], {
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 direct command, not npx)
47
- const result = spawnSync("pyright", ["--outputjson", ctx.filePath], {
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
- // Check if ruff is available
15
- const check = spawnSync("ruff", ["--version"], {
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("ruff", args, {
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
- // Check if ruff is available
24
- const check = spawnSync("ruff", ["--version"], {
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("ruff", args, {
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.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": {