pi-fast-subagent 0.5.0 → 0.6.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/README.md +145 -54
- package/agents/scout.md +1 -0
- package/agents.ts +41 -4
- package/index.ts +21 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,6 +61,101 @@ model: anthropic/claude-haiku-4-5
|
|
|
61
61
|
You are code exploration specialist. Read relevant files, trace data flow, summarize findings clearly.
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
### Agent frontmatter
|
|
65
|
+
|
|
66
|
+
| Field | Required | Description |
|
|
67
|
+
|-------|----------|-------------|
|
|
68
|
+
| `name` | yes | Unique agent identifier used in `subagent({ agent: "..." })` |
|
|
69
|
+
| `description` | yes | One-line description shown in `/fast-subagent:agent` |
|
|
70
|
+
| `model` | no | Model override, format `provider/model-id` (e.g. `anthropic/claude-haiku-4-5`) |
|
|
71
|
+
| `tools` | no | Tool allowlist (see below) |
|
|
72
|
+
|
|
73
|
+
### `tools:` field
|
|
74
|
+
|
|
75
|
+
Controls which tools the subagent has access to. Subagents inherit parent extensions (web_search, fetch_content, mcp, …) by default.
|
|
76
|
+
|
|
77
|
+
| Value | Behavior |
|
|
78
|
+
|-------|----------|
|
|
79
|
+
| *(omitted)* | Inherit everything — all builtins + all parent extensions (**default**) |
|
|
80
|
+
| `all` | Same as omitted — explicit "everything" |
|
|
81
|
+
| `none` | No tools at all — pure reasoning agent |
|
|
82
|
+
| comma list | Allowlist; extensions auto-load only if any listed tool is non-builtin |
|
|
83
|
+
|
|
84
|
+
Built-in tools: `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`.
|
|
85
|
+
|
|
86
|
+
Examples:
|
|
87
|
+
|
|
88
|
+
```md
|
|
89
|
+
---
|
|
90
|
+
name: writer
|
|
91
|
+
description: Pure-reasoning writing assistant
|
|
92
|
+
tools: none
|
|
93
|
+
---
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```md
|
|
97
|
+
---
|
|
98
|
+
name: coder
|
|
99
|
+
description: Lean code-editing agent, no extensions
|
|
100
|
+
tools: read, bash, edit, write, grep, find, ls
|
|
101
|
+
---
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```md
|
|
105
|
+
---
|
|
106
|
+
name: researcher
|
|
107
|
+
description: Web research agent
|
|
108
|
+
tools: read, write, web_search, fetch_content
|
|
109
|
+
---
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> **Performance note:** inheriting all extensions adds startup cost (extension init) and token cost (larger system prompt). For tight, focused agents, list tools explicitly — extensions are only loaded when the allowlist actually needs them.
|
|
113
|
+
|
|
114
|
+
## Background Agents
|
|
115
|
+
|
|
116
|
+
Every foreground subagent can be moved to background at any time. Background jobs run concurrently while you continue chatting. When a job finishes, pi automatically posts the result as a follow-up message.
|
|
117
|
+
|
|
118
|
+
### Status bar
|
|
119
|
+
|
|
120
|
+
While a foreground subagent is running, the pi status bar shows:
|
|
121
|
+
```
|
|
122
|
+
{agent-name} running · Ctrl+Shift+B to move to background
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
While background jobs are running:
|
|
126
|
+
```
|
|
127
|
+
⧗ N bg agents
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Moving to background
|
|
131
|
+
|
|
132
|
+
**Keyboard shortcut (while subagent is running):**
|
|
133
|
+
```
|
|
134
|
+
Ctrl+Shift+B
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Slash command:**
|
|
138
|
+
```
|
|
139
|
+
/fast-subagent:bg fg_ab12cd34
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Via tool call:**
|
|
143
|
+
```js
|
|
144
|
+
subagent({ action: "detach", jobId: "fg_ab12cd34" })
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Auto-completion announcement
|
|
148
|
+
|
|
149
|
+
When a background job finishes, pi injects a follow-up message automatically:
|
|
150
|
+
```
|
|
151
|
+
Background subagent ✓: sa_ab12cd34 (scout, 4.2s)
|
|
152
|
+
> Explore src and summarize architecture
|
|
153
|
+
|
|
154
|
+
<result output>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Failed jobs are announced the same way with ✗ and the error message.
|
|
158
|
+
|
|
64
159
|
## Slash Commands
|
|
65
160
|
|
|
66
161
|
### `/fast-subagent:agent`
|
|
@@ -81,13 +176,13 @@ Tab-completion is supported for agent names.
|
|
|
81
176
|
|
|
82
177
|
### `/fast-subagent:bg`
|
|
83
178
|
|
|
84
|
-
Detach running foreground subagent to background
|
|
179
|
+
Detach a running foreground subagent to background. Each foreground job has a `fg_` prefixed ID shown in the status bar.
|
|
85
180
|
|
|
86
181
|
```
|
|
87
182
|
/fast-subagent:bg fg_ab12cd34
|
|
88
183
|
```
|
|
89
184
|
|
|
90
|
-
Omit
|
|
185
|
+
Omit ID to list all active foreground jobs:
|
|
91
186
|
|
|
92
187
|
```
|
|
93
188
|
/fast-subagent:bg
|
|
@@ -95,13 +190,13 @@ Omit job id to list active foreground jobs:
|
|
|
95
190
|
|
|
96
191
|
### `/fast-subagent:bg-status`
|
|
97
192
|
|
|
98
|
-
|
|
193
|
+
Open selector UI showing all active background jobs. Arrow keys to navigate, Enter to view full details.
|
|
99
194
|
|
|
100
195
|
```
|
|
101
196
|
/fast-subagent:bg-status
|
|
102
197
|
```
|
|
103
198
|
|
|
104
|
-
|
|
199
|
+
Skip the selector — show details for a specific job directly:
|
|
105
200
|
|
|
106
201
|
```
|
|
107
202
|
/fast-subagent:bg-status sa_ab12cd34
|
|
@@ -109,52 +204,58 @@ Show details for specific background job:
|
|
|
109
204
|
|
|
110
205
|
### `/fast-subagent:bg-cancel`
|
|
111
206
|
|
|
112
|
-
|
|
207
|
+
Open selector UI to choose a running job to cancel:
|
|
113
208
|
|
|
114
209
|
```
|
|
115
210
|
/fast-subagent:bg-cancel
|
|
116
211
|
```
|
|
117
212
|
|
|
118
|
-
Cancel specific
|
|
213
|
+
Cancel a specific job directly:
|
|
119
214
|
|
|
120
215
|
```
|
|
121
216
|
/fast-subagent:bg-cancel sa_ab12cd34
|
|
122
217
|
```
|
|
123
218
|
|
|
124
|
-
##
|
|
219
|
+
## Keyboard Shortcuts
|
|
125
220
|
|
|
126
|
-
|
|
221
|
+
| Shortcut | Action |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `Ctrl+Shift+B` | Move active foreground subagent to background |
|
|
127
224
|
|
|
128
|
-
|
|
129
|
-
subagent({ action: "list" })
|
|
130
|
-
```
|
|
225
|
+
## Roadmap
|
|
131
226
|
|
|
132
|
-
|
|
227
|
+
Goal: keep this extension **small and focused** — aligned with pi's philosophy of minimal, composable tooling. No feature creep. Every addition must earn its place.
|
|
133
228
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
229
|
+
- **UI/UX polish** — improve visibility of running subagents: clearer status lines, better progress feedback, agent name + task always visible during execution
|
|
230
|
+
- ~~**Background subagents**~~ ✔ shipped in v0.4.0 — fire-and-forget with `background: true`, poll/cancel/detach support
|
|
231
|
+
|
|
232
|
+
## Notes
|
|
233
|
+
|
|
234
|
+
- Async/background isolation not supported in-process
|
|
235
|
+
- Git worktree isolation not supported
|
|
236
|
+
- Nested subagent depth limited to 2 by default
|
|
237
|
+
|
|
238
|
+
## Tool Reference
|
|
140
239
|
|
|
141
|
-
|
|
240
|
+
> These are `subagent` tool call examples used by the LLM internally. Not typically invoked directly by users.
|
|
241
|
+
|
|
242
|
+
### List / discover agents
|
|
142
243
|
|
|
143
244
|
```js
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
245
|
+
// List all agents
|
|
246
|
+
subagent({ action: "list" })
|
|
247
|
+
|
|
248
|
+
// Get details for a specific agent
|
|
249
|
+
subagent({ action: "get", agent: "scout" })
|
|
250
|
+
|
|
251
|
+
// Scope filter: "user" | "project" | "both" (default)
|
|
252
|
+
subagent({ action: "list", agentScope: "project" })
|
|
148
253
|
```
|
|
149
254
|
|
|
150
|
-
###
|
|
255
|
+
### Single
|
|
151
256
|
|
|
152
257
|
```js
|
|
153
|
-
subagent({
|
|
154
|
-
agent: "scout",
|
|
155
|
-
task: "Explore src and summarize architecture",
|
|
156
|
-
model: "anthropic/claude-haiku-4-5"
|
|
157
|
-
})
|
|
258
|
+
subagent({ agent: "scout", task: "Explore src and summarize architecture" })
|
|
158
259
|
```
|
|
159
260
|
|
|
160
261
|
### Parallel
|
|
@@ -165,42 +266,32 @@ subagent({
|
|
|
165
266
|
{ agent: "scout", task: "Map auth flow" },
|
|
166
267
|
{ agent: "scout", task: "Map navigation" }
|
|
167
268
|
],
|
|
168
|
-
concurrency: 2
|
|
269
|
+
concurrency: 2 // default: 4
|
|
169
270
|
})
|
|
271
|
+
|
|
272
|
+
// Repeat one task N times
|
|
273
|
+
subagent({ tasks: [{ agent: "scout", task: "Explore src", count: 3 }] })
|
|
170
274
|
```
|
|
171
275
|
|
|
172
|
-
### Background
|
|
276
|
+
### Background
|
|
173
277
|
|
|
174
278
|
```js
|
|
175
|
-
//
|
|
279
|
+
// Fire-and-forget — returns job ID immediately
|
|
176
280
|
subagent({ agent: "scout", task: "Explore src", background: true })
|
|
177
281
|
// → { jobId: "sa_ab12cd34", status: "running" }
|
|
178
282
|
|
|
179
|
-
|
|
180
|
-
subagent({ action: "
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
subagent({ action: "cancel", jobId: "sa_ab12cd34" })
|
|
184
|
-
|
|
185
|
-
// List all background jobs
|
|
186
|
-
subagent({ action: "status" })
|
|
187
|
-
|
|
188
|
-
// Detach a running foreground job to background
|
|
189
|
-
subagent({ action: "detach", jobId: "fg_ab12cd34" })
|
|
283
|
+
subagent({ action: "poll", jobId: "sa_ab12cd34" }) // check progress
|
|
284
|
+
subagent({ action: "cancel", jobId: "sa_ab12cd34" }) // abort
|
|
285
|
+
subagent({ action: "status" }) // list all bg jobs
|
|
286
|
+
subagent({ action: "detach", jobId: "fg_ab12cd34" }) // move fg → bg
|
|
190
287
|
```
|
|
191
288
|
|
|
192
|
-
|
|
289
|
+
### Options
|
|
193
290
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
## Notes
|
|
200
|
-
|
|
201
|
-
- Async/background isolation not supported in-process
|
|
202
|
-
- Git worktree isolation not supported
|
|
203
|
-
- Nested subagent depth limited to 2 by default
|
|
291
|
+
```js
|
|
292
|
+
subagent({ agent: "scout", task: "...", model: "anthropic/claude-haiku-4-5" })
|
|
293
|
+
subagent({ agent: "scout", task: "...", cwd: "/path/to/project" })
|
|
294
|
+
```
|
|
204
295
|
|
|
205
296
|
## Publish
|
|
206
297
|
|
package/agents/scout.md
CHANGED
package/agents.ts
CHANGED
|
@@ -8,16 +8,54 @@ import * as path from "node:path";
|
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { getAgentDir, parseFrontmatter } from "@mariozechner/pi-coding-agent";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* tools frontmatter semantics:
|
|
13
|
+
* unset → inherit everything (builtins + extensions) — same as `all`
|
|
14
|
+
* `all` → all builtins + all extension tools (web_search, fetch_content, mcp, …)
|
|
15
|
+
* `none` → no tools at all
|
|
16
|
+
* comma list → allowlist; extensions auto-loaded if any listed tool is non-builtin
|
|
17
|
+
* for lean "builtins-only" mode, list them explicitly:
|
|
18
|
+
* tools: read, bash, edit, write, grep, find, ls
|
|
19
|
+
*
|
|
20
|
+
* Represented as:
|
|
21
|
+
* "all" → everything (default when frontmatter omits `tools`)
|
|
22
|
+
* "none" → no tools
|
|
23
|
+
* string[] → allowlist
|
|
24
|
+
*/
|
|
25
|
+
export type AgentTools = "all" | "none" | string[];
|
|
26
|
+
|
|
27
|
+
export const BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"] as const;
|
|
28
|
+
|
|
11
29
|
export interface AgentConfig {
|
|
12
30
|
name: string;
|
|
13
31
|
description: string;
|
|
14
32
|
model?: string;
|
|
15
|
-
tools
|
|
33
|
+
tools: AgentTools;
|
|
16
34
|
systemPrompt: string;
|
|
17
35
|
source: "user" | "project";
|
|
18
36
|
filePath: string;
|
|
19
37
|
}
|
|
20
38
|
|
|
39
|
+
const BUILTIN_TOOLS = new Set<string>(BUILTIN_TOOL_NAMES);
|
|
40
|
+
|
|
41
|
+
export function agentNeedsExtensions(tools: AgentTools): boolean {
|
|
42
|
+
if (tools === "all") return true;
|
|
43
|
+
if (tools === "none") return false;
|
|
44
|
+
return tools.some((t) => !BUILTIN_TOOLS.has(t));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Default: everything. Agents list specific tools for lean / restricted mode.
|
|
48
|
+
function parseToolsField(raw: unknown): AgentTools {
|
|
49
|
+
if (raw === undefined || raw === null) return "all";
|
|
50
|
+
const str = String(raw).trim();
|
|
51
|
+
if (!str) return "all";
|
|
52
|
+
const lower = str.toLowerCase();
|
|
53
|
+
if (lower === "all") return "all";
|
|
54
|
+
if (lower === "none") return "none";
|
|
55
|
+
const list = str.split(",").map((t) => t.trim()).filter(Boolean);
|
|
56
|
+
return list.length ? list : "all";
|
|
57
|
+
}
|
|
58
|
+
|
|
21
59
|
function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig[] {
|
|
22
60
|
if (!fs.existsSync(dir)) return [];
|
|
23
61
|
let entries: fs.Dirent[];
|
|
@@ -36,13 +74,12 @@ function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig
|
|
|
36
74
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
37
75
|
const { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);
|
|
38
76
|
if (!frontmatter?.name || !frontmatter?.description) continue;
|
|
39
|
-
const
|
|
40
|
-
const tools = rawTools?.split(",").map((t: string) => t.trim()).filter(Boolean);
|
|
77
|
+
const tools = parseToolsField(frontmatter.tools);
|
|
41
78
|
agents.push({
|
|
42
79
|
name: frontmatter.name,
|
|
43
80
|
description: frontmatter.description,
|
|
44
81
|
model: frontmatter.model,
|
|
45
|
-
tools
|
|
82
|
+
tools,
|
|
46
83
|
systemPrompt: body.trim(),
|
|
47
84
|
source,
|
|
48
85
|
filePath,
|
package/index.ts
CHANGED
|
@@ -32,7 +32,13 @@ import {
|
|
|
32
32
|
|
|
33
33
|
type DefaultResourceLoaderOptions = ConstructorParameters<typeof DefaultResourceLoader>[0];
|
|
34
34
|
import { Type } from "@sinclair/typebox";
|
|
35
|
-
import { type AgentConfig, discoverAgents } from "./agents.js";
|
|
35
|
+
import { type AgentConfig, agentNeedsExtensions, discoverAgents } from "./agents.js";
|
|
36
|
+
|
|
37
|
+
function formatTools(tools: AgentConfig["tools"]): string {
|
|
38
|
+
if (tools === "all") return "all";
|
|
39
|
+
if (tools === "none") return "none";
|
|
40
|
+
return tools.join(", ");
|
|
41
|
+
}
|
|
36
42
|
|
|
37
43
|
// ─── Tool arg summarizer (compact one-liner per tool call) ─────────────────────
|
|
38
44
|
|
|
@@ -228,11 +234,14 @@ async function runAgent(
|
|
|
228
234
|
const { authStorage, modelRegistry } = getAuth();
|
|
229
235
|
const agentDir = getAgentDir();
|
|
230
236
|
|
|
231
|
-
// Build resource loader — no extensions/context files to keep subagent lean
|
|
237
|
+
// Build resource loader — no extensions/context files to keep subagent lean.
|
|
238
|
+
// Agents can opt in to extensions via `extensions: true` in frontmatter, which
|
|
239
|
+
// makes tools like web_search / fetch_content / mcp / etc. available to the
|
|
240
|
+
// subagent (subject to the optional `tools:` allowlist below).
|
|
232
241
|
const loaderOptions: DefaultResourceLoaderOptions = {
|
|
233
242
|
cwd,
|
|
234
243
|
agentDir,
|
|
235
|
-
noExtensions:
|
|
244
|
+
noExtensions: !agentNeedsExtensions(agent.tools),
|
|
236
245
|
noContextFiles: true,
|
|
237
246
|
noSkills: true,
|
|
238
247
|
};
|
|
@@ -264,8 +273,13 @@ async function runAgent(
|
|
|
264
273
|
}
|
|
265
274
|
}
|
|
266
275
|
|
|
267
|
-
//
|
|
268
|
-
|
|
276
|
+
// Apply tools allowlist.
|
|
277
|
+
// "all" → no restriction (everything registered stays active)
|
|
278
|
+
// "none" → disable every tool
|
|
279
|
+
// string[] → explicit allowlist
|
|
280
|
+
if (agent.tools === "none") {
|
|
281
|
+
session.setActiveToolsByName([]);
|
|
282
|
+
} else if (Array.isArray(agent.tools) && agent.tools.length > 0) {
|
|
269
283
|
session.setActiveToolsByName(agent.tools);
|
|
270
284
|
}
|
|
271
285
|
|
|
@@ -598,7 +612,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
598
612
|
`File: ${agent.filePath}`,
|
|
599
613
|
`Description: ${agent.description}`,
|
|
600
614
|
agent.model ? `Model: ${agent.model}` : "",
|
|
601
|
-
|
|
615
|
+
`Tools: ${formatTools(agent.tools)}`,
|
|
602
616
|
agent.systemPrompt ? `\nSystem prompt:\n${agent.systemPrompt}` : "",
|
|
603
617
|
].filter(Boolean).join("\n");
|
|
604
618
|
ctx.ui.notify(lines, "info");
|
|
@@ -984,7 +998,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
984
998
|
`## ${agent.name} [${agent.source}]`,
|
|
985
999
|
`**Description:** ${agent.description}`,
|
|
986
1000
|
agent.model ? `**Model:** ${agent.model}` : null,
|
|
987
|
-
|
|
1001
|
+
`**Tools:** ${formatTools(agent.tools)}`,
|
|
988
1002
|
agent.systemPrompt ? `\n**System prompt:**\n${agent.systemPrompt}` : null,
|
|
989
1003
|
].filter(Boolean).join("\n");
|
|
990
1004
|
return { content: [{ type: "text", text: info }] };
|