@johnnywu/pi-subagents 1.0.0 → 1.0.1

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
@@ -1,3 +1,10 @@
1
+ ## [1.0.1](https://github.com/jwu/pi-subagents/compare/v1.0.0...v1.0.1) (2026-05-31)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add spacing before subagent result status ([21932f5](https://github.com/jwu/pi-subagents/commit/21932f5cc7a59f504c60f36a6375f322c46acf8c))
7
+
1
8
  # 1.0.0 (2026-05-30)
2
9
 
3
10
 
package/README.md CHANGED
@@ -2,12 +2,120 @@
2
2
 
3
3
  Sub-agents extension for [pi](https://github.com/badlogic/pi-mono) coding agent.
4
4
 
5
+ Delegates tasks to isolated pi child processes — each running with its own model, system prompt, and tool set. Sub-agents inherit zero conversation context; all necessary context must be provided in the task description.
6
+
5
7
  ## Install
6
8
 
7
9
  ```bash
8
10
  pi install npm:@johnnywu/pi-subagents
9
11
  ```
10
12
 
13
+ ## Quick start
14
+
15
+ ### 1. Define an agent
16
+
17
+ Create a `.pi/agents/code-reviewer.md` file in your project:
18
+
19
+ ```markdown
20
+ ---
21
+ name: code-reviewer
22
+ description: Reviews code changes for correctness and style
23
+ tools: read, grep, find, ls, bash
24
+ model: anthropic/claude-sonnet-4-6
25
+ thinking: low
26
+ ---
27
+
28
+ You are a code reviewer. When given a diff or file list, read the relevant
29
+ files and provide a concise review covering:
30
+
31
+ - Logic errors and edge cases
32
+ - Style and consistency issues
33
+ - Performance concerns
34
+ - Test coverage gaps
35
+ ```
36
+
37
+ ### 2. Use it
38
+
39
+ The `subagent` tool is automatically registered. In a pi session:
40
+
41
+ ```
42
+ Review the changes in src/auth.ts using the code-reviewer agent
43
+ ```
44
+
45
+ Or instruct pi to delegate:
46
+
47
+ ```
48
+ Run the code-reviewer agent on the last three commits
49
+ ```
50
+
51
+ ## Agent configuration
52
+
53
+ Agents are Markdown files with YAML frontmatter.
54
+
55
+ | Field | Required | Default | Description |
56
+ |-------|----------|---------|-------------|
57
+ | `name` | **yes** | — | Unique agent identifier |
58
+ | `description` | no | — | Human-readable summary |
59
+ | `tools` | no | _none_ | Comma-separated tool whitelist (`read, write, bash, grep`, etc.) |
60
+ | `model` | no | parent's model | Provider/model-id (`anthropic/claude-sonnet-4-6`) |
61
+ | `thinking` | no | `off` | Reasoning level: `off`, `low`, `medium`, `high` |
62
+ | `systemPrompt` | no | `replace` | How the body is applied: `replace` (default system prompt) or `append` |
63
+ | `allowedAgents` | no | _all_ | Comma-separated list of sub-agents this agent may spawn |
64
+ | `maxDepth` | no | `10` | Maximum recursion depth (`0` = no sub-agents, `1` = one level, etc.) |
65
+
66
+ The Markdown body after the frontmatter is the agent's system prompt.
67
+
68
+ ### Example with all fields
69
+
70
+ ```markdown
71
+ ---
72
+ name: orchestrator
73
+ description: High-level planner that delegates to specialists
74
+ tools: subagent, read, grep, find
75
+ model: anthropic/claude-sonnet-4-6
76
+ thinking: high
77
+ systemPrompt: replace
78
+ allowedAgents: code-reviewer, refactor, test-writer
79
+ maxDepth: 2
80
+ ---
81
+
82
+ You are an orchestrator. Break complex tasks into sub-tasks and delegate
83
+ them to specialist agents. Combine their results and report a summary.
84
+ ```
85
+
86
+ ## Agent discovery
87
+
88
+ Agents are discovered from two locations (project overrides global):
89
+
90
+ | Scope | Path |
91
+ |-------|------|
92
+ | Global | `~/.pi/agent/agents/*.md` |
93
+ | Project | `.pi/agents/*.md` |
94
+
95
+ Only `.md` files are scanned. Files are parsed at extension load time; parse errors produce warnings but don't block other agents.
96
+
97
+ ## Recursion control
98
+
99
+ Sub-agents can spawn their own sub-agents (if the `subagent` tool is in their whitelist). Two mechanisms prevent unbounded recursion:
100
+
101
+ **`maxDepth`** — Hard limit counting from the originating agent. `maxDepth: 0` means the agent cannot spawn sub-agents. `maxDepth: 1` allows one level, etc. Defaults to `10` when the agent has `subagent` in tools.
102
+
103
+ **`allowedAgents`** — Whitelist enforced by the parent before spawning. A child process never sees agent names outside its parent's whitelist.
104
+
105
+ These are passed via environment variables (`PI_SUBAGENT_DEPTH`, `PI_SUBAGENT_MAX_DEPTH`, `PI_SUBAGENT_ALLOWED`).
106
+
107
+ ## Session storage
108
+
109
+ Sub-agent sessions are saved as `.jsonl` files for post-hoc debugging:
110
+
111
+ ```
112
+ ~/.pi/agent/sessions/--{safe-project-path}--/subagents/
113
+ ├── 2026-05-31T03-47-16-502Z_019e7c24-a395-707a-a262-ec5b1664ffa7.jsonl
114
+ └── ...
115
+ ```
116
+
117
+ Each file contains one JSON object per line — session headers, messages, tool calls, and usage data. Parent pi sessions live in the same project directory (no `subagents/` subdirectory).
118
+
11
119
  ## Development
12
120
 
13
121
  ```bash
@@ -23,7 +131,7 @@ bun run typecheck
23
131
  # Format
24
132
  bun run format
25
133
 
26
- # Release (local, requires GH_TOKEN and NPM_TOKEN)
134
+ # Release (requires GH_TOKEN and NPM_TOKEN)
27
135
  bun run release
28
136
  ```
29
137
 
@@ -1,5 +1,5 @@
1
- import { homedir } from 'node:os';
2
1
  import type { AgentProgress } from './subagent-executor.ts';
2
+ import { numberArg, preview, shortenPath, stringArg } from './tool-args.ts';
3
3
 
4
4
  export interface SubagentCallArgs {
5
5
  agent?: string;
@@ -41,10 +41,6 @@ export function contextUsageSeverity(usage: {
41
41
  return 'dim';
42
42
  }
43
43
 
44
- function preview(text: string, length: number): string {
45
- return text.length > length ? `${text.slice(0, length)}...` : text;
46
- }
47
-
48
44
  function elapsedSeconds(ms: number): number {
49
45
  return Math.round(ms / 1000);
50
46
  }
@@ -85,26 +81,10 @@ function indent(text: string, spaces: number): string {
85
81
  .join('\n');
86
82
  }
87
83
 
88
- function shortenPath(value: unknown): string | undefined {
89
- if (typeof value !== 'string') return undefined;
90
- const home = homedir();
91
- return value.startsWith(`${home}/`) ? `~/${value.slice(home.length + 1)}` : value;
92
- }
93
-
94
84
  function quote(value: string | undefined): string {
95
85
  return value ? JSON.stringify(value) : '';
96
86
  }
97
87
 
98
- function stringArg(args: Record<string, unknown>, key: string): string | undefined {
99
- const value = args[key];
100
- return typeof value === 'string' ? value : undefined;
101
- }
102
-
103
- function numberArg(args: Record<string, unknown>, key: string): number | undefined {
104
- const value = args[key];
105
- return typeof value === 'number' ? value : undefined;
106
- }
107
-
108
88
  function pathArg(args: Record<string, unknown>, fallback?: string): string {
109
89
  return shortenPath(args.path ?? args.file_path) ?? fallback ?? '...';
110
90
  }
@@ -221,6 +201,7 @@ export function formatSubagentResultLines(
221
201
  const toolLines = formatToolLineItems(progress, options);
222
202
  const usage = formatUsage(progress);
223
203
  const lines: SubagentResultLine[] = [
204
+ { text: '', kind: 'blank', singleLine: false },
224
205
  { text: statusLine, kind: 'status', singleLine: false },
225
206
  ...toolLines,
226
207
  ];
@@ -1,4 +1,3 @@
1
- import { homedir } from 'node:os';
2
1
  import {
3
2
  getMarkdownTheme,
4
3
  keyHint,
@@ -17,6 +16,7 @@ import {
17
16
  formatUsage,
18
17
  type SubagentResultLine,
19
18
  } from './subagent-render.ts';
19
+ import { numberArg, preview, shortenPath, stringArg } from './tool-args.ts';
20
20
 
21
21
  const SubagentParams = {
22
22
  type: 'object',
@@ -98,26 +98,6 @@ type CollapsedTheme = {
98
98
  bold: (text: string) => string;
99
99
  };
100
100
 
101
- function preview(text: string, length: number): string {
102
- return text.length > length ? `${text.slice(0, length)}...` : text;
103
- }
104
-
105
- function shortenPath(value: unknown): string | undefined {
106
- if (typeof value !== 'string') return undefined;
107
- const home = homedir();
108
- return value.startsWith(`${home}/`) ? `~/${value.slice(home.length + 1)}` : value;
109
- }
110
-
111
- function stringArg(args: Record<string, unknown>, key: string): string | undefined {
112
- const value = args[key];
113
- return typeof value === 'string' ? value : undefined;
114
- }
115
-
116
- function numberArg(args: Record<string, unknown>, key: string): number | undefined {
117
- const value = args[key];
118
- return typeof value === 'number' ? value : undefined;
119
- }
120
-
121
101
  function styledPathArg(
122
102
  args: Record<string, unknown>,
123
103
  theme: CollapsedTheme,
@@ -0,0 +1,21 @@
1
+ import { homedir } from 'node:os';
2
+
3
+ export function preview(text: string, length: number): string {
4
+ return text.length > length ? `${text.slice(0, length)}...` : text;
5
+ }
6
+
7
+ export function shortenPath(value: unknown): string | undefined {
8
+ if (typeof value !== 'string') return undefined;
9
+ const home = homedir();
10
+ return value.startsWith(`${home}/`) ? `~/${value.slice(home.length + 1)}` : value;
11
+ }
12
+
13
+ export function stringArg(args: Record<string, unknown>, key: string): string | undefined {
14
+ const value = args[key];
15
+ return typeof value === 'string' ? value : undefined;
16
+ }
17
+
18
+ export function numberArg(args: Record<string, unknown>, key: string): number | undefined {
19
+ const value = args[key];
20
+ return typeof value === 'number' ? value : undefined;
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnnywu/pi-subagents",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Sub-agents extension for pi coding agent.",
5
5
  "homepage": "https://github.com/jwu/pi-subagents#readme",
6
6
  "repository": {