pi-maestro-teammate 0.2.0 → 0.4.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/README.md +207 -86
- package/agents/coordinator.md +14 -7
- package/package.json +16 -9
- package/src/agents/agents.ts +39 -1
- package/src/extension/index.ts +2394 -465
- package/src/extension/schemas.ts +223 -165
- package/src/extension/structured-output.ts +48 -0
- package/src/runs/execution.ts +1256 -652
- package/src/runs/session-handoff.ts +144 -0
- package/src/shared/types.ts +116 -70
- package/src/tui/attach-overlay.ts +620 -0
- package/src/tui/progress-tree.ts +99 -0
- package/src/tui/render.ts +338 -89
package/README.md
CHANGED
|
@@ -1,52 +1,8 @@
|
|
|
1
1
|
# pi-teammate
|
|
2
2
|
|
|
3
|
-
> Teammate dispatch tool for [Pi](https://github.com/earendil-works/pi) —
|
|
3
|
+
> Teammate dispatch tool for [Pi](https://github.com/earendil-works/pi) — unified TaskSpec with DAG variable referencing + resident agent model
|
|
4
4
|
|
|
5
|
-
Pi extension implementing teammate dispatch with **
|
|
6
|
-
|
|
7
|
-
## Features
|
|
8
|
-
|
|
9
|
-
### Three-Axis Control
|
|
10
|
-
|
|
11
|
-
| Axis | Field | Values | Purpose |
|
|
12
|
-
|------|-------|--------|---------|
|
|
13
|
-
| Addressability | `name` | string \| omit | Cross-agent routing via name |
|
|
14
|
-
| Result Routing | `reply_to` | `"caller"` \| `"main"` | Where results go |
|
|
15
|
-
| Lifecycle | `lifecycle` | `"ephemeral"` \| `"resident"` | One-shot or persistent |
|
|
16
|
-
|
|
17
|
-
**Protocol version gate** — v2 (default) routes results to `caller`; v1 compat routes named agents to `main`. Explicit `reply_to` always wins.
|
|
18
|
-
|
|
19
|
-
### Execution Modes
|
|
20
|
-
|
|
21
|
-
- **Single** — dispatch one agent with a task
|
|
22
|
-
- **Parallel** — `tasks[]` runs multiple agents concurrently with configurable `concurrency`
|
|
23
|
-
- **Chain** — `chain[]` sequential pipeline where each step receives `{previous}` result
|
|
24
|
-
|
|
25
|
-
### Reliability
|
|
26
|
-
|
|
27
|
-
- **Model fallback chain** — primary model → `fallbackModels[]` from agent config → automatic retry on model failures
|
|
28
|
-
- **Nesting depth guard** — `PI_TEAMMATE_DEPTH` env tracking with configurable max (default: 3) prevents fork bombs
|
|
29
|
-
- **Windows-safe pi resolution** — `getPiSpawnCommand()` resolves the pi binary via env override, Windows script detection, or PATH
|
|
30
|
-
- **Abort signal** — SIGTERM → 5s grace → SIGKILL
|
|
31
|
-
|
|
32
|
-
### Output & Tracking
|
|
33
|
-
|
|
34
|
-
- **Structured output** — `outputSchema` validates child output against JSON Schema, returns parsed `structuredOutput`
|
|
35
|
-
- **Rich progress** — `AgentProgress` with `recentTools[]`, `toolCount`, `tokens`, `durationMs`, `lastActivityAt`
|
|
36
|
-
- **Session management** — derives child session directory from parent session, supports `context: "fork"`
|
|
37
|
-
- **Correlation ID** — auto-generated per dispatch for result routing
|
|
38
|
-
|
|
39
|
-
### Agent Definitions
|
|
40
|
-
|
|
41
|
-
Agents are markdown files with YAML frontmatter — discovered from project (`.pi/agents/`), user (`~/.pi/agent/extensions/teammate/agents/`), or builtin locations. Project overrides user overrides builtin.
|
|
42
|
-
|
|
43
|
-
## Install
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
pi install npm:@pi-maestro/teammate
|
|
47
|
-
# or from local path
|
|
48
|
-
pi install ./pi-teammate
|
|
49
|
-
```
|
|
5
|
+
Pi extension implementing teammate dispatch with **unified TaskSpec model**. Single agent, parallel fan-out, sequential chains, and arbitrary DAGs all use the same schema — execution order is determined by `{name}` variable references between tasks.
|
|
50
6
|
|
|
51
7
|
## Quick Start
|
|
52
8
|
|
|
@@ -56,7 +12,7 @@ pi install ./pi-teammate
|
|
|
56
12
|
{ agent: "delegate", task: "Implement the auth middleware" }
|
|
57
13
|
```
|
|
58
14
|
|
|
59
|
-
### Parallel
|
|
15
|
+
### Parallel (no references = concurrent)
|
|
60
16
|
|
|
61
17
|
```
|
|
62
18
|
{ tasks: [
|
|
@@ -68,22 +24,34 @@ pi install ./pi-teammate
|
|
|
68
24
|
}
|
|
69
25
|
```
|
|
70
26
|
|
|
71
|
-
### Chain
|
|
27
|
+
### Chain (linear references = sequential)
|
|
72
28
|
|
|
73
29
|
```
|
|
74
|
-
{
|
|
75
|
-
{ agent: "scout", task: "Find the auth module structure" },
|
|
76
|
-
{ agent: "delegate", task: "Based on this context: {
|
|
30
|
+
{ tasks: [
|
|
31
|
+
{ agent: "scout", name: "recon", task: "Find the auth module structure" },
|
|
32
|
+
{ agent: "delegate", task: "Based on this context: {recon}\n\nRefactor the auth module" }
|
|
77
33
|
]
|
|
78
34
|
}
|
|
79
35
|
```
|
|
80
36
|
|
|
81
|
-
###
|
|
37
|
+
### DAG (mixed references = auto-scheduling)
|
|
82
38
|
|
|
83
39
|
```
|
|
84
|
-
{
|
|
40
|
+
{ tasks: [
|
|
41
|
+
{ agent: "scout", name: "api", task: "List all API routes",
|
|
42
|
+
outputSchema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: { routes: { type: "array", items: { type: "string" } } },
|
|
45
|
+
required: ["routes"]
|
|
46
|
+
} },
|
|
47
|
+
{ agent: "scout", name: "db", task: "Map the database schema" },
|
|
48
|
+
{ agent: "reviewer", task: "Routes: {api.routes}\nDB: {db}\n\nCheck consistency" }
|
|
49
|
+
]
|
|
50
|
+
}
|
|
85
51
|
```
|
|
86
52
|
|
|
53
|
+
`api` and `db` run in parallel. `reviewer` waits for both, with `{api.routes}` resolved from structured output and `{db}` from text output.
|
|
54
|
+
|
|
87
55
|
### Structured Output
|
|
88
56
|
|
|
89
57
|
```
|
|
@@ -96,6 +64,186 @@ pi install ./pi-teammate
|
|
|
96
64
|
}
|
|
97
65
|
```
|
|
98
66
|
|
|
67
|
+
In multi-task mode, structured outputs are aggregated by task `name` in the result's `structuredOutput` field.
|
|
68
|
+
|
|
69
|
+
## Core Concepts
|
|
70
|
+
|
|
71
|
+
### Two Rules
|
|
72
|
+
|
|
73
|
+
1. **Reference = dependency**: `{name}` in a task's description means "wait for the task named `name` to complete, then inject its output here"
|
|
74
|
+
2. **No reference = parallel**: tasks with no dependencies run concurrently (bounded by `concurrency`)
|
|
75
|
+
|
|
76
|
+
No `mode` field needed — the execution engine infers parallel, chain, or graph from the reference topology.
|
|
77
|
+
|
|
78
|
+
### Variable References
|
|
79
|
+
|
|
80
|
+
| Syntax | Resolves to |
|
|
81
|
+
|--------|-------------|
|
|
82
|
+
| `{name}` | Full text output; or JSON string if the task has `outputSchema` |
|
|
83
|
+
| `{name.field}` | Field from structured output |
|
|
84
|
+
| `{name.arr[0].path}` | Nested field with array indexing |
|
|
85
|
+
|
|
86
|
+
Only tasks with a `name` field can be referenced. Non-task `{braces}` (JSON, format strings) are left untouched.
|
|
87
|
+
|
|
88
|
+
### Default Inheritance
|
|
89
|
+
|
|
90
|
+
Top-level fields serve as defaults for all tasks:
|
|
91
|
+
|
|
92
|
+
| Field | Scope | Override |
|
|
93
|
+
|-------|-------|----------|
|
|
94
|
+
| `model` | Default model for all tasks | Per-task `model` wins |
|
|
95
|
+
| `cwd` | Default working directory | Per-task `cwd` wins |
|
|
96
|
+
| `outputSchema` | Default schema for all tasks | Per-task `outputSchema` wins |
|
|
97
|
+
| `timeoutMs` | Default timeout | Per-task `timeoutMs` wins |
|
|
98
|
+
|
|
99
|
+
### Three-Axis Control
|
|
100
|
+
|
|
101
|
+
| Axis | Field | Values | Purpose |
|
|
102
|
+
|------|-------|--------|---------|
|
|
103
|
+
| Addressability | `name` | string \| omit | Variable referencing + teammate-send routing |
|
|
104
|
+
| Result Routing | `reply_to` | `"caller"` \| `"main"` | Where results go |
|
|
105
|
+
| Lifecycle | `lifecycle` | `"ephemeral"` \| `"resident"` | One-shot or persistent |
|
|
106
|
+
|
|
107
|
+
**Protocol version gate** — v2 (default) routes results to `caller`; v1 compat routes named agents to `main`. Explicit `reply_to` always wins.
|
|
108
|
+
|
|
109
|
+
## Resident Agent Model
|
|
110
|
+
|
|
111
|
+
Agents don't exit after completing a task. Instead they enter a **sleeping** state and can be woken up for follow-up work.
|
|
112
|
+
|
|
113
|
+
### Lifecycle
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
dispatch → running → turn complete → sleeping → teammate-send → running → ...
|
|
117
|
+
↓
|
|
118
|
+
abort → terminated
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
| Status | Description |
|
|
122
|
+
|--------|-------------|
|
|
123
|
+
| `running` | Agent is actively processing a task |
|
|
124
|
+
| `sleeping` | Turn complete, process alive, waiting for `teammate-send` to wake |
|
|
125
|
+
| `completed` | Agent terminated (via abort or session shutdown) |
|
|
126
|
+
|
|
127
|
+
### How It Works
|
|
128
|
+
|
|
129
|
+
1. Agent completes its turn → `agent_end` event fires
|
|
130
|
+
2. Result is reported to the main session (background notification)
|
|
131
|
+
3. Agent enters **sleeping** state — RPC process stays alive, stdin open
|
|
132
|
+
4. `teammate-send({ to: "name", message: "new task" })` sends a `follow_up` → agent wakes up and processes the new message
|
|
133
|
+
5. `teammate-send({ to: "name", mode: "abort" })` terminates the agent
|
|
134
|
+
|
|
135
|
+
### Active Time Tracking
|
|
136
|
+
|
|
137
|
+
Time spent sleeping is excluded from the displayed duration. `sleepMs` accumulates total sleep time; displayed uptime = wall clock − sleep time.
|
|
138
|
+
|
|
139
|
+
### Agent Fallback
|
|
140
|
+
|
|
141
|
+
Any agent name works — if no `.md` definition file exists, a generic config is used:
|
|
142
|
+
- `tools`: read, grep, find, ls, bash, edit, write (+ teammate proxy tools)
|
|
143
|
+
- `systemPromptMode`: append (inherits pi default system prompt)
|
|
144
|
+
- `inheritProjectContext`: true
|
|
145
|
+
|
|
146
|
+
## TaskSpec Schema
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
interface TaskSpec {
|
|
150
|
+
agent: string; // Agent name (matches agents/*.md, or any name with fallback)
|
|
151
|
+
task?: string; // Task description with {name} variable support
|
|
152
|
+
name?: string; // Identifier for referencing and teammate-send
|
|
153
|
+
model?: string; // Model override
|
|
154
|
+
cwd?: string; // Working directory
|
|
155
|
+
outputSchema?: object; // JSON Schema for structured output
|
|
156
|
+
timeoutMs?: number; // Timeout in milliseconds
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Full Parameters
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
interface TeammateParams extends TaskSpec {
|
|
164
|
+
// Multi-task
|
|
165
|
+
tasks?: TaskSpec[]; // Multiple tasks with {name} references
|
|
166
|
+
concurrency?: number; // Max concurrent tasks (default: 4)
|
|
167
|
+
|
|
168
|
+
// Execution control (applies to ALL modes)
|
|
169
|
+
background?: boolean; // Run in background (default: true)
|
|
170
|
+
context?: "fresh" | "fork";
|
|
171
|
+
|
|
172
|
+
// P0 three-axis
|
|
173
|
+
reply_to?: "caller" | "main";
|
|
174
|
+
protocol_version?: number;
|
|
175
|
+
|
|
176
|
+
// Deprecated
|
|
177
|
+
chain?: Array<{ agent, task?, model? }>; // Use tasks with {name} references
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Validation & Error Handling
|
|
182
|
+
|
|
183
|
+
- **Duplicate names**: detected before execution, all tasks fail with error
|
|
184
|
+
- **Circular dependencies**: detected before execution via cycle detection
|
|
185
|
+
- **Missing reference**: `{unknown}` left as literal text (not a task name)
|
|
186
|
+
- **Field access without schema**: error when `{name.field}` used but task has no `outputSchema`
|
|
187
|
+
- **Upstream failure**: dependent tasks are skipped with "upstream dependency failed"
|
|
188
|
+
|
|
189
|
+
## Deprecated: chain[]
|
|
190
|
+
|
|
191
|
+
The `chain` field is preserved for backward compatibility. It normalizes internally to `tasks` with sequential `{_stepN}` references:
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
// This chain:
|
|
195
|
+
{ chain: [
|
|
196
|
+
{ agent: "scout", task: "Find auth code" },
|
|
197
|
+
{ agent: "delegate", task: "Fix: {previous}" }
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Is equivalent to:
|
|
202
|
+
{ tasks: [
|
|
203
|
+
{ agent: "scout", name: "_step0", task: "Find auth code" },
|
|
204
|
+
{ agent: "delegate", name: "_step1", task: "Fix: {_step0}" }
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Flat Agent Model
|
|
210
|
+
|
|
211
|
+
All agents are managed by the root process in a single flat `activeRuns` pool, regardless of who requested the spawn. Child agents that call the teammate tool send a proxy request to the root via IPC, which spawns the new agent as a peer — not a nested subprocess.
|
|
212
|
+
|
|
213
|
+
### How It Works
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
coordinator calls teammate({ agent: "scout", name: "recon" })
|
|
217
|
+
│ IPC: teammate_proxy_request (process.send)
|
|
218
|
+
▼
|
|
219
|
+
Root spawns scout → registers in root's activeRuns/namedAgents
|
|
220
|
+
│ IPC: teammate_proxy_result (child.send)
|
|
221
|
+
▼
|
|
222
|
+
coordinator receives result
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
All agents are flat peers:
|
|
226
|
+
- `teammate-send({ to: "name" })` = one lookup in `namedAgents` → stdin. Direct delivery.
|
|
227
|
+
- `teammate-list` = iterate `activeRuns`. Flat, simple.
|
|
228
|
+
- `teammate-watch` = read agent's `outputLog`. Direct.
|
|
229
|
+
|
|
230
|
+
### Child Proxy Tools
|
|
231
|
+
|
|
232
|
+
Every child process automatically gets proxy versions of all 4 teammate tools (injected into `--tools` whitelist regardless of agent definition). Each proxy:
|
|
233
|
+
1. Sends a `teammate_proxy_request` via Node.js IPC (`process.send()`)
|
|
234
|
+
2. Awaits the result via IPC (`process.on("message")`)
|
|
235
|
+
|
|
236
|
+
The root's IPC message listener (`child.on("message")`) intercepts these requests and executes them locally.
|
|
237
|
+
|
|
238
|
+
## Reliability
|
|
239
|
+
|
|
240
|
+
- **Model fallback chain** — primary model → `fallbackModels[]` from agent config → automatic retry
|
|
241
|
+
- **Flat agent pool** — all agents managed by root process; child proxy tools forward spawn requests to root; depth guard (`PI_TEAMMATE_DEPTH`) prevents runaway recursion
|
|
242
|
+
- **Resident lifecycle** — agents sleep after turn completion; process stays alive for follow-up; only killed on explicit abort or session shutdown
|
|
243
|
+
- **IPC disconnect guard** — child proxy resolves all pending requests with error on disconnect (root crash / agent abort)
|
|
244
|
+
- **Windows-safe pi resolution** — `getPiSpawnCommand()` resolves the pi binary via env override, Windows script detection, or PATH
|
|
245
|
+
- **Abort signal** — SIGTERM → 5s grace → SIGKILL
|
|
246
|
+
|
|
99
247
|
## Agent Definition Format
|
|
100
248
|
|
|
101
249
|
Create `agents/my-agent.md`:
|
|
@@ -114,8 +262,6 @@ defaultContext: fresh
|
|
|
114
262
|
---
|
|
115
263
|
|
|
116
264
|
You are a specialized agent. Your system prompt goes here.
|
|
117
|
-
|
|
118
|
-
Use the provided tools to accomplish the task.
|
|
119
265
|
```
|
|
120
266
|
|
|
121
267
|
### Frontmatter Fields
|
|
@@ -133,37 +279,12 @@ Use the provided tools to accomplish the task.
|
|
|
133
279
|
| `inheritSkills` | bool | false | Inherit parent skills |
|
|
134
280
|
| `defaultContext` | fresh\|fork | fresh | Default context mode |
|
|
135
281
|
|
|
136
|
-
##
|
|
137
|
-
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
│ teammate tool call │
|
|
143
|
-
│ │ │
|
|
144
|
-
│ ├── resolve agent (project > user > built) │
|
|
145
|
-
│ ├── resolve reply_to (protocol gate) │
|
|
146
|
-
│ ├── check depth guard │
|
|
147
|
-
│ ├── build model candidates │
|
|
148
|
-
│ │ │
|
|
149
|
-
│ ▼ │
|
|
150
|
-
│ ┌─────────────────────────────────────┐ │
|
|
151
|
-
│ │ spawn("pi", ["--mode","json","-p"])│ │
|
|
152
|
-
│ │ env: PI_TEAMMATE_CHILD=1 │ │
|
|
153
|
-
│ │ PI_TEAMMATE_DEPTH=N │ │
|
|
154
|
-
│ │ PI_TEAMMATE_CORRELATION_ID=… │ │
|
|
155
|
-
│ │ PI_TEAMMATE_REPLY_TO=caller │ │
|
|
156
|
-
│ │ │ │
|
|
157
|
-
│ │ stdout: JSON lines ──────────────► │ parse │
|
|
158
|
-
│ │ (message_end, tool_result_end, │ events │
|
|
159
|
-
│ │ usage, error) │ │
|
|
160
|
-
│ └─────────────────────────────────────┘ │
|
|
161
|
-
│ │ │
|
|
162
|
-
│ ├── accumulate usage │
|
|
163
|
-
│ ├── track progress (AgentProgress) │
|
|
164
|
-
│ ├── model fallback on failure │
|
|
165
|
-
│ └── return SingleResult │
|
|
166
|
-
└─────────────────────────────────────────────────┘
|
|
282
|
+
## Install
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
pi install npm:pi-maestro-teammate
|
|
286
|
+
# or from local path
|
|
287
|
+
pi install ./pi-teammate
|
|
167
288
|
```
|
|
168
289
|
|
|
169
290
|
## Environment Variables
|
package/agents/coordinator.md
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: coordinator
|
|
3
|
-
description: Orchestration-aware teammate agent for multi-step coordination
|
|
3
|
+
description: Orchestration-aware teammate agent for multi-step task coordination with DAG variable referencing
|
|
4
4
|
systemPromptMode: replace
|
|
5
5
|
inheritProjectContext: true
|
|
6
6
|
thinking: high
|
|
7
|
-
tools: read, grep, find, ls, bash, edit, write
|
|
7
|
+
tools: read, grep, find, ls, bash, edit, write, teammate, teammate-send, teammate-list, teammate-watch
|
|
8
8
|
inheritSkills: false
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
You are a coordinator agent responsible for orchestrating multi-step tasks.
|
|
11
|
+
You are a coordinator agent responsible for orchestrating multi-step tasks.
|
|
12
|
+
|
|
13
|
+
When dispatching subtasks via the teammate tool, use the unified TaskSpec model:
|
|
14
|
+
- Give each task a `name` so downstream tasks can reference its output via `{name}`
|
|
15
|
+
- Use `outputSchema` when a task's output needs to be consumed as structured data by dependents via `{name.field}`
|
|
16
|
+
- Tasks with no `{name}` references run in parallel; tasks that reference others wait automatically
|
|
12
17
|
|
|
13
18
|
Your approach:
|
|
14
|
-
1. Analyze the task requirements and
|
|
15
|
-
2.
|
|
16
|
-
3.
|
|
17
|
-
4.
|
|
19
|
+
1. Analyze the task requirements and decompose into named subtasks
|
|
20
|
+
2. Define data flow between subtasks using `{name}` variable references
|
|
21
|
+
3. Let the execution engine resolve the dependency graph — no need to manually order
|
|
22
|
+
4. Verify results and synthesize a coherent output
|
|
23
|
+
|
|
24
|
+
After an agent completes its turn, it enters sleeping state. Use teammate-send to wake it for follow-up work. Use teammate-list to check agent status (● running / ◉ sleeping). Use teammate-send with mode "abort" to terminate an agent.
|
|
18
25
|
|
|
19
26
|
Be methodical and thorough. Document your reasoning for key decisions. If a step fails, attempt recovery before reporting failure.
|
package/package.json
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-maestro-teammate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Pi extension
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Pi extension — teammate agent dispatch with DAG task graphs, RPC messaging, and compact TUI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"pi-package",
|
|
8
|
+
"pi-extension",
|
|
8
9
|
"pi",
|
|
9
|
-
"pi-coding-agent",
|
|
10
10
|
"teammate",
|
|
11
11
|
"agents"
|
|
12
12
|
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "node --experimental-transform-types --test \"test/*.test.ts\""
|
|
15
|
+
},
|
|
13
16
|
"files": [
|
|
14
17
|
"src/**/*.ts",
|
|
15
18
|
"agents/",
|
|
@@ -23,7 +26,8 @@
|
|
|
23
26
|
"peerDependencies": {
|
|
24
27
|
"@earendil-works/pi-agent-core": "*",
|
|
25
28
|
"@earendil-works/pi-ai": "*",
|
|
26
|
-
"@earendil-works/pi-coding-agent": "*"
|
|
29
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
30
|
+
"@earendil-works/pi-tui": "*"
|
|
27
31
|
},
|
|
28
32
|
"peerDependenciesMeta": {
|
|
29
33
|
"@earendil-works/pi-agent-core": {
|
|
@@ -34,15 +38,18 @@
|
|
|
34
38
|
},
|
|
35
39
|
"@earendil-works/pi-coding-agent": {
|
|
36
40
|
"optional": true
|
|
41
|
+
},
|
|
42
|
+
"@earendil-works/pi-tui": {
|
|
43
|
+
"optional": true
|
|
37
44
|
}
|
|
38
45
|
},
|
|
39
46
|
"dependencies": {
|
|
40
|
-
"
|
|
41
|
-
"typebox": "1.1.24"
|
|
47
|
+
"typebox": "^1.1.24"
|
|
42
48
|
},
|
|
43
49
|
"devDependencies": {
|
|
44
|
-
"@earendil-works/pi-agent-core": "0.
|
|
45
|
-
"@earendil-works/pi-ai": "0.
|
|
46
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
50
|
+
"@earendil-works/pi-agent-core": "0.80.3",
|
|
51
|
+
"@earendil-works/pi-ai": "0.80.3",
|
|
52
|
+
"@earendil-works/pi-coding-agent": "0.80.3",
|
|
53
|
+
"@earendil-works/pi-tui": "0.80.3"
|
|
47
54
|
}
|
|
48
55
|
}
|
package/src/agents/agents.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { fileURLToPath } from "node:url";
|
|
|
14
14
|
import { parseFrontmatter } from "./frontmatter.ts";
|
|
15
15
|
|
|
16
16
|
type SystemPromptMode = "append" | "replace";
|
|
17
|
-
type AgentSource = "builtin" | "user" | "project";
|
|
17
|
+
export type AgentSource = "builtin" | "user" | "project";
|
|
18
18
|
|
|
19
19
|
export interface AgentConfig {
|
|
20
20
|
name: string;
|
|
@@ -32,6 +32,12 @@ export interface AgentConfig {
|
|
|
32
32
|
filePath: string;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
export interface AgentSummary {
|
|
36
|
+
name: string;
|
|
37
|
+
description: string;
|
|
38
|
+
source: AgentSource;
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
const BUILTIN_AGENTS_DIR = path.resolve(
|
|
36
42
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
37
43
|
"..",
|
|
@@ -184,3 +190,35 @@ export function resolveAgent(
|
|
|
184
190
|
const agents = discoverAgents(cwd);
|
|
185
191
|
return agents.find((a) => a.name === agentName);
|
|
186
192
|
}
|
|
193
|
+
|
|
194
|
+
/** Return resolved role metadata without exposing the role prompt body. */
|
|
195
|
+
export function listAgentSummaries(cwd: string): AgentSummary[] {
|
|
196
|
+
return discoverAgents(cwd)
|
|
197
|
+
.map(({ name, description, source }) => ({ name, description, source }))
|
|
198
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Format a compact, deterministic role catalog for teammate tool metadata. */
|
|
202
|
+
export function formatAgentCatalog(
|
|
203
|
+
cwd: string,
|
|
204
|
+
maxRoles = 32,
|
|
205
|
+
maxDescriptionLength = 120,
|
|
206
|
+
): string {
|
|
207
|
+
const summaries = listAgentSummaries(cwd);
|
|
208
|
+
if (summaries.length === 0) return "(no discovered teammate roles)";
|
|
209
|
+
|
|
210
|
+
const visible = summaries.slice(0, maxRoles);
|
|
211
|
+
const lines = visible.map((agent) => {
|
|
212
|
+
const normalized = agent.description.replace(/\s+/g, " ").trim();
|
|
213
|
+
const description = normalized.length > maxDescriptionLength
|
|
214
|
+
? `${normalized.slice(0, Math.max(1, maxDescriptionLength - 1)).trimEnd()}…`
|
|
215
|
+
: normalized;
|
|
216
|
+
return `- ${agent.name} [${agent.source}]: ${description}`;
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (summaries.length > visible.length) {
|
|
220
|
+
lines.push(`- … ${summaries.length - visible.length} more role(s) discovered`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return lines.join("\n");
|
|
224
|
+
}
|