pi-subagents 0.19.0 → 0.19.2

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.
@@ -15,8 +15,8 @@ agents into a workflow, or create/edit agents and chains on demand.
15
15
 
16
16
  ## When to Use
17
17
 
18
- - **Advisory review**: fork to `oracle` or `reviewer` for a branched review thread
19
- - **Implementation handoff**: have `oracle` advise, then `oracle-executor` or `worker` implement
18
+ - **Advisory review**: use fresh-context `reviewer` agents for adversarial code review, or fork to `oracle` when inherited decisions and drift matter
19
+ - **Implementation handoff**: have `oracle` advise, then `oracle-executor` or `worker` implement only after an approved direction
20
20
  - **Recon and planning**: use `scout` or `context-builder`, then `planner`
21
21
  - **Parallel exploration**: run multiple non-conflicting tasks concurrently
22
22
  - **Long-running work**: launch async/background runs and inspect them later
@@ -32,11 +32,19 @@ Humans often use the slash-command layer instead:
32
32
  - `/chain` — launch a chain of steps
33
33
  - `/parallel` — launch top-level parallel tasks
34
34
  - `/agents` — open the agents manager TUI
35
+ - `/run-chain` — launch a saved `.chain.md` workflow
35
36
  - `/subagents-status` — inspect active/recent async runs
37
+ - `/subagents-doctor` — diagnose setup, discovery, async paths, and intercom bridge state
36
38
 
37
39
  Prefer the tool when you are writing agent logic. Prefer the slash commands when
38
40
  you are guiding a human through an interactive flow.
39
41
 
42
+ Packaged prompt shortcuts are also available for repeatable workflows:
43
+ - `/parallel-review` — fresh-context reviewers with distinct review angles, then synthesis
44
+ - `/parallel-research` — combine `researcher` and `scout` for external evidence plus local code context
45
+ - `/gather-context-and-clarify` — scout/research first, then ask the user clarifying questions with `interview`
46
+ - `/oracle-executor` — send an explicitly approved implementation task to `oracle-executor`
47
+
40
48
  ## Builtin Agents
41
49
 
42
50
  Builtin agents load at the lowest priority. Project agents override user agents,
@@ -54,16 +62,40 @@ and user/project agents override builtins with the same name.
54
62
  | `oracle` | Decision-consistency advisory review | `openai-codex/gpt-5.5` | Advisory review, intercom coordination |
55
63
  | `oracle-executor` | Implementation after approval | `openai-codex/gpt-5.5` | Single-writer implementation after approval |
56
64
 
57
- Override builtin defaults via settings before copying full agent files when a
58
- small tweak is enough.
65
+ Override builtin defaults before copying full agent files when a small tweak is enough.
66
+
67
+ For one run, use inline config:
68
+
69
+ ```text
70
+ /run reviewer[model=anthropic/claude-sonnet-4] "Review this diff"
71
+ ```
72
+
73
+ For persistent tweaks, prefer `/agents`: choose the builtin, press `e`, change the model or other fields, then save a user or project override. User overrides apply everywhere. Project overrides apply only in that repo and win over user overrides.
59
74
 
60
75
  Settings locations:
61
76
  - User scope: `~/.pi/agent/settings.json`
62
77
  - Project scope: `.pi/settings.json`
63
78
 
79
+ Direct settings example:
80
+
81
+ ```json
82
+ {
83
+ "subagents": {
84
+ "agentOverrides": {
85
+ "reviewer": {
86
+ "model": "anthropic/claude-sonnet-4",
87
+ "thinking": "high",
88
+ "fallbackModels": ["openai/gpt-5-mini"]
89
+ }
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
64
95
  Useful override fields: `model`, `fallbackModels`, `thinking`,
65
96
  `systemPromptMode`, `inheritProjectContext`, `inheritSkills`, `disabled`,
66
- `skills`, `tools`, and `systemPrompt`.
97
+ `skills`, `tools`, and `systemPrompt`. Create a user or project agent with the
98
+ same name only when you want a substantially different agent.
67
99
 
68
100
  ## Discovery and Scope Rules
69
101
 
@@ -119,6 +151,21 @@ subagent({
119
151
  })
120
152
  ```
121
153
 
154
+ Top-level parallel tasks can override per-task behavior:
155
+
156
+ ```typescript
157
+ subagent({
158
+ tasks: [
159
+ { agent: "scout", task: "Map auth", output: "auth-context.md", progress: true },
160
+ { agent: "researcher", task: "Research OAuth best practices", output: "oauth-research.md" },
161
+ { agent: "reviewer", task: "Review auth tests", model: "anthropic/claude-sonnet-4" }
162
+ ],
163
+ concurrency: 3
164
+ })
165
+ ```
166
+
167
+ Avoid duplicate output paths in parallel tasks. Concurrent children should not write to the same file.
168
+
122
169
  ### Chain execution
123
170
 
124
171
  ```typescript
@@ -147,6 +194,14 @@ subagent({
147
194
 
148
195
  Inspect async runs with `subagent({ action: "status", id: "..." })`, `subagent({ action: "status" })` for active runs, or the `/subagents-status` slash command.
149
196
 
197
+ Use diagnostics when setup or child startup looks wrong:
198
+
199
+ ```typescript
200
+ subagent({ action: "doctor" })
201
+ ```
202
+
203
+ Humans can use `/subagents-doctor` for the same read-only report. It checks runtime paths, discovery counts, async support, current session context, and intercom bridge state.
204
+
150
205
  ### Subagent control
151
206
 
152
207
  Subagent control is the runtime visibility and intervention layer for delegated runs. It is separate from lifecycle status. Lifecycle status says whether a child is `queued`, `running`, `paused`, `complete`, or `failed`. Activity reporting is factual: it tracks the last observed activity time and the current tool when known. It does not pretend to know that a child is truly stuck.
@@ -198,6 +253,8 @@ subagent({
198
253
  Chains default to clarify mode unless you explicitly set `clarify: false`.
199
254
  For programmatic background launches, use `clarify: false, async: true`.
200
255
 
256
+ The `/agents` manager also has launch toggles for forked context, background execution, and worktree-isolated parallel runs. Use it when guiding a human who wants to inspect or edit the launch before starting.
257
+
201
258
  ## Worktree Isolation
202
259
 
203
260
  When multiple agents might write concurrently, use worktrees instead of letting
@@ -249,35 +306,44 @@ history as a baseline contract.
249
306
 
250
307
  ## Subagent + Intercom Coordination
251
308
 
252
- When `pi-intercom` is installed and enabled, delegated runs can coordinate with
253
- the orchestrator through the intercom bridge.
309
+ `pi-subagents` works without `pi-intercom`. When `pi-intercom` is installed and enabled, the intercom bridge can automatically give child agents a private coordination channel back to the parent session.
254
310
 
255
- ### Subagent asks the orchestrator
311
+ Most agents should not call `intercom` directly unless bridge instructions provide a target. Do not invent a target. Use the target from the injected bridge instructions or from a visible needs-attention notice.
312
+
313
+ Use `intercom` when:
314
+ - a subagent is blocked on a decision
315
+ - a child needs clarification instead of guessing
316
+ - a detached or async child needs to coordinate without waiting for normal tool return flow
317
+ - an advisory agent was explicitly asked to send a concise progress update
318
+
319
+ Message conventions:
320
+ - `ask` means the child needs a decision or clarification from the parent session.
321
+ - `send` means a short blocked/progress update, only when blocked or explicitly asked.
322
+ - Routine completion does not go through intercom. The normal subagent result still returns through `pi-subagents`.
323
+
324
+ If a bridge target is available, a child can ask:
256
325
 
257
326
  ```typescript
258
327
  intercom({
259
328
  action: "ask",
260
- to: "orchestrator",
329
+ to: "<bridge-provided-target>",
261
330
  message: "Should I optimize for readability or performance here?"
262
331
  })
263
332
  ```
264
333
 
265
- ### Orchestrator replies
334
+ The parent replies with:
266
335
 
267
336
  ```typescript
268
337
  intercom({ action: "reply", message: "Optimize for readability." })
269
338
  ```
270
339
 
271
- Or inspect unresolved asks first:
340
+ Or inspects unresolved asks first:
272
341
 
273
342
  ```typescript
274
343
  intercom({ action: "pending" })
275
344
  ```
276
345
 
277
- Use `intercom` when:
278
- - a subagent is blocked on a decision
279
- - an advisory agent wants to send a concise handoff mid-flight
280
- - a detached or async child needs to coordinate without waiting for normal tool return flow
346
+ If intercom messages do not show up, run `subagent({ action: "doctor" })` or `/subagents-doctor`.
281
347
 
282
348
  ## Management Mode
283
349
 
@@ -326,6 +392,8 @@ subagent({ action: "delete", agent: "my-agent" })
326
392
  Use management actions when the system needs to create or edit subagents on
327
393
  demand without dropping into raw file editing.
328
394
 
395
+ Management actions create or update user/project agent files. For small builtin changes such as a model swap, prefer `/agents` builtin overrides or `subagents.agentOverrides` in settings.
396
+
329
397
  ## Creating and Editing Agents by File
330
398
 
331
399
  A minimal agent file looks like this:
@@ -357,7 +425,12 @@ copying a full builtin file.
357
425
 
358
426
  ## Prompt Template Integration
359
427
 
360
- If `pi-prompt-template-model` is installed, prompt templates can delegate into
428
+ The package includes prompt shortcuts for common workflows: `/parallel-review`,
429
+ `/parallel-research`, `/gather-context-and-clarify`, and `/oracle-executor`.
430
+ Use them when the user wants repeatable review, research, clarification, or
431
+ approved execution patterns.
432
+
433
+ If `pi-prompt-template-model` is installed, additional user prompt templates can delegate into
361
434
  `pi-subagents`. This is useful when a slash command should always run through a
362
435
  particular agent or with forked context.
363
436
 
@@ -366,7 +439,7 @@ particular agent or with forked context.
366
439
  - **Forking requires a persisted parent session.** If the current session does not
367
440
  have a persisted session file, forked runs fail.
368
441
  - **Forked runs inherit parent history.** They are branched threads, not fresh
369
- filtered contexts.
442
+ filtered contexts. Use fresh context for adversarial reviewers unless the user explicitly asks for forked context.
370
443
  - **Default subagent nesting depth is 2.** Deeper recursive delegation is blocked
371
444
  unless configured otherwise.
372
445
  - **Attention signals are not lifecycle state.** `needs_attention` means no activity has been observed past the configured threshold. `paused` means the child turn was intentionally interrupted or is awaiting direction; it is not the same as `failed`.
@@ -386,7 +459,10 @@ for the actual write path.
386
459
  ### Use fork for branched advisory or execution threads
387
460
 
388
461
  Forked runs are useful when the child should reason in a separate thread while
389
- still inheriting the parent’s accumulated context.
462
+ still inheriting the parent’s accumulated context. They are especially useful for
463
+ `oracle`, which audits inherited decisions and drift. For adversarial code review,
464
+ prefer fresh-context reviewers that inspect the repo and diff directly unless the
465
+ user explicitly requests forked context.
390
466
 
391
467
  ### Prefer narrow tasks
392
468
 
@@ -426,8 +502,7 @@ subagent({
426
502
  subagent({ agent: "worker", task: "Add retry logic to the API client." })
427
503
  subagent({
428
504
  agent: "reviewer",
429
- task: "Review the retry logic implementation. Look for edge cases and race conditions.",
430
- context: "fork"
505
+ task: "Review the retry logic implementation. Inspect the repo and current diff directly. Look for edge cases and race conditions."
431
506
  })
432
507
  ```
433
508
 
@@ -442,6 +517,14 @@ subagent({
442
517
  })
443
518
  ```
444
519
 
520
+ ### Saved chain
521
+
522
+ ```text
523
+ /run-chain review-chain -- review this branch
524
+ ```
525
+
526
+ Use saved `.chain.md` workflows when the user wants a repeatable multi-agent flow without rewriting the chain each time.
527
+
445
528
  ## Error Handling
446
529
 
447
530
  **"Unknown agent"**
@@ -450,6 +533,12 @@ subagent({ action: "list" })
450
533
  // Check available agents and chains, then confirm scope/precedence.
451
534
  ```
452
535
 
536
+ **Setup, discovery, or intercom confusion**
537
+ ```typescript
538
+ subagent({ action: "doctor" })
539
+ // Check runtime paths, async support, discovery counts, current session, and intercom bridge state.
540
+ ```
541
+
453
542
  **"Max subagent depth exceeded"**
454
543
  ```typescript
455
544
  // Flatten the workflow or raise maxSubagentDepth in config.
@@ -464,3 +553,18 @@ subagent({ action: "list" })
464
553
  ```typescript
465
554
  // Resolve the current outbound ask before starting another one.
466
555
  ```
556
+
557
+ **Parallel output-path conflict**
558
+ ```typescript
559
+ // Give each parallel task a distinct output path, or disable output for tasks that do not need it.
560
+ ```
561
+
562
+ **Worktree launch fails**
563
+ ```typescript
564
+ // Ensure the git working tree is clean and task cwd overrides match the shared cwd.
565
+ ```
566
+
567
+ **Child fails before starting**
568
+ ```typescript
569
+ // Inspect /subagents-status detail, artifact metadata/output logs, and run doctor. Extension loader errors usually appear in child output logs.
570
+ ```
package/slash-commands.ts CHANGED
@@ -3,7 +3,7 @@ import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
5
5
  import { Key, matchesKey } from "@mariozechner/pi-tui";
6
- import { discoverAgents, discoverAgentsAll } from "./agents.ts";
6
+ import { discoverAgents, discoverAgentsAll, type ChainConfig } from "./agents.ts";
7
7
  import { AgentManagerComponent, type ManagerResult } from "./agent-manager.ts";
8
8
  import { SubagentsStatusComponent } from "./subagents-status.ts";
9
9
  import { discoverAvailableSkills } from "./skills.ts";
@@ -108,6 +108,36 @@ const makeAgentCompletions = (state: SubagentState, multiAgent: boolean) => (pre
108
108
  return agents.filter((a) => a.name.startsWith(lastWord)).map((a) => ({ value: `${beforeLastWord}${a.name}`, label: a.name }));
109
109
  };
110
110
 
111
+ const discoverSavedChains = (cwd: string): ChainConfig[] => {
112
+ const chainsByName = new Map<string, ChainConfig>();
113
+ for (const chain of discoverAgentsAll(cwd).chains) {
114
+ chainsByName.set(chain.name, chain);
115
+ }
116
+ return Array.from(chainsByName.values());
117
+ };
118
+
119
+ const makeChainCompletions = (state: SubagentState) => (prefix: string) => {
120
+ if (prefix.includes(" ")) return null;
121
+ return discoverSavedChains(state.baseCwd)
122
+ .filter((chain) => chain.name.startsWith(prefix))
123
+ .map((chain) => ({ value: chain.name, label: chain.name }));
124
+ };
125
+
126
+ const mapSavedChainSteps = (chain: ChainConfig, worktree = false): ChainStep[] => {
127
+ return (chain.steps as Array<ChainStep & { skills?: string[] | false }>).map((step) => {
128
+ if (isParallelStep(step)) return worktree ? { ...step, worktree: true } : { ...step };
129
+ return {
130
+ agent: step.agent,
131
+ task: step.task || undefined,
132
+ output: step.output,
133
+ reads: step.reads,
134
+ progress: step.progress,
135
+ skill: step.skill ?? step.skills,
136
+ model: step.model,
137
+ };
138
+ });
139
+ };
140
+
111
141
  async function requestSlashRun(
112
142
  pi: ExtensionAPI,
113
143
  ctx: ExtensionContext,
@@ -328,19 +358,7 @@ async function openAgentManager(
328
358
  if (result.action === "launch") {
329
359
  await runSlashSubagent(pi, ctx, { agent: result.agent, task: result.task, ...launchOptions });
330
360
  } else if (result.action === "launch-chain") {
331
- const chainParam = (result.chain.steps as unknown as ChainStep[]).map((step) => {
332
- if (isParallelStep(step)) return result.worktree ? { ...step, worktree: true } : { ...step };
333
- return {
334
- agent: step.agent,
335
- task: step.task || undefined,
336
- output: step.output,
337
- reads: step.reads,
338
- progress: step.progress,
339
- skill: step.skill ?? (step as typeof step & { skills?: string[] | false }).skills,
340
- model: step.model,
341
- };
342
- });
343
- await runSlashSubagent(pi, ctx, { chain: chainParam, task: result.task, ...launchOptions });
361
+ await runSlashSubagent(pi, ctx, { chain: mapSavedChainSteps(result.chain, result.worktree), task: result.task, ...launchOptions });
344
362
  } else if (result.action === "parallel") {
345
363
  await runSlashSubagent(pi, ctx, {
346
364
  tasks: result.tasks,
@@ -489,24 +507,53 @@ export function registerSlashCommands(
489
507
  },
490
508
  });
491
509
 
510
+ pi.registerCommand("run-chain", {
511
+ description: "Run a saved chain: /run-chain chainName -- task [--bg] [--fork]",
512
+ getArgumentCompletions: makeChainCompletions(state),
513
+ handler: async (args, ctx) => {
514
+ const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
515
+ const delimiterIndex = cleanedArgs.indexOf(" -- ");
516
+ const usage = "Usage: /run-chain <chainName> -- <task> [--bg] [--fork]";
517
+ if (delimiterIndex === -1) {
518
+ ctx.ui.notify(usage, "error");
519
+ return;
520
+ }
521
+ const chainName = cleanedArgs.slice(0, delimiterIndex).trim();
522
+ const task = cleanedArgs.slice(delimiterIndex + 4).trim();
523
+ if (!chainName || !task) {
524
+ ctx.ui.notify(usage, "error");
525
+ return;
526
+ }
527
+ const chain = discoverSavedChains(state.baseCwd).find((candidate) => candidate.name === chainName);
528
+ if (!chain) {
529
+ ctx.ui.notify(`Unknown chain: ${chainName}`, "error");
530
+ return;
531
+ }
532
+ const params: SubagentParamsLike = { chain: mapSavedChainSteps(chain), task, clarify: false, agentScope: "both" };
533
+ if (bg) params.async = true;
534
+ if (fork) params.context = "fork";
535
+ await runSlashSubagent(pi, ctx, params);
536
+ },
537
+ });
538
+
492
539
  pi.registerCommand("parallel", {
493
540
  description: "Run agents in parallel: /parallel scout \"task1\" -> reviewer \"task2\" [--bg] [--fork]",
494
541
  getArgumentCompletions: makeAgentCompletions(state, true),
495
- handler: async (args, ctx) => {
496
- const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
497
- const parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
498
- if (!parsed) return;
499
- const tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
500
- agent: name,
501
- task: stepTask ?? parsed.task,
502
- ...(config.output !== undefined ? { output: config.output } : {}),
503
- ...(config.reads !== undefined ? { reads: config.reads } : {}),
504
- ...(config.model ? { model: config.model } : {}),
505
- ...(config.skill !== undefined ? { skill: config.skill } : {}),
506
- ...(config.progress !== undefined ? { progress: config.progress } : {}),
507
- }));
508
- const params: SubagentParamsLike = { tasks, clarify: false, agentScope: "both" };
509
- if (bg) params.async = true;
542
+ handler: async (args, ctx) => {
543
+ const { args: cleanedArgs, bg, fork } = extractExecutionFlags(args);
544
+ const parsed = parseAgentArgs(state, cleanedArgs, "parallel", ctx);
545
+ if (!parsed) return;
546
+ const tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
547
+ agent: name,
548
+ task: stepTask ?? parsed.task,
549
+ ...(config.output !== undefined ? { output: config.output } : {}),
550
+ ...(config.reads !== undefined ? { reads: config.reads } : {}),
551
+ ...(config.model ? { model: config.model } : {}),
552
+ ...(config.skill !== undefined ? { skill: config.skill } : {}),
553
+ ...(config.progress !== undefined ? { progress: config.progress } : {}),
554
+ }));
555
+ const params: SubagentParamsLike = { tasks, clarify: false, agentScope: "both" };
556
+ if (bg) params.async = true;
510
557
  if (fork) params.context = "fork";
511
558
  await runSlashSubagent(pi, ctx, params);
512
559
  },
@@ -522,6 +569,13 @@ export function registerSlashCommands(
522
569
  },
523
570
  });
524
571
 
572
+ pi.registerCommand("subagents-doctor", {
573
+ description: "Show subagent diagnostics",
574
+ handler: async (_args, ctx) => {
575
+ await runSlashSubagent(pi, ctx, { action: "doctor" });
576
+ },
577
+ });
578
+
525
579
  pi.registerShortcut("ctrl+shift+a", {
526
580
  handler: async (ctx) => {
527
581
  await openAgentManager(pi, ctx);
@@ -1,8 +1,8 @@
1
1
  import type { AgentToolResult } from "@mariozechner/pi-agent-core";
2
2
  import type { Message } from "@mariozechner/pi-ai";
3
- import type { SubagentParamsLike } from "./subagent-executor.js";
4
- import type { SlashSubagentResponse, SlashSubagentUpdate } from "./slash-bridge.js";
5
- import { type Details, type SingleResult, type Usage, SLASH_RESULT_TYPE } from "./types.js";
3
+ import type { SubagentParamsLike } from "./subagent-executor.ts";
4
+ import type { SlashSubagentResponse, SlashSubagentUpdate } from "./slash-bridge.ts";
5
+ import { type Details, type SingleResult, type Usage, SLASH_RESULT_TYPE } from "./types.ts";
6
6
 
7
7
  export interface SlashMessageDetails {
8
8
  requestId: string;
@@ -9,6 +9,7 @@ import { ChainClarifyComponent, type ChainClarifyResult, type ModelInfo } from "
9
9
  import { executeChain } from "./chain-execution.ts";
10
10
  import { resolveExecutionAgentScope } from "./agent-scope.ts";
11
11
  import { handleManagementAction } from "./agent-management.ts";
12
+ import { buildDoctorReport } from "./doctor.ts";
12
13
  import { runSync } from "./execution.ts";
13
14
  import { resolveModelCandidate } from "./model-fallback.ts";
14
15
  import { aggregateParallelOutputs } from "./parallel-utils.ts";
@@ -1544,6 +1545,39 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
1544
1545
  const requestCwd = resolveRequestedCwd(ctx.cwd, params.cwd);
1545
1546
  const paramsWithResolvedCwd = params.cwd === undefined ? params : { ...params, cwd: requestCwd };
1546
1547
  if (params.action) {
1548
+ if (params.action === "doctor") {
1549
+ let currentSessionFile: string | null = null;
1550
+ let currentSessionId = deps.state.currentSessionId;
1551
+ let sessionError: string | undefined;
1552
+ try {
1553
+ currentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
1554
+ currentSessionId = ctx.sessionManager.getSessionId();
1555
+ } catch (error) {
1556
+ sessionError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
1557
+ }
1558
+ let orchestratorTarget: string | undefined;
1559
+ try {
1560
+ orchestratorTarget = resolveIntercomSessionTarget(deps.pi.getSessionName(), ctx.sessionManager.getSessionId());
1561
+ } catch {}
1562
+ return {
1563
+ content: [{
1564
+ type: "text",
1565
+ text: buildDoctorReport({
1566
+ cwd: requestCwd,
1567
+ config: deps.config,
1568
+ state: deps.state,
1569
+ context: paramsWithResolvedCwd.context,
1570
+ requestedSessionDir: paramsWithResolvedCwd.sessionDir,
1571
+ currentSessionFile,
1572
+ currentSessionId,
1573
+ orchestratorTarget,
1574
+ sessionError,
1575
+ expandTilde: deps.expandTilde,
1576
+ }),
1577
+ }],
1578
+ details: { mode: "management", results: [] },
1579
+ };
1580
+ }
1547
1581
  if (params.action === "status") {
1548
1582
  const foreground = getForegroundControl(deps.state, paramsWithResolvedCwd.id ?? paramsWithResolvedCwd.runId);
1549
1583
  if (foreground) return foregroundStatusResult(foreground);
@@ -1576,7 +1610,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
1576
1610
  details: { mode: "management", results: [] },
1577
1611
  };
1578
1612
  }
1579
- const validActions = ["list", "get", "create", "update", "delete", "status", "interrupt"];
1613
+ const validActions = ["list", "get", "create", "update", "delete", "status", "interrupt", "doctor"];
1580
1614
  if (!validActions.includes(params.action)) {
1581
1615
  return {
1582
1616
  content: [{ type: "text", text: `Unknown action: ${params.action}. Valid: ${validActions.join(", ")}` }],
@@ -3,10 +3,10 @@ import * as path from "node:path";
3
3
  import type { Theme } from "@mariozechner/pi-coding-agent";
4
4
  import type { Component, TUI } from "@mariozechner/pi-tui";
5
5
  import { matchesKey, truncateToWidth } from "@mariozechner/pi-tui";
6
- import { type AsyncRunOverlayData, type AsyncRunSummary, listAsyncRunsForOverlay } from "./async-status.js";
7
- import { ASYNC_DIR } from "./types.js";
8
- import { formatDuration, formatTokens, shortenPath } from "./formatters.js";
9
- import { formatScrollInfo, renderFooter, renderHeader, row } from "./render-helpers.js";
6
+ import { type AsyncRunOverlayData, type AsyncRunSummary, listAsyncRunsForOverlay } from "./async-status.ts";
7
+ import { ASYNC_DIR } from "./types.ts";
8
+ import { formatDuration, formatTokens, shortenPath } from "./formatters.ts";
9
+ import { formatScrollInfo, renderFooter, renderHeader, row } from "./render-helpers.ts";
10
10
 
11
11
  const AUTO_REFRESH_MS = 2000;
12
12
  const DETAIL_EVENT_LIMIT = 8;
@@ -178,13 +178,19 @@ export class SubagentsStatusComponent implements Component {
178
178
  private recent: AsyncRunSummary[] = [];
179
179
  private rows: StatusRow[] = [];
180
180
  private errorMessage?: string;
181
+ private tui: TUI;
182
+ private theme: Theme;
183
+ private done: () => void;
181
184
 
182
185
  constructor(
183
- private tui: TUI,
184
- private theme: Theme,
185
- private done: () => void,
186
+ tui: TUI,
187
+ theme: Theme,
188
+ done: () => void,
186
189
  deps: StatusOverlayDeps = {},
187
190
  ) {
191
+ this.tui = tui;
192
+ this.theme = theme;
193
+ this.done = done;
188
194
  this.listRunsForOverlay = deps.listRunsForOverlay ?? listAsyncRunsForOverlay;
189
195
  const refreshMs = deps.refreshMs ?? AUTO_REFRESH_MS;
190
196
  this.reload();