@kw-yeh/vibeflow 4.7.1 → 4.8.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 CHANGED
@@ -499,6 +499,196 @@ var init_tmux_backend = __esm({
499
499
  }
500
500
  });
501
501
 
502
+ // packages/core/src/launch.ts
503
+ function pathBasename(p) {
504
+ const norm = p.replace(/\\/g, "/").replace(/\/+$/, "");
505
+ const i = norm.lastIndexOf("/");
506
+ return i >= 0 ? norm.slice(i + 1) : norm;
507
+ }
508
+ function agentArtifactsDir(worktreePath, workspacePath) {
509
+ if (!worktreePath || !workspacePath) return null;
510
+ const dir = toShellPath(workspacePath);
511
+ const ws = pathBasename(worktreePath);
512
+ return `${dir}/${ws}${ARTIFACTS_DIR_SUFFIX}`;
513
+ }
514
+ function buildArtifactPrompt(artifactsDir = ARTIFACTS_FALLBACK_DIR) {
515
+ return [
516
+ "Artifact \u8A2D\u5B9A\uFF08\u672C\u6B21 session \u5168\u7A0B\u9069\u7528\uFF09\uFF1A",
517
+ `1. \u672C\u4EFB\u52D9\u7684 Artifact \u8CC7\u6599\u593E\u662F ${artifactsDir}/\uFF1B\u9700\u8981\u4FDD\u5B58\u7D66\u4F7F\u7528\u8005\u7684\u9A57\u8B49\u8B49\u64DA\u6216\u5831\u544A\u6642\uFF0C\u8ACB\u5BEB\u5165\u6B64\u8CC7\u6599\u593E\uFF08\u4E0D\u5B58\u5728\u8ACB\u5EFA\u7ACB\uFF09\u3002`,
518
+ `2. \u4F7F\u7528\u8005\u6703\u5728 VibeFlow \u7684 Artifacts \u5206\u9801\u6AA2\u8996\u6839\u76EE\u9304\u5167\u5BB9\uFF1B\u53EA\u653E\u4F60\u6703\u4E3B\u52D5\u8ACB\u4F7F\u7528\u8005\u904E\u76EE\u7684\u6A94\u6848\u3002`,
519
+ `3. \u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u66AB\u5B58\uFF08\u4E00\u6B21\u6027 script\u3001log\u3001\u4E2D\u9593\u8F38\u51FA\u3001debug \u6A94\uFF09\u653E\u5728 ${artifactsDir}/${SCRATCH_DIR_NAME}/\u3002`,
520
+ `4. \u82E5\u5DE5\u5177\u5148\u8F38\u51FA\u5230 /tmp\u3001/private/tmp\u3001$TMPDIR \u6216 ~/Downloads\uFF0C\u8ACB\u628A\u6700\u7D42\u8981\u5448\u73FE\u7684\u6A94\u6848\u8907\u88FD\u5230 ${artifactsDir}/ \u4E26\u78BA\u8A8D\u5B58\u5728\u3002`,
521
+ "5. Artifact \u8CC7\u6599\u593E\u4F4D\u65BC worktree \u4E4B\u5916\uFF1B\u4E0D\u8981\u628A\u5176\u4E2D\u5167\u5BB9\u52A0\u5165 git commit\uFF0C\u4E5F\u4E0D\u8981\u628A\u6700\u7D42\u4EA4\u4ED8\u7269\u53EA\u7559\u5728\u9019\u88E1\u3002"
522
+ ].join("\n");
523
+ }
524
+ function claudePermissionFlag(autoMode) {
525
+ return autoMode ? ` --permission-mode ${AUTO_PERMISSION_MODE}` : "";
526
+ }
527
+ function shellQuote(s) {
528
+ return `'${s.replace(/'/g, `'\\''`)}'`;
529
+ }
530
+ function toShellPath(p) {
531
+ return p.replace(/\\/g, "/");
532
+ }
533
+ function boardEnvPrefix(task, opts) {
534
+ const board = opts?.boardCli;
535
+ if (!board) return "";
536
+ const entries = [
537
+ ["VIBEFLOW_TASK_ID", task.id],
538
+ ["VIBEFLOW_PROJECT_PATH", task.projectPath],
539
+ ["VIBEFLOW_BRANCH", task.branch],
540
+ ["VIBEFLOW_BASE_BRANCH", task.baseBranch],
541
+ ["VIBEFLOW_STORE_DIR", board?.storeDir],
542
+ ["VIBEFLOW_CLI", board?.cliPath],
543
+ ["VIBEFLOW_CLI_LOADER", board?.loaderPath],
544
+ ["VIBEFLOW_AUTO_MODE", opts?.autoMode ? "1" : "0"]
545
+ ];
546
+ const exports = entries.filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${shellQuote(toShellPath(value))}`);
547
+ return exports.length ? `export ${exports.join(" ")}; ` : "";
548
+ }
549
+ function codexAutoFlag(autoMode) {
550
+ return autoMode ? "--dangerously-bypass-approvals-and-sandbox " : "";
551
+ }
552
+ function claudeResumeOrFresh(sessionId, worktreePath, tail) {
553
+ if (!worktreePath) return `claude --resume ${sessionId} ${tail}\r`;
554
+ const munged = worktreePath.replace(/[^a-zA-Z0-9]/g, "-");
555
+ const sessionFile = `"$HOME/.claude/projects/${munged}/${sessionId}.jsonl"`;
556
+ return `if [ -f ${sessionFile} ]; then claude --resume ${sessionId} ${tail}; else claude --session-id ${sessionId} ${tail}; fi\r`;
557
+ }
558
+ function buildClaudeSettings(worktreePath) {
559
+ const settings = { theme: "dark" };
560
+ if (worktreePath) {
561
+ const dir = `${toShellPath(worktreePath)}/${SUBAGENTS_DIR2}`;
562
+ const command = `mkdir -p "${dir}" && cat > "${dir}/$(date +%s)-$$-$RANDOM.json"`;
563
+ const taskHook = {
564
+ matcher: "Task",
565
+ hooks: [{ type: "command", command }]
566
+ };
567
+ const eventsDir = `${toShellPath(worktreePath)}/${PROGRESS_EVENTS_DIR}`;
568
+ const progress = {
569
+ hooks: [{ type: "command", command: `mkdir -p "${eventsDir}" && cat > "${eventsDir}/$(date +%s)-$$-$RANDOM.json"; exit 0` }]
570
+ };
571
+ settings.hooks = {
572
+ PreToolUse: [taskHook],
573
+ PostToolUse: [taskHook, { matcher: TODO_TOOLS, ...progress }],
574
+ Stop: [progress],
575
+ Notification: [progress],
576
+ UserPromptSubmit: [progress]
577
+ };
578
+ }
579
+ return JSON.stringify(settings);
580
+ }
581
+ function resolveSystemPrompt(custom) {
582
+ return custom && custom.trim() ? custom : "";
583
+ }
584
+ function buildPrompt(task) {
585
+ const lines = [`\u4EFB\u52D9\u6A19\u984C\uFF1A${task.title}`];
586
+ const description = task.description?.trim();
587
+ if (description) {
588
+ lines.push("", "\u4EFB\u52D9\u63CF\u8FF0\uFF1A", description);
589
+ }
590
+ return lines.join("\n");
591
+ }
592
+ function executorSessionId(taskId, runId) {
593
+ const source = runId ? `${taskId}:${runId}` : taskId;
594
+ const raw = source.replace(/[^0-9a-f]/gi, "").toLowerCase();
595
+ const hex = runId ? `${raw.slice(0, 24).padEnd(24, "0")}${namespaceHash(`executor:${source}`)}` : raw.padEnd(32, "0").slice(0, 32);
596
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-8${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
597
+ }
598
+ function namespaceHash(namespace) {
599
+ let hash = 2166136261;
600
+ for (let i = 0; i < namespace.length; i += 1) {
601
+ hash ^= namespace.charCodeAt(i);
602
+ hash = Math.imul(hash, 16777619);
603
+ }
604
+ return (hash >>> 0).toString(16).padStart(8, "0");
605
+ }
606
+ function assembleCommand(agent, systemPrompt, prompt, model, effort, opts, worktreePath, sessionId, workspacePath) {
607
+ let cmd;
608
+ if (agent === "claude") {
609
+ const settings = ` --settings ${shellQuote(buildClaudeSettings(worktreePath))}`;
610
+ const addDir = workspacePath ? ` --add-dir ${shellQuote(toShellPath(workspacePath))}` : "";
611
+ const modelFlag = model ? ` --model ${model}` : "";
612
+ const effortFlag = effort ? ` --effort ${effort}` : "";
613
+ const library = opts?.library;
614
+ const libraryFlags = library ? ` --plugin-dir ${shellQuote(toShellPath(library.pluginDir))} --add-dir ${shellQuote(toShellPath(library.libraryDir))}` : "";
615
+ const flags = `--chrome${claudePermissionFlag(opts?.autoMode)}${modelFlag}${effortFlag}${settings}${addDir}${libraryFlags}`;
616
+ const sysFlag = systemPrompt ? ` --append-system-prompt ${shellQuote(systemPrompt)}` : "";
617
+ const promptArg = prompt ? ` ${shellQuote(prompt)}` : "";
618
+ const tail = `${flags}${sysFlag}${promptArg}`;
619
+ cmd = sessionId && opts?.resume ? claudeResumeOrFresh(sessionId, worktreePath, tail) : `claude ${sessionId ? `--session-id ${sessionId} ` : opts?.resume ? "--continue " : ""}${tail}\r`;
620
+ } else {
621
+ const combined = [systemPrompt, prompt].filter(Boolean).join("\n\n");
622
+ const codexEffortFlag = effort ? `-c ${shellQuote(`model_reasoning_effort="${effort}"`)} ` : "";
623
+ const codexHome = opts?.library ? `CODEX_HOME=${shellQuote(toShellPath(opts.library.codexHome))} ` : "";
624
+ const promptArg = combined ? ` ${shellQuote(combined)}` : "";
625
+ cmd = `${codexHome}codex ${codexAutoFlag(opts?.autoMode)}${codexEffortFlag}--model ${model}${promptArg}\r`;
626
+ }
627
+ if (cmd.length > 2e5) console.warn(`[VibeFlow] launch command is ${cmd.length} bytes \u2014 approaching ARG_MAX`);
628
+ return cmd;
629
+ }
630
+ function normalizeModel(agent, model) {
631
+ return LEGACY_MODEL_FALLBACKS[agent]?.[model] ?? model;
632
+ }
633
+ function taskAgent(task) {
634
+ const agent = task.agentCli;
635
+ return agent && agent in AGENT_NAMES ? agent : "claude";
636
+ }
637
+ function taskModel(task) {
638
+ const agent = taskAgent(task);
639
+ return normalizeModel(agent, task.model || DEFAULT_MODELS[agent]);
640
+ }
641
+ function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
642
+ const agent = taskAgent(task);
643
+ const model = taskModel(task);
644
+ const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? ARTIFACTS_FALLBACK_DIR;
645
+ const builtInPrompt = buildArtifactPrompt(artifactsDir);
646
+ const libraryPrompt = opts?.library?.promptText?.trim();
647
+ const customPrompt = resolveSystemPrompt(systemPrompt);
648
+ const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
649
+ const includeTaskPrompt = opts?.includeTaskPrompt !== false;
650
+ const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
651
+ const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
652
+ return boardEnvPrefix(task, opts) + assembleCommand(
653
+ agent,
654
+ sys,
655
+ prompt,
656
+ model,
657
+ task.effort,
658
+ opts,
659
+ task.worktreePath,
660
+ sessionId,
661
+ workspacePath
662
+ );
663
+ }
664
+ var ARTIFACTS_DIR_SUFFIX, SCRATCH_DIR_NAME, ARTIFACTS_FALLBACK_DIR, AUTO_PERMISSION_MODE, SUBAGENTS_DIR2, PROGRESS_EVENTS_DIR, TODO_TOOLS, AGENT_NAMES, DEFAULT_MODELS, LEGACY_MODEL_FALLBACKS;
665
+ var init_launch = __esm({
666
+ "packages/core/src/launch.ts"() {
667
+ "use strict";
668
+ ARTIFACTS_DIR_SUFFIX = ".artifacts";
669
+ SCRATCH_DIR_NAME = "scratch";
670
+ ARTIFACTS_FALLBACK_DIR = ".vibeflow-artifacts";
671
+ AUTO_PERMISSION_MODE = "auto";
672
+ SUBAGENTS_DIR2 = ".vibeflow-subagents";
673
+ PROGRESS_EVENTS_DIR = ".vibeflow-events";
674
+ TODO_TOOLS = "TodoWrite|TaskCreate|TaskUpdate";
675
+ AGENT_NAMES = {
676
+ claude: "Claude Code",
677
+ codex: "Codex CLI"
678
+ };
679
+ DEFAULT_MODELS = {
680
+ claude: "sonnet",
681
+ codex: "gpt-5.5"
682
+ };
683
+ LEGACY_MODEL_FALLBACKS = {
684
+ codex: {
685
+ "gpt-5-codex": "gpt-5.5",
686
+ "gpt-5": "gpt-5.5"
687
+ }
688
+ };
689
+ }
690
+ });
691
+
502
692
  // packages/core/src/attachments.ts
503
693
  import { randomUUID } from "crypto";
504
694
  import { execFileSync as execFileSync3 } from "child_process";
@@ -599,6 +789,33 @@ var init_attachments = __esm({
599
789
  }
600
790
  });
601
791
 
792
+ // packages/core/src/effort.ts
793
+ function isAgentEffort(value) {
794
+ return typeof value === "string" && AGENT_EFFORTS.includes(value);
795
+ }
796
+ function allowedEfforts(model) {
797
+ return model?.efforts ?? null;
798
+ }
799
+ function selectableEfforts(model) {
800
+ return allowedEfforts(model) ?? UNKNOWN_MODEL_EFFORTS;
801
+ }
802
+ function clampEffort(effort, allowed) {
803
+ if (allowed.length === 0) return void 0;
804
+ if (effort === void 0 || allowed.includes(effort)) return effort;
805
+ const rank = AGENT_EFFORTS.indexOf(effort);
806
+ const ordered = AGENT_EFFORTS.filter((level) => allowed.includes(level));
807
+ const notDeeper = ordered.filter((level) => AGENT_EFFORTS.indexOf(level) <= rank);
808
+ return notDeeper.length > 0 ? notDeeper[notDeeper.length - 1] : ordered[0];
809
+ }
810
+ var AGENT_EFFORTS, UNKNOWN_MODEL_EFFORTS;
811
+ var init_effort = __esm({
812
+ "packages/core/src/effort.ts"() {
813
+ "use strict";
814
+ AGENT_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
815
+ UNKNOWN_MODEL_EFFORTS = ["low", "medium", "high", "xhigh"];
816
+ }
817
+ });
818
+
602
819
  // packages/core/src/progress.ts
603
820
  function emptyUsage() {
604
821
  return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0 };
@@ -883,9 +1100,9 @@ var init_progress = __esm({
883
1100
 
884
1101
  // packages/tui/src/task-form.ts
885
1102
  import { spawnSync } from "child_process";
886
- import fs21 from "fs";
887
- import os7 from "os";
888
- import path25 from "path";
1103
+ import fs22 from "fs";
1104
+ import os9 from "os";
1105
+ import path26 from "path";
889
1106
  import React, { useEffect, useRef, useState } from "react";
890
1107
  import { Box, Text, render, useApp, useInput } from "ink";
891
1108
  function newFormState(defaultAutoMode, agents) {
@@ -951,11 +1168,19 @@ function visibleFields(s, ctx) {
951
1168
  return fields;
952
1169
  }
953
1170
  function modelOptions(ctx, agentCli, current) {
954
- const listed = ctx.models[agentCli] ?? [];
1171
+ const listed = (ctx.models[agentCli] ?? []).map((m) => m.id);
955
1172
  const options = ["", ...listed];
956
1173
  if (current && !listed.includes(current)) options.push(current);
957
1174
  return options;
958
1175
  }
1176
+ function effortOptions(ctx, agentCli, model) {
1177
+ const id = taskModel({ agentCli, model });
1178
+ return selectableEfforts(ctx.models[agentCli]?.find((m) => m.id === id));
1179
+ }
1180
+ function fitEffort(ctx, s) {
1181
+ const effort = clampEffort(s.effort, effortOptions(ctx, s.agentCli, s.model)) ?? s.effort;
1182
+ return effort === s.effort ? s : { ...s, effort };
1183
+ }
959
1184
  function blockReason(s, ctx, loadingGit) {
960
1185
  if (!s.title.trim()) return "\u8ACB\u8F38\u5165\u4EFB\u52D9\u6A19\u984C";
961
1186
  if (loadingGit) return "\u5075\u6E2C Git \u72C0\u614B\u4E2D\u2026";
@@ -1004,17 +1229,17 @@ function editorCommand(env, platform) {
1004
1229
  return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
1005
1230
  }
1006
1231
  function editInEditor(text) {
1007
- const dir = fs21.mkdtempSync(path25.join(os7.tmpdir(), "vibeflow-"));
1008
- const file = path25.join(dir, "description.md");
1232
+ const dir = fs22.mkdtempSync(path26.join(os9.tmpdir(), "vibeflow-"));
1233
+ const file = path26.join(dir, "description.md");
1009
1234
  try {
1010
- fs21.writeFileSync(file, text);
1235
+ fs22.writeFileSync(file, text);
1011
1236
  const cmd = editorCommand(process.env, process.platform);
1012
1237
  const r = spawnSync(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
1013
1238
  if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
1014
1239
  if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
1015
- return { text: fs21.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
1240
+ return { text: fs22.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
1016
1241
  } finally {
1017
- fs21.rmSync(dir, { recursive: true, force: true });
1242
+ fs22.rmSync(dir, { recursive: true, force: true });
1018
1243
  }
1019
1244
  }
1020
1245
  function cycle(list, current, step) {
@@ -1085,10 +1310,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1085
1310
  if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
1086
1311
  else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
1087
1312
  else if (editing === "project") {
1088
- if (value2) setProject(path25.resolve(value2.replace(/^~(?=$|\/)/, os7.homedir())));
1313
+ if (value2) setProject(path26.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir())));
1089
1314
  } else if (editing === "attachments" && value2) {
1090
- const file = path25.resolve(value2.replace(/^~(?=$|\/)/, os7.homedir()));
1091
- if (!fs21.existsSync(file) || !fs21.statSync(file).isFile()) {
1315
+ const file = path26.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir()));
1316
+ if (!fs22.existsSync(file) || !fs22.statSync(file).isFile()) {
1092
1317
  setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
1093
1318
  return;
1094
1319
  }
@@ -1127,13 +1352,16 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1127
1352
  } else if (field === "baseBranch") {
1128
1353
  setS((prev) => ({ ...prev, baseBranch: cycle(prev.gitInfo?.branches ?? [], prev.baseBranch, step) }));
1129
1354
  } else if (field === "effort") {
1130
- setS((prev) => ({ ...prev, effort: cycle(EFFORTS.map((e) => e.value), prev.effort, step) }));
1355
+ setS((prev) => {
1356
+ const levels = effortOptions(ctx, prev.agentCli, prev.model);
1357
+ return levels.length ? { ...prev, effort: cycle([...levels], prev.effort, step) } : prev;
1358
+ });
1131
1359
  } else if (field === "autoMode") {
1132
1360
  setS((prev) => ({ ...prev, autoMode: !prev.autoMode }));
1133
1361
  } else if (field === "agent" && ctx.agents?.length) {
1134
- setS((prev) => ({ ...prev, agentCli: cycle(ctx.agents.map((a) => a.id), prev.agentCli, step), model: "" }));
1362
+ setS((prev) => fitEffort(ctx, { ...prev, agentCli: cycle(ctx.agents.map((a) => a.id), prev.agentCli, step), model: "" }));
1135
1363
  } else if (field === "model") {
1136
- setS((prev) => ({ ...prev, model: cycle(modelOptions(ctx, prev.agentCli, prev.model), prev.model, step) }));
1364
+ setS((prev) => fitEffort(ctx, { ...prev, model: cycle(modelOptions(ctx, prev.agentCli, prev.model), prev.model, step) }));
1137
1365
  }
1138
1366
  return;
1139
1367
  }
@@ -1168,14 +1396,15 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1168
1396
  case "branch":
1169
1397
  return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
1170
1398
  case "attachments":
1171
- return s.attachments.length ? s.attachments.map((a) => path25.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
1399
+ return s.attachments.length ? s.attachments.map((a) => path26.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
1172
1400
  case "description": {
1173
1401
  if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
1174
1402
  const lines = s.description.split("\n");
1175
1403
  return lines.length > 1 ? `${lines[0]} \u2026\uFF08\u5171 ${lines.length} \u884C\uFF09` : lines[0];
1176
1404
  }
1177
1405
  case "effort": {
1178
- const e = EFFORTS.find((x) => x.value === s.effort);
1406
+ if (effortOptions(ctx, s.agentCli, s.model).length === 0) return "\u4E0D\u9069\u7528";
1407
+ const e = EFFORTS.find((x) => x.value === s.effort) ?? EFFORTS[0];
1179
1408
  return `\u2039 ${e.label} \xB7 ${e.value} \u203A`;
1180
1409
  }
1181
1410
  case "autoMode":
@@ -1185,8 +1414,11 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1185
1414
  if (ctx.agents.length === 0) return "\u672A\u5075\u6E2C\u5230 Agent CLI\uFF08claude / codex\uFF09";
1186
1415
  return `\u2039 ${ctx.agents.find((a) => a.id === s.agentCli)?.name ?? s.agentCli} \u203A`;
1187
1416
  }
1188
- case "model":
1189
- return modelOptions(ctx, s.agentCli, s.model).length > 1 ? `\u2039 ${s.model || "\u4F7F\u7528\u9810\u8A2D model"} \u203A` : "\u7121\u6CD5\u53D6\u5F97 model list\uFF0C\u5C07\u4F7F\u7528\u9810\u8A2D model";
1417
+ case "model": {
1418
+ if (modelOptions(ctx, s.agentCli, s.model).length <= 1) return "\u7121\u6CD5\u53D6\u5F97 model list\uFF0C\u5C07\u4F7F\u7528\u9810\u8A2D model";
1419
+ const listed = ctx.models[s.agentCli]?.find((m) => m.id === s.model);
1420
+ return `\u2039 ${s.model ? listed?.label ?? s.model : "\u4F7F\u7528\u9810\u8A2D model"} \u203A`;
1421
+ }
1190
1422
  case "submit":
1191
1423
  return busy ?? (s.mode === "new" ? "\uFF3B \u5EFA\u7ACB\u4EFB\u52D9 \uFF3D" : "\uFF3B \u5132\u5B58\u8B8A\u66F4 \uFF3D");
1192
1424
  }
@@ -1199,13 +1431,17 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
1199
1431
  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";
1200
1432
  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";
1201
1433
  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`;
1202
- if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path25.basename(s.projectPath)}`;
1434
+ if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path26.basename(s.projectPath)}`;
1203
1435
  }
1204
1436
  if (field === "branch") {
1205
1437
  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";
1206
1438
  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;
1207
1439
  }
1208
- 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`;
1440
+ if (field === "effort") {
1441
+ if (effortOptions(ctx, s.agentCli, s.model).length === 0) return "\u9019\u500B model \u4E0D\u652F\u63F4\u8ABF\u6574\u63A8\u7406\u5F37\u5EA6\uFF0C\u555F\u52D5\u6642\u4E0D\u6703\u6307\u5B9A\u4EFB\u52D9\u8907\u96DC\u5EA6\u3002";
1442
+ const e = EFFORTS.find((x) => x.value === s.effort) ?? EFFORTS[0];
1443
+ return `${e.description} Claude Code \u8207 Codex \u6703\u5728\u555F\u52D5\u6B64\u4EFB\u52D9\u6642\u5957\u7528\uFF1B\u53EF\u9078\u7684\u7D1A\u8DDD\u4F9D model \u800C\u5B9A\u3002`;
1444
+ }
1209
1445
  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";
1210
1446
  if (field === "submit") return blockReason(s, ctx, loadingGit);
1211
1447
  return null;
@@ -1260,7 +1496,7 @@ async function loadFormContext(api) {
1260
1496
  const lists = await Promise.all(
1261
1497
  (agents ?? []).map(async (a) => {
1262
1498
  const list = await api.listAgentModels(a.id).catch(() => null);
1263
- return [a.id, list ? list.models.map((m) => m.id) : []];
1499
+ return [a.id, list ? list.models : []];
1264
1500
  })
1265
1501
  );
1266
1502
  return {
@@ -1277,13 +1513,17 @@ var h, EFFORTS, DEFAULT_EFFORT, LABELS, TEXT_FIELDS;
1277
1513
  var init_task_form = __esm({
1278
1514
  "packages/tui/src/task-form.ts"() {
1279
1515
  "use strict";
1516
+ init_effort();
1517
+ init_launch();
1280
1518
  init_attachments();
1281
1519
  h = React.createElement;
1282
1520
  EFFORTS = [
1283
1521
  { value: "low", label: "\u5FEB\u901F", description: "\u9069\u5408\u7BC4\u570D\u660E\u78BA\u3001\u4F4E\u98A8\u96AA\u7684\u5C0F\u4EFB\u52D9\u3002" },
1284
1522
  { value: "medium", label: "\u6A19\u6E96", description: "\u517C\u9867\u901F\u5EA6\u8207\u63A8\u7406\u6DF1\u5EA6\uFF0C\u9069\u5408\u4E00\u822C\u958B\u767C\u4EFB\u52D9\u3002" },
1285
1523
  { value: "high", label: "\u6DF1\u5165", description: "\u9069\u5408\u8DE8\u6A94\u6848\u3001\u9664\u932F\u6216\u9700\u8981\u8F03\u591A\u5224\u65B7\u7684\u4EFB\u52D9\u3002" },
1286
- { value: "xhigh", label: "\u6975\u81F4", description: "\u9069\u5408\u9AD8\u8907\u96DC\u5EA6\u3001\u9577\u93C8\u63A8\u7406\u6216\u9AD8\u98A8\u96AA\u8B8A\u66F4\u3002" }
1524
+ { value: "xhigh", label: "\u6975\u81F4", description: "\u9069\u5408\u9AD8\u8907\u96DC\u5EA6\u3001\u9577\u93C8\u63A8\u7406\u6216\u9AD8\u98A8\u96AA\u8B8A\u66F4\u3002" },
1525
+ { value: "max", label: "\u6700\u5927", description: "\u6700\u6DF1\u7684\u63A8\u7406\uFF0C\u9069\u5408\u6700\u56F0\u96E3\u7684\u554F\u984C\u3002" },
1526
+ { value: "ultra", label: "\u59D4\u6D3E", description: "\u6700\u6DF1\u63A8\u7406\u4E26\u81EA\u52D5\u59D4\u6D3E\u5B50\u4EFB\u52D9\uFF0C\u8017\u6642\u8207\u7528\u91CF\u53EF\u80FD\u660E\u986F\u589E\u52A0\u3002" }
1287
1527
  ];
1288
1528
  DEFAULT_EFFORT = "medium";
1289
1529
  LABELS = {
@@ -1319,7 +1559,7 @@ __export(src_exports, {
1319
1559
  showDiff: () => showDiff
1320
1560
  });
1321
1561
  import { spawnSync as spawnSync2 } from "child_process";
1322
- import path26 from "path";
1562
+ import path27 from "path";
1323
1563
  import React2, { useEffect as useEffect2, useState as useState2 } from "react";
1324
1564
  import { Box as Box2, Text as Text2, render as render2, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
1325
1565
  function findDetachKey(text) {
@@ -1615,7 +1855,7 @@ function showDiff(task) {
1615
1855
  });
1616
1856
  }
1617
1857
  function tmuxAttach(opts, sessionName) {
1618
- const conf = path26.join(opts.userDataDir, "tmux.conf");
1858
+ const conf = path27.join(opts.userDataDir, "tmux.conf");
1619
1859
  const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
1620
1860
  const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
1621
1861
  spawnSync2("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
@@ -1746,8 +1986,8 @@ var init_src = __esm({
1746
1986
  });
1747
1987
 
1748
1988
  // packages/cli/src/main.ts
1749
- import fs22 from "fs";
1750
- import path27 from "path";
1989
+ import fs23 from "fs";
1990
+ import path28 from "path";
1751
1991
  import { fileURLToPath } from "url";
1752
1992
  import { parseArgs as parseArgs2 } from "node:util";
1753
1993
 
@@ -2699,191 +2939,8 @@ function unwatchAllSubAgents() {
2699
2939
  }
2700
2940
  }
2701
2941
 
2702
- // packages/core/src/launch.ts
2703
- var ARTIFACTS_DIR_SUFFIX = ".artifacts";
2704
- var SCRATCH_DIR_NAME = "scratch";
2705
- var ARTIFACTS_FALLBACK_DIR = ".vibeflow-artifacts";
2706
- function pathBasename(p) {
2707
- const norm = p.replace(/\\/g, "/").replace(/\/+$/, "");
2708
- const i = norm.lastIndexOf("/");
2709
- return i >= 0 ? norm.slice(i + 1) : norm;
2710
- }
2711
- function agentArtifactsDir(worktreePath, workspacePath) {
2712
- if (!worktreePath || !workspacePath) return null;
2713
- const dir = toShellPath(workspacePath);
2714
- const ws = pathBasename(worktreePath);
2715
- return `${dir}/${ws}${ARTIFACTS_DIR_SUFFIX}`;
2716
- }
2717
- function buildArtifactPrompt(artifactsDir = ARTIFACTS_FALLBACK_DIR) {
2718
- return [
2719
- "Artifact \u8A2D\u5B9A\uFF08\u672C\u6B21 session \u5168\u7A0B\u9069\u7528\uFF09\uFF1A",
2720
- `1. \u672C\u4EFB\u52D9\u7684 Artifact \u8CC7\u6599\u593E\u662F ${artifactsDir}/\uFF1B\u9700\u8981\u4FDD\u5B58\u7D66\u4F7F\u7528\u8005\u7684\u9A57\u8B49\u8B49\u64DA\u6216\u5831\u544A\u6642\uFF0C\u8ACB\u5BEB\u5165\u6B64\u8CC7\u6599\u593E\uFF08\u4E0D\u5B58\u5728\u8ACB\u5EFA\u7ACB\uFF09\u3002`,
2721
- `2. \u4F7F\u7528\u8005\u6703\u5728 VibeFlow \u7684 Artifacts \u5206\u9801\u6AA2\u8996\u6839\u76EE\u9304\u5167\u5BB9\uFF1B\u53EA\u653E\u4F60\u6703\u4E3B\u52D5\u8ACB\u4F7F\u7528\u8005\u904E\u76EE\u7684\u6A94\u6848\u3002`,
2722
- `3. \u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u66AB\u5B58\uFF08\u4E00\u6B21\u6027 script\u3001log\u3001\u4E2D\u9593\u8F38\u51FA\u3001debug \u6A94\uFF09\u653E\u5728 ${artifactsDir}/${SCRATCH_DIR_NAME}/\u3002`,
2723
- `4. \u82E5\u5DE5\u5177\u5148\u8F38\u51FA\u5230 /tmp\u3001/private/tmp\u3001$TMPDIR \u6216 ~/Downloads\uFF0C\u8ACB\u628A\u6700\u7D42\u8981\u5448\u73FE\u7684\u6A94\u6848\u8907\u88FD\u5230 ${artifactsDir}/ \u4E26\u78BA\u8A8D\u5B58\u5728\u3002`,
2724
- "5. Artifact \u8CC7\u6599\u593E\u4F4D\u65BC worktree \u4E4B\u5916\uFF1B\u4E0D\u8981\u628A\u5176\u4E2D\u5167\u5BB9\u52A0\u5165 git commit\uFF0C\u4E5F\u4E0D\u8981\u628A\u6700\u7D42\u4EA4\u4ED8\u7269\u53EA\u7559\u5728\u9019\u88E1\u3002"
2725
- ].join("\n");
2726
- }
2727
- var AUTO_PERMISSION_MODE = "auto";
2728
- function claudePermissionFlag(autoMode) {
2729
- return autoMode ? ` --permission-mode ${AUTO_PERMISSION_MODE}` : "";
2730
- }
2731
- function shellQuote(s) {
2732
- return `'${s.replace(/'/g, `'\\''`)}'`;
2733
- }
2734
- function toShellPath(p) {
2735
- return p.replace(/\\/g, "/");
2736
- }
2737
- function boardEnvPrefix(task, opts) {
2738
- const board = opts?.boardCli;
2739
- if (!board) return "";
2740
- const entries = [
2741
- ["VIBEFLOW_TASK_ID", task.id],
2742
- ["VIBEFLOW_PROJECT_PATH", task.projectPath],
2743
- ["VIBEFLOW_BRANCH", task.branch],
2744
- ["VIBEFLOW_BASE_BRANCH", task.baseBranch],
2745
- ["VIBEFLOW_STORE_DIR", board?.storeDir],
2746
- ["VIBEFLOW_CLI", board?.cliPath],
2747
- ["VIBEFLOW_CLI_LOADER", board?.loaderPath],
2748
- ["VIBEFLOW_AUTO_MODE", opts?.autoMode ? "1" : "0"]
2749
- ];
2750
- const exports = entries.filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${shellQuote(toShellPath(value))}`);
2751
- return exports.length ? `export ${exports.join(" ")}; ` : "";
2752
- }
2753
- function codexAutoFlag(autoMode) {
2754
- return autoMode ? "--dangerously-bypass-approvals-and-sandbox " : "";
2755
- }
2756
- function claudeResumeOrFresh(sessionId, worktreePath, tail) {
2757
- if (!worktreePath) return `claude --resume ${sessionId} ${tail}\r`;
2758
- const munged = worktreePath.replace(/[^a-zA-Z0-9]/g, "-");
2759
- const sessionFile = `"$HOME/.claude/projects/${munged}/${sessionId}.jsonl"`;
2760
- return `if [ -f ${sessionFile} ]; then claude --resume ${sessionId} ${tail}; else claude --session-id ${sessionId} ${tail}; fi\r`;
2761
- }
2762
- var SUBAGENTS_DIR2 = ".vibeflow-subagents";
2763
- var PROGRESS_EVENTS_DIR = ".vibeflow-events";
2764
- var TODO_TOOLS = "TodoWrite|TaskCreate|TaskUpdate";
2765
- function buildClaudeSettings(worktreePath) {
2766
- const settings = { theme: "dark" };
2767
- if (worktreePath) {
2768
- const dir = `${toShellPath(worktreePath)}/${SUBAGENTS_DIR2}`;
2769
- const command = `mkdir -p "${dir}" && cat > "${dir}/$(date +%s)-$$-$RANDOM.json"`;
2770
- const taskHook = {
2771
- matcher: "Task",
2772
- hooks: [{ type: "command", command }]
2773
- };
2774
- const eventsDir = `${toShellPath(worktreePath)}/${PROGRESS_EVENTS_DIR}`;
2775
- const progress = {
2776
- hooks: [{ type: "command", command: `mkdir -p "${eventsDir}" && cat > "${eventsDir}/$(date +%s)-$$-$RANDOM.json"; exit 0` }]
2777
- };
2778
- settings.hooks = {
2779
- PreToolUse: [taskHook],
2780
- PostToolUse: [taskHook, { matcher: TODO_TOOLS, ...progress }],
2781
- Stop: [progress],
2782
- Notification: [progress],
2783
- UserPromptSubmit: [progress]
2784
- };
2785
- }
2786
- return JSON.stringify(settings);
2787
- }
2788
- function resolveSystemPrompt(custom) {
2789
- return custom && custom.trim() ? custom : "";
2790
- }
2791
- function buildPrompt(task) {
2792
- const lines = [`\u4EFB\u52D9\u6A19\u984C\uFF1A${task.title}`];
2793
- const description = task.description?.trim();
2794
- if (description) {
2795
- lines.push("", "\u4EFB\u52D9\u63CF\u8FF0\uFF1A", description);
2796
- }
2797
- return lines.join("\n");
2798
- }
2799
- function executorSessionId(taskId, runId) {
2800
- const source = runId ? `${taskId}:${runId}` : taskId;
2801
- const raw = source.replace(/[^0-9a-f]/gi, "").toLowerCase();
2802
- const hex = runId ? `${raw.slice(0, 24).padEnd(24, "0")}${namespaceHash(`executor:${source}`)}` : raw.padEnd(32, "0").slice(0, 32);
2803
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-8${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
2804
- }
2805
- function namespaceHash(namespace) {
2806
- let hash = 2166136261;
2807
- for (let i = 0; i < namespace.length; i += 1) {
2808
- hash ^= namespace.charCodeAt(i);
2809
- hash = Math.imul(hash, 16777619);
2810
- }
2811
- return (hash >>> 0).toString(16).padStart(8, "0");
2812
- }
2813
- function assembleCommand(agent, systemPrompt, prompt, model, effort, opts, worktreePath, sessionId, workspacePath) {
2814
- let cmd;
2815
- if (agent === "claude") {
2816
- const settings = ` --settings ${shellQuote(buildClaudeSettings(worktreePath))}`;
2817
- const addDir = workspacePath ? ` --add-dir ${shellQuote(toShellPath(workspacePath))}` : "";
2818
- const modelFlag = model ? ` --model ${model}` : "";
2819
- const effortFlag = effort ? ` --effort ${effort}` : "";
2820
- const library = opts?.library;
2821
- const libraryFlags = library ? ` --plugin-dir ${shellQuote(toShellPath(library.pluginDir))} --add-dir ${shellQuote(toShellPath(library.libraryDir))}` : "";
2822
- const flags = `--chrome${claudePermissionFlag(opts?.autoMode)}${modelFlag}${effortFlag}${settings}${addDir}${libraryFlags}`;
2823
- const sysFlag = systemPrompt ? ` --append-system-prompt ${shellQuote(systemPrompt)}` : "";
2824
- const promptArg = prompt ? ` ${shellQuote(prompt)}` : "";
2825
- const tail = `${flags}${sysFlag}${promptArg}`;
2826
- cmd = sessionId && opts?.resume ? claudeResumeOrFresh(sessionId, worktreePath, tail) : `claude ${sessionId ? `--session-id ${sessionId} ` : opts?.resume ? "--continue " : ""}${tail}\r`;
2827
- } else {
2828
- const combined = [systemPrompt, prompt].filter(Boolean).join("\n\n");
2829
- const codexEffortFlag = effort ? `-c ${shellQuote(`model_reasoning_effort="${effort}"`)} ` : "";
2830
- const codexHome = opts?.library ? `CODEX_HOME=${shellQuote(toShellPath(opts.library.codexHome))} ` : "";
2831
- const promptArg = combined ? ` ${shellQuote(combined)}` : "";
2832
- cmd = `${codexHome}codex ${codexAutoFlag(opts?.autoMode)}${codexEffortFlag}--model ${model}${promptArg}\r`;
2833
- }
2834
- if (cmd.length > 2e5) console.warn(`[VibeFlow] launch command is ${cmd.length} bytes \u2014 approaching ARG_MAX`);
2835
- return cmd;
2836
- }
2837
- var AGENT_NAMES = {
2838
- claude: "Claude Code",
2839
- codex: "Codex CLI"
2840
- };
2841
- var DEFAULT_MODELS = {
2842
- claude: "sonnet",
2843
- codex: "gpt-5.5"
2844
- };
2845
- var LEGACY_MODEL_FALLBACKS = {
2846
- codex: {
2847
- "gpt-5-codex": "gpt-5.5",
2848
- "gpt-5": "gpt-5.5"
2849
- }
2850
- };
2851
- function normalizeModel(agent, model) {
2852
- return LEGACY_MODEL_FALLBACKS[agent]?.[model] ?? model;
2853
- }
2854
- function taskAgent(task) {
2855
- const agent = task.agentCli;
2856
- return agent && agent in AGENT_NAMES ? agent : "claude";
2857
- }
2858
- function taskModel(task) {
2859
- const agent = taskAgent(task);
2860
- return normalizeModel(agent, task.model || DEFAULT_MODELS[agent]);
2861
- }
2862
- function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
2863
- const agent = taskAgent(task);
2864
- const model = taskModel(task);
2865
- const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? ARTIFACTS_FALLBACK_DIR;
2866
- const builtInPrompt = buildArtifactPrompt(artifactsDir);
2867
- const libraryPrompt = opts?.library?.promptText?.trim();
2868
- const customPrompt = resolveSystemPrompt(systemPrompt);
2869
- const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
2870
- const includeTaskPrompt = opts?.includeTaskPrompt !== false;
2871
- const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
2872
- const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
2873
- return boardEnvPrefix(task, opts) + assembleCommand(
2874
- agent,
2875
- sys,
2876
- prompt,
2877
- model,
2878
- task.effort,
2879
- opts,
2880
- task.worktreePath,
2881
- sessionId,
2882
- workspacePath
2883
- );
2884
- }
2885
-
2886
2942
  // packages/core/src/git.ts
2943
+ init_launch();
2887
2944
  init_env();
2888
2945
 
2889
2946
  // packages/core/src/github.ts
@@ -4142,10 +4199,10 @@ function taskAttachmentStagingPath(workspacePath, taskId) {
4142
4199
 
4143
4200
  // packages/core/src/agents.ts
4144
4201
  init_env();
4202
+ init_effort();
4145
4203
  import { execFile as execFile6 } from "child_process";
4146
4204
  import { promisify as promisify3 } from "util";
4147
4205
  var pexec3 = promisify3(execFile6);
4148
- var AGENT_EFFORTS = ["low", "medium", "high", "xhigh"];
4149
4206
  var DEFAULT_TASK_EFFORT = "medium";
4150
4207
  var AGENT_CLIS = [
4151
4208
  {
@@ -4535,8 +4592,8 @@ async function taskCommand(argv) {
4535
4592
  }
4536
4593
 
4537
4594
  // packages/cli/src/host.ts
4538
- import fs19 from "fs";
4539
- import path23 from "path";
4595
+ import fs20 from "fs";
4596
+ import path24 from "path";
4540
4597
  import { randomBytes as randomBytes2 } from "crypto";
4541
4598
 
4542
4599
  // packages/core/src/events.ts
@@ -5074,35 +5131,266 @@ async function syncBuiltinsAtStartup() {
5074
5131
  }
5075
5132
 
5076
5133
  // packages/core/src/service.ts
5077
- import fs17 from "fs";
5078
- import os6 from "os";
5079
- import path21 from "path";
5134
+ import fs18 from "fs";
5135
+ import os8 from "os";
5136
+ import path22 from "path";
5080
5137
  import { randomUUID as randomUUID4 } from "crypto";
5081
5138
 
5082
5139
  // packages/core/src/agent-models.ts
5083
- import { execFile as execFile7 } from "child_process";
5140
+ import fs13 from "fs";
5141
+ import os6 from "os";
5142
+ import path17 from "path";
5143
+
5144
+ // packages/core/src/claude-models.ts
5145
+ init_effort();
5084
5146
  import fs12 from "fs";
5085
5147
  import os4 from "os";
5086
5148
  import path16 from "path";
5087
- import { promisify as promisify4 } from "util";
5149
+
5150
+ // packages/core/src/json-lines-process.ts
5088
5151
  init_env();
5089
- var pexec4 = promisify4(execFile7);
5152
+ import { spawn as spawn4 } from "child_process";
5153
+ function spawnJsonLines(bin, args, opts) {
5154
+ const spawnOpts = { cwd: opts.cwd, env: opts.env ?? execEnv(), windowsHide: true };
5155
+ const proc = process.platform === "win32" ? spawn4([bin, ...args].join(" "), { ...spawnOpts, shell: true }) : spawn4(bin, args, spawnOpts);
5156
+ const inbox = [];
5157
+ const waiters = [];
5158
+ let failure = null;
5159
+ let stderr = "";
5160
+ let exited = false;
5161
+ const finished = () => {
5162
+ if (exited) return;
5163
+ exited = true;
5164
+ opts.onExit?.();
5165
+ };
5166
+ const fail2 = (err) => {
5167
+ if (failure) return;
5168
+ failure = err;
5169
+ for (const w of waiters.splice(0)) w.reject(err);
5170
+ };
5171
+ const deliver = (message) => {
5172
+ const i = waiters.findIndex((w) => w.match(message));
5173
+ if (i === -1) inbox.push(message);
5174
+ else waiters.splice(i, 1)[0].resolve(message);
5175
+ };
5176
+ let buffered = "";
5177
+ proc.stdout.setEncoding("utf8");
5178
+ proc.stdout.on("data", (chunk) => {
5179
+ buffered += chunk;
5180
+ let nl;
5181
+ while ((nl = buffered.indexOf("\n")) >= 0) {
5182
+ const line = buffered.slice(0, nl).trim();
5183
+ buffered = buffered.slice(nl + 1);
5184
+ if (!line) continue;
5185
+ let message;
5186
+ try {
5187
+ message = JSON.parse(line);
5188
+ } catch {
5189
+ continue;
5190
+ }
5191
+ deliver(message);
5192
+ }
5193
+ });
5194
+ proc.stderr.setEncoding("utf8");
5195
+ proc.stderr.on("data", (chunk) => {
5196
+ stderr = (stderr + chunk).slice(-2048);
5197
+ });
5198
+ proc.on("error", (err) => {
5199
+ fail2(err);
5200
+ if (proc.pid === void 0) {
5201
+ clearTimeout(timer);
5202
+ finished();
5203
+ }
5204
+ });
5205
+ proc.on("exit", (code2, signal) => {
5206
+ finished();
5207
+ clearTimeout(timer);
5208
+ clearTimeout(graceTimer);
5209
+ const reason = signal ? `was killed (${signal})` : `exited with code ${code2}`;
5210
+ fail2(new Error(`${bin} ${reason}${stderr.trim() ? `: ${stderr.trim()}` : ""}`));
5211
+ });
5212
+ proc.stdin.on("error", () => {
5213
+ });
5214
+ const kill = () => {
5215
+ if (exited) return;
5216
+ if (process.platform === "win32" && proc.pid !== void 0) {
5217
+ spawn4("taskkill", ["/pid", String(proc.pid), "/T", "/F"], { windowsHide: true }).on("error", () => proc.kill());
5218
+ return;
5219
+ }
5220
+ proc.kill();
5221
+ };
5222
+ const timer = setTimeout(() => {
5223
+ fail2(new Error(`${bin} did not answer within ${opts.timeoutMs}ms`));
5224
+ kill();
5225
+ }, opts.timeoutMs);
5226
+ let graceTimer;
5227
+ return {
5228
+ send(message) {
5229
+ if (!failure) proc.stdin.write(`${JSON.stringify(message)}
5230
+ `);
5231
+ },
5232
+ next(match) {
5233
+ const i = inbox.findIndex(match);
5234
+ if (i >= 0) return Promise.resolve(inbox.splice(i, 1)[0]);
5235
+ if (failure) return Promise.reject(failure);
5236
+ return new Promise((resolve, reject) => waiters.push({ match, resolve, reject }));
5237
+ },
5238
+ close() {
5239
+ clearTimeout(timer);
5240
+ fail2(new Error(`${bin} conversation closed`));
5241
+ proc.stdin.end();
5242
+ if (!exited && !graceTimer) graceTimer = setTimeout(kill, opts.exitGraceMs ?? 3e3);
5243
+ }
5244
+ };
5245
+ }
5246
+
5247
+ // packages/core/src/claude-models.ts
5248
+ var CLAUDE_MODELS_TIMEOUT_MS = 15e3;
5249
+ var REQUEST_ID = "vibeflow-models";
5250
+ var CLAUDE_ARGS = ["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"];
5251
+ function openClaude() {
5252
+ const cwd = fs12.mkdtempSync(path16.join(os4.tmpdir(), "vf-claude-models-"));
5253
+ return spawnJsonLines("claude", CLAUDE_ARGS, {
5254
+ cwd,
5255
+ timeoutMs: CLAUDE_MODELS_TIMEOUT_MS,
5256
+ onExit: () => fs12.rmSync(cwd, { recursive: true, force: true })
5257
+ });
5258
+ }
5259
+ function isRecord(value) {
5260
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5261
+ }
5262
+ function isOurResponse(message) {
5263
+ return isRecord(message) && message.type === "control_response" && isRecord(message.response) && message.response.request_id === REQUEST_ID;
5264
+ }
5265
+ function parseClaudeInitialize(message) {
5266
+ const outer = isRecord(message) && isRecord(message.response) ? message.response : null;
5267
+ if (outer?.subtype === "error") throw new Error(`claude refused the handshake: ${String(outer.error ?? "unknown error")}`);
5268
+ const body = outer && isRecord(outer.response) ? outer.response : null;
5269
+ if (!body || !Array.isArray(body.models)) throw new Error("claude handshake has no models array");
5270
+ const seen = /* @__PURE__ */ new Set();
5271
+ const models = [];
5272
+ for (const entry of body.models) {
5273
+ if (!isRecord(entry) || typeof entry.value !== "string") continue;
5274
+ const id = entry.value.trim();
5275
+ if (!id || id === "default" || seen.has(id)) continue;
5276
+ seen.add(id);
5277
+ const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
5278
+ const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
5279
+ const levels = entry.supportsEffort === true && Array.isArray(entry.supportedEffortLevels) ? entry.supportedEffortLevels.filter(isAgentEffort) : [];
5280
+ models.push({ id, label, ...description ? { description } : {}, efforts: levels });
5281
+ }
5282
+ if (models.length === 0) throw new Error("claude handshake lists no selectable models");
5283
+ return models;
5284
+ }
5285
+ async function fetchClaudeModels(open = openClaude) {
5286
+ const channel = open();
5287
+ try {
5288
+ channel.send({ type: "control_request", request_id: REQUEST_ID, request: { subtype: "initialize" } });
5289
+ return parseClaudeInitialize(await channel.next(isOurResponse));
5290
+ } finally {
5291
+ channel.close();
5292
+ }
5293
+ }
5294
+
5295
+ // packages/core/src/codex-models.ts
5296
+ init_effort();
5297
+ init_env();
5298
+ import os5 from "os";
5090
5299
  var CODEX_MODELS_TIMEOUT_MS = 2e4;
5300
+ var MAX_PAGES = 20;
5301
+ var CodexLoginRequiredError = class extends Error {
5302
+ constructor() {
5303
+ super("Codex CLI is not signed in; run `codex login`");
5304
+ this.name = "CodexLoginRequiredError";
5305
+ }
5306
+ };
5307
+ function codexAppServerCommand() {
5308
+ return {
5309
+ bin: "codex",
5310
+ args: ["app-server", "--listen", "stdio://"],
5311
+ cwd: os5.homedir(),
5312
+ env: execEnv()
5313
+ };
5314
+ }
5315
+ function openCodex() {
5316
+ const { bin, args, cwd, env } = codexAppServerCommand();
5317
+ return spawnJsonLines(bin, args, { cwd, env, timeoutMs: CODEX_MODELS_TIMEOUT_MS });
5318
+ }
5319
+ function isRecord2(value) {
5320
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5321
+ }
5322
+ var responseTo = (id) => (message) => isRecord2(message) && message.id === id && ("result" in message || "error" in message);
5323
+ function resultOf(message, method) {
5324
+ if (!isRecord2(message)) throw new Error(`codex ${method}: malformed response`);
5325
+ if (isRecord2(message.error)) throw new Error(`codex ${method}: ${String(message.error.message ?? "request failed")}`);
5326
+ if (!isRecord2(message.result)) throw new Error(`codex ${method}: response has no result`);
5327
+ return message.result;
5328
+ }
5329
+ function isCodexSignedIn(result) {
5330
+ return result.account != null || result.requiresOpenaiAuth === false;
5331
+ }
5332
+ function parseCodexModelPage(result) {
5333
+ if (!Array.isArray(result.data)) throw new Error("codex model/list has no data array");
5334
+ const models = [];
5335
+ for (const entry of result.data) {
5336
+ if (!isRecord2(entry) || typeof entry.id !== "string" || !entry.id.trim() || entry.hidden === true) continue;
5337
+ const id = entry.id.trim();
5338
+ const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
5339
+ const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
5340
+ const efforts = Array.isArray(entry.supportedReasoningEfforts) ? entry.supportedReasoningEfforts.map((e) => isRecord2(e) ? e.reasoningEffort : void 0).filter(isAgentEffort) : void 0;
5341
+ models.push({ id, label, ...description ? { description } : {}, ...efforts ? { efforts } : {} });
5342
+ }
5343
+ const nextCursor = typeof result.nextCursor === "string" && result.nextCursor ? result.nextCursor : null;
5344
+ return { models, nextCursor };
5345
+ }
5346
+ async function fetchCodexModels(clientVersion, open = openCodex) {
5347
+ const channel = open();
5348
+ try {
5349
+ channel.send({
5350
+ method: "initialize",
5351
+ id: 1,
5352
+ params: { clientInfo: { name: "vibeflow", title: "VibeFlow", version: clientVersion } }
5353
+ });
5354
+ resultOf(await channel.next(responseTo(1)), "initialize");
5355
+ channel.send({ method: "initialized", params: {} });
5356
+ channel.send({ method: "account/read", id: 2, params: { refreshToken: false } });
5357
+ if (!isCodexSignedIn(resultOf(await channel.next(responseTo(2)), "account/read"))) {
5358
+ throw new CodexLoginRequiredError();
5359
+ }
5360
+ const seen = /* @__PURE__ */ new Set();
5361
+ const models = [];
5362
+ let cursor = null;
5363
+ for (let page = 0; page < MAX_PAGES; page++) {
5364
+ const id = 3 + page;
5365
+ channel.send({
5366
+ method: "model/list",
5367
+ id,
5368
+ params: { limit: 100, includeHidden: false, ...cursor ? { cursor } : {} }
5369
+ });
5370
+ const parsed = parseCodexModelPage(resultOf(await channel.next(responseTo(id)), "model/list"));
5371
+ for (const model of parsed.models) {
5372
+ if (seen.has(model.id)) continue;
5373
+ seen.add(model.id);
5374
+ models.push(model);
5375
+ }
5376
+ cursor = parsed.nextCursor;
5377
+ if (!cursor) break;
5378
+ }
5379
+ if (cursor) throw new Error(`codex model/list did not finish within ${MAX_PAGES} pages`);
5380
+ if (models.length === 0) throw new Error("codex model/list lists no selectable models");
5381
+ return models;
5382
+ } finally {
5383
+ channel.close();
5384
+ }
5385
+ }
5386
+
5387
+ // packages/core/src/agent-models.ts
5091
5388
  function builtinModels(id) {
5092
5389
  return (AGENT_CLIS.find((a) => a.id === id) ?? AGENT_CLIS[0]).models;
5093
5390
  }
5094
5391
  function defaultCodexHome() {
5095
5392
  const configured = process.env.CODEX_HOME?.trim();
5096
- return configured ? configured : path16.join(os4.homedir(), ".codex");
5097
- }
5098
- async function defaultRunCodexModels() {
5099
- const { stdout } = await pexec4("codex", ["debug", "models"], {
5100
- env: execEnv(),
5101
- timeout: CODEX_MODELS_TIMEOUT_MS,
5102
- maxBuffer: 16 * 1024 * 1024,
5103
- windowsHide: true
5104
- });
5105
- return stdout;
5393
+ return configured ? configured : path17.join(os6.homedir(), ".codex");
5106
5394
  }
5107
5395
  function parseCodexCatalog(raw) {
5108
5396
  const body = JSON.parse(raw);
@@ -5127,36 +5415,75 @@ function parseCodexCatalog(raw) {
5127
5415
  function errorMessage(err) {
5128
5416
  return err instanceof Error ? err.message : String(err);
5129
5417
  }
5130
- async function listAgentModels(id, opts = {}) {
5131
- if (id !== "codex") return { models: builtinModels(id), source: "builtin" };
5132
- if (opts.refresh) {
5133
- try {
5134
- const stdout = await (opts.runCodexModels ?? defaultRunCodexModels)();
5135
- return { models: parseCodexCatalog(stdout), source: "codex-cli" };
5136
- } catch (err) {
5137
- return { models: builtinModels(id), source: "builtin", error: errorMessage(err) };
5138
- }
5139
- }
5140
- const cachePath = path16.join(opts.codexHome ?? defaultCodexHome(), "models_cache.json");
5418
+ async function readCodexCache(codexHome) {
5419
+ const cachePath = path17.join(codexHome, "models_cache.json");
5141
5420
  try {
5142
- const raw = await fs12.promises.readFile(cachePath, "utf8");
5421
+ const raw = await fs13.promises.readFile(cachePath, "utf8");
5143
5422
  return { models: parseCodexCatalog(raw), source: "codex-cache" };
5144
5423
  } catch (err) {
5145
5424
  const missing = err.code === "ENOENT";
5146
5425
  return {
5147
- models: builtinModels(id),
5426
+ models: builtinModels("codex"),
5148
5427
  source: "builtin",
5149
5428
  ...missing ? {} : { error: errorMessage(err) }
5150
5429
  };
5151
5430
  }
5152
5431
  }
5432
+ function createModelCatalog(opts) {
5433
+ const fetchClaude = opts.fetchClaude ?? (() => fetchClaudeModels());
5434
+ const fetchCodex = opts.fetchCodex ?? (() => fetchCodexModels(opts.version));
5435
+ const inFlight = /* @__PURE__ */ new Map();
5436
+ const settled = /* @__PURE__ */ new Map();
5437
+ const fallback = (id) => id === "codex" ? readCodexCache(opts.codexHome ?? defaultCodexHome()) : Promise.resolve({ models: builtinModels(id), source: "builtin" });
5438
+ const ask = async (id) => {
5439
+ try {
5440
+ const models = await (id === "codex" ? fetchCodex() : fetchClaude());
5441
+ return { models, source: id === "codex" ? "codex-app-server" : "claude-cli" };
5442
+ } catch (err) {
5443
+ const list = await fallback(id);
5444
+ return {
5445
+ ...list,
5446
+ error: errorMessage(err),
5447
+ ...err instanceof CodexLoginRequiredError ? { loginRequired: true } : {}
5448
+ };
5449
+ }
5450
+ };
5451
+ const refresh = (id) => {
5452
+ const running = inFlight.get(id);
5453
+ if (running) return running;
5454
+ const pending = ask(id).then((list) => {
5455
+ settled.set(id, list);
5456
+ return list;
5457
+ }).finally(() => inFlight.delete(id));
5458
+ inFlight.set(id, pending);
5459
+ return pending;
5460
+ };
5461
+ return {
5462
+ async get(id) {
5463
+ const answer = settled.get(id);
5464
+ if (answer) return answer;
5465
+ const list = await fallback(id);
5466
+ return inFlight.has(id) ? { ...list, pending: true } : list;
5467
+ },
5468
+ refresh,
5469
+ warm() {
5470
+ for (const agent of AGENT_CLIS) void refresh(agent.id);
5471
+ },
5472
+ findModel(id, modelId) {
5473
+ return settled.get(id)?.models.find((model) => model.id === modelId);
5474
+ }
5475
+ };
5476
+ }
5477
+
5478
+ // packages/core/src/service.ts
5479
+ init_effort();
5153
5480
 
5154
5481
  // packages/core/src/github-auth.ts
5155
5482
  init_env();
5156
- import { execFile as execFile8 } from "child_process";
5157
- import { promisify as promisify5 } from "util";
5483
+ import { execFile as execFile7 } from "child_process";
5484
+ import { promisify as promisify4 } from "util";
5158
5485
  import * as nodePty3 from "node-pty";
5159
- var pexec5 = promisify5(execFile8);
5486
+ var pexec4 = promisify4(execFile7);
5160
5487
  var GITHUB_HOST = "github.com";
5161
5488
  var DEVICE_URL = "https://github.com/login/device";
5162
5489
  var activeLogin = null;
@@ -5191,7 +5518,7 @@ function errorMessage2(err) {
5191
5518
  }
5192
5519
  async function getGitHubCliAuthStatus() {
5193
5520
  try {
5194
- const { stdout } = await pexec5(
5521
+ const { stdout } = await pexec4(
5195
5522
  "gh",
5196
5523
  ["auth", "status", "--hostname", GITHUB_HOST, "--json", "hosts"],
5197
5524
  {
@@ -5223,7 +5550,7 @@ async function logoutGitHubCli() {
5223
5550
  const status = await getGitHubCliAuthStatus();
5224
5551
  if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
5225
5552
  if (!status.login) return status;
5226
- await pexec5(
5553
+ await pexec4(
5227
5554
  "gh",
5228
5555
  ["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
5229
5556
  {
@@ -5306,45 +5633,45 @@ function startGitHubCliLogin(onEvent) {
5306
5633
  }
5307
5634
 
5308
5635
  // packages/core/src/board-cli.ts
5309
- import fs13 from "fs";
5310
- import path17 from "path";
5636
+ import fs14 from "fs";
5637
+ import path18 from "path";
5311
5638
  import { pathToFileURL } from "url";
5312
5639
  async function boardCliLaunchInfo() {
5313
- const storeDir = path17.dirname(getStorePath());
5640
+ const storeDir = path18.dirname(getStorePath());
5314
5641
  const entry = getPlatform().cliEntry?.();
5315
- if (entry && fs13.existsSync(entry)) return { storeDir, cliPath: entry };
5642
+ if (entry && fs14.existsSync(entry)) return { storeDir, cliPath: entry };
5316
5643
  const root = getPlatform().sourceRoot();
5317
5644
  if (!root) return { storeDir };
5318
- const cliPath = path17.join(root, "scripts", "vibeflow.mjs");
5319
- const loaderPath = path17.join(root, "test", "support", "register.mjs");
5320
- if (!fs13.existsSync(cliPath) || !fs13.existsSync(loaderPath)) return { storeDir };
5645
+ const cliPath = path18.join(root, "scripts", "vibeflow.mjs");
5646
+ const loaderPath = path18.join(root, "test", "support", "register.mjs");
5647
+ if (!fs14.existsSync(cliPath) || !fs14.existsSync(loaderPath)) return { storeDir };
5321
5648
  return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
5322
5649
  }
5323
5650
 
5324
5651
  // packages/core/src/decisions.ts
5325
- import fs14 from "fs";
5326
- import path18 from "path";
5652
+ import fs15 from "fs";
5653
+ import path19 from "path";
5327
5654
  var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
5328
5655
  function decisionsKey(worktreePath, branch) {
5329
- return worktreePath ? path18.basename(worktreePath) : branch.replace(/\//g, "-");
5656
+ return worktreePath ? path19.basename(worktreePath) : branch.replace(/\//g, "-");
5330
5657
  }
5331
5658
  function decisionsPath(workspacePath, key) {
5332
- return path18.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
5659
+ return path19.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
5333
5660
  }
5334
5661
  function deleteDecisions(workspacePath, key) {
5335
5662
  try {
5336
- fs14.rmSync(decisionsPath(workspacePath, key), { force: true });
5663
+ fs15.rmSync(decisionsPath(workspacePath, key), { force: true });
5337
5664
  } catch {
5338
5665
  }
5339
5666
  }
5340
5667
 
5341
5668
  // packages/core/src/specs.ts
5342
- import fs15 from "fs";
5343
- import path19 from "path";
5669
+ import fs16 from "fs";
5670
+ import path20 from "path";
5344
5671
  var SPEC_FILE_NAME = "spec.md";
5345
5672
  var MAX_SPEC_BYTES = 256 * 1024;
5346
5673
  function isSpecPath(p) {
5347
- return path19.posix.basename(p) === SPEC_FILE_NAME;
5674
+ return path20.posix.basename(p) === SPEC_FILE_NAME;
5348
5675
  }
5349
5676
  function clip2(buf) {
5350
5677
  return {
@@ -5359,10 +5686,10 @@ async function readWorktreeSpecs(worktreePath, baseBranch) {
5359
5686
  for (const entry of entries) {
5360
5687
  if (entry.status === "D" || !isSpecPath(entry.path)) continue;
5361
5688
  try {
5362
- const full = path19.join(worktreePath, entry.path);
5363
- const stat = fs15.statSync(full);
5689
+ const full = path20.join(worktreePath, entry.path);
5690
+ const stat = fs16.statSync(full);
5364
5691
  if (!stat.isFile()) continue;
5365
- specs.push({ path: entry.path, ...clip2(fs15.readFileSync(full)), updatedAt: stat.mtimeMs });
5692
+ specs.push({ path: entry.path, ...clip2(fs16.readFileSync(full)), updatedAt: stat.mtimeMs });
5366
5693
  } catch {
5367
5694
  }
5368
5695
  }
@@ -5427,7 +5754,7 @@ async function readSpecsAt(cwd, ref, paths) {
5427
5754
  // packages/core/src/chat-session.ts
5428
5755
  init_env();
5429
5756
  init_git_bash();
5430
- import { spawn as spawn5 } from "child_process";
5757
+ import { spawn as spawn6 } from "child_process";
5431
5758
  import { randomUUID as randomUUID3 } from "crypto";
5432
5759
 
5433
5760
  // packages/core/src/chat-store.ts
@@ -5583,7 +5910,7 @@ ${attachmentLines.join("\n")}` : text;
5583
5910
  `--append-system-prompt ${quote(systemPrompt)}`,
5584
5911
  quote(fullText)
5585
5912
  ].filter(Boolean).join(" ") : buildRawAgentCommand(agentCli, model, fullText, systemPrompt, worktreePath, quote, workspacePath);
5586
- const proc = spawn5(shell.command, shell.args(cmd), {
5913
+ const proc = spawn6(shell.command, shell.args(cmd), {
5587
5914
  cwd: worktreePath,
5588
5915
  env: execEnv()
5589
5916
  });
@@ -5709,20 +6036,22 @@ function startChatSend(opts, sender) {
5709
6036
 
5710
6037
  // packages/core/src/service.ts
5711
6038
  init_attachments();
6039
+ init_launch();
5712
6040
 
5713
6041
  // packages/core/src/progress-tracker.ts
5714
6042
  init_progress();
5715
- import fs16 from "fs";
5716
- import os5 from "os";
5717
- import path20 from "path";
6043
+ init_launch();
6044
+ import fs17 from "fs";
6045
+ import os7 from "os";
6046
+ import path21 from "path";
5718
6047
  var SINCE_SLACK_MS = 5e3;
5719
6048
  function claudeProjectDir(home, cwd) {
5720
- return path20.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
6049
+ return path21.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
5721
6050
  }
5722
6051
  function pathAliases(p) {
5723
- const resolved = path20.resolve(p);
6052
+ const resolved = path21.resolve(p);
5724
6053
  try {
5725
- const real = fs16.realpathSync(resolved);
6054
+ const real = fs17.realpathSync(resolved);
5726
6055
  return real === resolved ? [resolved] : [resolved, real];
5727
6056
  } catch {
5728
6057
  return [resolved];
@@ -5738,7 +6067,7 @@ function samePath(a, b) {
5738
6067
  }
5739
6068
  function listFiles(dir, test) {
5740
6069
  try {
5741
- return fs16.readdirSync(dir).filter(test).map((name) => path20.join(dir, name));
6070
+ return fs17.readdirSync(dir).filter(test).map((name) => path21.join(dir, name));
5742
6071
  } catch {
5743
6072
  return [];
5744
6073
  }
@@ -5746,22 +6075,22 @@ function listFiles(dir, test) {
5746
6075
  function firstLine(file) {
5747
6076
  let fd;
5748
6077
  try {
5749
- fd = fs16.openSync(file, "r");
6078
+ fd = fs17.openSync(file, "r");
5750
6079
  const buf = Buffer.alloc(64 * 1024);
5751
- const n = fs16.readSync(fd, buf, 0, buf.length, 0);
6080
+ const n = fs17.readSync(fd, buf, 0, buf.length, 0);
5752
6081
  const text = buf.subarray(0, n).toString("utf8");
5753
6082
  const nl = text.indexOf("\n");
5754
6083
  return nl === -1 ? null : text.slice(0, nl);
5755
6084
  } catch {
5756
6085
  return null;
5757
6086
  } finally {
5758
- if (fd !== void 0) fs16.closeSync(fd);
6087
+ if (fd !== void 0) fs17.closeSync(fd);
5759
6088
  }
5760
6089
  }
5761
6090
  function advance(file, cursor, reduce, initial) {
5762
6091
  let size;
5763
6092
  try {
5764
- size = fs16.statSync(file).size;
6093
+ size = fs17.statSync(file).size;
5765
6094
  } catch {
5766
6095
  return;
5767
6096
  }
@@ -5773,9 +6102,9 @@ function advance(file, cursor, reduce, initial) {
5773
6102
  if (size === cursor.offset) return;
5774
6103
  let fd;
5775
6104
  try {
5776
- fd = fs16.openSync(file, "r");
6105
+ fd = fs17.openSync(file, "r");
5777
6106
  const chunk = Buffer.alloc(size - cursor.offset);
5778
- const n = fs16.readSync(fd, chunk, 0, chunk.length, cursor.offset);
6107
+ const n = fs17.readSync(fd, chunk, 0, chunk.length, cursor.offset);
5779
6108
  cursor.offset += n;
5780
6109
  const data = Buffer.concat([cursor.rest, chunk.subarray(0, n)]);
5781
6110
  const nl = data.lastIndexOf(10);
@@ -5789,7 +6118,7 @@ function advance(file, cursor, reduce, initial) {
5789
6118
  }
5790
6119
  } catch {
5791
6120
  } finally {
5792
- if (fd !== void 0) fs16.closeSync(fd);
6121
+ if (fd !== void 0) fs17.closeSync(fd);
5793
6122
  }
5794
6123
  }
5795
6124
  function newCursor(state) {
@@ -5808,14 +6137,14 @@ function codexDayDirs(sessionsRoot, since) {
5808
6137
  const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
5809
6138
  while (cur <= end) {
5810
6139
  const pad = (n) => String(n).padStart(2, "0");
5811
- days.push(path20.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
6140
+ days.push(path21.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
5812
6141
  cur.setDate(cur.getDate() + 1);
5813
6142
  }
5814
6143
  return days;
5815
6144
  }
5816
6145
  function createProgressTracker(opts) {
5817
- const home = opts.homeDir ?? os5.homedir();
5818
- const codexHomes = opts.codexHomes ?? (() => [path20.join(home, ".codex")]);
6146
+ const home = opts.homeDir ?? os7.homedir();
6147
+ const codexHomes = opts.codexHomes ?? (() => [path21.join(home, ".codex")]);
5819
6148
  const pollMs = opts.pollMs ?? 2e3;
5820
6149
  const entries = /* @__PURE__ */ new Map();
5821
6150
  const belongsToRun = (target, startedAt) => target.since == null || startedAt != null && startedAt >= target.since - SINCE_SLACK_MS;
@@ -5824,7 +6153,7 @@ function createProgressTracker(opts) {
5824
6153
  const dirs = pathAliases(target.worktreePath).map((p) => claudeProjectDir(home, p));
5825
6154
  for (const file of dirs.flatMap((dir) => listFiles(dir, (n) => n.endsWith(".jsonl")))) {
5826
6155
  if (entry.claude.has(file) || entry.rejected.has(file)) continue;
5827
- const session = path20.basename(file, ".jsonl");
6156
+ const session = path21.basename(file, ".jsonl");
5828
6157
  if (session !== target.sessionId) {
5829
6158
  const head = firstLine(file);
5830
6159
  if (head == null) continue;
@@ -5837,8 +6166,8 @@ function createProgressTracker(opts) {
5837
6166
  entry.claude.set(file, newCursor(initialClaudeState()));
5838
6167
  }
5839
6168
  for (const file of Array.from(entry.claude.keys())) {
5840
- if (file.includes(`${path20.sep}subagents${path20.sep}`)) continue;
5841
- const sub = path20.join(file.slice(0, -".jsonl".length), "subagents");
6169
+ if (file.includes(`${path21.sep}subagents${path21.sep}`)) continue;
6170
+ const sub = path21.join(file.slice(0, -".jsonl".length), "subagents");
5842
6171
  for (const f of listFiles(sub, (n) => n.endsWith(".jsonl"))) {
5843
6172
  if (!entry.claude.has(f)) entry.claude.set(f, newCursor(initialClaudeState()));
5844
6173
  }
@@ -5847,7 +6176,7 @@ function createProgressTracker(opts) {
5847
6176
  function discoverCodex(entry) {
5848
6177
  const { target } = entry;
5849
6178
  for (const codexHome of codexHomes()) {
5850
- for (const day of codexDayDirs(path20.join(codexHome, "sessions"), target.since)) {
6179
+ for (const day of codexDayDirs(path21.join(codexHome, "sessions"), target.since)) {
5851
6180
  for (const file of listFiles(day, (n) => n.startsWith("rollout-") && n.endsWith(".jsonl"))) {
5852
6181
  if (entry.codex.has(file) || entry.rejected.has(file)) continue;
5853
6182
  const head = firstLine(file);
@@ -5864,11 +6193,11 @@ function createProgressTracker(opts) {
5864
6193
  }
5865
6194
  }
5866
6195
  function drainHooks(entry) {
5867
- const dir = path20.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
6196
+ const dir = path21.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
5868
6197
  for (const file of listFiles(dir, (n) => n.endsWith(".json")).sort()) {
5869
6198
  try {
5870
- const at = fs16.statSync(file).mtimeMs;
5871
- const event = JSON.parse(fs16.readFileSync(file, "utf8"));
6199
+ const at = fs17.statSync(file).mtimeMs;
6200
+ const event = JSON.parse(fs17.readFileSync(file, "utf8"));
5872
6201
  const foreign = entry.target.sessionId && typeof event.session_id === "string" && event.session_id !== entry.target.sessionId;
5873
6202
  const activity = foreign ? null : activityFromHook(event, at);
5874
6203
  if (activity) entry.hookActivity = latestActivity(entry.hookActivity ?? activity, activity);
@@ -5876,7 +6205,7 @@ function createProgressTracker(opts) {
5876
6205
  continue;
5877
6206
  }
5878
6207
  try {
5879
- fs16.unlinkSync(file);
6208
+ fs17.unlinkSync(file);
5880
6209
  } catch {
5881
6210
  }
5882
6211
  }
@@ -5888,8 +6217,8 @@ function createProgressTracker(opts) {
5888
6217
  if (target.agent === "claude") {
5889
6218
  for (const [file, cursor] of entry.claude) {
5890
6219
  runUsage = addUsage(runUsage, claudeUsageTotal(cursor.state));
5891
- if (file.includes(`${path20.sep}subagents${path20.sep}`)) continue;
5892
- const isExecutor = path20.basename(file, ".jsonl") === target.sessionId;
6220
+ if (file.includes(`${path21.sep}subagents${path21.sep}`)) continue;
6221
+ const isExecutor = path21.basename(file, ".jsonl") === target.sessionId;
5893
6222
  const s = cursor.state;
5894
6223
  if (isExecutor || !target.sessionId && (!primary || (s.startedAt ?? 0) > (primary.startedAt ?? 0))) {
5895
6224
  primary = { todos: s.todos, contextTokens: s.contextTokens, activity: s.activity, startedAt: s.startedAt };
@@ -5968,10 +6297,10 @@ function createProgressTracker(opts) {
5968
6297
  };
5969
6298
  entry.timer.unref?.();
5970
6299
  entries.set(target.taskId, entry);
5971
- const eventsDir = path20.join(target.worktreePath, PROGRESS_EVENTS_DIR);
6300
+ const eventsDir = path21.join(target.worktreePath, PROGRESS_EVENTS_DIR);
5972
6301
  try {
5973
- fs16.mkdirSync(eventsDir, { recursive: true });
5974
- entry.watcher = fs16.watch(eventsDir, () => {
6302
+ fs17.mkdirSync(eventsDir, { recursive: true });
6303
+ entry.watcher = fs17.watch(eventsDir, () => {
5975
6304
  if (entry.pending) return;
5976
6305
  entry.pending = setTimeout(() => {
5977
6306
  entry.pending = null;
@@ -6056,17 +6385,18 @@ function validBoard(value) {
6056
6385
  }
6057
6386
  return next;
6058
6387
  }
6059
- function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus }) {
6388
+ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus, modelCatalog }) {
6060
6389
  const sink = bus.sink();
6390
+ const models = modelCatalog ?? createModelCatalog({ version });
6061
6391
  let libraryCodexHome = null;
6062
6392
  void libraryRoot().then((root) => {
6063
6393
  libraryCodexHome = libraryPaths(root).codexHome;
6064
6394
  }).catch(() => {
6065
6395
  });
6066
- const home = homeDir ?? os6.homedir();
6396
+ const home = homeDir ?? os8.homedir();
6067
6397
  const progress = createProgressTracker({
6068
6398
  homeDir: home,
6069
- codexHomes: () => [path21.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
6399
+ codexHomes: () => [path22.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
6070
6400
  onUpdate: (taskId, value) => bus.emit("progress:update", { taskId, progress: value }),
6071
6401
  onNotify: (taskId, list) => {
6072
6402
  const prefs = resolveNotificationSettings(getSettings());
@@ -6130,12 +6460,16 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6130
6460
  requireTask(key.split(":")[0]);
6131
6461
  return key;
6132
6462
  }
6463
+ function launchEffort(task) {
6464
+ const model = models.findModel(taskAgent(task), taskModel(task));
6465
+ return model?.efforts ? clampEffort(task.effort, model.efforts) : task.effort;
6466
+ }
6133
6467
  async function launchCommand(task, intent) {
6134
6468
  const settings = getSettings();
6135
6469
  const library = await libraryLaunchInfo(task.worktreePath);
6136
6470
  const boardCli = await boardCliLaunchInfo();
6137
6471
  return buildAgentCommand(
6138
- task,
6472
+ { ...task, effort: launchEffort(task) },
6139
6473
  settings.systemPrompt ?? "",
6140
6474
  {
6141
6475
  resume: intent.resume === true,
@@ -6149,13 +6483,13 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6149
6483
  }
6150
6484
  function claudeSessionFile(cwd, sessionId) {
6151
6485
  const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
6152
- return path21.join(os6.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
6486
+ return path22.join(os8.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
6153
6487
  }
6154
6488
  async function removeTaskWorktree(task) {
6155
6489
  if (!task.projectPath || !task.worktreePath) return;
6156
6490
  const branch = task.branch || fallbackBranchName(task.id);
6157
6491
  await removeWorktree(task.projectPath, task.worktreePath);
6158
- deleteArtifacts(task.workspacePath ?? path21.dirname(task.worktreePath), task.worktreePath);
6492
+ deleteArtifacts(task.workspacePath ?? path22.dirname(task.worktreePath), task.worktreePath);
6159
6493
  await deleteBranch(task.projectPath, branch);
6160
6494
  }
6161
6495
  function columnOf(taskId) {
@@ -6246,7 +6580,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6246
6580
  "env:detectAgents": () => detectAgents(),
6247
6581
  "agents:listModels": (payload) => {
6248
6582
  const { agentId: rawId, refresh } = obj(payload, "payload");
6249
- return listAgentModels(agentId(rawId), { refresh: refresh === true });
6583
+ const id = agentId(rawId);
6584
+ return refresh === true ? models.refresh(id) : models.get(id);
6250
6585
  },
6251
6586
  // A task does not exist yet when a project is picked, so these two are the
6252
6587
  // ones that must take a path. They only inspect or `git init` it.
@@ -6260,8 +6595,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6260
6595
  "projects:listRecent": () => listRecentProjects(getStore()),
6261
6596
  // `vibeflow open <path>` while a host runs: only the host writes the store.
6262
6597
  "projects:record": (projectPath) => {
6263
- const p = path21.resolve(str(projectPath, "projectPath"));
6264
- if (!fs17.existsSync(p) || !fs17.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
6598
+ const p = path22.resolve(str(projectPath, "projectPath"));
6599
+ if (!fs18.existsSync(p) || !fs18.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
6265
6600
  recordRecentProject(getStore(), p);
6266
6601
  return listRecentProjects(getStore());
6267
6602
  },
@@ -6312,7 +6647,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6312
6647
  if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
6313
6648
  await teardownTask(task.id);
6314
6649
  await resetWorktreeToBase(task.worktreePath, task.baseBranch);
6315
- const workspacePath = task.workspacePath ?? path21.dirname(task.worktreePath);
6650
+ const workspacePath = task.workspacePath ?? path22.dirname(task.worktreePath);
6316
6651
  deleteArtifacts(workspacePath, task.worktreePath);
6317
6652
  deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
6318
6653
  resetSubAgents(task.worktreePath);
@@ -6336,6 +6671,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6336
6671
  if (columnOf(existing.id) !== "backlog") {
6337
6672
  throw new Error("\u53EA\u6709 Backlog \u7684\u4EFB\u52D9\u53EF\u4EE5\u7DE8\u8F2F");
6338
6673
  }
6674
+ if (p.effort !== void 0 && !AGENT_EFFORTS.includes(p.effort)) invalid("unknown effort");
6339
6675
  let gitPatch = {};
6340
6676
  const nextProject = p.projectPath || existing.projectPath;
6341
6677
  const nextBranch = p.branch === void 0 ? "" : str(p.branch, "branch").trim();
@@ -6355,7 +6691,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6355
6691
  }
6356
6692
  const info = await getGitInfo(nextProject);
6357
6693
  if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
6358
- const projectName = path21.basename(nextProject);
6694
+ const projectName = path22.basename(nextProject);
6359
6695
  const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
6360
6696
  await teardownTask(existing.id);
6361
6697
  await removeTaskWorktree(existing);
@@ -6449,7 +6785,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6449
6785
  "claude:sessionExists": (taskId) => {
6450
6786
  const task = requireTask(taskId);
6451
6787
  if (!task.worktreePath) return false;
6452
- return fs17.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
6788
+ return fs18.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
6453
6789
  },
6454
6790
  "sessions:list": async () => {
6455
6791
  const keys = await sessions.list();
@@ -6496,7 +6832,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6496
6832
  const task = requireTask(taskId);
6497
6833
  if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
6498
6834
  const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
6499
- if (!fs17.existsSync(dir)) return "artifacts directory does not exist yet";
6835
+ if (!fs18.existsSync(dir)) return "artifacts directory does not exist yet";
6500
6836
  return getPlatform().openPath(dir);
6501
6837
  },
6502
6838
  "task:getSpecs": async (taskId) => {
@@ -6630,7 +6966,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6630
6966
  }
6631
6967
  await removeTaskWorktree(task);
6632
6968
  if (task.workspacePath) {
6633
- fs17.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
6969
+ fs18.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
6634
6970
  }
6635
6971
  clearConversation(task.id);
6636
6972
  removeTask(task.id);
@@ -6683,9 +7019,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6683
7019
  // Watch the containing directory, not the file: JsonStore renames a temp
6684
7020
  // file over the target and so replaces the inode on every save.
6685
7021
  watchStore() {
6686
- const file = path21.basename(getStorePath());
7022
+ const file = path22.basename(getStorePath());
6687
7023
  let debounce = null;
6688
- storeWatcher = fs17.watch(path21.dirname(getStorePath()), (_event, filename) => {
7024
+ storeWatcher = fs18.watch(path22.dirname(getStorePath()), (_event, filename) => {
6689
7025
  if (filename !== file) return;
6690
7026
  if (debounce) clearTimeout(debounce);
6691
7027
  debounce = setTimeout(() => {
@@ -6697,6 +7033,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
6697
7033
  });
6698
7034
  return () => storeWatcher?.close();
6699
7035
  },
7036
+ warmModelCatalog() {
7037
+ models.warm();
7038
+ },
6700
7039
  shutdown() {
6701
7040
  sessions.shutdown();
6702
7041
  cancelAllChatSends();
@@ -6718,9 +7057,9 @@ function createSessionBackend(sink, choice) {
6718
7057
  }
6719
7058
 
6720
7059
  // packages/core/src/web-server.ts
6721
- import fs18 from "fs";
7060
+ import fs19 from "fs";
6722
7061
  import http from "http";
6723
- import path22 from "path";
7062
+ import path23 from "path";
6724
7063
  import { timingSafeEqual } from "crypto";
6725
7064
  import { WebSocketServer } from "ws";
6726
7065
  var TOKEN_COOKIE = "vf_token";
@@ -6773,14 +7112,14 @@ function resolveStatic(root, urlPath) {
6773
7112
  return null;
6774
7113
  }
6775
7114
  if (decoded.includes("\0")) return null;
6776
- const rel = path22.posix.normalize(decoded).replace(/^\/+/, "");
6777
- const base2 = path22.resolve(root);
6778
- const candidates = [rel, path22.posix.join(rel, "index.html"), `${rel}.html`];
7115
+ const rel = path23.posix.normalize(decoded).replace(/^\/+/, "");
7116
+ const base2 = path23.resolve(root);
7117
+ const candidates = [rel, path23.posix.join(rel, "index.html"), `${rel}.html`];
6779
7118
  for (const c of candidates) {
6780
- const full = path22.resolve(base2, c);
6781
- if (full !== base2 && !full.startsWith(base2 + path22.sep)) return null;
7119
+ const full = path23.resolve(base2, c);
7120
+ if (full !== base2 && !full.startsWith(base2 + path23.sep)) return null;
6782
7121
  try {
6783
- if (fs18.statSync(full).isFile()) return full;
7122
+ if (fs19.statSync(full).isFile()) return full;
6784
7123
  } catch {
6785
7124
  }
6786
7125
  }
@@ -6841,7 +7180,7 @@ async function startWebServer(options) {
6841
7180
  send(res, 404, "Not found");
6842
7181
  return;
6843
7182
  }
6844
- const ext = path22.extname(file).toLowerCase();
7183
+ const ext = path23.extname(file).toLowerCase();
6845
7184
  const immutable = url.pathname.startsWith("/_next/static/");
6846
7185
  res.writeHead(200, {
6847
7186
  "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
@@ -6854,7 +7193,7 @@ async function startWebServer(options) {
6854
7193
  res.end();
6855
7194
  return;
6856
7195
  }
6857
- fs18.createReadStream(file).pipe(res);
7196
+ fs19.createReadStream(file).pipe(res);
6858
7197
  }
6859
7198
  const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
6860
7199
  function reject(socket, status, reason) {
@@ -7018,6 +7357,7 @@ async function startHost(options) {
7018
7357
  lock.port = server.port;
7019
7358
  updateLock(options.userDataDir, lock);
7020
7359
  const stopWatch = core.watchStore();
7360
+ core.warmModelCatalog();
7021
7361
  const stop = () => stopping ??= (async () => {
7022
7362
  stopWatch();
7023
7363
  core.shutdown();
@@ -7034,7 +7374,7 @@ function loginUrl(lock, pathname = "/home/") {
7034
7374
  return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
7035
7375
  }
7036
7376
  function isWebDir(dir) {
7037
- return fs19.existsSync(path23.join(dir, "home", "index.html"));
7377
+ return fs20.existsSync(path24.join(dir, "home", "index.html"));
7038
7378
  }
7039
7379
 
7040
7380
  // packages/cli/src/remote.ts
@@ -7151,14 +7491,14 @@ function connectToHost(lock) {
7151
7491
  }
7152
7492
 
7153
7493
  // packages/cli/src/doctor.ts
7154
- import fs20 from "fs";
7155
- import path24 from "path";
7156
- import { execFile as execFile9 } from "child_process";
7157
- import { promisify as promisify6 } from "util";
7494
+ import fs21 from "fs";
7495
+ import path25 from "path";
7496
+ import { execFile as execFile8 } from "child_process";
7497
+ import { promisify as promisify5 } from "util";
7158
7498
  init_env();
7159
7499
  init_git_bash();
7160
7500
  init_tmux_backend();
7161
- var pexec6 = promisify6(execFile9);
7501
+ var pexec5 = promisify5(execFile8);
7162
7502
  function parseVersion(text) {
7163
7503
  const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
7164
7504
  return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
@@ -7172,7 +7512,7 @@ function versionAtLeast(have, want) {
7172
7512
  }
7173
7513
  async function run2(cmd, args) {
7174
7514
  try {
7175
- const { stdout } = await pexec6(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
7515
+ const { stdout } = await pexec5(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
7176
7516
  return String(stdout).trim();
7177
7517
  } catch {
7178
7518
  return null;
@@ -7292,10 +7632,10 @@ async function runDoctor(options) {
7292
7632
  });
7293
7633
  }
7294
7634
  try {
7295
- fs20.mkdirSync(options.userDataDir, { recursive: true });
7296
- const probe = path24.join(options.userDataDir, `.doctor-${process.pid}`);
7297
- fs20.writeFileSync(probe, "");
7298
- fs20.rmSync(probe);
7635
+ fs21.mkdirSync(options.userDataDir, { recursive: true });
7636
+ const probe = path25.join(options.userDataDir, `.doctor-${process.pid}`);
7637
+ fs21.writeFileSync(probe, "");
7638
+ fs21.rmSync(probe);
7299
7639
  checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
7300
7640
  } catch (err) {
7301
7641
  checks.push({
@@ -7349,24 +7689,24 @@ Options:
7349
7689
  `.trim();
7350
7690
  function detectLayout() {
7351
7691
  const file = fileURLToPath(import.meta.url);
7352
- const here = path27.dirname(file);
7353
- const bundledWeb = path27.join(here, "web");
7354
- if (isWebDir(bundledWeb) || path27.basename(here) === "dist") {
7692
+ const here = path28.dirname(file);
7693
+ const bundledWeb = path28.join(here, "web");
7694
+ if (isWebDir(bundledWeb) || path28.basename(here) === "dist") {
7355
7695
  return {
7356
- version: readVersion(path27.join(here, "..", "package.json")),
7696
+ version: readVersion(path28.join(here, "..", "package.json")),
7357
7697
  webDir: isWebDir(bundledWeb) ? bundledWeb : null,
7358
7698
  sourceRoot: null,
7359
7699
  cliEntry: file
7360
7700
  };
7361
7701
  }
7362
- const root = path27.resolve(here, "..", "..", "..");
7702
+ const root = path28.resolve(here, "..", "..", "..");
7363
7703
  const override = process.env.VIBEFLOW_WEB_DIR;
7364
- const webDir = override && isWebDir(override) ? override : isWebDir(path27.join(root, "app")) ? path27.join(root, "app") : null;
7365
- return { version: readVersion(path27.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
7704
+ const webDir = override && isWebDir(override) ? override : isWebDir(path28.join(root, "app")) ? path28.join(root, "app") : null;
7705
+ return { version: readVersion(path28.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
7366
7706
  }
7367
7707
  function readVersion(pkgPath) {
7368
7708
  try {
7369
- return JSON.parse(fs22.readFileSync(pkgPath, "utf8")).version;
7709
+ return JSON.parse(fs23.readFileSync(pkgPath, "utf8")).version;
7370
7710
  } catch {
7371
7711
  return "0.0.0";
7372
7712
  }
@@ -7572,8 +7912,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
7572
7912
  console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
7573
7913
  return 1;
7574
7914
  }
7575
- const dir = path27.resolve(target);
7576
- if (!fs22.existsSync(dir) || !fs22.statSync(dir).isDirectory()) {
7915
+ const dir = path28.resolve(target);
7916
+ if (!fs23.existsSync(dir) || !fs23.statSync(dir).isDirectory()) {
7577
7917
  console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
7578
7918
  return 1;
7579
7919
  }