@polderlabs/bizar 8.1.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (872) hide show
  1. package/.claude/worktrees/kanban-overhaul/.claude/CLAUDE.md +15 -0
  2. package/.claude/worktrees/kanban-overhaul/.claude/agents/_shared/AGENT_BASELINE.md +168 -0
  3. package/.claude/worktrees/kanban-overhaul/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
  4. package/.claude/worktrees/kanban-overhaul/.claude/agents/_shared/SKILLS.md +109 -0
  5. package/.claude/worktrees/kanban-overhaul/.claude/agents/agent-browser.md +80 -0
  6. package/.claude/worktrees/kanban-overhaul/.claude/agents/baldr.md +49 -0
  7. package/.claude/worktrees/kanban-overhaul/.claude/agents/forseti.md +51 -0
  8. package/.claude/worktrees/kanban-overhaul/.claude/agents/frigg.md +42 -0
  9. package/.claude/worktrees/kanban-overhaul/.claude/agents/heimdall.md +33 -0
  10. package/.claude/worktrees/kanban-overhaul/.claude/agents/hermod.md +53 -0
  11. package/.claude/worktrees/kanban-overhaul/.claude/agents/mimir.md +49 -0
  12. package/.claude/worktrees/kanban-overhaul/.claude/agents/odin.md +287 -0
  13. package/.claude/worktrees/kanban-overhaul/.claude/agents/quick.md +34 -0
  14. package/.claude/worktrees/kanban-overhaul/.claude/agents/semble-search.md +50 -0
  15. package/.claude/worktrees/kanban-overhaul/.claude/agents/thor.md +53 -0
  16. package/.claude/worktrees/kanban-overhaul/.claude/agents/tyr.md +56 -0
  17. package/.claude/worktrees/kanban-overhaul/.claude/agents/vidarr.md +54 -0
  18. package/.claude/worktrees/kanban-overhaul/.claude/agents/vor.md +53 -0
  19. package/.claude/worktrees/kanban-overhaul/.claude/commands/audit.md +25 -0
  20. package/.claude/worktrees/kanban-overhaul/.claude/commands/bizar.md +22 -0
  21. package/.claude/worktrees/kanban-overhaul/.claude/commands/explain.md +17 -0
  22. package/.claude/worktrees/kanban-overhaul/.claude/commands/init.md +29 -0
  23. package/.claude/worktrees/kanban-overhaul/.claude/commands/learn.md +44 -0
  24. package/.claude/worktrees/kanban-overhaul/.claude/commands/plan.md +35 -0
  25. package/.claude/worktrees/kanban-overhaul/.claude/commands/plow-through.md +50 -0
  26. package/.claude/worktrees/kanban-overhaul/.claude/commands/pr-review.md +49 -0
  27. package/.claude/worktrees/kanban-overhaul/.claude/commands/setup-provider.md +96 -0
  28. package/.claude/worktrees/kanban-overhaul/.claude/commands/tailscale-serve.md +100 -0
  29. package/.claude/worktrees/kanban-overhaul/.claude/commands/team.md +132 -0
  30. package/.claude/worktrees/kanban-overhaul/.claude/commands/test.md +62 -0
  31. package/.claude/worktrees/kanban-overhaul/.claude/commands/validate.md +68 -0
  32. package/.claude/worktrees/kanban-overhaul/.claude/commands/visual-plan.md +24 -0
  33. package/.claude/worktrees/kanban-overhaul/.claude/hooks/README.md +92 -0
  34. package/.claude/worktrees/kanban-overhaul/.claude/hooks/posttooluse-editwrite.mjs +91 -0
  35. package/.claude/worktrees/kanban-overhaul/.claude/hooks/pretooluse-bash.mjs +81 -0
  36. package/.claude/worktrees/kanban-overhaul/.claude/hooks/pretooluse-editwrite.mjs +139 -0
  37. package/.claude/worktrees/kanban-overhaul/.claude/hooks/sessionend-recall.mjs +74 -0
  38. package/.claude/worktrees/kanban-overhaul/.claude/hooks/sessionstart-prime.mjs +80 -0
  39. package/.claude/worktrees/kanban-overhaul/.claude/hooks/userpromptsubmit-tag.mjs +80 -0
  40. package/.claude/worktrees/kanban-overhaul/.claude/hooks/worker-suggest.mjs +110 -0
  41. package/.claude/worktrees/kanban-overhaul/.claude/settings.json +125 -0
  42. package/.claude/worktrees/kanban-overhaul/.claude/settings.local.json +8 -0
  43. package/.claude/worktrees/kanban-overhaul/.claude/skills/9router/SKILL.md +80 -0
  44. package/.claude/worktrees/kanban-overhaul/.claude/skills/9router-chat/SKILL.md +73 -0
  45. package/.claude/worktrees/kanban-overhaul/.claude/skills/9router-embeddings/SKILL.md +69 -0
  46. package/.claude/worktrees/kanban-overhaul/.claude/skills/9router-image/SKILL.md +86 -0
  47. package/.claude/worktrees/kanban-overhaul/.claude/skills/9router-stt/SKILL.md +79 -0
  48. package/.claude/worktrees/kanban-overhaul/.claude/skills/9router-tts/SKILL.md +80 -0
  49. package/.claude/worktrees/kanban-overhaul/.claude/skills/9router-web-fetch/SKILL.md +99 -0
  50. package/.claude/worktrees/kanban-overhaul/.claude/skills/9router-web-search/SKILL.md +91 -0
  51. package/.claude/worktrees/kanban-overhaul/.claude/skills/bizar/README.md +9 -0
  52. package/.claude/worktrees/kanban-overhaul/.claude/skills/bizar/SKILL.md +450 -0
  53. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-coding-standards/README.md +28 -0
  54. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
  55. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
  56. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
  57. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  58. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  59. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  60. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-testing/README.md +28 -0
  61. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-testing/SKILL.md +304 -0
  62. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-testing/references/coverage.md +370 -0
  63. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
  64. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  65. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-testing/references/mocking.md +364 -0
  66. package/.claude/worktrees/kanban-overhaul/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
  67. package/.claude/worktrees/kanban-overhaul/.claude/skills/cubesandbox/SKILL.md +148 -0
  68. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/README.md +41 -0
  69. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
  70. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  71. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
  72. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  73. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
  74. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  75. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  76. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
  77. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  78. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  79. package/.claude/worktrees/kanban-overhaul/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  80. package/.claude/worktrees/kanban-overhaul/.claude/skills/glyph/SKILL.md +163 -0
  81. package/.claude/worktrees/kanban-overhaul/.claude/skills/harness-engineering/SKILL.md +143 -0
  82. package/.claude/worktrees/kanban-overhaul/.claude/skills/lightrag/SKILL.md +81 -0
  83. package/.claude/worktrees/kanban-overhaul/.claude/skills/memory-protocol/SKILL.md +105 -0
  84. package/.claude/worktrees/kanban-overhaul/.claude/skills/obsidian/SKILL.md +306 -0
  85. package/.claude/worktrees/kanban-overhaul/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
  86. package/.claude/worktrees/kanban-overhaul/.claude/skills/self-improvement/SKILL.md +64 -0
  87. package/.claude/worktrees/kanban-overhaul/.dockerignore +31 -0
  88. package/.claude/worktrees/kanban-overhaul/.gitattributes +27 -0
  89. package/.claude/worktrees/kanban-overhaul/.harness/agents.json +17 -0
  90. package/.claude/worktrees/kanban-overhaul/.harness/arch-rules.json +69 -0
  91. package/.claude/worktrees/kanban-overhaul/.harness/topology.json +4 -0
  92. package/.claude/worktrees/kanban-overhaul/.nvmrc +1 -0
  93. package/.claude/worktrees/kanban-overhaul/.serena/project.yml +159 -0
  94. package/.claude/worktrees/kanban-overhaul/AGENTS.md +250 -0
  95. package/.claude/worktrees/kanban-overhaul/CHANGELOG.md +5410 -0
  96. package/.claude/worktrees/kanban-overhaul/CLAUDE.md +260 -0
  97. package/.claude/worktrees/kanban-overhaul/CURRENT_ISSUES_ AND_NEW_FEATURES.md +18 -0
  98. package/.claude/worktrees/kanban-overhaul/DECISIONS.md +66 -0
  99. package/.claude/worktrees/kanban-overhaul/Dockerfile +48 -0
  100. package/.claude/worktrees/kanban-overhaul/FINAL_GOAL.md +397 -0
  101. package/.claude/worktrees/kanban-overhaul/IMPLEMENTATION_PLAN.md +250 -0
  102. package/.claude/worktrees/kanban-overhaul/LICENSE +21 -0
  103. package/.claude/worktrees/kanban-overhaul/MILESTONES.md +257 -0
  104. package/.claude/worktrees/kanban-overhaul/Makefile +93 -0
  105. package/.claude/worktrees/kanban-overhaul/PROGRESS.md +1406 -0
  106. package/.claude/worktrees/kanban-overhaul/README.md +483 -0
  107. package/.claude/worktrees/kanban-overhaul/ROADMAP.md +609 -0
  108. package/.claude/worktrees/kanban-overhaul/bizar-dash/.graphifyignore +40 -0
  109. package/.claude/worktrees/kanban-overhaul/bizar-dash/.obsidian/README.md +14 -0
  110. package/.claude/worktrees/kanban-overhaul/bizar-dash/.obsidian/daily/2026-06-25.md +9 -0
  111. package/.claude/worktrees/kanban-overhaul/bizar-dash/.obsidian/vault.json +7 -0
  112. package/.claude/worktrees/kanban-overhaul/bizar-dash/.od-skills/web-prototype-e1429c1737/SKILL.md +103 -0
  113. package/.claude/worktrees/kanban-overhaul/bizar-dash/.od-skills/web-prototype-e1429c1737/assets/template.html +338 -0
  114. package/.claude/worktrees/kanban-overhaul/bizar-dash/.od-skills/web-prototype-e1429c1737/example.html +81 -0
  115. package/.claude/worktrees/kanban-overhaul/bizar-dash/.od-skills/web-prototype-e1429c1737/open-design.json +172 -0
  116. package/.claude/worktrees/kanban-overhaul/bizar-dash/.od-skills/web-prototype-e1429c1737/references/checklist.md +44 -0
  117. package/.claude/worktrees/kanban-overhaul/bizar-dash/.od-skills/web-prototype-e1429c1737/references/layouts.md +247 -0
  118. package/.claude/worktrees/kanban-overhaul/bizar-dash/ARCHITECTURE.md +197 -0
  119. package/.claude/worktrees/kanban-overhaul/bizar-dash/CHANGELOG.md +235 -0
  120. package/.claude/worktrees/kanban-overhaul/bizar-dash/DESIGN.md +912 -0
  121. package/.claude/worktrees/kanban-overhaul/bizar-dash/GLYPH.md +418 -0
  122. package/.claude/worktrees/kanban-overhaul/bizar-dash/PLAN.md +1135 -0
  123. package/.claude/worktrees/kanban-overhaul/bizar-dash/critique.json +26 -0
  124. package/.claude/worktrees/kanban-overhaul/bizar-dash/plans/debug-plan/comments.json +1 -0
  125. package/.claude/worktrees/kanban-overhaul/bizar-dash/plans/debug-plan/meta.json +10 -0
  126. package/.claude/worktrees/kanban-overhaul/bizar-dash/plans/debug-plan/plan.json +174 -0
  127. package/.claude/worktrees/kanban-overhaul/bizar-dash/scripts/smoke-bg-retry.mjs +246 -0
  128. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/agent-baseline/SKILL.md +80 -0
  129. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/agent-browser/SKILL.md +181 -0
  130. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/bizar/SKILL.md +116 -0
  131. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/chat/SKILL.md +74 -0
  132. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/eval/SKILL.md +237 -0
  133. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/headroom/SKILL.md +94 -0
  134. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/lightrag/SKILL.md +86 -0
  135. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/minimax/SKILL.md +80 -0
  136. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/obsidian/SKILL.md +68 -0
  137. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/providers/SKILL.md +75 -0
  138. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/publishing/SKILL.md +146 -0
  139. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/sdk/SKILL.md +138 -0
  140. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/self-improvement/SKILL.md +53 -0
  141. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/skills-cli/SKILL.md +94 -0
  142. package/.claude/worktrees/kanban-overhaul/bizar-dash/skills/usage/SKILL.md +62 -0
  143. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/cli/dashboard-ports.mjs +488 -0
  144. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/cli.mjs +537 -0
  145. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/activity-log.mjs +174 -0
  146. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/agents-store.mjs +465 -0
  147. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/api.mjs +223 -0
  148. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/artifact-template.html +810 -0
  149. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/artifacts-store.mjs +733 -0
  150. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/auth.mjs +548 -0
  151. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/background-store.mjs +446 -0
  152. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/backup-store.mjs +530 -0
  153. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/bg-poller.mjs +375 -0
  154. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/bg-retry.mjs +446 -0
  155. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/bg-spawner.mjs +390 -0
  156. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/browser.mjs +40 -0
  157. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/claude-bg-spawner.mjs +390 -0
  158. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/claude-info.mjs +412 -0
  159. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/claude-runner.mjs +284 -0
  160. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/claude-sdk.mjs +191 -0
  161. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/diagnostics-store.mjs +598 -0
  162. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/dialog-poller.mjs +105 -0
  163. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/dialog-store.mjs +177 -0
  164. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/digest-store.mjs +558 -0
  165. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/eval-store.mjs +281 -0
  166. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/eval.mjs +500 -0
  167. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/glyphs/mdx-compiler.mjs +596 -0
  168. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/headroom.mjs +649 -0
  169. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/lib/path-safe.mjs +283 -0
  170. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/lib/rate-limit.mjs +122 -0
  171. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/logger.mjs +71 -0
  172. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/memory-consolidator.mjs +225 -0
  173. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/memory-distillation.mjs +296 -0
  174. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/memory-git.mjs +473 -0
  175. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/memory-lightrag.mjs +1478 -0
  176. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/memory-obsidian.mjs +230 -0
  177. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/memory-schema.mjs +145 -0
  178. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/memory-secrets.mjs +84 -0
  179. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/memory-store.mjs +1093 -0
  180. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/metrics.mjs +268 -0
  181. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/minimax-usage-store.mjs +372 -0
  182. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/minimax.mjs +683 -0
  183. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/mod-security.mjs +398 -0
  184. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/mods-loader.mjs +1243 -0
  185. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/notifications-store.mjs +243 -0
  186. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/obsidian-store.mjs +318 -0
  187. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/ocr.mjs +55 -0
  188. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/otel.mjs +260 -0
  189. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/pair-store.mjs +138 -0
  190. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/progress-parser.mjs +221 -0
  191. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/projects-store.mjs +364 -0
  192. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/providers-store.mjs +1801 -0
  193. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/_shared.mjs +447 -0
  194. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/activity.mjs +195 -0
  195. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/agents-cc.mjs +194 -0
  196. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/agents.mjs +134 -0
  197. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/artifacts.mjs +279 -0
  198. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/auth.mjs +69 -0
  199. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/background.mjs +422 -0
  200. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/backup.mjs +112 -0
  201. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/chat.mjs +727 -0
  202. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/claude-session-detail.mjs +329 -0
  203. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/claude-sessions.mjs +259 -0
  204. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/clipboard.mjs +173 -0
  205. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/config.mjs +180 -0
  206. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/diagnostics.mjs +50 -0
  207. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/dialogs.mjs +86 -0
  208. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/digests.mjs +82 -0
  209. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/distill.mjs +129 -0
  210. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/doctor.mjs +71 -0
  211. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/env-vars.mjs +231 -0
  212. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/eval.mjs +187 -0
  213. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/fs.mjs +429 -0
  214. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
  215. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/goals.mjs +212 -0
  216. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/headroom.mjs +126 -0
  217. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/history.mjs +78 -0
  218. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/lightrag.mjs +169 -0
  219. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/memory.mjs +1524 -0
  220. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/minimax.mjs +133 -0
  221. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/misc.mjs +90 -0
  222. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/mods.mjs +359 -0
  223. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/notifications.mjs +54 -0
  224. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/obsidian.mjs +239 -0
  225. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/ocr.mjs +182 -0
  226. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/overview.mjs +92 -0
  227. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/pair.mjs +67 -0
  228. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/projects.mjs +175 -0
  229. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/providers.mjs +308 -0
  230. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/schedules.mjs +159 -0
  231. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/settings.mjs +51 -0
  232. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/skills.mjs +65 -0
  233. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/tailscale.mjs +46 -0
  234. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/tasks.mjs +518 -0
  235. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/themes.mjs +51 -0
  236. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/update.mjs +340 -0
  237. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/usage.mjs +136 -0
  238. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/users.mjs +102 -0
  239. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/voice.mjs +167 -0
  240. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes/workspaces.mjs +299 -0
  241. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes-v2/auth.mjs +59 -0
  242. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes-v2/events.mjs +121 -0
  243. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes-v2/health.mjs +21 -0
  244. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes-v2/index.mjs +90 -0
  245. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/routes-v2/sessions.mjs +111 -0
  246. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/schedules-runner.mjs +556 -0
  247. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/schedules-store.mjs +268 -0
  248. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/search-store.mjs +220 -0
  249. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/serve-info.mjs +1110 -0
  250. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/server.mjs +966 -0
  251. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/settings-store.mjs +45 -0
  252. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/skills-store.mjs +190 -0
  253. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/state.mjs +437 -0
  254. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/tailscale-store.mjs +113 -0
  255. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/task-delegator.mjs +969 -0
  256. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/tasks-store.mjs +556 -0
  257. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/tui.mjs +923 -0
  258. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/update-store.mjs +168 -0
  259. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/v2-auth-file.mjs +99 -0
  260. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/v2-event-bus.mjs +128 -0
  261. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/voice-store.mjs +229 -0
  262. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/voice-transcribe.mjs +72 -0
  263. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/watcher.mjs +81 -0
  264. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/workers/transcription-worker.mjs +213 -0
  265. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/workspaces.mjs +626 -0
  266. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/server/yaml.mjs +238 -0
  267. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/App.tsx +200 -0
  268. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/data/fetcher.ts +59 -0
  269. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/data/types.ts +168 -0
  270. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/data/useFetch.ts +62 -0
  271. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/data/useWebSocket.ts +153 -0
  272. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/main.tsx +28 -0
  273. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/shell/AppShell.tsx +90 -0
  274. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/shell/PageSkeleton.tsx +29 -0
  275. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/shell/Sidebar.tsx +361 -0
  276. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/shell/StatusBar.tsx +63 -0
  277. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/shell/Topbar.tsx +113 -0
  278. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/shell/index.ts +15 -0
  279. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/activity/ActivityFeed.tsx +137 -0
  280. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/agents/AgentActivity.tsx +101 -0
  281. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/agents/AgentCard.tsx +158 -0
  282. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/agents/AgentDetail.tsx +232 -0
  283. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Button.tsx +141 -0
  284. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/ButtonGroup.tsx +32 -0
  285. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Checkbox.tsx +49 -0
  286. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Field.tsx +78 -0
  287. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Form.tsx +23 -0
  288. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/IconButton.tsx +112 -0
  289. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Input.tsx +94 -0
  290. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/RadioGroup.tsx +74 -0
  291. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Select.tsx +171 -0
  292. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Slider.tsx +68 -0
  293. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Switch.tsx +56 -0
  294. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Textarea.tsx +64 -0
  295. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/Toggle.tsx +36 -0
  296. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/controls/ToggleGroup.tsx +85 -0
  297. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/Accordion.tsx +134 -0
  298. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/Avatar.tsx +175 -0
  299. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/Badge.tsx +93 -0
  300. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/BarList.tsx +101 -0
  301. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/Card.tsx +140 -0
  302. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/Chip.tsx +96 -0
  303. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/Kbd.tsx +42 -0
  304. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/ProgressBar.tsx +93 -0
  305. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/Sparkline.tsx +98 -0
  306. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/StatTile.tsx +150 -0
  307. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/Table.tsx +170 -0
  308. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/Timeline.tsx +130 -0
  309. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/data/ViewHeader.tsx +122 -0
  310. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Alert.tsx +95 -0
  311. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Banner.tsx +39 -0
  312. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/ContextMenu.tsx +143 -0
  313. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Dialog.tsx +149 -0
  314. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Drawer.tsx +8 -0
  315. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/DropdownMenu.tsx +231 -0
  316. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/EmptyState.tsx +48 -0
  317. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Popover.tsx +56 -0
  318. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Sheet.tsx +103 -0
  319. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Skeleton.tsx +67 -0
  320. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Spinner.tsx +30 -0
  321. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Toast.tsx +66 -0
  322. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/feedback/Tooltip.tsx +89 -0
  323. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/goals/GoalCard.tsx +176 -0
  324. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/goals/GoalDetail.tsx +245 -0
  325. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/goals/KeyResult.tsx +115 -0
  326. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/index.ts +349 -0
  327. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanBoard.tsx +115 -0
  328. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanCard.tsx +391 -0
  329. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanCardBadges.tsx +111 -0
  330. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanColumn.tsx +181 -0
  331. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanContextMenu.tsx +126 -0
  332. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanDetailDialog.tsx +638 -0
  333. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanEmptyColumn.tsx +43 -0
  334. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanProgress.tsx +130 -0
  335. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanQuickAdd.tsx +141 -0
  336. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/KanbanToolbar.tsx +291 -0
  337. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/kanban/useKanbanSelection.ts +90 -0
  338. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/libraries/LibraryGrid.tsx +30 -0
  339. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/libraries/LibraryItem.tsx +161 -0
  340. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/memory/MemoryVault.tsx +144 -0
  341. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/navigation/CommandPalette.tsx +224 -0
  342. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/navigation/NavLink.tsx +81 -0
  343. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/navigation/Pagination.tsx +164 -0
  344. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/navigation/Tabs.tsx +128 -0
  345. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/Box.tsx +144 -0
  346. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/Center.tsx +40 -0
  347. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/Cluster.tsx +67 -0
  348. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/Grid.tsx +104 -0
  349. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/Inline.tsx +77 -0
  350. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/Portal.tsx +33 -0
  351. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/ScrollArea.tsx +62 -0
  352. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/Separator.tsx +42 -0
  353. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/Stack.tsx +73 -0
  354. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/primitives/VisuallyHidden.tsx +49 -0
  355. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/settings/SettingsNav.tsx +91 -0
  356. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/settings/SettingsRow.tsx +88 -0
  357. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/settings/SettingsSection.tsx +109 -0
  358. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/styles/globals.css +220 -0
  359. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/styles/reset.css +57 -0
  360. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/styles/tokens.css +217 -0
  361. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/theme/DensityProvider.tsx +66 -0
  362. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/theme/ThemeProvider.tsx +94 -0
  363. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/theme/ThemeToggle.tsx +88 -0
  364. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/theme/useDensity.ts +15 -0
  365. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/theme/useTheme.ts +16 -0
  366. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/ui/utils/cx.ts +19 -0
  367. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Activity/ActivityView.tsx +71 -0
  368. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Agents/AgentsView.tsx +172 -0
  369. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/CommandPalette/AppCommandPalette.tsx +247 -0
  370. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Goals/GoalsView.tsx +137 -0
  371. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Libraries/LibrariesView.tsx +108 -0
  372. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Memory/MemoryView.tsx +77 -0
  373. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Overview/OverviewView.tsx +152 -0
  374. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Router.tsx +72 -0
  375. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Settings/SettingsView.tsx +330 -0
  376. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Tasks/CommandPalettePlaceholder.tsx +106 -0
  377. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/views/Tasks/TasksView.tsx +792 -0
  378. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8/vite-env.d.ts +15 -0
  379. package/.claude/worktrees/kanban-overhaul/bizar-dash/src/web/v8.html +17 -0
  380. package/.claude/worktrees/kanban-overhaul/bizar-dash/templates/mod/FORMAT.md +76 -0
  381. package/.claude/worktrees/kanban-overhaul/bizar-dash/templates/mod/hello-mod/README.md +19 -0
  382. package/.claude/worktrees/kanban-overhaul/bizar-dash/templates/mod/hello-mod/agents/greeter.md +8 -0
  383. package/.claude/worktrees/kanban-overhaul/bizar-dash/templates/mod/hello-mod/commands/hello.md +6 -0
  384. package/.claude/worktrees/kanban-overhaul/bizar-dash/templates/mod/hello-mod/mod.json +20 -0
  385. package/.claude/worktrees/kanban-overhaul/bizar-dash/templates/mod/hello-mod/routes/ping.mjs +9 -0
  386. package/.claude/worktrees/kanban-overhaul/bizar-dash/templates/mod/hello-mod/views/HelloView.tsx +10 -0
  387. package/.claude/worktrees/kanban-overhaul/bizar-dash/tests/setup.ts +63 -0
  388. package/.claude/worktrees/kanban-overhaul/bizar-dash/tests/smoke-v2.mjs +212 -0
  389. package/.claude/worktrees/kanban-overhaul/bizar-dash/tsconfig.json +23 -0
  390. package/.claude/worktrees/kanban-overhaul/bizar-dash/vite.config.ts +66 -0
  391. package/.claude/worktrees/kanban-overhaul/bizar-dash/vitest.config.ts +20 -0
  392. package/.claude/worktrees/kanban-overhaul/bizar-plugins/registry.json +62 -0
  393. package/.claude/worktrees/kanban-overhaul/bookmarklet/bizar-clipper.js +48 -0
  394. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/README.md +43 -0
  395. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/background.js +95 -0
  396. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/content.js +170 -0
  397. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/icons/icon-128.png +0 -0
  398. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/icons/icon-16.png +0 -0
  399. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/icons/icon-48.png +0 -0
  400. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/icons/icon.svg +4 -0
  401. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/manifest.json +32 -0
  402. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/popup.html +66 -0
  403. package/.claude/worktrees/kanban-overhaul/browser-extensions/bizar-clipper/popup.js +110 -0
  404. package/.claude/worktrees/kanban-overhaul/bun.lock +1558 -0
  405. package/.claude/worktrees/kanban-overhaul/cli/agent-browser-up.sh +152 -0
  406. package/.claude/worktrees/kanban-overhaul/cli/agent-browser-update.mjs +322 -0
  407. package/.claude/worktrees/kanban-overhaul/cli/artifact-cli.mjs +605 -0
  408. package/.claude/worktrees/kanban-overhaul/cli/artifact-render.mjs +621 -0
  409. package/.claude/worktrees/kanban-overhaul/cli/artifact-server.mjs +847 -0
  410. package/.claude/worktrees/kanban-overhaul/cli/artifact-templates.mjs +523 -0
  411. package/.claude/worktrees/kanban-overhaul/cli/artifact.mjs +63 -0
  412. package/.claude/worktrees/kanban-overhaul/cli/atomic.mjs +73 -0
  413. package/.claude/worktrees/kanban-overhaul/cli/audit.mjs +141 -0
  414. package/.claude/worktrees/kanban-overhaul/cli/banner.mjs +41 -0
  415. package/.claude/worktrees/kanban-overhaul/cli/bg.mjs +471 -0
  416. package/.claude/worktrees/kanban-overhaul/cli/bin.mjs +698 -0
  417. package/.claude/worktrees/kanban-overhaul/cli/bootstrap.mjs +77 -0
  418. package/.claude/worktrees/kanban-overhaul/cli/commands/artifact.mjs +20 -0
  419. package/.claude/worktrees/kanban-overhaul/cli/commands/claim.mjs +282 -0
  420. package/.claude/worktrees/kanban-overhaul/cli/commands/claude-cmd.mjs +348 -0
  421. package/.claude/worktrees/kanban-overhaul/cli/commands/clip.mjs +163 -0
  422. package/.claude/worktrees/kanban-overhaul/cli/commands/cost.mjs +279 -0
  423. package/.claude/worktrees/kanban-overhaul/cli/commands/dash.mjs +193 -0
  424. package/.claude/worktrees/kanban-overhaul/cli/commands/deploy/cloudflare.mjs +250 -0
  425. package/.claude/worktrees/kanban-overhaul/cli/commands/deploy/docker.mjs +222 -0
  426. package/.claude/worktrees/kanban-overhaul/cli/commands/deploy/fly.mjs +161 -0
  427. package/.claude/worktrees/kanban-overhaul/cli/commands/deploy/vercel.mjs +225 -0
  428. package/.claude/worktrees/kanban-overhaul/cli/commands/deploy.mjs +240 -0
  429. package/.claude/worktrees/kanban-overhaul/cli/commands/eval.mjs +396 -0
  430. package/.claude/worktrees/kanban-overhaul/cli/commands/headroom.mjs +204 -0
  431. package/.claude/worktrees/kanban-overhaul/cli/commands/install.mjs +170 -0
  432. package/.claude/worktrees/kanban-overhaul/cli/commands/lightrag.mjs +170 -0
  433. package/.claude/worktrees/kanban-overhaul/cli/commands/marketplace.mjs +64 -0
  434. package/.claude/worktrees/kanban-overhaul/cli/commands/memory.mjs +25 -0
  435. package/.claude/worktrees/kanban-overhaul/cli/commands/minimax.mjs +288 -0
  436. package/.claude/worktrees/kanban-overhaul/cli/commands/mod.mjs +185 -0
  437. package/.claude/worktrees/kanban-overhaul/cli/commands/ocr.mjs +182 -0
  438. package/.claude/worktrees/kanban-overhaul/cli/commands/plugin.mjs +358 -0
  439. package/.claude/worktrees/kanban-overhaul/cli/commands/rca.mjs +198 -0
  440. package/.claude/worktrees/kanban-overhaul/cli/commands/sandbox.mjs +220 -0
  441. package/.claude/worktrees/kanban-overhaul/cli/commands/service.mjs +79 -0
  442. package/.claude/worktrees/kanban-overhaul/cli/commands/setup-provider.mjs +466 -0
  443. package/.claude/worktrees/kanban-overhaul/cli/commands/tailscale.mjs +258 -0
  444. package/.claude/worktrees/kanban-overhaul/cli/commands/usage.mjs +113 -0
  445. package/.claude/worktrees/kanban-overhaul/cli/commands/util.mjs +577 -0
  446. package/.claude/worktrees/kanban-overhaul/cli/commands/validate.mjs +541 -0
  447. package/.claude/worktrees/kanban-overhaul/cli/commands/voice.mjs +211 -0
  448. package/.claude/worktrees/kanban-overhaul/cli/commands/workspace.mjs +247 -0
  449. package/.claude/worktrees/kanban-overhaul/cli/copy.mjs +586 -0
  450. package/.claude/worktrees/kanban-overhaul/cli/cost-gate.mjs +501 -0
  451. package/.claude/worktrees/kanban-overhaul/cli/dev-link.mjs +191 -0
  452. package/.claude/worktrees/kanban-overhaul/cli/digest.mjs +149 -0
  453. package/.claude/worktrees/kanban-overhaul/cli/doctor.mjs +217 -0
  454. package/.claude/worktrees/kanban-overhaul/cli/export.mjs +95 -0
  455. package/.claude/worktrees/kanban-overhaul/cli/feature-list-bridge.mjs +400 -0
  456. package/.claude/worktrees/kanban-overhaul/cli/heads-up.mjs +295 -0
  457. package/.claude/worktrees/kanban-overhaul/cli/init.mjs +368 -0
  458. package/.claude/worktrees/kanban-overhaul/cli/install.mjs +640 -0
  459. package/.claude/worktrees/kanban-overhaul/cli/memory-constants.mjs +76 -0
  460. package/.claude/worktrees/kanban-overhaul/cli/memory.mjs +1850 -0
  461. package/.claude/worktrees/kanban-overhaul/cli/plugin-runtime-deps.mjs +250 -0
  462. package/.claude/worktrees/kanban-overhaul/cli/post-install-smoke.mjs +256 -0
  463. package/.claude/worktrees/kanban-overhaul/cli/prompts.mjs +168 -0
  464. package/.claude/worktrees/kanban-overhaul/cli/providers-detect.mjs +233 -0
  465. package/.claude/worktrees/kanban-overhaul/cli/provision-claude.mjs +1176 -0
  466. package/.claude/worktrees/kanban-overhaul/cli/provision.mjs +2233 -0
  467. package/.claude/worktrees/kanban-overhaul/cli/repair.mjs +209 -0
  468. package/.claude/worktrees/kanban-overhaul/cli/service-controller.mjs +927 -0
  469. package/.claude/worktrees/kanban-overhaul/cli/service-env.mjs +139 -0
  470. package/.claude/worktrees/kanban-overhaul/cli/service.mjs +492 -0
  471. package/.claude/worktrees/kanban-overhaul/cli/update.mjs +29 -0
  472. package/.claude/worktrees/kanban-overhaul/cli/utils.mjs +221 -0
  473. package/.claude/worktrees/kanban-overhaul/cli/worker-dispatcher.mjs +259 -0
  474. package/.claude/worktrees/kanban-overhaul/config/AGENTS.md +595 -0
  475. package/.claude/worktrees/kanban-overhaul/config/agents/_shared/AGENT_BASELINE.md +155 -0
  476. package/.claude/worktrees/kanban-overhaul/config/agents/_shared/CLINE_TOOLS.md +398 -0
  477. package/.claude/worktrees/kanban-overhaul/config/agents/_shared/SKILLS.md +109 -0
  478. package/.claude/worktrees/kanban-overhaul/config/agents/agent-browser.md +90 -0
  479. package/.claude/worktrees/kanban-overhaul/config/agents/baldr.md +60 -0
  480. package/.claude/worktrees/kanban-overhaul/config/agents/forseti.md +60 -0
  481. package/.claude/worktrees/kanban-overhaul/config/agents/frigg.md +52 -0
  482. package/.claude/worktrees/kanban-overhaul/config/agents/heimdall.md +44 -0
  483. package/.claude/worktrees/kanban-overhaul/config/agents/hermod.md +64 -0
  484. package/.claude/worktrees/kanban-overhaul/config/agents/mimir.md +60 -0
  485. package/.claude/worktrees/kanban-overhaul/config/agents/odin.md +310 -0
  486. package/.claude/worktrees/kanban-overhaul/config/agents/quick.md +47 -0
  487. package/.claude/worktrees/kanban-overhaul/config/agents/semble-search.md +59 -0
  488. package/.claude/worktrees/kanban-overhaul/config/agents/thor.md +64 -0
  489. package/.claude/worktrees/kanban-overhaul/config/agents/tyr.md +67 -0
  490. package/.claude/worktrees/kanban-overhaul/config/agents/vidarr.md +65 -0
  491. package/.claude/worktrees/kanban-overhaul/config/agents/vor.md +62 -0
  492. package/.claude/worktrees/kanban-overhaul/config/commands/audit.md +5 -0
  493. package/.claude/worktrees/kanban-overhaul/config/commands/bizar.md +22 -0
  494. package/.claude/worktrees/kanban-overhaul/config/commands/explain.md +5 -0
  495. package/.claude/worktrees/kanban-overhaul/config/commands/init.md +26 -0
  496. package/.claude/worktrees/kanban-overhaul/config/commands/learn.md +5 -0
  497. package/.claude/worktrees/kanban-overhaul/config/commands/plan.md +30 -0
  498. package/.claude/worktrees/kanban-overhaul/config/commands/plow-through.md +47 -0
  499. package/.claude/worktrees/kanban-overhaul/config/commands/pr-review.md +5 -0
  500. package/.claude/worktrees/kanban-overhaul/config/commands/setup-provider.md +95 -0
  501. package/.claude/worktrees/kanban-overhaul/config/commands/tailscale-serve.md +97 -0
  502. package/.claude/worktrees/kanban-overhaul/config/commands/team.md +124 -0
  503. package/.claude/worktrees/kanban-overhaul/config/commands/test.md +60 -0
  504. package/.claude/worktrees/kanban-overhaul/config/commands/validate.md +66 -0
  505. package/.claude/worktrees/kanban-overhaul/config/commands/visual-plan.md +19 -0
  506. package/.claude/worktrees/kanban-overhaul/config/hooks/PostToolUse +66 -0
  507. package/.claude/worktrees/kanban-overhaul/config/hooks/PreToolUse +82 -0
  508. package/.claude/worktrees/kanban-overhaul/config/hooks/README.md +68 -0
  509. package/.claude/worktrees/kanban-overhaul/config/hooks/TaskResume +48 -0
  510. package/.claude/worktrees/kanban-overhaul/config/hooks/TaskStart +47 -0
  511. package/.claude/worktrees/kanban-overhaul/config/hooks/UserPromptSubmit +55 -0
  512. package/.claude/worktrees/kanban-overhaul/config/rules/general.md +9 -0
  513. package/.claude/worktrees/kanban-overhaul/config/rules/git.md +11 -0
  514. package/.claude/worktrees/kanban-overhaul/config/rules/javascript.md +10 -0
  515. package/.claude/worktrees/kanban-overhaul/config/rules/python.md +10 -0
  516. package/.claude/worktrees/kanban-overhaul/config/rules/testing.md +10 -0
  517. package/.claude/worktrees/kanban-overhaul/config/rules/thinking.md +56 -0
  518. package/.claude/worktrees/kanban-overhaul/config/rules/uncertainty.md +70 -0
  519. package/.claude/worktrees/kanban-overhaul/config/skills/9router/SKILL.md +80 -0
  520. package/.claude/worktrees/kanban-overhaul/config/skills/9router-chat/SKILL.md +73 -0
  521. package/.claude/worktrees/kanban-overhaul/config/skills/9router-embeddings/SKILL.md +69 -0
  522. package/.claude/worktrees/kanban-overhaul/config/skills/9router-image/SKILL.md +86 -0
  523. package/.claude/worktrees/kanban-overhaul/config/skills/9router-stt/SKILL.md +79 -0
  524. package/.claude/worktrees/kanban-overhaul/config/skills/9router-tts/SKILL.md +80 -0
  525. package/.claude/worktrees/kanban-overhaul/config/skills/9router-web-fetch/SKILL.md +99 -0
  526. package/.claude/worktrees/kanban-overhaul/config/skills/9router-web-search/SKILL.md +91 -0
  527. package/.claude/worktrees/kanban-overhaul/config/skills/bizar/README.md +9 -0
  528. package/.claude/worktrees/kanban-overhaul/config/skills/bizar/SKILL.md +447 -0
  529. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-coding-standards/README.md +28 -0
  530. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-coding-standards/SKILL.md +634 -0
  531. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-coding-standards/agents/openai.yaml +4 -0
  532. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-coding-standards/references/concurrency.md +320 -0
  533. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-coding-standards/references/error-handling.md +229 -0
  534. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  535. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  536. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  537. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-testing/README.md +28 -0
  538. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-testing/SKILL.md +304 -0
  539. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-testing/agents/openai.yaml +4 -0
  540. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-testing/references/coverage.md +370 -0
  541. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-testing/references/framework-compare.md +175 -0
  542. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  543. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-testing/references/mocking.md +364 -0
  544. package/.claude/worktrees/kanban-overhaul/config/skills/cpp-testing/references/tdd-workflow.md +308 -0
  545. package/.claude/worktrees/kanban-overhaul/config/skills/cubesandbox/SKILL.md +148 -0
  546. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/README.md +41 -0
  547. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/SKILL.md +439 -0
  548. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/agents/openai.yaml +4 -0
  549. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  550. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/references/host-tests.md +164 -0
  551. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  552. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/references/kconfig.md +159 -0
  553. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  554. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  555. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/references/nvs.md +121 -0
  556. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  557. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  558. package/.claude/worktrees/kanban-overhaul/config/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  559. package/.claude/worktrees/kanban-overhaul/config/skills/glyph/SKILL.md +163 -0
  560. package/.claude/worktrees/kanban-overhaul/config/skills/harness-engineering/SKILL.md +142 -0
  561. package/.claude/worktrees/kanban-overhaul/config/skills/lightrag/SKILL.md +81 -0
  562. package/.claude/worktrees/kanban-overhaul/config/skills/memory-protocol/SKILL.md +105 -0
  563. package/.claude/worktrees/kanban-overhaul/config/skills/obsidian/SKILL.md +306 -0
  564. package/.claude/worktrees/kanban-overhaul/config/skills/read-the-damn-docs/SKILL.md +113 -0
  565. package/.claude/worktrees/kanban-overhaul/config/skills/self-improvement/SKILL.md +64 -0
  566. package/.claude/worktrees/kanban-overhaul/config/trigger-patterns.json +186 -0
  567. package/.claude/worktrees/kanban-overhaul/docker-compose.yml +50 -0
  568. package/.claude/worktrees/kanban-overhaul/docs/A11Y.md +81 -0
  569. package/.claude/worktrees/kanban-overhaul/docs/DEPLOY.md +360 -0
  570. package/.claude/worktrees/kanban-overhaul/docs/DOCKER.md +250 -0
  571. package/.claude/worktrees/kanban-overhaul/docs/INDEX.md +223 -0
  572. package/.claude/worktrees/kanban-overhaul/docs/PLUGIN_REGISTRY.md +107 -0
  573. package/.claude/worktrees/kanban-overhaul/docs/RELEASING.md +693 -0
  574. package/.claude/worktrees/kanban-overhaul/docs/architecture.md +317 -0
  575. package/.claude/worktrees/kanban-overhaul/docs/code-review.md +242 -0
  576. package/.claude/worktrees/kanban-overhaul/docs/curator.md +144 -0
  577. package/.claude/worktrees/kanban-overhaul/docs/dashboard-ui-migration.md +807 -0
  578. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-001-cline-rewrite.md +114 -0
  579. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-002-in-process-clinecore.md +139 -0
  580. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-003-in-process-memory-vault.md +101 -0
  581. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-004-cline-agent-teams.md +112 -0
  582. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-005-bg-agents-via-dashboard.md +77 -0
  583. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-006-kanban-board.md +69 -0
  584. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-007-tool-approval-gate.md +147 -0
  585. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-008-skill-curator.md +100 -0
  586. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-009-pre-compaction-flush.md +106 -0
  587. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-010-knowledge-graph-tools.md +95 -0
  588. package/.claude/worktrees/kanban-overhaul/docs/decisions/DEC-011-claude-code-migration.md +172 -0
  589. package/.claude/worktrees/kanban-overhaul/docs/graph-tools.md +130 -0
  590. package/.claude/worktrees/kanban-overhaul/docs/migration-guide.md +471 -0
  591. package/.claude/worktrees/kanban-overhaul/docs/migrations/cline-replacement.md +118 -0
  592. package/.claude/worktrees/kanban-overhaul/docs/postmortems/2026-06-18-plugin-state-deadlock.md +250 -0
  593. package/.claude/worktrees/kanban-overhaul/docs/quality-document.md +74 -0
  594. package/.claude/worktrees/kanban-overhaul/docs/releases/v0.5.2.md +66 -0
  595. package/.claude/worktrees/kanban-overhaul/docs/releases/v0.5.4.md +85 -0
  596. package/.claude/worktrees/kanban-overhaul/docs/releases/v2.3.0.md +96 -0
  597. package/.claude/worktrees/kanban-overhaul/docs/releases/v3.24.0.md +155 -0
  598. package/.claude/worktrees/kanban-overhaul/docs/releases/v4.0.0.md +123 -0
  599. package/.claude/worktrees/kanban-overhaul/docs/releases/v7.0.0.md +173 -0
  600. package/.claude/worktrees/kanban-overhaul/docs/safety.md +216 -0
  601. package/.claude/worktrees/kanban-overhaul/feature_list.json +1297 -0
  602. package/.claude/worktrees/kanban-overhaul/init.sh +211 -0
  603. package/.claude/worktrees/kanban-overhaul/install.sh +498 -0
  604. package/.claude/worktrees/kanban-overhaul/mods-examples/impeccable/INSTRUCTIONS.md +82 -0
  605. package/.claude/worktrees/kanban-overhaul/mods-examples/impeccable/mod.json +37 -0
  606. package/.claude/worktrees/kanban-overhaul/mods-examples/impeccable/route.mjs +136 -0
  607. package/.claude/worktrees/kanban-overhaul/mods-examples/impeccable/web/index.html +180 -0
  608. package/.claude/worktrees/kanban-overhaul/mods-examples/lightrag/INSTRUCTIONS.md +81 -0
  609. package/.claude/worktrees/kanban-overhaul/mods-examples/lightrag/mod.json +61 -0
  610. package/.claude/worktrees/kanban-overhaul/mods-examples/lightrag/route.mjs +461 -0
  611. package/.claude/worktrees/kanban-overhaul/mods-examples/lightrag/web/index.html +263 -0
  612. package/.claude/worktrees/kanban-overhaul/mods-examples/ponytail/INSTRUCTIONS.md +87 -0
  613. package/.claude/worktrees/kanban-overhaul/mods-examples/ponytail/agents/thor.md +64 -0
  614. package/.claude/worktrees/kanban-overhaul/mods-examples/ponytail/mod.json +36 -0
  615. package/.claude/worktrees/kanban-overhaul/mods-examples/ponytail/route.mjs +98 -0
  616. package/.claude/worktrees/kanban-overhaul/mods-examples/ponytail/web/index.html +126 -0
  617. package/.claude/worktrees/kanban-overhaul/package.json +154 -0
  618. package/.claude/worktrees/kanban-overhaul/packages/sdk/ARCHITECTURE.md +70 -0
  619. package/.claude/worktrees/kanban-overhaul/packages/sdk/LICENSE +21 -0
  620. package/.claude/worktrees/kanban-overhaul/packages/sdk/README.md +80 -0
  621. package/.claude/worktrees/kanban-overhaul/packages/sdk/package.json +30 -0
  622. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/agent-registry.ts +304 -0
  623. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/consensus/byzantine.ts +587 -0
  624. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/consensus/index.ts +147 -0
  625. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/consensus/queen.ts +173 -0
  626. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/consensus/types.ts +199 -0
  627. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/dangerous-patterns.ts +127 -0
  628. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/federation/audit.ts +156 -0
  629. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/federation/budget.ts +269 -0
  630. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/federation/envelope.ts +144 -0
  631. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/federation/hmac.ts +160 -0
  632. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/federation/index.ts +443 -0
  633. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/federation/pii.ts +241 -0
  634. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/federation/policy.ts +112 -0
  635. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/federation/trust.ts +146 -0
  636. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/fingerprint.ts +0 -0
  637. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/index.ts +114 -0
  638. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/mcp/bin.ts +51 -0
  639. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/mcp/server.ts +848 -0
  640. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/memory/index.ts +160 -0
  641. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/router/codemod-intent.ts +125 -0
  642. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/router/index.ts +247 -0
  643. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
  644. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/router/memory-distillation.ts +249 -0
  645. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/router/model-router.ts +235 -0
  646. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/router/q-learning-router.ts +304 -0
  647. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/swarm-topology.ts +279 -0
  648. package/.claude/worktrees/kanban-overhaul/packages/sdk/src/version.ts +4 -0
  649. package/.claude/worktrees/kanban-overhaul/packages/sdk/tsconfig.json +25 -0
  650. package/.claude/worktrees/kanban-overhaul/packages/sdk/vitest.config.ts +9 -0
  651. package/.claude/worktrees/kanban-overhaul/plugins/bizar/ARCHITECTURE.md +72 -0
  652. package/.claude/worktrees/kanban-overhaul/plugins/bizar/CONSTRAINTS.md +62 -0
  653. package/.claude/worktrees/kanban-overhaul/plugins/bizar/LICENSE +21 -0
  654. package/.claude/worktrees/kanban-overhaul/plugins/bizar/README.md +46 -0
  655. package/.claude/worktrees/kanban-overhaul/plugins/bizar/index.ts +50 -0
  656. package/.claude/worktrees/kanban-overhaul/plugins/bizar/package.json +18 -0
  657. package/.claude/worktrees/kanban-overhaul/plugins/bizar/tsconfig.json +29 -0
  658. package/.claude/worktrees/kanban-overhaul/scripts/bh-full-e2e.mjs +298 -0
  659. package/.claude/worktrees/kanban-overhaul/scripts/check-agents.mjs +73 -0
  660. package/.claude/worktrees/kanban-overhaul/scripts/check-arch.sh +80 -0
  661. package/.claude/worktrees/kanban-overhaul/scripts/check-deps.mjs +492 -0
  662. package/.claude/worktrees/kanban-overhaul/scripts/clean-state-check.sh +81 -0
  663. package/.claude/worktrees/kanban-overhaul/scripts/git-hooks/pre-commit +59 -0
  664. package/.claude/worktrees/kanban-overhaul/scripts/git-hooks/pre-push +54 -0
  665. package/.claude/worktrees/kanban-overhaul/scripts/install-hooks.sh +29 -0
  666. package/.claude/worktrees/kanban-overhaul/scripts/install-service.mjs +190 -0
  667. package/.claude/worktrees/kanban-overhaul/scripts/mirror-claude-md.sh +78 -0
  668. package/.claude/worktrees/kanban-overhaul/scripts/session-trace.sh +52 -0
  669. package/.claude/worktrees/kanban-overhaul/scripts/test-in-container.sh +157 -0
  670. package/.claude/worktrees/kanban-overhaul/scripts/verify-feature.sh +102 -0
  671. package/.claude/worktrees/kanban-overhaul/skills-lock.json +11 -0
  672. package/.claude/worktrees/kanban-overhaul/templates/clean-state-checklist.md +55 -0
  673. package/.claude/worktrees/kanban-overhaul/templates/deploy/cloudflare/README.md +32 -0
  674. package/.claude/worktrees/kanban-overhaul/templates/deploy/cloudflare/functions-index.template.js +15 -0
  675. package/.claude/worktrees/kanban-overhaul/templates/deploy/cloudflare/wrangler.toml.template +9 -0
  676. package/.claude/worktrees/kanban-overhaul/templates/deploy/docker/.env.template +16 -0
  677. package/.claude/worktrees/kanban-overhaul/templates/deploy/docker/README.md +58 -0
  678. package/.claude/worktrees/kanban-overhaul/templates/deploy/docker/docker-compose.template.yml +23 -0
  679. package/.claude/worktrees/kanban-overhaul/templates/deploy/fly/README.md +35 -0
  680. package/.claude/worktrees/kanban-overhaul/templates/deploy/fly/fly.toml.template +28 -0
  681. package/.claude/worktrees/kanban-overhaul/templates/deploy/vercel/README.md +29 -0
  682. package/.claude/worktrees/kanban-overhaul/templates/deploy/vercel/api-index.template.js +18 -0
  683. package/.claude/worktrees/kanban-overhaul/templates/deploy/vercel/vercel.json.template +16 -0
  684. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/README.md +58 -0
  685. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/citation.json +16 -0
  686. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/code-review.json +16 -0
  687. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/code-search-basic.json +28 -0
  688. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/concise-output.json +16 -0
  689. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/context-window.json +16 -0
  690. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/error-recovery.json +16 -0
  691. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/json-output.json +20 -0
  692. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/latency-bounds.json +16 -0
  693. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/multi-language.json +16 -0
  694. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/regression-suite.json +79 -0
  695. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/response-format.json +30 -0
  696. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/safe-paths.json +16 -0
  697. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/tool-call-correctness.json +24 -0
  698. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/tool-call-multi-step.json +18 -0
  699. package/.claude/worktrees/kanban-overhaul/templates/eval-fixtures/unicode-handling.json +16 -0
  700. package/.claude/worktrees/kanban-overhaul/templates/evaluator-rubric.md +50 -0
  701. package/.claude/worktrees/kanban-overhaul/templates/plan/htmx.min.js +1 -0
  702. package/.claude/worktrees/kanban-overhaul/templates/plan/library/bug-investigation.mdx +79 -0
  703. package/.claude/worktrees/kanban-overhaul/templates/plan/library/decision-record.mdx +71 -0
  704. package/.claude/worktrees/kanban-overhaul/templates/plan/library/feature-design.mdx +92 -0
  705. package/.claude/worktrees/kanban-overhaul/templates/plan/meta.json.template +8 -0
  706. package/.claude/worktrees/kanban-overhaul/templates/plan/plan.mdx.template +46 -0
  707. package/.claude/worktrees/kanban-overhaul/templates/plugin-template/README.md +121 -0
  708. package/.claude/worktrees/kanban-overhaul/templates/plugin-template/index.js +66 -0
  709. package/.claude/worktrees/kanban-overhaul/templates/plugin-template/plugin.json +42 -0
  710. package/.claude/worktrees/kanban-overhaul/templates/plugin-template/tests/plugin.test.js +83 -0
  711. package/.claude/worktrees/kanban-overhaul/templates/schedules/daily-backup.json +12 -0
  712. package/.claude/worktrees/kanban-overhaul/templates/schedules/daily-cleanup.json +12 -0
  713. package/.claude/worktrees/kanban-overhaul/templates/schedules/hourly-health-check.json +12 -0
  714. package/.claude/worktrees/kanban-overhaul/templates/schedules/webhook-on-push.json +13 -0
  715. package/.claude/worktrees/kanban-overhaul/templates/schedules/weekly-digest.json +13 -0
  716. package/.claude/worktrees/kanban-overhaul/templates/sprint-contract.md +47 -0
  717. package/.claude/worktrees/kanban-overhaul/tsconfig.json +44 -0
  718. package/.claude/worktrees/kanban-overhaul/vite.config.ts +51 -0
  719. package/bizar-dash/dist/assets/ActivityFeed-BXIkGGQ1.js +16 -0
  720. package/bizar-dash/dist/assets/ActivityFeed-BXIkGGQ1.js.map +1 -0
  721. package/bizar-dash/dist/assets/ActivityView-CBxNNrZa.js +12 -0
  722. package/bizar-dash/dist/assets/ActivityView-CBxNNrZa.js.map +1 -0
  723. package/bizar-dash/dist/assets/AgentsView-uv4l21AH.js +23 -0
  724. package/bizar-dash/dist/assets/AgentsView-uv4l21AH.js.map +1 -0
  725. package/bizar-dash/dist/assets/BackgroundJobsView-CB4WXIpk.js +6 -0
  726. package/bizar-dash/dist/assets/BackgroundJobsView-CB4WXIpk.js.map +1 -0
  727. package/bizar-dash/dist/assets/{Card-BzJeYR2D.js → Card-CeZsWlcP.js} +1 -1
  728. package/bizar-dash/dist/assets/{Card-BzJeYR2D.js.map → Card-CeZsWlcP.js.map} +1 -1
  729. package/bizar-dash/dist/assets/{Chip-CN2bvm7a.js → Chip-OPjSKl2I.js} +1 -1
  730. package/bizar-dash/dist/assets/{Chip-CN2bvm7a.js.map → Chip-OPjSKl2I.js.map} +1 -1
  731. package/bizar-dash/dist/assets/{EmptyState-DEiBv4-r.js → EmptyState-BrLA8mbE.js} +1 -1
  732. package/bizar-dash/dist/assets/{EmptyState-DEiBv4-r.js.map → EmptyState-BrLA8mbE.js.map} +1 -1
  733. package/bizar-dash/dist/assets/GoalsView-B8R-kxQr.js +11 -0
  734. package/bizar-dash/dist/assets/GoalsView-B8R-kxQr.js.map +1 -0
  735. package/bizar-dash/dist/assets/Grid-B7NgNtff.js +1 -0
  736. package/bizar-dash/dist/assets/Grid-B7NgNtff.js.map +1 -0
  737. package/bizar-dash/dist/assets/Input-BW_5Sl6N.js +1 -0
  738. package/bizar-dash/dist/assets/Input-BW_5Sl6N.js.map +1 -0
  739. package/bizar-dash/dist/assets/{KanbanCard-DZEaAiEr.js → KanbanCard-B-NOSfMN.js} +1 -1
  740. package/bizar-dash/dist/assets/{KanbanCard-DZEaAiEr.js.map → KanbanCard-B-NOSfMN.js.map} +1 -1
  741. package/bizar-dash/dist/assets/{LibrariesView-CPgKGPpM.js → LibrariesView-D6eB7B2A.js} +6 -6
  742. package/bizar-dash/dist/assets/{LibrariesView-CPgKGPpM.js.map → LibrariesView-D6eB7B2A.js.map} +1 -1
  743. package/bizar-dash/dist/assets/MemoryView-MK4m1KbR.js +13 -0
  744. package/bizar-dash/dist/assets/MemoryView-MK4m1KbR.js.map +1 -0
  745. package/bizar-dash/dist/assets/OverviewView-CHavfYBa.js +1 -0
  746. package/bizar-dash/dist/assets/{OverviewView-BDegBWgg.js.map → OverviewView-CHavfYBa.js.map} +1 -1
  747. package/bizar-dash/dist/assets/SchedulesView-zV-0pbsz.js +1 -0
  748. package/bizar-dash/dist/assets/SchedulesView-zV-0pbsz.js.map +1 -0
  749. package/bizar-dash/dist/assets/{Select-xwxbK8Ed.js → Select-B6Ho36Tq.js} +3 -8
  750. package/bizar-dash/dist/assets/Select-B6Ho36Tq.js.map +1 -0
  751. package/bizar-dash/dist/assets/SettingsRow-Br21uRFz.js +1 -0
  752. package/bizar-dash/dist/assets/SettingsRow-Br21uRFz.js.map +1 -0
  753. package/bizar-dash/dist/assets/SettingsView-Cx3uJrus.js +38 -0
  754. package/bizar-dash/dist/assets/SettingsView-Cx3uJrus.js.map +1 -0
  755. package/bizar-dash/dist/assets/{Sheet-C_sIEw35.js → Sheet-Cmu_4Zea.js} +1 -1
  756. package/bizar-dash/dist/assets/{Sheet-C_sIEw35.js.map → Sheet-Cmu_4Zea.js.map} +1 -1
  757. package/bizar-dash/dist/assets/Slider-B_jPr-SR.js +1 -0
  758. package/bizar-dash/dist/assets/Slider-B_jPr-SR.js.map +1 -0
  759. package/bizar-dash/dist/assets/{Sparkline-D664vGWW.js → Sparkline-Cmqw0N39.js} +1 -1
  760. package/bizar-dash/dist/assets/{Sparkline-D664vGWW.js.map → Sparkline-Cmqw0N39.js.map} +1 -1
  761. package/bizar-dash/dist/assets/Switch-C7Qyu50Y.js +10 -0
  762. package/bizar-dash/dist/assets/Switch-C7Qyu50Y.js.map +1 -0
  763. package/bizar-dash/dist/assets/TasksView-BXADmjgb.js +1 -0
  764. package/bizar-dash/dist/assets/TasksView-BXADmjgb.js.map +1 -0
  765. package/bizar-dash/dist/assets/{Textarea-Dk97nmjO.js → Textarea-KIN98DVH.js} +1 -1
  766. package/bizar-dash/dist/assets/{Textarea-Dk97nmjO.js.map → Textarea-KIN98DVH.js.map} +1 -1
  767. package/bizar-dash/dist/assets/{ViewHeader-dPd4zAyj.js → ViewHeader-CPbqI-w8.js} +1 -1
  768. package/bizar-dash/dist/assets/{ViewHeader-dPd4zAyj.js.map → ViewHeader-CPbqI-w8.js.map} +1 -1
  769. package/bizar-dash/dist/assets/{brain-BajPVibR.js → brain-jN_ldx3U.js} +1 -1
  770. package/bizar-dash/dist/assets/{brain-BajPVibR.js.map → brain-jN_ldx3U.js.map} +1 -1
  771. package/bizar-dash/dist/assets/{calendar-CaP_Bbgc.js → calendar-DZGgPP2_.js} +1 -1
  772. package/bizar-dash/dist/assets/{calendar-CaP_Bbgc.js.map → calendar-DZGgPP2_.js.map} +1 -1
  773. package/bizar-dash/dist/assets/circle-check-XhUtiACZ.js +6 -0
  774. package/bizar-dash/dist/assets/circle-check-XhUtiACZ.js.map +1 -0
  775. package/bizar-dash/dist/assets/clock-BXBnSbox.js +6 -0
  776. package/bizar-dash/dist/assets/clock-BXBnSbox.js.map +1 -0
  777. package/bizar-dash/dist/assets/{cmdk-BLWIhKrV.js → cmdk-D15rXxc5.js} +2 -2
  778. package/bizar-dash/dist/assets/{cmdk-BLWIhKrV.js.map → cmdk-D15rXxc5.js.map} +1 -1
  779. package/bizar-dash/dist/assets/{dnd-kit-DU8GZk7s.js → dnd-kit-Bormfg7j.js} +1 -1
  780. package/bizar-dash/dist/assets/{dnd-kit-DU8GZk7s.js.map → dnd-kit-Bormfg7j.js.map} +1 -1
  781. package/bizar-dash/dist/assets/{git-pull-request-CFRRwdxp.js → git-pull-request-BCt0CEjc.js} +1 -1
  782. package/bizar-dash/dist/assets/{git-pull-request-CFRRwdxp.js.map → git-pull-request-BCt0CEjc.js.map} +1 -1
  783. package/bizar-dash/dist/assets/index-C0JBGB5M.js +1 -0
  784. package/bizar-dash/dist/assets/index-C0JBGB5M.js.map +1 -0
  785. package/bizar-dash/dist/assets/index-CrWI7sQR.js +1 -0
  786. package/bizar-dash/dist/assets/index-CrWI7sQR.js.map +1 -0
  787. package/bizar-dash/dist/assets/main-CCimEcWH.js +134 -0
  788. package/bizar-dash/dist/assets/main-CCimEcWH.js.map +1 -0
  789. package/bizar-dash/dist/assets/play-B0rTjpSg.js +6 -0
  790. package/bizar-dash/dist/assets/play-B0rTjpSg.js.map +1 -0
  791. package/bizar-dash/dist/assets/{plus-CVH1JB04.js → plus-BaTQsS9G.js} +1 -1
  792. package/bizar-dash/dist/assets/{plus-CVH1JB04.js.map → plus-BaTQsS9G.js.map} +1 -1
  793. package/bizar-dash/dist/assets/refresh-cw-CoatVcs3.js +6 -0
  794. package/bizar-dash/dist/assets/refresh-cw-CoatVcs3.js.map +1 -0
  795. package/bizar-dash/dist/assets/{terminal-B2sSd5t7.js → terminal-k-1HFHcN.js} +1 -1
  796. package/bizar-dash/dist/assets/{terminal-B2sSd5t7.js.map → terminal-k-1HFHcN.js.map} +1 -1
  797. package/bizar-dash/dist/assets/{trash-2-JtYRQ79q.js → trash-2-DtpKnAn2.js} +1 -1
  798. package/bizar-dash/dist/assets/{trash-2-JtYRQ79q.js.map → trash-2-DtpKnAn2.js.map} +1 -1
  799. package/bizar-dash/dist/assets/{triangle-alert-Nzbrm0I2.js → triangle-alert-FHSs_e5b.js} +1 -1
  800. package/bizar-dash/dist/assets/{triangle-alert-Nzbrm0I2.js.map → triangle-alert-FHSs_e5b.js.map} +1 -1
  801. package/bizar-dash/dist/assets/{wrench-B7go551L.js → wrench-CbBE0oQ0.js} +2 -2
  802. package/bizar-dash/dist/assets/{wrench-B7go551L.js.map → wrench-CbBE0oQ0.js.map} +1 -1
  803. package/bizar-dash/dist/v8.html +2 -2
  804. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  805. package/bizar-dash/src/server/api.mjs +5 -0
  806. package/bizar-dash/src/server/progress-parser.mjs +20 -4
  807. package/bizar-dash/src/server/routes/goals.mjs +10 -10
  808. package/bizar-dash/src/server/routes/spawn.mjs +59 -0
  809. package/bizar-dash/src/server/server.mjs +1 -1
  810. package/bizar-dash/src/web/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  811. package/bizar-dash/src/web/v8/App.tsx +56 -7
  812. package/bizar-dash/src/web/v8/__tests__/BackgroundJobsView.test.tsx +172 -0
  813. package/bizar-dash/src/web/v8/__tests__/MemoryView.test.tsx +91 -0
  814. package/bizar-dash/src/web/v8/__tests__/NotificationsPopover.test.tsx +91 -0
  815. package/bizar-dash/src/web/v8/__tests__/SchedulesView.test.tsx +157 -0
  816. package/bizar-dash/src/web/v8/__tests__/Topbar.test.tsx +86 -0
  817. package/bizar-dash/src/web/v8/__tests__/changelog.test.tsx +79 -0
  818. package/bizar-dash/src/web/v8/__tests__/theme.test.tsx +10 -4
  819. package/bizar-dash/src/web/v8/__tests__/views.test.tsx +6 -0
  820. package/bizar-dash/src/web/v8/data/types.ts +2 -0
  821. package/bizar-dash/src/web/v8/data/useWebSocket.ts +9 -1
  822. package/bizar-dash/src/web/v8/shell/Sidebar.tsx +46 -12
  823. package/bizar-dash/src/web/v8/shell/Topbar.tsx +30 -4
  824. package/bizar-dash/src/web/v8/ui/agents/AgentDetail.tsx +34 -3
  825. package/bizar-dash/src/web/v8/ui/agents/AgentStreamPanel.tsx +1 -1
  826. package/bizar-dash/src/web/v8/ui/feedback/NotificationsPopover.tsx +239 -0
  827. package/bizar-dash/src/web/v8/ui/goals/GoalDetail.tsx +7 -1
  828. package/bizar-dash/src/web/v8/views/Activity/ActivityView.tsx +393 -32
  829. package/bizar-dash/src/web/v8/views/BackgroundJobs/BackgroundJobsView.tsx +442 -0
  830. package/bizar-dash/src/web/v8/views/CommandPalette/AppCommandPalette.tsx +22 -7
  831. package/bizar-dash/src/web/v8/views/Goals/GoalsView.tsx +1 -1
  832. package/bizar-dash/src/web/v8/views/Memory/MemoryView.tsx +174 -20
  833. package/bizar-dash/src/web/v8/views/Router.tsx +10 -0
  834. package/bizar-dash/src/web/v8/views/Schedules/SchedulesView.tsx +395 -0
  835. package/bizar-dash/src/web/v8/views/Settings/SettingsView.tsx +21 -5
  836. package/bizar-dash/src/web/v8/views/Tasks/TasksView.tsx +9 -7
  837. package/bizar-dash/tests/goals-decompose.test.mjs +5 -3
  838. package/package.json +1 -1
  839. package/packages/sdk/.harness/agents.json +17 -0
  840. package/packages/sdk/.harness/topology.json +4 -0
  841. package/bizar-dash/dist/assets/ActivityFeed-CqF6iSDQ.js +0 -6
  842. package/bizar-dash/dist/assets/ActivityFeed-CqF6iSDQ.js.map +0 -1
  843. package/bizar-dash/dist/assets/ActivityView-qdhDjRtL.js +0 -1
  844. package/bizar-dash/dist/assets/ActivityView-qdhDjRtL.js.map +0 -1
  845. package/bizar-dash/dist/assets/AgentsView-BwXkOayl.js +0 -28
  846. package/bizar-dash/dist/assets/AgentsView-BwXkOayl.js.map +0 -1
  847. package/bizar-dash/dist/assets/Badge-BH4C3aUF.js +0 -1
  848. package/bizar-dash/dist/assets/Badge-BH4C3aUF.js.map +0 -1
  849. package/bizar-dash/dist/assets/Button-RmN1RKhE.js +0 -6
  850. package/bizar-dash/dist/assets/Button-RmN1RKhE.js.map +0 -1
  851. package/bizar-dash/dist/assets/GoalsView-CvsrzEez.js +0 -6
  852. package/bizar-dash/dist/assets/GoalsView-CvsrzEez.js.map +0 -1
  853. package/bizar-dash/dist/assets/MemoryView-Bbez5pMN.js +0 -13
  854. package/bizar-dash/dist/assets/MemoryView-Bbez5pMN.js.map +0 -1
  855. package/bizar-dash/dist/assets/OverviewView-BDegBWgg.js +0 -1
  856. package/bizar-dash/dist/assets/Select-xwxbK8Ed.js.map +0 -1
  857. package/bizar-dash/dist/assets/SettingsRow-CppSgkT_.js +0 -10
  858. package/bizar-dash/dist/assets/SettingsRow-CppSgkT_.js.map +0 -1
  859. package/bizar-dash/dist/assets/SettingsView-DUc2meHV.js +0 -43
  860. package/bizar-dash/dist/assets/SettingsView-DUc2meHV.js.map +0 -1
  861. package/bizar-dash/dist/assets/StatTile-BRsUIA6P.js +0 -16
  862. package/bizar-dash/dist/assets/StatTile-BRsUIA6P.js.map +0 -1
  863. package/bizar-dash/dist/assets/TasksView-DVyX5-DY.js +0 -1
  864. package/bizar-dash/dist/assets/TasksView-DVyX5-DY.js.map +0 -1
  865. package/bizar-dash/dist/assets/index-AL3OLUfz.js +0 -1
  866. package/bizar-dash/dist/assets/index-AL3OLUfz.js.map +0 -1
  867. package/bizar-dash/dist/assets/main-LxX9zTRk.js +0 -109
  868. package/bizar-dash/dist/assets/main-LxX9zTRk.js.map +0 -1
  869. package/bizar-dash/dist/assets/useFetch-CoVWwbml.js +0 -1
  870. package/bizar-dash/dist/assets/useFetch-CoVWwbml.js.map +0 -1
  871. package/bizar-dash/dist/assets/useWebSocket-CVIQB8yG.js +0 -1
  872. package/bizar-dash/dist/assets/useWebSocket-CVIQB8yG.js.map +0 -1
@@ -0,0 +1,1801 @@
1
+ /**
2
+ * src/server/providers-store.mjs
3
+ *
4
+ * v3.0.0 — Cline providers and MCPs management.
5
+ *
6
+ * Reads / writes the cline.json at ~/.config/cline/cline.json
7
+ * under the `provider` and `mcp` keys.
8
+ *
9
+ * v3.5.6 — `listAll()` falls back to inferring providers from agent
10
+ * `.md` frontmatter (the `model: provider/model-name` line) so the
11
+ * dashboard can surface the providers that are actually in use even
12
+ * when the user's cline.json has no top-level `provider` key.
13
+ * Also tries the running `cline serve` HTTP API for the canonical
14
+ * list. API keys are never echoed back in full — the response masks them.
15
+ *
16
+ * v4.6.0 — Backup keys: each provider now exposes a `keys[]` array with
17
+ * `{envVar, label, status, lastError, errorCount, lastUsed}`. The
18
+ * legacy `apiKey` / `backupApiKey` scalar fields are still read and
19
+ * written for back-compat with cline itself and existing tests;
20
+ * they are derived from the active key on load. Rotation helpers:
21
+ * - `getActiveKey(providerId)` — highest-priority usable key
22
+ * - `markKeyError(providerId, envVar, error)` — bumps errorCount,
23
+ * demotes to `cooldown` after ERROR_COOLDOWN_THRESHOLD errors
24
+ * - `rotateKey(providerId)` — moves to the next key, marks the
25
+ * current one `disabled`
26
+ * - `withKeyRotation(providerId, fn)` — runs `fn(key, envVar)`; on
27
+ * retryable errors (auth/429/quota/5xx) marks the current key as
28
+ * errored, rotates, and retries up to MAX_ROTATION_ATTEMPTS times
29
+ *
30
+ * v4.6.0 — Provider catalog: PROVIDER_CATALOG is a curated list of
31
+ * well-known providers with `keyHint` + `docs` + curated `models[]`.
32
+ * `searchProviders(query)` does a fuzzy match across id/name/docs.
33
+ */
34
+ import {
35
+ existsSync,
36
+ readFileSync,
37
+ writeFileSync,
38
+ renameSync,
39
+ mkdirSync,
40
+ readdirSync,
41
+ statSync,
42
+ } from 'node:fs';
43
+ import { dirname, join } from 'node:path';
44
+ import { homedir } from 'node:os';
45
+
46
+ const HOME = homedir();
47
+ const CLINE_JSON = join(HOME, '.config', 'cline', 'cline.json');
48
+ const CLINE_AGENTS_DIR = join(HOME, '.config', 'cline', 'agents');
49
+
50
+ function safeReadJSON(file, fallback = {}) {
51
+ try {
52
+ if (!existsSync(file)) return fallback;
53
+ const text = readFileSync(file, 'utf8');
54
+ if (!text.trim()) return fallback;
55
+ return JSON.parse(text);
56
+ } catch {
57
+ return fallback;
58
+ }
59
+ }
60
+
61
+ // Atomic JSON write: serialize to a sibling temp file, then rename into
62
+ // place. `rename` is atomic on POSIX (same filesystem), so a crash
63
+ // between write and rename never leaves a half-written / corrupt file.
64
+ function atomicWriteJson(filePath, data) {
65
+ const tmp = `${filePath}.tmp.${process.pid}`;
66
+ writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8');
67
+ renameSync(tmp, filePath);
68
+ }
69
+
70
+ // v5.0.0 — Bug S1: 1-second debounced cache for cline.json reads.
71
+ // `list()`/`listAll()` and other consumers call `loadConfig()` on every
72
+ // WS poll (every ~5s per client) and on every route hit. With N clients
73
+ // that becomes O(N) reads per minute. The cache collapses all reads
74
+ // within a 1-second window to a single read. Writes invalidate so
75
+ // subsequent reads see the new state.
76
+ //
77
+ // The cache lives on `globalThis` so it is shared across all module
78
+ // instances of providers-store.mjs — tests and code that re-imports
79
+ // the module (e.g. via `?cb=` cache-bust) must see the same cache,
80
+ // otherwise a write on one instance becomes invisible to another.
81
+ //
82
+ // The cache also tracks the file's mtime+size and invalidates if the
83
+ // on-disk file has been modified externally (e.g. an editor saving
84
+ // cline.json, or a test setup writing a fresh file). This prevents
85
+ // stale reads when the file changes outside our write paths.
86
+ const CLINE_JSON_CACHE_GLOBAL_KEY = '__bizar_cline_json_cache__';
87
+ function _clineCacheSlot() {
88
+ if (!globalThis[CLINE_JSON_CACHE_GLOBAL_KEY]) {
89
+ globalThis[CLINE_JSON_CACHE_GLOBAL_KEY] = { entry: null, at: 0 };
90
+ }
91
+ return globalThis[CLINE_JSON_CACHE_GLOBAL_KEY];
92
+ }
93
+
94
+ const CLINE_JSON_CACHE_TTL_MS = 1000;
95
+
96
+ function _fileStamp(filePath) {
97
+ // Best-effort stat — if stat fails (file missing, etc.), the caller
98
+ // will re-read and re-populate the cache.
99
+ try {
100
+ const st = statSync(filePath);
101
+ return `${st.size}:${st.mtimeMs}`;
102
+ } catch {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ function readClineJsonCached(filePath = CLINE_JSON) {
108
+ const slot = _clineCacheSlot();
109
+ const now = Date.now();
110
+ if (slot.entry && slot.entry.filePath === filePath) {
111
+ const age = now - slot.at;
112
+ if (age < CLINE_JSON_CACHE_TTL_MS) {
113
+ // Fast path: still within TTL. Verify the file hasn't been
114
+ // modified externally (e.g. another process, an editor, or a
115
+ // test that writes the file directly). The stamp check is cheap
116
+ // and protects correctness when the file is touched outside
117
+ // our write paths.
118
+ const stamp = _fileStamp(filePath);
119
+ if (stamp === null || stamp === slot.entry.stamp) {
120
+ return slot.entry.data;
121
+ }
122
+ }
123
+ }
124
+ const data = safeReadJSON(filePath, {});
125
+ slot.entry = { filePath, data, stamp: _fileStamp(filePath) };
126
+ slot.at = now;
127
+ return data;
128
+ }
129
+
130
+ function invalidateClineJsonCache() {
131
+ const slot = _clineCacheSlot();
132
+ slot.entry = null;
133
+ slot.at = 0;
134
+ }
135
+
136
+ function loadConfig() {
137
+ return readClineJsonCached(CLINE_JSON);
138
+ }
139
+
140
+ function saveConfig(data) {
141
+ mkdirSync(dirname(CLINE_JSON), { recursive: true });
142
+ atomicWriteJson(CLINE_JSON, data);
143
+ invalidateClineJsonCache();
144
+ }
145
+
146
+ // v4.6.0 — expose the on-disk load/save helpers so route modules can
147
+ // patch the same cline.json (e.g. the /api/llm/system-llm endpoint
148
+ // in routes/config.mjs). Prior sessions had a blocker where these
149
+ // were not exported and config.mjs referenced them implicitly.
150
+ export { loadConfig, saveConfig };
151
+
152
+ // v5.0.0 — expose cache helpers for tests and other consumers that need
153
+ // to force a re-read (e.g. settings-store after a write).
154
+ export { readClineJsonCached, invalidateClineJsonCache, CLINE_JSON_CACHE_TTL_MS };
155
+
156
+ // ── v4.6.0 Backup-key rotation constants ────────────────────────────────────
157
+ //
158
+ // ERROR_COOLDOWN_THRESHOLD: after this many recorded errors on the same
159
+ // envVar, the key is demoted to `cooldown` and `withKeyRotation` skips
160
+ // it. The operator can re-enable it via PUT
161
+ // /api/providers/:id/keys/:envVar/status with `{status: 'active'}` once
162
+ // the upstream quota/rate-limit window has reset.
163
+ //
164
+ // MAX_ROTATION_ATTEMPTS: bound on the number of times `withKeyRotation`
165
+ // will retry the user's fn with a different key before throwing. With
166
+ // `cooldown` skipping, this matches the typical "1 primary + 1 backup"
167
+ // deployment (2 attempts) with headroom for a 3rd emergency key.
168
+ //
169
+ // ROTATION_ERROR_PATTERNS: substring match against the lowercased error
170
+ // message — used as a fallback when no structured `status` is present
171
+ // (e.g. cline-runner's pre-throw errors). Order matters only for
172
+ // readability — first match wins.
173
+ const ERROR_COOLDOWN_THRESHOLD = 3;
174
+ const MAX_ROTATION_ATTEMPTS = 5;
175
+ const ROTATION_ERROR_PATTERNS = [
176
+ /rate[-_ ]?limit/i,
177
+ /too many requests/i,
178
+ /\b429\b/,
179
+ /\bquota\b/i,
180
+ /\binsufficient[_ ]?credits?\b/i,
181
+ /\bunauthorized\b/i,
182
+ /\b401\b/,
183
+ /\b403\b/,
184
+ /\binvalid[_ ]?api[_ ]?key\b/i,
185
+ /\bforbidden\b/i,
186
+ /\b5\d{2}\b/, // 5xx
187
+ ];
188
+
189
+ /**
190
+ * Heuristic: is `err` a retryable error for key rotation?
191
+ *
192
+ * Accepts an Error-shaped value, a structured `{ok:false, error, status}`,
193
+ * or a plain string. Returns true for:
194
+ * - HTTP status in {401, 403, 408, 409, 425, 429} (auth/rate-limit)
195
+ * - HTTP status in {500..599} (server errors)
196
+ * - error code starting with `http_4xx` / `http_5xx` (the convention
197
+ * minimax.mjs uses)
198
+ * - messages matching ROTATION_ERROR_PATTERNS
199
+ *
200
+ * Network errors (ECONNRESET, fetch failed, timeout) are NOT retryable
201
+ * here — they'd fail with the next key too, and we want to surface
202
+ * them quickly.
203
+ *
204
+ * @param {unknown} err
205
+ * @returns {boolean}
206
+ */
207
+ export function isRetryableError(err) {
208
+ if (!err) return false;
209
+ // Structured minimax.mjs result: {ok:false, error:'http_429', status:429}
210
+ const e = /** @type {any} */ (err);
211
+ const status = typeof e.status === 'number' ? e.status : null;
212
+ if (status !== null) {
213
+ if (status === 401 || status === 403 || status === 408 || status === 409 || status === 425 || status === 429) return true;
214
+ if (status >= 500 && status < 600) return true;
215
+ }
216
+ const code = typeof e.error === 'string' ? e.error : '';
217
+ if (/^http_(401|403|408|409|425|429)$/.test(code)) return true;
218
+ if (/^http_5\d{2}$/.test(code)) return true;
219
+ // Fall back to message-substring match — covers cline-runner
220
+ // errors and unknown callers.
221
+ const msg = (e.message || e.toString?.() || '').toString();
222
+ if (!msg) return false;
223
+ // Network errors are explicitly NOT retryable.
224
+ if (/ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|fetch failed|network error|abort(ed)?/i.test(msg)) {
225
+ return false;
226
+ }
227
+ return ROTATION_ERROR_PATTERNS.some((re) => re.test(msg));
228
+ }
229
+
230
+ // ── v4.6.0 keys[] shape helpers ─────────────────────────────────────────────
231
+ //
232
+ // Each provider entry may have a `keys[]` array. Every entry is
233
+ // { envVar, label, status, lastError, errorCount, lastUsed }
234
+ // where:
235
+ // - envVar: the env var name the caller reads at request time
236
+ // (e.g. "BIZAR_MINIMAX_KEY"). The actual key value is
237
+ // NEVER stored — we always go through env at request time.
238
+ // - label: free-text ("Primary", "Backup", "Work laptop")
239
+ // - status: 'active' | 'standby' | 'disabled' | 'cooldown'
240
+ // - active — used by default
241
+ // - standby — rotated to when the active key fails
242
+ // - disabled — operator disabled, never auto-selected
243
+ // - cooldown — auto-disabled after ERROR_COOLDOWN_THRESHOLD errors
244
+ // - lastError: string | null — last error message recorded
245
+ // - errorCount: integer — total errors recorded
246
+ // - lastUsed: number | null — epoch ms of last successful use
247
+ //
248
+ // The legacy `apiKey`/`backupApiKey` string fields are still kept on disk
249
+ // for back-compat with cline itself (which reads them) and the
250
+ // existing test suite. `migrateKeysShape()` synthesizes a `keys[]` from
251
+ // those fields whenever the provider is loaded.
252
+
253
+ const VALID_KEY_STATUSES = new Set(['active', 'standby', 'disabled', 'cooldown']);
254
+
255
+ /**
256
+ * Build a normalized `keys[]` array from a raw provider entry.
257
+ * Pure function — no side effects. Used by loadConfig() to normalize.
258
+ *
259
+ * @param {Record<string, any> | null | undefined} p
260
+ * @returns {Array<{envVar: string, label: string, status: string, lastError: string|null, errorCount: number, lastUsed: number|null}>}
261
+ */
262
+ export function keysFromProvider(p) {
263
+ if (!p || typeof p !== 'object') return [];
264
+ if (Array.isArray(p.keys) && p.keys.length > 0) {
265
+ return p.keys
266
+ .filter((k) => k && typeof k === 'object' && typeof k.envVar === 'string')
267
+ .map((k) => ({
268
+ envVar: String(k.envVar),
269
+ label: typeof k.label === 'string' ? k.label : 'Key',
270
+ status: VALID_KEY_STATUSES.has(k.status) ? k.status : 'standby',
271
+ lastError: typeof k.lastError === 'string' ? k.lastError : null,
272
+ errorCount: Number.isFinite(k.errorCount) ? Number(k.errorCount) : 0,
273
+ lastUsed: Number.isFinite(k.lastUsed) ? Number(k.lastUsed) : null,
274
+ }));
275
+ }
276
+ // Migrate legacy shape: apiKey/backupApiKey → keys[]
277
+ const out = [];
278
+ if (p.apiKey) {
279
+ out.push({
280
+ envVar: '',
281
+ label: 'Primary',
282
+ status: 'active',
283
+ lastError: null,
284
+ errorCount: 0,
285
+ lastUsed: null,
286
+ });
287
+ }
288
+ if (p.backupApiKey) {
289
+ out.push({
290
+ envVar: '',
291
+ label: 'Backup',
292
+ status: 'standby',
293
+ lastError: null,
294
+ errorCount: 0,
295
+ lastUsed: null,
296
+ });
297
+ }
298
+ return out;
299
+ }
300
+
301
+ /**
302
+ * Sync the legacy `apiKey`/`backupApiKey` fields from the `keys[]`
303
+ * array. The active key becomes `apiKey`; the first standby key
304
+ * becomes `backupApiKey`. Other entries are not persisted to the
305
+ * legacy shape (they only live in `keys[]`).
306
+ *
307
+ * Returns a new provider object — pure function. Callers that mutate
308
+ * the cfg in place should assign the result back.
309
+ *
310
+ * @param {Record<string, any>} p
311
+ * @returns {Record<string, any>}
312
+ */
313
+ export function syncLegacyKeys(p) {
314
+ if (!p || typeof p !== 'object') return p;
315
+ if (!Array.isArray(p.keys) || p.keys.length === 0) return p;
316
+ const next = { ...p };
317
+ const active = p.keys.find((k) => k.status === 'active');
318
+ const standby = p.keys.find((k) => k.status === 'standby');
319
+ // We DO NOT write the actual key value to `apiKey` — that lives in
320
+ // the env var. We only mirror whether a key is configured (truthy
321
+ // string) so cline's `options.apiKey` check sees a key present.
322
+ // Existing code that reads `apiKey`/`backupApiKey` and treats
323
+ // truthy-as-configured continues to work.
324
+ next.apiKey = active && active.envVar ? `<env:${active.envVar}>` : '';
325
+ next.backupApiKey = standby && standby.envVar ? `<env:${standby.envVar}>` : '';
326
+ return next;
327
+ }
328
+
329
+ // ── MiniMax model constants ──────────────────────────────────────────────────
330
+ //
331
+ // Cheap/fast everyday model — agents that need a lightweight workhorse.
332
+ export const MINIMAX_DEFAULT = 'MiniMax-M2.7';
333
+
334
+ // ── v4.6.0 Provider catalog ─────────────────────────────────────────────────
335
+ //
336
+ // Curated list of well-known providers. The dashboard's "Add provider"
337
+ // wizard surfaces this so the user can pick instead of typing every
338
+ // field. The catalog entry's `keyPattern` is a RegExp that rejects keys
339
+ // of the wrong shape (e.g. non-sk- prefix for OpenAI) before they reach
340
+ // the disk.
341
+ //
342
+ // `keyHint` is the human-readable example shown next to the paste box.
343
+ // `docs` is the marketing/docs URL surfaced as a link. `models` is the
344
+ // curated short list (the live probe may add more on top of this).
345
+
346
+ export const PROVIDER_CATALOG = Object.freeze([
347
+ Object.freeze({
348
+ id: 'cline',
349
+ name: 'Cline Zen',
350
+ baseURL: 'https://docs.cline.bot/zen/v1',
351
+ keyPattern: /^sk-[A-Za-z0-9]{20,}$/,
352
+ keyHint: 'sk-…',
353
+ docs: 'https://docs.cline.bot/zen',
354
+ models: [
355
+ 'gpt-5.5', 'gpt-5.5-pro', 'gpt-5.4', 'gpt-5.4-pro', 'gpt-5.4-mini', 'gpt-5.4-nano',
356
+ 'gpt-5.3-codex', 'gpt-5.3-codex-spark', 'gpt-5.2', 'gpt-5.2-codex',
357
+ 'gpt-5.1', 'gpt-5.1-codex', 'gpt-5.1-codex-max', 'gpt-5.1-codex-mini',
358
+ 'gpt-5', 'gpt-5-codex', 'gpt-5-nano',
359
+ 'claude-fable-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-opus-4-6', 'claude-opus-4-5',
360
+ 'claude-sonnet-5', 'claude-sonnet-4-6', 'claude-sonnet-4-5', 'claude-haiku-4-5',
361
+ 'gemini-3.5-flash', 'gemini-3.1-pro', 'gemini-3-flash',
362
+ 'qwen3.7-max', 'qwen3.7-plus', 'qwen3.6-plus', 'qwen3.5-plus',
363
+ 'deepseek-v4-pro', 'deepseek-v4-flash',
364
+ 'minimax-m3', 'minimax-m2.7', 'minimax-m2.5',
365
+ 'glm-5.2', 'glm-5.1', 'glm-5',
366
+ 'kimi-k2.7-code', 'kimi-k2.6', 'kimi-k2.5',
367
+ 'grok-build-0.1',
368
+ 'big-pickle', 'mimo-v2.5-free', 'north-mini-code-free', 'nemotron-3-ultra-free', 'deepseek-v4-flash-free',
369
+ ],
370
+ requiresKey: true,
371
+ }),
372
+ Object.freeze({
373
+ id: 'anthropic',
374
+ name: 'Anthropic',
375
+ baseURL: 'https://api.anthropic.com/v1',
376
+ keyPattern: /^sk-ant-[A-Za-z0-9_-]{20,}$/,
377
+ keyHint: 'sk-ant-…',
378
+ docs: 'https://docs.anthropic.com',
379
+ models: [
380
+ 'claude-fable-5',
381
+ 'claude-opus-4-8', 'claude-opus-4-7', 'claude-opus-4-6', 'claude-opus-4-5',
382
+ 'claude-sonnet-5', 'claude-sonnet-4-6', 'claude-sonnet-4-5', 'claude-haiku-4-5',
383
+ ],
384
+ requiresKey: true,
385
+ }),
386
+ Object.freeze({
387
+ id: 'openai',
388
+ name: 'OpenAI',
389
+ baseURL: 'https://api.openai.com/v1',
390
+ keyPattern: /^sk-[A-Za-z0-9]{20,}$/,
391
+ keyHint: 'sk-…',
392
+ docs: 'https://platform.openai.com',
393
+ models: ['gpt-5.5', 'gpt-5.5-pro', 'gpt-5.4', 'gpt-5', 'gpt-5-codex', 'gpt-5-nano', 'gpt-4.1', 'gpt-4o'],
394
+ requiresKey: true,
395
+ }),
396
+ Object.freeze({
397
+ id: 'google',
398
+ name: 'Google Gemini',
399
+ baseURL: 'https://generativelanguage.googleapis.com/v1beta',
400
+ keyPattern: /^AIza[A-Za-z0-9_-]{30,}$/,
401
+ keyHint: 'AIza…',
402
+ docs: 'https://ai.google.dev',
403
+ models: ['gemini-3.5-flash', 'gemini-3.1-pro', 'gemini-3-flash', 'gemini-2.5-pro'],
404
+ requiresKey: true,
405
+ }),
406
+ Object.freeze({
407
+ id: 'minimax',
408
+ name: 'MiniMax',
409
+ baseURL: 'https://api.minimax.io/v1',
410
+ keyPattern: /^sk-(cp|ant|or)-[A-Za-z0-9_-]{20,}$/,
411
+ keyHint: 'sk-cp-…, sk-ant-…, or sk-or-…',
412
+ docs: 'https://api.minimax.io',
413
+ models: [
414
+ Object.freeze({ id: 'MiniMax-M2.7-Flash', name: 'MiniMax M2.7 Flash', tier: 'cheap', recommended: true }),
415
+ Object.freeze({ id: 'MiniMax-M2.7', name: 'MiniMax M2.7', tier: 'cheap' }),
416
+ Object.freeze({ id: 'MiniMax-M3', name: 'MiniMax M3', tier: 'mid' }),
417
+ Object.freeze({ id: 'MiniMax-M3-Reasoning', name: 'MiniMax M3 Reasoning', tier: 'premium' }),
418
+ ],
419
+ requiresKey: true,
420
+ }),
421
+ Object.freeze({
422
+ id: 'groq',
423
+ name: 'Groq',
424
+ baseURL: 'https://api.groq.com/openai/v1',
425
+ keyPattern: /^gsk_[A-Za-z0-9]{20,}$/,
426
+ keyHint: 'gsk_…',
427
+ docs: 'https://console.groq.com',
428
+ models: ['llama-3.3-70b-versatile', 'llama-3.1-8b-instant', 'mixtral-8x7b-32768'],
429
+ requiresKey: true,
430
+ }),
431
+ Object.freeze({
432
+ id: 'mistral',
433
+ name: 'Mistral',
434
+ baseURL: 'https://api.mistral.ai/v1',
435
+ keyPattern: /^[A-Za-z0-9]{20,}$/,
436
+ keyHint: '20+ alphanumeric chars',
437
+ docs: 'https://docs.mistral.ai',
438
+ models: ['mistral-large-latest', 'mistral-medium-latest', 'mistral-small-latest', 'codestral-latest'],
439
+ requiresKey: true,
440
+ }),
441
+ Object.freeze({
442
+ id: 'cohere',
443
+ name: 'Cohere',
444
+ baseURL: 'https://api.cohere.ai/v1',
445
+ keyPattern: /^[A-Za-z0-9-]{20,}$/,
446
+ keyHint: '20+ alphanumeric/dash chars',
447
+ docs: 'https://docs.cohere.com',
448
+ models: ['command-r-plus', 'command-r', 'command', 'embed-english-v3.0'],
449
+ requiresKey: true,
450
+ }),
451
+ Object.freeze({
452
+ id: 'openrouter',
453
+ name: 'OpenRouter',
454
+ baseURL: 'https://openrouter.ai/api/v1',
455
+ keyPattern: /^sk-or-[A-Za-z0-9_-]{20,}$/,
456
+ keyHint: 'sk-or-…',
457
+ docs: 'https://openrouter.ai',
458
+ models: ['openai/gpt-5', 'anthropic/claude-opus-4-7', 'google/gemini-3-pro'],
459
+ requiresKey: true,
460
+ }),
461
+ Object.freeze({
462
+ id: 'deepseek',
463
+ name: 'DeepSeek',
464
+ baseURL: 'https://api.deepseek.com/v1',
465
+ keyPattern: /^sk-[A-Za-z0-9]{20,}$/,
466
+ keyHint: 'sk-…',
467
+ docs: 'https://platform.deepseek.com',
468
+ models: ['deepseek-chat', 'deepseek-reasoner', 'deepseek-coder'],
469
+ requiresKey: true,
470
+ }),
471
+ Object.freeze({
472
+ id: 'ollama',
473
+ name: 'Ollama (local)',
474
+ baseURL: 'http://localhost:11434/v1',
475
+ keyPattern: /^.*$/,
476
+ keyHint: 'no key needed (any value works)',
477
+ docs: 'https://ollama.com',
478
+ models: ['llama3.3', 'qwen2.5', 'mistral', 'codellama', 'phi3'],
479
+ requiresKey: false,
480
+ }),
481
+ Object.freeze({
482
+ id: 'lmstudio',
483
+ name: 'LM Studio (local)',
484
+ baseURL: 'http://localhost:1234/v1',
485
+ keyPattern: /^.*$/,
486
+ keyHint: 'no key needed (any value works)',
487
+ docs: 'https://lmstudio.ai',
488
+ models: [],
489
+ requiresKey: false,
490
+ }),
491
+ Object.freeze({
492
+ id: 'custom',
493
+ name: 'Custom OpenAI-compatible',
494
+ baseURL: '',
495
+ keyPattern: /^.*$/,
496
+ keyHint: 'paste your key (any format)',
497
+ docs: '',
498
+ models: [],
499
+ requiresKey: true,
500
+ }),
501
+ ]);
502
+
503
+ /**
504
+ * Find a catalog entry by id.
505
+ *
506
+ * @param {string} id
507
+ * @returns {Record<string, any> | null}
508
+ */
509
+ export function findCatalogEntry(id) {
510
+ if (!id || typeof id !== 'string') return null;
511
+ return PROVIDER_CATALOG.find((p) => p.id === id) || null;
512
+ }
513
+
514
+ /**
515
+ * Project a catalog entry to the safe shape returned to the UI.
516
+ * Strips `keyPattern` (a RegExp) so it survives JSON.stringify.
517
+ *
518
+ * @param {Record<string, any>} entry
519
+ */
520
+ function catalogPublicShape(entry) {
521
+ return {
522
+ id: entry.id,
523
+ name: entry.name,
524
+ baseURL: entry.baseURL,
525
+ keyHint: entry.keyHint,
526
+ docs: entry.docs,
527
+ models: Array.isArray(entry.models) ? entry.models.slice() : [],
528
+ requiresKey: entry.requiresKey !== false,
529
+ };
530
+ }
531
+
532
+ /**
533
+ * Return the full catalog in the UI-safe shape.
534
+ *
535
+ * @returns {Array<{id, name, baseURL, keyHint, docs, models, requiresKey}>}
536
+ */
537
+ export function listCatalog() {
538
+ return PROVIDER_CATALOG.map(catalogPublicShape);
539
+ }
540
+
541
+ /**
542
+ * Fuzzy-search the provider catalog. Match against id, name, and docs
543
+ * using case-insensitive substring matching with a simple relevance
544
+ * score. Empty query returns the full catalog in id order.
545
+ *
546
+ * @param {string} query
547
+ * @param {{ limit?: number }} [opts]
548
+ * @returns {Array<{id, name, baseURL, keyHint, docs, models, requiresKey, score: number}>}
549
+ */
550
+ export function searchProviders(query, { limit = 20 } = {}) {
551
+ if (!query || typeof query !== 'string' || !query.trim()) {
552
+ return listCatalog().slice(0, limit);
553
+ }
554
+ const q = query.trim().toLowerCase();
555
+ const tokens = q.split(/\s+/).filter((t) => t.length >= 1);
556
+ const out = [];
557
+ for (const entry of PROVIDER_CATALOG) {
558
+ let score = 0;
559
+ const id = entry.id.toLowerCase();
560
+ const name = entry.name.toLowerCase();
561
+ const docs = (entry.docs || '').toLowerCase();
562
+ const models = (entry.models || []).map((m) => String(m).toLowerCase());
563
+ for (const t of tokens) {
564
+ if (id === t) score += 100;
565
+ else if (id.startsWith(t)) score += 30;
566
+ else if (id.includes(t)) score += 10;
567
+ if (name.startsWith(t)) score += 20;
568
+ else if (name.includes(t)) score += 8;
569
+ if (docs.includes(t)) score += 3;
570
+ // Model name matches — lower weight because models are noisy
571
+ // (e.g. "gpt" matches lots of providers), but useful for the
572
+ // "I want a provider that offers claude" use case.
573
+ for (const m of models) {
574
+ if (m === t) score += 15;
575
+ else if (m.startsWith(t)) score += 5;
576
+ else if (m.includes(t)) score += 2;
577
+ }
578
+ }
579
+ if (score > 0) {
580
+ out.push({ ...catalogPublicShape(entry), score });
581
+ }
582
+ }
583
+ out.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
584
+ return out.slice(0, limit);
585
+ }
586
+
587
+ // ── v4.6.0 Rotation exports (top-level so they're importable) ────────────────
588
+
589
+ function getRawProvider(providerId) {
590
+ const cfg = loadConfig();
591
+ const p = cfg?.provider?.[providerId];
592
+ return p ? { cfg, provider: p } : null;
593
+ }
594
+
595
+ /**
596
+ * Return the highest-priority usable key for `providerId`.
597
+ *
598
+ * Priority order:
599
+ * 1. first key with status === 'active'
600
+ * 2. first key with status === 'standby'
601
+ * 3. first key with status === 'cooldown' (better than nothing)
602
+ *
603
+ * Returns `{envVar, label, status, key}` where `key` is the resolved
604
+ * value from `process.env[envVar]` (empty string if not set). Returns
605
+ * `null` if no key is configured at all.
606
+ *
607
+ * @param {string} providerId
608
+ * @returns {{envVar: string, label: string, status: string, key: string} | null}
609
+ */
610
+ export function getActiveKey(providerId) {
611
+ const got = getRawProvider(providerId);
612
+ if (!got) return null;
613
+ const keys = keysFromProvider(got.provider);
614
+ if (keys.length === 0) return null;
615
+ const order = ['active', 'standby', 'cooldown'];
616
+ for (const want of order) {
617
+ const k = keys.find((kk) => kk.status === want);
618
+ if (k) {
619
+ return {
620
+ envVar: k.envVar,
621
+ label: k.label,
622
+ status: k.status,
623
+ key: process.env[k.envVar] || '',
624
+ };
625
+ }
626
+ }
627
+ // All keys disabled — fall through to the first one (manual override)
628
+ const first = keys[0];
629
+ return {
630
+ envVar: first.envVar,
631
+ label: first.label,
632
+ status: first.status,
633
+ key: process.env[first.envVar] || '',
634
+ };
635
+ }
636
+
637
+ /**
638
+ * Persist a change to the provider's `keys[]` array.
639
+ * Re-syncs the legacy `apiKey`/`backupApiKey` fields and writes the
640
+ * entire config back atomically.
641
+ *
642
+ * @param {string} providerId
643
+ * @param {(keys: Array) => Array} mutator
644
+ */
645
+ function updateKeys(providerId, mutator) {
646
+ const cfg = loadConfig();
647
+ cfg.provider = cfg.provider || {};
648
+ const cur = cfg.provider[providerId];
649
+ if (!cur) throw new Error(`provider "${providerId}" not found`);
650
+ const nextKeys = mutator(keysFromProvider(cur));
651
+ const nextProvider = { ...cur, keys: nextKeys };
652
+ cfg.provider[providerId] = syncLegacyKeys(nextProvider);
653
+ saveConfig(cfg);
654
+ return cfg.provider[providerId];
655
+ }
656
+
657
+ /**
658
+ * Record an error on a specific key. Bumps `errorCount`, sets
659
+ * `lastError`, and demotes `status` to `cooldown` once the threshold
660
+ * is hit. Returns the (possibly updated) key entry.
661
+ *
662
+ * @param {string} providerId
663
+ * @param {string} envVar
664
+ * @param {string | Error} error
665
+ * @returns {{envVar: string, label: string, status: string, lastError: string|null, errorCount: number, lastUsed: number|null} | null}
666
+ */
667
+ export function markKeyError(providerId, envVar, error) {
668
+ if (!providerId || !envVar) return null;
669
+ const errMsg = error instanceof Error ? error.message : String(error || '');
670
+ return updateKeys(providerId, (keys) => {
671
+ const idx = keys.findIndex((k) => k.envVar === envVar);
672
+ if (idx === -1) return keys; // unknown envVar — no-op
673
+ const cur = keys[idx];
674
+ const errorCount = (cur.errorCount || 0) + 1;
675
+ const status = errorCount >= ERROR_COOLDOWN_THRESHOLD ? 'cooldown' : cur.status;
676
+ const next = {
677
+ ...cur,
678
+ errorCount,
679
+ lastError: errMsg.slice(0, 500),
680
+ status,
681
+ };
682
+ const out = keys.slice();
683
+ out[idx] = next;
684
+ return out;
685
+ })?.keys?.find((k) => k.envVar === envVar) || null;
686
+ }
687
+
688
+ /**
689
+ * Mark a key as used successfully (clears errorCount).
690
+ *
691
+ * @param {string} providerId
692
+ * @param {string} envVar
693
+ */
694
+ export function markKeySuccess(providerId, envVar) {
695
+ if (!providerId || !envVar) return;
696
+ updateKeys(providerId, (keys) => {
697
+ const idx = keys.findIndex((k) => k.envVar === envVar);
698
+ if (idx === -1) return keys;
699
+ const cur = keys[idx];
700
+ const out = keys.slice();
701
+ out[idx] = {
702
+ ...cur,
703
+ errorCount: 0,
704
+ lastError: null,
705
+ lastUsed: Date.now(),
706
+ };
707
+ return out;
708
+ });
709
+ }
710
+
711
+ /**
712
+ * Rotate to the next usable key. Marks the current active key as
713
+ * `disabled` and promotes the first `standby` (or any non-active
714
+ * non-disabled key) to `active`. Returns the new active key, or null
715
+ * if no keys remain.
716
+ *
717
+ * @param {string} providerId
718
+ * @returns {{envVar: string, label: string, status: string} | null}
719
+ */
720
+ export function rotateKey(providerId) {
721
+ const got = getRawProvider(providerId);
722
+ if (!got) return null;
723
+ let result = null;
724
+ updateKeys(providerId, (keys) => {
725
+ if (keys.length === 0) return keys;
726
+ const curIdx = keys.findIndex((k) => k.status === 'active');
727
+ const out = keys.map((k) => ({ ...k }));
728
+ if (curIdx !== -1) out[curIdx] = { ...out[curIdx], status: 'disabled' };
729
+ // Find next candidate: prefer standby, then anything not already
730
+ // active/disabled. We avoid `cooldown` if there are other options.
731
+ const nextIdx = out.findIndex((k, i) => i !== curIdx && k.status === 'standby');
732
+ const fallbackIdx = nextIdx === -1
733
+ ? out.findIndex((k, i) => i !== curIdx && k.status !== 'disabled' && k.status !== 'active')
734
+ : -1;
735
+ const promoteIdx = nextIdx !== -1 ? nextIdx : fallbackIdx;
736
+ if (promoteIdx === -1) return out; // nothing to rotate to
737
+ out[promoteIdx] = { ...out[promoteIdx], status: 'active' };
738
+ result = {
739
+ envVar: out[promoteIdx].envVar,
740
+ label: out[promoteIdx].label,
741
+ status: 'active',
742
+ };
743
+ return out;
744
+ });
745
+ return result;
746
+ }
747
+
748
+ /**
749
+ * Add a backup key to a provider. Creates the `keys[]` array from the
750
+ * legacy `apiKey` if it doesn't exist yet. Returns the inserted key
751
+ * entry.
752
+ *
753
+ * @param {string} providerId
754
+ * @param {string} envVar
755
+ * @param {string} [label]
756
+ * @returns {{envVar: string, label: string, status: string}}
757
+ */
758
+ export function addBackupKey(providerId, envVar, label) {
759
+ if (!providerId) throw new Error('providerId required');
760
+ if (!envVar || typeof envVar !== 'string') throw new Error('envVar required');
761
+ let inserted = null;
762
+ updateKeys(providerId, (keys) => {
763
+ if (keys.some((k) => k.envVar === envVar)) {
764
+ throw new Error(`key with envVar "${envVar}" already exists`);
765
+ }
766
+ // New key is standby by default; the active key stays primary.
767
+ const next = [
768
+ ...keys,
769
+ {
770
+ envVar,
771
+ label: typeof label === 'string' && label.trim() ? label.trim() : `Backup ${keys.length}`,
772
+ status: 'standby',
773
+ lastError: null,
774
+ errorCount: 0,
775
+ lastUsed: null,
776
+ },
777
+ ];
778
+ inserted = next[next.length - 1];
779
+ return next;
780
+ });
781
+ return inserted;
782
+ }
783
+
784
+ /**
785
+ * Remove a key from a provider. Refuses to remove the last remaining
786
+ * key (would leave the provider with no way to authenticate).
787
+ *
788
+ * @param {string} providerId
789
+ * @param {string} envVar
790
+ * @returns {boolean} true if removed
791
+ */
792
+ export function removeBackupKey(providerId, envVar) {
793
+ if (!providerId || !envVar) throw new Error('providerId and envVar required');
794
+ let removed = false;
795
+ try {
796
+ updateKeys(providerId, (keys) => {
797
+ if (keys.length <= 1) {
798
+ throw new Error('cannot remove the last key — at least one key must remain');
799
+ }
800
+ const next = keys.filter((k) => k.envVar !== envVar);
801
+ if (next.length === keys.length) {
802
+ throw new Error(`key with envVar "${envVar}" not found`);
803
+ }
804
+ removed = true;
805
+ return next;
806
+ });
807
+ } catch (err) {
808
+ if (removed) throw err; // re-throw real errors
809
+ throw err;
810
+ }
811
+ return removed;
812
+ }
813
+
814
+ /**
815
+ * Set a key's status manually (operator override).
816
+ *
817
+ * @param {string} providerId
818
+ * @param {string} envVar
819
+ * @param {'active'|'standby'|'disabled'|'cooldown'} status
820
+ */
821
+ export function setKeyStatus(providerId, envVar, status) {
822
+ if (!VALID_KEY_STATUSES.has(status)) {
823
+ throw new Error(`invalid status "${status}"`);
824
+ }
825
+ return updateKeys(providerId, (keys) => {
826
+ const idx = keys.findIndex((k) => k.envVar === envVar);
827
+ if (idx === -1) throw new Error(`key with envVar "${envVar}" not found`);
828
+ const out = keys.slice();
829
+ // When promoting to active, demote any other active key.
830
+ if (status === 'active') {
831
+ for (let i = 0; i < out.length; i++) {
832
+ if (i !== idx && out[i].status === 'active') {
833
+ out[i] = { ...out[i], status: 'standby' };
834
+ }
835
+ }
836
+ }
837
+ const cur = out[idx];
838
+ out[idx] = {
839
+ ...cur,
840
+ status,
841
+ // Resetting status clears the error bookkeeping.
842
+ errorCount: status === 'active' ? 0 : cur.errorCount,
843
+ lastError: status === 'active' ? null : cur.lastError,
844
+ };
845
+ return out;
846
+ })?.keys?.find((k) => k.envVar === envVar) || null;
847
+ }
848
+
849
+ /**
850
+ * Run `fn(key, envVar)` against the active key. On a retryable error
851
+ * (`isRetryableError`), mark the error and rotate to the next key —
852
+ * retrying up to `MAX_ROTATION_ATTEMPTS` times. Returns the first
853
+ * successful result, or throws the last error if all keys failed.
854
+ *
855
+ * Usage:
856
+ * const text = await withKeyRotation('minimax', async (key, envVar) => {
857
+ * return await callProvider(apiKey=key);
858
+ * });
859
+ *
860
+ * @template T
861
+ * @param {string} providerId
862
+ * @param {(key: string, envVar: string) => Promise<T>} fn
863
+ * @param {{ maxAttempts?: number, isRetryable?: (err: unknown) => boolean }} [opts]
864
+ * @returns {Promise<T>}
865
+ */
866
+ export async function withKeyRotation(providerId, fn, opts = {}) {
867
+ if (typeof fn !== 'function') throw new Error('fn must be a function');
868
+ const maxAttempts = Math.max(1, Math.min(opts.maxAttempts || MAX_ROTATION_ATTEMPTS, 16));
869
+ const retryable = opts.isRetryable || isRetryableError;
870
+ const tried = new Set();
871
+ let lastErr = null;
872
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
873
+ const active = getActiveKey(providerId);
874
+ if (!active) {
875
+ const err = new Error(`no keys configured for provider "${providerId}"`);
876
+ err.code = 'no_keys';
877
+ throw err;
878
+ }
879
+ if (tried.has(active.envVar)) {
880
+ // We've already tried this envVar — no more rotation candidates.
881
+ break;
882
+ }
883
+ tried.add(active.envVar);
884
+ try {
885
+ const result = await fn(active.key, active.envVar);
886
+ // Success — clear error bookkeeping on the key we used.
887
+ markKeySuccess(providerId, active.envVar);
888
+ return result;
889
+ } catch (err) {
890
+ lastErr = err;
891
+ if (!retryable(err)) throw err;
892
+ markKeyError(providerId, active.envVar, err);
893
+ const rotated = rotateKey(providerId);
894
+ if (!rotated) break;
895
+ }
896
+ }
897
+ // Exhausted all candidates
898
+ const err = lastErr instanceof Error ? lastErr : new Error(String(lastErr));
899
+ err.code = err.code || 'rotation_exhausted';
900
+ err.attempts = tried.size;
901
+ throw err;
902
+ }
903
+
904
+ function mask(value) {
905
+ if (typeof value !== 'string' || !value) return '';
906
+ // Short keys (≤8 chars) get a distinct indicator so the UI can show
907
+ // "***short***" instead of looking like the field is empty.
908
+ if (value.length <= 8) return '***short***';
909
+ return value.slice(0, 2) + '...' + value.slice(-2);
910
+ }
911
+
912
+ function unmask(stored, incoming) {
913
+ // Incoming is the user-typed value. If it's the masked form, keep stored.
914
+ if (typeof incoming === 'string' && incoming.startsWith('***') && incoming.endsWith('***')) {
915
+ return stored;
916
+ }
917
+ return incoming;
918
+ }
919
+
920
+ /**
921
+ * v3.20.10 — Three-way merge for fields that may be missing from a
922
+ * patch object. Used by `update()` so an operator who submits
923
+ * `{ backupApiKey: 'new-key' }` (without re-supplying `apiKey`)
924
+ * doesn't accidentally clear the stored `apiKey` to undefined.
925
+ *
926
+ * stored — current on-disk value (string)
927
+ * incoming — patch value (string | undefined)
928
+ * Returns:
929
+ * - `stored` if `incoming` is undefined (field not in patch)
930
+ * - `stored` if `incoming` is the masked `***...***` placeholder
931
+ * - `incoming` otherwise (real value to replace)
932
+ *
933
+ * Replaces the previous `unmask(stored, patch.apiKey)` which returned
934
+ * undefined whenever the patch didn't include apiKey — that lost the
935
+ * stored key on every partial-update. Same issue applied to the new
936
+ * `backupApiKey` field, so the helper covers both.
937
+ */
938
+ function preserveOrReplace(stored, incoming) {
939
+ if (incoming === undefined) return stored;
940
+ return unmask(stored, incoming);
941
+ }
942
+
943
+ export const providersStore = {
944
+ CLINE_JSON,
945
+
946
+ list() {
947
+ const cfg = loadConfig();
948
+ const providers = cfg.provider || {};
949
+ return Object.entries(providers).map(([id, p]) => ({
950
+ id,
951
+ name: p.name || id,
952
+ baseURL: p.baseURL || p.options?.baseURL || '',
953
+ // Legacy back-compat fields — read from disk as-is so existing
954
+ // tests pass and the UI keeps working. These are the actual key
955
+ // values, masked for display.
956
+ apiKey: mask(p.apiKey || p.options?.apiKey || ''),
957
+ backupApiKey: mask(p.backupApiKey || p.options?.backupApiKey || ''),
958
+ // v4.6.0 — full keys[] array. Synthesized from apiKey/backupApiKey
959
+ // if absent; never echoes the key value itself.
960
+ keys: keysFromProvider(p),
961
+ models: Array.isArray(p.models) ? p.models : [],
962
+ enabled: p.enabled !== false,
963
+ }));
964
+ },
965
+
966
+ /**
967
+ * v3.5.6 — Robust provider discovery. Tries three sources in order
968
+ * and merges them so the dashboard shows every provider that is
969
+ * actually in use, even when cline.json has no `provider` key.
970
+ *
971
+ * 1. cline.json `provider` / `providers` key — explicit config.
972
+ * 2. Agent `.md` frontmatter — every `model: provider/model` line
973
+ * implies a usable provider + model pair.
974
+ * 3. cline serve HTTP `/api/providers` (best-effort, 1.5s timeout).
975
+ *
976
+ * Each source contributes providers; duplicates (by id) are merged so
977
+ * the explicit config wins on baseURL/apiKey and the inferred sources
978
+ * contribute their known models.
979
+ *
980
+ * Returns an array of:
981
+ * { id, name, baseURL, apiKey, models: [{id, name, source?}],
982
+ * source: 'config'|'agents'|'serve'|'config+agents' }
983
+ */
984
+ async listAll() {
985
+ const byId = new Map();
986
+
987
+ const upsert = (id, patch, source) => {
988
+ const cur = byId.get(id) || {
989
+ id,
990
+ name: id,
991
+ baseURL: '',
992
+ apiKey: '',
993
+ backupApiKey: '',
994
+ keys: [],
995
+ models: [],
996
+ enabled: true,
997
+ source: '',
998
+ };
999
+ const next = { ...cur, ...patch };
1000
+ // Merge models by id
1001
+ const modelMap = new Map();
1002
+ for (const m of cur.models) modelMap.set(m.id, m);
1003
+ for (const m of patch.models || []) {
1004
+ const existing = modelMap.get(m.id);
1005
+ modelMap.set(m.id, existing ? { ...existing, ...m } : m);
1006
+ }
1007
+ next.models = Array.from(modelMap.values());
1008
+ next.source = cur.source
1009
+ ? cur.source.includes(source)
1010
+ ? cur.source
1011
+ : `${cur.source}+${source}`
1012
+ : source;
1013
+ byId.set(id, next);
1014
+ };
1015
+
1016
+ // Source 1: cline.json provider/providers key
1017
+ try {
1018
+ const cfg = loadConfig();
1019
+ const explicit = cfg.provider || cfg.providers || {};
1020
+ for (const [id, p] of Object.entries(explicit)) {
1021
+ if (!p || typeof p !== 'object') continue;
1022
+ upsert(
1023
+ id,
1024
+ {
1025
+ name: p.name || id,
1026
+ baseURL: p.baseURL || p.options?.baseURL || '',
1027
+ apiKey: mask(p.apiKey || p.options?.apiKey || ''),
1028
+ backupApiKey: mask(p.backupApiKey || p.options?.backupApiKey || ''),
1029
+ // v4.6.0 — include keys[] alongside the legacy fields
1030
+ keys: keysFromProvider(p),
1031
+ models: (Array.isArray(p.models) ? p.models : []).map((m) => ({
1032
+ id: typeof m === 'string' ? m : m.id || m.name || String(m),
1033
+ name: typeof m === 'string' ? m : m.name || m.id || String(m),
1034
+ })),
1035
+ enabled: p.enabled !== false,
1036
+ },
1037
+ 'config',
1038
+ );
1039
+ }
1040
+ } catch {
1041
+ /* best-effort */
1042
+ }
1043
+
1044
+ // Source 2: agent .md frontmatter
1045
+ try {
1046
+ if (existsSync(CLINE_AGENTS_DIR)) {
1047
+ for (const file of readdirSync(CLINE_AGENTS_DIR)) {
1048
+ if (!file.endsWith('.md')) continue;
1049
+ const full = join(CLINE_AGENTS_DIR, file);
1050
+ let raw;
1051
+ try {
1052
+ raw = readFileSync(full, 'utf8');
1053
+ } catch {
1054
+ continue;
1055
+ }
1056
+ const modelMatch = raw.match(/^model:\s*([^\s#]+)/m);
1057
+ if (!modelMatch) continue;
1058
+ const modelRef = modelMatch[1].trim();
1059
+ const slashIdx = modelRef.indexOf('/');
1060
+ if (slashIdx <= 0) continue;
1061
+ const providerId = modelRef.slice(0, slashIdx);
1062
+ const modelId = modelRef.slice(slashIdx + 1);
1063
+ if (!providerId || !modelId) continue;
1064
+ upsert(
1065
+ providerId,
1066
+ {
1067
+ models: [{ id: modelId, name: modelId, source: `agents/${file.replace(/\.md$/, '')}` }],
1068
+ },
1069
+ 'agents',
1070
+ );
1071
+ }
1072
+ }
1073
+ } catch {
1074
+ /* best-effort */
1075
+ }
1076
+
1077
+ // Source 3: claude CLI probe. v6.3.0 — Claude Code has no
1078
+ // equivalent of the old cline serve HTTP API. Instead we
1079
+ // ping `claude -v` to confirm the runtime is reachable and,
1080
+ // if so, surface it as a "claude-cli" provider entry with the
1081
+ // models Claude Code natively supports.
1082
+ try {
1083
+ const { spawnSync } = await import('node:child_process');
1084
+ const probe = spawnSync('claude', ['-v'], { stdio: 'ignore', timeout: 1500 });
1085
+ if (probe.status === 0) {
1086
+ upsert(
1087
+ 'claude-cli',
1088
+ {
1089
+ name: 'Claude Code CLI',
1090
+ baseURL: '',
1091
+ models: [
1092
+ { id: 'claude-opus-4', name: 'Claude Opus 4' },
1093
+ { id: 'claude-sonnet-4', name: 'Claude Sonnet 4' },
1094
+ { id: 'claude-haiku-4', name: 'Claude Haiku 4' },
1095
+ ],
1096
+ },
1097
+ 'cli',
1098
+ );
1099
+ }
1100
+ } catch {
1101
+ /* claude CLI unreachable — fall through */
1102
+ }
1103
+
1104
+ return Array.from(byId.values()).sort((a, b) => a.id.localeCompare(b.id));
1105
+ },
1106
+
1107
+ /**
1108
+ * v3.5.6 — Return the currently configured default provider + model.
1109
+ * Reads from cline.json (`model`, `provider`, `small_model`).
1110
+ * Falls back to scanning agents to find the default_agent's model.
1111
+ *
1112
+ * Returns: { providerId, modelId, source, agent?, smallModel? } or
1113
+ * null if nothing can be determined.
1114
+ */
1115
+ async getActive() {
1116
+ let providerId = null;
1117
+ let modelId = null;
1118
+ let source = null;
1119
+
1120
+ try {
1121
+ const cfg = loadConfig();
1122
+ const m = typeof cfg.model === 'string' ? cfg.model : '';
1123
+ if (m && m.includes('/')) {
1124
+ const slashIdx = m.indexOf('/');
1125
+ providerId = m.slice(0, slashIdx);
1126
+ modelId = m.slice(slashIdx + 1);
1127
+ source = 'cline.json';
1128
+ } else if (cfg.provider && typeof cfg.provider === 'object') {
1129
+ const firstId = Object.keys(cfg.provider)[0];
1130
+ if (firstId) {
1131
+ providerId = firstId;
1132
+ source = 'cline.json:provider';
1133
+ }
1134
+ }
1135
+
1136
+ let smallModel = null;
1137
+ if (typeof cfg.small_model === 'string' && cfg.small_model.includes('/')) {
1138
+ smallModel = cfg.small_model;
1139
+ }
1140
+
1141
+ let defaultAgent = typeof cfg.default_agent === 'string' ? cfg.default_agent : null;
1142
+
1143
+ // If we got a providerId but no modelId, look up the default_agent's model
1144
+ let agentModel = null;
1145
+ if (providerId && !modelId && defaultAgent && existsSync(CLINE_AGENTS_DIR)) {
1146
+ try {
1147
+ const file = join(CLINE_AGENTS_DIR, `${defaultAgent}.md`);
1148
+ if (existsSync(file)) {
1149
+ const raw = readFileSync(file, 'utf8');
1150
+ const match = raw.match(/^model:\s*([^\s#]+)/m);
1151
+ if (match) {
1152
+ agentModel = match[1].trim();
1153
+ if (agentModel.includes('/')) {
1154
+ const slashIdx = agentModel.indexOf('/');
1155
+ const inferredProvider = agentModel.slice(0, slashIdx);
1156
+ if (!providerId) providerId = inferredProvider;
1157
+ modelId = agentModel.slice(slashIdx + 1);
1158
+ source = `agents/${defaultAgent}`;
1159
+ }
1160
+ }
1161
+ }
1162
+ } catch {
1163
+ /* best-effort */
1164
+ }
1165
+ }
1166
+
1167
+ if (providerId) {
1168
+ return {
1169
+ providerId,
1170
+ modelId: modelId || null,
1171
+ source,
1172
+ agent: defaultAgent || null,
1173
+ smallModel,
1174
+ };
1175
+ }
1176
+ } catch {
1177
+ /* best-effort */
1178
+ }
1179
+
1180
+ // Last-resort: scan all agents, pick the most-referenced provider/model
1181
+ try {
1182
+ if (existsSync(CLINE_AGENTS_DIR)) {
1183
+ const counts = new Map(); // 'provider/model' -> count
1184
+ for (const file of readdirSync(CLINE_AGENTS_DIR)) {
1185
+ if (!file.endsWith('.md')) continue;
1186
+ try {
1187
+ const raw = readFileSync(join(CLINE_AGENTS_DIR, file), 'utf8');
1188
+ const m = raw.match(/^model:\s*([^\s#]+)/m);
1189
+ if (m) {
1190
+ const ref = m[1].trim();
1191
+ counts.set(ref, (counts.get(ref) || 0) + 1);
1192
+ }
1193
+ } catch {
1194
+ /* skip */
1195
+ }
1196
+ }
1197
+ if (counts.size > 0) {
1198
+ const top = Array.from(counts.entries()).sort((a, b) => b[1] - a[1])[0];
1199
+ const [ref] = top;
1200
+ if (ref.includes('/')) {
1201
+ const slashIdx = ref.indexOf('/');
1202
+ return {
1203
+ providerId: ref.slice(0, slashIdx),
1204
+ modelId: ref.slice(slashIdx + 1),
1205
+ source: 'agents:inferred',
1206
+ agent: null,
1207
+ smallModel: null,
1208
+ };
1209
+ }
1210
+ }
1211
+ }
1212
+ } catch {
1213
+ /* best-effort */
1214
+ }
1215
+
1216
+ return null;
1217
+ },
1218
+
1219
+ get(id) {
1220
+ return this.list().find((p) => p.id === id) || null;
1221
+ },
1222
+
1223
+ add(input) {
1224
+ if (!input || typeof input !== 'object') throw new Error('input required');
1225
+ // Auto-generate id from name if not provided
1226
+ const id = input.id || (input.name ? input.name.toLowerCase().replace(/[^a-z0-9_-]/g, '-').replace(/^-+|-+$/g, '').replace(/-+/g, '-') : `provider_${Date.now().toString(36)}`);
1227
+ if (!/^[a-z0-9][a-z0-9_-]{0,63}$/i.test(id)) {
1228
+ throw new Error('invalid id');
1229
+ }
1230
+ const cfg = loadConfig();
1231
+ cfg.provider = cfg.provider || {};
1232
+ if (cfg.provider[id]) throw new Error(`provider "${id}" exists`);
1233
+ cfg.provider[id] = {
1234
+ name: input.name || id,
1235
+ baseURL: input.baseURL || '',
1236
+ apiKey: input.apiKey || '',
1237
+ // v3.20.10 — backup key slot. Optional. Stored verbatim; masked
1238
+ // by list() / listAll(). If both keys are present, the operator
1239
+ // can swap them manually via the dashboard if the primary hits
1240
+ // a rate limit or quota.
1241
+ backupApiKey: input.backupApiKey || '',
1242
+ // v4.6.0 — also write a keys[] array so new code that consumes
1243
+ // the rotation-aware shape gets a normalized view. If the
1244
+ // caller also passed `keys[]` in the input, prefer that.
1245
+ keys: Array.isArray(input.keys) && input.keys.length > 0
1246
+ ? input.keys.map((k) => ({
1247
+ envVar: typeof k.envVar === 'string' ? k.envVar : '',
1248
+ label: typeof k.label === 'string' ? k.label : 'Key',
1249
+ status: VALID_KEY_STATUSES.has(k.status) ? k.status : 'standby',
1250
+ lastError: typeof k.lastError === 'string' ? k.lastError : null,
1251
+ errorCount: Number.isFinite(k.errorCount) ? Number(k.errorCount) : 0,
1252
+ lastUsed: Number.isFinite(k.lastUsed) ? Number(k.lastUsed) : null,
1253
+ }))
1254
+ : keysFromProvider({ apiKey: input.apiKey, backupApiKey: input.backupApiKey }),
1255
+ models: Array.isArray(input.models) ? input.models : [],
1256
+ enabled: input.enabled !== false,
1257
+ };
1258
+ saveConfig(cfg);
1259
+ return this.get(id);
1260
+ },
1261
+
1262
+ update(id, patch) {
1263
+ if (!patch || typeof patch !== 'object') throw new Error('patch required');
1264
+ const cfg = loadConfig();
1265
+ cfg.provider = cfg.provider || {};
1266
+ const cur = cfg.provider[id];
1267
+ if (!cur) throw new Error(`provider "${id}" not found`);
1268
+ cfg.provider[id] = {
1269
+ ...cur,
1270
+ name: patch.name ?? cur.name,
1271
+ baseURL: patch.baseURL ?? cur.baseURL,
1272
+ // v3.20.10 — preserveOrReplace: don't lose the stored key when
1273
+ // the patch omits the field. See preserveOrReplace() above.
1274
+ apiKey: preserveOrReplace(cur.apiKey, patch.apiKey),
1275
+ backupApiKey: preserveOrReplace(cur.backupApiKey, patch.backupApiKey),
1276
+ models: Array.isArray(patch.models) ? patch.models : cur.models,
1277
+ enabled: patch.enabled ?? cur.enabled,
1278
+ };
1279
+ saveConfig(cfg);
1280
+ return this.get(id);
1281
+ },
1282
+
1283
+ remove(id) {
1284
+ const cfg = loadConfig();
1285
+ if (!cfg.provider || !cfg.provider[id]) return false;
1286
+ delete cfg.provider[id];
1287
+ saveConfig(cfg);
1288
+ return true;
1289
+ },
1290
+
1291
+ /**
1292
+ * v4.4.14 — Add (or update) a provider with a user-supplied key.
1293
+ * Auto-fills the rest from the canonical preset + a live model
1294
+ * discovery probe. The user pastes the key; we figure out:
1295
+ * - the id (from name or explicit override)
1296
+ * - the display name (from preset)
1297
+ * - the baseURL (from preset)
1298
+ * - the model list (probes /v1/models and falls back to preset defaults)
1299
+ * - whether to set systemLlm to use this provider+model
1300
+ *
1301
+ * Returns the resulting provider plus a `probe` field describing
1302
+ * whether the model discovery succeeded and the models found.
1303
+ */
1304
+ async addWithAuto({ id, name, apiKey, groupId = 'default', setAsSystemLlm = true, preferredModel, systemLlmModel, timeoutMs = 8000 } = {}) {
1305
+ if (!apiKey || !apiKey.trim()) {
1306
+ return { ok: false, error: 'no_api_key', message: 'API key is required' };
1307
+ }
1308
+ // Resolve the preset by id (or by name as a fallback for unknown ids).
1309
+ const spec =
1310
+ this.KNOWN_PROVIDERS.find((p) => p.id === id) ||
1311
+ this.KNOWN_PROVIDERS.find((p) => (name || '').toLowerCase().includes((p.name || '').toLowerCase()));
1312
+ if (!spec) {
1313
+ return {
1314
+ ok: false,
1315
+ error: 'unknown_provider',
1316
+ message: `Unknown provider id "${id}". Known: ${this.KNOWN_PROVIDERS.map((p) => p.id).join(', ')}`,
1317
+ };
1318
+ }
1319
+ if (spec.keyPattern && !spec.keyPattern.test(apiKey)) {
1320
+ return {
1321
+ ok: false,
1322
+ error: 'invalid_key_format',
1323
+ message: `Key doesn't match expected pattern for ${spec.name} (${spec.keyPattern}).`,
1324
+ };
1325
+ }
1326
+ const finalId = spec.id; // always use the canonical id (e.g. "minimax", "cline")
1327
+ const baseURL = spec.baseURL;
1328
+ const finalName = spec.name;
1329
+
1330
+ // Probe /v1/models to discover the live model list. Falls back to
1331
+ // the preset's knownModels if the probe fails.
1332
+ let probeModels = [];
1333
+ let probeStatus = 'unknown';
1334
+ try {
1335
+ const ctrl = new AbortController();
1336
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
1337
+ const headers = { accept: 'application/json' };
1338
+ if (spec.id !== 'anthropic') headers['Authorization'] = `Bearer ${apiKey}`;
1339
+ const resp = await fetch(`${baseURL}/models`, { headers, signal: ctrl.signal });
1340
+ clearTimeout(timer);
1341
+ if (resp.ok) {
1342
+ const body = await resp.json().catch(() => null);
1343
+ const arr = Array.isArray(body?.data) ? body.data
1344
+ : Array.isArray(body) ? body
1345
+ : null;
1346
+ if (arr) {
1347
+ probeModels = arr.map((m) => m.id).filter(Boolean);
1348
+ probeStatus = 'ok';
1349
+ } else {
1350
+ probeStatus = 'unparseable';
1351
+ }
1352
+ } else {
1353
+ probeStatus = `http_${resp.status}`;
1354
+ }
1355
+ } catch (err) {
1356
+ probeStatus = err?.name === 'AbortError' ? 'timeout' : 'network';
1357
+ }
1358
+
1359
+ const models = probeModels.length > 0
1360
+ ? probeModels
1361
+ : Array.isArray(spec.defaultModels) ? spec.defaultModels
1362
+ : Array.isArray(spec.knownModels) ? spec.knownModels
1363
+ : [];
1364
+
1365
+ // Persist.
1366
+ const cfg = loadConfig();
1367
+ cfg.provider = cfg.provider || {};
1368
+ const cur = cfg.provider[finalId] || {};
1369
+ cfg.provider[finalId] = {
1370
+ name: finalName,
1371
+ baseURL,
1372
+ apiKey: apiKey.trim(),
1373
+ backupApiKey: cur.backupApiKey || '',
1374
+ // v4.6.0 — write a keys[] array. The active key's envVar is
1375
+ // empty here because the API key was pasted inline (not via
1376
+ // env var); backup keys carried over from a previous config
1377
+ // are preserved with their envVars.
1378
+ keys: keysFromProvider({
1379
+ apiKey: apiKey.trim(),
1380
+ backupApiKey: cur.backupApiKey || '',
1381
+ }),
1382
+ models,
1383
+ enabled: true,
1384
+ };
1385
+ if (groupId) cfg.provider[finalId].options = { ...(cfg.provider[finalId].options || {}), groupId };
1386
+
1387
+ // Apply system LLM config so the rest of the dashboard's
1388
+ // /api/llm/* calls (auto-title, enhance-prompt) hit this provider.
1389
+ if (setAsSystemLlm) {
1390
+ cfg.systemLlm = cfg.systemLlm || {};
1391
+ cfg.systemLlm.enabled = true;
1392
+ cfg.systemLlm.provider = finalId;
1393
+ const model = systemLlmModel
1394
+ || preferredModel
1395
+ || (Array.isArray(models) ? models[0] : null)
1396
+ || spec.defaultModel
1397
+ || 'MiniMax-M3';
1398
+ cfg.systemLlm.model = `${finalId}/${model}`;
1399
+ }
1400
+ saveConfig(cfg);
1401
+ return {
1402
+ ok: true,
1403
+ provider: cfg.provider[finalId],
1404
+ probe: {
1405
+ status: probeStatus,
1406
+ modelCount: probeModels.length,
1407
+ models,
1408
+ },
1409
+ systemLlm: cfg.systemLlm,
1410
+ };
1411
+ },
1412
+
1413
+ /**
1414
+ * v3.16.0 — Auto-detect providers from environment variables.
1415
+ *
1416
+ * Recognised API keys: ANTHROPIC_API_KEY, OPENAI_API_KEY,
1417
+ * GEMINI_API_KEY / GOOGLE_API_KEY, MISTRAL_API_KEY, GROQ_API_KEY,
1418
+ * COHERE_API_KEY, OPENROUTER_API_KEY, DEEPSEEK_API_KEY,
1419
+ * MINIMAX_API_KEY.
1420
+ *
1421
+ * v3.20.10 — Backup keys: every KNOWN_PROVIDER also accepts a
1422
+ * `<NAME>_BACKUP_API_KEY` env var (e.g. `MINIMAX_API_KEY_BACKUP`)
1423
+ * and a `backupApiKey` field in the config. autoDetect() returns
1424
+ * both, and the dashboard UI surfaces them in the Providers page
1425
+ * so the operator knows "if the primary hits a rate limit, swap in
1426
+ * the backup manually" without having to dig through shell history.
1427
+ *
1428
+ * Detection order (highest priority first):
1429
+ * 1. config.provider.<id>.apiKey
1430
+ * 2. config.provider.<id>.backupApiKey
1431
+ * 3. process.env[envKey] (primary)
1432
+ * 4. process.env[backupEnvKey] (backup)
1433
+ *
1434
+ * Each entry has a status:
1435
+ * - 'configured' — key present AND format checks out
1436
+ * - 'unknown' — env var set but format doesn't match known patterns
1437
+ * - 'no-key' — provider known but no key in env or config
1438
+ *
1439
+ * Probes the canonical /models endpoint with a 1.5s timeout to confirm
1440
+ * the key actually works. Probes are best-effort — failures don't
1441
+ * downgrade status from 'configured' to 'no-key'.
1442
+ */
1443
+ KNOWN_PROVIDERS: [
1444
+ {
1445
+ id: 'anthropic',
1446
+ name: 'Anthropic',
1447
+ envKeys: ['ANTHROPIC_API_KEY'],
1448
+ backupEnvKeys: ['ANTHROPIC_API_KEY_BACKUP', 'ANTHROPIC_BACKUP_API_KEY'],
1449
+ baseURL: 'https://api.anthropic.com/v1',
1450
+ keyPattern: /^sk-ant-[A-Za-z0-9_-]{20,}$/,
1451
+ },
1452
+ {
1453
+ id: 'openai',
1454
+ name: 'OpenAI',
1455
+ envKeys: ['OPENAI_API_KEY'],
1456
+ backupEnvKeys: ['OPENAI_API_KEY_BACKUP', 'OPENAI_BACKUP_API_KEY'],
1457
+ baseURL: 'https://api.openai.com/v1',
1458
+ keyPattern: /^sk-[A-Za-z0-9]{20,}$/,
1459
+ },
1460
+ {
1461
+ id: 'google',
1462
+ name: 'Google AI',
1463
+ envKeys: ['GEMINI_API_KEY', 'GOOGLE_API_KEY'],
1464
+ backupEnvKeys: ['GEMINI_API_KEY_BACKUP', 'GOOGLE_API_KEY_BACKUP'],
1465
+ baseURL: 'https://generativelanguage.googleapis.com/v1beta',
1466
+ keyPattern: /^AIza[A-Za-z0-9_-]{30,}$/,
1467
+ },
1468
+ {
1469
+ id: 'mistral',
1470
+ name: 'Mistral',
1471
+ envKeys: ['MISTRAL_API_KEY'],
1472
+ backupEnvKeys: ['MISTRAL_API_KEY_BACKUP'],
1473
+ baseURL: 'https://api.mistral.ai/v1',
1474
+ keyPattern: /^[A-Za-z0-9]{20,}$/,
1475
+ },
1476
+ {
1477
+ id: 'groq',
1478
+ name: 'Groq',
1479
+ envKeys: ['GROQ_API_KEY'],
1480
+ backupEnvKeys: ['GROQ_API_KEY_BACKUP'],
1481
+ baseURL: 'https://api.groq.com/openai/v1',
1482
+ keyPattern: /^gsk_[A-Za-z0-9]{20,}$/,
1483
+ },
1484
+ {
1485
+ id: 'cohere',
1486
+ name: 'Cohere',
1487
+ envKeys: ['COHERE_API_KEY'],
1488
+ backupEnvKeys: ['COHERE_API_KEY_BACKUP'],
1489
+ baseURL: 'https://api.cohere.com/v1',
1490
+ keyPattern: /^[A-Za-z0-9]{20,}$/,
1491
+ },
1492
+ {
1493
+ id: 'openrouter',
1494
+ name: 'OpenRouter',
1495
+ envKeys: ['OPENROUTER_API_KEY'],
1496
+ backupEnvKeys: ['OPENROUTER_API_KEY_BACKUP'],
1497
+ baseURL: 'https://openrouter.ai/api/v1',
1498
+ keyPattern: /^sk-or-[A-Za-z0-9_-]{20,}$/,
1499
+ },
1500
+ {
1501
+ id: 'deepseek',
1502
+ name: 'DeepSeek',
1503
+ envKeys: ['DEEPSEEK_API_KEY'],
1504
+ backupEnvKeys: ['DEEPSEEK_API_KEY_BACKUP'],
1505
+ baseURL: 'https://api.deepseek.com/v1',
1506
+ keyPattern: /^sk-[A-Za-z0-9]{20,}$/,
1507
+ },
1508
+ {
1509
+ // v4.5.0 — MiniMax provider, fully implemented. The dashboard
1510
+ // onboarding wizard writes the Subscription Key to
1511
+ // ~/.local/share/cline/auth.json (the canonical store written
1512
+ // by cline's `/connect` command). The MiniMax key has a
1513
+ // variable-length format; valid prefixes are `sk-cp-` (Token
1514
+ // Plan + Coding Plan), `sk-ant-` (Anthropic-format compatible),
1515
+ // and `sk-or-` (OpenRouter-style). The chat completions host
1516
+ // is `api.minimax.io/v1` (NOT `api.minimax.chat` which 404s).
1517
+ id: 'minimax',
1518
+ name: 'MiniMax',
1519
+ envKeys: ['MINIMAX_API_KEY', 'ANTHROPIC_API_KEY'],
1520
+ backupEnvKeys: ['MINIMAX_API_KEY_BACKUP', 'MINIMAX_BACKUP_API_KEY', 'ANTHROPIC_API_KEY_BACKUP'],
1521
+ baseURL: 'https://api.minimax.io/v1',
1522
+ keyPattern: /^sk-(cp|ant|or)-[A-Za-z0-9_-]{20,}$/,
1523
+ defaultModel: 'MiniMax-M3',
1524
+ defaultModels: [
1525
+ 'MiniMax-M2.7-Flash', 'MiniMax-M2.7', 'MiniMax-M2.7-highspeed',
1526
+ 'MiniMax-M3', 'MiniMax-M3-Reasoning',
1527
+ 'MiniMax-M2.5', 'MiniMax-M2.5-highspeed', 'MiniMax-M2.1',
1528
+ 'MiniMax-M2.1-highspeed', 'MiniMax-M2',
1529
+ ],
1530
+ },
1531
+ {
1532
+ // v4.4.14 — Cline Zen. Free tier (per-model free quota + paid
1533
+ // top-up). The user signs in to https://docs.cline.bot/auth and gets
1534
+ // an API key, but the dashboard does NOT require a key to add the
1535
+ // provider — we generate a placeholder that the user replaces via
1536
+ // `bizar minimax config` or the dashboard's edit modal. The base
1537
+ // URL is the public Zen endpoint; the model list is from the docs
1538
+ // and refreshes on each provider add (probes /v1/models).
1539
+ id: 'cline',
1540
+ name: 'Cline Zen',
1541
+ envKeys: ['CLINE_API_KEY'],
1542
+ backupEnvKeys: ['CLINE_API_KEY_BACKUP'],
1543
+ baseURL: 'https://docs.cline.bot/zen/v1',
1544
+ keyPattern: /^sk-[A-Za-z0-9]{20,}$/,
1545
+ // Zen uses two base URLs depending on model family:
1546
+ // - OpenAI-compatible: /v1/chat/completions (M3, M2.x, GLM, Kimi, Grok, DeepSeek, Gemini 3 Flash, Big Pickle, MiMo, North, Nemotron)
1547
+ // - Anthropic-format: /v1/messages (Claude family, Qwen3.7, Kimi K2.7)
1548
+ // - Gemini vertex: /v1/models/gemini-3.5-flash, gemini-3.1-pro, gemini-3-flash
1549
+ // The baseURL we store is the OpenAI-compatible path. Mods from
1550
+ // Anthropic/Gemini are still selectable (cline routes internally
1551
+ // to the right URL per model). Users with their own OpenAI/
1552
+ // Anthropic keys can also use Zen as a pass-through.
1553
+ defaultModel: 'gpt-5.5',
1554
+ defaultModels: [
1555
+ 'gpt-5.5', 'gpt-5.5-pro', 'gpt-5.4', 'gpt-5.4-pro', 'gpt-5.4-mini', 'gpt-5.4-nano',
1556
+ 'gpt-5.3-codex', 'gpt-5.3-codex-spark', 'gpt-5.2', 'gpt-5.2-codex',
1557
+ 'gpt-5.1', 'gpt-5.1-codex', 'gpt-5.1-codex-max', 'gpt-5.1-codex-mini',
1558
+ 'gpt-5', 'gpt-5-codex', 'gpt-5-nano',
1559
+ 'claude-fable-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-opus-4-6', 'claude-opus-4-5',
1560
+ 'claude-sonnet-5', 'claude-sonnet-4-6', 'claude-sonnet-4-5', 'claude-haiku-4-5',
1561
+ 'gemini-3.5-flash', 'gemini-3.1-pro', 'gemini-3-flash',
1562
+ 'qwen3.7-max', 'qwen3.7-plus', 'qwen3.6-plus', 'qwen3.5-plus',
1563
+ 'deepseek-v4-pro', 'deepseek-v4-flash',
1564
+ 'minimax-m3', 'minimax-m2.7', 'minimax-m2.5',
1565
+ 'glm-5.2', 'glm-5.1', 'glm-5',
1566
+ 'kimi-k2.7-code', 'kimi-k2.6', 'kimi-k2.5',
1567
+ 'grok-build-0.1',
1568
+ 'big-pickle', 'mimo-v2.5-free', 'north-mini-code-free', 'nemotron-3-ultra-free', 'deepseek-v4-flash-free',
1569
+ ],
1570
+ },
1571
+ ],
1572
+
1573
+ async autoDetect({ probe = true } = {}) {
1574
+ const result = [];
1575
+ for (const spec of this.KNOWN_PROVIDERS) {
1576
+ const cfgInfo = (() => {
1577
+ try {
1578
+ const cfg = loadConfig();
1579
+ const cfgProvider = cfg.provider?.[spec.id];
1580
+ if (!cfgProvider) return { apiKey: '', backupApiKey: '' };
1581
+ return {
1582
+ apiKey: cfgProvider.apiKey || cfgProvider.options?.apiKey || '',
1583
+ backupApiKey: cfgProvider.backupApiKey || cfgProvider.options?.backupApiKey || '',
1584
+ };
1585
+ } catch {
1586
+ return { apiKey: '', backupApiKey: '' };
1587
+ }
1588
+ })();
1589
+
1590
+ // Primary: config > env
1591
+ let apiKey = cfgInfo.apiKey;
1592
+ let keySource = cfgInfo.apiKey ? 'config' : '';
1593
+ if (!apiKey) {
1594
+ for (const k of spec.envKeys) {
1595
+ const v = process.env[k];
1596
+ if (typeof v === 'string' && v.length > 0) {
1597
+ apiKey = v;
1598
+ keySource = `env:${k}`;
1599
+ break;
1600
+ }
1601
+ }
1602
+ }
1603
+
1604
+ // Backup: config > env (independent from primary; you can have
1605
+ // a backup key without a primary, e.g. if you rotate).
1606
+ let backupApiKey = cfgInfo.backupApiKey;
1607
+ let backupSource = cfgInfo.backupApiKey ? 'config' : '';
1608
+ if (!backupApiKey) {
1609
+ for (const k of spec.backupEnvKeys || []) {
1610
+ const v = process.env[k];
1611
+ if (typeof v === 'string' && v.length > 0) {
1612
+ backupApiKey = v;
1613
+ backupSource = `env:${k}`;
1614
+ break;
1615
+ }
1616
+ }
1617
+ }
1618
+
1619
+ const status = !apiKey
1620
+ ? 'no-key'
1621
+ : spec.keyPattern && !spec.keyPattern.test(apiKey)
1622
+ ? 'unknown'
1623
+ : 'configured';
1624
+ const backupStatus = !backupApiKey
1625
+ ? 'no-key'
1626
+ : spec.keyPattern && !spec.keyPattern.test(backupApiKey)
1627
+ ? 'unknown'
1628
+ : 'configured';
1629
+
1630
+ // Probe primary
1631
+ const probeResult = await this._probeKey(spec, apiKey, probe && status === 'configured');
1632
+ const backupProbe = await this._probeKey(spec, backupApiKey, probe && backupStatus === 'configured');
1633
+
1634
+ result.push({
1635
+ id: spec.id,
1636
+ name: spec.name,
1637
+ baseURL: spec.baseURL,
1638
+ envKeys: spec.envKeys,
1639
+ backupEnvKeys: spec.backupEnvKeys || [],
1640
+ status,
1641
+ keySource,
1642
+ hasKey: !!apiKey,
1643
+ probed: probeResult,
1644
+ backup: {
1645
+ status: backupStatus,
1646
+ source: backupSource,
1647
+ hasKey: !!backupApiKey,
1648
+ probed: backupProbe,
1649
+ },
1650
+ });
1651
+ }
1652
+ return result;
1653
+ },
1654
+
1655
+ /**
1656
+ * v3.20.10 — Probe a single key against the provider's /models endpoint.
1657
+ * Returns { ok, status, modelCount? } or { ok: false, reason }.
1658
+ * Exposed as a separate helper so primary + backup keys can be
1659
+ * probed independently (each gets its own timeout + result).
1660
+ */
1661
+ async _probeKey(spec, apiKey, shouldProbe) {
1662
+ if (!shouldProbe || !apiKey || !spec.baseURL) return null;
1663
+ try {
1664
+ const ctrl = new AbortController();
1665
+ const timer = setTimeout(() => ctrl.abort(), 1500);
1666
+ const url = `${spec.baseURL}/models`;
1667
+ const resp = await fetch(url, {
1668
+ headers: { Authorization: `Bearer ${apiKey}` },
1669
+ signal: ctrl.signal,
1670
+ });
1671
+ clearTimeout(timer);
1672
+ const out = { ok: resp.ok, status: resp.status };
1673
+ if (resp.ok) {
1674
+ try {
1675
+ const body = await resp.json();
1676
+ if (Array.isArray(body?.data)) out.modelCount = body.data.length;
1677
+ else if (Array.isArray(body)) out.modelCount = body.length;
1678
+ } catch { /* ignore parse */ }
1679
+ } else {
1680
+ out.reason = `HTTP ${resp.status}`;
1681
+ }
1682
+ return out;
1683
+ } catch (err) {
1684
+ return {
1685
+ ok: false,
1686
+ reason: err && err.name === 'AbortError' ? 'timeout' : 'network',
1687
+ };
1688
+ }
1689
+ },
1690
+ };
1691
+
1692
+ export const mcpsStore = {
1693
+ CLINE_JSON,
1694
+
1695
+ list() {
1696
+ const cfg = loadConfig();
1697
+ const mcps = cfg.mcp || {};
1698
+ return Object.entries(mcps).map(([id, m]) => {
1699
+ const isRemote = m?.type === 'remote';
1700
+ // Local MCP: command is an array in newer cline.json. Older
1701
+ // format had separate `command` (string) + `args` (array). Normalize.
1702
+ const command = isRemote
1703
+ ? ''
1704
+ : Array.isArray(m.command)
1705
+ ? m.command.join(' ')
1706
+ : (m.command || '');
1707
+ const args = isRemote
1708
+ ? []
1709
+ : Array.isArray(m.command)
1710
+ ? m.command
1711
+ : Array.isArray(m.args)
1712
+ ? m.args
1713
+ : [];
1714
+ return {
1715
+ id,
1716
+ type: isRemote ? 'remote' : 'local',
1717
+ command,
1718
+ args,
1719
+ env: m.env || {},
1720
+ url: m.url || '',
1721
+ headers: m.headers || {},
1722
+ oauth: !!m.oauth,
1723
+ enabled: m.enabled !== false,
1724
+ };
1725
+ });
1726
+ },
1727
+
1728
+ get(id) {
1729
+ return this.list().find((m) => m.id === id) || null;
1730
+ },
1731
+
1732
+ add(input) {
1733
+ if (!input || !input.id) throw new Error('id is required');
1734
+ if (!/^[a-z0-9][a-z0-9_-]{0,63}$/i.test(input.id)) {
1735
+ throw new Error('invalid id');
1736
+ }
1737
+ const cfg = loadConfig();
1738
+ cfg.mcp = cfg.mcp || {};
1739
+ if (cfg.mcp[input.id]) throw new Error(`mcp "${input.id}" exists`);
1740
+ const isRemote = input.type === 'remote';
1741
+ cfg.mcp[input.id] = isRemote
1742
+ ? {
1743
+ type: 'remote',
1744
+ url: input.url || '',
1745
+ headers: input.headers || {},
1746
+ oauth: !!input.oauth,
1747
+ enabled: input.enabled !== false,
1748
+ }
1749
+ : {
1750
+ type: 'local',
1751
+ command: Array.isArray(input.args) && input.args.length > 0
1752
+ ? [input.command || '', ...input.args].filter(Boolean)
1753
+ : (input.command || ''),
1754
+ enabled: input.enabled !== false,
1755
+ ...(input.env && Object.keys(input.env).length > 0 ? { env: input.env } : {}),
1756
+ };
1757
+ saveConfig(cfg);
1758
+ return this.get(input.id);
1759
+ },
1760
+
1761
+ update(id, patch) {
1762
+ if (!patch || typeof patch !== 'object') throw new Error('patch required');
1763
+ const cfg = loadConfig();
1764
+ cfg.mcp = cfg.mcp || {};
1765
+ const cur = cfg.mcp[id];
1766
+ if (!cur) throw new Error(`mcp "${id}" not found`);
1767
+ const wasRemote = cur.type === 'remote';
1768
+ const isRemote = patch.type ? patch.type === 'remote' : wasRemote;
1769
+ if (isRemote) {
1770
+ cfg.mcp[id] = {
1771
+ type: 'remote',
1772
+ url: patch.url ?? cur.url ?? '',
1773
+ headers: patch.headers ?? cur.headers ?? {},
1774
+ oauth: patch.oauth ?? cur.oauth ?? false,
1775
+ enabled: patch.enabled ?? cur.enabled ?? true,
1776
+ };
1777
+ } else {
1778
+ const nextCommand = Array.isArray(patch.args) && patch.args.length > 0
1779
+ ? [patch.command ?? cur.command ?? '', ...patch.args].filter(Boolean)
1780
+ : (patch.command ?? cur.command ?? '');
1781
+ cfg.mcp[id] = {
1782
+ type: 'local',
1783
+ command: nextCommand,
1784
+ enabled: patch.enabled ?? cur.enabled ?? true,
1785
+ ...((patch.env && Object.keys(patch.env).length > 0) || cur.env
1786
+ ? { env: patch.env ?? cur.env ?? {} }
1787
+ : {}),
1788
+ };
1789
+ }
1790
+ saveConfig(cfg);
1791
+ return this.get(id);
1792
+ },
1793
+
1794
+ remove(id) {
1795
+ const cfg = loadConfig();
1796
+ if (!cfg.mcp || !cfg.mcp[id]) return false;
1797
+ delete cfg.mcp[id];
1798
+ saveConfig(cfg);
1799
+ return true;
1800
+ },
1801
+ };