crewswarm 0.8.1-beta

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 (362) hide show
  1. package/.env.example +155 -0
  2. package/LICENSE +21 -0
  3. package/README.md +316 -0
  4. package/apps/dashboard/dist/assets/chat-core-BwSoInmZ.js +1 -0
  5. package/apps/dashboard/dist/assets/chat-core-BwSoInmZ.js.br +0 -0
  6. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +1 -0
  7. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  8. package/apps/dashboard/dist/assets/components-CSUb80ze.js +1 -0
  9. package/apps/dashboard/dist/assets/components-CSUb80ze.js.br +0 -0
  10. package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js +1 -0
  11. package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js.br +0 -0
  12. package/apps/dashboard/dist/assets/index-CF0aJRtC.css +1 -0
  13. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  14. package/apps/dashboard/dist/assets/index-Px49zu76.js +2 -0
  15. package/apps/dashboard/dist/assets/index-Px49zu76.js.br +0 -0
  16. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js +1 -0
  17. package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
  18. package/apps/dashboard/dist/assets/setup-wizard-i3eEixlo.js +1 -0
  19. package/apps/dashboard/dist/assets/setup-wizard-i3eEixlo.js.br +0 -0
  20. package/apps/dashboard/dist/assets/tab-agents-tab-BThdsdJY.js +1 -0
  21. package/apps/dashboard/dist/assets/tab-agents-tab-BThdsdJY.js.br +0 -0
  22. package/apps/dashboard/dist/assets/tab-benchmarks-tab-DfCuAClu.js +1 -0
  23. package/apps/dashboard/dist/assets/tab-comms-tab-eHpOSBhG.js +1 -0
  24. package/apps/dashboard/dist/assets/tab-comms-tab-eHpOSBhG.js.br +0 -0
  25. package/apps/dashboard/dist/assets/tab-contacts-tab-yEegNyO4.js +1 -0
  26. package/apps/dashboard/dist/assets/tab-contacts-tab-yEegNyO4.js.br +0 -0
  27. package/apps/dashboard/dist/assets/tab-engines-tab-C3DYxTwy.js +1 -0
  28. package/apps/dashboard/dist/assets/tab-engines-tab-C3DYxTwy.js.br +0 -0
  29. package/apps/dashboard/dist/assets/tab-memory-tab-C59BYFQD.js +1 -0
  30. package/apps/dashboard/dist/assets/tab-memory-tab-C59BYFQD.js.br +0 -0
  31. package/apps/dashboard/dist/assets/tab-models-tab-9Ur7pXWA.js +1 -0
  32. package/apps/dashboard/dist/assets/tab-models-tab-9Ur7pXWA.js.br +0 -0
  33. package/apps/dashboard/dist/assets/tab-pm-loop-tab-D7mnDelU.js +1 -0
  34. package/apps/dashboard/dist/assets/tab-pm-loop-tab-D7mnDelU.js.br +0 -0
  35. package/apps/dashboard/dist/assets/tab-projects-tab-C6h2Mv1K.js +1 -0
  36. package/apps/dashboard/dist/assets/tab-projects-tab-C6h2Mv1K.js.br +0 -0
  37. package/apps/dashboard/dist/assets/tab-prompts-tab-C0wZvWK3.js +1 -0
  38. package/apps/dashboard/dist/assets/tab-prompts-tab-C0wZvWK3.js.br +0 -0
  39. package/apps/dashboard/dist/assets/tab-services-tab-DBj_w3bc.js +1 -0
  40. package/apps/dashboard/dist/assets/tab-services-tab-DBj_w3bc.js.br +0 -0
  41. package/apps/dashboard/dist/assets/tab-settings-tab-ezeqAjZk.js +1 -0
  42. package/apps/dashboard/dist/assets/tab-settings-tab-ezeqAjZk.js.br +0 -0
  43. package/apps/dashboard/dist/assets/tab-skills-tab-BYdU2whk.js +1 -0
  44. package/apps/dashboard/dist/assets/tab-skills-tab-BYdU2whk.js.br +0 -0
  45. package/apps/dashboard/dist/assets/tab-spending-tab-Bg6w9t_p.js +1 -0
  46. package/apps/dashboard/dist/assets/tab-spending-tab-Bg6w9t_p.js.br +0 -0
  47. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BBV9HB2X.js +1 -0
  48. package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BBV9HB2X.js.br +0 -0
  49. package/apps/dashboard/dist/assets/tab-swarm-tab-ChqLlEVs.js +1 -0
  50. package/apps/dashboard/dist/assets/tab-swarm-tab-ChqLlEVs.js.br +0 -0
  51. package/apps/dashboard/dist/assets/tab-usage-tab-B2UWXenJ.js +1 -0
  52. package/apps/dashboard/dist/assets/tab-usage-tab-B2UWXenJ.js.br +0 -0
  53. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js +1 -0
  54. package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
  55. package/apps/dashboard/dist/assets/tab-workflows-tab-6QSXLJ0i.js +1 -0
  56. package/apps/dashboard/dist/assets/tab-workflows-tab-6QSXLJ0i.js.br +0 -0
  57. package/apps/dashboard/dist/favicon.png +0 -0
  58. package/apps/dashboard/dist/index.html +6466 -0
  59. package/apps/dashboard/dist/index.html.br +0 -0
  60. package/apps/dashboard/dist/index.html.gz +0 -0
  61. package/apps/dashboard/dist/signup.html +446 -0
  62. package/apps/dashboard/index.html +6442 -0
  63. package/apps/dashboard/package.json +15 -0
  64. package/apps/dashboard/src/app.js +2823 -0
  65. package/apps/dashboard/src/app.js.br +0 -0
  66. package/apps/dashboard/src/app.js.gz +0 -0
  67. package/apps/dashboard/src/chat/chat-actions.js +1847 -0
  68. package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
  69. package/apps/dashboard/src/chat/unified-messages.js +327 -0
  70. package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
  71. package/apps/dashboard/src/cli-process.js +208 -0
  72. package/apps/dashboard/src/cli-process.js.br +0 -0
  73. package/apps/dashboard/src/cli-process.js.gz +0 -0
  74. package/apps/dashboard/src/components/active-tasks-panel.js +175 -0
  75. package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
  76. package/apps/dashboard/src/core/api.js +18 -0
  77. package/apps/dashboard/src/core/api.js.br +0 -0
  78. package/apps/dashboard/src/core/dom.js +220 -0
  79. package/apps/dashboard/src/core/dom.js.br +0 -0
  80. package/apps/dashboard/src/core/state.js +91 -0
  81. package/apps/dashboard/src/core/state.js.br +0 -0
  82. package/apps/dashboard/src/core/task-manager.js +134 -0
  83. package/apps/dashboard/src/core/task-manager.js.br +0 -0
  84. package/apps/dashboard/src/orchestration-status.js +127 -0
  85. package/apps/dashboard/src/orchestration-status.js.br +0 -0
  86. package/apps/dashboard/src/setup-wizard.js +555 -0
  87. package/apps/dashboard/src/setup-wizard.js.br +0 -0
  88. package/apps/dashboard/src/styles.css +2085 -0
  89. package/apps/dashboard/src/styles.css.br +0 -0
  90. package/apps/dashboard/src/styles.css.gz +0 -0
  91. package/apps/dashboard/src/tabs/agents-tab.js +2237 -0
  92. package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
  93. package/apps/dashboard/src/tabs/benchmarks-tab.js +229 -0
  94. package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
  95. package/apps/dashboard/src/tabs/comms-tab.js +955 -0
  96. package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
  97. package/apps/dashboard/src/tabs/contacts-tab.js +654 -0
  98. package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
  99. package/apps/dashboard/src/tabs/engines-tab.js +175 -0
  100. package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
  101. package/apps/dashboard/src/tabs/memory-tab.js +182 -0
  102. package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
  103. package/apps/dashboard/src/tabs/models-tab.js +441 -0
  104. package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
  105. package/apps/dashboard/src/tabs/pm-loop-tab.js +185 -0
  106. package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
  107. package/apps/dashboard/src/tabs/projects-tab.js +663 -0
  108. package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
  109. package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
  110. package/apps/dashboard/src/tabs/prompts-tab.js +160 -0
  111. package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
  112. package/apps/dashboard/src/tabs/services-tab.js +202 -0
  113. package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
  114. package/apps/dashboard/src/tabs/settings-tab.js +803 -0
  115. package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
  116. package/apps/dashboard/src/tabs/skills-tab.js +284 -0
  117. package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
  118. package/apps/dashboard/src/tabs/spending-tab.js +173 -0
  119. package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
  120. package/apps/dashboard/src/tabs/swarm-chat-tab.js +660 -0
  121. package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
  122. package/apps/dashboard/src/tabs/swarm-tab.js +538 -0
  123. package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
  124. package/apps/dashboard/src/tabs/usage-tab.js +390 -0
  125. package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
  126. package/apps/dashboard/src/tabs/waves-tab.js +238 -0
  127. package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
  128. package/apps/dashboard/src/tabs/workflows-tab.js +747 -0
  129. package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
  130. package/apps/vibe/.crew/agent-memory/pipeline.json +249 -0
  131. package/apps/vibe/.crew/cost.json +17 -0
  132. package/apps/vibe/.crew/json-parse-metrics.jsonl +22 -0
  133. package/apps/vibe/.crew/pipeline-metrics.jsonl +22 -0
  134. package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +5 -0
  135. package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +5 -0
  136. package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +1 -0
  137. package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +5 -0
  138. package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +5 -0
  139. package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +5 -0
  140. package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +5 -0
  141. package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +5 -0
  142. package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +5 -0
  143. package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +5 -0
  144. package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +5 -0
  145. package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +5 -0
  146. package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +5 -0
  147. package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +5 -0
  148. package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +5 -0
  149. package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +5 -0
  150. package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +5 -0
  151. package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +5 -0
  152. package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +2 -0
  153. package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +5 -0
  154. package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +5 -0
  155. package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +5 -0
  156. package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +5 -0
  157. package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +5 -0
  158. package/apps/vibe/.crew/sandbox.json +7 -0
  159. package/apps/vibe/.crew/session.json +285 -0
  160. package/apps/vibe/.crew/training-data.jsonl +0 -0
  161. package/apps/vibe/.github/workflows/studio-quality.yml +37 -0
  162. package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +12 -0
  163. package/apps/vibe/.studio-data/project-messages/general.jsonl +54 -0
  164. package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +10 -0
  165. package/apps/vibe/ARCHITECTURE.md +3393 -0
  166. package/apps/vibe/QUICK-REFERENCE.md +211 -0
  167. package/apps/vibe/README.md +76 -0
  168. package/apps/vibe/ROADMAP.md +41 -0
  169. package/apps/vibe/STUDIO-SETUP-COMPLETE.md +35 -0
  170. package/apps/vibe/VISUAL-GUIDE.md +378 -0
  171. package/apps/vibe/capture-demo.mjs +160 -0
  172. package/apps/vibe/capture-vibe-assets.mjs +71 -0
  173. package/apps/vibe/capture-vibe-video.mjs +260 -0
  174. package/apps/vibe/check-buttons.js +41 -0
  175. package/apps/vibe/diagnose.html +106 -0
  176. package/apps/vibe/fix-buttons.js +103 -0
  177. package/apps/vibe/index.html +3401 -0
  178. package/apps/vibe/package-lock.json +920 -0
  179. package/apps/vibe/package.json +31 -0
  180. package/apps/vibe/public/favicon.png +0 -0
  181. package/apps/vibe/scripts/studio-pty-host.py +117 -0
  182. package/apps/vibe/server.mjs +1835 -0
  183. package/apps/vibe/src/main.js +2846 -0
  184. package/apps/vibe/src/register-all-languages.js +98 -0
  185. package/apps/vibe/start-studio.sh +11 -0
  186. package/apps/vibe/test/accessibility-tests.js +77 -0
  187. package/apps/vibe/test/browser-performance-audit.mjs +205 -0
  188. package/apps/vibe/test/performance-tests.js +120 -0
  189. package/apps/vibe/test/security-tests.js +213 -0
  190. package/apps/vibe/tests/e2e.local.mjs +54 -0
  191. package/apps/vibe/tests/server.smoke.mjs +106 -0
  192. package/apps/vibe/update_website.mjs +74 -0
  193. package/apps/vibe/vite.config.js +19 -0
  194. package/apps/vibe/watch-server.mjs +108 -0
  195. package/contrib/openclaw-plugin/README.md +199 -0
  196. package/contrib/openclaw-plugin/index.ts +306 -0
  197. package/contrib/openclaw-plugin/openclaw.plugin.json +41 -0
  198. package/contrib/openclaw-plugin/package.json +27 -0
  199. package/contrib/openclaw-plugin/skills/crewswarm/SKILL.md +88 -0
  200. package/crew-lead.mjs +649 -0
  201. package/engines/claude-code.json +36 -0
  202. package/engines/codex.json +37 -0
  203. package/engines/crew-cli.json +42 -0
  204. package/engines/cursor.json +40 -0
  205. package/engines/docker-sandbox.json +38 -0
  206. package/engines/gemini-cli.json +75 -0
  207. package/engines/opencode.json +31 -0
  208. package/gateway-bridge.mjs +1575 -0
  209. package/install.sh +738 -0
  210. package/lib/agent-registry.mjs +232 -0
  211. package/lib/agents/daemon.mjs +121 -0
  212. package/lib/agents/dispatch.mjs +225 -0
  213. package/lib/agents/permissions.mjs +90 -0
  214. package/lib/agents/platform-formatting.mjs +102 -0
  215. package/lib/agents/registry.mjs +81 -0
  216. package/lib/agents/tool-instructions.mjs +257 -0
  217. package/lib/agents/validation.mjs +75 -0
  218. package/lib/approval/policy-manager.mjs +221 -0
  219. package/lib/autoharness/index.mjs +391 -0
  220. package/lib/bridges/cli-executor.mjs +332 -0
  221. package/lib/bridges/gateway-ws.mjs +345 -0
  222. package/lib/bridges/integration.mjs +229 -0
  223. package/lib/bridges/rag-helper.mjs +90 -0
  224. package/lib/browser/opencode-passthrough-filter.js +44 -0
  225. package/lib/browser/passthrough-stderr.js +109 -0
  226. package/lib/chat/autonomous-mentions.mjs +373 -0
  227. package/lib/chat/history.mjs +82 -0
  228. package/lib/chat/mention-routing-intent.mjs +136 -0
  229. package/lib/chat/participants.mjs +95 -0
  230. package/lib/chat/project-messages-rag.mjs +265 -0
  231. package/lib/chat/project-messages.mjs +479 -0
  232. package/lib/chat/shared-chat-prompt-overlay.mjs +52 -0
  233. package/lib/chat/thread-binding.mjs +34 -0
  234. package/lib/chat/unified-history.mjs +223 -0
  235. package/lib/chat/unified-wrapper.mjs +41 -0
  236. package/lib/cli-process-tracker.mjs +228 -0
  237. package/lib/collections/index.mjs +433 -0
  238. package/lib/contacts/identity-linker.mjs +248 -0
  239. package/lib/contacts/index.mjs +341 -0
  240. package/lib/crew-judge/PROMPT.md +93 -0
  241. package/lib/crew-judge/judge.mjs +260 -0
  242. package/lib/crew-lead/agent-manager.mjs +125 -0
  243. package/lib/crew-lead/background.mjs +270 -0
  244. package/lib/crew-lead/brain.mjs +110 -0
  245. package/lib/crew-lead/chat-handler.mjs +2603 -0
  246. package/lib/crew-lead/chat-handler.mjs.bak +1274 -0
  247. package/lib/crew-lead/classifier.mjs +83 -0
  248. package/lib/crew-lead/http-server.mjs +4824 -0
  249. package/lib/crew-lead/intent.mjs +102 -0
  250. package/lib/crew-lead/interval-manager.mjs +41 -0
  251. package/lib/crew-lead/llm-caller.mjs +544 -0
  252. package/lib/crew-lead/prompts.mjs +392 -0
  253. package/lib/crew-lead/retry-manager.mjs +118 -0
  254. package/lib/crew-lead/tools.mjs +318 -0
  255. package/lib/crew-lead/wave-dispatcher.mjs +798 -0
  256. package/lib/crew-lead/waves-config.json +73 -0
  257. package/lib/crew-lead/waves-loader.mjs +110 -0
  258. package/lib/crew-lead/ws-router.mjs +428 -0
  259. package/lib/dispatch/parsers.mjs +299 -0
  260. package/lib/domain-planning/detector.mjs +196 -0
  261. package/lib/domain-planning/prompts/crew-pm-cli.md +96 -0
  262. package/lib/domain-planning/prompts/crew-pm-core.md +122 -0
  263. package/lib/domain-planning/prompts/crew-pm-frontend.md +111 -0
  264. package/lib/engines/crew-cli-sandbox.mjs +422 -0
  265. package/lib/engines/crew-cli.mjs +155 -0
  266. package/lib/engines/cursor-launcher.mjs +110 -0
  267. package/lib/engines/engine-registry.mjs +253 -0
  268. package/lib/engines/llm-direct.mjs +184 -0
  269. package/lib/engines/opencode.mjs +256 -0
  270. package/lib/engines/ouroboros.mjs +114 -0
  271. package/lib/engines/rt-envelope.mjs +1643 -0
  272. package/lib/engines/rt-envelope.mjs.backup-current +870 -0
  273. package/lib/engines/runners.mjs +1367 -0
  274. package/lib/gemini-cli-passthrough-noise.mjs +37 -0
  275. package/lib/integrations/code-search.mjs +259 -0
  276. package/lib/integrations/greptile.mjs +148 -0
  277. package/lib/integrations/multimodal.mjs +313 -0
  278. package/lib/integrations/telegram-streaming.mjs +153 -0
  279. package/lib/integrations/tts.mjs +312 -0
  280. package/lib/integrations/twitter-links.mjs +294 -0
  281. package/lib/memory/shared-adapter.mjs +296 -0
  282. package/lib/pipeline/manager.mjs +539 -0
  283. package/lib/preferences/extractor.mjs +347 -0
  284. package/lib/project-dir.mjs +20 -0
  285. package/lib/runtime/config.mjs +388 -0
  286. package/lib/runtime/dlq.mjs +170 -0
  287. package/lib/runtime/log-rotation.mjs +82 -0
  288. package/lib/runtime/logger.mjs +58 -0
  289. package/lib/runtime/memory.mjs +421 -0
  290. package/lib/runtime/paths.mjs +76 -0
  291. package/lib/runtime/project-dir.mjs +127 -0
  292. package/lib/runtime/spending.mjs +204 -0
  293. package/lib/runtime/startup-guard.mjs +291 -0
  294. package/lib/runtime/task-lease.mjs +234 -0
  295. package/lib/runtime/telemetry-schema.mjs +208 -0
  296. package/lib/runtime/telemetry.mjs +101 -0
  297. package/lib/runtime/utils.mjs +64 -0
  298. package/lib/skills/index.mjs +265 -0
  299. package/lib/tools/browser.mjs +135 -0
  300. package/lib/tools/executor.mjs +913 -0
  301. package/lib/types.d.ts +57 -0
  302. package/package.json +106 -0
  303. package/pm-loop.mjs +1626 -0
  304. package/prompts/coder-back.md +27 -0
  305. package/prompts/coder-front.md +27 -0
  306. package/prompts/coder.md +28 -0
  307. package/prompts/copywriter.md +17 -0
  308. package/prompts/fixer.md +39 -0
  309. package/prompts/frontend.md +23 -0
  310. package/prompts/github.md +24 -0
  311. package/prompts/main.md +39 -0
  312. package/prompts/pm-cli.md +95 -0
  313. package/prompts/pm-core.md +121 -0
  314. package/prompts/pm-frontend.md +110 -0
  315. package/prompts/pm.md +234 -0
  316. package/prompts/qa.md +44 -0
  317. package/prompts/security.md +19 -0
  318. package/scripts/build-crew-chat.sh +28 -0
  319. package/scripts/build-llms-full.mjs +52 -0
  320. package/scripts/chatmock-login.sh +16 -0
  321. package/scripts/chatmock-serve.sh +16 -0
  322. package/scripts/check-dashboard.mjs +88 -0
  323. package/scripts/crew-scribe.mjs +326 -0
  324. package/scripts/dashboard-helpers.mjs +391 -0
  325. package/scripts/dashboard-validation.mjs +198 -0
  326. package/scripts/dashboard.mjs +9717 -0
  327. package/scripts/dlq-replay.mjs +61 -0
  328. package/scripts/doctor.mjs +196 -0
  329. package/scripts/file-lock.mjs +186 -0
  330. package/scripts/fresh-machine-smoke.sh +323 -0
  331. package/scripts/generate-changelog.mjs +227 -0
  332. package/scripts/generate-openapi.mjs +334 -0
  333. package/scripts/health-check.mjs +229 -0
  334. package/scripts/install-docker.sh +213 -0
  335. package/scripts/mcp-server.mjs +1625 -0
  336. package/scripts/opencrew-rt-daemon.mjs +568 -0
  337. package/scripts/openswitchctl +646 -0
  338. package/scripts/refactor-configs.mjs +39 -0
  339. package/scripts/release-check.sh +46 -0
  340. package/scripts/resolve-node-bin.sh +25 -0
  341. package/scripts/restart-all-from-repo.sh +329 -0
  342. package/scripts/restart-crew-lead.sh +98 -0
  343. package/scripts/restart-dashboard.sh +104 -0
  344. package/scripts/restart-service.sh +274 -0
  345. package/scripts/run-accessibility-audit.mjs +356 -0
  346. package/scripts/run-integration-bounded.mjs +188 -0
  347. package/scripts/run-scheduled-pipeline.mjs +230 -0
  348. package/scripts/run.mjs +41 -0
  349. package/scripts/scan-skills.mjs +79 -0
  350. package/scripts/setup-firewall.sh +128 -0
  351. package/scripts/smoke-dispatch.mjs +149 -0
  352. package/scripts/smoke.sh +163 -0
  353. package/scripts/start-crew.mjs +328 -0
  354. package/scripts/start.mjs +146 -0
  355. package/scripts/swiftbar-restart-service.sh +19 -0
  356. package/scripts/sync-agents.mjs +152 -0
  357. package/scripts/sync-prompts.mjs +79 -0
  358. package/scripts/validate-config.mjs +337 -0
  359. package/scripts/wow.mjs +89 -0
  360. package/telegram-bridge.mjs +2421 -0
  361. package/unified-orchestrator.mjs +519 -0
  362. package/whatsapp-bridge.mjs +1481 -0
@@ -0,0 +1,803 @@
1
+ /**
2
+ * Settings sub-tab loaders — extracted from app.js
3
+ * Deps: getJSON, postJSON (core/api), escHtml, showNotification (core/dom)
4
+ * Inject model deps via initSettingsTab({ getModels, populateModelDropdown })
5
+ */
6
+
7
+ import { getJSON, postJSON } from '../core/api.js';
8
+ import { escHtml, showNotification } from '../core/dom.js';
9
+ import { state } from '../core/state.js';
10
+ import { setStoredChatProjectId, updateChatProjectHint } from './projects-tab.js';
11
+
12
+ let _getModels = null;
13
+ let _populateModelDropdown = null;
14
+
15
+ export function initSettingsTab({ getModels, populateModelDropdown } = {}) {
16
+ _getModels = getModels || _getModels;
17
+ _populateModelDropdown = populateModelDropdown || _populateModelDropdown;
18
+ }
19
+
20
+ // ── OpenClaw / RT token ────────────────────────────────────────────────────────
21
+
22
+ export async function loadOpenClawStatus() {
23
+ const badge = document.getElementById('oclawBadge');
24
+ try {
25
+ const d = await getJSON('/api/settings/openclaw-status');
26
+ if (d.installed) {
27
+ badge.textContent = '● installed';
28
+ badge.style.background = 'rgba(52,211,153,0.15)';
29
+ badge.style.color = 'var(--green)';
30
+ badge.style.borderColor = 'rgba(52,211,153,0.3)';
31
+ } else {
32
+ badge.textContent = '○ not detected';
33
+ badge.style.background = 'rgba(107,114,128,0.12)';
34
+ badge.style.color = 'var(--text-2)';
35
+ badge.style.borderColor = 'var(--border)';
36
+ }
37
+ } catch { if (badge) badge.textContent = '? unknown'; }
38
+ }
39
+
40
+ export async function loadRTToken() {
41
+ try {
42
+ const d = await getJSON('/api/settings/rt-token');
43
+ const badge = document.getElementById('rtTokenBadge');
44
+ const inp = document.getElementById('rtTokenInput');
45
+ if (d.token) {
46
+ badge.textContent = 'set ✓';
47
+ badge.style.background = 'rgba(52,211,153,0.15)';
48
+ badge.style.color = 'var(--green)';
49
+ badge.style.borderColor = 'rgba(52,211,153,0.3)';
50
+ inp.placeholder = '••••••••••••••••••••••• (saved)';
51
+ } else {
52
+ badge.textContent = 'not set';
53
+ badge.style.background = 'rgba(251,191,36,0.15)';
54
+ badge.style.color = 'var(--yellow)';
55
+ badge.style.borderColor = 'rgba(251,191,36,0.3)';
56
+ }
57
+ } catch {}
58
+ }
59
+
60
+ export async function saveRTToken() {
61
+ const token = document.getElementById('rtTokenInput').value.trim();
62
+ if (!token) { showNotification('Paste a token first', 'error'); return; }
63
+ try {
64
+ await postJSON('/api/settings/rt-token', { token });
65
+ showNotification('RT Bus token saved');
66
+ document.getElementById('rtTokenInput').value = '';
67
+ loadRTToken();
68
+ } catch(e) { showNotification('Save failed: ' + e.message, 'error'); }
69
+ }
70
+
71
+ // ── Config Lock/Unlock ─────────────────────────────────────────────────────
72
+
73
+ export async function loadConfigLockStatus() {
74
+ const badge = document.getElementById('configLockBadge');
75
+ const status = document.getElementById('configLockStatus');
76
+ const lockBtn = document.querySelector('[data-action="lockConfig"]');
77
+ const unlockBtn = document.querySelector('[data-action="unlockConfig"]');
78
+
79
+ try {
80
+ const d = await getJSON('/api/config/lock-status');
81
+ if (d.locked) {
82
+ badge.textContent = '🔒 Locked';
83
+ badge.style.background = 'rgba(52,211,153,0.15)';
84
+ badge.style.color = 'var(--green)';
85
+ badge.style.borderColor = 'rgba(52,211,153,0.3)';
86
+ if (status) status.textContent = '✓ Config is protected from overwrites';
87
+
88
+ // Update button states - lock button is active (current state)
89
+ if (lockBtn) {
90
+ lockBtn.className = 'btn-primary';
91
+ lockBtn.style.opacity = '0.6';
92
+ lockBtn.style.pointerEvents = 'none';
93
+ }
94
+ if (unlockBtn) {
95
+ unlockBtn.className = 'btn-ghost';
96
+ unlockBtn.style.opacity = '1';
97
+ unlockBtn.style.pointerEvents = 'auto';
98
+ }
99
+ } else {
100
+ badge.textContent = '🔓 Unlocked';
101
+ badge.style.background = 'rgba(251,191,36,0.15)';
102
+ badge.style.color = 'var(--yellow)';
103
+ badge.style.borderColor = 'rgba(251,191,36,0.3)';
104
+ if (status) status.textContent = '⚠️ Config can be modified — lock it after making changes';
105
+
106
+ // Update button states - unlock button is active (current state)
107
+ if (lockBtn) {
108
+ lockBtn.className = 'btn-primary';
109
+ lockBtn.style.opacity = '1';
110
+ lockBtn.style.pointerEvents = 'auto';
111
+ }
112
+ if (unlockBtn) {
113
+ unlockBtn.className = 'btn-ghost';
114
+ unlockBtn.style.opacity = '0.6';
115
+ unlockBtn.style.pointerEvents = 'none';
116
+ }
117
+ }
118
+ } catch { if (badge) badge.textContent = '? unknown'; }
119
+ }
120
+
121
+ export async function lockConfig() {
122
+ try {
123
+ await postJSON('/api/config/lock', {});
124
+ showNotification('✓ Config locked — protected from overwrites');
125
+ loadConfigLockStatus();
126
+ } catch(e) { showNotification('Lock failed: ' + e.message, 'error'); }
127
+ }
128
+
129
+ export async function unlockConfig() {
130
+ try {
131
+ await postJSON('/api/config/unlock', {});
132
+ showNotification('✓ Config unlocked — you can now make changes');
133
+ loadConfigLockStatus();
134
+ } catch(e) { showNotification('Unlock failed: ' + e.message, 'error'); }
135
+ }
136
+
137
+ // ── OpenCode / engine settings ────────────────────────────────────────────────
138
+
139
+ export async function loadOpencodeProject() {
140
+ try {
141
+ const d = await getJSON('/api/settings/opencode-project');
142
+ const inp = document.getElementById('opencodeProjInput');
143
+ const st = document.getElementById('opencodeProjStatus');
144
+ if (inp) { inp.placeholder = d.dir || 'e.g. /Users/you/Desktop/myproject'; inp.value = d.dir || ''; }
145
+ if (st) st.textContent = d.dir ? ('✅ Current: ' + d.dir) : '⚠️ Not set — OpenCode will write files to the crewswarm repo root. Set this to your project folder.';
146
+ if (document.getElementById('opencodeFallbackSelect') && _getModels) {
147
+ await _getModels();
148
+ if (_populateModelDropdown) _populateModelDropdown('opencodeFallbackSelect', d.fallbackModel || '');
149
+ }
150
+ const fbSt = document.getElementById('opencodeFallbackStatus');
151
+ if (fbSt) fbSt.textContent = d.fallbackModel ? ('✅ Fallback: ' + d.fallbackModel) : '⚠️ Using default groq/kimi-k2-instruct-0905';
152
+
153
+ // Load primary OpenCode model
154
+ if (document.getElementById('opencodeModelSelect') && _getModels) {
155
+ await _getModels();
156
+ if (_populateModelDropdown) _populateModelDropdown('opencodeModelSelect', d.opencodeModel || '');
157
+ }
158
+ const ocSt = document.getElementById('opencodeModelStatus');
159
+ if (ocSt) ocSt.textContent = d.opencodeModel ? ('✅ Primary: ' + d.opencodeModel) : '⚠️ Using default groq/moonshotai/kimi-k2-instruct-0905';
160
+
161
+ // Load crew-lead model
162
+ const clSel = document.getElementById('crewLeadModelSelect');
163
+ if (clSel && d.crewLeadModel) clSel.value = d.crewLeadModel;
164
+ } catch {}
165
+ }
166
+
167
+ export async function saveOpencodeSettings() {
168
+ const dir = (document.getElementById('opencodeProjInput')?.value || '').trim();
169
+ const fallbackModel = (document.getElementById('opencodeFallbackSelect')?.value || '').trim();
170
+ try {
171
+ await postJSON('/api/settings/opencode-project', { dir: dir || undefined, fallbackModel: fallbackModel || undefined });
172
+ showNotification('OpenCode settings saved — fallback takes effect on next task (no restart needed)');
173
+ loadOpencodeProject();
174
+
175
+ // Sync to chat project dropdown if this directory matches a registered project
176
+ if (dir && state.projectsData) {
177
+ const matchingProj = Object.values(state.projectsData).find(p => p.outputDir === dir);
178
+ if (matchingProj) {
179
+ state.chatActiveProjectId = matchingProj.id;
180
+ setStoredChatProjectId(matchingProj.id);
181
+ const sel = document.getElementById('chatProjectSelect');
182
+ if (sel) sel.value = matchingProj.id;
183
+ updateChatProjectHint();
184
+ }
185
+ }
186
+ } catch(e) { showNotification('Save failed: ' + e.message, 'error'); }
187
+ }
188
+
189
+ export async function saveOpencodeModel() {
190
+ const sel = document.getElementById('opencodeModelSelect');
191
+ const opencodeModel = (sel?.value || '').trim();
192
+ const st = document.getElementById('opencodeModelStatus');
193
+ try {
194
+ await postJSON('/api/settings/opencode-project', { opencodeModel: opencodeModel || undefined });
195
+ if (st) { st.textContent = '✓ Saved'; st.style.color = 'var(--green-hi)'; }
196
+ showNotification(opencodeModel ? `Primary OpenCode model → ${opencodeModel}` : 'OpenCode model reset to default');
197
+ setTimeout(() => { if (st) st.textContent = opencodeModel ? ('✅ Primary: ' + opencodeModel) : '⚠️ Using default groq/moonshotai/kimi-k2-instruct-0905'; }, 3000);
198
+ } catch(e) {
199
+ if (st) { st.textContent = 'Error: ' + e.message; st.style.color = 'var(--red)'; }
200
+ showNotification('Save failed: ' + e.message, 'error');
201
+ }
202
+ }
203
+
204
+ export async function saveCrewLeadModel() {
205
+ const sel = document.getElementById('crewLeadModelSelect');
206
+ const crewLeadModel = (sel?.value || '').trim();
207
+ const st = document.getElementById('crewLeadModelStatus');
208
+ try {
209
+ await postJSON('/api/settings/opencode-project', { crewLeadModel: crewLeadModel || undefined });
210
+ if (st) { st.textContent = '✓ Saved'; st.style.color = 'var(--green-hi)'; }
211
+ showNotification(crewLeadModel ? `Crew lead model → ${crewLeadModel}` : 'Crew lead model reset to default');
212
+ setTimeout(() => { if (st) st.textContent = ''; }, 3000);
213
+ } catch(e) {
214
+ if (st) { st.textContent = 'Error: ' + e.message; st.style.color = 'var(--red)'; }
215
+ showNotification('Save failed: ' + e.message, 'error');
216
+ }
217
+ }
218
+
219
+ export async function loadBgConsciousness() {
220
+ const btn = document.getElementById('bgConsciousnessBtn');
221
+ const status = document.getElementById('bgConsciousnessStatus');
222
+ const modelInput = document.getElementById('bgConsciousnessModel');
223
+ try {
224
+ const d = await getJSON('/api/settings/bg-consciousness');
225
+ const on = d.enabled;
226
+ if (btn) {
227
+ btn.textContent = on ? '🟢 ON' : '⚫ OFF';
228
+ btn.style.background = on ? 'rgba(34,197,94,0.15)' : 'var(--surface-2)';
229
+ btn.style.borderColor = on ? 'var(--green-hi)' : 'var(--border)';
230
+ btn.style.color = on ? 'var(--green-hi)' : 'var(--text-2)';
231
+ }
232
+ if (modelInput && d.model) modelInput.placeholder = d.model;
233
+ if (status) status.textContent = on
234
+ ? 'Active — crew-lead reflects every ' + Math.round(d.intervalMs / 60000) + 'min when idle. Model: ' + d.model
235
+ : 'Off — crew-lead will not self-reflect between tasks.';
236
+ } catch(e) {
237
+ if (btn) btn.textContent = 'Error';
238
+ if (status) status.textContent = 'Could not load: ' + e.message;
239
+ }
240
+ }
241
+
242
+ export async function toggleBgConsciousness() {
243
+ try {
244
+ const current = await getJSON('/api/settings/bg-consciousness');
245
+ const d = await postJSON('/api/settings/bg-consciousness', { enabled: !current.enabled });
246
+ showNotification('Background consciousness ' + (d.enabled ? 'ENABLED' : 'DISABLED'));
247
+ loadBgConsciousness();
248
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
249
+ }
250
+
251
+ export async function saveBgConsciousnessModel() {
252
+ const modelInput = document.getElementById('bgConsciousnessModel');
253
+ const model = (modelInput?.value || '').trim();
254
+ if (!model) { showNotification('Enter a model first (e.g. groq/llama-3.3-70b-versatile)', 'error'); return; }
255
+ try {
256
+ await postJSON('/api/settings/bg-consciousness', { model });
257
+ showNotification('Background consciousness model → ' + model);
258
+ modelInput.value = '';
259
+ loadBgConsciousness();
260
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
261
+ }
262
+
263
+ export async function loadCursorWaves() {
264
+ const btn = document.getElementById('cursorWavesBtn');
265
+ const status = document.getElementById('cursorWavesStatus');
266
+ try {
267
+ const d = await getJSON('/api/settings/cursor-waves');
268
+ const on = d.enabled;
269
+ if (btn) {
270
+ btn.textContent = on ? '⚡ ON' : '⚫ OFF';
271
+ btn.style.background = on ? 'rgba(168,85,247,0.15)' : 'var(--surface-2)';
272
+ btn.style.borderColor = on ? '#a855f7' : 'var(--border)';
273
+ btn.style.color = on ? '#c084fc' : 'var(--text-2)';
274
+ }
275
+ if (status) status.textContent = on
276
+ ? 'Active — multi-agent waves fan out to Cursor subagents in parallel. crew-orchestrator coordinates each wave.'
277
+ : 'Off — each agent in a wave dispatches independently through the standard gateway.';
278
+ } catch(e) {
279
+ if (btn) btn.textContent = 'Error';
280
+ if (status) status.textContent = 'Could not load: ' + e.message;
281
+ }
282
+ }
283
+
284
+ export async function toggleCursorWaves() {
285
+ try {
286
+ const current = await getJSON('/api/settings/cursor-waves');
287
+ const d = await postJSON('/api/settings/cursor-waves', { enabled: !current.enabled });
288
+ showNotification('Cursor Parallel Waves ' + (d.enabled ? 'ENABLED ⚡' : 'DISABLED'));
289
+ loadCursorWaves();
290
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
291
+ }
292
+
293
+ export async function loadAutonomousMentions() {
294
+ const btn = document.getElementById('autonomousMentionsBtn');
295
+ const status = document.getElementById('autonomousMentionsStatus');
296
+ try {
297
+ const d = await getJSON('/api/settings/autonomous-mentions');
298
+ const on = d.enabled !== false;
299
+ if (btn) {
300
+ btn.textContent = on ? '🕸 ON' : '⚫ OFF';
301
+ btn.style.background = on ? 'rgba(52,211,153,0.15)' : 'var(--surface-2)';
302
+ btn.style.borderColor = on ? 'rgba(52,211,153,0.3)' : 'var(--border)';
303
+ btn.style.color = on ? 'var(--green)' : 'var(--text-2)';
304
+ }
305
+ if (status) {
306
+ status.textContent = on
307
+ ? 'Active — shared chat @mentions can auto-route to agents and CLI participants.'
308
+ : 'Off — @mentions are recorded in chat history, but no autonomous routing will fire.';
309
+ status.style.color = 'var(--text-3)';
310
+ }
311
+ } catch (e) {
312
+ if (btn) btn.textContent = 'Error';
313
+ if (status) status.textContent = 'Could not load: ' + e.message;
314
+ }
315
+ }
316
+
317
+ export async function toggleAutonomousMentions() {
318
+ try {
319
+ const current = await getJSON('/api/settings/autonomous-mentions');
320
+ const d = await postJSON('/api/settings/autonomous-mentions', {
321
+ enabled: !current.enabled,
322
+ });
323
+ showNotification(
324
+ 'Autonomous mention routing ' + (d.enabled ? 'ENABLED 🕸' : 'DISABLED'),
325
+ );
326
+ loadAutonomousMentions();
327
+ } catch (e) {
328
+ showNotification('Failed: ' + e.message, 'error');
329
+ }
330
+ }
331
+
332
+ export async function loadClaudeCode() {
333
+ const btn = document.getElementById('claudeCodeBtn');
334
+ const status = document.getElementById('claudeCodeStatus');
335
+ try {
336
+ const d = await getJSON('/api/settings/claude-code');
337
+ const on = d.enabled;
338
+ if (btn) {
339
+ btn.textContent = on ? '🤖 ON' : '⚫ OFF';
340
+ btn.style.background = on ? 'rgba(245,158,11,0.15)' : 'var(--surface-2)';
341
+ btn.style.borderColor = on ? 'var(--amber)' : 'var(--border)';
342
+ btn.style.color = on ? 'var(--yellow)' : 'var(--text-2)';
343
+ }
344
+ if (status) {
345
+ if (!d.hasKey) {
346
+ status.textContent = '⚠️ ANTHROPIC_API_KEY not set — add it to ~/.crewswarm/crewswarm.json under providers.anthropic.apiKey or set the env var.';
347
+ status.style.color = 'var(--amber)';
348
+ } else {
349
+ status.textContent = on
350
+ ? 'Active — tasks route through Claude Code CLI. Per-agent override: set useClaudeCode: true in crewswarm.json.'
351
+ : 'Off — tasks use direct LLM or OpenCode. Enable to run agents through Claude Code CLI.';
352
+ status.style.color = 'var(--text-3)';
353
+ }
354
+ }
355
+ } catch(e) {
356
+ if (btn) btn.textContent = 'Error';
357
+ if (status) status.textContent = 'Could not load: ' + e.message;
358
+ }
359
+ }
360
+
361
+ export async function toggleClaudeCode() {
362
+ try {
363
+ const current = await getJSON('/api/settings/claude-code');
364
+ if (!current.hasKey) {
365
+ showNotification('Set ANTHROPIC_API_KEY first — add it in ~/.crewswarm/crewswarm.json under providers.anthropic.apiKey', 'error');
366
+ return;
367
+ }
368
+ const d = await postJSON('/api/settings/claude-code', { enabled: !current.enabled });
369
+ showNotification('Claude Code executor ' + (d.enabled ? 'ENABLED 🤖' : 'DISABLED'));
370
+ loadClaudeCode();
371
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
372
+ }
373
+
374
+ export async function loadCodexExecutor() {
375
+ const btn = document.getElementById('codexBtn');
376
+ const status = document.getElementById('codexStatus');
377
+ try {
378
+ const d = await getJSON('/api/settings/codex');
379
+ const on = d.enabled;
380
+ if (btn) {
381
+ btn.textContent = on ? '🟣 ON' : '⚫ OFF';
382
+ btn.style.background = on ? 'rgba(168,85,247,0.15)' : 'var(--surface-2)';
383
+ btn.style.borderColor = on ? '#a855f7' : 'var(--border)';
384
+ btn.style.color = on ? '#a855f7' : 'var(--text-2)';
385
+ }
386
+ if (status) {
387
+ status.textContent = on
388
+ ? 'Active — tasks route through Codex CLI. Per-agent override: set useCodex: true in crewswarm.json.'
389
+ : 'Off — tasks use direct LLM or other engine. Enable to route all coding agents through Codex CLI.';
390
+ status.style.color = 'var(--text-3)';
391
+ }
392
+ } catch(e) {
393
+ if (btn) btn.textContent = 'Error';
394
+ if (status) { status.textContent = 'Could not load: ' + e.message; status.style.color = 'var(--text-3)'; }
395
+ }
396
+ }
397
+
398
+ export async function toggleCodexExecutor() {
399
+ try {
400
+ const current = await getJSON('/api/settings/codex');
401
+ const d = await postJSON('/api/settings/codex', { enabled: !current.enabled });
402
+ showNotification('Codex CLI executor ' + (d.enabled ? 'ENABLED 🟣' : 'DISABLED'));
403
+ loadCodexExecutor();
404
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
405
+ }
406
+
407
+ export async function loadGeminiCliExecutor() {
408
+ const btn = document.getElementById('geminiCliBtn');
409
+ const status = document.getElementById('geminiCliStatus');
410
+ try {
411
+ const d = await getJSON('/api/settings/gemini-cli');
412
+ const on = d.enabled;
413
+ if (btn) {
414
+ btn.textContent = on ? '🔵 ON' : '⚫ OFF';
415
+ btn.style.background = on ? 'rgba(66,133,244,0.15)' : 'var(--surface-2)';
416
+ btn.style.borderColor = on ? '#4285f4' : 'var(--border)';
417
+ btn.style.color = on ? '#4285f4' : 'var(--text-2)';
418
+ }
419
+ if (status) {
420
+ if (!d.installed) {
421
+ status.textContent = '⚠️ gemini binary not found — run: npm install -g @google/gemini-cli';
422
+ status.style.color = 'var(--amber)';
423
+ } else {
424
+ status.textContent = on
425
+ ? 'Active — tasks route through Gemini CLI. Run gemini auth login if you haven\'t authenticated yet.'
426
+ : 'Off — tasks use direct LLM or other engine. Enable to route coding agents through Gemini CLI (free Google OAuth tier).';
427
+ status.style.color = 'var(--text-3)';
428
+ }
429
+ }
430
+ } catch(e) {
431
+ if (btn) btn.textContent = 'Error';
432
+ if (status) { status.textContent = 'Could not load: ' + e.message; status.style.color = 'var(--text-3)'; }
433
+ }
434
+ }
435
+
436
+ export async function toggleGeminiCliExecutor() {
437
+ try {
438
+ const current = await getJSON('/api/settings/gemini-cli');
439
+ if (!current.installed) {
440
+ showNotification('Install Gemini CLI first: npm install -g @google/gemini-cli', 'error');
441
+ return;
442
+ }
443
+ const d = await postJSON('/api/settings/gemini-cli', { enabled: !current.enabled });
444
+ showNotification('Gemini CLI executor ' + (d.enabled ? 'ENABLED 🔵' : 'DISABLED'));
445
+ loadGeminiCliExecutor();
446
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
447
+ }
448
+
449
+ export async function loadCrewCliExecutor() {
450
+ const btn = document.getElementById('crewCliBtn');
451
+ const status = document.getElementById('crewCliStatus');
452
+ try {
453
+ const d = await getJSON('/api/settings/crew-cli');
454
+ const on = d.enabled;
455
+ if (btn) {
456
+ btn.textContent = on ? '🔧 ON' : '⚫ OFF';
457
+ btn.style.background = on ? 'rgba(16,185,129,0.15)' : 'var(--surface-2)';
458
+ btn.style.borderColor = on ? '#10b981' : 'var(--border)';
459
+ btn.style.color = on ? '#10b981' : 'var(--text-2)';
460
+ }
461
+ if (status) {
462
+ status.textContent = on
463
+ ? 'Active — multi-agent swarm tasks route through crew-cli with intelligent dispatch to specialists.'
464
+ : 'Off — tasks use direct LLM or other engine. Enable to route all coding agents through crew-cli natively.';
465
+ }
466
+ } catch(e) {
467
+ if (btn) btn.textContent = 'Error';
468
+ if (status) status.textContent = 'Could not load status';
469
+ }
470
+ }
471
+
472
+ export async function toggleCrewCliExecutor() {
473
+ try {
474
+ const current = await getJSON('/api/settings/crew-cli');
475
+ const d = await postJSON('/api/settings/crew-cli', { enabled: !current.enabled });
476
+ showNotification('Crew CLI executor ' + (d.enabled ? 'ENABLED 🔧' : 'DISABLED'));
477
+ loadCrewCliExecutor();
478
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
479
+ }
480
+
481
+ export async function loadOpencodeExecutor() {
482
+ const btn = document.getElementById('opencodeBtn');
483
+ const status = document.getElementById('opencodeStatus');
484
+ try {
485
+ const d = await getJSON('/api/settings/opencode');
486
+ const on = d.enabled;
487
+ if (btn) {
488
+ btn.textContent = on ? '⚡ ON' : '⚫ OFF';
489
+ btn.style.background = on ? 'rgba(52,211,153,0.15)' : 'var(--surface-2)';
490
+ btn.style.borderColor = on ? 'rgba(52,211,153,0.3)' : 'var(--border)';
491
+ btn.style.color = on ? 'var(--green)' : 'var(--text-2)';
492
+ }
493
+ if (status) {
494
+ if (!d.installed) {
495
+ status.textContent = '⚠️ opencode binary not found — install: npm install -g opencode';
496
+ status.style.color = 'var(--amber)';
497
+ } else {
498
+ status.textContent = on
499
+ ? '⚡ Active — coding agents route through OpenCode for full IDE context and session persistence.'
500
+ : '⚫ Off — tasks use direct LLM or other configured engine. Enable to run agents through OpenCode CLI.';
501
+ status.style.color = 'var(--text-3)';
502
+ }
503
+ }
504
+ } catch(e) {
505
+ if (btn) btn.textContent = 'Error';
506
+ if (status) { status.textContent = 'Could not load status'; status.style.color = 'var(--text-3)'; }
507
+ }
508
+ }
509
+
510
+ export async function toggleOpencodeExecutor() {
511
+ try {
512
+ const current = await getJSON('/api/settings/opencode');
513
+ if (!current.installed) {
514
+ showNotification('Install OpenCode CLI first: npm install -g opencode', 'error');
515
+ return;
516
+ }
517
+ const d = await postJSON('/api/settings/opencode', { enabled: !current.enabled });
518
+ showNotification('OpenCode executor ' + (d.enabled ? 'ENABLED ⚡' : 'DISABLED'));
519
+ loadOpencodeExecutor();
520
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
521
+ }
522
+
523
+ export async function loadGlobalFallback() {
524
+ try {
525
+ const d = await getJSON('/api/settings/global-fallback');
526
+ const el = document.getElementById('globalFallbackInput');
527
+ if (el) el.value = d.globalFallbackModel || '';
528
+ const status = document.getElementById('globalFallbackStatus');
529
+ if (status) status.textContent = d.globalFallbackModel
530
+ ? 'Active: any agent without a per-agent fallback will use ' + d.globalFallbackModel
531
+ : 'Not set — agents without fallback will use the built-in default (groq/llama-3.3-70b-versatile).';
532
+ } catch(e) { console.warn('loadGlobalFallback:', e.message); }
533
+ }
534
+
535
+ export async function saveGlobalFallback() {
536
+ const model = (document.getElementById('globalFallbackInput')?.value || '').trim();
537
+ try {
538
+ await postJSON('/api/settings/global-fallback', { globalFallbackModel: model });
539
+ showNotification(model ? 'Global fallback → ' + model : 'Global fallback cleared');
540
+ loadGlobalFallback();
541
+ } catch(e) { showNotification('Failed: ' + e.message, 'error'); }
542
+ }
543
+
544
+ export async function loadGlobalOcLoop() {
545
+ try {
546
+ const d = await getJSON('/api/settings/global-oc-loop');
547
+ const chk = document.getElementById('globalOcLoop');
548
+ const inp = document.getElementById('globalOcLoopRounds');
549
+ if (chk) chk.checked = d.enabled || false;
550
+ if (inp) inp.value = d.maxRounds ?? 10;
551
+ } catch(e) {}
552
+ }
553
+
554
+ export async function saveGlobalOcLoop() {
555
+ const enabled = document.getElementById('globalOcLoop')?.checked;
556
+ try {
557
+ await postJSON('/api/settings/global-oc-loop', { enabled });
558
+ showNotification('Global OC loop ' + (enabled ? 'enabled' : 'disabled'));
559
+ } catch(e) { showNotification('Failed: ' + e.message, true); }
560
+ }
561
+
562
+ export async function saveGlobalOcLoopRounds() {
563
+ const rounds = parseInt(document.getElementById('globalOcLoopRounds')?.value) || 10;
564
+ try {
565
+ await postJSON('/api/settings/global-oc-loop', { maxRounds: rounds });
566
+ showNotification('Max rounds set to ' + rounds);
567
+ } catch(e) { showNotification('Failed: ' + e.message, true); }
568
+ }
569
+
570
+ export async function loadPassthroughNotify() {
571
+ try {
572
+ const d = await getJSON('/api/settings/passthrough-notify');
573
+ const sel = document.getElementById('passthroughNotifySelect');
574
+ if (sel) sel.value = d.value || 'both';
575
+ } catch(e) {}
576
+ }
577
+
578
+ export async function savePassthroughNotify() {
579
+ const value = document.getElementById('passthroughNotifySelect')?.value || 'both';
580
+ const st = document.getElementById('passthroughNotifyStatus');
581
+ try {
582
+ await postJSON('/api/settings/passthrough-notify', { value });
583
+ if (st) { st.textContent = '✓ Saved — takes effect on the next passthrough'; st.style.color = 'var(--green-hi)'; }
584
+ showNotification('Passthrough notifications → ' + value);
585
+ } catch(e) {
586
+ if (st) { st.textContent = 'Error: ' + e.message; st.style.color = 'var(--red)'; }
587
+ }
588
+ }
589
+
590
+ export async function loadLoopBrain() {
591
+ try {
592
+ const d = await getJSON('/api/settings/loop-brain');
593
+ const inp = document.getElementById('loopBrainModel');
594
+ if (inp && d.loopBrain) inp.value = d.loopBrain;
595
+ } catch {}
596
+ }
597
+
598
+ export async function saveLoopBrain() {
599
+ const model = (document.getElementById('loopBrainModel')?.value || '').trim();
600
+ try {
601
+ await postJSON('/api/settings/loop-brain', { loopBrain: model || null });
602
+ showNotification(model ? `Loop brain → ${model}` : 'Loop brain cleared (each agent uses own model)');
603
+ } catch(e) { showNotification('Failed: ' + e.message, true); }
604
+ }
605
+
606
+ // ── Env vars (Security sub-tab) ───────────────────────────────────────────────
607
+
608
+ const ENV_GROUPS = [
609
+ {
610
+ label: 'Engine — OpenCode',
611
+ vars: [
612
+ { key: 'CREWSWARM_OPENCODE_ENABLED', hint: 'Route coding agents through OpenCode globally', default: 'off' },
613
+ { key: 'CREWSWARM_OPENCODE_MODEL', hint: 'Model passed to OpenCode — leave blank to use per-agent model', default: 'per-agent' },
614
+ { key: 'CREWSWARM_OPENCODE_TIMEOUT_MS', hint: 'ms before an OpenCode task is killed', default: '300000' },
615
+ { key: 'CREWSWARM_OPENCODE_AGENT', hint: 'Override agent name passed to OpenCode', default: 'auto' },
616
+ ],
617
+ },
618
+ {
619
+ label: 'Engine — Claude Code & Cursor',
620
+ note: 'Both use OAuth login (run claude or cursor once). No API key required.',
621
+ vars: [
622
+ { key: 'CREWSWARM_CLAUDE_CODE_MODEL', hint: 'Model passed to claude -p — leave blank for Claude Code default', default: 'claude default' },
623
+ { key: 'CREWSWARM_CURSOR_MODEL', hint: 'Cursor CLI --model when agent has no cursorCliModel (default: composer-2-fast)', default: 'composer-2-fast' },
624
+ ],
625
+ },
626
+ {
627
+ label: 'Engine — Codex & crew-cli',
628
+ note: 'These are the dashboard-wide defaults when an agent does not have a per-route model override.',
629
+ vars: [
630
+ { key: 'CREWSWARM_CODEX_MODEL', hint: 'Model passed to codex exec --model (leave blank for Codex default)', default: 'codex default' },
631
+ { key: 'CREWSWARM_CREW_CLI_MODEL', hint: 'Model passed to crew chat --model and gateway crew-cli engine', default: 'gemini-2.5-flash' },
632
+ ],
633
+ },
634
+ {
635
+ label: 'Engine — Gemini CLI',
636
+ note: 'Free tier via Google account — 60 req/min. Run gemini once to auth.',
637
+ vars: [
638
+ { key: 'CREWSWARM_GEMINI_CLI_ENABLED', hint: 'Route agents through Gemini CLI globally', default: 'off' },
639
+ { key: 'CREWSWARM_GEMINI_CLI_MODEL', hint: 'Model passed to gemini -p (e.g. gemini-2.0-flash) — blank for default', default: 'gemini default' },
640
+ ],
641
+ },
642
+ {
643
+ label: 'Engine — Docker Sandbox',
644
+ note: 'Runs any inner engine inside an isolated Docker microVM. API keys injected by network proxy — never exposed to the agent.',
645
+ vars: [
646
+ { key: 'CREWSWARM_DOCKER_SANDBOX', hint: 'Route all coding agents through Docker Sandbox globally', default: 'off' },
647
+ { key: 'CREWSWARM_DOCKER_SANDBOX_NAME', hint: 'Pre-created sandbox name', default: 'crewswarm' },
648
+ { key: 'CREWSWARM_DOCKER_SANDBOX_INNER_ENGINE', hint: 'Engine inside the sandbox: claude, opencode, or codex', default: 'claude' },
649
+ { key: 'CREWSWARM_DOCKER_SANDBOX_TIMEOUT_MS', hint: 'ms before a sandboxed task is killed', default: '300000' },
650
+ ],
651
+ },
652
+ {
653
+ label: 'Engine Loop & Dispatch',
654
+ vars: [
655
+ { key: 'CREWSWARM_ENGINE_LOOP', hint: 'Enable Ouroboros engine loop for all agents', default: 'off' },
656
+ { key: 'CREWSWARM_ENGINE_LOOP_MAX_ROUNDS', hint: 'Max STEP iterations per loop run', default: '10' },
657
+ { key: 'CREWSWARM_ENGINE_IDLE_TIMEOUT_MS', hint: 'Kill engine (Cursor/Claude) if no output for this many ms', default: '300000' },
658
+ { key: 'CREWSWARM_ENGINE_MAX_TOTAL_MS', hint: 'Absolute max ms for any single engine task', default: '2700000' },
659
+ { key: 'CREWSWARM_DISPATCH_TIMEOUT_MS', hint: 'ms before an unclaimed dispatch times out', default: '300000' },
660
+ { key: 'CREWSWARM_DISPATCH_CLAIMED_TIMEOUT_MS', hint: 'ms before a claimed (in-progress) dispatch times out', default: '900000' },
661
+ { key: 'CREWSWARM_RT_AGENT', hint: 'Agent ID used for the RT bus', default: 'crew-coder' },
662
+ ],
663
+ },
664
+ {
665
+ label: 'Ports',
666
+ vars: [
667
+ { key: 'CREW_LEAD_PORT', hint: 'crew-lead HTTP server port', default: '5010' },
668
+ { key: 'SWARM_DASH_PORT', hint: 'Dashboard port', default: '4319' },
669
+ { key: 'WA_HTTP_PORT', hint: 'WhatsApp bridge HTTP port', default: '3000' },
670
+ ],
671
+ },
672
+ {
673
+ label: 'Background Consciousness',
674
+ vars: [
675
+ { key: 'CREWSWARM_BG_CONSCIOUSNESS', hint: 'Enable idle reflection loop', default: 'off' },
676
+ { key: 'CREWSWARM_BG_CONSCIOUSNESS_INTERVAL_MS', hint: 'Idle reflection interval in ms', default: '900000' },
677
+ { key: 'CREWSWARM_BG_CONSCIOUSNESS_MODEL', hint: 'Model for background cycle (e.g. groq/llama-3.1-8b-instant)', default: 'groq/llama-3.1-8b-instant' },
678
+ ],
679
+ },
680
+ {
681
+ label: 'Messaging',
682
+ vars: [
683
+ { key: 'TELEGRAM_ALLOWED_USERNAMES', hint: 'Comma-separated Telegram usernames allowed to message the bot', default: 'all allowed' },
684
+ { key: 'WA_ALLOWED_NUMBERS', hint: 'Comma-separated WhatsApp numbers in intl format (+1555…)', default: 'all allowed' },
685
+ ],
686
+ },
687
+ {
688
+ label: 'Memory',
689
+ vars: [
690
+ { key: 'SHARED_MEMORY_NAMESPACE', hint: 'Namespace prefix for shared memory keys', default: 'crewswarm' },
691
+ { key: 'SHARED_MEMORY_DIR', hint: 'Directory for shared memory files', default: '~/.crewswarm/memory' },
692
+ ],
693
+ },
694
+ {
695
+ label: 'PM Loop',
696
+ vars: [
697
+ { key: 'PM_MAX_ITEMS', hint: 'Max roadmap items per PM loop run', default: '10' },
698
+ { key: 'PM_MAX_CONCURRENT', hint: 'Max concurrent agent tasks in PM loop', default: '20' },
699
+ { key: 'PM_USE_QA', hint: 'Include crew-qa review after each PM task', default: 'off' },
700
+ { key: 'PM_USE_SECURITY', hint: 'Include crew-security review for auth/key tasks', default: 'off' },
701
+ { key: 'PM_USE_SPECIALISTS', hint: 'Route tasks to specialist agents (front/back/github) by keyword', default: 'on' },
702
+ { key: 'PM_SELF_EXTEND', hint: 'Auto-generate new roadmap items when queue is empty', default: 'on' },
703
+ { key: 'PM_EXTEND_EVERY', hint: 'Generate new items every N completions (0 = only when empty)', default: '5' },
704
+ { key: 'PM_CODER_AGENT', hint: 'Override default coding agent for PM loop (e.g. crew-coder-front)', default: 'crew-coder' },
705
+ { key: 'PM_AGENT_IDLE_TIMEOUT_MS', hint: 'Kill PM dispatch if no activity for this many ms', default: '900000' },
706
+ { key: 'PHASED_TASK_TIMEOUT_MS', hint: 'Overall timeout for a single agent task in the PM loop', default: '600000' },
707
+ ],
708
+ },
709
+ ];
710
+
711
+ async function saveEnvVar(key, inputEl, statusEl) {
712
+ const val = inputEl.value.trim();
713
+ statusEl.textContent = 'Saving…';
714
+ statusEl.style.color = 'var(--text-3)';
715
+ try {
716
+ const r = await fetch('/api/env-advanced', {
717
+ method: 'POST',
718
+ headers: { 'Content-Type': 'application/json' },
719
+ body: JSON.stringify({ [key]: val || null }),
720
+ });
721
+ const d = await r.json();
722
+ if (d.ok) {
723
+ statusEl.textContent = val ? '✓ Saved' : '✓ Cleared';
724
+ statusEl.style.color = 'var(--green)';
725
+ } else {
726
+ statusEl.textContent = 'Error: ' + (d.error || 'unknown');
727
+ statusEl.style.color = 'var(--red, #f87171)';
728
+ }
729
+ } catch(e) {
730
+ statusEl.textContent = 'Error: ' + e.message;
731
+ statusEl.style.color = 'var(--red, #f87171)';
732
+ }
733
+ setTimeout(() => { statusEl.textContent = ''; }, 3000);
734
+ }
735
+
736
+ export async function loadEnvAdvanced() {
737
+ const box = document.getElementById('envAdvancedWidget');
738
+ if (!box) return;
739
+ try {
740
+ const [envBasic, d] = await Promise.all([
741
+ fetch('/api/env').then(r => r.json()).catch(() => ({})),
742
+ fetch('/api/env-advanced').then(r => r.json()).catch(() => ({ env: {} })),
743
+ ]);
744
+ const env = d.env || {};
745
+ const uptime = envBasic.uptime != null
746
+ ? (envBasic.uptime < 60 ? envBasic.uptime + 's' : Math.floor(envBasic.uptime / 60) + 'm') : '—';
747
+ let html = `<div style="display:flex;gap:24px;flex-wrap:wrap;font-size:11px;color:var(--text-3);margin-bottom:16px;padding-bottom:10px;border-bottom:1px solid var(--border);">
748
+ <span>cwd: <code style="color:var(--text-2);">${escHtml(envBasic.cwd || '—')}</code></span>
749
+ <span>node: <code style="color:var(--text-2);">${escHtml(envBasic.node || '—')}</code></span>
750
+ <span>uptime: <code style="color:var(--text-2);">${uptime}</code></span>
751
+ </div>`;
752
+ box.innerHTML = html;
753
+ for (const group of ENV_GROUPS) {
754
+ const section = document.createElement('div');
755
+ section.style.cssText = 'margin-bottom:18px;';
756
+ section.innerHTML = `<div style="font-size:11px;font-weight:700;color:var(--text-3);text-transform:uppercase;letter-spacing:.06em;margin-bottom:${group.note ? '4px' : '8px'};">${escHtml(group.label)}</div>`
757
+ + (group.note ? `<div style="font-size:11px;color:var(--accent);margin-bottom:8px;line-height:1.4;">${escHtml(group.note)}</div>` : '');
758
+ for (const { key, hint, default: def } of group.vars) {
759
+ const saved = env[key] ?? null;
760
+ // Show saved value if set, otherwise fall back to the default so users
761
+ // can see the effective value and edit from a sensible starting point.
762
+ const current = saved ?? def ?? '';
763
+ const isDefault = saved === null;
764
+ const placeholder = def ? `default: ${def}` : 'not set';
765
+ const row = document.createElement('div');
766
+ row.style.cssText = 'margin-bottom:8px;';
767
+ row.innerHTML = `
768
+ <div style="display:flex;align-items:baseline;gap:6px;margin-bottom:3px;">
769
+ <span style="font-size:11px;font-family:monospace;color:var(--accent);">${escHtml(key)}</span>
770
+ ${isDefault && def ? `<span style="font-size:10px;color:var(--text-3);font-family:monospace;background:var(--bg-1);padding:1px 5px;border-radius:4px;border:1px solid var(--border);">default</span>` : ''}
771
+ </div>
772
+ <div style="font-size:10px;color:var(--text-3);margin-bottom:4px;">${escHtml(hint)}</div>
773
+ <div style="display:flex;gap:6px;align-items:center;">
774
+ <input data-env-key="${escHtml(key)}" data-env-default="${escHtml(def || '')}" type="text" value="${escHtml(current)}"
775
+ placeholder="${escHtml(placeholder)}"
776
+ class="inp-sm inp-mono inp-flex" />
777
+ <button data-env-save="${escHtml(key)}" style="font-size:11px;padding:5px 10px;border-radius:6px;cursor:pointer;border:1px solid var(--border);background:var(--surface-2);color:var(--text-2);white-space:nowrap;">Save</button>
778
+ <span data-env-status="${escHtml(key)}" style="font-size:11px;min-width:50px;"></span>
779
+ </div>`;
780
+ section.appendChild(row);
781
+ }
782
+ box.appendChild(section);
783
+ }
784
+ box.querySelectorAll('[data-env-save]').forEach(btn => {
785
+ btn.addEventListener('click', () => {
786
+ const key = btn.dataset.envSave;
787
+ const inputEl = box.querySelector(`[data-env-key="${key}"]`);
788
+ const statusEl = box.querySelector(`[data-env-status="${key}"]`);
789
+ if (inputEl && statusEl) saveEnvVar(key, inputEl, statusEl);
790
+ });
791
+ });
792
+ box.querySelectorAll('[data-env-key]').forEach(inp => {
793
+ inp.addEventListener('input', () => {
794
+ // Dim if empty or if value matches the default (user hasn't customised)
795
+ const isDefault = inp.value === (inp.dataset.envDefault || '');
796
+ inp.style.color = 'var(--text-1)';
797
+ inp.style.opacity = isDefault ? '0.65' : '1';
798
+ });
799
+ });
800
+ } catch(e) {
801
+ if (box) box.textContent = 'Could not load: ' + e.message;
802
+ }
803
+ }