@polderlabs/bizar 3.23.1 → 4.2.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 (361) hide show
  1. package/README.md +83 -16
  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 +329 -0
  60. package/bizar-dash/src/server/memory-lightrag.mjs +767 -0
  61. package/bizar-dash/src/server/memory-schema.mjs +145 -0
  62. package/bizar-dash/src/server/memory-secrets.mjs +84 -0
  63. package/bizar-dash/src/server/memory-store.mjs +471 -0
  64. package/bizar-dash/src/server/mod-security.mjs +398 -0
  65. package/bizar-dash/src/server/mods-loader.mjs +1067 -0
  66. package/bizar-dash/src/server/notifications-store.mjs +243 -0
  67. package/bizar-dash/src/server/obsidian-store.mjs +318 -0
  68. package/bizar-dash/src/server/opencode-runner.mjs +279 -0
  69. package/bizar-dash/src/server/pair-store.mjs +138 -0
  70. package/bizar-dash/src/server/projects-store.mjs +364 -0
  71. package/bizar-dash/src/server/providers-store.mjs +789 -0
  72. package/bizar-dash/src/server/routes/_shared.mjs +415 -0
  73. package/bizar-dash/src/server/routes/activity.mjs +110 -0
  74. package/bizar-dash/src/server/routes/agents.mjs +134 -0
  75. package/bizar-dash/src/server/routes/artifacts.mjs +279 -0
  76. package/bizar-dash/src/server/routes/auth.mjs +69 -0
  77. package/bizar-dash/src/server/routes/background.mjs +165 -0
  78. package/bizar-dash/src/server/routes/chat.mjs +485 -0
  79. package/bizar-dash/src/server/routes/config.mjs +129 -0
  80. package/bizar-dash/src/server/routes/diagnostics.mjs +50 -0
  81. package/bizar-dash/src/server/routes/dialogs.mjs +86 -0
  82. package/bizar-dash/src/server/routes/fs.mjs +429 -0
  83. package/bizar-dash/src/server/routes/history.mjs +78 -0
  84. package/bizar-dash/src/server/routes/memory.mjs +658 -0
  85. package/bizar-dash/src/server/routes/misc.mjs +89 -0
  86. package/bizar-dash/src/server/routes/mods.mjs +359 -0
  87. package/bizar-dash/src/server/routes/notifications.mjs +54 -0
  88. package/bizar-dash/src/server/routes/obsidian.mjs +239 -0
  89. package/bizar-dash/src/server/routes/opencode-sessions.mjs +55 -0
  90. package/bizar-dash/src/server/routes/overview.mjs +91 -0
  91. package/bizar-dash/src/server/routes/pair.mjs +67 -0
  92. package/bizar-dash/src/server/routes/projects.mjs +175 -0
  93. package/bizar-dash/src/server/routes/providers.mjs +47 -0
  94. package/bizar-dash/src/server/routes/schedules.mjs +104 -0
  95. package/bizar-dash/src/server/routes/settings.mjs +51 -0
  96. package/bizar-dash/src/server/routes/skills.mjs +76 -0
  97. package/bizar-dash/src/server/routes/tasks.mjs +468 -0
  98. package/bizar-dash/src/server/routes/themes.mjs +51 -0
  99. package/bizar-dash/src/server/routes-v2/auth.mjs +59 -0
  100. package/bizar-dash/src/server/routes-v2/events.mjs +107 -0
  101. package/bizar-dash/src/server/routes-v2/health.mjs +21 -0
  102. package/bizar-dash/src/server/routes-v2/index.mjs +90 -0
  103. package/bizar-dash/src/server/routes-v2/sessions.mjs +111 -0
  104. package/bizar-dash/src/server/schedules-runner.mjs +369 -0
  105. package/bizar-dash/src/server/schedules-store.mjs +268 -0
  106. package/bizar-dash/src/server/search-store.mjs +220 -0
  107. package/bizar-dash/src/server/serve-info.mjs +684 -0
  108. package/bizar-dash/src/server/server.mjs +796 -0
  109. package/bizar-dash/src/server/settings-store.mjs +45 -0
  110. package/bizar-dash/src/server/skills-store.mjs +300 -0
  111. package/bizar-dash/src/server/state.mjs +437 -0
  112. package/bizar-dash/src/server/tailscale-store.mjs +113 -0
  113. package/bizar-dash/src/server/task-delegator.mjs +824 -0
  114. package/bizar-dash/src/server/tasks-store.mjs +508 -0
  115. package/bizar-dash/src/server/tui.mjs +923 -0
  116. package/bizar-dash/src/server/update-store.mjs +168 -0
  117. package/bizar-dash/src/server/v2-auth-file.mjs +99 -0
  118. package/bizar-dash/src/server/v2-event-bus.mjs +128 -0
  119. package/bizar-dash/src/server/watcher.mjs +81 -0
  120. package/bizar-dash/src/server/yaml.mjs +238 -0
  121. package/bizar-dash/src/web/App.tsx +718 -0
  122. package/bizar-dash/src/web/MobileApp.tsx +408 -0
  123. package/bizar-dash/src/web/components/ArtifactCreateDialog.tsx +95 -0
  124. package/bizar-dash/src/web/components/ArtifactListDialog.tsx +55 -0
  125. package/bizar-dash/src/web/components/ArtifactViewer.tsx +143 -0
  126. package/bizar-dash/src/web/components/AuditDialog.tsx +79 -0
  127. package/bizar-dash/src/web/components/BgStatusBadge.tsx +32 -0
  128. package/bizar-dash/src/web/components/Button.tsx +56 -0
  129. package/bizar-dash/src/web/components/CanvasContextMenu.tsx +68 -0
  130. package/bizar-dash/src/web/components/Card.tsx +40 -0
  131. package/bizar-dash/src/web/components/CollapsibleSection.tsx +72 -0
  132. package/bizar-dash/src/web/components/CommandDialog.tsx +66 -0
  133. package/bizar-dash/src/web/components/EmptyState.tsx +30 -0
  134. package/bizar-dash/src/web/components/FileBrowser.tsx +633 -0
  135. package/bizar-dash/src/web/components/HelpDialog.tsx +55 -0
  136. package/bizar-dash/src/web/components/KillConfirmDialog.tsx +88 -0
  137. package/bizar-dash/src/web/components/Modal.tsx +215 -0
  138. package/bizar-dash/src/web/components/Notifications.tsx +241 -0
  139. package/bizar-dash/src/web/components/SearchModal.tsx +224 -0
  140. package/bizar-dash/src/web/components/Sidebar.tsx +66 -0
  141. package/bizar-dash/src/web/components/Spinner.tsx +19 -0
  142. package/bizar-dash/src/web/components/StatusBadge.tsx +25 -0
  143. package/bizar-dash/src/web/components/Tag.tsx +28 -0
  144. package/bizar-dash/src/web/components/Toast.tsx +150 -0
  145. package/bizar-dash/src/web/components/Topbar.tsx +368 -0
  146. package/bizar-dash/src/web/components/VisualPlanDialog.tsx +58 -0
  147. package/bizar-dash/src/web/components/chat/AgentChip.tsx +68 -0
  148. package/bizar-dash/src/web/components/chat/ChatBubble.tsx +60 -0
  149. package/bizar-dash/src/web/components/chat/ChatThread.tsx +88 -0
  150. package/bizar-dash/src/web/components/chat/ChatTopBar.tsx +50 -0
  151. package/bizar-dash/src/web/components/chat/Composer.tsx +164 -0
  152. package/bizar-dash/src/web/components/chat/ConfirmModal.tsx +54 -0
  153. package/bizar-dash/src/web/components/chat/EmptyState.tsx +21 -0
  154. package/bizar-dash/src/web/components/chat/FirstRunGreeting.tsx +18 -0
  155. package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +32 -0
  156. package/bizar-dash/src/web/components/chat/InfoPanel.tsx +119 -0
  157. package/bizar-dash/src/web/components/chat/LoadingSkeleton.tsx +19 -0
  158. package/bizar-dash/src/web/components/chat/MessageBubble.tsx +60 -0
  159. package/bizar-dash/src/web/components/chat/SessionList.tsx +153 -0
  160. package/bizar-dash/src/web/components/chat/StreamingIndicator.tsx +9 -0
  161. package/bizar-dash/src/web/components/chat/SuggestionCards.tsx +55 -0
  162. package/bizar-dash/src/web/components/chat/WelcomeScreen.tsx +36 -0
  163. package/bizar-dash/src/web/components/chat/index.ts +20 -0
  164. package/bizar-dash/src/web/components/chat/useAutoGrowTextarea.ts +18 -0
  165. package/bizar-dash/src/web/components/chat/useChat.ts +219 -0
  166. package/bizar-dash/src/web/components/chat/useSlashCommands.ts +47 -0
  167. package/bizar-dash/src/web/index.html +37 -0
  168. package/bizar-dash/src/web/lib/api.ts +193 -0
  169. package/bizar-dash/src/web/lib/markdown.tsx +59 -0
  170. package/bizar-dash/src/web/lib/types.ts +668 -0
  171. package/bizar-dash/src/web/lib/utils.ts +114 -0
  172. package/bizar-dash/src/web/lib/ws.ts +176 -0
  173. package/bizar-dash/src/web/main.tsx +34 -0
  174. package/bizar-dash/src/web/mobile/MobileApp.tsx +278 -0
  175. package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +38 -0
  176. package/bizar-dash/src/web/mobile/MobileTopbar.tsx +115 -0
  177. package/bizar-dash/src/web/mobile/components/MobileBottomSheet.tsx +182 -0
  178. package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +64 -0
  179. package/bizar-dash/src/web/mobile/components/MobileModal.tsx +123 -0
  180. package/bizar-dash/src/web/mobile/views/MobileActivity.tsx +273 -0
  181. package/bizar-dash/src/web/mobile/views/MobileAgents.tsx +374 -0
  182. package/bizar-dash/src/web/mobile/views/MobileArtifactCanvas.tsx +439 -0
  183. package/bizar-dash/src/web/mobile/views/MobileArtifacts.tsx +143 -0
  184. package/bizar-dash/src/web/mobile/views/MobileChat.tsx +180 -0
  185. package/bizar-dash/src/web/mobile/views/MobileConfig.tsx +175 -0
  186. package/bizar-dash/src/web/mobile/views/MobileHistory.tsx +191 -0
  187. package/bizar-dash/src/web/mobile/views/MobileMods.tsx +149 -0
  188. package/bizar-dash/src/web/mobile/views/MobileMore.tsx +121 -0
  189. package/bizar-dash/src/web/mobile/views/MobileNotifications.tsx +137 -0
  190. package/bizar-dash/src/web/mobile/views/MobilePlans.tsx +26 -0
  191. package/bizar-dash/src/web/mobile/views/MobileSchedules.tsx +685 -0
  192. package/bizar-dash/src/web/mobile/views/MobileSearchModal.tsx +172 -0
  193. package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +388 -0
  194. package/bizar-dash/src/web/mobile/views/MobileSkills.tsx +153 -0
  195. package/bizar-dash/src/web/mobile/views/MobileTasks.tsx +564 -0
  196. package/bizar-dash/src/web/mobile.html +20 -0
  197. package/bizar-dash/src/web/mobile.tsx +13 -0
  198. package/bizar-dash/src/web/styles/chat.css +198 -0
  199. package/bizar-dash/src/web/styles/glyphs.css +960 -0
  200. package/bizar-dash/src/web/styles/main.css +8855 -0
  201. package/bizar-dash/src/web/styles/mobile-chat.css +5 -0
  202. package/bizar-dash/src/web/styles/mobile.css +1960 -0
  203. package/bizar-dash/src/web/views/Activity.tsx +1203 -0
  204. package/bizar-dash/src/web/views/Agents.tsx +701 -0
  205. package/bizar-dash/src/web/views/Artifacts.tsx +1480 -0
  206. package/bizar-dash/src/web/views/BackgroundAgents.tsx +450 -0
  207. package/bizar-dash/src/web/views/Chat.tsx +190 -0
  208. package/bizar-dash/src/web/views/Config.tsx +2063 -0
  209. package/bizar-dash/src/web/views/History.tsx +335 -0
  210. package/bizar-dash/src/web/views/ModView.tsx +207 -0
  211. package/bizar-dash/src/web/views/Mods.tsx +693 -0
  212. package/bizar-dash/src/web/views/Overview.tsx +812 -0
  213. package/bizar-dash/src/web/views/Providers.tsx +302 -0
  214. package/bizar-dash/src/web/views/Schedules.tsx +802 -0
  215. package/bizar-dash/src/web/views/Settings.tsx +1787 -0
  216. package/bizar-dash/src/web/views/Skills.tsx +431 -0
  217. package/bizar-dash/src/web/views/Tasks.tsx +1523 -0
  218. package/bizar-dash/src/web/views/glyphs/GlyphRenderer.tsx +613 -0
  219. package/bizar-dash/src/web/views/glyphs/components.tsx +1098 -0
  220. package/bizar-dash/templates/mod/FORMAT.md +76 -0
  221. package/bizar-dash/templates/mod/hello-mod/README.md +19 -0
  222. package/bizar-dash/templates/mod/hello-mod/agents/greeter.md +8 -0
  223. package/bizar-dash/templates/mod/hello-mod/commands/hello.md +6 -0
  224. package/bizar-dash/templates/mod/hello-mod/mod.json +20 -0
  225. package/bizar-dash/templates/mod/hello-mod/routes/ping.mjs +9 -0
  226. package/bizar-dash/templates/mod/hello-mod/views/HelloView.tsx +10 -0
  227. package/bizar-dash/tests/dashboard-ports.test.mjs +138 -0
  228. package/bizar-dash/tests/graphify-mod-spawn.node.test.mjs +90 -0
  229. package/bizar-dash/tests/memory-cli.test.mjs +542 -0
  230. package/bizar-dash/tests/memory-config.test.mjs +422 -0
  231. package/bizar-dash/tests/memory-git.test.mjs +246 -0
  232. package/bizar-dash/tests/memory-lightrag.test.mjs +153 -0
  233. package/bizar-dash/tests/memory-protocol-drift.test.mjs +45 -0
  234. package/bizar-dash/tests/memory-schema.test.mjs +198 -0
  235. package/bizar-dash/tests/memory-secrets.test.mjs +121 -0
  236. package/bizar-dash/tests/memory-store.test.mjs +390 -0
  237. package/bizar-dash/tests/memory-sync.test.mjs +141 -0
  238. package/bizar-dash/tests/mod-instructions.node.test.mjs +188 -0
  239. package/bizar-dash/tests/mod-security.test.mjs +273 -0
  240. package/bizar-dash/tests/mod-upgrade.node.test.mjs +357 -0
  241. package/bizar-dash/tests/no-agent-browser.node.test.mjs +98 -0
  242. package/bizar-dash/tests/obsidian-back-compat.test.mjs +199 -0
  243. package/bizar-dash/tests/opencode-runner.test.mjs +172 -0
  244. package/bizar-dash/tests/path-safe.test.mjs +108 -0
  245. package/bizar-dash/tests/providers-store-backup-keys.node.test.mjs +333 -0
  246. package/bizar-dash/tests/smoke-v2.mjs +212 -0
  247. package/bizar-dash/tests/strip-thinking.test.mjs +175 -0
  248. package/bizar-dash/tests/submit-feedback.test.mjs +353 -0
  249. package/bizar-dash/tests/tmux-wrap.test.mjs +145 -0
  250. package/bizar-dash/tests/yaml.test.mjs +128 -0
  251. package/bizar-dash/tsconfig.json +23 -0
  252. package/bizar-dash/vite.config.ts +27 -0
  253. package/cli/atomic.mjs +73 -0
  254. package/cli/banner.mjs +1 -1
  255. package/cli/bin.mjs +44 -47
  256. package/cli/bootstrap.mjs +1 -1
  257. package/cli/copy.mjs +25 -16
  258. package/cli/dev-link.mjs +17 -1
  259. package/cli/doctor.mjs +4 -4
  260. package/cli/doctor.test.mjs +2 -2
  261. package/cli/init.mjs +135 -1
  262. package/cli/install.mjs +31 -39
  263. package/cli/memory-constants.mjs +59 -0
  264. package/cli/memory.mjs +1436 -0
  265. package/cli/service.mjs +5 -6
  266. package/cli/utils.mjs +6 -3
  267. package/config/AGENTS.md +7 -7
  268. package/config/agents/_shared/AGENT_BASELINE.md +59 -61
  269. package/config/opencode.json +13 -38
  270. package/config/skills/memory-protocol/SKILL.md +105 -0
  271. package/config/skills/obsidian/SKILL.md +270 -65
  272. package/install.sh +59 -19
  273. package/package.json +49 -14
  274. package/packages/sdk/LICENSE +21 -0
  275. package/packages/sdk/README.md +80 -0
  276. package/packages/sdk/src/client.ts +183 -0
  277. package/packages/sdk/src/errors.ts +120 -0
  278. package/packages/sdk/src/events.ts +153 -0
  279. package/packages/sdk/src/index.ts +63 -0
  280. package/packages/sdk/src/types.ts +176 -0
  281. package/packages/sdk/src/version.ts +4 -0
  282. package/packages/sdk/tests/client.test.ts +217 -0
  283. package/packages/sdk/tests/errors.test.ts +108 -0
  284. package/packages/sdk/tests/events.test.ts +139 -0
  285. package/packages/sdk/tests/fixtures/fetch-mock.ts +152 -0
  286. package/packages/sdk/tests/fixtures/sse-mock.ts +30 -0
  287. package/packages/sdk/tsconfig.json +25 -0
  288. package/packages/sdk/vitest.config.ts +9 -0
  289. package/plugins/bizar/LICENSE +21 -0
  290. package/plugins/bizar/README.md +448 -0
  291. package/plugins/bizar/dist/index.js +29901 -0
  292. package/plugins/bizar/index.ts +1491 -0
  293. package/plugins/bizar/scripts/check-forbidden-imports.sh +33 -0
  294. package/plugins/bizar/src/background-state.ts +551 -0
  295. package/plugins/bizar/src/background.ts +1250 -0
  296. package/plugins/bizar/src/commands-impl.ts +464 -0
  297. package/plugins/bizar/src/commands.ts +1168 -0
  298. package/plugins/bizar/src/dashboard-client.ts +233 -0
  299. package/plugins/bizar/src/event-stream.ts +606 -0
  300. package/plugins/bizar/src/fingerprint.ts +120 -0
  301. package/plugins/bizar/src/handoff.ts +79 -0
  302. package/plugins/bizar/src/http-client.ts +467 -0
  303. package/plugins/bizar/src/key-rotation.ts +218 -0
  304. package/plugins/bizar/src/logger.ts +144 -0
  305. package/plugins/bizar/src/loop.ts +176 -0
  306. package/plugins/bizar/src/opencode-runner.ts +390 -0
  307. package/plugins/bizar/src/options.ts +421 -0
  308. package/plugins/bizar/src/plan-fs.ts +323 -0
  309. package/plugins/bizar/src/reasoning-clean.ts +454 -0
  310. package/plugins/bizar/src/report.ts +178 -0
  311. package/plugins/bizar/src/research-prompt.ts +35 -0
  312. package/plugins/bizar/src/serve-info.ts +228 -0
  313. package/plugins/bizar/src/serve.ts +496 -0
  314. package/plugins/bizar/src/settings.ts +349 -0
  315. package/plugins/bizar/src/state.ts +298 -0
  316. package/plugins/bizar/src/tools/bg-collect.ts +104 -0
  317. package/plugins/bizar/src/tools/bg-get-comments.ts +239 -0
  318. package/plugins/bizar/src/tools/bg-kill.ts +87 -0
  319. package/plugins/bizar/src/tools/bg-spawn.ts +401 -0
  320. package/plugins/bizar/src/tools/bg-status.ts +99 -0
  321. package/plugins/bizar/src/tools/open-kb.ts +191 -0
  322. package/plugins/bizar/src/tools/plan-action.ts +767 -0
  323. package/plugins/bizar/src/tools/read-glyph-feedback.ts +170 -0
  324. package/plugins/bizar/src/tools/wait-for-feedback.ts +402 -0
  325. package/plugins/bizar/tests/attach-handler-bug.test.ts +169 -0
  326. package/plugins/bizar/tests/background-state.test.ts +277 -0
  327. package/plugins/bizar/tests/background.test.ts +402 -0
  328. package/plugins/bizar/tests/block.test.ts +195 -0
  329. package/plugins/bizar/tests/canonical-key-order.test.ts +75 -0
  330. package/plugins/bizar/tests/commands-impl.test.ts +442 -0
  331. package/plugins/bizar/tests/commands.test.ts +584 -0
  332. package/plugins/bizar/tests/config.test.ts +122 -0
  333. package/plugins/bizar/tests/dashboard-client.test.ts +159 -0
  334. package/plugins/bizar/tests/dispose.test.ts +336 -0
  335. package/plugins/bizar/tests/event-stream.test.ts +409 -0
  336. package/plugins/bizar/tests/event.test.ts +262 -0
  337. package/plugins/bizar/tests/fingerprint.test.ts +162 -0
  338. package/plugins/bizar/tests/http-client.test.ts +404 -0
  339. package/plugins/bizar/tests/init-helpers.test.ts +203 -0
  340. package/plugins/bizar/tests/integration/slash-command.test.ts +348 -0
  341. package/plugins/bizar/tests/integration/tool-routing.test.ts +314 -0
  342. package/plugins/bizar/tests/key-rotation.test.ts +396 -0
  343. package/plugins/bizar/tests/loop.test.ts +397 -0
  344. package/plugins/bizar/tests/options.test.ts +276 -0
  345. package/plugins/bizar/tests/reasoning-clean.test.ts +422 -0
  346. package/plugins/bizar/tests/serve.test.ts +339 -0
  347. package/plugins/bizar/tests/settings.test.ts +351 -0
  348. package/plugins/bizar/tests/stall-think.test.ts +750 -0
  349. package/plugins/bizar/tests/state.test.ts +276 -0
  350. package/plugins/bizar/tests/tools/bg-collect.test.ts +337 -0
  351. package/plugins/bizar/tests/tools/bg-get-comments.test.ts +485 -0
  352. package/plugins/bizar/tests/tools/bg-kill.test.ts +235 -0
  353. package/plugins/bizar/tests/tools/bg-spawn-delegation.test.ts +147 -0
  354. package/plugins/bizar/tests/tools/bg-spawn.test.ts +311 -0
  355. package/plugins/bizar/tests/tools/bg-status.test.ts +217 -0
  356. package/plugins/bizar/tests/tools/opencode-runner.test.ts +115 -0
  357. package/plugins/bizar/tests/tools/plan-action.test.ts +599 -0
  358. package/plugins/bizar/tests/tools/read-glyph-feedback.test.ts +253 -0
  359. package/plugins/bizar/tests/tools/wait-for-feedback.test.ts +390 -0
  360. package/plugins/bizar/tests/update-deadlock.test.ts +145 -0
  361. package/plugins/bizar/tsconfig.json +29 -0
@@ -0,0 +1,810 @@
1
+ <!doctype html>
2
+ <!--
3
+ ============================================================================
4
+ BizarHarness Artifact Template
5
+
6
+ WHAT THIS IS
7
+ ============
8
+ A self-contained, opinionated HTML template for artifacts produced by
9
+ BizarHarness agents. It is a starting point — agents fill in the marked
10
+ placeholders and ship the rendered output as their deliverable.
11
+
12
+ HOW TO USE IT
13
+ =============
14
+ 1. Copy this file.
15
+ 2. Replace every {{PLACEHOLDER}} with the actual content for your task.
16
+ Required placeholders are listed in the Required Sections section
17
+ below; everything else is optional.
18
+ 3. Drop the rendered HTML into the html-artifact fenced block:
19
+
20
+ ```html-artifact
21
+ {comment}artifact-name:My Report{comment-end}
22
+ <!doctype html>
23
+ ...your rendered artifact...
24
+ ```
25
+ ```
26
+
27
+ The first-line comment is optional; it sets the artifact's name in
28
+ the dashboard. If omitted, the dashboard falls back to a generic
29
+ "artifact-id" label.
30
+
31
+ 4. Save the final HTML to disk or paste it into the chat. The
32
+ bg-poller in the dashboard scans every done agent output for the
33
+ html-artifact block, saves it to ~/.bizar/artifacts/, and links
34
+ it back to the originating task.
35
+
36
+ OUTPUT CONVENTION
37
+ =================
38
+ The convention the dashboard recognizes is:
39
+
40
+ ```html-artifact
41
+ {comment}artifact-name:Optional Name Here{comment-end}
42
+ ...HTML body...
43
+ ```
44
+
45
+ - The block MUST start with the literal fence ```html-artifact (or
46
+ ```htmlartifact; both are accepted) on its own line.
47
+ - The block MUST close with a plain ``` fence on its own line.
48
+ - The first HTML comment of the body, if present, may be
49
+ `{comment} artifact-name:Foo {comment-end}` to set the
50
+ artifact display name.
51
+ - Everything else is just HTML — keep it self-contained (no
52
+ external assets, no fetch calls to localhost).
53
+
54
+ REQUIRED SECTIONS
55
+ =================
56
+ The template below is organized as `<section data-section=...>`
57
+ elements. The dashboard does NOT parse these; they are conventions
58
+ for human readers. Replace the placeholders in:
59
+
60
+ - h1 title (Hero) : page H1
61
+ - hero-subtitle (Hero) : one-line subtitle
62
+ - meta-row (Hero) : date / agent / model
63
+ - stat-card x4 (Quick Stats) : 3-4 numeric metrics
64
+ - summary-text (Summary) : TL;DR paragraph
65
+ - tab buttons + tab panels (Tabs) : at least 3 tabs
66
+ - pre blocks (Code) : syntax-highlighted code
67
+ - steps (Steps) : ordered timeline
68
+ - files (Files) : list of files touched
69
+ - next-steps (Next Steps) : recommendations
70
+
71
+ OPTIONAL SECTIONS
72
+ =================
73
+ - Screenshots (paste a base64 or https URL into the img tag)
74
+ - Interactive demo (HTMX swap region + button)
75
+ - Charts (chart.js or hand-rolled SVG)
76
+
77
+ STYLE GUIDELINES
78
+ ================
79
+ - Use the supplied CSS custom properties (accent, surface-1, text-1)
80
+ so light/dark mode keeps working.
81
+ - Don't override the body font-family — the cascade handles it.
82
+ - Stick to Tailwind utility classes for layout. Custom CSS is fine
83
+ for unique components.
84
+ - Keep markup accessible: alt text on images, labels on inputs,
85
+ aria-labels on icon-only buttons.
86
+
87
+ DEPENDENCIES (CDN)
88
+ ==================
89
+ - htmx.org 1.9.x
90
+ - alpinejs 3.x
91
+ - tailwindcss 3.x (CDN play)
92
+ - highlight.js 11.x (code blocks)
93
+ - lucide icons (inline SVG recommended to keep offline-friendly)
94
+
95
+ ALL SCRIPT TAGS use `defer` to avoid blocking the parser.
96
+ ============================================================================
97
+ -->
98
+ <html lang="en" data-theme="auto">
99
+ <head>
100
+ <meta charset="UTF-8" />
101
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
102
+ <title>{{ARTIFACT_TITLE}} — BizarHarness</title>
103
+
104
+ <!-- Tailwind CSS via CDN. Configured to respect dark mode + theme tokens. -->
105
+ <script src="https://cdn.tailwindcss.com"></script>
106
+ <script>
107
+ // Tailwind play CDN: configure the design tokens so utility classes
108
+ // map to the CSS custom properties defined in :root below.
109
+ tailwind.config = {
110
+ darkMode: 'media',
111
+ theme: {
112
+ extend: {
113
+ fontFamily: {
114
+ sans: ['Inter', 'system-ui', '-apple-system', 'Segoe UI', 'Roboto', 'sans-serif'],
115
+ mono: ['JetBrains Mono', 'ui-monospace', 'SFMono-Regular', 'Menlo', 'monospace'],
116
+ display: ['"Cal Sans"', 'Inter', 'system-ui', 'sans-serif'],
117
+ },
118
+ colors: {
119
+ accent: 'rgb(var(--accent) / <alpha-value>)',
120
+ surface: {
121
+ 0: 'rgb(var(--surface-0) / <alpha-value>)',
122
+ 1: 'rgb(var(--surface-1) / <alpha-value>)',
123
+ 2: 'rgb(var(--surface-2) / <alpha-value>)',
124
+ 3: 'rgb(var(--surface-3) / <alpha-value>)',
125
+ },
126
+ ink: {
127
+ 0: 'rgb(var(--text-0) / <alpha-value>)',
128
+ 1: 'rgb(var(--text-1) / <alpha-value>)',
129
+ 2: 'rgb(var(--text-2) / <alpha-value>)',
130
+ },
131
+ line: 'rgb(var(--line) / <alpha-value>)',
132
+ },
133
+ },
134
+ },
135
+ };
136
+ </script>
137
+
138
+ <!-- Inter (UI) + JetBrains Mono (code) + Cal Sans (display). -->
139
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
140
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
141
+ <link
142
+ rel="stylesheet"
143
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap"
144
+ />
145
+ <link
146
+ rel="stylesheet"
147
+ href="https://fonts.cdnfonts.com/css/cal-sans"
148
+ />
149
+
150
+ <!-- highlight.js for code blocks (atom-one-dark + atom-one-light). -->
151
+ <link
152
+ rel="stylesheet"
153
+ href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"
154
+ media="(prefers-color-scheme: dark)"
155
+ />
156
+ <link
157
+ rel="stylesheet"
158
+ href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-light.min.css"
159
+ media="(prefers-color-scheme: light)"
160
+ />
161
+
162
+ <style>
163
+ /* --------------------------------------------------------------------
164
+ Design tokens — light & dark variants
165
+ Light mode: warm white surfaces, indigo accent.
166
+ Dark mode: graphite surfaces, violet accent.
167
+ Agents may override --accent to brand their artifact.
168
+ -------------------------------------------------------------------- */
169
+ :root {
170
+ --accent: 99 102 241; /* indigo-500 (light) */
171
+ --accent-strong: 79 70 229; /* indigo-600 */
172
+ --surface-0: 250 250 252; /* page bg (light) */
173
+ --surface-1: 255 255 255; /* card bg (light) */
174
+ --surface-2: 244 244 248; /* sunken (light) */
175
+ --surface-3: 228 228 235; /* border-ish (light) */
176
+ --text-0: 17 24 39; /* primary (light) */
177
+ --text-1: 55 65 81; /* secondary (light) */
178
+ --text-2: 107 114 128; /* tertiary (light) */
179
+ --line: 226 232 240;
180
+ --success: 16 185 129;
181
+ --warning: 245 158 11;
182
+ --danger: 239 68 68;
183
+ }
184
+ @media (prefers-color-scheme: dark) {
185
+ :root {
186
+ --accent: 167 139 250; /* violet-400 */
187
+ --accent-strong: 139 92 246; /* violet-500 */
188
+ --surface-0: 9 9 11; /* zinc-950 */
189
+ --surface-1: 17 17 23; /* zinc-900 */
190
+ --surface-2: 24 24 31; /* custom */
191
+ --surface-3: 39 39 48;
192
+ --text-0: 244 244 245; /* zinc-100 */
193
+ --text-1: 212 212 216; /* zinc-300 */
194
+ --text-2: 161 161 170; /* zinc-400 */
195
+ --line: 39 39 48;
196
+ --success: 52 211 153;
197
+ --warning: 251 191 36;
198
+ --danger: 248 113 113;
199
+ }
200
+ }
201
+
202
+ /* Body / base typography. */
203
+ html, body {
204
+ background: rgb(var(--surface-0));
205
+ color: rgb(var(--text-0));
206
+ font-family: 'Inter', system-ui, sans-serif;
207
+ font-feature-settings: 'cv11', 'ss01', 'ss03';
208
+ -webkit-font-smoothing: antialiased;
209
+ }
210
+ .font-display { font-family: 'Cal Sans', 'Inter', system-ui, sans-serif; letter-spacing: -0.02em; }
211
+ .font-mono { font-family: 'JetBrains Mono', ui-monospace, monospace; }
212
+
213
+ /* --------------------------------------------------------------------
214
+ Hero — gradient halo behind the title
215
+ -------------------------------------------------------------------- */
216
+ .hero {
217
+ position: relative;
218
+ overflow: hidden;
219
+ border-radius: 1.5rem;
220
+ background:
221
+ radial-gradient(ellipse at top left, rgb(var(--accent) / 0.12), transparent 60%),
222
+ radial-gradient(ellipse at bottom right, rgb(var(--accent-strong) / 0.10), transparent 60%),
223
+ linear-gradient(180deg, rgb(var(--surface-1)), rgb(var(--surface-1)));
224
+ border: 1px solid rgb(var(--line));
225
+ }
226
+ .hero::before {
227
+ content: "";
228
+ position: absolute;
229
+ inset: 0;
230
+ background-image:
231
+ linear-gradient(rgb(var(--line) / 0.4) 1px, transparent 1px),
232
+ linear-gradient(90deg, rgb(var(--line) / 0.4) 1px, transparent 1px);
233
+ background-size: 40px 40px;
234
+ mask-image: radial-gradient(ellipse at center, black 30%, transparent 75%);
235
+ pointer-events: none;
236
+ opacity: 0.5;
237
+ }
238
+ .hero > * { position: relative; z-index: 1; }
239
+
240
+ /* --------------------------------------------------------------------
241
+ Cards / surfaces
242
+ -------------------------------------------------------------------- */
243
+ .card {
244
+ background: rgb(var(--surface-1));
245
+ border: 1px solid rgb(var(--line));
246
+ border-radius: 1rem;
247
+ }
248
+ .card-sunken {
249
+ background: rgb(var(--surface-2));
250
+ border: 1px solid rgb(var(--line));
251
+ border-radius: 0.75rem;
252
+ }
253
+
254
+ /* --------------------------------------------------------------------
255
+ Stat card
256
+ -------------------------------------------------------------------- */
257
+ .stat-card {
258
+ background: rgb(var(--surface-1));
259
+ border: 1px solid rgb(var(--line));
260
+ border-radius: 1rem;
261
+ padding: 1.25rem;
262
+ transition: transform 200ms ease, border-color 200ms ease;
263
+ }
264
+ .stat-card:hover {
265
+ transform: translateY(-2px);
266
+ border-color: rgb(var(--accent) / 0.5);
267
+ }
268
+ .stat-value {
269
+ font-family: 'Cal Sans', 'Inter', sans-serif;
270
+ font-size: 2.25rem;
271
+ line-height: 1;
272
+ letter-spacing: -0.03em;
273
+ color: rgb(var(--text-0));
274
+ }
275
+ .stat-delta-up { color: rgb(var(--success)); }
276
+ .stat-delta-down { color: rgb(var(--danger)); }
277
+ .stat-delta-flat { color: rgb(var(--text-2)); }
278
+
279
+ /* --------------------------------------------------------------------
280
+ Tabs
281
+ -------------------------------------------------------------------- */
282
+ .tab-bar {
283
+ display: flex;
284
+ gap: 0.25rem;
285
+ padding: 0.25rem;
286
+ background: rgb(var(--surface-2));
287
+ border: 1px solid rgb(var(--line));
288
+ border-radius: 0.75rem;
289
+ overflow-x: auto;
290
+ scrollbar-width: none;
291
+ }
292
+ .tab-bar::-webkit-scrollbar { display: none; }
293
+ .tab {
294
+ flex: none;
295
+ padding: 0.5rem 1rem;
296
+ border-radius: 0.5rem;
297
+ font-weight: 500;
298
+ font-size: 0.875rem;
299
+ color: rgb(var(--text-1));
300
+ cursor: pointer;
301
+ transition: all 150ms ease;
302
+ user-select: none;
303
+ }
304
+ .tab:hover { color: rgb(var(--text-0)); }
305
+ .tab[aria-selected="true"] {
306
+ background: rgb(var(--surface-1));
307
+ color: rgb(var(--accent));
308
+ box-shadow: 0 1px 2px rgba(0,0,0,0.04);
309
+ }
310
+ .tab-panel { display: none; }
311
+ .tab-panel[data-active] { display: block; animation: fadeIn 200ms ease; }
312
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
313
+
314
+ /* --------------------------------------------------------------------
315
+ Code blocks — pad the highlighted pre so line numbers (if any) align
316
+ -------------------------------------------------------------------- */
317
+ pre.codeblock {
318
+ background: #0d1117; /* GitHub dark — works on both light/dark */
319
+ color: #c9d1d9;
320
+ border-radius: 0.75rem;
321
+ padding: 1.25rem 1.5rem;
322
+ overflow-x: auto;
323
+ font-family: 'JetBrains Mono', monospace;
324
+ font-size: 0.85rem;
325
+ line-height: 1.6;
326
+ border: 1px solid rgb(var(--line));
327
+ }
328
+ pre.codeblock code { font-family: inherit; }
329
+
330
+ /* --------------------------------------------------------------------
331
+ Steps timeline
332
+ -------------------------------------------------------------------- */
333
+ .step {
334
+ display: grid;
335
+ grid-template-columns: auto 1fr;
336
+ gap: 1rem;
337
+ align-items: flex-start;
338
+ padding: 0.75rem 0;
339
+ }
340
+ .step-dot {
341
+ width: 1.75rem;
342
+ height: 1.75rem;
343
+ border-radius: 9999px;
344
+ background: rgb(var(--accent) / 0.12);
345
+ color: rgb(var(--accent));
346
+ display: flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ font-weight: 600;
350
+ font-size: 0.85rem;
351
+ flex: none;
352
+ border: 1px solid rgb(var(--accent) / 0.3);
353
+ }
354
+ .step-line {
355
+ width: 2px;
356
+ background: rgb(var(--line));
357
+ margin-left: 0.85rem;
358
+ flex: none;
359
+ }
360
+
361
+ /* --------------------------------------------------------------------
362
+ File rows
363
+ -------------------------------------------------------------------- */
364
+ .file-row {
365
+ display: flex;
366
+ align-items: center;
367
+ gap: 0.75rem;
368
+ padding: 0.625rem 0.75rem;
369
+ border-radius: 0.5rem;
370
+ font-family: 'JetBrains Mono', monospace;
371
+ font-size: 0.85rem;
372
+ transition: background 100ms ease;
373
+ }
374
+ .file-row:hover { background: rgb(var(--surface-2)); }
375
+ .file-icon {
376
+ width: 1rem;
377
+ height: 1rem;
378
+ flex: none;
379
+ color: rgb(var(--text-2));
380
+ }
381
+
382
+ /* --------------------------------------------------------------------
383
+ Pills / badges
384
+ -------------------------------------------------------------------- */
385
+ .pill {
386
+ display: inline-flex;
387
+ align-items: center;
388
+ gap: 0.25rem;
389
+ padding: 0.125rem 0.625rem;
390
+ border-radius: 9999px;
391
+ font-size: 0.75rem;
392
+ font-weight: 500;
393
+ background: rgb(var(--surface-2));
394
+ color: rgb(var(--text-1));
395
+ border: 1px solid rgb(var(--line));
396
+ }
397
+ .pill-accent { background: rgb(var(--accent) / 0.10); color: rgb(var(--accent)); border-color: rgb(var(--accent) / 0.3); }
398
+ .pill-success { background: rgb(var(--success) / 0.10); color: rgb(var(--success)); border-color: rgb(var(--success) / 0.3); }
399
+ .pill-warning { background: rgb(var(--warning) / 0.10); color: rgb(var(--warning)); border-color: rgb(var(--warning) / 0.3); }
400
+
401
+ /* --------------------------------------------------------------------
402
+ Footer signature
403
+ -------------------------------------------------------------------- */
404
+ .signature {
405
+ font-family: 'Cal Sans', 'Inter', sans-serif;
406
+ background: linear-gradient(120deg, rgb(var(--accent)), rgb(var(--accent-strong)));
407
+ -webkit-background-clip: text;
408
+ background-clip: text;
409
+ -webkit-text-fill-color: transparent;
410
+ }
411
+
412
+ /* --------------------------------------------------------------------
413
+ Small utility — clickable cursor on Alpine x-cloak hidden elements
414
+ -------------------------------------------------------------------- */
415
+ [x-cloak] { display: none !important; }
416
+
417
+ /* --------------------------------------------------------------------
418
+ Print: drop the gradient hero for a clean printout
419
+ -------------------------------------------------------------------- */
420
+ @media print {
421
+ .hero::before { display: none; }
422
+ .tab-bar { display: none; }
423
+ .tab-panel { display: block !important; }
424
+ }
425
+ </style>
426
+ </head>
427
+ <body class="min-h-screen antialiased">
428
+
429
+ <!-- ====================================================================
430
+ Top nav (lightweight — just a back-link + theme indicator)
431
+ ==================================================================== -->
432
+ <header class="border-b border-line">
433
+ <div class="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
434
+ <a href="/" class="flex items-center gap-2 text-ink-1 hover:text-ink-0 transition">
435
+ <svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
436
+ <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
437
+ </svg>
438
+ <span class="font-medium text-sm">BizarHarness</span>
439
+ </a>
440
+ <div class="flex items-center gap-3 text-xs text-ink-2">
441
+ <span class="pill pill-accent">v3.5.5</span>
442
+ <span>{{AGENT_NAME}}</span>
443
+ </div>
444
+ </div>
445
+ </header>
446
+
447
+ <main class="max-w-6xl mx-auto px-6 py-10 space-y-12">
448
+
449
+ <!-- ================================================================
450
+ HERO — title, subtitle, metadata row
451
+ ================================================================ -->
452
+ <section class="hero px-8 py-12 md:px-12 md:py-16" data-section="hero" x-data="{ ts: new Date() }" x-init="() => {}">
453
+ <div class="flex flex-col gap-4">
454
+ <div class="flex items-center gap-2">
455
+ <span class="pill pill-success">
456
+ <svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
457
+ <path d="M5 13l4 4L19 7"></path>
458
+ </svg>
459
+ Completed
460
+ </span>
461
+ <span class="pill">{{ARTIFACT_TYPE}}</span>
462
+ <span class="pill" x-text="ts.toLocaleString()">—</span>
463
+ </div>
464
+
465
+ <h1 class="font-display text-4xl md:text-5xl lg:text-6xl text-ink-0 leading-[1.05]">
466
+ {{ARTIFACT_TITLE}}
467
+ </h1>
468
+
469
+ <p class="hero-subtitle text-ink-1 text-lg md:text-xl max-w-3xl">
470
+ {{ARTIFACT_SUBTITLE}}
471
+ </p>
472
+
473
+ <!-- Meta row: date · agent · model · duration · tokens -->
474
+ <div class="meta-row mt-4 flex flex-wrap gap-x-6 gap-y-2 text-sm text-ink-2">
475
+ <div class="flex items-center gap-1.5">
476
+ <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
477
+ <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
478
+ <line x1="16" y1="2" x2="16" y2="6"></line>
479
+ <line x1="8" y1="2" x2="8" y2="6"></line>
480
+ <line x1="3" y1="10" x2="21" y2="10"></line>
481
+ </svg>
482
+ <span>{{DATE}}</span>
483
+ </div>
484
+ <div class="flex items-center gap-1.5">
485
+ <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
486
+ <path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"></path>
487
+ <circle cx="12" cy="7" r="4"></circle>
488
+ </svg>
489
+ <span>{{AGENT_NAME}}</span>
490
+ </div>
491
+ <div class="flex items-center gap-1.5">
492
+ <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
493
+ <circle cx="12" cy="12" r="3"></circle>
494
+ <path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"></path>
495
+ </svg>
496
+ <span>{{MODEL}}</span>
497
+ </div>
498
+ <div class="flex items-center gap-1.5">
499
+ <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
500
+ <circle cx="12" cy="12" r="10"></circle>
501
+ <polyline points="12 6 12 12 16 14"></polyline>
502
+ </svg>
503
+ <span>{{DURATION}}</span>
504
+ </div>
505
+ </div>
506
+ </div>
507
+ </section>
508
+
509
+ <!-- ================================================================
510
+ QUICK STATS — 3-4 metric cards
511
+ ================================================================ -->
512
+ <section data-section="stats">
513
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
514
+ <div class="stat-card">
515
+ <div class="text-xs text-ink-2 uppercase tracking-wider font-medium">Lines changed</div>
516
+ <div class="stat-value mt-2">{{STAT_1_VALUE}}</div>
517
+ <div class="mt-2 text-xs stat-delta-up flex items-center gap-1">
518
+ <svg class="w-3 h-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 15l7-7 7 7"></path></svg>
519
+ <span>{{STAT_1_DELTA}}</span>
520
+ </div>
521
+ </div>
522
+ <div class="stat-card">
523
+ <div class="text-xs text-ink-2 uppercase tracking-wider font-medium">Files touched</div>
524
+ <div class="stat-value mt-2">{{STAT_2_VALUE}}</div>
525
+ <div class="mt-2 text-xs stat-delta-flat">{{STAT_2_NOTE}}</div>
526
+ </div>
527
+ <div class="stat-card">
528
+ <div class="text-xs text-ink-2 uppercase tracking-wider font-medium">Tests passing</div>
529
+ <div class="stat-value mt-2">{{STAT_3_VALUE}}</div>
530
+ <div class="mt-2 text-xs stat-delta-up">{{STAT_3_NOTE}}</div>
531
+ </div>
532
+ <div class="stat-card">
533
+ <div class="text-xs text-ink-2 uppercase tracking-wider font-medium">Tokens used</div>
534
+ <div class="stat-value mt-2">{{STAT_4_VALUE}}</div>
535
+ <div class="mt-2 text-xs stat-delta-down">{{STAT_4_NOTE}}</div>
536
+ </div>
537
+ </div>
538
+ </section>
539
+
540
+ <!-- ================================================================
541
+ SUMMARY — TL;DR paragraph
542
+ ================================================================ -->
543
+ <section data-section="summary" class="card p-8 md:p-10">
544
+ <div class="flex items-baseline justify-between mb-4">
545
+ <h2 class="font-display text-2xl text-ink-0">Summary</h2>
546
+ <span class="text-xs text-ink-2">TL;DR</span>
547
+ </div>
548
+ <p class="summary-text text-ink-1 text-base md:text-lg leading-relaxed">
549
+ {{SUMMARY_TEXT}}
550
+ </p>
551
+ </section>
552
+
553
+ <!-- ================================================================
554
+ TABS — Overview, Details, Code, Demo
555
+ ================================================================ -->
556
+ <section data-section="tabs" x-data="{ tab: 'overview' }">
557
+ <div class="tab-bar mb-6" role="tablist">
558
+ <button class="tab" :aria-selected="tab === 'overview'" @click="tab = 'overview'" role="tab">Overview</button>
559
+ <button class="tab" :aria-selected="tab === 'details'" @click="tab = 'details'" role="tab">Details</button>
560
+ <button class="tab" :aria-selected="tab === 'code'" @click="tab = 'code'" role="tab">Code</button>
561
+ <button class="tab" :aria-selected="tab === 'demo'" @click="tab = 'demo'" role="tab">Demo</button>
562
+ </div>
563
+
564
+ <!-- Overview tab -->
565
+ <div class="tab-panel" :data-active="tab === 'overview'" role="tabpanel">
566
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
567
+ <div class="card p-6">
568
+ <h3 class="font-display text-xl mb-3 text-ink-0">What changed</h3>
569
+ <p class="text-ink-1 leading-relaxed">{{OVERVIEW_WHAT_CHANGED}}</p>
570
+ </div>
571
+ <div class="card p-6">
572
+ <h3 class="font-display text-xl mb-3 text-ink-0">Why it matters</h3>
573
+ <p class="text-ink-1 leading-relaxed">{{OVERVIEW_WHY_IT_MATTERS}}</p>
574
+ </div>
575
+ </div>
576
+ </div>
577
+
578
+ <!-- Details tab -->
579
+ <div class="tab-panel" :data-active="tab === 'details'" role="tabpanel">
580
+ <div class="card p-6">
581
+ <h3 class="font-display text-xl mb-3 text-ink-0">Details</h3>
582
+ <div class="prose text-ink-1 max-w-none">
583
+ <p>{{DETAILS_BODY}}</p>
584
+ <ul class="mt-4 space-y-2 list-disc list-inside text-ink-1">
585
+ <li>{{DETAILS_LI_1}}</li>
586
+ <li>{{DETAILS_LI_2}}</li>
587
+ <li>{{DETAILS_LI_3}}</li>
588
+ </ul>
589
+ </div>
590
+ </div>
591
+ </div>
592
+
593
+ <!-- Code tab -->
594
+ <div class="tab-panel" :data-active="tab === 'code'" role="tabpanel">
595
+ <div class="space-y-6">
596
+ <div>
597
+ <div class="flex items-center justify-between mb-2">
598
+ <h3 class="font-display text-xl text-ink-0">Primary change</h3>
599
+ <button
600
+ class="pill hover:bg-surface-3 transition"
601
+ onclick="navigator.clipboard.writeText(this.previousElementSibling.querySelector('code').innerText).then(() => this.textContent='Copied!').catch(() => this.textContent='Failed')"
602
+ >
603
+ Copy
604
+ </button>
605
+ </div>
606
+ <div>
607
+ <pre class="codeblock"><code class="language-javascript">// {{CODE_FILE_1}}
608
+ {{CODE_SNIPPET_1}}</code></pre>
609
+ </div>
610
+ </div>
611
+ <div>
612
+ <h3 class="font-display text-xl mb-2 text-ink-0">Supporting code</h3>
613
+ <pre class="codeblock"><code class="language-bash">$ {{CODE_SNIPPET_2}}</code></pre>
614
+ </div>
615
+ </div>
616
+ </div>
617
+
618
+ <!-- Demo tab — interactive HTMX swap -->
619
+ <div class="tab-panel" :data-active="tab === 'demo'" role="tabpanel">
620
+ <div class="card p-6">
621
+ <h3 class="font-display text-xl mb-3 text-ink-0">Interactive demo</h3>
622
+ <p class="text-ink-1 mb-4">
623
+ Click the button — the dashboard fetches a small JSON snippet
624
+ from <code class="text-accent">/api/health</code> and swaps it
625
+ into the region below via HTMX. This is a live demo; HTMX is
626
+ the same library the dashboard uses for partial updates.
627
+ </p>
628
+
629
+ <!-- HTMX swap region. hx-get fires when the button is pressed. -->
630
+ <div
631
+ id="demo-region"
632
+ class="card-sunken p-4 font-mono text-sm text-ink-1 min-h-[5rem]"
633
+ hx-get="/api/health"
634
+ hx-trigger="demo-requested from:body"
635
+ hx-swap="innerHTML"
636
+ >
637
+ <span class="text-ink-2">// press the button to load /api/health</span>
638
+ </div>
639
+
640
+ <button
641
+ class="mt-4 inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-accent text-white font-medium hover:opacity-90 transition"
642
+ onclick="htmx.trigger('#demo-region', 'demo-requested')"
643
+ >
644
+ <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
645
+ <polyline points="23 4 23 10 17 10"></polyline>
646
+ <polyline points="1 20 1 14 7 14"></polyline>
647
+ <path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"></path>
648
+ </svg>
649
+ Load /api/health
650
+ </button>
651
+ </div>
652
+ </div>
653
+ </section>
654
+
655
+ <!-- ================================================================
656
+ STEPS / TIMELINE — what the agent did, in order
657
+ ================================================================ -->
658
+ <section data-section="steps" class="card p-8">
659
+ <h2 class="font-display text-2xl text-ink-0 mb-6">Process</h2>
660
+ <ol class="space-y-2">
661
+ <li class="step">
662
+ <div class="step-dot">1</div>
663
+ <div>
664
+ <h4 class="font-medium text-ink-0">{{STEP_1_TITLE}}</h4>
665
+ <p class="text-ink-1 text-sm mt-1">{{STEP_1_DESC}}</p>
666
+ </div>
667
+ </li>
668
+ <li class="step">
669
+ <div class="step-dot">2</div>
670
+ <div>
671
+ <h4 class="font-medium text-ink-0">{{STEP_2_TITLE}}</h4>
672
+ <p class="text-ink-1 text-sm mt-1">{{STEP_2_DESC}}</p>
673
+ </div>
674
+ </li>
675
+ <li class="step">
676
+ <div class="step-dot">3</div>
677
+ <div>
678
+ <h4 class="font-medium text-ink-0">{{STEP_3_TITLE}}</h4>
679
+ <p class="text-ink-1 text-sm mt-1">{{STEP_3_DESC}}</p>
680
+ </div>
681
+ </li>
682
+ <li class="step">
683
+ <div class="step-dot">4</div>
684
+ <div>
685
+ <h4 class="font-medium text-ink-0">{{STEP_4_TITLE}}</h4>
686
+ <p class="text-ink-1 text-sm mt-1">{{STEP_4_DESC}}</p>
687
+ </div>
688
+ </li>
689
+ </ol>
690
+ </section>
691
+
692
+ <!-- ================================================================
693
+ FILES TOUCHED
694
+ ================================================================ -->
695
+ <section data-section="files" class="card p-8">
696
+ <h2 class="font-display text-2xl text-ink-0 mb-4">Files touched</h2>
697
+ <div class="space-y-1">
698
+ <div class="file-row">
699
+ <svg class="file-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
700
+ <path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"></path>
701
+ <polyline points="13 2 13 9 20 9"></polyline>
702
+ </svg>
703
+ <span class="flex-1 text-ink-1">{{FILE_1_PATH}}</span>
704
+ <span class="pill pill-success text-xs">{{FILE_1_DELTA}}</span>
705
+ </div>
706
+ <div class="file-row">
707
+ <svg class="file-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
708
+ <path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"></path>
709
+ <polyline points="13 2 13 9 20 9"></polyline>
710
+ </svg>
711
+ <span class="flex-1 text-ink-1">{{FILE_2_PATH}}</span>
712
+ <span class="pill pill-warning text-xs">{{FILE_2_DELTA}}</span>
713
+ </div>
714
+ <div class="file-row">
715
+ <svg class="file-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
716
+ <path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"></path>
717
+ <polyline points="13 2 13 9 20 9"></polyline>
718
+ </svg>
719
+ <span class="flex-1 text-ink-1">{{FILE_3_PATH}}</span>
720
+ <span class="pill text-xs">{{FILE_3_DELTA}}</span>
721
+ </div>
722
+ </div>
723
+ </section>
724
+
725
+ <!-- ================================================================
726
+ NEXT STEPS
727
+ ================================================================ -->
728
+ <section data-section="next-steps" class="card p-8">
729
+ <h2 class="font-display text-2xl text-ink-0 mb-4">Next steps</h2>
730
+ <ul class="space-y-3 text-ink-1">
731
+ <li class="flex items-start gap-3">
732
+ <span class="step-dot !w-6 !h-6 !text-xs">1</span>
733
+ <span>{{NEXT_STEP_1}}</span>
734
+ </li>
735
+ <li class="flex items-start gap-3">
736
+ <span class="step-dot !w-6 !h-6 !text-xs">2</span>
737
+ <span>{{NEXT_STEP_2}}</span>
738
+ </li>
739
+ <li class="flex items-start gap-3">
740
+ <span class="step-dot !w-6 !h-6 !text-xs">3</span>
741
+ <span>{{NEXT_STEP_3}}</span>
742
+ </li>
743
+ </ul>
744
+ </section>
745
+
746
+ <!-- ================================================================
747
+ FOOTER
748
+ ================================================================ -->
749
+ <footer class="border-t border-line pt-8 pb-12">
750
+ <div class="flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
751
+ <div>
752
+ <div class="text-xs text-ink-2 uppercase tracking-wider mb-1">Generated by</div>
753
+ <div class="signature text-2xl">{{AGENT_NAME}}</div>
754
+ </div>
755
+ <div class="flex items-center gap-3 text-sm">
756
+ <a href="/" class="text-ink-1 hover:text-ink-0 transition flex items-center gap-1.5">
757
+ <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
758
+ <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
759
+ </svg>
760
+ Dashboard
761
+ </a>
762
+ <span class="text-ink-3">·</span>
763
+ <a href="#" class="text-ink-1 hover:text-ink-0 transition">View task</a>
764
+ <span class="text-ink-3">·</span>
765
+ <a href="#" class="text-ink-1 hover:text-ink-0 transition">Share artifact</a>
766
+ </div>
767
+ </div>
768
+ <p class="text-xs text-ink-2 mt-4">
769
+ Built with HTMX · Alpine.js · Tailwind CSS · highlight.js
770
+ </p>
771
+ </footer>
772
+
773
+ </main>
774
+
775
+ <!-- HTMX, Alpine.js, highlight.js. All `defer` so they don't block. -->
776
+ <script src="https://unpkg.com/htmx.org@1.9.12" defer></script>
777
+ <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.13.5/dist/cdn.min.js" defer></script>
778
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" defer></script>
779
+ <script>
780
+ // Once the page is fully loaded, syntax-highlight any <pre><code>
781
+ // blocks. The CDN script is `defer`-ed so we wait for it.
782
+ document.addEventListener('DOMContentLoaded', function () {
783
+ // highlight.js may have finished loading by DOMContentLoaded when
784
+ // the page is fast; otherwise poll briefly.
785
+ function tryHighlight(attempts) {
786
+ if (window.hljs) {
787
+ document.querySelectorAll('pre.codeblock code').forEach(function (el) {
788
+ try { window.hljs.highlightElement(el); } catch (e) { /* ignore */ }
789
+ });
790
+ } else if (attempts > 0) {
791
+ setTimeout(function () { tryHighlight(attempts - 1); }, 50);
792
+ }
793
+ }
794
+ tryHighlight(20);
795
+ });
796
+
797
+ // Tiny enhancement: format the HTMX demo response as pretty JSON
798
+ // (it arrives as a string when the response Content-Type is not
799
+ // application/json). We listen to htmx:afterSwap to re-render.
800
+ document.addEventListener('htmx:afterSwap', function (evt) {
801
+ if (evt.target && evt.target.id === 'demo-region') {
802
+ try {
803
+ const obj = JSON.parse(evt.target.textContent);
804
+ evt.target.textContent = JSON.stringify(obj, null, 2);
805
+ } catch (e) { /* leave as-is */ }
806
+ }
807
+ });
808
+ </script>
809
+ </body>
810
+ </html>