crewswarm 0.9.0 → 0.9.2
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 +2 -2
- package/apps/dashboard/dist/assets/{chat-core-CMoqlR6D.js → chat-core-Cx4sTxDd.js} +1 -1
- package/apps/dashboard/dist/assets/chat-core-Cx4sTxDd.js.br +0 -0
- package/apps/dashboard/dist/assets/cli-process-COMRNPqr.js.br +0 -0
- package/apps/dashboard/dist/assets/{components-CSUb80ze.js → components-BS9fQjE_.js} +1 -1
- package/apps/dashboard/dist/assets/components-BS9fQjE_.js.br +0 -0
- package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js +1 -0
- package/apps/dashboard/dist/assets/core-utils-CmOkXgzi.js.br +0 -0
- package/apps/dashboard/dist/assets/index-CF0aJRtC.css.br +0 -0
- package/apps/dashboard/dist/assets/{index-DqVVQLTW.js → index-DnClJ1ee.js} +2 -2
- package/apps/dashboard/dist/assets/index-DnClJ1ee.js.br +0 -0
- package/apps/dashboard/dist/assets/orchestration-Ca2DLWN-.js.br +0 -0
- package/apps/dashboard/dist/assets/{setup-wizard-D4g5DMhW.js → setup-wizard-CA0Or47w.js} +1 -1
- package/apps/dashboard/dist/assets/setup-wizard-CA0Or47w.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-agents-tab-BThdsdJY.js → tab-agents-tab-BgpIsjkw.js} +1 -1
- package/apps/dashboard/dist/assets/tab-agents-tab-BgpIsjkw.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-benchmarks-tab-DfCuAClu.js → tab-benchmarks-tab-BHjKCPm3.js} +1 -1
- package/apps/dashboard/dist/assets/{tab-comms-tab-eHpOSBhG.js → tab-comms-tab-kguqTIzD.js} +1 -1
- package/apps/dashboard/dist/assets/tab-comms-tab-kguqTIzD.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-contacts-tab-5LHSthJM.js → tab-contacts-tab-DiOyMYth.js} +1 -1
- package/apps/dashboard/dist/assets/tab-contacts-tab-DiOyMYth.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-engines-tab-C3DYxTwy.js → tab-engines-tab-BsdZVvU0.js} +1 -1
- package/apps/dashboard/dist/assets/tab-engines-tab-BsdZVvU0.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-memory-tab-C59BYFQD.js → tab-memory-tab-Cu6u13EQ.js} +1 -1
- package/apps/dashboard/dist/assets/tab-memory-tab-Cu6u13EQ.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-models-tab-CQzvaeVh.js → tab-models-tab-BLEjmd19.js} +1 -1
- package/apps/dashboard/dist/assets/tab-models-tab-BLEjmd19.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-pm-loop-tab-D7mnDelU.js → tab-pm-loop-tab-Bfd449B4.js} +1 -1
- package/apps/dashboard/dist/assets/tab-pm-loop-tab-Bfd449B4.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-projects-tab-C6h2Mv1K.js → tab-projects-tab-DhNWnlzt.js} +1 -1
- package/apps/dashboard/dist/assets/tab-projects-tab-DhNWnlzt.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-prompts-tab-C0wZvWK3.js → tab-prompts-tab-DVkUNaJd.js} +1 -1
- package/apps/dashboard/dist/assets/tab-prompts-tab-DVkUNaJd.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-services-tab-DBj_w3bc.js → tab-services-tab-DU_LH3uG.js} +1 -1
- package/apps/dashboard/dist/assets/tab-services-tab-DU_LH3uG.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-settings-tab-ezeqAjZk.js → tab-settings-tab-Bn4nXtDe.js} +1 -1
- package/apps/dashboard/dist/assets/tab-settings-tab-Bn4nXtDe.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-skills-tab-BYdU2whk.js → tab-skills-tab-BpY0uZHW.js} +1 -1
- package/apps/dashboard/dist/assets/tab-skills-tab-BpY0uZHW.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-spending-tab-Bg6w9t_p.js → tab-spending-tab-DEccQHnt.js} +1 -1
- package/apps/dashboard/dist/assets/tab-spending-tab-DEccQHnt.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-swarm-chat-tab-BBV9HB2X.js → tab-swarm-chat-tab-BNrd88-r.js} +1 -1
- package/apps/dashboard/dist/assets/tab-swarm-chat-tab-BNrd88-r.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-swarm-tab-ChqLlEVs.js → tab-swarm-tab-B1AcjL1W.js} +1 -1
- package/apps/dashboard/dist/assets/tab-swarm-tab-B1AcjL1W.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-usage-tab-B2UWXenJ.js → tab-usage-tab-BIOOnB-Y.js} +1 -1
- package/apps/dashboard/dist/assets/tab-usage-tab-BIOOnB-Y.js.br +0 -0
- package/apps/dashboard/dist/assets/tab-waves-tab-SaJDkb4x.js.br +0 -0
- package/apps/dashboard/dist/assets/{tab-workflows-tab-6QSXLJ0i.js → tab-workflows-tab-B-soSy1k.js} +1 -1
- package/apps/dashboard/dist/assets/tab-workflows-tab-B-soSy1k.js.br +0 -0
- package/apps/dashboard/dist/index.html +23 -23
- package/apps/dashboard/dist/index.html.br +0 -0
- package/apps/dashboard/dist/index.html.gz +0 -0
- package/apps/dashboard/index.html +71 -1
- package/apps/dashboard/src/app.js +5 -0
- package/apps/dashboard/src/core/dom.js +8 -0
- package/apps/dashboard/src/tabs/settings-tab.js +58 -0
- package/apps/vibe/.crew/agent-memory/pipeline.json +12 -1
- package/apps/vibe/.crew/cost.json +3 -3
- package/apps/vibe/.crew/json-parse-metrics.jsonl +1 -0
- package/apps/vibe/.crew/pipeline-metrics.jsonl +1 -0
- package/apps/vibe/.crew/pipeline-runs/pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2.jsonl +5 -0
- package/apps/vibe/.crew/session.json +10 -1
- package/apps/vibe/.studio-data/project-messages/general.jsonl +3 -0
- package/apps/vibe/index.html +4 -2
- package/apps/vibe/server.mjs +75 -3
- package/apps/vibe/src/main.js +126 -53
- package/crew-lead.mjs +14 -1
- package/lib/bridges/cli-executor.mjs +0 -2
- package/lib/bridges/tmux-bridge.mjs +200 -0
- package/lib/chat/unified-history.mjs +1 -1
- package/lib/cli-process-tracker.mjs +2 -1
- package/lib/crew-lead/http-server.mjs +286 -1
- package/lib/crew-lead/wave-dispatcher.mjs +40 -3
- package/lib/engines/crew-cli.mjs +3 -2
- package/lib/engines/llm-direct.mjs +4 -1
- package/lib/engines/rt-envelope.mjs +14 -5
- package/lib/engines/runners.mjs +30 -4
- package/lib/runtime/config.mjs +7 -0
- package/lib/sessions/session-manager.mjs +287 -0
- package/package.json +1 -1
- package/scripts/bench/performance_optimization.py +81 -0
- package/whatsapp-bridge.mjs +54 -10
- package/apps/dashboard/dist/assets/core-utils-CAVnDoe1.js +0 -1
|
@@ -286,8 +286,19 @@
|
|
|
286
286
|
"pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40"
|
|
287
287
|
],
|
|
288
288
|
"provider": "pipeline"
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"id": "756e1232-e81e-474a-b87d-86e5c7a9bfd1",
|
|
292
|
+
"content": "L2 Decision: direct-answer - User message is a simple greeting 'hi', which explicitly qualifies for DIRECT-ANSWER per constraints",
|
|
293
|
+
"critical": true,
|
|
294
|
+
"timestamp": "2026-03-26T17:46:37.435Z",
|
|
295
|
+
"tags": [
|
|
296
|
+
"l2-decision",
|
|
297
|
+
"pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2"
|
|
298
|
+
],
|
|
299
|
+
"provider": "pipeline"
|
|
289
300
|
}
|
|
290
301
|
],
|
|
291
302
|
"createdAt": "2026-03-24T20:43:06.375Z",
|
|
292
|
-
"updatedAt": "2026-03-
|
|
303
|
+
"updatedAt": "2026-03-26T17:46:37.435Z"
|
|
293
304
|
}
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"byModel": {},
|
|
4
4
|
"entries": [],
|
|
5
5
|
"cacheSavings": {
|
|
6
|
-
"hits":
|
|
6
|
+
"hits": 35,
|
|
7
7
|
"misses": 0,
|
|
8
|
-
"tokensSaved":
|
|
9
|
-
"usdSaved": 0.
|
|
8
|
+
"tokensSaved": 11200,
|
|
9
|
+
"usdSaved": 0.028000000000000008
|
|
10
10
|
},
|
|
11
11
|
"memoryMetrics": {
|
|
12
12
|
"recallUsed": 0,
|
|
@@ -24,3 +24,4 @@
|
|
|
24
24
|
{"ts":"2026-03-26T06:59:43.845Z","label":"L2 router (pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c)","attempt":1,"success":true,"repaired":false,"traceId":"pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c"}
|
|
25
25
|
{"ts":"2026-03-26T07:44:08.921Z","label":"L2 router (pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842)","attempt":1,"success":true,"repaired":false,"traceId":"pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842"}
|
|
26
26
|
{"ts":"2026-03-26T07:46:58.171Z","label":"L2 router (pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40)","attempt":1,"success":true,"repaired":false,"traceId":"pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40"}
|
|
27
|
+
{"ts":"2026-03-26T17:46:37.432Z","label":"L2 router (pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2)","attempt":1,"success":true,"repaired":false,"traceId":"pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2"}
|
|
@@ -24,3 +24,4 @@
|
|
|
24
24
|
{"ts":"2026-03-26T06:59:46.721Z","traceId":"pipeline-3da23550-22ed-4904-9a0a-8e79c1f3024c","decision":"execute-direct","qaEnabled":false,"qaApproved":true,"qaRounds":0,"contextChunksUsed":0,"contextCharsSaved":0,"totalCost":0.0007695,"executionPath":["l1-interface","l2-orchestrator","l3-executor-direct"]}
|
|
25
25
|
{"ts":"2026-03-26T07:44:12.132Z","traceId":"pipeline-6413fa33-a802-4b57-a8c0-a9056ad67842","decision":"execute-direct","qaEnabled":false,"qaApproved":true,"qaRounds":0,"contextChunksUsed":0,"contextCharsSaved":0,"totalCost":0.0008346,"executionPath":["l1-interface","l2-orchestrator","l3-executor-direct"]}
|
|
26
26
|
{"ts":"2026-03-26T07:46:58.176Z","traceId":"pipeline-9bef2dd2-6122-42e5-b3d9-19f4d80f9e40","decision":"direct-answer","qaEnabled":false,"qaApproved":true,"qaRounds":0,"contextChunksUsed":0,"contextCharsSaved":0,"totalCost":0.0001,"executionPath":["l1-interface","l2-orchestrator","l2-direct-response"]}
|
|
27
|
+
{"ts":"2026-03-26T17:46:37.437Z","traceId":"pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2","decision":"direct-answer","qaEnabled":false,"qaApproved":true,"qaRounds":0,"contextChunksUsed":0,"contextCharsSaved":0,"totalCost":0.0001,"executionPath":["l1-interface","l2-orchestrator","l2-direct-response"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{"ts":"2026-03-26T17:46:33.521Z","phase":"plan","userInput":"[Currently open file: /Users/jeffhobbs/CrewSwarm/apps/vibe/index.html]\n\nhi","sessionId":"a1d73702-1c81-42f8-810e-d8fb5982d103"}
|
|
2
|
+
{"ts":"2026-03-26T17:46:37.433Z","phase":"plan.completed","decision":"direct-answer","plan":{"decision":"direct-answer","reasoning":"User message is a simple greeting 'hi', which explicitly qualifies for DIRECT-ANSWER per constraints","directResponse":"Hi.","traceId":"pipeline-c1418f4e-b773-4ca1-84a3-216acf36e2f2"}}
|
|
3
|
+
{"ts":"2026-03-26T17:46:37.435Z","phase":"execute","decision":"direct-answer"}
|
|
4
|
+
{"ts":"2026-03-26T17:46:37.436Z","phase":"validate.input","response":"Hi."}
|
|
5
|
+
{"ts":"2026-03-26T17:46:37.436Z","phase":"complete","decision":"direct-answer","totalCost":0.0001,"durationMs":3915,"qaApproved":true}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sessionId": "a1d73702-1c81-42f8-810e-d8fb5982d103",
|
|
3
3
|
"createdAt": "2026-03-24T20:39:24.089Z",
|
|
4
|
-
"updatedAt": "2026-03-
|
|
4
|
+
"updatedAt": "2026-03-26T17:46:37.438Z",
|
|
5
5
|
"history": [
|
|
6
6
|
{
|
|
7
7
|
"input": "hi",
|
|
@@ -316,6 +316,15 @@
|
|
|
316
316
|
"model": "unknown",
|
|
317
317
|
"costUsd": 0.0001,
|
|
318
318
|
"timestamp": "2026-03-26T07:46:58.178Z"
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"input": "[Currently open file: /Users/jeffhobbs/CrewSwarm/apps/vibe/index.html]\n\nhi",
|
|
322
|
+
"response": "Hi.",
|
|
323
|
+
"decision": "direct-answer",
|
|
324
|
+
"agent": "unified-pipeline",
|
|
325
|
+
"model": "unknown",
|
|
326
|
+
"costUsd": 0.0001,
|
|
327
|
+
"timestamp": "2026-03-26T17:46:37.438Z"
|
|
319
328
|
}
|
|
320
329
|
]
|
|
321
330
|
}
|
|
@@ -76,3 +76,6 @@
|
|
|
76
76
|
{"role":"user","content":"Vibe turns my local machine into a high-fidelity command center. I’m no longer just writing code; I’m orchestrating a crew in real-time with zero latency and a gorgeous UI that makes shipping feel effortless.\" Gemini","ts":1774516924358,"source":"cli","metadata":{"engine":"gemini","agentName":"You","agentEmoji":"👤"}}
|
|
77
77
|
{"role":"assistant","content":"✦ **Gemini**\n\"Local files, global crew: the high-fidelity surface for shipping with a multi-agent pulse.\"","ts":1774516954600,"source":"cli","agent":"gemini","metadata":{"engine":"gemini","exitCode":0,"agentName":"gemini","agentEmoji":"⚡"}}
|
|
78
78
|
{"role":"assistant","content":"\"The local-first command center for orchestrating your AI crew with zero latency and high-fidelity flow.\" — Gemini","ts":1774516964996,"source":"cli","agent":"gemini","metadata":{"engine":"gemini","exitCode":0,"agentName":"gemini","agentEmoji":"⚡"}}
|
|
79
|
+
{"role":"user","content":"[Currently open file: /Users/jeffhobbs/CrewSwarm/apps/vibe/index.html]\n\nhi","ts":1774547192834,"source":"cli","metadata":{"engine":"crew-cli","agentName":"You","agentEmoji":"👤"}}
|
|
80
|
+
{"role":"assistant","content":"Hi.","ts":1774547197540,"source":"cli","agent":"crew-cli","metadata":{"engine":"crew-cli","exitCode":0,"agentName":"crew-cli","agentEmoji":"⚡"}}
|
|
81
|
+
{"role":"user","content":"[Currently open file: /Users/jeffhobbs/CrewSwarm/apps/vibe/index.html]\n\nhi","ts":1774548205770,"source":"cli","metadata":{"engine":"gemini","agentName":"You","agentEmoji":"👤"}}
|
package/apps/vibe/index.html
CHANGED
|
@@ -2452,9 +2452,11 @@
|
|
|
2452
2452
|
<div class="diff-preview-render" id="diff-preview-render"></div>
|
|
2453
2453
|
</div>
|
|
2454
2454
|
</div>
|
|
2455
|
-
<div class="actions">
|
|
2455
|
+
<div class="actions" style="display:flex;align-items:center;gap:8px;">
|
|
2456
2456
|
<button class="secondary" onclick="rejectDiff()">Dismiss</button>
|
|
2457
|
-
<button onclick="acceptDiff()">
|
|
2457
|
+
<button onclick="acceptDiff()">Accept</button>
|
|
2458
|
+
<button id="diff-accept-all-btn" onclick="acceptAllDiffs()" style="display:none;background:#10b981;">Accept All</button>
|
|
2459
|
+
<span id="diff-queue-counter" style="display:none;font-size:11px;color:var(--text-3);margin-left:4px;"></span>
|
|
2458
2460
|
</div>
|
|
2459
2461
|
</div>
|
|
2460
2462
|
</div>
|
package/apps/vibe/server.mjs
CHANGED
|
@@ -887,7 +887,61 @@ function createDefaultCliRelay(onChunk) {
|
|
|
887
887
|
};
|
|
888
888
|
}
|
|
889
889
|
|
|
890
|
+
function createClaudeStreamRelay(onChunk) {
|
|
891
|
+
let buffer = "";
|
|
892
|
+
let transcript = "";
|
|
893
|
+
|
|
894
|
+
return {
|
|
895
|
+
push(chunk) {
|
|
896
|
+
buffer += chunk.toString("utf8");
|
|
897
|
+
const lines = buffer.split("\n");
|
|
898
|
+
buffer = lines.pop() || "";
|
|
899
|
+
for (const line of lines) {
|
|
900
|
+
const trimmed = line.trim();
|
|
901
|
+
if (!trimmed) continue;
|
|
902
|
+
try {
|
|
903
|
+
const ev = JSON.parse(trimmed);
|
|
904
|
+
// Stream events with text deltas
|
|
905
|
+
if (ev.type === "stream_event") {
|
|
906
|
+
const inner = ev.event;
|
|
907
|
+
if (inner?.type === "content_block_delta" && inner?.delta?.type === "text_delta") {
|
|
908
|
+
const text = inner.delta.text || "";
|
|
909
|
+
transcript += text;
|
|
910
|
+
onChunk?.(text);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
// Final assistant message
|
|
914
|
+
if (ev.type === "assistant") {
|
|
915
|
+
const content = ev.message?.content;
|
|
916
|
+
if (Array.isArray(content)) {
|
|
917
|
+
for (const c of content) {
|
|
918
|
+
if (c.type === "text" && c.text) {
|
|
919
|
+
transcript += c.text;
|
|
920
|
+
onChunk?.(c.text);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
// Result event
|
|
926
|
+
if (ev.type === "result" && ev.result) {
|
|
927
|
+
if (!transcript) {
|
|
928
|
+
transcript = ev.result;
|
|
929
|
+
onChunk?.(ev.result);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
} catch { /* not JSON, skip */ }
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
finish() {
|
|
936
|
+
return transcript.trim();
|
|
937
|
+
},
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
|
|
890
941
|
function createCliRelay(engine, onChunk, onDone) {
|
|
942
|
+
if (engine === "claude") {
|
|
943
|
+
return createClaudeStreamRelay(onChunk);
|
|
944
|
+
}
|
|
891
945
|
if (engine === "cursor") {
|
|
892
946
|
return createCursorStreamRelay(onChunk, onDone);
|
|
893
947
|
}
|
|
@@ -1044,17 +1098,18 @@ export function getCliCommand(engine, projectDir, message, modelOverride, resume
|
|
|
1044
1098
|
case "claude":
|
|
1045
1099
|
// Claude Code uses OAuth — no API key needed
|
|
1046
1100
|
{
|
|
1047
|
-
const args = ["-p", "--setting-sources", "user"];
|
|
1101
|
+
const args = ["-p", "--setting-sources", "user", "--output-format", "stream-json", "--verbose"];
|
|
1048
1102
|
const model = modelOverride || process.env.CREWSWARM_CLAUDE_CODE_MODEL || "";
|
|
1049
1103
|
// Add workspace directory context
|
|
1050
1104
|
if (projectDir) args.push("--add-dir", projectDir);
|
|
1051
1105
|
if (model) args.push("--model", model);
|
|
1052
1106
|
// Resume: claude supports --resume <sessionId> for conversation continuity
|
|
1053
1107
|
if (resumeSession?.sessionId) args.push("--resume", resumeSession.sessionId);
|
|
1108
|
+
args.push(message);
|
|
1054
1109
|
return {
|
|
1055
1110
|
command: "claude",
|
|
1056
1111
|
args,
|
|
1057
|
-
stdin:
|
|
1112
|
+
stdin: null,
|
|
1058
1113
|
stripEnv: ["CLAUDECODE", "CLAUDE_CODE"],
|
|
1059
1114
|
};
|
|
1060
1115
|
}
|
|
@@ -1314,8 +1369,25 @@ function handleCliChatLocally(req, res, body) {
|
|
|
1314
1369
|
}
|
|
1315
1370
|
},
|
|
1316
1371
|
})
|
|
1317
|
-
.then(({ exitCode, transcript }) => {
|
|
1372
|
+
.then(async ({ exitCode, transcript }) => {
|
|
1318
1373
|
if (!clientClosed) {
|
|
1374
|
+
// Scan for files changed during CLI execution and notify frontend for diff preview
|
|
1375
|
+
try {
|
|
1376
|
+
const since = Date.now() - 120_000; // Last 2 minutes
|
|
1377
|
+
const { execSync } = await import("node:child_process");
|
|
1378
|
+
const sinceUnix = Math.floor(since / 1000);
|
|
1379
|
+
const changedFiles = execSync(
|
|
1380
|
+
`find "${projectDir}" -maxdepth 5 -type f -newermt "@${sinceUnix}" ! -path "*/node_modules/*" ! -path "*/.git/*" ! -path "*/.crew/*" 2>/dev/null | head -20`,
|
|
1381
|
+
{ encoding: "utf8", timeout: 3000 }
|
|
1382
|
+
).trim().split("\n").filter(Boolean);
|
|
1383
|
+
for (const filePath of changedFiles) {
|
|
1384
|
+
try {
|
|
1385
|
+
const relPath = path.relative(projectDir, filePath);
|
|
1386
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
1387
|
+
sendSseEvent(res, { type: "file-changed", path: relPath, content });
|
|
1388
|
+
} catch { /* binary or unreadable */ }
|
|
1389
|
+
}
|
|
1390
|
+
} catch { /* scan failed, non-fatal */ }
|
|
1319
1391
|
sendSseEvent(res, { type: "done", exitCode, transcript });
|
|
1320
1392
|
res.end();
|
|
1321
1393
|
}
|
package/apps/vibe/src/main.js
CHANGED
|
@@ -37,6 +37,7 @@ let chatMode = "crew-lead"; // 'crew-lead', 'direct', or 'cli'
|
|
|
37
37
|
let selectedAgent = null;
|
|
38
38
|
let ws = null;
|
|
39
39
|
let watchWs = null; // WebSocket for CLI file changes
|
|
40
|
+
let cliTaskActive = false; // True while a CLI engine is working (for diff preview)
|
|
40
41
|
let crewLeadEvents = null;
|
|
41
42
|
let crewLeadEventsReconnectTimer = null;
|
|
42
43
|
let lastAppendedAssistantContent = "";
|
|
@@ -1493,6 +1494,9 @@ async function sendChatMessage() {
|
|
|
1493
1494
|
renderImagePreview();
|
|
1494
1495
|
lastAppendedUserContent = message;
|
|
1495
1496
|
|
|
1497
|
+
// Track CLI task for diff preview
|
|
1498
|
+
cliTaskActive = true;
|
|
1499
|
+
|
|
1496
1500
|
// Typing indicator
|
|
1497
1501
|
const typingDiv = document.createElement("div");
|
|
1498
1502
|
typingDiv.id = "typing-indicator";
|
|
@@ -1658,6 +1662,20 @@ async function sendChatMessage() {
|
|
|
1658
1662
|
updateStreamingChatBubble(bubble, rawTranscript, { pending: true });
|
|
1659
1663
|
}
|
|
1660
1664
|
}
|
|
1665
|
+
} else if (event.type === "file-changed" && event.path && event.content) {
|
|
1666
|
+
// CLI engine wrote a file — show diff preview
|
|
1667
|
+
const oldContent = (activeTab?.path === event.path) ? activeTab.content : "";
|
|
1668
|
+
if (oldContent !== event.content) {
|
|
1669
|
+
showDiffPreview({ path: event.path, newContent: event.content });
|
|
1670
|
+
addTerminalLine(`🔄 ${event.path} changed — diff preview shown`, "info");
|
|
1671
|
+
} else {
|
|
1672
|
+
// Content same or file is new — just refresh
|
|
1673
|
+
if (activeTab?.path === event.path) {
|
|
1674
|
+
activeTab.content = event.content;
|
|
1675
|
+
editor?.setValue(event.content);
|
|
1676
|
+
}
|
|
1677
|
+
scheduleFileTreeRefresh();
|
|
1678
|
+
}
|
|
1661
1679
|
} else if (event.type === "done") {
|
|
1662
1680
|
exitCode = event.exitCode ?? 0;
|
|
1663
1681
|
const stderrTail = stderrFilter.flush();
|
|
@@ -1821,7 +1839,9 @@ async function sendChatMessage() {
|
|
|
1821
1839
|
}
|
|
1822
1840
|
|
|
1823
1841
|
chatMessages.scrollTop = chatMessages.scrollHeight;
|
|
1842
|
+
cliTaskActive = false;
|
|
1824
1843
|
} catch (err) {
|
|
1844
|
+
cliTaskActive = false;
|
|
1825
1845
|
document.getElementById("typing-indicator")?.remove();
|
|
1826
1846
|
// sourceInfo is now accessible here since it's declared outside the try block
|
|
1827
1847
|
const respondingAgent = getErrorBubbleAgentId();
|
|
@@ -2371,44 +2391,42 @@ window.addEventListener("resize", () => {
|
|
|
2371
2391
|
|
|
2372
2392
|
let diffEditor = null;
|
|
2373
2393
|
let pendingChange = null;
|
|
2394
|
+
let diffQueue = []; // Queue of { path, newContent, oldContent } for multi-file diffs
|
|
2374
2395
|
|
|
2375
2396
|
function parseFileChanges(agentReply) {
|
|
2376
|
-
// Parse agent response for @@WRITE_FILE markers
|
|
2377
2397
|
const fileRegex =
|
|
2378
2398
|
/@@WRITE_FILE\s+(.+?)\n([\s\S]+?)(?=@@END_FILE|@@WRITE_FILE|$)/g;
|
|
2379
2399
|
const changes = [];
|
|
2380
|
-
|
|
2381
2400
|
let match;
|
|
2382
2401
|
while ((match = fileRegex.exec(agentReply)) !== null) {
|
|
2383
|
-
changes.push({
|
|
2384
|
-
path: match[1].trim(),
|
|
2385
|
-
newContent: match[2].trim(),
|
|
2386
|
-
});
|
|
2402
|
+
changes.push({ path: match[1].trim(), newContent: match[2].trim() });
|
|
2387
2403
|
}
|
|
2388
|
-
|
|
2389
2404
|
return changes;
|
|
2390
2405
|
}
|
|
2391
2406
|
|
|
2392
2407
|
async function showDiffPreview(change) {
|
|
2408
|
+
// If a diff is already showing, queue this one
|
|
2409
|
+
const overlay = document.getElementById("diff-preview-overlay");
|
|
2410
|
+
if (overlay.classList.contains("visible") && pendingChange) {
|
|
2411
|
+
diffQueue.push(change);
|
|
2412
|
+
updateDiffCounter();
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2393
2416
|
pendingChange = change;
|
|
2394
2417
|
await ensureEditorReady();
|
|
2395
2418
|
await ensureEditorLanguage(detectLanguage(change.path));
|
|
2396
2419
|
|
|
2397
|
-
const overlay = document.getElementById("diff-preview-overlay");
|
|
2398
2420
|
const container = document.getElementById("diff-editor");
|
|
2399
2421
|
const filePathEl = document.getElementById("diff-file-path");
|
|
2400
2422
|
|
|
2401
2423
|
// Get current file content
|
|
2402
|
-
const oldContent =
|
|
2403
|
-
activeTab
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
// Create diff editor
|
|
2408
|
-
if (diffEditor) {
|
|
2409
|
-
diffEditor.dispose();
|
|
2410
|
-
}
|
|
2424
|
+
const oldContent = activeTab?.path === change.path
|
|
2425
|
+
? activeTab.content
|
|
2426
|
+
: await readFile(change.path).catch(() => "");
|
|
2427
|
+
pendingChange.oldContent = oldContent;
|
|
2411
2428
|
|
|
2429
|
+
if (diffEditor) diffEditor.dispose();
|
|
2412
2430
|
diffEditor = monaco.editor.createDiffEditor(container, {
|
|
2413
2431
|
theme: getPreferredMonacoTheme(),
|
|
2414
2432
|
readOnly: false,
|
|
@@ -2416,59 +2434,101 @@ async function showDiffPreview(change) {
|
|
|
2416
2434
|
renderSideBySide: true,
|
|
2417
2435
|
automaticLayout: true,
|
|
2418
2436
|
});
|
|
2419
|
-
|
|
2420
2437
|
diffEditor.setModel({
|
|
2421
|
-
original: monaco.editor.createModel(
|
|
2422
|
-
|
|
2423
|
-
detectLanguage(change.path),
|
|
2424
|
-
),
|
|
2425
|
-
modified: monaco.editor.createModel(
|
|
2426
|
-
change.newContent,
|
|
2427
|
-
detectLanguage(change.path),
|
|
2428
|
-
),
|
|
2438
|
+
original: monaco.editor.createModel(oldContent, detectLanguage(change.path)),
|
|
2439
|
+
modified: monaco.editor.createModel(change.newContent, detectLanguage(change.path)),
|
|
2429
2440
|
});
|
|
2430
2441
|
|
|
2431
2442
|
filePathEl.textContent = change.path;
|
|
2432
2443
|
overlay.classList.add("visible");
|
|
2444
|
+
updateDiffCounter();
|
|
2433
2445
|
}
|
|
2434
2446
|
|
|
2435
|
-
|
|
2436
|
-
|
|
2447
|
+
function updateDiffCounter() {
|
|
2448
|
+
const counter = document.getElementById("diff-queue-counter");
|
|
2449
|
+
const acceptAllBtn = document.getElementById("diff-accept-all-btn");
|
|
2450
|
+
const total = diffQueue.length + (pendingChange ? 1 : 0);
|
|
2451
|
+
if (counter) {
|
|
2452
|
+
counter.textContent = total > 1 ? `${total} files changed` : "";
|
|
2453
|
+
counter.style.display = total > 1 ? "inline" : "none";
|
|
2454
|
+
}
|
|
2455
|
+
if (acceptAllBtn) {
|
|
2456
|
+
acceptAllBtn.style.display = total > 1 ? "inline-block" : "none";
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2437
2459
|
|
|
2460
|
+
async function applyOneChange(change) {
|
|
2438
2461
|
try {
|
|
2439
2462
|
await fetchJSON(`${STUDIO_API}/api/studio/file-content`, {
|
|
2440
2463
|
method: "POST",
|
|
2441
|
-
headers: {
|
|
2442
|
-
|
|
2443
|
-
},
|
|
2444
|
-
body: JSON.stringify({
|
|
2445
|
-
path: pendingChange.path,
|
|
2446
|
-
content: pendingChange.newContent,
|
|
2447
|
-
}),
|
|
2464
|
+
headers: { "Content-Type": "application/json" },
|
|
2465
|
+
body: JSON.stringify({ path: change.path, content: change.newContent }),
|
|
2448
2466
|
});
|
|
2449
|
-
|
|
2450
|
-
const openTab = openTabs.find((tab) => tab.path === pendingChange.path);
|
|
2467
|
+
const openTab = openTabs.find((tab) => tab.path === change.path);
|
|
2451
2468
|
if (openTab) {
|
|
2452
|
-
openTab.content =
|
|
2453
|
-
if (activeTab?.path ===
|
|
2454
|
-
editor.setValue(pendingChange.newContent);
|
|
2455
|
-
}
|
|
2469
|
+
openTab.content = change.newContent;
|
|
2470
|
+
if (activeTab?.path === change.path) editor?.setValue(change.newContent);
|
|
2456
2471
|
}
|
|
2472
|
+
addTerminalLine(`✅ Applied ${change.path}`, "success");
|
|
2473
|
+
} catch (err) {
|
|
2474
|
+
addTerminalLine(`❌ Failed to apply ${change.path}: ${err.message}`, "error");
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2457
2477
|
|
|
2458
|
-
|
|
2459
|
-
|
|
2478
|
+
async function showNextDiff() {
|
|
2479
|
+
if (diffQueue.length > 0) {
|
|
2480
|
+
const next = diffQueue.shift();
|
|
2481
|
+
pendingChange = null;
|
|
2482
|
+
await showDiffPreview(next);
|
|
2483
|
+
} else {
|
|
2460
2484
|
closeDiffPreview();
|
|
2461
|
-
|
|
2462
|
-
addTerminalLine(`❌ Failed to apply diff: ${err.message}`, "error");
|
|
2485
|
+
await loadFileTree();
|
|
2463
2486
|
}
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
window.acceptDiff = async function () {
|
|
2490
|
+
if (!pendingChange) return;
|
|
2491
|
+
await applyOneChange(pendingChange);
|
|
2492
|
+
pendingChange = null;
|
|
2493
|
+
await showNextDiff();
|
|
2464
2494
|
};
|
|
2465
2495
|
|
|
2466
|
-
window.
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2496
|
+
window.acceptAllDiffs = async function () {
|
|
2497
|
+
// Accept current + all queued
|
|
2498
|
+
if (pendingChange) {
|
|
2499
|
+
await applyOneChange(pendingChange);
|
|
2500
|
+
pendingChange = null;
|
|
2501
|
+
}
|
|
2502
|
+
for (const change of diffQueue) {
|
|
2503
|
+
await applyOneChange(change);
|
|
2504
|
+
}
|
|
2505
|
+
diffQueue = [];
|
|
2471
2506
|
closeDiffPreview();
|
|
2507
|
+
await loadFileTree();
|
|
2508
|
+
addTerminalLine(`✅ All changes applied`, "success");
|
|
2509
|
+
};
|
|
2510
|
+
|
|
2511
|
+
window.rejectDiff = async function () {
|
|
2512
|
+
if (pendingChange) {
|
|
2513
|
+
const openTab = openTabs.find((tab) => tab.path === pendingChange.path);
|
|
2514
|
+
if (openTab && pendingChange.oldContent !== undefined && openTab.content !== pendingChange.newContent) {
|
|
2515
|
+
try {
|
|
2516
|
+
await fetchJSON(`${STUDIO_API}/api/studio/file-content`, {
|
|
2517
|
+
method: "POST",
|
|
2518
|
+
headers: { "Content-Type": "application/json" },
|
|
2519
|
+
body: JSON.stringify({ path: pendingChange.path, content: pendingChange.oldContent }),
|
|
2520
|
+
});
|
|
2521
|
+
if (activeTab?.path === pendingChange.path) editor?.setValue(pendingChange.oldContent);
|
|
2522
|
+
addTerminalLine(`↩️ Reverted ${pendingChange.path}`, "warning");
|
|
2523
|
+
} catch {
|
|
2524
|
+
addTerminalLine(`⚠️ Could not revert ${pendingChange.path}`, "error");
|
|
2525
|
+
}
|
|
2526
|
+
} else {
|
|
2527
|
+
addTerminalLine(`🗑️ Dismissed ${pendingChange.path}`, "warning");
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
pendingChange = null;
|
|
2531
|
+
await showNextDiff();
|
|
2472
2532
|
};
|
|
2473
2533
|
|
|
2474
2534
|
window.addEventListener("studio-themechange", () => {
|
|
@@ -2595,8 +2655,15 @@ function connectStudioWatch() {
|
|
|
2595
2655
|
// File changed by CLI
|
|
2596
2656
|
addTerminalLine(`🔄 ${msg.path} updated by CLI`, "info");
|
|
2597
2657
|
|
|
2598
|
-
// If file is
|
|
2599
|
-
if (activeTab && activeTab.path === msg.path && msg.content) {
|
|
2658
|
+
// If file is open in editor AND a CLI task is active, show diff preview
|
|
2659
|
+
if (cliTaskActive && activeTab && activeTab.path === msg.path && msg.content) {
|
|
2660
|
+
const oldContent = activeTab.content || "";
|
|
2661
|
+
if (oldContent !== msg.content) {
|
|
2662
|
+
showDiffPreview({ path: msg.path, newContent: msg.content });
|
|
2663
|
+
addTerminalLine(` ↳ Diff preview shown — accept or dismiss`, "info");
|
|
2664
|
+
}
|
|
2665
|
+
} else if (activeTab && activeTab.path === msg.path && msg.content) {
|
|
2666
|
+
// No active task — silently reload as before
|
|
2600
2667
|
activeTab.content = msg.content;
|
|
2601
2668
|
editor?.setValue(msg.content);
|
|
2602
2669
|
addTerminalLine(` ↳ Reloaded in editor`, "success");
|
|
@@ -2605,7 +2672,13 @@ function connectStudioWatch() {
|
|
|
2605
2672
|
// Refresh file tree to show changes
|
|
2606
2673
|
scheduleFileTreeRefresh();
|
|
2607
2674
|
} else if (msg.type === "file-created") {
|
|
2608
|
-
|
|
2675
|
+
// For new files during active CLI task, show diff (old content is empty)
|
|
2676
|
+
if (cliTaskActive && msg.content) {
|
|
2677
|
+
showDiffPreview({ path: msg.path, newContent: msg.content });
|
|
2678
|
+
addTerminalLine(`✨ ${msg.path} created by CLI — diff preview shown`, "success");
|
|
2679
|
+
} else {
|
|
2680
|
+
addTerminalLine(`✨ ${msg.path} created by CLI`, "success");
|
|
2681
|
+
}
|
|
2609
2682
|
scheduleFileTreeRefresh();
|
|
2610
2683
|
} else if (msg.type === "file-deleted") {
|
|
2611
2684
|
addTerminalLine(`🗑️ ${msg.path} deleted by CLI`, "warning");
|
package/crew-lead.mjs
CHANGED
|
@@ -33,8 +33,10 @@ import {
|
|
|
33
33
|
DISPATCH_TIMEOUT_MS,
|
|
34
34
|
DISPATCH_CLAIMED_TIMEOUT_MS,
|
|
35
35
|
loadCursorWavesEnabled,
|
|
36
|
-
loadClaudeCodeEnabled
|
|
36
|
+
loadClaudeCodeEnabled,
|
|
37
|
+
loadTmuxBridgeEnabled
|
|
37
38
|
} from "./lib/runtime/config.mjs";
|
|
39
|
+
import { _reset as resetTmuxBridge } from "./lib/bridges/tmux-bridge.mjs";
|
|
38
40
|
import {
|
|
39
41
|
CREWSWARM_TOOL_NAMES,
|
|
40
42
|
readAgentTools,
|
|
@@ -157,6 +159,7 @@ function broadcastSSE(payload) {
|
|
|
157
159
|
|
|
158
160
|
let _cursorWavesEnabled = loadCursorWavesEnabled();
|
|
159
161
|
let _claudeCodeEnabled = loadClaudeCodeEnabled();
|
|
162
|
+
let _tmuxBridgeEnabled = loadTmuxBridgeEnabled();
|
|
160
163
|
|
|
161
164
|
const BG_CONSCIOUSNESS_INTERVAL_MS = Number(process.env.CREWSWARM_BG_CONSCIOUSNESS_INTERVAL_MS) || 15 * 60 * 1000;
|
|
162
165
|
let BG_CONSCIOUSNESS_MODEL = (() => {
|
|
@@ -501,6 +504,15 @@ const bgConsciousnessRef = {
|
|
|
501
504
|
};
|
|
502
505
|
const cursorWavesRef = { get enabled() { return _cursorWavesEnabled; }, set enabled(v) { _cursorWavesEnabled = v; } };
|
|
503
506
|
const claudeCodeRef = { get enabled() { return _claudeCodeEnabled; }, set enabled(v) { _claudeCodeEnabled = v; } };
|
|
507
|
+
const tmuxBridgeRef = {
|
|
508
|
+
get enabled() { return _tmuxBridgeEnabled; },
|
|
509
|
+
set enabled(v) {
|
|
510
|
+
_tmuxBridgeEnabled = v;
|
|
511
|
+
// Sync env var and reset detection cache so tmux-bridge module picks up runtime changes
|
|
512
|
+
process.env.CREWSWARM_TMUX_BRIDGE = v ? "1" : "0";
|
|
513
|
+
resetTmuxBridge();
|
|
514
|
+
},
|
|
515
|
+
};
|
|
504
516
|
|
|
505
517
|
// connectRT is initialized after RT_URL/RT_TOKEN — use a mutable ref so HTTP server can call it
|
|
506
518
|
let _connectRT = () => { throw new Error("connectRT not initialized yet"); };
|
|
@@ -544,6 +556,7 @@ initHttpServer({
|
|
|
544
556
|
bgConsciousnessIntervalMs: BG_CONSCIOUSNESS_INTERVAL_MS,
|
|
545
557
|
cursorWavesRef,
|
|
546
558
|
claudeCodeRef,
|
|
559
|
+
tmuxBridgeRef,
|
|
547
560
|
});
|
|
548
561
|
createAndStartServer(PORT);
|
|
549
562
|
|