@jaggerxtrm/specialists 3.17.0 → 3.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +268 -134
  2. package/config/mandatory-rules/executor-delivery.md +4 -0
  3. package/config/mandatory-rules/json-only-final-output.md +13 -0
  4. package/config/mandatory-rules/service-skills-diff-scan-mandatory.md +26 -0
  5. package/config/mandatory-rules/service-skills-gitnexus-triage.md +5 -0
  6. package/config/mandatory-rules/test-runner-execution-scope.md +4 -0
  7. package/config/skills/setup-specialists/SKILL.md +556 -0
  8. package/config/skills/specialists-creator/SKILL.md +132 -4
  9. package/config/skills/specialists-creator/scripts/audit-spec-uniformity.mjs +1 -1
  10. package/config/skills/using-specialists-v3/SKILL.md +80 -20
  11. package/config/specialists/bare.specialist.json +3 -3
  12. package/config/specialists/changelog-drafter.specialist.json +5 -4
  13. package/config/specialists/changelog-keeper.specialist.json +7 -6
  14. package/config/specialists/debugger.specialist.json +2 -2
  15. package/config/specialists/executor.specialist.json +2 -2
  16. package/config/specialists/explorer.specialist.json +4 -4
  17. package/config/specialists/memory-processor.specialist.json +3 -3
  18. package/config/specialists/node-coordinator.specialist.json +3 -3
  19. package/config/specialists/obligations-scanner.specialist.json +67 -17
  20. package/config/specialists/overthinker.specialist.json +3 -3
  21. package/config/specialists/planner.specialist.json +4 -4
  22. package/config/specialists/quant-methodologist.specialist.json +145 -0
  23. package/config/specialists/quant-researcher.specialist.json +144 -0
  24. package/config/specialists/researcher.specialist.json +5 -5
  25. package/config/specialists/reviewer.specialist.json +1 -1
  26. package/config/specialists/seconder.specialist.json +2 -2
  27. package/config/specialists/security-auditor.specialist.json +2 -2
  28. package/config/specialists/service-skills-sync.specialist.json +90 -75
  29. package/config/specialists/specialists-creator.specialist.json +4 -4
  30. package/config/specialists/sync-docs.specialist.json +4 -4
  31. package/config/specialists/test-engineer.specialist.json +3 -3
  32. package/config/specialists/test-runner.specialist.json +7 -7
  33. package/config/specialists/transcriber.specialist.json +3 -3
  34. package/config/specialists/xt-merge.specialist.json +4 -4
  35. package/dist/asset-contract.json +22 -3
  36. package/dist/index.js +28711 -18517
  37. package/dist/lib.js +10020 -6150
  38. package/dist/types/cli/console/components.d.ts +83 -0
  39. package/dist/types/cli/console/components.d.ts.map +1 -0
  40. package/dist/types/cli/console/config-source.d.ts +58 -0
  41. package/dist/types/cli/console/config-source.d.ts.map +1 -0
  42. package/dist/types/cli/console/forensic.d.ts +11 -0
  43. package/dist/types/cli/console/forensic.d.ts.map +1 -0
  44. package/dist/types/cli/console/git.d.ts +28 -0
  45. package/dist/types/cli/console/git.d.ts.map +1 -0
  46. package/dist/types/cli/console/help.d.ts +2 -0
  47. package/dist/types/cli/console/help.d.ts.map +1 -0
  48. package/dist/types/cli/console/log.d.ts +13 -0
  49. package/dist/types/cli/console/log.d.ts.map +1 -0
  50. package/dist/types/cli/console/repo-config.d.ts +26 -0
  51. package/dist/types/cli/console/repo-config.d.ts.map +1 -0
  52. package/dist/types/cli/console/repo-discovery.d.ts +12 -0
  53. package/dist/types/cli/console/repo-discovery.d.ts.map +1 -0
  54. package/dist/types/cli/console/runtime.d.ts +12 -0
  55. package/dist/types/cli/console/runtime.d.ts.map +1 -0
  56. package/dist/types/cli/console/subscribe-prototype.d.ts +18 -0
  57. package/dist/types/cli/console/subscribe-prototype.d.ts.map +1 -0
  58. package/dist/types/cli/console/theme.d.ts +91 -0
  59. package/dist/types/cli/console/theme.d.ts.map +1 -0
  60. package/dist/types/cli/console/types.d.ts +231 -0
  61. package/dist/types/cli/console/types.d.ts.map +1 -0
  62. package/dist/types/cli/console/view-model.d.ts +252 -0
  63. package/dist/types/cli/console/view-model.d.ts.map +1 -0
  64. package/dist/types/cli/console.d.ts +2 -0
  65. package/dist/types/cli/console.d.ts.map +1 -0
  66. package/dist/types/cli/db.d.ts.map +1 -1
  67. package/dist/types/cli/doctor.d.ts.map +1 -1
  68. package/dist/types/cli/edit.d.ts.map +1 -1
  69. package/dist/types/cli/epic.d.ts.map +1 -1
  70. package/dist/types/cli/feed.d.ts.map +1 -1
  71. package/dist/types/cli/forensic.d.ts +2 -0
  72. package/dist/types/cli/forensic.d.ts.map +1 -0
  73. package/dist/types/cli/format-helpers.d.ts +4 -2
  74. package/dist/types/cli/format-helpers.d.ts.map +1 -1
  75. package/dist/types/cli/help.d.ts.map +1 -1
  76. package/dist/types/cli/init.d.ts +10 -0
  77. package/dist/types/cli/init.d.ts.map +1 -1
  78. package/dist/types/cli/list.d.ts.map +1 -1
  79. package/dist/types/cli/log.d.ts.map +1 -1
  80. package/dist/types/cli/merge.d.ts.map +1 -1
  81. package/dist/types/cli/metrics.d.ts +2 -0
  82. package/dist/types/cli/metrics.d.ts.map +1 -0
  83. package/dist/types/cli/ps.d.ts.map +1 -1
  84. package/dist/types/cli/result.d.ts.map +1 -1
  85. package/dist/types/cli/run.d.ts +20 -0
  86. package/dist/types/cli/run.d.ts.map +1 -1
  87. package/dist/types/cli/script.d.ts +3 -0
  88. package/dist/types/cli/script.d.ts.map +1 -1
  89. package/dist/types/cli/serve.d.ts.map +1 -1
  90. package/dist/types/cli/setup.d.ts +19 -1
  91. package/dist/types/cli/setup.d.ts.map +1 -1
  92. package/dist/types/cli/status.d.ts.map +1 -1
  93. package/dist/types/cli/version-check.d.ts +1 -0
  94. package/dist/types/cli/version-check.d.ts.map +1 -1
  95. package/dist/types/index.d.ts +1 -1
  96. package/dist/types/pi/session.d.ts +12 -1
  97. package/dist/types/pi/session.d.ts.map +1 -1
  98. package/dist/types/server.d.ts +15 -0
  99. package/dist/types/server.d.ts.map +1 -1
  100. package/dist/types/specialist/benchmarks.d.ts +37 -0
  101. package/dist/types/specialist/benchmarks.d.ts.map +1 -0
  102. package/dist/types/specialist/chain-identity.d.ts +7 -1
  103. package/dist/types/specialist/chain-identity.d.ts.map +1 -1
  104. package/dist/types/specialist/control.d.ts.map +1 -1
  105. package/dist/types/specialist/dead-job-audit.d.ts +20 -0
  106. package/dist/types/specialist/dead-job-audit.d.ts.map +1 -0
  107. package/dist/types/specialist/forensic-events.d.ts +138 -0
  108. package/dist/types/specialist/forensic-events.d.ts.map +1 -0
  109. package/dist/types/specialist/forensic-renderer.d.ts +34 -0
  110. package/dist/types/specialist/forensic-renderer.d.ts.map +1 -0
  111. package/dist/types/specialist/git-diff-evidence.d.ts +28 -0
  112. package/dist/types/specialist/git-diff-evidence.d.ts.map +1 -0
  113. package/dist/types/specialist/global-config.d.ts +389 -0
  114. package/dist/types/specialist/global-config.d.ts.map +1 -0
  115. package/dist/types/specialist/launch.d.ts +9 -0
  116. package/dist/types/specialist/launch.d.ts.map +1 -1
  117. package/dist/types/specialist/live-aggregates.d.ts +46 -0
  118. package/dist/types/specialist/live-aggregates.d.ts.map +1 -0
  119. package/dist/types/specialist/loader.d.ts +50 -1
  120. package/dist/types/specialist/loader.d.ts.map +1 -1
  121. package/dist/types/specialist/model-chain.d.ts +7 -0
  122. package/dist/types/specialist/model-chain.d.ts.map +1 -0
  123. package/dist/types/specialist/model-probes.d.ts +28 -0
  124. package/dist/types/specialist/model-probes.d.ts.map +1 -0
  125. package/dist/types/specialist/node-contract.d.ts +18 -18
  126. package/dist/types/specialist/node-supervisor.d.ts.map +1 -1
  127. package/dist/types/specialist/observability-db.d.ts +1 -1
  128. package/dist/types/specialist/observability-db.d.ts.map +1 -1
  129. package/dist/types/specialist/observability-sqlite.d.ts +101 -0
  130. package/dist/types/specialist/observability-sqlite.d.ts.map +1 -1
  131. package/dist/types/specialist/pr-drift-refresh.d.ts +17 -0
  132. package/dist/types/specialist/pr-drift-refresh.d.ts.map +1 -0
  133. package/dist/types/specialist/preset-resolver.d.ts +56 -0
  134. package/dist/types/specialist/preset-resolver.d.ts.map +1 -0
  135. package/dist/types/specialist/prometheus-projection.d.ts +25 -0
  136. package/dist/types/specialist/prometheus-projection.d.ts.map +1 -0
  137. package/dist/types/specialist/runner.d.ts +29 -1
  138. package/dist/types/specialist/runner.d.ts.map +1 -1
  139. package/dist/types/specialist/schema.d.ts +163 -54
  140. package/dist/types/specialist/schema.d.ts.map +1 -1
  141. package/dist/types/specialist/script-runner.d.ts +5 -1
  142. package/dist/types/specialist/script-runner.d.ts.map +1 -1
  143. package/dist/types/specialist/snapshot-diff.d.ts +8 -0
  144. package/dist/types/specialist/snapshot-diff.d.ts.map +1 -0
  145. package/dist/types/specialist/source-queue.d.ts +13 -0
  146. package/dist/types/specialist/source-queue.d.ts.map +1 -0
  147. package/dist/types/specialist/status-load.d.ts.map +1 -1
  148. package/dist/types/specialist/supervisor.d.ts +25 -1
  149. package/dist/types/specialist/supervisor.d.ts.map +1 -1
  150. package/dist/types/specialist/timeline-events.d.ts +70 -1
  151. package/dist/types/specialist/timeline-events.d.ts.map +1 -1
  152. package/dist/types/tools/specialist/resume_specialist.tool.d.ts +4 -4
  153. package/dist/types/tools/specialist/specialist_status.tool.d.ts +1 -1
  154. package/dist/types/tools/specialist/steer_specialist.tool.d.ts +4 -4
  155. package/dist/types/tools/specialist/use_specialist.tool.d.ts +16 -16
  156. package/docs/ARCHITECTURE.md +1176 -0
  157. package/docs/TODO.md +9 -0
  158. package/docs/architecture.md +11 -0
  159. package/docs/archive/2026-03-13-bd-decision-context-template-plan.md +96 -0
  160. package/docs/archive/AGENT-HANDOFF.md +351 -0
  161. package/docs/archive/PARITY-ANALYSIS.md +296 -0
  162. package/docs/archive/SPECIALISTS_REFACTOR.md.md +30 -0
  163. package/docs/archive/cc-programmatic.md +216 -0
  164. package/docs/archive/claude-agent-sdk.md +594 -0
  165. package/docs/archive/decision-specialist-directory.md +41 -0
  166. package/docs/archive/discoveries.md +148 -0
  167. package/docs/archive/executor-benchmark-protocol.md +198 -0
  168. package/docs/archive/future-features.md +66 -0
  169. package/docs/archive/gzrx-completion-critique.md +183 -0
  170. package/docs/archive/gzrx-research-notes.md +401 -0
  171. package/docs/archive/gzrx-tool-catalog.md +760 -0
  172. package/docs/archive/iron-review-hardening-qa-chain-substrate.md +322 -0
  173. package/docs/archive/iron-review-hardening.html +1004 -0
  174. package/docs/archive/issuetracking.md +312 -0
  175. package/docs/archive/qa-v3.0.2.md +220 -0
  176. package/docs/archive/restructure.md +231 -0
  177. package/docs/archive/script-specialists.md +1254 -0
  178. package/docs/archive/spec-v3.md +792 -0
  179. package/docs/archive/specialist-stats.md +127 -0
  180. package/docs/archive/specialists-friction-audit.md +1347 -0
  181. package/docs/archive/specialists-runtime-critique.md +170 -0
  182. package/docs/archive/specialists-service-evaluation.md +713 -0
  183. package/docs/archive/specialists-substrate-alignment.md +255 -0
  184. package/docs/archive/substrate-review.md +1288 -0
  185. package/docs/archive/test-writer-specialist.md +254 -0
  186. package/docs/archive/using-specialists-v3-improvements-2026-05-09.md +600 -0
  187. package/docs/archive/xtrm-specialists-analysis.md +314 -0
  188. package/docs/authoring.md +701 -0
  189. package/docs/background-jobs.md +203 -0
  190. package/docs/bare-specialists.md +83 -0
  191. package/docs/benchmarks/executor-benchmark-runner.md +66 -0
  192. package/docs/bootstrap.md +161 -0
  193. package/docs/cli-reference.md +1645 -0
  194. package/docs/deploying-alongside.md +155 -0
  195. package/docs/design/README.md +36 -0
  196. package/docs/design/darth-feedor-migration.md +290 -0
  197. package/docs/design/roadmap/README.md +32 -0
  198. package/docs/design/roadmap/chain-templates/README.md +146 -0
  199. package/docs/design/roadmap/chain-templates/code-quick.formula.json +27 -0
  200. package/docs/design/roadmap/chain-templates/code-standard.formula.json +126 -0
  201. package/docs/design/roadmap/chain-templates/code-with-advisors.formula.json +128 -0
  202. package/docs/design/roadmap/chain-templates/code-with-tests.formula.json +76 -0
  203. package/docs/design/roadmap/chain-templates/debug.formula.json +128 -0
  204. package/docs/design/roadmap/chain-templates/doc-sync.formula.json +28 -0
  205. package/docs/design/roadmap/chain-templates/memory-hygiene.formula.json +27 -0
  206. package/docs/design/roadmap/chain-templates/planning.formula.json +27 -0
  207. package/docs/design/roadmap/chain-templates/premortem.formula.json +26 -0
  208. package/docs/design/roadmap/chain-templates/release-prep.formula.json +36 -0
  209. package/docs/design/roadmap/chain-templates/research-only.formula.json +28 -0
  210. package/docs/design/roadmap/chain-templates/restitch.formula.json +125 -0
  211. package/docs/design/roadmap/chain-templates/security-deep.formula.json +161 -0
  212. package/docs/design/roadmap/chain-templates/test-only.formula.json +52 -0
  213. package/docs/design/roadmap/chain-templates/triage.formula.json +35 -0
  214. package/docs/design/roadmap/history/handoff-from-substrate-design.md +87 -0
  215. package/docs/design/roadmap/history/substrate-reconciliation.md +105 -0
  216. package/docs/design/roadmap/specialists-roadmap.md +1261 -0
  217. package/docs/design/sp-console-subscribe-via-materializer.md +231 -0
  218. package/docs/design/sp-console-tui-mock-v2.html +293 -0
  219. package/docs/design/sp-console-tui-mock.html +120 -0
  220. package/docs/design/sp-console-tui.md +340 -0
  221. package/docs/design/specialist-agentops-suite.md +323 -0
  222. package/docs/design/substrate/channels.md +14 -0
  223. package/docs/design/substrate/devops-platform-engineering-prd.md +446 -0
  224. package/docs/design/substrate/html-design-example.md +339 -0
  225. package/docs/design/xtrm-tiers-architecture.svg +132 -0
  226. package/docs/devops/dependency-verdict-materialization.md +46 -0
  227. package/docs/epic-readiness.md +75 -0
  228. package/docs/examples/mercury-atomic-summarizer.specialist.json +26 -0
  229. package/docs/examples/smoke-echo-text-expected-keys.specialist.json +25 -0
  230. package/docs/examples/smoke-echo.specialist.json +25 -0
  231. package/docs/features.md +1577 -0
  232. package/docs/hooks.md +81 -0
  233. package/docs/installation.md +142 -0
  234. package/docs/manifest.md +184 -0
  235. package/docs/mcp-servers.md +73 -0
  236. package/docs/mcp-tools.md +71 -0
  237. package/docs/nodes.md +231 -0
  238. package/docs/observability-metrics.md +152 -0
  239. package/docs/operator/sp-console-v2-walkthrough.md +410 -0
  240. package/docs/overrides-guide.md +306 -0
  241. package/docs/pi-rpc-boundary.md +118 -0
  242. package/docs/pi-session.md +195 -0
  243. package/docs/release.md +22 -0
  244. package/docs/skills.md +132 -0
  245. package/docs/specialists/handoff-schema.md +181 -0
  246. package/docs/specialists-catalog.md +99 -0
  247. package/docs/specialists-service-install.md +226 -0
  248. package/docs/specialists-service.md +363 -0
  249. package/docs/surface-ownership.md +138 -0
  250. package/docs/upgrade-notes/kan-90-global-user-config.md +114 -0
  251. package/docs/upgrade-notes/kan-91-expanded-overrides.md +261 -0
  252. package/docs/workflow.md +114 -0
  253. package/docs/worktree.md +71 -0
  254. package/docs/worktrees.md +309 -0
  255. package/package.json +7 -4
@@ -0,0 +1,792 @@
1
+ ---
2
+ title: Specialists v3 — Implementation Specification
3
+ scope: spec-v3
4
+ category: reference
5
+ version: 1.0.0
6
+ updated: 2026-03-19
7
+ domain: []
8
+ ---
9
+
10
+ <!-- INDEX: auto-generated by validate_doc.py — do not edit manually -->
11
+ | Section | Summary |
12
+ |---|---|
13
+ | [1. Architecture Overview](#1-architecture-overview) | Problem: N poll calls per run |
14
+ | [2. File Layout](#2-file-layout) | On completion, status changes to "done" and elapsed_s is final |
15
+ | [3. CLI Commands](#3-cli-commands) | **Foreground (default, existing behavior)**: Streams pi output to stdout in real time |
16
+ | [4. Pi Subprocess Protocol](#4-pi-subprocess-protocol) | **--append-system-prompt**: The specialist's prompt |
17
+ | [5. Schema Changes (.specialist.yaml)](#5-schema-changes-specialistyaml) | skills |
18
+ | [6. Hook Notification](#6-hook-notification) | File: ~/ |
19
+ | [7. PiAgentSession Changes](#7-piagentsession-changes) | Changes: |
20
+ | [8. SpecialistRunner Changes](#8-specialistrunner-changes) | SpecialistRunner |
21
+ | [9. MCP Server Changes](#9-mcp-server-changes) | **specialist_init**: Unchanged |
22
+ | [10. Operational Concerns](#10-operational-concerns) | `specialists init` must add ` |
23
+ | [11. Migration Path](#11-migration-path) | 1 |
24
+ | [12. Testing Strategy](#12-testing-strategy) | - Supervisor lifecycle: mock PiAgentSession, verify status |
25
+ | [13. Verification Checklist](#13-verification-checklist) | All items verified against pi-mono source (packages/coding-agent) and pi --help output |
26
+ <!-- END INDEX -->
27
+
28
+ # Specialists v3 — Implementation Specification
29
+
30
+ > **Status**: Final spec. Ready for implementation.
31
+ > **Date**: 2026-03-11
32
+ > **Context**: This spec supersedes the file-backed registry + RpcClient plan from restructure.md.
33
+ > **Core decision**: CLI is the execution plane. MCP is the control plane. Bash background jobs replace MCP async polling.
34
+
35
+ ---
36
+
37
+ ## 1. Architecture Overview
38
+
39
+ ### Current (v2)
40
+
41
+ ```
42
+ Claude ──MCP──▶ start_specialist ──▶ PiAgentSession (subprocess)
43
+ Claude ──MCP──▶ poll_specialist ──▶ JobRegistry (in-memory) ──▶ delta + status
44
+ Claude ──MCP──▶ poll_specialist ──▶ ...repeat N times...
45
+ Claude ──MCP──▶ poll_specialist ──▶ status: done, full output
46
+ ```
47
+
48
+ Problem: N poll calls per run. Each is a visible tool use. Thinking tokens, tool markers,
49
+ and text deltas streamed into context window on every poll. 60-second specialist = 30+ tool
50
+ calls, tens of thousands of wasted context tokens.
51
+
52
+ ### Target (v3)
53
+
54
+ ```
55
+ Claude ──Bash──▶ specialists run overthinker --prompt "..." --background
56
+ └▶ spawns pi --mode rpc (supervisor stays alive)
57
+ └▶ writes status.json on lifecycle events
58
+ └▶ writes events.jsonl (lifecycle + tool events only)
59
+ └▶ on agent_end: queries get_last_assistant_text → result.txt
60
+ └▶ queries get_state → session path → session.txt
61
+ └▶ closes stdin → pi exits → supervisor exits
62
+
63
+ Claude ──Bash──▶ specialists status (one-liner per job, ~30 tokens)
64
+ Claude ──Bash──▶ specialists result <id> (full output, once, when done)
65
+ Claude ──Bash──▶ specialists run X --continue <id> --prompt "..." (multi-turn)
66
+ ```
67
+
68
+ Hook notification (optional): UserPromptSubmit hook checks .specialists/jobs/*/status.json
69
+ for newly completed jobs, injects one-line banner on next user prompt.
70
+
71
+ ### MCP Role (reduced)
72
+
73
+ MCP server retains: specialist_init, list_specialists, specialist_status (circuit breaker
74
+ health), use_specialist (quick synchronous tasks). The async job tools (start_specialist,
75
+ poll_specialist, stop_specialist, run_parallel) are deprecated — their functionality moves
76
+ to CLI commands.
77
+
78
+ ---
79
+
80
+ ## 2. File Layout
81
+
82
+ ```
83
+ .specialists/ # project root, parallel to .beads/
84
+ jobs/
85
+ <job-id>/
86
+ status.json # overwritten on every lifecycle event
87
+ events.jsonl # append-only lifecycle + tool events
88
+ result.txt # written once on agent_end (clean assistant text)
89
+ session.jsonl # pi writes here directly (--session flag)
90
+ ready/ # completion markers for hook notification
91
+ <job-id> # empty file, existence = "done"
92
+ ```
93
+
94
+ ### status.json (overwritten per lifecycle event)
95
+
96
+ ```json
97
+ {
98
+ "id": "a1b2c3",
99
+ "specialist": "overthinker",
100
+ "status": "running",
101
+ "current_event": "tool_execution",
102
+ "current_tool": "bash(grep -r 'pattern' src/)",
103
+ "model": "anthropic/claude-sonnet-4-6",
104
+ "backend": "anthropic",
105
+ "pid": 48210,
106
+ "started_at_ms": 1710000000000,
107
+ "elapsed_s": 47,
108
+ "last_event_at_ms": 1710000047000,
109
+ "bead_id": "bead-xyz",
110
+ "session_file": ".specialists/jobs/a1b2c3/session.jsonl",
111
+ "error": null
112
+ }
113
+ ```
114
+
115
+ On completion, status changes to "done" and elapsed_s is final.
116
+ On error, status is "error" and error field contains the message.
117
+ On cancel, status is "cancelled".
118
+ On stall detection, status remains "running" but a "stalled": true field is added.
119
+
120
+ ### events.jsonl (append-only, lifecycle + tool events)
121
+
122
+ ```jsonl
123
+ {"ts":1710000000,"event":"starting","specialist":"overthinker","model":"anthropic/claude-sonnet-4-6","id":"a1b2c3"}
124
+ {"ts":1710000003,"event":"thinking"}
125
+ {"ts":1710000008,"event":"toolcall","tool":"read","args":"src/index.ts"}
126
+ {"ts":1710000009,"event":"tool_execution","tool":"read"}
127
+ {"ts":1710000009,"event":"tool_execution_end","tool":"read","duration_ms":340}
128
+ {"ts":1710000015,"event":"toolcall","tool":"bash","args":"grep -r 'pattern' src/"}
129
+ {"ts":1710000016,"event":"tool_execution","tool":"bash"}
130
+ {"ts":1710000022,"event":"tool_execution_end","tool":"bash","duration_ms":6100}
131
+ {"ts":1710000030,"event":"text"}
132
+ {"ts":1710000047,"event":"done","duration_ms":47000,"session_file":".specialists/jobs/a1b2c3/session.jsonl"}
133
+ ```
134
+
135
+ NOT captured in events.jsonl: thinking_delta content, text_delta content, message_start,
136
+ thinking_end. These are high-frequency events that add noise without diagnostic value.
137
+
138
+ ### result.txt
139
+
140
+ Plain text. The clean final assistant output extracted from pi's get_last_assistant_text
141
+ RPC call. Written once on agent_end. This is what `specialists result <id>` prints.
142
+
143
+ ### session.jsonl
144
+
145
+ Pi's native session file. Written by pi directly via `--session <path>`. Contains the full
146
+ message history (user, assistant, tool calls, tool results) in pi's internal JSONL format.
147
+ Used for `--continue` multi-turn resumption. We never read or parse this file — pi owns it.
148
+
149
+ ---
150
+
151
+ ## 3. CLI Commands
152
+
153
+ ### 3.1 specialists run <name> (MODIFIED — add --background)
154
+
155
+ ```
156
+ specialists run <name> --prompt "..." [--background] [--model <model>] [--no-beads]
157
+ [--continue <job-id>] [--timeout <ms>]
158
+ ```
159
+
160
+ **Foreground (default, existing behavior)**: Streams pi output to stdout in real time.
161
+ Blocks until completion. Exit code reflects success/failure.
162
+
163
+ **Background (--background, new)**: Spawns pi subprocess, prints job ID to stdout, writes
164
+ status/events/result files. The `specialists run` process stays alive as a supervisor
165
+ (not a daemon) — Claude backgrounds it with `&` in bash.
166
+
167
+ **Continue (--continue <job-id>)**: Reads session file path from
168
+ `.specialists/jobs/<job-id>/session.txt` or from status.json. Spawns pi with
169
+ `--session <path>`. Full context from previous run is available.
170
+
171
+ Supervisor lifecycle (--background mode):
172
+ 1. Generate job ID (nanoid or uuid short)
173
+ 2. Create .specialists/jobs/<id>/ directory
174
+ 3. Write initial status.json (status: "starting")
175
+ 4. Run pre-scripts (existing mechanism, unchanged)
176
+ 5. Spawn pi subprocess with --mode rpc, --session .specialists/jobs/<id>/session.jsonl
177
+ 6. Print job ID to stdout: "Job started: <id>"
178
+ 7. Read NDJSON from pi stdout:
179
+ - On lifecycle/tool events → update status.json, append to events.jsonl
180
+ - On thinking_start/text_delta → update current_event in status.json only (no content)
181
+ - Track last_event_at_ms for stall detection
182
+ 8. On agent_end:
183
+ a. Send {"type": "get_last_assistant_text"} via stdin → read response → write result.txt
184
+ b. Update status.json to "done"
185
+ c. Write done event to events.jsonl
186
+ d. Touch .specialists/ready/<id> (for hook notification)
187
+ e. Close stdin → pi exits
188
+ 9. Run post-scripts (existing mechanism, unchanged)
189
+ 10. Close beads issue (if created)
190
+ 11. Supervisor process exits
191
+
192
+ Stall detection (in-process, not a daemon):
193
+ - Configurable per-specialist via stall_timeout_ms in YAML (default: null = disabled)
194
+ - If last_event_at_ms is older than stall_timeout_ms, write "stalled": true to status.json
195
+ - Do NOT auto-kill — surface the stall in status, let Claude or the user decide
196
+
197
+ Error handling:
198
+ - Pi process exits non-zero without agent_end → write status "error", close bead as ERROR
199
+ - Pi process killed by OOM/signal → same as above, capture exit code in error field
200
+ - Pre-script failure → write to status.json, still start pi (pre-script output is context, not a gate)
201
+ - Post-script failure → swallow silently (existing behavior, unchanged)
202
+
203
+ ### 3.2 specialists status (MODIFIED — add job status)
204
+
205
+ ```
206
+ specialists status [--job <id>] [--json]
207
+ ```
208
+
209
+ Without --job: existing system health output (specialist count, pi version, beads status,
210
+ MCP registration) PLUS a job summary table:
211
+
212
+ ```
213
+ ── Active Jobs ───────────────────────────────────────────────────────────
214
+ a1b2c3 overthinker running 1m12s tool: bash(grep -r ...)
215
+ d4e5f6 bug-hunt running 0m34s thinking
216
+ g7h8i9 init-session done 0m08s ✓
217
+ ```
218
+
219
+ With --job <id>: detailed single-job view:
220
+
221
+ ```
222
+ overthinker | running | 1m12s | model: anthropic/claude-sonnet-4-6
223
+ Current: tool_execution bash(grep -r 'pattern' src/)
224
+ Bead: bead-xyz
225
+ Session: .specialists/jobs/a1b2c3/session.jsonl
226
+ Events: 14 (5 tool calls)
227
+ ```
228
+
229
+ Implementation: reads .specialists/jobs/*/status.json. Pure file reads, no MCP, no
230
+ subprocess interaction.
231
+
232
+ ### 3.3 specialists result <id> (NEW)
233
+
234
+ ```
235
+ specialists result <id> [--raw]
236
+ ```
237
+
238
+ Prints result.txt to stdout. If the job is still running, prints current status and exits
239
+ with code 1. --raw skips any formatting/headers and prints only the raw result text.
240
+
241
+ ### 3.4 specialists feed (NEW)
242
+
243
+ ```
244
+ specialists feed [--job <id>] [--follow]
245
+ ```
246
+
247
+ Formatted tail of events.jsonl files. Without --job, shows all active jobs interleaved
248
+ with job ID prefix. --follow keeps tailing (like tail -f).
249
+
250
+ Output format:
251
+ ```
252
+ [a1b2c3] overthinker 00:12 toolcall bash(grep -r 'pattern' src/)
253
+ [a1b2c3] overthinker 00:18 tool_end bash 6.1s
254
+ [d4e5f6] bug-hunt 00:34 thinking
255
+ [a1b2c3] overthinker 00:22 text
256
+ [a1b2c3] overthinker 00:47 done 47.0s
257
+ ```
258
+
259
+ ### 3.5 specialists stop <id> (NEW — CLI equivalent of MCP stop_specialist)
260
+
261
+ ```
262
+ specialists stop <id>
263
+ ```
264
+
265
+ Reads status.json for the PID, sends SIGTERM. If the supervisor process is still alive,
266
+ it detects the child exit, writes "cancelled" status, closes bead. If the supervisor
267
+ is gone (orphaned pi process), sends SIGTERM directly to the PID.
268
+
269
+ ### 3.6 specialists run (EXISTING — foreground, unchanged)
270
+
271
+ Existing behavior preserved. No --background flag = streams to stdout, blocks until done.
272
+ Used by humans directly and by Claude for quick synchronous tasks where background
273
+ overhead is not worth it.
274
+
275
+ ---
276
+
277
+ ## 4. Pi Subprocess Protocol
278
+
279
+ ### 4.1 Spawn Arguments
280
+
281
+ ```
282
+ pi --mode rpc \
283
+ --provider <provider> \
284
+ --model <model> \
285
+ --append-system-prompt <system_prompt> \
286
+ --tools <tool_list> # always set — see below
287
+ --skill <path> # repeated for each skills.paths entry
288
+ --session .specialists/jobs/<id>/session.jsonl \
289
+ --no-extensions # prevent user extensions from emitting UI requests
290
+ <any additional args from specialist config>
291
+ ```
292
+
293
+ **--append-system-prompt**: The specialist's prompt.system field. Takes priority over
294
+ AGENTS.md. This IS the specialist's identity — its behavioral constraints, output format
295
+ requirements, and role definition. Not template-rendered (no variable substitution).
296
+
297
+ **--no-extensions**: Always set in background mode. User-installed pi extensions can emit
298
+ `extension_ui_request` events (dialogs, confirmations) on stdout and block waiting for a
299
+ response from stdin. The supervisor never sends these responses — causing a silent hang.
300
+ `--no-extensions` disables extension discovery entirely. (`--no-input` does not exist in pi.)
301
+
302
+ **--tools**: Always set explicitly. Pi's default when no `--tools` flag is passed is
303
+ `read,bash,edit,write` — which silently excludes `grep`, `find`, and `ls`. Per-tier mapping:
304
+
305
+ | Permission | --tools value |
306
+ |------------|---------------|
307
+ | READ_ONLY | `read,bash,grep,find,ls` |
308
+ | BASH_ONLY | `bash` |
309
+ | LOW | `read,bash,edit,write,grep,find,ls` |
310
+ | MEDIUM | `read,bash,edit,write,grep,find,ls` |
311
+ | HIGH | `read,bash,edit,write,grep,find,ls` |
312
+
313
+ `mapPermissionToTools()` in `src/pi/session.ts` must be updated to match this table.
314
+
315
+ **--skill**: One flag per entry in the specialist's skills.paths array. Paths resolved:
316
+ - Absolute paths: passed as-is
317
+ - ~/... paths: expanded to home directory
318
+ - Relative paths: resolved from project root (cwd)
319
+ - Missing paths: logged as warning in events.jsonl, not fatal
320
+
321
+ **--session**: Always set in --background mode. Points to
322
+ .specialists/jobs/<id>/session.jsonl. Pi creates and writes this file. We never read it
323
+ directly — it's pi's internal format. We store the path for --continue.
324
+
325
+ ### 4.2 Stdin RPC Commands
326
+
327
+ The supervisor sends three types of commands via stdin, all as JSON + newline:
328
+
329
+ **Prompt** (on start):
330
+ ```json
331
+ {"type": "prompt", "message": "<rendered task template>"}
332
+ ```
333
+
334
+ > **Important**: Pi's RPC mode responds to `prompt` immediately with
335
+ > `{"type":"response","command":"prompt","success":true}` — this is an ack that the command
336
+ > was received, NOT a completion signal. The agent starts working asynchronously after this
337
+ > ack. The supervisor must NOT treat the prompt response as completion — it must continue
338
+ > reading stdout events until `agent_end` arrives.
339
+
340
+ **Get state** (after agent_end, before close):
341
+ ```json
342
+ {"type": "get_state"}
343
+ ```
344
+ Response includes sessionFile, sessionId, isStreaming, model. We extract sessionFile
345
+ to confirm the session path (should match what we passed via --session).
346
+
347
+ **Get last assistant text** (after agent_end, before close):
348
+ ```json
349
+ {"type": "get_last_assistant_text"}
350
+ ```
351
+ Response: `{"type":"response","command":"get_last_assistant_text","success":true,"data":{"text":"..."|null}}`
352
+
353
+ `data.text` is `null` if no assistant message exists (e.g. pi crashed before responding).
354
+ The supervisor must null-check before writing result.txt — write an empty file or skip
355
+ writing entirely if `text` is null.
356
+
357
+ **Close**: stdin.end() — sends EOF, pi exits. Only called after the above two queries
358
+ complete. This replaces the current stdin.end() hack that fires immediately after prompt.
359
+
360
+ ### 4.3 Stdout NDJSON Event Processing
361
+
362
+ Events read from pi's stdout, one JSON object per line. The supervisor categorizes:
363
+
364
+ **Status-updating events** (update status.json current_event + current_tool):
365
+ - thinking_start, thinking_delta → current_event: "thinking"
366
+ - toolcall_start → current_event: "toolcall", current_tool: tool name + args
367
+ - tool_execution_start, tool_execution_update → current_event: "tool_execution"
368
+ - tool_execution_end → current_event: "tool_execution_end"
369
+ - message_update with text_delta → current_event: "text"
370
+ - agent_end → triggers finalization sequence (4.2 above)
371
+
372
+ **JSONL-logged events** (append to events.jsonl):
373
+ - thinking_start (not deltas — just the transition)
374
+ - toolcall_start (with tool name and args summary)
375
+ - tool_execution_end (with tool name and duration)
376
+ - text (first text_delta only — marks transition to output)
377
+ - agent_end (with duration)
378
+
379
+ **Dropped events** (not written anywhere):
380
+ - thinking_delta (high frequency, content not needed)
381
+ - thinking_end (redundant — next event implies it)
382
+ - text_delta content (high frequency, full text available via get_last_assistant_text)
383
+ - message_start (metadata only, captured elsewhere)
384
+
385
+ ### 4.4 No stdin.end() on prompt
386
+
387
+ This is the key protocol change from current PiAgentSession. Currently, prompt() calls
388
+ stdin.end() immediately to trigger EOF, which makes every session single-turn. In v3:
389
+
390
+ 1. prompt() sends the JSON command via stdin.write(), does NOT close stdin
391
+ 2. Supervisor reads NDJSON events from stdout until agent_end
392
+ 3. Supervisor sends get_state and get_last_assistant_text queries via stdin
393
+ 4. Supervisor calls stdin.end() only after receiving both responses
394
+ 5. Pi sees EOF, exits cleanly
395
+
396
+ This enables multi-turn (--continue) because the session file is complete and valid.
397
+ Pi wrote the full conversation to the session JSONL during execution.
398
+
399
+ ---
400
+
401
+ ## 5. Schema Changes (.specialist.yaml)
402
+
403
+ ### 5.1 New field: skills.paths
404
+
405
+ ```yaml
406
+ skills:
407
+ paths: # NEW — pi --skill injection
408
+ - ~/.agents/skills/docker-patterns/ # user-scope skill directory
409
+ - ./skills/mercury-infra/ # project-scope skill directory
410
+ scripts: # EXISTING — unchanged
411
+ - path: ./scripts/check-env.sh
412
+ phase: pre
413
+ inject_output: true
414
+ ```
415
+
416
+ skills.paths is an optional array of strings. Each entry becomes a --skill <path> flag
417
+ on the pi command line. Pi loads these as on-demand context (README files, tool
418
+ definitions, etc.) that the model can reference without bloating the initial prompt.
419
+
420
+ Path resolution:
421
+ - ~/... → os.homedir() + rest
422
+ - ./... → path.resolve(cwd, rest)
423
+ - /... → absolute, as-is
424
+
425
+ Validation: paths are validated at specialist load time (loader.ts). Missing paths
426
+ produce a warning log, not a load failure. This allows user-scope skills that may not
427
+ exist on every machine.
428
+
429
+ ### 5.2 New field: execution.stall_timeout_ms
430
+
431
+ ```yaml
432
+ execution:
433
+ stall_timeout_ms: 30000 # NEW — optional, default: null (disabled)
434
+ ```
435
+
436
+ If set, the supervisor checks last_event_at_ms on a 5-second interval. If no event
437
+ received for stall_timeout_ms, writes "stalled": true to status.json. Does NOT auto-kill.
438
+ Surfaces in `specialists status` output.
439
+
440
+ ### 5.3 Existing fields — no changes
441
+
442
+ All existing schema fields remain unchanged:
443
+ - metadata (name, version, description, category, tags, updated)
444
+ - execution (mode, model, fallback_model, timeout_ms, response_format, permission_required)
445
+ - prompt (system, task_template, skill_inherit)
446
+ - skills.scripts (path, phase, inject_output)
447
+ - communication (publishes)
448
+ - beads_integration (auto, always, never)
449
+
450
+ ---
451
+
452
+ ## 6. Hook Notification
453
+
454
+ ### 6.1 Completion hook (NEW — UserPromptSubmit)
455
+
456
+ File: ~/.claude/hooks/specialists-complete.mjs (installed by `specialists install`)
457
+
458
+ Trigger: UserPromptSubmit — fires on every user message in Claude Code.
459
+
460
+ Logic:
461
+ 1. Scan .specialists/ready/ for any files
462
+ 2. For each file found:
463
+ a. Read .specialists/jobs/<id>/status.json
464
+ b. Format one-line banner
465
+ c. Delete the ready marker file (prevents repeat notifications)
466
+ 3. Inject banner(s) into the prompt
467
+
468
+ Banner format:
469
+ ```
470
+ [Specialist 'overthinker' completed (job a1b2c3, 1m47s). Run: specialists result a1b2c3]
471
+ ```
472
+
473
+ Multiple completions produce multiple banners, one per line.
474
+
475
+ If no ready markers exist, hook exits silently (no injection, no overhead).
476
+
477
+ ### 6.2 Existing hooks — unchanged
478
+
479
+ The four existing hooks (main-guard, beads-edit-gate, beads-commit-gate, beads-stop-gate)
480
+ are unchanged. The new hook is additive.
481
+
482
+ ---
483
+
484
+ ## 7. PiAgentSession Changes
485
+
486
+ ### 7.1 Current interface (SessionFactory — 6 methods)
487
+
488
+ ```typescript
489
+ start(): Promise<void> // spawn subprocess
490
+ prompt(task: string): Promise<void> // send task, currently closes stdin
491
+ waitForDone(): Promise<void> // resolve on agent_end
492
+ getLastOutput(): Promise<string> // return final text
493
+ kill(): void // SIGTERM + cleanup
494
+ meta: { backend, model, sessionId, startedAt }
495
+ ```
496
+
497
+ ### 7.2 Modified interface
498
+
499
+ ```typescript
500
+ start(): Promise<void> // spawn subprocess (unchanged)
501
+ prompt(task: string): Promise<void> // send RPC prompt command, DO NOT close stdin
502
+ waitForDone(timeout?: number): Promise<void> // resolve on agent_end, with timeout
503
+ getLastOutput(): Promise<string> // send get_last_assistant_text RPC, return response
504
+ getState(): Promise<SessionState> // send get_state RPC, return session info
505
+ close(): Promise<void> // send EOF (stdin.end()), wait for process exit
506
+ kill(): void // SIGTERM + cleanup (unchanged, for abort/cancel)
507
+ meta: { backend, model, sessionId, startedAt }
508
+ ```
509
+
510
+ Changes:
511
+ 1. prompt() sends {"type": "prompt", "message": task} via stdin.write(), no stdin.end()
512
+ 2. waitForDone() adds optional timeout parameter — Promise.race with timeout
513
+ 3. getLastOutput() sends {"type": "get_last_assistant_text"} via stdin, parses response
514
+ 4. getState() NEW — sends {"type": "get_state"} via stdin, parses response
515
+ 5. close() NEW — calls stdin.end(), waits for process exit event
516
+ 6. kill() unchanged — SIGTERM, idempotent
517
+
518
+ The supervisor sequence becomes:
519
+ ```
520
+ await session.start()
521
+ await session.prompt(renderedTask)
522
+ await session.waitForDone(timeoutMs) // blocks until agent_end or timeout
523
+ const output = await session.getLastOutput() // RPC query
524
+ const state = await session.getState() // RPC query
525
+ await session.close() // EOF → clean exit
526
+ ```
527
+
528
+ ### 7.3 NDJSON handling — unchanged
529
+
530
+ The manual line buffer (_lineBuffer + split on \n + process complete lines) is correct
531
+ and handles large payloads (100KB+ agent_end events). No change needed. The only
532
+ difference: agent_end no longer triggers stdin.end(). It resolves the waitForDone()
533
+ promise, and the supervisor decides when to query state and close.
534
+
535
+ ### 7.4 RPC response handling — new
536
+
537
+ Currently, PiAgentSession only reads events from stdout. It never sends commands via
538
+ stdin (except the implicit single prompt via the CLI args). With the new protocol:
539
+
540
+ After agent_end, stdin is still open. The supervisor sends JSON commands and reads
541
+ responses from stdout. Responses are JSON objects with type: "response", keyed by the
542
+ command type. The existing NDJSON line reader handles these — they arrive as regular
543
+ JSON lines on stdout.
544
+
545
+ Parsing:
546
+ ```typescript
547
+ // After agent_end, switch to command-response mode
548
+ async sendCommand(cmd: object): Promise<any> {
549
+ const line = JSON.stringify(cmd) + '\n';
550
+ this.proc.stdin.write(line);
551
+ // Wait for next complete JSON line on stdout that is a response
552
+ return new Promise((resolve) => {
553
+ this._pendingCommand = resolve;
554
+ });
555
+ }
556
+ ```
557
+
558
+ The _handleLine method checks: if the parsed JSON has type === "response", resolve
559
+ _pendingCommand. Otherwise, process as a normal event.
560
+
561
+ ---
562
+
563
+ ## 8. SpecialistRunner Changes
564
+
565
+ ### 8.1 Background mode support
566
+
567
+ SpecialistRunner.run() currently blocks until completion. For --background, the CLI's
568
+ run command handles the lifecycle — it calls runner.run() with callbacks that write to
569
+ files instead of streaming to stdout.
570
+
571
+ The runner itself does NOT need a "background mode". The CLI command is the supervisor.
572
+ Runner provides the execution; the CLI provides the lifecycle management.
573
+
574
+ ### 8.2 Session path passthrough
575
+
576
+ Runner must accept an optional sessionPath in PiSessionOptions and pass it through to
577
+ PiAgentSession.start() as the --session flag. For --continue, the CLI reads the session
578
+ path from the previous job's status.json and passes it to the runner.
579
+
580
+ ### 8.3 Skills paths passthrough
581
+
582
+ Runner must accept an optional skillPaths: string[] in PiSessionOptions and pass each
583
+ as a --skill flag. Skills paths come from the specialist's YAML config (skills.paths),
584
+ resolved by the loader at discovery time.
585
+
586
+ ### 8.4 Timeout enforcement
587
+
588
+ Runner should pass execution.timeout_ms to session.waitForDone(timeout). On timeout,
589
+ call session.kill() and throw a TimeoutError. Circuit breaker records the failure.
590
+
591
+ ---
592
+
593
+ ## 9. MCP Server Changes
594
+
595
+ ### 9.1 Retained tools
596
+
597
+ **specialist_init**: Unchanged. Returns available specialists and beads status.
598
+
599
+ **list_specialists**: Unchanged. Pure read.
600
+
601
+ **specialist_status**: Modified — add job summary from .specialists/jobs/*/status.json.
602
+ Returns circuit breaker health + active/recent job list.
603
+
604
+ **use_specialist**: Unchanged. Synchronous execution for quick tasks. Calls runner.run()
605
+ directly. Appropriate for sub-15-second specialists (init-session, report-generator).
606
+
607
+ ### 9.2 Deprecated tools
608
+
609
+ **start_specialist**: Deprecated. Replaced by `specialists run --background` via bash.
610
+ Keep functional for backward compatibility but add a note in the tool description
611
+ suggesting the CLI alternative.
612
+
613
+ **poll_specialist**: Deprecated. Replaced by `specialists status` and
614
+ `specialists result` via bash.
615
+
616
+ **stop_specialist**: Deprecated. Replaced by `specialists stop` via bash.
617
+
618
+ **run_parallel**: Deprecated. Claude can run multiple `specialists run --background &`
619
+ commands. Or use the synchronous path for quick parallel tasks via use_specialist.
620
+
621
+ ### 9.3 SIGTERM handler (NEW)
622
+
623
+ ```typescript
624
+ process.on('SIGTERM', () => {
625
+ // Kill all running pi subprocesses managed by use_specialist (sync path)
626
+ registry.getAllRunning().forEach(job => job.killFn?.());
627
+ process.exit(0);
628
+ });
629
+ ```
630
+
631
+ Only covers the MCP server's own subprocess (use_specialist sync path). Background
632
+ jobs are owned by their supervisor processes, not the MCP server.
633
+
634
+ ---
635
+
636
+ ## 10. Operational Concerns
637
+
638
+ ### 10.1 .gitignore
639
+
640
+ `specialists init` must add `.specialists/` to .gitignore. Job files can contain full
641
+ specialist output, session history, and environment variables from pre-scripts.
642
+
643
+ ### 10.2 Cleanup / GC
644
+
645
+ On `specialists run --background` start, before creating the new job directory:
646
+ scan .specialists/jobs/ and delete any directory older than 7 days. Configurable
647
+ via SPECIALISTS_JOB_TTL_DAYS env var. Simple fs.stat + rm -rf.
648
+
649
+ ### 10.3 Beads crash recovery
650
+
651
+ On `specialists run --background` start (same GC scan): check for jobs with
652
+ status "running" that have no live process (PID check). Close their beads as ERROR.
653
+ Mark status as "error" with error: "MCP server or supervisor crashed".
654
+
655
+ ### 10.4 File atomicity
656
+
657
+ status.json: written via write-to-temp + fs.rename. Updated frequently (every lifecycle
658
+ event), must never be half-written.
659
+
660
+ events.jsonl: written via fs.appendFile with O_APPEND. Atomic for writes < 4KB on Linux.
661
+ Event lines are well under 4KB.
662
+
663
+ result.txt: written once via write-to-temp + fs.rename. Single write on completion.
664
+
665
+ ### 10.5 File descriptor management
666
+
667
+ The supervisor opens events.jsonl for append at job start and keeps the fd open for the
668
+ duration. On completion, error, or cancel: fd.close() is called in a finally block.
669
+ On supervisor crash: OS closes the fd automatically.
670
+
671
+ ---
672
+
673
+ ## 11. Migration Path
674
+
675
+ ### Phase 1: File layout + background mode (HIGH VALUE, LOW RISK)
676
+
677
+ 1. Create .specialists/ directory structure
678
+ 2. Add --background flag to `specialists run`
679
+ 3. Implement supervisor lifecycle (status.json, events.jsonl, result.txt)
680
+ 4. Add `specialists status` job listing (reads status.json files)
681
+ 5. Add `specialists result` command
682
+ 6. Add `specialists feed` command
683
+ 7. Add `specialists stop` command (CLI)
684
+ 8. Add .gitignore entry in `specialists init`
685
+ 9. Add GC on startup
686
+
687
+ Does NOT touch: PiAgentSession internals, MCP tools, SpecialistRunner.
688
+ Uses existing PiAgentSession with its current limitations (single-turn only, no RPC
689
+ queries post-completion). result.txt is populated from the existing getLastOutput()
690
+ which reads from the in-memory _lastOutput captured at agent_end.
691
+
692
+ This phase delivers: background execution, lightweight status polling via bash,
693
+ full output retrieval, feed/debugging CLI. Everything except multi-turn and RPC queries.
694
+
695
+ ### Phase 2: PiAgentSession protocol upgrade (MEDIUM VALUE, MEDIUM RISK)
696
+
697
+ 1. Remove stdin.end() from prompt() — send RPC JSON command instead
698
+ 2. Add waitForDone(timeout) with Promise.race
699
+ 3. Add getState() RPC command
700
+ 4. Add getLastOutput() via RPC (replaces in-memory capture)
701
+ 5. Add close() method (explicit EOF)
702
+ 6. Add --session flag passthrough
703
+ 7. Add --skill flag passthrough
704
+ 8. Add stall detection (in-process setInterval in supervisor)
705
+ 9. Update SpecialistRunner for sessionPath and skillPaths options
706
+
707
+ Prerequisite: verify that pi --mode rpc accepts JSON commands on stdin (not just
708
+ CLI args). The audit's PiAgentSession code and pi's RPC docs both indicate this works,
709
+ but test before implementing.
710
+
711
+ This phase delivers: multi-turn via --continue, session persistence, skill injection,
712
+ timeout enforcement, stall detection.
713
+
714
+ ### Phase 3: Hook notification + MCP deprecation (LOW VALUE, LOW RISK)
715
+
716
+ 1. Create specialists-complete.mjs hook
717
+ 2. Add to `specialists install` hook deployment
718
+ 3. Add deprecation notes to start_specialist, poll_specialist, stop_specialist,
719
+ run_parallel tool descriptions
720
+ 4. Add SIGTERM handler to MCP server
721
+
722
+ ### Phase 4: Schema + built-in specialist updates
723
+
724
+ 1. Add skills.paths to schema (zod validation, loader resolution)
725
+ 2. Add execution.stall_timeout_ms to schema
726
+ 3. Update built-in specialists to use skills.paths where appropriate
727
+ 4. Create example skill directories in ~/.agents/skills/
728
+
729
+ ---
730
+
731
+ ## 12. Testing Strategy
732
+
733
+ ### Phase 1 tests
734
+
735
+ - Supervisor lifecycle: mock PiAgentSession, verify status.json written at each stage
736
+ - status.json atomicity: concurrent write + read, verify no partial JSON
737
+ - events.jsonl: verify correct events logged, no thinking deltas
738
+ - result.txt: verify written only on completion, not on error/cancel
739
+ - GC: create old job dirs, verify cleanup
740
+ - specialists status: verify reads from file, formats correctly
741
+ - specialists result: verify returns result.txt content, error if still running
742
+ - specialists stop: verify SIGTERM sent to correct PID
743
+
744
+ ### Phase 2 tests
745
+
746
+ - RPC prompt command: verify JSON sent via stdin, stdin NOT closed
747
+ - RPC get_state: verify command sent after agent_end, response parsed
748
+ - RPC get_last_assistant_text: verify command sent, response parsed
749
+ - close(): verify stdin.end() called, process exit awaited
750
+ - waitForDone(timeout): verify timeout triggers kill + rejection
751
+ - Stall detection: verify stalled flag set after configurable interval
752
+ - --session flag: verify passed to pi subprocess args
753
+ - --skill flag: verify each skills.paths entry becomes a --skill arg
754
+ - --continue: verify session path read from previous job, passed to pi
755
+
756
+ ### Phase 1 does NOT require
757
+
758
+ - Integration tests against real pi subprocess (existing mock-based tests cover this)
759
+ - MCP tool handler tests (tools are unchanged in Phase 1)
760
+ - NDJSON parser tests (parser is unchanged in Phase 1)
761
+
762
+ ---
763
+
764
+ ## 13. Verification Checklist
765
+
766
+ All items verified against pi-mono source (packages/coding-agent) and pi --help output.
767
+
768
+ - [x] `pi --mode rpc` accepts {"type": "prompt", "message": "..."} on stdin
769
+ — confirmed: rpc-mode.ts handleCommand case "prompt"
770
+ - [x] `pi --session /arbitrary/path/session.jsonl` accepts paths outside ~/.pi/
771
+ — confirmed: main.ts resolveSessionPath() treats any arg containing "/" as a direct path
772
+ - [x] `pi --append-system-prompt "..."` exists and works as expected
773
+ — confirmed: pi --help
774
+ - [x] `pi --skill <path>` loads skill directories correctly, repeatable
775
+ — confirmed: pi --help
776
+ - [x] After agent_end, pi accepts further stdin commands before stdin is closed
777
+ — confirmed: runRpcMode() never exits on agent_end; loop runs until stdin EOF
778
+ - [x] `get_state` response includes sessionFile field
779
+ — confirmed: rpc-mode.ts get_state handler: sessionFile: session.sessionFile
780
+ - [x] `get_last_assistant_text` returns clean text
781
+ — confirmed: agent-session.ts getLastAssistantText(); returns null (not empty string)
782
+ when no assistant message exists — null-guard required before writing result.txt
783
+
784
+ **Additional findings from pi-mono source audit:**
785
+
786
+ - `--no-input` does not exist in pi — removed from spawn args (section 4.1)
787
+ - `prompt` RPC command responds with an immediate ack, not a completion signal — supervisor
788
+ must keep reading stdout until agent_end (see section 4.2 note)
789
+ - `--no-extensions` required to prevent user extensions from hanging the supervisor
790
+ via unanswered extension_ui_request events (see section 4.1)
791
+ - Pi's `--tools` default (`read,bash,edit,write`) excludes grep/find/ls — all tiers now
792
+ use explicit tool lists in mapPermissionToTools() (see section 4.1 table)