@bacnh85/pi-subagent 0.4.1 → 0.6.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 +56 -0
- package/README.md +139 -64
- package/agent-format.md +20 -5
- package/agents/reviewer.md +28 -22
- package/agents/scout.md +3 -3
- package/agents/worker.md +2 -2
- package/{agents.ts → extensions/agents.ts} +95 -11
- package/{index.ts → extensions/index.ts} +314 -265
- package/extensions/model.ts +86 -0
- package/extensions/package.json +3 -0
- package/{render.ts → extensions/render.ts} +9 -9
- package/{runner.ts → extensions/runner.ts} +97 -50
- package/extensions/security.ts +504 -0
- package/extensions/service.ts +90 -0
- package/{thread-viewer.ts → extensions/thread-viewer.ts} +2 -126
- package/{threads.ts → extensions/threads.ts} +1 -11
- package/package.json +47 -27
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.6.0 (2026-07-12)
|
|
4
|
+
|
|
5
|
+
### Security (breaking changes)
|
|
6
|
+
|
|
7
|
+
- **Project-agent confirmation removed from tool schema.** The `confirmProjectAgents` parameter is no longer exposed to the LLM. Project-agent approval is enforced via trusted configuration only. Interactive sessions prompt for confirmation; headless sessions fail closed unless `allowUnconfirmedProjectAgents` is explicitly enabled through trusted configuration (environment variable `PI_SUBAGENT_ALLOW_UNCONFIRMED_PROJECT_AGENTS=true` or pi settings).
|
|
8
|
+
|
|
9
|
+
- **Child working directories confined to the workspace.** Tool-specified `cwd` values are validated against the workspace root. Relative paths are resolved within the workspace; `..` traversal, absolute paths outside the workspace, and symlink escapes are rejected. A trusted `allowExternalCwd` setting (env `PI_SUBAGENT_ALLOW_EXTERNAL_CWD=true` or pi settings) can opt out.
|
|
10
|
+
|
|
11
|
+
- **Tool allowlist enforced.** Child agent tools are validated against a fixed allowlist: `read`, `grep`, `find`, `ls`, `bash`, `edit`, `write`. The `subagent` tool is always rejected. Unknown or misspelled tool names produce clear errors. Read-only service execution cannot gain `bash`, `edit`, or `write`.
|
|
12
|
+
|
|
13
|
+
- **Default timeout added.** Every child execution receives a default 10-minute timeout (`DEFAULT_TIMEOUT_MS`). Maximum allowed timeout is 60 minutes (`MAX_TIMEOUT_MS`). Timeout errors are distinguishable from parent cancellation.
|
|
14
|
+
|
|
15
|
+
### Reliability
|
|
16
|
+
|
|
17
|
+
- **Abort signal composition fixed.** `createCombinedAbortSignal()` correctly combines multiple abort signals with proper listener cleanup. Works without `AbortSignal.any()` via a manual fallback that removes all listeners after the first abort.
|
|
18
|
+
|
|
19
|
+
- **Parallel abort listeners cleaned up.** Parent-signal listeners attached during parallel execution are removed in a `finally` block after completion.
|
|
20
|
+
|
|
21
|
+
- **Canonical result status.** `SubAgentResult.status` classifies outcomes as `"success"`, `"partial"`, `"error"`, `"aborted"`, or `"timeout"`. Known Pi SDK stop reasons are classified explicitly; unknown reasons default conservatively to `"error"`.
|
|
22
|
+
|
|
23
|
+
- **Validation hardened.** Numeric and collection limits (timeout, max parallel tasks, concurrency, chain length, output cap, instructions length) are enforced at both schema and runtime levels.
|
|
24
|
+
|
|
25
|
+
- **Parallel result ordering preserved.** Results remain in input-task order regardless of completion order.
|
|
26
|
+
|
|
27
|
+
- **`abortOnFailure` behavior deterministic.** First canonical failure aborts running siblings; queued tasks never start; completed tasks retain their results.
|
|
28
|
+
|
|
29
|
+
### Agent discovery
|
|
30
|
+
|
|
31
|
+
- **Malformed agent files produce diagnostics.** Missing name, missing description, empty name, invalid model, invalid thinking levels, and unreadable files are reported with file path and severity. Valid agents continue to load.
|
|
32
|
+
|
|
33
|
+
### Packaging
|
|
34
|
+
|
|
35
|
+
- **Peer dependency ranges constrained.** `@earendil-works/pi-*` dependencies use `>=0.80.0 <0.81.0`; `typebox` uses `>=1.3.0 <2.0.0`.
|
|
36
|
+
|
|
37
|
+
- **Node engine requirement added.** `engines.node: ">=20.18"`.
|
|
38
|
+
|
|
39
|
+
- **Scripts fixed.** `npm test` uses locally installed `mocha` (no `npx`). Added `npm run check` (typecheck + test).
|
|
40
|
+
|
|
41
|
+
- **Package metadata updated.** `homepage` points to the package subdirectory.
|
|
42
|
+
|
|
43
|
+
- **`security.ts` added to published files.**
|
|
44
|
+
|
|
45
|
+
- **`CHANGELOG.md` added to published files.**
|
|
46
|
+
|
|
47
|
+
### Documentation
|
|
48
|
+
|
|
49
|
+
- **Security model section** added to README covering project-agent trust, cwd confinement, tool validation, timeout defaults, cancellation, result status, and compatibility.
|
|
50
|
+
|
|
51
|
+
### Backward compatibility
|
|
52
|
+
|
|
53
|
+
- `SubAgentResult.status` is a new field; existing consumers that ignore unknown fields remain compatible.
|
|
54
|
+
- Tool schema no longer accepts `confirmProjectAgents`; model-generated calls using it will be silently ignored (the field is fully removed from the schema, not just deprecated).
|
|
55
|
+
- Child `cwd` values that previously worked outside the workspace are now rejected unless the trusted `allowExternalCwd` setting is enabled.
|
|
56
|
+
- `combineAbortSignals()` is still exported from `runner.ts` but delegates to `createCombinedAbortSignal()` internally.
|
package/README.md
CHANGED
|
@@ -1,100 +1,175 @@
|
|
|
1
1
|
# pi-subagent
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **In-process execution**: Uses `createAgentSession()` directly — no `spawn("pi")` overhead
|
|
8
|
-
- **Minimal token budget**: Only the agent's system prompt (no AGENTS.md, no extensions, no thinking)
|
|
9
|
-
- **Streaming progress**: Real-time tool-call and text updates during execution
|
|
10
|
-
- **Three modes**: single, parallel (max 8 tasks, 4 concurrent), chain (sequential with `{previous}`)
|
|
11
|
-
- **Abort support**: Esc propagates to all sub-agents
|
|
12
|
-
- **TUI rendering**: Collapsed/expanded views with tool-call formatting and usage stats
|
|
13
|
-
- **Bundled agents**: scout, reviewer, worker — overridable with your own
|
|
14
|
-
- **Thread viewing**: `/agent` slash command to view subagent threads in isolation
|
|
3
|
+
Isolated in-process subagents for Pi. The `subagent` tool supports single, parallel (8 tasks, 4 concurrent), and chained execution; `/agent` opens inspectable child threads.
|
|
15
4
|
|
|
16
5
|
## Install
|
|
17
6
|
|
|
18
7
|
```bash
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
pi install ./extensions/pi-subagent
|
|
8
|
+
pi install npm:@bacnh85/pi-subagent
|
|
9
|
+
# local checkout
|
|
10
|
+
pi install ./pi-subagent
|
|
23
11
|
```
|
|
24
12
|
|
|
25
|
-
|
|
13
|
+
Requires Node.js >= 20.18.
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
## Bundled roles
|
|
16
|
+
|
|
17
|
+
| Role | Model | Thinking | Tools |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| `scout` | parent model | low | read, grep, find, ls |
|
|
20
|
+
| `reviewer` | parent model | high | read, grep, find, ls |
|
|
21
|
+
| `worker` | parent model | medium | read, bash, edit, write, grep, find, ls |
|
|
22
|
+
| `general-purpose` | parent model | off | read, bash, edit, write, grep, find, ls |
|
|
23
|
+
|
|
24
|
+
Bundled roles inherit the parent model so they work with the account already active in Pi. User/project agent files may override `model` and `thinking`.
|
|
25
|
+
|
|
26
|
+
## Agent files
|
|
27
|
+
|
|
28
|
+
Create `~/.pi/agent/agents/*.md` or `.pi/agents/*.md`:
|
|
29
|
+
|
|
30
|
+
```markdown
|
|
31
|
+
---
|
|
32
|
+
name: scout-fast
|
|
33
|
+
description: Locate relevant files and symbols
|
|
34
|
+
tools: read, grep, find, ls
|
|
35
|
+
model: optional-provider/optional-model
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
Return concise evidence with file/symbol anchors.
|
|
29
39
|
```
|
|
30
40
|
|
|
31
|
-
|
|
41
|
+
Agent definitions are cached with file-signature invalidation; `/subagent reload` clears the cache.
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
## Context and limits
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
Children use in-memory SDK sessions with no extensions, skills, prompt templates, or automatic `AGENTS.md` loading. The optional `instructions` argument passes a bounded 16 KB task/repository contract. Only Pi built-in tools are available; Serena, FFF, web, and Munin are not available in lean children.
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
2. Select a thread to view its full output (task, tool calls, final result, usage stats)
|
|
39
|
-
3. Within the viewer: `Esc` to close, `Alt+←`/`Alt+→` to cycle between threads, `↑/↓` to scroll
|
|
47
|
+
Threads are session-memory only and are cleared when Pi replaces or reloads the session. Timeout and parent cancellation propagate to child sessions. Subagents cannot recursively invoke `subagent`.
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
see all output at once, you can focus on one thread at a time.
|
|
49
|
+
## Security model
|
|
43
50
|
|
|
44
|
-
###
|
|
51
|
+
### Project-local agents
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
Agent files under `.pi/agents/` are controlled by the current repository. A project agent's system prompt may instruct a child to execute shell commands or modify files.
|
|
54
|
+
|
|
55
|
+
- **Project-agent approval cannot be disabled by the model.** The `confirmProjectAgents` parameter is not exposed in the tool schema. Confirmation policy comes from trusted user configuration only.
|
|
56
|
+
- **Interactive sessions** prompt the user before executing project agents.
|
|
57
|
+
- **Headless sessions fail closed.** Project agents are not executed without UI confirmation unless the trusted setting `allowUnconfirmedProjectAgents` is enabled (via `PI_SUBAGENT_ALLOW_UNCONFIRMED_PROJECT_AGENTS=true` environment variable or pi settings).
|
|
58
|
+
- **The extension service path** (`pi-subagent:run` event) follows the same policy.
|
|
59
|
+
|
|
60
|
+
### Child working directories
|
|
61
|
+
|
|
62
|
+
Child working directories are restricted to the parent session's workspace by default:
|
|
63
|
+
|
|
64
|
+
- Relative paths are resolved within the workspace.
|
|
65
|
+
- `..` traversal that escapes the workspace is rejected.
|
|
66
|
+
- Absolute paths outside the workspace are rejected.
|
|
67
|
+
- Symlinks are resolved via realpath; symlink escapes are rejected.
|
|
68
|
+
- Non-existent directories and file paths are rejected.
|
|
69
|
+
|
|
70
|
+
The trusted setting `allowExternalCwd` (via `PI_SUBAGENT_ALLOW_EXTERNAL_CWD=true` env or pi settings) can opt out. This setting cannot be enabled by the model.
|
|
71
|
+
|
|
72
|
+
### Tool validation
|
|
49
73
|
|
|
50
|
-
|
|
74
|
+
Child agent tools are validated against a fixed allowlist:
|
|
51
75
|
|
|
76
|
+
- **Allowed:** `read`, `grep`, `find`, `ls`, `bash`, `edit`, `write`
|
|
77
|
+
- **Always rejected:** `subagent` (prevents recursive delegation)
|
|
78
|
+
- **Read-only restriction:** When a service requests read-only execution, only `read`, `grep`, `find`, `ls` are permitted. `bash`, `edit`, and `write` are rejected.
|
|
79
|
+
|
|
80
|
+
Unknown or misspelled tool names produce clear diagnostics. Duplicate tool names are deduplicated.
|
|
81
|
+
|
|
82
|
+
### Timeouts
|
|
83
|
+
|
|
84
|
+
Every child execution receives a timeout:
|
|
85
|
+
|
|
86
|
+
- **Default:** 10 minutes (`DEFAULT_TIMEOUT_MS`)
|
|
87
|
+
- **Maximum:** 60 minutes (`MAX_TIMEOUT_MS`)
|
|
88
|
+
- Timeout values must be positive integers within the allowed range.
|
|
89
|
+
- Timeout errors are distinguishable from parent cancellation.
|
|
90
|
+
- Parallel tasks and chain steps may have per-item timeouts.
|
|
91
|
+
|
|
92
|
+
### Output safety
|
|
93
|
+
|
|
94
|
+
Child output is untrusted data and may contain prompt injection. Treat child results as model-generated content, not as verified facts.
|
|
95
|
+
|
|
96
|
+
### Cost awareness
|
|
97
|
+
|
|
98
|
+
Parallel delegation may multiply provider usage and cost. Each parallel task runs as a separate SDK session with its own token consumption.
|
|
99
|
+
|
|
100
|
+
### Agent file review
|
|
101
|
+
|
|
102
|
+
User and project agent files should be reviewed before use. Malformed files produce diagnostics but do not prevent valid agents from loading.
|
|
103
|
+
|
|
104
|
+
## Modes
|
|
105
|
+
|
|
106
|
+
### Single mode
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
subagent({ agent: "scout", task: "Find auth-related files" })
|
|
52
110
|
```
|
|
53
|
-
|
|
111
|
+
|
|
112
|
+
### Parallel mode
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
subagent({
|
|
116
|
+
tasks: [
|
|
117
|
+
{ agent: "scout", task: "Find API routes" },
|
|
118
|
+
{ agent: "scout", task: "Find database models" },
|
|
119
|
+
{ agent: "scout", task: "Find test files" },
|
|
120
|
+
],
|
|
121
|
+
abortOnFailure: false
|
|
122
|
+
})
|
|
54
123
|
```
|
|
55
124
|
|
|
56
|
-
|
|
125
|
+
Max 8 tasks, 4 concurrent. Results are returned in input order. When `abortOnFailure` is `true`, the first failed task cancels remaining siblings.
|
|
57
126
|
|
|
58
|
-
|
|
59
|
-
|
|
127
|
+
### Chain mode
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
subagent({
|
|
131
|
+
chain: [
|
|
132
|
+
{ agent: "scout", task: "Find API routes" },
|
|
133
|
+
{ agent: "worker", task: "Based on this, implement the routes: {previous}" },
|
|
134
|
+
]
|
|
135
|
+
})
|
|
60
136
|
```
|
|
61
137
|
|
|
62
|
-
|
|
138
|
+
`{previous}` in each step's task is replaced with the previous step's output. The chain stops on the first failed step.
|
|
63
139
|
|
|
64
|
-
|
|
65
|
-
|-------|-------|-------|---------|
|
|
66
|
-
| `scout` | Haiku | read, grep, find, ls | Fast codebase recon |
|
|
67
|
-
| `reviewer` | Sonnet | read, grep, find, ls, bash | Code review |
|
|
68
|
-
| `worker` | Sonnet | all | General implementation |
|
|
140
|
+
## Result status
|
|
69
141
|
|
|
70
|
-
|
|
142
|
+
Each `SubAgentResult` includes a canonical `status` field:
|
|
71
143
|
|
|
72
|
-
|
|
144
|
+
| Status | Meaning |
|
|
145
|
+
|--------|---------|
|
|
146
|
+
| `success` | Completed normally |
|
|
147
|
+
| `partial` | Truncated (max_tokens, length, context_limit) |
|
|
148
|
+
| `error` | Provider error, tool error, or unknown stop reason |
|
|
149
|
+
| `aborted` | Cancelled by parent or sibling |
|
|
150
|
+
| `timeout` | Exceeded the allowed timeout |
|
|
73
151
|
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
name: my-agent
|
|
77
|
-
description: When to use this agent
|
|
78
|
-
tools: read, grep, find, ls, bash
|
|
79
|
-
model: claude-haiku-4-5
|
|
80
|
-
---
|
|
152
|
+
The raw `stopReason` from the Pi SDK is preserved in the result.
|
|
81
153
|
|
|
82
|
-
|
|
83
|
-
```
|
|
154
|
+
## Timeout and cancellation
|
|
84
155
|
|
|
85
|
-
|
|
156
|
+
- **Default timeout:** 10 minutes per child.
|
|
157
|
+
- **Per-task/step override:** Use `timeout` in task/step params.
|
|
158
|
+
- **Parent cancellation:** Aborting the parent tool call cancels all children.
|
|
159
|
+
- **Sibling cancellation:** In parallel mode with `abortOnFailure: true`, the first failed task cancels running siblings.
|
|
160
|
+
- **Timeout vs. abort:** Timeout errors set `status: "timeout"` and `stopReason: "timeout"`; parent cancellation sets `status: "aborted"`.
|
|
86
161
|
|
|
87
|
-
##
|
|
162
|
+
## Extension contract
|
|
88
163
|
|
|
89
|
-
|
|
164
|
+
`pi-subagent` owns the `pi-subagent:run` event contract for one named-agent request. `pi-review` uses it for isolated review. Requests use an immediate boolean `accept()` claim and exactly one `respond()` callback; this suppresses duplicate responders while missing services and timeouts remain caller-controlled.
|
|
90
165
|
|
|
91
|
-
|
|
92
|
-
- Skipping AGENTS.md, extensions, skills, prompt templates
|
|
93
|
-
- Disabling thinking, compaction, retry
|
|
94
|
-
- Using in-memory sessions (no disk I/O)
|
|
95
|
-
- Sharing the parent's auth/model infrastructure
|
|
166
|
+
## Compatibility
|
|
96
167
|
|
|
97
|
-
|
|
168
|
+
- Requires `@earendil-works/pi-coding-agent >=0.80.0 <0.81.0`
|
|
169
|
+
- Requires `@earendil-works/pi-ai >=0.80.0 <0.81.0`
|
|
170
|
+
- Requires `@earendil-works/pi-agent-core >=0.80.0 <0.81.0`
|
|
171
|
+
- Requires `@earendil-works/pi-tui >=0.80.0 <0.81.0`
|
|
172
|
+
- Requires `typebox >=1.3.0 <2.0.0`
|
|
173
|
+
- Requires Node.js >= 20.18
|
|
98
174
|
|
|
99
|
-
|
|
100
|
-
- Peer dependencies satisfied by the pi runtime (no extra npm install needed)
|
|
175
|
+
See [`agent-format.md`](./agent-format.md) for all frontmatter fields.
|
package/agent-format.md
CHANGED
|
@@ -8,7 +8,7 @@ Sub-agents are defined as Markdown files with YAML frontmatter.
|
|
|
8
8
|
|----------|-------|
|
|
9
9
|
| `~/.pi/agent/agents/*.md` | User-level (all projects) |
|
|
10
10
|
| `.pi/agents/*.md` | Project-level |
|
|
11
|
-
| `<
|
|
11
|
+
| `<package>/agents/*.md` | Bundled with pi-subagent |
|
|
12
12
|
|
|
13
13
|
Project agents override user agents with the same name when `agentScope: "both"`.
|
|
14
14
|
|
|
@@ -19,7 +19,8 @@ Project agents override user agents with the same name when `agentScope: "both"`
|
|
|
19
19
|
name: my-agent # Required. Unique identifier (kebab-case).
|
|
20
20
|
description: ... # Required. When to use this agent.
|
|
21
21
|
tools: read, grep, ... # Optional. Comma-separated tool names. Defaults to all.
|
|
22
|
-
model:
|
|
22
|
+
model: provider/model # Optional. Defaults to parent's model.
|
|
23
|
+
thinking: low # Optional: off|minimal|low|medium|high|xhigh|max.
|
|
23
24
|
---
|
|
24
25
|
```
|
|
25
26
|
|
|
@@ -31,12 +32,22 @@ The body after frontmatter becomes the agent's **entire system prompt**. No pi d
|
|
|
31
32
|
|
|
32
33
|
## Available Tools
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
Child agent tools are validated against a fixed allowlist:
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
| Category | Tools |
|
|
38
|
+
|----------|-------|
|
|
39
|
+
| Read-only | `read`, `grep`, `find`, `ls` |
|
|
40
|
+
| Mutation | `edit`, `write` |
|
|
41
|
+
| Execution | `bash` |
|
|
42
|
+
|
|
43
|
+
The `subagent` tool is always rejected to prevent recursive delegation.
|
|
44
|
+
Unknown or misspelled tool names produce a clear error.
|
|
45
|
+
Duplicate names are deduplicated automatically.
|
|
37
46
|
|
|
38
47
|
Custom/extension tools are NOT available to sub-agents by default (each runs in an isolated in-memory session with no extensions).
|
|
39
48
|
|
|
49
|
+
Read-only service execution (used by `pi-review`) restricts tools to the read-only category.
|
|
50
|
+
|
|
40
51
|
## Model Resolution
|
|
41
52
|
|
|
42
53
|
Model IDs are resolved via `getModel("provider", "id")`. Common values:
|
|
@@ -47,13 +58,17 @@ Model IDs are resolved via `getModel("provider", "id")`. Common values:
|
|
|
47
58
|
|
|
48
59
|
If not specified, defaults to the parent session's model.
|
|
49
60
|
|
|
61
|
+
## Instruction handoff
|
|
62
|
+
|
|
63
|
+
Children do not automatically load repository instructions. Callers may pass an `instructions` task contract, truncated to 16 KB. Use this for relevant repository rules or review contracts rather than copying the parent transcript.
|
|
64
|
+
|
|
50
65
|
## Token Budget
|
|
51
66
|
|
|
52
67
|
Each sub-agent runs with:
|
|
53
68
|
- **System prompt**: agent body only (~200-1K tokens typical)
|
|
54
69
|
- **No AGENTS.md**: saves 500-5K tokens
|
|
55
70
|
- **No extensions/skills loaded**: saves 200-1K tokens
|
|
56
|
-
- **Thinking
|
|
71
|
+
- **Thinking per role**: defaults off; bundled scout/reviewer/worker choose low/high/medium
|
|
57
72
|
- **No compaction**: avoids compaction token cost
|
|
58
73
|
|
|
59
74
|
This is ~10x leaner than spawning a full `pi` process.
|
package/agents/reviewer.md
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: reviewer
|
|
3
|
-
description: Code review specialist. Use for
|
|
4
|
-
tools: read, grep, find, ls
|
|
5
|
-
|
|
3
|
+
description: Code review specialist. Use for correctness, security, regression, and meaningful test-gap review.
|
|
4
|
+
tools: read, grep, find, ls
|
|
5
|
+
thinking: high
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
You are
|
|
8
|
+
You are an independent senior code reviewer. Inspect the requested Git scope with read-only tools.
|
|
9
9
|
|
|
10
|
-
Focus on:
|
|
11
|
-
1.
|
|
12
|
-
2.
|
|
13
|
-
3.
|
|
14
|
-
4.
|
|
15
|
-
5. **Best practices**: Idiomatic patterns, testability, documentation
|
|
10
|
+
Focus only on actionable issues introduced by the reviewed change:
|
|
11
|
+
1. Correctness and edge cases
|
|
12
|
+
2. Security and data loss
|
|
13
|
+
3. Regressions and API compatibility
|
|
14
|
+
4. Missing tests that allow a likely bug to escape
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
Avoid style noise, praise, and speculative redesign. Every finding needs code evidence.
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
Return JSON only:
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"summary": "compact scope/result summary",
|
|
22
|
+
"findings": [
|
|
23
|
+
{
|
|
24
|
+
"severity": "critical|high|medium|low",
|
|
25
|
+
"file": "relative/path",
|
|
26
|
+
"line": 1,
|
|
27
|
+
"issue": "what is wrong and why it matters",
|
|
28
|
+
"evidence": "specific inspected code evidence",
|
|
29
|
+
"suggestedFix": "smallest safe fix",
|
|
30
|
+
"blocking": true
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
21
35
|
|
|
22
|
-
|
|
23
|
-
For each issue:
|
|
24
|
-
- **Severity**: critical | high | medium | low
|
|
25
|
-
- **File**: path with line numbers
|
|
26
|
-
- **Problem**: What's wrong
|
|
27
|
-
- **Fix**: Suggested change (code block)
|
|
28
|
-
|
|
29
|
-
## Overall Assessment
|
|
30
|
-
Green/yellow/red with reasoning.
|
|
36
|
+
Use an empty `findings` array when clean. Do not modify files or Git state.
|
package/agents/scout.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: scout
|
|
3
3
|
description: Fast codebase recon that returns compressed context for handoff. Use for finding files, understanding structure, locating symbols.
|
|
4
4
|
tools: read, grep, find, ls
|
|
5
|
-
|
|
5
|
+
thinking: low
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
You are a scout. Quickly investigate a codebase and return structured findings that another agent can use without re-reading everything.
|
|
@@ -22,8 +22,8 @@ Strategy:
|
|
|
22
22
|
|
|
23
23
|
Output format:
|
|
24
24
|
|
|
25
|
-
##
|
|
26
|
-
List
|
|
25
|
+
## Evidence
|
|
26
|
+
List exact file/symbol anchors and relevant line ranges:
|
|
27
27
|
1. `path/to/file.ts` (lines 10-50) - Description of what's here
|
|
28
28
|
2. `path/to/other.ts` (lines 100-150) - Description
|
|
29
29
|
3. ...
|
package/agents/worker.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: worker
|
|
3
|
-
description: General-purpose coding agent with full tool access. Use
|
|
4
|
-
|
|
3
|
+
description: General-purpose coding agent with full tool access. Use only when explicitly requested for isolated implementation.
|
|
4
|
+
thinking: medium
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
You are a skilled software engineer. Implement the requested task with care and precision.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent discovery and configuration for pi-
|
|
2
|
+
* Agent discovery and configuration for pi-subagent.
|
|
3
3
|
*
|
|
4
4
|
* Loads agent definitions from Markdown files with YAML frontmatter.
|
|
5
5
|
* Discovers from user-level (~/.pi/agent/agents/), project-level
|
|
@@ -18,6 +18,7 @@ export interface AgentConfig {
|
|
|
18
18
|
description: string;
|
|
19
19
|
tools?: string[];
|
|
20
20
|
model?: string;
|
|
21
|
+
thinking?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
21
22
|
systemPrompt: string;
|
|
22
23
|
source: "user" | "project" | "bundled";
|
|
23
24
|
filePath: string;
|
|
@@ -26,6 +27,14 @@ export interface AgentConfig {
|
|
|
26
27
|
export interface AgentDiscoveryResult {
|
|
27
28
|
agents: AgentConfig[];
|
|
28
29
|
projectAgentsDir: string | null;
|
|
30
|
+
diagnostics: AgentDiscoveryDiagnostic[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface AgentDiscoveryDiagnostic {
|
|
34
|
+
filePath: string;
|
|
35
|
+
issue: string;
|
|
36
|
+
/** 'warn' for recoverable issues, 'error' for file-skip issues. */
|
|
37
|
+
severity: "warn" | "error";
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
interface AgentCache {
|
|
@@ -46,7 +55,11 @@ export function invalidateAgentCache(): void {
|
|
|
46
55
|
_cache = null;
|
|
47
56
|
}
|
|
48
57
|
|
|
49
|
-
function loadAgentsFromDir(
|
|
58
|
+
function loadAgentsFromDir(
|
|
59
|
+
dir: string,
|
|
60
|
+
source: "user" | "project" | "bundled",
|
|
61
|
+
diagnostics: AgentDiscoveryDiagnostic[],
|
|
62
|
+
): AgentConfig[] {
|
|
50
63
|
const agents: AgentConfig[] = [];
|
|
51
64
|
|
|
52
65
|
if (!fs.existsSync(dir)) return agents;
|
|
@@ -54,25 +67,72 @@ function loadAgentsFromDir(dir: string, source: "user" | "project" | "bundled"):
|
|
|
54
67
|
let entries: fs.Dirent[];
|
|
55
68
|
try {
|
|
56
69
|
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
57
|
-
} catch {
|
|
70
|
+
} catch (err) {
|
|
71
|
+
diagnostics.push({
|
|
72
|
+
filePath: dir,
|
|
73
|
+
issue: `Cannot read directory: ${err instanceof Error ? err.message : String(err)}`,
|
|
74
|
+
severity: "warn",
|
|
75
|
+
});
|
|
58
76
|
return agents;
|
|
59
77
|
}
|
|
60
78
|
|
|
61
79
|
for (const entry of entries) {
|
|
62
80
|
if (!entry.name.endsWith(".md")) continue;
|
|
63
|
-
if (!entry.isFile() && !entry.isSymbolicLink())
|
|
81
|
+
if (!entry.isFile() && !entry.isSymbolicLink()) {
|
|
82
|
+
diagnostics.push({
|
|
83
|
+
filePath: path.join(dir, entry.name),
|
|
84
|
+
issue: `Not a regular file or symlink, skipping.`,
|
|
85
|
+
severity: "warn",
|
|
86
|
+
});
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
64
89
|
|
|
65
90
|
const filePath = path.join(dir, entry.name);
|
|
66
91
|
let content: string;
|
|
67
92
|
try {
|
|
68
93
|
content = fs.readFileSync(filePath, "utf-8");
|
|
69
|
-
} catch {
|
|
94
|
+
} catch (err) {
|
|
95
|
+
diagnostics.push({
|
|
96
|
+
filePath,
|
|
97
|
+
issue: `Cannot read file: ${err instanceof Error ? err.message : String(err)}`,
|
|
98
|
+
severity: "error",
|
|
99
|
+
});
|
|
70
100
|
continue;
|
|
71
101
|
}
|
|
72
102
|
|
|
73
103
|
const { frontmatter, body } = parseFrontmatter<Record<string, unknown>>(content);
|
|
74
104
|
|
|
75
|
-
if (typeof frontmatter.name !== "string" || typeof frontmatter.description !== "string")
|
|
105
|
+
if (typeof frontmatter.name !== "string" || typeof frontmatter.description !== "string") {
|
|
106
|
+
if (typeof frontmatter.name !== "string" && typeof frontmatter.description !== "string") {
|
|
107
|
+
diagnostics.push({
|
|
108
|
+
filePath,
|
|
109
|
+
issue: `Missing both "name" and "description" in frontmatter. Agent file skipped.`,
|
|
110
|
+
severity: "error",
|
|
111
|
+
});
|
|
112
|
+
} else if (typeof frontmatter.name !== "string") {
|
|
113
|
+
diagnostics.push({
|
|
114
|
+
filePath,
|
|
115
|
+
issue: `Missing "name" in frontmatter. Agent file skipped.`,
|
|
116
|
+
severity: "error",
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
diagnostics.push({
|
|
120
|
+
filePath,
|
|
121
|
+
issue: `Missing "description" in frontmatter. Agent file skipped.`,
|
|
122
|
+
severity: "error",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!frontmatter.name.trim()) {
|
|
129
|
+
diagnostics.push({
|
|
130
|
+
filePath,
|
|
131
|
+
issue: `"name" in frontmatter is empty. Agent file skipped.`,
|
|
132
|
+
severity: "error",
|
|
133
|
+
});
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
76
136
|
|
|
77
137
|
const tools =
|
|
78
138
|
typeof frontmatter.tools === "string"
|
|
@@ -81,11 +141,33 @@ function loadAgentsFromDir(dir: string, source: "user" | "project" | "bundled"):
|
|
|
81
141
|
? (frontmatter.tools as unknown[]).filter((t): t is string => typeof t === "string")
|
|
82
142
|
: undefined;
|
|
83
143
|
|
|
144
|
+
if (typeof frontmatter.model === "string" && frontmatter.model && !frontmatter.model.includes("/")) {
|
|
145
|
+
diagnostics.push({
|
|
146
|
+
filePath,
|
|
147
|
+
issue: `Model "${frontmatter.model}" does not include a provider prefix (e.g., "anthropic/claude-sonnet-4-20250514"). Resolution may fail.`,
|
|
148
|
+
severity: "warn",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (typeof frontmatter.thinking === "string" && frontmatter.thinking) {
|
|
153
|
+
const validLevels = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
154
|
+
if (!validLevels.includes(frontmatter.thinking)) {
|
|
155
|
+
diagnostics.push({
|
|
156
|
+
filePath,
|
|
157
|
+
issue: `Invalid thinking level "${frontmatter.thinking}". Valid values: ${validLevels.join(", ")}. Using default.`,
|
|
158
|
+
severity: "warn",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
84
163
|
agents.push({
|
|
85
164
|
name: frontmatter.name,
|
|
86
165
|
description: frontmatter.description,
|
|
87
166
|
tools: tools && tools.length > 0 ? tools : undefined,
|
|
88
167
|
model: typeof frontmatter.model === "string" ? frontmatter.model : undefined,
|
|
168
|
+
thinking: typeof frontmatter.thinking === "string" && ["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(frontmatter.thinking)
|
|
169
|
+
? frontmatter.thinking as AgentConfig["thinking"]
|
|
170
|
+
: undefined,
|
|
89
171
|
systemPrompt: body,
|
|
90
172
|
source,
|
|
91
173
|
filePath,
|
|
@@ -165,16 +247,18 @@ export function discoverAgents(
|
|
|
165
247
|
}
|
|
166
248
|
}
|
|
167
249
|
if (!stale) {
|
|
168
|
-
return { agents: _cache.agents, projectAgentsDir: _cache.projectAgentsDir };
|
|
250
|
+
return { agents: _cache.agents, projectAgentsDir: _cache.projectAgentsDir, diagnostics: [] };
|
|
169
251
|
}
|
|
170
252
|
// Cache is stale — rebuild below
|
|
171
253
|
_cache = null;
|
|
172
254
|
}
|
|
173
255
|
|
|
174
|
-
const
|
|
256
|
+
const diagnostics: AgentDiscoveryDiagnostic[] = [];
|
|
257
|
+
|
|
258
|
+
const userAgents = scope === "project" ? [] : loadAgentsFromDir(userDir, "user", diagnostics);
|
|
175
259
|
const projectAgents =
|
|
176
|
-
scope === "user" || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, "project");
|
|
177
|
-
const bundledAgents = loadAgentsFromDir(bundledAgentsDir, "bundled");
|
|
260
|
+
scope === "user" || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, "project", diagnostics);
|
|
261
|
+
const bundledAgents = loadAgentsFromDir(bundledAgentsDir, "bundled", diagnostics);
|
|
178
262
|
|
|
179
263
|
const agentMap = new Map<string, AgentConfig>();
|
|
180
264
|
|
|
@@ -205,7 +289,7 @@ export function discoverAgents(
|
|
|
205
289
|
dirSignatures,
|
|
206
290
|
};
|
|
207
291
|
|
|
208
|
-
return { agents, projectAgentsDir };
|
|
292
|
+
return { agents, projectAgentsDir, diagnostics };
|
|
209
293
|
}
|
|
210
294
|
|
|
211
295
|
export function formatAgentList(agents: AgentConfig[], maxItems: number): { text: string; remaining: number } {
|