pi-subagents 0.8.4 → 0.9.0

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 CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.9.0] - 2026-02-17
6
+
7
+ ### Added
8
+ - **Builtin agents** — the extension now ships with a default set of agent definitions in `agents/`. These are loaded with lowest priority so user and project agents always override them. New users get a useful set of agents out of the box without manual setup.
9
+ - `scout` — fast codebase recon (claude-haiku-4-5)
10
+ - `planner` — implementation plans from context (claude-opus-4-6, thinking: high)
11
+ - `worker` — general-purpose execution (claude-sonnet-4-6)
12
+ - `reviewer` — validates implementation against plans (gpt-5.2, thinking: high)
13
+ - `code-reviewer` — bug hunting and code review (claude-opus-4-6, thinking: high)
14
+ - `context-builder` — analyzes requirements and codebase (claude-sonnet-4-6)
15
+ - `researcher` — autonomous web research with search, evaluation, and synthesis (claude-sonnet-4-6)
16
+ - **`"builtin"` agent source** — new third tier in agent discovery. Priority: builtin < user < project. Builtin agents appear in listings with a `[builtin]` badge and cannot be modified or deleted through management actions (create a same-named user agent to override instead).
17
+
18
+ ### Fixed
19
+ - Async subagent session sharing no longer fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`. The runner tried `require.resolve("@mariozechner/pi-coding-agent/package.json")` to find pi's HTML export module, but pi's `exports` map doesn't include that subpath. The fix resolves the package root in the main pi process by walking up from `process.argv[1]` and passes it to the spawned runner through the config, bypassing `require.resolve` entirely. The Windows CLI resolution fallback in `getPiSpawnCommand` benefits from the same walk-up function.
20
+
21
+ ## [0.8.5] - 2026-02-16
22
+
23
+ ### Fixed
24
+ - Async subagent execution no longer fails with "jiti not found" on machines without a global `jiti` install. The jiti resolution now tries three strategies: vanilla `jiti`, the `@mariozechner/jiti` fork, and finally resolves `@mariozechner/jiti` from pi's own installation via `process.argv[1]`. Since pi always ships the fork as a dependency, async mode now works out of the box.
25
+ - Improved the "jiti not found" error message to explain what's needed and how to fix it.
26
+
5
27
  ## [0.8.4] - 2026-02-13
6
28
 
7
29
  ### Fixed
package/agents.ts CHANGED
@@ -11,6 +11,8 @@ import { mergeAgentsForScope } from "./agent-selection.js";
11
11
 
12
12
  export type AgentScope = "user" | "project" | "both";
13
13
 
14
+ export type AgentSource = "builtin" | "user" | "project";
15
+
14
16
  export interface AgentConfig {
15
17
  name: string;
16
18
  description: string;
@@ -19,7 +21,7 @@ export interface AgentConfig {
19
21
  model?: string;
20
22
  thinking?: string;
21
23
  systemPrompt: string;
22
- source: "user" | "project";
24
+ source: AgentSource;
23
25
  filePath: string;
24
26
  skills?: string[];
25
27
  extensions?: string[];
@@ -44,7 +46,7 @@ export interface ChainStepConfig {
44
46
  export interface ChainConfig {
45
47
  name: string;
46
48
  description: string;
47
- source: "user" | "project";
49
+ source: AgentSource;
48
50
  filePath: string;
49
51
  steps: ChainStepConfig[];
50
52
  extraFields?: Record<string, string>;
@@ -85,7 +87,7 @@ function parseFrontmatter(content: string): { frontmatter: Record<string, string
85
87
  return { frontmatter, body };
86
88
  }
87
89
 
88
- function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig[] {
90
+ function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
89
91
  const agents: AgentConfig[] = [];
90
92
 
91
93
  if (!fs.existsSync(dir)) {
@@ -184,7 +186,7 @@ function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig
184
186
  return agents;
185
187
  }
186
188
 
187
- function loadChainsFromDir(dir: string, source: "user" | "project"): ChainConfig[] {
189
+ function loadChainsFromDir(dir: string, source: AgentSource): ChainConfig[] {
188
190
  const chains: ChainConfig[] = [];
189
191
 
190
192
  if (!fs.existsSync(dir)) {
@@ -240,13 +242,16 @@ function findNearestProjectAgentsDir(cwd: string): string | null {
240
242
  }
241
243
  }
242
244
 
245
+ const BUILTIN_AGENTS_DIR = path.join(os.homedir(), ".pi", "agent", "extensions", "subagent", "agents");
246
+
243
247
  export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
244
248
  const userDir = path.join(os.homedir(), ".pi", "agent", "agents");
245
249
  const projectAgentsDir = findNearestProjectAgentsDir(cwd);
246
250
 
251
+ const builtinAgents = loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin");
247
252
  const userAgents = scope === "project" ? [] : loadAgentsFromDir(userDir, "user");
248
253
  const projectAgents = scope === "user" || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, "project");
249
- const agents = mergeAgentsForScope(scope, userAgents, projectAgents);
254
+ const agents = mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents);
250
255
 
251
256
  return { agents, projectAgentsDir };
252
257
  }
@@ -13,6 +13,7 @@ import type { AgentConfig } from "./agents.js";
13
13
  import { applyThinkingSuffix } from "./execution.js";
14
14
  import { injectSingleOutputInstruction, resolveSingleOutputPath } from "./single-output.js";
15
15
  import { isParallelStep, resolveStepBehavior, type ChainStep, type SequentialStep, type StepOverrides } from "./settings.js";
16
+ import { resolvePiPackageRoot } from "./pi-spawn.js";
16
17
  import { buildSkillInjection, normalizeSkillInput, resolveSkills } from "./skills.js";
17
18
  import {
18
19
  type ArtifactConfig,
@@ -23,12 +24,24 @@ import {
23
24
  } from "./types.js";
24
25
 
25
26
  const require = createRequire(import.meta.url);
27
+ const piPackageRoot = resolvePiPackageRoot();
26
28
  const jitiCliPath: string | undefined = (() => {
27
- try {
28
- return path.join(path.dirname(require.resolve("jiti/package.json")), "lib/jiti-cli.mjs");
29
- } catch {
30
- return undefined;
29
+ const candidates: Array<() => string> = [
30
+ () => path.join(path.dirname(require.resolve("jiti/package.json")), "lib/jiti-cli.mjs"),
31
+ () => path.join(path.dirname(require.resolve("@mariozechner/jiti/package.json")), "lib/jiti-cli.mjs"),
32
+ () => {
33
+ const piEntry = fs.realpathSync(process.argv[1]);
34
+ const piRequire = createRequire(piEntry);
35
+ return path.join(path.dirname(piRequire.resolve("@mariozechner/jiti/package.json")), "lib/jiti-cli.mjs");
36
+ },
37
+ ];
38
+ for (const candidate of candidates) {
39
+ try {
40
+ const p = candidate();
41
+ if (fs.existsSync(p)) return p;
42
+ } catch {}
31
43
  }
44
+ return undefined;
32
45
  })();
33
46
 
34
47
  export interface AsyncExecutionContext {
@@ -177,6 +190,7 @@ export function executeAsyncChain(
177
190
  sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
178
191
  asyncDir,
179
192
  sessionId: ctx.currentSessionId,
193
+ piPackageRoot,
180
194
  },
181
195
  id,
182
196
  runnerCwd,
@@ -254,6 +268,7 @@ export function executeAsyncSingle(
254
268
  sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
255
269
  asyncDir,
256
270
  sessionId: ctx.currentSessionId,
271
+ piPackageRoot,
257
272
  },
258
273
  id,
259
274
  runnerCwd,
package/index.ts CHANGED
@@ -335,7 +335,7 @@ MANAGEMENT (use action field — omit agent/task/chain/tasks):
335
335
  if (effectiveAsync) {
336
336
  if (!isAsyncAvailable()) {
337
337
  return {
338
- content: [{ type: "text", text: "jiti not found" }],
338
+ content: [{ type: "text", text: "Async mode requires jiti for TypeScript execution but it could not be found. Install globally: npm install -g jiti" }],
339
339
  isError: true,
340
340
  details: { mode: "single" as const, results: [] },
341
341
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.8.4",
3
+ "version": "0.9.0",
4
4
  "description": "Pi extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
package/pi-spawn.ts CHANGED
@@ -4,6 +4,22 @@ import * as path from "node:path";
4
4
 
5
5
  const require = createRequire(import.meta.url);
6
6
 
7
+ export function resolvePiPackageRoot(): string | undefined {
8
+ try {
9
+ const entry = process.argv[1];
10
+ if (!entry) return undefined;
11
+ let dir = path.dirname(fs.realpathSync(entry));
12
+ while (dir !== path.dirname(dir)) {
13
+ try {
14
+ const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf-8"));
15
+ if (pkg.name === "@mariozechner/pi-coding-agent") return dir;
16
+ } catch {}
17
+ dir = path.dirname(dir);
18
+ }
19
+ } catch {}
20
+ return undefined;
21
+ }
22
+
7
23
  export interface PiSpawnDeps {
8
24
  platform?: NodeJS.Platform;
9
25
  execPath?: string;
@@ -11,6 +27,7 @@ export interface PiSpawnDeps {
11
27
  existsSync?: (filePath: string) => boolean;
12
28
  readFileSync?: (filePath: string, encoding: "utf-8") => string;
13
29
  resolvePackageJson?: () => string;
30
+ piPackageRoot?: string;
14
31
  }
15
32
 
16
33
  export interface PiSpawnCommand {
@@ -40,7 +57,11 @@ export function resolveWindowsPiCliScript(deps: PiSpawnDeps = {}): string | unde
40
57
  }
41
58
 
42
59
  try {
43
- const resolvePackageJson = deps.resolvePackageJson ?? (() => require.resolve("@mariozechner/pi-coding-agent/package.json"));
60
+ const resolvePackageJson = deps.resolvePackageJson ?? (() => {
61
+ const root = deps.piPackageRoot ?? resolvePiPackageRoot();
62
+ if (root) return path.join(root, "package.json");
63
+ return require.resolve("@mariozechner/pi-coding-agent/package.json");
64
+ });
44
65
  const packageJsonPath = resolvePackageJson();
45
66
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as {
46
67
  bin?: string | Record<string, string>;
@@ -44,6 +44,7 @@ interface SubagentRunConfig {
44
44
  sessionDir?: string;
45
45
  asyncDir: string;
46
46
  sessionId?: string | null;
47
+ piPackageRoot?: string;
47
48
  }
48
49
 
49
50
  interface StepResult {
@@ -134,8 +135,8 @@ function runPiStreaming(
134
135
  });
135
136
  }
136
137
 
137
- async function exportSessionHtml(sessionFile: string, outputDir: string): Promise<string> {
138
- const pkgRoot = path.dirname(require.resolve("@mariozechner/pi-coding-agent/package.json"));
138
+ async function exportSessionHtml(sessionFile: string, outputDir: string, piPackageRoot?: string): Promise<string> {
139
+ const pkgRoot = piPackageRoot ?? path.dirname(require.resolve("@mariozechner/pi-coding-agent/package.json"));
139
140
  const exportModulePath = path.join(pkgRoot, "dist", "core", "export-html", "index.js");
140
141
  const moduleUrl = pathToFileURL(exportModulePath).href;
141
142
  const mod = await import(moduleUrl);
@@ -516,7 +517,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
516
517
  sessionFile = findLatestSessionFile(config.sessionDir) ?? undefined;
517
518
  if (sessionFile) {
518
519
  try {
519
- const htmlPath = await exportSessionHtml(sessionFile, config.sessionDir);
520
+ const htmlPath = await exportSessionHtml(sessionFile, config.sessionDir, config.piPackageRoot);
520
521
  const share = createShareLink(htmlPath);
521
522
  if ("error" in share) shareError = share.error;
522
523
  else {