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.
- package/README.md +22 -9
- package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js +1 -0
- package/apps/dashboard/dist/assets/chat-core-uXb_C0GM.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js +1 -0
- package/apps/dashboard/dist/assets/cli-process-CNZ_UBCt.js.br +0 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js +2 -0
- package/apps/dashboard/dist/assets/index-BeVllEj_.js.br +0 -0
- package/apps/dashboard/dist/assets/{index-CF0aJRtC.css → index-D-sRshvg.css} +1 -1
- package/apps/dashboard/dist/assets/index-D-sRshvg.css.br +0 -0
- package/apps/dashboard/dist/assets/tab-benchmarks-tab-BHjKCPm3.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js +1 -0
- package/apps/dashboard/dist/assets/tab-models-tab-dNRgsTOO.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-pm-loop-tab-Bfd449B4.js → tab-pm-loop-tab-DiAPTJXu.js} +1 -1
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-DiAPTJXu.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-projects-tab-DhNWnlzt.js → tab-projects-tab-SFH4E--a.js} +1 -1
- package/apps/dashboard/dist/assets/tab-projects-tab-SFH4E--a.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js +1 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-CuvH_Fj_.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js +1 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-DR7PJ7NB.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js +1 -0
- package/apps/dashboard/dist/assets/tab-testing-tab-CezZOZcJ.js.br +0 -0
- package/apps/dashboard/dist/index.html +135 -15
- package/apps/dashboard/dist/index.html.br +0 -0
- package/apps/dashboard/dist/index.html.gz +0 -0
- package/apps/vibe/README.md +2 -2
- package/apps/vibe/package.json +1 -1
- package/apps/vibe/server.mjs +101 -56
- package/crew-lead.mjs +34 -4
- package/lib/bridges/cli-executor.mjs +1 -1
- package/lib/bridges/gateway-ws.mjs +4 -0
- package/lib/browser/passthrough-stderr.js +1 -0
- package/lib/chat/project-messages.mjs +3 -5
- package/lib/cli-process-tracker.mjs +3 -2
- package/lib/contacts/identity-linker.mjs +1 -0
- package/lib/crew-judge/judge.mjs +19 -18
- package/lib/crew-lead/agent-manager.mjs +1 -1
- package/lib/crew-lead/background.mjs +14 -1
- package/lib/crew-lead/chat-handler.mjs +38 -1
- package/lib/crew-lead/http-server.mjs +106 -57
- package/lib/crew-lead/llm-caller.mjs +24 -8
- package/lib/crew-lead/prompts.mjs +14 -1
- package/lib/crew-lead/tools.mjs +3 -2
- package/lib/crew-lead/wave-dispatcher.mjs +19 -5
- package/lib/crew-lead/ws-router.mjs +219 -27
- package/lib/engines/crew-cli.mjs +1 -1
- package/lib/engines/engine-registry.mjs +14 -3
- package/lib/engines/rt-envelope.mjs +1 -0
- package/lib/engines/runners.mjs +28 -4
- package/lib/gemini-cli-passthrough-noise.mjs +1 -1
- package/lib/integrations/code-search.mjs +4 -3
- package/lib/memory/shared-adapter.mjs +23 -10
- package/lib/pipeline/manager.mjs +2 -1
- package/lib/runtime/config.mjs +1 -1
- package/lib/runtime/paths.mjs +12 -8
- package/lib/runtime/spending.mjs +2 -1
- package/package.json +42 -14
- package/scripts/capture-build-flow.mjs +118 -0
- package/scripts/coverage-report.mjs +209 -0
- package/scripts/coverage-summary.mjs +47 -0
- package/scripts/dashboard-validation.mjs +76 -0
- package/scripts/dashboard.mjs +1667 -551
- package/scripts/generate-openapi.mjs +683 -277
- package/scripts/live-bridge-matrix.mjs +79 -0
- package/scripts/live-cli-matrix.mjs +166 -0
- package/scripts/live-crewchat-check.mjs +42 -0
- package/scripts/live-engine-matrix.mjs +50 -0
- package/scripts/live-provider-failover-matrix.mjs +107 -0
- package/scripts/live-provider-matrix.mjs +228 -0
- package/scripts/restart-all-from-repo.sh +4 -4
- package/scripts/restart-service.sh +12 -9
- package/scripts/smoke-dispatch.mjs +4 -1
- package/scripts/test-blast-radius.mjs +204 -0
- package/scripts/test-report-summary.mjs +88 -0
- package/scripts/test-reporter.mjs +651 -0
- package/scripts/test-rerun.mjs +136 -0
- package/scripts/tmux-bridge +130 -0
- package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js +0 -1
- package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js +0 -1
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
- package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
- package/apps/dashboard/dist/assets/index-DnClJ1ee.js +0 -2
- package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js +0 -1
- package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js +0 -1
- package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js +0 -1
- package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
- package/apps/dashboard/index.html +0 -6529
- package/apps/dashboard/package.json +0 -15
- package/apps/dashboard/src/app.js +0 -2828
- package/apps/dashboard/src/app.js.br +0 -0
- package/apps/dashboard/src/app.js.gz +0 -0
- package/apps/dashboard/src/chat/chat-actions.js +0 -1847
- package/apps/dashboard/src/chat/chat-actions.js.br +0 -0
- package/apps/dashboard/src/chat/unified-messages.js +0 -327
- package/apps/dashboard/src/chat/unified-messages.js.br +0 -0
- package/apps/dashboard/src/cli-process.js +0 -208
- package/apps/dashboard/src/cli-process.js.br +0 -0
- package/apps/dashboard/src/cli-process.js.gz +0 -0
- package/apps/dashboard/src/components/active-tasks-panel.js +0 -175
- package/apps/dashboard/src/components/active-tasks-panel.js.br +0 -0
- package/apps/dashboard/src/core/api.js +0 -18
- package/apps/dashboard/src/core/api.js.br +0 -0
- package/apps/dashboard/src/core/dom.js +0 -228
- package/apps/dashboard/src/core/dom.js.br +0 -0
- package/apps/dashboard/src/core/state.js +0 -91
- package/apps/dashboard/src/core/state.js.br +0 -0
- package/apps/dashboard/src/core/task-manager.js +0 -134
- package/apps/dashboard/src/core/task-manager.js.br +0 -0
- package/apps/dashboard/src/orchestration-status.js +0 -127
- package/apps/dashboard/src/orchestration-status.js.br +0 -0
- package/apps/dashboard/src/setup-wizard.js +0 -562
- package/apps/dashboard/src/setup-wizard.js.br +0 -0
- package/apps/dashboard/src/styles.css +0 -2085
- package/apps/dashboard/src/styles.css.br +0 -0
- package/apps/dashboard/src/styles.css.gz +0 -0
- package/apps/dashboard/src/tabs/agents-tab.js +0 -2237
- package/apps/dashboard/src/tabs/agents-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/benchmarks-tab.js +0 -229
- package/apps/dashboard/src/tabs/benchmarks-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/comms-tab.js +0 -955
- package/apps/dashboard/src/tabs/comms-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/contacts-tab.js +0 -654
- package/apps/dashboard/src/tabs/contacts-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/engines-tab.js +0 -175
- package/apps/dashboard/src/tabs/engines-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/memory-tab.js +0 -182
- package/apps/dashboard/src/tabs/memory-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/models-tab.js +0 -450
- package/apps/dashboard/src/tabs/models-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/pm-loop-tab.js +0 -185
- package/apps/dashboard/src/tabs/pm-loop-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/projects-tab.js +0 -663
- package/apps/dashboard/src/tabs/projects-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/projects-tab.js.gz +0 -0
- package/apps/dashboard/src/tabs/prompts-tab.js +0 -160
- package/apps/dashboard/src/tabs/prompts-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/services-tab.js +0 -202
- package/apps/dashboard/src/tabs/services-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/settings-tab.js +0 -861
- package/apps/dashboard/src/tabs/settings-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/skills-tab.js +0 -284
- package/apps/dashboard/src/tabs/skills-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/spending-tab.js +0 -173
- package/apps/dashboard/src/tabs/spending-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/swarm-chat-tab.js +0 -660
- package/apps/dashboard/src/tabs/swarm-chat-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/swarm-tab.js +0 -538
- package/apps/dashboard/src/tabs/swarm-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/usage-tab.js +0 -390
- package/apps/dashboard/src/tabs/usage-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/waves-tab.js +0 -238
- package/apps/dashboard/src/tabs/waves-tab.js.br +0 -0
- package/apps/dashboard/src/tabs/workflows-tab.js +0 -747
- package/apps/dashboard/src/tabs/workflows-tab.js.br +0 -0
- package/apps/vibe/.crew/agent-memory/pipeline.json +0 -304
- package/apps/vibe/.crew/cost.json +0 -17
- package/apps/vibe/.crew/json-parse-metrics.jsonl +0 -27
- package/apps/vibe/.crew/pipeline-metrics.jsonl +0 -27
- package/apps/vibe/.crew/pipeline-runs/pipeline-0f90c392-2425-4ae5-850c-bd9d17b1d690.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-1c269dd9-a63f-4fba-af81-5cf08048ef06.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-288a7765-da24-4a22-89bc-1f3cc9b0562c.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-2c78fd22-a657-4bd1-bc49-0679fb384409.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-3e6fe08d-3264-404a-8df3-aab7efef10e7.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-42eec610-57fe-4e09-9e7e-b315038495c2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-4438eb4c-ae13-42b1-90e2-b043d8983be8.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-4740a9f5-86e7-44b6-a394-de433e291727.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-49e1da6a-957e-48fd-9220-415019e4f8e2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-4c9251db-be68-427b-a3fc-a264f2b5778d.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-65e29a57-664d-4196-8109-017e364f182e.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-6aa04bc5-9593-4b1f-b58d-3bf2978cb602.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-6e1cba53-9b70-457e-99e0-59199149dd21.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-749f41cc-4dac-4204-be64-873a6080a0d2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-74d68121-e181-4864-bd9a-c3211341dfaf.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-8509bc24-142d-4e07-b44a-a50bf99d1103.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-960339c6-07ca-43ce-9900-f6e1702b39b9.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-9c6480a9-7031-4146-b241-825b9a2d1de1.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-9fd42426-8492-4157-9d5f-e1537c060489.jsonl +0 -2
- package/apps/vibe/.crew/pipeline-runs/pipeline-ad6d40a3-2f5e-46a9-a345-47caaccc51aa.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-bc606133-8d5b-4535-8d85-f1a29cdaa981.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-c1a13ccd-634a-4d01-a4a7-1177b8a752ff.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-c7d27b42-249e-4bd4-8f26-6aa998110b8a.jsonl +0 -5
- package/apps/vibe/.crew/pipeline-runs/pipeline-cca2e9b9-4a34-4d25-a311-5c793fa7e91e.jsonl +0 -5
- package/apps/vibe/.crew/sandbox.json +0 -7
- package/apps/vibe/.crew/session.json +0 -330
- package/apps/vibe/.crew/training-data.jsonl +0 -0
- package/apps/vibe/.github/workflows/studio-quality.yml +0 -37
- package/apps/vibe/.studio-data/project-messages/chuck-norris.jsonl +0 -18
- package/apps/vibe/.studio-data/project-messages/general.jsonl +0 -81
- package/apps/vibe/.studio-data/project-messages/studio-local.jsonl +0 -18
- package/apps/vibe/ARCHITECTURE.md +0 -3393
- package/apps/vibe/QUICK-REFERENCE.md +0 -211
- package/apps/vibe/ROADMAP.md +0 -41
- package/apps/vibe/STUDIO-SETUP-COMPLETE.md +0 -35
- package/apps/vibe/VISUAL-GUIDE.md +0 -378
- package/apps/vibe/capture-demo.mjs +0 -160
- package/apps/vibe/capture-full-demo.mjs +0 -255
- package/apps/vibe/capture-quickstart.mjs +0 -256
- package/apps/vibe/capture-vibe-assets.mjs +0 -71
- package/apps/vibe/capture-vibe-video.mjs +0 -260
- package/apps/vibe/check-buttons.js +0 -41
- package/apps/vibe/diagnose.html +0 -106
- package/apps/vibe/fix-buttons.js +0 -103
- package/apps/vibe/index.html +0 -3404
- package/apps/vibe/package-lock.json +0 -920
- package/apps/vibe/scripts/studio-pty-host.py +0 -117
- package/apps/vibe/src/main.js +0 -2940
- package/apps/vibe/src/register-all-languages.js +0 -98
- package/apps/vibe/start-studio.sh +0 -11
- package/apps/vibe/test/accessibility-tests.js +0 -77
- package/apps/vibe/test/browser-performance-audit.mjs +0 -205
- package/apps/vibe/test/performance-tests.js +0 -120
- package/apps/vibe/test/security-tests.js +0 -213
- package/apps/vibe/tests/e2e.local.mjs +0 -54
- package/apps/vibe/tests/server.smoke.mjs +0 -106
- package/apps/vibe/update_website.mjs +0 -74
- package/apps/vibe/vite.config.js +0 -19
- package/lib/crew-lead/chat-handler.mjs.bak +0 -1274
- 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 "+ New Project" 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
|
-
' · ' + proj.roadmap.done + ' done · ' + proj.roadmap.pending + ' pending' +
|
|
388
|
-
(proj.running ? ' <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
|
-
}
|
|
Binary file
|