@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,1799 +0,0 @@
1
- /**
2
- * commands.ts
3
- *
4
- * v0.5.0 — Pure slash-command parser.
5
- *
6
- * The plugin's `chat.message` hook calls `parseSlashCommand(text, ctx)`
7
- * on every user message. If the message starts with `/`, this function
8
- * classifies it as a known command or returns `null` (so the message
9
- * passes through to the LLM normally).
10
- *
11
- * Design constraints:
12
- * - Pure function. No I/O, no environment reads, no Date.now() calls
13
- * inside the parser itself.
14
- * - The function is the **only** place slash commands are parsed.
15
- * The hook gathers context (current settings, available plan slugs)
16
- * and feeds it to the parser, then dispatches the returned
17
- * `sideEffect` via `executeSideEffect()` in commands-impl.ts.
18
- * - Returns `null` for non-slash messages so the LLM still sees them.
19
- *
20
- * Output shape (per slash command):
21
- * `{ handled: true, response, settingsPatch?, sideEffect? }`
22
- *
23
- * - `response`: text surfaced to the user / LLM.
24
- * - `settingsPatch`: optional partial `PlanSettings` to apply.
25
- * - `sideEffect`: optional descriptor for the hook to execute. The
26
- * `tool_invocation` kind routes through `executeToolInvocation`,
27
- * which builds a synthetic `ToolContext` and invokes the
28
- * registered tool directly. The other kinds (`create_plan`,
29
- * `list_plans`, `open_plan_url`) are handled by
30
- * `executeSideEffect` in commands-impl.ts.
31
- *
32
- * The parser is unit-tested directly (tests/commands.test.ts).
33
- * Side-effect execution is tested in tests/commands-impl.test.ts.
34
- */
35
-
36
- import type { DefaultTemplate, PlanSettings } from "./settings.js";
37
- import { KNOWN_TEMPLATES } from "./settings.js";
38
-
39
- // --- Public types --------------------------------------------------------
40
-
41
- /**
42
- * What the parser asks the chat.message hook to do on the host side.
43
- *
44
- * v0.5.0 — extended with the `tool_invocation` kind (per C3). Subcommand
45
- * parsers translate the user's typed subcommand into a `tool_invocation`
46
- * side-effect that calls a real `bizar_*` tool with a synthetic
47
- * `ToolContext`. The chat hook does not need to know about the tool
48
- * layer; it just hands the descriptor to `executeToolInvocation`.
49
- */
50
- export type SideEffect =
51
- | {
52
- kind: "create_plan";
53
- slug: string;
54
- template: DefaultTemplate | null;
55
- }
56
- | {
57
- kind: "open_plan_url";
58
- slug: string;
59
- }
60
- | {
61
- kind: "list_plans";
62
- }
63
- | {
64
- kind: "launch_dashboard";
65
- defaultPort: number;
66
- }
67
- | {
68
- kind: "tool_invocation";
69
- toolName: string;
70
- args: unknown;
71
- };
72
-
73
- /** Dialog component types that can be rendered in the dashboard. */
74
- export type DialogComponent =
75
- | "visual-plan"
76
- | "plan-create"
77
- | "plan-list"
78
- | "help"
79
- | "audit"
80
- | "generic";
81
-
82
- export interface DialogDescriptor {
83
- id: string;
84
- title: string;
85
- command: string;
86
- component: DialogComponent;
87
- data?: Record<string, unknown>;
88
- }
89
-
90
- function generateId(): string {
91
- return `dlg_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
92
- }
93
-
94
- export interface SlashCommandResult {
95
- handled: true;
96
- /** Text shown to the user / LLM. */
97
- response: string;
98
- /** Optional settings mutation. */
99
- settingsPatch?: Partial<PlanSettings>;
100
- /** Optional side-effect to perform (file I/O lives in the hook, not here). */
101
- sideEffect?: SideEffect;
102
- /** Optional dialog to open in the dashboard instead of showing text. */
103
- dialog?: DialogDescriptor;
104
- }
105
-
106
- /**
107
- * Context passed to the parser. The parser does NOT call `getCurrentSettings`
108
- * itself — instead the caller injects the live values. This keeps the parser
109
- * pure and lets tests stub the context.
110
- */
111
- export interface ParseContext {
112
- /** Current plan settings (already loaded by the caller). */
113
- currentSettings: PlanSettings;
114
- /** Slugs of plans that exist in the worktree's plans/ directory. */
115
- availablePlanSlugs?: readonly string[];
116
- /**
117
- * The default port for `/plan open <slug>` URLs. The viewer runs in-process
118
- * on 127.0.0.1:4321-4330. v0.5.0 defers actually starting the server; the
119
- * URL is informational only.
120
- */
121
- defaultPort?: number;
122
- }
123
-
124
- // --- Internals -----------------------------------------------------------
125
-
126
- /** Same slug rule used by `cli/plan.mjs`. */
127
- const SLUG_REGEX = /^[a-z0-9][a-z0-9-]{0,63}$/;
128
-
129
- function isValidSlug(s: string): boolean {
130
- return SLUG_REGEX.test(s);
131
- }
132
-
133
- function isKnownTemplate(t: string): t is DefaultTemplate {
134
- return (KNOWN_TEMPLATES as readonly string[]).includes(t);
135
- }
136
-
137
- function titleCase(slug: string): string {
138
- return slug
139
- .split(/[-_]/)
140
- .map((w) => (w.length === 0 ? w : w[0]!.toUpperCase() + w.slice(1)))
141
- .join(" ");
142
- }
143
-
144
- /** Valid status values for `/plan status`. Mirrors plan-action.ts. */
145
- const PLAN_STATUSES = [
146
- "draft",
147
- "approved",
148
- "rejected",
149
- "in-progress",
150
- "done",
151
- ] as const;
152
- type PlanStatus = (typeof PLAN_STATUSES)[number];
153
-
154
- function isPlanStatus(s: string): s is PlanStatus {
155
- return (PLAN_STATUSES as readonly string[]).includes(s);
156
- }
157
-
158
- // --- Tokenization --------------------------------------------------------
159
-
160
- /**
161
- * Tokenize a subcommand argument string into argv-like tokens, with
162
- * double-quoted strings preserved as a single token. This is just
163
- * enough for our flag parser; it does not support escape sequences
164
- * beyond `\"`.
165
- *
166
- * Examples:
167
- * `add foo --title "Hello world" --type task` →
168
- * ["add", "foo", "--title", "Hello world", "--type", "task"]
169
- * `comment foo "Make this bigger"` →
170
- * ["comment", "foo", "Make this bigger"]
171
- */
172
- function tokenize(input: string): string[] {
173
- const tokens: string[] = [];
174
- let cur = "";
175
- let inQuotes = false;
176
- for (let i = 0; i < input.length; i++) {
177
- const ch = input[i]!;
178
- if (inQuotes) {
179
- if (ch === "\\" && i + 1 < input.length && input[i + 1] === '"') {
180
- cur += '"';
181
- i++;
182
- } else if (ch === '"') {
183
- inQuotes = false;
184
- } else {
185
- cur += ch;
186
- }
187
- continue;
188
- }
189
- if (ch === '"') {
190
- inQuotes = true;
191
- continue;
192
- }
193
- if (ch === " " || ch === "\t") {
194
- if (cur !== "") {
195
- tokens.push(cur);
196
- cur = "";
197
- }
198
- continue;
199
- }
200
- cur += ch;
201
- }
202
- if (cur !== "") tokens.push(cur);
203
- return tokens;
204
- }
205
-
206
- interface ParsedFlags {
207
- positional: string[];
208
- flags: Record<string, string>;
209
- }
210
-
211
- /**
212
- * Parse a flag list of the form `--key value` (or `--key=value`).
213
- * Positional arguments (no leading `--`) come back in order. Flags
214
- * without a value (e.g. `--verbose`) are stored as `""`.
215
- */
216
- function parseFlags(tokens: string[]): ParsedFlags {
217
- const positional: string[] = [];
218
- const flags: Record<string, string> = {};
219
- for (let i = 0; i < tokens.length; i++) {
220
- const t = tokens[i]!;
221
- if (t.startsWith("--")) {
222
- const eq = t.indexOf("=");
223
- if (eq !== -1) {
224
- flags[t.slice(2, eq)] = t.slice(eq + 1);
225
- continue;
226
- }
227
- const key = t.slice(2);
228
- const next = tokens[i + 1];
229
- if (next !== undefined && !next.startsWith("--")) {
230
- flags[key] = next;
231
- i++;
232
- } else {
233
- flags[key] = "";
234
- }
235
- } else {
236
- positional.push(t);
237
- }
238
- }
239
- return { positional, flags };
240
- }
241
-
242
- // --- The main parser -----------------------------------------------------
243
-
244
- /**
245
- * Parse a user message and decide whether it's a slash command.
246
- *
247
- * Returns:
248
- * - `null` — not a slash command; pass through.
249
- * - `SlashCommandResult` — slash command was recognized.
250
- *
251
- * Recognized commands:
252
- * - `/visual-plan on|off|status` — settings patch only
253
- * - `/plan new <slug> [template]` — create_plan
254
- * - `/plan list` — list_plans
255
- * - `/plan open <slug>` — open_plan_url
256
- * - `/plan get <slug>` — tool_invocation: bizar_plan_action (get_canvas)
257
- * - `/plan add <slug> [--title … --type … --x N --y N …]`
258
- * — tool_invocation: bizar_plan_action (add_element)
259
- * - `/plan update <slug> <id> [--x N --y N --title T --content C]`
260
- * — tool_invocation: bizar_plan_action (update_element)
261
- * - `/plan delete <slug> <id>` — tool_invocation: bizar_plan_action (delete_element)
262
- * - `/plan comment <slug> [id] "text"`
263
- * — tool_invocation: bizar_plan_action (add_comment)
264
- * - `/plan status <slug> <status>` — tool_invocation: bizar_plan_action (set_status)
265
- * - `/plan comments <slug> [id]` — tool_invocation: bizar_get_plan_comments
266
- * - `/plan wait <slug> [--timeout N]`
267
- * — deferred (returns response; no tool call)
268
- * - `/kb` — open the Bizar Memory vault in Obsidian
269
- * - `/help` / `/commands` — help text
270
- */
271
- export function parseSlashCommand(
272
- text: string,
273
- ctx: ParseContext,
274
- ): SlashCommandResult | null {
275
- if (typeof text !== "string") return null;
276
- const trimmed = text.trim();
277
- if (trimmed === "") return null;
278
- if (!trimmed.startsWith("/")) return null;
279
-
280
- // Just a bare slash — not a command
281
- if (trimmed === "/") return null;
282
-
283
- // Match `/<command>` optionally followed by whitespace and args
284
- const match = /^\/([a-zA-Z][\w-]*)(?:\s+(.*))?$/.exec(trimmed);
285
- if (match === null) return null;
286
-
287
- const command = match[1]!.toLowerCase();
288
- const rest = (match[2] ?? "").trim();
289
-
290
- switch (command) {
291
- case "visual-plan":
292
- return handleVisualPlan(rest, ctx);
293
- case "plan":
294
- return handlePlan(rest, ctx);
295
- case "bizar":
296
- return handleBizar(rest, ctx);
297
- case "kb":
298
- return handleKb(rest, ctx);
299
- case "odin":
300
- return handleOdin(rest, ctx);
301
- case "loop":
302
- return handleLoop(rest, ctx);
303
- case "kanban":
304
- return handleKanban(rest, ctx);
305
- case "review":
306
- return handleReview(rest, ctx);
307
- case "handoff":
308
- return handleHandoff(rest, ctx);
309
- case "grill":
310
- return handleGrill(rest, ctx);
311
- case "glyph":
312
- return handleGlyph(rest, ctx);
313
- case "decision":
314
- return handleDecision(rest, ctx);
315
- case "issue":
316
- return handleIssue(rest, ctx);
317
- case "digest":
318
- return handleDigest(rest, ctx);
319
- case "usage":
320
- return handleUsage(rest, ctx);
321
- case "memory":
322
- return handleMemory(rest, ctx);
323
- case "spawn":
324
- return handleSpawn(rest, ctx);
325
- case "team":
326
- return handleTeam(rest, ctx);
327
- case "audit":
328
- return handleAudit(rest, ctx);
329
- case "deploy":
330
- return handleDeploy(rest, ctx);
331
- case "status":
332
- return handleStatus(rest, ctx);
333
- case "lightrag":
334
- return handleLightrag(rest, ctx);
335
- case "headroom":
336
- return handleHeadroom(rest, ctx);
337
- case "minimax":
338
- return handleMiniMax(rest, ctx);
339
- case "service":
340
- return handleService(rest, ctx);
341
- case "dash":
342
- return handleDash(rest, ctx);
343
- case "mod":
344
- return handleMod(rest, ctx);
345
- case "init":
346
- return handleInit(rest, ctx);
347
- case "dev":
348
- return handleDev(rest, ctx);
349
- case "test":
350
- return handleTest(rest, ctx);
351
- case "agent-browser":
352
- return handleAgentBrowser(rest, ctx);
353
- case "plow-through":
354
- return handlePlowThrough(rest, ctx);
355
- case "tailscale":
356
- return handleTailscale(rest, ctx);
357
- case "providers":
358
- return handleProviders(rest, ctx);
359
- case "clip":
360
- return handleClip(rest, ctx);
361
- case "ocr":
362
- return handleOcr(rest, ctx);
363
- case "workspace":
364
- return handleWorkspace(rest, ctx);
365
- case "voice":
366
- return handleVoice(rest, ctx);
367
- case "help":
368
- case "commands":
369
- return helpResult();
370
- default:
371
- return {
372
- handled: true,
373
- response: `Unknown command: /${command}. Try /help for a list of available commands.`,
374
- };
375
- }
376
- }
377
-
378
- // --- Command handlers ----------------------------------------------------
379
-
380
- function handleVisualPlan(arg: string, ctx: ParseContext): SlashCommandResult {
381
- const lc = arg.toLowerCase();
382
- const currentEnabled = ctx.currentSettings.visualPlanEnabled;
383
-
384
- if (lc === "") {
385
- // No argument — return current state as a dialog
386
- return {
387
- handled: true,
388
- response: `Visual plan mode is currently ${currentEnabled ? "on" : "off"}.`,
389
- dialog: {
390
- id: generateId(),
391
- title: "Visual Plan",
392
- command: "/visual-plan",
393
- component: "visual-plan",
394
- data: {
395
- enabled: currentEnabled,
396
- previousEnabled: currentEnabled,
397
- defaultTemplate: ctx.currentSettings.defaultTemplate,
398
- lastUsedSlug: ctx.currentSettings.lastUsedSlug ?? null,
399
- mode: "status",
400
- },
401
- },
402
- };
403
- }
404
-
405
- if (lc === "on" || lc === "true" || lc === "1" || lc === "enable") {
406
- return {
407
- handled: true,
408
- response: `Visual plan mode is now on.`,
409
- settingsPatch: { visualPlanEnabled: true },
410
- dialog: {
411
- id: generateId(),
412
- title: "Visual Plan",
413
- command: "/visual-plan on",
414
- component: "visual-plan",
415
- data: {
416
- enabled: true,
417
- previousEnabled: currentEnabled,
418
- mode: "toggle",
419
- },
420
- },
421
- };
422
- }
423
-
424
- if (lc === "off" || lc === "false" || lc === "0" || lc === "disable") {
425
- return {
426
- handled: true,
427
- response: `Visual plan mode is now off.`,
428
- settingsPatch: { visualPlanEnabled: false },
429
- dialog: {
430
- id: generateId(),
431
- title: "Visual Plan",
432
- command: "/visual-plan off",
433
- component: "visual-plan",
434
- data: {
435
- enabled: false,
436
- previousEnabled: currentEnabled,
437
- mode: "toggle",
438
- },
439
- },
440
- };
441
- }
442
-
443
- if (lc === "status" || lc === "state" || lc === "?") {
444
- return {
445
- handled: true,
446
- response: `Visual plan mode is currently ${currentEnabled ? "on" : "off"}.`,
447
- dialog: {
448
- id: generateId(),
449
- title: "Visual Plan",
450
- command: "/visual-plan status",
451
- component: "visual-plan",
452
- data: {
453
- enabled: currentEnabled,
454
- previousEnabled: currentEnabled,
455
- mode: "status",
456
- },
457
- },
458
- };
459
- }
460
-
461
- return {
462
- handled: true,
463
- response: `Unknown argument to /visual-plan: "${arg}". Use "on" or "off".`,
464
- };
465
- }
466
-
467
- function handlePlan(arg: string, ctx: ParseContext): SlashCommandResult {
468
- if (arg === "") {
469
- return helpPlan();
470
- }
471
-
472
- // Split subcommand from its args
473
- const tokens = tokenize(arg);
474
- const sub = (tokens[0] ?? "").toLowerCase();
475
- const subTokens = tokens.slice(1);
476
-
477
- switch (sub) {
478
- case "new":
479
- return handlePlanNew(subTokens, ctx);
480
- case "list":
481
- case "ls":
482
- return handlePlanList(ctx);
483
- case "open":
484
- return handlePlanOpen(subTokens, ctx);
485
- case "get":
486
- return handlePlanGet(subTokens);
487
- case "add":
488
- return handlePlanAdd(subTokens);
489
- case "update":
490
- return handlePlanUpdate(subTokens);
491
- case "delete":
492
- case "del":
493
- case "rm":
494
- return handlePlanDelete(subTokens);
495
- case "comment":
496
- case "comments":
497
- // `/plan comment` is the add-comment verb; `/plan comments` is the
498
- // list-comments verb. Disambiguate by looking at the first token.
499
- if (sub === "comments") return handlePlanComments(subTokens);
500
- return handlePlanComment(subTokens);
501
- case "status":
502
- return handlePlanStatus(subTokens);
503
- case "comments-list":
504
- return handlePlanComments(subTokens);
505
- case "wait":
506
- return handlePlanWait(subTokens);
507
- default:
508
- return {
509
- handled: true,
510
- response: `Unknown /plan subcommand: "${sub}". Try /plan for usage.`,
511
- };
512
- }
513
- }
514
-
515
- function helpPlan(): SlashCommandResult {
516
- return {
517
- handled: true,
518
- response: "Plan commands: /plan new <slug> [template] | /plan list | /plan open <slug> | /plan get <slug> | /plan add <slug> | /plan update <slug> <id> | /plan delete <slug> <id> | /plan comment <slug> [id] \"text\" | /plan comments <slug> [id] | /plan status <slug> <status> | /plan wait <slug> [--timeout N]. Run /plan <subcommand> for details.",
519
- dialog: {
520
- id: generateId(),
521
- title: "Plan Commands",
522
- command: "/plan",
523
- component: "help",
524
- data: {
525
- commands: [
526
- { cmd: "/plan new <slug> [template]", desc: "Create a new plan" },
527
- { cmd: "/plan list", desc: "List all plans in the worktree" },
528
- { cmd: "/plan open <slug>", desc: "Open a plan in the viewer" },
529
- { cmd: "/plan get <slug>", desc: "Fetch the full canvas" },
530
- { cmd: "/plan add <slug> --title T --type kind", desc: "Add an element to a plan" },
531
- { cmd: "/plan update <slug> <id> [--x N --y N …]", desc: "Patch an existing element" },
532
- { cmd: "/plan delete <slug> <id>", desc: "Remove an element" },
533
- { cmd: "/plan comment <slug> [id] \"text\"", desc: "Add a comment" },
534
- { cmd: "/plan comments <slug> [id]", desc: "Read comments on a plan" },
535
- { cmd: "/plan status <slug> <status>", desc: "Set the plan's status" },
536
- { cmd: "/plan wait <slug> [--timeout N]", desc: "Wait for feedback (deferred)" },
537
- ],
538
- templates: KNOWN_TEMPLATES,
539
- statuses: PLAN_STATUSES,
540
- },
541
- },
542
- };
543
- }
544
-
545
- // --- /plan new ------------------------------------------------------------
546
-
547
- function handlePlanNew(args: string[], ctx: ParseContext): SlashCommandResult {
548
- if (args.length === 0 || args[0] === "") {
549
- return {
550
- handled: true,
551
- response: "Usage: /plan new <slug> [template]. Available templates: " + KNOWN_TEMPLATES.join(", ") + ".",
552
- dialog: {
553
- id: generateId(),
554
- title: "Create New Plan",
555
- command: "/plan new",
556
- component: "plan-create",
557
- data: {
558
- templates: [...KNOWN_TEMPLATES],
559
- defaultTemplate: ctx.currentSettings.defaultTemplate,
560
- suggestedSlug: "",
561
- },
562
- },
563
- };
564
- }
565
-
566
- const slug = args[0]!;
567
- if (!isValidSlug(slug)) {
568
- return {
569
- handled: true,
570
- response:
571
- `Invalid slug "${slug}". Slug must be lowercase, may contain hyphens, ` +
572
- `must start with an alphanumeric character, and be 1–64 characters.`,
573
- };
574
- }
575
-
576
- // Optional second arg is the template name
577
- let template: DefaultTemplate | null = null;
578
- if (args.length >= 2 && args[1] !== "") {
579
- const candidate = args[1]!.toLowerCase();
580
- if (!isKnownTemplate(candidate)) {
581
- return {
582
- handled: true,
583
- response:
584
- `Unknown template "${args[1]}". Available: ${KNOWN_TEMPLATES.join(", ")}.`,
585
- };
586
- }
587
- template = candidate;
588
- }
589
-
590
- const resolvedTemplate = template ?? ctx.currentSettings.defaultTemplate;
591
-
592
- return {
593
- handled: true,
594
- response: `Created plan "${titleCase(slug)}" with the "${resolvedTemplate}" template. Use /plan open ${slug} to open it.`,
595
- sideEffect: {
596
- kind: "create_plan",
597
- slug,
598
- template,
599
- },
600
- settingsPatch: { lastUsedSlug: slug },
601
- dialog: {
602
- id: generateId(),
603
- title: "Plan Created",
604
- command: `/plan new ${slug}`,
605
- component: "generic",
606
- data: {
607
- message: `Plan "${titleCase(slug)}" created with the "${resolvedTemplate}" template.`,
608
- detail: `Use /plan open ${slug} to open it.`,
609
- },
610
- },
611
- };
612
- }
613
-
614
- // --- /plan list ----------------------------------------------------------
615
-
616
- function handlePlanList(ctx: ParseContext): SlashCommandResult {
617
- const slugs = ctx.availablePlanSlugs ?? [];
618
- if (slugs.length === 0) {
619
- return {
620
- handled: true,
621
- response: "No plans found in the current worktree. Use /plan new <slug> to create one.",
622
- sideEffect: { kind: "list_plans" },
623
- dialog: {
624
- id: generateId(),
625
- title: "Plans",
626
- command: "/plan list",
627
- component: "plan-list",
628
- data: {
629
- plans: slugs,
630
- count: slugs.length,
631
- },
632
- },
633
- };
634
- }
635
- return {
636
- handled: true,
637
- response: `Found ${slugs.length} plan(s) (${slugs.length}): ${slugs.join(", ")}.`,
638
- sideEffect: { kind: "list_plans" },
639
- dialog: {
640
- id: generateId(),
641
- title: "Plans",
642
- command: "/plan list",
643
- component: "plan-list",
644
- data: {
645
- plans: slugs,
646
- count: slugs.length,
647
- },
648
- },
649
- };
650
- }
651
-
652
- // --- /plan open ----------------------------------------------------------
653
-
654
- function handlePlanOpen(args: string[], ctx: ParseContext): SlashCommandResult {
655
- if (args.length === 0 || args[0] === "") {
656
- return {
657
- handled: true,
658
- response: "Usage: /plan open <slug>",
659
- };
660
- }
661
-
662
- const slug = args[0]!;
663
- if (!isValidSlug(slug)) {
664
- return {
665
- handled: true,
666
- response: `Invalid slug "${slug}". Slug must match ^[a-z0-9][a-z0-9-]{0,63}$.`,
667
- };
668
- }
669
-
670
- const port = ctx.defaultPort ?? 4321;
671
- const url = `http://localhost:${port}/${slug}/`;
672
-
673
- return {
674
- handled: true,
675
- response: `Opening plan "${slug}" at ${url}.`,
676
- settingsPatch: { lastUsedSlug: slug },
677
- sideEffect: {
678
- kind: "open_plan_url",
679
- slug,
680
- },
681
- dialog: {
682
- id: generateId(),
683
- title: "Opening Plan",
684
- command: `/plan open ${slug}`,
685
- component: "generic",
686
- data: {
687
- message: `Opening plan: ${slug}`,
688
- url,
689
- },
690
- },
691
- };
692
- }
693
-
694
- // --- /plan get -----------------------------------------------------------
695
-
696
- function handlePlanGet(args: string[]): SlashCommandResult {
697
- if (args.length === 0 || args[0] === "") {
698
- return { handled: true, response: "Usage: /plan get <slug>" };
699
- }
700
- const slug = args[0]!;
701
- if (!isValidSlug(slug)) {
702
- return {
703
- handled: true,
704
- response: `Invalid slug "${slug}". Slug must match ^[a-z0-9][a-z0-9-]{0,63}$.`,
705
- };
706
- }
707
- return {
708
- handled: true,
709
- response: `Fetching canvas for plan "${slug}"…`,
710
- sideEffect: {
711
- kind: "tool_invocation",
712
- toolName: "bizar_plan_action",
713
- args: { action: "get_canvas", planSlug: slug },
714
- },
715
- settingsPatch: { lastUsedSlug: slug },
716
- dialog: {
717
- id: generateId(),
718
- title: "Plan Canvas",
719
- command: `/plan get ${slug}`,
720
- component: "generic",
721
- data: {
722
- message: `Fetching canvas for plan "${slug}"…`,
723
- },
724
- },
725
- };
726
- }
727
-
728
- // --- /plan add -----------------------------------------------------------
729
-
730
- const ELEMENT_FLAG_KEYS = [
731
- "title",
732
- "content",
733
- "type",
734
- "id",
735
- ] as const;
736
- type ElementFlagKey = (typeof ELEMENT_FLAG_KEYS)[number];
737
-
738
- const ELEMENT_NUMERIC_FLAG_KEYS = ["x", "y", "width", "height"] as const;
739
- type ElementNumericFlagKey = (typeof ELEMENT_NUMERIC_FLAG_KEYS)[number];
740
-
741
- function readElementFlags(flags: Record<string, string>): Record<string, unknown> {
742
- const out: Record<string, unknown> = {};
743
- for (const k of ELEMENT_FLAG_KEYS) {
744
- const v = flags[k];
745
- if (v !== undefined && v !== "") out[k] = v;
746
- }
747
- for (const k of ELEMENT_NUMERIC_FLAG_KEYS) {
748
- const v = flags[k];
749
- if (v !== undefined && v !== "") {
750
- const n = Number(v);
751
- if (Number.isFinite(n)) out[k] = n;
752
- }
753
- }
754
- return out;
755
- }
756
-
757
- function handlePlanAdd(args: string[]): SlashCommandResult {
758
- if (args.length === 0 || args[0] === "") {
759
- return {
760
- handled: true,
761
- response: 'Usage: /plan add <slug> --title "Title" --type task [--x N --y N --width N --height N --content "…"]',
762
- };
763
- }
764
- const slug = args[0]!;
765
- if (!isValidSlug(slug)) {
766
- return {
767
- handled: true,
768
- response: `Invalid slug "${slug}". Slug must match ^[a-z0-9][a-z0-9-]{0,63}$.`,
769
- };
770
- }
771
- const { flags } = parseFlags(args.slice(1));
772
- const element = readElementFlags(flags);
773
- if (Object.keys(element).length === 0) {
774
- return {
775
- handled: true,
776
- response:
777
- `Usage: /plan add ${slug} --title "Title" --type task [--x N --y N --width N --height N --content "…"]\n` +
778
- `At least one of --title / --type / --content / --x / --y is required.`,
779
- };
780
- }
781
- return {
782
- handled: true,
783
- response: "",
784
- sideEffect: {
785
- kind: "tool_invocation",
786
- toolName: "bizar_plan_action",
787
- args: { action: "add_element", planSlug: slug, element },
788
- },
789
- settingsPatch: { lastUsedSlug: slug },
790
- dialog: {
791
- id: generateId(),
792
- title: "Element Added",
793
- command: `/plan add ${slug}`,
794
- component: "generic",
795
- data: {
796
- message: `Adding element to plan "${slug}"…`,
797
- },
798
- },
799
- };
800
- }
801
-
802
- // --- /plan update --------------------------------------------------------
803
-
804
- function handlePlanUpdate(args: string[]): SlashCommandResult {
805
- if (args.length < 2 || args[0] === "" || args[1] === "") {
806
- return {
807
- handled: true,
808
- response:
809
- 'Usage: /plan update <slug> <elementId> [--x N --y N --title T --content C --width N --height N --type kind]',
810
- };
811
- }
812
- const slug = args[0]!;
813
- const elementId = args[1]!;
814
- if (!isValidSlug(slug)) {
815
- return {
816
- handled: true,
817
- response: `Invalid slug "${slug}". Slug must match ^[a-z0-9][a-z0-9-]{0,63}$.`,
818
- };
819
- }
820
- if (elementId === "") {
821
- return {
822
- handled: true,
823
- response: "Element id cannot be empty.",
824
- };
825
- }
826
- const { flags } = parseFlags(args.slice(2));
827
- const element = readElementFlags(flags);
828
- if (Object.keys(element).length === 0) {
829
- return {
830
- handled: true,
831
- response:
832
- `Usage: /plan update ${slug} ${elementId} [--x N --y N --title T --content C --width N --height N --type kind]\n` +
833
- `At least one update flag is required.`,
834
- };
835
- }
836
- return {
837
- handled: true,
838
- response: "",
839
- sideEffect: {
840
- kind: "tool_invocation",
841
- toolName: "bizar_plan_action",
842
- args: { action: "update_element", planSlug: slug, elementId, element },
843
- },
844
- settingsPatch: { lastUsedSlug: slug },
845
- dialog: {
846
- id: generateId(),
847
- title: "Element Updated",
848
- command: `/plan update ${slug}`,
849
- component: "generic",
850
- data: {
851
- message: `Updating element ${elementId} in plan "${slug}"…`,
852
- },
853
- },
854
- };
855
- }
856
-
857
- // --- /plan delete --------------------------------------------------------
858
-
859
- function handlePlanDelete(args: string[]): SlashCommandResult {
860
- if (args.length < 2 || args[0] === "" || args[1] === "") {
861
- return {
862
- handled: true,
863
- response: "Usage: /plan delete <slug> <elementId>",
864
- };
865
- }
866
- const slug = args[0]!;
867
- const elementId = args[1]!;
868
- if (!isValidSlug(slug)) {
869
- return {
870
- handled: true,
871
- response: `Invalid slug "${slug}". Slug must match ^[a-z0-9][a-z0-9-]{0,63}$.`,
872
- };
873
- }
874
- return {
875
- handled: true,
876
- response: "",
877
- sideEffect: {
878
- kind: "tool_invocation",
879
- toolName: "bizar_plan_action",
880
- args: { action: "delete_element", planSlug: slug, elementId },
881
- },
882
- settingsPatch: { lastUsedSlug: slug },
883
- dialog: {
884
- id: generateId(),
885
- title: "Element Deleted",
886
- command: `/plan delete ${slug}`,
887
- component: "generic",
888
- data: {
889
- message: `Deleting element ${elementId} from plan "${slug}"…`,
890
- },
891
- },
892
- };
893
- }
894
-
895
- // --- /plan comment (add) -------------------------------------------------
896
-
897
- function handlePlanComment(args: string[]): SlashCommandResult {
898
- // /plan comment <slug> [elementId] "text"
899
- // The text is the LAST positional arg. The slug is first. The middle
900
- // arg (if any) is the elementId. We re-tokenize the raw arg string
901
- // (NOT the tokenized form) so that quoted text is preserved as a
902
- // single token. We've already tokenized at the parent level, so
903
- // we re-tokenize the raw form here by joining back together.
904
- // The parent passes a tokenized array, so we look at the trailing
905
- // token and assume the text is the last non-flag argument. The
906
- // parent already did quote-preserving tokenization, so trailing
907
- // tokens after the slug/elementId are the text.
908
- if (args.length < 2 || args[0] === "") {
909
- return {
910
- handled: true,
911
- response: 'Usage: /plan comment <slug> [elementId] "text"',
912
- };
913
- }
914
- const slug = args[0]!;
915
- if (!isValidSlug(slug)) {
916
- return {
917
- handled: true,
918
- response: `Invalid slug "${slug}". Slug must match ^[a-z0-9][a-z0-9-]{0,63}$.`,
919
- };
920
- }
921
- // Disambiguate: if the second token is a flag, then there's no
922
- // elementId and the rest is the text.
923
- if (args[1]!.startsWith("--")) {
924
- // No elementId; everything after slug is text.
925
- const text = args.slice(1).join(" ").trim();
926
- if (text === "") {
927
- return { handled: true, response: "Comment text is required." };
928
- }
929
- return commentSideEffect(slug, null, text);
930
- }
931
- // If exactly 2 args, treat second as text (no elementId).
932
- if (args.length === 2) {
933
- return commentSideEffect(slug, null, args[1]!);
934
- }
935
- // 3+ args: slug, elementId, text. The text may itself be quoted
936
- // already (the parent tokenizer preserves quoted strings), so we
937
- // join the rest with spaces.
938
- const elementId = args[1]!;
939
- const text = args.slice(2).join(" ").trim();
940
- if (text === "") {
941
- return { handled: true, response: "Comment text is required." };
942
- }
943
- return commentSideEffect(slug, elementId, text);
944
- }
945
-
946
- function commentSideEffect(
947
- slug: string,
948
- elementId: string | null,
949
- text: string,
950
- ): SlashCommandResult {
951
- return {
952
- handled: true,
953
- response: "",
954
- sideEffect: {
955
- kind: "tool_invocation",
956
- toolName: "bizar_plan_action",
957
- args: {
958
- action: "add_comment",
959
- planSlug: slug,
960
- comment: {
961
- elementId,
962
- author: "user",
963
- text,
964
- },
965
- },
966
- },
967
- settingsPatch: { lastUsedSlug: slug },
968
- dialog: {
969
- id: generateId(),
970
- title: "Comment Added",
971
- command: `/plan comment ${slug}`,
972
- component: "generic",
973
- data: {
974
- message:
975
- elementId === null
976
- ? `Adding canvas-pinned comment to plan "${slug}"…`
977
- : `Adding comment to element ${elementId} on plan "${slug}"…`,
978
- },
979
- },
980
- };
981
- }
982
-
983
- // --- /plan comments (list) -----------------------------------------------
984
-
985
- function handlePlanComments(args: string[]): SlashCommandResult {
986
- if (args.length === 0 || args[0] === "") {
987
- return { handled: true, response: "Usage: /plan comments <slug> [elementId]" };
988
- }
989
- const slug = args[0]!;
990
- if (!isValidSlug(slug)) {
991
- return {
992
- handled: true,
993
- response: `Invalid slug "${slug}". Slug must match ^[a-z0-9][a-z0-9-]{0,63}$.`,
994
- };
995
- }
996
- const elementId = args.length >= 2 ? args[1]! : undefined;
997
- return {
998
- handled: true,
999
- response: "",
1000
- sideEffect: {
1001
- kind: "tool_invocation",
1002
- toolName: "bizar_get_plan_comments",
1003
- args: elementId === undefined ? { planSlug: slug } : { planSlug: slug, elementId },
1004
- },
1005
- settingsPatch: { lastUsedSlug: slug },
1006
- dialog: {
1007
- id: generateId(),
1008
- title: "Comments",
1009
- command: `/plan comments ${slug}`,
1010
- component: "generic",
1011
- data: {
1012
- message:
1013
- elementId === undefined
1014
- ? `Reading comments on plan "${slug}"…`
1015
- : `Reading comments on element ${elementId} of plan "${slug}"…`,
1016
- },
1017
- },
1018
- };
1019
- }
1020
-
1021
- // --- /plan status --------------------------------------------------------
1022
-
1023
- function handlePlanStatus(args: string[]): SlashCommandResult {
1024
- if (args.length < 2 || args[0] === "" || args[1] === "") {
1025
- return {
1026
- handled: true,
1027
- response: `Usage: /plan status <slug> <status>\nAvailable: ${PLAN_STATUSES.join(", ")}`,
1028
- };
1029
- }
1030
- const slug = args[0]!;
1031
- if (!isValidSlug(slug)) {
1032
- return {
1033
- handled: true,
1034
- response: `Invalid slug "${slug}". Slug must match ^[a-z0-9][a-z0-9-]{0,63}$.`,
1035
- };
1036
- }
1037
- const status = args[1]!;
1038
- if (!isPlanStatus(status)) {
1039
- return {
1040
- handled: true,
1041
- response: `Invalid status "${status}". Available: ${PLAN_STATUSES.join(", ")}.`,
1042
- };
1043
- }
1044
- return {
1045
- handled: true,
1046
- response: "",
1047
- sideEffect: {
1048
- kind: "tool_invocation",
1049
- toolName: "bizar_plan_action",
1050
- args: { action: "set_status", planSlug: slug, status },
1051
- },
1052
- settingsPatch: { lastUsedSlug: slug },
1053
- dialog: {
1054
- id: generateId(),
1055
- title: "Status Updated",
1056
- command: `/plan status ${slug}`,
1057
- component: "generic",
1058
- data: {
1059
- message: `Setting plan "${slug}" status to "${status}"…`,
1060
- },
1061
- },
1062
- };
1063
- }
1064
-
1065
- // --- /plan wait (deferred) -----------------------------------------------
1066
-
1067
- function handlePlanWait(args: string[]): SlashCommandResult {
1068
- if (args.length === 0 || args[0] === "") {
1069
- return { handled: true, response: "Usage: /plan wait <slug> [--timeout N]" };
1070
- }
1071
- const slug = args[0]!;
1072
- if (!isValidSlug(slug)) {
1073
- return {
1074
- handled: true,
1075
- response: `Invalid slug "${slug}". Slug must match ^[a-z0-9][a-z0-9-]{0,63}$.`,
1076
- };
1077
- }
1078
- // Per the audit: /plan wait is deferred from MVP. We acknowledge the
1079
- // command and point the user at the real tool. The slash command
1080
- // does NOT block — that's the hard-rule requirement.
1081
- return {
1082
- handled: true,
1083
- response:
1084
- `"/plan wait" is deferred from the v0.5.0 MVP.\n` +
1085
- `Until the SSE-based command ships, the agent should call ` +
1086
- `\`bizar_wait_for_feedback\` directly as a tool call. It polls every 2s ` +
1087
- `and returns when the user adds a comment, approves/rejects the plan, ` +
1088
- `or the timeout fires (default 10 min, max 30 min).\n` +
1089
- `\n` +
1090
- `For now, the LLM can call the tool itself — this is the path the ` +
1091
- `visual-plan flow uses. A future release will wire /plan wait to an ` +
1092
- `SSE push from the local viewer so the user does not see a polling ` +
1093
- `loop.`,
1094
- };
1095
- }
1096
-
1097
- // --- /bizar --------------------------------------------------------------
1098
-
1099
- /**
1100
- * v2.5.0 — `/bizar [args]` launches the dashboard or routes a sub-request.
1101
- *
1102
- * Behavior:
1103
- * - `/bizar` (no args) — emits a `launch_dashboard` side-effect. The
1104
- * executor spawns `bizar dash start` as a detached child
1105
- * process, then the host surfaces the URL in the response.
1106
- * - `/bizar <args>` — passes the args to the menu command file. Today
1107
- * the menu routes intent (`/explain`, `/plan`, `/audit`, etc.); the
1108
- * response is the menu's natural-language routing advice.
1109
- *
1110
- * Note: the menu text lives in `config/commands/bizar.md` and is shipped
1111
- * via the CLI package. The plugin only handles the no-arg case for the
1112
- * side-effect; with args we return a short pointer so the user knows
1113
- * where the routing table lives.
1114
- */
1115
- function handleBizar(arg: string, ctx: ParseContext): SlashCommandResult {
1116
- const trimmed = arg.trim();
1117
-
1118
- if (trimmed === "") {
1119
- const port = ctx.defaultPort ?? 4321;
1120
- return {
1121
- handled: true,
1122
- response: "",
1123
- sideEffect: {
1124
- kind: "launch_dashboard",
1125
- defaultPort: port,
1126
- },
1127
- dialog: {
1128
- id: generateId(),
1129
- title: "Dashboard",
1130
- command: "/bizar",
1131
- component: "generic",
1132
- data: {
1133
- message: "Dashboard launching in the background…",
1134
- url: `http://localhost:${port}/`,
1135
- },
1136
- },
1137
- };
1138
- }
1139
-
1140
- // With args, defer to the menu command file shipped with the CLI.
1141
- return {
1142
- handled: true,
1143
- response: "",
1144
- dialog: {
1145
- id: generateId(),
1146
- title: "Bizar Commands",
1147
- command: `/bizar ${trimmed}`,
1148
- component: "help",
1149
- data: {
1150
- commands: [
1151
- { cmd: "/bizar", desc: "Launch the Bizar dashboard" },
1152
- { cmd: "/bizar explain <question>", desc: "Read-only code Q&A" },
1153
- { cmd: "/bizar plan <args>", desc: "Manage plans" },
1154
- { cmd: "/bizar audit", desc: "Run security audit" },
1155
- { cmd: "/bizar learn", desc: "Extract patterns from session" },
1156
- { cmd: "/bizar init", desc: "Initialize .bizar/ in this project" },
1157
- { cmd: "/bizar pr-review", desc: "PR review" },
1158
- ],
1159
- },
1160
- },
1161
- };
1162
- }
1163
-
1164
- // --- /kb -----------------------------------------------------------------
1165
-
1166
- /**
1167
- * v4.0.0 — `/kb` opens the Bizar Memory vault in Obsidian.
1168
- *
1169
- * Behavior:
1170
- * - Routes through the `bizar_open_kb` tool via tool_invocation.
1171
- * - The tool resolves the vault path from .bizar/memory.json and
1172
- * spawns Obsidian (or xdg-open as fallback).
1173
- * - If memory isn't initialized, the tool returns a helpful message.
1174
- *
1175
- * No subcommands. With args, returns a help pointer.
1176
- */
1177
- function handleKb(arg: string, _ctx: ParseContext): SlashCommandResult {
1178
- const trimmed = arg.trim();
1179
-
1180
- if (trimmed !== "") {
1181
- return {
1182
- handled: true,
1183
- response:
1184
- `Unknown argument: \`${trimmed}\`. \`/kb\` takes no arguments.\n\n` +
1185
- `Use \`/kb\` to open the Bizar Memory vault in Obsidian.\n` +
1186
- `Use \`bizar memory status\` to see vault details.`,
1187
- };
1188
- }
1189
-
1190
- return {
1191
- handled: true,
1192
- response: "Opening Bizar Memory vault in Obsidian…",
1193
- sideEffect: {
1194
- kind: "tool_invocation",
1195
- toolName: "bizar_open_kb",
1196
- args: {},
1197
- },
1198
- };
1199
- }
1200
-
1201
- // --- /help ----------------------------------------------------------------
1202
-
1203
- /**
1204
- * Helper: build a SlashCommandResult that invokes a Bizar tool.
1205
- * Most slash commands are thin shims over an existing tool.
1206
- */
1207
- function toolInvocation(toolName: string, args: unknown, response: string): SlashCommandResult {
1208
- return {
1209
- handled: true,
1210
- response,
1211
- sideEffect: { kind: "tool_invocation", toolName, args },
1212
- };
1213
- }
1214
-
1215
- /**
1216
- * Helper: parse `--key=value` and `--key value` flag pairs from an arg
1217
- * string. Returns `{ positional: string[], flags: Record<string,string> }`.
1218
- *
1219
- * NOTE: Named `parseSlashFlags` to avoid colliding with the existing
1220
- * `parseFlags(tokens: string[])` helper used by the legacy `/plan *`
1221
- * handlers.
1222
- */
1223
- function parseSlashFlags(arg: string): { positional: string[]; flags: Record<string, string> } {
1224
- const tokens = arg.split(/\s+/).filter((t) => t.length > 0);
1225
- const positional: string[] = [];
1226
- const flags: Record<string, string> = {};
1227
- for (let i = 0; i < tokens.length; i++) {
1228
- const t = tokens[i]!;
1229
- if (t.startsWith("--")) {
1230
- const eq = t.indexOf("=");
1231
- if (eq !== -1) {
1232
- flags[t.slice(2, eq)] = t.slice(eq + 1);
1233
- continue;
1234
- }
1235
- const key = t.slice(2);
1236
- const next = tokens[i + 1];
1237
- if (next !== undefined && !next.startsWith("--")) {
1238
- flags[key] = next;
1239
- i++;
1240
- } else {
1241
- flags[key] = "";
1242
- }
1243
- } else {
1244
- positional.push(t);
1245
- }
1246
- }
1247
- return { positional, flags };
1248
- }
1249
-
1250
- /**
1251
- * `/odin <task>` — Spawn a Cline agent team orchestrated by Odin.
1252
- *
1253
- * v6.0.0 — Decomposes the task via `odin.decomposeTask`, builds an Odin
1254
- * system prompt with subtasks + roles, and dispatches via
1255
- * `bizar_spawn_team`. The Odin lead coordinates teammates via Cline's
1256
- * TeamSendMessage.
1257
- */
1258
- function handleOdin(arg: string, _ctx: ParseContext): SlashCommandResult {
1259
- const { positional, flags } = parseSlashFlags(arg);
1260
- const task = positional.join(" ").trim();
1261
- if (!task) {
1262
- return {
1263
- handled: true,
1264
- response:
1265
- "Usage: /odin <task>\n\nSpawn a Cline agent team orchestrated by Odin. Odin decomposes the task and dispatches subtasks to teammates.",
1266
- };
1267
- }
1268
- const maxAgents = flags["max"] ? parseInt(flags["max"], 10) : 3;
1269
-
1270
- // Lazy import to avoid a circular load at module init.
1271
- // The odin module is small and side-effect free.
1272
- // eslint-disable-next-line @typescript-eslint/no-require-imports
1273
- const { decomposeTask, buildOdinPrompt } = require("./odin.js") as typeof import("./odin.js");
1274
- const decomposition = decomposeTask(task, Math.max(2, Math.min(6, maxAgents)));
1275
- const mission = buildOdinPrompt(task, decomposition);
1276
-
1277
- return toolInvocation(
1278
- "bizar_spawn_team",
1279
- {
1280
- teamName: "odin-squad",
1281
- mission,
1282
- maxAgents: decomposition.estimatedAgents,
1283
- // Pass the decomposition to the team so individual teammates can
1284
- // pick up their assigned subtasks.
1285
- metadata: {
1286
- odinDecomposition: decomposition,
1287
- originalTask: task,
1288
- },
1289
- },
1290
- `Spawning Odin team for: ${task} (${decomposition.subtasks.length} subtasks, ${decomposition.estimatedAgents} agents)`,
1291
- );
1292
- }
1293
-
1294
- /**
1295
- * `/loop <pattern> <task>` — Start a continuous loop.
1296
- * Patterns: ralph | repl | cron | plan-execute
1297
- */
1298
- function handleLoop(arg: string, _ctx: ParseContext): SlashCommandResult {
1299
- const { positional, flags } = parseSlashFlags(arg);
1300
- const [pattern, ...rest] = positional;
1301
- const task = rest.join(" ").trim();
1302
- if (!pattern) {
1303
- return {
1304
- handled: true,
1305
- response:
1306
- "Usage: /loop <pattern> <task> [options]\n\nPatterns: ralph | repl | cron | plan-execute\nOptions: --interval N (cron) | --max N (default 10)",
1307
- };
1308
- }
1309
- const valid = new Set(["ralph", "repl", "cron", "plan-execute"]);
1310
- if (!valid.has(pattern)) {
1311
- return { handled: true, response: `Unknown loop pattern: ${pattern}. Valid: ${[...valid].join(", ")}` };
1312
- }
1313
- if (!task) return { handled: true, response: `Usage: /loop ${pattern} <task>` };
1314
- return toolInvocation(
1315
- "bizar_loop_start",
1316
- {
1317
- pattern,
1318
- task,
1319
- interval: flags["interval"] ? parseInt(flags["interval"], 10) : undefined,
1320
- maxIterations: flags["max"] ? parseInt(flags["max"], 10) : 10,
1321
- },
1322
- `Starting ${pattern} loop: ${task}`,
1323
- );
1324
- }
1325
-
1326
- /**
1327
- * `/kanban [list|add|move]` — Kanban board operations.
1328
- */
1329
- function handleKanban(arg: string, _ctx: ParseContext): SlashCommandResult {
1330
- const { positional, flags } = parseSlashFlags(arg);
1331
- const sub = positional[0] || "list";
1332
- const args = positional.slice(1);
1333
- if (sub === "list") return toolInvocation("bizar_kanban_list", {}, "Listing kanban board…");
1334
- if (sub === "add") {
1335
- return toolInvocation(
1336
- "bizar_kanban_add",
1337
- { title: args.join(" "), column: flags["column"] || "todo" },
1338
- `Adding kanban card: ${args.join(" ")}`,
1339
- );
1340
- }
1341
- if (sub === "move") {
1342
- return toolInvocation(
1343
- "bizar_kanban_move",
1344
- { cardId: args[0], column: args[1] },
1345
- `Moving card ${args[0]} → ${args[1]}`,
1346
- );
1347
- }
1348
- return { handled: true, response: `Unknown /kanban subcommand: ${sub}. Try list|add|move.` };
1349
- }
1350
-
1351
- /**
1352
- * `/review [scope]` — Run a code review.
1353
- */
1354
- function handleReview(arg: string, _ctx: ParseContext): SlashCommandResult {
1355
- const scope = arg.trim() || ".";
1356
- return toolInvocation("bizar_review", { scope }, `Running review on ${scope}…`);
1357
- }
1358
-
1359
- /**
1360
- * `/handoff <text>` — Compact current session into a handoff document.
1361
- */
1362
- function handleHandoff(arg: string, _ctx: ParseContext): SlashCommandResult {
1363
- const text = arg.trim();
1364
- if (!text) return { handled: true, response: "Usage: /handoff <summary text>" };
1365
- return toolInvocation("bizar_handoff_write", { text }, "Writing handoff document…");
1366
- }
1367
-
1368
- /**
1369
- * `/grill <topic>` — Run a relentless interview on a topic.
1370
- */
1371
- function handleGrill(arg: string, _ctx: ParseContext): SlashCommandResult {
1372
- const topic = arg.trim();
1373
- if (!topic) return { handled: true, response: "Usage: /grill <topic to be grilled on>" };
1374
- return toolInvocation("bizar_grill", { topic, mode: "grill" }, `Grilling on: ${topic}`);
1375
- }
1376
-
1377
- /**
1378
- * `/glyph <type> <slug>` — Create a visual glyph.
1379
- */
1380
- function handleGlyph(arg: string, _ctx: ParseContext): SlashCommandResult {
1381
- const { positional } = parseSlashFlags(arg);
1382
- const [type, ...rest] = positional;
1383
- const slug = rest.join(" ").trim();
1384
- if (!type || !slug) {
1385
- return {
1386
- handled: true,
1387
- response: "Usage: /glyph <type> <slug>\n\nTypes: plan, recap, design, postmortem, handoff",
1388
- };
1389
- }
1390
- return toolInvocation("bizar_glyph_create", { type, slug }, `Creating glyph: ${slug}`);
1391
- }
1392
-
1393
- /**
1394
- * `/decision <title>` — Document an architectural decision (ADR).
1395
- */
1396
- function handleDecision(arg: string, _ctx: ParseContext): SlashCommandResult {
1397
- const title = arg.trim();
1398
- if (!title) return { handled: true, response: "Usage: /decision <ADR title>" };
1399
- return toolInvocation("bizar_decision_write", { title }, `Documenting decision: ${title}`);
1400
- }
1401
-
1402
- /**
1403
- * `/issue <title>` — File an issue.
1404
- */
1405
- function handleIssue(arg: string, _ctx: ParseContext): SlashCommandResult {
1406
- const title = arg.trim();
1407
- if (!title) return { handled: true, response: "Usage: /issue <issue title>" };
1408
- return toolInvocation("bizar_issue_create", { title }, `Filing issue: ${title}`);
1409
- }
1410
-
1411
- /**
1412
- * `/digest [period]` — Show or generate a digest.
1413
- */
1414
- function handleDigest(arg: string, _ctx: ParseContext): SlashCommandResult {
1415
- const period = arg.trim() || "weekly";
1416
- return toolInvocation("bizar_digest", { period, action: "show" }, `Showing ${period} digest…`);
1417
- }
1418
-
1419
- /**
1420
- * `/usage [period]` — Show usage analytics.
1421
- */
1422
- function handleUsage(arg: string, _ctx: ParseContext): SlashCommandResult {
1423
- const period = arg.trim() || "24h";
1424
- return toolInvocation("bizar_usage", { period }, `Showing ${period} usage…`);
1425
- }
1426
-
1427
- /**
1428
- * `/memory <subcommand>` — Memory vault operations.
1429
- */
1430
- function handleMemory(arg: string, _ctx: ParseContext): SlashCommandResult {
1431
- const { positional } = parseSlashFlags(arg);
1432
- const sub = positional[0] || "status";
1433
- const rest = positional.slice(1);
1434
- switch (sub) {
1435
- case "status": return toolInvocation("bizar_memory_status", {}, "Memory status…");
1436
- case "search": return toolInvocation(
1437
- "bizar_memory_search", { query: rest.join(" "), mode: "semantic" },
1438
- `Searching memory: ${rest.join(" ")}`,
1439
- );
1440
- case "read": return toolInvocation(
1441
- "bizar_memory_read", { path: rest.join(" ") },
1442
- `Reading memory note ${rest.join(" ")}`,
1443
- );
1444
- case "write": return toolInvocation(
1445
- "bizar_memory_write", { path: rest.join(" ") },
1446
- `Writing memory note ${rest.join(" ")}`,
1447
- );
1448
- case "list": return toolInvocation("bizar_memory_list", {}, "Listing memory notes…");
1449
- case "link": return toolInvocation(
1450
- "bizar_memory_link", { url: rest[0] },
1451
- `Linking memory vault to ${rest[0]}`,
1452
- );
1453
- }
1454
- return {
1455
- handled: true,
1456
- response: `Unknown /memory subcommand: ${sub}. Try: status|search|read|write|list|link`,
1457
- };
1458
- }
1459
-
1460
- /**
1461
- * `/spawn <agent> <prompt>` — Spawn a background agent.
1462
- */
1463
- function handleSpawn(arg: string, _ctx: ParseContext): SlashCommandResult {
1464
- const { positional, flags } = parseSlashFlags(arg);
1465
- const agent = positional[0];
1466
- const prompt = positional.slice(1).join(" ");
1467
- if (!agent || !prompt) {
1468
- return {
1469
- handled: true,
1470
- response: "Usage: /spawn <agent> <prompt>\n\nAgents: odin, heimdall, thor, tyr, vidarr, forseti, frigg, mimir, hermod",
1471
- };
1472
- }
1473
- return toolInvocation(
1474
- "bizar_spawn_background",
1475
- {
1476
- agent,
1477
- prompt,
1478
- timeoutMs: flags["timeout"] ? parseInt(flags["timeout"], 10) : 300000,
1479
- },
1480
- `Spawning ${agent}: ${prompt.slice(0, 80)}…`,
1481
- );
1482
- }
1483
-
1484
- /**
1485
- * `/team [status|stop|list]` — Team operations.
1486
- */
1487
- function handleTeam(arg: string, _ctx: ParseContext): SlashCommandResult {
1488
- const { positional } = parseSlashFlags(arg);
1489
- const sub = positional[0] || "status";
1490
- switch (sub) {
1491
- case "status":
1492
- return toolInvocation("bizar_team_status", { sessionId: positional[1] }, "Team status…");
1493
- case "stop":
1494
- return toolInvocation(
1495
- "bizar_team_stop", { sessionId: positional[1] },
1496
- `Stopping team ${positional[1] || "(active)"}`,
1497
- );
1498
- case "list":
1499
- return toolInvocation("bizar_team_list", {}, "Listing teams…");
1500
- }
1501
- return { handled: true, response: `Unknown /team subcommand: ${sub}. Try: status|stop|list` };
1502
- }
1503
-
1504
- /**
1505
- * `/audit` — Run security audit.
1506
- */
1507
- function handleAudit(_arg: string, _ctx: ParseContext): SlashCommandResult {
1508
- return toolInvocation("bizar_audit", {}, "Running security audit…");
1509
- }
1510
-
1511
- /**
1512
- * `/deploy <target>` — One-click deploy.
1513
- */
1514
- function handleDeploy(arg: string, _ctx: ParseContext): SlashCommandResult {
1515
- const target = arg.trim() || "vercel";
1516
- return toolInvocation("bizar_deploy", { target }, `Deploying to ${target}…`);
1517
- }
1518
-
1519
- /**
1520
- * `/status` — Show current session status.
1521
- */
1522
- function handleStatus(_arg: string, _ctx: ParseContext): SlashCommandResult {
1523
- return toolInvocation("bizar_status", {}, "Session status…");
1524
- }
1525
-
1526
- /**
1527
- * `/lightrag <subcommand>` — LightRAG server management.
1528
- */
1529
- function handleLightrag(arg: string, _ctx: ParseContext): SlashCommandResult {
1530
- const { positional } = parseSlashFlags(arg);
1531
- const sub = positional[0] || "status";
1532
- return toolInvocation(
1533
- "bizar_lightrag", { subcommand: sub, args: positional.slice(1) },
1534
- `LightRAG ${sub}…`,
1535
- );
1536
- }
1537
-
1538
- /**
1539
- * `/headroom <subcommand>` — Headroom context compression.
1540
- */
1541
- function handleHeadroom(arg: string, _ctx: ParseContext): SlashCommandResult {
1542
- const { positional } = parseSlashFlags(arg);
1543
- const sub = positional[0] || "status";
1544
- return toolInvocation(
1545
- "bizar_headroom", { subcommand: sub, args: positional.slice(1) },
1546
- `Headroom ${sub}…`,
1547
- );
1548
- }
1549
-
1550
- /**
1551
- * `/minimax <subcommand>` — MiniMax Token Plan integration.
1552
- */
1553
- function handleMiniMax(arg: string, _ctx: ParseContext): SlashCommandResult {
1554
- const { positional } = parseSlashFlags(arg);
1555
- const sub = positional[0] || "status";
1556
- return toolInvocation(
1557
- "bizar_minimax", { subcommand: sub, args: positional.slice(1) },
1558
- `MiniMax ${sub}…`,
1559
- );
1560
- }
1561
-
1562
- /**
1563
- * `/service <subcommand>` — Background service management.
1564
- */
1565
- function handleService(arg: string, _ctx: ParseContext): SlashCommandResult {
1566
- const { positional } = parseSlashFlags(arg);
1567
- const sub = positional[0] || "status";
1568
- return toolInvocation(
1569
- "bizar_service", { subcommand: sub, args: positional.slice(1) },
1570
- `Service ${sub}…`,
1571
- );
1572
- }
1573
-
1574
- /**
1575
- * `/dash <subcommand>` — Dashboard control.
1576
- */
1577
- function handleDash(arg: string, _ctx: ParseContext): SlashCommandResult {
1578
- const { positional } = parseSlashFlags(arg);
1579
- const sub = positional[0] || "status";
1580
- return toolInvocation(
1581
- "bizar_dash", { subcommand: sub, args: positional.slice(1) },
1582
- `Dashboard ${sub}…`,
1583
- );
1584
- }
1585
-
1586
- /**
1587
- * `/mod <subcommand>` — Mod management.
1588
- */
1589
- function handleMod(arg: string, _ctx: ParseContext): SlashCommandResult {
1590
- const { positional } = parseSlashFlags(arg);
1591
- const sub = positional[0] || "list";
1592
- return toolInvocation(
1593
- "bizar_mod", { subcommand: sub, args: positional.slice(1) },
1594
- `Mod ${sub}…`,
1595
- );
1596
- }
1597
-
1598
- /**
1599
- * `/init` — Initialize Bizar in the current project.
1600
- */
1601
- function handleInit(_arg: string, _ctx: ParseContext): SlashCommandResult {
1602
- return toolInvocation(
1603
- "bizar_init", { projectRoot: process.cwd() },
1604
- "Initializing Bizar in project…",
1605
- );
1606
- }
1607
-
1608
- /**
1609
- * `/dev <subcommand>` — Developer commands.
1610
- */
1611
- function handleDev(arg: string, _ctx: ParseContext): SlashCommandResult {
1612
- const { positional } = parseSlashFlags(arg);
1613
- const sub = positional[0] || "status";
1614
- return toolInvocation(
1615
- "bizar_dev", { subcommand: sub, args: positional.slice(1) },
1616
- `Dev ${sub}…`,
1617
- );
1618
- }
1619
-
1620
- /**
1621
- * `/test` — Run the project's test suite.
1622
- */
1623
- function handleTest(_arg: string, _ctx: ParseContext): SlashCommandResult {
1624
- return toolInvocation("bizar_test_gate", {}, "Running tests…");
1625
- }
1626
-
1627
- /**
1628
- * `/agent-browser <subcommand>` — Browser automation.
1629
- */
1630
- function handleAgentBrowser(arg: string, _ctx: ParseContext): SlashCommandResult {
1631
- const { positional } = parseSlashFlags(arg);
1632
- const sub = positional[0] || "status";
1633
- return toolInvocation(
1634
- "bizar_agent_browser", { subcommand: sub, args: positional.slice(1) },
1635
- `Agent-browser ${sub}…`,
1636
- );
1637
- }
1638
-
1639
- /**
1640
- * `/plow-through <task>` — Run the plow-through sprint workflow.
1641
- */
1642
- function handlePlowThrough(arg: string, _ctx: ParseContext): SlashCommandResult {
1643
- const task = arg.trim();
1644
- if (!task) return { handled: true, response: "Usage: /plow-through <task>" };
1645
- return toolInvocation("bizar_plow_through", { task }, `Plowing through: ${task}`);
1646
- }
1647
-
1648
- /**
1649
- * `/tailscale <subcommand>` — Tailscale integration.
1650
- */
1651
- function handleTailscale(arg: string, _ctx: ParseContext): SlashCommandResult {
1652
- const { positional } = parseSlashFlags(arg);
1653
- const sub = positional[0] || "status";
1654
- return toolInvocation(
1655
- "bizar_tailscale", { subcommand: sub, args: positional.slice(1) },
1656
- `Tailscale ${sub}…`,
1657
- );
1658
- }
1659
-
1660
- /**
1661
- * `/providers <subcommand>` — Provider detection.
1662
- */
1663
- function handleProviders(arg: string, _ctx: ParseContext): SlashCommandResult {
1664
- const { positional } = parseSlashFlags(arg);
1665
- const sub = positional[0] || "detect";
1666
- return toolInvocation(
1667
- "bizar_providers", { subcommand: sub, args: positional.slice(1) },
1668
- `Providers ${sub}…`,
1669
- );
1670
- }
1671
-
1672
- /**
1673
- * `/clip <subcommand>` — Web clipper.
1674
- */
1675
- function handleClip(arg: string, _ctx: ParseContext): SlashCommandResult {
1676
- const { positional } = parseSlashFlags(arg);
1677
- const sub = positional[0] || "list";
1678
- return toolInvocation(
1679
- "bizar_clip", { subcommand: sub, args: positional.slice(1) },
1680
- `Clip ${sub}…`,
1681
- );
1682
- }
1683
-
1684
- /**
1685
- * `/ocr <subcommand>` — OCR operations.
1686
- */
1687
- function handleOcr(arg: string, _ctx: ParseContext): SlashCommandResult {
1688
- const { positional } = parseSlashFlags(arg);
1689
- const sub = positional[0] || "list";
1690
- return toolInvocation(
1691
- "bizar_ocr", { subcommand: sub, args: positional.slice(1) },
1692
- `OCR ${sub}…`,
1693
- );
1694
- }
1695
-
1696
- /**
1697
- * `/workspace <subcommand>` — Workspace management.
1698
- */
1699
- function handleWorkspace(arg: string, _ctx: ParseContext): SlashCommandResult {
1700
- const { positional } = parseSlashFlags(arg);
1701
- const sub = positional[0] || "status";
1702
- return toolInvocation(
1703
- "bizar_workspace", { subcommand: sub, args: positional.slice(1) },
1704
- `Workspace ${sub}…`,
1705
- );
1706
- }
1707
-
1708
- /**
1709
- * `/voice <subcommand>` — Voice notes.
1710
- */
1711
- function handleVoice(arg: string, _ctx: ParseContext): SlashCommandResult {
1712
- const { positional } = parseSlashFlags(arg);
1713
- const sub = positional[0] || "status";
1714
- return toolInvocation(
1715
- "bizar_voice", { subcommand: sub, args: positional.slice(1) },
1716
- `Voice ${sub}…`,
1717
- );
1718
- }
1719
-
1720
- function helpResult(): SlashCommandResult {
1721
- return {
1722
- handled: true,
1723
- response:
1724
- "Available commands — Bizar commands: /odin <task>, /loop <pattern> <task>, /kanban [list|add|move], " +
1725
- "/review [scope], /handoff <text>, /grill <topic>, /glyph <type> <slug>, " +
1726
- "/decision <title>, /issue <title>, /digest [period], /usage [period], " +
1727
- "/memory <sub>, /spawn <agent> <prompt>, /team <sub>, /audit, /deploy [target], " +
1728
- "/status, /lightrag <sub>, /headroom <sub>, /minimax <sub>, /service <sub>, " +
1729
- "/dash <sub>, /mod <sub>, /init, /dev <sub>, /test, /agent-browser <sub>, " +
1730
- "/plow-through <task>, /tailscale <sub>, /providers <sub>, /clip <sub>, " +
1731
- "/ocr <sub>, /workspace <sub>, /voice <sub>, " +
1732
- "/visual-plan [on|off|status], /plan new <slug> [template], /plan list, /plan open <slug>, " +
1733
- "/plan get <slug>, /plan add <slug>, /plan update <slug> <id>, /plan delete <slug> <id>, " +
1734
- "/plan comment <slug> [id] \"text\", /plan comments <slug> [id], /plan status <slug> <status>, " +
1735
- "/plan wait <slug> [--timeout N], /bizar <args>, /kb, /help.",
1736
- dialog: {
1737
- id: generateId(),
1738
- title: "Bizar Commands",
1739
- command: "/help",
1740
- component: "help",
1741
- data: {
1742
- commands: [
1743
- { cmd: "/odin <task>", desc: "Spawn a Cline agent team orchestrated by Odin" },
1744
- { cmd: "/loop <pattern> <task>", desc: "Start a loop (ralph/repl/cron/plan-execute)" },
1745
- { cmd: "/kanban [list|add|move]", desc: "Kanban board operations" },
1746
- { cmd: "/review [scope]", desc: "Run a code review" },
1747
- { cmd: "/handoff <text>", desc: "Compact current session into a handoff document" },
1748
- { cmd: "/grill <topic>", desc: "Run a relentless interview" },
1749
- { cmd: "/glyph <type> <slug>", desc: "Create a visual glyph (plan/recap/design)" },
1750
- { cmd: "/decision <title>", desc: "Document an architectural decision (ADR)" },
1751
- { cmd: "/issue <title>", desc: "File an issue" },
1752
- { cmd: "/digest [period]", desc: "Show or generate a digest" },
1753
- { cmd: "/usage [period]", desc: "Show usage analytics" },
1754
- { cmd: "/memory <subcommand>", desc: "Memory vault operations (status/search/read/write/list/link)" },
1755
- { cmd: "/spawn <agent> <prompt>", desc: "Spawn a background agent" },
1756
- { cmd: "/team [status|stop|list]", desc: "Team operations" },
1757
- { cmd: "/audit", desc: "Run security audit" },
1758
- { cmd: "/deploy <target>", desc: "One-click deploy (vercel/cloudflare/fly/docker)" },
1759
- { cmd: "/status", desc: "Show current session status" },
1760
- { cmd: "/lightrag <subcommand>", desc: "LightRAG server management" },
1761
- { cmd: "/headroom <subcommand>", desc: "Headroom context compression" },
1762
- { cmd: "/minimax <subcommand>", desc: "MiniMax Token Plan integration" },
1763
- { cmd: "/service <subcommand>", desc: "Background service management" },
1764
- { cmd: "/dash <subcommand>", desc: "Dashboard control" },
1765
- { cmd: "/mod <subcommand>", desc: "Mod management" },
1766
- { cmd: "/init", desc: "Initialize Bizar in current project" },
1767
- { cmd: "/dev <subcommand>", desc: "Developer commands (link/unlink/build)" },
1768
- { cmd: "/test", desc: "Run the project's test suite" },
1769
- { cmd: "/agent-browser <subcommand>", desc: "Browser automation" },
1770
- { cmd: "/plow-through <task>", desc: "Run the plow-through sprint workflow" },
1771
- { cmd: "/tailscale <subcommand>", desc: "Tailscale integration" },
1772
- { cmd: "/providers <subcommand>", desc: "Provider detection" },
1773
- { cmd: "/clip <subcommand>", desc: "Web clipper" },
1774
- { cmd: "/ocr <subcommand>", desc: "OCR operations" },
1775
- { cmd: "/workspace <subcommand>", desc: "Workspace management" },
1776
- { cmd: "/voice <subcommand>", desc: "Voice notes" },
1777
- { cmd: "/visual-plan [on|off|status]", desc: "Toggle or view visual plan mode" },
1778
- { cmd: "/plan new <slug> [template]", desc: "Create a new plan" },
1779
- { cmd: "/plan list", desc: "List all plans in the worktree" },
1780
- { cmd: "/plan open <slug>", desc: "Open a plan in the viewer" },
1781
- { cmd: "/plan get <slug>", desc: "Fetch the full canvas" },
1782
- { cmd: "/plan add <slug> --title T --type kind", desc: "Add an element to a plan" },
1783
- { cmd: "/plan update <slug> <id> [flags]", desc: "Patch an existing element" },
1784
- { cmd: "/plan delete <slug> <id>", desc: "Remove an element" },
1785
- { cmd: "/plan comment <slug> [id] \"text\"", desc: "Add a comment" },
1786
- { cmd: "/plan comments <slug> [id]", desc: "Read comments on a plan" },
1787
- { cmd: "/plan status <slug> <status>", desc: "Set the plan's status" },
1788
- { cmd: "/plan wait <slug> [--timeout N]", desc: "Wait for feedback (deferred)" },
1789
- { cmd: "/bizar", desc: "Launch the Bizar dashboard" },
1790
- { cmd: "/bizar <args>", desc: "Route a request via the menu" },
1791
- { cmd: "/kb", desc: "Open the Bizar Memory vault in Obsidian" },
1792
- { cmd: "/help | /commands", desc: "Show this help" },
1793
- ],
1794
- templates: [...KNOWN_TEMPLATES],
1795
- statuses: [...PLAN_STATUSES],
1796
- },
1797
- },
1798
- };
1799
- }