@kw-yeh/vibeflow 4.0.5 → 4.1.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
@@ -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 fs19 from "fs";
602
+ import os6 from "os";
603
+ import path23 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 = fs19.mkdtempSync(path23.join(os6.tmpdir(), "vibeflow-"));
723
+ const file = path23.join(dir, "description.md");
724
724
  try {
725
- fs17.writeFileSync(file, text);
725
+ fs19.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: fs19.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
731
731
  } finally {
732
- fs17.rmSync(dir, { recursive: true, force: true });
732
+ fs19.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(path23.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 = path23.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir()));
803
+ if (!fs19.existsSync(file) || !fs19.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 ? path23.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) => path23.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"}/${path23.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 path24 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 = path24.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 fs20 from "fs";
1425
+ import path25 from "path";
1420
1426
  import { fileURLToPath } from "url";
1421
1427
  import { parseArgs as parseArgs2 } from "node:util";
1422
1428
 
@@ -1520,6 +1526,12 @@ function createNodePlatform(options = {}) {
1520
1526
  userDataDir: () => userData,
1521
1527
  sourceRoot: () => root,
1522
1528
  cliEntry: () => options.cliEntry ?? null,
1529
+ builtinSkillsDir: () => {
1530
+ if (options.builtinSkillsDir !== void 0) return options.builtinSkillsDir;
1531
+ if (options.cliEntry) return path2.join(path2.dirname(options.cliEntry), "builtin-skills");
1532
+ if (root) return path2.join(root, "packages", "core", "builtin-skills");
1533
+ return null;
1534
+ },
1523
1535
  openExternal: async (url) => {
1524
1536
  const err = await openWithOs(url);
1525
1537
  if (err) throw new Error(err);
@@ -1681,6 +1693,11 @@ function migrateStore(store) {
1681
1693
  if (!store.has("recentProjects")) {
1682
1694
  store.set("recentProjects", recentProjectsFromBoard(store.get("board")));
1683
1695
  }
1696
+ const settings = store.get("settings");
1697
+ if (settings && "agentConnections" in settings) {
1698
+ const { agentConnections: _dropped, ...rest } = settings;
1699
+ store.set("settings", rest);
1700
+ }
1684
1701
  if (persistedVersion < STATE_VERSION) {
1685
1702
  store.set("version", STATE_VERSION);
1686
1703
  }
@@ -1760,8 +1777,6 @@ function createBridge(t) {
1760
1777
  },
1761
1778
  setBoard: (board) => t.invoke("vibeflow:setBoard", board),
1762
1779
  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
1780
  getGithubAuthStatus: () => t.invoke("settings:githubAuthStatus"),
1766
1781
  startGithubAuthLogin: () => t.invoke("settings:startGithubAuthLogin"),
1767
1782
  cancelGithubAuthLogin: () => t.invoke("settings:cancelGithubAuthLogin"),
@@ -1776,6 +1791,8 @@ function createBridge(t) {
1776
1791
  initRepository: (projectPath) => t.invoke("git:initRepository", projectPath),
1777
1792
  /** Agent CLIs (claude / codex) actually installed on PATH. */
1778
1793
  detectAgents: () => t.invoke("env:detectAgents"),
1794
+ /** Models the agent's own CLI offers; `refresh` asks the CLI instead of its cache. */
1795
+ listAgentModels: (agentId2, refresh = false) => t.invoke("agents:listModels", { agentId: agentId2, refresh }),
1779
1796
  createTask: (payload) => t.invoke("vibeflow:createTask", payload),
1780
1797
  updateTask: (payload) => t.invoke("vibeflow:updateTask", payload),
1781
1798
  /** Clear prior execution state and prepare a fresh run of this task. */
@@ -1810,6 +1827,10 @@ function createBridge(t) {
1810
1827
  setLibraryEntryDescription: (payload) => t.invoke("library:setDescription", payload),
1811
1828
  setLibraryEntryEnabled: (payload) => t.invoke("library:setEnabled", payload),
1812
1829
  deleteLibraryEntry: (payload) => t.invoke("library:delete", payload),
1830
+ /** Overwrite or bring back a shipped skill with the version this install carries. */
1831
+ restoreBuiltinSkill: (payload) => t.invoke("library:restoreBuiltin", payload),
1832
+ /** Shipped skills the user deleted, offered for restore. */
1833
+ listRemovedBuiltinSkills: () => t.invoke("library:removedBuiltins"),
1813
1834
  approve: (taskId, message) => t.invoke("git:approve", { taskId, message }),
1814
1835
  /** Use the task's agent CLI to generate a commit message for branch changes. */
1815
1836
  generateCommitMessage: (taskId) => t.invoke("git:generateCommitMessage", taskId),
@@ -2680,7 +2701,41 @@ async function copyIgnoredEntry(projectPath, worktreePath, entry) {
2680
2701
  const src = path12.join(projectPath, entry);
2681
2702
  const dest = path12.join(worktreePath, entry);
2682
2703
  await fs8.mkdir(path12.dirname(dest), { recursive: true });
2683
- await fs8.cp(src, dest, { recursive: true });
2704
+ await fs8.cp(src, dest, {
2705
+ recursive: true,
2706
+ filter: async (from, to) => {
2707
+ if (!(await fs8.lstat(from)).isSymbolicLink()) return true;
2708
+ await copyIgnoredLink(projectPath, worktreePath, from, to);
2709
+ return false;
2710
+ }
2711
+ });
2712
+ }
2713
+ async function copyIgnoredLink(projectPath, worktreePath, from, to) {
2714
+ const raw = (await fs8.readlink(from)).replace(/^\\\\\?\\/, "");
2715
+ const resolved = path12.resolve(path12.dirname(from), raw);
2716
+ const inProject = path12.relative(projectPath, resolved);
2717
+ const insideProject = inProject !== "" && !inProject.startsWith("..") && !path12.isAbsolute(inProject);
2718
+ const target = insideProject ? path12.join(worktreePath, inProject) : resolved;
2719
+ let isDirectory;
2720
+ try {
2721
+ isDirectory = (await fs8.stat(resolved)).isDirectory();
2722
+ } catch {
2723
+ return;
2724
+ }
2725
+ await fs8.mkdir(path12.dirname(to), { recursive: true });
2726
+ if (process.platform !== "win32") {
2727
+ await fs8.symlink(path12.isAbsolute(raw) ? target : raw, to);
2728
+ return;
2729
+ }
2730
+ if (isDirectory) {
2731
+ await fs8.symlink(target, to, "junction");
2732
+ return;
2733
+ }
2734
+ try {
2735
+ await fs8.symlink(target, to, "file");
2736
+ } catch {
2737
+ await fs8.copyFile(resolved, to);
2738
+ }
2684
2739
  }
2685
2740
  async function copySmallIgnoredFiles(projectPath, worktreePath) {
2686
2741
  const entries = await listIgnoredCopyEntries(projectPath);
@@ -3519,8 +3574,8 @@ async function taskCommand(argv) {
3519
3574
  }
3520
3575
 
3521
3576
  // packages/cli/src/host.ts
3522
- import fs15 from "fs";
3523
- import path19 from "path";
3577
+ import fs17 from "fs";
3578
+ import path21 from "path";
3524
3579
  import { randomBytes as randomBytes2 } from "crypto";
3525
3580
 
3526
3581
  // packages/core/src/events.ts
@@ -3546,178 +3601,725 @@ var EventBus = class {
3546
3601
  }
3547
3602
  };
3548
3603
 
3549
- // packages/core/src/service.ts
3550
- import fs13 from "fs";
3551
- import os4 from "os";
3552
- import path17 from "path";
3553
- import { randomUUID as randomUUID4 } from "crypto";
3604
+ // packages/core/src/library-builtins.ts
3605
+ import fs11 from "fs";
3606
+ import path15 from "path";
3554
3607
 
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
- }
3582
- });
3583
- if (!response.ok) {
3584
- throw new Error(providerAuthError(response.status));
3585
- }
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)));
3608
+ // packages/core/src/library.ts
3609
+ import { createHash } from "crypto";
3610
+ import fs10 from "fs";
3611
+ import os3 from "os";
3612
+ import path14 from "path";
3613
+ var LIBRARY_DIR = "library";
3614
+ var LIBRARY_KINDS = ["skill", "prompt", "script"];
3615
+ var KIND_DIR = {
3616
+ skill: "skills",
3617
+ prompt: "prompts",
3618
+ script: "scripts"
3619
+ };
3620
+ var INDEX_FILE = "index.json";
3621
+ var MAX_NAME_BYTES = 100;
3622
+ function libraryPaths(root) {
3623
+ return {
3624
+ root,
3625
+ skills: path14.join(root, KIND_DIR.skill),
3626
+ prompts: path14.join(root, KIND_DIR.prompt),
3627
+ scripts: path14.join(root, KIND_DIR.script),
3628
+ plugin: path14.join(root, "plugin"),
3629
+ codexHome: path14.join(root, "codex-home")
3630
+ };
3589
3631
  }
3590
-
3591
- // packages/core/src/github-auth.ts
3592
- init_env();
3593
- import { execFile as execFile5 } from "child_process";
3594
- import { promisify as promisify3 } from "util";
3595
- import * as nodePty3 from "node-pty";
3596
- var pexec3 = promisify3(execFile5);
3597
- var GITHUB_HOST = "github.com";
3598
- var DEVICE_URL = "https://github.com/login/device";
3599
- var activeLogin = null;
3600
- function stripAnsi(value) {
3601
- return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x07/g, "");
3632
+ async function libraryRoot() {
3633
+ return path14.join(getPlatform().userDataDir(), LIBRARY_DIR);
3602
3634
  }
3603
- function normalizeCode(code2) {
3604
- return code2.replace(/\s+/g, "").toUpperCase();
3635
+ function truncateUtf82(value, maxBytes) {
3636
+ let result = "";
3637
+ for (const character of value) {
3638
+ if (Buffer.byteLength(result + character) > maxBytes) break;
3639
+ result += character;
3640
+ }
3641
+ return result;
3605
3642
  }
3606
- function extractDeviceCode(output) {
3607
- const cleaned = stripAnsi(output);
3608
- const explicit = cleaned.match(/one-time code:\s*([A-Z0-9-]+)/i);
3609
- if (explicit?.[1]) return normalizeCode(explicit[1]);
3610
- const fallback = cleaned.match(/\b([A-Z0-9]{4}-[A-Z0-9]{4})\b/i);
3611
- return fallback?.[1] ? normalizeCode(fallback[1]) : null;
3643
+ function safeLibraryName(name) {
3644
+ const basename2 = name.replace(/\\/g, "/").split("/").pop() ?? "";
3645
+ const cleaned = truncateUtf82(
3646
+ basename2.replace(/[\u0000-\u001f\u007f]/g, "").trim(),
3647
+ MAX_NAME_BYTES
3648
+ );
3649
+ return cleaned && cleaned !== "." && cleaned !== ".." ? cleaned : "untitled";
3612
3650
  }
3613
- function statusFromBody(body) {
3614
- const accounts = body.hosts?.[GITHUB_HOST] ?? [];
3615
- const active = accounts.find((account) => account.active) ?? accounts[0];
3616
- if (!active) return { installed: true, authenticated: false };
3617
- return {
3618
- installed: true,
3619
- authenticated: Boolean(active.login) && !active.error,
3620
- login: active.login,
3621
- gitProtocol: active.gitProtocol,
3622
- error: active.error
3623
- };
3651
+ function entryKey(kind, name) {
3652
+ return `${kind}/${name}`;
3624
3653
  }
3625
- function errorMessage(err) {
3626
- if (err instanceof Error) return err.message;
3627
- return String(err);
3654
+ function indexPath(root) {
3655
+ return path14.join(root, INDEX_FILE);
3628
3656
  }
3629
- async function getGitHubCliAuthStatus() {
3657
+ function readIndexFile(root) {
3658
+ let raw;
3630
3659
  try {
3631
- const { stdout } = await pexec3(
3632
- "gh",
3633
- ["auth", "status", "--hostname", GITHUB_HOST, "--json", "hosts"],
3634
- {
3635
- env: execEnv(),
3636
- timeout: 1e4,
3637
- windowsHide: true
3638
- }
3639
- );
3640
- const body = JSON.parse(stdout.toString());
3641
- return statusFromBody(body);
3642
- } catch (err) {
3643
- const maybeStdout = err.stdout;
3644
- if (maybeStdout) {
3645
- try {
3646
- const body = JSON.parse(maybeStdout.toString());
3647
- return statusFromBody(body);
3648
- } catch {
3649
- }
3660
+ raw = fs10.readFileSync(indexPath(root), "utf8");
3661
+ } catch {
3662
+ return { version: 1, entries: {} };
3663
+ }
3664
+ try {
3665
+ const parsed = JSON.parse(raw);
3666
+ if (parsed && typeof parsed === "object" && "entries" in parsed) {
3667
+ const entries = parsed.entries;
3668
+ if (entries && typeof entries === "object") return { version: 1, entries };
3650
3669
  }
3651
- const message = errorMessage(err);
3652
- return {
3653
- installed: !/ENOENT|not found/i.test(message),
3654
- authenticated: false,
3655
- error: message
3656
- };
3670
+ } catch {
3657
3671
  }
3672
+ return null;
3658
3673
  }
3659
- async function logoutGitHubCli() {
3660
- const status = await getGitHubCliAuthStatus();
3661
- if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
3662
- if (!status.login) return status;
3663
- await pexec3(
3664
- "gh",
3665
- ["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
3666
- {
3667
- env: execEnv(),
3668
- timeout: 1e4,
3669
- windowsHide: true
3670
- }
3671
- );
3672
- return getGitHubCliAuthStatus();
3674
+ function readIndex(root) {
3675
+ return readIndexFile(root) ?? { version: 1, entries: {} };
3673
3676
  }
3674
- function cancelGitHubCliLogin() {
3675
- if (!activeLogin) return;
3677
+ function indexIsCorrupt(root) {
3678
+ return readIndexFile(root) === null;
3679
+ }
3680
+ function writeIndex(root, index) {
3681
+ fs10.mkdirSync(root, { recursive: true });
3682
+ const file = indexPath(root);
3683
+ if (indexIsCorrupt(root)) fs10.renameSync(file, `${file}.corrupt-${Date.now()}`);
3684
+ const tmp = `${file}.${process.pid}.tmp`;
3685
+ fs10.writeFileSync(tmp, `${JSON.stringify(index, null, 2)}
3686
+ `);
3687
+ fs10.renameSync(tmp, file);
3688
+ }
3689
+ function ensureLibrary(root) {
3690
+ const paths = libraryPaths(root);
3691
+ for (const kind of LIBRARY_KINDS) {
3692
+ fs10.mkdirSync(path14.join(root, KIND_DIR[kind]), { recursive: true });
3693
+ }
3694
+ return paths;
3695
+ }
3696
+ function readSkillDescription(skillMdPath) {
3697
+ let content;
3676
3698
  try {
3677
- activeLogin.kill();
3699
+ content = fs10.readFileSync(skillMdPath, "utf8");
3678
3700
  } catch {
3701
+ return void 0;
3679
3702
  }
3680
- activeLogin = null;
3703
+ const lines = content.split(/\r?\n/);
3704
+ if (lines[0]?.trim() !== "---") return void 0;
3705
+ for (let i = 1; i < lines.length; i += 1) {
3706
+ const line = lines[i];
3707
+ if (line.trim() === "---") break;
3708
+ const match = /^description:\s*(.+)$/.exec(line);
3709
+ if (match) return match[1].trim().replace(/^["']|["']$/g, "");
3710
+ }
3711
+ return void 0;
3681
3712
  }
3682
- function startGitHubCliLogin(onEvent) {
3683
- cancelGitHubCliLogin();
3684
- onEvent({ type: "starting" });
3685
- let output = "";
3686
- let sentBrowserChoice = false;
3687
- let emittedCode = null;
3688
- let continuedAfterCode = false;
3689
- let completed = false;
3690
- let proc;
3713
+ function entryPath(root, kind, name) {
3714
+ return path14.join(root, KIND_DIR[kind], name);
3715
+ }
3716
+ var HASH_IGNORED = /* @__PURE__ */ new Set(["__pycache__", ".DS_Store"]);
3717
+ function hashSkillDir(dir) {
3718
+ const files = [];
3719
+ const walk = (rel) => {
3720
+ for (const dirent of fs10.readdirSync(path14.join(dir, rel), { withFileTypes: true })) {
3721
+ if (HASH_IGNORED.has(dirent.name)) continue;
3722
+ const child = rel ? `${rel}/${dirent.name}` : dirent.name;
3723
+ if (fs10.statSync(path14.join(dir, child)).isDirectory()) walk(child);
3724
+ else files.push(child);
3725
+ }
3726
+ };
3727
+ walk("");
3728
+ const hash = createHash("sha256");
3729
+ for (const rel of files.sort()) {
3730
+ hash.update(rel).update("\0").update(fs10.readFileSync(path14.join(dir, rel))).update("\0");
3731
+ }
3732
+ return hash.digest("hex");
3733
+ }
3734
+ function builtinStatus(full, name, recorded, builtinDir) {
3691
3735
  try {
3692
- proc = nodePty3.spawn(
3693
- "gh",
3694
- ["auth", "login", "--hostname", GITHUB_HOST, "--git-protocol", "https"],
3695
- {
3696
- name: "xterm-256color",
3697
- cwd: process.cwd(),
3698
- env: { ...execEnv(), BROWSER: "true", TERM: "xterm-256color" },
3699
- cols: 100,
3700
- rows: 24
3736
+ const modified = hashSkillDir(full) !== recorded;
3737
+ let updateAvailable = false;
3738
+ if (modified && builtinDir) {
3739
+ const shipped = path14.join(builtinDir, name);
3740
+ if (fs10.existsSync(path14.join(shipped, "SKILL.md"))) {
3741
+ updateAvailable = hashSkillDir(shipped) !== recorded;
3701
3742
  }
3702
- );
3703
- } catch (err) {
3704
- onEvent({ type: "error", error: errorMessage(err) });
3705
- return;
3743
+ }
3744
+ return { modified, updateAvailable };
3745
+ } catch {
3746
+ return { modified: true, updateAvailable: false };
3706
3747
  }
3707
- activeLogin = proc;
3708
- proc.onData((data) => {
3709
- output += data;
3710
- const cleaned = stripAnsi(output);
3711
- if (!sentBrowserChoice && cleaned.includes("Login with a web browser")) {
3712
- sentBrowserChoice = true;
3713
- proc.write("\r");
3748
+ }
3749
+ function listLibrary(root, builtinDir) {
3750
+ const index = readIndex(root);
3751
+ const entries = [];
3752
+ for (const kind of LIBRARY_KINDS) {
3753
+ const dir = path14.join(root, KIND_DIR[kind]);
3754
+ let dirents;
3755
+ try {
3756
+ dirents = fs10.readdirSync(dir, { withFileTypes: true });
3757
+ } catch {
3758
+ continue;
3714
3759
  }
3715
- const code2 = extractDeviceCode(output);
3716
- if (code2 && code2 !== emittedCode) {
3717
- emittedCode = code2;
3718
- onEvent({ type: "code", code: code2, url: DEVICE_URL });
3760
+ for (const dirent of dirents) {
3761
+ if (dirent.name.startsWith(".")) continue;
3762
+ const wantsDirectory = kind === "skill";
3763
+ let stat;
3764
+ try {
3765
+ stat = fs10.statSync(path14.join(dir, dirent.name));
3766
+ } catch {
3767
+ continue;
3768
+ }
3769
+ if (stat.isDirectory() !== wantsDirectory) continue;
3770
+ const name = dirent.name;
3771
+ const full = path14.join(dir, name);
3772
+ if (wantsDirectory && !fs10.existsSync(path14.join(full, "SKILL.md"))) continue;
3773
+ const key = entryKey(kind, name);
3774
+ const record = index.entries[key] ?? {};
3775
+ entries.push({
3776
+ key,
3777
+ kind,
3778
+ name,
3779
+ path: full,
3780
+ description: wantsDirectory ? readSkillDescription(path14.join(full, "SKILL.md")) : record.description,
3781
+ sourcePath: record.sourcePath,
3782
+ importedAt: record.importedAt,
3783
+ enabled: record.enabled !== false,
3784
+ builtin: wantsDirectory && record.builtinHash && builtinDir !== void 0 ? builtinStatus(full, name, record.builtinHash, builtinDir) : void 0
3785
+ });
3719
3786
  }
3720
- if (code2 && !continuedAfterCode) {
3787
+ }
3788
+ return entries.sort((a, b) => a.key.localeCompare(b.key));
3789
+ }
3790
+ function annotate(root, key, patch) {
3791
+ const index = readIndex(root);
3792
+ index.entries[key] = { ...index.entries[key], ...patch };
3793
+ writeIndex(root, index);
3794
+ }
3795
+ function importEntry(root, kind, sourcePath) {
3796
+ ensureLibrary(root);
3797
+ const resolved = path14.resolve(sourcePath);
3798
+ const stat = fs10.statSync(resolved);
3799
+ if (kind === "skill") {
3800
+ if (!stat.isDirectory()) throw new Error("skill \u5FC5\u9808\u662F\u542B SKILL.md \u7684\u76EE\u9304");
3801
+ if (!fs10.existsSync(path14.join(resolved, "SKILL.md"))) {
3802
+ throw new Error("\u76EE\u9304\u5167\u627E\u4E0D\u5230 SKILL.md");
3803
+ }
3804
+ } else if (!stat.isFile()) {
3805
+ throw new Error(`${kind} \u5FC5\u9808\u662F\u55AE\u4E00\u6A94\u6848`);
3806
+ }
3807
+ const name = safeLibraryName(path14.basename(resolved));
3808
+ const target = entryPath(root, kind, name);
3809
+ fs10.rmSync(target, { recursive: true, force: true });
3810
+ fs10.cpSync(resolved, target, { recursive: kind === "skill", dereference: true });
3811
+ if (kind === "script") fs10.chmodSync(target, 493);
3812
+ const key = entryKey(kind, name);
3813
+ const index = readIndex(root);
3814
+ const { builtinHash: _hash, builtinDeleted: _deleted, ...kept } = index.entries[key] ?? {};
3815
+ index.entries[key] = { ...kept, sourcePath: resolved, importedAt: Date.now(), enabled: true };
3816
+ writeIndex(root, index);
3817
+ return requireEntry(root, kind, name);
3818
+ }
3819
+ function createEntry(root, kind, rawName, content, description) {
3820
+ ensureLibrary(root);
3821
+ const name = safeLibraryName(rawName);
3822
+ const target = entryPath(root, kind, name);
3823
+ if (fs10.existsSync(target)) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u9805\u76EE\uFF1A${name}`);
3824
+ if (kind === "skill") {
3825
+ fs10.mkdirSync(target, { recursive: true });
3826
+ fs10.writeFileSync(path14.join(target, "SKILL.md"), content);
3827
+ } else {
3828
+ fs10.writeFileSync(target, content);
3829
+ if (kind === "script") fs10.chmodSync(target, 493);
3830
+ }
3831
+ annotate(root, entryKey(kind, name), {
3832
+ importedAt: Date.now(),
3833
+ enabled: true,
3834
+ description
3835
+ });
3836
+ return requireEntry(root, kind, name);
3837
+ }
3838
+ function setEntryDescription(root, kind, name, description) {
3839
+ annotate(root, entryKey(kind, name), { description });
3840
+ }
3841
+ function updateEntry(root, kind, name, content) {
3842
+ const target = entryPath(root, kind, name);
3843
+ if (!fs10.existsSync(target)) throw new Error(`\u627E\u4E0D\u5230\u9805\u76EE\uFF1A${name}`);
3844
+ fs10.writeFileSync(kind === "skill" ? path14.join(target, "SKILL.md") : target, content);
3845
+ return requireEntry(root, kind, name);
3846
+ }
3847
+ function readEntryContent(root, kind, name) {
3848
+ const target = entryPath(root, kind, name);
3849
+ return fs10.readFileSync(kind === "skill" ? path14.join(target, "SKILL.md") : target, "utf8");
3850
+ }
3851
+ function markBuiltinDeleted(root, key) {
3852
+ const index = readIndex(root);
3853
+ index.entries[key] = { builtinDeleted: true };
3854
+ writeIndex(root, index);
3855
+ }
3856
+ function deleteEntry(root, kind, name) {
3857
+ fs10.rmSync(entryPath(root, kind, name), { recursive: true, force: true });
3858
+ const key = entryKey(kind, name);
3859
+ const record = readIndex(root).entries[key];
3860
+ if (record?.builtinHash || record?.builtinDeleted) {
3861
+ markBuiltinDeleted(root, key);
3862
+ return;
3863
+ }
3864
+ const index = readIndex(root);
3865
+ delete index.entries[key];
3866
+ writeIndex(root, index);
3867
+ }
3868
+ function setEntryEnabled(root, kind, name, enabled) {
3869
+ annotate(root, entryKey(kind, name), { enabled });
3870
+ }
3871
+ function requireEntry(root, kind, name) {
3872
+ const found2 = listLibrary(root).find((e) => e.key === entryKey(kind, name));
3873
+ if (!found2) throw new Error(`\u9805\u76EE\u5BEB\u5165\u5F8C\u8B80\u4E0D\u56DE\u4F86\uFF1A${kind}/${name}`);
3874
+ return found2;
3875
+ }
3876
+ function projectSkillsDir(worktreePath) {
3877
+ return path14.join(worktreePath, ".claude", "skills");
3878
+ }
3879
+ function skillNamesIn(dir) {
3880
+ try {
3881
+ return fs10.readdirSync(dir, { withFileTypes: true }).filter((d) => !d.name.startsWith(".")).map((d) => d.name).filter((name) => fs10.existsSync(path14.join(dir, name, "SKILL.md"))).sort();
3882
+ } catch {
3883
+ return [];
3884
+ }
3885
+ }
3886
+ var PLUGIN_MANIFEST_DIR = ".claude-plugin";
3887
+ function buildPluginDir(root) {
3888
+ const paths = libraryPaths(root);
3889
+ const enabled = listLibrary(root).filter((e) => e.kind === "skill" && e.enabled);
3890
+ fs10.rmSync(path14.join(paths.plugin, "skills"), { recursive: true, force: true });
3891
+ fs10.mkdirSync(path14.join(paths.plugin, PLUGIN_MANIFEST_DIR), { recursive: true });
3892
+ fs10.mkdirSync(path14.join(paths.plugin, "skills"), { recursive: true });
3893
+ fs10.writeFileSync(
3894
+ path14.join(paths.plugin, PLUGIN_MANIFEST_DIR, "plugin.json"),
3895
+ `${JSON.stringify(
3896
+ {
3897
+ name: "vibeflow-library",
3898
+ version: "1.0.0",
3899
+ description: "Skills delivered by VibeFlow for this task."
3900
+ },
3901
+ null,
3902
+ 2
3903
+ )}
3904
+ `
3905
+ );
3906
+ for (const entry of enabled) {
3907
+ fs10.cpSync(entry.path, path14.join(paths.plugin, "skills", entry.name), {
3908
+ recursive: true,
3909
+ dereference: true
3910
+ });
3911
+ }
3912
+ return { pluginDir: paths.plugin, skills: enabled.map((e) => e.name) };
3913
+ }
3914
+ function linkOrSkip(target, linkPath) {
3915
+ if (!fs10.existsSync(target)) return false;
3916
+ const source = path14.resolve(target);
3917
+ const attempts = fs10.statSync(source).isDirectory() ? [
3918
+ () => fs10.symlinkSync(source, linkPath, "junction"),
3919
+ () => fs10.cpSync(source, linkPath, { recursive: true, dereference: true })
3920
+ ] : [
3921
+ () => fs10.symlinkSync(source, linkPath),
3922
+ () => fs10.linkSync(source, linkPath),
3923
+ () => fs10.copyFileSync(source, linkPath)
3924
+ ];
3925
+ for (const attempt of attempts) {
3926
+ try {
3927
+ fs10.rmSync(linkPath, { recursive: true, force: true });
3928
+ attempt();
3929
+ return true;
3930
+ } catch {
3931
+ }
3932
+ }
3933
+ return false;
3934
+ }
3935
+ function buildCodexHome(root, worktreePath, userCodexHome = path14.join(os3.homedir(), ".codex")) {
3936
+ const paths = libraryPaths(root);
3937
+ const skillsDir = path14.join(paths.codexHome, "skills");
3938
+ fs10.rmSync(skillsDir, { recursive: true, force: true });
3939
+ fs10.mkdirSync(skillsDir, { recursive: true });
3940
+ for (const file of ["auth.json", "config.toml"]) {
3941
+ linkOrSkip(path14.join(userCodexHome, file), path14.join(paths.codexHome, file));
3942
+ }
3943
+ const result = {
3944
+ codexHome: paths.codexHome,
3945
+ project: [],
3946
+ user: [],
3947
+ library: [],
3948
+ shadowed: []
3949
+ };
3950
+ const claimed = /* @__PURE__ */ new Map();
3951
+ const projectDir = worktreePath ? projectSkillsDir(worktreePath) : void 0;
3952
+ if (projectDir) {
3953
+ for (const name of skillNamesIn(projectDir)) {
3954
+ if (linkOrSkip(path14.join(projectDir, name), path14.join(skillsDir, name))) {
3955
+ result.project.push(name);
3956
+ claimed.set(name, "project");
3957
+ }
3958
+ }
3959
+ }
3960
+ const userDir = path14.join(userCodexHome, "skills");
3961
+ for (const name of skillNamesIn(userDir)) {
3962
+ const owner = claimed.get(name);
3963
+ if (owner) {
3964
+ result.shadowed.push({ name, keptFrom: owner, dropped: "user" });
3965
+ continue;
3966
+ }
3967
+ if (linkOrSkip(path14.join(userDir, name), path14.join(skillsDir, name))) {
3968
+ result.user.push(name);
3969
+ claimed.set(name, "user");
3970
+ }
3971
+ }
3972
+ for (const entry of listLibrary(root)) {
3973
+ if (entry.kind !== "skill" || !entry.enabled) continue;
3974
+ const owner = claimed.get(entry.name);
3975
+ if (owner) {
3976
+ result.shadowed.push({ name: entry.name, keptFrom: owner, dropped: "library" });
3977
+ continue;
3978
+ }
3979
+ fs10.cpSync(entry.path, path14.join(skillsDir, entry.name), {
3980
+ recursive: true,
3981
+ dereference: true
3982
+ });
3983
+ result.library.push(entry.name);
3984
+ }
3985
+ return result;
3986
+ }
3987
+ function libraryLaunchInfoAt(root, worktreePath, userCodexHome) {
3988
+ const entries = listLibrary(root).filter((e) => e.enabled);
3989
+ if (entries.length === 0) return null;
3990
+ const { pluginDir } = buildPluginDir(root);
3991
+ const { codexHome } = buildCodexHome(root, worktreePath, userCodexHome);
3992
+ const scripts = entries.filter((e) => e.kind === "script").map((e) => ({ name: e.name, path: e.path, description: e.description }));
3993
+ const promptBodies = entries.filter((e) => e.kind === "prompt").map((e) => readEntryContent(root, "prompt", e.name).trim()).filter(Boolean);
3994
+ const lines = [...promptBodies];
3995
+ if (scripts.length > 0) {
3996
+ lines.push(
3997
+ "\u53EF\u7528\u7684\u5DE5\u5177\u8173\u672C\uFF08\u9700\u8981\u6642\u81EA\u884C\u57F7\u884C\uFF0C\u4E0D\u9700\u8981\u5C31\u5225\u8DD1\uFF09\uFF1A",
3998
+ ...scripts.map((s) => `- ${s.path}${s.description ? ` \u2014 ${s.description}` : ""}`)
3999
+ );
4000
+ }
4001
+ return {
4002
+ pluginDir,
4003
+ codexHome,
4004
+ libraryDir: root,
4005
+ scripts,
4006
+ promptText: lines.join("\n\n")
4007
+ };
4008
+ }
4009
+ async function libraryLaunchInfo(worktreePath) {
4010
+ return libraryLaunchInfoAt(await libraryRoot(), worktreePath);
4011
+ }
4012
+
4013
+ // packages/core/src/library-builtins.ts
4014
+ function builtinSkillsDir() {
4015
+ return getPlatform().builtinSkillsDir?.() ?? null;
4016
+ }
4017
+ function installBuiltin(root, builtinDir, name) {
4018
+ const target = entryPath(root, "skill", name);
4019
+ const staging = entryPath(root, "skill", `.${name}.installing`);
4020
+ fs11.rmSync(staging, { recursive: true, force: true });
4021
+ try {
4022
+ fs11.cpSync(path15.join(builtinDir, name), staging, {
4023
+ recursive: true,
4024
+ dereference: true,
4025
+ filter: (src) => path15.basename(src) !== "__pycache__"
4026
+ });
4027
+ fs11.rmSync(target, { recursive: true, force: true });
4028
+ fs11.renameSync(staging, target);
4029
+ } finally {
4030
+ fs11.rmSync(staging, { recursive: true, force: true });
4031
+ }
4032
+ return hashSkillDir(target);
4033
+ }
4034
+ function syncBuiltinSkills(root, builtinDir) {
4035
+ const result = { installed: [], updated: [] };
4036
+ if (!builtinDir) return result;
4037
+ const names = skillNamesIn(builtinDir);
4038
+ if (names.length === 0) return result;
4039
+ if (indexIsCorrupt(root)) {
4040
+ console.error("Library index.json is unreadable; skipping built-in skill sync");
4041
+ return result;
4042
+ }
4043
+ ensureLibrary(root);
4044
+ for (const name of names) {
4045
+ try {
4046
+ syncOne(root, builtinDir, name, result);
4047
+ } catch (err) {
4048
+ console.error(`Failed to sync built-in skill "${name}":`, err);
4049
+ }
4050
+ }
4051
+ return result;
4052
+ }
4053
+ function syncOne(root, builtinDir, name, result) {
4054
+ const key = entryKey("skill", name);
4055
+ const record = readIndex(root).entries[key] ?? {};
4056
+ if (record.builtinDeleted) return;
4057
+ const target = entryPath(root, "skill", name);
4058
+ if (!record.builtinHash) {
4059
+ if (fs11.existsSync(target)) return;
4060
+ annotate(root, key, {
4061
+ builtinHash: installBuiltin(root, builtinDir, name),
4062
+ enabled: true,
4063
+ importedAt: Date.now()
4064
+ });
4065
+ result.installed.push(name);
4066
+ return;
4067
+ }
4068
+ if (!fs11.existsSync(path15.join(target, "SKILL.md"))) {
4069
+ markBuiltinDeleted(root, key);
4070
+ return;
4071
+ }
4072
+ if (hashSkillDir(path15.join(builtinDir, name)) === record.builtinHash) return;
4073
+ if (hashSkillDir(target) !== record.builtinHash) return;
4074
+ annotate(root, key, { builtinHash: installBuiltin(root, builtinDir, name) });
4075
+ result.updated.push(name);
4076
+ }
4077
+ function restoreBuiltinSkill(root, builtinDir, name) {
4078
+ if (!builtinDir || safeLibraryName(name) !== name || !skillNamesIn(builtinDir).includes(name)) {
4079
+ throw new Error(`\u4E0D\u662F\u5167\u5EFA skill\uFF1A${name}`);
4080
+ }
4081
+ const key = entryKey("skill", name);
4082
+ if (fs11.existsSync(entryPath(root, "skill", name)) && !readIndex(root).entries[key]?.builtinHash) {
4083
+ throw new Error(`\u5DF2\u6709\u540C\u540D\u7684\u81EA\u8A02\u9805\u76EE\uFF1A${name}`);
4084
+ }
4085
+ ensureLibrary(root);
4086
+ const builtinHash = installBuiltin(root, builtinDir, name);
4087
+ const index = readIndex(root);
4088
+ const record = { ...index.entries[key], builtinHash, enabled: true, importedAt: Date.now() };
4089
+ delete record.builtinDeleted;
4090
+ delete record.sourcePath;
4091
+ index.entries[key] = record;
4092
+ writeIndex(root, index);
4093
+ const entry = listLibrary(root, builtinDir).find((e) => e.key === key);
4094
+ if (!entry) throw new Error(`\u9084\u539F\u5931\u6557\uFF1A${name}`);
4095
+ return entry;
4096
+ }
4097
+ function removedBuiltinSkills(root, builtinDir) {
4098
+ if (!builtinDir) return [];
4099
+ const index = readIndex(root);
4100
+ return skillNamesIn(builtinDir).filter(
4101
+ (name) => index.entries[entryKey("skill", name)]?.builtinDeleted === true && !fs11.existsSync(entryPath(root, "skill", name))
4102
+ );
4103
+ }
4104
+ async function syncBuiltinsAtStartup() {
4105
+ try {
4106
+ const { installed, updated } = syncBuiltinSkills(await libraryRoot(), builtinSkillsDir());
4107
+ if (installed.length || updated.length) {
4108
+ console.log(`Built-in skills \u2014 installed: ${installed.join(", ") || "-"}; updated: ${updated.join(", ") || "-"}`);
4109
+ }
4110
+ } catch (err) {
4111
+ console.error("Failed to sync built-in skills:", err);
4112
+ }
4113
+ }
4114
+
4115
+ // packages/core/src/service.ts
4116
+ import fs15 from "fs";
4117
+ import os5 from "os";
4118
+ import path19 from "path";
4119
+ import { randomUUID as randomUUID4 } from "crypto";
4120
+
4121
+ // packages/core/src/agent-models.ts
4122
+ import { execFile as execFile5 } from "child_process";
4123
+ import fs12 from "fs";
4124
+ import os4 from "os";
4125
+ import path16 from "path";
4126
+ import { promisify as promisify3 } from "util";
4127
+ init_env();
4128
+ var pexec3 = promisify3(execFile5);
4129
+ var CODEX_MODELS_TIMEOUT_MS = 2e4;
4130
+ function builtinModels(id) {
4131
+ return (AGENT_CLIS.find((a) => a.id === id) ?? AGENT_CLIS[0]).models;
4132
+ }
4133
+ function defaultCodexHome() {
4134
+ const configured = process.env.CODEX_HOME?.trim();
4135
+ return configured ? configured : path16.join(os4.homedir(), ".codex");
4136
+ }
4137
+ async function defaultRunCodexModels() {
4138
+ const { stdout } = await pexec3("codex", ["debug", "models"], {
4139
+ env: execEnv(),
4140
+ timeout: CODEX_MODELS_TIMEOUT_MS,
4141
+ maxBuffer: 16 * 1024 * 1024,
4142
+ windowsHide: true
4143
+ });
4144
+ return stdout;
4145
+ }
4146
+ function parseCodexCatalog(raw) {
4147
+ const body = JSON.parse(raw);
4148
+ if (!Array.isArray(body.models)) throw new Error("Codex model catalog has no models array");
4149
+ const seen = /* @__PURE__ */ new Set();
4150
+ const models = [];
4151
+ for (const entry of body.models) {
4152
+ if (!entry || typeof entry !== "object") continue;
4153
+ const { slug, display_name: displayName, visibility } = entry;
4154
+ if (typeof slug !== "string" || !slug.trim() || visibility === "hide") continue;
4155
+ const id = slug.trim();
4156
+ if (seen.has(id)) continue;
4157
+ seen.add(id);
4158
+ models.push({
4159
+ id,
4160
+ label: typeof displayName === "string" && displayName.trim() ? displayName.trim() : id
4161
+ });
4162
+ }
4163
+ if (models.length === 0) throw new Error("Codex model catalog lists no selectable models");
4164
+ return models;
4165
+ }
4166
+ function errorMessage(err) {
4167
+ return err instanceof Error ? err.message : String(err);
4168
+ }
4169
+ async function listAgentModels(id, opts = {}) {
4170
+ if (id !== "codex") return { models: builtinModels(id), source: "builtin" };
4171
+ if (opts.refresh) {
4172
+ try {
4173
+ const stdout = await (opts.runCodexModels ?? defaultRunCodexModels)();
4174
+ return { models: parseCodexCatalog(stdout), source: "codex-cli" };
4175
+ } catch (err) {
4176
+ return { models: builtinModels(id), source: "builtin", error: errorMessage(err) };
4177
+ }
4178
+ }
4179
+ const cachePath = path16.join(opts.codexHome ?? defaultCodexHome(), "models_cache.json");
4180
+ try {
4181
+ const raw = await fs12.promises.readFile(cachePath, "utf8");
4182
+ return { models: parseCodexCatalog(raw), source: "codex-cache" };
4183
+ } catch (err) {
4184
+ const missing = err.code === "ENOENT";
4185
+ return {
4186
+ models: builtinModels(id),
4187
+ source: "builtin",
4188
+ ...missing ? {} : { error: errorMessage(err) }
4189
+ };
4190
+ }
4191
+ }
4192
+
4193
+ // packages/core/src/github-auth.ts
4194
+ init_env();
4195
+ import { execFile as execFile6 } from "child_process";
4196
+ import { promisify as promisify4 } from "util";
4197
+ import * as nodePty3 from "node-pty";
4198
+ var pexec4 = promisify4(execFile6);
4199
+ var GITHUB_HOST = "github.com";
4200
+ var DEVICE_URL = "https://github.com/login/device";
4201
+ var activeLogin = null;
4202
+ function stripAnsi(value) {
4203
+ return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x07/g, "");
4204
+ }
4205
+ function normalizeCode(code2) {
4206
+ return code2.replace(/\s+/g, "").toUpperCase();
4207
+ }
4208
+ function extractDeviceCode(output) {
4209
+ const cleaned = stripAnsi(output);
4210
+ const explicit = cleaned.match(/one-time code:\s*([A-Z0-9-]+)/i);
4211
+ if (explicit?.[1]) return normalizeCode(explicit[1]);
4212
+ const fallback = cleaned.match(/\b([A-Z0-9]{4}-[A-Z0-9]{4})\b/i);
4213
+ return fallback?.[1] ? normalizeCode(fallback[1]) : null;
4214
+ }
4215
+ function statusFromBody(body) {
4216
+ const accounts = body.hosts?.[GITHUB_HOST] ?? [];
4217
+ const active = accounts.find((account) => account.active) ?? accounts[0];
4218
+ if (!active) return { installed: true, authenticated: false };
4219
+ return {
4220
+ installed: true,
4221
+ authenticated: Boolean(active.login) && !active.error,
4222
+ login: active.login,
4223
+ gitProtocol: active.gitProtocol,
4224
+ error: active.error
4225
+ };
4226
+ }
4227
+ function errorMessage2(err) {
4228
+ if (err instanceof Error) return err.message;
4229
+ return String(err);
4230
+ }
4231
+ async function getGitHubCliAuthStatus() {
4232
+ try {
4233
+ const { stdout } = await pexec4(
4234
+ "gh",
4235
+ ["auth", "status", "--hostname", GITHUB_HOST, "--json", "hosts"],
4236
+ {
4237
+ env: execEnv(),
4238
+ timeout: 1e4,
4239
+ windowsHide: true
4240
+ }
4241
+ );
4242
+ const body = JSON.parse(stdout.toString());
4243
+ return statusFromBody(body);
4244
+ } catch (err) {
4245
+ const maybeStdout = err.stdout;
4246
+ if (maybeStdout) {
4247
+ try {
4248
+ const body = JSON.parse(maybeStdout.toString());
4249
+ return statusFromBody(body);
4250
+ } catch {
4251
+ }
4252
+ }
4253
+ const message = errorMessage2(err);
4254
+ return {
4255
+ installed: !/ENOENT|not found/i.test(message),
4256
+ authenticated: false,
4257
+ error: message
4258
+ };
4259
+ }
4260
+ }
4261
+ async function logoutGitHubCli() {
4262
+ const status = await getGitHubCliAuthStatus();
4263
+ if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
4264
+ if (!status.login) return status;
4265
+ await pexec4(
4266
+ "gh",
4267
+ ["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
4268
+ {
4269
+ env: execEnv(),
4270
+ timeout: 1e4,
4271
+ windowsHide: true
4272
+ }
4273
+ );
4274
+ return getGitHubCliAuthStatus();
4275
+ }
4276
+ function cancelGitHubCliLogin() {
4277
+ if (!activeLogin) return;
4278
+ try {
4279
+ activeLogin.kill();
4280
+ } catch {
4281
+ }
4282
+ activeLogin = null;
4283
+ }
4284
+ function startGitHubCliLogin(onEvent) {
4285
+ cancelGitHubCliLogin();
4286
+ onEvent({ type: "starting" });
4287
+ let output = "";
4288
+ let sentBrowserChoice = false;
4289
+ let emittedCode = null;
4290
+ let continuedAfterCode = false;
4291
+ let completed = false;
4292
+ let proc;
4293
+ try {
4294
+ proc = nodePty3.spawn(
4295
+ "gh",
4296
+ ["auth", "login", "--hostname", GITHUB_HOST, "--git-protocol", "https"],
4297
+ {
4298
+ name: "xterm-256color",
4299
+ cwd: process.cwd(),
4300
+ env: { ...execEnv(), BROWSER: "true", TERM: "xterm-256color" },
4301
+ cols: 100,
4302
+ rows: 24
4303
+ }
4304
+ );
4305
+ } catch (err) {
4306
+ onEvent({ type: "error", error: errorMessage2(err) });
4307
+ return;
4308
+ }
4309
+ activeLogin = proc;
4310
+ proc.onData((data) => {
4311
+ output += data;
4312
+ const cleaned = stripAnsi(output);
4313
+ if (!sentBrowserChoice && cleaned.includes("Login with a web browser")) {
4314
+ sentBrowserChoice = true;
4315
+ proc.write("\r");
4316
+ }
4317
+ const code2 = extractDeviceCode(output);
4318
+ if (code2 && code2 !== emittedCode) {
4319
+ emittedCode = code2;
4320
+ onEvent({ type: "code", code: code2, url: DEVICE_URL });
4321
+ }
4322
+ if (code2 && !continuedAfterCode) {
3721
4323
  continuedAfterCode = true;
3722
4324
  proc.write("\r");
3723
4325
  }
@@ -3743,38 +4345,38 @@ function startGitHubCliLogin(onEvent) {
3743
4345
  }
3744
4346
 
3745
4347
  // packages/core/src/board-cli.ts
3746
- import fs10 from "fs";
3747
- import path14 from "path";
4348
+ import fs13 from "fs";
4349
+ import path17 from "path";
3748
4350
  import { pathToFileURL } from "url";
3749
4351
  async function boardCliLaunchInfo() {
3750
- const storeDir = path14.dirname(getStorePath());
4352
+ const storeDir = path17.dirname(getStorePath());
3751
4353
  const entry = getPlatform().cliEntry?.();
3752
- if (entry && fs10.existsSync(entry)) return { storeDir, cliPath: entry };
4354
+ if (entry && fs13.existsSync(entry)) return { storeDir, cliPath: entry };
3753
4355
  const root = getPlatform().sourceRoot();
3754
4356
  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 };
4357
+ const cliPath = path17.join(root, "scripts", "vibeflow.mjs");
4358
+ const loaderPath = path17.join(root, "test", "support", "register.mjs");
4359
+ if (!fs13.existsSync(cliPath) || !fs13.existsSync(loaderPath)) return { storeDir };
3758
4360
  return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
3759
4361
  }
3760
4362
 
3761
4363
  // packages/core/src/decisions.ts
3762
- import fs11 from "fs";
3763
- import path15 from "path";
4364
+ import fs14 from "fs";
4365
+ import path18 from "path";
3764
4366
  var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
3765
4367
  var MAX_DECISION_BYTES = 256 * 1024;
3766
4368
  function decisionsKey(worktreePath, branch) {
3767
- return worktreePath ? path15.basename(worktreePath) : branch.replace(/\//g, "-");
4369
+ return worktreePath ? path18.basename(worktreePath) : branch.replace(/\//g, "-");
3768
4370
  }
3769
4371
  function decisionsPath(workspacePath, key) {
3770
- return path15.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
4372
+ return path18.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
3771
4373
  }
3772
4374
  function readDecisions(workspacePath, key) {
3773
4375
  const target = decisionsPath(workspacePath, key);
3774
4376
  try {
3775
- const stat = fs11.statSync(target);
4377
+ const stat = fs14.statSync(target);
3776
4378
  if (!stat.isFile()) return { path: target, markdown: null };
3777
- const buf = fs11.readFileSync(target);
4379
+ const buf = fs14.readFileSync(target);
3778
4380
  return {
3779
4381
  path: target,
3780
4382
  markdown: buf.subarray(0, MAX_DECISION_BYTES).toString("utf8"),
@@ -3787,7 +4389,7 @@ function readDecisions(workspacePath, key) {
3787
4389
  }
3788
4390
  function deleteDecisions(workspacePath, key) {
3789
4391
  try {
3790
- fs11.rmSync(decisionsPath(workspacePath, key), { force: true });
4392
+ fs14.rmSync(decisionsPath(workspacePath, key), { force: true });
3791
4393
  } catch {
3792
4394
  }
3793
4395
  }
@@ -4012,414 +4614,71 @@ ${attachmentLines.join("\n")}` : text;
4012
4614
  }
4013
4615
  }
4014
4616
  }
4015
- if (event.type === "user" && event.message?.content) {
4016
- for (const block of event.message.content) {
4017
- if (block.type === "tool_result") {
4018
- const toolName = toolNames.get(block.tool_use_id ?? "") ?? "Tool";
4019
- const raw2 = block.content;
4020
- const detail = typeof raw2 === "string" ? raw2 : Array.isArray(raw2) ? raw2.map((c) => c.text ?? "").join("\n") : "";
4021
- pushPhase({
4022
- taskId,
4023
- id: `${taskId}-${seq++}`,
4024
- phaseType: "tool_result",
4025
- phaseSummary: `Got result from \`${toolName}\``,
4026
- phaseDetail: detail,
4027
- done: true
4028
- });
4029
- }
4030
- }
4031
- }
4032
- if (event.type === "result" && event.result) {
4033
- const remaining = event.result.slice(accumulated.length);
4034
- if (remaining) push({ taskId, delta: remaining, done: false });
4035
- accumulated = event.result;
4036
- }
4037
- } catch {
4038
- }
4039
- }
4040
- });
4041
- proc.stderr.on("data", (raw) => {
4042
- const msg = raw.toString().trim();
4043
- if (msg) push({ taskId, delta: "", done: false, error: msg });
4044
- });
4045
- proc.on("close", (code2) => {
4046
- const assistantText = accumulated || "\uFF08\u7121\u56DE\u61C9\uFF09";
4047
- appendMessage(taskId, {
4048
- id: randomUUID3().slice(0, 8),
4049
- role: "assistant",
4050
- text: assistantText,
4051
- ts: Date.now()
4052
- });
4053
- const error = code2 !== 0 ? `Process exited with code ${code2}` : void 0;
4054
- push({ taskId, delta: assistantText, done: true, error });
4055
- });
4056
- return () => {
4057
- try {
4058
- proc.kill("SIGTERM");
4059
- } catch {
4060
- }
4061
- };
4062
- }
4063
- var activeProcs = /* @__PURE__ */ new Map();
4064
- function cancelChatSend(taskId) {
4065
- activeProcs.get(taskId)?.();
4066
- activeProcs.delete(taskId);
4067
- }
4068
- function cancelAllChatSends() {
4069
- activeProcs.forEach((kill) => kill());
4070
- activeProcs.clear();
4071
- }
4072
- function startChatSend(opts, sender) {
4073
- cancelChatSend(opts.taskId);
4074
- const kill = sendChatMessage(opts, sender);
4075
- activeProcs.set(opts.taskId, kill);
4076
- }
4077
-
4078
- // packages/core/src/service.ts
4079
- init_attachments();
4080
-
4081
- // packages/core/src/library.ts
4082
- import fs12 from "fs";
4083
- import os3 from "os";
4084
- import path16 from "path";
4085
- var LIBRARY_DIR = "library";
4086
- var LIBRARY_KINDS = ["skill", "prompt", "script"];
4087
- var KIND_DIR = {
4088
- skill: "skills",
4089
- prompt: "prompts",
4090
- script: "scripts"
4091
- };
4092
- var INDEX_FILE = "index.json";
4093
- var MAX_NAME_BYTES = 100;
4094
- function libraryPaths(root) {
4095
- return {
4096
- 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")
4102
- };
4103
- }
4104
- async function libraryRoot() {
4105
- return path16.join(getPlatform().userDataDir(), LIBRARY_DIR);
4106
- }
4107
- function truncateUtf82(value, maxBytes) {
4108
- let result = "";
4109
- for (const character of value) {
4110
- if (Buffer.byteLength(result + character) > maxBytes) break;
4111
- result += character;
4112
- }
4113
- return result;
4114
- }
4115
- function safeLibraryName(name) {
4116
- const basename2 = name.replace(/\\/g, "/").split("/").pop() ?? "";
4117
- const cleaned = truncateUtf82(
4118
- basename2.replace(/[\u0000-\u001f\u007f]/g, "").trim(),
4119
- MAX_NAME_BYTES
4120
- );
4121
- return cleaned && cleaned !== "." && cleaned !== ".." ? cleaned : "untitled";
4122
- }
4123
- function entryKey(kind, name) {
4124
- return `${kind}/${name}`;
4125
- }
4126
- function indexPath(root) {
4127
- return path16.join(root, INDEX_FILE);
4128
- }
4129
- function readIndex(root) {
4130
- try {
4131
- const parsed = JSON.parse(fs12.readFileSync(indexPath(root), "utf8"));
4132
- if (parsed && typeof parsed === "object" && "entries" in parsed) {
4133
- const entries = parsed.entries;
4134
- if (entries && typeof entries === "object") return { version: 1, entries };
4135
- }
4136
- } catch {
4137
- }
4138
- return { version: 1, entries: {} };
4139
- }
4140
- function writeIndex(root, index) {
4141
- fs12.mkdirSync(root, { recursive: true });
4142
- fs12.writeFileSync(indexPath(root), `${JSON.stringify(index, null, 2)}
4143
- `);
4144
- }
4145
- function ensureLibrary(root) {
4146
- const paths = libraryPaths(root);
4147
- for (const kind of LIBRARY_KINDS) {
4148
- fs12.mkdirSync(path16.join(root, KIND_DIR[kind]), { recursive: true });
4149
- }
4150
- return paths;
4151
- }
4152
- function readSkillDescription(skillMdPath) {
4153
- let content;
4154
- try {
4155
- content = fs12.readFileSync(skillMdPath, "utf8");
4156
- } catch {
4157
- return void 0;
4158
- }
4159
- const lines = content.split(/\r?\n/);
4160
- if (lines[0]?.trim() !== "---") return void 0;
4161
- for (let i = 1; i < lines.length; i += 1) {
4162
- const line = lines[i];
4163
- if (line.trim() === "---") break;
4164
- const match = /^description:\s*(.+)$/.exec(line);
4165
- if (match) return match[1].trim().replace(/^["']|["']$/g, "");
4166
- }
4167
- return void 0;
4168
- }
4169
- function entryPath(root, kind, name) {
4170
- return path16.join(root, KIND_DIR[kind], name);
4171
- }
4172
- function listLibrary(root) {
4173
- const index = readIndex(root);
4174
- const entries = [];
4175
- for (const kind of LIBRARY_KINDS) {
4176
- const dir = path16.join(root, KIND_DIR[kind]);
4177
- let dirents;
4178
- try {
4179
- dirents = fs12.readdirSync(dir, { withFileTypes: true });
4180
- } catch {
4181
- continue;
4182
- }
4183
- for (const dirent of dirents) {
4184
- if (dirent.name.startsWith(".")) continue;
4185
- const wantsDirectory = kind === "skill";
4186
- let stat;
4187
- try {
4188
- stat = fs12.statSync(path16.join(dir, dirent.name));
4617
+ if (event.type === "user" && event.message?.content) {
4618
+ for (const block of event.message.content) {
4619
+ if (block.type === "tool_result") {
4620
+ const toolName = toolNames.get(block.tool_use_id ?? "") ?? "Tool";
4621
+ const raw2 = block.content;
4622
+ const detail = typeof raw2 === "string" ? raw2 : Array.isArray(raw2) ? raw2.map((c) => c.text ?? "").join("\n") : "";
4623
+ pushPhase({
4624
+ taskId,
4625
+ id: `${taskId}-${seq++}`,
4626
+ phaseType: "tool_result",
4627
+ phaseSummary: `Got result from \`${toolName}\``,
4628
+ phaseDetail: detail,
4629
+ done: true
4630
+ });
4631
+ }
4632
+ }
4633
+ }
4634
+ if (event.type === "result" && event.result) {
4635
+ const remaining = event.result.slice(accumulated.length);
4636
+ if (remaining) push({ taskId, delta: remaining, done: false });
4637
+ accumulated = event.result;
4638
+ }
4189
4639
  } catch {
4190
- continue;
4191
4640
  }
4192
- if (stat.isDirectory() !== wantsDirectory) continue;
4193
- const name = dirent.name;
4194
- const full = path16.join(dir, name);
4195
- if (wantsDirectory && !fs12.existsSync(path16.join(full, "SKILL.md"))) continue;
4196
- const key = entryKey(kind, name);
4197
- const record = index.entries[key] ?? {};
4198
- entries.push({
4199
- key,
4200
- kind,
4201
- name,
4202
- path: full,
4203
- description: wantsDirectory ? readSkillDescription(path16.join(full, "SKILL.md")) : record.description,
4204
- sourcePath: record.sourcePath,
4205
- importedAt: record.importedAt,
4206
- enabled: record.enabled !== false
4207
- });
4208
- }
4209
- }
4210
- return entries.sort((a, b) => a.key.localeCompare(b.key));
4211
- }
4212
- function annotate(root, key, patch) {
4213
- const index = readIndex(root);
4214
- index.entries[key] = { ...index.entries[key], ...patch };
4215
- writeIndex(root, index);
4216
- }
4217
- function importEntry(root, kind, sourcePath) {
4218
- ensureLibrary(root);
4219
- const resolved = path16.resolve(sourcePath);
4220
- const stat = fs12.statSync(resolved);
4221
- if (kind === "skill") {
4222
- 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"))) {
4224
- throw new Error("\u76EE\u9304\u5167\u627E\u4E0D\u5230 SKILL.md");
4225
4641
  }
4226
- } else if (!stat.isFile()) {
4227
- throw new Error(`${kind} \u5FC5\u9808\u662F\u55AE\u4E00\u6A94\u6848`);
4228
- }
4229
- const name = safeLibraryName(path16.basename(resolved));
4230
- 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);
4234
- annotate(root, entryKey(kind, name), {
4235
- sourcePath: resolved,
4236
- importedAt: Date.now(),
4237
- enabled: true
4238
4642
  });
4239
- return requireEntry(root, kind, name);
4240
- }
4241
- function createEntry(root, kind, rawName, content, description) {
4242
- ensureLibrary(root);
4243
- const name = safeLibraryName(rawName);
4244
- const target = entryPath(root, kind, name);
4245
- if (fs12.existsSync(target)) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u9805\u76EE\uFF1A${name}`);
4246
- if (kind === "skill") {
4247
- fs12.mkdirSync(target, { recursive: true });
4248
- fs12.writeFileSync(path16.join(target, "SKILL.md"), content);
4249
- } else {
4250
- fs12.writeFileSync(target, content);
4251
- if (kind === "script") fs12.chmodSync(target, 493);
4252
- }
4253
- annotate(root, entryKey(kind, name), {
4254
- importedAt: Date.now(),
4255
- enabled: true,
4256
- description
4643
+ proc.stderr.on("data", (raw) => {
4644
+ const msg = raw.toString().trim();
4645
+ if (msg) push({ taskId, delta: "", done: false, error: msg });
4257
4646
  });
4258
- return requireEntry(root, kind, name);
4259
- }
4260
- function setEntryDescription(root, kind, name, description) {
4261
- annotate(root, entryKey(kind, name), { description });
4262
- }
4263
- function updateEntry(root, kind, name, content) {
4264
- 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);
4267
- return requireEntry(root, kind, name);
4268
- }
4269
- function readEntryContent(root, kind, name) {
4270
- const target = entryPath(root, kind, name);
4271
- return fs12.readFileSync(kind === "skill" ? path16.join(target, "SKILL.md") : target, "utf8");
4272
- }
4273
- function deleteEntry(root, kind, name) {
4274
- fs12.rmSync(entryPath(root, kind, name), { recursive: true, force: true });
4275
- const index = readIndex(root);
4276
- delete index.entries[entryKey(kind, name)];
4277
- writeIndex(root, index);
4278
- }
4279
- function setEntryEnabled(root, kind, name, enabled) {
4280
- annotate(root, entryKey(kind, name), { enabled });
4281
- }
4282
- function requireEntry(root, kind, name) {
4283
- const found2 = listLibrary(root).find((e) => e.key === entryKey(kind, name));
4284
- if (!found2) throw new Error(`\u9805\u76EE\u5BEB\u5165\u5F8C\u8B80\u4E0D\u56DE\u4F86\uFF1A${kind}/${name}`);
4285
- return found2;
4286
- }
4287
- function projectSkillsDir(worktreePath) {
4288
- return path16.join(worktreePath, ".claude", "skills");
4289
- }
4290
- function skillNamesIn(dir) {
4291
- 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();
4293
- } catch {
4294
- return [];
4295
- }
4296
- }
4297
- var PLUGIN_MANIFEST_DIR = ".claude-plugin";
4298
- function buildPluginDir(root) {
4299
- const paths = libraryPaths(root);
4300
- 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"),
4306
- `${JSON.stringify(
4307
- {
4308
- name: "vibeflow-library",
4309
- version: "1.0.0",
4310
- description: "Skills delivered by VibeFlow for this task."
4311
- },
4312
- null,
4313
- 2
4314
- )}
4315
- `
4316
- );
4317
- for (const entry of enabled) {
4318
- fs12.cpSync(entry.path, path16.join(paths.plugin, "skills", entry.name), {
4319
- recursive: true,
4320
- dereference: true
4647
+ proc.on("close", (code2) => {
4648
+ const assistantText = accumulated || "\uFF08\u7121\u56DE\u61C9\uFF09";
4649
+ appendMessage(taskId, {
4650
+ id: randomUUID3().slice(0, 8),
4651
+ role: "assistant",
4652
+ text: assistantText,
4653
+ ts: Date.now()
4321
4654
  });
4322
- }
4323
- return { pluginDir: paths.plugin, skills: enabled.map((e) => e.name) };
4324
- }
4325
- 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 })
4331
- ] : [
4332
- () => fs12.symlinkSync(source, linkPath),
4333
- () => fs12.linkSync(source, linkPath),
4334
- () => fs12.copyFileSync(source, linkPath)
4335
- ];
4336
- for (const attempt of attempts) {
4655
+ const error = code2 !== 0 ? `Process exited with code ${code2}` : void 0;
4656
+ push({ taskId, delta: assistantText, done: true, error });
4657
+ });
4658
+ return () => {
4337
4659
  try {
4338
- fs12.rmSync(linkPath, { recursive: true, force: true });
4339
- attempt();
4340
- return true;
4660
+ proc.kill("SIGTERM");
4341
4661
  } catch {
4342
4662
  }
4343
- }
4344
- return false;
4345
- }
4346
- function buildCodexHome(root, worktreePath, userCodexHome = path16.join(os3.homedir(), ".codex")) {
4347
- 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 });
4351
- for (const file of ["auth.json", "config.toml"]) {
4352
- linkOrSkip(path16.join(userCodexHome, file), path16.join(paths.codexHome, file));
4353
- }
4354
- const result = {
4355
- codexHome: paths.codexHome,
4356
- project: [],
4357
- user: [],
4358
- library: [],
4359
- shadowed: []
4360
4663
  };
4361
- const claimed = /* @__PURE__ */ new Map();
4362
- const projectDir = worktreePath ? projectSkillsDir(worktreePath) : void 0;
4363
- if (projectDir) {
4364
- for (const name of skillNamesIn(projectDir)) {
4365
- if (linkOrSkip(path16.join(projectDir, name), path16.join(skillsDir, name))) {
4366
- result.project.push(name);
4367
- claimed.set(name, "project");
4368
- }
4369
- }
4370
- }
4371
- const userDir = path16.join(userCodexHome, "skills");
4372
- for (const name of skillNamesIn(userDir)) {
4373
- const owner = claimed.get(name);
4374
- if (owner) {
4375
- result.shadowed.push({ name, keptFrom: owner, dropped: "user" });
4376
- continue;
4377
- }
4378
- if (linkOrSkip(path16.join(userDir, name), path16.join(skillsDir, name))) {
4379
- result.user.push(name);
4380
- claimed.set(name, "user");
4381
- }
4382
- }
4383
- for (const entry of listLibrary(root)) {
4384
- if (entry.kind !== "skill" || !entry.enabled) continue;
4385
- const owner = claimed.get(entry.name);
4386
- if (owner) {
4387
- result.shadowed.push({ name: entry.name, keptFrom: owner, dropped: "library" });
4388
- continue;
4389
- }
4390
- fs12.cpSync(entry.path, path16.join(skillsDir, entry.name), {
4391
- recursive: true,
4392
- dereference: true
4393
- });
4394
- result.library.push(entry.name);
4395
- }
4396
- return result;
4397
4664
  }
4398
- function libraryLaunchInfoAt(root, worktreePath, userCodexHome) {
4399
- const entries = listLibrary(root).filter((e) => e.enabled);
4400
- if (entries.length === 0) return null;
4401
- const { pluginDir } = buildPluginDir(root);
4402
- const { codexHome } = buildCodexHome(root, worktreePath, userCodexHome);
4403
- const scripts = entries.filter((e) => e.kind === "script").map((e) => ({ name: e.name, path: e.path, description: e.description }));
4404
- const promptBodies = entries.filter((e) => e.kind === "prompt").map((e) => readEntryContent(root, "prompt", e.name).trim()).filter(Boolean);
4405
- const lines = [...promptBodies];
4406
- if (scripts.length > 0) {
4407
- lines.push(
4408
- "\u53EF\u7528\u7684\u5DE5\u5177\u8173\u672C\uFF08\u9700\u8981\u6642\u81EA\u884C\u57F7\u884C\uFF0C\u4E0D\u9700\u8981\u5C31\u5225\u8DD1\uFF09\uFF1A",
4409
- ...scripts.map((s) => `- ${s.path}${s.description ? ` \u2014 ${s.description}` : ""}`)
4410
- );
4411
- }
4412
- return {
4413
- pluginDir,
4414
- codexHome,
4415
- libraryDir: root,
4416
- scripts,
4417
- promptText: lines.join("\n\n")
4418
- };
4665
+ var activeProcs = /* @__PURE__ */ new Map();
4666
+ function cancelChatSend(taskId) {
4667
+ activeProcs.get(taskId)?.();
4668
+ activeProcs.delete(taskId);
4419
4669
  }
4420
- async function libraryLaunchInfo(worktreePath) {
4421
- return libraryLaunchInfoAt(await libraryRoot(), worktreePath);
4670
+ function cancelAllChatSends() {
4671
+ activeProcs.forEach((kill) => kill());
4672
+ activeProcs.clear();
4422
4673
  }
4674
+ function startChatSend(opts, sender) {
4675
+ cancelChatSend(opts.taskId);
4676
+ const kill = sendChatMessage(opts, sender);
4677
+ activeProcs.set(opts.taskId, kill);
4678
+ }
4679
+
4680
+ // packages/core/src/service.ts
4681
+ init_attachments();
4423
4682
 
4424
4683
  // packages/core/src/launch.ts
4425
4684
  var ARTIFACTS_DIR_SUFFIX2 = ".artifacts";
@@ -4703,13 +4962,13 @@ function createCore({ sessions, bus, version }) {
4703
4962
  }
4704
4963
  function claudeSessionFile(cwd, sessionId) {
4705
4964
  const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
4706
- return path17.join(os4.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
4965
+ return path19.join(os5.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
4707
4966
  }
4708
4967
  async function removeTaskWorktree(task) {
4709
4968
  if (!task.projectPath || !task.worktreePath) return;
4710
4969
  const branch = task.branch || fallbackBranchName(task.id);
4711
4970
  await removeWorktree(task.projectPath, task.worktreePath);
4712
- deleteArtifacts(task.workspacePath ?? path17.dirname(task.worktreePath), task.worktreePath);
4971
+ deleteArtifacts(task.workspacePath ?? path19.dirname(task.worktreePath), task.worktreePath);
4713
4972
  await deleteBranch(task.projectPath, branch);
4714
4973
  }
4715
4974
  const handlers = {
@@ -4720,66 +4979,7 @@ function createCore({ sessions, bus, version }) {
4720
4979
  return getState();
4721
4980
  },
4722
4981
  "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
- }
4982
+ setSettings(obj(patch, "patch"));
4783
4983
  return getState();
4784
4984
  },
4785
4985
  "settings:githubAuthStatus": () => getGitHubCliAuthStatus(),
@@ -4791,6 +4991,10 @@ function createCore({ sessions, bus, version }) {
4791
4991
  },
4792
4992
  "settings:logoutGithubAuth": () => logoutGitHubCli(),
4793
4993
  "env:detectAgents": () => detectAgents(),
4994
+ "agents:listModels": (payload) => {
4995
+ const { agentId: rawId, refresh } = obj(payload, "payload");
4996
+ return listAgentModels(agentId(rawId), { refresh: refresh === true });
4997
+ },
4794
4998
  // A task does not exist yet when a project is picked, so these two are the
4795
4999
  // ones that must take a path. They only inspect or `git init` it.
4796
5000
  "git:getInfo": (projectPath) => getGitInfo(str(projectPath ?? "", "projectPath")),
@@ -4803,8 +5007,8 @@ function createCore({ sessions, bus, version }) {
4803
5007
  "projects:listRecent": () => listRecentProjects(getStore()),
4804
5008
  // `vibeflow open <path>` while a host runs: only the host writes the store.
4805
5009
  "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}`);
5010
+ const p = path19.resolve(str(projectPath, "projectPath"));
5011
+ if (!fs15.existsSync(p) || !fs15.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
4808
5012
  recordRecentProject(getStore(), p);
4809
5013
  return listRecentProjects(getStore());
4810
5014
  },
@@ -4844,7 +5048,7 @@ function createCore({ sessions, bus, version }) {
4844
5048
  if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
4845
5049
  await teardownTask(task.id);
4846
5050
  await resetWorktreeToBase(task.worktreePath, task.baseBranch);
4847
- const workspacePath = task.workspacePath ?? path17.dirname(task.worktreePath);
5051
+ const workspacePath = task.workspacePath ?? path19.dirname(task.worktreePath);
4848
5052
  deleteArtifacts(workspacePath, task.worktreePath);
4849
5053
  deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
4850
5054
  resetSubAgents(task.worktreePath);
@@ -4883,9 +5087,9 @@ function createCore({ sessions, bus, version }) {
4883
5087
  if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
4884
5088
  await teardownTask(existing.id);
4885
5089
  await removeTaskWorktree(existing);
4886
- const projectName = path17.basename(nextProject);
5090
+ const projectName = path19.basename(nextProject);
4887
5091
  const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
4888
- await fs13.promises.mkdir(workspacePath, { recursive: true });
5092
+ await fs15.promises.mkdir(workspacePath, { recursive: true });
4889
5093
  const result = await provisionWorktree(
4890
5094
  nextProject,
4891
5095
  workspacePath,
@@ -4973,7 +5177,7 @@ function createCore({ sessions, bus, version }) {
4973
5177
  "claude:sessionExists": (taskId) => {
4974
5178
  const task = requireTask(taskId);
4975
5179
  if (!task.worktreePath) return false;
4976
- return fs13.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
5180
+ return fs15.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
4977
5181
  },
4978
5182
  "sessions:list": async () => {
4979
5183
  const keys = await sessions.list();
@@ -4992,9 +5196,9 @@ function createCore({ sessions, bus, version }) {
4992
5196
  "git:getDiffEntries": (taskId, opts) => {
4993
5197
  const task = requireTask(taskId);
4994
5198
  if (!task.worktreePath) return [];
4995
- const fetch2 = opts && typeof opts === "object" ? opts.fetch : void 0;
5199
+ const fetch = opts && typeof opts === "object" ? opts.fetch : void 0;
4996
5200
  return getWorktreeDiffEntries(task.worktreePath, task.baseBranch ?? "HEAD", {
4997
- fetch: typeof fetch2 === "boolean" ? fetch2 : void 0
5201
+ fetch: typeof fetch === "boolean" ? fetch : void 0
4998
5202
  });
4999
5203
  },
5000
5204
  "git:getDiffFile": (taskId, filePath) => {
@@ -5020,7 +5224,7 @@ function createCore({ sessions, bus, version }) {
5020
5224
  const task = requireTask(taskId);
5021
5225
  if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
5022
5226
  const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
5023
- if (!fs13.existsSync(dir)) return "artifacts directory does not exist yet";
5227
+ if (!fs15.existsSync(dir)) return "artifacts directory does not exist yet";
5024
5228
  return getPlatform().openPath(dir);
5025
5229
  },
5026
5230
  "task:getDecisions": (taskId) => {
@@ -5029,7 +5233,7 @@ function createCore({ sessions, bus, version }) {
5029
5233
  return readDecisions(task.workspacePath, decisionsKey(task.worktreePath, task.branch));
5030
5234
  },
5031
5235
  "board:getCliLaunchInfo": () => boardCliLaunchInfo(),
5032
- "library:list": async () => listLibrary(await libraryRoot()),
5236
+ "library:list": async () => listLibrary(await libraryRoot(), builtinSkillsDir()),
5033
5237
  // The source path was picked by the user (native picker or typed path).
5034
5238
  "library:import": async (payload) => {
5035
5239
  const p = obj(payload, "payload");
@@ -5068,6 +5272,11 @@ function createCore({ sessions, bus, version }) {
5068
5272
  deleteEntry(await libraryRoot(), libraryKind(p.kind), str(p.name, "name"));
5069
5273
  return true;
5070
5274
  },
5275
+ "library:restoreBuiltin": async (payload) => {
5276
+ const p = obj(payload, "payload");
5277
+ return restoreBuiltinSkill(await libraryRoot(), builtinSkillsDir(), str(p.name, "name"));
5278
+ },
5279
+ "library:removedBuiltins": async () => removedBuiltinSkills(await libraryRoot(), builtinSkillsDir()),
5071
5280
  // Approve: commit everything in the worktree and push the branch upstream.
5072
5281
  "git:approve": async (payload) => {
5073
5282
  const p = obj(payload, "payload");
@@ -5173,9 +5382,9 @@ function createCore({ sessions, bus, version }) {
5173
5382
  // Watch the containing directory, not the file: JsonStore renames a temp
5174
5383
  // file over the target and so replaces the inode on every save.
5175
5384
  watchStore() {
5176
- const file = path17.basename(getStorePath());
5385
+ const file = path19.basename(getStorePath());
5177
5386
  let debounce = null;
5178
- storeWatcher = fs13.watch(path17.dirname(getStorePath()), (_event, filename) => {
5387
+ storeWatcher = fs15.watch(path19.dirname(getStorePath()), (_event, filename) => {
5179
5388
  if (filename !== file) return;
5180
5389
  if (debounce) clearTimeout(debounce);
5181
5390
  debounce = setTimeout(() => {
@@ -5207,9 +5416,9 @@ function createSessionBackend(sink, choice) {
5207
5416
  }
5208
5417
 
5209
5418
  // packages/core/src/web-server.ts
5210
- import fs14 from "fs";
5419
+ import fs16 from "fs";
5211
5420
  import http from "http";
5212
- import path18 from "path";
5421
+ import path20 from "path";
5213
5422
  import { timingSafeEqual } from "crypto";
5214
5423
  import { WebSocketServer } from "ws";
5215
5424
  var TOKEN_COOKIE = "vf_token";
@@ -5262,14 +5471,14 @@ function resolveStatic(root, urlPath) {
5262
5471
  return null;
5263
5472
  }
5264
5473
  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`];
5474
+ const rel = path20.posix.normalize(decoded).replace(/^\/+/, "");
5475
+ const base = path20.resolve(root);
5476
+ const candidates = [rel, path20.posix.join(rel, "index.html"), `${rel}.html`];
5268
5477
  for (const c of candidates) {
5269
- const full = path18.resolve(base, c);
5270
- if (full !== base && !full.startsWith(base + path18.sep)) return null;
5478
+ const full = path20.resolve(base, c);
5479
+ if (full !== base && !full.startsWith(base + path20.sep)) return null;
5271
5480
  try {
5272
- if (fs14.statSync(full).isFile()) return full;
5481
+ if (fs16.statSync(full).isFile()) return full;
5273
5482
  } catch {
5274
5483
  }
5275
5484
  }
@@ -5330,7 +5539,7 @@ async function startWebServer(options) {
5330
5539
  send(res, 404, "Not found");
5331
5540
  return;
5332
5541
  }
5333
- const ext = path18.extname(file).toLowerCase();
5542
+ const ext = path20.extname(file).toLowerCase();
5334
5543
  const immutable = url.pathname.startsWith("/_next/static/");
5335
5544
  res.writeHead(200, {
5336
5545
  "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
@@ -5343,7 +5552,7 @@ async function startWebServer(options) {
5343
5552
  res.end();
5344
5553
  return;
5345
5554
  }
5346
- fs14.createReadStream(file).pipe(res);
5555
+ fs16.createReadStream(file).pipe(res);
5347
5556
  }
5348
5557
  const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
5349
5558
  function reject(socket, status, reason) {
@@ -5468,6 +5677,7 @@ async function startHost(options) {
5468
5677
  return startHost(options);
5469
5678
  }
5470
5679
  try {
5680
+ await syncBuiltinsAtStartup();
5471
5681
  const bus = new EventBus();
5472
5682
  const sessions = createSessionBackend(bus.sink(), {
5473
5683
  prefer: options.backend,
@@ -5514,7 +5724,7 @@ function loginUrl(lock, pathname = "/home/") {
5514
5724
  return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
5515
5725
  }
5516
5726
  function isWebDir(dir) {
5517
- return fs15.existsSync(path19.join(dir, "home", "index.html"));
5727
+ return fs17.existsSync(path21.join(dir, "home", "index.html"));
5518
5728
  }
5519
5729
 
5520
5730
  // packages/cli/src/remote.ts
@@ -5631,14 +5841,14 @@ function connectToHost(lock) {
5631
5841
  }
5632
5842
 
5633
5843
  // 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";
5844
+ import fs18 from "fs";
5845
+ import path22 from "path";
5846
+ import { execFile as execFile7 } from "child_process";
5847
+ import { promisify as promisify5 } from "util";
5638
5848
  init_env();
5639
5849
  init_git_bash();
5640
5850
  init_tmux_backend();
5641
- var pexec4 = promisify4(execFile6);
5851
+ var pexec5 = promisify5(execFile7);
5642
5852
  function parseVersion(text) {
5643
5853
  const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
5644
5854
  return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
@@ -5652,7 +5862,7 @@ function versionAtLeast(have, want) {
5652
5862
  }
5653
5863
  async function run(cmd, args) {
5654
5864
  try {
5655
- const { stdout } = await pexec4(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
5865
+ const { stdout } = await pexec5(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
5656
5866
  return String(stdout).trim();
5657
5867
  } catch {
5658
5868
  return null;
@@ -5772,10 +5982,10 @@ async function runDoctor(options) {
5772
5982
  });
5773
5983
  }
5774
5984
  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);
5985
+ fs18.mkdirSync(options.userDataDir, { recursive: true });
5986
+ const probe = path22.join(options.userDataDir, `.doctor-${process.pid}`);
5987
+ fs18.writeFileSync(probe, "");
5988
+ fs18.rmSync(probe);
5779
5989
  checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
5780
5990
  } catch (err) {
5781
5991
  checks.push({
@@ -5829,24 +6039,24 @@ Options:
5829
6039
  `.trim();
5830
6040
  function detectLayout() {
5831
6041
  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") {
6042
+ const here = path25.dirname(file);
6043
+ const bundledWeb = path25.join(here, "web");
6044
+ if (isWebDir(bundledWeb) || path25.basename(here) === "dist") {
5835
6045
  return {
5836
- version: readVersion(path23.join(here, "..", "package.json")),
6046
+ version: readVersion(path25.join(here, "..", "package.json")),
5837
6047
  webDir: isWebDir(bundledWeb) ? bundledWeb : null,
5838
6048
  sourceRoot: null,
5839
6049
  cliEntry: file
5840
6050
  };
5841
6051
  }
5842
- const root = path23.resolve(here, "..", "..", "..");
6052
+ const root = path25.resolve(here, "..", "..", "..");
5843
6053
  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 };
6054
+ const webDir = override && isWebDir(override) ? override : isWebDir(path25.join(root, "app")) ? path25.join(root, "app") : null;
6055
+ return { version: readVersion(path25.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
5846
6056
  }
5847
6057
  function readVersion(pkgPath) {
5848
6058
  try {
5849
- return JSON.parse(fs18.readFileSync(pkgPath, "utf8")).version;
6059
+ return JSON.parse(fs20.readFileSync(pkgPath, "utf8")).version;
5850
6060
  } catch {
5851
6061
  return "0.0.0";
5852
6062
  }
@@ -6052,8 +6262,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
6052
6262
  console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
6053
6263
  return 1;
6054
6264
  }
6055
- const dir = path23.resolve(target);
6056
- if (!fs18.existsSync(dir) || !fs18.statSync(dir).isDirectory()) {
6265
+ const dir = path25.resolve(target);
6266
+ if (!fs20.existsSync(dir) || !fs20.statSync(dir).isDirectory()) {
6057
6267
  console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
6058
6268
  return 1;
6059
6269
  }