pi-crew 0.1.31 → 0.1.33

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.
Files changed (39) hide show
  1. package/README.md +80 -27
  2. package/docs/architecture.md +18 -9
  3. package/docs/refactor-tasks-phase3.md +1 -1
  4. package/docs/research-extension-examples.md +297 -0
  5. package/docs/research-extension-system.md +324 -0
  6. package/docs/research-optimization-plan.md +548 -0
  7. package/docs/research-pi-coding-agent.md +357 -0
  8. package/docs/resource-formats.md +4 -3
  9. package/docs/usage.md +3 -3
  10. package/package.json +1 -1
  11. package/schema.json +53 -1
  12. package/src/agents/discover-agents.ts +2 -2
  13. package/src/config/config.ts +48 -1
  14. package/src/config/defaults.ts +3 -2
  15. package/src/extension/autonomous-policy.ts +56 -2
  16. package/src/extension/import-index.ts +4 -3
  17. package/src/extension/management.ts +2 -2
  18. package/src/extension/project-init.ts +9 -7
  19. package/src/extension/register.ts +63 -0
  20. package/src/extension/registration/artifact-cleanup.ts +4 -3
  21. package/src/extension/registration/compaction-guard.ts +125 -0
  22. package/src/extension/registration/subagent-tools.ts +27 -8
  23. package/src/extension/registration/team-tool.ts +18 -1
  24. package/src/extension/run-import.ts +4 -4
  25. package/src/extension/run-index.ts +14 -14
  26. package/src/extension/team-recommendation.ts +16 -8
  27. package/src/extension/team-tool/api.ts +3 -0
  28. package/src/extension/team-tool/doctor.ts +6 -5
  29. package/src/runtime/manifest-cache.ts +3 -5
  30. package/src/runtime/subagent-manager.ts +29 -2
  31. package/src/runtime/task-packet.ts +11 -2
  32. package/src/runtime/task-runner/prompt-builder.ts +3 -0
  33. package/src/schema/config-schema.ts +12 -0
  34. package/src/state/state-store.ts +8 -9
  35. package/src/teams/discover-teams.ts +2 -2
  36. package/src/utils/paths.ts +34 -7
  37. package/src/workflows/discover-workflows.ts +2 -2
  38. package/src/worktree/cleanup.ts +3 -1
  39. package/src/worktree/worktree-manager.ts +3 -1
package/README.md CHANGED
@@ -132,9 +132,12 @@ User config path:
132
132
  Project config path:
133
133
 
134
134
  ```text
135
- .pi/teams/config.json
135
+ .crew/config.json # default (new projects)
136
+ .pi/teams/config.json # legacy (when the repo already has .pi/)
136
137
  ```
137
138
 
139
+ The project root is auto-detected by walking up from the current directory and stopping at any of: `.git`, `.pi`, `.crew`, `.hg`, `.svn`, `.factory`, `.omc`, or any common manifest file (`package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `pom.xml`, `composer.json`, `build.gradle[.kts]`). If the project already has a `.pi/` directory, pi-crew reuses it under `.pi/teams/` to avoid creating a parallel layout; otherwise it uses `.crew/`.
140
+
138
141
  Config merge priority:
139
142
 
140
143
  ```text
@@ -179,6 +182,11 @@ Supported config:
179
182
  "showModel": true,
180
183
  "showTokens": true,
181
184
  "showTools": true
185
+ },
186
+ "tools": {
187
+ "enableClaudeStyleAliases": true,
188
+ "enableSteer": true,
189
+ "terminateOnForeground": false
182
190
  }
183
191
  }
184
192
  ```
@@ -186,6 +194,7 @@ Supported config:
186
194
  Safety notes:
187
195
 
188
196
  - Foreground child-process runs continue in the Pi extension process and return control to chat immediately, so large workflows do not block the interactive session. They are interrupted on session shutdown. Use `async: true` only for intentionally detached runs that may survive the current session.
197
+ - `tools.terminateOnForeground` is an opt-in power-user setting. When true, foreground `Agent`/`crew_agent` calls return with `terminate: true` after the child result is available, saving one follow-up LLM turn. Default is false so the assistant can still summarize raw worker output.
189
198
 
190
199
  UI notes:
191
200
 
@@ -454,12 +463,20 @@ User resources:
454
463
  ~/.pi/agent/workflows/*.workflow.md
455
464
  ```
456
465
 
457
- Project resources:
466
+ Project resources (new default layout):
467
+
468
+ ```text
469
+ .crew/agents/*.md
470
+ .crew/teams/*.team.md
471
+ .crew/workflows/*.workflow.md
472
+ ```
473
+
474
+ Legacy layout (when `.pi/` already exists in the repo):
458
475
 
459
476
  ```text
460
- .pi/agents/*.md
461
- .pi/teams/*.team.md
462
- .pi/workflows/*.workflow.md
477
+ .pi/teams/agents/*.md
478
+ .pi/teams/teams/*.team.md
479
+ .pi/teams/workflows/*.workflow.md
463
480
  ```
464
481
 
465
482
  Discovery priority:
@@ -525,33 +542,41 @@ review
525
542
 
526
543
  ## State layout
527
544
 
528
- Project-local state is preferred when the cwd has `.git` or `.pi`; otherwise user-global state is used.
545
+ Project-local state is preferred when the cwd is inside a recognised project (any of the markers listed in the Config section above). Otherwise pi-crew falls back to user-global state.
529
546
 
530
- Typical project-local state:
547
+ The project state root (`<crewRoot>` below) resolves to:
531
548
 
532
549
  ```text
533
- .pi/teams/state/runs/{runId}/manifest.json
534
- .pi/teams/state/runs/{runId}/tasks.json
535
- .pi/teams/state/runs/{runId}/events.jsonl
536
- .pi/teams/artifacts/{runId}/...
537
- .pi/teams/worktrees/{runId}/{taskId}
538
- .pi/teams/imports/{runId}/run-export.json
550
+ <repoRoot>/.crew/ # default, used for new projects
551
+ <repoRoot>/.pi/teams/ # legacy reuse when .pi/ already exists
552
+ ```
553
+
554
+ Typical project-local state (`<crewRoot>` is one of the two paths above):
555
+
556
+ ```text
557
+ <crewRoot>/state/runs/{runId}/manifest.json
558
+ <crewRoot>/state/runs/{runId}/tasks.json
559
+ <crewRoot>/state/runs/{runId}/events.jsonl
560
+ <crewRoot>/artifacts/{runId}/...
561
+ <crewRoot>/worktrees/{runId}/{taskId}
562
+ <crewRoot>/imports/{runId}/run-export.json
539
563
  ```
540
564
 
541
565
  Mailbox state:
542
566
 
543
567
  ```text
544
- .pi/teams/state/runs/{runId}/mailbox/inbox.jsonl
545
- .pi/teams/state/runs/{runId}/mailbox/outbox.jsonl
546
- .pi/teams/state/runs/{runId}/mailbox/delivery.json
547
- .pi/teams/state/runs/{runId}/mailbox/tasks/{taskId}/inbox.jsonl
548
- .pi/teams/state/runs/{runId}/mailbox/tasks/{taskId}/outbox.jsonl
568
+ <crewRoot>/state/runs/{runId}/mailbox/inbox.jsonl
569
+ <crewRoot>/state/runs/{runId}/mailbox/outbox.jsonl
570
+ <crewRoot>/state/runs/{runId}/mailbox/delivery.json
571
+ <crewRoot>/state/runs/{runId}/mailbox/tasks/{taskId}/inbox.jsonl
572
+ <crewRoot>/state/runs/{runId}/mailbox/tasks/{taskId}/outbox.jsonl
549
573
  ```
550
574
 
551
- User-global fallback:
575
+ User-global fallback (shared with other Pi tools):
552
576
 
553
577
  ```text
554
- ~/.pi/agent/extensions/pi-crew/runs/...
578
+ ~/.pi/agent/extensions/pi-crew/state/runs/...
579
+ ~/.pi/agent/extensions/pi-crew/artifacts/...
555
580
  ~/.pi/agent/extensions/pi-crew/imports/...
556
581
  ```
557
582
 
@@ -570,18 +595,34 @@ Optionally copy builtin resources:
570
595
  /team-init --copy-builtins --overwrite
571
596
  ```
572
597
 
573
- Created directories:
598
+ Created directories (new projects):
574
599
 
575
600
  ```text
576
- .pi/agents/
577
- .pi/teams/
578
- .pi/workflows/
601
+ .crew/agents/
602
+ .crew/teams/
603
+ .crew/workflows/
604
+ .crew/imports/
605
+ ```
606
+
607
+ If the project already has `.pi/`, the legacy layout is initialised instead:
608
+
609
+ ```text
610
+ .pi/teams/agents/
611
+ .pi/teams/teams/
612
+ .pi/teams/workflows/
579
613
  .pi/teams/imports/
580
614
  ```
581
615
 
582
- `.gitignore` entries:
616
+ `.gitignore` entries are written for whichever layout is active, e.g.:
583
617
 
584
618
  ```text
619
+ # new layout
620
+ .crew/state/
621
+ .crew/artifacts/
622
+ .crew/worktrees/
623
+ .crew/imports/
624
+
625
+ # legacy layout
585
626
  .pi/teams/state/
586
627
  .pi/teams/artifacts/
587
628
  .pi/teams/worktrees/
@@ -597,14 +638,26 @@ Export writes:
597
638
  {artifactsRoot}/export/run-export.md
598
639
  ```
599
640
 
600
- Import stores bundles under:
641
+ Import stores bundles under (new layout):
642
+
643
+ ```text
644
+ .crew/imports/{runId}/run-export.json
645
+ .crew/imports/{runId}/README.md
646
+ ```
647
+
648
+ or under the legacy layout when `.pi/` already exists:
601
649
 
602
650
  ```text
603
651
  .pi/teams/imports/{runId}/run-export.json
604
652
  .pi/teams/imports/{runId}/README.md
605
653
  ```
606
654
 
607
- or user-global imports with `--user`.
655
+ or user-global imports with `--user`:
656
+
657
+ ```text
658
+ ~/.pi/agent/extensions/pi-crew/imports/{runId}/run-export.json
659
+ ~/.pi/agent/extensions/pi-crew/imports/{runId}/README.md
660
+ ```
608
661
 
609
662
  ## Doctor and validation
610
663
 
@@ -12,12 +12,14 @@ Runtime layer
12
12
  team runner, task graph scheduler, child Pi process runner, async runner,
13
13
  model fallback, policy engine, worktree manager, live-session experimental path
14
14
 
15
- State layer
16
- .pi/teams/state/runs/{runId}/manifest.json
17
- .pi/teams/state/runs/{runId}/tasks.json
18
- .pi/teams/state/runs/{runId}/events.jsonl
19
- .pi/teams/state/runs/{runId}/agents/{taskId}/status.json
20
- .pi/teams/artifacts/{runId}/...
15
+ State layer (project root resolves to <crewRoot>:
16
+ - .crew/ when no .pi/ exists in the repo (default)
17
+ - .pi/teams/ when the repo already has .pi/ (legacy reuse))
18
+ <crewRoot>/state/runs/{runId}/manifest.json
19
+ <crewRoot>/state/runs/{runId}/tasks.json
20
+ <crewRoot>/state/runs/{runId}/events.jsonl
21
+ <crewRoot>/state/runs/{runId}/agents/{taskId}/status.json
22
+ <crewRoot>/artifacts/{runId}/...
21
23
  ```
22
24
 
23
25
  ## Run flow
@@ -104,10 +106,10 @@ Model choice is based on Pi's current configuration/model registry, not hardcode
104
106
 
105
107
  ## State layer
106
108
 
107
- Run state is under:
109
+ Run state is under `<crewRoot>` (`.crew/` for new projects, or `.pi/teams/` when the repo already has `.pi/`):
108
110
 
109
111
  ```text
110
- .pi/teams/state/runs/{runId}/
112
+ <crewRoot>/state/runs/{runId}/
111
113
  manifest.json run metadata/status/artifacts/async pid
112
114
  tasks.json task graph and per-task status
113
115
  events.jsonl append-only run events
@@ -125,7 +127,7 @@ Run state is under:
125
127
  Artifacts are under:
126
128
 
127
129
  ```text
128
- .pi/teams/artifacts/{runId}/
130
+ <crewRoot>/artifacts/{runId}/
129
131
  goal.md
130
132
  prompts/{taskId}.md
131
133
  results/{taskId}.txt
@@ -136,6 +138,13 @@ Artifacts are under:
136
138
  summary.md
137
139
  ```
138
140
 
141
+ `<crewRoot>` resolution is centralised in `src/utils/paths.ts#projectCrewRoot()`:
142
+
143
+ - if `<repoRoot>/.pi/` already exists, return `<repoRoot>/.pi/teams/` (legacy reuse, no parallel `.crew/`)
144
+ - otherwise return `<repoRoot>/.crew/` (default for fresh projects)
145
+
146
+ User-global fallback (when no project root is detected) lives under `~/.pi/agent/extensions/pi-crew/`.
147
+
139
148
  Atomic writes use temp-file replace with retry for transient Windows `EPERM`/`EBUSY`/`EACCES`. JSONL append paths are best-effort where used for observers/progress; write failures must not crash child output parsing.
140
149
 
141
150
  ## UI and observability
@@ -278,7 +278,7 @@ export const MAX_PARALLEL_CONCURRENCY: number;
278
278
  **Source**: `source/pi-subagents/artifacts.ts` (hàm `cleanupOldArtifacts`)
279
279
  **Đích**: bổ sung vào `pi-crew/src/state/artifact-store.ts`
280
280
 
281
- **Lý do**: Pi-crew `.pi/teams/state/artifacts/` không có TTL → run cũ tích lũy mãi. Pattern subagents:
281
+ **Lý do**: Pi-crew `<crewRoot>/state/artifacts/` (`<crewRoot>` = `.crew/` mới hoặc `.pi/teams/` legacy) không có TTL → run cũ tích lũy mãi. Pattern subagents:
282
282
  - File `.last-cleanup` chứa timestamp.
283
283
  - Nếu marker mới hơn 24h → skip (không scan dir lớn mỗi extension load).
284
284
  - Nếu cần scan: xoá file mtime > `maxAgeDays * 24h`.
@@ -0,0 +1,297 @@
1
+ # Research: Extension Examples & Patterns
2
+
3
+ > Ngày: 2026-04-29 | Read-only research | Source: `source/pi-mono/packages/coding-agent/examples/extensions/`
4
+
5
+ ## 1. Example Catalog (86 files, 60+ extensions)
6
+
7
+ ### 1.1 Sorted by relevance to pi-crew
8
+
9
+ | Priority | Example | Relevance |
10
+ |---|---|---|
11
+ | ⭐⭐⭐ | `subagent/` | Most similar to pi-crew: child Pi spawning, parallel, chain |
12
+ | ⭐⭐⭐ | `custom-compaction.ts` | Hook compaction — useful for preserving run state |
13
+ | ⭐⭐⭐ | `event-bus.ts` | Cross-extension communication pattern |
14
+ | ⭐⭐⭐ | `plan-mode/` | State persistence, dynamic tools, widget management |
15
+ | ⭐⭐⭐ | `structured-output.ts` | `terminate: true` — save LLM turns |
16
+ | ⭐⭐ | `handoff.ts` | Context transfer to new session |
17
+ | ⭐⭐ | `dynamic-tools.ts` | Register tools at runtime |
18
+ | ⭐⭐ | `permission-gate.ts` | Gate dangerous operations |
19
+ | ⭐⭐ | `trigger-compact.ts` | Proactive compaction monitoring |
20
+ | ⭐⭐ | `send-user-message.ts` | sendUserMessage pattern |
21
+ | ⭐ | `dirty-repo-guard.ts` | Guard against uncommitted changes |
22
+ | ⭐ | `model-status.ts` | Model status in footer |
23
+ | ⭐ | `confirm-destructive.ts` | Confirm destructive operations |
24
+
25
+ ## 2. Deep Analysis of Key Examples
26
+
27
+ ### 2.1 subagent/ — The Reference Implementation
28
+
29
+ **Files:**
30
+ - `index.ts` (~530 dòng): Main tool with execute + render
31
+ - `agents.ts` (~130 dòng): Agent discovery (user/project scope)
32
+
33
+ **Architecture:**
34
+ ```
35
+ subagent tool
36
+ ├── Single: runSingleAgent() → spawn pi --mode json -p
37
+ ├── Parallel: mapWithConcurrencyLimit(tasks, 4, runSingleAgent)
38
+ └── Chain: sequential loop with {previous} placeholder
39
+ ```
40
+
41
+ **Key patterns:**
42
+ - Agent discovery: `discoverAgents(cwd, scope)` — scans `.md` files with YAML frontmatter
43
+ - Child process: `getPiInvocation()` detects current runtime (node/bun/pi binary)
44
+ - Streaming: `onUpdate` callback for partial results during execution
45
+ - Render: `renderCall()` + `renderResult()` with collapsed/expanded views
46
+ - Abort: AbortSignal propagated to child process
47
+
48
+ **What pi-crew does better:**
49
+ - Durable state (manifest, tasks, events) instead of in-memory only
50
+ - Team/workflow abstraction instead of flat agent list
51
+ - Task graph with DAG dependencies instead of linear chain
52
+ - Async background runner with PID tracking
53
+ - Policy engine for limits/retry/escalation
54
+ - Mailbox for inter-task communication
55
+ - Worktree isolation per task
56
+
57
+ **What pi-crew could adopt from this:**
58
+ - `terminate: true` on final results (not used in example either, but available)
59
+ - `renderCall/Result` custom rendering patterns
60
+ - `mapWithConcurrencyLimit` pattern (pi-crew already has similar)
61
+
62
+ ### 2.2 custom-compaction.ts — Custom Compaction
63
+
64
+ **Pattern:**
65
+ ```typescript
66
+ pi.on("session_before_compact", async (event, ctx) => {
67
+ // 1. Get preparation data
68
+ const { messagesToSummarize, turnPrefixMessages, tokensBefore, firstKeptEntryId } = event.preparation;
69
+
70
+ // 2. Use different model for summarization (cheaper)
71
+ const model = ctx.modelRegistry.find("google", "gemini-2.5-flash");
72
+
73
+ // 3. Custom prompt
74
+ const summary = await complete(model, { messages: [...] }, { apiKey, signal });
75
+
76
+ // 4. Return custom compaction result
77
+ return {
78
+ compaction: { summary, firstKeptEntryId, tokensBefore }
79
+ };
80
+ });
81
+ ```
82
+
83
+ **Relevance to pi-crew:**
84
+ - Can use cheap model to summarize completed tasks
85
+ - Can protect foreground runs from being compacted mid-execution
86
+ - Can store structured artifact index in compaction `details`
87
+
88
+ ### 2.3 event-bus.ts — Cross-Extension Communication
89
+
90
+ **Pattern:**
91
+ ```typescript
92
+ // Extension A: emit events
93
+ pi.events.emit("my:notification", { message: "hello", from: "ext-a" });
94
+
95
+ // Extension B: listen
96
+ pi.events.on("my:notification", (data) => {
97
+ currentCtx?.ui.notify(`Event from ${data.from}: ${data.message}`);
98
+ });
99
+ ```
100
+
101
+ **Relevance to pi-crew:**
102
+ - Already used for internal events (`subagent.stuck-blocked`)
103
+ - Could publish structured events for other extensions to consume:
104
+ - `pi-crew:run:completed`
105
+ - `pi-crew:subagent:completed`
106
+ - `pi-crew:run:failed`
107
+
108
+ ### 2.4 plan-mode/ — State Persistence + Dynamic Tools
109
+
110
+ **Key patterns:**
111
+
112
+ State persistence:
113
+ ```typescript
114
+ // Save
115
+ pi.appendEntry("plan-mode", { enabled, todos, executing });
116
+
117
+ // Restore on session_start
118
+ const entries = ctx.sessionManager.getEntries();
119
+ const state = entries
120
+ .filter(e => e.type === "custom" && e.customType === "plan-mode")
121
+ .pop()?.data;
122
+ ```
123
+
124
+ Dynamic tools:
125
+ ```typescript
126
+ // Switch between tool sets
127
+ if (planModeEnabled) {
128
+ pi.setActiveTools(["read", "bash", "grep", "find", "ls"]);
129
+ } else {
130
+ pi.setActiveTools(["read", "bash", "edit", "write"]);
131
+ }
132
+ ```
133
+
134
+ Tool call gate:
135
+ ```typescript
136
+ pi.on("tool_call", async (event) => {
137
+ if (planModeEnabled && event.toolName === "bash") {
138
+ if (!isSafeCommand(event.input.command)) {
139
+ return { block: true, reason: "..." };
140
+ }
141
+ }
142
+ });
143
+ ```
144
+
145
+ **Relevance to pi-crew:**
146
+ - `pi.appendEntry` pattern for cross-session run awareness
147
+ - `pi.setActiveTools` could be used to restrict tools during team runs
148
+ - `tool_call` gate for destructive team actions
149
+
150
+ ### 2.5 structured-output.ts — terminate: true
151
+
152
+ **Pattern:**
153
+ ```typescript
154
+ async execute(_toolCallId, params) {
155
+ return {
156
+ content: [{ type: "text", text: "Done" }],
157
+ details: { headline, summary, actionItems },
158
+ terminate: true, // ← No follow-up LLM turn needed
159
+ };
160
+ }
161
+ ```
162
+
163
+ **Relevance to pi-crew:**
164
+ - `Agent` tool results could use `terminate: true` when background run queued
165
+ - `get_subagent_result` could terminate when result is final
166
+ - `team` tool status/list/recommend actions could terminate
167
+
168
+ ### 2.6 handoff.ts — Context Transfer to New Session
169
+
170
+ **Pattern:**
171
+ ```typescript
172
+ // 1. Extract conversation context
173
+ const messages = ctx.sessionManager.getBranch()
174
+ .filter(e => e.type === "message")
175
+ .map(e => e.message);
176
+
177
+ // 2. Generate focused prompt
178
+ const prompt = await complete(model, { systemPrompt, messages }, { apiKey });
179
+
180
+ // 3. Create new session with pre-filled editor
181
+ await ctx.newSession({
182
+ parentSession: currentSessionFile,
183
+ withSession: async (replacementCtx) => {
184
+ replacementCtx.ui.setEditorText(prompt);
185
+ },
186
+ });
187
+ ```
188
+
189
+ **Relevance to pi-crew:**
190
+ - When a task in a team run needs isolated context, could handoff to new session
191
+ - Parent session tracking via `parentSession`
192
+
193
+ ### 2.7 permission-gate.ts — Dangerous Operation Gate
194
+
195
+ **Pattern:**
196
+ ```typescript
197
+ pi.on("tool_call", async (event, ctx) => {
198
+ if (event.toolName !== "bash") return;
199
+ if (isDangerousPattern(event.input.command)) {
200
+ const choice = await ctx.ui.select("Allow?", ["Yes", "No"]);
201
+ if (choice !== "Yes") {
202
+ return { block: true, reason: "Blocked by user" };
203
+ }
204
+ }
205
+ });
206
+ ```
207
+
208
+ **Relevance to pi-crew:**
209
+ - Gate destructive team actions (delete, forget, prune)
210
+ - Only allow with explicit `confirm: true` parameter
211
+
212
+ ### 2.8 trigger-compact.ts — Proactive Compaction
213
+
214
+ **Pattern:**
215
+ ```typescript
216
+ pi.on("turn_end", (_event, ctx) => {
217
+ const usage = ctx.getContextUsage();
218
+ if (usage?.tokens && usage.tokens > THRESHOLD) {
219
+ ctx.compact({ customInstructions: "..." });
220
+ }
221
+ });
222
+ ```
223
+
224
+ **Relevance to pi-crew:**
225
+ - Monitor context during long team runs
226
+ - Auto-compact before hitting overflow errors
227
+ - Use compact's callback to track state
228
+
229
+ ## 3. Pattern Summary
230
+
231
+ ### 3.1 Patterns pi-crew already implements well
232
+
233
+ | Pattern | pi-crew implementation |
234
+ |---|---|
235
+ | Child Pi spawning | `SubagentManager` + `spawn.ts` with full process management |
236
+ | Parallel execution | `mapConcurrent` in team runner |
237
+ | State persistence | Durable file-based (manifest, tasks, events, artifacts) |
238
+ | Widget rendering | `CrewWidget`, `LiveRunSidebar`, `Powerbar` |
239
+ | Lifecycle hooks | `session_start`, `session_before_switch`, `session_shutdown` |
240
+ | Config merge | `loadConfig` with user/project priority |
241
+ | Abort propagation | `AbortController` trees in foreground runs |
242
+
243
+ ### 3.2 Patterns pi-crew could adopt
244
+
245
+ | Pattern | Current status | Recommendation |
246
+ |---|---|---|
247
+ | `terminate: true` | ❌ Not used | Add to Agent/get_subagent_result |
248
+ | `session_before_compact` hook | ❌ Not hooked | Cancel compact during foreground runs |
249
+ | Custom compaction model | ❌ Not used | Use Haiku/Gemini Flash for task summaries |
250
+ | `pi.events` publish | ⚠️ Internal only | Add public structured events |
251
+ | `pi.appendEntry` | ❌ Not used | Cross-session run references |
252
+ | `tool_call` permission gate | ❌ Not gated | Gate destructive team actions |
253
+ | Config-driven tool registration | ❌ Always all | Register tools per config |
254
+ | Working indicator | ❌ Widget only | Use `ctx.ui.setWorkingIndicator` |
255
+ | Session name auto-set | ❌ Manual only | Auto-name from team run context |
256
+ | `ctx.compact()` proactive | ❌ No monitoring | Monitor + auto-compact at threshold |
257
+
258
+ ## 4. Example: Complete Tool with terminate + render
259
+
260
+ This shows a hypothetical optimized pi-crew Agent tool:
261
+
262
+ ```typescript
263
+ // OPTIMIZED Agent tool pattern
264
+ const AgentTool = defineTool({
265
+ name: "Agent",
266
+ label: "Agent",
267
+ description: "Launch a real pi-crew subagent...",
268
+ parameters: Type.Object({
269
+ prompt: Type.String(),
270
+ description: Type.String(),
271
+ subagent_type: Type.String(),
272
+ run_in_background: Type.Optional(Type.Boolean()),
273
+ }),
274
+ async execute(_id, params, signal, _onUpdate, ctx) {
275
+ // ... spawn subagent ...
276
+ if (params.run_in_background) {
277
+ return {
278
+ content: [{ type: "text", text: `Agent queued. ID: ${record.id}` }],
279
+ details: { agentId: record.id, status: "queued" },
280
+ terminate: true, // ← No need for LLM follow-up
281
+ };
282
+ }
283
+ await record.promise;
284
+ const output = readResult(record);
285
+ return {
286
+ content: [{ type: "text", text: output }],
287
+ details: { agentId: record.id, status: record.status },
288
+ terminate: true, // ← Final result, save LLM turn
289
+ };
290
+ },
291
+ renderResult(result, { expanded }, theme) {
292
+ // Custom rendering with colored status icons
293
+ // Collapsed/expanded views
294
+ // Usage stats display
295
+ },
296
+ });
297
+ ```