taskplane 0.25.1 → 0.25.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.
@@ -25,7 +25,7 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
25
25
  import { Type } from "@mariozechner/pi-ai";
26
26
  import { writeFileSync, readFileSync, existsSync, mkdirSync, renameSync, unlinkSync } from "fs";
27
27
  import { join, dirname } from "path";
28
- import { spawn as nodeSpawn } from "child_process";
28
+ import { spawn as nodeSpawn, spawnSync } from "child_process";
29
29
  import { randomBytes } from "crypto";
30
30
  import { buildExpansionRequestId, type SegmentExpansionRequest } from "./types.ts";
31
31
 
@@ -359,32 +359,60 @@ export default function (pi: ExtensionAPI) {
359
359
  /**
360
360
  * Resolve the Pi CLI entrypoint path (same logic as agent-host.ts).
361
361
  */
362
+ function getNpmGlobalRoot(): string {
363
+ try {
364
+ const result = spawnSync("npm", ["root", "-g"], { encoding: "utf-8", timeout: 5000, shell: true });
365
+ return result.stdout?.trim() || "";
366
+ } catch { return ""; }
367
+ }
368
+
362
369
  function resolvePiCli(): string {
363
- const relPath = join("node_modules", "@mariozechner", "pi-coding-agent", "dist", "cli.js");
370
+ const relPath = join("@mariozechner", "pi-coding-agent", "dist", "cli.js");
364
371
  const candidates: string[] = [];
365
- if (process.env.APPDATA) candidates.push(join(process.env.APPDATA, "npm", relPath));
372
+
373
+ // Dynamic: npm root -g (covers nvm, Homebrew, volta, and all custom prefixes)
374
+ const npmRoot = getNpmGlobalRoot();
375
+ if (npmRoot) candidates.push(join(npmRoot, relPath));
376
+
377
+ // Well-known static paths
366
378
  const home = process.env.HOME || process.env.USERPROFILE || "";
379
+ if (process.env.APPDATA) candidates.push(join(process.env.APPDATA, "npm", "node_modules", relPath));
367
380
  if (home) {
368
- candidates.push(join(home, "AppData", "Roaming", "npm", relPath));
369
- candidates.push(join(home, ".npm-global", "lib", relPath));
381
+ candidates.push(join(home, "AppData", "Roaming", "npm", "node_modules", relPath));
382
+ candidates.push(join(home, ".npm-global", "lib", "node_modules", relPath));
370
383
  }
371
- candidates.push(join("/usr", "local", "lib", relPath));
384
+ candidates.push(join("/usr", "local", "lib", "node_modules", relPath));
385
+ candidates.push(join("/opt", "homebrew", "lib", "node_modules", relPath));
386
+
372
387
  for (const c of candidates) {
373
388
  if (existsSync(c)) return c;
374
389
  }
375
- throw new Error("Cannot find Pi CLI entrypoint");
390
+ throw new Error("Cannot find Pi CLI entrypoint. Run: npm root -g to verify your npm global path.");
376
391
  }
377
392
 
378
393
  /**
379
394
  * Load the reviewer system prompt from base template + local override.
380
395
  */
381
396
  function loadReviewerPrompt(): string {
382
- const basePaths = [
383
- process.env.APPDATA ? join(process.env.APPDATA, "npm", "node_modules", "taskplane", "templates", "agents", "task-reviewer.md") : "",
384
- (process.env.HOME || process.env.USERPROFILE || "") ? join(process.env.HOME || process.env.USERPROFILE || "", "AppData", "Roaming", "npm", "node_modules", "taskplane", "templates", "agents", "task-reviewer.md") : "",
385
- ].filter(Boolean);
397
+ const relPath = join("taskplane", "templates", "agents", "task-reviewer.md");
398
+ const candidates: string[] = [];
399
+
400
+ // Dynamic: npm root -g
401
+ const npmRoot = getNpmGlobalRoot();
402
+ if (npmRoot) candidates.push(join(npmRoot, relPath));
403
+
404
+ // Well-known static paths
405
+ const home = process.env.HOME || process.env.USERPROFILE || "";
406
+ if (process.env.APPDATA) candidates.push(join(process.env.APPDATA, "npm", "node_modules", relPath));
407
+ if (home) {
408
+ candidates.push(join(home, "AppData", "Roaming", "npm", "node_modules", relPath));
409
+ candidates.push(join(home, ".npm-global", "lib", "node_modules", relPath));
410
+ }
411
+ candidates.push(join("/usr", "local", "lib", "node_modules", relPath));
412
+ candidates.push(join("/opt", "homebrew", "lib", "node_modules", relPath));
413
+
386
414
  let basePrompt = "You are a code reviewer. Read the request and write your review to the specified output file.";
387
- for (const p of basePaths) {
415
+ for (const p of candidates) {
388
416
  try {
389
417
  if (!existsSync(p)) continue;
390
418
  const raw = readFileSync(p, "utf-8");
@@ -2126,31 +2126,20 @@ function parseAgentFile(filePath: string): { fm: Record<string, string>; body: s
2126
2126
  * @since TP-117
2127
2127
  */
2128
2128
  function loadBaseAgentPrompt(agentName: string): string {
2129
- const relPath = join("node_modules", "taskplane", "templates", "agents", `${agentName}.md`);
2130
- const candidates: string[] = [];
2131
-
2132
- // Global npm paths
2133
- const home = process.env.HOME || process.env.USERPROFILE || "";
2134
- if (process.env.APPDATA) candidates.push(join(process.env.APPDATA, "npm", relPath));
2135
- if (home) {
2136
- candidates.push(join(home, "AppData", "Roaming", "npm", relPath));
2137
- candidates.push(join(home, ".npm-global", "lib", relPath));
2138
- }
2139
- candidates.push(join("/usr", "local", "lib", relPath));
2140
- candidates.push(join("/opt", "homebrew", "lib", relPath));
2141
-
2142
- // Dynamic: npm root -g
2129
+ // Use the same robust resolution as resolveTaskplanePackageFile, which
2130
+ // handles all npm setups (nvm, Homebrew, volta, Windows, etc.) via
2131
+ // npm root -g caching and well-known fallback paths.
2132
+ // This avoids loadBaseAgentPrompt silently returning "" (which would
2133
+ // cause the worker to skip reviews because the review_step instructions
2134
+ // live in the base template, not the local .pi/agents/ override).
2135
+ const relPath = join("templates", "agents", `${agentName}.md`);
2143
2136
  try {
2144
- const result = spawnSync("npm", ["root", "-g"], { encoding: "utf-8", timeout: 5000, shell: true });
2145
- if (result.stdout?.trim()) {
2146
- candidates.push(join(result.stdout.trim(), "taskplane", "templates", "agents", `${agentName}.md`));
2137
+ const resolved = resolveTaskplanePackageFile(process.cwd(), relPath);
2138
+ if (existsSync(resolved)) {
2139
+ const def = parseAgentFile(resolved);
2140
+ if (def?.body) return def.body;
2147
2141
  }
2148
- } catch { /* ignore */ }
2149
-
2150
- for (const p of candidates) {
2151
- const def = parseAgentFile(p);
2152
- if (def?.body) return def.body;
2153
- }
2142
+ } catch { /* fall through */ }
2154
2143
  return "";
2155
2144
  }
2156
2145
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.25.1",
3
+ "version": "0.25.3",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",