@fitchmultz/pi-subagents 0.0.0-stage → 0.44.4

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 (342) hide show
  1. package/CHANGELOG.md +1645 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1529 -2
  4. package/agents/context-builder.md +97 -0
  5. package/agents/debugger.md +51 -0
  6. package/agents/delegate.md +13 -0
  7. package/agents/fixer.md +37 -0
  8. package/agents/oracle.md +71 -0
  9. package/agents/planner.md +72 -0
  10. package/agents/researcher.md +51 -0
  11. package/agents/reviewer-claude.md +69 -0
  12. package/agents/reviewer-gpt.md +68 -0
  13. package/agents/reviewer-ponytail.md +82 -0
  14. package/agents/reviewer-security.md +81 -0
  15. package/agents/reviewer.md +69 -0
  16. package/agents/scout.md +66 -0
  17. package/agents/ui-designer.md +51 -0
  18. package/agents/watcher.md +26 -0
  19. package/agents/worker.md +46 -0
  20. package/agents/writer.md +36 -0
  21. package/dist/agents/agent-management.js +923 -0
  22. package/dist/agents/agent-scope.js +7 -0
  23. package/dist/agents/agent-selection.js +20 -0
  24. package/dist/agents/agent-serializer.js +100 -0
  25. package/dist/agents/agents.js +752 -0
  26. package/dist/agents/chain-serializer.js +367 -0
  27. package/dist/agents/frontmatter.js +21 -0
  28. package/dist/agents/identity.js +29 -0
  29. package/dist/agents/skills.js +570 -0
  30. package/dist/extension/build-info.js +1 -0
  31. package/dist/extension/compact-tools.js +112 -0
  32. package/dist/extension/config.js +22 -0
  33. package/dist/extension/control-notices.js +29 -0
  34. package/dist/extension/doctor.js +143 -0
  35. package/dist/extension/fanout-child.js +326 -0
  36. package/dist/extension/index.js +577 -0
  37. package/dist/extension/schemas.js +416 -0
  38. package/dist/extension/tool-input.js +20 -0
  39. package/dist/extension/tool-result.js +14 -0
  40. package/dist/history/canonical-result.js +81 -0
  41. package/dist/history/database.js +72 -0
  42. package/dist/history/index.js +194 -0
  43. package/dist/history/ingest.js +447 -0
  44. package/dist/history/queries.js +350 -0
  45. package/dist/history/store.js +216 -0
  46. package/dist/history/types.js +4 -0
  47. package/dist/history/worker.js +461 -0
  48. package/dist/intercom/intercom-bridge.js +112 -0
  49. package/dist/intercom/live-intercom.js +94 -0
  50. package/dist/intercom/result-intercom.js +373 -0
  51. package/dist/pi-intercom/agent-dir.js +10 -0
  52. package/dist/pi-intercom/broker/broker.js +426 -0
  53. package/dist/pi-intercom/broker/client.js +492 -0
  54. package/dist/pi-intercom/broker/framing.js +65 -0
  55. package/dist/pi-intercom/broker/paths.js +42 -0
  56. package/dist/pi-intercom/broker/pid.js +66 -0
  57. package/dist/pi-intercom/broker/spawn.js +221 -0
  58. package/dist/pi-intercom/config.js +102 -0
  59. package/dist/pi-intercom/index.js +2942 -0
  60. package/dist/pi-intercom/reply-tracker.js +182 -0
  61. package/dist/pi-intercom/session-targets.js +128 -0
  62. package/dist/pi-intercom/subagent-live-events.js +132 -0
  63. package/dist/pi-intercom/topics.js +157 -0
  64. package/dist/pi-intercom/types.js +163 -0
  65. package/dist/pi-intercom/ui/compose.js +269 -0
  66. package/dist/pi-intercom/ui/inline-message.js +80 -0
  67. package/dist/pi-intercom/ui/session-list.js +124 -0
  68. package/dist/runs/background/async-control.js +95 -0
  69. package/dist/runs/background/async-execution.js +676 -0
  70. package/dist/runs/background/async-job-tracker.js +383 -0
  71. package/dist/runs/background/async-result-file.js +58 -0
  72. package/dist/runs/background/async-resume.js +305 -0
  73. package/dist/runs/background/async-status.js +442 -0
  74. package/dist/runs/background/completion-dedupe.js +69 -0
  75. package/dist/runs/background/completion-delivery.js +245 -0
  76. package/dist/runs/background/notify.js +71 -0
  77. package/dist/runs/background/parallel-groups.js +55 -0
  78. package/dist/runs/background/result-watcher.js +426 -0
  79. package/dist/runs/background/run-id-resolver.js +64 -0
  80. package/dist/runs/background/run-status.js +382 -0
  81. package/dist/runs/background/stale-run-reconciler.js +335 -0
  82. package/dist/runs/background/subagent-runner-launcher.js +55 -0
  83. package/dist/runs/background/subagent-runner.js +1872 -0
  84. package/dist/runs/background/top-level-async.js +5 -0
  85. package/dist/runs/foreground/chain-clarify.js +1152 -0
  86. package/dist/runs/foreground/clarify-invocation.js +72 -0
  87. package/dist/runs/foreground/execution-input.js +423 -0
  88. package/dist/runs/foreground/foreground-control.js +817 -0
  89. package/dist/runs/foreground/question-control.js +77 -0
  90. package/dist/runs/foreground/run-async-path.js +205 -0
  91. package/dist/runs/foreground/subagent-executor.js +442 -0
  92. package/dist/runs/foreground/subagent-params.js +165 -0
  93. package/dist/runs/foreground/wait-run.js +139 -0
  94. package/dist/runs/shared/acceptance-contract.js +253 -0
  95. package/dist/runs/shared/acceptance-evaluation.js +256 -0
  96. package/dist/runs/shared/acceptance-finalization.js +280 -0
  97. package/dist/runs/shared/acceptance-reports.js +147 -0
  98. package/dist/runs/shared/acceptance.js +4 -0
  99. package/dist/runs/shared/chain-outputs.js +110 -0
  100. package/dist/runs/shared/child-attempt.js +693 -0
  101. package/dist/runs/shared/child-execution-cwd.js +96 -0
  102. package/dist/runs/shared/child-observations.js +114 -0
  103. package/dist/runs/shared/claude-code.js +199 -0
  104. package/dist/runs/shared/completion-guard.js +31 -0
  105. package/dist/runs/shared/dynamic-fanout.js +274 -0
  106. package/dist/runs/shared/history-index.js +72 -0
  107. package/dist/runs/shared/mcp-direct-tool-allowlist.js +334 -0
  108. package/dist/runs/shared/model-fallback.js +185 -0
  109. package/dist/runs/shared/mutating-tool-guard.js +170 -0
  110. package/dist/runs/shared/native-finalization.js +101 -0
  111. package/dist/runs/shared/native-usage.js +153 -0
  112. package/dist/runs/shared/nested-events.js +700 -0
  113. package/dist/runs/shared/nested-path.js +48 -0
  114. package/dist/runs/shared/nested-render.js +108 -0
  115. package/dist/runs/shared/parallel-utils.js +61 -0
  116. package/dist/runs/shared/parent-receipts.js +68 -0
  117. package/dist/runs/shared/parent-usage.js +121 -0
  118. package/dist/runs/shared/pi-args.js +310 -0
  119. package/dist/runs/shared/pi-spawn.js +52 -0
  120. package/dist/runs/shared/run-history-queries.js +45 -0
  121. package/dist/runs/shared/run-history.js +103 -0
  122. package/dist/runs/shared/run-id-ambiguity.js +4 -0
  123. package/dist/runs/shared/run-records.js +679 -0
  124. package/dist/runs/shared/session-cwd-preload.js +33 -0
  125. package/dist/runs/shared/single-output.js +222 -0
  126. package/dist/runs/shared/streaming-text.js +15 -0
  127. package/dist/runs/shared/structured-output.js +55 -0
  128. package/dist/runs/shared/subagent-control.js +179 -0
  129. package/dist/runs/shared/subagent-prompt-runtime.js +197 -0
  130. package/dist/runs/shared/subagent-tool-loop-guard.js +66 -0
  131. package/dist/runs/shared/supervisor-questions.js +320 -0
  132. package/dist/runs/shared/workflow-graph.js +214 -0
  133. package/dist/runs/shared/workflow-policy.js +84 -0
  134. package/dist/runs/shared/worktree.js +525 -0
  135. package/dist/shared/agent-context-policy.js +126 -0
  136. package/dist/shared/artifacts.js +96 -0
  137. package/dist/shared/atomic-json.js +13 -0
  138. package/dist/shared/cooperative.js +20 -0
  139. package/dist/shared/execution-cwd.js +14 -0
  140. package/dist/shared/file-coalescer.js +21 -0
  141. package/dist/shared/fork-context.js +49 -0
  142. package/dist/shared/formatters.js +85 -0
  143. package/dist/shared/journal-reader.js +407 -0
  144. package/dist/shared/lazy-tools.js +32 -0
  145. package/dist/shared/model-info.js +68 -0
  146. package/dist/shared/native-session.js +15 -0
  147. package/dist/shared/native-tui.js +18 -0
  148. package/dist/shared/native-typebox.js +20 -0
  149. package/dist/shared/post-exit-stdio-guard.js +197 -0
  150. package/dist/shared/prompt-sections.js +8 -0
  151. package/dist/shared/session-entries.js +52 -0
  152. package/dist/shared/session-identity.js +14 -0
  153. package/dist/shared/settings.js +254 -0
  154. package/dist/shared/status-format.js +111 -0
  155. package/dist/shared/temp-root.js +114 -0
  156. package/dist/shared/types.js +207 -0
  157. package/dist/shared/ui-mode.js +3 -0
  158. package/dist/shared/utils.js +334 -0
  159. package/dist/slash/prompt-template-bridge.js +298 -0
  160. package/dist/slash/slash-bridge.js +116 -0
  161. package/dist/slash/slash-commands.js +607 -0
  162. package/dist/slash/slash-live-state.js +246 -0
  163. package/dist/tui/action-hints.js +75 -0
  164. package/dist/tui/agent-history.js +197 -0
  165. package/dist/tui/agent-view.js +1656 -0
  166. package/dist/tui/render.js +1515 -0
  167. package/docs/intercom.md +579 -0
  168. package/docs/performance.md +121 -0
  169. package/package.json +101 -4
  170. package/prompts/gather-context-and-clarify.md +14 -0
  171. package/prompts/parallel-cleanup.md +62 -0
  172. package/prompts/parallel-context-build.md +56 -0
  173. package/prompts/parallel-handoff-plan.md +62 -0
  174. package/prompts/parallel-research.md +51 -0
  175. package/prompts/parallel-review.md +59 -0
  176. package/prompts/review-loop.md +42 -0
  177. package/scripts/build.mjs +134 -0
  178. package/scripts/check-compat.mjs +48 -0
  179. package/scripts/compat-host.mjs +19 -0
  180. package/scripts/linux-smoke.sh +51 -0
  181. package/scripts/local-install-smoke.mjs +66 -0
  182. package/scripts/native-package-smoke.mjs +82 -0
  183. package/scripts/package-smoke.mjs +205 -0
  184. package/scripts/prepare.mjs +67 -0
  185. package/scripts/real-pi-smoke.mjs +415 -0
  186. package/scripts/run-tests.mjs +149 -0
  187. package/skills/pi-intercom/SKILL.md +113 -0
  188. package/skills/pi-intercom/evals/evals.json +29 -0
  189. package/skills/pi-intercom/evals/trigger-evals.json +47 -0
  190. package/skills/pi-intercom/references/peer-sessions.md +69 -0
  191. package/skills/pi-subagents/SKILL.md +76 -0
  192. package/skills/pi-subagents/evals/evals.json +29 -0
  193. package/skills/pi-subagents/evals/trigger-evals.json +42 -0
  194. package/skills/pi-subagents/references/full-orchestration-guide.md +863 -0
  195. package/src/agents/agent-management.ts +813 -0
  196. package/src/agents/agent-scope.ts +7 -0
  197. package/src/agents/agent-selection.ts +23 -0
  198. package/src/agents/agent-serializer.ts +99 -0
  199. package/src/agents/agents.ts +916 -0
  200. package/src/agents/chain-serializer.ts +329 -0
  201. package/src/agents/frontmatter.ts +21 -0
  202. package/src/agents/identity.ts +30 -0
  203. package/src/agents/skills.ts +637 -0
  204. package/src/extension/build-info.ts +2 -0
  205. package/src/extension/compact-tools.ts +114 -0
  206. package/src/extension/config.ts +23 -0
  207. package/src/extension/control-notices.ts +47 -0
  208. package/src/extension/doctor.ts +171 -0
  209. package/src/extension/fanout-child.ts +312 -0
  210. package/src/extension/index.ts +649 -0
  211. package/src/extension/schemas.ts +444 -0
  212. package/src/extension/tool-input.ts +19 -0
  213. package/src/extension/tool-result.ts +16 -0
  214. package/src/history/canonical-result.ts +62 -0
  215. package/src/history/database.ts +59 -0
  216. package/src/history/index.ts +152 -0
  217. package/src/history/ingest.ts +332 -0
  218. package/src/history/queries.ts +241 -0
  219. package/src/history/store.ts +203 -0
  220. package/src/history/types.ts +166 -0
  221. package/src/history/worker.ts +317 -0
  222. package/src/intercom/intercom-bridge.ts +123 -0
  223. package/src/intercom/live-intercom.ts +104 -0
  224. package/src/intercom/result-intercom.ts +429 -0
  225. package/src/pi-intercom/agent-dir.ts +9 -0
  226. package/src/pi-intercom/broker/broker.ts +463 -0
  227. package/src/pi-intercom/broker/client.ts +558 -0
  228. package/src/pi-intercom/broker/framing.ts +77 -0
  229. package/src/pi-intercom/broker/paths.ts +47 -0
  230. package/src/pi-intercom/broker/pid.ts +59 -0
  231. package/src/pi-intercom/broker/spawn.ts +248 -0
  232. package/src/pi-intercom/config.ts +142 -0
  233. package/src/pi-intercom/index.ts +2929 -0
  234. package/src/pi-intercom/reply-tracker.ts +211 -0
  235. package/src/pi-intercom/session-targets.ts +172 -0
  236. package/src/pi-intercom/subagent-live-events.ts +133 -0
  237. package/src/pi-intercom/topics.ts +125 -0
  238. package/src/pi-intercom/types.ts +271 -0
  239. package/src/pi-intercom/ui/compose.ts +289 -0
  240. package/src/pi-intercom/ui/inline-message.ts +92 -0
  241. package/src/pi-intercom/ui/session-list.ts +139 -0
  242. package/src/runs/background/async-control.ts +89 -0
  243. package/src/runs/background/async-execution.ts +850 -0
  244. package/src/runs/background/async-job-tracker.ts +385 -0
  245. package/src/runs/background/async-result-file.ts +62 -0
  246. package/src/runs/background/async-resume.ts +333 -0
  247. package/src/runs/background/async-status.ts +481 -0
  248. package/src/runs/background/completion-dedupe.ts +79 -0
  249. package/src/runs/background/completion-delivery.ts +195 -0
  250. package/src/runs/background/notify.ts +123 -0
  251. package/src/runs/background/parallel-groups.ts +53 -0
  252. package/src/runs/background/result-watcher.ts +423 -0
  253. package/src/runs/background/run-id-resolver.ts +69 -0
  254. package/src/runs/background/run-status.ts +384 -0
  255. package/src/runs/background/stale-run-reconciler.ts +369 -0
  256. package/src/runs/background/subagent-runner-launcher.ts +56 -0
  257. package/src/runs/background/subagent-runner.ts +2053 -0
  258. package/src/runs/background/top-level-async.ts +13 -0
  259. package/src/runs/foreground/chain-clarify.ts +1340 -0
  260. package/src/runs/foreground/clarify-invocation.ts +77 -0
  261. package/src/runs/foreground/execution-input.ts +467 -0
  262. package/src/runs/foreground/foreground-control.ts +821 -0
  263. package/src/runs/foreground/question-control.ts +70 -0
  264. package/src/runs/foreground/run-async-path.ts +243 -0
  265. package/src/runs/foreground/subagent-executor.ts +501 -0
  266. package/src/runs/foreground/subagent-params.ts +320 -0
  267. package/src/runs/foreground/wait-run.ts +121 -0
  268. package/src/runs/shared/acceptance-contract.ts +257 -0
  269. package/src/runs/shared/acceptance-evaluation.ts +275 -0
  270. package/src/runs/shared/acceptance-finalization.ts +353 -0
  271. package/src/runs/shared/acceptance-reports.ts +152 -0
  272. package/src/runs/shared/acceptance.ts +33 -0
  273. package/src/runs/shared/chain-outputs.ts +114 -0
  274. package/src/runs/shared/child-attempt.ts +623 -0
  275. package/src/runs/shared/child-execution-cwd.ts +91 -0
  276. package/src/runs/shared/child-observations.ts +99 -0
  277. package/src/runs/shared/claude-code.ts +243 -0
  278. package/src/runs/shared/completion-guard.ts +35 -0
  279. package/src/runs/shared/dynamic-fanout.ts +295 -0
  280. package/src/runs/shared/history-index.ts +69 -0
  281. package/src/runs/shared/mcp-direct-tool-allowlist.ts +376 -0
  282. package/src/runs/shared/model-fallback.ts +219 -0
  283. package/src/runs/shared/mutating-tool-guard.ts +194 -0
  284. package/src/runs/shared/native-finalization.ts +128 -0
  285. package/src/runs/shared/native-usage.ts +113 -0
  286. package/src/runs/shared/nested-events.ts +752 -0
  287. package/src/runs/shared/nested-path.ts +52 -0
  288. package/src/runs/shared/nested-render.ts +119 -0
  289. package/src/runs/shared/parallel-utils.ts +153 -0
  290. package/src/runs/shared/parent-receipts.ts +59 -0
  291. package/src/runs/shared/parent-usage.ts +121 -0
  292. package/src/runs/shared/pi-args.ts +350 -0
  293. package/src/runs/shared/pi-spawn.ts +52 -0
  294. package/src/runs/shared/run-history-queries.ts +44 -0
  295. package/src/runs/shared/run-history.ts +93 -0
  296. package/src/runs/shared/run-id-ambiguity.ts +4 -0
  297. package/src/runs/shared/run-records.ts +607 -0
  298. package/src/runs/shared/session-cwd-preload.ts +27 -0
  299. package/src/runs/shared/single-output.ts +267 -0
  300. package/src/runs/shared/streaming-text.ts +18 -0
  301. package/src/runs/shared/structured-output.ts +68 -0
  302. package/src/runs/shared/subagent-control.ts +219 -0
  303. package/src/runs/shared/subagent-prompt-runtime.ts +201 -0
  304. package/src/runs/shared/subagent-tool-loop-guard.ts +89 -0
  305. package/src/runs/shared/supervisor-questions.ts +372 -0
  306. package/src/runs/shared/workflow-graph.ts +220 -0
  307. package/src/runs/shared/workflow-policy.ts +125 -0
  308. package/src/runs/shared/worktree.ts +635 -0
  309. package/src/shared/agent-context-policy.ts +184 -0
  310. package/src/shared/artifacts.ts +95 -0
  311. package/src/shared/atomic-json.ts +16 -0
  312. package/src/shared/cooperative.ts +21 -0
  313. package/src/shared/execution-cwd.ts +14 -0
  314. package/src/shared/file-coalescer.ts +29 -0
  315. package/src/shared/fork-context.ts +68 -0
  316. package/src/shared/formatters.ts +84 -0
  317. package/src/shared/journal-reader.ts +333 -0
  318. package/src/shared/lazy-tools.ts +36 -0
  319. package/src/shared/model-info.ts +81 -0
  320. package/src/shared/native-session.ts +8 -0
  321. package/src/shared/native-tui.ts +10 -0
  322. package/src/shared/native-typebox.ts +14 -0
  323. package/src/shared/post-exit-stdio-guard.ts +204 -0
  324. package/src/shared/prompt-sections.ts +10 -0
  325. package/src/shared/session-entries.ts +49 -0
  326. package/src/shared/session-identity.ts +18 -0
  327. package/src/shared/settings.ts +384 -0
  328. package/src/shared/status-format.ts +123 -0
  329. package/src/shared/temp-root.ts +107 -0
  330. package/src/shared/types.ts +1259 -0
  331. package/src/shared/ui-mode.ts +5 -0
  332. package/src/shared/utils.ts +342 -0
  333. package/src/slash/prompt-template-bridge.ts +408 -0
  334. package/src/slash/slash-bridge.ts +178 -0
  335. package/src/slash/slash-commands.ts +617 -0
  336. package/src/slash/slash-live-state.ts +292 -0
  337. package/src/tui/action-hints.ts +69 -0
  338. package/src/tui/agent-history.ts +210 -0
  339. package/src/tui/agent-view.ts +1323 -0
  340. package/src/tui/render.ts +1550 -0
  341. package/tsconfig.build.json +12 -0
  342. package/tsconfig.json +19 -0
@@ -0,0 +1,607 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { Key, matchesKey } from "@earendil-works/pi-tui";
5
+ import { discoverAgents, discoverAgentsAll } from "../agents/agents.js";
6
+ import { isDynamicParallelStep, isParallelStep } from "../shared/settings.js";
7
+ import { assertJsonSchemaObject } from "../runs/shared/structured-output.js";
8
+ import { applySlashUpdate, buildSlashInitialResult, failSlashResult, finalizeSlashResult, } from "./slash-live-state.js";
9
+ import { SLASH_RESULT_TYPE, SLASH_SUBAGENT_CANCEL_EVENT, SLASH_SUBAGENT_REQUEST_EVENT, SLASH_SUBAGENT_RESPONSE_EVENT, SLASH_SUBAGENT_STARTED_EVENT, SLASH_SUBAGENT_UPDATE_EVENT, } from "../shared/types.js";
10
+ import { isTuiContext } from "../shared/ui-mode.js";
11
+ import { resolveExecutionCwd } from "../shared/execution-cwd.js";
12
+ const parseInlineConfig = (raw) => {
13
+ const config = {};
14
+ for (const part of raw.split(",")) {
15
+ const trimmed = part.trim();
16
+ if (!trimmed)
17
+ continue;
18
+ const eq = trimmed.indexOf("=");
19
+ if (eq === -1) {
20
+ if (trimmed !== "progress")
21
+ throw new Error(`Unknown inline option '${trimmed}'.`);
22
+ config.progress = true;
23
+ continue;
24
+ }
25
+ const key = trimmed.slice(0, eq).trim();
26
+ const val = trimmed.slice(eq + 1).trim();
27
+ switch (key) {
28
+ case "output":
29
+ config.output = val === "false" ? false : val;
30
+ break;
31
+ case "outputMode":
32
+ if (val !== "inline" && val !== "file-only")
33
+ throw new Error("outputMode must be inline or file-only.");
34
+ config.outputMode = val;
35
+ break;
36
+ case "reads":
37
+ config.reads = val === "false" ? false : val.split("+").filter(Boolean);
38
+ break;
39
+ case "model":
40
+ config.model = val || undefined;
41
+ break;
42
+ case "skill":
43
+ case "skills":
44
+ config.skill = val === "false" ? false : val.split("+").filter(Boolean);
45
+ break;
46
+ case "progress":
47
+ if (val !== "true" && val !== "false")
48
+ throw new Error("progress must be true or false.");
49
+ config.progress = val === "true";
50
+ break;
51
+ default: throw new Error(`Unknown inline option '${key}'.`);
52
+ }
53
+ }
54
+ return config;
55
+ };
56
+ const parseAgentToken = (token) => {
57
+ const bracket = token.indexOf("[");
58
+ if (bracket === -1)
59
+ return { name: token, config: {} };
60
+ const end = token.lastIndexOf("]");
61
+ if (end !== token.length - 1 || end < bracket)
62
+ throw new Error(`Malformed inline options in '${token}'; close the trailing ].`);
63
+ const name = token.slice(0, bracket);
64
+ if (!name)
65
+ throw new Error("Agent name is required before inline options.");
66
+ return { name, config: parseInlineConfig(token.slice(bracket + 1, end)) };
67
+ };
68
+ const unwrapQuotedTask = (task) => {
69
+ const match = task.match(/^(?:"([\s\S]*)"|'([\s\S]*)')$/);
70
+ return match ? (match[1] ?? match[2] ?? "") : task;
71
+ };
72
+ const extractExecutionFlags = (rawArgs) => {
73
+ let args = rawArgs.trim();
74
+ let asyncMode;
75
+ let fork = false;
76
+ while (true) {
77
+ if (args.endsWith(" --bg") || args === "--bg") {
78
+ if (asyncMode === false)
79
+ return { args, fork, error: "Choose only one of --bg or --fg" };
80
+ asyncMode = true;
81
+ args = args === "--bg" ? "" : args.slice(0, -5).trim();
82
+ continue;
83
+ }
84
+ if (args.endsWith(" --fg") || args === "--fg") {
85
+ if (asyncMode === true)
86
+ return { args, fork, error: "Choose only one of --bg or --fg" };
87
+ asyncMode = false;
88
+ args = args === "--fg" ? "" : args.slice(0, -5).trim();
89
+ continue;
90
+ }
91
+ if (args.endsWith(" --fork") || args === "--fork") {
92
+ fork = true;
93
+ args = args === "--fork" ? "" : args.slice(0, -7).trim();
94
+ continue;
95
+ }
96
+ break;
97
+ }
98
+ return { args, async: asyncMode, fork };
99
+ };
100
+ const projectTrusted = (state) => state.lastUiContext ? state.lastUiContext.isProjectTrusted() : true;
101
+ const completionCwd = (pi, state) => {
102
+ try {
103
+ return state.lastUiContext ? resolveExecutionCwd(pi, state.lastUiContext) : state.baseCwd;
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ };
109
+ const makeAgentCompletions = (pi, state, multiAgent) => (prefix) => {
110
+ const cwd = completionCwd(pi, state);
111
+ if (!cwd)
112
+ return null;
113
+ const agents = discoverAgents(cwd, "both", { projectTrusted: projectTrusted(state) }).agents;
114
+ if (!multiAgent) {
115
+ if (prefix.includes(" "))
116
+ return null;
117
+ return agents.filter((a) => a.name.startsWith(prefix)).map((a) => ({ value: a.name, label: a.name }));
118
+ }
119
+ const lastArrow = prefix.lastIndexOf(" -> ");
120
+ const segment = lastArrow !== -1 ? prefix.slice(lastArrow + 4) : prefix;
121
+ if (segment.includes(" -- ") || segment.includes('"') || segment.includes("'"))
122
+ return null;
123
+ const lastWord = (prefix.match(/(\S*)$/) || ["", ""])[1];
124
+ const beforeLastWord = prefix.slice(0, prefix.length - lastWord.length);
125
+ if (lastWord === "->") {
126
+ return agents.map((a) => ({ value: `${prefix} ${a.name}`, label: a.name }));
127
+ }
128
+ return agents.filter((a) => a.name.startsWith(lastWord)).map((a) => ({ value: `${beforeLastWord}${a.name}`, label: a.name }));
129
+ };
130
+ const discoverSavedChains = (cwd, projectTrusted) => {
131
+ const chainsByName = new Map();
132
+ for (const chain of discoverAgentsAll(cwd, { projectTrusted }).chains) {
133
+ const existing = chainsByName.get(chain.name);
134
+ const projectOverride = existing?.source === "user" && chain.source === "project";
135
+ const sameScopeJsonOverride = existing?.source === chain.source && chain.filePath.endsWith(".chain.json") && !existing.filePath.endsWith(".chain.json");
136
+ if (!existing || projectOverride || sameScopeJsonOverride)
137
+ chainsByName.set(chain.name, chain);
138
+ }
139
+ return Array.from(chainsByName.values());
140
+ };
141
+ const makeChainCompletions = (pi, state) => (prefix) => {
142
+ const cwd = completionCwd(pi, state);
143
+ if (prefix.includes(" ") || !cwd)
144
+ return null;
145
+ return discoverSavedChains(cwd, projectTrusted(state))
146
+ .filter((chain) => chain.name.startsWith(prefix))
147
+ .map((chain) => ({ value: chain.name, label: chain.name }));
148
+ };
149
+ function loadSavedOutputSchema(chain, stepAgent, outputSchema) {
150
+ if (outputSchema === undefined)
151
+ return undefined;
152
+ if (typeof outputSchema === "string") {
153
+ const schemaPath = path.isAbsolute(outputSchema)
154
+ ? outputSchema
155
+ : path.join(path.dirname(chain.filePath), outputSchema);
156
+ const parsed = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
157
+ assertJsonSchemaObject(parsed, `outputSchema for chain '${chain.name}' step '${stepAgent}' (${schemaPath})`);
158
+ return parsed;
159
+ }
160
+ assertJsonSchemaObject(outputSchema, `outputSchema for chain '${chain.name}' step '${stepAgent}'`);
161
+ return outputSchema;
162
+ }
163
+ const mapSavedChainSteps = (chain, worktree = false) => {
164
+ return chain.steps.map((step) => {
165
+ if (isParallelStep(step)) {
166
+ const parallel = step.parallel.map((task) => {
167
+ const { outputSchema: rawOutputSchema, ...rest } = task;
168
+ const outputSchema = loadSavedOutputSchema(chain, task.agent, rawOutputSchema);
169
+ return { ...rest, ...(outputSchema ? { outputSchema } : {}) };
170
+ });
171
+ return { ...step, parallel, ...(worktree ? { worktree: true } : {}) };
172
+ }
173
+ if (isDynamicParallelStep(step)) {
174
+ const { outputSchema: rawOutputSchema, ...parallelRest } = step.parallel;
175
+ const outputSchema = loadSavedOutputSchema(chain, step.parallel.agent, rawOutputSchema);
176
+ const collectSchema = loadSavedOutputSchema(chain, `${step.collect.as} collection`, step.collect.outputSchema);
177
+ return {
178
+ ...step,
179
+ parallel: { ...parallelRest, ...(outputSchema ? { outputSchema } : {}) },
180
+ collect: { ...step.collect, ...(collectSchema ? { outputSchema: collectSchema } : {}) },
181
+ };
182
+ }
183
+ const outputSchema = loadSavedOutputSchema(chain, step.agent, step.outputSchema);
184
+ return {
185
+ agent: step.agent,
186
+ ...(step.task ? { task: step.task } : {}),
187
+ ...(step.cwd !== undefined ? { cwd: step.cwd } : {}),
188
+ ...(step.phase ? { phase: step.phase } : {}),
189
+ ...(step.label ? { label: step.label } : {}),
190
+ ...(step.as ? { as: step.as } : {}),
191
+ ...(outputSchema ? { outputSchema } : {}),
192
+ output: step.output,
193
+ outputMode: step.outputMode,
194
+ reads: step.reads,
195
+ progress: step.progress,
196
+ skill: step.skill ?? step.skills,
197
+ model: step.model,
198
+ ...(step.acceptance !== undefined ? { acceptance: step.acceptance } : {}),
199
+ };
200
+ });
201
+ };
202
+ async function requestSlashRun(pi, ctx, requestId, params, executionCwd) {
203
+ return new Promise((resolve, reject) => {
204
+ let done = false;
205
+ let started = false;
206
+ const startTimeoutMs = 15_000;
207
+ const startTimeout = setTimeout(() => {
208
+ finish(() => reject(new Error("Slash subagent bridge did not start within 15s. Ensure the extension is loaded correctly.")));
209
+ }, startTimeoutMs);
210
+ const onStarted = (data) => {
211
+ if (done || !data || typeof data !== "object")
212
+ return;
213
+ if (data.requestId !== requestId)
214
+ return;
215
+ started = true;
216
+ clearTimeout(startTimeout);
217
+ if (ctx.hasUI)
218
+ ctx.ui.setStatus("subagent-slash", "running...");
219
+ };
220
+ const onResponse = (data) => {
221
+ if (done || !data || typeof data !== "object")
222
+ return;
223
+ const response = data;
224
+ if (response.requestId !== requestId)
225
+ return;
226
+ clearTimeout(startTimeout);
227
+ finish(() => resolve(response));
228
+ };
229
+ const onUpdate = (data) => {
230
+ if (done || !data || typeof data !== "object")
231
+ return;
232
+ const update = data;
233
+ if (update.requestId !== requestId)
234
+ return;
235
+ applySlashUpdate(requestId, update);
236
+ if (!ctx.hasUI)
237
+ return;
238
+ const tool = update.currentTool ? ` ${update.currentTool}` : "";
239
+ const count = update.toolCount ?? 0;
240
+ ctx.ui.setStatus("subagent-slash", `${count} tools${tool}`);
241
+ };
242
+ const onTerminalInput = isTuiContext(ctx)
243
+ ? ctx.ui.onTerminalInput((input) => {
244
+ if (!matchesKey(input, Key.escape))
245
+ return undefined;
246
+ pi.events.emit(SLASH_SUBAGENT_CANCEL_EVENT, { requestId });
247
+ finish(() => reject(new Error("Cancelled")));
248
+ return { consume: true };
249
+ })
250
+ : undefined;
251
+ const unsubStarted = pi.events.on(SLASH_SUBAGENT_STARTED_EVENT, onStarted);
252
+ const unsubResponse = pi.events.on(SLASH_SUBAGENT_RESPONSE_EVENT, onResponse);
253
+ const unsubUpdate = pi.events.on(SLASH_SUBAGENT_UPDATE_EVENT, onUpdate);
254
+ const finish = (next) => {
255
+ if (done)
256
+ return;
257
+ done = true;
258
+ clearTimeout(startTimeout);
259
+ unsubStarted();
260
+ unsubResponse();
261
+ unsubUpdate();
262
+ onTerminalInput?.();
263
+ next();
264
+ };
265
+ pi.events.emit(SLASH_SUBAGENT_REQUEST_EVENT, { requestId, params, ...(executionCwd ? { executionCwd } : {}) });
266
+ // Bridge emits STARTED synchronously during REQUEST emit.
267
+ // If not started, no bridge received the request.
268
+ if (!started && done)
269
+ return;
270
+ if (!started) {
271
+ finish(() => reject(new Error("No slash subagent bridge responded. Ensure the subagent extension is loaded correctly.")));
272
+ }
273
+ });
274
+ }
275
+ function extractSlashMessageText(content) {
276
+ if (typeof content === "string")
277
+ return content;
278
+ if (!Array.isArray(content))
279
+ return "";
280
+ return content
281
+ .filter((part) => part?.type === "text" && typeof part.text === "string")
282
+ .map((part) => part.text)
283
+ .join("\n");
284
+ }
285
+ function formatExportPathList(paths) {
286
+ return paths.map((file) => `- \`${file}\``).join("\n");
287
+ }
288
+ function collectResultPaths(results, getPath) {
289
+ return results
290
+ .map(getPath)
291
+ .filter((file) => typeof file === "string" && file.length > 0);
292
+ }
293
+ function buildSlashExportText(response) {
294
+ const output = extractSlashMessageText(response.result.content) || response.errorText || "(no output)";
295
+ const results = response.result.details?.results ?? [];
296
+ const sessionFiles = collectResultPaths(results, (result) => result.sessionFile);
297
+ const savedOutputs = collectResultPaths(results, (result) => result.savedOutputPath);
298
+ const artifactOutputs = collectResultPaths(results, (result) => result.artifactPaths?.outputPath);
299
+ const sections = ["## Subagent result", output];
300
+ if (sessionFiles.length > 0)
301
+ sections.push("## Child session exports", formatExportPathList(sessionFiles));
302
+ if (savedOutputs.length > 0)
303
+ sections.push("## Saved outputs", formatExportPathList(savedOutputs));
304
+ if (artifactOutputs.length > 0)
305
+ sections.push("## Artifact outputs", formatExportPathList(artifactOutputs));
306
+ return sections.join("\n\n");
307
+ }
308
+ async function runSlashSubagent(pi, ctx, params, executionCwd) {
309
+ const requestId = randomUUID();
310
+ const initialDetails = buildSlashInitialResult(requestId, params);
311
+ const initialText = extractSlashMessageText(initialDetails.result.content) || "Running subagent...";
312
+ pi.sendMessage({
313
+ customType: SLASH_RESULT_TYPE,
314
+ content: initialText,
315
+ display: true,
316
+ details: initialDetails,
317
+ });
318
+ try {
319
+ const response = await requestSlashRun(pi, ctx, requestId, params, executionCwd);
320
+ const finalDetails = finalizeSlashResult(response);
321
+ pi.sendMessage({
322
+ customType: SLASH_RESULT_TYPE,
323
+ content: buildSlashExportText(response),
324
+ display: false,
325
+ details: finalDetails,
326
+ });
327
+ if (ctx.hasUI) {
328
+ ctx.ui.setStatus("subagent-slash", undefined);
329
+ }
330
+ if (response.isError && ctx.hasUI) {
331
+ ctx.ui.notify(response.errorText || "Subagent failed", "error");
332
+ }
333
+ }
334
+ catch (error) {
335
+ const message = error instanceof Error ? error.message : String(error);
336
+ const failedDetails = failSlashResult(requestId, params, message);
337
+ pi.sendMessage({
338
+ customType: SLASH_RESULT_TYPE,
339
+ content: `## Subagent result\n\n${message}`,
340
+ display: false,
341
+ details: failedDetails,
342
+ });
343
+ if (ctx.hasUI) {
344
+ ctx.ui.setStatus("subagent-slash", undefined);
345
+ }
346
+ if (message === "Cancelled") {
347
+ if (ctx.hasUI)
348
+ ctx.ui.notify("Cancelled", "warning");
349
+ return;
350
+ }
351
+ if (ctx.hasUI)
352
+ ctx.ui.notify(message, "error");
353
+ }
354
+ }
355
+ const parseAgentArgs = (cwd, args, command, ctx) => {
356
+ const input = args.trim();
357
+ const usage = `Usage: /${command} agent1 "task1" -> agent2 "task2"`;
358
+ let steps;
359
+ let sharedTask;
360
+ let perStep = false;
361
+ if (input.includes(" -> ")) {
362
+ perStep = true;
363
+ const segments = input.split(" -> ");
364
+ steps = [];
365
+ for (const seg of segments) {
366
+ const trimmed = seg.trim();
367
+ if (!trimmed)
368
+ continue;
369
+ let agentPart;
370
+ let task;
371
+ const qMatch = trimmed.match(/^(\S+(?:\[[^\]]*\])?)\s+(?:"([^"]*)"|'([^']*)')$/);
372
+ if (qMatch) {
373
+ agentPart = qMatch[1];
374
+ task = (qMatch[2] ?? qMatch[3]) || undefined;
375
+ }
376
+ else {
377
+ const dashIdx = trimmed.indexOf(" -- ");
378
+ if (dashIdx !== -1) {
379
+ agentPart = trimmed.slice(0, dashIdx).trim();
380
+ task = trimmed.slice(dashIdx + 4).trim() || undefined;
381
+ }
382
+ else {
383
+ agentPart = trimmed;
384
+ }
385
+ }
386
+ try {
387
+ const parsed = parseAgentToken(agentPart);
388
+ steps.push({ ...parsed, task });
389
+ }
390
+ catch (error) {
391
+ ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
392
+ return null;
393
+ }
394
+ }
395
+ sharedTask = steps.find((s) => s.task)?.task ?? "";
396
+ }
397
+ else {
398
+ const delimiterIndex = input.indexOf(" -- ");
399
+ if (delimiterIndex === -1) {
400
+ ctx.ui.notify(usage, "error");
401
+ return null;
402
+ }
403
+ const agentsPart = input.slice(0, delimiterIndex).trim();
404
+ sharedTask = input.slice(delimiterIndex + 4).trim();
405
+ if (!agentsPart || !sharedTask) {
406
+ ctx.ui.notify(usage, "error");
407
+ return null;
408
+ }
409
+ try {
410
+ steps = agentsPart.split(/\s+/).filter(Boolean).map((token) => parseAgentToken(token));
411
+ }
412
+ catch (error) {
413
+ ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
414
+ return null;
415
+ }
416
+ }
417
+ if (steps.length === 0) {
418
+ ctx.ui.notify(usage, "error");
419
+ return null;
420
+ }
421
+ const agents = discoverAgents(cwd, "both", { projectTrusted: ctx.isProjectTrusted() }).agents;
422
+ for (const step of steps) {
423
+ if (!agents.find((a) => a.name === step.name)) {
424
+ ctx.ui.notify(`Unknown agent: ${step.name}`, "error");
425
+ return null;
426
+ }
427
+ }
428
+ if (command === "chain" && !steps[0]?.task && (perStep || !sharedTask)) {
429
+ ctx.ui.notify(`First step must have a task: /chain agent "task" -> agent2`, "error");
430
+ return null;
431
+ }
432
+ if (command === "parallel" && !steps.some((s) => s.task) && !sharedTask) {
433
+ ctx.ui.notify("At least one step must have a task", "error");
434
+ return null;
435
+ }
436
+ return { steps, task: sharedTask };
437
+ };
438
+ export function registerSlashCommands(pi, state) {
439
+ pi.registerCommand("run", {
440
+ description: "Run a subagent directly: /run agent[output=file] [task] [--bg|--fg] [--fork]",
441
+ getArgumentCompletions: makeAgentCompletions(pi, state, false),
442
+ handler: async (args, ctx) => {
443
+ const flags = extractExecutionFlags(args);
444
+ if (flags.error) {
445
+ ctx.ui.notify(flags.error, "error");
446
+ return;
447
+ }
448
+ const { args: cleanedArgs, async: asyncMode, fork } = flags;
449
+ const input = cleanedArgs.trim();
450
+ const firstSpace = input.indexOf(" ");
451
+ if (!input) {
452
+ ctx.ui.notify("Usage: /run <agent> [task] [--bg|--fg] [--fork]", "error");
453
+ return;
454
+ }
455
+ let parsedAgent;
456
+ try {
457
+ parsedAgent = parseAgentToken(firstSpace === -1 ? input : input.slice(0, firstSpace));
458
+ }
459
+ catch (error) {
460
+ ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
461
+ return;
462
+ }
463
+ const { name: agentName, config: inline } = parsedAgent;
464
+ const task = firstSpace === -1 ? "" : unwrapQuotedTask(input.slice(firstSpace + 1).trim());
465
+ const cwd = resolveExecutionCwd(pi, ctx);
466
+ const agents = discoverAgents(cwd, "both", { projectTrusted: ctx.isProjectTrusted() }).agents;
467
+ if (!agents.find((a) => a.name === agentName)) {
468
+ ctx.ui.notify(`Unknown agent: ${agentName}`, "error");
469
+ return;
470
+ }
471
+ let finalTask = task;
472
+ if (inline.reads && Array.isArray(inline.reads) && inline.reads.length > 0) {
473
+ finalTask = `[Read from: ${inline.reads.join(", ")}]\n\n${finalTask}`;
474
+ }
475
+ const params = { agent: agentName, task: finalTask, clarify: false, agentScope: "both" };
476
+ if (inline.output !== undefined)
477
+ params.output = inline.output;
478
+ if (inline.outputMode !== undefined)
479
+ params.outputMode = inline.outputMode;
480
+ if (inline.skill !== undefined)
481
+ params.skill = inline.skill;
482
+ if (inline.model)
483
+ params.model = inline.model;
484
+ if (inline.progress !== undefined)
485
+ params.progress = inline.progress;
486
+ if (asyncMode !== undefined)
487
+ params.async = asyncMode;
488
+ if (fork)
489
+ params.context = "fork";
490
+ await runSlashSubagent(pi, ctx, params, cwd);
491
+ },
492
+ });
493
+ pi.registerCommand("chain", {
494
+ description: "Run agents in sequence: /chain scout \"task\" -> oracle [--bg|--fg] [--fork]",
495
+ getArgumentCompletions: makeAgentCompletions(pi, state, true),
496
+ handler: async (args, ctx) => {
497
+ const flags = extractExecutionFlags(args);
498
+ if (flags.error) {
499
+ ctx.ui.notify(flags.error, "error");
500
+ return;
501
+ }
502
+ const { args: cleanedArgs, async: asyncMode, fork } = flags;
503
+ const cwd = resolveExecutionCwd(pi, ctx);
504
+ const parsed = parseAgentArgs(cwd, cleanedArgs, "chain", ctx);
505
+ if (!parsed)
506
+ return;
507
+ const chain = parsed.steps.map(({ name, config, task: stepTask }, i) => ({
508
+ agent: name,
509
+ ...(stepTask ? { task: stepTask } : i === 0 && parsed.task ? { task: parsed.task } : {}),
510
+ ...(config.output !== undefined ? { output: config.output } : {}),
511
+ ...(config.outputMode !== undefined ? { outputMode: config.outputMode } : {}),
512
+ ...(config.reads !== undefined ? { reads: config.reads } : {}),
513
+ ...(config.model ? { model: config.model } : {}),
514
+ ...(config.skill !== undefined ? { skill: config.skill } : {}),
515
+ ...(config.progress !== undefined ? { progress: config.progress } : {}),
516
+ }));
517
+ const params = { chain, task: parsed.task, clarify: false, agentScope: "both" };
518
+ if (asyncMode !== undefined)
519
+ params.async = asyncMode;
520
+ if (fork)
521
+ params.context = "fork";
522
+ await runSlashSubagent(pi, ctx, params, cwd);
523
+ },
524
+ });
525
+ pi.registerCommand("run-chain", {
526
+ description: "Run a saved chain: /run-chain chainName -- task [--bg|--fg] [--fork]",
527
+ getArgumentCompletions: makeChainCompletions(pi, state),
528
+ handler: async (args, ctx) => {
529
+ const flags = extractExecutionFlags(args);
530
+ if (flags.error) {
531
+ ctx.ui.notify(flags.error, "error");
532
+ return;
533
+ }
534
+ const { args: cleanedArgs, async: asyncMode, fork } = flags;
535
+ const delimiterIndex = cleanedArgs.indexOf(" -- ");
536
+ const usage = "Usage: /run-chain <chainName> -- <task> [--bg|--fg] [--fork]";
537
+ if (delimiterIndex === -1) {
538
+ ctx.ui.notify(usage, "error");
539
+ return;
540
+ }
541
+ const chainName = cleanedArgs.slice(0, delimiterIndex).trim();
542
+ const task = cleanedArgs.slice(delimiterIndex + 4).trim();
543
+ if (!chainName || !task) {
544
+ ctx.ui.notify(usage, "error");
545
+ return;
546
+ }
547
+ const cwd = resolveExecutionCwd(pi, ctx);
548
+ const chain = discoverSavedChains(cwd, ctx.isProjectTrusted()).find((candidate) => candidate.name === chainName);
549
+ if (!chain) {
550
+ ctx.ui.notify(`Unknown chain: ${chainName}`, "error");
551
+ return;
552
+ }
553
+ let savedSteps;
554
+ try {
555
+ savedSteps = mapSavedChainSteps(chain);
556
+ }
557
+ catch (error) {
558
+ ctx.ui.notify(`Cannot load chain '${chainName}': ${error instanceof Error ? error.message : String(error)}`, "error");
559
+ return;
560
+ }
561
+ const params = { chain: savedSteps, task, clarify: false, agentScope: "both" };
562
+ if (asyncMode !== undefined)
563
+ params.async = asyncMode;
564
+ if (fork)
565
+ params.context = "fork";
566
+ await runSlashSubagent(pi, ctx, params, cwd);
567
+ },
568
+ });
569
+ pi.registerCommand("parallel", {
570
+ description: "Run agents in parallel: /parallel scout \"task1\" -> oracle \"task2\" [--bg|--fg] [--fork]",
571
+ getArgumentCompletions: makeAgentCompletions(pi, state, true),
572
+ handler: async (args, ctx) => {
573
+ const flags = extractExecutionFlags(args);
574
+ if (flags.error) {
575
+ ctx.ui.notify(flags.error, "error");
576
+ return;
577
+ }
578
+ const { args: cleanedArgs, async: asyncMode, fork } = flags;
579
+ const cwd = resolveExecutionCwd(pi, ctx);
580
+ const parsed = parseAgentArgs(cwd, cleanedArgs, "parallel", ctx);
581
+ if (!parsed)
582
+ return;
583
+ const tasks = parsed.steps.map(({ name, config, task: stepTask }) => ({
584
+ agent: name,
585
+ task: stepTask ?? parsed.task,
586
+ ...(config.output !== undefined ? { output: config.output } : {}),
587
+ ...(config.outputMode !== undefined ? { outputMode: config.outputMode } : {}),
588
+ ...(config.reads !== undefined ? { reads: config.reads } : {}),
589
+ ...(config.model ? { model: config.model } : {}),
590
+ ...(config.skill !== undefined ? { skill: config.skill } : {}),
591
+ ...(config.progress !== undefined ? { progress: config.progress } : {}),
592
+ }));
593
+ const params = { tasks, clarify: false, agentScope: "both" };
594
+ if (asyncMode !== undefined)
595
+ params.async = asyncMode;
596
+ if (fork)
597
+ params.context = "fork";
598
+ await runSlashSubagent(pi, ctx, params, cwd);
599
+ },
600
+ });
601
+ pi.registerCommand("subagents-doctor", {
602
+ description: "Show subagent diagnostics",
603
+ handler: async (_args, ctx) => {
604
+ await runSlashSubagent(pi, ctx, { action: "doctor" });
605
+ },
606
+ });
607
+ }