@rohaquinlop/pi-subagents 0.2.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 ADDED
@@ -0,0 +1,171 @@
1
+ # @rohaquinlop/pi-subagents
2
+
3
+ A [pi](https://github.com/earendil-works/pi) extension that registers a single `subagent` tool with three agents:
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pi install @rohaquinlop/pi-subagents
9
+ ```
10
+
11
+ | Agent | Tools | Model | Purpose |
12
+ |-------|-------|-------|---------|
13
+ | **scout** | read, grep, find, ls | deepseek-v4-flash | Fast codebase recon |
14
+ | **researcher** | web_search, web_fetch | deepseek-v4-flash | Web research |
15
+ | **worker** | read, write, edit, safe_bash, web_search, web_fetch, subagent | deepseek-v4-flash | Code changes (can dispatch scout/researcher to protect its own context) |
16
+
17
+ `worker` is allowlisted to spawn only `scout` and `researcher` (via `subagent_agents` in its frontmatter), so the chain stops at depth 2 — a worker cannot recurse into another worker.
18
+
19
+ ## Dependencies
20
+
21
+ `safe_bash` ships in this repo (`tools/safe-bash.ts`). `web_search` and `web_fetch` do not — `researcher` and `worker` depend on them. Grab those two extensions from [amosblomqvist/pi-config](https://github.com/amosblomqvist/pi-config) (`extensions/web-search/`, `extensions/web-fetch/`) and drop them into `~/.pi/agent/extensions/`. Without them the affected agents will launch with an empty tool allowlist and silently do nothing useful.
22
+
23
+ ## Usage
24
+
25
+ One tool call = one subagent:
26
+ ```json
27
+ { "agent": "scout", "task": "Find all auth-related files in src/" }
28
+ ```
29
+
30
+ To fan out, emit multiple `subagent` tool calls in the same assistant turn — pi runs them in parallel automatically. A per-process semaphore caps simultaneous subagents at `maxConcurrency` (default 4); calls past the cap wait their turn.
31
+
32
+ Each subagent runs as an isolated `pi` process with no inherited context — all context must be in the task description.
33
+
34
+ ## Config
35
+
36
+ Optional `config.json` next to `index.ts`:
37
+
38
+ ```json
39
+ { "maxConcurrency": 4 }
40
+ ```
41
+
42
+ ## Output
43
+
44
+ Subagents return text only — there's no file handoff. If the parent needs artifacts, instruct the subagent to `write` them and return the path.
45
+
46
+ Large outputs (>`DEFAULT_MAX_BYTES`) are head-truncated before being returned to the parent.
47
+
48
+ ## UI
49
+
50
+ Two levels, toggled with `ctrl+o`:
51
+
52
+ - **Collapsed (default):** the tool call shows one line — `subagent <agent> <60-char task preview>`. The result block shows the agent header (status, tool count, duration), the chronological tool log (one line per call, running calls marked with `▸`), the latest prose "thinking" line, and a usage line (tokens in/out, cache, cost, context-window gauge).
53
+ - **Expanded:** the call header streams the full task body live as the parent writes it (like `write`/`edit`). The result block additionally renders the subagent's full final output as markdown. Nested children (when a worker spawns scout/researcher) render inline, indented under the row that dispatched them, with their own per-row context gauge.
54
+
55
+ ## Registering Agents from Other Extensions
56
+
57
+ Other extensions can dynamically register and unregister agents at runtime. This is useful for domain-specific agents that should only be available when a particular extension is active.
58
+
59
+ ### 1. Define agent `.md` files
60
+
61
+ Create markdown files with YAML frontmatter in your extension's directory (e.g. `my-extension/agents/my-agent.md`):
62
+
63
+ ```markdown
64
+ ---
65
+ name: my-agent
66
+ description: Does a specific thing
67
+ tools: web_search, video_extract
68
+ model: claude-sonnet-4-20250514
69
+ ---
70
+
71
+ You are an agent that does a specific thing...
72
+ ```
73
+
74
+ Frontmatter fields:
75
+ - **name** (required) — unique agent name, used in `{ agent: "my-agent" }` calls
76
+ - **description** — short description
77
+ - **tools** — comma-separated list of tools the agent needs (builtin or extension). Include `subagent` here to let this agent spawn other agents.
78
+ - **model** — provider-agnostic model identifier (e.g. `deepseek-v4-flash`). Pi resolves it from any registered provider that serves it.
79
+ - **thinking** — reasoning level: `off`, `low`, `medium`, `high` (defaults to `medium`)
80
+ - **subagent_agents** — if `subagent` is in `tools`, restrict which agents this one may spawn. Comma-separated list of agent names. Omit for no restriction. Enforced by passing `PI_SUBAGENT_ALLOWED` env to the child `pi` process — the child's subagents extension filters its registry before any tool description sees it, so the child LLM literally can't reference an agent outside the allowlist.
81
+
82
+ The markdown body becomes the agent's system prompt.
83
+
84
+ ### 2. Register agents via `globalThis.__pi_subagents`
85
+
86
+ Pi loads extensions via jiti, which creates separate module instances. Direct imports from the subagents extension will reference a different `agents` array than the one the `subagent` tool uses. Use the `globalThis` bridge instead:
87
+
88
+ ```typescript
89
+ import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
90
+ import * as fs from "node:fs";
91
+ import * as path from "node:path";
92
+
93
+ interface AgentConfig {
94
+ name: string;
95
+ description: string;
96
+ tools: string[];
97
+ model: string;
98
+ thinking: string; // "off" | "low" | "medium" | "high"
99
+ systemPrompt: string;
100
+ filePath: string;
101
+ subagentAgents?: string[]; // optional spawn-allowlist when `subagent` is in tools
102
+ }
103
+
104
+ const AGENTS_DIR = path.join(path.dirname(new URL(import.meta.url).pathname), "agents");
105
+
106
+ function registerMyAgents(): void {
107
+ const subagents = (globalThis as any).__pi_subagents as
108
+ | { registerAgent: (config: AgentConfig) => void; unregisterAgent: (name: string) => void }
109
+ | undefined;
110
+ if (!subagents) return; // subagents extension not loaded
111
+
112
+ for (const entry of fs.readdirSync(AGENTS_DIR)) {
113
+ if (!entry.endsWith(".md")) continue;
114
+ const filePath = path.join(AGENTS_DIR, entry);
115
+ const content = fs.readFileSync(filePath, "utf-8");
116
+ const { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);
117
+ if (!frontmatter.name) continue;
118
+
119
+ const tools = (frontmatter.tools || "").split(",").map(t => t.trim()).filter(Boolean);
120
+ const subagentAgents = frontmatter.subagent_agents
121
+ ? frontmatter.subagent_agents.split(",").map(t => t.trim()).filter(Boolean)
122
+ : undefined;
123
+ try {
124
+ subagents.registerAgent({
125
+ name: frontmatter.name,
126
+ description: frontmatter.description || "",
127
+ tools,
128
+ model: frontmatter.model || "anthropic/claude-sonnet-4-6",
129
+ thinking: frontmatter.thinking || "medium",
130
+ systemPrompt: body,
131
+ filePath,
132
+ ...(subagentAgents ? { subagentAgents } : {}),
133
+ });
134
+ } catch {
135
+ // Already registered — skip
136
+ }
137
+ }
138
+ }
139
+ ```
140
+
141
+ Call `registerMyAgents()` when your extension activates (e.g. in a command handler). The agents become available to the `subagent` tool immediately.
142
+
143
+ ### 3. Adding custom tool support
144
+
145
+ If your agents need tools beyond the built-in set, those tools must be mapped in the `CUSTOM_TOOL_EXTENSIONS` record in `subagents/index.ts`:
146
+
147
+ ```typescript
148
+ const CUSTOM_TOOL_EXTENSIONS: Record<string, string> = {
149
+ web_search: path.join(EXT_BASE, "web-search", "index.ts"),
150
+ web_fetch: path.join(EXT_BASE, "web-fetch", "index.ts"),
151
+ safe_bash: path.join(TOOLS_DIR, "safe-bash.ts"),
152
+ video_extract: path.join(EXT_BASE, "video-extract", "index.ts"),
153
+ youtube_search: path.join(EXT_BASE, "youtube-search", "index.ts"),
154
+ google_image_search: path.join(EXT_BASE, "google-image-search", "index.ts"),
155
+ };
156
+ ```
157
+
158
+ Built-in tools (`read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`) work automatically. Any other tool the agent lists in its frontmatter must have a corresponding entry here pointing to the extension's `index.ts`.
159
+
160
+ The `subagent` tool itself is listed in `CUSTOM_TOOL_EXTENSIONS` pointing back to this extension's own `index.ts` — that's how an agent like `worker` can recursively spawn other agents. Recursion is bounded only by each agent's `subagent_agents` allowlist (e.g. worker can spawn scout/researcher, neither of which declares the `subagent` tool, so the chain stops at depth 2).
161
+
162
+ ## Structure
163
+
164
+ ```
165
+ @rohaquinlop/pi-subagents/
166
+ ├── index.ts # Extension entry point
167
+ ├── agents/ # Built-in agent configs (frontmatter + system prompt)
168
+ ├── lib/ # Pure helper functions (agent discovery, frontmatter parsing)
169
+ └── tools/ # Extensions loaded into subagent processes
170
+ └── safe-bash.ts # bash with dangerous command blocking
171
+ ```
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: researcher
3
+ description: Web researcher — searches the web and synthesizes findings
4
+ tools: web_search, web_fetch
5
+ model: deepseek-v4-flash
6
+ thinking: medium
7
+ ---
8
+
9
+ You are a research specialist. Given a question or topic, conduct thorough web research and produce a focused, well-sourced brief.
10
+
11
+ Process:
12
+ 1. Break the question into 2-4 searchable facets
13
+ 2. Search with `web_search` using varied angles
14
+ 3. Read the answers. Identify what's well-covered, what has gaps.
15
+ 4. For the 2-3 most promising source URLs, use `web_fetch` to get full page content
16
+ 5. Synthesize everything into a brief that directly answers the question
17
+
18
+ Search strategy — always vary your angles:
19
+ - Direct answer query (the obvious one)
20
+ - Authoritative source query (official docs, specs, primary sources)
21
+ - Practical experience query (case studies, benchmarks, real-world usage)
22
+ - Recent developments query (only if the topic is time-sensitive)
23
+
24
+ Evaluation — what to keep vs drop:
25
+ - Official docs and primary sources outweigh blog posts and forum threads
26
+ - Recent sources outweigh stale ones
27
+ - Sources that directly address the question outweigh tangentially related ones
28
+ - Drop: SEO filler, outdated info, beginner tutorials (unless that's the audience)
29
+
30
+ If the first round of searches doesn't fully answer the question, search again with refined queries targeting the gaps.
31
+
32
+ Output format:
33
+
34
+ ## Summary
35
+ 2-3 sentence direct answer.
36
+
37
+ ## Findings
38
+ Numbered findings with inline source citations:
39
+ 1. **Finding** — explanation. [Source](url)
40
+ 2. **Finding** — explanation. [Source](url)
41
+
42
+ ## Sources
43
+ - Kept: Source Title (url) — why relevant
44
+ - Dropped: Source Title — why excluded
45
+
46
+ ## Gaps
47
+ What couldn't be answered. Suggested next steps.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: scout
3
+ description: Fast codebase recon — explores files, finds patterns, maps architecture
4
+ tools: read, grep, find, ls
5
+ model: deepseek-v4-flash
6
+ thinking: medium
7
+ ---
8
+
9
+ You are a scout agent. Quickly investigate a codebase and return structured findings.
10
+
11
+ Thoroughness (infer from task, default medium):
12
+ - Quick: Targeted lookups, key files only
13
+ - Medium: Follow imports, read critical sections
14
+ - Thorough: Trace all dependencies, check tests/types
15
+
16
+ Strategy:
17
+ 1. grep/find to locate relevant code
18
+ 2. Read key sections (not entire files)
19
+ 3. Identify types, interfaces, key functions
20
+ 4. Note dependencies between files
21
+
22
+ Output format:
23
+
24
+ ## Files Found
25
+ List with exact line ranges:
26
+ 1. `path/to/file.ts` (lines 10-50) — Description
27
+ 2. `path/to/other.ts` (lines 100-150) — Description
28
+
29
+ ## Key Code
30
+ Critical types, interfaces, or functions with actual code snippets.
31
+
32
+ ## Architecture
33
+ Brief explanation of how the pieces connect.
34
+
35
+ ## Start Here
36
+ Which file to look at first and why.
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: worker
3
+ description: General-purpose worker — reads, writes, and edits code
4
+ tools: read, write, edit, safe_bash, web_search, web_fetch, subagent
5
+ subagent_agents: scout, researcher
6
+ model: deepseek-v4-flash
7
+ thinking: medium
8
+ ---
9
+
10
+ You are a worker agent. You operate in an isolated context — you have no knowledge of any prior conversation.
11
+
12
+ Work autonomously to complete the assigned task. All necessary context will be provided in the task description.
13
+
14
+ Guidelines:
15
+ - Read files before editing to understand existing code
16
+ - Make targeted edits, not wholesale rewrites
17
+ - Use safe_bash for running commands (tests, builds, installs, etc.)
18
+ - If something fails, diagnose and fix it
19
+ - Report what you did and what changed when done
20
+
21
+ ## Delegation — protecting your context window
22
+
23
+ Your context is finite. Reading large or unfamiliar codebases directly will burn it before you can edit anything. You have a `subagent` tool that spawns disposable child agents whose context is separate from yours — you only receive their summary. Use it.
24
+
25
+ You can dispatch:
26
+ - **scout** — read-only recon (read, grep, find, ls). Returns a structured map of files, line ranges, and key snippets. Cheap (haiku). Use for *exploring unfamiliar territory*.
27
+ - **researcher** — web research (web_search, web_fetch). Returns a sourced brief. Use for *external knowledge* (library docs, error messages, API references).
28
+
29
+ ### When to dispatch a scout vs. read directly
30
+
31
+ Dispatch a scout when:
32
+ - The task brief names a feature/area but not specific files ("fix the auth flow", "add a field to user settings")
33
+ - You'd need to grep + read 5+ files just to orient
34
+ - You only need to know *where* something lives or *what shape* it has, not its full source
35
+
36
+ Read directly when:
37
+ - The brief gives you explicit file paths
38
+ - You already know the file you need to edit
39
+ - You need the exact bytes for an `edit` call (scouts return summaries, not verbatim source — re-read the 1–3 files you actually edit)
40
+
41
+ A good rhythm: **scout to find, read to edit.** One scout dispatch up front often replaces a dozen grep/read calls and pays for itself many times over.
42
+
43
+ ### When to dispatch a researcher vs. web_fetch directly
44
+
45
+ Dispatch a researcher when:
46
+ - The question is open-ended ("what's the idiomatic way to X in library Y")
47
+ - You'd need to search + read 3+ pages to triangulate
48
+ - You want sources synthesized, not raw HTML in your context
49
+
50
+ Fetch directly when:
51
+ - You already have the exact URL (a known docs page, a GitHub issue)
52
+ - You need a single specific piece of information from one page
53
+
54
+ ### Parallelism
55
+
56
+ If you need two independent investigations (e.g. "map the auth code" AND "look up the library's session API"), emit multiple `subagent` tool calls in the same turn — pi runs them in parallel automatically. Don't serialize independent work.
57
+
58
+ ### What a subagent doesn't replace
59
+
60
+ Subagents can't edit files for you. You still do the `edit`/`write` calls yourself, with the focused context the scouts gave you. Treat them as a context-protecting prefetch, not a substitute for thinking.
61
+
62
+ ## Output format when done
63
+
64
+ ## Changes Made
65
+ - `path/to/file.ts` — what changed and why
66
+
67
+ ## Verification
68
+ How you verified the changes work (tests run, build succeeded, etc.)
69
+
70
+ ## Notes
71
+ Any caveats, follow-up items, or decisions made.