crewswarm 0.9.2 → 0.9.4

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 (228) hide show
  1. package/README.md +22 -9
  2. package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js +1 -0
  3. package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js.br +0 -0
  4. package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js +1 -0
  5. package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js.br +0 -0
  6. package/apps/dashboard/dist/assets/index-BeVllEj_.js +2 -0
  7. package/apps/dashboard/dist/assets/index-BeVllEj_.js.br +0 -0
  8. package/apps/dashboard/dist/assets/{index-CF0aJRtC.css → index-D-sRshvg.css} +1 -1
  9. package/apps/dashboard/dist/assets/index-D-sRshvg.css.br +0 -0
  10. package/apps/dashboard/dist/assets/tab-benchmarks-tab-BHjKCPm3.js.br +0 -0
  11. package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js +1 -0
  12. package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js.br +0 -0
  13. package/apps/dashboard/dist/assets/{tab-pm-loop-tab-Bfd449B4.js → tab-pm-loop-tab-DiAPTJXu.js} +1 -1
  14. package/apps/dashboard/dist/assets/tab-pm-loop-tab-DiAPTJXu.js.br +0 -0
  15. package/apps/dashboard/dist/assets/{tab-projects-tab-DhNWnlzt.js → tab-projects-tab-SFH4E--a.js} +1 -1
  16. package/apps/dashboard/dist/assets/tab-projects-tab-SFH4E--a.js.br +0 -0
  17. package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js +1 -0
  18. package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js.br +0 -0
  19. package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js +1 -0
  20. package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js.br +0 -0
  21. package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js +1 -0
  22. package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js.br +0 -0
  23. package/apps/dashboard/dist/index.html +135 -15
  24. package/apps/dashboard/dist/index.html.br +0 -0
  25. package/apps/dashboard/dist/index.html.gz +0 -0
  26. package/apps/vibe/README.md +2 -2
  27. package/apps/vibe/package.json +1 -1
  28. package/apps/vibe/server.mjs +101 -56
  29. package/crew-lead.mjs +34 -4
  30. package/lib/bridges/cli-executor.mjs +1 -1
  31. package/lib/bridges/gateway-ws.mjs +4 -0
  32. package/lib/browser/passthrough-stderr.js +1 -0
  33. package/lib/chat/project-messages.mjs +3 -5
  34. package/lib/cli-process-tracker.mjs +3 -2
  35. package/lib/contacts/identity-linker.mjs +1 -0
  36. package/lib/crew-judge/judge.mjs +19 -18
  37. package/lib/crew-lead/agent-manager.mjs +1 -1
  38. package/lib/crew-lead/background.mjs +14 -1
  39. package/lib/crew-lead/chat-handler.mjs +38 -1
  40. package/lib/crew-lead/http-server.mjs +106 -57
  41. package/lib/crew-lead/llm-caller.mjs +24 -8
  42. package/lib/crew-lead/prompts.mjs +14 -1
  43. package/lib/crew-lead/tools.mjs +3 -2
  44. package/lib/crew-lead/wave-dispatcher.mjs +19 -5
  45. package/lib/crew-lead/ws-router.mjs +219 -27
  46. package/lib/engines/crew-cli.mjs +1 -1
  47. package/lib/engines/engine-registry.mjs +14 -3
  48. package/lib/engines/rt-envelope.mjs +1 -0
  49. package/lib/engines/runners.mjs +28 -4
  50. package/lib/gemini-cli-passthrough-noise.mjs +1 -1
  51. package/lib/integrations/code-search.mjs +4 -3
  52. package/lib/memory/shared-adapter.mjs +23 -10
  53. package/lib/pipeline/manager.mjs +2 -1
  54. package/lib/runtime/config.mjs +1 -1
  55. package/lib/runtime/paths.mjs +12 -8
  56. package/lib/runtime/spending.mjs +2 -1
  57. package/package.json +42 -14
  58. package/scripts/capture-build-flow.mjs +118 -0
  59. package/scripts/coverage-report.mjs +209 -0
  60. package/scripts/coverage-summary.mjs +47 -0
  61. package/scripts/dashboard-validation.mjs +76 -0
  62. package/scripts/dashboard.mjs +1667 -551
  63. package/scripts/generate-openapi.mjs +683 -277
  64. package/scripts/live-bridge-matrix.mjs +79 -0
  65. package/scripts/live-cli-matrix.mjs +166 -0
  66. package/scripts/live-crewchat-check.mjs +42 -0
  67. package/scripts/live-engine-matrix.mjs +50 -0
  68. package/scripts/live-provider-failover-matrix.mjs +107 -0
  69. package/scripts/live-provider-matrix.mjs +228 -0
  70. package/scripts/restart-all-from-repo.sh +4 -4
  71. package/scripts/restart-service.sh +12 -9
  72. package/scripts/smoke-dispatch.mjs +4 -1
  73. package/scripts/test-blast-radius.mjs +204 -0
  74. package/scripts/test-report-summary.mjs +88 -0
  75. package/scripts/test-reporter.mjs +651 -0
  76. package/scripts/test-rerun.mjs +136 -0
  77. package/scripts/tmux-bridge +130 -0
  78. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js +0 -1
  79. package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
  80. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +0 -1
  81. package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
  82. package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
  83. package/apps/dashboard/dist/assets/index-DnClJ1ee.js +0 -2
  84. package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
  85. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js +0 -1
  86. package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
  87. package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
  88. package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
  89. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js +0 -1
  90. package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
  91. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js +0 -1
  92. package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
  93. package/apps/dashboard/index.html +0 -6529
  94. package/apps/dashboard/package.json +0 -15
  95. package/apps/dashboard/src/app.js +0 -2828
  96. package/apps/dashboard/src/app.js.br +0 -0
  97. package/apps/dashboard/src/app.js.gz +0 -0
  98. package/apps/dashboard/src/chat/chat-actions.js +0 -1847
  99. package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
  100. package/apps/dashboard/src/chat/unified-messages.js +0 -327
  101. package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
  102. package/apps/dashboard/src/cli-process.js +0 -208
  103. package/apps/dashboard/src/cli-process.js.br +0 -0
  104. package/apps/dashboard/src/cli-process.js.gz +0 -0
  105. package/apps/dashboard/src/components/active-tasks-panel.js +0 -175
  106. package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
  107. package/apps/dashboard/src/core/api.js +0 -18
  108. package/apps/dashboard/src/core/api.js.br +0 -0
  109. package/apps/dashboard/src/core/dom.js +0 -228
  110. package/apps/dashboard/src/core/dom.js.br +0 -0
  111. package/apps/dashboard/src/core/state.js +0 -91
  112. package/apps/dashboard/src/core/state.js.br +0 -0
  113. package/apps/dashboard/src/core/task-manager.js +0 -134
  114. package/apps/dashboard/src/core/task-manager.js.br +0 -0
  115. package/apps/dashboard/src/orchestration-status.js +0 -127
  116. package/apps/dashboard/src/orchestration-status.js.br +0 -0
  117. package/apps/dashboard/src/setup-wizard.js +0 -562
  118. package/apps/dashboard/src/setup-wizard.js.br +0 -0
  119. package/apps/dashboard/src/styles.css +0 -2085
  120. package/apps/dashboard/src/styles.css.br +0 -0
  121. package/apps/dashboard/src/styles.css.gz +0 -0
  122. package/apps/dashboard/src/tabs/agents-tab.js +0 -2237
  123. package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
  124. package/apps/dashboard/src/tabs/benchmarks-tab.js +0 -229
  125. package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
  126. package/apps/dashboard/src/tabs/comms-tab.js +0 -955
  127. package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
  128. package/apps/dashboard/src/tabs/contacts-tab.js +0 -654
  129. package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
  130. package/apps/dashboard/src/tabs/engines-tab.js +0 -175
  131. package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
  132. package/apps/dashboard/src/tabs/memory-tab.js +0 -182
  133. package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
  134. package/apps/dashboard/src/tabs/models-tab.js +0 -450
  135. package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
  136. package/apps/dashboard/src/tabs/pm-loop-tab.js +0 -185
  137. package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
  138. package/apps/dashboard/src/tabs/projects-tab.js +0 -663
  139. package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
  140. package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
  141. package/apps/dashboard/src/tabs/prompts-tab.js +0 -160
  142. package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
  143. package/apps/dashboard/src/tabs/services-tab.js +0 -202
  144. package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
  145. package/apps/dashboard/src/tabs/settings-tab.js +0 -861
  146. package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
  147. package/apps/dashboard/src/tabs/skills-tab.js +0 -284
  148. package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
  149. package/apps/dashboard/src/tabs/spending-tab.js +0 -173
  150. package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
  151. package/apps/dashboard/src/tabs/swarm-chat-tab.js +0 -660
  152. package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
  153. package/apps/dashboard/src/tabs/swarm-tab.js +0 -538
  154. package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
  155. package/apps/dashboard/src/tabs/usage-tab.js +0 -390
  156. package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
  157. package/apps/dashboard/src/tabs/waves-tab.js +0 -238
  158. package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
  159. package/apps/dashboard/src/tabs/workflows-tab.js +0 -747
  160. package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
  161. package/apps/vibe/.crew/agent-memory/pipeline.json +0 -304
  162. package/apps/vibe/.crew/cost.json +0 -17
  163. package/apps/vibe/.crew/json-parse-metrics.jsonl +0 -27
  164. package/apps/vibe/.crew/pipeline-metrics.jsonl +0 -27
  165. package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +0 -5
  166. package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +0 -5
  167. package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +0 -5
  168. package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +0 -5
  169. package/apps/vibe/.crew/pipeline-runs/pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c.jsonl +0 -5
  170. package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +0 -5
  171. package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +0 -5
  172. package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +0 -5
  173. package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +0 -5
  174. package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +0 -5
  175. package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +0 -5
  176. package/apps/vibe/.crew/pipeline-runs/pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842.jsonl +0 -5
  177. package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +0 -5
  178. package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +0 -5
  179. package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +0 -5
  180. package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +0 -5
  181. package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +0 -5
  182. package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +0 -5
  183. package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +0 -5
  184. package/apps/vibe/.crew/pipeline-runs/pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40.jsonl +0 -5
  185. package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +0 -5
  186. package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +0 -2
  187. package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +0 -5
  188. package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +0 -5
  189. package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +0 -5
  190. package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +0 -5
  191. package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +0 -5
  192. package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +0 -5
  193. package/apps/vibe/.crew/sandbox.json +0 -7
  194. package/apps/vibe/.crew/session.json +0 -330
  195. package/apps/vibe/.crew/training-data.jsonl +0 -0
  196. package/apps/vibe/.github/workflows/studio-quality.yml +0 -37
  197. package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +0 -18
  198. package/apps/vibe/.studio-data/project-messages/general.jsonl +0 -81
  199. package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +0 -18
  200. package/apps/vibe/ARCHITECTURE.md +0 -3393
  201. package/apps/vibe/QUICK-REFERENCE.md +0 -211
  202. package/apps/vibe/ROADMAP.md +0 -41
  203. package/apps/vibe/STUDIO-SETUP-COMPLETE.md +0 -35
  204. package/apps/vibe/VISUAL-GUIDE.md +0 -378
  205. package/apps/vibe/capture-demo.mjs +0 -160
  206. package/apps/vibe/capture-full-demo.mjs +0 -255
  207. package/apps/vibe/capture-quickstart.mjs +0 -256
  208. package/apps/vibe/capture-vibe-assets.mjs +0 -71
  209. package/apps/vibe/capture-vibe-video.mjs +0 -260
  210. package/apps/vibe/check-buttons.js +0 -41
  211. package/apps/vibe/diagnose.html +0 -106
  212. package/apps/vibe/fix-buttons.js +0 -103
  213. package/apps/vibe/index.html +0 -3404
  214. package/apps/vibe/package-lock.json +0 -920
  215. package/apps/vibe/scripts/studio-pty-host.py +0 -117
  216. package/apps/vibe/src/main.js +0 -2940
  217. package/apps/vibe/src/register-all-languages.js +0 -98
  218. package/apps/vibe/start-studio.sh +0 -11
  219. package/apps/vibe/test/accessibility-tests.js +0 -77
  220. package/apps/vibe/test/browser-performance-audit.mjs +0 -205
  221. package/apps/vibe/test/performance-tests.js +0 -120
  222. package/apps/vibe/test/security-tests.js +0 -213
  223. package/apps/vibe/tests/e2e.local.mjs +0 -54
  224. package/apps/vibe/tests/server.smoke.mjs +0 -106
  225. package/apps/vibe/update_website.mjs +0 -74
  226. package/apps/vibe/vite.config.js +0 -19
  227. package/lib/crew-lead/chat-handler.mjs.bak +0 -1274
  228. package/lib/engines/rt-envelope.mjs.backup-current +0 -870
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ const jsonMode = process.argv.includes("--json");
8
+ const home = os.homedir();
9
+ const crewDir = path.join(home, ".crewswarm");
10
+ const logsDir = path.join(crewDir, "logs");
11
+
12
+ function exists(file) {
13
+ try {
14
+ return fs.existsSync(file);
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+
20
+ function readJson(file) {
21
+ try {
22
+ return JSON.parse(fs.readFileSync(file, "utf8"));
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+
28
+ const telegramCfg = readJson(path.join(crewDir, "telegram-bridge.json"));
29
+ const whatsappCfg = readJson(path.join(crewDir, "whatsapp-bridge.json"));
30
+
31
+ const payload = {
32
+ telegram: {
33
+ configured: Boolean(telegramCfg?.token),
34
+ allowedChats: Array.isArray(telegramCfg?.allowedChatIds) ? telegramCfg.allowedChatIds.length : 0,
35
+ logFile: path.join(logsDir, "telegram-bridge.jsonl"),
36
+ messagesFile: path.join(logsDir, "telegram-messages.jsonl"),
37
+ logPresent: exists(path.join(logsDir, "telegram-bridge.jsonl")),
38
+ messagesPresent: exists(path.join(logsDir, "telegram-messages.jsonl")),
39
+ },
40
+ whatsapp: {
41
+ configured: exists(path.join(crewDir, "whatsapp-auth", "creds.json")),
42
+ allowedNumbers: Array.isArray(whatsappCfg?.allowedNumbers) ? whatsappCfg.allowedNumbers.length : 0,
43
+ logFile: path.join(logsDir, "whatsapp-bridge.jsonl"),
44
+ messagesFile: path.join(logsDir, "whatsapp-messages.jsonl"),
45
+ logPresent: exists(path.join(logsDir, "whatsapp-bridge.jsonl")),
46
+ messagesPresent: exists(path.join(logsDir, "whatsapp-messages.jsonl")),
47
+ },
48
+ checklist: [
49
+ "1. Start the stack with `npm run restart-all`.",
50
+ "2. Run `node scripts/health-check.mjs` and confirm crew-lead/dashboard are up.",
51
+ "3. For Telegram: run `node --test test/e2e/telegram-roundtrip.test.mjs` with a configured bot token.",
52
+ "4. For WhatsApp: run `node --test test/e2e/whatsapp-roundtrip.test.mjs` after QR auth is established.",
53
+ "5. Send one inbound message and confirm project/history logs update.",
54
+ ],
55
+ };
56
+
57
+ if (jsonMode) {
58
+ console.log(JSON.stringify(payload, null, 2));
59
+ process.exit(0);
60
+ }
61
+
62
+ console.log("CrewSwarm live bridge matrix");
63
+ console.log("");
64
+ console.log("Telegram:");
65
+ console.log(` configured: ${payload.telegram.configured ? "yes" : "no"}`);
66
+ console.log(` allowed chats: ${payload.telegram.allowedChats}`);
67
+ console.log(` log present: ${payload.telegram.logPresent ? "yes" : "no"}`);
68
+ console.log(` messages log present: ${payload.telegram.messagesPresent ? "yes" : "no"}`);
69
+ console.log("");
70
+ console.log("WhatsApp:");
71
+ console.log(` configured: ${payload.whatsapp.configured ? "yes" : "no"}`);
72
+ console.log(` allowed numbers: ${payload.whatsapp.allowedNumbers}`);
73
+ console.log(` log present: ${payload.whatsapp.logPresent ? "yes" : "no"}`);
74
+ console.log(` messages log present: ${payload.whatsapp.messagesPresent ? "yes" : "no"}`);
75
+ console.log("");
76
+ console.log("Checklist:");
77
+ for (const item of payload.checklist) {
78
+ console.log(` ${item}`);
79
+ }
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import { execFileSync, spawn } from "node:child_process";
5
+
6
+ const jsonMode = process.argv.includes("--json");
7
+ const smokeMode = process.argv.includes("--smoke");
8
+ const cwd = process.cwd();
9
+ const PROMPT = "Reply with exactly CLI_MATRIX_OK and nothing else.";
10
+ const openCodeModel = process.env.CREWSWARM_OPENCODE_MODEL || "opencode/big-pickle";
11
+
12
+ function hasBin(bin) {
13
+ try {
14
+ if (bin.includes("/") && fs.existsSync(bin)) return true;
15
+ execFileSync("which", [bin], { stdio: "pipe", timeout: 4000 });
16
+ return true;
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ function runCli(bin, args, timeoutMs = Number(process.env.CREWSWARM_LIVE_CLI_TIMEOUT_MS || 30000)) {
23
+ return new Promise((resolve) => {
24
+ const child = spawn(bin, args, {
25
+ cwd,
26
+ env: process.env,
27
+ stdio: ["ignore", "pipe", "pipe"],
28
+ });
29
+ let stdout = "";
30
+ let stderr = "";
31
+ let done = false;
32
+ const timer = setTimeout(() => {
33
+ if (!done) {
34
+ done = true;
35
+ child.kill("SIGKILL");
36
+ resolve({ ok: false, error: `timeout after ${timeoutMs}ms`, stdout, stderr });
37
+ }
38
+ }, timeoutMs);
39
+
40
+ child.stdout.on("data", (chunk) => { stdout += chunk.toString(); });
41
+ child.stderr.on("data", (chunk) => { stderr += chunk.toString(); });
42
+ child.on("error", (error) => {
43
+ if (done) return;
44
+ done = true;
45
+ clearTimeout(timer);
46
+ resolve({ ok: false, error: String(error.message || error), stdout, stderr });
47
+ });
48
+ child.on("close", (code) => {
49
+ if (done) return;
50
+ done = true;
51
+ clearTimeout(timer);
52
+ const combined = `${stdout}\n${stderr}`;
53
+ resolve({
54
+ ok: code === 0 && combined.includes("CLI_MATRIX_OK"),
55
+ code,
56
+ stdout,
57
+ stderr,
58
+ });
59
+ });
60
+ });
61
+ }
62
+
63
+ const cursorBin = process.env.CURSOR_CLI_BIN || "agent";
64
+ const clis = [
65
+ {
66
+ id: "claude",
67
+ available: hasBin("claude"),
68
+ command: ["claude", ["-p", "--print", PROMPT]],
69
+ timeoutMs: Number(process.env.CREWSWARM_LIVE_CLAUDE_TIMEOUT_MS || 120000),
70
+ },
71
+ {
72
+ id: "codex",
73
+ available: hasBin("codex"),
74
+ command: ["codex", ["exec", "--sandbox", "read-only", "--json", PROMPT]],
75
+ timeoutMs: Number(process.env.CREWSWARM_LIVE_CODEX_TIMEOUT_MS || 30000),
76
+ },
77
+ {
78
+ id: "cursor",
79
+ available: hasBin(cursorBin),
80
+ command: [
81
+ cursorBin,
82
+ [
83
+ "-p",
84
+ "--force",
85
+ "--trust",
86
+ "--output-format",
87
+ "stream-json",
88
+ PROMPT,
89
+ "--model",
90
+ process.env.CREWSWARM_CURSOR_MODEL || "composer-2-fast",
91
+ "--workspace",
92
+ cwd,
93
+ ],
94
+ ],
95
+ timeoutMs: Number(process.env.CREWSWARM_LIVE_CURSOR_TIMEOUT_MS || 30000),
96
+ },
97
+ {
98
+ id: "gemini",
99
+ available: hasBin("gemini"),
100
+ command: ["gemini", ["-p", PROMPT]],
101
+ timeoutMs: Number(process.env.CREWSWARM_LIVE_GEMINI_TIMEOUT_MS || 120000),
102
+ },
103
+ {
104
+ id: "opencode",
105
+ available: hasBin("opencode"),
106
+ command: ["opencode", ["run", "--model", openCodeModel, PROMPT]],
107
+ timeoutMs: Number(process.env.CREWSWARM_LIVE_OPENCODE_TIMEOUT_MS || 120000),
108
+ },
109
+ {
110
+ id: "crew-cli",
111
+ available: true,
112
+ command: ["node", ["crew-cli/bin/crew.js", "exec", PROMPT]],
113
+ timeoutMs: Number(process.env.CREWSWARM_LIVE_CREWCLI_TIMEOUT_MS || 30000),
114
+ },
115
+ ];
116
+
117
+ const results = [];
118
+ if (smokeMode) {
119
+ for (const cli of clis.filter((item) => item.available)) {
120
+ const [bin, args] = cli.command;
121
+ const started = Date.now();
122
+ const result = await runCli(bin, args, cli.timeoutMs);
123
+ results.push({
124
+ cli: cli.id,
125
+ durationMs: Date.now() - started,
126
+ ...result,
127
+ });
128
+ }
129
+ }
130
+
131
+ const payload = {
132
+ cwd,
133
+ smokeMode,
134
+ clis: clis.map(({ id, available, command }) => ({ id, available, command: [command[0], ...command[1]] })),
135
+ results,
136
+ checklist: [
137
+ "Run `node scripts/live-cli-matrix.mjs --smoke` to execute one tiny one-shot through each installed CLI.",
138
+ "Expect some failures if auth/session state or credits are missing for a given lane.",
139
+ "Use this as a release-time trust check, not an always-on CI gate.",
140
+ ],
141
+ };
142
+
143
+ if (jsonMode) {
144
+ console.log(JSON.stringify(payload, null, 2));
145
+ process.exit(0);
146
+ }
147
+
148
+ console.log("CrewSwarm live CLI matrix");
149
+ console.log("");
150
+ for (const cli of payload.clis) {
151
+ console.log(`${cli.id.padEnd(10)} ${cli.available ? "available" : "missing"}`);
152
+ }
153
+ if (smokeMode) {
154
+ console.log("");
155
+ console.log("Smoke results:");
156
+ for (const result of results) {
157
+ const status = result.ok ? "PASS" : "FAIL";
158
+ const detail = result.ok
159
+ ? (result.stdout || result.stderr).trim().slice(0, 120)
160
+ : (result.error || result.stderr || result.stdout || "").trim().slice(0, 160);
161
+ console.log(`${status.padEnd(5)} ${result.cli.padEnd(10)} ${detail}`);
162
+ }
163
+ } else {
164
+ console.log("");
165
+ console.log("Run with --smoke to execute one tiny real one-shot through each installed CLI.");
166
+ }
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ const jsonMode = process.argv.includes("--json");
8
+ const repoRoot = process.cwd();
9
+ const appPath = path.join(os.homedir(), "Applications", "crewchat.app");
10
+ const buildScript = path.join(repoRoot, "build-crewchat.sh");
11
+ const sourcePath = path.join(repoRoot, "apps", "crewchat", "CrewChat.swift");
12
+
13
+ const payload = {
14
+ buildScriptPresent: fs.existsSync(buildScript),
15
+ sourcePresent: fs.existsSync(sourcePath),
16
+ installedAppPresent: fs.existsSync(appPath),
17
+ appPath,
18
+ checklist: [
19
+ "1. Build: ./build-crewchat.sh",
20
+ "2. Launch: open -a crewchat.app",
21
+ "3. Switch between crew-lead, CLI, and agent modes.",
22
+ "4. Send one text message, one image, and one voice note.",
23
+ "5. Confirm per-project and per-mode history isolation.",
24
+ ],
25
+ };
26
+
27
+ if (jsonMode) {
28
+ console.log(JSON.stringify(payload, null, 2));
29
+ process.exit(0);
30
+ }
31
+
32
+ console.log("crewchat live verification");
33
+ console.log("");
34
+ console.log(`Build script present: ${payload.buildScriptPresent ? "yes" : "no"}`);
35
+ console.log(`Source present: ${payload.sourcePresent ? "yes" : "no"}`);
36
+ console.log(`Installed app present: ${payload.installedAppPresent ? "yes" : "no"}`);
37
+ console.log(`App path: ${payload.appPath}`);
38
+ console.log("");
39
+ console.log("Checklist:");
40
+ for (const item of payload.checklist) {
41
+ console.log(` ${item}`);
42
+ }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ const configPath = path.join(os.homedir(), ".crewswarm", "crewswarm.json");
8
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
9
+ const agents = Array.isArray(config.agents) ? config.agents : config.agents?.list || [];
10
+
11
+ function detectRoute(agent) {
12
+ if (agent.useCursorCli) return "cursor";
13
+ if (agent.useClaudeCode) return "claude-code";
14
+ if (agent.useCodex) return "codex";
15
+ if (agent.useGeminiCli) return "gemini-cli";
16
+ if (agent.useCrewCLI) return "crew-cli";
17
+ if (agent.useOpenCode) return "opencode";
18
+ if (agent.useDockerSandbox) return "docker-sandbox";
19
+ return "direct";
20
+ }
21
+
22
+ function modelForRoute(agent, route) {
23
+ if (route === "cursor") return agent.cursorCliModel || "(auto)";
24
+ if (route === "claude-code") return agent.claudeCodeModel || "(auto)";
25
+ if (route === "codex") return agent.codexModel || "(auto)";
26
+ if (route === "gemini-cli") return agent.geminiCliModel || "(auto)";
27
+ if (route === "crew-cli") return agent.crewCliModel || "(default)";
28
+ if (route === "opencode") return agent.opencodeModel || "(default)";
29
+ return agent.model || "(none)";
30
+ }
31
+
32
+ const rows = agents.map((agent) => {
33
+ const route = detectRoute(agent);
34
+ return {
35
+ id: agent.id,
36
+ route,
37
+ model: modelForRoute(agent, route),
38
+ fallback: agent.fallbackModel || agent.opencodeFallbackModel || "",
39
+ };
40
+ });
41
+
42
+ console.log("CrewSwarm live engine matrix");
43
+ console.log("");
44
+ for (const row of rows) {
45
+ console.log(`${row.id.padEnd(22)} ${row.route.padEnd(15)} ${row.model}${row.fallback ? ` fallback=${row.fallback}` : ""}`);
46
+ }
47
+
48
+ console.log("");
49
+ console.log("Next step:");
50
+ console.log("Run a real task through each important route and confirm the observed runtime/model in logs or UI.");
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ const jsonMode = process.argv.includes("--json");
8
+ const configPath = path.join(os.homedir(), ".crewswarm", "crewswarm.json");
9
+
10
+ function readConfig() {
11
+ try {
12
+ return JSON.parse(fs.readFileSync(configPath, "utf8"));
13
+ } catch {
14
+ return {};
15
+ }
16
+ }
17
+
18
+ const cfg = readConfig();
19
+ const providers = cfg.providers || {};
20
+ const agents = Array.isArray(cfg.agents) ? cfg.agents : cfg.agents?.list || [];
21
+
22
+ const activeProviders = Object.entries(providers)
23
+ .filter(([, value]) => value?.apiKey && String(value.apiKey).trim())
24
+ .map(([id, value]) => ({
25
+ id,
26
+ hasBaseUrl: Boolean(value.baseUrl),
27
+ }));
28
+
29
+ const interestingAgents = agents
30
+ .filter((agent) => {
31
+ return (
32
+ agent.id === "crew-main" ||
33
+ agent.id === "crew-coder" ||
34
+ agent.id === "crew-qa" ||
35
+ agent.id === "crew-pm" ||
36
+ agent.useClaudeCode ||
37
+ agent.useCodex ||
38
+ agent.useGeminiCli ||
39
+ agent.useCrewCLI ||
40
+ agent.useCursorCli
41
+ );
42
+ })
43
+ .map((agent) => ({
44
+ id: agent.id,
45
+ primary: agent.model || "(none)",
46
+ fallback: agent.fallbackModel || agent.opencodeFallbackModel || "",
47
+ route:
48
+ agent.useClaudeCode ? "claude-code" :
49
+ agent.useCodex ? "codex" :
50
+ agent.useGeminiCli ? "gemini-cli" :
51
+ agent.useCrewCLI ? "crew-cli" :
52
+ agent.useCursorCli ? "cursor" :
53
+ agent.useOpenCode ? "opencode" :
54
+ "direct",
55
+ }));
56
+
57
+ const payload = {
58
+ configPath,
59
+ configuredProviders: activeProviders,
60
+ agents: interestingAgents,
61
+ checklist: [
62
+ "1. Run `npm run restart-all` and confirm `node scripts/health-check.mjs` passes.",
63
+ "2. For each important route, trigger one real task and confirm the observed runtime/model in logs or UI.",
64
+ "3. Intentionally disable or exhaust one primary provider, then confirm fallback activates.",
65
+ "4. Record which routes fail closed vs. fail over successfully.",
66
+ ],
67
+ sampleTasks: [
68
+ 'crew-main: "say: PROVIDER_FAILOVER_OK"',
69
+ 'crew-coder: "Create test-output/provider-fallback.txt with one line: PROVIDER_FALLBACK_OK"',
70
+ 'crew-qa: "Summarize which provider/model you are using in one line"',
71
+ ],
72
+ };
73
+
74
+ if (jsonMode) {
75
+ console.log(JSON.stringify(payload, null, 2));
76
+ process.exit(0);
77
+ }
78
+
79
+ console.log("CrewSwarm live provider/failover matrix");
80
+ console.log("");
81
+ console.log(`Config: ${configPath}`);
82
+ console.log("");
83
+ console.log("Configured providers:");
84
+ if (!activeProviders.length) {
85
+ console.log(" (none)");
86
+ } else {
87
+ for (const provider of activeProviders) {
88
+ console.log(` - ${provider.id}${provider.hasBaseUrl ? " (custom baseUrl)" : ""}`);
89
+ }
90
+ }
91
+ console.log("");
92
+ console.log("Interesting agents/routes:");
93
+ for (const agent of interestingAgents) {
94
+ console.log(
95
+ ` - ${agent.id}: route=${agent.route} primary=${agent.primary}${agent.fallback ? ` fallback=${agent.fallback}` : ""}`,
96
+ );
97
+ }
98
+ console.log("");
99
+ console.log("Checklist:");
100
+ for (const item of payload.checklist) {
101
+ console.log(` ${item}`);
102
+ }
103
+ console.log("");
104
+ console.log("Sample tasks:");
105
+ for (const item of payload.sampleTasks) {
106
+ console.log(` - ${item}`);
107
+ }
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { _callLLMOnce } from "../lib/crew-lead/llm-caller.mjs";
7
+
8
+ const jsonMode = process.argv.includes("--json");
9
+ const smokeMode = process.argv.includes("--smoke");
10
+ const configPath = path.join(os.homedir(), ".crewswarm", "crewswarm.json");
11
+ const SMOKE_PROMPT = "Reply with exactly PROVIDER_MATRIX_OK and nothing else.";
12
+
13
+ function readConfig() {
14
+ try {
15
+ return JSON.parse(fs.readFileSync(configPath, "utf8"));
16
+ } catch {
17
+ return {};
18
+ }
19
+ }
20
+
21
+ function modelFromConfig(providerId, providerCfg, agents) {
22
+ const directModel = String(
23
+ providerCfg?.defaultModel || providerCfg?.model || providerCfg?.chatModel || "",
24
+ ).trim();
25
+ if (directModel) return directModel;
26
+
27
+ const listed = Array.isArray(providerCfg?.models)
28
+ ? providerCfg.models.map((entry) => String(entry?.id || "").trim()).filter(Boolean)
29
+ : [];
30
+
31
+ const preferredDefaults = {
32
+ anthropic: "claude-3-haiku-20240307",
33
+ nvidia: "meta/llama-3.1-8b-instruct",
34
+ ollama: "qwen3-coder:480b-cloud",
35
+ "openai-local": "gpt-5.1",
36
+ opencode: "gpt-5.1",
37
+ };
38
+ const preferred = preferredDefaults[providerId];
39
+ if (preferred && listed.includes(preferred)) return preferred;
40
+
41
+ const prefixMap = {
42
+ google: ["google/", "gemini"],
43
+ anthropic: ["anthropic/", "claude"],
44
+ openai: ["openai/", "gpt-"],
45
+ xai: ["xai/", "grok"],
46
+ deepseek: ["deepseek/", "deepseek"],
47
+ groq: ["groq/", "llama", "mixtral", "qwen", "kimi", "compound"],
48
+ mistral: ["mistral/", "mistral"],
49
+ perplexity: ["perplexity/", "sonar"],
50
+ nvidia: ["nvidia/", "meta/", "google/", "deepseek-ai/"],
51
+ cerebras: ["cerebras/"],
52
+ openrouter: ["openrouter/"],
53
+ fireworks: ["fireworks/", "accounts/fireworks/"],
54
+ opencode: [],
55
+ "openai-local": ["openai-local/"],
56
+ };
57
+ const prefixes = prefixMap[providerId] || [];
58
+ const found = agents.find((agent) => {
59
+ const model = String(agent?.model || "").toLowerCase();
60
+ return prefixes.some((prefix) => model.startsWith(prefix) || model.includes(prefix));
61
+ });
62
+ if (found?.model) return String(found.model);
63
+
64
+ const firstListed = listed[0];
65
+ if (firstListed) return String(firstListed);
66
+
67
+ const hardcoded = {
68
+ google: "gemini-2.5-flash",
69
+ anthropic: "claude-3-haiku-20240307",
70
+ openai: "gpt-5.1",
71
+ xai: "grok-4.1-fast",
72
+ deepseek: "deepseek-chat",
73
+ groq: "llama-3.1-8b-instant",
74
+ mistral: "mistral-small-latest",
75
+ perplexity: "sonar",
76
+ nvidia: "meta/llama-3.1-8b-instruct",
77
+ cerebras: "llama-3.3-70b",
78
+ openrouter: "meta-llama/llama-3.1-8b-instruct:free",
79
+ fireworks: "accounts/fireworks/models/gpt-oss-20b",
80
+ opencode: "gpt-5.1",
81
+ ollama: "qwen3-coder:480b-cloud",
82
+ "openai-local": "gpt-5.1",
83
+ };
84
+ return hardcoded[providerId] || "(unknown)";
85
+ }
86
+
87
+ function normalizeModel(providerId, modelId, providerCfg) {
88
+ const raw = String(modelId || "").trim();
89
+ if (!raw) return raw;
90
+
91
+ const prefixStrips = {
92
+ google: [/^google\/models\//i, /^google\//i],
93
+ anthropic: [/^anthropic\//i],
94
+ openai: [/^openai\//i],
95
+ xai: [/^xai\//i],
96
+ deepseek: [/^deepseek\//i],
97
+ perplexity: [/^perplexity\//i],
98
+ openrouter: [/^openrouter\//i],
99
+ fireworks: [/^fireworks\//i],
100
+ };
101
+
102
+ let normalized = raw;
103
+ for (const pattern of prefixStrips[providerId] || []) {
104
+ normalized = normalized.replace(pattern, "");
105
+ }
106
+
107
+ if (providerId === "google" && normalized.startsWith("models/")) {
108
+ normalized = normalized.replace(/^models\//i, "");
109
+ }
110
+
111
+ const listed = Array.isArray(providerCfg?.models)
112
+ ? providerCfg.models.map((entry) => String(entry?.id || "").trim()).filter(Boolean)
113
+ : [];
114
+ if (listed.includes(normalized)) return normalized;
115
+ if (listed.includes(raw)) return raw;
116
+
117
+ if (
118
+ providerId === "nvidia" &&
119
+ listed.length > 0 &&
120
+ (normalized.includes("gemini") || normalized.includes("grok") || normalized.includes("claude"))
121
+ ) {
122
+ return listed[0];
123
+ }
124
+
125
+ return normalized;
126
+ }
127
+
128
+ function mapProviderKey(providerId) {
129
+ if (providerId === "google") return "google";
130
+ if (providerId === "xai") return "xai";
131
+ return providerId;
132
+ }
133
+
134
+ async function smokeProvider(providerId, providerCfg, modelId) {
135
+ const messages = [{ role: "user", content: SMOKE_PROMPT }];
136
+ const started = Date.now();
137
+ try {
138
+ const timeoutMs =
139
+ providerId === "ollama"
140
+ ? Number(process.env.CREWSWARM_LIVE_OLLAMA_TIMEOUT_MS || 90000)
141
+ : providerId === "openai-local"
142
+ ? Number(process.env.CREWSWARM_LIVE_OPENAI_LOCAL_TIMEOUT_MS || 45000)
143
+ : Number(process.env.CREWSWARM_LIVE_PROVIDER_TIMEOUT_MS || 20000);
144
+ const reply = await _callLLMOnce(
145
+ providerCfg.baseUrl,
146
+ providerCfg.apiKey,
147
+ modelId,
148
+ mapProviderKey(providerId),
149
+ messages,
150
+ { stream: false, timeoutMs },
151
+ );
152
+ const text = String(reply || "").trim();
153
+ return {
154
+ ok: text.includes("PROVIDER_MATRIX_OK"),
155
+ reply: text.slice(0, 120),
156
+ durationMs: Date.now() - started,
157
+ };
158
+ } catch (error) {
159
+ return {
160
+ ok: false,
161
+ error: String(error?.message || error),
162
+ durationMs: Date.now() - started,
163
+ };
164
+ }
165
+ }
166
+
167
+ const cfg = readConfig();
168
+ const providers = cfg.providers || {};
169
+ const agents = Array.isArray(cfg.agents) ? cfg.agents : cfg.agents?.list || [];
170
+
171
+ const configuredProviders = Object.entries(providers)
172
+ .filter(([, value]) => value?.apiKey && value?.baseUrl)
173
+ .map(([id, value]) => ({
174
+ id,
175
+ baseUrl: value.baseUrl,
176
+ model: normalizeModel(id, modelFromConfig(id, value, agents), value),
177
+ listedModels: Array.isArray(value.models) ? value.models.length : 0,
178
+ }));
179
+
180
+ const results = [];
181
+ if (smokeMode) {
182
+ for (const provider of configuredProviders) {
183
+ const providerCfg = providers[provider.id];
184
+ results.push({
185
+ provider: provider.id,
186
+ model: provider.model,
187
+ ...(await smokeProvider(provider.id, providerCfg, provider.model)),
188
+ });
189
+ }
190
+ }
191
+
192
+ const payload = {
193
+ configPath,
194
+ smokeMode,
195
+ providers: configuredProviders,
196
+ results,
197
+ checklist: [
198
+ "Use `node scripts/live-provider-matrix.mjs --smoke` to run one tiny real call per configured provider.",
199
+ "Use `node scripts/live-provider-failover-matrix.mjs` to inspect route/fallback expectations.",
200
+ "Treat failures as wiring, auth, quota, or model-selection issues to investigate before release.",
201
+ ],
202
+ };
203
+
204
+ if (jsonMode) {
205
+ console.log(JSON.stringify(payload, null, 2));
206
+ process.exit(0);
207
+ }
208
+
209
+ console.log("CrewSwarm live provider matrix");
210
+ console.log("");
211
+ for (const provider of configuredProviders) {
212
+ console.log(
213
+ `${provider.id.padEnd(14)} model=${provider.model}${provider.listedModels ? ` listedModels=${provider.listedModels}` : ""}`,
214
+ );
215
+ }
216
+
217
+ if (smokeMode) {
218
+ console.log("");
219
+ console.log("Smoke results:");
220
+ for (const result of results) {
221
+ const status = result.ok ? "PASS" : "FAIL";
222
+ const detail = result.ok ? result.reply : result.error;
223
+ console.log(`${status.padEnd(5)} ${result.provider.padEnd(14)} ${result.model} ${detail}`);
224
+ }
225
+ } else {
226
+ console.log("");
227
+ console.log("Run with --smoke to send one tiny real request through each configured provider.");
228
+ }
@@ -241,13 +241,13 @@ else
241
241
  SVC_STATUS["mcp-server"]="up"
242
242
  fi
243
243
 
244
- # ── 8. Vibe Studio + file watcher (ports 3333, 3334) ────────────────────────
244
+ # ── 8. Vibe + file watcher (ports 3333, 3334) ───────────────────────────────
245
245
  if [[ "$START_STUDIO" -eq 1 ]]; then
246
246
  echo ""
247
- echo "Starting Vibe (Studio) + file watcher (ports 3333, 3334)..."
247
+ echo "Starting Vibe + file watcher (ports 3333, 3334)..."
248
248
  if [[ ! -d "$REPO_DIR/apps/vibe/dist" ]]; then
249
- echo " Building Studio (first run)..."
250
- (cd "$REPO_DIR" && npm run studio:build) >/dev/null 2>&1 || true
249
+ echo " Building Vibe (first run)..."
250
+ (cd "$REPO_DIR" && npm run vibe:build) >/dev/null 2>&1 || true
251
251
  fi
252
252
  sleep 1
253
253
  nohup "$NODE" "$REPO_DIR/apps/vibe/server.mjs" >> /tmp/studio.log 2>&1 &