@polderlabs/bizar 3.23.0 → 4.0.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 (346) hide show
  1. package/README.md +72 -2
  2. package/bizar-dash/.bizar/activity.log +3 -0
  3. package/bizar-dash/.bizar/graph/.graphify_analysis.json +1540 -0
  4. package/bizar-dash/.bizar/graph/.graphify_labels.json +1 -0
  5. package/bizar-dash/.bizar/graph/GRAPH_REPORT.md +404 -0
  6. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/0661b816358db84dfdb7f10443db8a61152fd20b3fee950caf8fbf32920a1790.json +1 -0
  7. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/16da06d774142a6e5d74829ad982531286a6193ef798de8f593db029e123ec32.json +1 -0
  8. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/27e5697c01254c69c64c5a33896669073b40afb5ba88375102ae77fa5404136d.json +1 -0
  9. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/532997898027254aa4c0932243dc71d2f55888181232b65974961934997dc85b.json +1 -0
  10. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/84e3451a64c7f99f0cd6a88b0748d7426780486f53ab4935b7476b28c08f8293.json +1 -0
  11. package/bizar-dash/.bizar/graph/cache/ast/v0.8.46/88e5ea5f3e78fa69353fe9e6a8d1a05391d54605e25600cb125531eb5a6432ff.json +1 -0
  12. package/bizar-dash/.bizar/graph/cache/stat-index.json +1 -0
  13. package/bizar-dash/.bizar/graph/graph.html +307 -0
  14. package/bizar-dash/.bizar/graph/graph.json +40267 -0
  15. package/bizar-dash/.bizar/graph/manifest.json +762 -0
  16. package/bizar-dash/.graphifyignore +40 -0
  17. package/bizar-dash/.obsidian/README.md +14 -0
  18. package/bizar-dash/.obsidian/daily/2026-06-25.md +9 -0
  19. package/bizar-dash/.obsidian/vault.json +7 -0
  20. package/bizar-dash/.od-skills/web-prototype-e1429c1737/SKILL.md +103 -0
  21. package/bizar-dash/.od-skills/web-prototype-e1429c1737/assets/template.html +338 -0
  22. package/bizar-dash/.od-skills/web-prototype-e1429c1737/example.html +81 -0
  23. package/bizar-dash/.od-skills/web-prototype-e1429c1737/open-design.json +172 -0
  24. package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/checklist.md +44 -0
  25. package/bizar-dash/.od-skills/web-prototype-e1429c1737/references/layouts.md +247 -0
  26. package/bizar-dash/CHANGELOG.md +474 -0
  27. package/bizar-dash/DESIGN.md +136 -0
  28. package/bizar-dash/dist/assets/main-B3RgW6FS.js +331 -0
  29. package/bizar-dash/dist/assets/main-B3RgW6FS.js.map +1 -0
  30. package/bizar-dash/dist/assets/main-DlJ8wgLR.css +1 -0
  31. package/bizar-dash/dist/assets/mobile-CsZQAswA.css +1 -0
  32. package/bizar-dash/dist/assets/mobile-DbxIw8BG.js +2 -0
  33. package/bizar-dash/dist/assets/mobile-DbxIw8BG.js.map +1 -0
  34. package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js +354 -0
  35. package/bizar-dash/dist/assets/mobile-Dvq2d53Y.js.map +1 -0
  36. package/bizar-dash/dist/index.html +40 -0
  37. package/bizar-dash/dist/mobile.html +22 -0
  38. package/bizar-dash/plans/debug-plan/comments.json +1 -0
  39. package/bizar-dash/plans/debug-plan/meta.json +10 -0
  40. package/bizar-dash/plans/debug-plan/plan.json +174 -0
  41. package/bizar-dash/scripts/smoke-bg-retry.mjs +246 -0
  42. package/bizar-dash/src/cli/dashboard-ports.mjs +488 -0
  43. package/bizar-dash/src/cli.mjs +522 -0
  44. package/bizar-dash/src/server/activity-log.mjs +174 -0
  45. package/bizar-dash/src/server/agents-store.mjs +465 -0
  46. package/bizar-dash/src/server/api.mjs +138 -0
  47. package/bizar-dash/src/server/artifact-template.html +810 -0
  48. package/bizar-dash/src/server/artifacts-store.mjs +733 -0
  49. package/bizar-dash/src/server/auth.mjs +381 -0
  50. package/bizar-dash/src/server/background-store.mjs +447 -0
  51. package/bizar-dash/src/server/bg-poller.mjs +305 -0
  52. package/bizar-dash/src/server/bg-retry.mjs +574 -0
  53. package/bizar-dash/src/server/browser.mjs +40 -0
  54. package/bizar-dash/src/server/diagnostics-store.mjs +148 -0
  55. package/bizar-dash/src/server/dialog-poller.mjs +105 -0
  56. package/bizar-dash/src/server/dialog-store.mjs +177 -0
  57. package/bizar-dash/src/server/glyphs/mdx-compiler.mjs +583 -0
  58. package/bizar-dash/src/server/lib/path-safe.mjs +283 -0
  59. package/bizar-dash/src/server/memory-git.mjs +249 -0
  60. package/bizar-dash/src/server/memory-schema.mjs +145 -0
  61. package/bizar-dash/src/server/memory-secrets.mjs +84 -0
  62. package/bizar-dash/src/server/memory-store.mjs +424 -0
  63. package/bizar-dash/src/server/mod-security.mjs +398 -0
  64. package/bizar-dash/src/server/mods-loader.mjs +1067 -0
  65. package/bizar-dash/src/server/notifications-store.mjs +243 -0
  66. package/bizar-dash/src/server/obsidian-store.mjs +318 -0
  67. package/bizar-dash/src/server/opencode-runner.mjs +279 -0
  68. package/bizar-dash/src/server/pair-store.mjs +138 -0
  69. package/bizar-dash/src/server/projects-store.mjs +364 -0
  70. package/bizar-dash/src/server/providers-store.mjs +789 -0
  71. package/bizar-dash/src/server/routes/_shared.mjs +415 -0
  72. package/bizar-dash/src/server/routes/activity.mjs +110 -0
  73. package/bizar-dash/src/server/routes/agents.mjs +134 -0
  74. package/bizar-dash/src/server/routes/artifacts.mjs +279 -0
  75. package/bizar-dash/src/server/routes/auth.mjs +69 -0
  76. package/bizar-dash/src/server/routes/background.mjs +165 -0
  77. package/bizar-dash/src/server/routes/chat.mjs +485 -0
  78. package/bizar-dash/src/server/routes/config.mjs +129 -0
  79. package/bizar-dash/src/server/routes/diagnostics.mjs +50 -0
  80. package/bizar-dash/src/server/routes/dialogs.mjs +86 -0
  81. package/bizar-dash/src/server/routes/fs.mjs +429 -0
  82. package/bizar-dash/src/server/routes/history.mjs +78 -0
  83. package/bizar-dash/src/server/routes/memory.mjs +499 -0
  84. package/bizar-dash/src/server/routes/misc.mjs +89 -0
  85. package/bizar-dash/src/server/routes/mods.mjs +359 -0
  86. package/bizar-dash/src/server/routes/notifications.mjs +54 -0
  87. package/bizar-dash/src/server/routes/obsidian.mjs +239 -0
  88. package/bizar-dash/src/server/routes/opencode-sessions.mjs +55 -0
  89. package/bizar-dash/src/server/routes/overview.mjs +91 -0
  90. package/bizar-dash/src/server/routes/pair.mjs +67 -0
  91. package/bizar-dash/src/server/routes/projects.mjs +175 -0
  92. package/bizar-dash/src/server/routes/providers.mjs +47 -0
  93. package/bizar-dash/src/server/routes/schedules.mjs +104 -0
  94. package/bizar-dash/src/server/routes/settings.mjs +51 -0
  95. package/bizar-dash/src/server/routes/skills.mjs +76 -0
  96. package/bizar-dash/src/server/routes/tasks.mjs +468 -0
  97. package/bizar-dash/src/server/routes/themes.mjs +51 -0
  98. package/bizar-dash/src/server/routes-v2/auth.mjs +59 -0
  99. package/bizar-dash/src/server/routes-v2/events.mjs +107 -0
  100. package/bizar-dash/src/server/routes-v2/health.mjs +21 -0
  101. package/bizar-dash/src/server/routes-v2/index.mjs +90 -0
  102. package/bizar-dash/src/server/routes-v2/sessions.mjs +111 -0
  103. package/bizar-dash/src/server/schedules-runner.mjs +369 -0
  104. package/bizar-dash/src/server/schedules-store.mjs +268 -0
  105. package/bizar-dash/src/server/search-store.mjs +220 -0
  106. package/bizar-dash/src/server/serve-info.mjs +684 -0
  107. package/bizar-dash/src/server/server.mjs +796 -0
  108. package/bizar-dash/src/server/settings-store.mjs +45 -0
  109. package/bizar-dash/src/server/skills-store.mjs +300 -0
  110. package/bizar-dash/src/server/state.mjs +437 -0
  111. package/bizar-dash/src/server/tailscale-store.mjs +113 -0
  112. package/bizar-dash/src/server/task-delegator.mjs +824 -0
  113. package/bizar-dash/src/server/tasks-store.mjs +508 -0
  114. package/bizar-dash/src/server/tui.mjs +923 -0
  115. package/bizar-dash/src/server/update-store.mjs +168 -0
  116. package/bizar-dash/src/server/v2-auth-file.mjs +99 -0
  117. package/bizar-dash/src/server/v2-event-bus.mjs +128 -0
  118. package/bizar-dash/src/server/watcher.mjs +81 -0
  119. package/bizar-dash/src/server/yaml.mjs +238 -0
  120. package/bizar-dash/src/web/App.tsx +718 -0
  121. package/bizar-dash/src/web/MobileApp.tsx +408 -0
  122. package/bizar-dash/src/web/components/ArtifactCreateDialog.tsx +95 -0
  123. package/bizar-dash/src/web/components/ArtifactListDialog.tsx +55 -0
  124. package/bizar-dash/src/web/components/ArtifactViewer.tsx +143 -0
  125. package/bizar-dash/src/web/components/AuditDialog.tsx +79 -0
  126. package/bizar-dash/src/web/components/BgStatusBadge.tsx +32 -0
  127. package/bizar-dash/src/web/components/Button.tsx +56 -0
  128. package/bizar-dash/src/web/components/CanvasContextMenu.tsx +68 -0
  129. package/bizar-dash/src/web/components/Card.tsx +40 -0
  130. package/bizar-dash/src/web/components/CollapsibleSection.tsx +72 -0
  131. package/bizar-dash/src/web/components/CommandDialog.tsx +66 -0
  132. package/bizar-dash/src/web/components/EmptyState.tsx +30 -0
  133. package/bizar-dash/src/web/components/FileBrowser.tsx +633 -0
  134. package/bizar-dash/src/web/components/HelpDialog.tsx +55 -0
  135. package/bizar-dash/src/web/components/KillConfirmDialog.tsx +88 -0
  136. package/bizar-dash/src/web/components/Modal.tsx +215 -0
  137. package/bizar-dash/src/web/components/Notifications.tsx +241 -0
  138. package/bizar-dash/src/web/components/SearchModal.tsx +224 -0
  139. package/bizar-dash/src/web/components/Sidebar.tsx +66 -0
  140. package/bizar-dash/src/web/components/Spinner.tsx +19 -0
  141. package/bizar-dash/src/web/components/StatusBadge.tsx +25 -0
  142. package/bizar-dash/src/web/components/Tag.tsx +28 -0
  143. package/bizar-dash/src/web/components/Toast.tsx +150 -0
  144. package/bizar-dash/src/web/components/Topbar.tsx +368 -0
  145. package/bizar-dash/src/web/components/VisualPlanDialog.tsx +58 -0
  146. package/bizar-dash/src/web/components/chat/AgentChip.tsx +68 -0
  147. package/bizar-dash/src/web/components/chat/ChatBubble.tsx +60 -0
  148. package/bizar-dash/src/web/components/chat/ChatThread.tsx +88 -0
  149. package/bizar-dash/src/web/components/chat/ChatTopBar.tsx +50 -0
  150. package/bizar-dash/src/web/components/chat/Composer.tsx +164 -0
  151. package/bizar-dash/src/web/components/chat/ConfirmModal.tsx +54 -0
  152. package/bizar-dash/src/web/components/chat/EmptyState.tsx +21 -0
  153. package/bizar-dash/src/web/components/chat/FirstRunGreeting.tsx +18 -0
  154. package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +32 -0
  155. package/bizar-dash/src/web/components/chat/InfoPanel.tsx +119 -0
  156. package/bizar-dash/src/web/components/chat/LoadingSkeleton.tsx +19 -0
  157. package/bizar-dash/src/web/components/chat/MessageBubble.tsx +60 -0
  158. package/bizar-dash/src/web/components/chat/SessionList.tsx +153 -0
  159. package/bizar-dash/src/web/components/chat/StreamingIndicator.tsx +9 -0
  160. package/bizar-dash/src/web/components/chat/SuggestionCards.tsx +55 -0
  161. package/bizar-dash/src/web/components/chat/WelcomeScreen.tsx +36 -0
  162. package/bizar-dash/src/web/components/chat/index.ts +20 -0
  163. package/bizar-dash/src/web/components/chat/useAutoGrowTextarea.ts +18 -0
  164. package/bizar-dash/src/web/components/chat/useChat.ts +219 -0
  165. package/bizar-dash/src/web/components/chat/useSlashCommands.ts +47 -0
  166. package/bizar-dash/src/web/index.html +37 -0
  167. package/bizar-dash/src/web/lib/api.ts +193 -0
  168. package/bizar-dash/src/web/lib/markdown.tsx +59 -0
  169. package/bizar-dash/src/web/lib/types.ts +668 -0
  170. package/bizar-dash/src/web/lib/utils.ts +114 -0
  171. package/bizar-dash/src/web/lib/ws.ts +176 -0
  172. package/bizar-dash/src/web/main.tsx +34 -0
  173. package/bizar-dash/src/web/mobile/MobileApp.tsx +278 -0
  174. package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +38 -0
  175. package/bizar-dash/src/web/mobile/MobileTopbar.tsx +115 -0
  176. package/bizar-dash/src/web/mobile/components/MobileBottomSheet.tsx +182 -0
  177. package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +64 -0
  178. package/bizar-dash/src/web/mobile/components/MobileModal.tsx +123 -0
  179. package/bizar-dash/src/web/mobile/views/MobileActivity.tsx +273 -0
  180. package/bizar-dash/src/web/mobile/views/MobileAgents.tsx +374 -0
  181. package/bizar-dash/src/web/mobile/views/MobileArtifactCanvas.tsx +439 -0
  182. package/bizar-dash/src/web/mobile/views/MobileArtifacts.tsx +143 -0
  183. package/bizar-dash/src/web/mobile/views/MobileChat.tsx +180 -0
  184. package/bizar-dash/src/web/mobile/views/MobileConfig.tsx +175 -0
  185. package/bizar-dash/src/web/mobile/views/MobileHistory.tsx +191 -0
  186. package/bizar-dash/src/web/mobile/views/MobileMods.tsx +149 -0
  187. package/bizar-dash/src/web/mobile/views/MobileMore.tsx +121 -0
  188. package/bizar-dash/src/web/mobile/views/MobileNotifications.tsx +137 -0
  189. package/bizar-dash/src/web/mobile/views/MobilePlans.tsx +26 -0
  190. package/bizar-dash/src/web/mobile/views/MobileSchedules.tsx +685 -0
  191. package/bizar-dash/src/web/mobile/views/MobileSearchModal.tsx +172 -0
  192. package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +388 -0
  193. package/bizar-dash/src/web/mobile/views/MobileSkills.tsx +153 -0
  194. package/bizar-dash/src/web/mobile/views/MobileTasks.tsx +564 -0
  195. package/bizar-dash/src/web/mobile.html +20 -0
  196. package/bizar-dash/src/web/mobile.tsx +13 -0
  197. package/bizar-dash/src/web/styles/chat.css +198 -0
  198. package/bizar-dash/src/web/styles/glyphs.css +960 -0
  199. package/bizar-dash/src/web/styles/main.css +8855 -0
  200. package/bizar-dash/src/web/styles/mobile-chat.css +5 -0
  201. package/bizar-dash/src/web/styles/mobile.css +1960 -0
  202. package/bizar-dash/src/web/views/Activity.tsx +1203 -0
  203. package/bizar-dash/src/web/views/Agents.tsx +701 -0
  204. package/bizar-dash/src/web/views/Artifacts.tsx +1480 -0
  205. package/bizar-dash/src/web/views/BackgroundAgents.tsx +450 -0
  206. package/bizar-dash/src/web/views/Chat.tsx +190 -0
  207. package/bizar-dash/src/web/views/Config.tsx +1603 -0
  208. package/bizar-dash/src/web/views/History.tsx +335 -0
  209. package/bizar-dash/src/web/views/ModView.tsx +207 -0
  210. package/bizar-dash/src/web/views/Mods.tsx +693 -0
  211. package/bizar-dash/src/web/views/Overview.tsx +812 -0
  212. package/bizar-dash/src/web/views/Providers.tsx +302 -0
  213. package/bizar-dash/src/web/views/Schedules.tsx +802 -0
  214. package/bizar-dash/src/web/views/Settings.tsx +1787 -0
  215. package/bizar-dash/src/web/views/Skills.tsx +431 -0
  216. package/bizar-dash/src/web/views/Tasks.tsx +1523 -0
  217. package/bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx +613 -0
  218. package/bizar-dash/src/web/views/glyphs/components.tsx +1098 -0
  219. package/bizar-dash/templates/mod/FORMAT.md +76 -0
  220. package/bizar-dash/templates/mod/hello-mod/README.md +19 -0
  221. package/bizar-dash/templates/mod/hello-mod/agents/greeter.md +8 -0
  222. package/bizar-dash/templates/mod/hello-mod/commands/hello.md +6 -0
  223. package/bizar-dash/templates/mod/hello-mod/mod.json +20 -0
  224. package/bizar-dash/templates/mod/hello-mod/routes/ping.mjs +9 -0
  225. package/bizar-dash/templates/mod/hello-mod/views/HelloView.tsx +10 -0
  226. package/bizar-dash/tests/dashboard-ports.test.mjs +138 -0
  227. package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +90 -0
  228. package/bizar-dash/tests/memory-git.test.mjs +138 -0
  229. package/bizar-dash/tests/memory-schema.test.mjs +198 -0
  230. package/bizar-dash/tests/memory-secrets.test.mjs +121 -0
  231. package/bizar-dash/tests/memory-store.test.mjs +390 -0
  232. package/bizar-dash/tests/memory-sync.test.mjs +141 -0
  233. package/bizar-dash/tests/mod-instructions.node.test.mjs +188 -0
  234. package/bizar-dash/tests/mod-security.test.mjs +273 -0
  235. package/bizar-dash/tests/mod-upgrade.node.test.mjs +357 -0
  236. package/bizar-dash/tests/no-agent-browser.node.test.mjs +98 -0
  237. package/bizar-dash/tests/obsidian-back-compat.test.mjs +199 -0
  238. package/bizar-dash/tests/opencode-runner.test.mjs +172 -0
  239. package/bizar-dash/tests/path-safe.test.mjs +108 -0
  240. package/bizar-dash/tests/providers-store-backup-keys.node.test.mjs +333 -0
  241. package/bizar-dash/tests/smoke-v2.mjs +212 -0
  242. package/bizar-dash/tests/strip-thinking.test.mjs +175 -0
  243. package/bizar-dash/tests/submit-feedback.test.mjs +353 -0
  244. package/bizar-dash/tests/tmux-wrap.test.mjs +145 -0
  245. package/bizar-dash/tests/yaml.test.mjs +128 -0
  246. package/bizar-dash/tsconfig.json +23 -0
  247. package/bizar-dash/vite.config.ts +27 -0
  248. package/cli/atomic.mjs +73 -0
  249. package/cli/bin.mjs +43 -46
  250. package/cli/copy.mjs +3 -0
  251. package/cli/dev-link.mjs +17 -1
  252. package/cli/init.mjs +135 -1
  253. package/cli/install.mjs +10 -23
  254. package/cli/memory-constants.mjs +59 -0
  255. package/cli/memory.mjs +757 -0
  256. package/cli/service.mjs +5 -6
  257. package/config/skills/obsidian/SKILL.md +213 -65
  258. package/install.sh +48 -18
  259. package/package.json +49 -14
  260. package/packages/sdk/LICENSE +21 -0
  261. package/packages/sdk/README.md +80 -0
  262. package/packages/sdk/src/client.ts +183 -0
  263. package/packages/sdk/src/errors.ts +120 -0
  264. package/packages/sdk/src/events.ts +153 -0
  265. package/packages/sdk/src/index.ts +63 -0
  266. package/packages/sdk/src/types.ts +176 -0
  267. package/packages/sdk/src/version.ts +4 -0
  268. package/packages/sdk/tests/client.test.ts +217 -0
  269. package/packages/sdk/tests/errors.test.ts +108 -0
  270. package/packages/sdk/tests/events.test.ts +139 -0
  271. package/packages/sdk/tests/fixtures/fetch-mock.ts +152 -0
  272. package/packages/sdk/tests/fixtures/sse-mock.ts +30 -0
  273. package/packages/sdk/tsconfig.json +25 -0
  274. package/packages/sdk/vitest.config.ts +9 -0
  275. package/plugins/bizar/LICENSE +21 -0
  276. package/plugins/bizar/README.md +448 -0
  277. package/plugins/bizar/dist/index.js +29901 -0
  278. package/plugins/bizar/index.ts +1484 -0
  279. package/plugins/bizar/scripts/check-forbidden-imports.sh +33 -0
  280. package/plugins/bizar/src/background-state.ts +551 -0
  281. package/plugins/bizar/src/background.ts +1250 -0
  282. package/plugins/bizar/src/commands-impl.ts +464 -0
  283. package/plugins/bizar/src/commands.ts +1127 -0
  284. package/plugins/bizar/src/dashboard-client.ts +233 -0
  285. package/plugins/bizar/src/event-stream.ts +606 -0
  286. package/plugins/bizar/src/fingerprint.ts +120 -0
  287. package/plugins/bizar/src/handoff.ts +79 -0
  288. package/plugins/bizar/src/http-client.ts +467 -0
  289. package/plugins/bizar/src/key-rotation.ts +218 -0
  290. package/plugins/bizar/src/logger.ts +144 -0
  291. package/plugins/bizar/src/loop.ts +176 -0
  292. package/plugins/bizar/src/opencode-runner.ts +390 -0
  293. package/plugins/bizar/src/options.ts +421 -0
  294. package/plugins/bizar/src/plan-fs.ts +323 -0
  295. package/plugins/bizar/src/reasoning-clean.ts +454 -0
  296. package/plugins/bizar/src/report.ts +178 -0
  297. package/plugins/bizar/src/research-prompt.ts +35 -0
  298. package/plugins/bizar/src/serve-info.ts +228 -0
  299. package/plugins/bizar/src/serve.ts +496 -0
  300. package/plugins/bizar/src/settings.ts +349 -0
  301. package/plugins/bizar/src/state.ts +298 -0
  302. package/plugins/bizar/src/tools/bg-collect.ts +104 -0
  303. package/plugins/bizar/src/tools/bg-get-comments.ts +239 -0
  304. package/plugins/bizar/src/tools/bg-kill.ts +87 -0
  305. package/plugins/bizar/src/tools/bg-spawn.ts +401 -0
  306. package/plugins/bizar/src/tools/bg-status.ts +99 -0
  307. package/plugins/bizar/src/tools/plan-action.ts +767 -0
  308. package/plugins/bizar/src/tools/read-glyph-feedback.ts +170 -0
  309. package/plugins/bizar/src/tools/wait-for-feedback.ts +402 -0
  310. package/plugins/bizar/tests/attach-handler-bug.test.ts +169 -0
  311. package/plugins/bizar/tests/background-state.test.ts +277 -0
  312. package/plugins/bizar/tests/background.test.ts +402 -0
  313. package/plugins/bizar/tests/block.test.ts +195 -0
  314. package/plugins/bizar/tests/canonical-key-order.test.ts +75 -0
  315. package/plugins/bizar/tests/commands-impl.test.ts +442 -0
  316. package/plugins/bizar/tests/commands.test.ts +548 -0
  317. package/plugins/bizar/tests/config.test.ts +122 -0
  318. package/plugins/bizar/tests/dashboard-client.test.ts +159 -0
  319. package/plugins/bizar/tests/dispose.test.ts +336 -0
  320. package/plugins/bizar/tests/event-stream.test.ts +409 -0
  321. package/plugins/bizar/tests/event.test.ts +262 -0
  322. package/plugins/bizar/tests/fingerprint.test.ts +162 -0
  323. package/plugins/bizar/tests/http-client.test.ts +404 -0
  324. package/plugins/bizar/tests/init-helpers.test.ts +203 -0
  325. package/plugins/bizar/tests/integration/slash-command.test.ts +348 -0
  326. package/plugins/bizar/tests/integration/tool-routing.test.ts +314 -0
  327. package/plugins/bizar/tests/key-rotation.test.ts +396 -0
  328. package/plugins/bizar/tests/loop.test.ts +397 -0
  329. package/plugins/bizar/tests/options.test.ts +276 -0
  330. package/plugins/bizar/tests/reasoning-clean.test.ts +422 -0
  331. package/plugins/bizar/tests/serve.test.ts +339 -0
  332. package/plugins/bizar/tests/settings.test.ts +351 -0
  333. package/plugins/bizar/tests/stall-think.test.ts +750 -0
  334. package/plugins/bizar/tests/state.test.ts +276 -0
  335. package/plugins/bizar/tests/tools/bg-collect.test.ts +337 -0
  336. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +485 -0
  337. package/plugins/bizar/tests/tools/bg-kill.test.ts +235 -0
  338. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +147 -0
  339. package/plugins/bizar/tests/tools/bg-spawn.test.ts +311 -0
  340. package/plugins/bizar/tests/tools/bg-status.test.ts +217 -0
  341. package/plugins/bizar/tests/tools/opencode-runner.test.ts +115 -0
  342. package/plugins/bizar/tests/tools/plan-action.test.ts +599 -0
  343. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +253 -0
  344. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +390 -0
  345. package/plugins/bizar/tests/update-deadlock.test.ts +145 -0
  346. package/plugins/bizar/tsconfig.json +29 -0
@@ -0,0 +1,508 @@
1
+ /**
2
+ * src/server/tasks-store.mjs
3
+ *
4
+ * v3.0.0 — Per-project tasks with extended fields:
5
+ * assignee, parent, dependencies, timeSpent, recurring, attachments,
6
+ * comments, activity
7
+ *
8
+ * Storage: per-project `~/.config/opencode/projects/<id>/tasks.json`
9
+ *
10
+ * Backward compat: if no active project is set, fall back to the legacy
11
+ * global location (`~/.config/bizar/tasks.json`).
12
+ */
13
+ import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from 'node:fs';
14
+ import { dirname, join } from 'node:path';
15
+ import { homedir } from 'node:os';
16
+ import { randomBytes } from 'node:crypto';
17
+ import { projectsStore } from './projects-store.mjs';
18
+
19
+ const HOME = homedir();
20
+ const LEGACY_FILE = join(HOME, '.config', 'bizar', 'tasks.json');
21
+
22
+ // Atomic JSON write: serialize to a sibling temp file, then rename into
23
+ // place. `rename` is atomic on POSIX (same filesystem), so a crash
24
+ // between write and rename never leaves a half-written / corrupt file.
25
+ function atomicWriteJson(filePath, data) {
26
+ const tmp = `${filePath}.tmp.${process.pid}`;
27
+ writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8');
28
+ renameSync(tmp, filePath);
29
+ }
30
+
31
+ let _busy = false;
32
+ const _waiters = [];
33
+
34
+ function acquire() {
35
+ return new Promise((resolve) => {
36
+ if (!_busy) {
37
+ _busy = true;
38
+ resolve();
39
+ } else {
40
+ _waiters.push({ resolve });
41
+ }
42
+ });
43
+ }
44
+
45
+ function release() {
46
+ if (_waiters.length > 0) {
47
+ _waiters.shift().resolve();
48
+ } else {
49
+ _busy = false;
50
+ }
51
+ }
52
+
53
+ function safeReadJSON(file, fallback = null) {
54
+ try {
55
+ if (!existsSync(file)) return fallback;
56
+ const text = readFileSync(file, 'utf8');
57
+ if (!text.trim()) return fallback;
58
+ return JSON.parse(text);
59
+ } catch {
60
+ return fallback;
61
+ }
62
+ }
63
+
64
+ function genId() {
65
+ return 'tsk_' + randomBytes(6).toString('hex').slice(0, 10);
66
+ }
67
+
68
+ function genShortId() {
69
+ return randomBytes(4).toString('hex').slice(0, 8);
70
+ }
71
+
72
+ function resolveStorageFile(projectId) {
73
+ if (projectId) {
74
+ const dir = projectsStore.ensureProjectDir(projectId);
75
+ return join(dir, 'tasks.json');
76
+ }
77
+ // legacy fallback
78
+ return LEGACY_FILE;
79
+ }
80
+
81
+ function loadStore(file) {
82
+ const raw = safeReadJSON(file, null);
83
+ if (!raw || typeof raw !== 'object' || !Array.isArray(raw.tasks)) {
84
+ return { version: 2, tasks: [] };
85
+ }
86
+ return raw;
87
+ }
88
+
89
+ function saveStore(file, store) {
90
+ mkdirSync(dirname(file), { recursive: true });
91
+ atomicWriteJson(file, store);
92
+ }
93
+
94
+ function appendActivity(task, type, data) {
95
+ task.activity = task.activity || [];
96
+ task.activity.push({
97
+ id: 'act_' + genShortId(),
98
+ type,
99
+ ts: new Date().toISOString(),
100
+ data: data || null,
101
+ });
102
+ if (task.activity.length > 100) task.activity = task.activity.slice(-100);
103
+ }
104
+
105
+ export const tasksStore = {
106
+ /** Load tasks for a project (or legacy). */
107
+ loadTasks(projectId, opts = {}) {
108
+ const file = resolveStorageFile(projectId);
109
+ const store = loadStore(file);
110
+ const includeArchived = !!opts.includeArchived;
111
+ const onlyArchived = !!opts.onlyArchived;
112
+ let tasks = store.tasks.slice();
113
+ if (onlyArchived) {
114
+ tasks = tasks.filter((t) => t.archived);
115
+ } else if (!includeArchived) {
116
+ tasks = tasks.filter((t) => !t.archived);
117
+ }
118
+ return tasks.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
119
+ },
120
+
121
+ /** Save all tasks. */
122
+ saveTasks(projectId, tasks) {
123
+ const file = resolveStorageFile(projectId);
124
+ saveStore(file, { version: 2, tasks });
125
+ },
126
+
127
+ /**
128
+ * v3.5.4 — Look up a single task by id. Returns null if the id does
129
+ * not exist. Loads the full project file once; callers that need
130
+ * many ids should batch via `loadTasks` + filter instead.
131
+ */
132
+ async getById(projectId, id) {
133
+ if (!id) return null;
134
+ const file = resolveStorageFile(projectId);
135
+ const store = loadStore(file);
136
+ const t = (store.tasks || []).find((x) => x && x.id === id);
137
+ return t ? { ...t } : null;
138
+ },
139
+
140
+ /** Create a task. */
141
+ async create(projectId, input) {
142
+ if (!input || typeof input !== 'object') {
143
+ throw new Error('task input required');
144
+ }
145
+ if (!input.title || typeof input.title !== 'string' || input.title.length > 200) {
146
+ throw new Error('title required (1-200 chars)');
147
+ }
148
+ await acquire();
149
+ try {
150
+ const file = resolveStorageFile(projectId);
151
+ const store = loadStore(file);
152
+ const now = new Date().toISOString();
153
+ const task = {
154
+ id: genId(),
155
+ title: input.title,
156
+ description: input.description || '',
157
+ status: ['queued', 'doing', 'done', 'blocked', 'archived'].includes(input.status) ? input.status : 'queued',
158
+ tags: Array.isArray(input.tags) ? input.tags : [],
159
+ priority: ['low', 'normal', 'high'].includes(input.priority) ? input.priority : 'normal',
160
+ assignee: input.assignee || null,
161
+ parent: input.parent || null,
162
+ dependencies: Array.isArray(input.dependencies) ? input.dependencies : [],
163
+ timeSpent: 0,
164
+ recurring: input.recurring || null,
165
+ attachments: Array.isArray(input.attachments) ? input.attachments : [],
166
+ comments: [],
167
+ activity: [],
168
+ archived: false,
169
+ workedBy: null,
170
+ dueDate: typeof input.dueDate === 'string' ? input.dueDate : null,
171
+ // v3.2.0 — fields for the task delegator.
172
+ subtasks: Array.isArray(input.subtasks) ? input.subtasks : undefined,
173
+ metadata: input.metadata && typeof input.metadata === 'object' ? input.metadata : undefined,
174
+ createdAt: now,
175
+ updatedAt: now,
176
+ completedAt: null,
177
+ };
178
+ appendActivity(task, 'created', { priority: task.priority });
179
+ store.tasks.push(task);
180
+ saveStore(file, store);
181
+ return task;
182
+ } finally {
183
+ release();
184
+ }
185
+ },
186
+
187
+ /** Update a task. */
188
+ async update(projectId, id, patch) {
189
+ await acquire();
190
+ try {
191
+ const file = resolveStorageFile(projectId);
192
+ const store = loadStore(file);
193
+ const idx = store.tasks.findIndex((t) => t.id === id);
194
+ if (idx === -1) return null;
195
+ const task = { ...store.tasks[idx] };
196
+
197
+ if (typeof patch.title === 'string') task.title = patch.title.slice(0, 200);
198
+ if (typeof patch.description === 'string') task.description = patch.description;
199
+ if (['queued', 'doing', 'done', 'blocked', 'archived'].includes(patch.status)) {
200
+ if (patch.status !== task.status) {
201
+ appendActivity(task, 'status', { from: task.status, to: patch.status });
202
+ }
203
+ task.status = patch.status;
204
+ }
205
+ if (Array.isArray(patch.tags)) task.tags = patch.tags;
206
+ if (['low', 'normal', 'high'].includes(patch.priority)) {
207
+ task.priority = patch.priority;
208
+ }
209
+ if (typeof patch.assignee === 'string' || patch.assignee === null) {
210
+ task.assignee = patch.assignee;
211
+ }
212
+ if (typeof patch.parent === 'string' || patch.parent === null) {
213
+ task.parent = patch.parent;
214
+ }
215
+ if (Array.isArray(patch.dependencies)) task.dependencies = patch.dependencies;
216
+ if (typeof patch.timeSpent === 'number' && Number.isFinite(patch.timeSpent)) {
217
+ task.timeSpent = Math.max(0, patch.timeSpent);
218
+ }
219
+ if (patch.recurring === null || typeof patch.recurring === 'object') {
220
+ task.recurring = patch.recurring;
221
+ }
222
+ if (Array.isArray(patch.attachments)) task.attachments = patch.attachments;
223
+ if (patch.archived === true) task.archived = true;
224
+ if (patch.archived === false) task.archived = false;
225
+ if (typeof patch.workedBy === 'string' || patch.workedBy === null) {
226
+ task.workedBy = patch.workedBy;
227
+ }
228
+ if (typeof patch.dueDate === 'string' || patch.dueDate === null) {
229
+ task.dueDate = patch.dueDate || null;
230
+ }
231
+ // v3.2.0 — generic metadata bag (used by task-delegator to
232
+ // record bg instance IDs, dispatch timestamps, etc.).
233
+ if (patch.metadata && typeof patch.metadata === 'object') {
234
+ task.metadata = { ...(task.metadata || {}), ...patch.metadata };
235
+ }
236
+ // v3.2.0 — main tasks track their subtask ids.
237
+ if (Array.isArray(patch.subtasks)) {
238
+ task.subtasks = patch.subtasks.map(String);
239
+ }
240
+
241
+ task.updatedAt = new Date().toISOString();
242
+ if (task.status === 'done' && !task.completedAt) {
243
+ task.completedAt = task.updatedAt;
244
+ appendActivity(task, 'completed', null);
245
+ } else if (task.status !== 'done') {
246
+ task.completedAt = null;
247
+ }
248
+
249
+ store.tasks[idx] = task;
250
+ saveStore(file, store);
251
+ return task;
252
+ } finally {
253
+ release();
254
+ }
255
+ },
256
+
257
+ async delete(projectId, id) {
258
+ await acquire();
259
+ try {
260
+ const file = resolveStorageFile(projectId);
261
+ const store = loadStore(file);
262
+ const before = store.tasks.length;
263
+ store.tasks = store.tasks.filter((t) => t.id !== id);
264
+ if (store.tasks.length === before) return false;
265
+ saveStore(file, store);
266
+ return true;
267
+ } finally {
268
+ release();
269
+ }
270
+ },
271
+
272
+ async move(projectId, id, newStatus) {
273
+ return this.update(projectId, id, { status: newStatus });
274
+ },
275
+
276
+ /**
277
+ * v3.3.0 — Update progress for a task. Used by agents to push
278
+ * real-time status. Patches metadata.progress (0-100) and
279
+ * metadata.currentStep (string). Also appends a row to
280
+ * metadata.progressHistory for the timeline view.
281
+ */
282
+ async updateProgress(projectId, id, { progress, step, agent } = {}) {
283
+ await acquire();
284
+ try {
285
+ const file = resolveStorageFile(projectId);
286
+ const store = loadStore(file);
287
+ const idx = store.tasks.findIndex((t) => t.id === id);
288
+ if (idx === -1) return null;
289
+ const task = { ...store.tasks[idx] };
290
+ const meta = { ...(task.metadata || {}) };
291
+ const clamped = Math.max(0, Math.min(100, Number.isFinite(progress) ? progress : 0));
292
+ meta.progress = clamped;
293
+ if (typeof step === 'string') meta.currentStep = step;
294
+ if (typeof agent === 'string') meta.progressAgent = agent;
295
+ if (!Array.isArray(meta.progressHistory)) meta.progressHistory = [];
296
+ meta.progressHistory.push({ ts: new Date().toISOString(), progress: clamped, step: step || null, agent: agent || null });
297
+ // Hard-cap the history length to avoid unbounded growth.
298
+ if (meta.progressHistory.length > 100) {
299
+ meta.progressHistory = meta.progressHistory.slice(-100);
300
+ }
301
+ meta.startedAt = meta.startedAt || new Date().toISOString();
302
+ task.metadata = meta;
303
+ task.updatedAt = new Date().toISOString();
304
+ appendActivity(task, 'progress', { progress: clamped, step: step || null, agent: agent || null });
305
+ store.tasks[idx] = task;
306
+ saveStore(file, store);
307
+ return task;
308
+ } finally {
309
+ release();
310
+ }
311
+ },
312
+
313
+ /** Add a comment to a task. */
314
+ async addComment(projectId, id, text) {
315
+ if (!text || typeof text !== 'string') throw new Error('comment text required');
316
+ await acquire();
317
+ try {
318
+ const file = resolveStorageFile(projectId);
319
+ const store = loadStore(file);
320
+ const task = store.tasks.find((t) => t.id === id);
321
+ if (!task) return null;
322
+ task.comments = task.comments || [];
323
+ const comment = {
324
+ id: 'cmt_' + genShortId(),
325
+ text: text.slice(0, 4000),
326
+ createdAt: new Date().toISOString(),
327
+ };
328
+ task.comments.push(comment);
329
+ appendActivity(task, 'comment', { commentId: comment.id });
330
+ task.updatedAt = new Date().toISOString();
331
+ saveStore(file, store);
332
+ return task;
333
+ } finally {
334
+ release();
335
+ }
336
+ },
337
+
338
+ /** Toggle the time-tracking timer. Returns the task. */
339
+ async toggleTimer(projectId, id) {
340
+ await acquire();
341
+ try {
342
+ const file = resolveStorageFile(projectId);
343
+ const store = loadStore(file);
344
+ const task = store.tasks.find((t) => t.id === id);
345
+ if (!task) return null;
346
+ const now = Date.now();
347
+ if (task._timerStart) {
348
+ const elapsed = Math.floor((now - task._timerStart) / 1000);
349
+ task.timeSpent = (task.timeSpent || 0) + elapsed;
350
+ appendActivity(task, 'timer-stop', { elapsed });
351
+ delete task._timerStart;
352
+ } else {
353
+ task._timerStart = now;
354
+ appendActivity(task, 'timer-start', null);
355
+ }
356
+ task.updatedAt = new Date().toISOString();
357
+ saveStore(file, store);
358
+ return task;
359
+ } finally {
360
+ release();
361
+ }
362
+ },
363
+
364
+ // v3.1.0 ─────────────────────────────────────────────────────────
365
+ /** Start the timer for a task (idempotent). */
366
+ async startTimer(projectId, id) {
367
+ await acquire();
368
+ try {
369
+ const file = resolveStorageFile(projectId);
370
+ const store = loadStore(file);
371
+ const task = store.tasks.find((t) => t.id === id);
372
+ if (!task) return null;
373
+ if (!task._timerStart) {
374
+ task._timerStart = Date.now();
375
+ appendActivity(task, 'timer-start', null);
376
+ task.updatedAt = new Date().toISOString();
377
+ saveStore(file, store);
378
+ }
379
+ return task;
380
+ } finally {
381
+ release();
382
+ }
383
+ },
384
+
385
+ /** Stop the timer for a task. */
386
+ async stopTimer(projectId, id) {
387
+ await acquire();
388
+ try {
389
+ const file = resolveStorageFile(projectId);
390
+ const store = loadStore(file);
391
+ const task = store.tasks.find((t) => t.id === id);
392
+ if (!task) return null;
393
+ if (task._timerStart) {
394
+ const elapsed = Math.floor((Date.now() - task._timerStart) / 1000);
395
+ task.timeSpent = (task.timeSpent || 0) + elapsed;
396
+ appendActivity(task, 'timer-stop', { elapsed });
397
+ delete task._timerStart;
398
+ task.updatedAt = new Date().toISOString();
399
+ saveStore(file, store);
400
+ }
401
+ return task;
402
+ } finally {
403
+ release();
404
+ }
405
+ },
406
+
407
+ /** Archive a task (keeps it in the store; the UI hides it by default). */
408
+ async archive(projectId, id) {
409
+ return this.update(projectId, id, { archived: true, status: 'archived' });
410
+ },
411
+
412
+ /** Unarchive — restore to the prior status (queued) by default. */
413
+ async unarchive(projectId, id) {
414
+ return this.update(projectId, id, { archived: false, status: 'queued' });
415
+ },
416
+
417
+ /**
418
+ * Bulk action over a set of task ids. Actions: 'archive' | 'unarchive' |
419
+ * 'delete' | 'move' | 'tag' | 'assign' | 'priority'.
420
+ * Returns { affected: [{ id, ok, error? }, ...] }.
421
+ */
422
+ async bulk(projectId, ids, action, params = {}) {
423
+ const out = [];
424
+ if (!Array.isArray(ids) || ids.length === 0) {
425
+ return { affected: out, action, params };
426
+ }
427
+ for (const id of ids) {
428
+ try {
429
+ let result = null;
430
+ if (action === 'archive') result = await this.archive(projectId, id);
431
+ else if (action === 'unarchive') result = await this.unarchive(projectId, id);
432
+ else if (action === 'delete') {
433
+ const ok = await this.delete(projectId, id);
434
+ result = ok ? { id, deleted: true } : null;
435
+ } else if (action === 'move') {
436
+ result = await this.move(projectId, id, params.status);
437
+ } else if (action === 'priority') {
438
+ result = await this.update(projectId, id, { priority: params.priority });
439
+ } else if (action === 'assign') {
440
+ result = await this.update(projectId, id, { assignee: params.assignee });
441
+ } else if (action === 'tag') {
442
+ const t = await this.update(projectId, id, { tags: Array.isArray(params.tags) ? params.tags : [] });
443
+ result = t;
444
+ } else {
445
+ out.push({ id, ok: false, error: `unknown action: ${action}` });
446
+ continue;
447
+ }
448
+ out.push({ id, ok: !!result });
449
+ } catch (err) {
450
+ out.push({ id, ok: false, error: err.message });
451
+ }
452
+ }
453
+ return { affected: out, action, params };
454
+ },
455
+
456
+ /**
457
+ * Mark a task as "worked on" by an agent. The dashboard listens for
458
+ * this on the WS and updates the agent's status accordingly. If the
459
+ * task is recurring, completing it spawns the next occurrence when
460
+ * `complete=true`.
461
+ */
462
+ async setWorkedBy(projectId, id, agentName, opts = {}) {
463
+ const updated = await this.update(projectId, id, { workedBy: agentName });
464
+ if (!updated) return null;
465
+ if (opts.status) {
466
+ const moved = await this.move(projectId, id, opts.status);
467
+ if (moved?.recurring && opts.status === 'done' && opts.complete) {
468
+ await this.spawnNextRecurrence(projectId, moved);
469
+ }
470
+ return moved;
471
+ }
472
+ return updated;
473
+ },
474
+
475
+ /** Create the next occurrence of a recurring task. */
476
+ async spawnNextRecurrence(projectId, task) {
477
+ if (!task.recurring) return null;
478
+ const now = new Date().toISOString();
479
+ const next = {
480
+ title: task.title,
481
+ description: task.description || '',
482
+ status: 'queued',
483
+ priority: task.priority || 'normal',
484
+ assignee: task.assignee || null,
485
+ tags: task.tags || [],
486
+ recurring: task.recurring,
487
+ parent: task.id,
488
+ };
489
+ const created = await this.create(projectId, next);
490
+ if (created && task.recurring) {
491
+ // Persist lastGenerated on the original task.
492
+ task.recurring = { ...task.recurring, lastGenerated: now };
493
+ await acquire();
494
+ try {
495
+ const file = resolveStorageFile(projectId);
496
+ const store = loadStore(file);
497
+ const idx = store.tasks.findIndex((t) => t.id === task.id);
498
+ if (idx >= 0) {
499
+ store.tasks[idx] = task;
500
+ saveStore(file, store);
501
+ }
502
+ } finally {
503
+ release();
504
+ }
505
+ }
506
+ return created;
507
+ },
508
+ };