@kw-yeh/vibeflow 4.11.32 → 4.11.34
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/dist/vibeflow.mjs +506 -191
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/chunks/{3927.4f1724c11c5f42a8.js → 3927.8202e4c7eeb07487.js} +1 -1
- package/dist/web/_next/static/chunks/6646-4e91d829d8130f2c.js +9 -0
- package/dist/web/_next/static/chunks/7045.7cf0eb88acb1ee65.js +1 -0
- package/dist/web/_next/static/chunks/7915.e23554d5280c643a.js +1 -0
- package/dist/web/_next/static/chunks/{8259.90c4d5a968aca55b.js → 8259.62f5c49f967282f1.js} +1 -1
- package/dist/web/_next/static/chunks/pages/{_app-6528e28fe89b0344.js → _app-8718d90c431152c1.js} +1 -1
- package/dist/web/_next/static/chunks/pages/home-0fe9adbbe738c60f.js +13 -0
- package/dist/web/_next/static/chunks/{webpack-a3102c3e2504881b.js → webpack-9dc72759edb7420f.js} +1 -1
- package/dist/web/_next/static/css/1b8cc89c93b78db0.css +2 -0
- package/dist/web/_next/static/{7sUchVzedIXEiwH9u5qkP → iybQktKL4OxAZEKjzdSYP}/_buildManifest.js +1 -1
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/1068-2abb4920b8ee6ac6.js +0 -9
- package/dist/web/_next/static/chunks/7045.cf4993f5eb0f9eb4.js +0 -1
- package/dist/web/_next/static/chunks/7915.c6925370d1383e2e.js +0 -1
- package/dist/web/_next/static/chunks/pages/home-5fcfcde1fead971f.js +0 -13
- package/dist/web/_next/static/css/517bd79243800caf.css +0 -2
- /package/dist/web/_next/static/{7sUchVzedIXEiwH9u5qkP → iybQktKL4OxAZEKjzdSYP}/_ssgManifest.js +0 -0
package/dist/vibeflow.mjs
CHANGED
|
@@ -594,6 +594,38 @@ function buildClaudeSettings(worktreePath) {
|
|
|
594
594
|
function resolveSystemPrompt(custom) {
|
|
595
595
|
return custom && custom.trim() ? custom : "";
|
|
596
596
|
}
|
|
597
|
+
function masterSystemPrompt(opts) {
|
|
598
|
+
const lines = [
|
|
599
|
+
"\u4F60\u662F VibeFlow \u7684 Master agent\uFF0C\u4E0D\u5C6C\u65BC\u4EFB\u4F55 repo\uFF0C\u5DE5\u4F5C\u76EE\u9304\u53EA\u662F\u4F60\u7684\u7B46\u8A18\u5340\u3002",
|
|
600
|
+
"\u4F60\u7684\u5DE5\u4F5C\u662F\u628A\u4F7F\u7528\u8005\u7684\u4EFB\u52D9\u62C6\u89E3\u3001\u6D3E\u7D66 slave \u5361\u7247\u57F7\u884C\u3001\u8FFD\u8E64\u5230\u5B8C\u6210\uFF0C\u800C\u4E0D\u662F\u81EA\u5DF1\u6539\u7A0B\u5F0F\u78BC\u3002",
|
|
601
|
+
""
|
|
602
|
+
];
|
|
603
|
+
if (!opts.canWrite) {
|
|
604
|
+
lines.push(
|
|
605
|
+
"\u9019\u6B21\u555F\u52D5\u7121\u6CD5\u4F7F\u7528 board CLI\uFF08\u627E\u4E0D\u5230 VIBEFLOW_CLI\uFF09\uFF0C\u4F60\u4E0D\u80FD\u5EFA\u7ACB\u6216\u555F\u52D5\u5361\u7247\u3002",
|
|
606
|
+
"\u8ACB\u628A\u62C6\u89E3\u7D50\u679C\u6574\u7406\u6210\u5361\u7247\u6E05\u55AE\uFF08\u6A19\u984C\u3001\u5C08\u6848\u8DEF\u5F91\u3001prompt\u3001\u5148\u5F8C\u9806\u5E8F\uFF09\u4EA4\u7D66\u4F7F\u7528\u8005\u624B\u52D5\u5EFA\u7ACB\u3002"
|
|
607
|
+
);
|
|
608
|
+
return lines.join("\n");
|
|
609
|
+
}
|
|
610
|
+
lines.push(
|
|
611
|
+
'\u5148\u5B9A\u7FA9\u9019\u500B shell \u51FD\u5F0F\uFF0C\u4E4B\u5F8C\u6240\u6709\u6307\u4EE4\u90FD\u7528\u5B83\uFF0C\u4E26\u4E14\u4E00\u5F8B\u5E36\u4E0A --store-path "$VIBEFLOW_STORE_DIR"\uFF1A',
|
|
612
|
+
' VF() { if [ -n "$VIBEFLOW_CLI_LOADER" ]; then node --experimental-strip-types --import "$VIBEFLOW_CLI_LOADER" "$VIBEFLOW_CLI" "$@"; else node "$VIBEFLOW_CLI" "$@"; fi; }',
|
|
613
|
+
"",
|
|
614
|
+
"1. \u5224\u65B7\uFF1A\u4E00\u5F35\u5361\u505A\u5F97\u5B8C\u5C31\u53EA\u5EFA\u4E00\u5F35\uFF1B\u6D89\u53CA\u591A\u500B repo \u6216\u53EF\u5E73\u884C\u7684\u7368\u7ACB\u5DE5\u4F5C\u624D\u62C6\u6210\u591A\u5F35\u3002\u6BCF\u5F35\u53EA\u52D5\u4E00\u500B repo\uFF0C\u5404\u81EA\u80FD\u7368\u7ACB\u9A57\u6536\u3002",
|
|
615
|
+
'2. \u6D3E\u5DE5\uFF1AVF task create --store-path "$VIBEFLOW_STORE_DIR" --master "$VIBEFLOW_TASK_ID" --project <repo \u7D55\u5C0D\u8DEF\u5F91> --title <\u6A19\u984C> --prompt <\u5167\u5BB9>',
|
|
616
|
+
" prompt \u5FC5\u9808\u81EA\u5305\u542B\uFF1A\u80CC\u666F\u3001\u7BC4\u570D\u3001\u9A57\u6536\u689D\u4EF6\u3001\u8207\u5176\u4ED6\u5361\u7684\u4ECB\u9762\u7D04\u5B9A\u2014\u2014slave \u770B\u4E0D\u5230\u4F60\u7684\u5C0D\u8A71\u3002",
|
|
617
|
+
" \u9700\u8981\u6642\u53EF\u52A0 --agent / --model / --effort / --base-branch\u3002"
|
|
618
|
+
);
|
|
619
|
+
lines.push(
|
|
620
|
+
opts.autoMode ? '3. \u555F\u52D5\uFF1A\u6C92\u6709\u524D\u7F6E\u4F9D\u8CF4\u7684\u5361\u76F4\u63A5 VF task start --store-path "$VIBEFLOW_STORE_DIR" --task <id>\uFF1B\u6709\u4F9D\u8CF4\u7684\u5148\u7559\u5728 Backlog\uFF0C\u524D\u7F6E\u5361\u5B8C\u6210\u5F8C\u518D\u555F\u52D5\u3002' : "3. \u555F\u52D5\uFF1A\u4E0D\u8981\u81EA\u5DF1\u555F\u52D5\u5361\u7247\u3002\u5217\u51FA\u4F60\u5EFA\u4E86\u54EA\u4E9B\u5361\u3001\u5EFA\u8B70\u7684\u555F\u52D5\u9806\u5E8F\uFF0C\u7B49\u4F7F\u7528\u8005\u5728 VibeFlow \u6309\u958B\u59CB\u3002"
|
|
621
|
+
);
|
|
622
|
+
lines.push(
|
|
623
|
+
'4. \u76E3\u807D\uFF1AVF task list --store-path "$VIBEFLOW_STORE_DIR" --master "$VIBEFLOW_TASK_ID" \u6703\u56DE\u50B3\u6BCF\u5F35\u5361\u7684\u6B04\u4F4D\u3001\u662F\u5426\u5728\u8DD1\u3001todo \u9032\u5EA6\u3001\u6D3B\u52D5\u72C0\u614B\u8207 PR\u3002',
|
|
624
|
+
" \u5B9A\u671F\u67E5\u770B\uFF1B\u5361\u5728\u7B49\u5F85\u6B0A\u9650\u3001\u6709 launchError\u3001\u6216\u9577\u6642\u9593\u6C92\u6709\u6D3B\u52D5\u6642\uFF0C\u544A\u8A34\u4F7F\u7528\u8005\u4E26\u63D0\u51FA\u8655\u7406\u65B9\u5F0F\u3002",
|
|
625
|
+
"5. \u4F60\u4E0D\u6703\u628A\u5361\u7247\u79FB\u5230 Done\u3001\u4E0D\u6703\u5408\u4F75 PR\u3002\u5168\u90E8 slave \u5B8C\u6210\u5F8C\uFF0C\u6574\u7406\u6BCF\u5F35\u7684\u7D50\u679C\u8207 PR \u9023\u7D50\uFF0C\u4EA4\u7D66\u4F7F\u7528\u8005\u505A\u6700\u5F8C\u78BA\u8A8D\u3002"
|
|
626
|
+
);
|
|
627
|
+
return lines.join("\n");
|
|
628
|
+
}
|
|
597
629
|
function buildPrompt(task) {
|
|
598
630
|
const lines = [`\u4EFB\u52D9\u6A19\u984C\uFF1A${task.title}`];
|
|
599
631
|
const description = task.description?.trim();
|
|
@@ -654,7 +686,8 @@ function taskModel(task) {
|
|
|
654
686
|
function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
|
|
655
687
|
const agent = taskAgent(task);
|
|
656
688
|
const model = taskModel(task);
|
|
657
|
-
const
|
|
689
|
+
const custom = resolveSystemPrompt(systemPrompt);
|
|
690
|
+
const sys = task.role === "master" ? [custom, masterSystemPrompt({ canWrite: Boolean(opts?.boardCli?.cliPath), autoMode: opts?.autoMode === true })].filter(Boolean).join("\n\n") : custom;
|
|
658
691
|
const includeTaskPrompt = opts?.includeTaskPrompt !== false;
|
|
659
692
|
const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
|
|
660
693
|
const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
|
|
@@ -1116,6 +1149,7 @@ function newFormState(defaultAutoMode, agents) {
|
|
|
1116
1149
|
const agentCli = agents && agents.length > 0 && !agents.some((a) => a.id === "claude") ? agents[0].id : "claude";
|
|
1117
1150
|
return {
|
|
1118
1151
|
mode: "new",
|
|
1152
|
+
role: "slave",
|
|
1119
1153
|
title: "",
|
|
1120
1154
|
description: "",
|
|
1121
1155
|
projectPath: null,
|
|
@@ -1135,6 +1169,7 @@ function editFormState(task, defaultAutoMode) {
|
|
|
1135
1169
|
return {
|
|
1136
1170
|
mode: "edit",
|
|
1137
1171
|
taskId: task.id,
|
|
1172
|
+
role: task.role === "master" ? "master" : "slave",
|
|
1138
1173
|
title: task.title,
|
|
1139
1174
|
description: task.description ?? "",
|
|
1140
1175
|
projectPath: task.projectPath ?? null,
|
|
@@ -1161,8 +1196,11 @@ function visibleFields(s, ctx) {
|
|
|
1161
1196
|
const missing = isProjectMissing(ctx.recent, s.projectPath);
|
|
1162
1197
|
const isRepo = s.gitInfo?.isRepo ?? false;
|
|
1163
1198
|
const hasRemote = s.gitInfo?.hasRemote ?? false;
|
|
1199
|
+
if (s.role === "master") {
|
|
1200
|
+
return s.mode === "new" ? ["role", "title", "attachments", "description", "effort", "autoMode", "agent", "model", "submit"] : ["title", "description", "effort", "autoMode", "agent", "model", "submit"];
|
|
1201
|
+
}
|
|
1164
1202
|
if (s.mode === "new") {
|
|
1165
|
-
const fields2 = ["title", "project"];
|
|
1203
|
+
const fields2 = ["role", "title", "project"];
|
|
1166
1204
|
if (s.gitInfo && !missing && !isRepo) fields2.push("gitInit");
|
|
1167
1205
|
if (isRepo && hasRemote) fields2.push("baseBranch");
|
|
1168
1206
|
if (isRepo) fields2.push("branch");
|
|
@@ -1189,6 +1227,7 @@ function fitEffort(ctx, s) {
|
|
|
1189
1227
|
}
|
|
1190
1228
|
function blockReason(s, ctx, loadingGit) {
|
|
1191
1229
|
if (!s.title.trim()) return "\u8ACB\u8F38\u5165\u4EFB\u52D9\u6A19\u984C";
|
|
1230
|
+
if (s.role === "master") return null;
|
|
1192
1231
|
if (loadingGit) return "\u5075\u6E2C Git \u72C0\u614B\u4E2D\u2026";
|
|
1193
1232
|
const missing = isProjectMissing(ctx.recent, s.projectPath);
|
|
1194
1233
|
if (s.mode === "new") {
|
|
@@ -1205,12 +1244,15 @@ function blockReason(s, ctx, loadingGit) {
|
|
|
1205
1244
|
}
|
|
1206
1245
|
function createPayload(s) {
|
|
1207
1246
|
const hasRemote = s.gitInfo?.hasRemote ?? false;
|
|
1247
|
+
const target = s.role === "master" ? { role: "master" } : {
|
|
1248
|
+
projectPath: s.projectPath,
|
|
1249
|
+
baseBranch: hasRemote ? s.baseBranch || null : null,
|
|
1250
|
+
branch: s.branch.trim() || void 0
|
|
1251
|
+
};
|
|
1208
1252
|
return {
|
|
1209
1253
|
title: s.title.trim(),
|
|
1210
1254
|
description: s.description.trim(),
|
|
1211
|
-
|
|
1212
|
-
baseBranch: hasRemote ? s.baseBranch || null : null,
|
|
1213
|
-
branch: s.branch.trim() || void 0,
|
|
1255
|
+
...target,
|
|
1214
1256
|
agentCli: s.agentCli,
|
|
1215
1257
|
model: s.model || void 0,
|
|
1216
1258
|
effort: s.effort,
|
|
@@ -1227,8 +1269,10 @@ function updatePayload(s) {
|
|
|
1227
1269
|
model: s.model || void 0,
|
|
1228
1270
|
effort: s.effort,
|
|
1229
1271
|
autoMode: s.autoMode,
|
|
1230
|
-
...s.
|
|
1231
|
-
|
|
1272
|
+
...s.role === "master" ? {} : {
|
|
1273
|
+
...s.projectPath ? { projectPath: s.projectPath, baseBranch: s.baseBranch || null } : {},
|
|
1274
|
+
branch: s.branch.trim()
|
|
1275
|
+
}
|
|
1232
1276
|
};
|
|
1233
1277
|
}
|
|
1234
1278
|
function editorCommand(env, platform) {
|
|
@@ -1254,7 +1298,7 @@ function cycle(list, current, step) {
|
|
|
1254
1298
|
return list[((i < 0 ? 0 : i + step) % list.length + list.length) % list.length];
|
|
1255
1299
|
}
|
|
1256
1300
|
function sameForm(a, b) {
|
|
1257
|
-
const pick = (s) => [s.title, s.description, s.projectPath, s.baseBranch, s.branch, s.attachments.join("\n"), s.effort, s.autoMode, s.agentCli, s.model];
|
|
1301
|
+
const pick = (s) => [s.role, s.title, s.description, s.projectPath, s.baseBranch, s.branch, s.attachments.join("\n"), s.effort, s.autoMode, s.agentCli, s.model];
|
|
1258
1302
|
return JSON.stringify(pick(a)) === JSON.stringify(pick(b));
|
|
1259
1303
|
}
|
|
1260
1304
|
function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, onResult }) {
|
|
@@ -1352,7 +1396,9 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1352
1396
|
if (key.downArrow || key.tab) return setFocus((f) => Math.min(fields.length - 1, f + 1));
|
|
1353
1397
|
const step = key.leftArrow ? -1 : key.rightArrow ? 1 : 0;
|
|
1354
1398
|
if (step) {
|
|
1355
|
-
if (field === "
|
|
1399
|
+
if (field === "role") {
|
|
1400
|
+
setS((prev) => ({ ...prev, role: prev.role === "master" ? "slave" : "master" }));
|
|
1401
|
+
} else if (field === "project") {
|
|
1356
1402
|
const paths = ctx.recent.map((p) => p.path);
|
|
1357
1403
|
if (paths.length) setProject(cycle(paths, s.projectPath ?? paths[paths.length - 1], step));
|
|
1358
1404
|
} else if (field === "baseBranch") {
|
|
@@ -1389,6 +1435,8 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1389
1435
|
const value = (f) => {
|
|
1390
1436
|
if (editing === f) return `${draft}\u258F`;
|
|
1391
1437
|
switch (f) {
|
|
1438
|
+
case "role":
|
|
1439
|
+
return s.role === "master" ? "\u2039 Master\uFF1A\u4E0D\u7D81 repo\uFF0C\u62C6\u89E3\u4E26\u6D3E\u5DE5\u7D66\u5176\u4ED6\u5361\u7247 \u203A" : "\u2039 Slave\uFF1A\u5728\u4E00\u500B Git \u5C08\u6848\u88E1\u57F7\u884C \u203A";
|
|
1392
1440
|
case "title":
|
|
1393
1441
|
return s.title || "\uFF08\u4F8B\u5982\uFF1A\u5BE6\u4F5C\u767B\u5165\u9801\u9762\uFF09";
|
|
1394
1442
|
case "project": {
|
|
@@ -1534,6 +1582,7 @@ var init_task_form = __esm({
|
|
|
1534
1582
|
];
|
|
1535
1583
|
DEFAULT_EFFORT = "auto";
|
|
1536
1584
|
LABELS = {
|
|
1585
|
+
role: "\u89D2\u8272",
|
|
1537
1586
|
title: "\u4EFB\u52D9\u6A19\u984C",
|
|
1538
1587
|
project: "\u5C08\u6848\u8CC7\u6599\u593E",
|
|
1539
1588
|
gitInit: "\u521D\u59CB\u5316 Git",
|
|
@@ -1811,13 +1860,16 @@ function BoardView({ opts, initial, initialMessage, onAction }) {
|
|
|
1811
1860
|
...tasks.length === 0 ? [h2(Text2, { key: "empty", dimColor: true }, "\u2014")] : tasks.map((t, ti) => {
|
|
1812
1861
|
const isSel = active && ti === sel.index;
|
|
1813
1862
|
const mark = running.has(t.id) ? "\u25CF " : " ";
|
|
1863
|
+
const role = t.role === "master" ? "\u25C6 " : t.masterId ? "\u21B3 " : "";
|
|
1864
|
+
const masterTitle = t.masterId ? COLUMNS2.flatMap((c) => board[c]).find((m) => m.id === t.masterId)?.title : void 0;
|
|
1865
|
+
const detail = t.role === "master" ? `Master \xB7 ${COLUMNS2.flatMap((c) => board[c]).filter((x) => x.masterId === t.id).length} \u500B slave` : `${t.projectName ?? ""} \xB7 ${t.branch}${masterTitle ? ` \xB7 \u25C6 ${masterTitle}` : ""}`;
|
|
1814
1866
|
const summary = col === "backlog" ? "" : progressSummary(col === "in_progress" ? progress[t.id] : null, t.usage);
|
|
1815
1867
|
return h2(
|
|
1816
1868
|
Box2,
|
|
1817
1869
|
{ key: t.id, flexDirection: "column" },
|
|
1818
|
-
h2(Text2, { inverse: isSel, color: running.has(t.id) ? "green" : void 0 }, truncate2(`${mark}${t.title}`, colWidth - 4)),
|
|
1870
|
+
h2(Text2, { inverse: isSel, color: running.has(t.id) ? "green" : t.role === "master" ? "magenta" : void 0 }, truncate2(`${mark}${role}${t.title}`, colWidth - 4)),
|
|
1819
1871
|
summary ? h2(Text2, { dimColor: !isSel, color: isSel ? "cyan" : void 0 }, truncate2(` ${summary}`, colWidth - 4)) : null,
|
|
1820
|
-
isSel ? h2(Text2, { dimColor: true }, truncate2(` ${
|
|
1872
|
+
isSel ? h2(Text2, { dimColor: true }, truncate2(` ${detail}`, colWidth - 4)) : null
|
|
1821
1873
|
);
|
|
1822
1874
|
})
|
|
1823
1875
|
);
|
|
@@ -2331,6 +2383,9 @@ function recordRecentProject(store, projectPath) {
|
|
|
2331
2383
|
}
|
|
2332
2384
|
|
|
2333
2385
|
// packages/core/src/store.ts
|
|
2386
|
+
function isMasterTask(task) {
|
|
2387
|
+
return task.role === "master";
|
|
2388
|
+
}
|
|
2334
2389
|
var DEFAULT_NOTIFICATION_SETTINGS = {
|
|
2335
2390
|
enabled: true,
|
|
2336
2391
|
stepCompleted: false,
|
|
@@ -2454,7 +2509,7 @@ function updateTask(taskId, patch) {
|
|
|
2454
2509
|
function removeTask(taskId) {
|
|
2455
2510
|
const board = getStore().get("board");
|
|
2456
2511
|
Object.keys(board).forEach((col) => {
|
|
2457
|
-
board[col] = board[col].filter((t) => t.id !== taskId);
|
|
2512
|
+
board[col] = board[col].filter((t) => t.id !== taskId).map((t) => t.masterId === taskId ? { ...t, masterId: void 0 } : t);
|
|
2458
2513
|
});
|
|
2459
2514
|
getStore().set("board", board);
|
|
2460
2515
|
}
|
|
@@ -2520,6 +2575,10 @@ function createBridge(t) {
|
|
|
2520
2575
|
listAgentModels: (agentId2, refresh = false) => t.invoke("agents:listModels", { agentId: agentId2, refresh }),
|
|
2521
2576
|
createTask: (payload) => t.invoke("vibeflow:createTask", payload),
|
|
2522
2577
|
updateTask: (payload) => t.invoke("vibeflow:updateTask", payload),
|
|
2578
|
+
/** Move a Backlog card to In Progress and start its agent on the host. */
|
|
2579
|
+
startTask: (taskId) => t.invoke("vibeflow:startTask", taskId),
|
|
2580
|
+
/** Cards as a master sees them; filter by master or a single card. */
|
|
2581
|
+
taskSummaries: (filter) => t.invoke("task:summaries", filter),
|
|
2523
2582
|
/** Clear prior execution state and prepare a fresh run of this task. */
|
|
2524
2583
|
resetTaskRun: (taskId) => t.invoke("vibeflow:resetTaskRun", taskId),
|
|
2525
2584
|
removeTask: (taskId) => t.invoke("vibeflow:removeTask", taskId),
|
|
@@ -3182,6 +3241,7 @@ function linkFromRef(ref, state) {
|
|
|
3182
3241
|
return { kind: ref.kind, number: ref.number, url: ref.url, state };
|
|
3183
3242
|
}
|
|
3184
3243
|
function hasRealBranch(task) {
|
|
3244
|
+
if (task.role === "master") return false;
|
|
3185
3245
|
return Boolean(task.provisionedAt || task.worktreePath || task.outcome);
|
|
3186
3246
|
}
|
|
3187
3247
|
function resolveTaskLinks(tasks, repoOfProject, prIndex) {
|
|
@@ -4353,8 +4413,28 @@ async function detectAgents() {
|
|
|
4353
4413
|
|
|
4354
4414
|
// packages/core/src/tasks.ts
|
|
4355
4415
|
init_attachments();
|
|
4416
|
+
function mastersRoot(storeFile) {
|
|
4417
|
+
return path13.join(path13.dirname(storeFile), "masters");
|
|
4418
|
+
}
|
|
4419
|
+
function taskError(message, code2) {
|
|
4420
|
+
return Object.assign(new Error(message), { code: code2 });
|
|
4421
|
+
}
|
|
4422
|
+
function assertMasterRef(board, masterId, taskId) {
|
|
4423
|
+
const master = Object.values(board).flat().find((t) => t.id === masterId);
|
|
4424
|
+
if (!master) throw taskError(`\u627E\u4E0D\u5230 master \u5361\u7247\uFF1A${masterId}`, "MASTER_NOT_FOUND");
|
|
4425
|
+
if (!isMasterTask(master)) throw taskError(`\u5361\u7247 ${masterId} \u4E0D\u662F master`, "NOT_A_MASTER");
|
|
4426
|
+
if (taskId && masterId === taskId) throw taskError("\u5361\u7247\u4E0D\u80FD\u6307\u5B9A\u81EA\u5DF1\u70BA master", "NOT_A_MASTER");
|
|
4427
|
+
}
|
|
4428
|
+
async function assertDirectory(dir) {
|
|
4429
|
+
const stat = await fs10.stat(dir).catch(() => null);
|
|
4430
|
+
if (!path13.isAbsolute(dir) || !stat?.isDirectory()) {
|
|
4431
|
+
throw taskError(`\u627E\u4E0D\u5230\u8CC7\u6599\u593E\uFF1A${dir}`, "WORK_DIR_NOT_FOUND");
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4356
4434
|
async function createTaskFromInput(input) {
|
|
4357
|
-
|
|
4435
|
+
if (input.role === "master") return createMasterTask(input);
|
|
4436
|
+
const projectPath = input.projectPath;
|
|
4437
|
+
if (!projectPath) throw taskError("\u5C1A\u672A\u9078\u64C7\u5C08\u6848\u8CC7\u6599\u593E", "PROJECT_NOT_FOUND");
|
|
4358
4438
|
try {
|
|
4359
4439
|
await fs10.access(projectPath);
|
|
4360
4440
|
} catch {
|
|
@@ -4370,6 +4450,7 @@ async function createTaskFromInput(input) {
|
|
|
4370
4450
|
const taskId = randomUUID2().slice(0, 8);
|
|
4371
4451
|
const explicitBranch = input.branch?.trim() || null;
|
|
4372
4452
|
const store = input.storePath ? getStoreAtPath(input.storePath) : getStore();
|
|
4453
|
+
if (input.masterId) assertMasterRef(store.get("board"), input.masterId);
|
|
4373
4454
|
const projectName = path13.basename(projectPath);
|
|
4374
4455
|
const workspacePath = projectWorkstationPath(
|
|
4375
4456
|
resolveWorkstationPath(store.get("settings")),
|
|
@@ -4410,6 +4491,7 @@ async function createTaskFromInput(input) {
|
|
|
4410
4491
|
model: input.model || void 0,
|
|
4411
4492
|
effort: input.effort ?? DEFAULT_TASK_EFFORT,
|
|
4412
4493
|
autoMode: input.autoMode,
|
|
4494
|
+
...input.masterId ? { masterId: input.masterId } : {},
|
|
4413
4495
|
...input.github ? { github: input.github } : {},
|
|
4414
4496
|
...input.jira ? { jira: input.jira } : {}
|
|
4415
4497
|
};
|
|
@@ -4426,7 +4508,54 @@ async function createTaskFromInput(input) {
|
|
|
4426
4508
|
}
|
|
4427
4509
|
return { task, storePath: store.path };
|
|
4428
4510
|
}
|
|
4511
|
+
async function createMasterTask(input) {
|
|
4512
|
+
if (input.masterId) throw taskError("master \u5361\u7247\u4E0D\u80FD\u518D\u96B8\u5C6C\u5176\u4ED6 master", "NOT_A_MASTER");
|
|
4513
|
+
const workDir = input.workDir?.trim() || void 0;
|
|
4514
|
+
if (workDir) await assertDirectory(workDir);
|
|
4515
|
+
const store = input.storePath ? getStoreAtPath(input.storePath) : getStore();
|
|
4516
|
+
const taskId = randomUUID2().slice(0, 8);
|
|
4517
|
+
const workspacePath = path13.join(mastersRoot(store.path), taskId);
|
|
4518
|
+
const draft = {
|
|
4519
|
+
id: taskId,
|
|
4520
|
+
title: input.title.trim() || `Master ${taskId}`,
|
|
4521
|
+
branch: "",
|
|
4522
|
+
role: "master",
|
|
4523
|
+
workspacePath,
|
|
4524
|
+
...workDir ? { workDir } : {}
|
|
4525
|
+
};
|
|
4526
|
+
const col = input.status ?? "backlog";
|
|
4527
|
+
const provisioned = col === "backlog" ? null : await provisionTaskWorktree(draft);
|
|
4528
|
+
const attachments = provisioned?.worktreePath ? writeAttachments(provisioned.worktreePath, input.attachments ?? []) : writeAttachmentsTo(taskAttachmentStagingPath(workspacePath, taskId), input.attachments ?? []);
|
|
4529
|
+
const attachmentLines = attachments.map((attachment) => `[\u9644\u4EF6: ${attachment.path}]`);
|
|
4530
|
+
const baseDescription = input.description?.trim();
|
|
4531
|
+
const description = attachmentLines.length ? [baseDescription, attachmentLines.join("\n")].filter(Boolean).join("\n\n") : baseDescription || void 0;
|
|
4532
|
+
const task = {
|
|
4533
|
+
...draft,
|
|
4534
|
+
...provisioned,
|
|
4535
|
+
description,
|
|
4536
|
+
createdAt: Date.now(),
|
|
4537
|
+
agentCli: input.agentCli ?? "claude",
|
|
4538
|
+
model: input.model || void 0,
|
|
4539
|
+
effort: input.effort ?? DEFAULT_TASK_EFFORT,
|
|
4540
|
+
autoMode: input.autoMode
|
|
4541
|
+
};
|
|
4542
|
+
try {
|
|
4543
|
+
const board = store.get("board");
|
|
4544
|
+
board[col] = [task, ...board[col]];
|
|
4545
|
+
store.set("board", board);
|
|
4546
|
+
} catch (err) {
|
|
4547
|
+
throw taskError(`Store \u5BEB\u5165\u5931\u6557\uFF1A${err.message}`, "STORE_WRITE_FAILED");
|
|
4548
|
+
}
|
|
4549
|
+
return { task, storePath: store.path };
|
|
4550
|
+
}
|
|
4429
4551
|
async function provisionTaskWorktree(task) {
|
|
4552
|
+
if (isMasterTask(task)) {
|
|
4553
|
+
if (!task.workspacePath) throw taskError("master \u5361\u7247\u6C92\u6709\u5DE5\u4F5C\u8CC7\u6599\u593E", "WORKTREE_CREATE_FAILED");
|
|
4554
|
+
const worktreePath = task.workDir ?? path13.join(task.workspacePath, "work");
|
|
4555
|
+
await fs10.mkdir(worktreePath, { recursive: true });
|
|
4556
|
+
await fs10.mkdir(task.workspacePath, { recursive: true });
|
|
4557
|
+
return { branch: "", worktreePath, provisionedAt: Date.now() };
|
|
4558
|
+
}
|
|
4430
4559
|
if (!task.projectPath || !task.workspacePath) {
|
|
4431
4560
|
throw Object.assign(new Error("\u4EFB\u52D9\u6C92\u6709\u5C08\u6848\u8CC7\u6599\u593E\uFF0C\u7121\u6CD5\u5EFA\u7ACB worktree"), {
|
|
4432
4561
|
code: "WORKTREE_CREATE_FAILED"
|
|
@@ -4470,12 +4599,17 @@ function updateTaskFromInput(input) {
|
|
|
4470
4599
|
});
|
|
4471
4600
|
}
|
|
4472
4601
|
const current = board[from].find((t) => t.id === input.taskId);
|
|
4602
|
+
if (input.masterId) {
|
|
4603
|
+
if (isMasterTask(current)) throw taskError("master \u5361\u7247\u4E0D\u80FD\u518D\u96B8\u5C6C\u5176\u4ED6 master", "NOT_A_MASTER");
|
|
4604
|
+
assertMasterRef(board, input.masterId, current.id);
|
|
4605
|
+
}
|
|
4473
4606
|
const title = input.title?.trim();
|
|
4474
4607
|
const description = input.description?.trim();
|
|
4475
4608
|
const task = {
|
|
4476
4609
|
...current,
|
|
4477
4610
|
...title ? { title } : {},
|
|
4478
|
-
...input.description !== void 0 ? { description: description || void 0 } : {}
|
|
4611
|
+
...input.description !== void 0 ? { description: description || void 0 } : {},
|
|
4612
|
+
...input.masterId !== void 0 ? { masterId: input.masterId || void 0 } : {}
|
|
4479
4613
|
};
|
|
4480
4614
|
const to = input.status ?? from;
|
|
4481
4615
|
if (to === from) {
|
|
@@ -4497,9 +4631,125 @@ function updateTaskFromInput(input) {
|
|
|
4497
4631
|
|
|
4498
4632
|
// packages/cli/src/task-command.ts
|
|
4499
4633
|
init_attachments();
|
|
4634
|
+
|
|
4635
|
+
// packages/cli/src/remote.ts
|
|
4636
|
+
import WebSocket from "ws";
|
|
4637
|
+
|
|
4638
|
+
// packages/core/src/ws-transport.ts
|
|
4639
|
+
var OPEN = 1;
|
|
4640
|
+
var MAX_BACKOFF_MS = 5e3;
|
|
4641
|
+
function createWsTransport(options) {
|
|
4642
|
+
let socket = null;
|
|
4643
|
+
let nextId = 1;
|
|
4644
|
+
let connectedOnce = false;
|
|
4645
|
+
let closed = false;
|
|
4646
|
+
let backoff = 250;
|
|
4647
|
+
const queue = [];
|
|
4648
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4649
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
4650
|
+
function flush() {
|
|
4651
|
+
while (socket && socket.readyState === OPEN && queue.length > 0) socket.send(queue.shift());
|
|
4652
|
+
}
|
|
4653
|
+
function open() {
|
|
4654
|
+
if (closed) return;
|
|
4655
|
+
const ws = options.connect();
|
|
4656
|
+
socket = ws;
|
|
4657
|
+
ws.onopen = () => {
|
|
4658
|
+
backoff = 250;
|
|
4659
|
+
options.onStatus?.(true);
|
|
4660
|
+
flush();
|
|
4661
|
+
if (connectedOnce) options.onReconnect?.();
|
|
4662
|
+
connectedOnce = true;
|
|
4663
|
+
};
|
|
4664
|
+
ws.onmessage = (ev) => {
|
|
4665
|
+
let msg;
|
|
4666
|
+
try {
|
|
4667
|
+
msg = JSON.parse(String(ev.data));
|
|
4668
|
+
} catch {
|
|
4669
|
+
return;
|
|
4670
|
+
}
|
|
4671
|
+
if (typeof msg.event === "string") {
|
|
4672
|
+
dispatch(msg.event, msg.payload);
|
|
4673
|
+
return;
|
|
4674
|
+
}
|
|
4675
|
+
const entry = typeof msg.id === "number" ? pending.get(msg.id) : void 0;
|
|
4676
|
+
if (!entry) return;
|
|
4677
|
+
pending.delete(msg.id);
|
|
4678
|
+
if (msg.error) entry.reject(Object.assign(new Error(msg.error.message), { code: msg.error.code }));
|
|
4679
|
+
else entry.resolve(msg.result === null ? void 0 : msg.result);
|
|
4680
|
+
};
|
|
4681
|
+
ws.onerror = () => {
|
|
4682
|
+
};
|
|
4683
|
+
ws.onclose = () => {
|
|
4684
|
+
if (socket !== ws) return;
|
|
4685
|
+
socket = null;
|
|
4686
|
+
options.onStatus?.(false);
|
|
4687
|
+
for (const [id, entry] of Array.from(pending)) {
|
|
4688
|
+
pending.delete(id);
|
|
4689
|
+
entry.reject(new Error("\u8207 VibeFlow core \u7684\u9023\u7DDA\u4E2D\u65B7"));
|
|
4690
|
+
}
|
|
4691
|
+
if (closed) return;
|
|
4692
|
+
setTimeout(open, backoff);
|
|
4693
|
+
backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
|
|
4694
|
+
};
|
|
4695
|
+
}
|
|
4696
|
+
function dispatch(channel, payload) {
|
|
4697
|
+
for (const cb of Array.from(listeners.get(channel) ?? [])) cb(payload);
|
|
4698
|
+
}
|
|
4699
|
+
function post(message) {
|
|
4700
|
+
const data = JSON.stringify(message);
|
|
4701
|
+
if (socket && socket.readyState === OPEN) socket.send(data);
|
|
4702
|
+
else queue.push(data);
|
|
4703
|
+
}
|
|
4704
|
+
open();
|
|
4705
|
+
return {
|
|
4706
|
+
kind: "ws",
|
|
4707
|
+
invoke(method, ...params) {
|
|
4708
|
+
const id = nextId++;
|
|
4709
|
+
return new Promise((resolve, reject) => {
|
|
4710
|
+
pending.set(id, { resolve, reject });
|
|
4711
|
+
post({ id, method, params });
|
|
4712
|
+
});
|
|
4713
|
+
},
|
|
4714
|
+
send(method, ...params) {
|
|
4715
|
+
post({ method, params });
|
|
4716
|
+
},
|
|
4717
|
+
on(channel, callback) {
|
|
4718
|
+
let set = listeners.get(channel);
|
|
4719
|
+
if (!set) listeners.set(channel, set = /* @__PURE__ */ new Set());
|
|
4720
|
+
set.add(callback);
|
|
4721
|
+
return () => {
|
|
4722
|
+
set.delete(callback);
|
|
4723
|
+
};
|
|
4724
|
+
},
|
|
4725
|
+
close() {
|
|
4726
|
+
closed = true;
|
|
4727
|
+
socket?.close();
|
|
4728
|
+
},
|
|
4729
|
+
dispatch
|
|
4730
|
+
};
|
|
4731
|
+
}
|
|
4732
|
+
|
|
4733
|
+
// packages/cli/src/remote.ts
|
|
4734
|
+
function connectToHost(lock) {
|
|
4735
|
+
const url = `ws://127.0.0.1:${lock.port}/ws`;
|
|
4736
|
+
const transport = createWsTransport({
|
|
4737
|
+
connect: () => new WebSocket(url, {
|
|
4738
|
+
headers: { Authorization: `Bearer ${lock.token}`, Host: `127.0.0.1:${lock.port}` }
|
|
4739
|
+
})
|
|
4740
|
+
});
|
|
4741
|
+
return {
|
|
4742
|
+
api: createBridge(transport),
|
|
4743
|
+
invoke: (method, ...params) => transport.invoke(method, ...params),
|
|
4744
|
+
close: () => transport.close()
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
|
|
4748
|
+
// packages/cli/src/task-command.ts
|
|
4500
4749
|
var AGENT_IDS = AGENT_CLIS.map((agent) => agent.id);
|
|
4501
4750
|
var STATUSES = ["backlog", "in_progress", "done"];
|
|
4502
|
-
var SUBCOMMANDS = ["create", "update", "artifact"];
|
|
4751
|
+
var SUBCOMMANDS = ["create", "update", "start", "list", "show", "artifact"];
|
|
4752
|
+
var ROLES = ["slave", "master"];
|
|
4503
4753
|
var MODES = ["existing", "new"];
|
|
4504
4754
|
var AUTO_MODES = ["on", "off"];
|
|
4505
4755
|
var TASK_USAGE = `
|
|
@@ -4508,10 +4758,18 @@ VibeFlow task commands
|
|
|
4508
4758
|
Usage:
|
|
4509
4759
|
vibeflow task create [options]
|
|
4510
4760
|
vibeflow task update [options]
|
|
4761
|
+
vibeflow task start --task <id> [options]
|
|
4762
|
+
vibeflow task list [--master <id>] [options]
|
|
4763
|
+
vibeflow task show --task <id> [options]
|
|
4511
4764
|
vibeflow task artifact add --file <path> [options]
|
|
4512
4765
|
|
|
4513
4766
|
task create options:
|
|
4514
|
-
--
|
|
4767
|
+
--role <role> ${ROLES.join(" | ")} (default: slave). A master has no project:
|
|
4768
|
+
it runs in its own folder and dispatches other cards.
|
|
4769
|
+
--project <path> Target project directory (required for a slave)
|
|
4770
|
+
--work-dir <path> Master only: run in this existing folder (git or not)
|
|
4771
|
+
instead of one VibeFlow creates
|
|
4772
|
+
--master <id> Dispatch the new card under this master card
|
|
4515
4773
|
--title <text> Task title (required)
|
|
4516
4774
|
--prompt <text> Task prompt / description (required)
|
|
4517
4775
|
--status <column> ${STATUSES.join(" | ")} (default: backlog)
|
|
@@ -4535,11 +4793,19 @@ task update options:
|
|
|
4535
4793
|
--title <text> New card title
|
|
4536
4794
|
--prompt <text> New card description (empty string clears it)
|
|
4537
4795
|
--status <column> ${STATUSES.join(" | ")} \u2014 moves the card between columns
|
|
4796
|
+
--master <id|none> Put the card under a master, or "none" to take it out
|
|
4538
4797
|
--store-path <dir> Explicit store directory
|
|
4539
4798
|
--profile <name> dev | prod \u2014 shorthand for common store paths
|
|
4540
4799
|
|
|
4541
|
-
At least one of --title / --prompt / --status is required. Branch,
|
|
4542
|
-
and project path are provisioned on disk and cannot be changed here.
|
|
4800
|
+
At least one of --title / --prompt / --status / --master is required. Branch,
|
|
4801
|
+
worktree and project path are provisioned on disk and cannot be changed here.
|
|
4802
|
+
|
|
4803
|
+
task start / list / show (need the running VibeFlow host):
|
|
4804
|
+
start --task <id> Move a Backlog card to In Progress and start its agent,
|
|
4805
|
+
the same as dragging it in the app
|
|
4806
|
+
list [--master <id>] Every card, or one master's cards, with column, whether
|
|
4807
|
+
its agent is running, todo progress, activity and PR
|
|
4808
|
+
show --task <id> The same summary for one card
|
|
4543
4809
|
|
|
4544
4810
|
task artifact add options:
|
|
4545
4811
|
--file <path> Copy a file into the card's user-facing Artifacts folder
|
|
@@ -4549,7 +4815,7 @@ task artifact add options:
|
|
|
4549
4815
|
|
|
4550
4816
|
Notes:
|
|
4551
4817
|
A card created with --status in_progress does NOT start running by itself.
|
|
4552
|
-
|
|
4818
|
+
Use "task start" on a Backlog card, or start it from the app.
|
|
4553
4819
|
|
|
4554
4820
|
Examples:
|
|
4555
4821
|
vibeflow task create \\
|
|
@@ -4602,6 +4868,9 @@ async function taskCommand(argv) {
|
|
|
4602
4868
|
args: argv,
|
|
4603
4869
|
options: {
|
|
4604
4870
|
project: { type: "string" },
|
|
4871
|
+
role: { type: "string" },
|
|
4872
|
+
"work-dir": { type: "string" },
|
|
4873
|
+
master: { type: "string" },
|
|
4605
4874
|
title: { type: "string" },
|
|
4606
4875
|
prompt: { type: "string" },
|
|
4607
4876
|
task: { type: "string" },
|
|
@@ -4629,7 +4898,7 @@ async function taskCommand(argv) {
|
|
|
4629
4898
|
const [sub, action] = positionals;
|
|
4630
4899
|
if (values.help || !sub) return null;
|
|
4631
4900
|
const storePath = resolveStorePath(
|
|
4632
|
-
values["store-path"] ?? (sub === "artifact" ? process.env.VIBEFLOW_STORE_DIR : void 0),
|
|
4901
|
+
values["store-path"] ?? (sub === "artifact" || HOST_COMMANDS.includes(sub) ? process.env.VIBEFLOW_STORE_DIR : void 0),
|
|
4633
4902
|
values.profile
|
|
4634
4903
|
);
|
|
4635
4904
|
setPlatform(createNodePlatform({ userDataDir: storePath }));
|
|
@@ -4644,17 +4913,19 @@ async function taskCommand(argv) {
|
|
|
4644
4913
|
if (!task2) fail("TASK_NOT_FOUND", `Task not found: ${taskId}`);
|
|
4645
4914
|
return { taskId, path: addTaskArtifact(task2, values.file) };
|
|
4646
4915
|
}
|
|
4916
|
+
if (HOST_COMMANDS.includes(sub)) return hostCommand(sub, values, storePath);
|
|
4647
4917
|
if (sub === "update") {
|
|
4648
4918
|
if (!values.task) fail("MISSING_ARGUMENT", "Missing required arguments: --task");
|
|
4649
4919
|
requireOneOf("--status", values.status, STATUSES);
|
|
4650
|
-
if (values.title === void 0 && values.prompt === void 0 && values.status === void 0) {
|
|
4651
|
-
fail("MISSING_ARGUMENT", "task update needs at least one of: --title, --prompt, --status");
|
|
4920
|
+
if (values.title === void 0 && values.prompt === void 0 && values.status === void 0 && values.master === void 0) {
|
|
4921
|
+
fail("MISSING_ARGUMENT", "task update needs at least one of: --title, --prompt, --status, --master");
|
|
4652
4922
|
}
|
|
4653
4923
|
const { task: task2, storePath: resolvedPath2 } = updateTaskFromInput({
|
|
4654
4924
|
taskId: values.task,
|
|
4655
4925
|
title: values.title,
|
|
4656
4926
|
description: values.prompt,
|
|
4657
4927
|
status: values.status,
|
|
4928
|
+
masterId: values.master === void 0 ? void 0 : values.master === "none" ? null : values.master,
|
|
4658
4929
|
storePath
|
|
4659
4930
|
});
|
|
4660
4931
|
return {
|
|
@@ -4664,12 +4935,19 @@ async function taskCommand(argv) {
|
|
|
4664
4935
|
title: task2.title,
|
|
4665
4936
|
description: task2.description,
|
|
4666
4937
|
branch: task2.branch,
|
|
4667
|
-
worktreePath: task2.worktreePath ?? null
|
|
4938
|
+
worktreePath: task2.worktreePath ?? null,
|
|
4939
|
+
masterId: task2.masterId ?? null
|
|
4668
4940
|
}
|
|
4669
4941
|
};
|
|
4670
4942
|
}
|
|
4943
|
+
requireOneOf("--role", values.role, ROLES);
|
|
4944
|
+
const master = values.role === "master";
|
|
4945
|
+
if (!master && values["work-dir"] !== void 0) fail("INVALID_ARGUMENT", "--work-dir is only for --role master");
|
|
4946
|
+
if (master && (values.project || values["base-branch"] || values.branch || values.mode)) {
|
|
4947
|
+
fail("INVALID_ARGUMENT", "A master card has no project: drop --project / --base-branch / --branch / --mode");
|
|
4948
|
+
}
|
|
4671
4949
|
const missing = [];
|
|
4672
|
-
if (!values.project) missing.push("--project");
|
|
4950
|
+
if (!master && !values.project) missing.push("--project");
|
|
4673
4951
|
if (!values.title) missing.push("--title");
|
|
4674
4952
|
if (!values.prompt) missing.push("--prompt");
|
|
4675
4953
|
if (missing.length) fail("MISSING_ARGUMENT", `Missing required arguments: ${missing.join(", ")}`);
|
|
@@ -4686,6 +4964,9 @@ async function taskCommand(argv) {
|
|
|
4686
4964
|
}
|
|
4687
4965
|
const { task, storePath: resolvedPath } = await createTaskFromInput({
|
|
4688
4966
|
projectPath: values.project,
|
|
4967
|
+
role: master ? "master" : void 0,
|
|
4968
|
+
workDir: values["work-dir"],
|
|
4969
|
+
masterId: values.master,
|
|
4689
4970
|
title: values.title,
|
|
4690
4971
|
description: values.prompt,
|
|
4691
4972
|
status: values.status ?? "backlog",
|
|
@@ -4704,7 +4985,9 @@ async function taskCommand(argv) {
|
|
|
4704
4985
|
task: {
|
|
4705
4986
|
id: task.id,
|
|
4706
4987
|
title: task.title,
|
|
4707
|
-
|
|
4988
|
+
role: task.role ?? "slave",
|
|
4989
|
+
masterId: task.masterId ?? null,
|
|
4990
|
+
projectPath: task.projectPath ?? null,
|
|
4708
4991
|
branch: task.branch,
|
|
4709
4992
|
worktreePath: task.worktreePath ?? null,
|
|
4710
4993
|
baseBranch: task.baseBranch,
|
|
@@ -4715,6 +4998,28 @@ async function taskCommand(argv) {
|
|
|
4715
4998
|
}
|
|
4716
4999
|
};
|
|
4717
5000
|
}
|
|
5001
|
+
var HOST_COMMANDS = ["start", "list", "show"];
|
|
5002
|
+
async function hostCommand(sub, values, storePath) {
|
|
5003
|
+
if (sub !== "list" && !values.task) fail("MISSING_ARGUMENT", "Missing required arguments: --task");
|
|
5004
|
+
const lock = liveHost(storePath);
|
|
5005
|
+
if (!lock) fail("HOST_NOT_RUNNING", `No VibeFlow host is running for ${storePath}. Start it with \`vibeflow\` first.`);
|
|
5006
|
+
const remote = connectToHost(lock);
|
|
5007
|
+
try {
|
|
5008
|
+
if (sub === "start") {
|
|
5009
|
+
const { task } = await remote.api.startTask(values.task);
|
|
5010
|
+
return { task: { id: task.id, title: task.title, worktreePath: task.worktreePath ?? null } };
|
|
5011
|
+
}
|
|
5012
|
+
if (sub === "show") {
|
|
5013
|
+
const [task] = await remote.api.taskSummaries({ taskId: values.task });
|
|
5014
|
+
return { task };
|
|
5015
|
+
}
|
|
5016
|
+
return { tasks: await remote.api.taskSummaries(values.master ? { masterId: values.master } : void 0) };
|
|
5017
|
+
} catch (err) {
|
|
5018
|
+
fail("HOST_ERROR", err.message);
|
|
5019
|
+
} finally {
|
|
5020
|
+
remote.close();
|
|
5021
|
+
}
|
|
5022
|
+
}
|
|
4718
5023
|
|
|
4719
5024
|
// packages/cli/src/host.ts
|
|
4720
5025
|
import fs25 from "fs";
|
|
@@ -6978,6 +7283,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6978
7283
|
bus.emit("progress:notify", { taskId, title: task.title, notifications });
|
|
6979
7284
|
}
|
|
6980
7285
|
});
|
|
7286
|
+
function gitWorktree(task) {
|
|
7287
|
+
return isMasterTask(task) ? void 0 : task.worktreePath;
|
|
7288
|
+
}
|
|
6981
7289
|
function progressTarget(task) {
|
|
6982
7290
|
if (!task.worktreePath) return null;
|
|
6983
7291
|
const agent = taskAgent(task);
|
|
@@ -7155,6 +7463,87 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7155
7463
|
authStatus: jiraAuthStatus ?? (() => getJiraAuthStatus(acliRunner)),
|
|
7156
7464
|
pointFields: () => getSettings().jira?.storyPointsFields?.length ? getSettings().jira.storyPointsFields : DEFAULT_POINT_FIELDS
|
|
7157
7465
|
});
|
|
7466
|
+
async function startTaskSession(requested, key, p) {
|
|
7467
|
+
const pending = provisioning.get(requested.id);
|
|
7468
|
+
const task = p.launch ? await ensureProvisioned(requested) : pending ? await pending : requested;
|
|
7469
|
+
const cwd = task.worktreePath;
|
|
7470
|
+
if (!cwd) throw new Error("\u4EFB\u52D9\u5C1A\u672A\u958B\u59CB\u57F7\u884C\uFF0C\u9084\u6C92\u6709 worktree");
|
|
7471
|
+
let command;
|
|
7472
|
+
let fresh = p.fresh === true;
|
|
7473
|
+
if (p.launch) {
|
|
7474
|
+
const intent = obj(p.launch, "launch");
|
|
7475
|
+
if (intent.resume && await sessions.isAlive(key)) {
|
|
7476
|
+
fresh = false;
|
|
7477
|
+
} else {
|
|
7478
|
+
command = (await launchCommand(task, intent)).replace(/\r$/, "");
|
|
7479
|
+
}
|
|
7480
|
+
}
|
|
7481
|
+
if (p.launch) ensureTaskArtifactDirs(task);
|
|
7482
|
+
const result = await sessions.start(key, {
|
|
7483
|
+
cwd,
|
|
7484
|
+
command,
|
|
7485
|
+
fresh,
|
|
7486
|
+
cols: typeof p.cols === "number" ? p.cols : void 0,
|
|
7487
|
+
rows: typeof p.rows === "number" ? p.rows : void 0,
|
|
7488
|
+
// Session ended (natural exit included), so stop its watcher.
|
|
7489
|
+
onExit: () => unwatchSubAgents(key)
|
|
7490
|
+
});
|
|
7491
|
+
terminalDirectories.set(key, cwd);
|
|
7492
|
+
watchSubAgents(key, cwd, (subAgents) => {
|
|
7493
|
+
bus.emit("subagents:update", { taskId: task.id, subAgents });
|
|
7494
|
+
});
|
|
7495
|
+
if (p.launch) {
|
|
7496
|
+
const current = findTask(task.id);
|
|
7497
|
+
if (current) trackProgress(current);
|
|
7498
|
+
}
|
|
7499
|
+
return result;
|
|
7500
|
+
}
|
|
7501
|
+
async function summarizeTasks(filter) {
|
|
7502
|
+
const board = getState().board;
|
|
7503
|
+
const alive = new Set(await sessions.list());
|
|
7504
|
+
const summaries = [];
|
|
7505
|
+
for (const column of COLUMNS) {
|
|
7506
|
+
for (const task of board[column]) {
|
|
7507
|
+
if (filter.taskId && task.id !== filter.taskId) continue;
|
|
7508
|
+
if (filter.masterId && task.masterId !== filter.masterId) continue;
|
|
7509
|
+
if (column === "in_progress" && !progress.isTracking(task.id)) trackProgress(task);
|
|
7510
|
+
const snapshot = progress.isTracking(task.id) ? progress.snapshot(task.id) : null;
|
|
7511
|
+
const todos = snapshot?.todos;
|
|
7512
|
+
const current = todos?.find((t) => t.status === "in_progress");
|
|
7513
|
+
const pr = task.outcome?.pr;
|
|
7514
|
+
summaries.push({
|
|
7515
|
+
id: task.id,
|
|
7516
|
+
title: task.title,
|
|
7517
|
+
column,
|
|
7518
|
+
...task.role ? { role: task.role } : {},
|
|
7519
|
+
...task.masterId ? { masterId: task.masterId } : {},
|
|
7520
|
+
...task.projectName ? { projectName: task.projectName } : {},
|
|
7521
|
+
...task.branch ? { branch: task.branch } : {},
|
|
7522
|
+
running: alive.has(task.id),
|
|
7523
|
+
...task.launchedAt ? { launchedAt: task.launchedAt } : {},
|
|
7524
|
+
...task.launchError ? { launchError: task.launchError } : {},
|
|
7525
|
+
...todos ? {
|
|
7526
|
+
todos: {
|
|
7527
|
+
done: todos.filter((t) => t.status === "completed").length,
|
|
7528
|
+
total: todos.length,
|
|
7529
|
+
...current ? { current: current.activeForm ?? current.content } : {}
|
|
7530
|
+
}
|
|
7531
|
+
} : {},
|
|
7532
|
+
...snapshot ? { activity: snapshot.activity } : {},
|
|
7533
|
+
...pr ? { pr: { number: pr.number, url: pr.url, state: pr.state } } : {},
|
|
7534
|
+
...task.outcome ? {
|
|
7535
|
+
outcome: {
|
|
7536
|
+
commits: task.outcome.commits.length,
|
|
7537
|
+
files: task.outcome.files.length,
|
|
7538
|
+
additions: task.outcome.additions,
|
|
7539
|
+
deletions: task.outcome.deletions
|
|
7540
|
+
}
|
|
7541
|
+
} : {}
|
|
7542
|
+
});
|
|
7543
|
+
}
|
|
7544
|
+
}
|
|
7545
|
+
return summaries;
|
|
7546
|
+
}
|
|
7158
7547
|
const handlers = {
|
|
7159
7548
|
"vibeflow:getState": () => getState(),
|
|
7160
7549
|
// A card's progress right now (todo list, usage, activity), for a frontend
|
|
@@ -7254,8 +7643,13 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7254
7643
|
},
|
|
7255
7644
|
"vibeflow:createTask": async (payload) => {
|
|
7256
7645
|
const p = obj(payload, "payload");
|
|
7257
|
-
if (
|
|
7258
|
-
|
|
7646
|
+
if (p.role !== void 0 && p.role !== "master") invalid("unknown role");
|
|
7647
|
+
if (p.role !== "master") {
|
|
7648
|
+
if (!p.projectPath) throw new Error("\u5C1A\u672A\u9078\u64C7\u5C08\u6848\u8CC7\u6599\u593E");
|
|
7649
|
+
str(p.projectPath, "projectPath");
|
|
7650
|
+
}
|
|
7651
|
+
if (p.workDir !== void 0) str(p.workDir, "workDir");
|
|
7652
|
+
if (p.masterId !== void 0) str(p.masterId, "masterId");
|
|
7259
7653
|
str(p.title ?? "", "title");
|
|
7260
7654
|
if (p.effort !== void 0 && !AGENT_EFFORTS.includes(p.effort)) invalid("unknown effort");
|
|
7261
7655
|
if (p.agentCli !== void 0 && !AGENT_IDS2.includes(p.agentCli)) invalid("unknown agent");
|
|
@@ -7263,6 +7657,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7263
7657
|
if (p.jira !== void 0 && !isJiraRef(p.jira)) invalid("jira must be a Jira ticket ref");
|
|
7264
7658
|
const { task } = await createTaskFromInput({
|
|
7265
7659
|
projectPath: p.projectPath,
|
|
7660
|
+
role: p.role,
|
|
7661
|
+
workDir: p.workDir,
|
|
7662
|
+
masterId: p.masterId,
|
|
7266
7663
|
title: p.title,
|
|
7267
7664
|
description: p.description,
|
|
7268
7665
|
baseBranch: p.baseBranch,
|
|
@@ -7290,6 +7687,18 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7290
7687
|
// were new. Launching again is the caller's decision.
|
|
7291
7688
|
"vibeflow:resetTaskRun": async (taskId) => {
|
|
7292
7689
|
const task = requireTask(taskId);
|
|
7690
|
+
if (isMasterTask(task) && task.worktreePath) {
|
|
7691
|
+
await teardownTask(task.id);
|
|
7692
|
+
if (task.workspacePath) deleteArtifacts(task.workspacePath, task.worktreePath);
|
|
7693
|
+
resetSubAgents(task.worktreePath);
|
|
7694
|
+
const usage2 = usageThroughCurrentRun(task);
|
|
7695
|
+
progress.untrack(task.id);
|
|
7696
|
+
updateTask(task.id, { launchedAt: Date.now(), runId: randomUUID5(), jevRoute: void 0, ...usage2 ? { usage: usage2 } : {} });
|
|
7697
|
+
const reset2 = findTask(task.id);
|
|
7698
|
+
if (!reset2) throw new Error("\u627E\u4E0D\u5230\u8981\u91CD\u7F6E\u7684\u4EFB\u52D9");
|
|
7699
|
+
bus.emit("subagents:update", { taskId: task.id, subAgents: [] });
|
|
7700
|
+
return { state: getState(), task: reset2 };
|
|
7701
|
+
}
|
|
7293
7702
|
if (!task.worktreePath) {
|
|
7294
7703
|
await teardownTask(task.id);
|
|
7295
7704
|
progress.untrack(task.id);
|
|
@@ -7326,8 +7735,16 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7326
7735
|
throw new Error("\u53EA\u6709 Backlog \u7684\u4EFB\u52D9\u53EF\u4EE5\u7DE8\u8F2F");
|
|
7327
7736
|
}
|
|
7328
7737
|
if (p.effort !== void 0 && !AGENT_EFFORTS.includes(p.effort)) invalid("unknown effort");
|
|
7738
|
+
let masterPatch = {};
|
|
7739
|
+
if (p.masterId !== void 0) {
|
|
7740
|
+
if (p.masterId !== null) {
|
|
7741
|
+
if (isMasterTask(existing)) invalid("a master cannot belong to another master");
|
|
7742
|
+
assertMasterRef(getState().board, str(p.masterId, "masterId"), existing.id);
|
|
7743
|
+
}
|
|
7744
|
+
masterPatch = { masterId: p.masterId || void 0 };
|
|
7745
|
+
}
|
|
7329
7746
|
let gitPatch = {};
|
|
7330
|
-
const nextProject = p.projectPath || existing.projectPath;
|
|
7747
|
+
const nextProject = isMasterTask(existing) ? void 0 : p.projectPath || existing.projectPath;
|
|
7331
7748
|
const nextBranch = p.branch === void 0 ? "" : str(p.branch, "branch").trim();
|
|
7332
7749
|
const projectChanged2 = Boolean(p.projectPath && p.projectPath !== existing.projectPath);
|
|
7333
7750
|
const baseChanged = p.baseBranch != null && p.baseBranch !== (existing.baseBranch ?? null);
|
|
@@ -7372,6 +7789,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7372
7789
|
jevRoute: void 0,
|
|
7373
7790
|
effort: p.effort,
|
|
7374
7791
|
autoMode: p.autoMode,
|
|
7792
|
+
...masterPatch,
|
|
7375
7793
|
...gitPatch
|
|
7376
7794
|
});
|
|
7377
7795
|
return getState();
|
|
@@ -7382,40 +7800,35 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7382
7800
|
"pty:start": async (payload) => {
|
|
7383
7801
|
const p = obj(payload, "payload");
|
|
7384
7802
|
const requested = requireTask(p.taskId);
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
const result = await sessions.start(key, {
|
|
7402
|
-
cwd,
|
|
7403
|
-
command,
|
|
7404
|
-
fresh,
|
|
7405
|
-
cols: typeof p.cols === "number" ? p.cols : void 0,
|
|
7406
|
-
rows: typeof p.rows === "number" ? p.rows : void 0,
|
|
7407
|
-
// Session ended (natural exit included), so stop its watcher.
|
|
7408
|
-
onExit: () => unwatchSubAgents(key)
|
|
7803
|
+
return startTaskSession(requested, sessionKeyFor(requested.id, p.sessionKey), p);
|
|
7804
|
+
},
|
|
7805
|
+
// What dragging a card into In Progress does, without a frontend: the card
|
|
7806
|
+
// moves, provisions and starts its agent here, so whoever opens it later
|
|
7807
|
+
// attaches to a run that is already going. This is how a master starts the
|
|
7808
|
+
// cards it dispatched.
|
|
7809
|
+
"vibeflow:startTask": async (taskId) => {
|
|
7810
|
+
const task = requireTask(taskId);
|
|
7811
|
+
const column = columnOf(task.id);
|
|
7812
|
+
if (column !== "backlog") throw new Error(`\u53EA\u6709 Backlog \u7684\u5361\u7247\u53EF\u4EE5\u555F\u52D5\uFF08\u76EE\u524D\u5728 ${column}\uFF09`);
|
|
7813
|
+
const board = getState().board;
|
|
7814
|
+
const started = { ...task, launchedAt: Date.now(), launchError: void 0 };
|
|
7815
|
+
setBoard({
|
|
7816
|
+
backlog: board.backlog.filter((t) => t.id !== task.id),
|
|
7817
|
+
in_progress: [started, ...board.in_progress],
|
|
7818
|
+
done: board.done
|
|
7409
7819
|
});
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7820
|
+
bus.emit("state:changed", getState());
|
|
7821
|
+
await startTaskSession(started, task.id, { taskId: task.id, launch: {} });
|
|
7822
|
+
return { state: getState(), task: findTask(task.id) ?? started };
|
|
7823
|
+
},
|
|
7824
|
+
// Every card on the board, or one master's cards, as the CLI and a master's
|
|
7825
|
+
// agent read them.
|
|
7826
|
+
"task:summaries": (payload) => {
|
|
7827
|
+
const p = payload === void 0 ? {} : obj(payload, "payload");
|
|
7828
|
+
return summarizeTasks({
|
|
7829
|
+
masterId: p.masterId === void 0 ? void 0 : requireTask(p.masterId).id,
|
|
7830
|
+
taskId: p.taskId === void 0 ? void 0 : requireTask(p.taskId).id
|
|
7413
7831
|
});
|
|
7414
|
-
if (p.launch) {
|
|
7415
|
-
const current = findTask(task.id);
|
|
7416
|
-
if (current) trackProgress(current);
|
|
7417
|
-
}
|
|
7418
|
-
return result;
|
|
7419
7832
|
},
|
|
7420
7833
|
"terminal:start": async (payload) => {
|
|
7421
7834
|
const p = obj(payload, "payload");
|
|
@@ -7431,8 +7844,11 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7431
7844
|
if (p.taskId) {
|
|
7432
7845
|
const requested = requireTask(p.taskId);
|
|
7433
7846
|
const column = columnOf(requested.id);
|
|
7434
|
-
const task = column === "in_progress"
|
|
7435
|
-
cwd = str(
|
|
7847
|
+
const task = !requested.worktreePath && (column === "in_progress" || isMasterTask(requested)) ? await ensureProvisioned(requested) : requested;
|
|
7848
|
+
cwd = str(
|
|
7849
|
+
isMasterTask(task) ? task.worktreePath : column === "backlog" ? task.projectPath : task.worktreePath ?? task.projectPath,
|
|
7850
|
+
"directory"
|
|
7851
|
+
);
|
|
7436
7852
|
projectPath = task.projectPath ?? cwd;
|
|
7437
7853
|
} else if (p.projectPath) {
|
|
7438
7854
|
cwd = str(p.projectPath, "projectPath");
|
|
@@ -7526,8 +7942,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7526
7942
|
},
|
|
7527
7943
|
"git:getDiff": (taskId) => {
|
|
7528
7944
|
const task = requireTask(taskId);
|
|
7529
|
-
|
|
7530
|
-
|
|
7945
|
+
const worktree = gitWorktree(task);
|
|
7946
|
+
if (!worktree) return [];
|
|
7947
|
+
return getWorktreeDiff(worktree, task.baseBranch ?? "HEAD");
|
|
7531
7948
|
},
|
|
7532
7949
|
/**
|
|
7533
7950
|
* Changed-file list without content — cheap enough for the sidebar to poll.
|
|
@@ -7536,16 +7953,18 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7536
7953
|
*/
|
|
7537
7954
|
"git:getDiffEntries": (taskId, opts) => {
|
|
7538
7955
|
const task = requireTask(taskId);
|
|
7539
|
-
|
|
7956
|
+
const worktree = gitWorktree(task);
|
|
7957
|
+
if (!worktree) return [];
|
|
7540
7958
|
const fetch2 = opts && typeof opts === "object" ? opts.fetch : void 0;
|
|
7541
|
-
return getWorktreeDiffEntries(
|
|
7959
|
+
return getWorktreeDiffEntries(worktree, task.baseBranch ?? "HEAD", {
|
|
7542
7960
|
fetch: typeof fetch2 === "boolean" ? fetch2 : void 0
|
|
7543
7961
|
});
|
|
7544
7962
|
},
|
|
7545
7963
|
"git:getDiffFile": (taskId, filePath) => {
|
|
7546
7964
|
const task = requireTask(taskId);
|
|
7547
|
-
|
|
7548
|
-
|
|
7965
|
+
const worktree = gitWorktree(task);
|
|
7966
|
+
if (!worktree) return null;
|
|
7967
|
+
return getWorktreeDiffFile(worktree, task.baseBranch ?? "HEAD", str(filePath, "filePath"));
|
|
7549
7968
|
},
|
|
7550
7969
|
"task:listArtifacts": (taskId) => {
|
|
7551
7970
|
const task = requireTask(taskId);
|
|
@@ -7571,6 +7990,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7571
7990
|
"task:getSpecs": async (taskId) => {
|
|
7572
7991
|
const task = requireTask(taskId);
|
|
7573
7992
|
const base2 = task.baseBranch ?? "main";
|
|
7993
|
+
if (isMasterTask(task)) return [];
|
|
7574
7994
|
if (task.worktreePath) return readWorktreeSpecs(task.worktreePath, base2);
|
|
7575
7995
|
const since = task.provisionedAt ?? task.launchedAt ?? (task.pushed !== void 0 ? task.createdAt : void 0);
|
|
7576
7996
|
if (!task.projectPath || !since) return [];
|
|
@@ -7626,25 +8046,29 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7626
8046
|
"git:approve": async (payload) => {
|
|
7627
8047
|
const p = obj(payload, "payload");
|
|
7628
8048
|
const task = requireTask(p.taskId);
|
|
7629
|
-
|
|
7630
|
-
|
|
8049
|
+
const worktree = gitWorktree(task);
|
|
8050
|
+
if (!worktree) throw new Error("\u627E\u4E0D\u5230\u6B64\u4EFB\u52D9\u7684 worktree");
|
|
8051
|
+
const result = await commitAndPush(worktree, str(p.message, "message"));
|
|
7631
8052
|
if (result.pushed) updateTask(task.id, { pushed: true });
|
|
7632
8053
|
return { result, state: getState() };
|
|
7633
8054
|
},
|
|
7634
8055
|
"git:generateCommitMessage": (taskId) => {
|
|
7635
8056
|
const task = requireTask(taskId);
|
|
7636
|
-
|
|
7637
|
-
|
|
8057
|
+
const worktree = gitWorktree(task);
|
|
8058
|
+
if (!worktree) throw new Error("\u627E\u4E0D\u5230\u6B64\u4EFB\u52D9\u7684 worktree");
|
|
8059
|
+
return generateCommitMessage(worktree, task.baseBranch ?? "main", task.agentCli ?? "claude");
|
|
7638
8060
|
},
|
|
7639
8061
|
"git:getPrStatus": (taskId) => {
|
|
7640
8062
|
const task = requireTask(taskId);
|
|
7641
|
-
|
|
7642
|
-
|
|
8063
|
+
const worktree = gitWorktree(task);
|
|
8064
|
+
if (!worktree) return null;
|
|
8065
|
+
return getPrStatus(worktree);
|
|
7643
8066
|
},
|
|
7644
8067
|
"git:getGithubCompareUrl": (taskId) => {
|
|
7645
8068
|
const task = requireTask(taskId);
|
|
7646
|
-
|
|
7647
|
-
|
|
8069
|
+
const worktree = gitWorktree(task);
|
|
8070
|
+
if (!worktree) return null;
|
|
8071
|
+
return getGithubCompareUrl(worktree, task.baseBranch ?? "main");
|
|
7648
8072
|
},
|
|
7649
8073
|
// One dialog at a time: a second click while it is open joins it instead
|
|
7650
8074
|
// of stacking another window on the host.
|
|
@@ -7672,8 +8096,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7672
8096
|
await teardownTask(task.id);
|
|
7673
8097
|
cancelChatSend(task.id);
|
|
7674
8098
|
let outcome;
|
|
7675
|
-
|
|
7676
|
-
|
|
8099
|
+
const worktree = gitWorktree(task);
|
|
8100
|
+
if (worktree) {
|
|
8101
|
+
outcome = await captureTaskOutcome(worktree, task.baseBranch ?? "main") ?? void 0;
|
|
7677
8102
|
}
|
|
7678
8103
|
const usage = usageThroughCurrentRun(task);
|
|
7679
8104
|
progress.untrack(task.id);
|
|
@@ -7702,6 +8127,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
7702
8127
|
if (task.workspacePath) {
|
|
7703
8128
|
fs21.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
|
|
7704
8129
|
}
|
|
8130
|
+
if (isMasterTask(task) && task.workspacePath) {
|
|
8131
|
+
fs21.rmSync(task.workspacePath, { recursive: true, force: true });
|
|
8132
|
+
}
|
|
7705
8133
|
clearConversation(task.id);
|
|
7706
8134
|
removeTask(task.id);
|
|
7707
8135
|
return getState();
|
|
@@ -8412,119 +8840,6 @@ function isWebDir(dir) {
|
|
|
8412
8840
|
return fs25.existsSync(path28.join(dir, "home", "index.html"));
|
|
8413
8841
|
}
|
|
8414
8842
|
|
|
8415
|
-
// packages/cli/src/remote.ts
|
|
8416
|
-
import WebSocket from "ws";
|
|
8417
|
-
|
|
8418
|
-
// packages/core/src/ws-transport.ts
|
|
8419
|
-
var OPEN = 1;
|
|
8420
|
-
var MAX_BACKOFF_MS = 5e3;
|
|
8421
|
-
function createWsTransport(options) {
|
|
8422
|
-
let socket = null;
|
|
8423
|
-
let nextId = 1;
|
|
8424
|
-
let connectedOnce = false;
|
|
8425
|
-
let closed = false;
|
|
8426
|
-
let backoff = 250;
|
|
8427
|
-
const queue = [];
|
|
8428
|
-
const pending = /* @__PURE__ */ new Map();
|
|
8429
|
-
const listeners = /* @__PURE__ */ new Map();
|
|
8430
|
-
function flush() {
|
|
8431
|
-
while (socket && socket.readyState === OPEN && queue.length > 0) socket.send(queue.shift());
|
|
8432
|
-
}
|
|
8433
|
-
function open() {
|
|
8434
|
-
if (closed) return;
|
|
8435
|
-
const ws = options.connect();
|
|
8436
|
-
socket = ws;
|
|
8437
|
-
ws.onopen = () => {
|
|
8438
|
-
backoff = 250;
|
|
8439
|
-
options.onStatus?.(true);
|
|
8440
|
-
flush();
|
|
8441
|
-
if (connectedOnce) options.onReconnect?.();
|
|
8442
|
-
connectedOnce = true;
|
|
8443
|
-
};
|
|
8444
|
-
ws.onmessage = (ev) => {
|
|
8445
|
-
let msg;
|
|
8446
|
-
try {
|
|
8447
|
-
msg = JSON.parse(String(ev.data));
|
|
8448
|
-
} catch {
|
|
8449
|
-
return;
|
|
8450
|
-
}
|
|
8451
|
-
if (typeof msg.event === "string") {
|
|
8452
|
-
dispatch(msg.event, msg.payload);
|
|
8453
|
-
return;
|
|
8454
|
-
}
|
|
8455
|
-
const entry = typeof msg.id === "number" ? pending.get(msg.id) : void 0;
|
|
8456
|
-
if (!entry) return;
|
|
8457
|
-
pending.delete(msg.id);
|
|
8458
|
-
if (msg.error) entry.reject(Object.assign(new Error(msg.error.message), { code: msg.error.code }));
|
|
8459
|
-
else entry.resolve(msg.result === null ? void 0 : msg.result);
|
|
8460
|
-
};
|
|
8461
|
-
ws.onerror = () => {
|
|
8462
|
-
};
|
|
8463
|
-
ws.onclose = () => {
|
|
8464
|
-
if (socket !== ws) return;
|
|
8465
|
-
socket = null;
|
|
8466
|
-
options.onStatus?.(false);
|
|
8467
|
-
for (const [id, entry] of Array.from(pending)) {
|
|
8468
|
-
pending.delete(id);
|
|
8469
|
-
entry.reject(new Error("\u8207 VibeFlow core \u7684\u9023\u7DDA\u4E2D\u65B7"));
|
|
8470
|
-
}
|
|
8471
|
-
if (closed) return;
|
|
8472
|
-
setTimeout(open, backoff);
|
|
8473
|
-
backoff = Math.min(backoff * 2, MAX_BACKOFF_MS);
|
|
8474
|
-
};
|
|
8475
|
-
}
|
|
8476
|
-
function dispatch(channel, payload) {
|
|
8477
|
-
for (const cb of Array.from(listeners.get(channel) ?? [])) cb(payload);
|
|
8478
|
-
}
|
|
8479
|
-
function post(message) {
|
|
8480
|
-
const data = JSON.stringify(message);
|
|
8481
|
-
if (socket && socket.readyState === OPEN) socket.send(data);
|
|
8482
|
-
else queue.push(data);
|
|
8483
|
-
}
|
|
8484
|
-
open();
|
|
8485
|
-
return {
|
|
8486
|
-
kind: "ws",
|
|
8487
|
-
invoke(method, ...params) {
|
|
8488
|
-
const id = nextId++;
|
|
8489
|
-
return new Promise((resolve, reject) => {
|
|
8490
|
-
pending.set(id, { resolve, reject });
|
|
8491
|
-
post({ id, method, params });
|
|
8492
|
-
});
|
|
8493
|
-
},
|
|
8494
|
-
send(method, ...params) {
|
|
8495
|
-
post({ method, params });
|
|
8496
|
-
},
|
|
8497
|
-
on(channel, callback) {
|
|
8498
|
-
let set = listeners.get(channel);
|
|
8499
|
-
if (!set) listeners.set(channel, set = /* @__PURE__ */ new Set());
|
|
8500
|
-
set.add(callback);
|
|
8501
|
-
return () => {
|
|
8502
|
-
set.delete(callback);
|
|
8503
|
-
};
|
|
8504
|
-
},
|
|
8505
|
-
close() {
|
|
8506
|
-
closed = true;
|
|
8507
|
-
socket?.close();
|
|
8508
|
-
},
|
|
8509
|
-
dispatch
|
|
8510
|
-
};
|
|
8511
|
-
}
|
|
8512
|
-
|
|
8513
|
-
// packages/cli/src/remote.ts
|
|
8514
|
-
function connectToHost(lock) {
|
|
8515
|
-
const url = `ws://127.0.0.1:${lock.port}/ws`;
|
|
8516
|
-
const transport = createWsTransport({
|
|
8517
|
-
connect: () => new WebSocket(url, {
|
|
8518
|
-
headers: { Authorization: `Bearer ${lock.token}`, Host: `127.0.0.1:${lock.port}` }
|
|
8519
|
-
})
|
|
8520
|
-
});
|
|
8521
|
-
return {
|
|
8522
|
-
api: createBridge(transport),
|
|
8523
|
-
invoke: (method, ...params) => transport.invoke(method, ...params),
|
|
8524
|
-
close: () => transport.close()
|
|
8525
|
-
};
|
|
8526
|
-
}
|
|
8527
|
-
|
|
8528
8843
|
// packages/cli/src/doctor.ts
|
|
8529
8844
|
import fs26 from "fs";
|
|
8530
8845
|
import path29 from "path";
|