oira666_pi-subagent 0.1.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/LICENSE +21 -0
- package/README.md +327 -0
- package/agents/code-architect.md +24 -0
- package/agents/code-reviwer.md +23 -0
- package/agents/code-writer.md +18 -0
- package/agents.ts +185 -0
- package/index.ts +865 -0
- package/package.json +68 -0
- package/render.ts +595 -0
- package/runner.ts +419 -0
- package/types.ts +135 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael Jakl
|
|
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,327 @@
|
|
|
1
|
+
# Pi Subagent
|
|
2
|
+
|
|
3
|
+
**Delegate tasks to specialized subagents with configurable context modes (`spawn` / `fork`).**
|
|
4
|
+
|
|
5
|
+
There are many subagent extensions for pi, this one is mine.
|
|
6
|
+
|
|
7
|
+
## Why Pi Subagent
|
|
8
|
+
|
|
9
|
+
**Specialization** — Use tailored agents for specific tasks like refactoring, documentation, or research.
|
|
10
|
+
|
|
11
|
+
**Context Control** — Choose `spawn` (fresh context) or `fork` (inherit current session context), depending on the task.
|
|
12
|
+
|
|
13
|
+
**Parallel Execution** — Run multiple agents at once.
|
|
14
|
+
|
|
15
|
+
**A Simpler Fork** — This extension intentionally keeps the surface area small and predictable compared to heavier implementations. It supports nested delegation with depth/cycle guards, but avoids broader scope-selection complexity. If you want the minimal, “just delegate” experience, this is it.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
### Option 1: Install from npm (recommended)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pi install npm:@mjakl/pi-subagent
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Option 2: Install via git
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pi install git:github.com/mjakl/pi-subagent
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Option 3: Manual Installation
|
|
32
|
+
|
|
33
|
+
Clone this repository to your Pi extensions directory:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cd ~/.pi/agent/extensions
|
|
37
|
+
git clone https://github.com/mjakl/pi-subagent.git
|
|
38
|
+
cd pi-subagent
|
|
39
|
+
npm install
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Configuration
|
|
43
|
+
|
|
44
|
+
### Delegation Guards (Depth + Cycle Prevention)
|
|
45
|
+
|
|
46
|
+
By default, this extension enforces two runtime guards:
|
|
47
|
+
|
|
48
|
+
1. **Depth guard** (`--subagent-max-depth`, default `3`)
|
|
49
|
+
- Main agent starts at depth `0`
|
|
50
|
+
- Delegation is allowed while `currentDepth < maxDepth`
|
|
51
|
+
- With default depth `3`: depth `0`, `1`, and `2` can delegate; depth `3` cannot
|
|
52
|
+
2. **Cycle guard** (`--subagent-prevent-cycles`, default `true`)
|
|
53
|
+
- Blocks delegating to any agent name already present in the current delegation stack
|
|
54
|
+
- Prevents self-recursion (`writer -> writer`) and loops (`planner -> reviewer -> planner`)
|
|
55
|
+
|
|
56
|
+
You can configure depth with either:
|
|
57
|
+
|
|
58
|
+
- CLI flag: `--subagent-max-depth <n>`
|
|
59
|
+
- Environment variable: `PI_SUBAGENT_MAX_DEPTH=<n>`
|
|
60
|
+
|
|
61
|
+
`n` must be a non-negative integer.
|
|
62
|
+
|
|
63
|
+
You can configure cycle prevention with either:
|
|
64
|
+
|
|
65
|
+
- CLI flag: `--subagent-prevent-cycles` / `--no-subagent-prevent-cycles`
|
|
66
|
+
- Environment variable: `PI_SUBAGENT_PREVENT_CYCLES=true|false`
|
|
67
|
+
|
|
68
|
+
Internal env vars managed by the extension and propagated to child processes:
|
|
69
|
+
|
|
70
|
+
- `PI_SUBAGENT_DEPTH`
|
|
71
|
+
- `PI_SUBAGENT_MAX_DEPTH`
|
|
72
|
+
- `PI_SUBAGENT_STACK` (JSON array of ancestor agent names, e.g. `["scout","planner"]`)
|
|
73
|
+
- `PI_SUBAGENT_PREVENT_CYCLES`
|
|
74
|
+
|
|
75
|
+
Examples:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Default behavior: depth 3 + cycle prevention enabled
|
|
79
|
+
pi
|
|
80
|
+
|
|
81
|
+
# Restrict to one nested level (main -> child -> grandchild)
|
|
82
|
+
pi --subagent-max-depth 2
|
|
83
|
+
|
|
84
|
+
# Disable subagent delegation entirely
|
|
85
|
+
pi --subagent-max-depth 0
|
|
86
|
+
|
|
87
|
+
# Allow depth 3 but disable cycle prevention (not recommended)
|
|
88
|
+
pi --subagent-max-depth 3 --no-subagent-prevent-cycles
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Tool Call Shape
|
|
92
|
+
|
|
93
|
+
`subagent` always accepts a top-level `tasks` array:
|
|
94
|
+
|
|
95
|
+
- One task = single-agent delegation
|
|
96
|
+
- Multiple tasks = parallel delegation
|
|
97
|
+
|
|
98
|
+
Single-task example:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{ "tasks": [{ "agent": "code-writer", "task": "Implement the API change" }], "mode": "spawn" }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Multi-task example:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{ "tasks": [{ "agent": "code-writer", "task": "Draft the implementation" }, { "agent": "code-reviwer", "task": "Review the plan" }], "mode": "fork" }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Each task item supports:
|
|
111
|
+
|
|
112
|
+
- `agent` — subagent name
|
|
113
|
+
- `task` — delegated task text
|
|
114
|
+
- `cwd` — optional working directory override for that task
|
|
115
|
+
|
|
116
|
+
### Parallel Execution Limits
|
|
117
|
+
|
|
118
|
+
For multi-task calls, two environment variables control fan-out:
|
|
119
|
+
|
|
120
|
+
- `PI_SUBAGENT_MAX_PARALLEL_TASKS` — maximum number of tasks allowed in one call (default: `16`)
|
|
121
|
+
- `PI_SUBAGENT_MAX_CONCURRENCY` — maximum number of subagents running at the same time inside that call (default: `8`)
|
|
122
|
+
|
|
123
|
+
`PI_SUBAGENT_MAX_CONCURRENCY` is effectively clamped to at least `1`.
|
|
124
|
+
|
|
125
|
+
### Project-local Agent Confirmation
|
|
126
|
+
|
|
127
|
+
Project-local agents from `.pi/agents/*.md` can be gated by `PI_SUBAGENT_CONFIRM_PROJECT_AGENTS`:
|
|
128
|
+
|
|
129
|
+
- `true`, `ask`, or `once` (default) — prompt with **Yes once**, **Yes for this session**, or **No**
|
|
130
|
+
- `false` or `never` — skip the prompt and allow project-local agents immediately
|
|
131
|
+
- `session` — allow project-local agents for the rest of the current session without prompting
|
|
132
|
+
|
|
133
|
+
If you choose **Yes for this session** in the UI, the choice is remembered and you will not be asked again in that session. In non-UI mode, `ask` blocks execution because the extension cannot prompt.
|
|
134
|
+
|
|
135
|
+
### Context Mode (`spawn` vs `fork`)
|
|
136
|
+
|
|
137
|
+
`subagent` supports a top-level `mode` switch:
|
|
138
|
+
|
|
139
|
+
- `spawn` (default) — Child receives only the task string (`Task: ...`). Best for isolated, reproducible work; typically lower token/cost and less context leakage.
|
|
140
|
+
- `fork` — Child receives a forked snapshot of the current session context **plus** the task string. Best for follow-up work that depends on prior context; typically higher token/cost and may include sensitive context.
|
|
141
|
+
|
|
142
|
+
Quick rule of thumb:
|
|
143
|
+
|
|
144
|
+
- Start with `spawn` for one-off tasks.
|
|
145
|
+
- Use `fork` when the delegated task depends on the current session's prior discussion, reads, or decisions.
|
|
146
|
+
|
|
147
|
+
Examples:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{ "tasks": [{ "agent": "code-writer", "task": "Implement the migration" }], "mode": "spawn" }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{ "tasks": [{ "agent": "code-reviwer", "task": "Double-check this migration" }], "mode": "fork" }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
If omitted, mode defaults to `spawn`.
|
|
158
|
+
|
|
159
|
+
### Subagent Definitions
|
|
160
|
+
|
|
161
|
+
Subagents are defined as Markdown files with YAML frontmatter.
|
|
162
|
+
|
|
163
|
+
**User Agents:** `~/.pi/agent/agents/*.md`
|
|
164
|
+
**Project Agents:** `.pi/agents/*.md`
|
|
165
|
+
**Bundled Fallback Agents:** `agents/code-writer.md`, `agents/code-reviwer.md`, `agents/code-architect.md`
|
|
166
|
+
|
|
167
|
+
The extension always loads user and project agents first. If a project agent shares a name with a user agent, the project agent wins. The bundled fallback agents are only discovered when no user or project agents are found at all. If you have any user or project agents configured, the bundled defaults are hidden and not discoverable. When project agents are requested, Pi can prompt for confirmation before running them, depending on `PI_SUBAGENT_CONFIRM_PROJECT_AGENTS`.
|
|
168
|
+
|
|
169
|
+
If nothing is configured yet, these fallback agents are available by default:
|
|
170
|
+
|
|
171
|
+
- `code-writer` — implementation and refactoring
|
|
172
|
+
- `code-reviwer` — code review and risk finding
|
|
173
|
+
- `code-architect` — technical design and approach selection
|
|
174
|
+
|
|
175
|
+
Example agent (`~/.pi/agent/agents/writer.md`):
|
|
176
|
+
|
|
177
|
+
```markdown
|
|
178
|
+
---
|
|
179
|
+
name: writer
|
|
180
|
+
description: Expert technical writer and editor
|
|
181
|
+
model: anthropic/claude-3-5-sonnet
|
|
182
|
+
tools: read, write
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
You are an expert technical writer. Your task is to improve the clarity and conciseness of the provided text.
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Note: this repository includes bundled fallback agents in `agents/code-writer.md`, `agents/code-reviwer.md`, and `agents/code-architect.md`.
|
|
189
|
+
|
|
190
|
+
### Frontmatter Fields
|
|
191
|
+
|
|
192
|
+
| Field | Required | Default | Description |
|
|
193
|
+
| ------------- | -------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
194
|
+
| `name` | Yes | — | Agent identifier used in tool calls (must match exactly) |
|
|
195
|
+
| `description` | Yes | — | What the agent does (shown to the main agent) |
|
|
196
|
+
| `model` | No | Uses the default pi model | Overrides the model for this agent. You can include a provider prefix (e.g. `anthropic/claude-3-5-sonnet` or `openrouter/claude-3.5-sonnet`) to force a specific provider. |
|
|
197
|
+
| `thinking` | No | Uses Pi's default thinking level | Sets the thinking level (`off`, `minimal`, `low`, `medium`, `high`, `xhigh`). Equivalent to `--thinking`. |
|
|
198
|
+
| `tools` | No | `read,bash,edit,write` | Comma-separated list of **built-in** tools to enable for this agent. If omitted, defaults apply. |
|
|
199
|
+
|
|
200
|
+
Notes:
|
|
201
|
+
|
|
202
|
+
- `model` accepts `provider/model` syntax — this is a Pi feature. Use it when multiple providers offer the same model ID.
|
|
203
|
+
- `thinking` uses the same values as Pi's `--thinking` flag; it's recommended to set it explicitly since thinking support varies by model.
|
|
204
|
+
- `tools` only controls built-in tools. Extension tools remain available unless extensions are disabled.
|
|
205
|
+
- The Markdown body below the frontmatter becomes the agent's system prompt and is **appended** to Pi's default system prompt (it does **not** replace it).
|
|
206
|
+
|
|
207
|
+
### Writing a Good Agent File
|
|
208
|
+
|
|
209
|
+
- **Description matters** — the main agent uses the `description` to decide which subagent to call, so be specific about what the agent is good at.
|
|
210
|
+
- **Tool scope is optional but helpful** — reducing tools can keep the agent focused, but you can leave defaults if unsure.
|
|
211
|
+
- **Model + thinking is the power combo** — selecting the right model and thinking level is often the biggest quality boost.
|
|
212
|
+
|
|
213
|
+
### Available Built-in Tools
|
|
214
|
+
|
|
215
|
+
Available Tools (default: `read`, `bash`, `edit`, `write`):
|
|
216
|
+
|
|
217
|
+
- `read` — Read file contents
|
|
218
|
+
- `bash` — Execute bash commands
|
|
219
|
+
- `edit` — Edit files with find/replace
|
|
220
|
+
- `write` — Write files (creates/overwrites)
|
|
221
|
+
- `grep` — Search file contents (read-only, off by default)
|
|
222
|
+
- `find` — Find files by glob pattern (read-only, off by default)
|
|
223
|
+
- `ls` — List directory contents (read-only, off by default)
|
|
224
|
+
|
|
225
|
+
Tip: for a read-only tool selection, use `read,find,ls,grep`. As soon as you include `edit`, `write`, or `bash`, the agent can practically go wild.
|
|
226
|
+
|
|
227
|
+
## How Communication Works
|
|
228
|
+
|
|
229
|
+
### The Isolation Model
|
|
230
|
+
|
|
231
|
+
Each subagent always runs in a **separate `pi` process**:
|
|
232
|
+
|
|
233
|
+
- ❌ No shared memory/state with the parent process
|
|
234
|
+
- ❌ No visibility into sibling subagents
|
|
235
|
+
- ✅ Its own model/tool/runtime loop
|
|
236
|
+
- ✅ Started with `PI_OFFLINE=1` to skip startup network operations and reduce spawn latency
|
|
237
|
+
|
|
238
|
+
What it can see depends on `mode`:
|
|
239
|
+
|
|
240
|
+
- `spawn` (default)
|
|
241
|
+
- ✅ Receives: subagent system prompt + `Task: ...`
|
|
242
|
+
- ❌ Does **not** receive parent session history
|
|
243
|
+
- `fork`
|
|
244
|
+
- ✅ Receives: forked snapshot of current parent session context + `Task: ...`
|
|
245
|
+
|
|
246
|
+
### What Gets Sent to Subagents
|
|
247
|
+
|
|
248
|
+
#### `spawn` mode (default)
|
|
249
|
+
|
|
250
|
+
`subagent({ tasks: [{ agent: "writer", task: "Document the API" }] })` sends:
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
[System Prompt from ~/.pi/agent/agents/writer.md]
|
|
254
|
+
|
|
255
|
+
User: Task: Document the API
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
No parent conversation history is included. In `spawn`, include all required context in `task`.
|
|
259
|
+
|
|
260
|
+
#### `fork` mode
|
|
261
|
+
|
|
262
|
+
`subagent({ tasks: [{ agent: "writer", task: "Document the API" }], mode: "fork" })` sends:
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
[Forked snapshot of current session context]
|
|
266
|
+
[System Prompt from ~/.pi/agent/agents/writer.md]
|
|
267
|
+
|
|
268
|
+
User: Task: Document the API
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Note: `fork` copies session context, not transient runtime-only prompt mutations from the parent process.
|
|
272
|
+
|
|
273
|
+
### What Comes Back to the Main Agent
|
|
274
|
+
|
|
275
|
+
| Data | Main Agent Sees | TUI Shows |
|
|
276
|
+
| --------------------------- | ------------------------ | ---------------------- |
|
|
277
|
+
| Final text output | ✅ Yes — full, unbounded | ✅ Yes |
|
|
278
|
+
| Tool calls made by subagent | ❌ No | ✅ Yes (expanded view) |
|
|
279
|
+
| Token usage / cost | ❌ No | ✅ Yes |
|
|
280
|
+
| Reasoning/thinking steps | ❌ No | ❌ No |
|
|
281
|
+
| Error messages | ✅ Yes (on failure) | ✅ Yes |
|
|
282
|
+
|
|
283
|
+
**Key point:** The main agent receives **only the final assistant text** from each subagent. Not the tool calls, not the reasoning, not the intermediate steps. This prevents context pollution while still giving you the results.
|
|
284
|
+
|
|
285
|
+
### Parallel Mode Behavior
|
|
286
|
+
|
|
287
|
+
When running multiple agents in parallel:
|
|
288
|
+
|
|
289
|
+
- Subagents run concurrently up to `PI_SUBAGENT_MAX_CONCURRENCY` (default `8`)
|
|
290
|
+
- The top-level `mode` applies to all tasks in that call
|
|
291
|
+
- Main agent receives a combined result after all finish:
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
Parallel: 3/3 succeeded
|
|
295
|
+
|
|
296
|
+
[writer] completed: Full output text here...
|
|
297
|
+
[tester] completed: Full output text here...
|
|
298
|
+
[reviewer] completed: Full output text here...
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Features
|
|
302
|
+
|
|
303
|
+
- **Auto-Discovery** — Agents are found at startup and their descriptions are injected into the main agent's system prompt.
|
|
304
|
+
- **Context Mode Switch** — `spawn` (fresh context) and `fork` (session snapshot + task) per call.
|
|
305
|
+
- **Depth + Cycle Guards** — Depth limiting and ancestry-cycle checks prevent runaway recursive delegation by default.
|
|
306
|
+
- **Streaming Updates** — Watch subagent progress in real-time as tool calls and outputs stream in.
|
|
307
|
+
- **Nested Delegation** — Subagents can call `subagent` again, subject to depth and cycle guards.
|
|
308
|
+
- **Rich TUI Rendering** — Collapsed/expanded views with usage stats, nested delegation trees, tool call previews, and markdown output.
|
|
309
|
+
- **Security Confirmation** — Project-local agents can require explicit user approval, with one-time and session-wide approval options.
|
|
310
|
+
|
|
311
|
+
## Project Structure
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
index.ts — Extension entry point: lifecycle hooks, tool registration, mode orchestration
|
|
315
|
+
agents.ts — Agent discovery: reads and parses .md files from user/project directories
|
|
316
|
+
runner.ts — Process runner: starts `pi` subprocesses in spawn/fork context modes and streams JSON events
|
|
317
|
+
render.ts — TUI rendering: renderCall and renderResult for the subagent tool
|
|
318
|
+
types.ts — Shared types and pure helper functions
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Attribution
|
|
322
|
+
|
|
323
|
+
Inspired by implementations from [vaayne/agent-kit](https://github.com/vaayne/agent-kit) and [mariozechner/pi-mono](https://github.com/badlogic/pi-mono).
|
|
324
|
+
|
|
325
|
+
## License
|
|
326
|
+
|
|
327
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-architect
|
|
3
|
+
description: Technical design agent for shaping implementations, APIs, module boundaries, and tradeoffs before coding. Use this agent for plans and architecture decisions.
|
|
4
|
+
thinking: high
|
|
5
|
+
tools: read,bash,grep,find,ls
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a senior software architect focused on practical design.
|
|
9
|
+
|
|
10
|
+
Your job is to propose implementation approaches that balance simplicity,
|
|
11
|
+
maintainability, extensibility, and delivery speed.
|
|
12
|
+
|
|
13
|
+
Guidelines:
|
|
14
|
+
- Start from the current codebase and constraints, not an idealized rewrite.
|
|
15
|
+
- Prefer simple designs with clear ownership and minimal moving parts.
|
|
16
|
+
- Call out tradeoffs, risks, migration concerns, and compatibility implications.
|
|
17
|
+
- Recommend concrete module boundaries, data flow, and rollout steps when useful.
|
|
18
|
+
- Avoid unnecessary abstraction.
|
|
19
|
+
|
|
20
|
+
In your final response:
|
|
21
|
+
- Present the recommended approach first.
|
|
22
|
+
- Include 1-2 viable alternatives when relevant.
|
|
23
|
+
- Explain why the recommendation fits this codebase.
|
|
24
|
+
- Highlight the biggest implementation risks or unknowns.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-reviwer
|
|
3
|
+
description: Code review specialist for finding bugs, regressions, edge cases, and maintainability issues. Use this agent to review code, plans, or patches.
|
|
4
|
+
thinking: high
|
|
5
|
+
tools: read,bash,grep,find,ls
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a skeptical, detail-oriented code reviewer.
|
|
9
|
+
|
|
10
|
+
Your goal is to identify the most important correctness, reliability, security,
|
|
11
|
+
and maintainability issues in the provided code or plan.
|
|
12
|
+
|
|
13
|
+
Guidelines:
|
|
14
|
+
- Prioritize concrete issues over stylistic preferences.
|
|
15
|
+
- Look for broken assumptions, missing edge-case handling, risky changes, and test gaps.
|
|
16
|
+
- Prefer concise findings with clear reasoning and likely impact.
|
|
17
|
+
- If the code looks good, say so explicitly instead of inventing problems.
|
|
18
|
+
- Do not edit files; focus on analysis and recommendations.
|
|
19
|
+
|
|
20
|
+
In your final response:
|
|
21
|
+
- List findings ordered by severity.
|
|
22
|
+
- Include file paths or symbols when possible.
|
|
23
|
+
- If there are no meaningful issues, say "No significant issues found" and mention any residual risks briefly.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-writer
|
|
3
|
+
description: Focused implementation agent for writing and refactoring code with small, reliable diffs. Use this agent when you want code changes made directly.
|
|
4
|
+
thinking: medium
|
|
5
|
+
tools: read,bash,edit,write
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are a pragmatic software engineer focused on implementation.
|
|
9
|
+
|
|
10
|
+
Your job is to turn requirements into small, correct code changes.
|
|
11
|
+
|
|
12
|
+
Guidelines:
|
|
13
|
+
- Read the relevant files before editing.
|
|
14
|
+
- Prefer minimal diffs that fit the existing style and architecture.
|
|
15
|
+
- Preserve working behavior unless the task explicitly changes it.
|
|
16
|
+
- When details are ambiguous, choose the simplest reasonable implementation and state your assumption.
|
|
17
|
+
- If helpful, run targeted commands to inspect the codebase or validate your changes.
|
|
18
|
+
- In your final response, summarize what you changed, note any assumptions, and mention any validation you performed.
|
package/agents.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent discovery and configuration.
|
|
3
|
+
*
|
|
4
|
+
* Agents are Markdown files with YAML frontmatter that define name, description,
|
|
5
|
+
* optional model/tools, and a system prompt body.
|
|
6
|
+
*
|
|
7
|
+
* Lookup locations:
|
|
8
|
+
* - User agents: ~/.pi/agent/agents/*.md
|
|
9
|
+
* - Project agents: .pi/agents/*.md (walks up from cwd)
|
|
10
|
+
* - Bundled agents: ./agents/*.md (fallback only when no user/project agents exist)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { parseFrontmatter } from "@mariozechner/pi-coding-agent";
|
|
14
|
+
import * as fs from "node:fs";
|
|
15
|
+
import * as os from "node:os";
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
export type AgentScope = "user" | "project" | "both";
|
|
20
|
+
export type AgentSource = "user" | "project" | "builtin";
|
|
21
|
+
|
|
22
|
+
export interface AgentConfig {
|
|
23
|
+
name: string;
|
|
24
|
+
description: string;
|
|
25
|
+
tools?: string[];
|
|
26
|
+
model?: string;
|
|
27
|
+
thinking?: string;
|
|
28
|
+
systemPrompt: string;
|
|
29
|
+
source: AgentSource;
|
|
30
|
+
filePath: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface AgentDiscoveryResult {
|
|
34
|
+
agents: AgentConfig[];
|
|
35
|
+
projectAgentsDir: string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const BUNDLED_AGENTS_DIR = path.join(
|
|
39
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
40
|
+
"agents",
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Internal helpers
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
function isDirectory(p: string): boolean {
|
|
48
|
+
try {
|
|
49
|
+
return fs.statSync(p).isDirectory();
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Walk up from `cwd` looking for a `.pi/agents` directory. */
|
|
56
|
+
function findNearestProjectAgentsDir(cwd: string): string | null {
|
|
57
|
+
let dir = cwd;
|
|
58
|
+
while (true) {
|
|
59
|
+
const candidate = path.join(dir, ".pi", "agents");
|
|
60
|
+
if (isDirectory(candidate)) return candidate;
|
|
61
|
+
const parent = path.dirname(dir);
|
|
62
|
+
if (parent === dir) return null;
|
|
63
|
+
dir = parent;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Parse a single agent markdown file into an AgentConfig. Returns null on skip. */
|
|
68
|
+
function parseAgentFile(filePath: string, source: AgentSource): AgentConfig | null {
|
|
69
|
+
let content: string;
|
|
70
|
+
try {
|
|
71
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let parsed: { frontmatter: Record<string, unknown>; body: string };
|
|
77
|
+
try {
|
|
78
|
+
parsed = parseFrontmatter<Record<string, unknown>>(content);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
81
|
+
console.warn(`[pi-subagent] Skipping invalid agent file "${filePath}": ${message}`);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const frontmatter = parsed.frontmatter ?? {};
|
|
86
|
+
const body = parsed.body ?? "";
|
|
87
|
+
|
|
88
|
+
const name = typeof frontmatter.name === "string" ? frontmatter.name.trim() : "";
|
|
89
|
+
const description = typeof frontmatter.description === "string" ? frontmatter.description.trim() : "";
|
|
90
|
+
if (!name || !description) return null;
|
|
91
|
+
|
|
92
|
+
let tools: string[] | undefined;
|
|
93
|
+
if (typeof frontmatter.tools === "string") {
|
|
94
|
+
const parsedTools = frontmatter.tools
|
|
95
|
+
.split(",")
|
|
96
|
+
.map((t) => t.trim())
|
|
97
|
+
.filter(Boolean);
|
|
98
|
+
if (parsedTools.length > 0) tools = parsedTools;
|
|
99
|
+
} else if (Array.isArray(frontmatter.tools)) {
|
|
100
|
+
const parsedTools = frontmatter.tools
|
|
101
|
+
.filter((t): t is string => typeof t === "string")
|
|
102
|
+
.map((t) => t.trim())
|
|
103
|
+
.filter(Boolean);
|
|
104
|
+
if (parsedTools.length > 0) tools = parsedTools;
|
|
105
|
+
} else if (frontmatter.tools !== undefined) {
|
|
106
|
+
console.warn(
|
|
107
|
+
`[pi-subagent] Ignoring invalid tools field in "${filePath}". Expected a comma-separated string or string array.`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
name,
|
|
113
|
+
description,
|
|
114
|
+
tools,
|
|
115
|
+
model: typeof frontmatter.model === "string" ? frontmatter.model : undefined,
|
|
116
|
+
thinking: typeof frontmatter.thinking === "string" ? frontmatter.thinking : undefined,
|
|
117
|
+
systemPrompt: body,
|
|
118
|
+
source,
|
|
119
|
+
filePath,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Load all agent definitions from a directory. */
|
|
124
|
+
function loadAgentsFromDir(dir: string, source: AgentSource): AgentConfig[] {
|
|
125
|
+
if (!fs.existsSync(dir)) return [];
|
|
126
|
+
|
|
127
|
+
let entries: fs.Dirent[];
|
|
128
|
+
try {
|
|
129
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
130
|
+
} catch {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
135
|
+
|
|
136
|
+
const agents: AgentConfig[] = [];
|
|
137
|
+
for (const entry of entries) {
|
|
138
|
+
if (!entry.name.endsWith(".md")) continue;
|
|
139
|
+
if (!entry.isFile() && !entry.isSymbolicLink()) continue;
|
|
140
|
+
|
|
141
|
+
const agent = parseAgentFile(path.join(dir, entry.name), source);
|
|
142
|
+
if (agent) agents.push(agent);
|
|
143
|
+
}
|
|
144
|
+
return agents;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function dedupeAgents(
|
|
148
|
+
userAgents: AgentConfig[],
|
|
149
|
+
projectAgents: AgentConfig[],
|
|
150
|
+
): AgentConfig[] {
|
|
151
|
+
const agentMap = new Map<string, AgentConfig>();
|
|
152
|
+
for (const agent of userAgents) agentMap.set(agent.name, agent);
|
|
153
|
+
for (const agent of projectAgents) agentMap.set(agent.name, agent);
|
|
154
|
+
return Array.from(agentMap.values());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// Public API
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Discover all available agents according to the requested scope.
|
|
163
|
+
*
|
|
164
|
+
* When scope is "both", project agents override user agents with the same name.
|
|
165
|
+
* If no user or project agents exist at all, bundled fallback agents are returned.
|
|
166
|
+
*/
|
|
167
|
+
export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
|
|
168
|
+
const userDir = path.join(os.homedir(), ".pi", "agent", "agents");
|
|
169
|
+
const projectAgentsDir = findNearestProjectAgentsDir(cwd);
|
|
170
|
+
|
|
171
|
+
const userAgents = loadAgentsFromDir(userDir, "user");
|
|
172
|
+
const projectAgents = projectAgentsDir ? loadAgentsFromDir(projectAgentsDir, "project") : [];
|
|
173
|
+
|
|
174
|
+
const hasConfiguredAgents = userAgents.length > 0 || projectAgents.length > 0;
|
|
175
|
+
if (!hasConfiguredAgents) {
|
|
176
|
+
return {
|
|
177
|
+
agents: loadAgentsFromDir(BUNDLED_AGENTS_DIR, "builtin"),
|
|
178
|
+
projectAgentsDir,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (scope === "user") return { agents: userAgents, projectAgentsDir };
|
|
183
|
+
if (scope === "project") return { agents: projectAgents, projectAgentsDir };
|
|
184
|
+
return { agents: dedupeAgents(userAgents, projectAgents), projectAgentsDir };
|
|
185
|
+
}
|