@narumitw/pi-subagents 0.1.10
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/LICENSE +21 -0
- package/README.md +115 -0
- package/package.json +47 -0
- package/src/agents.ts +201 -0
- package/src/subagents.ts +1197 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 narumiruna
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# @narumitw/pi-subagents
|
|
2
|
+
|
|
3
|
+
Delegate work from Pi to specialized subagents running in isolated `pi --mode json -p --no-session` subprocesses.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pi install npm:@narumitw/pi-subagents
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Try locally from this repository:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pi -e ./extensions/pi-subagents
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## What it adds
|
|
18
|
+
|
|
19
|
+
A `subagent` tool with three execution modes:
|
|
20
|
+
|
|
21
|
+
- **single**: run one `{ agent, task }`
|
|
22
|
+
- **parallel**: run multiple `{ agent, task }` jobs with bounded concurrency
|
|
23
|
+
- **parallel + aggregator**: run parallel jobs, then fan their complete outputs into one follow-up agent
|
|
24
|
+
- **chain**: run sequential steps, passing prior output with `{previous}`
|
|
25
|
+
|
|
26
|
+
The design borrows from Pi/Claude-style subagents: each worker has its own system prompt, tool boundary, optional model, subprocess context window, streaming progress, abort propagation, hard subprocess timeout, complete final output in tool details, and summarized sidechain result.
|
|
27
|
+
|
|
28
|
+
## Built-in agents
|
|
29
|
+
|
|
30
|
+
These are available without setup and can be overridden by user/project agents of the same name:
|
|
31
|
+
|
|
32
|
+
| Agent | Purpose | Tools |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| `scout` | Read-only codebase reconnaissance | `read`, `grep`, `find`, `ls`, `bash` |
|
|
35
|
+
| `planner` | Grounded implementation plans | `read`, `grep`, `find`, `ls` |
|
|
36
|
+
| `reviewer` | Independent review and verification | `read`, `grep`, `find`, `ls`, `bash` |
|
|
37
|
+
| `worker` | General-purpose implementation | Pi default tools |
|
|
38
|
+
| `general`, `general-purpose` | Aliases for `worker` | Pi default tools |
|
|
39
|
+
|
|
40
|
+
Built-in agents intentionally inherit the active/default Pi model instead of forcing a model alias; this keeps subprocesses usable across provider setups.
|
|
41
|
+
|
|
42
|
+
## Custom agents
|
|
43
|
+
|
|
44
|
+
Create markdown files in:
|
|
45
|
+
|
|
46
|
+
- `~/.pi/agent/agents/*.md` for user agents
|
|
47
|
+
- `.pi/agents/*.md` for project agents
|
|
48
|
+
|
|
49
|
+
```markdown
|
|
50
|
+
---
|
|
51
|
+
name: api-reviewer
|
|
52
|
+
description: Review API changes for compatibility and tests
|
|
53
|
+
tools: read, grep, find, ls, bash
|
|
54
|
+
model: sonnet
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
You are an API review subagent. Do not edit files. Check compatibility,
|
|
58
|
+
test coverage, and migration risks. Report PASS/FAIL/PARTIAL with evidence.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Project agents are repo-controlled and disabled by default. To allow them, set `agentScope` to `"project"` or `"both"`; interactive sessions ask for confirmation.
|
|
62
|
+
|
|
63
|
+
## Runtime limits
|
|
64
|
+
|
|
65
|
+
Each subagent subprocess has a hard timeout to avoid runaway workers. Set `timeoutMs` on the top-level call or per task/chain step. The default is `PI_SUBAGENT_TIMEOUT_MS`, or 600000ms (10 minutes) when unset. On timeout, the extension sends SIGTERM, escalates to SIGKILL after a short grace period, and returns any partial messages/stderr collected so far.
|
|
66
|
+
|
|
67
|
+
## Example tool calls
|
|
68
|
+
|
|
69
|
+
Single:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{ "agent": "scout", "task": "Find the statusline extension entry points" }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Parallel:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"tasks": [
|
|
80
|
+
{ "agent": "scout", "task": "Map package metadata files", "timeoutMs": 30000 },
|
|
81
|
+
{ "agent": "reviewer", "task": "Review TypeScript config consistency" }
|
|
82
|
+
],
|
|
83
|
+
"timeoutMs": 120000
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Parallel with fan-in aggregation:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"tasks": [
|
|
92
|
+
{ "agent": "scout", "task": "Find auth-related code" },
|
|
93
|
+
{ "agent": "scout", "task": "Find auth-related tests" }
|
|
94
|
+
],
|
|
95
|
+
"aggregator": {
|
|
96
|
+
"agent": "reviewer",
|
|
97
|
+
"task": "Merge, dedupe, and verify these findings. Use {previous}."
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Chain:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"chain": [
|
|
107
|
+
{ "agent": "scout", "task": "Find subagent-related code" },
|
|
108
|
+
{ "agent": "planner", "task": "Using this context, plan the extension: {previous}" }
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Safety notes
|
|
114
|
+
|
|
115
|
+
Subagents are separate Pi processes and may use the tools allowed by their agent definition. Treat project-local agent prompts as code: only enable them in trusted repositories.
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@narumitw/pi-subagents",
|
|
3
|
+
"version": "0.1.10",
|
|
4
|
+
"description": "Pi extension for delegating work to specialized isolated subagents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"private": false,
|
|
8
|
+
"keywords": [
|
|
9
|
+
"pi-package",
|
|
10
|
+
"pi-extension",
|
|
11
|
+
"pi",
|
|
12
|
+
"subagents",
|
|
13
|
+
"agents",
|
|
14
|
+
"automation"
|
|
15
|
+
],
|
|
16
|
+
"files": [
|
|
17
|
+
"src",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"pi": {
|
|
22
|
+
"extensions": [
|
|
23
|
+
"./src/subagents.ts"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"check": "biome check . && npm run typecheck",
|
|
28
|
+
"format": "biome check --write .",
|
|
29
|
+
"typecheck": "tsc --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@mariozechner/pi-agent-core": "0.73.0",
|
|
33
|
+
"@mariozechner/pi-ai": "0.73.0",
|
|
34
|
+
"@mariozechner/pi-coding-agent": "0.73.0",
|
|
35
|
+
"@mariozechner/pi-tui": "0.73.0",
|
|
36
|
+
"typebox": "^1.1.37"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@biomejs/biome": "2.4.14",
|
|
40
|
+
"typescript": "6.0.3"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/narumiruna/pi-extensions",
|
|
45
|
+
"directory": "extensions/pi-subagents"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/agents.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent discovery and configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from "node:fs";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
import { getAgentDir, parseFrontmatter } from "@mariozechner/pi-coding-agent";
|
|
8
|
+
|
|
9
|
+
export type AgentScope = "user" | "project" | "both";
|
|
10
|
+
|
|
11
|
+
export type AgentSource = "built-in" | "user" | "project";
|
|
12
|
+
|
|
13
|
+
export interface AgentConfig {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
tools?: string[];
|
|
17
|
+
model?: string;
|
|
18
|
+
systemPrompt: string;
|
|
19
|
+
source: AgentSource;
|
|
20
|
+
filePath: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const BUILT_IN_AGENTS: AgentConfig[] = [
|
|
24
|
+
{
|
|
25
|
+
name: "scout",
|
|
26
|
+
description: "Read-only codebase reconnaissance; returns concise findings with paths and evidence.",
|
|
27
|
+
tools: ["read", "grep", "find", "ls", "bash"],
|
|
28
|
+
source: "built-in",
|
|
29
|
+
filePath: "built-in:scout",
|
|
30
|
+
systemPrompt: [
|
|
31
|
+
"You are a scout subagent. Explore the codebase quickly and report grounded findings.",
|
|
32
|
+
"Do not edit files. Prefer read, grep, find, ls, and safe bash inspection commands.",
|
|
33
|
+
"Return concise bullets with exact file paths, symbols, and open questions.",
|
|
34
|
+
].join("\n"),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "planner",
|
|
38
|
+
description: "Turns reconnaissance into a lean implementation or migration plan.",
|
|
39
|
+
tools: ["read", "grep", "find", "ls"],
|
|
40
|
+
source: "built-in",
|
|
41
|
+
filePath: "built-in:planner",
|
|
42
|
+
systemPrompt: [
|
|
43
|
+
"You are a planner subagent. Produce executable, verifiable plans only.",
|
|
44
|
+
"Do not modify files. Ground the plan in the repository's actual structure.",
|
|
45
|
+
"Call out assumptions, risks, sequencing, and verification commands.",
|
|
46
|
+
].join("\n"),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "reviewer",
|
|
50
|
+
description: "Independent code review and verification agent for completed changes.",
|
|
51
|
+
tools: ["read", "grep", "find", "ls", "bash"],
|
|
52
|
+
source: "built-in",
|
|
53
|
+
filePath: "built-in:reviewer",
|
|
54
|
+
systemPrompt: [
|
|
55
|
+
"You are a reviewer subagent. Review changes adversarially and verify claims.",
|
|
56
|
+
"Do not edit files. Run safe inspection or test commands when useful.",
|
|
57
|
+
"Report PASS, FAIL, or PARTIAL with evidence, commands run, and specific follow-ups.",
|
|
58
|
+
].join("\n"),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "worker",
|
|
62
|
+
description: "General-purpose implementation worker with the default Pi tool set.",
|
|
63
|
+
source: "built-in",
|
|
64
|
+
filePath: "built-in:worker",
|
|
65
|
+
systemPrompt: workerSystemPrompt(),
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "general",
|
|
69
|
+
description: "Alias for worker; kept for model-generated subagent names.",
|
|
70
|
+
source: "built-in",
|
|
71
|
+
filePath: "built-in:general",
|
|
72
|
+
systemPrompt: workerSystemPrompt(),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "general-purpose",
|
|
76
|
+
description: "Alias for worker; compatible with common subagent naming conventions.",
|
|
77
|
+
source: "built-in",
|
|
78
|
+
filePath: "built-in:general-purpose",
|
|
79
|
+
systemPrompt: workerSystemPrompt(),
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
function workerSystemPrompt(): string {
|
|
84
|
+
return [
|
|
85
|
+
"You are a focused worker subagent running in an isolated Pi process.",
|
|
86
|
+
"Complete the delegated task directly. Keep scope tight and avoid unrelated changes.",
|
|
87
|
+
"When done, summarize files changed, commands run, and any remaining risks.",
|
|
88
|
+
].join("\n");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface AgentDiscoveryResult {
|
|
92
|
+
agents: AgentConfig[];
|
|
93
|
+
projectAgentsDir: string | null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig[] {
|
|
97
|
+
const agents: AgentConfig[] = [];
|
|
98
|
+
|
|
99
|
+
if (!fs.existsSync(dir)) {
|
|
100
|
+
return agents;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let entries: fs.Dirent[];
|
|
104
|
+
try {
|
|
105
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
106
|
+
} catch {
|
|
107
|
+
return agents;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const entry of entries) {
|
|
111
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
112
|
+
if (!entry.isFile() && !entry.isSymbolicLink()) continue;
|
|
113
|
+
|
|
114
|
+
const filePath = path.join(dir, entry.name);
|
|
115
|
+
let content: string;
|
|
116
|
+
try {
|
|
117
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
118
|
+
} catch {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);
|
|
123
|
+
|
|
124
|
+
if (!frontmatter.name || !frontmatter.description) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const tools = frontmatter.tools
|
|
129
|
+
?.split(",")
|
|
130
|
+
.map((t: string) => t.trim())
|
|
131
|
+
.filter(Boolean);
|
|
132
|
+
|
|
133
|
+
agents.push({
|
|
134
|
+
name: frontmatter.name,
|
|
135
|
+
description: frontmatter.description,
|
|
136
|
+
tools: tools && tools.length > 0 ? tools : undefined,
|
|
137
|
+
model: frontmatter.model,
|
|
138
|
+
systemPrompt: body,
|
|
139
|
+
source,
|
|
140
|
+
filePath,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return agents;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function isDirectory(p: string): boolean {
|
|
148
|
+
try {
|
|
149
|
+
return fs.statSync(p).isDirectory();
|
|
150
|
+
} catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function findNearestProjectAgentsDir(cwd: string): string | null {
|
|
156
|
+
let currentDir = cwd;
|
|
157
|
+
while (true) {
|
|
158
|
+
const candidate = path.join(currentDir, ".pi", "agents");
|
|
159
|
+
if (isDirectory(candidate)) return candidate;
|
|
160
|
+
|
|
161
|
+
const parentDir = path.dirname(currentDir);
|
|
162
|
+
if (parentDir === currentDir) return null;
|
|
163
|
+
currentDir = parentDir;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
|
|
168
|
+
const userDir = path.join(getAgentDir(), "agents");
|
|
169
|
+
const projectAgentsDir = findNearestProjectAgentsDir(cwd);
|
|
170
|
+
|
|
171
|
+
const userAgents = scope === "project" ? [] : loadAgentsFromDir(userDir, "user");
|
|
172
|
+
const projectAgents = scope === "user" || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, "project");
|
|
173
|
+
|
|
174
|
+
const agentMap = new Map<string, AgentConfig>();
|
|
175
|
+
|
|
176
|
+
// Lowest priority: built-ins are always available, then user agents, then
|
|
177
|
+
// trusted project agents if requested. This mirrors the subagent boundary
|
|
178
|
+
// pattern in ./src: stable built-ins plus overridable local definitions.
|
|
179
|
+
for (const agent of BUILT_IN_AGENTS) agentMap.set(agent.name, agent);
|
|
180
|
+
|
|
181
|
+
if (scope === "both") {
|
|
182
|
+
for (const agent of userAgents) agentMap.set(agent.name, agent);
|
|
183
|
+
for (const agent of projectAgents) agentMap.set(agent.name, agent);
|
|
184
|
+
} else if (scope === "user") {
|
|
185
|
+
for (const agent of userAgents) agentMap.set(agent.name, agent);
|
|
186
|
+
} else {
|
|
187
|
+
for (const agent of projectAgents) agentMap.set(agent.name, agent);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return { agents: Array.from(agentMap.values()), projectAgentsDir };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function formatAgentList(agents: AgentConfig[], maxItems: number): { text: string; remaining: number } {
|
|
194
|
+
if (agents.length === 0) return { text: "none", remaining: 0 };
|
|
195
|
+
const listed = agents.slice(0, maxItems);
|
|
196
|
+
const remaining = agents.length - listed.length;
|
|
197
|
+
return {
|
|
198
|
+
text: listed.map((a) => `${a.name} (${a.source}): ${a.description}`).join("; "),
|
|
199
|
+
remaining,
|
|
200
|
+
};
|
|
201
|
+
}
|