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
@@ -1,663 +0,0 @@
1
- /**
2
- * Projects + Build tab — extracted from app.js
3
- * Deps: getJSON, postJSON (core/api), escHtml, showNotification (core/dom), state (core/state)
4
- * Uses showChat, showBuild (app.js) via injected helpers or window globals
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
-
11
- // ── Nav ───────────────────────────────────────────────────────────────────────
12
-
13
- export function showBuild(helpers) {
14
- helpers.hideAllViews();
15
- document.getElementById('buildView').classList.add('active');
16
- helpers.setNavActive('navBuild');
17
- loadPhasedProgress();
18
- }
19
-
20
- export function showProjects(helpers) {
21
- helpers.hideAllViews();
22
- document.getElementById('projectsView').classList.add('active');
23
- helpers.setNavActive('navProjects');
24
- loadProjects();
25
- }
26
-
27
- // ── Project list ──────────────────────────────────────────────────────────────
28
-
29
- export async function loadProjects() {
30
- const list = document.getElementById('projectsList');
31
- list.innerHTML = '<div class="meta" style="padding:20px;">Loading projects...</div>';
32
- try {
33
- const data = await getJSON('/api/projects');
34
- const projects = data.projects || [];
35
- state.projectsData = {};
36
- projects.forEach(p => { state.projectsData[p.id] = p; });
37
- populateChatProjectDropdown(projects);
38
- if (!projects.length) {
39
- list.innerHTML = '<div class="meta" style="padding:20px;">No projects yet. Click &quot;+ New Project&quot; to create one.</div>';
40
- return;
41
- }
42
- list.innerHTML = projects.map(p => {
43
- const id = escHtml(p.id);
44
- const pct = p.roadmap.total ? Math.round((p.roadmap.done / p.roadmap.total) * 100) : 0;
45
- const barColor = pct === 100 ? 'var(--green)' : pct > 50 ? 'var(--accent)' : 'var(--yellow)';
46
- const statusBg = p.status === 'active' ? 'rgba(52,211,153,0.1)' : 'var(--bg-card2)';
47
- const statusColor= p.status === 'active' ? 'var(--green)' : 'var(--text-3)';
48
- const retryBtn = p.roadmap.failed
49
- ? '<button data-action="retry-failed" data-id="' + id + '" style="background:rgba(248,113,113,0.15);color:var(--red);border:1px solid rgba(248,113,113,0.3);border-radius:6px;padding:6px 12px;cursor:pointer;font-size:13px;font-weight:600;">↩ Retry ' + p.roadmap.failed + ' failed</button>'
50
- : '';
51
- return '<div class="card" id="proj-card-' + id + '" data-proj-id="' + id + '">'
52
- + '<div id="proj-view-' + id + '">'
53
- + '<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:12px;">'
54
- + '<div>'
55
- + '<strong style="font-size:15px;">' + escHtml(p.name) + '</strong>'
56
- + '<span style="margin-left:10px;font-size:11px;padding:2px 8px;border-radius:999px;background:' + statusBg + ';color:' + statusColor + ';border:1px solid ' + statusColor + '40;">' + escHtml(p.status) + '</span>'
57
- + (p.running ? '<span style="margin-left:8px;font-size:11px;padding:2px 8px;border-radius:999px;background:rgba(99,102,241,0.15);color:var(--purple);border:1px solid rgba(99,102,241,0.3);">▶ running</span>' : '')
58
- + (p.description ? '<div class="meta" style="margin-top:4px;">' + escHtml(p.description) + '</div>' : '')
59
- + '</div>'
60
- + '<div class="meta">' + new Date(p.created).toLocaleDateString() + '</div>'
61
- + '</div>'
62
- + '<div style="margin-bottom:12px;">'
63
- + '<div style="display:flex;justify-content:space-between;margin-bottom:6px;">'
64
- + '<span class="meta">Roadmap</span>'
65
- + '<span class="meta">' + p.roadmap.done + '/' + p.roadmap.total + ' done' + (p.roadmap.failed ? ' · ' + p.roadmap.failed + ' failed' : '') + ' · ' + p.roadmap.pending + ' pending</span>'
66
- + '</div>'
67
- + '<div class="prog-bar"><div class="prog-fill" style="width:' + pct + '%;background:' + barColor + ';"></div></div>'
68
- + '</div>'
69
- + '<div style="font-size:11px;color:var(--text-3);margin-bottom:12px;font-family:monospace;">' + escHtml(p.outputDir) + '</div>'
70
- + '<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">'
71
- + '<button data-action="pm-toggle" data-id="' + id + '" class="' + (p.running ? 'btn-red' : 'btn-green') + '" style="font-size:13px;">' + (p.running ? '⏹ Stop PM Loop' : '▶ Start PM Loop') + '</button>'
72
- + '<button data-action="open-build" data-id="' + id + '" class="btn-ghost" style="font-size:13px;">🔧 Build tab</button>'
73
- + '<button data-action="edit-roadmap" data-id="' + id + '" class="btn-ghost" style="font-size:13px;" id="roadmap-btn-' + id + '">📋 Roadmap</button>'
74
- + '<button data-action="chat-project" data-id="' + id + '" data-name="' + escHtml(p.name) + '" class="btn-ghost" style="font-size:13px;">🧠 Chat</button>'
75
- + retryBtn
76
- + '<label style="margin-left:auto;display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px;color:var(--text-3);user-select:none;" title="When enabled, crew-lead automatically starts the next ROADMAP phase when the current pipeline completes">'
77
- + '<input type="checkbox" data-action="toggle-auto-advance" data-id="' + id + '" ' + (p.autoAdvance ? 'checked' : '') + ' style="accent-color:var(--green);width:14px;height:14px;cursor:pointer;">'
78
- + '⚡ Auto-advance'
79
- + '</label>'
80
- + '<button data-action="edit" data-id="' + id + '" style="background:transparent;color:var(--text-3);border:1px solid var(--border);border-radius:6px;padding:4px 10px;cursor:pointer;font-size:12px;" title="Edit project">✎ Edit</button>'
81
- + '<button data-action="delete" data-id="' + id + '" style="background:transparent;color:var(--text-3);border:1px solid var(--border);border-radius:6px;padding:4px 10px;cursor:pointer;font-size:12px;" title="Remove from dashboard (files stay on disk)">🗑 Delete</button>'
82
- + '</div>'
83
- + '</div>'
84
- + '<div id="proj-edit-' + id + '" style="display:none;padding:12px;border-top:1px solid var(--border);margin-top:12px;">'
85
- + '<div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Name</label><input id="proj-name-' + id + '" type="text" value="' + escHtml(p.name) + '" style="margin-top:4px;" /></div>'
86
- + '<div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Description</label><input id="proj-desc-' + id + '" type="text" value="' + escHtml(p.description || '') + '" style="margin-top:4px;" placeholder="Optional" /></div>'
87
- + '<div style="margin-bottom:8px;"><label style="font-size:11px;color:var(--text-3);">Output directory</label><input id="proj-dir-' + id + '" type="text" value="' + escHtml(p.outputDir || '') + '" style="margin-top:4px;" /></div>'
88
- + '<div style="display:flex;gap:8px;"><button data-action="save-project-edit" data-id="' + id + '" class="btn-green" style="font-size:12px;">Save</button><button data-action="cancel-project-edit" data-id="' + id + '" class="btn-ghost" style="font-size:12px;">Cancel</button></div>'
89
- + '</div>'
90
- + '<div id="proj-pm-status-' + id + '" style="display:none;margin-top:10px;font-size:12px;padding:8px 12px;background:rgba(99,102,241,0.08);border-radius:6px;border:1px solid rgba(99,102,241,0.2);color:#a5b4fc;"></div>'
91
- + '<div id="rm-editor-' + id + '" style="display:none;margin-top:14px;">'
92
- + '<div style="display:flex;gap:8px;align-items:center;margin-bottom:8px;flex-wrap:wrap;">'
93
- + '<span class="field-label" style="margin:0;">ROADMAP</span>'
94
- + '<span class="meta" style="font-family:monospace;">' + escHtml(p.roadmapFile) + '</span>'
95
- + '<div style="margin-left:auto;display:flex;gap:6px;">'
96
- + '<button data-action="add-item" data-id="' + id + '" style="font-size:11px;padding:3px 10px;background:var(--green);color:#000;">+ Add item</button>'
97
- + '<button data-action="skip-next" data-id="' + id + '" style="font-size:11px;padding:3px 10px;background:var(--yellow);color:#000;">⏭ Skip next</button>'
98
- + '<button data-action="reset-failed" data-id="' + id + '" style="font-size:11px;padding:3px 10px;" class="btn-ghost">↩ Reset failed</button>'
99
- + '<button data-action="save-roadmap" data-id="' + id + '" style="font-size:11px;padding:3px 10px;background:var(--accent);color:#000;">💾 Save</button>'
100
- + '<button data-action="close-editor" data-id="' + id + '" style="font-size:11px;padding:3px 10px;" class="btn-ghost">✕</button>'
101
- + '</div>'
102
- + '</div>'
103
- + '<div style="display:flex;gap:8px;margin-bottom:8px;">'
104
- + '<input id="rm-add-' + id + '" type="text" placeholder="New item text… (Enter to add)" style="flex:1;font-size:13px;" data-rm-add-id="' + id + '" />'
105
- + '</div>'
106
- + '<textarea id="rm-ta-' + id + '" rows="16" class="rm-textarea" spellcheck="false"></textarea>'
107
- + '<div id="rm-status-' + id + '" class="meta" style="margin-top:6px;min-height:16px;"></div>'
108
- + '</div>'
109
- + '</div>';
110
- }).join('');
111
-
112
- list.querySelectorAll('[data-rm-add-id]').forEach(inp => {
113
- inp.addEventListener('keydown', e => { if (e.key === 'Enter') addRoadmapItem(inp.dataset.rmAddId); });
114
- });
115
-
116
- } catch(e) { list.innerHTML = '<div class="meta" style="padding:20px;color:var(--red-hi);">Failed to load projects: ' + escHtml(e.message) + '</div>'; }
117
- }
118
-
119
- export function toggleProjectEdit(projectId) {
120
- const viewEl = document.getElementById('proj-view-' + projectId);
121
- const editEl = document.getElementById('proj-edit-' + projectId);
122
- if (!viewEl || !editEl) return;
123
- const isEditing = editEl.style.display !== 'none';
124
- viewEl.style.display = isEditing ? '' : 'none';
125
- editEl.style.display = isEditing ? 'none' : 'block';
126
- }
127
-
128
- export async function saveProjectEdit(projectId) {
129
- const name = document.getElementById('proj-name-' + projectId)?.value?.trim();
130
- const description = document.getElementById('proj-desc-' + projectId)?.value?.trim();
131
- const outputDir = document.getElementById('proj-dir-' + projectId)?.value?.trim();
132
- if (!name) { showNotification('Project name is required', true); return; }
133
- try {
134
- await postJSON('/api/projects/update', { projectId, name, description, outputDir });
135
- showNotification('Project saved');
136
- toggleProjectEdit(projectId);
137
- loadProjects();
138
- } catch(e) { showNotification('Failed: ' + e.message, true); }
139
- }
140
-
141
- export function initProjectsList(deps) {
142
- const el = document.getElementById('projectsList');
143
- if (!el) return;
144
- el.addEventListener('click', e => {
145
- const btn = e.target.closest('[data-action]');
146
- if (!btn) return;
147
- const id = btn.dataset.id;
148
- const proj = state.projectsData[id];
149
- switch (btn.dataset.action) {
150
- case 'pm-toggle': proj && proj.running ? stopProjectPMLoop(id) : startProjectPMLoop(id); break;
151
- case 'open-build': openProjectInBuild(id, deps); break;
152
- case 'edit-roadmap': proj && openRoadmapEditor(id, proj.roadmapFile); break;
153
- case 'retry-failed': proj && retryFailed(proj.roadmapFile); break;
154
- case 'delete': deleteProject(id); break;
155
- case 'chat-project': {
156
- deps.showChat();
157
- autoSelectChatProject(id);
158
- document.getElementById('chatInput')?.focus();
159
- break;
160
- }
161
- case 'toggle-auto-advance': {
162
- const checked = btn.checked;
163
- postJSON('/api/projects/update', { projectId: id, autoAdvance: checked })
164
- .then(() => {
165
- if (state.projectsData[id]) state.projectsData[id].autoAdvance = checked;
166
- showNotification('Auto-advance ' + (checked ? 'enabled' : 'disabled') + ' for ' + (proj?.name || id));
167
- })
168
- .catch(e => { showNotification('Failed: ' + e.message, true); btn.checked = !checked; });
169
- return;
170
- }
171
- case 'edit': toggleProjectEdit(id); break;
172
- case 'save-project-edit': saveProjectEdit(id); break;
173
- case 'cancel-project-edit': toggleProjectEdit(id); break;
174
- case 'add-item': addRoadmapItem(id); break;
175
- case 'skip-next': skipNextItem(id); break;
176
- case 'reset-failed': resetAllFailed(id); break;
177
- case 'save-roadmap': saveRoadmap(id); break;
178
- case 'close-editor': closeRoadmapEditor(id); break;
179
- }
180
- });
181
- }
182
-
183
- // ── Chat project dropdown ─────────────────────────────────────────────────────
184
-
185
- const CHAT_ACTIVE_PROJECT_KEY = 'crewswarm_chat_active_project_id';
186
-
187
- export function getStoredChatProjectId() {
188
- try { return localStorage.getItem(CHAT_ACTIVE_PROJECT_KEY) || ''; } catch { return ''; }
189
- }
190
- export function setStoredChatProjectId(id) {
191
- try { if (id) localStorage.setItem(CHAT_ACTIVE_PROJECT_KEY, id); else localStorage.removeItem(CHAT_ACTIVE_PROJECT_KEY); } catch {}
192
- }
193
-
194
- function persistSharedActiveProjectId(id) {
195
- const normalizedId =
196
- id && String(id).trim() && id !== "undefined" ? String(id).trim() : "general";
197
- return fetch("/api/ui/active-project", {
198
- method: "POST",
199
- headers: { "content-type": "application/json" },
200
- body: JSON.stringify({ projectId: normalizedId }),
201
- }).catch(() => {});
202
- }
203
-
204
- export function populateChatProjectDropdown(projects) {
205
- const tabsContainer = document.getElementById('chatProjectTabs');
206
- if (!tabsContainer) return;
207
-
208
- const prev = getStoredChatProjectId() || state.chatActiveProjectId || 'general';
209
-
210
- // Clear existing tabs except General (first child)
211
- while (tabsContainer.children.length > 1) {
212
- tabsContainer.removeChild(tabsContainer.lastChild);
213
- }
214
-
215
- // Deduplicate by ID; ensure every project has a stable id (backend should send it; fallback from name)
216
- const seen = new Set();
217
- const uniqueProjects = (projects || []).filter(p => {
218
- const id = p.id || (p.name && p.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''));
219
- if (!id || seen.has(id)) return false;
220
- seen.add(id);
221
- return true;
222
- }).map(p => ({ ...p, id: p.id || (p.name && p.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')) }));
223
-
224
- // Add project tabs (only projects with a valid id)
225
- uniqueProjects.forEach(p => {
226
- if (!p.id) return;
227
- const tab = document.createElement('button');
228
- tab.className = 'project-tab';
229
- tab.dataset.projectId = p.id;
230
- tab.textContent = `📁 ${p.name || p.id}`;
231
- tab.onclick = () => window.selectProjectTab(p.id);
232
- tabsContainer.appendChild(tab);
233
- });
234
-
235
- console.log('[Projects] Populated tabs:', uniqueProjects.length, 'projects');
236
-
237
- // Restore the active tab styling without forcing navigation back to Chat.
238
- const availableIds = new Set(['general', ...uniqueProjects.map((p) => p.id).filter(Boolean)]);
239
- const activeProjectId = availableIds.has(prev) ? prev : 'general';
240
- state.chatActiveProjectId = activeProjectId;
241
- setStoredChatProjectId(activeProjectId);
242
- persistSharedActiveProjectId(activeProjectId);
243
- Array.from(tabsContainer.children).forEach((tab) => {
244
- tab.classList.toggle('active', tab.dataset.projectId === activeProjectId);
245
- });
246
- }
247
-
248
-
249
- export function onChatProjectChange() {
250
- // Legacy: now handled by tab clicks
251
- const sel = document.getElementById('chatProjectSelect');
252
- if (sel) {
253
- state.chatActiveProjectId = sel.value;
254
- setStoredChatProjectId(state.chatActiveProjectId);
255
- persistSharedActiveProjectId(state.chatActiveProjectId);
256
- updateChatProjectHint();
257
- }
258
- }
259
-
260
- export function updateChatProjectHint() {
261
- const hint = document.getElementById('chatProjectHint');
262
- if (!hint) return;
263
- if (state.chatActiveProjectId && state.projectsData[state.chatActiveProjectId]) {
264
- const p = state.projectsData[state.chatActiveProjectId];
265
- hint.textContent = p.outputDir || '';
266
- hint.style.display = p.outputDir ? 'block' : 'none';
267
- } else {
268
- hint.style.display = 'none';
269
- }
270
- }
271
-
272
- export function autoSelectChatProject(projectId) {
273
- state.chatActiveProjectId = projectId;
274
- setStoredChatProjectId(projectId);
275
- persistSharedActiveProjectId(projectId);
276
- const sel = document.getElementById('chatProjectSelect');
277
- if (sel && sel.querySelector('option[value="' + projectId + '"]')) {
278
- sel.value = projectId;
279
- updateChatProjectHint();
280
- }
281
- }
282
-
283
- // ── PM loop controls ──────────────────────────────────────────────────────────
284
-
285
- export async function resumeProject(projectId) {
286
- try {
287
- const resp = await fetch('/api/pm-loop/start', { method:'POST', headers:{'content-type':'application/json'}, body: JSON.stringify({ projectId }) });
288
- const r = await resp.json();
289
- if (r.alreadyRunning) { showNotification('PM Loop already running (pid ' + r.pid + ')', true); return; }
290
- showNotification('PM Loop started for project ' + projectId + ' (pid ' + r.pid + ')');
291
- setTimeout(loadProjects, 3000);
292
- } catch(e) { showNotification('Failed: ' + e.message, true); }
293
- }
294
-
295
- export async function stopProjectPMLoop(projectId) {
296
- try {
297
- await postJSON('/api/pm-loop/stop', { projectId });
298
- showNotification('Stop signal sent — PM will finish current task then halt.');
299
- const statusEl = document.getElementById('proj-pm-status-' + projectId);
300
- if (statusEl) { statusEl.style.display = 'block'; statusEl.textContent = '⛔ Stopping after current task…'; }
301
- setTimeout(loadProjects, 3000);
302
- } catch(e) { showNotification('Stop failed: ' + e.message, true); }
303
- }
304
-
305
- export async function startProjectPMLoop(projectId) {
306
- const statusEl = document.getElementById('proj-pm-status-' + projectId);
307
- try {
308
- if (statusEl) { statusEl.style.display = 'block'; statusEl.textContent = '⚙ Starting PM Loop…'; }
309
- const r = await postJSON('/api/pm-loop/start', { projectId });
310
- if (r.alreadyRunning) {
311
- showNotification('PM Loop already running (pid ' + r.pid + ')', true);
312
- if (statusEl) statusEl.textContent = '▶ Already running (pid ' + r.pid + ')';
313
- return;
314
- }
315
- showNotification('PM Loop started (pid ' + r.pid + ')');
316
- if (statusEl) statusEl.textContent = '▶ Running (pid ' + r.pid + ') — check Build tab for live log';
317
- setTimeout(loadProjects, 3000);
318
- } catch(e) {
319
- showNotification('Start failed: ' + e.message, true);
320
- if (statusEl) statusEl.style.display = 'none';
321
- }
322
- }
323
-
324
- export async function deleteProject(projectId) {
325
- const proj = state.projectsData[projectId];
326
- const name = proj ? proj.name : projectId;
327
- if (!confirm('Remove "' + name + '" from the dashboard registry?\n\nFiles on disk are NOT deleted.')) return;
328
- try {
329
- await postJSON('/api/projects/delete', { projectId });
330
- showNotification('Project "' + name + '" removed from dashboard.');
331
- loadProjects();
332
- } catch(e) { showNotification('Delete failed: ' + e.message, true); }
333
- }
334
-
335
- export function openProjectInBuild(projectId, deps) {
336
- deps.showBuild();
337
- loadBuildProjectPicker().then(() => {
338
- const sel = document.getElementById('buildProjectPicker');
339
- if (sel) { sel.value = projectId; onBuildProjectChange(); }
340
- });
341
- }
342
-
343
- // ── Build tab project picker ──────────────────────────────────────────────────
344
-
345
- let _buildProjects = {};
346
-
347
- export function getBuildProjectById(projectId) {
348
- return _buildProjects[projectId] || null;
349
- }
350
-
351
- export async function loadBuildProjectPicker() {
352
- try {
353
- const data = await getJSON('/api/projects');
354
- _buildProjects = {};
355
- const sel = document.getElementById('buildProjectPicker');
356
- const cur = sel ? sel.value : '';
357
- if (!sel) return;
358
- sel.innerHTML = '<option value="">— No project (use defaults) —</option>';
359
- (data.projects || []).forEach(p => {
360
- _buildProjects[p.id] = p;
361
- const opt = document.createElement('option');
362
- opt.value = p.id;
363
- opt.textContent = p.name + (p.running ? ' ▶' : '') + ' (' + p.roadmap.pending + ' pending)';
364
- if (p.id === cur) opt.selected = true;
365
- sel.appendChild(opt);
366
- });
367
- onBuildProjectChange();
368
- } catch(e) { /* ignore */ }
369
- }
370
-
371
- export function onBuildProjectChange() {
372
- const sel = document.getElementById('buildProjectPicker');
373
- const info = document.getElementById('buildProjectInfo');
374
- const label = document.getElementById('pmLoopProjectLabel');
375
- const phasedLabel = document.getElementById('phasedProgressLabel');
376
- const proj = _buildProjects[sel ? sel.value : ''];
377
- if (proj) {
378
- info.style.display = 'block';
379
- info.innerHTML =
380
- '<b>' + proj.name + '</b><br>' +
381
- 'Output: ' + proj.outputDir + '<br>' +
382
- 'Roadmap: ' + proj.roadmapFile + '<br>' +
383
- 'Tasks: ' + proj.roadmap.done + ' done · ' + proj.roadmap.pending + ' pending · ' + proj.roadmap.failed + ' failed' +
384
- (proj.running ? '<br><span style="color:var(--purple);">▶ PM Loop is running</span>' : '');
385
- if (label) label.innerHTML =
386
- '<b style="color:var(--accent);">▶ ' + proj.name + '</b>' +
387
- ' &nbsp;·&nbsp; ' + proj.roadmap.done + ' done · ' + proj.roadmap.pending + ' pending' +
388
- (proj.running ? ' &nbsp;<span style="color:var(--green-hi); font-weight:600;">● running</span>' : '');
389
- if (phasedLabel) phasedLabel.textContent = '▶ ' + proj.name;
390
- } else {
391
- info.style.display = 'none';
392
- if (label) label.innerHTML = '← Select a project above';
393
- if (phasedLabel) phasedLabel.textContent = 'All projects (no project selected)';
394
- }
395
- // Reload phased progress with new project filter
396
- loadPhasedProgress();
397
- }
398
-
399
- // ── Stop build ────────────────────────────────────────────────────────────────
400
-
401
- export async function stopBuild() {
402
- try {
403
- await postJSON('/api/build/stop', {});
404
- showNotification('Build stop signal sent');
405
- document.getElementById('stopBuildBtn').style.display = 'none';
406
- document.getElementById('runBuildBtn').style.display = '';
407
- document.getElementById('buildStatus').textContent = '';
408
- } catch(e) { showNotification('Stop failed: ' + e.message, true); }
409
- }
410
-
411
- export async function stopContinuousBuild() {
412
- try {
413
- await postJSON('/api/continuous-build/stop', {});
414
- showNotification('Continuous build stop signal sent');
415
- document.getElementById('stopContinuousBtn').style.display = 'none';
416
- document.getElementById('continuousBuildBtn').style.display = '';
417
- } catch(e) { showNotification('Stop failed: ' + e.message, true); }
418
- }
419
-
420
- export async function retryFailed(roadmapFile) {
421
- if (!confirm('Reset all [!] failed items back to [ ] pending so the PM Loop retries them?')) return;
422
- try {
423
- const r = await postJSON('/api/roadmap/retry-failed', { roadmapFile });
424
- if (r.count === 0) { showNotification('No failed items found in roadmap', true); return; }
425
- showNotification('↩ ' + r.count + ' failed item' + (r.count !== 1 ? 's' : '') + ' reset — click Resume to retry');
426
- await loadProjects();
427
- } catch(e) { showNotification('Retry failed: ' + e.message, true); }
428
- }
429
-
430
- // ── Roadmap editor ────────────────────────────────────────────────────────────
431
-
432
- const _roadmapFiles = {};
433
-
434
- export async function openRoadmapEditor(projectId, roadmapFile) {
435
- _roadmapFiles[projectId] = roadmapFile;
436
- const panel = document.getElementById('rm-editor-' + projectId);
437
- const ta = document.getElementById('rm-ta-' + projectId);
438
- const btn = document.getElementById('roadmap-btn-' + projectId);
439
- if (!panel || !ta) return;
440
- if (panel.style.display !== 'none') { closeRoadmapEditor(projectId); return; }
441
- panel.style.display = 'block';
442
- if (btn) btn.textContent = '📋 Editing…';
443
- ta.value = 'Loading…';
444
- try {
445
- const r = await postJSON('/api/roadmap/read', { roadmapFile });
446
- ta.value = r.content || '';
447
- setRmStatus(projectId, 'Loaded · ' + (r.content || '').split('\n').length + ' lines');
448
- } catch(e) { ta.value = ''; setRmStatus(projectId, 'Error: ' + e.message, true); }
449
- }
450
-
451
- export function closeRoadmapEditor(projectId) {
452
- const panel = document.getElementById('rm-editor-' + projectId);
453
- const btn = document.getElementById('roadmap-btn-' + projectId);
454
- if (panel) panel.style.display = 'none';
455
- if (btn) btn.textContent = '📋 Edit Roadmap';
456
- }
457
-
458
- function setRmStatus(projectId, msg, isErr) {
459
- const el = document.getElementById('rm-status-' + projectId);
460
- if (!el) return;
461
- el.textContent = msg;
462
- el.style.color = isErr ? 'var(--red)' : 'var(--text-2)';
463
- }
464
-
465
- export async function saveRoadmap(projectId) {
466
- const ta = document.getElementById('rm-ta-' + projectId);
467
- const roadmapFile = _roadmapFiles[projectId];
468
- if (!ta || !roadmapFile) return;
469
- try {
470
- await postJSON('/api/roadmap/write', { roadmapFile, content: ta.value });
471
- setRmStatus(projectId, '✓ Saved — ' + new Date().toLocaleTimeString());
472
- showNotification('Roadmap saved');
473
- setTimeout(loadProjects, 800);
474
- } catch(e) { setRmStatus(projectId, 'Save failed: ' + e.message, true); }
475
- }
476
-
477
- export function addRoadmapItem(projectId) {
478
- const ta = document.getElementById('rm-ta-' + projectId);
479
- const input = document.getElementById('rm-add-' + projectId);
480
- if (!ta) return;
481
- const text = (input ? input.value.trim() : '') || 'New task';
482
- if (!text) return;
483
- const line = '- [ ] ' + text;
484
- ta.value = ta.value.trimEnd() + '\n' + line + '\n';
485
- ta.scrollTop = ta.scrollHeight;
486
- if (input) input.value = '';
487
- setRmStatus(projectId, 'Item added — click 💾 Save to persist');
488
- }
489
-
490
- export function skipNextItem(projectId) {
491
- const ta = document.getElementById('rm-ta-' + projectId);
492
- if (!ta) return;
493
- const lines = ta.value.split('\n');
494
- let skipped = false;
495
- for (let i = 0; i < lines.length; i++) {
496
- if (/^- \[ \]/.test(lines[i])) {
497
- lines[i] = lines[i].replace('- [ ]', '- [x]') + ' ✓ skipped';
498
- skipped = true;
499
- break;
500
- }
501
- }
502
- if (skipped) {
503
- ta.value = lines.join('\n');
504
- setRmStatus(projectId, 'Next pending item skipped — click 💾 Save to persist');
505
- } else {
506
- setRmStatus(projectId, 'No pending items to skip');
507
- }
508
- }
509
-
510
- export async function resetAllFailed(projectId) {
511
- const ta = document.getElementById('rm-ta-' + projectId);
512
- if (!ta) return;
513
- const before = (ta.value.match(/\[!\]/g) || []).length;
514
- if (!before) { setRmStatus(projectId, 'No failed items to reset'); return; }
515
- ta.value = ta.value
516
- .split('\n')
517
- .map(l => l.replace(/\[!\]/, '[ ]').replace(/\s+✗\s+\d+:\d+:\d+/g, ''))
518
- .join('\n');
519
- setRmStatus(projectId, before + ' failed item(s) reset — click 💾 Save to persist');
520
- }
521
-
522
- // ── Build tab ─────────────────────────────────────────────────────────────────
523
-
524
- export async function loadPhasedProgress() {
525
- const box = document.getElementById('phasedProgress');
526
- if (!box) return;
527
- const projectId = document.getElementById('buildProjectPicker')?.value || '';
528
- const label = document.getElementById('phasedProgressLabel');
529
- try {
530
- const url = '/api/phased-progress' + (projectId ? '?projectId=' + encodeURIComponent(projectId) : '');
531
- const data = await getJSON(url);
532
- const scopeText = projectId ? 'This project' : 'All projects (no project selected)';
533
- if (label) label.textContent = scopeText;
534
- if (!data.length) {
535
- if (projectId) {
536
- box.innerHTML = '<div style="padding:20px;color:var(--text-3);text-align:center;">No phased builds yet for this project.<br><br>💡 Tip: Click <b>▶ Run Build</b> or <b>🔁 Build Until Done</b> above to start.</div>';
537
- } else {
538
- box.textContent = 'No phased runs yet.';
539
- }
540
- return;
541
- }
542
- box.innerHTML = data.map(e => {
543
- const phase = e.phase || '?';
544
- const agent = e.agent || '?';
545
- const task = (e.task || '').slice(0, 50) + ((e.task || '').length > 50 ? '...' : '');
546
- const status = e.status === 'completed' ? '✅' : '❌';
547
- const dur = e.duration_s != null ? e.duration_s + 's' : '';
548
-
549
- // Add timestamp in human-readable format
550
- let timeStr = '';
551
- if (e.timestamp) {
552
- const d = new Date(e.timestamp);
553
- const hours = d.getHours();
554
- const mins = d.getMinutes().toString().padStart(2, '0');
555
- const ampm = hours >= 12 ? 'PM' : 'AM';
556
- const hrs12 = hours % 12 || 12;
557
- timeStr = `${hrs12}:${mins} ${ampm}`;
558
- }
559
-
560
- // Add project name if showing all projects
561
- const projName = !projectId && e.projectId && _buildProjects[e.projectId] ?
562
- `<span style="color:var(--text-3);font-size:10px;"> · ${_buildProjects[e.projectId].name}</span>` : '';
563
-
564
- return `<div style="margin-bottom:4px;">${status} [${phase}] ${agent}: ${task} ${dur}${timeStr ? ` <span style="color:var(--text-3);font-size:10px;">· ${timeStr}</span>` : ''}${projName}</div>`;
565
- }).join('');
566
- box.scrollTop = box.scrollHeight;
567
- } catch (e) { box.textContent = 'Could not load progress.'; }
568
- }
569
-
570
- export async function runBuild() {
571
- const req = document.getElementById('buildRequirement').value.trim();
572
- if (!req) { showNotification('Enter a requirement', true); return; }
573
- const status = document.getElementById('buildStatus');
574
- const btn = document.getElementById('runBuildBtn');
575
- const stopBtn = document.getElementById('stopBuildBtn');
576
- const projectId = document.getElementById('buildProjectPicker')?.value || '';
577
- try {
578
- status.textContent = 'Starting...';
579
- btn.disabled = true;
580
- const r = await postJSON('/api/build', { requirement: req, projectId });
581
- showNotification('Build started (pid ' + r.pid + '). Watch RT Messages or Phased Progress.');
582
- status.textContent = 'Running (pid ' + r.pid + ')';
583
- btn.style.display = 'none';
584
- if (stopBtn) stopBtn.style.display = '';
585
- setTimeout(() => {
586
- status.textContent = '';
587
- btn.disabled = false;
588
- btn.style.display = '';
589
- if (stopBtn) stopBtn.style.display = 'none';
590
- }, 120000);
591
- } catch (e) { showNotification('Build failed: ' + e.message, true); status.textContent = ''; btn.disabled = false; }
592
- }
593
-
594
- export async function enhancePrompt() {
595
- const ta = document.getElementById('buildRequirement');
596
- const raw = ta.value.trim();
597
- const btn = document.getElementById('enhancePromptBtn');
598
- if (!raw) { showNotification('Type an idea first', true); return; }
599
- try {
600
- btn.disabled = true;
601
- document.getElementById('buildStatus').textContent = 'Enhancing...';
602
- const r = await postJSON('/api/enhance-prompt', { text: raw });
603
- if (r.enhanced) { ta.value = r.enhanced; showNotification('Prompt updated'); }
604
- else { showNotification(r.error || 'No result', true); }
605
- } catch (e) { showNotification('Enhance failed: ' + e.message, true); }
606
- finally { btn.disabled = false; document.getElementById('buildStatus').textContent = ''; }
607
- }
608
-
609
- export async function continuousBuildRun() {
610
- const req = document.getElementById('buildRequirement').value.trim();
611
- if (!req) { showNotification('Enter a requirement first', true); return; }
612
- const status = document.getElementById('buildStatus');
613
- const btn = document.getElementById('continuousBuildBtn');
614
- const stopBtn = document.getElementById('stopContinuousBtn');
615
- const logBox = document.getElementById('buildLiveLog');
616
- const projectId = document.getElementById('buildProjectPicker')?.value || '';
617
- try {
618
- status.textContent = 'Running continuously...';
619
- btn.disabled = true;
620
- btn.style.display = 'none';
621
- if (stopBtn) stopBtn.style.display = '';
622
- logBox.style.display = 'block';
623
- logBox.textContent = '⚙ Starting continuous build...\n';
624
- const r = await postJSON('/api/continuous-build', { requirement: req, projectId });
625
- logBox.textContent += '✅ Spawned (pid ' + r.pid + '). Checking progress below and in RT Messages tab.\n';
626
- showNotification('Continuous build started — will keep going until all sections are done.');
627
- status.textContent = 'Running (continuous)';
628
- const poller = setInterval(async () => {
629
- try {
630
- const lg = await fetch('/api/continuous-build/log').then(r2 => r2.json());
631
- if (lg.lines && lg.lines.length) {
632
- logBox.textContent = lg.lines.map(l => {
633
- const icon = l.status === 'completed' ? '✅' : l.status === 'failed' ? '❌' : l.status === 'done' ? '🏁' : '·';
634
- return `${icon} [rd${l.round||'?'}] ${l.agent ? l.agent+': ' : ''}${l.task || l.status || JSON.stringify(l)}`;
635
- }).join('\n');
636
- logBox.scrollTop = logBox.scrollHeight;
637
- const last = lg.lines[lg.lines.length - 1];
638
- if (last && last.status === 'done') {
639
- clearInterval(poller);
640
- btn.disabled = false;
641
- btn.style.display = '';
642
- if (stopBtn) stopBtn.style.display = 'none';
643
- status.textContent = '🏁 Done!';
644
- showNotification('🏁 Continuous build complete!');
645
- }
646
- }
647
- } catch(_){}
648
- }, 4000);
649
- setTimeout(() => {
650
- clearInterval(poller);
651
- btn.disabled = false;
652
- btn.style.display = '';
653
- if (stopBtn) stopBtn.style.display = 'none';
654
- if (status.textContent.includes('continuous')) status.textContent = '';
655
- }, 30 * 60 * 1000);
656
- } catch (e) {
657
- showNotification('Continuous build failed: ' + e.message, true);
658
- status.textContent = '';
659
- btn.disabled = false;
660
- btn.style.display = '';
661
- if (stopBtn) stopBtn.style.display = 'none';
662
- }
663
- }