@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,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,