@kw-yeh/vibeflow 4.0.4 → 4.0.6

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
@@ -598,9 +598,9 @@ var init_attachments = __esm({
598
598
 
599
599
  // packages/tui/src/task-form.ts
600
600
  import { spawnSync } from "child_process";
601
- import fs17 from "fs";
602
- import os5 from "os";
603
- import path21 from "path";
601
+ import fs18 from "fs";
602
+ import os6 from "os";
603
+ import path22 from "path";
604
604
  import React, { useEffect, useRef, useState } from "react";
605
605
  import { Box, Text, render, useApp, useInput } from "ink";
606
606
  function newFormState(defaultAutoMode, agents) {
@@ -667,7 +667,7 @@ function visibleFields(s, ctx) {
667
667
  return fields;
668
668
  }
669
669
  function modelOptions(ctx, agentCli, current) {
670
- const listed = ctx.connections[agentCli]?.models ?? [];
670
+ const listed = ctx.models[agentCli] ?? [];
671
671
  const options = ["", ...listed];
672
672
  if (current && !listed.includes(current)) options.push(current);
673
673
  return options;
@@ -719,17 +719,17 @@ function editorCommand(env, platform) {
719
719
  return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
720
720
  }
721
721
  function editInEditor(text) {
722
- const dir = fs17.mkdtempSync(path21.join(os5.tmpdir(), "vibeflow-"));
723
- const file = path21.join(dir, "description.md");
722
+ const dir = fs18.mkdtempSync(path22.join(os6.tmpdir(), "vibeflow-"));
723
+ const file = path22.join(dir, "description.md");
724
724
  try {
725
- fs17.writeFileSync(file, text);
725
+ fs18.writeFileSync(file, text);
726
726
  const cmd = editorCommand(process.env, process.platform);
727
727
  const r = spawnSync(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
728
728
  if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
729
729
  if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
730
- return { text: fs17.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
730
+ return { text: fs18.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
731
731
  } finally {
732
- fs17.rmSync(dir, { recursive: true, force: true });
732
+ fs18.rmSync(dir, { recursive: true, force: true });
733
733
  }
734
734
  }
735
735
  function cycle(list, current, step) {
@@ -797,10 +797,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
797
797
  if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
798
798
  else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
799
799
  else if (editing === "project") {
800
- if (value2) setProject(path21.resolve(value2.replace(/^~(?=$|\/)/, os5.homedir())));
800
+ if (value2) setProject(path22.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir())));
801
801
  } else if (editing === "attachments" && value2) {
802
- const file = path21.resolve(value2.replace(/^~(?=$|\/)/, os5.homedir()));
803
- if (!fs17.existsSync(file) || !fs17.statSync(file).isFile()) {
802
+ const file = path22.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir()));
803
+ if (!fs18.existsSync(file) || !fs18.statSync(file).isFile()) {
804
804
  setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
805
805
  return;
806
806
  }
@@ -870,7 +870,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
870
870
  case "title":
871
871
  return s.title || "\uFF08\u4F8B\u5982\uFF1A\u5BE6\u4F5C\u767B\u5165\u9801\u9762\uFF09";
872
872
  case "project": {
873
- if (projectLocked) return `${s.projectPath ? path21.basename(s.projectPath) : "\u2014"}\uFF08\u4EFB\u52D9\u5DF2\u958B\u59CB\uFF0C\u9396\u5B9A\uFF09`;
873
+ if (projectLocked) return `${s.projectPath ? path22.basename(s.projectPath) : "\u2014"}\uFF08\u4EFB\u52D9\u5DF2\u958B\u59CB\uFF0C\u9396\u5B9A\uFF09`;
874
874
  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
875
  return `${s.projectPath}${missing ? "\uFF08\u8DEF\u5F91\u907A\u5931\uFF09" : ""}`;
876
876
  }
@@ -881,7 +881,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
881
881
  case "branch":
882
882
  return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
883
883
  case "attachments":
884
- return s.attachments.length ? s.attachments.map((a) => path21.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
884
+ return s.attachments.length ? s.attachments.map((a) => path22.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
885
885
  case "description": {
886
886
  if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
887
887
  const lines = s.description.split("\n");
@@ -899,7 +899,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
899
899
  return `\u2039 ${ctx.agents.find((a) => a.id === s.agentCli)?.name ?? s.agentCli} \u203A`;
900
900
  }
901
901
  case "model":
902
- return modelOptions(ctx, s.agentCli, s.model).length > 1 ? `\u2039 ${s.model || "\u4F7F\u7528\u9810\u8A2D model"} \u203A` : "\u5C1A\u672A\u9023\u7DDA\u6216\u7121\u6CD5\u53D6\u5F97 model list\uFF0C\u5C07\u4F7F\u7528\u9810\u8A2D model";
902
+ 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";
903
903
  case "submit":
904
904
  return busy ?? (s.mode === "new" ? "\uFF3B \u5EFA\u7ACB\u4EFB\u52D9 \uFF3D" : "\uFF3B \u5132\u5B58\u8B8A\u66F4 \uFF3D");
905
905
  }
@@ -912,7 +912,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
912
912
  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
913
  if (s.mode === "edit" && projectChanged(s)) return "\u66F4\u63DB\u5C08\u6848\u6703\u5728\u65B0\u5C08\u6848\u91CD\u5EFA worktree\uFF08\u6B64\u4EFB\u52D9\u5C1A\u672A\u958B\u59CB\uFF0C\u7121\u8B8A\u66F4\u6703\u907A\u5931\uFF09\u3002";
914
914
  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
- if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path21.basename(s.projectPath)}`;
915
+ if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path22.basename(s.projectPath)}`;
916
916
  }
917
917
  if (field === "branch") return info?.hasRemote ? "\u586B\u5BEB\u5F8C\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\uFF1B\u82E5 remote \u5DF2\u6709\u9019\u500B\u5206\u652F\uFF0C\u5247\u76F4\u63A5\u53D6\u56DE\u4E26\u63A5\u7E8C\u4E0A\u9762\u7684\u5DE5\u4F5C\u3002" : "\u586B\u5BEB\u5F8C\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u3002";
918
918
  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`;
@@ -967,11 +967,17 @@ async function loadFormContext(api) {
967
967
  api.listRecentProjects(),
968
968
  Promise.race([api.detectAgents(), timeout]).catch(() => null)
969
969
  ]).finally(() => clearTimeout(timer));
970
+ const lists = await Promise.all(
971
+ (agents ?? []).map(async (a) => {
972
+ const list = await api.listAgentModels(a.id).catch(() => null);
973
+ return [a.id, list ? list.models.map((m) => m.id) : []];
974
+ })
975
+ );
970
976
  return {
971
977
  ctx: {
972
978
  recent,
973
979
  agents,
974
- connections: state.settings.agentConnections ?? {},
980
+ models: Object.fromEntries(lists),
975
981
  workstationPath: state.settings.workstationPath?.trim() ?? ""
976
982
  },
977
983
  autoMode: state.settings.autoMode
@@ -1023,7 +1029,7 @@ __export(src_exports, {
1023
1029
  showDiff: () => showDiff
1024
1030
  });
1025
1031
  import { spawnSync as spawnSync2 } from "child_process";
1026
- import path22 from "path";
1032
+ import path23 from "path";
1027
1033
  import React2, { useEffect as useEffect2, useState as useState2 } from "react";
1028
1034
  import { Box as Box2, Text as Text2, render as render2, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
1029
1035
  function findDetachKey(text) {
@@ -1285,7 +1291,7 @@ function showDiff(task) {
1285
1291
  });
1286
1292
  }
1287
1293
  function tmuxAttach(opts, sessionName) {
1288
- const conf = path22.join(opts.userDataDir, "tmux.conf");
1294
+ const conf = path23.join(opts.userDataDir, "tmux.conf");
1289
1295
  const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
1290
1296
  const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
1291
1297
  spawnSync2("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
@@ -1415,8 +1421,8 @@ var init_src = __esm({
1415
1421
  });
1416
1422
 
1417
1423
  // packages/cli/src/main.ts
1418
- import fs18 from "fs";
1419
- import path23 from "path";
1424
+ import fs19 from "fs";
1425
+ import path24 from "path";
1420
1426
  import { fileURLToPath } from "url";
1421
1427
  import { parseArgs as parseArgs2 } from "node:util";
1422
1428
 
@@ -1681,6 +1687,11 @@ function migrateStore(store) {
1681
1687
  if (!store.has("recentProjects")) {
1682
1688
  store.set("recentProjects", recentProjectsFromBoard(store.get("board")));
1683
1689
  }
1690
+ const settings = store.get("settings");
1691
+ if (settings && "agentConnections" in settings) {
1692
+ const { agentConnections: _dropped, ...rest } = settings;
1693
+ store.set("settings", rest);
1694
+ }
1684
1695
  if (persistedVersion < STATE_VERSION) {
1685
1696
  store.set("version", STATE_VERSION);
1686
1697
  }
@@ -1760,8 +1771,6 @@ function createBridge(t) {
1760
1771
  },
1761
1772
  setBoard: (board) => t.invoke("vibeflow:setBoard", board),
1762
1773
  setSettings: (patch) => t.invoke("vibeflow:setSettings", patch),
1763
- connectAgent: (agentId2, apiKey) => t.invoke("settings:connectAgent", { agentId: agentId2, apiKey }),
1764
- refreshAgentModels: (agentId2) => t.invoke("settings:refreshAgentModels", agentId2),
1765
1774
  getGithubAuthStatus: () => t.invoke("settings:githubAuthStatus"),
1766
1775
  startGithubAuthLogin: () => t.invoke("settings:startGithubAuthLogin"),
1767
1776
  cancelGithubAuthLogin: () => t.invoke("settings:cancelGithubAuthLogin"),
@@ -1776,6 +1785,8 @@ function createBridge(t) {
1776
1785
  initRepository: (projectPath) => t.invoke("git:initRepository", projectPath),
1777
1786
  /** Agent CLIs (claude / codex) actually installed on PATH. */
1778
1787
  detectAgents: () => t.invoke("env:detectAgents"),
1788
+ /** Models the agent's own CLI offers; `refresh` asks the CLI instead of its cache. */
1789
+ listAgentModels: (agentId2, refresh = false) => t.invoke("agents:listModels", { agentId: agentId2, refresh }),
1779
1790
  createTask: (payload) => t.invoke("vibeflow:createTask", payload),
1780
1791
  updateTask: (payload) => t.invoke("vibeflow:updateTask", payload),
1781
1792
  /** Clear prior execution state and prepare a fresh run of this task. */
@@ -3519,8 +3530,8 @@ async function taskCommand(argv) {
3519
3530
  }
3520
3531
 
3521
3532
  // packages/cli/src/host.ts
3522
- import fs15 from "fs";
3523
- import path19 from "path";
3533
+ import fs16 from "fs";
3534
+ import path20 from "path";
3524
3535
  import { randomBytes as randomBytes2 } from "crypto";
3525
3536
 
3526
3537
  // packages/core/src/events.ts
@@ -3547,53 +3558,89 @@ var EventBus = class {
3547
3558
  };
3548
3559
 
3549
3560
  // packages/core/src/service.ts
3550
- import fs13 from "fs";
3551
- import os4 from "os";
3552
- import path17 from "path";
3561
+ import fs14 from "fs";
3562
+ import os5 from "os";
3563
+ import path18 from "path";
3553
3564
  import { randomUUID as randomUUID4 } from "crypto";
3554
3565
 
3555
- // packages/core/src/agent-connections.ts
3556
- function modelIdFromUnknown(value) {
3557
- if (!value || typeof value !== "object") return null;
3558
- const id = value.id;
3559
- return typeof id === "string" && id.trim() ? id.trim() : null;
3560
- }
3561
- function sortModels(ids) {
3562
- return Array.from(new Set(ids)).sort((a, b) => a.localeCompare(b));
3563
- }
3564
- function providerAuthError(status) {
3565
- if (status === 401 || status === 403) {
3566
- return "API key \u7121\u6548\u6216\u6C92\u6709\u6B0A\u9650\uFF0C\u8ACB\u78BA\u8A8D\u5F8C\u91CD\u65B0\u8F38\u5165\u3002";
3567
- }
3568
- return `\u53D6\u5F97 model list \u5931\u6557\uFF08HTTP ${status}\uFF09\u3002`;
3569
- }
3570
- async function fetchAgentModels(agentId2, apiKey) {
3571
- const key = apiKey.trim();
3572
- if (!key) throw new Error("\u8ACB\u8F38\u5165 API key\u3002");
3573
- const response = agentId2 === "claude" ? await fetch("https://api.anthropic.com/v1/models", {
3574
- headers: {
3575
- "x-api-key": key,
3576
- "anthropic-version": "2023-06-01"
3577
- }
3578
- }) : await fetch("https://api.openai.com/v1/models", {
3579
- headers: {
3580
- Authorization: `Bearer ${key}`
3581
- }
3566
+ // packages/core/src/agent-models.ts
3567
+ import { execFile as execFile5 } from "child_process";
3568
+ import fs10 from "fs";
3569
+ import os3 from "os";
3570
+ import path14 from "path";
3571
+ import { promisify as promisify3 } from "util";
3572
+ init_env();
3573
+ var pexec3 = promisify3(execFile5);
3574
+ var CODEX_MODELS_TIMEOUT_MS = 2e4;
3575
+ function builtinModels(id) {
3576
+ return (AGENT_CLIS.find((a) => a.id === id) ?? AGENT_CLIS[0]).models;
3577
+ }
3578
+ function defaultCodexHome() {
3579
+ const configured = process.env.CODEX_HOME?.trim();
3580
+ return configured ? configured : path14.join(os3.homedir(), ".codex");
3581
+ }
3582
+ async function defaultRunCodexModels() {
3583
+ const { stdout } = await pexec3("codex", ["debug", "models"], {
3584
+ env: execEnv(),
3585
+ timeout: CODEX_MODELS_TIMEOUT_MS,
3586
+ maxBuffer: 16 * 1024 * 1024,
3587
+ windowsHide: true
3582
3588
  });
3583
- if (!response.ok) {
3584
- throw new Error(providerAuthError(response.status));
3589
+ return stdout;
3590
+ }
3591
+ function parseCodexCatalog(raw) {
3592
+ const body = JSON.parse(raw);
3593
+ if (!Array.isArray(body.models)) throw new Error("Codex model catalog has no models array");
3594
+ const seen = /* @__PURE__ */ new Set();
3595
+ const models = [];
3596
+ for (const entry of body.models) {
3597
+ if (!entry || typeof entry !== "object") continue;
3598
+ const { slug, display_name: displayName, visibility } = entry;
3599
+ if (typeof slug !== "string" || !slug.trim() || visibility === "hide") continue;
3600
+ const id = slug.trim();
3601
+ if (seen.has(id)) continue;
3602
+ seen.add(id);
3603
+ models.push({
3604
+ id,
3605
+ label: typeof displayName === "string" && displayName.trim() ? displayName.trim() : id
3606
+ });
3607
+ }
3608
+ if (models.length === 0) throw new Error("Codex model catalog lists no selectable models");
3609
+ return models;
3610
+ }
3611
+ function errorMessage(err) {
3612
+ return err instanceof Error ? err.message : String(err);
3613
+ }
3614
+ async function listAgentModels(id, opts = {}) {
3615
+ if (id !== "codex") return { models: builtinModels(id), source: "builtin" };
3616
+ if (opts.refresh) {
3617
+ try {
3618
+ const stdout = await (opts.runCodexModels ?? defaultRunCodexModels)();
3619
+ return { models: parseCodexCatalog(stdout), source: "codex-cli" };
3620
+ } catch (err) {
3621
+ return { models: builtinModels(id), source: "builtin", error: errorMessage(err) };
3622
+ }
3623
+ }
3624
+ const cachePath = path14.join(opts.codexHome ?? defaultCodexHome(), "models_cache.json");
3625
+ try {
3626
+ const raw = await fs10.promises.readFile(cachePath, "utf8");
3627
+ return { models: parseCodexCatalog(raw), source: "codex-cache" };
3628
+ } catch (err) {
3629
+ const missing = err.code === "ENOENT";
3630
+ return {
3631
+ models: builtinModels(id),
3632
+ source: "builtin",
3633
+ ...missing ? {} : { error: errorMessage(err) }
3634
+ };
3585
3635
  }
3586
- const body = await response.json();
3587
- const data = Array.isArray(body.data) ? body.data : [];
3588
- return sortModels(data.map(modelIdFromUnknown).filter((id) => Boolean(id)));
3589
3636
  }
3590
3637
 
3591
3638
  // packages/core/src/github-auth.ts
3592
3639
  init_env();
3593
- import { execFile as execFile5 } from "child_process";
3594
- import { promisify as promisify3 } from "util";
3640
+ import { execFile as execFile6 } from "child_process";
3641
+ import { promisify as promisify4 } from "util";
3595
3642
  import * as nodePty3 from "node-pty";
3596
- var pexec3 = promisify3(execFile5);
3643
+ var pexec4 = promisify4(execFile6);
3597
3644
  var GITHUB_HOST = "github.com";
3598
3645
  var DEVICE_URL = "https://github.com/login/device";
3599
3646
  var activeLogin = null;
@@ -3622,13 +3669,13 @@ function statusFromBody(body) {
3622
3669
  error: active.error
3623
3670
  };
3624
3671
  }
3625
- function errorMessage(err) {
3672
+ function errorMessage2(err) {
3626
3673
  if (err instanceof Error) return err.message;
3627
3674
  return String(err);
3628
3675
  }
3629
3676
  async function getGitHubCliAuthStatus() {
3630
3677
  try {
3631
- const { stdout } = await pexec3(
3678
+ const { stdout } = await pexec4(
3632
3679
  "gh",
3633
3680
  ["auth", "status", "--hostname", GITHUB_HOST, "--json", "hosts"],
3634
3681
  {
@@ -3648,7 +3695,7 @@ async function getGitHubCliAuthStatus() {
3648
3695
  } catch {
3649
3696
  }
3650
3697
  }
3651
- const message = errorMessage(err);
3698
+ const message = errorMessage2(err);
3652
3699
  return {
3653
3700
  installed: !/ENOENT|not found/i.test(message),
3654
3701
  authenticated: false,
@@ -3660,7 +3707,7 @@ async function logoutGitHubCli() {
3660
3707
  const status = await getGitHubCliAuthStatus();
3661
3708
  if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
3662
3709
  if (!status.login) return status;
3663
- await pexec3(
3710
+ await pexec4(
3664
3711
  "gh",
3665
3712
  ["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
3666
3713
  {
@@ -3701,7 +3748,7 @@ function startGitHubCliLogin(onEvent) {
3701
3748
  }
3702
3749
  );
3703
3750
  } catch (err) {
3704
- onEvent({ type: "error", error: errorMessage(err) });
3751
+ onEvent({ type: "error", error: errorMessage2(err) });
3705
3752
  return;
3706
3753
  }
3707
3754
  activeLogin = proc;
@@ -3743,38 +3790,38 @@ function startGitHubCliLogin(onEvent) {
3743
3790
  }
3744
3791
 
3745
3792
  // packages/core/src/board-cli.ts
3746
- import fs10 from "fs";
3747
- import path14 from "path";
3793
+ import fs11 from "fs";
3794
+ import path15 from "path";
3748
3795
  import { pathToFileURL } from "url";
3749
3796
  async function boardCliLaunchInfo() {
3750
- const storeDir = path14.dirname(getStorePath());
3797
+ const storeDir = path15.dirname(getStorePath());
3751
3798
  const entry = getPlatform().cliEntry?.();
3752
- if (entry && fs10.existsSync(entry)) return { storeDir, cliPath: entry };
3799
+ if (entry && fs11.existsSync(entry)) return { storeDir, cliPath: entry };
3753
3800
  const root = getPlatform().sourceRoot();
3754
3801
  if (!root) return { storeDir };
3755
- const cliPath = path14.join(root, "scripts", "vibeflow.mjs");
3756
- const loaderPath = path14.join(root, "test", "support", "register.mjs");
3757
- if (!fs10.existsSync(cliPath) || !fs10.existsSync(loaderPath)) return { storeDir };
3802
+ const cliPath = path15.join(root, "scripts", "vibeflow.mjs");
3803
+ const loaderPath = path15.join(root, "test", "support", "register.mjs");
3804
+ if (!fs11.existsSync(cliPath) || !fs11.existsSync(loaderPath)) return { storeDir };
3758
3805
  return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
3759
3806
  }
3760
3807
 
3761
3808
  // packages/core/src/decisions.ts
3762
- import fs11 from "fs";
3763
- import path15 from "path";
3809
+ import fs12 from "fs";
3810
+ import path16 from "path";
3764
3811
  var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
3765
3812
  var MAX_DECISION_BYTES = 256 * 1024;
3766
3813
  function decisionsKey(worktreePath, branch) {
3767
- return worktreePath ? path15.basename(worktreePath) : branch.replace(/\//g, "-");
3814
+ return worktreePath ? path16.basename(worktreePath) : branch.replace(/\//g, "-");
3768
3815
  }
3769
3816
  function decisionsPath(workspacePath, key) {
3770
- return path15.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
3817
+ return path16.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
3771
3818
  }
3772
3819
  function readDecisions(workspacePath, key) {
3773
3820
  const target = decisionsPath(workspacePath, key);
3774
3821
  try {
3775
- const stat = fs11.statSync(target);
3822
+ const stat = fs12.statSync(target);
3776
3823
  if (!stat.isFile()) return { path: target, markdown: null };
3777
- const buf = fs11.readFileSync(target);
3824
+ const buf = fs12.readFileSync(target);
3778
3825
  return {
3779
3826
  path: target,
3780
3827
  markdown: buf.subarray(0, MAX_DECISION_BYTES).toString("utf8"),
@@ -3787,7 +3834,7 @@ function readDecisions(workspacePath, key) {
3787
3834
  }
3788
3835
  function deleteDecisions(workspacePath, key) {
3789
3836
  try {
3790
- fs11.rmSync(decisionsPath(workspacePath, key), { force: true });
3837
+ fs12.rmSync(decisionsPath(workspacePath, key), { force: true });
3791
3838
  } catch {
3792
3839
  }
3793
3840
  }
@@ -4079,9 +4126,9 @@ function startChatSend(opts, sender) {
4079
4126
  init_attachments();
4080
4127
 
4081
4128
  // packages/core/src/library.ts
4082
- import fs12 from "fs";
4083
- import os3 from "os";
4084
- import path16 from "path";
4129
+ import fs13 from "fs";
4130
+ import os4 from "os";
4131
+ import path17 from "path";
4085
4132
  var LIBRARY_DIR = "library";
4086
4133
  var LIBRARY_KINDS = ["skill", "prompt", "script"];
4087
4134
  var KIND_DIR = {
@@ -4094,15 +4141,15 @@ var MAX_NAME_BYTES = 100;
4094
4141
  function libraryPaths(root) {
4095
4142
  return {
4096
4143
  root,
4097
- skills: path16.join(root, KIND_DIR.skill),
4098
- prompts: path16.join(root, KIND_DIR.prompt),
4099
- scripts: path16.join(root, KIND_DIR.script),
4100
- plugin: path16.join(root, "plugin"),
4101
- codexHome: path16.join(root, "codex-home")
4144
+ skills: path17.join(root, KIND_DIR.skill),
4145
+ prompts: path17.join(root, KIND_DIR.prompt),
4146
+ scripts: path17.join(root, KIND_DIR.script),
4147
+ plugin: path17.join(root, "plugin"),
4148
+ codexHome: path17.join(root, "codex-home")
4102
4149
  };
4103
4150
  }
4104
4151
  async function libraryRoot() {
4105
- return path16.join(getPlatform().userDataDir(), LIBRARY_DIR);
4152
+ return path17.join(getPlatform().userDataDir(), LIBRARY_DIR);
4106
4153
  }
4107
4154
  function truncateUtf82(value, maxBytes) {
4108
4155
  let result = "";
@@ -4124,11 +4171,11 @@ function entryKey(kind, name) {
4124
4171
  return `${kind}/${name}`;
4125
4172
  }
4126
4173
  function indexPath(root) {
4127
- return path16.join(root, INDEX_FILE);
4174
+ return path17.join(root, INDEX_FILE);
4128
4175
  }
4129
4176
  function readIndex(root) {
4130
4177
  try {
4131
- const parsed = JSON.parse(fs12.readFileSync(indexPath(root), "utf8"));
4178
+ const parsed = JSON.parse(fs13.readFileSync(indexPath(root), "utf8"));
4132
4179
  if (parsed && typeof parsed === "object" && "entries" in parsed) {
4133
4180
  const entries = parsed.entries;
4134
4181
  if (entries && typeof entries === "object") return { version: 1, entries };
@@ -4138,21 +4185,21 @@ function readIndex(root) {
4138
4185
  return { version: 1, entries: {} };
4139
4186
  }
4140
4187
  function writeIndex(root, index) {
4141
- fs12.mkdirSync(root, { recursive: true });
4142
- fs12.writeFileSync(indexPath(root), `${JSON.stringify(index, null, 2)}
4188
+ fs13.mkdirSync(root, { recursive: true });
4189
+ fs13.writeFileSync(indexPath(root), `${JSON.stringify(index, null, 2)}
4143
4190
  `);
4144
4191
  }
4145
4192
  function ensureLibrary(root) {
4146
4193
  const paths = libraryPaths(root);
4147
4194
  for (const kind of LIBRARY_KINDS) {
4148
- fs12.mkdirSync(path16.join(root, KIND_DIR[kind]), { recursive: true });
4195
+ fs13.mkdirSync(path17.join(root, KIND_DIR[kind]), { recursive: true });
4149
4196
  }
4150
4197
  return paths;
4151
4198
  }
4152
4199
  function readSkillDescription(skillMdPath) {
4153
4200
  let content;
4154
4201
  try {
4155
- content = fs12.readFileSync(skillMdPath, "utf8");
4202
+ content = fs13.readFileSync(skillMdPath, "utf8");
4156
4203
  } catch {
4157
4204
  return void 0;
4158
4205
  }
@@ -4167,16 +4214,16 @@ function readSkillDescription(skillMdPath) {
4167
4214
  return void 0;
4168
4215
  }
4169
4216
  function entryPath(root, kind, name) {
4170
- return path16.join(root, KIND_DIR[kind], name);
4217
+ return path17.join(root, KIND_DIR[kind], name);
4171
4218
  }
4172
4219
  function listLibrary(root) {
4173
4220
  const index = readIndex(root);
4174
4221
  const entries = [];
4175
4222
  for (const kind of LIBRARY_KINDS) {
4176
- const dir = path16.join(root, KIND_DIR[kind]);
4223
+ const dir = path17.join(root, KIND_DIR[kind]);
4177
4224
  let dirents;
4178
4225
  try {
4179
- dirents = fs12.readdirSync(dir, { withFileTypes: true });
4226
+ dirents = fs13.readdirSync(dir, { withFileTypes: true });
4180
4227
  } catch {
4181
4228
  continue;
4182
4229
  }
@@ -4185,14 +4232,14 @@ function listLibrary(root) {
4185
4232
  const wantsDirectory = kind === "skill";
4186
4233
  let stat;
4187
4234
  try {
4188
- stat = fs12.statSync(path16.join(dir, dirent.name));
4235
+ stat = fs13.statSync(path17.join(dir, dirent.name));
4189
4236
  } catch {
4190
4237
  continue;
4191
4238
  }
4192
4239
  if (stat.isDirectory() !== wantsDirectory) continue;
4193
4240
  const name = dirent.name;
4194
- const full = path16.join(dir, name);
4195
- if (wantsDirectory && !fs12.existsSync(path16.join(full, "SKILL.md"))) continue;
4241
+ const full = path17.join(dir, name);
4242
+ if (wantsDirectory && !fs13.existsSync(path17.join(full, "SKILL.md"))) continue;
4196
4243
  const key = entryKey(kind, name);
4197
4244
  const record = index.entries[key] ?? {};
4198
4245
  entries.push({
@@ -4200,7 +4247,7 @@ function listLibrary(root) {
4200
4247
  kind,
4201
4248
  name,
4202
4249
  path: full,
4203
- description: wantsDirectory ? readSkillDescription(path16.join(full, "SKILL.md")) : record.description,
4250
+ description: wantsDirectory ? readSkillDescription(path17.join(full, "SKILL.md")) : record.description,
4204
4251
  sourcePath: record.sourcePath,
4205
4252
  importedAt: record.importedAt,
4206
4253
  enabled: record.enabled !== false
@@ -4216,21 +4263,21 @@ function annotate(root, key, patch) {
4216
4263
  }
4217
4264
  function importEntry(root, kind, sourcePath) {
4218
4265
  ensureLibrary(root);
4219
- const resolved = path16.resolve(sourcePath);
4220
- const stat = fs12.statSync(resolved);
4266
+ const resolved = path17.resolve(sourcePath);
4267
+ const stat = fs13.statSync(resolved);
4221
4268
  if (kind === "skill") {
4222
4269
  if (!stat.isDirectory()) throw new Error("skill \u5FC5\u9808\u662F\u542B SKILL.md \u7684\u76EE\u9304");
4223
- if (!fs12.existsSync(path16.join(resolved, "SKILL.md"))) {
4270
+ if (!fs13.existsSync(path17.join(resolved, "SKILL.md"))) {
4224
4271
  throw new Error("\u76EE\u9304\u5167\u627E\u4E0D\u5230 SKILL.md");
4225
4272
  }
4226
4273
  } else if (!stat.isFile()) {
4227
4274
  throw new Error(`${kind} \u5FC5\u9808\u662F\u55AE\u4E00\u6A94\u6848`);
4228
4275
  }
4229
- const name = safeLibraryName(path16.basename(resolved));
4276
+ const name = safeLibraryName(path17.basename(resolved));
4230
4277
  const target = entryPath(root, kind, name);
4231
- fs12.rmSync(target, { recursive: true, force: true });
4232
- fs12.cpSync(resolved, target, { recursive: kind === "skill", dereference: true });
4233
- if (kind === "script") fs12.chmodSync(target, 493);
4278
+ fs13.rmSync(target, { recursive: true, force: true });
4279
+ fs13.cpSync(resolved, target, { recursive: kind === "skill", dereference: true });
4280
+ if (kind === "script") fs13.chmodSync(target, 493);
4234
4281
  annotate(root, entryKey(kind, name), {
4235
4282
  sourcePath: resolved,
4236
4283
  importedAt: Date.now(),
@@ -4242,13 +4289,13 @@ function createEntry(root, kind, rawName, content, description) {
4242
4289
  ensureLibrary(root);
4243
4290
  const name = safeLibraryName(rawName);
4244
4291
  const target = entryPath(root, kind, name);
4245
- if (fs12.existsSync(target)) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u9805\u76EE\uFF1A${name}`);
4292
+ if (fs13.existsSync(target)) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u9805\u76EE\uFF1A${name}`);
4246
4293
  if (kind === "skill") {
4247
- fs12.mkdirSync(target, { recursive: true });
4248
- fs12.writeFileSync(path16.join(target, "SKILL.md"), content);
4294
+ fs13.mkdirSync(target, { recursive: true });
4295
+ fs13.writeFileSync(path17.join(target, "SKILL.md"), content);
4249
4296
  } else {
4250
- fs12.writeFileSync(target, content);
4251
- if (kind === "script") fs12.chmodSync(target, 493);
4297
+ fs13.writeFileSync(target, content);
4298
+ if (kind === "script") fs13.chmodSync(target, 493);
4252
4299
  }
4253
4300
  annotate(root, entryKey(kind, name), {
4254
4301
  importedAt: Date.now(),
@@ -4262,16 +4309,16 @@ function setEntryDescription(root, kind, name, description) {
4262
4309
  }
4263
4310
  function updateEntry(root, kind, name, content) {
4264
4311
  const target = entryPath(root, kind, name);
4265
- if (!fs12.existsSync(target)) throw new Error(`\u627E\u4E0D\u5230\u9805\u76EE\uFF1A${name}`);
4266
- fs12.writeFileSync(kind === "skill" ? path16.join(target, "SKILL.md") : target, content);
4312
+ if (!fs13.existsSync(target)) throw new Error(`\u627E\u4E0D\u5230\u9805\u76EE\uFF1A${name}`);
4313
+ fs13.writeFileSync(kind === "skill" ? path17.join(target, "SKILL.md") : target, content);
4267
4314
  return requireEntry(root, kind, name);
4268
4315
  }
4269
4316
  function readEntryContent(root, kind, name) {
4270
4317
  const target = entryPath(root, kind, name);
4271
- return fs12.readFileSync(kind === "skill" ? path16.join(target, "SKILL.md") : target, "utf8");
4318
+ return fs13.readFileSync(kind === "skill" ? path17.join(target, "SKILL.md") : target, "utf8");
4272
4319
  }
4273
4320
  function deleteEntry(root, kind, name) {
4274
- fs12.rmSync(entryPath(root, kind, name), { recursive: true, force: true });
4321
+ fs13.rmSync(entryPath(root, kind, name), { recursive: true, force: true });
4275
4322
  const index = readIndex(root);
4276
4323
  delete index.entries[entryKey(kind, name)];
4277
4324
  writeIndex(root, index);
@@ -4285,11 +4332,11 @@ function requireEntry(root, kind, name) {
4285
4332
  return found2;
4286
4333
  }
4287
4334
  function projectSkillsDir(worktreePath) {
4288
- return path16.join(worktreePath, ".claude", "skills");
4335
+ return path17.join(worktreePath, ".claude", "skills");
4289
4336
  }
4290
4337
  function skillNamesIn(dir) {
4291
4338
  try {
4292
- return fs12.readdirSync(dir, { withFileTypes: true }).filter((d) => !d.name.startsWith(".")).map((d) => d.name).filter((name) => fs12.existsSync(path16.join(dir, name, "SKILL.md"))).sort();
4339
+ return fs13.readdirSync(dir, { withFileTypes: true }).filter((d) => !d.name.startsWith(".")).map((d) => d.name).filter((name) => fs13.existsSync(path17.join(dir, name, "SKILL.md"))).sort();
4293
4340
  } catch {
4294
4341
  return [];
4295
4342
  }
@@ -4298,11 +4345,11 @@ var PLUGIN_MANIFEST_DIR = ".claude-plugin";
4298
4345
  function buildPluginDir(root) {
4299
4346
  const paths = libraryPaths(root);
4300
4347
  const enabled = listLibrary(root).filter((e) => e.kind === "skill" && e.enabled);
4301
- fs12.rmSync(path16.join(paths.plugin, "skills"), { recursive: true, force: true });
4302
- fs12.mkdirSync(path16.join(paths.plugin, PLUGIN_MANIFEST_DIR), { recursive: true });
4303
- fs12.mkdirSync(path16.join(paths.plugin, "skills"), { recursive: true });
4304
- fs12.writeFileSync(
4305
- path16.join(paths.plugin, PLUGIN_MANIFEST_DIR, "plugin.json"),
4348
+ fs13.rmSync(path17.join(paths.plugin, "skills"), { recursive: true, force: true });
4349
+ fs13.mkdirSync(path17.join(paths.plugin, PLUGIN_MANIFEST_DIR), { recursive: true });
4350
+ fs13.mkdirSync(path17.join(paths.plugin, "skills"), { recursive: true });
4351
+ fs13.writeFileSync(
4352
+ path17.join(paths.plugin, PLUGIN_MANIFEST_DIR, "plugin.json"),
4306
4353
  `${JSON.stringify(
4307
4354
  {
4308
4355
  name: "vibeflow-library",
@@ -4315,7 +4362,7 @@ function buildPluginDir(root) {
4315
4362
  `
4316
4363
  );
4317
4364
  for (const entry of enabled) {
4318
- fs12.cpSync(entry.path, path16.join(paths.plugin, "skills", entry.name), {
4365
+ fs13.cpSync(entry.path, path17.join(paths.plugin, "skills", entry.name), {
4319
4366
  recursive: true,
4320
4367
  dereference: true
4321
4368
  });
@@ -4323,19 +4370,19 @@ function buildPluginDir(root) {
4323
4370
  return { pluginDir: paths.plugin, skills: enabled.map((e) => e.name) };
4324
4371
  }
4325
4372
  function linkOrSkip(target, linkPath) {
4326
- if (!fs12.existsSync(target)) return false;
4327
- const source = path16.resolve(target);
4328
- const attempts = fs12.statSync(source).isDirectory() ? [
4329
- () => fs12.symlinkSync(source, linkPath, "junction"),
4330
- () => fs12.cpSync(source, linkPath, { recursive: true, dereference: true })
4373
+ if (!fs13.existsSync(target)) return false;
4374
+ const source = path17.resolve(target);
4375
+ const attempts = fs13.statSync(source).isDirectory() ? [
4376
+ () => fs13.symlinkSync(source, linkPath, "junction"),
4377
+ () => fs13.cpSync(source, linkPath, { recursive: true, dereference: true })
4331
4378
  ] : [
4332
- () => fs12.symlinkSync(source, linkPath),
4333
- () => fs12.linkSync(source, linkPath),
4334
- () => fs12.copyFileSync(source, linkPath)
4379
+ () => fs13.symlinkSync(source, linkPath),
4380
+ () => fs13.linkSync(source, linkPath),
4381
+ () => fs13.copyFileSync(source, linkPath)
4335
4382
  ];
4336
4383
  for (const attempt of attempts) {
4337
4384
  try {
4338
- fs12.rmSync(linkPath, { recursive: true, force: true });
4385
+ fs13.rmSync(linkPath, { recursive: true, force: true });
4339
4386
  attempt();
4340
4387
  return true;
4341
4388
  } catch {
@@ -4343,13 +4390,13 @@ function linkOrSkip(target, linkPath) {
4343
4390
  }
4344
4391
  return false;
4345
4392
  }
4346
- function buildCodexHome(root, worktreePath, userCodexHome = path16.join(os3.homedir(), ".codex")) {
4393
+ function buildCodexHome(root, worktreePath, userCodexHome = path17.join(os4.homedir(), ".codex")) {
4347
4394
  const paths = libraryPaths(root);
4348
- const skillsDir = path16.join(paths.codexHome, "skills");
4349
- fs12.rmSync(skillsDir, { recursive: true, force: true });
4350
- fs12.mkdirSync(skillsDir, { recursive: true });
4395
+ const skillsDir = path17.join(paths.codexHome, "skills");
4396
+ fs13.rmSync(skillsDir, { recursive: true, force: true });
4397
+ fs13.mkdirSync(skillsDir, { recursive: true });
4351
4398
  for (const file of ["auth.json", "config.toml"]) {
4352
- linkOrSkip(path16.join(userCodexHome, file), path16.join(paths.codexHome, file));
4399
+ linkOrSkip(path17.join(userCodexHome, file), path17.join(paths.codexHome, file));
4353
4400
  }
4354
4401
  const result = {
4355
4402
  codexHome: paths.codexHome,
@@ -4362,20 +4409,20 @@ function buildCodexHome(root, worktreePath, userCodexHome = path16.join(os3.home
4362
4409
  const projectDir = worktreePath ? projectSkillsDir(worktreePath) : void 0;
4363
4410
  if (projectDir) {
4364
4411
  for (const name of skillNamesIn(projectDir)) {
4365
- if (linkOrSkip(path16.join(projectDir, name), path16.join(skillsDir, name))) {
4412
+ if (linkOrSkip(path17.join(projectDir, name), path17.join(skillsDir, name))) {
4366
4413
  result.project.push(name);
4367
4414
  claimed.set(name, "project");
4368
4415
  }
4369
4416
  }
4370
4417
  }
4371
- const userDir = path16.join(userCodexHome, "skills");
4418
+ const userDir = path17.join(userCodexHome, "skills");
4372
4419
  for (const name of skillNamesIn(userDir)) {
4373
4420
  const owner = claimed.get(name);
4374
4421
  if (owner) {
4375
4422
  result.shadowed.push({ name, keptFrom: owner, dropped: "user" });
4376
4423
  continue;
4377
4424
  }
4378
- if (linkOrSkip(path16.join(userDir, name), path16.join(skillsDir, name))) {
4425
+ if (linkOrSkip(path17.join(userDir, name), path17.join(skillsDir, name))) {
4379
4426
  result.user.push(name);
4380
4427
  claimed.set(name, "user");
4381
4428
  }
@@ -4387,7 +4434,7 @@ function buildCodexHome(root, worktreePath, userCodexHome = path16.join(os3.home
4387
4434
  result.shadowed.push({ name: entry.name, keptFrom: owner, dropped: "library" });
4388
4435
  continue;
4389
4436
  }
4390
- fs12.cpSync(entry.path, path16.join(skillsDir, entry.name), {
4437
+ fs13.cpSync(entry.path, path17.join(skillsDir, entry.name), {
4391
4438
  recursive: true,
4392
4439
  dereference: true
4393
4440
  });
@@ -4703,13 +4750,13 @@ function createCore({ sessions, bus, version }) {
4703
4750
  }
4704
4751
  function claudeSessionFile(cwd, sessionId) {
4705
4752
  const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
4706
- return path17.join(os4.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
4753
+ return path18.join(os5.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
4707
4754
  }
4708
4755
  async function removeTaskWorktree(task) {
4709
4756
  if (!task.projectPath || !task.worktreePath) return;
4710
4757
  const branch = task.branch || fallbackBranchName(task.id);
4711
4758
  await removeWorktree(task.projectPath, task.worktreePath);
4712
- deleteArtifacts(task.workspacePath ?? path17.dirname(task.worktreePath), task.worktreePath);
4759
+ deleteArtifacts(task.workspacePath ?? path18.dirname(task.worktreePath), task.worktreePath);
4713
4760
  await deleteBranch(task.projectPath, branch);
4714
4761
  }
4715
4762
  const handlers = {
@@ -4720,66 +4767,7 @@ function createCore({ sessions, bus, version }) {
4720
4767
  return getState();
4721
4768
  },
4722
4769
  "vibeflow:setSettings": (patch) => {
4723
- const p = obj(patch, "patch");
4724
- const { agentConnections: _ignored, ...rest } = p;
4725
- setSettings(rest);
4726
- return getState();
4727
- },
4728
- "settings:connectAgent": async (payload) => {
4729
- const { agentId: rawId, apiKey } = obj(payload, "payload");
4730
- const id = agentId(rawId);
4731
- const key = str(apiKey, "apiKey");
4732
- const models = await fetchAgentModels(id, key);
4733
- const state = getState();
4734
- setSettings({
4735
- agentConnections: {
4736
- ...state.settings.agentConnections ?? {},
4737
- [id]: {
4738
- connected: true,
4739
- apiKey: key.trim(),
4740
- models,
4741
- error: void 0,
4742
- updatedAt: Date.now()
4743
- }
4744
- }
4745
- });
4746
- return getState();
4747
- },
4748
- "settings:refreshAgentModels": async (rawId) => {
4749
- const id = agentId(rawId);
4750
- const connections = getState().settings.agentConnections ?? {};
4751
- const existing = connections[id];
4752
- if (!existing?.apiKey) {
4753
- setSettings({
4754
- agentConnections: {
4755
- ...connections,
4756
- [id]: { connected: false, models: [], error: "\u5C1A\u672A\u7D81\u5B9A API key\u3002", updatedAt: Date.now() }
4757
- }
4758
- });
4759
- return getState();
4760
- }
4761
- try {
4762
- const models = await fetchAgentModels(id, existing.apiKey);
4763
- setSettings({
4764
- agentConnections: {
4765
- ...connections,
4766
- [id]: { ...existing, connected: true, models, error: void 0, updatedAt: Date.now() }
4767
- }
4768
- });
4769
- } catch (err) {
4770
- setSettings({
4771
- agentConnections: {
4772
- ...connections,
4773
- [id]: {
4774
- ...existing,
4775
- connected: false,
4776
- models: [],
4777
- error: err instanceof Error ? err.message : String(err),
4778
- updatedAt: Date.now()
4779
- }
4780
- }
4781
- });
4782
- }
4770
+ setSettings(obj(patch, "patch"));
4783
4771
  return getState();
4784
4772
  },
4785
4773
  "settings:githubAuthStatus": () => getGitHubCliAuthStatus(),
@@ -4791,6 +4779,10 @@ function createCore({ sessions, bus, version }) {
4791
4779
  },
4792
4780
  "settings:logoutGithubAuth": () => logoutGitHubCli(),
4793
4781
  "env:detectAgents": () => detectAgents(),
4782
+ "agents:listModels": (payload) => {
4783
+ const { agentId: rawId, refresh } = obj(payload, "payload");
4784
+ return listAgentModels(agentId(rawId), { refresh: refresh === true });
4785
+ },
4794
4786
  // A task does not exist yet when a project is picked, so these two are the
4795
4787
  // ones that must take a path. They only inspect or `git init` it.
4796
4788
  "git:getInfo": (projectPath) => getGitInfo(str(projectPath ?? "", "projectPath")),
@@ -4803,8 +4795,8 @@ function createCore({ sessions, bus, version }) {
4803
4795
  "projects:listRecent": () => listRecentProjects(getStore()),
4804
4796
  // `vibeflow open <path>` while a host runs: only the host writes the store.
4805
4797
  "projects:record": (projectPath) => {
4806
- const p = path17.resolve(str(projectPath, "projectPath"));
4807
- if (!fs13.existsSync(p) || !fs13.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
4798
+ const p = path18.resolve(str(projectPath, "projectPath"));
4799
+ if (!fs14.existsSync(p) || !fs14.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
4808
4800
  recordRecentProject(getStore(), p);
4809
4801
  return listRecentProjects(getStore());
4810
4802
  },
@@ -4844,7 +4836,7 @@ function createCore({ sessions, bus, version }) {
4844
4836
  if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
4845
4837
  await teardownTask(task.id);
4846
4838
  await resetWorktreeToBase(task.worktreePath, task.baseBranch);
4847
- const workspacePath = task.workspacePath ?? path17.dirname(task.worktreePath);
4839
+ const workspacePath = task.workspacePath ?? path18.dirname(task.worktreePath);
4848
4840
  deleteArtifacts(workspacePath, task.worktreePath);
4849
4841
  deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
4850
4842
  resetSubAgents(task.worktreePath);
@@ -4883,9 +4875,9 @@ function createCore({ sessions, bus, version }) {
4883
4875
  if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
4884
4876
  await teardownTask(existing.id);
4885
4877
  await removeTaskWorktree(existing);
4886
- const projectName = path17.basename(nextProject);
4878
+ const projectName = path18.basename(nextProject);
4887
4879
  const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
4888
- await fs13.promises.mkdir(workspacePath, { recursive: true });
4880
+ await fs14.promises.mkdir(workspacePath, { recursive: true });
4889
4881
  const result = await provisionWorktree(
4890
4882
  nextProject,
4891
4883
  workspacePath,
@@ -4973,7 +4965,7 @@ function createCore({ sessions, bus, version }) {
4973
4965
  "claude:sessionExists": (taskId) => {
4974
4966
  const task = requireTask(taskId);
4975
4967
  if (!task.worktreePath) return false;
4976
- return fs13.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
4968
+ return fs14.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
4977
4969
  },
4978
4970
  "sessions:list": async () => {
4979
4971
  const keys = await sessions.list();
@@ -4992,9 +4984,9 @@ function createCore({ sessions, bus, version }) {
4992
4984
  "git:getDiffEntries": (taskId, opts) => {
4993
4985
  const task = requireTask(taskId);
4994
4986
  if (!task.worktreePath) return [];
4995
- const fetch2 = opts && typeof opts === "object" ? opts.fetch : void 0;
4987
+ const fetch = opts && typeof opts === "object" ? opts.fetch : void 0;
4996
4988
  return getWorktreeDiffEntries(task.worktreePath, task.baseBranch ?? "HEAD", {
4997
- fetch: typeof fetch2 === "boolean" ? fetch2 : void 0
4989
+ fetch: typeof fetch === "boolean" ? fetch : void 0
4998
4990
  });
4999
4991
  },
5000
4992
  "git:getDiffFile": (taskId, filePath) => {
@@ -5020,7 +5012,7 @@ function createCore({ sessions, bus, version }) {
5020
5012
  const task = requireTask(taskId);
5021
5013
  if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
5022
5014
  const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
5023
- if (!fs13.existsSync(dir)) return "artifacts directory does not exist yet";
5015
+ if (!fs14.existsSync(dir)) return "artifacts directory does not exist yet";
5024
5016
  return getPlatform().openPath(dir);
5025
5017
  },
5026
5018
  "task:getDecisions": (taskId) => {
@@ -5173,9 +5165,9 @@ function createCore({ sessions, bus, version }) {
5173
5165
  // Watch the containing directory, not the file: JsonStore renames a temp
5174
5166
  // file over the target and so replaces the inode on every save.
5175
5167
  watchStore() {
5176
- const file = path17.basename(getStorePath());
5168
+ const file = path18.basename(getStorePath());
5177
5169
  let debounce = null;
5178
- storeWatcher = fs13.watch(path17.dirname(getStorePath()), (_event, filename) => {
5170
+ storeWatcher = fs14.watch(path18.dirname(getStorePath()), (_event, filename) => {
5179
5171
  if (filename !== file) return;
5180
5172
  if (debounce) clearTimeout(debounce);
5181
5173
  debounce = setTimeout(() => {
@@ -5207,9 +5199,9 @@ function createSessionBackend(sink, choice) {
5207
5199
  }
5208
5200
 
5209
5201
  // packages/core/src/web-server.ts
5210
- import fs14 from "fs";
5202
+ import fs15 from "fs";
5211
5203
  import http from "http";
5212
- import path18 from "path";
5204
+ import path19 from "path";
5213
5205
  import { timingSafeEqual } from "crypto";
5214
5206
  import { WebSocketServer } from "ws";
5215
5207
  var TOKEN_COOKIE = "vf_token";
@@ -5262,14 +5254,14 @@ function resolveStatic(root, urlPath) {
5262
5254
  return null;
5263
5255
  }
5264
5256
  if (decoded.includes("\0")) return null;
5265
- const rel = path18.posix.normalize(decoded).replace(/^\/+/, "");
5266
- const base = path18.resolve(root);
5267
- const candidates = [rel, path18.posix.join(rel, "index.html"), `${rel}.html`];
5257
+ const rel = path19.posix.normalize(decoded).replace(/^\/+/, "");
5258
+ const base = path19.resolve(root);
5259
+ const candidates = [rel, path19.posix.join(rel, "index.html"), `${rel}.html`];
5268
5260
  for (const c of candidates) {
5269
- const full = path18.resolve(base, c);
5270
- if (full !== base && !full.startsWith(base + path18.sep)) return null;
5261
+ const full = path19.resolve(base, c);
5262
+ if (full !== base && !full.startsWith(base + path19.sep)) return null;
5271
5263
  try {
5272
- if (fs14.statSync(full).isFile()) return full;
5264
+ if (fs15.statSync(full).isFile()) return full;
5273
5265
  } catch {
5274
5266
  }
5275
5267
  }
@@ -5330,7 +5322,7 @@ async function startWebServer(options) {
5330
5322
  send(res, 404, "Not found");
5331
5323
  return;
5332
5324
  }
5333
- const ext = path18.extname(file).toLowerCase();
5325
+ const ext = path19.extname(file).toLowerCase();
5334
5326
  const immutable = url.pathname.startsWith("/_next/static/");
5335
5327
  res.writeHead(200, {
5336
5328
  "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
@@ -5343,7 +5335,7 @@ async function startWebServer(options) {
5343
5335
  res.end();
5344
5336
  return;
5345
5337
  }
5346
- fs14.createReadStream(file).pipe(res);
5338
+ fs15.createReadStream(file).pipe(res);
5347
5339
  }
5348
5340
  const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
5349
5341
  function reject(socket, status, reason) {
@@ -5514,7 +5506,7 @@ function loginUrl(lock, pathname = "/home/") {
5514
5506
  return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
5515
5507
  }
5516
5508
  function isWebDir(dir) {
5517
- return fs15.existsSync(path19.join(dir, "home", "index.html"));
5509
+ return fs16.existsSync(path20.join(dir, "home", "index.html"));
5518
5510
  }
5519
5511
 
5520
5512
  // packages/cli/src/remote.ts
@@ -5631,14 +5623,14 @@ function connectToHost(lock) {
5631
5623
  }
5632
5624
 
5633
5625
  // packages/cli/src/doctor.ts
5634
- import fs16 from "fs";
5635
- import path20 from "path";
5636
- import { execFile as execFile6 } from "child_process";
5637
- import { promisify as promisify4 } from "util";
5626
+ import fs17 from "fs";
5627
+ import path21 from "path";
5628
+ import { execFile as execFile7 } from "child_process";
5629
+ import { promisify as promisify5 } from "util";
5638
5630
  init_env();
5639
5631
  init_git_bash();
5640
5632
  init_tmux_backend();
5641
- var pexec4 = promisify4(execFile6);
5633
+ var pexec5 = promisify5(execFile7);
5642
5634
  function parseVersion(text) {
5643
5635
  const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
5644
5636
  return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
@@ -5652,7 +5644,7 @@ function versionAtLeast(have, want) {
5652
5644
  }
5653
5645
  async function run(cmd, args) {
5654
5646
  try {
5655
- const { stdout } = await pexec4(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
5647
+ const { stdout } = await pexec5(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
5656
5648
  return String(stdout).trim();
5657
5649
  } catch {
5658
5650
  return null;
@@ -5772,10 +5764,10 @@ async function runDoctor(options) {
5772
5764
  });
5773
5765
  }
5774
5766
  try {
5775
- fs16.mkdirSync(options.userDataDir, { recursive: true });
5776
- const probe = path20.join(options.userDataDir, `.doctor-${process.pid}`);
5777
- fs16.writeFileSync(probe, "");
5778
- fs16.rmSync(probe);
5767
+ fs17.mkdirSync(options.userDataDir, { recursive: true });
5768
+ const probe = path21.join(options.userDataDir, `.doctor-${process.pid}`);
5769
+ fs17.writeFileSync(probe, "");
5770
+ fs17.rmSync(probe);
5779
5771
  checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
5780
5772
  } catch (err) {
5781
5773
  checks.push({
@@ -5829,24 +5821,24 @@ Options:
5829
5821
  `.trim();
5830
5822
  function detectLayout() {
5831
5823
  const file = fileURLToPath(import.meta.url);
5832
- const here = path23.dirname(file);
5833
- const bundledWeb = path23.join(here, "web");
5834
- if (isWebDir(bundledWeb) || path23.basename(here) === "dist") {
5824
+ const here = path24.dirname(file);
5825
+ const bundledWeb = path24.join(here, "web");
5826
+ if (isWebDir(bundledWeb) || path24.basename(here) === "dist") {
5835
5827
  return {
5836
- version: readVersion(path23.join(here, "..", "package.json")),
5828
+ version: readVersion(path24.join(here, "..", "package.json")),
5837
5829
  webDir: isWebDir(bundledWeb) ? bundledWeb : null,
5838
5830
  sourceRoot: null,
5839
5831
  cliEntry: file
5840
5832
  };
5841
5833
  }
5842
- const root = path23.resolve(here, "..", "..", "..");
5834
+ const root = path24.resolve(here, "..", "..", "..");
5843
5835
  const override = process.env.VIBEFLOW_WEB_DIR;
5844
- const webDir = override && isWebDir(override) ? override : isWebDir(path23.join(root, "app")) ? path23.join(root, "app") : null;
5845
- return { version: readVersion(path23.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
5836
+ const webDir = override && isWebDir(override) ? override : isWebDir(path24.join(root, "app")) ? path24.join(root, "app") : null;
5837
+ return { version: readVersion(path24.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
5846
5838
  }
5847
5839
  function readVersion(pkgPath) {
5848
5840
  try {
5849
- return JSON.parse(fs18.readFileSync(pkgPath, "utf8")).version;
5841
+ return JSON.parse(fs19.readFileSync(pkgPath, "utf8")).version;
5850
5842
  } catch {
5851
5843
  return "0.0.0";
5852
5844
  }
@@ -6052,8 +6044,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
6052
6044
  console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
6053
6045
  return 1;
6054
6046
  }
6055
- const dir = path23.resolve(target);
6056
- if (!fs18.existsSync(dir) || !fs18.statSync(dir).isDirectory()) {
6047
+ const dir = path24.resolve(target);
6048
+ if (!fs19.existsSync(dir) || !fs19.statSync(dir).isDirectory()) {
6057
6049
  console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
6058
6050
  return 1;
6059
6051
  }