taskplane 0.25.3 → 0.25.4

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.
@@ -359,11 +359,14 @@ export default function (pi: ExtensionAPI) {
359
359
  /**
360
360
  * Resolve the Pi CLI entrypoint path (same logic as agent-host.ts).
361
361
  */
362
+ let _npmRootCache: string | null = null;
362
363
  function getNpmGlobalRoot(): string {
364
+ if (_npmRootCache !== null) return _npmRootCache;
363
365
  try {
364
366
  const result = spawnSync("npm", ["root", "-g"], { encoding: "utf-8", timeout: 5000, shell: true });
365
- return result.stdout?.trim() || "";
366
- } catch { return ""; }
367
+ _npmRootCache = result.stdout?.trim() || "";
368
+ } catch { _npmRootCache = ""; }
369
+ return _npmRootCache;
367
370
  }
368
371
 
369
372
  function resolvePiCli(): string {
@@ -20,7 +20,7 @@
20
20
  * @since TP-104
21
21
  */
22
22
 
23
- import { spawn, type ChildProcess } from "child_process";
23
+ import { spawn, spawnSync, type ChildProcess } from "child_process";
24
24
  import {
25
25
  readFileSync, writeFileSync, appendFileSync, mkdirSync,
26
26
  existsSync, readdirSync, renameSync,
@@ -55,46 +55,54 @@ import { appendMailboxAuditEvent } from "./mailbox.ts";
55
55
  * with `shell: false`. This function resolves the underlying JS file.
56
56
  *
57
57
  * Resolution order:
58
- * 1. APPDATA/npm/node_modules/@mariozechner/pi-coding-agent/dist/cli.js
59
- * 2. HOME/.npm-global/lib/node_modules/...
60
- * 3. /usr/local/lib/node_modules/...
58
+ * 1. npm root -g (dynamic — covers nvm, Homebrew, volta, custom prefix)
59
+ * 2. APPDATA/npm/node_modules/... (Windows)
60
+ * 3. HOME/.npm-global/lib/node_modules/...
61
+ * 4. /usr/local/lib/node_modules/...
62
+ * 5. /opt/homebrew/lib/node_modules/... (macOS Homebrew)
61
63
  *
62
64
  * @returns Absolute path to the Pi CLI JS entrypoint
63
65
  * @throws Error if Pi CLI cannot be found
64
66
  *
65
67
  * @since TP-104
66
68
  */
69
+ let _npmGlobalRootCache: string | null = null;
70
+ function getNpmGlobalRoot(): string {
71
+ if (_npmGlobalRootCache !== null) return _npmGlobalRootCache;
72
+ try {
73
+ const result = spawnSync("npm", ["root", "-g"], { encoding: "utf-8", timeout: 5000, shell: true });
74
+ _npmGlobalRootCache = result.stdout?.trim() || "";
75
+ } catch {
76
+ _npmGlobalRootCache = "";
77
+ }
78
+ return _npmGlobalRootCache;
79
+ }
80
+
67
81
  export function resolvePiCliPath(): string {
68
- const relPath = join("node_modules", "@mariozechner", "pi-coding-agent", "dist", "cli.js");
82
+ const relPath = join("@mariozechner", "pi-coding-agent", "dist", "cli.js");
69
83
  const candidates: string[] = [];
70
84
 
71
- if (process.env.APPDATA) {
72
- candidates.push(join(process.env.APPDATA, "npm", relPath));
73
- }
85
+ // Dynamic first: covers nvm, Homebrew, volta, custom npm prefix
86
+ const npmRoot = getNpmGlobalRoot();
87
+ if (npmRoot) candidates.push(join(npmRoot, relPath));
88
+
89
+ // Well-known static fallbacks
74
90
  const home = process.env.HOME || process.env.USERPROFILE || "";
91
+ if (process.env.APPDATA) candidates.push(join(process.env.APPDATA, "npm", "node_modules", relPath));
75
92
  if (home) {
76
- candidates.push(join(home, "AppData", "Roaming", "npm", relPath));
77
- candidates.push(join(home, ".npm-global", "lib", relPath));
93
+ candidates.push(join(home, "AppData", "Roaming", "npm", "node_modules", relPath));
94
+ candidates.push(join(home, ".npm-global", "lib", "node_modules", relPath));
78
95
  }
79
- candidates.push(join("/usr", "local", "lib", relPath));
80
- candidates.push(join("/opt", "homebrew", "lib", relPath));
81
-
82
- // Dynamic: npm root -g
83
- try {
84
- const { spawnSync } = require("child_process");
85
- const result = spawnSync("npm", ["root", "-g"], { encoding: "utf-8", timeout: 5000, shell: true });
86
- if (result.stdout?.trim()) {
87
- candidates.push(join(result.stdout.trim(), "@mariozechner", "pi-coding-agent", "dist", "cli.js"));
88
- }
89
- } catch { /* ignore */ }
96
+ candidates.push(join("/usr", "local", "lib", "node_modules", relPath));
97
+ candidates.push(join("/opt", "homebrew", "lib", "node_modules", relPath));
90
98
 
91
99
  for (const candidate of candidates) {
92
100
  if (existsSync(candidate)) return candidate;
93
101
  }
94
102
 
95
103
  throw new Error(
96
- "Cannot find Pi CLI entrypoint (cli.js). Ensure pi is installed globally. " +
97
- `Searched: ${candidates.slice(0, 4).join(", ")}`,
104
+ "Cannot find Pi CLI entrypoint (cli.js). Ensure pi is installed globally via 'pi install'. " +
105
+ `npm root -g: ${npmRoot || "(not found)"}`,
98
106
  );
99
107
  }
100
108
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.25.3",
3
+ "version": "0.25.4",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",