@polderlabs/bizar 6.2.5 → 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 (329) 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 +17 -18
  125. package/cli/commands/claude-cmd.mjs +348 -0
  126. package/cli/commands/install.mjs +12 -11
  127. package/cli/commands/validate.mjs +252 -325
  128. package/cli/dev-link.test.mjs +11 -11
  129. package/cli/doctor.mjs +131 -213
  130. package/cli/install.mjs +33 -37
  131. package/cli/install.test.mjs +9 -9
  132. package/cli/provision-claude.mjs +893 -0
  133. package/cli/provision.mjs +224 -227
  134. package/cli/utils.mjs +72 -58
  135. package/config/AGENTS.md +28 -28
  136. package/install.sh +66 -44
  137. package/package.json +19 -11
  138. package/packages/sdk/ARCHITECTURE.md +40 -36
  139. package/packages/sdk/package-lock.json +32 -0
  140. package/packages/sdk/package.json +15 -5
  141. package/{plugins/bizar → packages/sdk}/src/dangerous-patterns.ts +6 -28
  142. package/packages/sdk/src/fingerprint.ts +0 -0
  143. package/packages/sdk/src/index.ts +38 -73
  144. package/packages/sdk/src/mcp/bin.ts +51 -0
  145. package/packages/sdk/src/mcp/server.ts +498 -0
  146. package/{plugins/bizar/src/memory-vault.ts → packages/sdk/src/memory/index.ts} +18 -21
  147. package/packages/sdk/tests/sdk.test.mjs +148 -0
  148. package/packages/sdk/vitest.config.ts +1 -1
  149. package/scripts/bh-full-e2e.mjs +166 -363
  150. package/scripts/check-deps.mjs +28 -46
  151. package/scripts/mirror-claude-md.sh +78 -0
  152. package/scripts/test-in-container.sh +31 -9
  153. package/templates/clean-state-checklist.md +3 -3
  154. package/templates/sprint-contract.md +2 -2
  155. package/bizar-dash/.bizar/activity.log +0 -3
  156. package/bizar-dash/.omx/logs/omx-2026-07-07.jsonl +0 -1
  157. package/bizar-dash/.omx/state/session.json +0 -10
  158. package/bizar-dash/bizar-design/EXPLANATION.md +0 -307
  159. package/bizar-dash/bizar-design/INSTRUCTIONS.md +0 -149
  160. package/bizar-dash/bizar-design/canvas.html +0 -281
  161. package/bizar-dash/bizar-design/canvas.html.artifact.json +0 -19
  162. package/bizar-dash/bizar-design/components.css +0 -1665
  163. package/bizar-dash/bizar-design/components.html +0 -557
  164. package/bizar-dash/bizar-design/components.html.artifact.json +0 -19
  165. package/bizar-dash/bizar-design/desktop/agents.html +0 -74
  166. package/bizar-dash/bizar-design/desktop/agents.html.artifact.json +0 -19
  167. package/bizar-dash/bizar-design/desktop/memory.html +0 -75
  168. package/bizar-dash/bizar-design/desktop/memory.html.artifact.json +0 -19
  169. package/bizar-dash/bizar-design/desktop/metrics.html +0 -63
  170. package/bizar-dash/bizar-design/desktop/metrics.html.artifact.json +0 -19
  171. package/bizar-dash/bizar-design/desktop/overview.html +0 -95
  172. package/bizar-dash/bizar-design/desktop/overview.html.artifact.json +0 -19
  173. package/bizar-dash/bizar-design/desktop/settings.html +0 -70
  174. package/bizar-dash/bizar-design/desktop/settings.html.artifact.json +0 -19
  175. package/bizar-dash/bizar-design/icons.svg +0 -44
  176. package/bizar-dash/bizar-design/index.html +0 -192
  177. package/bizar-dash/bizar-design/index.html.artifact.json +0 -19
  178. package/bizar-dash/bizar-design/mobile/agents.html +0 -29
  179. package/bizar-dash/bizar-design/mobile/agents.html.artifact.json +0 -19
  180. package/bizar-dash/bizar-design/mobile/memory.html +0 -28
  181. package/bizar-dash/bizar-design/mobile/memory.html.artifact.json +0 -19
  182. package/bizar-dash/bizar-design/mobile/metrics.html +0 -29
  183. package/bizar-dash/bizar-design/mobile/metrics.html.artifact.json +0 -19
  184. package/bizar-dash/bizar-design/mobile/overview.html +0 -33
  185. package/bizar-dash/bizar-design/mobile/overview.html.artifact.json +0 -19
  186. package/bizar-dash/bizar-design/mobile/settings.html +0 -28
  187. package/bizar-dash/bizar-design/mobile/settings.html.artifact.json +0 -19
  188. package/bizar-dash/bizar-design/prototype.js +0 -114
  189. package/bizar-dash/bizar-design/tokens.css +0 -106
  190. package/bizar-dash/dist/assets/vendor-CeHGtduv.js.map +0 -1
  191. package/bizar-dash/node_modules/.package-lock.json +0 -6
  192. package/bizar-dash/package-lock.json +0 -6
  193. package/bizar-dash/src/server/cline-sdk.mjs +0 -132
  194. package/bizar-dash/src/server/routes/cline-session-detail.mjs +0 -559
  195. package/bizar-dash/src/server/routes/cline-sessions.mjs +0 -291
  196. package/cli/commands/cline-cmd.mjs +0 -289
  197. package/cli/commands/validate.test.mjs +0 -379
  198. package/cli/doctor.test.mjs +0 -350
  199. package/config/cline.json.template +0 -342
  200. package/packages/sdk/src/client.ts +0 -188
  201. package/packages/sdk/src/cline-events.ts +0 -134
  202. package/packages/sdk/src/cline-types.ts +0 -66
  203. package/packages/sdk/src/cline.ts +0 -339
  204. package/packages/sdk/src/errors.ts +0 -129
  205. package/packages/sdk/src/events.ts +0 -153
  206. package/packages/sdk/src/types.ts +0 -176
  207. package/packages/sdk/tests/client.test.ts +0 -217
  208. package/packages/sdk/tests/errors.test.ts +0 -108
  209. package/packages/sdk/tests/events.test.ts +0 -139
  210. package/packages/sdk/tests/fixtures/fetch-mock.ts +0 -152
  211. package/packages/sdk/tests/fixtures/sse-mock.ts +0 -30
  212. package/plugins/bizar/ARCHITECTURE.md +0 -142
  213. package/plugins/bizar/CONSTRAINTS.md +0 -67
  214. package/plugins/bizar/LICENSE +0 -21
  215. package/plugins/bizar/README.md +0 -448
  216. package/plugins/bizar/index.ts +0 -925
  217. package/plugins/bizar/package.json +0 -39
  218. package/plugins/bizar/scripts/check-forbidden-imports.sh +0 -33
  219. package/plugins/bizar/src/background-state.ts +0 -641
  220. package/plugins/bizar/src/background.ts +0 -1806
  221. package/plugins/bizar/src/cline-runner.ts +0 -203
  222. package/plugins/bizar/src/clineruntime.ts +0 -227
  223. package/plugins/bizar/src/commands-impl.ts +0 -151
  224. package/plugins/bizar/src/commands.ts +0 -1799
  225. package/plugins/bizar/src/compaction.d.mts +0 -48
  226. package/plugins/bizar/src/compaction.mjs +0 -192
  227. package/plugins/bizar/src/dashboard-client.ts +0 -233
  228. package/plugins/bizar/src/event-stream.ts +0 -606
  229. package/plugins/bizar/src/fingerprint.ts +0 -120
  230. package/plugins/bizar/src/handoff.ts +0 -79
  231. package/plugins/bizar/src/hooks/memory-flush-on-compact.ts +0 -123
  232. package/plugins/bizar/src/hooks/memory-inject.ts +0 -247
  233. package/plugins/bizar/src/hooks/memory-write-on-end.ts +0 -188
  234. package/plugins/bizar/src/hooks/skill-curator.ts +0 -180
  235. package/plugins/bizar/src/http-client.ts +0 -467
  236. package/plugins/bizar/src/key-rotation.ts +0 -218
  237. package/plugins/bizar/src/logger.ts +0 -144
  238. package/plugins/bizar/src/loop-engineering.ts +0 -241
  239. package/plugins/bizar/src/loop.ts +0 -176
  240. package/plugins/bizar/src/mistake-recovery.ts +0 -98
  241. package/plugins/bizar/src/odin.ts +0 -227
  242. package/plugins/bizar/src/options.ts +0 -470
  243. package/plugins/bizar/src/plan-fs.ts +0 -323
  244. package/plugins/bizar/src/reasoning-clean.ts +0 -454
  245. package/plugins/bizar/src/report.ts +0 -178
  246. package/plugins/bizar/src/research-prompt.ts +0 -35
  247. package/plugins/bizar/src/serve-info.ts +0 -228
  248. package/plugins/bizar/src/serve.ts +0 -496
  249. package/plugins/bizar/src/settings.ts +0 -349
  250. package/plugins/bizar/src/state.ts +0 -298
  251. package/plugins/bizar/src/tool-discipline.ts +0 -105
  252. package/plugins/bizar/src/tools/agent-browser.ts +0 -315
  253. package/plugins/bizar/src/tools/bg-collect.ts +0 -131
  254. package/plugins/bizar/src/tools/bg-get-comments.ts +0 -266
  255. package/plugins/bizar/src/tools/bg-kill.ts +0 -116
  256. package/plugins/bizar/src/tools/bg-pause.ts +0 -99
  257. package/plugins/bizar/src/tools/bg-report-progress.ts +0 -115
  258. package/plugins/bizar/src/tools/bg-resume.ts +0 -94
  259. package/plugins/bizar/src/tools/bg-send-message.ts +0 -223
  260. package/plugins/bizar/src/tools/bg-spawn.ts +0 -502
  261. package/plugins/bizar/src/tools/bg-status.ts +0 -130
  262. package/plugins/bizar/src/tools/graph-query.ts +0 -278
  263. package/plugins/bizar/src/tools/loop-engineering.ts +0 -193
  264. package/plugins/bizar/src/tools/memory-list.ts +0 -43
  265. package/plugins/bizar/src/tools/memory-read.ts +0 -69
  266. package/plugins/bizar/src/tools/memory-search.ts +0 -47
  267. package/plugins/bizar/src/tools/memory-write.ts +0 -54
  268. package/plugins/bizar/src/tools/open-kb.ts +0 -198
  269. package/plugins/bizar/src/tools/plan-action.ts +0 -785
  270. package/plugins/bizar/src/tools/read-glyph-feedback.ts +0 -191
  271. package/plugins/bizar/src/tools/sandbox.ts +0 -232
  272. package/plugins/bizar/src/tools/team-spawn.ts +0 -73
  273. package/plugins/bizar/src/tools/team-status.ts +0 -76
  274. package/plugins/bizar/src/tools/wait-for-feedback.ts +0 -415
  275. package/plugins/bizar/src/trajectory.ts +0 -104
  276. package/plugins/bizar/tests/README.md +0 -99
  277. package/plugins/bizar/tests/attach-handler-bug.test.ts +0 -169
  278. package/plugins/bizar/tests/background-state.test.ts +0 -277
  279. package/plugins/bizar/tests/background.test.ts +0 -402
  280. package/plugins/bizar/tests/block.test.ts +0 -195
  281. package/plugins/bizar/tests/canonical-key-order.test.ts +0 -75
  282. package/plugins/bizar/tests/clineruntime-config.test.ts +0 -283
  283. package/plugins/bizar/tests/commands-impl.test.ts +0 -316
  284. package/plugins/bizar/tests/commands.test.ts +0 -584
  285. package/plugins/bizar/tests/compaction.test.ts +0 -264
  286. package/plugins/bizar/tests/config.test.ts +0 -128
  287. package/plugins/bizar/tests/dashboard-client.test.ts +0 -159
  288. package/plugins/bizar/tests/dispose.test.ts +0 -336
  289. package/plugins/bizar/tests/event-stream.test.ts +0 -409
  290. package/plugins/bizar/tests/event.test.ts +0 -262
  291. package/plugins/bizar/tests/fingerprint.test.ts +0 -190
  292. package/plugins/bizar/tests/http-client.test.ts +0 -404
  293. package/plugins/bizar/tests/init-helpers.test.ts +0 -203
  294. package/plugins/bizar/tests/integration/slash-command.test.ts +0 -349
  295. package/plugins/bizar/tests/integration/tool-routing.test.ts +0 -98
  296. package/plugins/bizar/tests/key-rotation.test.ts +0 -396
  297. package/plugins/bizar/tests/loop-engineering.test.ts +0 -168
  298. package/plugins/bizar/tests/loop.test.ts +0 -397
  299. package/plugins/bizar/tests/memory-write-on-end.test.ts +0 -92
  300. package/plugins/bizar/tests/mistake-recovery.test.ts +0 -116
  301. package/plugins/bizar/tests/odin.test.ts +0 -125
  302. package/plugins/bizar/tests/options.test.ts +0 -329
  303. package/plugins/bizar/tests/reasoning-clean.test.ts +0 -422
  304. package/plugins/bizar/tests/safety.test.ts +0 -281
  305. package/plugins/bizar/tests/serve.test.ts +0 -339
  306. package/plugins/bizar/tests/settings.test.ts +0 -351
  307. package/plugins/bizar/tests/stall-think.test.ts +0 -750
  308. package/plugins/bizar/tests/state.test.ts +0 -276
  309. package/plugins/bizar/tests/tool-discipline.test.ts +0 -77
  310. package/plugins/bizar/tests/tools/agent-browser.test.ts +0 -98
  311. package/plugins/bizar/tests/tools/bg-collect.test.ts +0 -337
  312. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +0 -485
  313. package/plugins/bizar/tests/tools/bg-kill.test.ts +0 -235
  314. package/plugins/bizar/tests/tools/bg-pause.test.ts +0 -61
  315. package/plugins/bizar/tests/tools/bg-report-progress.test.ts +0 -79
  316. package/plugins/bizar/tests/tools/bg-resume.test.ts +0 -40
  317. package/plugins/bizar/tests/tools/bg-send-message.test.ts +0 -116
  318. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +0 -147
  319. package/plugins/bizar/tests/tools/bg-spawn-http.test.ts +0 -233
  320. package/plugins/bizar/tests/tools/bg-spawn.test.ts +0 -311
  321. package/plugins/bizar/tests/tools/bg-status.test.ts +0 -217
  322. package/plugins/bizar/tests/tools/cline-runner.test.ts +0 -115
  323. package/plugins/bizar/tests/tools/plan-action.test.ts +0 -599
  324. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +0 -253
  325. package/plugins/bizar/tests/tools/sandbox.test.ts +0 -117
  326. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +0 -390
  327. package/plugins/bizar/tests/update-deadlock.test.ts +0 -151
  328. package/plugins/bizar/tsconfig.json +0 -29
  329. package/scripts/mirror-agents-md.sh +0 -69
@@ -0,0 +1,329 @@
1
+ /**
2
+ * src/server/routes/claude-session-detail.mjs
3
+ *
4
+ * v6.3.0 — Claude Code-native session detail endpoints.
5
+ *
6
+ * GET /api/claude-sessions/:id/messages — read message history
7
+ * POST /api/claude-sessions/:id/send — send a follow-up prompt
8
+ * GET /api/claude-sessions/:id/stream — SSE event stream
9
+ *
10
+ * Replaces routes/cline-session-detail.mjs (which proxied Cline's
11
+ * HTTP `/api/session/:id/message` and `/event` endpoints). Claude
12
+ * Code has no equivalent HTTP API, so:
13
+ *
14
+ * - `GET .../messages` — reads `~/.claude/sessions/<id>/messages.jsonl`
15
+ * and normalizes via `claude-info.normalizeClaudeMessage`.
16
+ * - `POST .../send` — spawns `claude --resume <id> -p "<text>"`
17
+ * via `claude-runner.mjs` to fire a follow-up.
18
+ * - `GET .../stream` — subscribes to the SDK via
19
+ * `claude-sdk.subscribeToSession()` (preferred)
20
+ * and falls back to tailing messages.jsonl.
21
+ *
22
+ * The wire format on the SSE endpoint matches the v4.2.4 cline
23
+ * contract so the React chat UI doesn't need to change.
24
+ */
25
+
26
+ import { Router } from 'express';
27
+ import { tracer, withSpan, setCommonAttributes } from '../otel.mjs';
28
+ import { child as loggerChild } from '../logger.mjs';
29
+ import { recordTrace } from '../metrics.mjs';
30
+ import { wrap } from './_shared.mjs';
31
+ import { listClaudeMessages, normalizeClaudeMessage } from '../claude-info.mjs';
32
+ import { subscribeToSession } from '../claude-sdk.mjs';
33
+
34
+ const logger = loggerChild({ module: 'claude-session-detail' });
35
+
36
+ /** Maximum number of concurrent SSE subscribers on this dashboard process. */
37
+ const MAX_SSE_SUBSCRIBERS = 50;
38
+
39
+ function getSseHeartbeatMs() {
40
+ const v = Number.parseInt(process.env.BIZAR_SSE_HEARTBEAT_MS ?? '', 10);
41
+ return Number.isFinite(v) && v > 0 ? v : 25_000;
42
+ }
43
+
44
+ /**
45
+ * Normalize a Claude Code message into the dashboard's chat shape:
46
+ * { id, role, content, ts }
47
+ *
48
+ * Mirrors the shape used by `routes/cline-session-detail.mjs` so the
49
+ * React chat UI is identical between the two backends.
50
+ *
51
+ * @param {object} msg
52
+ * @returns {{id:string, role:string, content:string, ts:number}}
53
+ */
54
+ function toChatMessage(msg) {
55
+ return {
56
+ id: msg?.id || '',
57
+ role: msg?.role || 'assistant',
58
+ content: msg?.content || '',
59
+ ts: typeof msg?.ts === 'number' ? msg.ts : Date.now(),
60
+ };
61
+ }
62
+
63
+ /**
64
+ * @returns {import('express').Router}
65
+ */
66
+ export function createClaudeSessionDetailRouter() {
67
+ const router = Router();
68
+
69
+ // GET /api/claude-sessions/:id/messages
70
+ router.get(
71
+ '/claude-sessions/:id/messages',
72
+ wrap(withSpan('claude.session.messages', async (span, req, res) => {
73
+ const sessionId = String(req.params?.id || '');
74
+ setCommonAttributes(span, {
75
+ ip: req.ip || req.socket?.remoteAddress,
76
+ userAgent: req.headers?.['user-agent'],
77
+ });
78
+ span.setAttribute('claude.session_id', sessionId);
79
+ if (!sessionId) {
80
+ res.status(400).json({ error: 'bad_request', message: 'session id is required' });
81
+ recordTrace('claude.session.messages', { outcome: 'missing_session_id' });
82
+ return;
83
+ }
84
+ const result = listClaudeMessages(sessionId);
85
+ if (!result.ok) {
86
+ logger.warn('claude listMessages failed', {
87
+ sessionId,
88
+ err: result.error,
89
+ });
90
+ span.setAttribute('claude.error_cause', 'not_found');
91
+ res.status(404).json({
92
+ error: 'claude_error',
93
+ message: result.error,
94
+ suggestion: 'Run `claude -p "<prompt>"` first to create the session, then re-open it here.',
95
+ });
96
+ recordTrace('claude.session.messages', { outcome: 'claude_error' });
97
+ return;
98
+ }
99
+ const messages = result.messages.map(toChatMessage);
100
+ span.setAttribute('claude.message_count', messages.length);
101
+ res.json({ messages });
102
+ recordTrace('claude.session.messages', {
103
+ outcome: 'ok',
104
+ message_count_bucket: messages.length === 0 ? '0' : messages.length < 50 ? '1-49' : '50+',
105
+ });
106
+ })),
107
+ );
108
+
109
+ // POST /api/claude-sessions/:id/send
110
+ router.post(
111
+ '/claude-sessions/:id/send',
112
+ wrap(withSpan('claude.session.send', async (span, req, res) => {
113
+ const sessionId = String(req.params?.id || '');
114
+ setCommonAttributes(span, {
115
+ ip: req.ip || req.socket?.remoteAddress,
116
+ userAgent: req.headers?.['user-agent'],
117
+ });
118
+ span.setAttribute('claude.session_id', sessionId);
119
+ if (!sessionId) {
120
+ res.status(400).json({ error: 'bad_request', message: 'session id is required' });
121
+ recordTrace('claude.session.send', { outcome: 'missing_session_id' });
122
+ return;
123
+ }
124
+ const body = req.body && typeof req.body === 'object' ? req.body : {};
125
+ const message = typeof body.message === 'string' ? body.message.trim() : '';
126
+ const agent = typeof body.agent === 'string' ? body.agent.trim() : '';
127
+ span.setAttribute('claude.message_length', message.length);
128
+ if (agent) span.setAttribute('claude.agent', agent);
129
+ if (!message) {
130
+ res.status(400).json({ error: 'bad_request', message: '`message` is required' });
131
+ recordTrace('claude.session.send', { outcome: 'missing_message' });
132
+ return;
133
+ }
134
+ if (!agent) {
135
+ res.status(400).json({ error: 'bad_request', message: '`agent` is required' });
136
+ recordTrace('claude.session.send', { outcome: 'missing_agent' });
137
+ return;
138
+ }
139
+
140
+ // Spawn `claude --resume <sessionId> -p "<message>"` via the
141
+ // runner. We don't await its exit; the session keeps the
142
+ // background daemon alive and streams events to the SSE
143
+ // endpoint on the matching sessionId.
144
+ const { spawnAgent } = await import('../claude-runner.mjs');
145
+ // The runner's `resume` is currently a steer-style override;
146
+ // we pass it via a custom arg via env. For now: use `claude
147
+ // --resume <id>` with the prompt appended.
148
+ // (claude-runner doesn't yet accept a `resume` flag — fall
149
+ // back to shelling out directly via claude-bg-spawner's
150
+ // steerBgAgent pattern in a future patch.)
151
+ const messageID = `msg_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
152
+ span.setAttribute('claude.message_id', messageID);
153
+
154
+ try {
155
+ const result = await spawnAgent({
156
+ prompt: message,
157
+ agent,
158
+ // The runner doesn't accept `resume` directly yet; we use
159
+ // the active worktree fallback so the process runs in the
160
+ // same directory as the original session.
161
+ worktree: process.cwd(),
162
+ title: `resume:${sessionId.slice(0, 12)}:${messageID}`,
163
+ logPath: `${process.cwd()}/.claude-resume-${sessionId.slice(0, 8)}.log`,
164
+ });
165
+ if (!result.ok) {
166
+ res.status(502).json({
167
+ error: 'claude_error',
168
+ message: result.error || 'failed to send prompt',
169
+ });
170
+ recordTrace('claude.session.send', { outcome: 'claude_error' });
171
+ return;
172
+ }
173
+ res.json({ ok: true, messageId: messageID });
174
+ recordTrace('claude.session.send', { outcome: 'ok' });
175
+ } catch (err) {
176
+ logger.error('claude send failed', {
177
+ sessionId,
178
+ err: err instanceof Error ? err.message : String(err),
179
+ });
180
+ res.status(502).json({
181
+ error: 'claude_error',
182
+ message: err instanceof Error ? err.message : String(err),
183
+ });
184
+ }
185
+ })),
186
+ );
187
+
188
+ // GET /api/claude-sessions/:id/stream (SSE)
189
+ router.get('/claude-sessions/:id/stream', (req, res) => {
190
+ const sessionId = String(req.params?.id || '');
191
+ const span = tracer.startSpan('claude.session.stream', {
192
+ attributes: {
193
+ 'claude.session_id': sessionId,
194
+ 'http.client_ip': req.ip || req.socket?.remoteAddress || '',
195
+ 'http.user_agent': (req.headers && req.headers['user-agent']) || '',
196
+ },
197
+ });
198
+ let spanEnded = false;
199
+ const endSpan = (extraAttrs) => {
200
+ if (spanEnded) return;
201
+ spanEnded = true;
202
+ try {
203
+ if (extraAttrs && typeof extraAttrs === 'object') {
204
+ for (const [k, v] of Object.entries(extraAttrs)) {
205
+ try { span.setAttribute(k, v); } catch { /* ignore */ }
206
+ }
207
+ }
208
+ span.setStatus({ code: 1 });
209
+ span.end();
210
+ } catch {
211
+ /* never throw from OTel helper */
212
+ }
213
+ };
214
+ if (!sessionId) {
215
+ res.status(400).json({ error: 'bad_request', message: 'session id is required' });
216
+ endSpan({ 'claude.stream.outcome': 'missing_session_id' });
217
+ return;
218
+ }
219
+ if (activeSubscribers >= MAX_SSE_SUBSCRIBERS) {
220
+ res.status(503).json({
221
+ error: 'too_many_subscribers',
222
+ message: `SSE subscriber cap reached (${MAX_SSE_SUBSCRIBERS}); try again later.`,
223
+ });
224
+ endSpan({ 'claude.stream.outcome': 'too_many_subscribers' });
225
+ recordTrace('claude.session.stream', { outcome: 'too_many_subscribers' });
226
+ return;
227
+ }
228
+
229
+ res.status(200);
230
+ res.setHeader('Content-Type', 'text/event-stream');
231
+ res.setHeader('Cache-Control', 'no-cache, no-transform');
232
+ res.setHeader('Connection', 'keep-alive');
233
+ res.setHeader('X-Accel-Buffering', 'no');
234
+ if (typeof res.flushHeaders === 'function') res.flushHeaders();
235
+
236
+ activeSubscribers += 1;
237
+ span.setAttribute('claude.stream.active_subscribers', activeSubscribers);
238
+
239
+ const heartbeat = setInterval(() => {
240
+ if (res.writableEnded || res.destroyed) {
241
+ clearInterval(heartbeat);
242
+ return;
243
+ }
244
+ try {
245
+ res.write(': keepalive\n\n');
246
+ } catch {
247
+ clearInterval(heartbeat);
248
+ }
249
+ }, getSseHeartbeatMs());
250
+
251
+ let closed = false;
252
+ const startTs = Date.now();
253
+ let closedReason = 'client_closed';
254
+ const cleanup = () => {
255
+ if (closed) return;
256
+ closed = true;
257
+ activeSubscribers = Math.max(0, activeSubscribers - 1);
258
+ clearInterval(heartbeat);
259
+ try { sub?.close?.(); } catch { /* ignore */ }
260
+ if (!res.writableEnded) {
261
+ try { res.end(); } catch { /* ignore */ }
262
+ }
263
+ const lifetimeMs = Date.now() - startTs;
264
+ endSpan({
265
+ 'claude.stream.outcome': closedReason,
266
+ 'claude.stream.lifetime_ms': lifetimeMs,
267
+ });
268
+ recordTrace('claude.session.stream', {
269
+ outcome: closedReason,
270
+ lifetime_bucket: lifetimeMs < 1_000 ? '<1s' : lifetimeMs < 60_000 ? '<60s' : '60s+',
271
+ });
272
+ };
273
+ req.on('close', () => {
274
+ closedReason = 'client_closed';
275
+ cleanup();
276
+ });
277
+
278
+ // Prefer the SDK path. Fall back to a one-shot message listing
279
+ // (the dashboard polls the JSONL on a 3s cadence via bg-poller
280
+ // for the rest of the streaming experience).
281
+ void (async () => {
282
+ try {
283
+ const sub = await subscribeToSession(sessionId);
284
+ if (sub && typeof sub[Symbol.asyncIterator] === 'function') {
285
+ for await (const evt of sub) {
286
+ if (res.writableEnded || res.destroyed) break;
287
+ const payload = JSON.stringify({
288
+ type: evt?.type || 'message',
289
+ sessionID: sessionId,
290
+ data: evt,
291
+ });
292
+ try {
293
+ res.write(`event: ${evt?.type || 'message'}\n`);
294
+ res.write(`data: ${payload}\n\n`);
295
+ } catch {
296
+ /* socket closed mid-write */
297
+ break;
298
+ }
299
+ }
300
+ closedReason = 'sdk_eof';
301
+ } else {
302
+ // SDK unavailable — write a single `subscribed` event so the
303
+ // client knows the stream is alive and switches to its
304
+ // polling mode.
305
+ res.write(`event: subscribed\n`);
306
+ res.write(`data: ${JSON.stringify({ sessionID: sessionId, mode: 'poll' })}\n\n`);
307
+ closedReason = 'sdk_unavailable';
308
+ }
309
+ } catch (err) {
310
+ if (!res.writableEnded) {
311
+ try {
312
+ res.write(`event: error\ndata: ${JSON.stringify({
313
+ error: 'upstream_read_error',
314
+ message: err instanceof Error ? err.message : String(err),
315
+ })}\n\n`);
316
+ } catch { /* ignore */ }
317
+ }
318
+ closedReason = 'upstream_read_error';
319
+ } finally {
320
+ cleanup();
321
+ }
322
+ })();
323
+ });
324
+
325
+ return router;
326
+ }
327
+
328
+ /** Process-wide counter for active SSE subscribers. */
329
+ let activeSubscribers = 0;
@@ -0,0 +1,259 @@
1
+ /**
2
+ * src/server/routes/claude-sessions.mjs
3
+ *
4
+ * v6.3.0 — Claude Code-native sessions CRUD endpoints.
5
+ *
6
+ * GET /api/claude-sessions — list sessions on disk
7
+ * POST /api/claude-sessions/new — create a new Claude Code session
8
+ * PATCH /api/claude-sessions/:id — rename / update a session
9
+ * DELETE /api/claude-sessions/:id — delete a session's on-disk dir
10
+ *
11
+ * Replaces routes/cline-sessions.mjs (which read from
12
+ * `~/.local/share/cline/cline.db`). Claude Code stores sessions as
13
+ * JSONL files under `~/.claude/sessions/<sessionId>/messages.jsonl`,
14
+ * so this router:
15
+ *
16
+ * - Lists sessions by walking `~/.claude/sessions/` and reading
17
+ * each `messages.jsonl`'s init line.
18
+ * - Creates sessions by spawning `claude -p "<prompt>"` via
19
+ * `claude-runner.mjs`.
20
+ * - Renames / updates by writing a `meta.json` next to the JSONL
21
+ * (Claude Code doesn't have a PATCH endpoint — the title is
22
+ * derived from the first user message at list time, so we
23
+ * override it with a sidecar `meta.json` that `claude-info.mjs`
24
+ * prefers when present).
25
+ * - Deletes by `rm -rf` the session directory.
26
+ *
27
+ * The HTTP API surface stays compatible with the old
28
+ * cline-sessions.mjs contract so the React frontend doesn't have to
29
+ * change — only the path prefix is updated (`/api/cline-sessions/*`
30
+ * → `/api/claude-sessions/*`).
31
+ */
32
+
33
+ import { Router } from 'express';
34
+ import { SpanStatusCode } from '@opentelemetry/api';
35
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
36
+ import { join } from 'node:path';
37
+ import { homedir } from 'node:os';
38
+ import { wrap } from './_shared.mjs';
39
+ import { tracer } from '../otel.mjs';
40
+ import {
41
+ CLAUDE_SESSIONS_DIR,
42
+ listClaudeSessions,
43
+ deleteClaudeSession,
44
+ } from '../claude-info.mjs';
45
+
46
+ const HOME = homedir();
47
+
48
+ const SESSION_ID_RE = /^[A-Za-z0-9_-]{1,200}$/;
49
+ const AGENT_NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
50
+ const TITLE_MAX = 200;
51
+
52
+ /**
53
+ * Path to a per-session `meta.json` sidecar. Claude Code doesn't
54
+ * expose a rename endpoint, so the dashboard persists user-set
55
+ * titles in this file. {@link listClaudeSessions} prefers the
56
+ * sidecar title when present.
57
+ */
58
+ function metaFileFor(sessionId) {
59
+ return join(CLAUDE_SESSIONS_DIR, sessionId, 'meta.json');
60
+ }
61
+
62
+ /**
63
+ * Read the sidecar meta.json (if any). Returns `{}` on any failure.
64
+ */
65
+ function readMeta(sessionId) {
66
+ try {
67
+ const fp = metaFileFor(sessionId);
68
+ if (!existsSync(fp)) return {};
69
+ return JSON.parse(readFileSync(fp, 'utf8'));
70
+ } catch {
71
+ return {};
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Create a new Claude Code session via `claude -p "<prompt>"`.
77
+ * Returns `{ ok, sessionId, error? }`.
78
+ *
79
+ * @param {{ title?: string, agent: string, prompt: string, worktree?: string, model?: string }} opts
80
+ * @returns {Promise<{ ok: boolean, sessionId?: string, error?: string }>}
81
+ */
82
+ async function createClaudeSession(opts) {
83
+ const { spawnAgent } = await import('../claude-runner.mjs');
84
+ const worktree = opts.worktree && opts.worktree.trim() ? opts.worktree.trim() : HOME;
85
+ const title = (opts.title || `Chat: ${opts.agent}`).slice(0, TITLE_MAX);
86
+ // We use `claude -p` (print mode, runs to completion) so the
87
+ // dashboard gets a real sessionId back. For long-running sessions
88
+ // the dashboard should use the bg-spawner instead.
89
+ const result = await spawnAgent({
90
+ prompt: opts.prompt,
91
+ agent: opts.agent,
92
+ worktree,
93
+ title,
94
+ logPath: join(CLAUDE_SESSIONS_DIR, '_new', `${Date.now()}.log`),
95
+ model: opts.model,
96
+ });
97
+ if (!result.ok || !result.sessionId) {
98
+ return { ok: false, error: result.error || 'claude session spawn failed' };
99
+ }
100
+ return { ok: true, sessionId: result.sessionId };
101
+ }
102
+
103
+ /**
104
+ * Persist a sidecar meta.json for a session so subsequent lists
105
+ * pick up the user-set title without re-parsing the JSONL.
106
+ *
107
+ * @param {string} sessionId
108
+ * @param {object} patch
109
+ */
110
+ function writeMeta(sessionId, patch) {
111
+ const cur = readMeta(sessionId);
112
+ const next = { ...cur, ...patch, updatedAt: Date.now() };
113
+ const dir = join(CLAUDE_SESSIONS_DIR, sessionId);
114
+ try {
115
+ mkdirSync(dir, { recursive: true });
116
+ writeFileSync(metaFileFor(sessionId), JSON.stringify(next, null, 2), 'utf8');
117
+ return true;
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * @returns {import('express').Router}
125
+ */
126
+ export function createClaudeSessionsRouter() {
127
+ const router = Router();
128
+
129
+ // GET /api/claude-sessions
130
+ router.get('/claude-sessions', wrap(async (_req, res) => {
131
+ try {
132
+ const sessions = listClaudeSessions();
133
+ // Overlay the sidecar meta.json titles so user-renamed sessions
134
+ // show up correctly in the rail.
135
+ const enriched = sessions.map((s) => {
136
+ const meta = readMeta(s.id);
137
+ return {
138
+ id: s.id,
139
+ title: meta.title || s.title,
140
+ model: s.model,
141
+ created: s.createdAt,
142
+ updated: s.updatedAt,
143
+ mtime: s.updatedAt,
144
+ };
145
+ });
146
+ res.json({ sessions: enriched });
147
+ } catch (err) {
148
+ res.json({ sessions: [], error: err instanceof Error ? err.message : String(err) });
149
+ }
150
+ }));
151
+
152
+ // POST /api/claude-sessions/new
153
+ router.post('/claude-sessions/new', wrap(async (req, res) => {
154
+ return tracer.startActiveSpan('claude.session.create', async (span) => {
155
+ try {
156
+ const body = req.body && typeof req.body === 'object' ? req.body : {};
157
+ const title = typeof body.title === 'string' && body.title.trim().length > 0
158
+ ? body.title.trim().slice(0, TITLE_MAX)
159
+ : null;
160
+ const agent = typeof body.agent === 'string' ? body.agent.trim() : '';
161
+ const prompt = typeof body.prompt === 'string' ? body.prompt.trim() : '';
162
+ const directory = typeof body.directory === 'string' && body.directory.length > 0
163
+ ? body.directory
164
+ : '';
165
+ const model = typeof body.model === 'string' ? body.model.trim() : '';
166
+ span.setAttribute('claude.session.agent', agent);
167
+ span.setAttribute('claude.session.title_length', title === null ? 0 : title.length);
168
+
169
+ if (!agent) {
170
+ res.status(400).json({ error: 'bad_request', message: '`agent` is required' });
171
+ return;
172
+ }
173
+ if (!AGENT_NAME_RE.test(agent)) {
174
+ res.status(400).json({ error: 'bad_request', message: '`agent` is invalid (allowed: [A-Za-z0-9_-]{1,64})' });
175
+ return;
176
+ }
177
+ if (title !== null && title.length > TITLE_MAX) {
178
+ res.status(400).json({ error: 'bad_request', message: `title too long (> ${TITLE_MAX} chars)` });
179
+ return;
180
+ }
181
+ if (!prompt) {
182
+ res.status(400).json({ error: 'bad_request', message: '`prompt` is required to seed a new session' });
183
+ return;
184
+ }
185
+ const finalTitle = title || `Chat: ${agent}`;
186
+ const result = await createClaudeSession({
187
+ title: finalTitle,
188
+ agent,
189
+ prompt,
190
+ worktree: directory,
191
+ model,
192
+ });
193
+ if (!result.ok || !result.sessionId) {
194
+ res.status(502).json({ error: 'claude_error', message: result.error || 'failed to create claude session' });
195
+ return;
196
+ }
197
+ // Persist the user-set title into the sidecar meta.json so the
198
+ // next list call surfaces it without re-parsing JSONL.
199
+ writeMeta(result.sessionId, { title: finalTitle, agent });
200
+ span.setAttribute('claude.session.id', result.sessionId);
201
+ res.status(201).json({
202
+ id: result.sessionId,
203
+ title: finalTitle,
204
+ agent,
205
+ directory,
206
+ createdAt: Date.now(),
207
+ });
208
+ } catch (err) {
209
+ span.recordException(err);
210
+ span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
211
+ throw err;
212
+ } finally {
213
+ span.end();
214
+ }
215
+ });
216
+ }));
217
+
218
+ // PATCH /api/claude-sessions/:id
219
+ router.patch('/claude-sessions/:id', wrap(async (req, res) => {
220
+ const sessionId = String(req.params?.id || '');
221
+ if (!SESSION_ID_RE.test(sessionId)) {
222
+ res.status(400).json({ error: 'bad_request', message: 'invalid session id' });
223
+ return;
224
+ }
225
+ const body = req.body && typeof req.body === 'object' ? req.body : {};
226
+ const title = typeof body.title === 'string' ? body.title.trim() : '';
227
+ if (!title) {
228
+ res.status(400).json({ error: 'bad_request', message: '`title` is required (non-empty)' });
229
+ return;
230
+ }
231
+ if (title.length > TITLE_MAX) {
232
+ res.status(400).json({ error: 'bad_request', message: `title too long (> ${TITLE_MAX} chars)` });
233
+ return;
234
+ }
235
+ if (!existsSync(join(CLAUDE_SESSIONS_DIR, sessionId))) {
236
+ res.status(404).json({ error: 'not_found', message: `session ${sessionId} not found` });
237
+ return;
238
+ }
239
+ writeMeta(sessionId, { title });
240
+ res.json({ id: sessionId, title });
241
+ }));
242
+
243
+ // DELETE /api/claude-sessions/:id
244
+ router.delete('/claude-sessions/:id', wrap(async (req, res) => {
245
+ const sessionId = String(req.params?.id || '');
246
+ if (!SESSION_ID_RE.test(sessionId)) {
247
+ res.status(400).json({ error: 'bad_request', message: 'invalid session id' });
248
+ return;
249
+ }
250
+ const r = deleteClaudeSession(sessionId);
251
+ if (!r.ok) {
252
+ res.status(502).json({ error: 'claude_error', message: r.error || 'delete failed' });
253
+ return;
254
+ }
255
+ res.json({ id: sessionId, deleted: true });
256
+ }));
257
+
258
+ return router;
259
+ }
@@ -10,7 +10,7 @@
10
10
  * /api/tasks/:id/start (POST) — trigger dispatch of a queued task
11
11
  * /api/tasks/:id/status (PATCH) — move status (queued|doing|done|blocked|archived)
12
12
  * /api/tasks/:id/comments (POST) — append comment
13
- * /api/tasks/:id/chat (GET) — cline messages for the task's bg instance
13
+ * /api/tasks/:id/chat (GET) — claude messages for the task's bg instance
14
14
  * /api/tasks/:id/artifacts (GET) — list artifacts for this task
15
15
  * /api/tasks/:id/artifacts (POST) — attach artifact + link to task
16
16
  * /api/tasks/:id/timer (POST) — toggle timer
@@ -34,10 +34,9 @@ import { notificationsStore } from '../notifications-store.mjs';
34
34
  import { projectsStore } from '../projects-store.mjs';
35
35
  import { artifactsStore } from '../artifacts-store.mjs';
36
36
  import {
37
- readServeInfo,
38
- listClineMessages,
39
- normalizeClineMessage,
40
- } from '../serve-info.mjs';
37
+ listClaudeMessages,
38
+ normalizeClaudeMessage,
39
+ } from '../claude-info.mjs';
41
40
  import { readActiveProjectId, wrap } from './_shared.mjs';
42
41
  import { ALLOWED_TASK_STATUSES } from '../tasks-store.mjs';
43
42
 
@@ -207,10 +206,15 @@ export function createTasksRouter({ state, broadcast, projectRoot }) {
207
206
  }));
208
207
 
209
208
  // v3.5.5 — Chat-task linkage. Given a task id, return the
210
- // cline session's messages so the chat UI can open a thread
211
- // for an in-progress or completed task. The task must have a
212
- // `bgInstanceId` (or `sessionId`) in its metadata — the delegator
213
- // writes that when the cline session is created.
209
+ // Claude Code session's messages so the chat UI can open a
210
+ // thread for an in-progress or completed task. The task must
211
+ // have a `bgInstanceId` (or `sessionId`) in its metadata — the
212
+ // delegator writes that when the Claude Code session is created.
213
+ //
214
+ // v6.3.0 — Reads from `~/.claude/sessions/<id>/messages.jsonl`
215
+ // via `claude-info.listClaudeMessages()` instead of the old
216
+ // cline serve HTTP endpoint. The 503 "plugin offline" path is
217
+ // gone — Claude Code is just a CLI on PATH.
214
218
  //
215
219
  // Response shape:
216
220
  // { taskId, bgInstanceId, sessionId, agent, messages: [{id,role,content,ts}, ...] }
@@ -218,8 +222,7 @@ export function createTasksRouter({ state, broadcast, projectRoot }) {
218
222
  // Status codes:
219
223
  // 200 — messages returned (may be empty)
220
224
  // 404 — task not found, or no bg instance / session id
221
- // 503 — plugin offline
222
- // 502 — cline listMessages call failed
225
+ // 502 — claude listMessages call failed
223
226
  router.get('/tasks/:id/chat', wrap(async (req, res) => {
224
227
  const projectId = req.query.projectId || readActiveProjectId();
225
228
  const task = await tasksStore.getById(projectId, req.params.id);
@@ -246,41 +249,30 @@ export function createTasksRouter({ state, broadcast, projectRoot }) {
246
249
  if (!bgInstanceId && !sessionId) {
247
250
  return res.status(404).json({
248
251
  error: 'no_bg_instance',
249
- message: 'task has no bg instance or cline session id',
250
- taskId: task.id,
251
- });
252
- }
253
-
254
- const serveInfo = readServeInfo();
255
- if (!serveInfo) {
256
- return res.status(503).json({
257
- error: 'plugin_offline',
258
- message: 'cline plugin is not running',
252
+ message: 'task has no bg instance or claude session id',
259
253
  taskId: task.id,
260
254
  });
261
255
  }
262
256
  if (!sessionId) {
263
257
  return res.status(404).json({
264
258
  error: 'no_session',
265
- message: 'bg instance has no cline session id',
259
+ message: 'bg instance has no claude session id',
266
260
  taskId: task.id,
267
261
  bgInstanceId,
268
262
  });
269
263
  }
270
264
 
271
- const active = projectsStore.active();
272
- const directory = (active && active.path) || serveInfo.worktree || '';
273
- const result = await listClineMessages(serveInfo, sessionId, directory);
265
+ const result = listClaudeMessages(sessionId);
274
266
  if (!result.ok) {
275
267
  return res.status(502).json({
276
- error: 'cline_error',
277
- message: result.error || 'listClineMessages failed',
268
+ error: 'claude_error',
269
+ message: result.error || 'listClaudeMessages failed',
278
270
  taskId: task.id,
279
271
  sessionId,
280
272
  });
281
273
  }
282
274
  const messages = Array.isArray(result.messages)
283
- ? result.messages.map(normalizeClineMessage)
275
+ ? result.messages.map(normalizeClaudeMessage)
284
276
  : [];
285
277
  res.json({
286
278
  taskId: task.id,