@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
@@ -1 +1 @@
1
- {"version":3,"mappings":"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","names":["VirtualList","items","renderItem","itemHeight","height","overscan","className","scrollTop","setScrollTop","useState","containerRef","useRef","visibleRange","useMemo","start","end","totalHeight","offsetY","visibleItems","jsx","e","item","i","ChatTopBar","activeProject","sessionCount","sessionsOpen","infoOpen","onToggleSessions","onToggleInfo","onOpenOverview","jsxs","Folder","ChevronDown","LoadingSkeleton","count","_","SuggestionCards","projectName","onPickSuggestion","suggestions","Code","BookOpen","Lightbulb","ImageIcon","s","WelcomeScreen","variant","displayName","MessageBubble","message","pinned","onCopy","onDelete","onTogglePin","onRegenerate","role","ts","formatTime","content","isUser","Copy","RefreshCw","Pin","Trash2","ReactMarkdown","remarkGfm","ChatThread","messages","loading","sessionId","activeSource","innerRef","useEffect","el","p","wrapped","stream","m","AgentChip","agent","setAgent","agents","open","setOpen","ref","handler","current","display","v","Bot","a","useAutoGrowTextarea","value","maxHeight","useLayoutEffect","next","Composer","model","setModel","text","setText","sending","onSend","attachments","setAttachments","onAttach","inputRef","fileInputRef","onKeyDown","base","onFiles","files","names","cur","_a","Paperclip","j","X","enhanced","enhancePrompt","Sparkles","Send","useChat","snapshot","settings","initialTaskId","bizarMessages","setBizarMessages","sessions","setSessions","setSessionId","setLoading","busy","setBusy","clineSessions","setClineSessions","clineMessages","setClineMessages","setActiveSource","activeClineSessionId","setActiveClineSessionId","clineError","setClineError","clineSuggestion","setClineSuggestion","clineErrorInfo","setClineErrorInfo","sessionStates","setSessionStates","stickToBottom","setStickToBottom","newMessageCount","setNewMessageCount","seenClineMessages","setSeenClineMessages","setPinned","listRef","toastRef","clineEsRef","sseReconnectAttemptRef","sseReconnectTimerRef","sseCurrentSessionIdRef","sseAutoReconnectRef","appendClineMessage","useCallback","msg","idx","markSeen","id","setSessionDisplayState","patch","prev","markSessionStreaming","streaming","setToast","toast","loadChat","sid","url","data","api","err","loadTaskChat","taskId","refreshSessions","refreshClineSessions","enriched","openSseForSession","tok","connect","es","onEnvelope","evt","sidForState","payload","_b","_c","_d","parseData","raw","parsed","props","sessionID","messageID","type","inner","innerData","namedTypes","name","nextAttempt","delayMs","closeClineSession","loadClineSession","info","ApiError","d","code","canRetry","selectBizarSession","onCreateSession","b","created","clineErr","_e","trimmed","optimisticId","optimistic","response","messageId","deleteMessage","togglePin","copyMessage","handleScroll","atBottom","jumpToLatest","renameSession","title","deleteSession","n","closed","__vitePreload","Ws","BUILTIN_COMMANDS","useSlashCommands","query","setQuery","allCommands","q","c"],"ignoreList":[],"sources":["../../src/web/components/VirtualList.tsx","../../src/web/components/chat/ChatTopBar.tsx","../../src/web/components/chat/LoadingSkeleton.tsx","../../src/web/components/chat/SuggestionCards.tsx","../../src/web/components/chat/WelcomeScreen.tsx","../../src/web/components/chat/MessageBubble.tsx","../../src/web/components/chat/ChatThread.tsx","../../src/web/components/chat/AgentChip.tsx","../../src/web/components/chat/useAutoGrowTextarea.ts","../../src/web/components/chat/Composer.tsx","../../src/web/components/chat/useChat.ts","../../src/web/components/chat/useSlashCommands.ts"],"sourcesContent":["// src/web/components/VirtualList.tsx — hand-rolled virtual scrolling (no external deps).\nimport React, { useState, useRef, useEffect, useMemo } from 'react';\n\nexport function VirtualList<T>({\n items,\n renderItem,\n itemHeight = 80,\n height = 400,\n overscan = 5,\n className,\n}: {\n items: T[];\n renderItem: (item: T, index: number) => React.ReactNode;\n itemHeight?: number;\n height?: number;\n overscan?: number;\n className?: string;\n}) {\n const [scrollTop, setScrollTop] = useState(0);\n const containerRef = useRef<HTMLDivElement>(null);\n\n const visibleRange = useMemo(() => {\n const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);\n const end = Math.min(\n items.length,\n Math.ceil((scrollTop + height) / itemHeight) + overscan,\n );\n return { start, end };\n }, [scrollTop, itemHeight, height, overscan, items.length]);\n\n const totalHeight = items.length * itemHeight;\n const offsetY = visibleRange.start * itemHeight;\n const visibleItems = items.slice(visibleRange.start, visibleRange.end);\n\n return (\n <div\n ref={containerRef}\n onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)}\n style={{ height, overflowY: 'auto' }}\n className={className}\n >\n <div style={{ height: totalHeight, position: 'relative' }}>\n <div style={{ transform: `translateY(${offsetY}px)` }}>\n {visibleItems.map((item, i) => (\n <div key={visibleRange.start + i} style={{ height: itemHeight }}>\n {renderItem(item, visibleRange.start + i)}\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","// src/components/chat/ChatTopBar.tsx — 48px topbar with project chip + panel toggles.\n\nimport { Folder, ChevronDown } from 'lucide-react';\n\ninterface Props {\n activeProject: { name: string; path?: string } | null;\n sessionCount: number;\n sessionsOpen: boolean;\n infoOpen: boolean;\n onToggleSessions: () => void;\n onToggleInfo: () => void;\n onOpenOverview: () => void;\n}\n\nexport function ChatTopBar({\n activeProject,\n sessionCount,\n sessionsOpen,\n infoOpen,\n onToggleSessions,\n onToggleInfo,\n onOpenOverview,\n}: Props) {\n return (\n <header className=\"chat-topbar\">\n <button type=\"button\" className=\"chat-topbar-project\" onClick={onOpenOverview}>\n <Folder size={14} />\n <span className=\"chat-topbar-project-name\">{activeProject?.name ?? 'No project'}</span>\n <ChevronDown size={12} />\n </button>\n <div className=\"chat-topbar-spacer\" />\n <button\n type=\"button\"\n className={`chat-topbar-btn ${sessionsOpen ? 'active' : ''}`}\n onClick={onToggleSessions}\n aria-pressed={sessionsOpen}\n >\n Sessions ({sessionCount})\n </button>\n <button\n type=\"button\"\n className={`chat-topbar-btn ${infoOpen ? 'active' : ''}`}\n onClick={onToggleInfo}\n aria-pressed={infoOpen}\n >\n Info\n </button>\n </header>\n );\n}","// src/components/chat/LoadingSkeleton.tsx — pulsing skeleton bubbles while loading.\n\ninterface Props {\n count?: number;\n}\n\nexport function LoadingSkeleton({ count = 3 }: Props) {\n return (\n <div className=\"chat-loading-skeleton\" aria-label=\"Loading messages\">\n {Array.from({ length: count }).map((_, i) => (\n <div key={i} className=\"chat-loading-skeleton-bubble\">\n <div className=\"chat-loading-skeleton-meta\" />\n <div className=\"chat-loading-skeleton-line\" />\n <div className=\"chat-loading-skeleton-line short\" />\n </div>\n ))}\n </div>\n );\n}","// src/components/chat/SuggestionCards.tsx — 4 hardcoded suggestion cards for first-run.\n\nimport { Code, BookOpen, Lightbulb, Image as ImageIcon } from 'lucide-react';\n\ninterface Props {\n projectName: string;\n onPickSuggestion: (text: string) => void;\n}\n\nexport function SuggestionCards({ projectName, onPickSuggestion }: Props) {\n const suggestions = [\n {\n icon: <Code size={16} />,\n title: projectName ? `Explain ${projectName}` : 'Explain this project',\n preview: 'Give me a tour of the codebase, highlight the architecture, and call out any anti-patterns or hotspots.',\n prompt: projectName\n ? `Explain the ${projectName} project. Give me a tour of the codebase, highlight the architecture, and call out any anti-patterns or hotspots.`\n : 'Explain the current project. Give me a tour of the codebase, highlight the architecture, and call out any anti-patterns or hotspots.',\n },\n {\n icon: <BookOpen size={16} />,\n title: 'Summarize recent sessions',\n preview: 'Read my last 5 chat sessions and give me a 3-bullet summary of what I worked on and what got stuck.',\n prompt: 'Read my last 5 chat sessions and give me a 3-bullet summary of what I worked on and what got stuck.',\n },\n {\n icon: <Lightbulb size={16} />,\n title: 'Suggest a refactor',\n preview: 'Look for the largest, most painful files in this project and propose a concrete refactor for the worst one.',\n prompt: 'Look for the largest, most painful files in this project and propose a concrete refactor for the worst one.',\n },\n {\n icon: <ImageIcon size={16} />,\n title: 'Find visual regressions',\n preview: 'Open the dashboard, screenshot the Chat tab, and flag any layout problems or anti-patterns you see.',\n prompt: 'Open the dashboard, screenshot the Chat tab, and flag any layout problems or anti-patterns you see.',\n },\n ];\n\n return (\n <div className=\"chat-suggestions\">\n {suggestions.map((s, i) => (\n <button\n key={i}\n type=\"button\"\n className=\"chat-suggestion\"\n onClick={() => onPickSuggestion(s.prompt)}\n >\n <div className=\"chat-suggestion-title\">{s.icon}<span>{s.title}</span></div>\n <div className=\"chat-suggestion-preview\">{s.preview}</div>\n </button>\n ))}\n </div>\n );\n}","// src/components/chat/WelcomeScreen.tsx — gradient greeting + no-project state.\n\nimport { Folder } from 'lucide-react';\nimport { SuggestionCards } from './SuggestionCards';\n\ninterface Props {\n variant: 'empty' | 'no-project';\n projectName: string;\n onPickSuggestion: (text: string) => void;\n}\n\nexport function WelcomeScreen({ variant, projectName, onPickSuggestion }: Props) {\n if (variant === 'no-project') {\n return (\n <div className=\"chat-welcome\">\n <h1 className=\"chat-welcome-h1\">\n <span className=\"chat-greeting-hello\">No project selected.</span>\n </h1>\n <p className=\"chat-welcome-subtitle\">Pick a project in Overview to scope chat sessions.</p>\n </div>\n );\n }\n\n const displayName = projectName || 'there';\n\n return (\n <div className=\"chat-welcome\">\n <h1 className=\"chat-welcome-h1\">\n <span className=\"chat-greeting-hello\">Hello, </span>\n <span className=\"chat-greeting-name\">{displayName}.</span>\n </h1>\n <p className=\"chat-welcome-subtitle\">How can I help you today?</p>\n <SuggestionCards projectName={projectName} onPickSuggestion={onPickSuggestion} />\n </div>\n );\n}","// src/components/chat/MessageBubble.tsx — user/assistant message with hover actions.\n\nimport ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport { Copy, RefreshCw, Pin, Trash2 } from 'lucide-react';\nimport { formatTime } from '../../lib/utils';\n\ninterface Message {\n role?: string;\n content?: string;\n message?: string;\n agent?: string;\n ts?: string | number;\n}\n\ninterface Props {\n message: Message;\n pinned: boolean;\n onCopy: () => void;\n onDelete: () => void;\n onTogglePin: () => void;\n onRegenerate: () => void;\n}\n\nexport function MessageBubble({ message, pinned, onCopy, onDelete, onTogglePin, onRegenerate }: Props) {\n const role = (message.role || 'assistant').toLowerCase();\n const ts = message.ts ? formatTime(message.ts) : '';\n const content = message.content || message.message || '';\n const isUser = role === 'user';\n\n return (\n <article className={`chat-message ${isUser ? 'chat-message-user' : 'chat-message-assistant'} ${pinned ? 'chat-message-pinned' : ''}`}>\n <header className=\"chat-message-meta\">\n <span className=\"chat-message-role\">{role}</span>\n {message.agent && <span className=\"chat-message-agent\">@{message.agent}</span>}\n {ts && <time className=\"chat-message-time\">{ts}</time>}\n {pinned && <span className=\"chat-message-pin\">pinned</span>}\n <div className=\"chat-message-actions\">\n <button type=\"button\" className=\"chat-msg-action-btn\" onClick={onCopy} title=\"Copy\" aria-label=\"Copy\">\n <Copy size={12} />\n </button>\n {!isUser && (\n <button type=\"button\" className=\"chat-msg-action-btn\" onClick={onRegenerate} title=\"Regenerate\" aria-label=\"Regenerate\">\n <RefreshCw size={12} />\n </button>\n )}\n <button type=\"button\" className=\"chat-msg-action-btn\" onClick={onTogglePin} title=\"Pin\" aria-label=\"Pin\">\n <Pin size={12} />\n </button>\n <button type=\"button\" className=\"chat-msg-action-btn chat-msg-action-btn-danger\" onClick={onDelete} title=\"Delete\" aria-label=\"Delete\">\n <Trash2 size={12} />\n </button>\n </div>\n </header>\n <div className=\"chat-message-content markdown-body\">\n <ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>\n </div>\n </article>\n );\n}","// src/components/chat/ChatThread.tsx — message list, welcome screen, loading skeleton.\n//\n// v4.2.5 — accepts `activeSource` so the loading skeleton can hint\n// which stream is loading and the welcome variant for \"cline\"\n// can differ from the local chat welcome.\n\nimport { useEffect, useRef } from 'react';\nimport { LoadingSkeleton } from './LoadingSkeleton';\nimport { WelcomeScreen } from './WelcomeScreen';\nimport { MessageBubble } from './MessageBubble';\nimport { VirtualList } from '../VirtualList';\n\ninterface Message {\n role?: string;\n content?: string;\n message?: string;\n agent?: string;\n ts?: string | number;\n}\n\ninterface Props {\n messages: Message[];\n loading: boolean;\n activeProject: { name: string } | null;\n sessionId: string;\n pinned: Set<number>;\n /** Which stream currently owns the thread — purely cosmetic, lets\n * the empty/loading states be slightly more specific. The type\n * uses `unknown` so we don't pull in a tight coupling; the value\n * is reflected back into the loading skeleton and welcome screen\n * via data attributes so CSS can style accordingly. */\n activeSource?: 'bizar' | 'cline' | null;\n onPickSuggestion: (text: string) => void;\n onCopy: (m: Message) => void;\n onDelete: (idx: number) => void;\n onTogglePin: (idx: number) => void;\n onRegenerate: (messageId: string) => void;\n}\n\nexport function ChatThread({\n messages,\n loading,\n activeProject,\n sessionId,\n pinned,\n activeSource,\n onPickSuggestion,\n onCopy,\n onDelete,\n onTogglePin,\n onRegenerate,\n}: Props) {\n const innerRef = useRef<HTMLDivElement>(null);\n\n // Only auto-scroll when we own the scroll container (i.e. when\n // rendered directly as the message scroller). When embedded\n // inside Chat.tsx's chat-thread-scroll, the parent handles scroll.\n useEffect(() => {\n const el = innerRef.current;\n if (!el) return;\n let p: HTMLElement | null = el.parentElement;\n let wrapped = false;\n while (p) {\n if (p.classList.contains('chat-thread-scroll')) {\n wrapped = true;\n break;\n }\n p = p.parentElement;\n }\n if (!wrapped) el.scrollTop = el.scrollHeight;\n }, [messages]);\n\n const stream: 'bizar' | 'cline' = activeSource === 'cline' ? 'cline' : 'bizar';\n\n if (loading) {\n return (\n <div className=\"chat-thread legacy\" ref={innerRef} data-stream={stream}>\n <LoadingSkeleton count={3} />\n </div>\n );\n }\n\n if (!activeProject) {\n return (\n <div className=\"chat-thread legacy\" ref={innerRef} data-stream={stream}>\n <WelcomeScreen\n variant=\"no-project\"\n projectName=\"\"\n onPickSuggestion={onPickSuggestion}\n />\n </div>\n );\n }\n\n if (messages.length === 0) {\n return (\n <div className=\"chat-thread legacy\" ref={innerRef} data-stream={stream}>\n <WelcomeScreen\n variant=\"empty\"\n projectName={activeProject.name}\n onPickSuggestion={onPickSuggestion}\n />\n </div>\n );\n }\n\n return (\n <div className=\"chat-thread legacy\" ref={innerRef}>\n <VirtualList\n items={messages}\n itemHeight={120}\n height={800}\n className=\"chat-thread-virtual\"\n renderItem={(m, i) => (\n <MessageBubble\n message={m}\n pinned={pinned.has(i)}\n onCopy={() => onCopy(m)}\n onDelete={() => onDelete(i)}\n onTogglePin={() => onTogglePin(i)}\n onRegenerate={() => onRegenerate(String(m.ts ?? i))}\n />\n )}\n />\n </div>\n );\n}\n","// src/components/chat/AgentChip.tsx — custom popover agent picker, replaces <select>.\n\nimport { useEffect, useRef, useState } from 'react';\nimport { Bot, ChevronDown } from 'lucide-react';\n\ninterface Props {\n agent: string;\n setAgent: (a: string) => void;\n agents: Array<{ name: string }>;\n}\n\nexport function AgentChip({ agent, setAgent, agents }: Props) {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', handler);\n return () => document.removeEventListener('mousedown', handler);\n }, [open]);\n\n const current = agent || 'no agent';\n const display = current.startsWith('@') ? current.slice(1) : current;\n\n return (\n <div className=\"chat-agent-chip\" ref={ref}>\n <button\n type=\"button\"\n className=\"chat-agent-chip-btn\"\n onClick={() => setOpen((v) => !v)}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n >\n <Bot size={14} />\n <span>{display}</span>\n <ChevronDown size={12} className={open ? 'rotated' : ''} />\n </button>\n {open && (\n <div className=\"chat-agent-chip-menu\" role=\"listbox\">\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={!agent}\n onClick={() => { setAgent(''); setOpen(false); }}\n >\n <span>no agent</span>\n </button>\n {agents.map((a) => (\n <button\n key={a.name}\n type=\"button\"\n role=\"option\"\n aria-selected={agent === a.name}\n onClick={() => { setAgent(a.name); setOpen(false); }}\n >\n <span>@{a.name}</span>\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}","// src/components/chat/useAutoGrowTextarea.ts — auto-resize textarea up to maxHeight.\n\nimport { useLayoutEffect } from 'react';\n\nexport function useAutoGrowTextarea(\n ref: React.RefObject<HTMLTextAreaElement | null>,\n value: string,\n maxHeight = 240,\n): void {\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n el.style.height = 'auto';\n const next = Math.min(el.scrollHeight, maxHeight);\n el.style.height = `${next}px`;\n el.style.overflowY = el.scrollHeight > maxHeight ? 'auto' : 'hidden';\n }, [ref, value, maxHeight]);\n}","// src/components/chat/Composer.tsx — textarea + toolbar, used inside FloatingComposer.\n\nimport { useRef } from 'react';\nimport { Send, Paperclip, X, Sparkles } from 'lucide-react';\nimport { enhancePrompt } from '../../lib/api';\nimport { AgentChip } from './AgentChip';\nimport { useAutoGrowTextarea } from './useAutoGrowTextarea';\n\ninterface Props {\n agent: string;\n setAgent: (a: string) => void;\n model: string;\n setModel: (m: string) => void;\n text: string;\n setText: (t: string) => void;\n sending: boolean;\n onSend: () => void;\n attachments: string[];\n setAttachments: (a: string[] | ((prev: string[]) => string[])) => void;\n suggestions: Array<{ cmd: string; desc: string }>;\n onPickSuggestion: (cmd: string) => void;\n agents: Array<{ name: string }>;\n onAttach: () => void;\n}\n\nexport function Composer({\n agent,\n setAgent,\n model,\n setModel,\n text,\n setText,\n sending,\n onSend,\n attachments,\n setAttachments,\n suggestions,\n onPickSuggestion,\n agents,\n onAttach,\n}: Props) {\n const inputRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n useAutoGrowTextarea(inputRef, text, 160);\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n onSend();\n } else if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n onSend();\n } else if (e.key === 'Tab' && suggestions.length) {\n e.preventDefault();\n const base = suggestions[0].cmd.split(' ')[0];\n setText(`${base} `);\n }\n };\n\n const onFiles = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files;\n if (!files) return;\n const names: string[] = [];\n for (let i = 0; i < files.length; i++) names.push(files[i].name);\n setAttachments((cur) => [...cur, ...names]);\n e.target.value = '';\n };\n\n return (\n <div className=\"chat-composer\">\n {suggestions.length > 0 && (\n <div className=\"chat-composer-suggestions\">\n {suggestions.map((s) => (\n <button\n key={s.cmd}\n type=\"button\"\n className=\"chat-composer-suggestion\"\n onClick={() => {\n const base = s.cmd.split(' ')[0];\n setText(`${base} `);\n inputRef.current?.focus();\n }}\n >\n <span className=\"mono\">{s.cmd}</span>\n <span>{s.desc}</span>\n </button>\n ))}\n </div>\n )}\n\n {attachments.length > 0 && (\n <div className=\"chat-composer-attachments\">\n {attachments.map((a, i) => (\n <span key={i} className=\"chat-composer-attachment-tag\">\n <Paperclip size={10} /> {a}\n <button\n type=\"button\"\n className=\"chat-composer-attachment-remove\"\n onClick={() => setAttachments((cur) => cur.filter((_, j) => j !== i))}\n aria-label={`Remove ${a}`}\n >\n <X size={10} />\n </button>\n </span>\n ))}\n </div>\n )}\n\n <div className=\"chat-composer-input\">\n <AgentChip agent={agent} setAgent={setAgent} agents={agents} />\n <textarea\n ref={inputRef}\n className=\"chat-composer-textarea\"\n placeholder={sending ? 'Sending…' : 'Send a message…'}\n rows={1}\n value={text}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={onKeyDown}\n disabled={sending}\n aria-label=\"Message\"\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n style={{ display: 'none' }}\n onChange={onFiles}\n aria-label=\"Attach files\"\n tabIndex={-1}\n />\n <button\n type=\"button\"\n className=\"chat-attach-btn\"\n onClick={onAttach}\n title=\"Attach files\"\n aria-label=\"Attach files\"\n >\n <Paperclip size={14} />\n </button>\n <button\n type=\"button\"\n className=\"chat-attach-btn\"\n onClick={async () => {\n if (!text.trim()) return;\n const enhanced = await enhancePrompt(text);\n if (enhanced !== text) setText(enhanced);\n }}\n title=\"Enhance prompt with AI\"\n aria-label=\"Enhance prompt\"\n >\n <Sparkles size={14} />\n </button>\n <button\n type=\"button\"\n className=\"chat-send-btn\"\n onClick={onSend}\n disabled={sending || !text.trim()}\n title=\"Send (Enter)\"\n aria-label=\"Send message\"\n >\n <Send size={16} />\n </button>\n </div>\n </div>\n );\n}","// src/components/chat/useChat.ts — shared chat state: messages, sessions,\n// send, polling, SSE, session-state tracking.\n//\n// v4.2.5 — Chat overhaul. Supports two parallel message streams:\n// - bizar (GET /api/chat)\n// - cline (GET /api/cline-sessions/:id/messages + SSE)\n//\n// v5.0.0 — bug #4 fix: `loadClineSession` failure path now extracts\n// structured `code`, `cause`, `status`, and `suggestion` from the\n// server's ApiError.data envelope and exposes them via a new\n// `clineErrorInfo` field. `ChatInfoPanel` consumes that field to\n// render a structured \"Couldn't load session\" panel with the server's\n// `suggestion` verbatim, plus a Retry button.\n//\n// Changes from v3.22:\n// * SSE has automatic reconnect-with-backoff (1s → 30s, doubled on\n// each error up to a cap). Closes cleanly on unmount and on\n// session-switch (no leaked EventSource).\n// * Optimistic send uses a stable `msg_*` ID and is deduped when\n// cline echoes the message back via SSE `message.updated`/`message.user.*`.\n// * `clineMessages` keeps server-provided IDs distinct from\n// optimistic IDs so the merge is unambiguous (server echo\n// replaces optimistic by matching messageID; otherwise both stick\n// around briefly until the user/session state catches up).\n// * `onSend` routes to `/cline-sessions/:id/send` when an\n// cline session is active, `/chat` otherwise. Errors surface\n// via toast + we always remove the optimistic placeholder.\n// * Sessions are managed by source:\n// - `bizarSessions` → local .jsonl store, POST /chat/sessions\n// - `clineSessions` → cline serve, GET /cline-sessions\n// The rail shows both side-by-side with a source indicator and\n// uses separate rename/delete endpoints per source.\n// * All async actions surface a single `busy: { create, send, rename, delete }`\n// so the rail can disable buttons properly.\n// * No `any` leaks — all SSE event shapes are typed via\n// `ClineSseEnvelope`.\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type {\n ChatMessage,\n ChatResponse,\n ChatSession,\n Settings,\n Snapshot,\n} from '../../lib/types';\nimport { api, ApiError } from '../../lib/api';\nimport type { SessionState } from './ChatRail';\nimport type { AgentTreeNode } from './AgentNode';\n\nexport interface SessionDisplayState {\n /** idle / streaming / awaiting — derived from the latest SSE event. */\n state: SessionState;\n /** Number of unread (i.e. arrived while the user wasn't viewing) messages. */\n unread: number;\n /** Pinned? (cosmetic for now; pinned state lives in the parent.) */\n pinned: boolean;\n /** Sub-agent tree, if any. */\n tree?: { root: AgentTreeNode };\n}\n\n/** Per-source action busy flags. Used by rail/info-panel to disable buttons. */\nexport interface ChatBusyState {\n create: boolean;\n send: boolean;\n rename: boolean;\n delete: boolean;\n}\n\n/**\n * Structured error envelope surfaced by `loadClineSession` when\n * the upstream `/api/cline-sessions/:id/messages` call fails.\n *\n * - `code` mirrors the server's `error` field (`plugin_offline`,\n * `directory_unknown`, `cline_error`, `bad_request`).\n * - `cause` is the server's structured cause (`network`, `timeout`,\n * `unknown`, or a raw ECONNREFUSED-style string).\n * - `suggestion` is the operator-friendly hint from the server\n * (e.g. \"Run `bizar doctor` for diagnostics\").\n * - `canRetry` is `true` whenever the failure is transient (network\n * error, upstream 502) — i.e. the user can fix it by trying again\n * after starting the plugin / waiting for cline to come back.\n * False for hard failures (bad_request).\n */\nexport interface ClineErrorInfo {\n type: 'session_load_failed';\n code: string;\n message: string;\n cause?: string;\n status?: number;\n suggestion?: string | null;\n canRetry: boolean;\n}\n\n/** Shape of one cline SSE envelope after unwrapping. */\ninterface ClineSseEnvelope {\n type: string;\n sessionID?: string;\n messageID?: string;\n /** Raw upstream payload (kept for forwarders that want full fidelity). */\n data?: unknown;\n}\n\n/**\n * The chat hook returns a stable bag of state + actions used by\n * both Chat.tsx (desktop) and MobileChat.tsx (mobile).\n *\n * The hook owns the lifecycle of the cline SSE stream. Callers\n * should never instantiate their own EventSource for the same\n * session — they should always go through this hook.\n */\nexport function useChat(snapshot: Snapshot, settings: Settings, initialTaskId?: string | null) {\n // ── Bizar stream ────────────────────────────────────────────────────────────\n const [bizarMessages, setBizarMessages] = useState<ChatMessage[]>([]);\n const [sessions, setSessions] = useState<ChatSession[]>([]);\n const [sessionId, setSessionId] = useState('');\n const [loading, setLoading] = useState(true);\n const [busy, setBusy] = useState<ChatBusyState>({\n create: false,\n send: false,\n rename: false,\n delete: false,\n });\n\n // ── Cline stream ─────────────────────────────────────────────────────────\n const [clineSessions, setClineSessions] = useState<ChatSession[]>([]);\n const [clineMessages, setClineMessages] = useState<ChatMessage[]>([]);\n const [activeSource, setActiveSource] = useState<'bizar' | 'cline' | null>(null);\n const [activeClineSessionId, setActiveClineSessionId] = useState<string | null>(null);\n const [clineError, setClineError] = useState<string | null>(null);\n const [clineSuggestion, setClineSuggestion] = useState<string | null>(null);\n /**\n * Structured error info for the cline session load path. New in\n * v5.0.0 — `ChatInfoPanel` consumes this to render its own error\n * section (in addition to the chat-thread-header error already\n * rendered by Chat.tsx). `null` when no error.\n */\n const [clineErrorInfo, setClineErrorInfo] = useState<ClineErrorInfo | null>(null);\n\n // ── v3.22 — session-state, unread, tree per session ────────────────────────\n const [sessionStates, setSessionStates] = useState<Record<string, SessionDisplayState>>({});\n const [stickToBottom, setStickToBottom] = useState(true);\n const [newMessageCount, setNewMessageCount] = useState(0);\n\n // Set of messageIDs the user has already accepted (or that arrived\n // via SSE) — used by the cline path to dedupe optimistic + echo.\n const [seenClineMessages, setSeenClineMessages] = useState<Set<string>>(new Set());\n\n // Backwards-compat alias (chat.sendSession delete etc. used `pinned` historically).\n const [pinned, setPinned] = useState<Set<number>>(new Set());\n\n const listRef = useRef<HTMLDivElement>(null);\n const toastRef = useRef<\n | {\n error: (msg: string) => void;\n success: (msg: string) => void;\n info: (msg: string) => void;\n warning: (msg: string) => void;\n }\n | null\n >(null);\n\n // ── SSE lifecycle refs ──────────────────────────────────────────────────────\n const clineEsRef = useRef<EventSource | null>(null);\n const sseReconnectAttemptRef = useRef(0);\n const sseReconnectTimerRef = useRef<number | null>(null);\n // The id of the session we're CURRENTLY streaming. When the user\n // switches sessions, this id changes and the SSE handler short-\n // circuits any in-flight appends.\n const sseCurrentSessionIdRef = useRef<string | null>(null);\n // Whether the SSE connection should be auto-reconnected on error.\n // Set to false on explicit disconnect (session switch / unmount).\n const sseAutoReconnectRef = useRef(false);\n\n /** Append `msg` to `clineMessages`, replacing any prior\n * optimistic entry whose content matches or whose ID matches. */\n const appendClineMessage = useCallback((msg: ChatMessage) => {\n if (!msg) return;\n setClineMessages((cur) => {\n // Existing exact-ID match: skip.\n if (msg.id && cur.some((m) => m.id === msg.id)) return cur;\n // Optimistic placeholder (starts with msg_) with the same role\n // and content: replace it. This is how cline's echo of our\n // own message gets deduplicated against the optimistic copy.\n const idx = cur.findIndex(\n (m) =>\n typeof m.id === 'string' &&\n m.id.startsWith('msg_') &&\n m.role === msg.role &&\n (m.content || '') === (msg.content || ''),\n );\n if (idx >= 0) {\n const next = cur.slice();\n next[idx] = { ...msg, id: msg.id ?? cur[idx].id };\n return next;\n }\n return [...cur, msg];\n });\n }, []);\n\n /** Mark a messageID as seen — used to dedupe echo rounds. */\n const markSeen = useCallback((id: string | undefined) => {\n if (!id) return;\n setSeenClineMessages((cur) => {\n if (cur.has(id)) return cur;\n const next = new Set(cur);\n next.add(id);\n return next;\n });\n }, []);\n\n // ── Helpers to mutate per-session state ────────────────────────────────────\n const setSessionDisplayState = useCallback(\n (id: string, patch: Partial<SessionDisplayState>) => {\n setSessionStates((cur) => {\n const prev: SessionDisplayState =\n cur[id] ?? { state: 'idle', unread: 0, pinned: false };\n return { ...cur, [id]: { ...prev, ...patch } };\n });\n },\n [],\n );\n\n const markSessionStreaming = useCallback(\n (id: string, streaming: boolean) => {\n setSessionDisplayState(id, { state: streaming ? 'streaming' : 'idle' });\n },\n [setSessionDisplayState],\n );\n\n /** ─── Toast binding (set by the consuming view via `setToast`) ──── */\n const setToast = useCallback(\n (toast: typeof toastRef.current) => {\n toastRef.current = toast;\n },\n [],\n );\n\n // ── Load bizar chat ─────────────────────────────────────────────────────────\n const loadChat = useCallback(\n async (sid?: string) => {\n try {\n const url = sid\n ? `/chat?session=${encodeURIComponent(sid)}`\n : '/chat?limit=200';\n const data = await api.get<ChatResponse>(url);\n setBizarMessages(data.messages || []);\n setSessions(data.sessions || []);\n } catch (err) {\n toastRef.current?.error(\n `Chat load failed: ${(err as Error).message}`,\n );\n } finally {\n setLoading(false);\n }\n },\n [],\n );\n\n const loadTaskChat = useCallback(async (taskId: string) => {\n setLoading(true);\n try {\n const data = await api.get<{\n sessionId?: string;\n messages: ChatMessage[];\n }>(`/tasks/${encodeURIComponent(taskId)}/chat`);\n setBizarMessages(data.messages || []);\n setSessionId(data.sessionId || taskId);\n } catch (err) {\n toastRef.current?.error(\n `Task chat load failed: ${(err as Error).message}`,\n );\n } finally {\n setLoading(false);\n }\n }, []);\n\n const refreshSessions = useCallback(async () => {\n try {\n const data = await api.get<{ sessions: ChatSession[] }>('/chat/sessions');\n setSessions(data.sessions || []);\n } catch {\n /* best-effort */\n }\n }, []);\n\n const refreshClineSessions = useCallback(async () => {\n try {\n const data = await api.get<{ sessions: ChatSession[] }>(\n '/cline-sessions',\n );\n // Enrich with source marker.\n const enriched: ChatSession[] = (data.sessions || []).map((s) => ({\n ...s,\n source: 'cline' as const,\n title: s.title || s.id,\n }));\n setClineSessions(enriched);\n } catch {\n /* best-effort */\n }\n }, []);\n\n // ── Cline SSE connection ─────────────────────────────────────────────────\n //\n // Single source of truth for the EventSource lifecycle. Callers:\n // - `loadClineSession(id)` — close any current, open a new one.\n // - `closeClineSession()` — close and stop auto-reconnect.\n // - unmount — close and stop.\n //\n // On error / network drop, the EventSource triggers `onerror`. We\n // close the connection and schedule a reconnect (with exponential\n // backoff: 1s, 2s, 4s, …, capped at 30s) UNLESS `sseAutoReconnectRef`\n // is false (explicit disconnect).\n //\n const openSseForSession = useCallback(\n (id: string) => {\n // Close any existing connection first.\n if (clineEsRef.current) {\n try {\n clineEsRef.current.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n }\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n sseReconnectTimerRef.current = null;\n }\n\n // Build the SSE URL. EventSource can't set headers, so we\n // pass the auth token as ?token= when set (loopback makes this\n // optional, but harmless).\n const tok = api.getToken();\n const url = tok\n ? `/api/cline-sessions/${encodeURIComponent(id)}/stream?token=${encodeURIComponent(tok)}`\n : `/api/cline-sessions/${encodeURIComponent(id)}/stream`;\n\n sseCurrentSessionIdRef.current = id;\n sseAutoReconnectRef.current = true;\n sseReconnectAttemptRef.current = 0;\n setClineError(null);\n setClineSuggestion(null);\n setClineErrorInfo(null);\n\n const connect = () => {\n if (!sseAutoReconnectRef.current) return;\n if (sseCurrentSessionIdRef.current !== id) return;\n const es = new EventSource(url);\n clineEsRef.current = es;\n\n // Helper: dispatch one parsed envelope to local state.\n const onEnvelope = (evt: ClineSseEnvelope | null) => {\n if (!evt || !evt.type) return;\n // Drop events for OTHER sessions even though the proxy\n // already filters — defense in depth.\n if (evt.sessionID && evt.sessionID !== id) return;\n\n const ts = new Date().toISOString();\n const sidForState = id;\n\n if (\n evt.type === 'message.user.created' ||\n evt.type === 'message.user.updated'\n ) {\n const payload = evt.data as\n | { messageID?: string; data?: { content?: string } }\n | undefined;\n appendClineMessage({\n id: payload?.messageID ?? evt.messageID,\n role: 'user',\n content: payload?.data?.content ?? '',\n ts,\n });\n markSeen(payload?.messageID ?? evt.messageID);\n return;\n }\n if (\n evt.type === 'message.assistant.created' ||\n evt.type === 'message.assistant.updated'\n ) {\n const payload = evt.data as\n | { messageID?: string; data?: { content?: string } }\n | undefined;\n appendClineMessage({\n id: payload?.messageID ?? evt.messageID,\n role: 'assistant',\n content: payload?.data?.content ?? '',\n ts,\n });\n markSeen(payload?.messageID ?? evt.messageID);\n markSessionStreaming(sidForState, true);\n return;\n }\n if (evt.type === 'message.part.updated') {\n const payload = evt.data as\n | {\n part?: { type?: string; text?: string };\n messageID?: string;\n id?: string;\n }\n | undefined;\n const text = payload?.part?.text ?? '';\n if (!text) return;\n appendClineMessage({\n id: payload?.id ?? payload?.messageID ?? evt.messageID,\n role: 'assistant',\n content: text,\n ts,\n });\n markSeen(payload?.id ?? payload?.messageID ?? evt.messageID);\n markSessionStreaming(sidForState, true);\n return;\n }\n if (\n evt.type === 'session.idle' ||\n evt.type === 'message.assistant.completed' ||\n evt.type === 'session.completed'\n ) {\n markSessionStreaming(sidForState, false);\n return;\n }\n if (\n evt.type === 'session.awaiting' ||\n evt.type === 'session.awaiting_input' ||\n evt.type === 'message.assistant.awaiting'\n ) {\n setSessionDisplayState(sidForState, { state: 'awaiting' });\n return;\n }\n if (evt.type === 'session.error') {\n setClineError(\n (evt.data as { message?: string } | undefined)?.message ||\n 'cline session error',\n );\n return;\n }\n // Other event types are intentionally ignored — the proxy\n // is canonical, we don't need to project every upstream\n // event into local state.\n };\n\n // Stable parser — the server may send both `{type: \"x\"}` and\n // `sync` envelopes with a `.<n>` version suffix. We accept both.\n const parseData = (raw: string | null | undefined):\n | ClineSseEnvelope\n | null => {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n // Direct shape: {type, properties: {...}}\n if (typeof parsed.type === 'string') {\n const props = parsed.properties as\n | Record<string, unknown>\n | undefined;\n const data = (parsed.data as Record<string, unknown> | undefined) ?? props;\n const sessionID = (\n typeof parsed.sessionID === 'string'\n ? parsed.sessionID\n : typeof props?.sessionID === 'string'\n ? (props.sessionID as string)\n : typeof data?.sessionID === 'string'\n ? (data.sessionID as string)\n : undefined\n );\n const messageID = (\n typeof parsed.messageID === 'string'\n ? parsed.messageID\n : typeof props?.messageID === 'string'\n ? (props.messageID as string)\n : typeof data?.messageID === 'string'\n ? (data.messageID as string)\n : undefined\n );\n let type = parsed.type as string;\n const m = /\\.\\d+$/.exec(type);\n if (m) type = type.slice(0, m.index);\n return { type, sessionID, messageID, data: parsed };\n }\n // Sync envelope: {type: \"sync\", syncEvent: {...}}\n if (\n parsed.type === 'sync' &&\n parsed.syncEvent &&\n typeof parsed.syncEvent === 'object'\n ) {\n const inner = parsed.syncEvent as Record<string, unknown>;\n let type = (inner.type as string) || '';\n const m = /\\.\\d+$/.exec(type);\n if (m) type = type.slice(0, m.index);\n const innerData = (inner.data as Record<string, unknown>) || {};\n return {\n type,\n sessionID:\n typeof innerData.sessionID === 'string'\n ? innerData.sessionID\n : undefined,\n messageID:\n typeof innerData.messageID === 'string'\n ? innerData.messageID\n : undefined,\n data: inner,\n };\n }\n return null;\n } catch {\n return null;\n }\n };\n\n // register handlers per named event type\n const namedTypes = [\n 'message.user.created',\n 'message.user.updated',\n 'message.assistant.created',\n 'message.assistant.updated',\n 'message.part.updated',\n 'session.idle',\n 'message.assistant.completed',\n 'session.completed',\n 'session.awaiting',\n 'session.awaiting_input',\n 'message.assistant.awaiting',\n 'session.error',\n ];\n for (const name of namedTypes) {\n es.addEventListener(name, (e: MessageEvent) => {\n onEnvelope(parseData((e as MessageEvent).data));\n });\n }\n\n es.onerror = () => {\n if (sseCurrentSessionIdRef.current !== id) return;\n if (!sseAutoReconnectRef.current) {\n try {\n es.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n return;\n }\n try {\n es.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n\n const nextAttempt = sseReconnectAttemptRef.current + 1;\n sseReconnectAttemptRef.current = nextAttempt;\n // Backoff: 1s, 2s, 4s, 8s, … capped at 30s.\n const delayMs = Math.min(30_000, 1000 * 2 ** (nextAttempt - 1));\n\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n }\n sseReconnectTimerRef.current = window.setTimeout(() => {\n sseReconnectTimerRef.current = null;\n if (\n sseAutoReconnectRef.current &&\n sseCurrentSessionIdRef.current === id\n ) {\n connect();\n }\n }, delayMs);\n };\n\n es.onopen = () => {\n // Successful (re)connect — reset backoff.\n sseReconnectAttemptRef.current = 0;\n setClineError(null);\n setClineSuggestion(null);\n setClineErrorInfo(null);\n };\n };\n\n connect();\n },\n [appendClineMessage, markSeen, markSessionStreaming, setSessionDisplayState],\n );\n\n const closeClineSession = useCallback(() => {\n // Disable auto-reconnect first so the onerror handler short-circuits.\n sseAutoReconnectRef.current = false;\n sseCurrentSessionIdRef.current = null;\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n sseReconnectTimerRef.current = null;\n }\n if (clineEsRef.current) {\n try {\n clineEsRef.current.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n }\n setClineMessages([]);\n setSeenClineMessages(new Set());\n setClineError(null);\n setClineSuggestion(null);\n setClineErrorInfo(null);\n setActiveClineSessionId(null);\n setActiveSource('bizar');\n }, []);\n\n const loadClineSession = useCallback(\n async (id: string) => {\n // Close any previous cline session cleanly.\n sseAutoReconnectRef.current = false;\n if (clineEsRef.current) {\n try {\n clineEsRef.current.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n }\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n sseReconnectTimerRef.current = null;\n }\n\n setClineError(null);\n setClineSuggestion(null);\n setClineErrorInfo(null);\n setLoading(true);\n\n try {\n const data = await api.get<{ messages: ChatMessage[] }>(\n `/cline-sessions/${encodeURIComponent(id)}/messages`,\n );\n setClineMessages(data.messages || []);\n } catch (err) {\n const msg = (err as Error).message;\n // v5.0.0 — bug #4: surface the server's structured error\n // envelope so the UI can render a useful suggestion instead\n // of the raw upstream error string. The server returns\n // `{error, message, cause?, status?, suggestion?}` for every\n // non-2xx response (see serve-info.mjs + routes/cline-\n // session-detail.mjs).\n let info: ClineErrorInfo | null = null;\n if (err instanceof ApiError && err.data && typeof err.data === 'object') {\n const d = err.data as {\n error?: string;\n message?: string;\n cause?: string;\n status?: number;\n suggestion?: string;\n };\n // canRetry: 503/502 are transient (plugin offline /\n // upstream hiccup); the operator can fix the plugin and\n // try again. 400 is a hard bad_request — retrying with the\n // same payload won't help.\n const code = d.error || 'unknown';\n const canRetry =\n code !== 'bad_request' &&\n code !== 'too_many_subscribers' &&\n code !== 'unauthorized';\n info = {\n type: 'session_load_failed',\n code,\n message: d.message || msg,\n cause: d.cause,\n status: d.status,\n suggestion: d.suggestion ?? null,\n canRetry,\n };\n } else if (err instanceof ApiError) {\n info = {\n type: 'session_load_failed',\n code: 'unknown',\n message: msg,\n suggestion: null,\n canRetry: true,\n };\n }\n // Legacy fields — kept so the existing Chat.tsx thread-header\n // error display continues to work unchanged.\n setClineError(\n info\n ? `Cline session load failed (${info.code}): ${info.message}`\n : `Cline session load failed: ${msg}`,\n );\n setClineSuggestion(info?.suggestion ?? null);\n setClineErrorInfo(info);\n toastRef.current?.error(\n info\n ? `Cline session load failed: ${info.message}`\n : `Cline session load failed: ${msg}`,\n );\n setClineMessages([]);\n } finally {\n setLoading(false);\n }\n\n setActiveClineSessionId(id);\n setActiveSource('cline');\n markSessionStreaming(id, false);\n openSseForSession(id);\n },\n [markSessionStreaming, openSseForSession],\n );\n\n // ── Select a bizar session ──────────────────────────────────────────────────\n const selectBizarSession = useCallback(\n (sid: string) => {\n closeClineSession();\n setSessionId(sid);\n setActiveSource('bizar');\n loadChat(sid);\n // Reset unread for the session being opened.\n setSessionDisplayState(sid, { unread: 0 });\n },\n [closeClineSession, loadChat, setSessionDisplayState],\n );\n\n // ── Create session (cline primary; bizar fallback) ──────────────────────\n const onCreateSession = useCallback(async (): Promise<{\n ok: boolean;\n source?: 'bizar' | 'cline';\n id?: string;\n }> => {\n if (busy.create) return { ok: false };\n const agent = settings.defaultAgent || 'odin';\n setBusy((b) => ({ ...b, create: true }));\n try {\n // Try to create a real cline session first. This is the\n // primary create path: it gives the user a full conversation\n // roundtrip with SSE streaming, abort, etc.\n try {\n const created = await api.post<{\n id: string;\n title: string;\n agent: string;\n directory: string;\n createdAt: number;\n }>('/cline-sessions/new', { agent });\n await refreshClineSessions();\n // Open the newly created session right away so the user is\n // dropped into a fresh empty thread, not stuck on the\n // previous one.\n await loadClineSession(created.id);\n toastRef.current?.success(`Session ${created.id} created.`);\n return { ok: true, source: 'cline', id: created.id };\n } catch (clineErr) {\n // Cline plugin offline (503) — fall back to the local\n // per-project .jsonl session. Otherwise, surface the error.\n if (!(clineErr instanceof ApiError) || clineErr.status !== 503) {\n toastRef.current?.error(\n `Create failed: ${(clineErr as Error).message}`,\n );\n return { ok: false };\n }\n }\n\n // Fallback: local jsonl session.\n if (!snapshot.activeProject) {\n toastRef.current?.error(\n 'Pick a project in Overview to scope chat sessions.',\n );\n return { ok: false };\n }\n const created = await api.post<ChatSession>('/chat/sessions', {});\n setSessionId(created.id);\n setBizarMessages([]);\n setPinned(new Set());\n setSessionDisplayState(created.id, { state: 'idle', unread: 0 });\n await loadChat(created.id);\n refreshSessions().catch(() => {});\n toastRef.current?.success(`Session ${created.id} created.`);\n return { ok: true, source: 'bizar', id: created.id };\n } catch (err) {\n toastRef.current?.error(`Create failed: ${(err as Error).message}`);\n return { ok: false };\n } finally {\n setBusy((b) => ({ ...b, create: false }));\n }\n }, [\n busy.create,\n settings.defaultAgent,\n snapshot.activeProject,\n refreshClineSessions,\n loadClineSession,\n loadChat,\n refreshSessions,\n setSessionDisplayState,\n ]);\n\n // ── Send message ────────────────────────────────────────────────────────────\n const onSend = useCallback(\n async (\n message: string,\n agent: string,\n model: string,\n attachments?: string[],\n ): Promise<{ ok: boolean }> => {\n if (busy.send) return { ok: false };\n const trimmed = message.trim();\n if (!trimmed) return { ok: false };\n\n if (activeSource === 'cline' && activeClineSessionId) {\n const sid = activeClineSessionId;\n const optimisticId = `msg_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;\n const optimistic: ChatMessage = {\n id: optimisticId,\n role: 'user',\n content: trimmed,\n agent,\n ts: new Date().toISOString(),\n };\n appendClineMessage(optimistic);\n markSeen(optimisticId);\n setBusy((b) => ({ ...b, send: true }));\n try {\n await api.post<{ ok: boolean; messageId: string }>(\n `/cline-sessions/${encodeURIComponent(sid)}/send`,\n {\n message: trimmed,\n agent,\n },\n );\n markSessionStreaming(sid, true);\n return { ok: true };\n } catch (err) {\n // Remove optimistic on failure.\n setClineMessages((cur) => cur.filter((m) => m.id !== optimisticId));\n toastRef.current?.error(\n `Send failed: ${(err as Error).message}`,\n );\n return { ok: false };\n } finally {\n setBusy((b) => ({ ...b, send: false }));\n }\n }\n\n // Bizar fallback.\n const optimistic: ChatMessage = {\n role: 'user',\n content: trimmed,\n agent,\n ts: new Date().toISOString(),\n };\n setBizarMessages((cur) => [...cur, optimistic]);\n setBusy((b) => ({ ...b, send: true }));\n markSessionStreaming(sessionId, true);\n try {\n const response = await api.post<{ messages?: ChatMessage[] }>(\n '/chat',\n {\n message: trimmed,\n agent,\n model,\n attachments: attachments || [],\n },\n );\n if (response?.messages) {\n for (const msg of response.messages) {\n if (msg.role === 'assistant') {\n setBizarMessages((cur) => {\n const exists = cur.some((m) => m.ts === msg.ts);\n return exists ? cur : [...cur, msg];\n });\n }\n }\n } else {\n toastRef.current?.info(\n 'Still processing… the response will appear shortly.',\n );\n }\n return { ok: true };\n } catch (err) {\n setBizarMessages((cur) => cur.filter((m) => m !== optimistic));\n toastRef.current?.error(\n `Send failed: ${(err as Error).message}`,\n );\n return { ok: false };\n } finally {\n setBusy((b) => ({ ...b, send: false }));\n markSessionStreaming(sessionId, false);\n }\n },\n [\n activeSource,\n activeClineSessionId,\n appendClineMessage,\n markSeen,\n markSessionStreaming,\n sessionId,\n busy.send,\n ],\n );\n\n const onRegenerate = useCallback(\n async (messageId: string) => {\n if (!sessionId) {\n toastRef.current?.error('No active session to regenerate.');\n return;\n }\n try {\n await api.post('/chat/regenerate', { sessionId, messageId });\n await loadChat(sessionId);\n } catch (err) {\n toastRef.current?.error(\n `Regenerate failed: ${(err as Error).message}`,\n );\n }\n },\n [sessionId, loadChat],\n );\n\n const deleteMessage = useCallback(\n (idx: number) => {\n if (activeSource === 'cline') {\n setClineMessages((cur) => cur.filter((_, i) => i !== idx));\n } else {\n setBizarMessages((cur) => cur.filter((_, i) => i !== idx));\n }\n },\n [activeSource],\n );\n\n const togglePin = useCallback((idx: number) => {\n setPinned((cur) => {\n const next = new Set(cur);\n if (next.has(idx)) next.delete(idx);\n else next.add(idx);\n return next;\n });\n }, []);\n\n const copyMessage = useCallback((m: ChatMessage) => {\n const text = m.content || m.message || '';\n try {\n navigator.clipboard?.writeText(text).then(\n () => toastRef.current?.success('Copied.'),\n () => toastRef.current?.error('Copy failed.'),\n );\n } catch {\n toastRef.current?.error('Copy failed.');\n }\n }, []);\n\n // ── Jump-to-latest ──────────────────────────────────────────────────────────\n const handleScroll = useCallback(() => {\n const el = listRef.current;\n if (!el) return;\n const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;\n const atBottom = distFromBottom < 32;\n setStickToBottom(atBottom);\n if (atBottom) setNewMessageCount(0);\n }, []);\n\n const jumpToLatest = useCallback(() => {\n const el = listRef.current;\n if (!el) return;\n el.scrollTop = el.scrollHeight;\n setStickToBottom(true);\n setNewMessageCount(0);\n }, []);\n\n // ── Session rename / delete (rail row menu + info panel actions) ────────────\n const renameSession = useCallback(\n async (id: string, title: string): Promise<boolean> => {\n if (busy.rename) return false;\n setBusy((b) => ({ ...b, rename: true }));\n try {\n const trimmed = title.trim();\n if (!trimmed) {\n toastRef.current?.error('Title cannot be empty.');\n return false;\n }\n // Try the cline path first if this id is in the cline\n // session list, otherwise the local path. We dispatch by\n // inspecting the in-memory set (avoids a round trip).\n const isCline = clineSessions.some((s) => s.id === id);\n if (isCline) {\n await api.patch<{ id: string; title: string }>(\n `/cline-sessions/${encodeURIComponent(id)}`,\n { title: trimmed },\n );\n setClineSessions((cur) =>\n cur.map((s) => (s.id === id ? { ...s, title: trimmed } : s)),\n );\n } else {\n await api.post(\n `/chat/sessions/${encodeURIComponent(id)}/rename`,\n { title: trimmed },\n );\n setSessions((cur) =>\n cur.map((s) => (s.id === id ? { ...s, title: trimmed } : s)),\n );\n }\n toastRef.current?.success('Renamed.');\n return true;\n } catch (err) {\n toastRef.current?.error(\n `Rename failed: ${(err as Error).message}`,\n );\n return false;\n } finally {\n setBusy((b) => ({ ...b, rename: false }));\n }\n },\n [busy.rename, clineSessions],\n );\n\n const deleteSession = useCallback(\n async (id: string): Promise<boolean> => {\n if (busy.delete) return false;\n setBusy((b) => ({ ...b, delete: true }));\n try {\n const isCline = clineSessions.some((s) => s.id === id);\n if (isCline) {\n await api.del(`/cline-sessions/${encodeURIComponent(id)}`);\n setClineSessions((cur) => cur.filter((s) => s.id !== id));\n if (activeClineSessionId === id) {\n closeClineSession();\n }\n } else {\n await api.del(`/chat/sessions/${encodeURIComponent(id)}`);\n setSessions((cur) => cur.filter((s) => s.id !== id));\n if (sessionId === id) {\n setSessionId('');\n setBizarMessages([]);\n }\n }\n setSessionStates((cur) => {\n const next = { ...cur };\n delete next[id];\n return next;\n });\n toastRef.current?.success('Session deleted.');\n return true;\n } catch (err) {\n toastRef.current?.error(\n `Delete failed: ${(err as Error).message}`,\n );\n return false;\n } finally {\n setBusy((b) => ({ ...b, delete: false }));\n }\n },\n [\n busy.delete,\n clineSessions,\n activeClineSessionId,\n closeClineSession,\n sessionId,\n ],\n );\n\n // ── Auto-scroll on new messages ─────────────────────────────────────────────\n useEffect(() => {\n if (listRef.current) {\n if (stickToBottom) {\n listRef.current.scrollTop = listRef.current.scrollHeight;\n } else {\n setNewMessageCount((n) => n + 1);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [bizarMessages, clineMessages]);\n\n // ── Cleanup SSE on unmount ──────────────────────────────────────────────────\n useEffect(() => {\n return () => {\n sseAutoReconnectRef.current = false;\n sseCurrentSessionIdRef.current = null;\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n sseReconnectTimerRef.current = null;\n }\n if (clineEsRef.current) {\n try {\n clineEsRef.current.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n }\n };\n }, []);\n\n // ── Initial load ────────────────────────────────────────────────────────────\n useEffect(() => {\n if (initialTaskId) {\n loadTaskChat(initialTaskId);\n } else {\n loadChat();\n }\n refreshClineSessions();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ── Listen for bizar:setChatTask events ────────────────────────────────────\n useEffect(() => {\n const handler = (e: Event) => {\n const taskId = (e as CustomEvent<{ taskId: string }>).detail?.taskId;\n if (taskId) loadTaskChat(taskId);\n };\n window.addEventListener('bizar:setChatTask', handler);\n return () => window.removeEventListener('bizar:setChatTask', handler);\n }, [loadTaskChat]);\n\n // ── WebSocket for live bizar updates ────────────────────────────────────────\n useEffect(() => {\n let closed = false;\n import('../../lib/ws').then(({ Ws }) => {\n if (closed) return;\n const ws = new Ws();\n ws.on((msg: { type: string; message?: ChatMessage }) => {\n if (msg.type !== 'chat:message') return;\n const next = msg.message;\n if (!next) return;\n setBizarMessages((cur) => {\n const exists = cur.some((m) => m.ts === next.ts);\n return exists ? cur : [...cur, next];\n });\n });\n });\n return () => {\n closed = true;\n };\n }, []);\n\n return {\n // ── State ────────────────────────────────────────────────────────────────\n messages:\n activeSource === 'cline' ? clineMessages : bizarMessages,\n bizarMessages,\n clineMessages,\n sessions,\n clineSessions,\n sessionId,\n setSessionId,\n loading,\n sending: busy.send,\n pinned,\n listRef,\n // ── Cline state ──────────────────────────────────────────────────────\n activeSource,\n activeClineSessionId,\n clineError,\n clineSuggestion,\n /**\n * v5.0.0 — bug #4: structured error info consumed by\n * `ChatInfoPanel`. `null` when no error is active.\n */\n clineErrorInfo,\n /**\n * v5.0.0 — bug #4: retry helper exposed for `ChatInfoPanel`'s\n * Retry button. Calls `loadClineSession` on the currently\n * active cline session (or no-op if none is active).\n */\n retryClineSession: () => {\n if (activeClineSessionId) void loadClineSession(activeClineSessionId);\n },\n // ── v3.22 — per-session display state ──────────────────────────────────\n sessionStates,\n busy,\n getSessionDisplay: (s: ChatSession) => ({\n ...s,\n state: sessionStates[s.id]?.state ?? 'idle',\n unread: sessionStates[s.id]?.unread ?? 0,\n pinned: sessionStates[s.id]?.pinned ?? false,\n tree: sessionStates[s.id]?.tree,\n }),\n // ── Jump-to-latest ──────────────────────────────────────────────────────\n stickToBottom,\n newMessageCount,\n handleScroll,\n jumpToLatest,\n // ── Toast integration ──────────────────────────────────────────────────\n setToast,\n // ── Actions ─────────────────────────────────────────────────────────────\n loadChat,\n loadTaskChat,\n refreshSessions,\n refreshClineSessions,\n loadClineSession,\n closeClineSession,\n selectBizarSession,\n onCreateSession,\n onSend,\n onRegenerate,\n deleteMessage,\n togglePin,\n copyMessage,\n renameSession,\n deleteSession,\n seenClineMessages,\n };\n}\n","// src/components/chat/useSlashCommands.ts — filter slash-command suggestions from mods.\n\nimport { useMemo, useState } from 'react';\nimport type { Mod, Snapshot } from '../../lib/types';\n\nexport type SlashCommand = { cmd: string; desc: string; mod?: string };\n\nconst BUILTIN_COMMANDS: SlashCommand[] = [\n { cmd: '/visual-plan [on|off|status]', desc: 'Toggle or view visual plan mode' },\n { cmd: '/plan new <slug> [template]', desc: 'Create a new plan' },\n { cmd: '/plan list', desc: 'List all plans' },\n { cmd: '/plan open <slug>', desc: 'Open a plan in the viewer' },\n { cmd: '/plan status <slug> <status>', desc: 'Set plan status' },\n { cmd: '/plan get <slug>', desc: 'Fetch plan canvas' },\n { cmd: '/plan add <slug> --title T --type kind', desc: 'Add element to a plan' },\n { cmd: '/plan update <slug> <id>', desc: 'Update a plan element' },\n { cmd: '/plan delete <slug> <id>', desc: 'Delete a plan element' },\n { cmd: '/plan comment <slug> [id] \"text\"', desc: 'Add a comment' },\n { cmd: '/plan comments <slug> [id]', desc: 'Read plan comments' },\n { cmd: '/bizar', desc: 'Launch Bizar dashboard' },\n { cmd: '/bizar <args>', desc: 'Route via Bizar menu' },\n { cmd: '/audit', desc: 'Run security audit' },\n { cmd: '/explain <q>', desc: 'Read-only code Q&A' },\n { cmd: '/init', desc: 'Initialize .bizar/ in this project' },\n { cmd: '/learn', desc: 'Extract patterns from session' },\n { cmd: '/pr-review', desc: 'PR review' },\n { cmd: '/help | /commands', desc: 'Show all Bizar commands' },\n];\n\nexport function useSlashCommands(snapshot: Snapshot) {\n const [query, setQuery] = useState('');\n\n const allCommands = useMemo<SlashCommand[]>(() => [\n ...BUILTIN_COMMANDS,\n ...(snapshot.mods || []).flatMap((m: Mod) =>\n m.entry?.command ? [{ cmd: `/${m.id}`, desc: m.description || m.name, mod: m.id }] : [],\n ),\n ], [snapshot.mods]);\n\n const suggestions = useMemo(() => {\n if (!query.startsWith('/') || query.includes(' ')) return [];\n const q = query.toLowerCase();\n return allCommands.filter((c) => c.cmd.toLowerCase().startsWith(q)).slice(0, 6);\n }, [query, allCommands]);\n\n return { allCommands, suggestions, setQuery };\n}"],"file":"assets/useSlashCommands-DtITw8Xv.js"}
1
+ {"version":3,"mappings":"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","names":["VirtualList","items","renderItem","itemHeight","height","overscan","className","scrollTop","setScrollTop","useState","containerRef","useRef","visibleRange","useMemo","start","end","totalHeight","offsetY","visibleItems","jsx","e","item","i","ChatTopBar","activeProject","sessionCount","sessionsOpen","infoOpen","onToggleSessions","onToggleInfo","onOpenOverview","jsxs","Folder","ChevronDown","LoadingSkeleton","count","_","SuggestionCards","projectName","onPickSuggestion","suggestions","Code","BookOpen","Lightbulb","ImageIcon","s","WelcomeScreen","variant","displayName","MessageBubble","message","pinned","onCopy","onDelete","onTogglePin","onRegenerate","role","ts","formatTime","content","isUser","Copy","RefreshCw","Pin","Trash2","ReactMarkdown","remarkGfm","ChatThread","messages","loading","sessionId","activeSource","innerRef","useEffect","el","p","wrapped","stream","m","AgentChip","agent","setAgent","agents","open","setOpen","ref","handler","current","display","v","Bot","a","useAutoGrowTextarea","value","maxHeight","useLayoutEffect","next","Composer","model","setModel","text","setText","sending","onSend","attachments","setAttachments","onAttach","inputRef","fileInputRef","onKeyDown","base","onFiles","files","names","cur","_a","Paperclip","j","X","enhanced","enhancePrompt","Sparkles","Send","useChat","snapshot","settings","initialTaskId","bizarMessages","setBizarMessages","sessions","setSessions","setSessionId","setLoading","busy","setBusy","clineSessions","setClineSessions","clineMessages","setClineMessages","setActiveSource","activeClineSessionId","setActiveClineSessionId","clineError","setClineError","clineSuggestion","setClineSuggestion","clineErrorInfo","setClineErrorInfo","sessionStates","setSessionStates","stickToBottom","setStickToBottom","newMessageCount","setNewMessageCount","seenClineMessages","setSeenClineMessages","setPinned","listRef","toastRef","clineEsRef","sseReconnectAttemptRef","sseReconnectTimerRef","sseCurrentSessionIdRef","sseAutoReconnectRef","appendClineMessage","useCallback","msg","idx","markSeen","id","setSessionDisplayState","patch","prev","markSessionStreaming","streaming","setToast","toast","loadChat","sid","url","data","api","err","loadTaskChat","taskId","refreshSessions","refreshClineSessions","enriched","openSseForSession","tok","connect","es","onEnvelope","evt","sidForState","payload","_b","_c","_d","parseData","raw","parsed","props","sessionID","messageID","type","inner","innerData","namedTypes","name","nextAttempt","delayMs","closeClineSession","loadClineSession","info","ApiError","d","code","canRetry","selectBizarSession","onCreateSession","b","created","clineErr","_e","trimmed","optimisticId","optimistic","response","messageId","deleteMessage","togglePin","copyMessage","handleScroll","atBottom","jumpToLatest","renameSession","title","deleteSession","n","closed","__vitePreload","Ws","BUILTIN_COMMANDS","useSlashCommands","query","setQuery","allCommands","q","c"],"ignoreList":[],"sources":["../../src/web/components/VirtualList.tsx","../../src/web/components/chat/ChatTopBar.tsx","../../src/web/components/chat/LoadingSkeleton.tsx","../../src/web/components/chat/SuggestionCards.tsx","../../src/web/components/chat/WelcomeScreen.tsx","../../src/web/components/chat/MessageBubble.tsx","../../src/web/components/chat/ChatThread.tsx","../../src/web/components/chat/AgentChip.tsx","../../src/web/components/chat/useAutoGrowTextarea.ts","../../src/web/components/chat/Composer.tsx","../../src/web/components/chat/useChat.ts","../../src/web/components/chat/useSlashCommands.ts"],"sourcesContent":["// src/web/components/VirtualList.tsx — hand-rolled virtual scrolling (no external deps).\nimport React, { useState, useRef, useEffect, useMemo } from 'react';\n\nexport function VirtualList<T>({\n items,\n renderItem,\n itemHeight = 80,\n height = 400,\n overscan = 5,\n className,\n}: {\n items: T[];\n renderItem: (item: T, index: number) => React.ReactNode;\n itemHeight?: number;\n height?: number;\n overscan?: number;\n className?: string;\n}) {\n const [scrollTop, setScrollTop] = useState(0);\n const containerRef = useRef<HTMLDivElement>(null);\n\n const visibleRange = useMemo(() => {\n const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);\n const end = Math.min(\n items.length,\n Math.ceil((scrollTop + height) / itemHeight) + overscan,\n );\n return { start, end };\n }, [scrollTop, itemHeight, height, overscan, items.length]);\n\n const totalHeight = items.length * itemHeight;\n const offsetY = visibleRange.start * itemHeight;\n const visibleItems = items.slice(visibleRange.start, visibleRange.end);\n\n return (\n <div\n ref={containerRef}\n onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)}\n style={{ height, overflowY: 'auto' }}\n className={className}\n >\n <div style={{ height: totalHeight, position: 'relative' }}>\n <div style={{ transform: `translateY(${offsetY}px)` }}>\n {visibleItems.map((item, i) => (\n <div key={visibleRange.start + i} style={{ height: itemHeight }}>\n {renderItem(item, visibleRange.start + i)}\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n","// src/components/chat/ChatTopBar.tsx — 48px topbar with project chip + panel toggles.\n\nimport { Folder, ChevronDown } from 'lucide-react';\n\ninterface Props {\n activeProject: { name: string; path?: string } | null;\n sessionCount: number;\n sessionsOpen: boolean;\n infoOpen: boolean;\n onToggleSessions: () => void;\n onToggleInfo: () => void;\n onOpenOverview: () => void;\n}\n\nexport function ChatTopBar({\n activeProject,\n sessionCount,\n sessionsOpen,\n infoOpen,\n onToggleSessions,\n onToggleInfo,\n onOpenOverview,\n}: Props) {\n return (\n <header className=\"chat-topbar\">\n <button type=\"button\" className=\"chat-topbar-project\" onClick={onOpenOverview}>\n <Folder size={14} />\n <span className=\"chat-topbar-project-name\">{activeProject?.name ?? 'No project'}</span>\n <ChevronDown size={12} />\n </button>\n <div className=\"chat-topbar-spacer\" />\n <button\n type=\"button\"\n className={`chat-topbar-btn ${sessionsOpen ? 'active' : ''}`}\n onClick={onToggleSessions}\n aria-pressed={sessionsOpen}\n >\n Sessions ({sessionCount})\n </button>\n <button\n type=\"button\"\n className={`chat-topbar-btn ${infoOpen ? 'active' : ''}`}\n onClick={onToggleInfo}\n aria-pressed={infoOpen}\n >\n Info\n </button>\n </header>\n );\n}","// src/components/chat/LoadingSkeleton.tsx — pulsing skeleton bubbles while loading.\n\ninterface Props {\n count?: number;\n}\n\nexport function LoadingSkeleton({ count = 3 }: Props) {\n return (\n <div className=\"chat-loading-skeleton\" aria-label=\"Loading messages\">\n {Array.from({ length: count }).map((_, i) => (\n <div key={i} className=\"chat-loading-skeleton-bubble\">\n <div className=\"chat-loading-skeleton-meta\" />\n <div className=\"chat-loading-skeleton-line\" />\n <div className=\"chat-loading-skeleton-line short\" />\n </div>\n ))}\n </div>\n );\n}","// src/components/chat/SuggestionCards.tsx — 4 hardcoded suggestion cards for first-run.\n\nimport { Code, BookOpen, Lightbulb, Image as ImageIcon } from 'lucide-react';\n\ninterface Props {\n projectName: string;\n onPickSuggestion: (text: string) => void;\n}\n\nexport function SuggestionCards({ projectName, onPickSuggestion }: Props) {\n const suggestions = [\n {\n icon: <Code size={16} />,\n title: projectName ? `Explain ${projectName}` : 'Explain this project',\n preview: 'Give me a tour of the codebase, highlight the architecture, and call out any anti-patterns or hotspots.',\n prompt: projectName\n ? `Explain the ${projectName} project. Give me a tour of the codebase, highlight the architecture, and call out any anti-patterns or hotspots.`\n : 'Explain the current project. Give me a tour of the codebase, highlight the architecture, and call out any anti-patterns or hotspots.',\n },\n {\n icon: <BookOpen size={16} />,\n title: 'Summarize recent sessions',\n preview: 'Read my last 5 chat sessions and give me a 3-bullet summary of what I worked on and what got stuck.',\n prompt: 'Read my last 5 chat sessions and give me a 3-bullet summary of what I worked on and what got stuck.',\n },\n {\n icon: <Lightbulb size={16} />,\n title: 'Suggest a refactor',\n preview: 'Look for the largest, most painful files in this project and propose a concrete refactor for the worst one.',\n prompt: 'Look for the largest, most painful files in this project and propose a concrete refactor for the worst one.',\n },\n {\n icon: <ImageIcon size={16} />,\n title: 'Find visual regressions',\n preview: 'Open the dashboard, screenshot the Chat tab, and flag any layout problems or anti-patterns you see.',\n prompt: 'Open the dashboard, screenshot the Chat tab, and flag any layout problems or anti-patterns you see.',\n },\n ];\n\n return (\n <div className=\"chat-suggestions\">\n {suggestions.map((s, i) => (\n <button\n key={i}\n type=\"button\"\n className=\"chat-suggestion\"\n onClick={() => onPickSuggestion(s.prompt)}\n >\n <div className=\"chat-suggestion-title\">{s.icon}<span>{s.title}</span></div>\n <div className=\"chat-suggestion-preview\">{s.preview}</div>\n </button>\n ))}\n </div>\n );\n}","// src/components/chat/WelcomeScreen.tsx — gradient greeting + no-project state.\n\nimport { Folder } from 'lucide-react';\nimport { SuggestionCards } from './SuggestionCards';\n\ninterface Props {\n variant: 'empty' | 'no-project';\n projectName: string;\n onPickSuggestion: (text: string) => void;\n}\n\nexport function WelcomeScreen({ variant, projectName, onPickSuggestion }: Props) {\n if (variant === 'no-project') {\n return (\n <div className=\"chat-welcome\">\n <h1 className=\"chat-welcome-h1\">\n <span className=\"chat-greeting-hello\">No project selected.</span>\n </h1>\n <p className=\"chat-welcome-subtitle\">Pick a project in Overview to scope chat sessions.</p>\n </div>\n );\n }\n\n const displayName = projectName || 'there';\n\n return (\n <div className=\"chat-welcome\">\n <h1 className=\"chat-welcome-h1\">\n <span className=\"chat-greeting-hello\">Hello, </span>\n <span className=\"chat-greeting-name\">{displayName}.</span>\n </h1>\n <p className=\"chat-welcome-subtitle\">How can I help you today?</p>\n <SuggestionCards projectName={projectName} onPickSuggestion={onPickSuggestion} />\n </div>\n );\n}","// src/components/chat/MessageBubble.tsx — user/assistant message with hover actions.\n\nimport ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport { Copy, RefreshCw, Pin, Trash2 } from 'lucide-react';\nimport { formatTime } from '../../lib/utils';\n\ninterface Message {\n role?: string;\n content?: string;\n message?: string;\n agent?: string;\n ts?: string | number;\n}\n\ninterface Props {\n message: Message;\n pinned: boolean;\n onCopy: () => void;\n onDelete: () => void;\n onTogglePin: () => void;\n onRegenerate: () => void;\n}\n\nexport function MessageBubble({ message, pinned, onCopy, onDelete, onTogglePin, onRegenerate }: Props) {\n const role = (message.role || 'assistant').toLowerCase();\n const ts = message.ts ? formatTime(message.ts) : '';\n const content = message.content || message.message || '';\n const isUser = role === 'user';\n\n return (\n <article className={`chat-message ${isUser ? 'chat-message-user' : 'chat-message-assistant'} ${pinned ? 'chat-message-pinned' : ''}`}>\n <header className=\"chat-message-meta\">\n <span className=\"chat-message-role\">{role}</span>\n {message.agent && <span className=\"chat-message-agent\">@{message.agent}</span>}\n {ts && <time className=\"chat-message-time\">{ts}</time>}\n {pinned && <span className=\"chat-message-pin\">pinned</span>}\n <div className=\"chat-message-actions\">\n <button type=\"button\" className=\"chat-msg-action-btn\" onClick={onCopy} title=\"Copy\" aria-label=\"Copy\">\n <Copy size={12} />\n </button>\n {!isUser && (\n <button type=\"button\" className=\"chat-msg-action-btn\" onClick={onRegenerate} title=\"Regenerate\" aria-label=\"Regenerate\">\n <RefreshCw size={12} />\n </button>\n )}\n <button type=\"button\" className=\"chat-msg-action-btn\" onClick={onTogglePin} title=\"Pin\" aria-label=\"Pin\">\n <Pin size={12} />\n </button>\n <button type=\"button\" className=\"chat-msg-action-btn chat-msg-action-btn-danger\" onClick={onDelete} title=\"Delete\" aria-label=\"Delete\">\n <Trash2 size={12} />\n </button>\n </div>\n </header>\n <div className=\"chat-message-content markdown-body\">\n <ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>\n </div>\n </article>\n );\n}","// src/components/chat/ChatThread.tsx — message list, welcome screen, loading skeleton.\n//\n// v4.2.5 — accepts `activeSource` so the loading skeleton can hint\n// which stream is loading and the welcome variant for \"cline\"\n// can differ from the local chat welcome.\n\nimport { useEffect, useRef } from 'react';\nimport { LoadingSkeleton } from './LoadingSkeleton';\nimport { WelcomeScreen } from './WelcomeScreen';\nimport { MessageBubble } from './MessageBubble';\nimport { VirtualList } from '../VirtualList';\n\ninterface Message {\n role?: string;\n content?: string;\n message?: string;\n agent?: string;\n ts?: string | number;\n}\n\ninterface Props {\n messages: Message[];\n loading: boolean;\n activeProject: { name: string } | null;\n sessionId: string;\n pinned: Set<number>;\n /** Which stream currently owns the thread — purely cosmetic, lets\n * the empty/loading states be slightly more specific. The type\n * uses `unknown` so we don't pull in a tight coupling; the value\n * is reflected back into the loading skeleton and welcome screen\n * via data attributes so CSS can style accordingly. */\n activeSource?: 'bizar' | 'cline' | null;\n onPickSuggestion: (text: string) => void;\n onCopy: (m: Message) => void;\n onDelete: (idx: number) => void;\n onTogglePin: (idx: number) => void;\n onRegenerate: (messageId: string) => void;\n}\n\nexport function ChatThread({\n messages,\n loading,\n activeProject,\n sessionId,\n pinned,\n activeSource,\n onPickSuggestion,\n onCopy,\n onDelete,\n onTogglePin,\n onRegenerate,\n}: Props) {\n const innerRef = useRef<HTMLDivElement>(null);\n\n // Only auto-scroll when we own the scroll container (i.e. when\n // rendered directly as the message scroller). When embedded\n // inside Chat.tsx's chat-thread-scroll, the parent handles scroll.\n useEffect(() => {\n const el = innerRef.current;\n if (!el) return;\n let p: HTMLElement | null = el.parentElement;\n let wrapped = false;\n while (p) {\n if (p.classList.contains('chat-thread-scroll')) {\n wrapped = true;\n break;\n }\n p = p.parentElement;\n }\n if (!wrapped) el.scrollTop = el.scrollHeight;\n }, [messages]);\n\n const stream: 'bizar' | 'cline' = activeSource === 'cline' ? 'cline' : 'bizar';\n\n if (loading) {\n return (\n <div className=\"chat-thread legacy\" ref={innerRef} data-stream={stream}>\n <LoadingSkeleton count={3} />\n </div>\n );\n }\n\n if (!activeProject) {\n return (\n <div className=\"chat-thread legacy\" ref={innerRef} data-stream={stream}>\n <WelcomeScreen\n variant=\"no-project\"\n projectName=\"\"\n onPickSuggestion={onPickSuggestion}\n />\n </div>\n );\n }\n\n if (messages.length === 0) {\n return (\n <div className=\"chat-thread legacy\" ref={innerRef} data-stream={stream}>\n <WelcomeScreen\n variant=\"empty\"\n projectName={activeProject.name}\n onPickSuggestion={onPickSuggestion}\n />\n </div>\n );\n }\n\n return (\n <div className=\"chat-thread legacy\" ref={innerRef}>\n <VirtualList\n items={messages}\n itemHeight={120}\n height={800}\n className=\"chat-thread-virtual\"\n renderItem={(m, i) => (\n <MessageBubble\n message={m}\n pinned={pinned.has(i)}\n onCopy={() => onCopy(m)}\n onDelete={() => onDelete(i)}\n onTogglePin={() => onTogglePin(i)}\n onRegenerate={() => onRegenerate(String(m.ts ?? i))}\n />\n )}\n />\n </div>\n );\n}\n","// src/components/chat/AgentChip.tsx — custom popover agent picker, replaces <select>.\n\nimport { useEffect, useRef, useState } from 'react';\nimport { Bot, ChevronDown } from 'lucide-react';\n\ninterface Props {\n agent: string;\n setAgent: (a: string) => void;\n agents: Array<{ name: string }>;\n}\n\nexport function AgentChip({ agent, setAgent, agents }: Props) {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener('mousedown', handler);\n return () => document.removeEventListener('mousedown', handler);\n }, [open]);\n\n const current = agent || 'no agent';\n const display = current.startsWith('@') ? current.slice(1) : current;\n\n return (\n <div className=\"chat-agent-chip\" ref={ref}>\n <button\n type=\"button\"\n className=\"chat-agent-chip-btn\"\n onClick={() => setOpen((v) => !v)}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n >\n <Bot size={14} />\n <span>{display}</span>\n <ChevronDown size={12} className={open ? 'rotated' : ''} />\n </button>\n {open && (\n <div className=\"chat-agent-chip-menu\" role=\"listbox\">\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={!agent}\n onClick={() => { setAgent(''); setOpen(false); }}\n >\n <span>no agent</span>\n </button>\n {agents.map((a) => (\n <button\n key={a.name}\n type=\"button\"\n role=\"option\"\n aria-selected={agent === a.name}\n onClick={() => { setAgent(a.name); setOpen(false); }}\n >\n <span>@{a.name}</span>\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}","// src/components/chat/useAutoGrowTextarea.ts — auto-resize textarea up to maxHeight.\n\nimport { useLayoutEffect } from 'react';\n\nexport function useAutoGrowTextarea(\n ref: React.RefObject<HTMLTextAreaElement | null>,\n value: string,\n maxHeight = 240,\n): void {\n useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n el.style.height = 'auto';\n const next = Math.min(el.scrollHeight, maxHeight);\n el.style.height = `${next}px`;\n el.style.overflowY = el.scrollHeight > maxHeight ? 'auto' : 'hidden';\n }, [ref, value, maxHeight]);\n}","// src/components/chat/Composer.tsx — textarea + toolbar, used inside FloatingComposer.\n\nimport { useRef } from 'react';\nimport { Send, Paperclip, X, Sparkles } from 'lucide-react';\nimport { enhancePrompt } from '../../lib/api';\nimport { AgentChip } from './AgentChip';\nimport { useAutoGrowTextarea } from './useAutoGrowTextarea';\n\ninterface Props {\n agent: string;\n setAgent: (a: string) => void;\n model: string;\n setModel: (m: string) => void;\n text: string;\n setText: (t: string) => void;\n sending: boolean;\n onSend: () => void;\n attachments: string[];\n setAttachments: (a: string[] | ((prev: string[]) => string[])) => void;\n suggestions: Array<{ cmd: string; desc: string }>;\n onPickSuggestion: (cmd: string) => void;\n agents: Array<{ name: string }>;\n onAttach: () => void;\n}\n\nexport function Composer({\n agent,\n setAgent,\n model,\n setModel,\n text,\n setText,\n sending,\n onSend,\n attachments,\n setAttachments,\n suggestions,\n onPickSuggestion,\n agents,\n onAttach,\n}: Props) {\n const inputRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n useAutoGrowTextarea(inputRef, text, 160);\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n onSend();\n } else if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n onSend();\n } else if (e.key === 'Tab' && suggestions.length) {\n e.preventDefault();\n const base = suggestions[0].cmd.split(' ')[0];\n setText(`${base} `);\n }\n };\n\n const onFiles = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files;\n if (!files) return;\n const names: string[] = [];\n for (let i = 0; i < files.length; i++) names.push(files[i].name);\n setAttachments((cur) => [...cur, ...names]);\n e.target.value = '';\n };\n\n return (\n <div className=\"chat-composer\">\n {suggestions.length > 0 && (\n <div className=\"chat-composer-suggestions\">\n {suggestions.map((s) => (\n <button\n key={s.cmd}\n type=\"button\"\n className=\"chat-composer-suggestion\"\n onClick={() => {\n const base = s.cmd.split(' ')[0];\n setText(`${base} `);\n inputRef.current?.focus();\n }}\n >\n <span className=\"mono\">{s.cmd}</span>\n <span>{s.desc}</span>\n </button>\n ))}\n </div>\n )}\n\n {attachments.length > 0 && (\n <div className=\"chat-composer-attachments\">\n {attachments.map((a, i) => (\n <span key={i} className=\"chat-composer-attachment-tag\">\n <Paperclip size={10} /> {a}\n <button\n type=\"button\"\n className=\"chat-composer-attachment-remove\"\n onClick={() => setAttachments((cur) => cur.filter((_, j) => j !== i))}\n aria-label={`Remove ${a}`}\n >\n <X size={10} />\n </button>\n </span>\n ))}\n </div>\n )}\n\n <div className=\"chat-composer-input\">\n <AgentChip agent={agent} setAgent={setAgent} agents={agents} />\n <textarea\n ref={inputRef}\n className=\"chat-composer-textarea\"\n placeholder={sending ? 'Sending…' : 'Send a message…'}\n rows={1}\n value={text}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={onKeyDown}\n disabled={sending}\n aria-label=\"Message\"\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n style={{ display: 'none' }}\n onChange={onFiles}\n aria-label=\"Attach files\"\n tabIndex={-1}\n />\n <button\n type=\"button\"\n className=\"chat-attach-btn\"\n onClick={onAttach}\n title=\"Attach files\"\n aria-label=\"Attach files\"\n >\n <Paperclip size={14} />\n </button>\n <button\n type=\"button\"\n className=\"chat-attach-btn\"\n onClick={async () => {\n if (!text.trim()) return;\n const enhanced = await enhancePrompt(text);\n if (enhanced !== text) setText(enhanced);\n }}\n title=\"Enhance prompt with AI\"\n aria-label=\"Enhance prompt\"\n >\n <Sparkles size={14} />\n </button>\n <button\n type=\"button\"\n className=\"chat-send-btn\"\n onClick={onSend}\n disabled={sending || !text.trim()}\n title=\"Send (Enter)\"\n aria-label=\"Send message\"\n >\n <Send size={16} />\n </button>\n </div>\n </div>\n );\n}","// src/components/chat/useChat.ts — shared chat state: messages, sessions,\n// send, polling, SSE, session-state tracking.\n//\n// v6.3.0 — Claude Code migration. Supports two parallel message streams:\n// - bizar (GET /api/chat)\n// - claude (GET /api/claude-sessions/:id/messages + SSE)\n//\n// Changes from v4.2.5:\n// * Endpoint prefix changed: `/api/claude-sessions/*` → `/api/claude-sessions/*`\n// * SSE event type names changed to match the Claude SDK envelope\n// (`message.part.updated` → `delta`/`text_delta`, etc.)\n// * Server no longer needs the `clineErrorInfo` 503 path; the\n// `claudeErrorInfo` field stays for parity but the dashboard\n// mostly sees 502s now (claude CLI not on PATH / spawn failed).\n// * Optimistic send uses a stable `msg_*` ID and is deduped when\n// claude echoes the message back via SDK events.\n// * `claudeMessages` keeps server-provided IDs distinct from\n// optimistic IDs so the merge is unambiguous (server echo\n// replaces optimistic by matching messageID; otherwise both stick\n// around briefly until the user/session state catches up).\n// * `onSend` routes to `/claude-sessions/:id/send` when a\n// claude session is active, `/chat` otherwise. Errors surface\n// via toast + we always remove the optimistic placeholder.\n// * Sessions are managed by source:\n// - `bizarSessions` → local .jsonl store, POST /chat/sessions\n// - `claudeSessions` → Claude Code, GET /claude-sessions\n// The rail shows both side-by-side with a source indicator and\n// uses separate rename/delete endpoints per source.\n// * All async actions surface a single `busy: { create, send, rename, delete }`\n// so the rail can disable buttons properly.\n// * No `any` leaks — all SSE event shapes are typed via\n// `ClineSseEnvelope`.\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type {\n ChatMessage,\n ChatResponse,\n ChatSession,\n Settings,\n Snapshot,\n} from '../../lib/types';\nimport { api, ApiError } from '../../lib/api';\nimport type { SessionState } from './ChatRail';\nimport type { AgentTreeNode } from './AgentNode';\n\nexport interface SessionDisplayState {\n /** idle / streaming / awaiting — derived from the latest SSE event. */\n state: SessionState;\n /** Number of unread (i.e. arrived while the user wasn't viewing) messages. */\n unread: number;\n /** Pinned? (cosmetic for now; pinned state lives in the parent.) */\n pinned: boolean;\n /** Sub-agent tree, if any. */\n tree?: { root: AgentTreeNode };\n}\n\n/** Per-source action busy flags. Used by rail/info-panel to disable buttons. */\nexport interface ChatBusyState {\n create: boolean;\n send: boolean;\n rename: boolean;\n delete: boolean;\n}\n\n/**\n * Structured error envelope surfaced by `loadClineSession` when\n * the upstream `/api/claude-sessions/:id/messages` call fails.\n *\n * - `code` mirrors the server's `error` field (`plugin_offline`,\n * `directory_unknown`, `cline_error`, `bad_request`).\n * - `cause` is the server's structured cause (`network`, `timeout`,\n * `unknown`, or a raw ECONNREFUSED-style string).\n * - `suggestion` is the operator-friendly hint from the server\n * (e.g. \"Run `bizar doctor` for diagnostics\").\n * - `canRetry` is `true` whenever the failure is transient (network\n * error, upstream 502) — i.e. the user can fix it by trying again\n * after starting the plugin / waiting for cline to come back.\n * False for hard failures (bad_request).\n */\nexport interface ClineErrorInfo {\n type: 'session_load_failed';\n code: string;\n message: string;\n cause?: string;\n status?: number;\n suggestion?: string | null;\n canRetry: boolean;\n}\n\n/** Shape of one cline SSE envelope after unwrapping. */\ninterface ClineSseEnvelope {\n type: string;\n sessionID?: string;\n messageID?: string;\n /** Raw upstream payload (kept for forwarders that want full fidelity). */\n data?: unknown;\n}\n\n/**\n * The chat hook returns a stable bag of state + actions used by\n * both Chat.tsx (desktop) and MobileChat.tsx (mobile).\n *\n * The hook owns the lifecycle of the cline SSE stream. Callers\n * should never instantiate their own EventSource for the same\n * session — they should always go through this hook.\n */\nexport function useChat(snapshot: Snapshot, settings: Settings, initialTaskId?: string | null) {\n // ── Bizar stream ────────────────────────────────────────────────────────────\n const [bizarMessages, setBizarMessages] = useState<ChatMessage[]>([]);\n const [sessions, setSessions] = useState<ChatSession[]>([]);\n const [sessionId, setSessionId] = useState('');\n const [loading, setLoading] = useState(true);\n const [busy, setBusy] = useState<ChatBusyState>({\n create: false,\n send: false,\n rename: false,\n delete: false,\n });\n\n // ── Cline stream ─────────────────────────────────────────────────────────\n const [clineSessions, setClineSessions] = useState<ChatSession[]>([]);\n const [clineMessages, setClineMessages] = useState<ChatMessage[]>([]);\n const [activeSource, setActiveSource] = useState<'bizar' | 'cline' | null>(null);\n const [activeClineSessionId, setActiveClineSessionId] = useState<string | null>(null);\n const [clineError, setClineError] = useState<string | null>(null);\n const [clineSuggestion, setClineSuggestion] = useState<string | null>(null);\n /**\n * Structured error info for the cline session load path. New in\n * v5.0.0 — `ChatInfoPanel` consumes this to render its own error\n * section (in addition to the chat-thread-header error already\n * rendered by Chat.tsx). `null` when no error.\n */\n const [clineErrorInfo, setClineErrorInfo] = useState<ClineErrorInfo | null>(null);\n\n // ── v3.22 — session-state, unread, tree per session ────────────────────────\n const [sessionStates, setSessionStates] = useState<Record<string, SessionDisplayState>>({});\n const [stickToBottom, setStickToBottom] = useState(true);\n const [newMessageCount, setNewMessageCount] = useState(0);\n\n // Set of messageIDs the user has already accepted (or that arrived\n // via SSE) — used by the cline path to dedupe optimistic + echo.\n const [seenClineMessages, setSeenClineMessages] = useState<Set<string>>(new Set());\n\n // Backwards-compat alias (chat.sendSession delete etc. used `pinned` historically).\n const [pinned, setPinned] = useState<Set<number>>(new Set());\n\n const listRef = useRef<HTMLDivElement>(null);\n const toastRef = useRef<\n | {\n error: (msg: string) => void;\n success: (msg: string) => void;\n info: (msg: string) => void;\n warning: (msg: string) => void;\n }\n | null\n >(null);\n\n // ── SSE lifecycle refs ──────────────────────────────────────────────────────\n const clineEsRef = useRef<EventSource | null>(null);\n const sseReconnectAttemptRef = useRef(0);\n const sseReconnectTimerRef = useRef<number | null>(null);\n // The id of the session we're CURRENTLY streaming. When the user\n // switches sessions, this id changes and the SSE handler short-\n // circuits any in-flight appends.\n const sseCurrentSessionIdRef = useRef<string | null>(null);\n // Whether the SSE connection should be auto-reconnected on error.\n // Set to false on explicit disconnect (session switch / unmount).\n const sseAutoReconnectRef = useRef(false);\n\n /** Append `msg` to `clineMessages`, replacing any prior\n * optimistic entry whose content matches or whose ID matches. */\n const appendClineMessage = useCallback((msg: ChatMessage) => {\n if (!msg) return;\n setClineMessages((cur) => {\n // Existing exact-ID match: skip.\n if (msg.id && cur.some((m) => m.id === msg.id)) return cur;\n // Optimistic placeholder (starts with msg_) with the same role\n // and content: replace it. This is how cline's echo of our\n // own message gets deduplicated against the optimistic copy.\n const idx = cur.findIndex(\n (m) =>\n typeof m.id === 'string' &&\n m.id.startsWith('msg_') &&\n m.role === msg.role &&\n (m.content || '') === (msg.content || ''),\n );\n if (idx >= 0) {\n const next = cur.slice();\n next[idx] = { ...msg, id: msg.id ?? cur[idx].id };\n return next;\n }\n return [...cur, msg];\n });\n }, []);\n\n /** Mark a messageID as seen — used to dedupe echo rounds. */\n const markSeen = useCallback((id: string | undefined) => {\n if (!id) return;\n setSeenClineMessages((cur) => {\n if (cur.has(id)) return cur;\n const next = new Set(cur);\n next.add(id);\n return next;\n });\n }, []);\n\n // ── Helpers to mutate per-session state ────────────────────────────────────\n const setSessionDisplayState = useCallback(\n (id: string, patch: Partial<SessionDisplayState>) => {\n setSessionStates((cur) => {\n const prev: SessionDisplayState =\n cur[id] ?? { state: 'idle', unread: 0, pinned: false };\n return { ...cur, [id]: { ...prev, ...patch } };\n });\n },\n [],\n );\n\n const markSessionStreaming = useCallback(\n (id: string, streaming: boolean) => {\n setSessionDisplayState(id, { state: streaming ? 'streaming' : 'idle' });\n },\n [setSessionDisplayState],\n );\n\n /** ─── Toast binding (set by the consuming view via `setToast`) ──── */\n const setToast = useCallback(\n (toast: typeof toastRef.current) => {\n toastRef.current = toast;\n },\n [],\n );\n\n // ── Load bizar chat ─────────────────────────────────────────────────────────\n const loadChat = useCallback(\n async (sid?: string) => {\n try {\n const url = sid\n ? `/chat?session=${encodeURIComponent(sid)}`\n : '/chat?limit=200';\n const data = await api.get<ChatResponse>(url);\n setBizarMessages(data.messages || []);\n setSessions(data.sessions || []);\n } catch (err) {\n toastRef.current?.error(\n `Chat load failed: ${(err as Error).message}`,\n );\n } finally {\n setLoading(false);\n }\n },\n [],\n );\n\n const loadTaskChat = useCallback(async (taskId: string) => {\n setLoading(true);\n try {\n const data = await api.get<{\n sessionId?: string;\n messages: ChatMessage[];\n }>(`/tasks/${encodeURIComponent(taskId)}/chat`);\n setBizarMessages(data.messages || []);\n setSessionId(data.sessionId || taskId);\n } catch (err) {\n toastRef.current?.error(\n `Task chat load failed: ${(err as Error).message}`,\n );\n } finally {\n setLoading(false);\n }\n }, []);\n\n const refreshSessions = useCallback(async () => {\n try {\n const data = await api.get<{ sessions: ChatSession[] }>('/chat/sessions');\n setSessions(data.sessions || []);\n } catch {\n /* best-effort */\n }\n }, []);\n\n const refreshClineSessions = useCallback(async () => {\n try {\n const data = await api.get<{ sessions: ChatSession[] }>(\n '/claude-sessions',\n );\n // Enrich with source marker.\n const enriched: ChatSession[] = (data.sessions || []).map((s) => ({\n ...s,\n source: 'cline' as const,\n title: s.title || s.id,\n }));\n setClineSessions(enriched);\n } catch {\n /* best-effort */\n }\n }, []);\n\n // ── Cline SSE connection ─────────────────────────────────────────────────\n //\n // Single source of truth for the EventSource lifecycle. Callers:\n // - `loadClineSession(id)` — close any current, open a new one.\n // - `closeClineSession()` — close and stop auto-reconnect.\n // - unmount — close and stop.\n //\n // On error / network drop, the EventSource triggers `onerror`. We\n // close the connection and schedule a reconnect (with exponential\n // backoff: 1s, 2s, 4s, …, capped at 30s) UNLESS `sseAutoReconnectRef`\n // is false (explicit disconnect).\n //\n const openSseForSession = useCallback(\n (id: string) => {\n // Close any existing connection first.\n if (clineEsRef.current) {\n try {\n clineEsRef.current.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n }\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n sseReconnectTimerRef.current = null;\n }\n\n // Build the SSE URL. EventSource can't set headers, so we\n // pass the auth token as ?token= when set (loopback makes this\n // optional, but harmless).\n const tok = api.getToken();\n const url = tok\n ? `/api/claude-sessions/${encodeURIComponent(id)}/stream?token=${encodeURIComponent(tok)}`\n : `/api/claude-sessions/${encodeURIComponent(id)}/stream`;\n\n sseCurrentSessionIdRef.current = id;\n sseAutoReconnectRef.current = true;\n sseReconnectAttemptRef.current = 0;\n setClineError(null);\n setClineSuggestion(null);\n setClineErrorInfo(null);\n\n const connect = () => {\n if (!sseAutoReconnectRef.current) return;\n if (sseCurrentSessionIdRef.current !== id) return;\n const es = new EventSource(url);\n clineEsRef.current = es;\n\n // Helper: dispatch one parsed envelope to local state.\n const onEnvelope = (evt: ClineSseEnvelope | null) => {\n if (!evt || !evt.type) return;\n // Drop events for OTHER sessions even though the proxy\n // already filters — defense in depth.\n if (evt.sessionID && evt.sessionID !== id) return;\n\n const ts = new Date().toISOString();\n const sidForState = id;\n\n if (\n evt.type === 'message.user.created' ||\n evt.type === 'message.user.updated'\n ) {\n const payload = evt.data as\n | { messageID?: string; data?: { content?: string } }\n | undefined;\n appendClineMessage({\n id: payload?.messageID ?? evt.messageID,\n role: 'user',\n content: payload?.data?.content ?? '',\n ts,\n });\n markSeen(payload?.messageID ?? evt.messageID);\n return;\n }\n if (\n evt.type === 'message.assistant.created' ||\n evt.type === 'message.assistant.updated'\n ) {\n const payload = evt.data as\n | { messageID?: string; data?: { content?: string } }\n | undefined;\n appendClineMessage({\n id: payload?.messageID ?? evt.messageID,\n role: 'assistant',\n content: payload?.data?.content ?? '',\n ts,\n });\n markSeen(payload?.messageID ?? evt.messageID);\n markSessionStreaming(sidForState, true);\n return;\n }\n if (evt.type === 'message.part.updated') {\n const payload = evt.data as\n | {\n part?: { type?: string; text?: string };\n messageID?: string;\n id?: string;\n }\n | undefined;\n const text = payload?.part?.text ?? '';\n if (!text) return;\n appendClineMessage({\n id: payload?.id ?? payload?.messageID ?? evt.messageID,\n role: 'assistant',\n content: text,\n ts,\n });\n markSeen(payload?.id ?? payload?.messageID ?? evt.messageID);\n markSessionStreaming(sidForState, true);\n return;\n }\n if (\n evt.type === 'session.idle' ||\n evt.type === 'message.assistant.completed' ||\n evt.type === 'session.completed'\n ) {\n markSessionStreaming(sidForState, false);\n return;\n }\n if (\n evt.type === 'session.awaiting' ||\n evt.type === 'session.awaiting_input' ||\n evt.type === 'message.assistant.awaiting'\n ) {\n setSessionDisplayState(sidForState, { state: 'awaiting' });\n return;\n }\n if (evt.type === 'session.error') {\n setClineError(\n (evt.data as { message?: string } | undefined)?.message ||\n 'cline session error',\n );\n return;\n }\n // Other event types are intentionally ignored — the proxy\n // is canonical, we don't need to project every upstream\n // event into local state.\n };\n\n // Stable parser — the server may send both `{type: \"x\"}` and\n // `sync` envelopes with a `.<n>` version suffix. We accept both.\n const parseData = (raw: string | null | undefined):\n | ClineSseEnvelope\n | null => {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n // Direct shape: {type, properties: {...}}\n if (typeof parsed.type === 'string') {\n const props = parsed.properties as\n | Record<string, unknown>\n | undefined;\n const data = (parsed.data as Record<string, unknown> | undefined) ?? props;\n const sessionID = (\n typeof parsed.sessionID === 'string'\n ? parsed.sessionID\n : typeof props?.sessionID === 'string'\n ? (props.sessionID as string)\n : typeof data?.sessionID === 'string'\n ? (data.sessionID as string)\n : undefined\n );\n const messageID = (\n typeof parsed.messageID === 'string'\n ? parsed.messageID\n : typeof props?.messageID === 'string'\n ? (props.messageID as string)\n : typeof data?.messageID === 'string'\n ? (data.messageID as string)\n : undefined\n );\n let type = parsed.type as string;\n const m = /\\.\\d+$/.exec(type);\n if (m) type = type.slice(0, m.index);\n return { type, sessionID, messageID, data: parsed };\n }\n // Sync envelope: {type: \"sync\", syncEvent: {...}}\n if (\n parsed.type === 'sync' &&\n parsed.syncEvent &&\n typeof parsed.syncEvent === 'object'\n ) {\n const inner = parsed.syncEvent as Record<string, unknown>;\n let type = (inner.type as string) || '';\n const m = /\\.\\d+$/.exec(type);\n if (m) type = type.slice(0, m.index);\n const innerData = (inner.data as Record<string, unknown>) || {};\n return {\n type,\n sessionID:\n typeof innerData.sessionID === 'string'\n ? innerData.sessionID\n : undefined,\n messageID:\n typeof innerData.messageID === 'string'\n ? innerData.messageID\n : undefined,\n data: inner,\n };\n }\n return null;\n } catch {\n return null;\n }\n };\n\n // register handlers per named event type\n const namedTypes = [\n 'message.user.created',\n 'message.user.updated',\n 'message.assistant.created',\n 'message.assistant.updated',\n 'message.part.updated',\n 'session.idle',\n 'message.assistant.completed',\n 'session.completed',\n 'session.awaiting',\n 'session.awaiting_input',\n 'message.assistant.awaiting',\n 'session.error',\n ];\n for (const name of namedTypes) {\n es.addEventListener(name, (e: MessageEvent) => {\n onEnvelope(parseData((e as MessageEvent).data));\n });\n }\n\n es.onerror = () => {\n if (sseCurrentSessionIdRef.current !== id) return;\n if (!sseAutoReconnectRef.current) {\n try {\n es.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n return;\n }\n try {\n es.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n\n const nextAttempt = sseReconnectAttemptRef.current + 1;\n sseReconnectAttemptRef.current = nextAttempt;\n // Backoff: 1s, 2s, 4s, 8s, … capped at 30s.\n const delayMs = Math.min(30_000, 1000 * 2 ** (nextAttempt - 1));\n\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n }\n sseReconnectTimerRef.current = window.setTimeout(() => {\n sseReconnectTimerRef.current = null;\n if (\n sseAutoReconnectRef.current &&\n sseCurrentSessionIdRef.current === id\n ) {\n connect();\n }\n }, delayMs);\n };\n\n es.onopen = () => {\n // Successful (re)connect — reset backoff.\n sseReconnectAttemptRef.current = 0;\n setClineError(null);\n setClineSuggestion(null);\n setClineErrorInfo(null);\n };\n };\n\n connect();\n },\n [appendClineMessage, markSeen, markSessionStreaming, setSessionDisplayState],\n );\n\n const closeClineSession = useCallback(() => {\n // Disable auto-reconnect first so the onerror handler short-circuits.\n sseAutoReconnectRef.current = false;\n sseCurrentSessionIdRef.current = null;\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n sseReconnectTimerRef.current = null;\n }\n if (clineEsRef.current) {\n try {\n clineEsRef.current.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n }\n setClineMessages([]);\n setSeenClineMessages(new Set());\n setClineError(null);\n setClineSuggestion(null);\n setClineErrorInfo(null);\n setActiveClineSessionId(null);\n setActiveSource('bizar');\n }, []);\n\n const loadClineSession = useCallback(\n async (id: string) => {\n // Close any previous cline session cleanly.\n sseAutoReconnectRef.current = false;\n if (clineEsRef.current) {\n try {\n clineEsRef.current.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n }\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n sseReconnectTimerRef.current = null;\n }\n\n setClineError(null);\n setClineSuggestion(null);\n setClineErrorInfo(null);\n setLoading(true);\n\n try {\n const data = await api.get<{ messages: ChatMessage[] }>(\n `/claude-sessions/${encodeURIComponent(id)}/messages`,\n );\n setClineMessages(data.messages || []);\n } catch (err) {\n const msg = (err as Error).message;\n // v5.0.0 — bug #4: surface the server's structured error\n // envelope so the UI can render a useful suggestion instead\n // of the raw upstream error string. The server returns\n // `{error, message, cause?, status?, suggestion?}` for every\n // non-2xx response (see serve-info.mjs + routes/cline-\n // session-detail.mjs).\n let info: ClineErrorInfo | null = null;\n if (err instanceof ApiError && err.data && typeof err.data === 'object') {\n const d = err.data as {\n error?: string;\n message?: string;\n cause?: string;\n status?: number;\n suggestion?: string;\n };\n // canRetry: 503/502 are transient (plugin offline /\n // upstream hiccup); the operator can fix the plugin and\n // try again. 400 is a hard bad_request — retrying with the\n // same payload won't help.\n const code = d.error || 'unknown';\n const canRetry =\n code !== 'bad_request' &&\n code !== 'too_many_subscribers' &&\n code !== 'unauthorized';\n info = {\n type: 'session_load_failed',\n code,\n message: d.message || msg,\n cause: d.cause,\n status: d.status,\n suggestion: d.suggestion ?? null,\n canRetry,\n };\n } else if (err instanceof ApiError) {\n info = {\n type: 'session_load_failed',\n code: 'unknown',\n message: msg,\n suggestion: null,\n canRetry: true,\n };\n }\n // Legacy fields — kept so the existing Chat.tsx thread-header\n // error display continues to work unchanged.\n setClineError(\n info\n ? `Cline session load failed (${info.code}): ${info.message}`\n : `Cline session load failed: ${msg}`,\n );\n setClineSuggestion(info?.suggestion ?? null);\n setClineErrorInfo(info);\n toastRef.current?.error(\n info\n ? `Cline session load failed: ${info.message}`\n : `Cline session load failed: ${msg}`,\n );\n setClineMessages([]);\n } finally {\n setLoading(false);\n }\n\n setActiveClineSessionId(id);\n setActiveSource('cline');\n markSessionStreaming(id, false);\n openSseForSession(id);\n },\n [markSessionStreaming, openSseForSession],\n );\n\n // ── Select a bizar session ──────────────────────────────────────────────────\n const selectBizarSession = useCallback(\n (sid: string) => {\n closeClineSession();\n setSessionId(sid);\n setActiveSource('bizar');\n loadChat(sid);\n // Reset unread for the session being opened.\n setSessionDisplayState(sid, { unread: 0 });\n },\n [closeClineSession, loadChat, setSessionDisplayState],\n );\n\n // ── Create session (cline primary; bizar fallback) ──────────────────────\n const onCreateSession = useCallback(async (): Promise<{\n ok: boolean;\n source?: 'bizar' | 'cline';\n id?: string;\n }> => {\n if (busy.create) return { ok: false };\n const agent = settings.defaultAgent || 'odin';\n setBusy((b) => ({ ...b, create: true }));\n try {\n // Try to create a real cline session first. This is the\n // primary create path: it gives the user a full conversation\n // roundtrip with SSE streaming, abort, etc.\n try {\n const created = await api.post<{\n id: string;\n title: string;\n agent: string;\n directory: string;\n createdAt: number;\n }>('/claude-sessions/new', { agent });\n await refreshClineSessions();\n // Open the newly created session right away so the user is\n // dropped into a fresh empty thread, not stuck on the\n // previous one.\n await loadClineSession(created.id);\n toastRef.current?.success(`Session ${created.id} created.`);\n return { ok: true, source: 'cline', id: created.id };\n } catch (clineErr) {\n // Cline plugin offline (503) — fall back to the local\n // per-project .jsonl session. Otherwise, surface the error.\n if (!(clineErr instanceof ApiError) || clineErr.status !== 503) {\n toastRef.current?.error(\n `Create failed: ${(clineErr as Error).message}`,\n );\n return { ok: false };\n }\n }\n\n // Fallback: local jsonl session.\n if (!snapshot.activeProject) {\n toastRef.current?.error(\n 'Pick a project in Overview to scope chat sessions.',\n );\n return { ok: false };\n }\n const created = await api.post<ChatSession>('/chat/sessions', {});\n setSessionId(created.id);\n setBizarMessages([]);\n setPinned(new Set());\n setSessionDisplayState(created.id, { state: 'idle', unread: 0 });\n await loadChat(created.id);\n refreshSessions().catch(() => {});\n toastRef.current?.success(`Session ${created.id} created.`);\n return { ok: true, source: 'bizar', id: created.id };\n } catch (err) {\n toastRef.current?.error(`Create failed: ${(err as Error).message}`);\n return { ok: false };\n } finally {\n setBusy((b) => ({ ...b, create: false }));\n }\n }, [\n busy.create,\n settings.defaultAgent,\n snapshot.activeProject,\n refreshClineSessions,\n loadClineSession,\n loadChat,\n refreshSessions,\n setSessionDisplayState,\n ]);\n\n // ── Send message ────────────────────────────────────────────────────────────\n const onSend = useCallback(\n async (\n message: string,\n agent: string,\n model: string,\n attachments?: string[],\n ): Promise<{ ok: boolean }> => {\n if (busy.send) return { ok: false };\n const trimmed = message.trim();\n if (!trimmed) return { ok: false };\n\n if (activeSource === 'cline' && activeClineSessionId) {\n const sid = activeClineSessionId;\n const optimisticId = `msg_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;\n const optimistic: ChatMessage = {\n id: optimisticId,\n role: 'user',\n content: trimmed,\n agent,\n ts: new Date().toISOString(),\n };\n appendClineMessage(optimistic);\n markSeen(optimisticId);\n setBusy((b) => ({ ...b, send: true }));\n try {\n await api.post<{ ok: boolean; messageId: string }>(\n `/claude-sessions/${encodeURIComponent(sid)}/send`,\n {\n message: trimmed,\n agent,\n },\n );\n markSessionStreaming(sid, true);\n return { ok: true };\n } catch (err) {\n // Remove optimistic on failure.\n setClineMessages((cur) => cur.filter((m) => m.id !== optimisticId));\n toastRef.current?.error(\n `Send failed: ${(err as Error).message}`,\n );\n return { ok: false };\n } finally {\n setBusy((b) => ({ ...b, send: false }));\n }\n }\n\n // Bizar fallback.\n const optimistic: ChatMessage = {\n role: 'user',\n content: trimmed,\n agent,\n ts: new Date().toISOString(),\n };\n setBizarMessages((cur) => [...cur, optimistic]);\n setBusy((b) => ({ ...b, send: true }));\n markSessionStreaming(sessionId, true);\n try {\n const response = await api.post<{ messages?: ChatMessage[] }>(\n '/chat',\n {\n message: trimmed,\n agent,\n model,\n attachments: attachments || [],\n },\n );\n if (response?.messages) {\n for (const msg of response.messages) {\n if (msg.role === 'assistant') {\n setBizarMessages((cur) => {\n const exists = cur.some((m) => m.ts === msg.ts);\n return exists ? cur : [...cur, msg];\n });\n }\n }\n } else {\n toastRef.current?.info(\n 'Still processing… the response will appear shortly.',\n );\n }\n return { ok: true };\n } catch (err) {\n setBizarMessages((cur) => cur.filter((m) => m !== optimistic));\n toastRef.current?.error(\n `Send failed: ${(err as Error).message}`,\n );\n return { ok: false };\n } finally {\n setBusy((b) => ({ ...b, send: false }));\n markSessionStreaming(sessionId, false);\n }\n },\n [\n activeSource,\n activeClineSessionId,\n appendClineMessage,\n markSeen,\n markSessionStreaming,\n sessionId,\n busy.send,\n ],\n );\n\n const onRegenerate = useCallback(\n async (messageId: string) => {\n if (!sessionId) {\n toastRef.current?.error('No active session to regenerate.');\n return;\n }\n try {\n await api.post('/chat/regenerate', { sessionId, messageId });\n await loadChat(sessionId);\n } catch (err) {\n toastRef.current?.error(\n `Regenerate failed: ${(err as Error).message}`,\n );\n }\n },\n [sessionId, loadChat],\n );\n\n const deleteMessage = useCallback(\n (idx: number) => {\n if (activeSource === 'cline') {\n setClineMessages((cur) => cur.filter((_, i) => i !== idx));\n } else {\n setBizarMessages((cur) => cur.filter((_, i) => i !== idx));\n }\n },\n [activeSource],\n );\n\n const togglePin = useCallback((idx: number) => {\n setPinned((cur) => {\n const next = new Set(cur);\n if (next.has(idx)) next.delete(idx);\n else next.add(idx);\n return next;\n });\n }, []);\n\n const copyMessage = useCallback((m: ChatMessage) => {\n const text = m.content || m.message || '';\n try {\n navigator.clipboard?.writeText(text).then(\n () => toastRef.current?.success('Copied.'),\n () => toastRef.current?.error('Copy failed.'),\n );\n } catch {\n toastRef.current?.error('Copy failed.');\n }\n }, []);\n\n // ── Jump-to-latest ──────────────────────────────────────────────────────────\n const handleScroll = useCallback(() => {\n const el = listRef.current;\n if (!el) return;\n const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;\n const atBottom = distFromBottom < 32;\n setStickToBottom(atBottom);\n if (atBottom) setNewMessageCount(0);\n }, []);\n\n const jumpToLatest = useCallback(() => {\n const el = listRef.current;\n if (!el) return;\n el.scrollTop = el.scrollHeight;\n setStickToBottom(true);\n setNewMessageCount(0);\n }, []);\n\n // ── Session rename / delete (rail row menu + info panel actions) ────────────\n const renameSession = useCallback(\n async (id: string, title: string): Promise<boolean> => {\n if (busy.rename) return false;\n setBusy((b) => ({ ...b, rename: true }));\n try {\n const trimmed = title.trim();\n if (!trimmed) {\n toastRef.current?.error('Title cannot be empty.');\n return false;\n }\n // Try the cline path first if this id is in the cline\n // session list, otherwise the local path. We dispatch by\n // inspecting the in-memory set (avoids a round trip).\n const isCline = clineSessions.some((s) => s.id === id);\n if (isCline) {\n await api.patch<{ id: string; title: string }>(\n `/claude-sessions/${encodeURIComponent(id)}`,\n { title: trimmed },\n );\n setClineSessions((cur) =>\n cur.map((s) => (s.id === id ? { ...s, title: trimmed } : s)),\n );\n } else {\n await api.post(\n `/chat/sessions/${encodeURIComponent(id)}/rename`,\n { title: trimmed },\n );\n setSessions((cur) =>\n cur.map((s) => (s.id === id ? { ...s, title: trimmed } : s)),\n );\n }\n toastRef.current?.success('Renamed.');\n return true;\n } catch (err) {\n toastRef.current?.error(\n `Rename failed: ${(err as Error).message}`,\n );\n return false;\n } finally {\n setBusy((b) => ({ ...b, rename: false }));\n }\n },\n [busy.rename, clineSessions],\n );\n\n const deleteSession = useCallback(\n async (id: string): Promise<boolean> => {\n if (busy.delete) return false;\n setBusy((b) => ({ ...b, delete: true }));\n try {\n const isCline = clineSessions.some((s) => s.id === id);\n if (isCline) {\n await api.del(`/claude-sessions/${encodeURIComponent(id)}`);\n setClineSessions((cur) => cur.filter((s) => s.id !== id));\n if (activeClineSessionId === id) {\n closeClineSession();\n }\n } else {\n await api.del(`/chat/sessions/${encodeURIComponent(id)}`);\n setSessions((cur) => cur.filter((s) => s.id !== id));\n if (sessionId === id) {\n setSessionId('');\n setBizarMessages([]);\n }\n }\n setSessionStates((cur) => {\n const next = { ...cur };\n delete next[id];\n return next;\n });\n toastRef.current?.success('Session deleted.');\n return true;\n } catch (err) {\n toastRef.current?.error(\n `Delete failed: ${(err as Error).message}`,\n );\n return false;\n } finally {\n setBusy((b) => ({ ...b, delete: false }));\n }\n },\n [\n busy.delete,\n clineSessions,\n activeClineSessionId,\n closeClineSession,\n sessionId,\n ],\n );\n\n // ── Auto-scroll on new messages ─────────────────────────────────────────────\n useEffect(() => {\n if (listRef.current) {\n if (stickToBottom) {\n listRef.current.scrollTop = listRef.current.scrollHeight;\n } else {\n setNewMessageCount((n) => n + 1);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [bizarMessages, clineMessages]);\n\n // ── Cleanup SSE on unmount ──────────────────────────────────────────────────\n useEffect(() => {\n return () => {\n sseAutoReconnectRef.current = false;\n sseCurrentSessionIdRef.current = null;\n if (sseReconnectTimerRef.current !== null) {\n clearTimeout(sseReconnectTimerRef.current);\n sseReconnectTimerRef.current = null;\n }\n if (clineEsRef.current) {\n try {\n clineEsRef.current.close();\n } catch {\n /* noop */\n }\n clineEsRef.current = null;\n }\n };\n }, []);\n\n // ── Initial load ────────────────────────────────────────────────────────────\n useEffect(() => {\n if (initialTaskId) {\n loadTaskChat(initialTaskId);\n } else {\n loadChat();\n }\n refreshClineSessions();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ── Listen for bizar:setChatTask events ────────────────────────────────────\n useEffect(() => {\n const handler = (e: Event) => {\n const taskId = (e as CustomEvent<{ taskId: string }>).detail?.taskId;\n if (taskId) loadTaskChat(taskId);\n };\n window.addEventListener('bizar:setChatTask', handler);\n return () => window.removeEventListener('bizar:setChatTask', handler);\n }, [loadTaskChat]);\n\n // ── WebSocket for live bizar updates ────────────────────────────────────────\n useEffect(() => {\n let closed = false;\n import('../../lib/ws').then(({ Ws }) => {\n if (closed) return;\n const ws = new Ws();\n ws.on((msg: { type: string; message?: ChatMessage }) => {\n if (msg.type !== 'chat:message') return;\n const next = msg.message;\n if (!next) return;\n setBizarMessages((cur) => {\n const exists = cur.some((m) => m.ts === next.ts);\n return exists ? cur : [...cur, next];\n });\n });\n });\n return () => {\n closed = true;\n };\n }, []);\n\n return {\n // ── State ────────────────────────────────────────────────────────────────\n messages:\n activeSource === 'cline' ? clineMessages : bizarMessages,\n bizarMessages,\n clineMessages,\n sessions,\n clineSessions,\n sessionId,\n setSessionId,\n loading,\n sending: busy.send,\n pinned,\n listRef,\n // ── Cline state ──────────────────────────────────────────────────────\n activeSource,\n activeClineSessionId,\n clineError,\n clineSuggestion,\n /**\n * v5.0.0 — bug #4: structured error info consumed by\n * `ChatInfoPanel`. `null` when no error is active.\n */\n clineErrorInfo,\n /**\n * v5.0.0 — bug #4: retry helper exposed for `ChatInfoPanel`'s\n * Retry button. Calls `loadClineSession` on the currently\n * active cline session (or no-op if none is active).\n */\n retryClineSession: () => {\n if (activeClineSessionId) void loadClineSession(activeClineSessionId);\n },\n // ── v3.22 — per-session display state ──────────────────────────────────\n sessionStates,\n busy,\n getSessionDisplay: (s: ChatSession) => ({\n ...s,\n state: sessionStates[s.id]?.state ?? 'idle',\n unread: sessionStates[s.id]?.unread ?? 0,\n pinned: sessionStates[s.id]?.pinned ?? false,\n tree: sessionStates[s.id]?.tree,\n }),\n // ── Jump-to-latest ──────────────────────────────────────────────────────\n stickToBottom,\n newMessageCount,\n handleScroll,\n jumpToLatest,\n // ── Toast integration ──────────────────────────────────────────────────\n setToast,\n // ── Actions ─────────────────────────────────────────────────────────────\n loadChat,\n loadTaskChat,\n refreshSessions,\n refreshClineSessions,\n loadClineSession,\n closeClineSession,\n selectBizarSession,\n onCreateSession,\n onSend,\n onRegenerate,\n deleteMessage,\n togglePin,\n copyMessage,\n renameSession,\n deleteSession,\n seenClineMessages,\n };\n}\n","// src/components/chat/useSlashCommands.ts — filter slash-command suggestions from mods.\n\nimport { useMemo, useState } from 'react';\nimport type { Mod, Snapshot } from '../../lib/types';\n\nexport type SlashCommand = { cmd: string; desc: string; mod?: string };\n\nconst BUILTIN_COMMANDS: SlashCommand[] = [\n { cmd: '/visual-plan [on|off|status]', desc: 'Toggle or view visual plan mode' },\n { cmd: '/plan new <slug> [template]', desc: 'Create a new plan' },\n { cmd: '/plan list', desc: 'List all plans' },\n { cmd: '/plan open <slug>', desc: 'Open a plan in the viewer' },\n { cmd: '/plan status <slug> <status>', desc: 'Set plan status' },\n { cmd: '/plan get <slug>', desc: 'Fetch plan canvas' },\n { cmd: '/plan add <slug> --title T --type kind', desc: 'Add element to a plan' },\n { cmd: '/plan update <slug> <id>', desc: 'Update a plan element' },\n { cmd: '/plan delete <slug> <id>', desc: 'Delete a plan element' },\n { cmd: '/plan comment <slug> [id] \"text\"', desc: 'Add a comment' },\n { cmd: '/plan comments <slug> [id]', desc: 'Read plan comments' },\n { cmd: '/bizar', desc: 'Launch Bizar dashboard' },\n { cmd: '/bizar <args>', desc: 'Route via Bizar menu' },\n { cmd: '/audit', desc: 'Run security audit' },\n { cmd: '/explain <q>', desc: 'Read-only code Q&A' },\n { cmd: '/init', desc: 'Initialize .bizar/ in this project' },\n { cmd: '/learn', desc: 'Extract patterns from session' },\n { cmd: '/pr-review', desc: 'PR review' },\n { cmd: '/help | /commands', desc: 'Show all Bizar commands' },\n];\n\nexport function useSlashCommands(snapshot: Snapshot) {\n const [query, setQuery] = useState('');\n\n const allCommands = useMemo<SlashCommand[]>(() => [\n ...BUILTIN_COMMANDS,\n ...(snapshot.mods || []).flatMap((m: Mod) =>\n m.entry?.command ? [{ cmd: `/${m.id}`, desc: m.description || m.name, mod: m.id }] : [],\n ),\n ], [snapshot.mods]);\n\n const suggestions = useMemo(() => {\n if (!query.startsWith('/') || query.includes(' ')) return [];\n const q = query.toLowerCase();\n return allCommands.filter((c) => c.cmd.toLowerCase().startsWith(q)).slice(0, 6);\n }, [query, allCommands]);\n\n return { allCommands, suggestions, setQuery };\n}"],"file":"assets/useSlashCommands-Bd7_FA6U.js"}