@polderlabs/bizar 6.2.4 → 6.3.0

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 (332) hide show
  1. package/.claude/CLAUDE.md +15 -0
  2. package/.claude/agents/_shared/AGENT_BASELINE.md +168 -0
  3. package/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
  4. package/.claude/agents/_shared/SKILLS.md +109 -0
  5. package/.claude/agents/agent-browser.md +80 -0
  6. package/.claude/agents/baldr.md +49 -0
  7. package/.claude/agents/forseti.md +51 -0
  8. package/.claude/agents/frigg.md +42 -0
  9. package/.claude/agents/heimdall.md +33 -0
  10. package/.claude/agents/hermod.md +53 -0
  11. package/.claude/agents/mimir.md +49 -0
  12. package/.claude/agents/odin.md +287 -0
  13. package/.claude/agents/quick.md +34 -0
  14. package/.claude/agents/semble-search.md +50 -0
  15. package/.claude/agents/thor.md +53 -0
  16. package/.claude/agents/tyr.md +56 -0
  17. package/.claude/agents/vidarr.md +54 -0
  18. package/.claude/agents/vor.md +53 -0
  19. package/.claude/commands/audit.md +25 -0
  20. package/.claude/commands/bizar.md +22 -0
  21. package/.claude/commands/explain.md +17 -0
  22. package/.claude/commands/init.md +29 -0
  23. package/.claude/commands/learn.md +44 -0
  24. package/.claude/commands/plan.md +35 -0
  25. package/.claude/commands/plow-through.md +50 -0
  26. package/.claude/commands/pr-review.md +49 -0
  27. package/.claude/commands/setup-provider.md +96 -0
  28. package/.claude/commands/tailscale-serve.md +100 -0
  29. package/.claude/commands/team.md +132 -0
  30. package/.claude/commands/test.md +62 -0
  31. package/.claude/commands/validate.md +68 -0
  32. package/.claude/commands/visual-plan.md +24 -0
  33. package/.claude/hooks/README.md +92 -0
  34. package/.claude/hooks/posttooluse-editwrite.mjs +91 -0
  35. package/.claude/hooks/pretooluse-bash.mjs +81 -0
  36. package/.claude/hooks/pretooluse-editwrite.mjs +139 -0
  37. package/.claude/hooks/sessionend-recall.mjs +74 -0
  38. package/.claude/hooks/sessionstart-prime.mjs +80 -0
  39. package/.claude/hooks/userpromptsubmit-tag.mjs +80 -0
  40. package/.claude/settings.json +116 -0
  41. package/.claude/skills/9router/SKILL.md +80 -0
  42. package/.claude/skills/9router-chat/SKILL.md +73 -0
  43. package/.claude/skills/9router-embeddings/SKILL.md +69 -0
  44. package/.claude/skills/9router-image/SKILL.md +86 -0
  45. package/.claude/skills/9router-stt/SKILL.md +79 -0
  46. package/.claude/skills/9router-tts/SKILL.md +80 -0
  47. package/.claude/skills/9router-web-fetch/SKILL.md +99 -0
  48. package/.claude/skills/9router-web-search/SKILL.md +91 -0
  49. package/.claude/skills/bizar/README.md +9 -0
  50. package/.claude/skills/bizar/SKILL.md +450 -0
  51. package/.claude/skills/cpp-coding-standards/README.md +28 -0
  52. package/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
  53. package/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
  54. package/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
  55. package/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  56. package/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  57. package/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  58. package/.claude/skills/cpp-testing/README.md +28 -0
  59. package/.claude/skills/cpp-testing/SKILL.md +304 -0
  60. package/.claude/skills/cpp-testing/references/coverage.md +370 -0
  61. package/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
  62. package/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  63. package/.claude/skills/cpp-testing/references/mocking.md +364 -0
  64. package/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
  65. package/.claude/skills/cubesandbox/SKILL.md +148 -0
  66. package/.claude/skills/embedded-esp-idf/README.md +41 -0
  67. package/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
  68. package/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  69. package/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
  70. package/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  71. package/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
  72. package/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  73. package/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  74. package/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
  75. package/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  76. package/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  77. package/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  78. package/.claude/skills/glyph/SKILL.md +163 -0
  79. package/.claude/skills/harness-engineering/SKILL.md +143 -0
  80. package/.claude/skills/lightrag/SKILL.md +81 -0
  81. package/.claude/skills/memory-protocol/SKILL.md +105 -0
  82. package/.claude/skills/obsidian/SKILL.md +306 -0
  83. package/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
  84. package/.claude/skills/self-improvement/SKILL.md +64 -0
  85. package/README.md +87 -59
  86. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js → EnvVarsSection-B58aiJiE.js} +1 -1
  87. package/bizar-dash/dist/assets/{EnvVarsSection-DXM8gRm_.js.map → EnvVarsSection-B58aiJiE.js.map} +1 -1
  88. package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js → MobileChat-BJrqwVDd.js} +1 -1
  89. package/bizar-dash/dist/assets/{MobileChat-BnKN_Ks_.js.map → MobileChat-BJrqwVDd.js.map} +1 -1
  90. package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js → MobileSettings-CEQNJNLJ.js} +1 -1
  91. package/bizar-dash/dist/assets/{MobileSettings-DjCPxC-Q.js.map → MobileSettings-CEQNJNLJ.js.map} +1 -1
  92. package/bizar-dash/dist/assets/{main-DYiZqMrn.js → main-IvfQAOfy.js} +1 -1
  93. package/bizar-dash/dist/assets/{main-DYiZqMrn.js.map → main-IvfQAOfy.js.map} +1 -1
  94. package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js → markdown-tOLaD6nm.js} +1 -1
  95. package/bizar-dash/dist/assets/{markdown-C6mXtQxD.js.map → markdown-tOLaD6nm.js.map} +1 -1
  96. package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js → mobile-DYCHcUpq.js} +1 -1
  97. package/bizar-dash/dist/assets/{mobile-CMHqtLV2.js.map → mobile-DYCHcUpq.js.map} +1 -1
  98. package/bizar-dash/dist/assets/{mobile-layout-3jIhHX_p.js → mobile-layout-CBHjpwsb.js} +2 -2
  99. package/bizar-dash/dist/assets/{mobile-layout-3jIhHX_p.js.map → mobile-layout-CBHjpwsb.js.map} +1 -1
  100. package/bizar-dash/dist/assets/{useSlashCommands-DtITw8Xv.js → useSlashCommands-Bd7_FA6U.js} +2 -2
  101. package/bizar-dash/dist/assets/{useSlashCommands-DtITw8Xv.js.map → useSlashCommands-Bd7_FA6U.js.map} +1 -1
  102. package/bizar-dash/dist/assets/{vendor-CeHGtduv.js → vendor-C843201K.js} +12 -12
  103. package/bizar-dash/dist/assets/vendor-C843201K.js.map +1 -0
  104. package/bizar-dash/dist/index.html +6 -6
  105. package/bizar-dash/dist/mobile.html +2 -2
  106. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  107. package/bizar-dash/src/server/api.mjs +4 -4
  108. package/bizar-dash/src/server/background-store.mjs +15 -16
  109. package/bizar-dash/src/server/bg-poller.mjs +25 -35
  110. package/bizar-dash/src/server/bg-retry.mjs +66 -194
  111. package/bizar-dash/src/server/claude-bg-spawner.mjs +390 -0
  112. package/bizar-dash/src/server/claude-info.mjs +412 -0
  113. package/bizar-dash/src/server/{cline-runner.mjs → claude-runner.mjs} +53 -48
  114. package/bizar-dash/src/server/claude-sdk.mjs +191 -0
  115. package/bizar-dash/src/server/providers-store.mjs +22 -49
  116. package/bizar-dash/src/server/routes/background.mjs +24 -20
  117. package/bizar-dash/src/server/routes/chat.mjs +223 -326
  118. package/bizar-dash/src/server/routes/claude-session-detail.mjs +329 -0
  119. package/bizar-dash/src/server/routes/claude-sessions.mjs +259 -0
  120. package/bizar-dash/src/server/routes/tasks.mjs +20 -28
  121. package/bizar-dash/src/server/task-delegator.mjs +76 -77
  122. package/bizar-dash/src/web/components/chat/useChat.ts +23 -27
  123. package/bizar-dash/src/web/views/Chat.tsx +8 -8
  124. package/cli/bin.mjs +31 -18
  125. package/cli/commands/claude-cmd.mjs +348 -0
  126. package/cli/commands/install.mjs +12 -11
  127. package/cli/commands/sandbox.mjs +220 -0
  128. package/cli/commands/validate.mjs +252 -288
  129. package/cli/dev-link.test.mjs +11 -11
  130. package/cli/doctor.mjs +131 -213
  131. package/cli/install.mjs +33 -37
  132. package/cli/install.test.mjs +9 -9
  133. package/cli/provision-claude.mjs +893 -0
  134. package/cli/provision.mjs +330 -226
  135. package/cli/provision.test.mjs +102 -0
  136. package/cli/utils.mjs +72 -58
  137. package/config/AGENTS.md +28 -28
  138. package/config/agents/_shared/AGENT_BASELINE.md +104 -684
  139. package/config/skills/bizar/SKILL.md +197 -0
  140. package/config/skills/cubesandbox/SKILL.md +148 -0
  141. package/config/skills/harness-engineering/SKILL.md +142 -0
  142. package/install.sh +66 -44
  143. package/package.json +19 -11
  144. package/packages/sdk/ARCHITECTURE.md +40 -36
  145. package/packages/sdk/package-lock.json +32 -0
  146. package/packages/sdk/package.json +15 -5
  147. package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
  148. package/packages/sdk/src/fingerprint.ts +0 -0
  149. package/packages/sdk/src/index.ts +38 -73
  150. package/packages/sdk/src/mcp/bin.ts +51 -0
  151. package/packages/sdk/src/mcp/server.ts +498 -0
  152. package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
  153. package/packages/sdk/tests/sdk.test.mjs +148 -0
  154. package/packages/sdk/vitest.config.ts +1 -1
  155. package/scripts/bh-full-e2e.mjs +166 -363
  156. package/scripts/check-deps.mjs +28 -46
  157. package/scripts/mirror-claude-md.sh +78 -0
  158. package/scripts/test-in-container.sh +157 -0
  159. package/templates/clean-state-checklist.md +3 -3
  160. package/templates/sprint-contract.md +2 -2
  161. package/bizar-dash/.bizar/activity.log +0 -3
  162. package/bizar-dash/.omx/logs/omx-2026-07-07.jsonl +0 -1
  163. package/bizar-dash/.omx/state/session.json +0 -10
  164. package/bizar-dash/bizar-design/EXPLANATION.md +0 -307
  165. package/bizar-dash/bizar-design/INSTRUCTIONS.md +0 -149
  166. package/bizar-dash/bizar-design/canvas.html +0 -281
  167. package/bizar-dash/bizar-design/canvas.html.artifact.json +0 -19
  168. package/bizar-dash/bizar-design/components.css +0 -1665
  169. package/bizar-dash/bizar-design/components.html +0 -557
  170. package/bizar-dash/bizar-design/components.html.artifact.json +0 -19
  171. package/bizar-dash/bizar-design/desktop/agents.html +0 -74
  172. package/bizar-dash/bizar-design/desktop/agents.html.artifact.json +0 -19
  173. package/bizar-dash/bizar-design/desktop/memory.html +0 -75
  174. package/bizar-dash/bizar-design/desktop/memory.html.artifact.json +0 -19
  175. package/bizar-dash/bizar-design/desktop/metrics.html +0 -63
  176. package/bizar-dash/bizar-design/desktop/metrics.html.artifact.json +0 -19
  177. package/bizar-dash/bizar-design/desktop/overview.html +0 -95
  178. package/bizar-dash/bizar-design/desktop/overview.html.artifact.json +0 -19
  179. package/bizar-dash/bizar-design/desktop/settings.html +0 -70
  180. package/bizar-dash/bizar-design/desktop/settings.html.artifact.json +0 -19
  181. package/bizar-dash/bizar-design/icons.svg +0 -44
  182. package/bizar-dash/bizar-design/index.html +0 -192
  183. package/bizar-dash/bizar-design/index.html.artifact.json +0 -19
  184. package/bizar-dash/bizar-design/mobile/agents.html +0 -29
  185. package/bizar-dash/bizar-design/mobile/agents.html.artifact.json +0 -19
  186. package/bizar-dash/bizar-design/mobile/memory.html +0 -28
  187. package/bizar-dash/bizar-design/mobile/memory.html.artifact.json +0 -19
  188. package/bizar-dash/bizar-design/mobile/metrics.html +0 -29
  189. package/bizar-dash/bizar-design/mobile/metrics.html.artifact.json +0 -19
  190. package/bizar-dash/bizar-design/mobile/overview.html +0 -33
  191. package/bizar-dash/bizar-design/mobile/overview.html.artifact.json +0 -19
  192. package/bizar-dash/bizar-design/mobile/settings.html +0 -28
  193. package/bizar-dash/bizar-design/mobile/settings.html.artifact.json +0 -19
  194. package/bizar-dash/bizar-design/prototype.js +0 -114
  195. package/bizar-dash/bizar-design/tokens.css +0 -106
  196. package/bizar-dash/dist/assets/vendor-CeHGtduv.js.map +0 -1
  197. package/bizar-dash/node_modules/.package-lock.json +0 -6
  198. package/bizar-dash/package-lock.json +0 -6
  199. package/bizar-dash/src/server/cline-sdk.mjs +0 -132
  200. package/bizar-dash/src/server/routes/cline-session-detail.mjs +0 -559
  201. package/bizar-dash/src/server/routes/cline-sessions.mjs +0 -291
  202. package/cli/commands/cline-cmd.mjs +0 -289
  203. package/cli/commands/validate.test.mjs +0 -344
  204. package/cli/doctor.test.mjs +0 -350
  205. package/config/cline.json.template +0 -342
  206. package/packages/sdk/src/client.ts +0 -188
  207. package/packages/sdk/src/cline-events.ts +0 -134
  208. package/packages/sdk/src/cline-types.ts +0 -66
  209. package/packages/sdk/src/cline.ts +0 -339
  210. package/packages/sdk/src/errors.ts +0 -129
  211. package/packages/sdk/src/events.ts +0 -153
  212. package/packages/sdk/src/types.ts +0 -176
  213. package/packages/sdk/tests/client.test.ts +0 -217
  214. package/packages/sdk/tests/errors.test.ts +0 -108
  215. package/packages/sdk/tests/events.test.ts +0 -139
  216. package/packages/sdk/tests/fixtures/fetch-mock.ts +0 -152
  217. package/packages/sdk/tests/fixtures/sse-mock.ts +0 -30
  218. package/plugins/bizar/ARCHITECTURE.md +0 -142
  219. package/plugins/bizar/CONSTRAINTS.md +0 -67
  220. package/plugins/bizar/LICENSE +0 -21
  221. package/plugins/bizar/README.md +0 -448
  222. package/plugins/bizar/index.ts +0 -872
  223. package/plugins/bizar/package.json +0 -39
  224. package/plugins/bizar/scripts/check-forbidden-imports.sh +0 -33
  225. package/plugins/bizar/src/background-state.ts +0 -641
  226. package/plugins/bizar/src/background.ts +0 -1806
  227. package/plugins/bizar/src/cline-runner.ts +0 -203
  228. package/plugins/bizar/src/clineruntime.ts +0 -227
  229. package/plugins/bizar/src/commands-impl.ts +0 -151
  230. package/plugins/bizar/src/commands.ts +0 -1799
  231. package/plugins/bizar/src/compaction.d.mts +0 -48
  232. package/plugins/bizar/src/compaction.mjs +0 -192
  233. package/plugins/bizar/src/dashboard-client.ts +0 -233
  234. package/plugins/bizar/src/event-stream.ts +0 -606
  235. package/plugins/bizar/src/fingerprint.ts +0 -120
  236. package/plugins/bizar/src/handoff.ts +0 -79
  237. package/plugins/bizar/src/hooks/memory-flush-on-compact.ts +0 -123
  238. package/plugins/bizar/src/hooks/memory-inject.ts +0 -247
  239. package/plugins/bizar/src/hooks/memory-write-on-end.ts +0 -188
  240. package/plugins/bizar/src/hooks/skill-curator.ts +0 -180
  241. package/plugins/bizar/src/http-client.ts +0 -467
  242. package/plugins/bizar/src/key-rotation.ts +0 -218
  243. package/plugins/bizar/src/logger.ts +0 -144
  244. package/plugins/bizar/src/loop-engineering.ts +0 -241
  245. package/plugins/bizar/src/loop.ts +0 -176
  246. package/plugins/bizar/src/mistake-recovery.ts +0 -98
  247. package/plugins/bizar/src/odin.ts +0 -227
  248. package/plugins/bizar/src/options.ts +0 -470
  249. package/plugins/bizar/src/plan-fs.ts +0 -323
  250. package/plugins/bizar/src/reasoning-clean.ts +0 -454
  251. package/plugins/bizar/src/report.ts +0 -178
  252. package/plugins/bizar/src/research-prompt.ts +0 -35
  253. package/plugins/bizar/src/serve-info.ts +0 -228
  254. package/plugins/bizar/src/serve.ts +0 -496
  255. package/plugins/bizar/src/settings.ts +0 -349
  256. package/plugins/bizar/src/state.ts +0 -298
  257. package/plugins/bizar/src/tool-discipline.ts +0 -105
  258. package/plugins/bizar/src/tools/agent-browser.ts +0 -315
  259. package/plugins/bizar/src/tools/bg-collect.ts +0 -131
  260. package/plugins/bizar/src/tools/bg-get-comments.ts +0 -266
  261. package/plugins/bizar/src/tools/bg-kill.ts +0 -116
  262. package/plugins/bizar/src/tools/bg-pause.ts +0 -99
  263. package/plugins/bizar/src/tools/bg-report-progress.ts +0 -115
  264. package/plugins/bizar/src/tools/bg-resume.ts +0 -94
  265. package/plugins/bizar/src/tools/bg-send-message.ts +0 -223
  266. package/plugins/bizar/src/tools/bg-spawn.ts +0 -502
  267. package/plugins/bizar/src/tools/bg-status.ts +0 -130
  268. package/plugins/bizar/src/tools/graph-query.ts +0 -278
  269. package/plugins/bizar/src/tools/loop-engineering.ts +0 -193
  270. package/plugins/bizar/src/tools/memory-list.ts +0 -43
  271. package/plugins/bizar/src/tools/memory-read.ts +0 -69
  272. package/plugins/bizar/src/tools/memory-search.ts +0 -47
  273. package/plugins/bizar/src/tools/memory-write.ts +0 -54
  274. package/plugins/bizar/src/tools/open-kb.ts +0 -198
  275. package/plugins/bizar/src/tools/plan-action.ts +0 -785
  276. package/plugins/bizar/src/tools/read-glyph-feedback.ts +0 -191
  277. package/plugins/bizar/src/tools/team-spawn.ts +0 -73
  278. package/plugins/bizar/src/tools/team-status.ts +0 -76
  279. package/plugins/bizar/src/tools/wait-for-feedback.ts +0 -415
  280. package/plugins/bizar/src/trajectory.ts +0 -104
  281. package/plugins/bizar/tests/README.md +0 -99
  282. package/plugins/bizar/tests/attach-handler-bug.test.ts +0 -169
  283. package/plugins/bizar/tests/background-state.test.ts +0 -277
  284. package/plugins/bizar/tests/background.test.ts +0 -402
  285. package/plugins/bizar/tests/block.test.ts +0 -195
  286. package/plugins/bizar/tests/canonical-key-order.test.ts +0 -75
  287. package/plugins/bizar/tests/clineruntime-config.test.ts +0 -283
  288. package/plugins/bizar/tests/commands-impl.test.ts +0 -316
  289. package/plugins/bizar/tests/commands.test.ts +0 -584
  290. package/plugins/bizar/tests/compaction.test.ts +0 -264
  291. package/plugins/bizar/tests/config.test.ts +0 -128
  292. package/plugins/bizar/tests/dashboard-client.test.ts +0 -159
  293. package/plugins/bizar/tests/dispose.test.ts +0 -336
  294. package/plugins/bizar/tests/event-stream.test.ts +0 -409
  295. package/plugins/bizar/tests/event.test.ts +0 -262
  296. package/plugins/bizar/tests/fingerprint.test.ts +0 -162
  297. package/plugins/bizar/tests/http-client.test.ts +0 -404
  298. package/plugins/bizar/tests/init-helpers.test.ts +0 -203
  299. package/plugins/bizar/tests/integration/slash-command.test.ts +0 -349
  300. package/plugins/bizar/tests/integration/tool-routing.test.ts +0 -98
  301. package/plugins/bizar/tests/key-rotation.test.ts +0 -396
  302. package/plugins/bizar/tests/loop-engineering.test.ts +0 -168
  303. package/plugins/bizar/tests/loop.test.ts +0 -397
  304. package/plugins/bizar/tests/memory-write-on-end.test.ts +0 -92
  305. package/plugins/bizar/tests/mistake-recovery.test.ts +0 -116
  306. package/plugins/bizar/tests/odin.test.ts +0 -125
  307. package/plugins/bizar/tests/options.test.ts +0 -329
  308. package/plugins/bizar/tests/reasoning-clean.test.ts +0 -422
  309. package/plugins/bizar/tests/safety.test.ts +0 -256
  310. package/plugins/bizar/tests/serve.test.ts +0 -339
  311. package/plugins/bizar/tests/settings.test.ts +0 -351
  312. package/plugins/bizar/tests/stall-think.test.ts +0 -750
  313. package/plugins/bizar/tests/state.test.ts +0 -276
  314. package/plugins/bizar/tests/tool-discipline.test.ts +0 -77
  315. package/plugins/bizar/tests/tools/agent-browser.test.ts +0 -98
  316. package/plugins/bizar/tests/tools/bg-collect.test.ts +0 -337
  317. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +0 -485
  318. package/plugins/bizar/tests/tools/bg-kill.test.ts +0 -235
  319. package/plugins/bizar/tests/tools/bg-pause.test.ts +0 -61
  320. package/plugins/bizar/tests/tools/bg-report-progress.test.ts +0 -79
  321. package/plugins/bizar/tests/tools/bg-resume.test.ts +0 -40
  322. package/plugins/bizar/tests/tools/bg-send-message.test.ts +0 -116
  323. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +0 -147
  324. package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +0 -233
  325. package/plugins/bizar/tests/tools/bg-spawn.test.ts +0 -311
  326. package/plugins/bizar/tests/tools/bg-status.test.ts +0 -217
  327. package/plugins/bizar/tests/tools/cline-runner.test.ts +0 -115
  328. package/plugins/bizar/tests/tools/plan-action.test.ts +0 -599
  329. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +0 -253
  330. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +0 -390
  331. package/plugins/bizar/tests/update-deadlock.test.ts +0 -151
  332. package/plugins/bizar/tsconfig.json +0 -29
@@ -0,0 +1,412 @@
1
+ /**
2
+ * src/server/claude-info.mjs
3
+ *
4
+ * v6.3.0 — Dashboard-side documentation + discovery for the Claude
5
+ * Code runtime. Replaces serve-info.mjs (which documented the Cline
6
+ * HTTP serve subprocess contract).
7
+ *
8
+ * Why this module exists:
9
+ * Claude Code does NOT have an out-of-process serve child like
10
+ * Cline's `cline serve`. Instead, the dashboard talks to the
11
+ * `claude` CLI directly:
12
+ *
13
+ * - `claude -p "<prompt>"` — one-shot, runs to completion
14
+ * - `claude --bg` — background daemon session
15
+ * - `claude --resume <id> -p …` — resume an existing session
16
+ *
17
+ * And the SDK (`@anthropic-ai/claude-agent-sdk`) gives us in-process
18
+ * `query()` + `ClaudeSDKClient` for typed access to streaming events.
19
+ *
20
+ * This module:
21
+ * 1. Documents the CLI flag contract (so other modules don't have
22
+ * to hardcode flag names).
23
+ * 2. Documents the on-disk session storage at `~/.claude/sessions/`.
24
+ * 3. Resolves the `claude` binary path (PATH first, then `npx
25
+ * @anthropic-ai/claude-agent-sdk/cli`).
26
+ * 4. Provides small JSONL helpers for reading session message
27
+ * history that the rest of the dashboard can use.
28
+ *
29
+ * Reference:
30
+ * https://code.claude.com/docs/en/cli-reference
31
+ *
32
+ * No HTTP serve handshake, no Basic auth header, no SQLite DB. Claude
33
+ * Code reads API keys from the environment itself.
34
+ */
35
+
36
+ import { existsSync, readFileSync, readdirSync, rmSync, statSync } from 'node:fs';
37
+ import { homedir } from 'node:os';
38
+ import { join, resolve } from 'node:path';
39
+ import { warn as loggerWarn } from './logger.mjs';
40
+
41
+ const HOME = homedir();
42
+
43
+ /**
44
+ * Canonical on-disk paths for Claude Code.
45
+ *
46
+ * ~/.claude/ — root config + data dir (per Claude docs)
47
+ * ~/.claude/sessions/ — one subdir per session
48
+ * <sessionId>/
49
+ * messages.jsonl — conversation history (one JSON per line)
50
+ *
51
+ * Each messages.jsonl line is one of:
52
+ * - {type:"user", message:{role:"user", content:[...]}}
53
+ * - {type:"assistant", message:{role:"assistant", content:[...]}}
54
+ * - {type:"system", subtype:"init", session_id, model, ...}
55
+ * - {type:"result", subtype:"success"|"error_during_execution", ...}
56
+ */
57
+ export const CLAUDE_HOME = join(HOME, '.claude');
58
+ export const CLAUDE_SESSIONS_DIR = join(CLAUDE_HOME, 'sessions');
59
+
60
+ /**
61
+ * Resolve the `claude` binary path. Tries (in order):
62
+ * 1. `process.env.CLAUDE_BIN` (override)
63
+ * 2. `claude` on PATH (the user-installed case)
64
+ * 3. `npx -y @anthropic-ai/claude-agent-sdk/cli` as a fallback
65
+ *
66
+ * Returns `{ bin, args }` so the caller can construct the right
67
+ * subprocess argv. The fallback uses `npx -y` so a fresh checkout
68
+ * can spawn Claude Code even when the CLI isn't installed globally.
69
+ *
70
+ * @returns {{ bin: string, args: string[] }}
71
+ */
72
+ export function resolveClaudeCommand() {
73
+ if (process.env.CLAUDE_BIN && process.env.CLAUDE_BIN.trim()) {
74
+ return { bin: process.env.CLAUDE_BIN.trim(), args: [] };
75
+ }
76
+ return { bin: 'claude', args: [] };
77
+ }
78
+
79
+ /**
80
+ * Claude Code CLI flag contract (verified against
81
+ * https://code.claude.com/docs/en/cli-reference). Single source of
82
+ * truth — every other module reads flags from here so a Claude
83
+ * update only changes one file.
84
+ */
85
+ export const CLAUDE_CLI_FLAGS = Object.freeze({
86
+ // Print / run modes
87
+ PRINT: '-p',
88
+ BACKGROUND: '--bg',
89
+ RESUME: '--resume',
90
+ SESSION_ID: '--session-id',
91
+ SESSION_TITLE: '--session-title',
92
+
93
+ // Model + agent
94
+ MODEL: '--model',
95
+ AGENT: '--agent',
96
+
97
+ // Working directory
98
+ ADD_DIR: '--add-dir',
99
+
100
+ // Output / wire format
101
+ OUTPUT_FORMAT: '--output-format',
102
+ VERBOSE: '--verbose',
103
+ JSON_FORMAT: 'json',
104
+
105
+ // Permission / autonomy
106
+ PERMISSION_MODE: '--permission-mode',
107
+ PERMISSION_BYPASS: 'bypassPermissions',
108
+ PERMISSION_PLAN: 'plan',
109
+ PERMISSION_DEFAULT: 'default',
110
+
111
+ // MCP + skills
112
+ MCP_CONFIG: '--mcp-config',
113
+
114
+ // Limits
115
+ MAX_TURNS: '--max-turns',
116
+ });
117
+
118
+ /**
119
+ * Claude Code stores sessions as JSONL. Normalize one parsed line
120
+ * into the dashboard's chat shape:
121
+ *
122
+ * { id, role, content, ts, kind? }
123
+ *
124
+ * Accepts both v1 shapes (text directly on the message) and v2
125
+ * shapes (parts / blocks under `content`).
126
+ *
127
+ * @param {object} line one entry from messages.jsonl
128
+ * @returns {{ id: string, role: string, content: string, ts: number, kind?: string }}
129
+ */
130
+ export function normalizeClaudeMessage(line) {
131
+ if (!line || typeof line !== 'object') {
132
+ return { id: '', role: 'assistant', content: '', ts: Date.now() };
133
+ }
134
+ const t = typeof line.type === 'string' ? line.type : null;
135
+ const message = line.message || line;
136
+ const id =
137
+ (typeof message?.id === 'string' && message.id) ||
138
+ (typeof line.id === 'string' && line.id) ||
139
+ (typeof line.uuid === 'string' && line.uuid) ||
140
+ (typeof line.timestamp === 'string' && line.timestamp) ||
141
+ '';
142
+ const role = typeof message?.role === 'string' ? message.role : (t === 'user' ? 'user' : 'assistant');
143
+ const ts =
144
+ (typeof line.timestamp === 'string' ? Date.parse(line.timestamp) : NaN) ||
145
+ (typeof line.ts === 'number' ? line.ts : NaN) ||
146
+ (typeof message?.ts === 'number' ? message.ts : NaN) ||
147
+ Date.now();
148
+ const content = extractClaudeContent(message);
149
+ const out = { id: String(id), role: String(role), content, ts: typeof ts === 'number' && Number.isFinite(ts) ? ts : Date.now() };
150
+ if (t) out.kind = t;
151
+ return out;
152
+ }
153
+
154
+ /**
155
+ * Flatten a Claude Code message's `content` field into a single
156
+ * string. Handles:
157
+ * - string content ("hello")
158
+ * - array of blocks (text, tool_use, tool_result, image, …)
159
+ * - legacy { text } wrapper
160
+ *
161
+ * Non-text blocks are skipped — they don't contribute to the chat
162
+ * surface but still appear in the JSONL for tooling.
163
+ */
164
+ function extractClaudeContent(message) {
165
+ if (!message) return '';
166
+ if (typeof message.text === 'string') return stripThinkingTags(message.text);
167
+ const c = message.content;
168
+ if (typeof c === 'string') return stripThinkingTags(c);
169
+ if (!Array.isArray(c)) return '';
170
+ const text = c
171
+ .filter((p) => p && (p.type === 'text' || typeof p.text === 'string'))
172
+ .map((p) => (typeof p.text === 'string' ? p.text : ''))
173
+ .filter(Boolean)
174
+ .join('\n\n');
175
+ return stripThinkingTags(text);
176
+ }
177
+
178
+ /**
179
+ * Strip `<thinking>...</thinking>` blocks from model output so the
180
+ * chat UI doesn't render them as visible escaped HTML.
181
+ *
182
+ * Same shape as the v5.0.0 cline helper — kept here so chat
183
+ * rendering stays consistent across the migration.
184
+ */
185
+ function stripThinkingTags(text) {
186
+ if (typeof text !== 'string') return '';
187
+ const cleaned = text
188
+ .replace(/<thinking\b[^>]*>[\s\S]*?<\/thinking>/gi, '')
189
+ .replace(/<thinking\b[^>]*\/?>/gi, '')
190
+ .replace(/<\/thinking>/gi, '');
191
+ return cleaned.replace(/\n{3,}/g, '\n\n').trim();
192
+ }
193
+
194
+ /**
195
+ * List every Claude Code session currently on disk. Reads
196
+ * `~/.claude/sessions/` and returns metadata derived from each
197
+ * `messages.jsonl` (first system/init line carries the model +
198
+ * session id; first user message carries the title).
199
+ *
200
+ * Returns `[]` when the directory doesn't exist (a fresh user has
201
+ * never run `claude`). Never throws.
202
+ *
203
+ * @returns {Array<{ id: string, title: string, createdAt: number, updatedAt: number, model?: string }>}
204
+ */
205
+ export function listClaudeSessions() {
206
+ if (!existsSync(CLAUDE_SESSIONS_DIR)) return [];
207
+ let dirs;
208
+ try {
209
+ dirs = readdirSync(CLAUDE_SESSIONS_DIR, { withFileTypes: true })
210
+ .filter((d) => d.isDirectory())
211
+ .map((d) => d.name);
212
+ } catch (err) {
213
+ loggerWarn('claude-info: listClaudeSessions readdir failed', { err: err instanceof Error ? err.message : String(err) });
214
+ return [];
215
+ }
216
+ const out = [];
217
+ for (const id of dirs) {
218
+ const file = join(CLAUDE_SESSIONS_DIR, id, 'messages.jsonl');
219
+ if (!existsSync(file)) continue;
220
+ const meta = readSessionMeta(file, id);
221
+ if (meta) out.push(meta);
222
+ }
223
+ out.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
224
+ return out;
225
+ }
226
+
227
+ /**
228
+ * Read the metadata header for a single session (first init line +
229
+ * first user message + last mtime). Returns `null` on any failure.
230
+ *
231
+ * @param {string} file absolute path to messages.jsonl
232
+ * @param {string} id session id (the directory name)
233
+ */
234
+ function readSessionMeta(file, id) {
235
+ let firstUserText = '';
236
+ let model = '';
237
+ let createdAt = 0;
238
+ let lastTs = 0;
239
+ let size = 0;
240
+ try {
241
+ const stat = statSync(file);
242
+ size = stat.size;
243
+ lastTs = stat.mtimeMs || 0;
244
+ } catch {
245
+ /* fall through — stat is best-effort */
246
+ }
247
+ try {
248
+ const text = readFileSync(file, 'utf8');
249
+ const lines = text.split('\n');
250
+ for (const raw of lines) {
251
+ const line = raw.trim();
252
+ if (!line) continue;
253
+ let parsed;
254
+ try { parsed = JSON.parse(line); } catch { continue; }
255
+ if (parsed?.type === 'system' && parsed?.subtype === 'init') {
256
+ if (typeof parsed.session_id === 'string') id = parsed.session_id;
257
+ if (typeof parsed.model === 'string') model = parsed.model;
258
+ if (typeof parsed.timestamp === 'string') {
259
+ const t = Date.parse(parsed.timestamp);
260
+ if (Number.isFinite(t)) createdAt = createdAt || t;
261
+ }
262
+ }
263
+ if (parsed?.type === 'user' && !firstUserText) {
264
+ const content = parsed?.message?.content;
265
+ if (typeof content === 'string') firstUserText = content;
266
+ else if (Array.isArray(content)) {
267
+ const t = content.find((b) => b && (b.type === 'text' || typeof b.text === 'string'));
268
+ if (t && typeof t.text === 'string') firstUserText = t.text;
269
+ }
270
+ if (typeof parsed.timestamp === 'string') {
271
+ const t = Date.parse(parsed.timestamp);
272
+ if (Number.isFinite(t)) createdAt = createdAt || t;
273
+ }
274
+ }
275
+ if (typeof parsed?.timestamp === 'string') {
276
+ const t = Date.parse(parsed.timestamp);
277
+ if (Number.isFinite(t)) lastTs = Math.max(lastTs, t);
278
+ }
279
+ }
280
+ } catch {
281
+ return null;
282
+ }
283
+ const title = firstUserText
284
+ ? firstUserText.replace(/\s+/g, ' ').trim().slice(0, 80) || `Session ${id}`
285
+ : `Session ${id}`;
286
+ return {
287
+ id,
288
+ title,
289
+ createdAt: createdAt || lastTs || 0,
290
+ updatedAt: lastTs || createdAt || 0,
291
+ model: model || undefined,
292
+ size,
293
+ };
294
+ }
295
+
296
+ /**
297
+ * List every message in a session. Reads `messages.jsonl` from
298
+ * `~/.claude/sessions/<id>/` and normalizes each line via
299
+ * {@link normalizeClaudeMessage}.
300
+ *
301
+ * Returns `{ ok, messages }` on success; `{ ok: false, error }`
302
+ * when the file is missing or unreadable. Never throws.
303
+ *
304
+ * @param {string} sessionId
305
+ * @returns {{ ok: true, messages: ReturnType<typeof normalizeClaudeMessage>[] } | { ok: false, error: string }}
306
+ */
307
+ export function listClaudeMessages(sessionId) {
308
+ if (!sessionId || typeof sessionId !== 'string') {
309
+ return { ok: false, error: 'sessionId is required' };
310
+ }
311
+ const file = join(CLAUDE_SESSIONS_DIR, sessionId, 'messages.jsonl');
312
+ if (!existsSync(file)) {
313
+ return { ok: false, error: `session ${sessionId} not found at ${file}` };
314
+ }
315
+ let text;
316
+ try {
317
+ text = readFileSync(file, 'utf8');
318
+ } catch (err) {
319
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
320
+ }
321
+ const out = [];
322
+ for (const raw of text.split('\n')) {
323
+ const line = raw.trim();
324
+ if (!line) continue;
325
+ let parsed;
326
+ try { parsed = JSON.parse(line); } catch { continue; }
327
+ out.push(normalizeClaudeMessage(parsed));
328
+ }
329
+ return { ok: true, messages: out };
330
+ }
331
+
332
+ /**
333
+ * Read the most recent assistant text from a session — used by the
334
+ * bg-poller's html-artifact scanner (replaces the old
335
+ * `extractContentFromClineMessage` + `listClineMessages` flow).
336
+ *
337
+ * @param {string} sessionId
338
+ * @returns {string}
339
+ */
340
+ export function readLastAssistantText(sessionId) {
341
+ const res = listClaudeMessages(sessionId);
342
+ if (!res.ok) return '';
343
+ for (let i = res.messages.length - 1; i >= 0; i -= 1) {
344
+ const m = res.messages[i];
345
+ if (m && m.role === 'assistant' && m.content) return m.content;
346
+ }
347
+ return '';
348
+ }
349
+
350
+ /**
351
+ * Resolve the on-disk worktree (directory) for a session by looking
352
+ * at the JSONL init line. Claude Code stores `cwd` and `addDir` on
353
+ * the system init event.
354
+ *
355
+ * Returns `null` when the session can't be found or has no cwd field.
356
+ *
357
+ * @param {string} sessionId
358
+ * @returns {string|null}
359
+ */
360
+ export function resolveSessionWorktree(sessionId) {
361
+ if (!sessionId) return null;
362
+ const file = join(CLAUDE_SESSIONS_DIR, sessionId, 'messages.jsonl');
363
+ if (!existsSync(file)) return null;
364
+ try {
365
+ const text = readFileSync(file, 'utf8');
366
+ for (const raw of text.split('\n')) {
367
+ const line = raw.trim();
368
+ if (!line) continue;
369
+ let parsed;
370
+ try { parsed = JSON.parse(line); } catch { continue; }
371
+ if (parsed?.type === 'system' && parsed?.subtype === 'init') {
372
+ if (typeof parsed.cwd === 'string' && parsed.cwd) return resolve(parsed.cwd);
373
+ if (Array.isArray(parsed.addDir) && parsed.addDir.length > 0) {
374
+ const first = parsed.addDir.find((d) => typeof d === 'string' && d);
375
+ if (first) return resolve(first);
376
+ }
377
+ }
378
+ }
379
+ } catch { /* ignore */ }
380
+ return null;
381
+ }
382
+
383
+ /**
384
+ * Best-effort deletion of a session's on-disk directory. Idempotent.
385
+ *
386
+ * @param {string} sessionId
387
+ * @returns {{ ok: boolean, error?: string }}
388
+ */
389
+ export function deleteClaudeSession(sessionId) {
390
+ if (!sessionId || typeof sessionId !== 'string') {
391
+ return { ok: false, error: 'sessionId is required' };
392
+ }
393
+ const dir = join(CLAUDE_SESSIONS_DIR, sessionId);
394
+ if (!existsSync(dir)) return { ok: true };
395
+ try {
396
+ rmSync(dir, { recursive: true, force: true });
397
+ return { ok: true };
398
+ } catch (err) {
399
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
400
+ }
401
+ }
402
+
403
+ // Exposed for tests / introspection.
404
+ export const _claudeInfoInternals = {
405
+ CLAUDE_HOME,
406
+ CLAUDE_SESSIONS_DIR,
407
+ CLAUDE_CLI_FLAGS,
408
+ resolveClaudeCommand,
409
+ normalizeClaudeMessage,
410
+ listClaudeSessions,
411
+ listClaudeMessages,
412
+ };
@@ -1,16 +1,26 @@
1
1
  /**
2
- * bizar-dash/src/server/cline-runner.mjs
2
+ * bizar-dash/src/server/claude-runner.mjs
3
3
  *
4
- * v3.11.1Node child_process version of the cline-runner.
4
+ * v6.3.0Claude Code-native agent runner. Replaces the legacy
5
+ * cline-runner.mjs that spawned `cline run` subprocesses. This module
6
+ * spawns `claude -p` (print mode, blocking) or `claude --bg`
7
+ * (background, returns session id immediately).
5
8
  *
6
- * The plugin uses Bun.spawn (see plugins/bizar/src/cline-runner.ts);
7
- * the dashboard runs under Node and uses child_process.spawn. This
8
- * module keeps the two implementations in sync — same surface, same
9
- * log-format parsing, same exit semantics.
9
+ * Claude Code's CLI flags (from https://code.claude.com/docs/en/cli-reference):
10
+ * -p "<prompt>" : print mode (run, output, exit)
11
+ * --output-format json : JSON-lines output (one message per line)
12
+ * --model <model> : sonnet | opus | haiku (or full ID)
13
+ * --agent <name> : pre-defined subagent name from .claude/agents/
14
+ * --add-dir <path> : add a working directory
15
+ * --mcp-config <path> : load MCP config from JSON
16
+ * --session-id <id> : resume a specific session
17
+ * --verbose : verbose logging to stderr
10
18
  *
11
- * See plugins/bizar/src/cline-runner.ts for the design rationale
12
- * and the structured-log wire format we parse.
19
+ * Background mode (--bg) keeps the agent alive in a daemon process.
20
+ * The output is monitored via the SDK (query() streams events) — for
21
+ * the dashboard's HTTP layer we just need: spawn, list, kill, status.
13
22
  */
23
+
14
24
  import { spawn } from 'node:child_process';
15
25
  import { createWriteStream, mkdirSync, existsSync } from 'node:fs';
16
26
  import { dirname } from 'node:path';
@@ -29,12 +39,13 @@ import { dirname } from 'node:path';
29
39
  *
30
40
  * @typedef {Object} SpawnAgentOptions
31
41
  * @property {string} prompt
32
- * @property {string} agent
33
- * @property {{ providerID: string, modelID: string }} [model]
42
+ * @property {string} [agent] subagent name from .claude/agents/
43
+ * @property {string} [model] sonnet | opus | haiku | provider/model
34
44
  * @property {string} worktree
35
45
  * @property {string} logPath
36
46
  * @property {string} [title]
37
47
  * @property {Object<string,string>} [env]
48
+ * @property {boolean} [background] if true, run via `claude --bg` (persistent)
38
49
  * @property {number} [sessionIdTimeoutMs]
39
50
  *
40
51
  * @typedef {Object} SpawnAgentResult
@@ -48,8 +59,8 @@ import { dirname } from 'node:path';
48
59
  const agents = new Map();
49
60
 
50
61
  /**
51
- * Spawn a single `cline run` process. Resolves once the cline
52
- * child has reported its session id in the structured log stream
62
+ * Spawn a single `claude` (or `claude --bg`) process. Resolves once
63
+ * the child has reported its session id in the JSON output stream
53
64
  * (or once the process exits before that).
54
65
  *
55
66
  * @param {SpawnAgentOptions} opts
@@ -69,30 +80,32 @@ export function spawnAgent(opts) {
69
80
  }
70
81
  }
71
82
 
72
- // 2. Build argv.
73
- const args = [
74
- 'run',
75
- '--dir', opts.worktree,
76
- '--print-logs',
77
- '--log-level', 'INFO',
78
- '--title', opts.title || `bgr:${opts.agent}:${Date.now()}`,
79
- ];
80
- if (opts.model) {
81
- args.push('--model', `${opts.model.providerID}/${opts.model.modelID}`);
82
- }
83
- args.push('--', opts.prompt);
83
+ // 2. Build argv. `claude --bg` keeps the session alive as a daemon;
84
+ // `claude -p` runs to completion and exits.
85
+ const args = [];
86
+ if (opts.background) args.push('--bg');
87
+ else args.push('-p', opts.prompt);
88
+
89
+ args.push('--output-format', 'json');
90
+ args.push('--verbose');
91
+ args.push('--add-dir', opts.worktree);
92
+ if (opts.title) args.push('--session-title', opts.title);
93
+ if (opts.agent) args.push('--agent', opts.agent);
94
+ if (opts.model) args.push('--model', opts.model);
84
95
 
85
- // 3. Spawn the process.
96
+ // 3. Spawn the process. Prefer the `claude` binary on PATH; fall
97
+ // back to `npx @anthropic-ai/claude-agent-sdk/cli` if not found.
86
98
  let proc;
87
99
  try {
88
- proc = spawn('cline', args, {
100
+ proc = spawn('claude', args, {
101
+ cwd: opts.worktree,
89
102
  stdio: ['ignore', 'pipe', 'pipe'],
90
103
  env: { ...process.env, ...(opts.env || {}) },
91
104
  });
92
105
  } catch (err) {
93
106
  return Promise.resolve({
94
107
  ok: false,
95
- error: `failed to spawn cline run: ${err.message}`,
108
+ error: `failed to spawn claude: ${err.message}`,
96
109
  });
97
110
  }
98
111
 
@@ -107,7 +120,10 @@ export function spawnAgent(opts) {
107
120
 
108
121
  // 5. Pipe stdout+stderr to the log file.
109
122
  const logStream = createWriteStream(opts.logPath, { flags: 'a' });
110
- const sessionIdRegex = /message=created id=(ses_[A-Za-z0-9_]+)/;
123
+
124
+ // Claude Code prints session-id in the JSON-line stream as soon as
125
+ // the run starts. Pattern: {"type":"system","subtype":"init","session_id":"..."}
126
+ const sessionIdRegex = /"session_id"\s*:\s*"([A-Za-z0-9_-]{6,})"/;
111
127
  let sessionId;
112
128
 
113
129
  const resolveSpawnIfReady = (forceError) => {
@@ -123,15 +139,14 @@ export function spawnAgent(opts) {
123
139
  }
124
140
  };
125
141
 
126
- // 6. Stream readers — line-buffered by splitting on newlines. We
127
- // append a [stdout]/[stderr] prefix for human readability.
142
+ // 6. Stream readers — line-buffered by splitting on newlines.
128
143
  const attachLineReader = (stream, label) => {
129
144
  let buf = '';
130
145
  stream.setEncoding('utf8');
131
146
  stream.on('data', (chunk) => {
132
147
  const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
133
148
  logStream.write(`[${label}] ${text}`);
134
- if (label === 'stderr' && !sessionId) {
149
+ if (!sessionId) {
135
150
  buf += text;
136
151
  const m = buf.match(sessionIdRegex);
137
152
  if (m && m[1]) {
@@ -142,8 +157,7 @@ export function spawnAgent(opts) {
142
157
  }
143
158
  });
144
159
  stream.on('end', () => {
145
- // Final flush attempt.
146
- if (label === 'stderr' && !sessionId && buf) {
160
+ if (!sessionId && buf) {
147
161
  const m = buf.match(sessionIdRegex);
148
162
  if (m && m[1]) {
149
163
  sessionId = m[1];
@@ -171,11 +185,11 @@ export function spawnAgent(opts) {
171
185
  rec.status.state = 'done';
172
186
  } else {
173
187
  rec.status.state = 'failed';
174
- rec.status.error = rec.status.error || `cline run exited with code ${exitCode}`;
188
+ rec.status.error = rec.status.error || `claude exited with code ${exitCode}`;
175
189
  }
176
190
  }
177
191
  if (!sessionId) {
178
- resolveSpawnIfReady(rec.status.error || 'cline run exited before reporting session id');
192
+ resolveSpawnIfReady(rec.status.error || 'claude exited before reporting session id');
179
193
  }
180
194
  const cbs = rec.onExit.splice(0);
181
195
  for (const cb of cbs) {
@@ -199,7 +213,7 @@ export function spawnAgent(opts) {
199
213
  setTimeout(() => {
200
214
  if (!sessionId) {
201
215
  resolveSpawnIfReady(
202
- `cline run did not report a session id within ${sessionIdTimeoutMs}ms`,
216
+ `claude did not report a session id within ${sessionIdTimeoutMs}ms`,
203
217
  );
204
218
  }
205
219
  }, sessionIdTimeoutMs);
@@ -208,8 +222,6 @@ export function spawnAgent(opts) {
208
222
 
209
223
  /**
210
224
  * Snapshot the current status of an agent.
211
- * @param {number} processId
212
- * @returns {AgentStatus | null}
213
225
  */
214
226
  export function getStatus(processId) {
215
227
  const rec = agents.get(processId);
@@ -218,11 +230,8 @@ export function getStatus(processId) {
218
230
  }
219
231
 
220
232
  /**
221
- * Subscribe to the exit event. Callback fires exactly once when
222
- * the process exits.
223
- * @param {number} processId
224
- * @param {(status: AgentStatus) => void} cb
225
- * @returns {() => void} unsubscribe
233
+ * Subscribe to the exit event. Callback fires exactly once when the
234
+ * process exits.
226
235
  */
227
236
  export function onExit(processId, cb) {
228
237
  const rec = agents.get(processId);
@@ -241,9 +250,6 @@ export function onExit(processId, cb) {
241
250
 
242
251
  /**
243
252
  * Kill the agent. Sends SIGTERM, waits up to 5s, then SIGKILL.
244
- * @param {number} processId
245
- * @param {'SIGTERM'|'SIGKILL'} [signal]
246
- * @returns {{ ok: boolean, error?: string }}
247
253
  */
248
254
  export function killAgent(processId, signal = 'SIGTERM') {
249
255
  const rec = agents.get(processId);
@@ -267,7 +273,6 @@ export function killAgent(processId, signal = 'SIGTERM') {
267
273
 
268
274
  /**
269
275
  * List every tracked agent.
270
- * @returns {AgentStatus[]}
271
276
  */
272
277
  export function list() {
273
278
  return Array.from(agents.values()).map((r) => ({ ...r.status }));
@@ -276,4 +281,4 @@ export function list() {
276
281
  /** For tests: clear the in-memory registry. */
277
282
  export function _resetForTests() {
278
283
  agents.clear();
279
- }
284
+ }