@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,153 @@
1
+ /**
2
+ * tests/memory-lightrag.test.mjs
3
+ *
4
+ * v4.1.0 — Tests for the LightRAG orchestrator.
5
+ *
6
+ * Skips entirely if `lightrag-server` is not installed (the user can
7
+ * install it with `uv tool install "lightrag-hku[api]"`).
8
+ */
9
+
10
+ import { test, describe, before } from 'node:test';
11
+ import assert from 'node:assert/strict';
12
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ import { tmpdir } from 'node:os';
15
+ import { fileURLToPath } from 'node:url';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = import.meta.dirname || join(__filename, '..');
19
+
20
+ const {
21
+ findLightragBinary,
22
+ isInstalled,
23
+ resolveLightRAGConfig,
24
+ reindexVault,
25
+ query,
26
+ isRunning,
27
+ } = await import('../src/server/memory-lightrag.mjs');
28
+
29
+ const lightragAvailable = findLightragBinary() !== null;
30
+
31
+ describe('memory-lightrag — preflight', { skip: !lightragAvailable }, () => {
32
+ test('findLightragBinary returns a path', () => {
33
+ const bin = findLightragBinary();
34
+ assert.ok(bin, 'expected binary path');
35
+ assert.ok(existsSync(bin), `expected ${bin} to exist`);
36
+ });
37
+
38
+ test('isInstalled returns true', async () => {
39
+ const ok = await isInstalled();
40
+ assert.equal(ok, true);
41
+ });
42
+ });
43
+
44
+ describe('memory-lightrag — config', { skip: !lightragAvailable }, () => {
45
+ test('resolveLightRAGConfig returns defaults when no memory.json', () => {
46
+ const fakeRoot = join(tmpdir(), 'bizar-memlight-test-no-config-' + Date.now());
47
+ mkdirSync(fakeRoot, { recursive: true });
48
+ try {
49
+ const cfg = resolveLightRAGConfig(fakeRoot);
50
+ assert.equal(cfg.enabled, true);
51
+ assert.equal(cfg.host, '127.0.0.1');
52
+ assert.equal(cfg.port, 9621);
53
+ assert.ok(cfg.workingDir.includes('.bizar/lightrag'));
54
+ } finally {
55
+ rmSync(fakeRoot, { recursive: true, force: true });
56
+ }
57
+ });
58
+
59
+ test('resolveLightRAGConfig reads user memory.json', () => {
60
+ const fakeRoot = join(tmpdir(), 'bizar-memlight-test-cfg-' + Date.now());
61
+ mkdirSync(join(fakeRoot, '.bizar'), { recursive: true });
62
+ writeFileSync(join(fakeRoot, '.bizar', 'memory.json'), JSON.stringify({
63
+ lightrag: { enabled: false, port: 9999, host: '0.0.0.0' },
64
+ }));
65
+ try {
66
+ const cfg = resolveLightRAGConfig(fakeRoot);
67
+ assert.equal(cfg.enabled, false);
68
+ assert.equal(cfg.port, 9999);
69
+ assert.equal(cfg.host, '0.0.0.0');
70
+ } finally {
71
+ rmSync(fakeRoot, { recursive: true, force: true });
72
+ }
73
+ });
74
+ });
75
+
76
+ describe('memory-lightrag — reindex (live)', { skip: !lightragAvailable }, () => {
77
+ let tmpRoot;
78
+ let noteCount = 0;
79
+
80
+ before(async () => {
81
+ tmpRoot = join(tmpdir(), 'bizar-memlight-test-' + Date.now());
82
+ const bizarDir = join(tmpRoot, '.bizar');
83
+ mkdirSync(join(bizarDir, 'memory'), { recursive: true });
84
+ mkdirSync(join(bizarDir, 'memory', 'projects', 'testproj'), { recursive: true });
85
+
86
+ // Write a minimal memory.json (managed mode).
87
+ writeFileSync(join(bizarDir, 'memory.json'), JSON.stringify({
88
+ version: 1,
89
+ backend: 'bizar-local',
90
+ projectId: 'testproj',
91
+ memoryRepo: {
92
+ mode: 'managed',
93
+ path: join(bizarDir, 'memory'),
94
+ remote: null,
95
+ branch: 'main',
96
+ namespace: 'projects/testproj',
97
+ },
98
+ namespaces: {
99
+ project: 'projects/testproj',
100
+ global: 'global/bizar',
101
+ user: 'users/local',
102
+ },
103
+ lightrag: {
104
+ enabled: true,
105
+ host: '127.0.0.1',
106
+ port: 9621,
107
+ workingDir: join(bizarDir, 'lightrag'),
108
+ },
109
+ }));
110
+
111
+ // Write a couple of test notes.
112
+ const noteA = `---
113
+ memory_id: mem_test_a
114
+ type: architecture_decision
115
+ project_id: testproj
116
+ status: active
117
+ confidence: verified
118
+ created: 2026-06-29T00:00:00Z
119
+ updated: 2026-06-29T00:00:00Z
120
+ tags: [test]
121
+ ---
122
+
123
+ # Test decision A
124
+
125
+ LightRAG should be reachable.`;
126
+ writeFileSync(join(bizarDir, 'memory', 'projects', 'testproj', 'a.md'), noteA);
127
+ noteCount = 1;
128
+ });
129
+
130
+ test('reindexVault writes a marker file', async () => {
131
+ const result = await reindexVault(tmpRoot, {});
132
+ // LightRAG server may not start if no LLM creds are present (default
133
+ // binding is ollama). We assert the orchestrator produced a result
134
+ // and wrote a marker file regardless of insert success.
135
+ assert.ok(result, 'reindexVault returned a result');
136
+ assert.ok(result.noteCount >= 1, `should report at least one note, got ${result.noteCount}`);
137
+ assert.ok(result.markerPath, 'markerPath must be set');
138
+ assert.ok(existsSync(result.markerPath), 'marker file must exist on disk');
139
+ const marker = JSON.parse(readFileSync(result.markerPath, 'utf8'));
140
+ assert.ok(marker.attemptedAt, 'marker has attemptedAt');
141
+ assert.equal(marker.noteCount, noteCount, 'marker counts all notes');
142
+ assert.equal(typeof marker.durationMs, 'number');
143
+ assert.equal(typeof marker.inserted, 'number');
144
+ assert.equal(typeof marker.failed, 'number');
145
+ }, { timeout: 60_000 });
146
+
147
+ test('isRunning reports server health', async () => {
148
+ const cfg = resolveLightRAGConfig(tmpRoot);
149
+ const running = await isRunning(cfg);
150
+ // We don't assert running=true — the server may not be up in CI.
151
+ assert.equal(typeof running, 'boolean');
152
+ });
153
+ });
@@ -0,0 +1,45 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { readFileSync } from 'node:fs';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { dirname, resolve } from 'node:path';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const REPO_ROOT = resolve(__dirname, '..', '..');
9
+
10
+ function read(rel) {
11
+ return readFileSync(resolve(REPO_ROOT, rel), 'utf8');
12
+ }
13
+
14
+ test('memory-protocol SKILL.md has MANDATORY framing at the top', () => {
15
+ const skill = read('config/skills/memory-protocol/SKILL.md');
16
+ // The "MANDATORY" badge must appear in the first 10 lines
17
+ const head = skill.split('\n').slice(0, 10).join('\n');
18
+ assert.match(head, /MANDATORY/, 'memory-protocol/SKILL.md must mark the protocol as MANDATORY in the first 10 lines');
19
+ });
20
+
21
+ test('AGENT_BASELINE.md section 5 has MANDATORY marker', () => {
22
+ const baseline = read('config/agents/_shared/AGENT_BASELINE.md');
23
+ // Find section 5 header and the next 20 lines
24
+ const section5Idx = baseline.indexOf('## 5.');
25
+ assert.ok(section5Idx >= 0, 'AGENT_BASELINE.md must have a section 5');
26
+ const section5Head = baseline.slice(section5Idx, section5Idx + 600);
27
+ assert.match(section5Head, /MANDATORY/, 'AGENT_BASELINE.md section 5 must mark memory check as MANDATORY');
28
+ });
29
+
30
+ test('AGENT_BASELINE.md section 13 has mandatory language', () => {
31
+ const baseline = read('config/agents/_shared/AGENT_BASELINE.md');
32
+ const section13Idx = baseline.indexOf('## 13.');
33
+ assert.ok(section13Idx >= 0, 'AGENT_BASELINE.md must have a section 13');
34
+ const section13Head = baseline.slice(section13Idx, section13Idx + 3000);
35
+ // Either the ⚠️ ENFORCED marker we just added, or the existing "mandatory, not optional" line
36
+ const hasMandatory = /MANDATORY/.test(section13Head) || /mandatory, not optional/i.test(section13Head);
37
+ assert.ok(hasMandatory, 'AGENT_BASELINE.md section 13 must contain MANDATORY framing or the existing "mandatory, not optional" language');
38
+ });
39
+
40
+ test('AGENT_BASELINE.md session-start bootstrap commands are present', () => {
41
+ const baseline = read('config/agents/_shared/AGENT_BASELINE.md');
42
+ // The actual commands agents must run
43
+ assert.match(baseline, /bizar memory search/, 'AGENT_BASELINE.md must reference `bizar memory search`');
44
+ assert.match(baseline, /bizar memory status/, 'AGENT_BASELINE.md must reference `bizar memory status`');
45
+ });
@@ -0,0 +1,198 @@
1
+ /**
2
+ * tests/memory-schema.test.mjs
3
+ *
4
+ * Tests for the memory schema validator.
5
+ */
6
+
7
+ import { describe, it } from 'node:test';
8
+ import assert from 'node:assert';
9
+ import { validateNote, defaultFrontmatter, REQUIRED_FIELDS, VALID_TYPES, VALID_STATUSES, VALID_CONFIDENCES } from '../src/server/memory-schema.mjs';
10
+
11
+ const NOW = new Date().toISOString();
12
+
13
+ describe('validateNote', () => {
14
+ it('passes note with all required fields', () => {
15
+ const fm = {
16
+ memory_id: 'test_123',
17
+ type: 'architecture_decision',
18
+ project_id: 'my-project',
19
+ status: 'active',
20
+ confidence: 'verified',
21
+ created: NOW,
22
+ updated: NOW,
23
+ tags: ['arch', 'api'],
24
+ };
25
+ const { valid, errors } = validateNote(fm, 'Some body content');
26
+ assert.strictEqual(valid, true);
27
+ assert.deepStrictEqual(errors, []);
28
+ });
29
+
30
+ it('fails when memory_id is missing', () => {
31
+ const fm = {
32
+ type: 'architecture_decision',
33
+ project_id: 'my-project',
34
+ status: 'active',
35
+ confidence: 'verified',
36
+ created: NOW,
37
+ updated: NOW,
38
+ tags: [],
39
+ };
40
+ const { valid, errors } = validateNote(fm, 'body');
41
+ assert.strictEqual(valid, false);
42
+ assert.ok(errors.some((e) => e.includes('memory_id')));
43
+ });
44
+
45
+ it('fails when type is invalid', () => {
46
+ const fm = {
47
+ memory_id: 'test_123',
48
+ type: 'unknown_type',
49
+ project_id: 'my-project',
50
+ status: 'active',
51
+ confidence: 'verified',
52
+ created: NOW,
53
+ updated: NOW,
54
+ tags: [],
55
+ };
56
+ const { valid, errors } = validateNote(fm, 'body');
57
+ assert.strictEqual(valid, false);
58
+ assert.ok(errors.some((e) => e.includes('invalid type')));
59
+ });
60
+
61
+ it('fails when status is invalid', () => {
62
+ const fm = {
63
+ memory_id: 'test_123',
64
+ type: 'architecture_decision',
65
+ project_id: 'my-project',
66
+ status: 'bogus',
67
+ confidence: 'verified',
68
+ created: NOW,
69
+ updated: NOW,
70
+ tags: [],
71
+ };
72
+ const { valid, errors } = validateNote(fm, 'body');
73
+ assert.strictEqual(valid, false);
74
+ assert.ok(errors.some((e) => e.includes('invalid status')));
75
+ });
76
+
77
+ it('fails when confidence is invalid', () => {
78
+ const fm = {
79
+ memory_id: 'test_123',
80
+ type: 'architecture_decision',
81
+ project_id: 'my-project',
82
+ status: 'active',
83
+ confidence: 'bogus',
84
+ created: NOW,
85
+ updated: NOW,
86
+ tags: [],
87
+ };
88
+ const { valid, errors } = validateNote(fm, 'body');
89
+ assert.strictEqual(valid, false);
90
+ assert.ok(errors.some((e) => e.includes('invalid confidence')));
91
+ });
92
+
93
+ it('fails when tags is not an array', () => {
94
+ const fm = {
95
+ memory_id: 'test_123',
96
+ type: 'architecture_decision',
97
+ project_id: 'my-project',
98
+ status: 'active',
99
+ confidence: 'verified',
100
+ created: NOW,
101
+ updated: NOW,
102
+ tags: 'not-an-array',
103
+ };
104
+ const { valid, errors } = validateNote(fm, 'body');
105
+ assert.strictEqual(valid, false);
106
+ assert.ok(errors.some((e) => e.includes('tags must be an array')));
107
+ });
108
+
109
+ it('warns when body is empty', () => {
110
+ const fm = {
111
+ memory_id: 'test_123',
112
+ type: 'architecture_decision',
113
+ project_id: 'my-project',
114
+ status: 'active',
115
+ confidence: 'verified',
116
+ created: NOW,
117
+ updated: NOW,
118
+ tags: [],
119
+ };
120
+ const { warnings } = validateNote(fm, ' \n\n ');
121
+ assert.ok(warnings.some((w) => w.includes('body is empty')));
122
+ });
123
+
124
+ it('warns when created/updated is not ISO date', () => {
125
+ const fm = {
126
+ memory_id: 'test_123',
127
+ type: 'architecture_decision',
128
+ project_id: 'my-project',
129
+ status: 'active',
130
+ confidence: 'verified',
131
+ created: 'not-a-date',
132
+ updated: 'also-not-a-date',
133
+ tags: [],
134
+ };
135
+ const { warnings } = validateNote(fm, 'body');
136
+ assert.ok(warnings.some((w) => w.includes('created')));
137
+ assert.ok(warnings.some((w) => w.includes('updated')));
138
+ });
139
+ });
140
+
141
+ describe('defaultFrontmatter', () => {
142
+ it('fills in all missing required fields', () => {
143
+ const result = defaultFrontmatter({ type: 'bug_pattern' });
144
+ assert.ok(result.memory_id.startsWith('bug_pattern_'));
145
+ assert.strictEqual(result.type, 'bug_pattern');
146
+ assert.strictEqual(result.status, 'draft');
147
+ assert.strictEqual(result.confidence, 'inferred');
148
+ assert.ok(Array.isArray(result.tags));
149
+ assert.ok(result.created);
150
+ assert.ok(result.updated);
151
+ });
152
+
153
+ it('preserves user-provided values', () => {
154
+ const result = defaultFrontmatter({
155
+ type: 'command',
156
+ project_id: 'my-project',
157
+ status: 'active',
158
+ tags: ['cli'],
159
+ });
160
+ assert.strictEqual(result.type, 'command');
161
+ assert.strictEqual(result.project_id, 'my-project');
162
+ assert.strictEqual(result.status, 'active');
163
+ assert.deepStrictEqual(result.tags, ['cli']);
164
+ });
165
+
166
+ it('does not overwrite explicit memory_id', () => {
167
+ const result = defaultFrontmatter({ memory_id: 'my-custom-id', type: 'session_summary' });
168
+ assert.strictEqual(result.memory_id, 'my-custom-id');
169
+ });
170
+ });
171
+
172
+ describe('schema constants', () => {
173
+ it('REQUIRED_FIELDS has exactly 8 fields', () => {
174
+ assert.strictEqual(REQUIRED_FIELDS.length, 8);
175
+ assert.ok(REQUIRED_FIELDS.includes('memory_id'));
176
+ assert.ok(REQUIRED_FIELDS.includes('type'));
177
+ assert.ok(REQUIRED_FIELDS.includes('tags'));
178
+ });
179
+
180
+ it('VALID_TYPES has 11 types', () => {
181
+ assert.strictEqual(VALID_TYPES.length, 11);
182
+ assert.ok(VALID_TYPES.includes('architecture_decision'));
183
+ assert.ok(VALID_TYPES.includes('session_summary'));
184
+ });
185
+
186
+ it('VALID_STATUSES has 6 statuses', () => {
187
+ assert.strictEqual(VALID_STATUSES.length, 6);
188
+ assert.ok(VALID_STATUSES.includes('active'));
189
+ assert.ok(VALID_STATUSES.includes('conflict'));
190
+ });
191
+
192
+ it('VALID_CONFIDENCES has 3 values', () => {
193
+ assert.strictEqual(VALID_CONFIDENCES.length, 3);
194
+ assert.ok(VALID_CONFIDENCES.includes('verified'));
195
+ assert.ok(VALID_CONFIDENCES.includes('inferred'));
196
+ assert.ok(VALID_CONFIDENCES.includes('speculative'));
197
+ });
198
+ });
@@ -0,0 +1,121 @@
1
+ /**
2
+ * tests/memory-secrets.test.mjs
3
+ *
4
+ * Tests for the secret scanner.
5
+ */
6
+
7
+ import { describe, it } from 'node:test';
8
+ import assert from 'node:assert';
9
+ import { scan, hasHighFindings, SECRET_PATTERNS } from '../src/server/memory-secrets.mjs';
10
+
11
+ describe('scan', () => {
12
+ it('returns safe: true for clean text', () => {
13
+ const result = scan('This is a normal note about memory architecture and planning.');
14
+ assert.strictEqual(result.safe, true);
15
+ assert.deepStrictEqual(result.findings, []);
16
+ });
17
+
18
+ it('detects AWS access key', () => {
19
+ const result = scan('AWS_KEY=AKIAIOSFODNN7EXAMPLE');
20
+ assert.strictEqual(result.safe, false);
21
+ assert.ok(result.findings.some((f) => f.id === 'aws_access_key'));
22
+ });
23
+
24
+ it('detects GitHub PAT classic', () => {
25
+ const result = scan('ghp_abcdefghijklmnopqrstuvwxyz1234567890ABCD');
26
+ assert.strictEqual(result.safe, false);
27
+ assert.ok(result.findings.some((f) => f.id === 'github_pat_classic'));
28
+ });
29
+
30
+ it('detects Stripe live key', () => {
31
+ const result = scan('sk_live_51234567890abcdefghijklmnop');
32
+ assert.strictEqual(result.safe, false);
33
+ assert.ok(result.findings.some((f) => f.id === 'stripe_live'));
34
+ });
35
+
36
+ it('detects Slack token', () => {
37
+ const result = scan('xoxb-1234567890123-1234567890123-AbCdEfGhIjKlMnOpQrStUvWx');
38
+ assert.strictEqual(result.safe, false);
39
+ assert.ok(result.findings.some((f) => f.id === 'slack_token'));
40
+ });
41
+
42
+ it('detects private key PEM', () => {
43
+ const result = scan('-----BEGIN RSA PRIVATE KEY-----\nMIIE...\n-----END RSA PRIVATE KEY-----');
44
+ assert.strictEqual(result.safe, false);
45
+ assert.ok(result.findings.some((f) => f.id === 'private_key_pem'));
46
+ });
47
+
48
+ it('detects bearer token with base64-like value', () => {
49
+ const result = scan('Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0');
50
+ assert.strictEqual(result.safe, false);
51
+ assert.ok(result.findings.some((f) => f.id === 'bearer_token'));
52
+ });
53
+
54
+ it('detects api_key_assignment', () => {
55
+ const result = scan('api_key: abcdefghijklmnopqrstuvwxyz123456');
56
+ // api_key_assignment is MEDIUM severity — safe stays true
57
+ assert.strictEqual(result.safe, true);
58
+ assert.ok(result.findings.some((f) => f.id === 'api_key_assignment'));
59
+ });
60
+
61
+ it('does NOT trigger on "my API is fast"', () => {
62
+ const result = scan('my API is fast');
63
+ assert.strictEqual(result.safe, true);
64
+ });
65
+
66
+ it('does NOT trigger on "bearer of bad tidings"', () => {
67
+ const result = scan('bearer of bad tidings');
68
+ assert.strictEqual(result.safe, true);
69
+ });
70
+
71
+ it('does NOT trigger on "172.16 is too low" (incomplete private IP)', () => {
72
+ const result = scan('172.16 is too low');
73
+ assert.strictEqual(result.safe, true);
74
+ });
75
+
76
+ it('does NOT trigger on "hello world"', () => {
77
+ const result = scan('hello world');
78
+ assert.strictEqual(result.safe, true);
79
+ });
80
+
81
+ it('does NOT trigger on "the api_keyname is descriptive"', () => {
82
+ const result = scan('the api_keyname is descriptive');
83
+ assert.strictEqual(result.safe, true);
84
+ });
85
+
86
+ it('does NOT trigger on bare Bearer without long token', () => {
87
+ const result = scan('Bearer of course');
88
+ assert.strictEqual(result.safe, true);
89
+ });
90
+
91
+ it('returns line numbers in findings', () => {
92
+ const text = 'line one\nline two with AKIAIOSFODNN7EXAMPLE key\nline three';
93
+ const result = scan(text);
94
+ const aws = result.findings.find((f) => f.id === 'aws_access_key');
95
+ assert.strictEqual(aws.line, 2);
96
+ });
97
+
98
+ it('hasHighFindings returns true for HIGH severity', () => {
99
+ const result = scan('AKIAIOSFODNN7EXAMPLE');
100
+ assert.strictEqual(hasHighFindings(result), true);
101
+ });
102
+
103
+ it('hasHighFindings returns false for MEDIUM-only', () => {
104
+ const result = scan('api_key: abcdefghijklmnopqrstuvwxyz123456');
105
+ assert.strictEqual(hasHighFindings(result), false);
106
+ });
107
+
108
+ it('hasHighFindings returns false for no findings', () => {
109
+ const result = scan('hello world');
110
+ assert.strictEqual(hasHighFindings(result), false);
111
+ });
112
+
113
+ it('all 12 patterns are defined with severity HIGH or MEDIUM', () => {
114
+ for (const p of SECRET_PATTERNS) {
115
+ assert.ok(p.id, `pattern missing id: ${JSON.stringify(p)}`);
116
+ assert.ok(p.pattern instanceof RegExp, `pattern ${p.id} is not a RegExp`);
117
+ assert.ok(p.severity === 'HIGH' || p.severity === 'MEDIUM', `invalid severity for ${p.id}`);
118
+ }
119
+ assert.strictEqual(SECRET_PATTERNS.length, 12);
120
+ });
121
+ });