@kw-yeh/vibeflow 4.2.0 → 4.4.0
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 +301 -97
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/{lyOZoF78HzRwmQ3DYf88f → WryXzkD7rqukLA95d06Zt}/_buildManifest.js +1 -1
- package/dist/web/_next/static/chunks/{8833-76c31db372a29bb8.js → 2524-abaf3a55023f6066.js} +2 -2
- package/dist/web/_next/static/chunks/pages/{_app-014e95fe86cd1d39.js → _app-2a4ae532bdcab6f2.js} +1 -1
- package/dist/web/_next/static/chunks/pages/home-3c2a73bbb48cf9a2.js +12 -0
- package/dist/web/_next/static/css/{c380333c2873a2b8.css → 24b804d01aa3af3c.css} +1 -1
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/pages/home-e692e9e0783d2fdf.js +0 -11
- /package/dist/web/_next/static/{lyOZoF78HzRwmQ3DYf88f → WryXzkD7rqukLA95d06Zt}/_ssgManifest.js +0 -0
package/dist/vibeflow.mjs
CHANGED
|
@@ -280,7 +280,7 @@ var init_session_backend = __esm({
|
|
|
280
280
|
});
|
|
281
281
|
|
|
282
282
|
// packages/core/src/tmux-backend.ts
|
|
283
|
-
import { execFile as
|
|
283
|
+
import { execFile as execFile3, execFileSync as execFileSync2 } from "child_process";
|
|
284
284
|
import fs3 from "fs";
|
|
285
285
|
import path7 from "path";
|
|
286
286
|
import * as nodePty2 from "node-pty";
|
|
@@ -289,7 +289,7 @@ function q(value) {
|
|
|
289
289
|
}
|
|
290
290
|
function defaultRun(env) {
|
|
291
291
|
return (args) => new Promise((resolve) => {
|
|
292
|
-
|
|
292
|
+
execFile3("tmux", args, { env }, (err, stdout) => {
|
|
293
293
|
const code2 = err ? typeof err.code === "number" ? err.code : 1 : 0;
|
|
294
294
|
resolve({ code: code2, stdout: String(stdout ?? "") });
|
|
295
295
|
});
|
|
@@ -555,14 +555,17 @@ ${entry}
|
|
|
555
555
|
}
|
|
556
556
|
}
|
|
557
557
|
function writeAttachments(worktreePath, inputs) {
|
|
558
|
+
if (inputs.length === 0) return [];
|
|
559
|
+
ensureAttachmentExclude(worktreePath);
|
|
560
|
+
return writeAttachmentsTo(path10.join(worktreePath, ATTACHMENTS_DIR), inputs);
|
|
561
|
+
}
|
|
562
|
+
function writeAttachmentsTo(attachmentDir, inputs) {
|
|
558
563
|
if (inputs.length === 0) return [];
|
|
559
564
|
const decodedInputs = inputs.map((input) => ({
|
|
560
565
|
input,
|
|
561
566
|
name: safeAttachmentName(input.name),
|
|
562
567
|
bytes: decodeAttachment(input)
|
|
563
568
|
}));
|
|
564
|
-
ensureAttachmentExclude(worktreePath);
|
|
565
|
-
const attachmentDir = path10.join(worktreePath, ATTACHMENTS_DIR);
|
|
566
569
|
fs6.mkdirSync(attachmentDir, { recursive: true });
|
|
567
570
|
return decodedInputs.map(({ input, name, bytes }) => {
|
|
568
571
|
const id = randomUUID().slice(0, 8);
|
|
@@ -607,7 +610,6 @@ function newFormState(defaultAutoMode, agents) {
|
|
|
607
610
|
const agentCli = agents && agents.length > 0 && !agents.some((a) => a.id === "claude") ? agents[0].id : "claude";
|
|
608
611
|
return {
|
|
609
612
|
mode: "new",
|
|
610
|
-
launched: false,
|
|
611
613
|
title: "",
|
|
612
614
|
description: "",
|
|
613
615
|
projectPath: null,
|
|
@@ -627,11 +629,11 @@ function editFormState(task, defaultAutoMode) {
|
|
|
627
629
|
return {
|
|
628
630
|
mode: "edit",
|
|
629
631
|
taskId: task.id,
|
|
630
|
-
launched: Boolean(task.launchedAt),
|
|
631
632
|
title: task.title,
|
|
632
633
|
description: task.description ?? "",
|
|
633
634
|
projectPath: task.projectPath ?? null,
|
|
634
635
|
originalProjectPath: task.projectPath ?? null,
|
|
636
|
+
worktreePath: task.worktreePath,
|
|
635
637
|
gitInfo: null,
|
|
636
638
|
gitInfoPath: null,
|
|
637
639
|
baseBranch: task.baseBranch ?? "",
|
|
@@ -662,8 +664,8 @@ function visibleFields(s, ctx) {
|
|
|
662
664
|
return fields2;
|
|
663
665
|
}
|
|
664
666
|
const fields = ["title", "description", "effort", "autoMode", "project"];
|
|
665
|
-
if (
|
|
666
|
-
fields.push("agent", "model", "submit");
|
|
667
|
+
if (isRepo && hasRemote) fields.push("baseBranch");
|
|
668
|
+
fields.push("branch", "agent", "model", "submit");
|
|
667
669
|
return fields;
|
|
668
670
|
}
|
|
669
671
|
function modelOptions(ctx, agentCli, current) {
|
|
@@ -712,7 +714,8 @@ function updatePayload(s) {
|
|
|
712
714
|
model: s.model || void 0,
|
|
713
715
|
effort: s.effort,
|
|
714
716
|
autoMode: s.autoMode,
|
|
715
|
-
|
|
717
|
+
...s.projectPath ? { projectPath: s.projectPath, baseBranch: s.baseBranch || null } : {},
|
|
718
|
+
branch: s.branch.trim()
|
|
716
719
|
};
|
|
717
720
|
}
|
|
718
721
|
function editorCommand(env, platform) {
|
|
@@ -754,16 +757,20 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
754
757
|
const lookup = useRef(0);
|
|
755
758
|
const fields = visibleFields(s, ctx);
|
|
756
759
|
const field = fields[Math.min(focus, fields.length - 1)];
|
|
757
|
-
const projectLocked = s.mode === "edit" && s.launched;
|
|
758
760
|
const missing = isProjectMissing(ctx.recent, s.projectPath);
|
|
759
761
|
useEffect(() => {
|
|
760
|
-
if (!s.projectPath || missing || s.gitInfoPath === s.projectPath
|
|
762
|
+
if (!s.projectPath || missing || s.gitInfoPath === s.projectPath) return;
|
|
761
763
|
const target = s.projectPath;
|
|
762
764
|
const id = ++lookup.current;
|
|
763
765
|
setLoadingGit(true);
|
|
764
766
|
void api.getGitInfo(target).then((info) => {
|
|
765
767
|
if (id !== lookup.current) return;
|
|
766
|
-
setS((prev) => ({
|
|
768
|
+
setS((prev) => ({
|
|
769
|
+
...prev,
|
|
770
|
+
gitInfo: info,
|
|
771
|
+
gitInfoPath: target,
|
|
772
|
+
baseBranch: prev.mode === "edit" && target === prev.originalProjectPath && prev.baseBranch ? prev.baseBranch : info.defaultBase ?? ""
|
|
773
|
+
}));
|
|
767
774
|
}).catch((err) => id === lookup.current && setMessage(`\u5075\u6E2C Git \u5931\u6557\uFF1A${err.message}`)).finally(() => id === lookup.current && setLoadingGit(false));
|
|
768
775
|
}, [s.projectPath]);
|
|
769
776
|
const leave = (result) => {
|
|
@@ -771,7 +778,6 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
771
778
|
exit();
|
|
772
779
|
};
|
|
773
780
|
const setProject = (projectPath) => {
|
|
774
|
-
if (projectLocked) return;
|
|
775
781
|
setS((prev) => ({ ...prev, projectPath, gitInfo: null, gitInfoPath: null, baseBranch: "" }));
|
|
776
782
|
};
|
|
777
783
|
const submit = async () => {
|
|
@@ -833,7 +839,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
833
839
|
if (key.downArrow || key.tab) return setFocus((f) => Math.min(fields.length - 1, f + 1));
|
|
834
840
|
const step = key.leftArrow ? -1 : key.rightArrow ? 1 : 0;
|
|
835
841
|
if (step) {
|
|
836
|
-
if (field === "project"
|
|
842
|
+
if (field === "project") {
|
|
837
843
|
const paths = ctx.recent.map((p) => p.path);
|
|
838
844
|
if (paths.length) setProject(cycle(paths, s.projectPath ?? paths[paths.length - 1], step));
|
|
839
845
|
} else if (field === "baseBranch") {
|
|
@@ -859,7 +865,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
859
865
|
void api.initRepository(s.projectPath).then((info) => setS((prev) => ({ ...prev, gitInfo: info, gitInfoPath: prev.projectPath, baseBranch: info.defaultBase ?? "" }))).catch((err) => setMessage(err.message)).finally(() => setBusy(null));
|
|
860
866
|
return;
|
|
861
867
|
}
|
|
862
|
-
if (TEXT_FIELDS.includes(field)
|
|
868
|
+
if (TEXT_FIELDS.includes(field)) {
|
|
863
869
|
setEditing(field);
|
|
864
870
|
setDraft(field === "title" ? s.title : field === "branch" ? s.branch : field === "project" ? s.projectPath ?? "" : "");
|
|
865
871
|
}
|
|
@@ -870,7 +876,6 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
870
876
|
case "title":
|
|
871
877
|
return s.title || "\uFF08\u4F8B\u5982\uFF1A\u5BE6\u4F5C\u767B\u5165\u9801\u9762\uFF09";
|
|
872
878
|
case "project": {
|
|
873
|
-
if (projectLocked) return `${s.projectPath ? path24.basename(s.projectPath) : "\u2014"}\uFF08\u4EFB\u52D9\u5DF2\u958B\u59CB\uFF0C\u9396\u5B9A\uFF09`;
|
|
874
879
|
if (!s.projectPath) return ctx.recent.length ? "\u2190/\u2192 \u9078\u64C7\u4F7F\u7528\u904E\u7684\u5C08\u6848\uFF0C\u6216 Enter \u8F38\u5165\u8DEF\u5F91" : "Enter \u8F38\u5165\u5C08\u6848\u8CC7\u6599\u593E\u7684\u7D55\u5C0D\u8DEF\u5F91";
|
|
875
880
|
return `${s.projectPath}${missing ? "\uFF08\u8DEF\u5F91\u907A\u5931\uFF09" : ""}`;
|
|
876
881
|
}
|
|
@@ -906,15 +911,18 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
906
911
|
};
|
|
907
912
|
const note = () => {
|
|
908
913
|
const info = s.gitInfo;
|
|
909
|
-
if (field === "project"
|
|
914
|
+
if (field === "project") {
|
|
910
915
|
if (loadingGit) return "\u5075\u6E2C Git \u72C0\u614B\u4E2D\u2026";
|
|
911
916
|
if (missing) return `\u627E\u4E0D\u5230 ${s.projectPath}\uFF0C\u5C08\u6848\u53EF\u80FD\u5DF2\u88AB\u79FB\u52D5\u6216\u522A\u9664\u3002`;
|
|
912
917
|
if (info && !info.isRepo) return "\u9019\u500B\u8CC7\u6599\u593E\u4E0D\u662F Git repository\u3002\u8ACB\u6539\u9078\u4E00\u500B Git \u5C08\u6848\uFF0C\u6216\u5728\u4E0B\u65B9\u521D\u59CB\u5316\u3002";
|
|
913
|
-
if (s.mode === "edit" && projectChanged(s)) return "\
|
|
918
|
+
if (s.mode === "edit" && projectChanged(s)) return "\u5206\u652F\u6703\u5728\u65B0\u5C08\u6848\u3001\u5361\u7247\u958B\u59CB\u57F7\u884C\u6642\u624D\u5EFA\u7ACB\u3002";
|
|
914
919
|
if (info?.isRepo && !info.hasRemote) return `\u6B64 repository \u6C92\u6709 remote\uFF0C\u5C07\u4EE5\u76EE\u524D\u5206\u652F (${info.currentBranch ?? "HEAD"}) \u70BA\u57FA\u6E96\u5EFA\u7ACB\u672C\u5730 worktree\u3002`;
|
|
915
920
|
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path24.basename(s.projectPath)}`;
|
|
916
921
|
}
|
|
917
|
-
if (field === "branch")
|
|
922
|
+
if (field === "branch") {
|
|
923
|
+
const when = info?.hasRemote ? "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u4E26\u63A8\u5230 origin\uFF1B\u82E5 remote \u5DF2\u6709\u9019\u500B\u5206\u652F\uFF0C\u5247\u76F4\u63A5\u53D6\u56DE\u4E26\u63A5\u7E8C\u4E0A\u9762\u7684\u5DE5\u4F5C\u3002" : "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u3002";
|
|
924
|
+
return s.mode === "edit" && s.worktreePath ? `${when} \u9019\u5F35\u5361\u5DF2\u6709 worktree\uFF1A\u66F4\u6539\u5206\u652F\u3001\u5C08\u6848\u6216\u57FA\u6E96\u5206\u652F\u6703\u79FB\u9664\u5B83\uFF08\u6709\u672A\u5B8C\u6210\u8B8A\u66F4\u6642\u7121\u6CD5\u5132\u5B58\uFF09\u3002` : when;
|
|
925
|
+
}
|
|
918
926
|
if (field === "effort") return `${EFFORTS.find((x) => x.value === s.effort).description} Claude Code \u8207 Codex \u6703\u5728\u555F\u52D5\u6B64\u4EFB\u52D9\u6642\u5957\u7528\uFF1B\u5BE6\u969B\u53EF\u7528\u7D1A\u8DDD\u4F9D model \u800C\u5B9A\u3002`;
|
|
919
927
|
if (field === "autoMode") return s.autoMode ? "\u958B\u555F\uFF1AAgent \u6703\u76F4\u63A5\u4FEE\u6539\u6A94\u6848\u3001\u57F7\u884C\u6307\u4EE4\uFF0C\u4E0D\u6703\u9010\u6B21\u5411\u4F60\u78BA\u8A8D\u3002" : "\u95DC\u9589\uFF1AAgent \u6BCF\u6B21\u8981\u52D5\u4F5C\u524D\uFF0C\u90FD\u6703\u5728\u7D42\u7AEF\u6A5F\u88E1\u7B49\u4F60\u5141\u8A31\u3002";
|
|
920
928
|
if (field === "submit") return blockReason(s, ctx, loadingGit);
|
|
@@ -1130,6 +1138,9 @@ function BoardView({ opts, initial, initialMessage, onAction }) {
|
|
|
1130
1138
|
}
|
|
1131
1139
|
setSel({ column: sel.column + direction, index: 0 });
|
|
1132
1140
|
if (move.effect === "launch") {
|
|
1141
|
+
if (!move.task.worktreePath) {
|
|
1142
|
+
setMessage(`\u6B63\u5728\u70BA\u300C${move.task.title}\u300D\u5F9E\u6700\u65B0\u7684 ${move.task.baseBranch ?? "\u57FA\u6E96\u5206\u652F"} \u5EFA\u7ACB\u5206\u652F ${move.task.branch} \u8207 worktree\uFF0C\u9700\u8981\u5E7E\u79D2\u9418\u2026`);
|
|
1143
|
+
}
|
|
1133
1144
|
await api.term.start({ taskId: move.task.id, launch: {} });
|
|
1134
1145
|
setMessage(`\u5DF2\u555F\u52D5\u300C${move.task.title}\u300D\u7684 agent\u3002Enter \u53EF\u9032\u5165\u7D42\u7AEF\u3002`);
|
|
1135
1146
|
} else if (move.effect === "cleanup") {
|
|
@@ -1189,8 +1200,14 @@ function BoardView({ opts, initial, initialMessage, onAction }) {
|
|
|
1189
1200
|
return;
|
|
1190
1201
|
}
|
|
1191
1202
|
if (!selected) return;
|
|
1192
|
-
if (input === "e")
|
|
1203
|
+
if (input === "e") {
|
|
1204
|
+
if (COLUMNS2[sel.column] !== "backlog") return setMessage("\u57F7\u884C\u4E2D\u8207\u5DF2\u5B8C\u6210\u7684\u5361\u7247\u53EA\u80FD\u67E5\u770B\uFF0C\u7121\u6CD5\u7DE8\u8F2F");
|
|
1205
|
+
return leave({ type: "edit", task: selected });
|
|
1206
|
+
}
|
|
1193
1207
|
if (key.return) {
|
|
1208
|
+
if (!selected.worktreePath && COLUMNS2[sel.column] === "backlog") {
|
|
1209
|
+
return setMessage("\u9019\u5F35\u5361\u5C1A\u672A\u958B\u59CB\u57F7\u884C\uFF0C\u5206\u652F\u8207 worktree \u6703\u5728\u958B\u59CB\u6642\u5EFA\u7ACB");
|
|
1210
|
+
}
|
|
1194
1211
|
if (!selected.worktreePath && !selected.projectPath) return setMessage("\u9019\u5F35\u5361\u6C92\u6709\u5DE5\u4F5C\u76EE\u9304");
|
|
1195
1212
|
if (COLUMNS2[sel.column] === "done") return setMessage("\u5DF2\u5B8C\u6210\u7684\u5361\u7247\u6C92\u6709\u7D42\u7AEF");
|
|
1196
1213
|
return leave({ type: "attach", task: selected });
|
|
@@ -1486,7 +1503,103 @@ function releaseLock(userDataDir) {
|
|
|
1486
1503
|
}
|
|
1487
1504
|
|
|
1488
1505
|
// packages/core/src/platform.ts
|
|
1506
|
+
import { execFile as execFile2 } from "child_process";
|
|
1507
|
+
|
|
1508
|
+
// packages/core/src/folder-dialog.ts
|
|
1489
1509
|
import { execFile } from "child_process";
|
|
1510
|
+
var MAC_SCRIPT = [
|
|
1511
|
+
"on run argv",
|
|
1512
|
+
"activate",
|
|
1513
|
+
"if (count of argv) > 1 then",
|
|
1514
|
+
"return POSIX path of (choose folder with prompt (item 1 of argv) default location (POSIX file (item 2 of argv)))",
|
|
1515
|
+
"end if",
|
|
1516
|
+
"return POSIX path of (choose folder with prompt (item 1 of argv))",
|
|
1517
|
+
"end run"
|
|
1518
|
+
];
|
|
1519
|
+
var WINDOWS_SCRIPT = [
|
|
1520
|
+
"[Console]::OutputEncoding = [Text.Encoding]::UTF8",
|
|
1521
|
+
"Add-Type -AssemblyName System.Windows.Forms",
|
|
1522
|
+
"$owner = New-Object System.Windows.Forms.Form -Property @{ TopMost = $true }",
|
|
1523
|
+
"$dialog = New-Object System.Windows.Forms.FolderBrowserDialog",
|
|
1524
|
+
"$dialog.Description = $env:VIBEFLOW_DIALOG_TITLE",
|
|
1525
|
+
"$dialog.ShowNewFolderButton = $true",
|
|
1526
|
+
"if ($env:VIBEFLOW_DIALOG_PATH) { $dialog.SelectedPath = $env:VIBEFLOW_DIALOG_PATH }",
|
|
1527
|
+
"if ($dialog.ShowDialog($owner) -eq [System.Windows.Forms.DialogResult]::OK) { $dialog.SelectedPath }"
|
|
1528
|
+
].join("; ");
|
|
1529
|
+
function folderDialogCommands(platform, { title = "\u9078\u64C7\u8CC7\u6599\u593E", defaultPath } = {}) {
|
|
1530
|
+
if (platform === "darwin") {
|
|
1531
|
+
return [
|
|
1532
|
+
{
|
|
1533
|
+
cmd: "osascript",
|
|
1534
|
+
args: [...MAC_SCRIPT.flatMap((line) => ["-e", line]), title, ...defaultPath ? [defaultPath] : []],
|
|
1535
|
+
// osascript exits 1 on any script error; only -128 is "User canceled".
|
|
1536
|
+
cancelCodes: [1],
|
|
1537
|
+
cancelStderr: /-128/
|
|
1538
|
+
}
|
|
1539
|
+
];
|
|
1540
|
+
}
|
|
1541
|
+
if (platform === "win32") {
|
|
1542
|
+
return [
|
|
1543
|
+
{
|
|
1544
|
+
cmd: "powershell.exe",
|
|
1545
|
+
args: ["-NoProfile", "-NonInteractive", "-STA", "-Command", WINDOWS_SCRIPT],
|
|
1546
|
+
env: { VIBEFLOW_DIALOG_TITLE: title, VIBEFLOW_DIALOG_PATH: defaultPath ?? "" },
|
|
1547
|
+
cancelCodes: []
|
|
1548
|
+
}
|
|
1549
|
+
];
|
|
1550
|
+
}
|
|
1551
|
+
const start = defaultPath ? `${defaultPath.replace(/\/+$/, "")}/` : void 0;
|
|
1552
|
+
return [
|
|
1553
|
+
{
|
|
1554
|
+
cmd: "zenity",
|
|
1555
|
+
args: ["--file-selection", "--directory", `--title=${title}`, ...start ? [`--filename=${start}`] : []],
|
|
1556
|
+
cancelCodes: [1]
|
|
1557
|
+
},
|
|
1558
|
+
{
|
|
1559
|
+
cmd: "kdialog",
|
|
1560
|
+
args: ["--getexistingdirectory", defaultPath ?? ".", "--title", title],
|
|
1561
|
+
cancelCodes: [1]
|
|
1562
|
+
}
|
|
1563
|
+
];
|
|
1564
|
+
}
|
|
1565
|
+
function parseFolderDialogOutput(stdout) {
|
|
1566
|
+
const line = stdout.trim();
|
|
1567
|
+
if (!line) return null;
|
|
1568
|
+
if (line === "/" || /^[A-Za-z]:[\\/]$/.test(line)) return line;
|
|
1569
|
+
return line.replace(/[\\/]+$/, "");
|
|
1570
|
+
}
|
|
1571
|
+
function run(command) {
|
|
1572
|
+
return new Promise((resolve, reject) => {
|
|
1573
|
+
execFile(
|
|
1574
|
+
command.cmd,
|
|
1575
|
+
command.args,
|
|
1576
|
+
{ windowsHide: false, encoding: "utf8", env: { ...process.env, ...command.env } },
|
|
1577
|
+
(err, stdout, stderr) => {
|
|
1578
|
+
if (err) {
|
|
1579
|
+
const code2 = err.code;
|
|
1580
|
+
if (code2 === "ENOENT") return resolve("missing");
|
|
1581
|
+
const canceled = typeof code2 === "number" && command.cancelCodes.includes(code2) && (!command.cancelStderr || command.cancelStderr.test(stderr));
|
|
1582
|
+
if (canceled) return resolve({ canceled: true });
|
|
1583
|
+
return reject(new Error(`\u7121\u6CD5\u958B\u555F\u8CC7\u6599\u593E\u9078\u64C7\u8996\u7A97\uFF1A${stderr.trim() || err.message}`));
|
|
1584
|
+
}
|
|
1585
|
+
const picked = parseFolderDialogOutput(stdout);
|
|
1586
|
+
resolve(picked ? { path: picked } : { canceled: true });
|
|
1587
|
+
}
|
|
1588
|
+
);
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
async function pickFolderWithOs(options = {}) {
|
|
1592
|
+
if (process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
|
|
1593
|
+
return { unsupported: true };
|
|
1594
|
+
}
|
|
1595
|
+
for (const command of folderDialogCommands(process.platform, options)) {
|
|
1596
|
+
const result = await run(command);
|
|
1597
|
+
if (result !== "missing") return result;
|
|
1598
|
+
}
|
|
1599
|
+
return { unsupported: true };
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// packages/core/src/platform.ts
|
|
1490
1603
|
import { homedir } from "os";
|
|
1491
1604
|
import path2 from "path";
|
|
1492
1605
|
var _platform = null;
|
|
@@ -1516,7 +1629,7 @@ function openCommand(target) {
|
|
|
1516
1629
|
function openWithOs(target) {
|
|
1517
1630
|
const [cmd, args] = openCommand(target);
|
|
1518
1631
|
return new Promise((resolve) => {
|
|
1519
|
-
|
|
1632
|
+
execFile2(cmd, args, { windowsHide: true }, (err) => resolve(err ? err.message : ""));
|
|
1520
1633
|
});
|
|
1521
1634
|
}
|
|
1522
1635
|
function createNodePlatform(options = {}) {
|
|
@@ -1536,7 +1649,8 @@ function createNodePlatform(options = {}) {
|
|
|
1536
1649
|
const err = await openWithOs(url);
|
|
1537
1650
|
if (err) throw new Error(err);
|
|
1538
1651
|
},
|
|
1539
|
-
openPath: (target) => openWithOs(target)
|
|
1652
|
+
openPath: (target) => openWithOs(target),
|
|
1653
|
+
pickFolder: (opts) => pickFolderWithOs(opts)
|
|
1540
1654
|
};
|
|
1541
1655
|
}
|
|
1542
1656
|
|
|
@@ -1840,6 +1954,8 @@ function createBridge(t) {
|
|
|
1840
1954
|
getGithubCompareUrl: (taskId) => t.invoke("git:getGithubCompareUrl", taskId),
|
|
1841
1955
|
/** Open a URL in the system default browser. */
|
|
1842
1956
|
openExternal: (url) => t.invoke("shell:openExternal", url),
|
|
1957
|
+
/** The OS folder dialog, shown on the machine core runs on. */
|
|
1958
|
+
pickFolder: (options) => t.invoke("dialog:pickFolder", options),
|
|
1843
1959
|
cleanupTask: (taskId) => t.invoke("vibeflow:cleanupTask", taskId),
|
|
1844
1960
|
/** Live sub-agent updates pushed from main while a session runs. */
|
|
1845
1961
|
onSubAgentsUpdate: (callback) => {
|
|
@@ -2079,7 +2195,7 @@ async function generateBranchName(title, description) {
|
|
|
2079
2195
|
}
|
|
2080
2196
|
|
|
2081
2197
|
// packages/core/src/git.ts
|
|
2082
|
-
import { execFile as
|
|
2198
|
+
import { execFile as execFile4 } from "child_process";
|
|
2083
2199
|
import { promisify } from "util";
|
|
2084
2200
|
import path12 from "path";
|
|
2085
2201
|
import fs8 from "fs/promises";
|
|
@@ -2400,7 +2516,7 @@ function deleteArtifacts(baseDir, worktreePath) {
|
|
|
2400
2516
|
}
|
|
2401
2517
|
|
|
2402
2518
|
// packages/core/src/git.ts
|
|
2403
|
-
var pexec = promisify(
|
|
2519
|
+
var pexec = promisify(execFile4);
|
|
2404
2520
|
async function git(cwd, args) {
|
|
2405
2521
|
const { stdout } = await pexec("git", args, {
|
|
2406
2522
|
cwd,
|
|
@@ -3200,12 +3316,15 @@ import { join as join2 } from "path";
|
|
|
3200
3316
|
function projectWorkstationPath(workstationRoot, projectName) {
|
|
3201
3317
|
return join2(workstationRoot, projectName);
|
|
3202
3318
|
}
|
|
3319
|
+
function taskAttachmentStagingPath(workspacePath, taskId) {
|
|
3320
|
+
return join2(workspacePath, ".attachments", taskId);
|
|
3321
|
+
}
|
|
3203
3322
|
|
|
3204
3323
|
// packages/core/src/agents.ts
|
|
3205
3324
|
init_env();
|
|
3206
|
-
import { execFile as
|
|
3325
|
+
import { execFile as execFile5 } from "child_process";
|
|
3207
3326
|
import { promisify as promisify2 } from "util";
|
|
3208
|
-
var pexec2 = promisify2(
|
|
3327
|
+
var pexec2 = promisify2(execFile5);
|
|
3209
3328
|
var AGENT_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
3210
3329
|
var DEFAULT_TASK_EFFORT = "medium";
|
|
3211
3330
|
var AGENT_CLIS = [
|
|
@@ -3278,25 +3397,21 @@ async function createTaskFromInput(input) {
|
|
|
3278
3397
|
if (explicitBranch) {
|
|
3279
3398
|
await assertBranchAvailable(projectPath, workspacePath, explicitBranch);
|
|
3280
3399
|
}
|
|
3281
|
-
const
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
);
|
|
3297
|
-
}
|
|
3298
|
-
const attachments = writeAttachments(
|
|
3299
|
-
provisionResult.worktreePath,
|
|
3400
|
+
const branch = explicitBranch ?? await generateBranchName(input.title, input.description);
|
|
3401
|
+
const draft = {
|
|
3402
|
+
id: taskId,
|
|
3403
|
+
title: input.title.trim() || `Task ${taskId}`,
|
|
3404
|
+
branch: branch ?? fallbackBranchName(taskId),
|
|
3405
|
+
branchExplicit: explicitBranch ? true : void 0,
|
|
3406
|
+
projectPath,
|
|
3407
|
+
projectName,
|
|
3408
|
+
workspacePath,
|
|
3409
|
+
baseBranch: input.baseBranch || info.defaultBase || info.currentBranch || void 0
|
|
3410
|
+
};
|
|
3411
|
+
const col = input.status ?? "backlog";
|
|
3412
|
+
const provisioned = col === "backlog" ? null : await provisionTaskWorktree(draft);
|
|
3413
|
+
const attachments = provisioned?.worktreePath ? writeAttachments(provisioned.worktreePath, input.attachments ?? []) : writeAttachmentsTo(
|
|
3414
|
+
taskAttachmentStagingPath(workspacePath, taskId),
|
|
3300
3415
|
input.attachments ?? []
|
|
3301
3416
|
);
|
|
3302
3417
|
const attachmentLines = attachments.map(
|
|
@@ -3305,16 +3420,9 @@ async function createTaskFromInput(input) {
|
|
|
3305
3420
|
const baseDescription = input.description?.trim();
|
|
3306
3421
|
const description = attachmentLines.length ? [baseDescription, attachmentLines.join("\n")].filter(Boolean).join("\n\n") : baseDescription || void 0;
|
|
3307
3422
|
const task = {
|
|
3308
|
-
|
|
3309
|
-
|
|
3423
|
+
...draft,
|
|
3424
|
+
...provisioned,
|
|
3310
3425
|
description,
|
|
3311
|
-
branch: provisionResult.branch,
|
|
3312
|
-
projectPath,
|
|
3313
|
-
projectName,
|
|
3314
|
-
worktreePath: provisionResult.worktreePath,
|
|
3315
|
-
workspacePath,
|
|
3316
|
-
baseBranch: provisionResult.baseBranch,
|
|
3317
|
-
pushed: provisionResult.pushed,
|
|
3318
3426
|
createdAt: Date.now(),
|
|
3319
3427
|
agentCli: input.agentCli ?? "claude",
|
|
3320
3428
|
model: input.model || void 0,
|
|
@@ -3323,7 +3431,6 @@ async function createTaskFromInput(input) {
|
|
|
3323
3431
|
};
|
|
3324
3432
|
try {
|
|
3325
3433
|
const board = store.get("board");
|
|
3326
|
-
const col = input.status ?? "backlog";
|
|
3327
3434
|
board[col] = [task, ...board[col]];
|
|
3328
3435
|
store.set("board", board);
|
|
3329
3436
|
recordRecentProject(store, projectPath);
|
|
@@ -3335,6 +3442,38 @@ async function createTaskFromInput(input) {
|
|
|
3335
3442
|
}
|
|
3336
3443
|
return { task, storePath: store.path };
|
|
3337
3444
|
}
|
|
3445
|
+
async function provisionTaskWorktree(task) {
|
|
3446
|
+
if (!task.projectPath || !task.workspacePath) {
|
|
3447
|
+
throw Object.assign(new Error("\u4EFB\u52D9\u6C92\u6709\u5C08\u6848\u8CC7\u6599\u593E\uFF0C\u7121\u6CD5\u5EFA\u7ACB worktree"), {
|
|
3448
|
+
code: "WORKTREE_CREATE_FAILED"
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
await fs9.mkdir(task.workspacePath, { recursive: true });
|
|
3452
|
+
if (task.branchExplicit) {
|
|
3453
|
+
await assertBranchAvailable(task.projectPath, task.workspacePath, task.branch);
|
|
3454
|
+
}
|
|
3455
|
+
try {
|
|
3456
|
+
const result = await provisionWorktree(
|
|
3457
|
+
task.projectPath,
|
|
3458
|
+
task.workspacePath,
|
|
3459
|
+
task.id,
|
|
3460
|
+
task.baseBranch ?? null,
|
|
3461
|
+
task.branch,
|
|
3462
|
+
{ explicitBranch: task.branchExplicit === true }
|
|
3463
|
+
);
|
|
3464
|
+
return {
|
|
3465
|
+
branch: result.branch,
|
|
3466
|
+
worktreePath: result.worktreePath,
|
|
3467
|
+
baseBranch: result.baseBranch,
|
|
3468
|
+
pushed: result.pushed
|
|
3469
|
+
};
|
|
3470
|
+
} catch (err) {
|
|
3471
|
+
throw Object.assign(
|
|
3472
|
+
new Error(`Worktree \u5EFA\u7ACB\u5931\u6557\uFF1A${err.message}`),
|
|
3473
|
+
{ code: "WORKTREE_CREATE_FAILED" }
|
|
3474
|
+
);
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3338
3477
|
function updateTaskFromInput(input) {
|
|
3339
3478
|
const store = input.storePath ? getStoreAtPath(input.storePath) : getStore();
|
|
3340
3479
|
const board = store.get("board");
|
|
@@ -3521,7 +3660,7 @@ async function taskCommand(argv) {
|
|
|
3521
3660
|
title: task2.title,
|
|
3522
3661
|
description: task2.description,
|
|
3523
3662
|
branch: task2.branch,
|
|
3524
|
-
worktreePath: task2.worktreePath
|
|
3663
|
+
worktreePath: task2.worktreePath ?? null
|
|
3525
3664
|
}
|
|
3526
3665
|
};
|
|
3527
3666
|
}
|
|
@@ -3563,7 +3702,7 @@ async function taskCommand(argv) {
|
|
|
3563
3702
|
title: task.title,
|
|
3564
3703
|
projectPath: task.projectPath,
|
|
3565
3704
|
branch: task.branch,
|
|
3566
|
-
worktreePath: task.worktreePath,
|
|
3705
|
+
worktreePath: task.worktreePath ?? null,
|
|
3567
3706
|
baseBranch: task.baseBranch,
|
|
3568
3707
|
agentCli: task.agentCli,
|
|
3569
3708
|
model: task.model,
|
|
@@ -4119,13 +4258,13 @@ import path20 from "path";
|
|
|
4119
4258
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
4120
4259
|
|
|
4121
4260
|
// packages/core/src/agent-models.ts
|
|
4122
|
-
import { execFile as
|
|
4261
|
+
import { execFile as execFile6 } from "child_process";
|
|
4123
4262
|
import fs12 from "fs";
|
|
4124
4263
|
import os4 from "os";
|
|
4125
4264
|
import path16 from "path";
|
|
4126
4265
|
import { promisify as promisify3 } from "util";
|
|
4127
4266
|
init_env();
|
|
4128
|
-
var pexec3 = promisify3(
|
|
4267
|
+
var pexec3 = promisify3(execFile6);
|
|
4129
4268
|
var CODEX_MODELS_TIMEOUT_MS = 2e4;
|
|
4130
4269
|
function builtinModels(id) {
|
|
4131
4270
|
return (AGENT_CLIS.find((a) => a.id === id) ?? AGENT_CLIS[0]).models;
|
|
@@ -4192,10 +4331,10 @@ async function listAgentModels(id, opts = {}) {
|
|
|
4192
4331
|
|
|
4193
4332
|
// packages/core/src/github-auth.ts
|
|
4194
4333
|
init_env();
|
|
4195
|
-
import { execFile as
|
|
4334
|
+
import { execFile as execFile7 } from "child_process";
|
|
4196
4335
|
import { promisify as promisify4 } from "util";
|
|
4197
4336
|
import * as nodePty3 from "node-pty";
|
|
4198
|
-
var pexec4 = promisify4(
|
|
4337
|
+
var pexec4 = promisify4(execFile7);
|
|
4199
4338
|
var GITHUB_HOST = "github.com";
|
|
4200
4339
|
var DEVICE_URL = "https://github.com/login/device";
|
|
4201
4340
|
var activeLogin = null;
|
|
@@ -5000,6 +5139,46 @@ function createCore({ sessions, bus, version }) {
|
|
|
5000
5139
|
deleteArtifacts(task.workspacePath ?? path20.dirname(task.worktreePath), task.worktreePath);
|
|
5001
5140
|
await deleteBranch(task.projectPath, branch);
|
|
5002
5141
|
}
|
|
5142
|
+
function columnOf(taskId) {
|
|
5143
|
+
const board = getState().board;
|
|
5144
|
+
return COLUMNS.find((col) => board[col].some((t) => t.id === taskId)) ?? null;
|
|
5145
|
+
}
|
|
5146
|
+
const provisioning = /* @__PURE__ */ new Map();
|
|
5147
|
+
function ensureProvisioned(task) {
|
|
5148
|
+
if (task.worktreePath) return Promise.resolve(task);
|
|
5149
|
+
const pending = provisioning.get(task.id);
|
|
5150
|
+
if (pending) return pending;
|
|
5151
|
+
const run3 = (async () => {
|
|
5152
|
+
try {
|
|
5153
|
+
updateTask(task.id, { ...await provisionTaskWorktree(task), launchError: void 0 });
|
|
5154
|
+
bus.emit("state:changed", getState());
|
|
5155
|
+
} catch (err) {
|
|
5156
|
+
returnUnstarted(task.id, err.message);
|
|
5157
|
+
throw err;
|
|
5158
|
+
}
|
|
5159
|
+
const provisioned = findTask(task.id);
|
|
5160
|
+
if (!provisioned) throw new Error("\u627E\u4E0D\u5230\u8981\u57F7\u884C\u7684\u4EFB\u52D9");
|
|
5161
|
+
return provisioned;
|
|
5162
|
+
})().finally(() => provisioning.delete(task.id));
|
|
5163
|
+
provisioning.set(task.id, run3);
|
|
5164
|
+
return run3;
|
|
5165
|
+
}
|
|
5166
|
+
function returnUnstarted(taskId, launchError) {
|
|
5167
|
+
const current = findTask(taskId);
|
|
5168
|
+
if (!current) return;
|
|
5169
|
+
const board = getState().board;
|
|
5170
|
+
const next = {
|
|
5171
|
+
backlog: [
|
|
5172
|
+
{ ...current, launchedAt: void 0, launchError },
|
|
5173
|
+
...board.backlog.filter((t) => t.id !== taskId)
|
|
5174
|
+
],
|
|
5175
|
+
in_progress: board.in_progress.filter((t) => t.id !== taskId),
|
|
5176
|
+
done: board.done.filter((t) => t.id !== taskId)
|
|
5177
|
+
};
|
|
5178
|
+
setBoard(next);
|
|
5179
|
+
bus.emit("state:changed", getState());
|
|
5180
|
+
}
|
|
5181
|
+
let folderDialog = null;
|
|
5003
5182
|
const handlers = {
|
|
5004
5183
|
"vibeflow:getState": () => getState(),
|
|
5005
5184
|
"app:getVersion": () => version,
|
|
@@ -5073,7 +5252,13 @@ function createCore({ sessions, bus, version }) {
|
|
|
5073
5252
|
// were new. Launching again is the caller's decision.
|
|
5074
5253
|
"vibeflow:resetTaskRun": async (taskId) => {
|
|
5075
5254
|
const task = requireTask(taskId);
|
|
5076
|
-
if (!task.worktreePath)
|
|
5255
|
+
if (!task.worktreePath) {
|
|
5256
|
+
await teardownTask(task.id);
|
|
5257
|
+
updateTask(task.id, { launchedAt: Date.now(), runId: randomUUID4() });
|
|
5258
|
+
const reset2 = findTask(task.id);
|
|
5259
|
+
if (!reset2) throw new Error("\u627E\u4E0D\u5230\u8981\u91CD\u7F6E\u7684\u4EFB\u52D9");
|
|
5260
|
+
return { state: getState(), task: reset2 };
|
|
5261
|
+
}
|
|
5077
5262
|
if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
|
|
5078
5263
|
await teardownTask(task.id);
|
|
5079
5264
|
await resetWorktreeToBase(task.worktreePath, task.baseBranch);
|
|
@@ -5087,21 +5272,25 @@ function createCore({ sessions, bus, version }) {
|
|
|
5087
5272
|
bus.emit("subagents:update", { taskId: task.id, subAgents: [] });
|
|
5088
5273
|
return { state: getState(), task: reset };
|
|
5089
5274
|
},
|
|
5090
|
-
// Edit an existing card's fields.
|
|
5091
|
-
//
|
|
5092
|
-
//
|
|
5093
|
-
//
|
|
5275
|
+
// Edit an existing card's fields. Only a Backlog card is editable: once it
|
|
5276
|
+
// runs, what it was asked to do and where it does it are history. The
|
|
5277
|
+
// git-bound fields (project, base, branch) are just stored — the worktree is
|
|
5278
|
+
// built when the card starts. A Backlog card that already has one (made
|
|
5279
|
+
// before provisioning moved to launch, or returned from a run) gives it up,
|
|
5280
|
+
// but only while it holds no changes.
|
|
5094
5281
|
"vibeflow:updateTask": async (payload) => {
|
|
5095
5282
|
const p = obj(payload, "payload");
|
|
5096
5283
|
const existing = requireTask(p.taskId);
|
|
5284
|
+
if (columnOf(existing.id) !== "backlog") {
|
|
5285
|
+
throw new Error("\u53EA\u6709 Backlog \u7684\u4EFB\u52D9\u53EF\u4EE5\u7DE8\u8F2F");
|
|
5286
|
+
}
|
|
5097
5287
|
let gitPatch = {};
|
|
5098
5288
|
const nextProject = p.projectPath || existing.projectPath;
|
|
5289
|
+
const nextBranch = p.branch === void 0 ? "" : str(p.branch, "branch").trim();
|
|
5099
5290
|
const projectChanged2 = Boolean(p.projectPath && p.projectPath !== existing.projectPath);
|
|
5100
5291
|
const baseChanged = p.baseBranch != null && p.baseBranch !== (existing.baseBranch ?? null);
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
throw new Error("\u4EFB\u52D9\u5DF2\u958B\u59CB\u57F7\u884C\uFF0C\u7121\u6CD5\u66F4\u63DB\u5C08\u6848\u8CC7\u6599\u593E\u6216\u57FA\u6E96\u5206\u652F");
|
|
5104
|
-
}
|
|
5292
|
+
const branchChanged = Boolean(nextBranch && nextBranch !== existing.branch);
|
|
5293
|
+
if (nextProject && (projectChanged2 || baseChanged || branchChanged)) {
|
|
5105
5294
|
if (existing.worktreePath) {
|
|
5106
5295
|
const diff = await getWorktreeDiffEntries(
|
|
5107
5296
|
existing.worktreePath,
|
|
@@ -5109,35 +5298,30 @@ function createCore({ sessions, bus, version }) {
|
|
|
5109
5298
|
{ fetch: false }
|
|
5110
5299
|
);
|
|
5111
5300
|
if (diff.length > 0) {
|
|
5112
|
-
throw new Error("\u76EE\u524D worktree \u5DF2\u6709\u8B8A\u66F4\uFF0C\u7121\u6CD5\u66F4\u63DB\u5C08\u6848\u8CC7\u6599\u593E\
|
|
5301
|
+
throw new Error("\u76EE\u524D worktree \u5DF2\u6709\u8B8A\u66F4\uFF0C\u7121\u6CD5\u66F4\u63DB\u5C08\u6848\u8CC7\u6599\u593E\u3001\u57FA\u6E96\u5206\u652F\u6216\u5206\u652F\u540D\u7A31");
|
|
5113
5302
|
}
|
|
5114
5303
|
}
|
|
5115
5304
|
const info = await getGitInfo(nextProject);
|
|
5116
5305
|
if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
|
|
5117
|
-
await teardownTask(existing.id);
|
|
5118
|
-
await removeTaskWorktree(existing);
|
|
5119
5306
|
const projectName = path20.basename(nextProject);
|
|
5120
5307
|
const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
|
|
5121
|
-
await
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
workspacePath,
|
|
5125
|
-
existing.id,
|
|
5126
|
-
p.baseBranch ?? existing.baseBranch ?? null,
|
|
5127
|
-
existing.branch
|
|
5128
|
-
);
|
|
5308
|
+
await teardownTask(existing.id);
|
|
5309
|
+
await removeTaskWorktree(existing);
|
|
5310
|
+
if (branchChanged) await assertBranchAvailable(nextProject, workspacePath, nextBranch);
|
|
5129
5311
|
recordRecentProject(getStore(), nextProject);
|
|
5130
5312
|
gitPatch = {
|
|
5131
5313
|
projectPath: nextProject,
|
|
5132
5314
|
projectName,
|
|
5133
|
-
branch: result.branch,
|
|
5134
|
-
worktreePath: result.worktreePath,
|
|
5135
5315
|
workspacePath,
|
|
5136
|
-
baseBranch:
|
|
5137
|
-
|
|
5316
|
+
baseBranch: (p.baseBranch ?? (projectChanged2 ? null : existing.baseBranch)) || info.defaultBase || info.currentBranch || void 0,
|
|
5317
|
+
...branchChanged ? { branch: nextBranch, branchExplicit: true } : {},
|
|
5318
|
+
worktreePath: void 0,
|
|
5319
|
+
pushed: void 0,
|
|
5320
|
+
launchedAt: void 0
|
|
5138
5321
|
};
|
|
5139
5322
|
}
|
|
5140
5323
|
updateTask(existing.id, {
|
|
5324
|
+
launchError: void 0,
|
|
5141
5325
|
title: (p.title ?? "").trim() || `Task ${existing.id}`,
|
|
5142
5326
|
description: p.description?.trim() || void 0,
|
|
5143
5327
|
agentCli: p.agentCli,
|
|
@@ -5153,10 +5337,12 @@ function createCore({ sessions, bus, version }) {
|
|
|
5153
5337
|
// string or a cwd, so a connected client cannot run anything else.
|
|
5154
5338
|
"pty:start": async (payload) => {
|
|
5155
5339
|
const p = obj(payload, "payload");
|
|
5156
|
-
const
|
|
5157
|
-
const key = sessionKeyFor(
|
|
5158
|
-
const
|
|
5159
|
-
|
|
5340
|
+
const requested = requireTask(p.taskId);
|
|
5341
|
+
const key = sessionKeyFor(requested.id, p.sessionKey);
|
|
5342
|
+
const pending = provisioning.get(requested.id);
|
|
5343
|
+
const task = p.launch ? await ensureProvisioned(requested) : pending ? await pending : requested;
|
|
5344
|
+
const cwd = task.worktreePath;
|
|
5345
|
+
if (!cwd) throw new Error("\u4EFB\u52D9\u5C1A\u672A\u958B\u59CB\u57F7\u884C\uFF0C\u9084\u6C92\u6709 worktree");
|
|
5160
5346
|
let command;
|
|
5161
5347
|
let fresh = p.fresh === true;
|
|
5162
5348
|
if (p.launch) {
|
|
@@ -5260,7 +5446,7 @@ function createCore({ sessions, bus, version }) {
|
|
|
5260
5446
|
const task = requireTask(taskId);
|
|
5261
5447
|
const base = task.baseBranch ?? "main";
|
|
5262
5448
|
if (task.worktreePath) return readWorktreeSpecs(task.worktreePath, base);
|
|
5263
|
-
if (!task.projectPath) return [];
|
|
5449
|
+
if (!task.projectPath || !task.launchedAt) return [];
|
|
5264
5450
|
const changed = task.outcome?.files.filter((f) => f.status !== "D").map((f) => f.path);
|
|
5265
5451
|
return readBranchSpecs(task.projectPath, task.branch, base, changed);
|
|
5266
5452
|
},
|
|
@@ -5333,6 +5519,19 @@ function createCore({ sessions, bus, version }) {
|
|
|
5333
5519
|
if (!task.worktreePath) return null;
|
|
5334
5520
|
return getGithubCompareUrl(task.worktreePath, task.baseBranch ?? "main");
|
|
5335
5521
|
},
|
|
5522
|
+
// One dialog at a time: a second click while it is open joins it instead
|
|
5523
|
+
// of stacking another window on the host.
|
|
5524
|
+
"dialog:pickFolder": (options) => {
|
|
5525
|
+
const o = options === void 0 || options === null ? {} : obj(options, "options");
|
|
5526
|
+
const title = o.title === void 0 ? void 0 : str(o.title, "title");
|
|
5527
|
+
const defaultPath = o.defaultPath === void 0 ? void 0 : str(o.defaultPath, "defaultPath");
|
|
5528
|
+
const pick = getPlatform().pickFolder;
|
|
5529
|
+
if (!pick) return { unsupported: true };
|
|
5530
|
+
folderDialog ??= pick({ title, defaultPath: defaultPath || void 0 }).finally(() => {
|
|
5531
|
+
folderDialog = null;
|
|
5532
|
+
});
|
|
5533
|
+
return folderDialog;
|
|
5534
|
+
},
|
|
5336
5535
|
"shell:openExternal": (url) => {
|
|
5337
5536
|
const u = str(url, "url");
|
|
5338
5537
|
if (!/^https?:\/\//i.test(u)) invalid("only http(s) URLs can be opened");
|
|
@@ -5365,6 +5564,9 @@ function createCore({ sessions, bus, version }) {
|
|
|
5365
5564
|
deleteDecisions(task.workspacePath, decisionsKey(task.worktreePath, task.branch));
|
|
5366
5565
|
}
|
|
5367
5566
|
await removeTaskWorktree(task);
|
|
5567
|
+
if (task.workspacePath) {
|
|
5568
|
+
fs16.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
|
|
5569
|
+
}
|
|
5368
5570
|
clearConversation(task.id);
|
|
5369
5571
|
removeTask(task.id);
|
|
5370
5572
|
return getState();
|
|
@@ -5372,9 +5574,11 @@ function createCore({ sessions, bus, version }) {
|
|
|
5372
5574
|
"attachments:write": (payload) => {
|
|
5373
5575
|
const p = obj(payload, "payload");
|
|
5374
5576
|
const task = requireTask(p.taskId);
|
|
5375
|
-
if (!task.worktreePath) throw new Error("\u627E\u4E0D\u5230\u4EFB\u52D9 worktree");
|
|
5376
5577
|
if (!Array.isArray(p.attachments)) invalid("attachments must be an array");
|
|
5377
|
-
|
|
5578
|
+
const inputs = p.attachments;
|
|
5579
|
+
if (task.worktreePath) return writeAttachments(task.worktreePath, inputs);
|
|
5580
|
+
if (!task.workspacePath) throw new Error("\u627E\u4E0D\u5230\u4EFB\u52D9 worktree");
|
|
5581
|
+
return writeAttachmentsTo(taskAttachmentStagingPath(task.workspacePath, task.id), inputs);
|
|
5378
5582
|
},
|
|
5379
5583
|
"chat:load": (taskId) => loadConversation(requireTask(taskId).id),
|
|
5380
5584
|
"chat:cancel": (taskId) => {
|
|
@@ -5875,12 +6079,12 @@ function connectToHost(lock) {
|
|
|
5875
6079
|
// packages/cli/src/doctor.ts
|
|
5876
6080
|
import fs19 from "fs";
|
|
5877
6081
|
import path23 from "path";
|
|
5878
|
-
import { execFile as
|
|
6082
|
+
import { execFile as execFile8 } from "child_process";
|
|
5879
6083
|
import { promisify as promisify5 } from "util";
|
|
5880
6084
|
init_env();
|
|
5881
6085
|
init_git_bash();
|
|
5882
6086
|
init_tmux_backend();
|
|
5883
|
-
var pexec5 = promisify5(
|
|
6087
|
+
var pexec5 = promisify5(execFile8);
|
|
5884
6088
|
function parseVersion(text) {
|
|
5885
6089
|
const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
|
|
5886
6090
|
return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
|
|
@@ -5892,7 +6096,7 @@ function versionAtLeast(have, want) {
|
|
|
5892
6096
|
}
|
|
5893
6097
|
return true;
|
|
5894
6098
|
}
|
|
5895
|
-
async function
|
|
6099
|
+
async function run2(cmd, args) {
|
|
5896
6100
|
try {
|
|
5897
6101
|
const { stdout } = await pexec5(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
|
|
5898
6102
|
return String(stdout).trim();
|
|
@@ -5952,7 +6156,7 @@ async function runDoctor(options) {
|
|
|
5952
6156
|
checks.push(
|
|
5953
6157
|
versionAtLeast(nodeVersion, [22, 0]) ? { id: "node", name: "Node.js", level: "ok", detail: `v${process.versions.node}` } : { id: "node", name: "Node.js", level: "fail", detail: `v${process.versions.node}\uFF0C\u9700\u8981 22 \u4EE5\u4E0A`, fix: installHint("node") }
|
|
5954
6158
|
);
|
|
5955
|
-
const git2 = await
|
|
6159
|
+
const git2 = await run2("git", ["--version"]);
|
|
5956
6160
|
if (!git2) {
|
|
5957
6161
|
checks.push({ id: "git", name: "Git", level: "fail", detail: "PATH \u4E2D\u627E\u4E0D\u5230 git", fix: installHint("git") });
|
|
5958
6162
|
} else {
|
|
@@ -5980,7 +6184,7 @@ async function runDoctor(options) {
|
|
|
5980
6184
|
fix: "\u8981\u8B93\u4EFB\u52D9\u5728\u80CC\u666F\u6301\u7E8C\u57F7\u884C\uFF0C\u8ACB\u5728 WSL \u5167\u4F7F\u7528 vibeflow"
|
|
5981
6185
|
});
|
|
5982
6186
|
} else {
|
|
5983
|
-
const tmux = await
|
|
6187
|
+
const tmux = await run2("tmux", ["-V"]);
|
|
5984
6188
|
checks.push(
|
|
5985
6189
|
tmux && hasTmux() ? { id: "tmux", name: "tmux", level: "ok", detail: `${tmux}\uFF08\u4EFB\u52D9\u53EF\u5728\u80CC\u666F\u6301\u7E8C\u57F7\u884C\uFF09` } : {
|
|
5986
6190
|
id: "tmux",
|