@kw-yeh/vibeflow 4.0.6 → 4.2.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 fs18 from "fs";
601
+ import fs20 from "fs";
602
602
  import os6 from "os";
603
- import path22 from "path";
603
+ import path24 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) {
@@ -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 = fs18.mkdtempSync(path22.join(os6.tmpdir(), "vibeflow-"));
723
- const file = path22.join(dir, "description.md");
722
+ const dir = fs20.mkdtempSync(path24.join(os6.tmpdir(), "vibeflow-"));
723
+ const file = path24.join(dir, "description.md");
724
724
  try {
725
- fs18.writeFileSync(file, text);
725
+ fs20.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: fs18.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
730
+ return { text: fs20.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
731
731
  } finally {
732
- fs18.rmSync(dir, { recursive: true, force: true });
732
+ fs20.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(path22.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir())));
800
+ if (value2) setProject(path24.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir())));
801
801
  } else if (editing === "attachments" && value2) {
802
- const file = path22.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir()));
803
- if (!fs18.existsSync(file) || !fs18.statSync(file).isFile()) {
802
+ const file = path24.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir()));
803
+ if (!fs20.existsSync(file) || !fs20.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 ? path22.basename(s.projectPath) : "\u2014"}\uFF08\u4EFB\u52D9\u5DF2\u958B\u59CB\uFF0C\u9396\u5B9A\uFF09`;
873
+ if (projectLocked) return `${s.projectPath ? path24.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) => path22.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
884
+ return s.attachments.length ? s.attachments.map((a) => path24.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");
@@ -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"}/${path22.basename(s.projectPath)}`;
915
+ if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path24.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`;
@@ -1029,7 +1029,7 @@ __export(src_exports, {
1029
1029
  showDiff: () => showDiff
1030
1030
  });
1031
1031
  import { spawnSync as spawnSync2 } from "child_process";
1032
- import path23 from "path";
1032
+ import path25 from "path";
1033
1033
  import React2, { useEffect as useEffect2, useState as useState2 } from "react";
1034
1034
  import { Box as Box2, Text as Text2, render as render2, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
1035
1035
  function findDetachKey(text) {
@@ -1291,7 +1291,7 @@ function showDiff(task) {
1291
1291
  });
1292
1292
  }
1293
1293
  function tmuxAttach(opts, sessionName) {
1294
- const conf = path23.join(opts.userDataDir, "tmux.conf");
1294
+ const conf = path25.join(opts.userDataDir, "tmux.conf");
1295
1295
  const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
1296
1296
  const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
1297
1297
  spawnSync2("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
@@ -1421,8 +1421,8 @@ var init_src = __esm({
1421
1421
  });
1422
1422
 
1423
1423
  // packages/cli/src/main.ts
1424
- import fs19 from "fs";
1425
- import path24 from "path";
1424
+ import fs21 from "fs";
1425
+ import path26 from "path";
1426
1426
  import { fileURLToPath } from "url";
1427
1427
  import { parseArgs as parseArgs2 } from "node:util";
1428
1428
 
@@ -1526,6 +1526,12 @@ function createNodePlatform(options = {}) {
1526
1526
  userDataDir: () => userData,
1527
1527
  sourceRoot: () => root,
1528
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
+ },
1529
1535
  openExternal: async (url) => {
1530
1536
  const err = await openWithOs(url);
1531
1537
  if (err) throw new Error(err);
@@ -1808,8 +1814,8 @@ function createBridge(t) {
1808
1814
  * in-app preview will not inline. Resolves to '' on success, else a reason.
1809
1815
  */
1810
1816
  openArtifactsDir: (taskId) => t.invoke("task:openArtifactsDir", taskId),
1811
- /** The task's decision record; null when it has no workspace folder. */
1812
- getDecisions: (taskId) => t.invoke("task:getDecisions", taskId),
1817
+ /** Every spec.md the task's branch added or changed; empty when there is none. */
1818
+ getSpecs: (taskId) => t.invoke("task:getSpecs", taskId),
1813
1819
  /** Store dir + CLI paths for launch injection, so an agent can write cards. */
1814
1820
  getBoardCliLaunchInfo: () => t.invoke("board:getCliLaunchInfo"),
1815
1821
  /** VibeFlow's own skill / prompt / script store. */
@@ -1821,6 +1827,10 @@ function createBridge(t) {
1821
1827
  setLibraryEntryDescription: (payload) => t.invoke("library:setDescription", payload),
1822
1828
  setLibraryEntryEnabled: (payload) => t.invoke("library:setEnabled", payload),
1823
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"),
1824
1834
  approve: (taskId, message) => t.invoke("git:approve", { taskId, message }),
1825
1835
  /** Use the task's agent CLI to generate a commit message for branch changes. */
1826
1836
  generateCommitMessage: (taskId) => t.invoke("git:generateCommitMessage", taskId),
@@ -2691,7 +2701,41 @@ async function copyIgnoredEntry(projectPath, worktreePath, entry) {
2691
2701
  const src = path12.join(projectPath, entry);
2692
2702
  const dest = path12.join(worktreePath, entry);
2693
2703
  await fs8.mkdir(path12.dirname(dest), { recursive: true });
2694
- 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
+ }
2695
2739
  }
2696
2740
  async function copySmallIgnoredFiles(projectPath, worktreePath) {
2697
2741
  const entries = await listIgnoredCopyEntries(projectPath);
@@ -3530,8 +3574,8 @@ async function taskCommand(argv) {
3530
3574
  }
3531
3575
 
3532
3576
  // packages/cli/src/host.ts
3533
- import fs16 from "fs";
3534
- import path20 from "path";
3577
+ import fs18 from "fs";
3578
+ import path22 from "path";
3535
3579
  import { randomBytes as randomBytes2 } from "crypto";
3536
3580
 
3537
3581
  // packages/core/src/events.ts
@@ -3557,190 +3601,701 @@ var EventBus = class {
3557
3601
  }
3558
3602
  };
3559
3603
 
3560
- // packages/core/src/service.ts
3561
- import fs14 from "fs";
3562
- import os5 from "os";
3563
- import path18 from "path";
3564
- import { randomUUID as randomUUID4 } from "crypto";
3604
+ // packages/core/src/library-builtins.ts
3605
+ import fs11 from "fs";
3606
+ import path15 from "path";
3565
3607
 
3566
- // packages/core/src/agent-models.ts
3567
- import { execFile as execFile5 } from "child_process";
3608
+ // packages/core/src/library.ts
3609
+ import { createHash } from "crypto";
3568
3610
  import fs10 from "fs";
3569
3611
  import os3 from "os";
3570
3612
  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");
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
+ };
3581
3631
  }
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
3588
- });
3589
- return stdout;
3632
+ async function libraryRoot() {
3633
+ return path14.join(getPlatform().userDataDir(), LIBRARY_DIR);
3590
3634
  }
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
- });
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;
3607
3640
  }
3608
- if (models.length === 0) throw new Error("Codex model catalog lists no selectable models");
3609
- return models;
3641
+ return result;
3610
3642
  }
3611
- function errorMessage(err) {
3612
- return err instanceof Error ? err.message : String(err);
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";
3613
3650
  }
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
- }
3651
+ function entryKey(kind, name) {
3652
+ return `${kind}/${name}`;
3653
+ }
3654
+ function indexPath(root) {
3655
+ return path14.join(root, INDEX_FILE);
3656
+ }
3657
+ function readIndexFile(root) {
3658
+ let raw;
3659
+ try {
3660
+ raw = fs10.readFileSync(indexPath(root), "utf8");
3661
+ } catch {
3662
+ return { version: 1, entries: {} };
3623
3663
  }
3624
- const cachePath = path14.join(opts.codexHome ?? defaultCodexHome(), "models_cache.json");
3625
3664
  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
- };
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 };
3669
+ }
3670
+ } catch {
3635
3671
  }
3672
+ return null;
3636
3673
  }
3637
-
3638
- // packages/core/src/github-auth.ts
3639
- init_env();
3640
- import { execFile as execFile6 } from "child_process";
3641
- import { promisify as promisify4 } from "util";
3642
- import * as nodePty3 from "node-pty";
3643
- var pexec4 = promisify4(execFile6);
3644
- var GITHUB_HOST = "github.com";
3645
- var DEVICE_URL = "https://github.com/login/device";
3646
- var activeLogin = null;
3647
- function stripAnsi(value) {
3648
- return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x07/g, "");
3649
- }
3650
- function normalizeCode(code2) {
3651
- return code2.replace(/\s+/g, "").toUpperCase();
3674
+ function readIndex(root) {
3675
+ return readIndexFile(root) ?? { version: 1, entries: {} };
3652
3676
  }
3653
- function extractDeviceCode(output) {
3654
- const cleaned = stripAnsi(output);
3655
- const explicit = cleaned.match(/one-time code:\s*([A-Z0-9-]+)/i);
3656
- if (explicit?.[1]) return normalizeCode(explicit[1]);
3657
- const fallback = cleaned.match(/\b([A-Z0-9]{4}-[A-Z0-9]{4})\b/i);
3658
- return fallback?.[1] ? normalizeCode(fallback[1]) : null;
3677
+ function indexIsCorrupt(root) {
3678
+ return readIndexFile(root) === null;
3659
3679
  }
3660
- function statusFromBody(body) {
3661
- const accounts = body.hosts?.[GITHUB_HOST] ?? [];
3662
- const active = accounts.find((account) => account.active) ?? accounts[0];
3663
- if (!active) return { installed: true, authenticated: false };
3664
- return {
3665
- installed: true,
3666
- authenticated: Boolean(active.login) && !active.error,
3667
- login: active.login,
3668
- gitProtocol: active.gitProtocol,
3669
- error: active.error
3670
- };
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);
3671
3688
  }
3672
- function errorMessage2(err) {
3673
- if (err instanceof Error) return err.message;
3674
- return String(err);
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;
3675
3695
  }
3676
- async function getGitHubCliAuthStatus() {
3696
+ function readSkillDescription(skillMdPath) {
3697
+ let content;
3677
3698
  try {
3678
- const { stdout } = await pexec4(
3679
- "gh",
3680
- ["auth", "status", "--hostname", GITHUB_HOST, "--json", "hosts"],
3681
- {
3682
- env: execEnv(),
3683
- timeout: 1e4,
3684
- windowsHide: true
3685
- }
3686
- );
3687
- const body = JSON.parse(stdout.toString());
3688
- return statusFromBody(body);
3689
- } catch (err) {
3690
- const maybeStdout = err.stdout;
3691
- if (maybeStdout) {
3692
- try {
3693
- const body = JSON.parse(maybeStdout.toString());
3694
- return statusFromBody(body);
3695
- } catch {
3696
- }
3697
- }
3698
- const message = errorMessage2(err);
3699
- return {
3700
- installed: !/ENOENT|not found/i.test(message),
3701
- authenticated: false,
3702
- error: message
3703
- };
3699
+ content = fs10.readFileSync(skillMdPath, "utf8");
3700
+ } catch {
3701
+ return void 0;
3704
3702
  }
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;
3705
3712
  }
3706
- async function logoutGitHubCli() {
3707
- const status = await getGitHubCliAuthStatus();
3708
- if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
3709
- if (!status.login) return status;
3710
- await pexec4(
3711
- "gh",
3712
- ["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
3713
- {
3714
- env: execEnv(),
3715
- timeout: 1e4,
3716
- windowsHide: true
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);
3717
3725
  }
3718
- );
3719
- return getGitHubCliAuthStatus();
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");
3720
3733
  }
3721
- function cancelGitHubCliLogin() {
3722
- if (!activeLogin) return;
3734
+ function builtinStatus(full, name, recorded, builtinDir) {
3723
3735
  try {
3724
- activeLogin.kill();
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;
3742
+ }
3743
+ }
3744
+ return { modified, updateAvailable };
3725
3745
  } catch {
3746
+ return { modified: true, updateAvailable: false };
3726
3747
  }
3727
- activeLogin = null;
3728
3748
  }
3729
- function startGitHubCliLogin(onEvent) {
3730
- cancelGitHubCliLogin();
3731
- onEvent({ type: "starting" });
3732
- let output = "";
3733
- let sentBrowserChoice = false;
3734
- let emittedCode = null;
3735
- let continuedAfterCode = false;
3736
- let completed = false;
3737
- let proc;
3738
- try {
3739
- proc = nodePty3.spawn(
3740
- "gh",
3741
- ["auth", "login", "--hostname", GITHUB_HOST, "--git-protocol", "https"],
3742
- {
3743
- name: "xterm-256color",
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;
3759
+ }
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
+ });
3786
+ }
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 fs16 from "fs";
4117
+ import os5 from "os";
4118
+ import path20 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",
3744
4299
  cwd: process.cwd(),
3745
4300
  env: { ...execEnv(), BROWSER: "true", TERM: "xterm-256color" },
3746
4301
  cols: 100,
@@ -3790,53 +4345,100 @@ function startGitHubCliLogin(onEvent) {
3790
4345
  }
3791
4346
 
3792
4347
  // packages/core/src/board-cli.ts
3793
- import fs11 from "fs";
3794
- import path15 from "path";
4348
+ import fs13 from "fs";
4349
+ import path17 from "path";
3795
4350
  import { pathToFileURL } from "url";
3796
4351
  async function boardCliLaunchInfo() {
3797
- const storeDir = path15.dirname(getStorePath());
4352
+ const storeDir = path17.dirname(getStorePath());
3798
4353
  const entry = getPlatform().cliEntry?.();
3799
- if (entry && fs11.existsSync(entry)) return { storeDir, cliPath: entry };
4354
+ if (entry && fs13.existsSync(entry)) return { storeDir, cliPath: entry };
3800
4355
  const root = getPlatform().sourceRoot();
3801
4356
  if (!root) 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 };
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 };
3805
4360
  return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
3806
4361
  }
3807
4362
 
3808
4363
  // packages/core/src/decisions.ts
3809
- import fs12 from "fs";
3810
- import path16 from "path";
4364
+ import fs14 from "fs";
4365
+ import path18 from "path";
3811
4366
  var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
3812
- var MAX_DECISION_BYTES = 256 * 1024;
3813
4367
  function decisionsKey(worktreePath, branch) {
3814
- return worktreePath ? path16.basename(worktreePath) : branch.replace(/\//g, "-");
4368
+ return worktreePath ? path18.basename(worktreePath) : branch.replace(/\//g, "-");
3815
4369
  }
3816
4370
  function decisionsPath(workspacePath, key) {
3817
- return path16.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
4371
+ return path18.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
3818
4372
  }
3819
- function readDecisions(workspacePath, key) {
3820
- const target = decisionsPath(workspacePath, key);
4373
+ function deleteDecisions(workspacePath, key) {
3821
4374
  try {
3822
- const stat = fs12.statSync(target);
3823
- if (!stat.isFile()) return { path: target, markdown: null };
3824
- const buf = fs12.readFileSync(target);
3825
- return {
3826
- path: target,
3827
- markdown: buf.subarray(0, MAX_DECISION_BYTES).toString("utf8"),
3828
- updatedAt: stat.mtimeMs,
3829
- ...buf.byteLength > MAX_DECISION_BYTES ? { truncated: true } : {}
3830
- };
4375
+ fs14.rmSync(decisionsPath(workspacePath, key), { force: true });
3831
4376
  } catch {
3832
- return { path: target, markdown: null };
3833
4377
  }
3834
4378
  }
3835
- function deleteDecisions(workspacePath, key) {
4379
+
4380
+ // packages/core/src/specs.ts
4381
+ import fs15 from "fs";
4382
+ import path19 from "path";
4383
+ var SPEC_FILE_NAME = "spec.md";
4384
+ var MAX_SPEC_BYTES = 256 * 1024;
4385
+ function isSpecPath(p) {
4386
+ return path19.posix.basename(p) === SPEC_FILE_NAME;
4387
+ }
4388
+ function clip2(buf) {
4389
+ return {
4390
+ markdown: buf.subarray(0, MAX_SPEC_BYTES).toString("utf8"),
4391
+ ...buf.byteLength > MAX_SPEC_BYTES ? { truncated: true } : {}
4392
+ };
4393
+ }
4394
+ async function readWorktreeSpecs(worktreePath, baseBranch) {
4395
+ const baseRef = await resolveBaseRef(worktreePath, baseBranch);
4396
+ const entries = await collectDiffEntries(worktreePath, baseRef);
4397
+ const specs = [];
4398
+ for (const entry of entries) {
4399
+ if (entry.status === "D" || !isSpecPath(entry.path)) continue;
4400
+ try {
4401
+ const full = path19.join(worktreePath, entry.path);
4402
+ const stat = fs15.statSync(full);
4403
+ if (!stat.isFile()) continue;
4404
+ specs.push({ path: entry.path, ...clip2(fs15.readFileSync(full)), updatedAt: stat.mtimeMs });
4405
+ } catch {
4406
+ }
4407
+ }
4408
+ return specs.sort((a, b) => a.path.localeCompare(b.path));
4409
+ }
4410
+ async function readBranchSpecs(projectPath, branch, baseBranch, changedPaths) {
4411
+ const ref = `refs/heads/${branch}`;
3836
4412
  try {
3837
- fs12.rmSync(decisionsPath(workspacePath, key), { force: true });
4413
+ await git(projectPath, ["rev-parse", "--verify", ref]);
3838
4414
  } catch {
4415
+ return [];
4416
+ }
4417
+ let paths = changedPaths;
4418
+ if (!paths) {
4419
+ const baseRef = await resolveBaseRef(projectPath, baseBranch);
4420
+ const names = await git(projectPath, [
4421
+ "diff",
4422
+ "--name-only",
4423
+ "--diff-filter=d",
4424
+ `${baseRef}...${ref}`
4425
+ ]).catch(() => "");
4426
+ paths = names.split("\n").map((l) => l.trim());
4427
+ }
4428
+ const specs = [];
4429
+ for (const p of paths.filter(isSpecPath)) {
4430
+ try {
4431
+ const content = await git(projectPath, ["show", `${ref}:${p}`]);
4432
+ const ct = await git(projectPath, ["log", "-1", "--format=%ct", ref, "--", p]);
4433
+ specs.push({
4434
+ path: p,
4435
+ ...clip2(Buffer.from(content, "utf8")),
4436
+ ...ct ? { updatedAt: Number(ct) * 1e3 } : {}
4437
+ });
4438
+ } catch {
4439
+ }
3839
4440
  }
4441
+ return specs.sort((a, b) => a.path.localeCompare(b.path));
3840
4442
  }
3841
4443
 
3842
4444
  // packages/core/src/chat-session.ts
@@ -4042,432 +4644,89 @@ ${attachmentLines.join("\n")}` : text;
4042
4644
  phaseType: "thinking",
4043
4645
  phaseSummary: "Thinking\u2026",
4044
4646
  phaseDetail: block.thinking,
4045
- done: true
4046
- });
4047
- } else if (block.type === "tool_use" && block.name) {
4048
- const toolId = block.id ?? "";
4049
- toolNames.set(toolId, block.name);
4050
- const { summary, detail } = summarizeToolUse(block.name, block.input ?? {});
4051
- pushPhase({
4052
- taskId,
4053
- id: `${taskId}-${seq++}`,
4054
- phaseType: "tool_use",
4055
- phaseSummary: summary,
4056
- phaseDetail: detail,
4057
- done: true
4058
- });
4059
- }
4060
- }
4061
- }
4062
- if (event.type === "user" && event.message?.content) {
4063
- for (const block of event.message.content) {
4064
- if (block.type === "tool_result") {
4065
- const toolName = toolNames.get(block.tool_use_id ?? "") ?? "Tool";
4066
- const raw2 = block.content;
4067
- const detail = typeof raw2 === "string" ? raw2 : Array.isArray(raw2) ? raw2.map((c) => c.text ?? "").join("\n") : "";
4068
- pushPhase({
4069
- taskId,
4070
- id: `${taskId}-${seq++}`,
4071
- phaseType: "tool_result",
4072
- phaseSummary: `Got result from \`${toolName}\``,
4073
- phaseDetail: detail,
4074
- done: true
4075
- });
4076
- }
4077
- }
4078
- }
4079
- if (event.type === "result" && event.result) {
4080
- const remaining = event.result.slice(accumulated.length);
4081
- if (remaining) push({ taskId, delta: remaining, done: false });
4082
- accumulated = event.result;
4083
- }
4084
- } catch {
4085
- }
4086
- }
4087
- });
4088
- proc.stderr.on("data", (raw) => {
4089
- const msg = raw.toString().trim();
4090
- if (msg) push({ taskId, delta: "", done: false, error: msg });
4091
- });
4092
- proc.on("close", (code2) => {
4093
- const assistantText = accumulated || "\uFF08\u7121\u56DE\u61C9\uFF09";
4094
- appendMessage(taskId, {
4095
- id: randomUUID3().slice(0, 8),
4096
- role: "assistant",
4097
- text: assistantText,
4098
- ts: Date.now()
4099
- });
4100
- const error = code2 !== 0 ? `Process exited with code ${code2}` : void 0;
4101
- push({ taskId, delta: assistantText, done: true, error });
4102
- });
4103
- return () => {
4104
- try {
4105
- proc.kill("SIGTERM");
4106
- } catch {
4107
- }
4108
- };
4109
- }
4110
- var activeProcs = /* @__PURE__ */ new Map();
4111
- function cancelChatSend(taskId) {
4112
- activeProcs.get(taskId)?.();
4113
- activeProcs.delete(taskId);
4114
- }
4115
- function cancelAllChatSends() {
4116
- activeProcs.forEach((kill) => kill());
4117
- activeProcs.clear();
4118
- }
4119
- function startChatSend(opts, sender) {
4120
- cancelChatSend(opts.taskId);
4121
- const kill = sendChatMessage(opts, sender);
4122
- activeProcs.set(opts.taskId, kill);
4123
- }
4124
-
4125
- // packages/core/src/service.ts
4126
- init_attachments();
4127
-
4128
- // packages/core/src/library.ts
4129
- import fs13 from "fs";
4130
- import os4 from "os";
4131
- import path17 from "path";
4132
- var LIBRARY_DIR = "library";
4133
- var LIBRARY_KINDS = ["skill", "prompt", "script"];
4134
- var KIND_DIR = {
4135
- skill: "skills",
4136
- prompt: "prompts",
4137
- script: "scripts"
4138
- };
4139
- var INDEX_FILE = "index.json";
4140
- var MAX_NAME_BYTES = 100;
4141
- function libraryPaths(root) {
4142
- return {
4143
- root,
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")
4149
- };
4150
- }
4151
- async function libraryRoot() {
4152
- return path17.join(getPlatform().userDataDir(), LIBRARY_DIR);
4153
- }
4154
- function truncateUtf82(value, maxBytes) {
4155
- let result = "";
4156
- for (const character of value) {
4157
- if (Buffer.byteLength(result + character) > maxBytes) break;
4158
- result += character;
4159
- }
4160
- return result;
4161
- }
4162
- function safeLibraryName(name) {
4163
- const basename2 = name.replace(/\\/g, "/").split("/").pop() ?? "";
4164
- const cleaned = truncateUtf82(
4165
- basename2.replace(/[\u0000-\u001f\u007f]/g, "").trim(),
4166
- MAX_NAME_BYTES
4167
- );
4168
- return cleaned && cleaned !== "." && cleaned !== ".." ? cleaned : "untitled";
4169
- }
4170
- function entryKey(kind, name) {
4171
- return `${kind}/${name}`;
4172
- }
4173
- function indexPath(root) {
4174
- return path17.join(root, INDEX_FILE);
4175
- }
4176
- function readIndex(root) {
4177
- try {
4178
- const parsed = JSON.parse(fs13.readFileSync(indexPath(root), "utf8"));
4179
- if (parsed && typeof parsed === "object" && "entries" in parsed) {
4180
- const entries = parsed.entries;
4181
- if (entries && typeof entries === "object") return { version: 1, entries };
4182
- }
4183
- } catch {
4184
- }
4185
- return { version: 1, entries: {} };
4186
- }
4187
- function writeIndex(root, index) {
4188
- fs13.mkdirSync(root, { recursive: true });
4189
- fs13.writeFileSync(indexPath(root), `${JSON.stringify(index, null, 2)}
4190
- `);
4191
- }
4192
- function ensureLibrary(root) {
4193
- const paths = libraryPaths(root);
4194
- for (const kind of LIBRARY_KINDS) {
4195
- fs13.mkdirSync(path17.join(root, KIND_DIR[kind]), { recursive: true });
4196
- }
4197
- return paths;
4198
- }
4199
- function readSkillDescription(skillMdPath) {
4200
- let content;
4201
- try {
4202
- content = fs13.readFileSync(skillMdPath, "utf8");
4203
- } catch {
4204
- return void 0;
4205
- }
4206
- const lines = content.split(/\r?\n/);
4207
- if (lines[0]?.trim() !== "---") return void 0;
4208
- for (let i = 1; i < lines.length; i += 1) {
4209
- const line = lines[i];
4210
- if (line.trim() === "---") break;
4211
- const match = /^description:\s*(.+)$/.exec(line);
4212
- if (match) return match[1].trim().replace(/^["']|["']$/g, "");
4213
- }
4214
- return void 0;
4215
- }
4216
- function entryPath(root, kind, name) {
4217
- return path17.join(root, KIND_DIR[kind], name);
4218
- }
4219
- function listLibrary(root) {
4220
- const index = readIndex(root);
4221
- const entries = [];
4222
- for (const kind of LIBRARY_KINDS) {
4223
- const dir = path17.join(root, KIND_DIR[kind]);
4224
- let dirents;
4225
- try {
4226
- dirents = fs13.readdirSync(dir, { withFileTypes: true });
4227
- } catch {
4228
- continue;
4229
- }
4230
- for (const dirent of dirents) {
4231
- if (dirent.name.startsWith(".")) continue;
4232
- const wantsDirectory = kind === "skill";
4233
- let stat;
4234
- try {
4235
- stat = fs13.statSync(path17.join(dir, dirent.name));
4647
+ done: true
4648
+ });
4649
+ } else if (block.type === "tool_use" && block.name) {
4650
+ const toolId = block.id ?? "";
4651
+ toolNames.set(toolId, block.name);
4652
+ const { summary, detail } = summarizeToolUse(block.name, block.input ?? {});
4653
+ pushPhase({
4654
+ taskId,
4655
+ id: `${taskId}-${seq++}`,
4656
+ phaseType: "tool_use",
4657
+ phaseSummary: summary,
4658
+ phaseDetail: detail,
4659
+ done: true
4660
+ });
4661
+ }
4662
+ }
4663
+ }
4664
+ if (event.type === "user" && event.message?.content) {
4665
+ for (const block of event.message.content) {
4666
+ if (block.type === "tool_result") {
4667
+ const toolName = toolNames.get(block.tool_use_id ?? "") ?? "Tool";
4668
+ const raw2 = block.content;
4669
+ const detail = typeof raw2 === "string" ? raw2 : Array.isArray(raw2) ? raw2.map((c) => c.text ?? "").join("\n") : "";
4670
+ pushPhase({
4671
+ taskId,
4672
+ id: `${taskId}-${seq++}`,
4673
+ phaseType: "tool_result",
4674
+ phaseSummary: `Got result from \`${toolName}\``,
4675
+ phaseDetail: detail,
4676
+ done: true
4677
+ });
4678
+ }
4679
+ }
4680
+ }
4681
+ if (event.type === "result" && event.result) {
4682
+ const remaining = event.result.slice(accumulated.length);
4683
+ if (remaining) push({ taskId, delta: remaining, done: false });
4684
+ accumulated = event.result;
4685
+ }
4236
4686
  } catch {
4237
- continue;
4238
4687
  }
4239
- if (stat.isDirectory() !== wantsDirectory) continue;
4240
- const name = dirent.name;
4241
- const full = path17.join(dir, name);
4242
- if (wantsDirectory && !fs13.existsSync(path17.join(full, "SKILL.md"))) continue;
4243
- const key = entryKey(kind, name);
4244
- const record = index.entries[key] ?? {};
4245
- entries.push({
4246
- key,
4247
- kind,
4248
- name,
4249
- path: full,
4250
- description: wantsDirectory ? readSkillDescription(path17.join(full, "SKILL.md")) : record.description,
4251
- sourcePath: record.sourcePath,
4252
- importedAt: record.importedAt,
4253
- enabled: record.enabled !== false
4254
- });
4255
- }
4256
- }
4257
- return entries.sort((a, b) => a.key.localeCompare(b.key));
4258
- }
4259
- function annotate(root, key, patch) {
4260
- const index = readIndex(root);
4261
- index.entries[key] = { ...index.entries[key], ...patch };
4262
- writeIndex(root, index);
4263
- }
4264
- function importEntry(root, kind, sourcePath) {
4265
- ensureLibrary(root);
4266
- const resolved = path17.resolve(sourcePath);
4267
- const stat = fs13.statSync(resolved);
4268
- if (kind === "skill") {
4269
- if (!stat.isDirectory()) throw new Error("skill \u5FC5\u9808\u662F\u542B SKILL.md \u7684\u76EE\u9304");
4270
- if (!fs13.existsSync(path17.join(resolved, "SKILL.md"))) {
4271
- throw new Error("\u76EE\u9304\u5167\u627E\u4E0D\u5230 SKILL.md");
4272
4688
  }
4273
- } else if (!stat.isFile()) {
4274
- throw new Error(`${kind} \u5FC5\u9808\u662F\u55AE\u4E00\u6A94\u6848`);
4275
- }
4276
- const name = safeLibraryName(path17.basename(resolved));
4277
- const target = entryPath(root, kind, name);
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);
4281
- annotate(root, entryKey(kind, name), {
4282
- sourcePath: resolved,
4283
- importedAt: Date.now(),
4284
- enabled: true
4285
4689
  });
4286
- return requireEntry(root, kind, name);
4287
- }
4288
- function createEntry(root, kind, rawName, content, description) {
4289
- ensureLibrary(root);
4290
- const name = safeLibraryName(rawName);
4291
- const target = entryPath(root, kind, name);
4292
- if (fs13.existsSync(target)) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u9805\u76EE\uFF1A${name}`);
4293
- if (kind === "skill") {
4294
- fs13.mkdirSync(target, { recursive: true });
4295
- fs13.writeFileSync(path17.join(target, "SKILL.md"), content);
4296
- } else {
4297
- fs13.writeFileSync(target, content);
4298
- if (kind === "script") fs13.chmodSync(target, 493);
4299
- }
4300
- annotate(root, entryKey(kind, name), {
4301
- importedAt: Date.now(),
4302
- enabled: true,
4303
- description
4690
+ proc.stderr.on("data", (raw) => {
4691
+ const msg = raw.toString().trim();
4692
+ if (msg) push({ taskId, delta: "", done: false, error: msg });
4304
4693
  });
4305
- return requireEntry(root, kind, name);
4306
- }
4307
- function setEntryDescription(root, kind, name, description) {
4308
- annotate(root, entryKey(kind, name), { description });
4309
- }
4310
- function updateEntry(root, kind, name, content) {
4311
- const target = entryPath(root, kind, name);
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);
4314
- return requireEntry(root, kind, name);
4315
- }
4316
- function readEntryContent(root, kind, name) {
4317
- const target = entryPath(root, kind, name);
4318
- return fs13.readFileSync(kind === "skill" ? path17.join(target, "SKILL.md") : target, "utf8");
4319
- }
4320
- function deleteEntry(root, kind, name) {
4321
- fs13.rmSync(entryPath(root, kind, name), { recursive: true, force: true });
4322
- const index = readIndex(root);
4323
- delete index.entries[entryKey(kind, name)];
4324
- writeIndex(root, index);
4325
- }
4326
- function setEntryEnabled(root, kind, name, enabled) {
4327
- annotate(root, entryKey(kind, name), { enabled });
4328
- }
4329
- function requireEntry(root, kind, name) {
4330
- const found2 = listLibrary(root).find((e) => e.key === entryKey(kind, name));
4331
- if (!found2) throw new Error(`\u9805\u76EE\u5BEB\u5165\u5F8C\u8B80\u4E0D\u56DE\u4F86\uFF1A${kind}/${name}`);
4332
- return found2;
4333
- }
4334
- function projectSkillsDir(worktreePath) {
4335
- return path17.join(worktreePath, ".claude", "skills");
4336
- }
4337
- function skillNamesIn(dir) {
4338
- try {
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();
4340
- } catch {
4341
- return [];
4342
- }
4343
- }
4344
- var PLUGIN_MANIFEST_DIR = ".claude-plugin";
4345
- function buildPluginDir(root) {
4346
- const paths = libraryPaths(root);
4347
- const enabled = listLibrary(root).filter((e) => e.kind === "skill" && e.enabled);
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"),
4353
- `${JSON.stringify(
4354
- {
4355
- name: "vibeflow-library",
4356
- version: "1.0.0",
4357
- description: "Skills delivered by VibeFlow for this task."
4358
- },
4359
- null,
4360
- 2
4361
- )}
4362
- `
4363
- );
4364
- for (const entry of enabled) {
4365
- fs13.cpSync(entry.path, path17.join(paths.plugin, "skills", entry.name), {
4366
- recursive: true,
4367
- dereference: true
4694
+ proc.on("close", (code2) => {
4695
+ const assistantText = accumulated || "\uFF08\u7121\u56DE\u61C9\uFF09";
4696
+ appendMessage(taskId, {
4697
+ id: randomUUID3().slice(0, 8),
4698
+ role: "assistant",
4699
+ text: assistantText,
4700
+ ts: Date.now()
4368
4701
  });
4369
- }
4370
- return { pluginDir: paths.plugin, skills: enabled.map((e) => e.name) };
4371
- }
4372
- function linkOrSkip(target, linkPath) {
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 })
4378
- ] : [
4379
- () => fs13.symlinkSync(source, linkPath),
4380
- () => fs13.linkSync(source, linkPath),
4381
- () => fs13.copyFileSync(source, linkPath)
4382
- ];
4383
- for (const attempt of attempts) {
4702
+ const error = code2 !== 0 ? `Process exited with code ${code2}` : void 0;
4703
+ push({ taskId, delta: assistantText, done: true, error });
4704
+ });
4705
+ return () => {
4384
4706
  try {
4385
- fs13.rmSync(linkPath, { recursive: true, force: true });
4386
- attempt();
4387
- return true;
4707
+ proc.kill("SIGTERM");
4388
4708
  } catch {
4389
4709
  }
4390
- }
4391
- return false;
4392
- }
4393
- function buildCodexHome(root, worktreePath, userCodexHome = path17.join(os4.homedir(), ".codex")) {
4394
- const paths = libraryPaths(root);
4395
- const skillsDir = path17.join(paths.codexHome, "skills");
4396
- fs13.rmSync(skillsDir, { recursive: true, force: true });
4397
- fs13.mkdirSync(skillsDir, { recursive: true });
4398
- for (const file of ["auth.json", "config.toml"]) {
4399
- linkOrSkip(path17.join(userCodexHome, file), path17.join(paths.codexHome, file));
4400
- }
4401
- const result = {
4402
- codexHome: paths.codexHome,
4403
- project: [],
4404
- user: [],
4405
- library: [],
4406
- shadowed: []
4407
4710
  };
4408
- const claimed = /* @__PURE__ */ new Map();
4409
- const projectDir = worktreePath ? projectSkillsDir(worktreePath) : void 0;
4410
- if (projectDir) {
4411
- for (const name of skillNamesIn(projectDir)) {
4412
- if (linkOrSkip(path17.join(projectDir, name), path17.join(skillsDir, name))) {
4413
- result.project.push(name);
4414
- claimed.set(name, "project");
4415
- }
4416
- }
4417
- }
4418
- const userDir = path17.join(userCodexHome, "skills");
4419
- for (const name of skillNamesIn(userDir)) {
4420
- const owner = claimed.get(name);
4421
- if (owner) {
4422
- result.shadowed.push({ name, keptFrom: owner, dropped: "user" });
4423
- continue;
4424
- }
4425
- if (linkOrSkip(path17.join(userDir, name), path17.join(skillsDir, name))) {
4426
- result.user.push(name);
4427
- claimed.set(name, "user");
4428
- }
4429
- }
4430
- for (const entry of listLibrary(root)) {
4431
- if (entry.kind !== "skill" || !entry.enabled) continue;
4432
- const owner = claimed.get(entry.name);
4433
- if (owner) {
4434
- result.shadowed.push({ name: entry.name, keptFrom: owner, dropped: "library" });
4435
- continue;
4436
- }
4437
- fs13.cpSync(entry.path, path17.join(skillsDir, entry.name), {
4438
- recursive: true,
4439
- dereference: true
4440
- });
4441
- result.library.push(entry.name);
4442
- }
4443
- return result;
4444
4711
  }
4445
- function libraryLaunchInfoAt(root, worktreePath, userCodexHome) {
4446
- const entries = listLibrary(root).filter((e) => e.enabled);
4447
- if (entries.length === 0) return null;
4448
- const { pluginDir } = buildPluginDir(root);
4449
- const { codexHome } = buildCodexHome(root, worktreePath, userCodexHome);
4450
- const scripts = entries.filter((e) => e.kind === "script").map((e) => ({ name: e.name, path: e.path, description: e.description }));
4451
- const promptBodies = entries.filter((e) => e.kind === "prompt").map((e) => readEntryContent(root, "prompt", e.name).trim()).filter(Boolean);
4452
- const lines = [...promptBodies];
4453
- if (scripts.length > 0) {
4454
- lines.push(
4455
- "\u53EF\u7528\u7684\u5DE5\u5177\u8173\u672C\uFF08\u9700\u8981\u6642\u81EA\u884C\u57F7\u884C\uFF0C\u4E0D\u9700\u8981\u5C31\u5225\u8DD1\uFF09\uFF1A",
4456
- ...scripts.map((s) => `- ${s.path}${s.description ? ` \u2014 ${s.description}` : ""}`)
4457
- );
4458
- }
4459
- return {
4460
- pluginDir,
4461
- codexHome,
4462
- libraryDir: root,
4463
- scripts,
4464
- promptText: lines.join("\n\n")
4465
- };
4712
+ var activeProcs = /* @__PURE__ */ new Map();
4713
+ function cancelChatSend(taskId) {
4714
+ activeProcs.get(taskId)?.();
4715
+ activeProcs.delete(taskId);
4466
4716
  }
4467
- async function libraryLaunchInfo(worktreePath) {
4468
- return libraryLaunchInfoAt(await libraryRoot(), worktreePath);
4717
+ function cancelAllChatSends() {
4718
+ activeProcs.forEach((kill) => kill());
4719
+ activeProcs.clear();
4720
+ }
4721
+ function startChatSend(opts, sender) {
4722
+ cancelChatSend(opts.taskId);
4723
+ const kill = sendChatMessage(opts, sender);
4724
+ activeProcs.set(opts.taskId, kill);
4469
4725
  }
4470
4726
 
4727
+ // packages/core/src/service.ts
4728
+ init_attachments();
4729
+
4471
4730
  // packages/core/src/launch.ts
4472
4731
  var ARTIFACTS_DIR_SUFFIX2 = ".artifacts";
4473
4732
  var SCRATCH_DIR_NAME2 = "scratch";
@@ -4493,22 +4752,6 @@ function buildArtifactPrompt(artifactsDir = ARTIFACTS_FALLBACK_DIR2) {
4493
4752
  "5. Artifact \u8CC7\u6599\u593E\u4F4D\u65BC worktree \u4E4B\u5916\uFF1B\u4E0D\u8981\u628A\u5176\u4E2D\u5167\u5BB9\u52A0\u5165 git commit\uFF0C\u4E5F\u4E0D\u8981\u628A\u6700\u7D42\u4EA4\u4ED8\u7269\u53EA\u7559\u5728\u9019\u88E1\u3002"
4494
4753
  ].join("\n");
4495
4754
  }
4496
- var DECISIONS_FILE_SUFFIX2 = ".DECISIONS.md";
4497
- function taskDecisionsPath(worktreePath, workspacePath) {
4498
- if (!worktreePath || !workspacePath) return null;
4499
- return `${toShellPath(workspacePath)}/${pathBasename(worktreePath)}${DECISIONS_FILE_SUFFIX2}`;
4500
- }
4501
- function buildDecisionPrompt(decisionsPath2) {
4502
- return [
4503
- "\u6C7A\u7B56\u66F8\uFF08\u672C\u6B21 session \u5168\u7A0B\u9069\u7528\uFF09\uFF1A",
4504
- `1. \u672C\u4EFB\u52D9\u7684\u6C7A\u7B56\u66F8\u662F ${decisionsPath2}\uFF1B\u505A\u51FA\u65B0\u6C7A\u7B56\u3001\u6216\u8ABF\u6574\u65E2\u6709\u6C7A\u7B56\u6642\uFF0C\u7576\u4E0B\u5C31\u66F4\u65B0\u5B83\uFF08\u4E0D\u5B58\u5728\u8ACB\u5EFA\u7ACB\uFF09\u3002`,
4505
- "2. \u53EA\u5BEB\u300C\u9019\u6B21\u65B0\u589E\u4E86\u4EC0\u9EBC\u6C7A\u7B56\u3001\u8ABF\u6574\u4E86\u4EC0\u9EBC\u65E2\u6709\u6C7A\u7B56\u300D\u8207\u7406\u7531\u3002\u9700\u6C42\u8986\u8FF0\u3001\u9032\u5EA6\u56DE\u5831\u3001\u5BE6\u4F5C\u6D41\u6C34\u5E33\u3001\u672C\u4F86\u5C31\u6210\u7ACB\u7684\u6163\u4F8B\u90FD\u4E0D\u8981\u5BEB\u3002",
4506
- "3. \u6BCF\u5247\u6C7A\u7B56\u4E00\u500B `## <\u4E00\u53E5\u8A71\u8B1B\u5B8C\u7684\u6C7A\u7B56>` \u6BB5\u843D\uFF0C\u5E95\u4E0B 2-4 \u884C\u4EA4\u4EE3\uFF1A\u70BA\u4EC0\u9EBC\u9019\u6A23\u9078\u3001\u5426\u6C7A\u4E86\u4EC0\u9EBC\u66FF\u4EE3\u65B9\u6848\u3001\u8AB0\u4E4B\u5F8C\u6703\u88AB\u9019\u500B\u6C7A\u7B56\u7D81\u4F4F\u3002",
4507
- "4. \u8981\u8AAA\u660E\u6D41\u7A0B\u3001\u72C0\u614B\u8F49\u63DB\u6216\u6A21\u7D44\u95DC\u4FC2\u6642\uFF0C\u7528 mermaid fence \u756B\u5716\uFF0CVibeFlow \u6703\u76F4\u63A5\u6E32\u67D3\u3002",
4508
- "5. \u9019\u6B21\u6C92\u6709\u4EFB\u4F55\u6C7A\u7B56\u5C31\u4E0D\u8981\u5EFA\u7ACB\u9019\u500B\u6A94\u6848\uFF1B\u5BE7\u53EF\u7559\u767D\uFF0C\u4E0D\u8981\u6E4A\u5167\u5BB9\u3002",
4509
- "6. \u7528\u7E41\u9AD4\u4E2D\u6587\u66F8\u5BEB\uFF0C\u7A0B\u5F0F\u8B58\u5225\u5B57\u4FDD\u7559\u82F1\u6587\u3002"
4510
- ].join("\n");
4511
- }
4512
4755
  var AUTO_PERMISSION_MODE = "auto";
4513
4756
  function claudePermissionFlag(autoMode) {
4514
4757
  return autoMode ? ` --permission-mode ${AUTO_PERMISSION_MODE}` : "";
@@ -4637,11 +4880,9 @@ function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
4637
4880
  const model = taskModel(task);
4638
4881
  const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? ARTIFACTS_FALLBACK_DIR2;
4639
4882
  const builtInPrompt = buildArtifactPrompt(artifactsDir);
4640
- const decisionsPath2 = taskDecisionsPath(task.worktreePath, workspacePath);
4641
- const decisionPrompt = decisionsPath2 ? buildDecisionPrompt(decisionsPath2) : "";
4642
4883
  const libraryPrompt = opts?.library?.promptText?.trim();
4643
4884
  const customPrompt = resolveSystemPrompt(systemPrompt);
4644
- const sys = [builtInPrompt, decisionPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
4885
+ const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
4645
4886
  const includeTaskPrompt = opts?.includeTaskPrompt !== false;
4646
4887
  const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
4647
4888
  const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
@@ -4750,13 +4991,13 @@ function createCore({ sessions, bus, version }) {
4750
4991
  }
4751
4992
  function claudeSessionFile(cwd, sessionId) {
4752
4993
  const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
4753
- return path18.join(os5.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
4994
+ return path20.join(os5.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
4754
4995
  }
4755
4996
  async function removeTaskWorktree(task) {
4756
4997
  if (!task.projectPath || !task.worktreePath) return;
4757
4998
  const branch = task.branch || fallbackBranchName(task.id);
4758
4999
  await removeWorktree(task.projectPath, task.worktreePath);
4759
- deleteArtifacts(task.workspacePath ?? path18.dirname(task.worktreePath), task.worktreePath);
5000
+ deleteArtifacts(task.workspacePath ?? path20.dirname(task.worktreePath), task.worktreePath);
4760
5001
  await deleteBranch(task.projectPath, branch);
4761
5002
  }
4762
5003
  const handlers = {
@@ -4795,8 +5036,8 @@ function createCore({ sessions, bus, version }) {
4795
5036
  "projects:listRecent": () => listRecentProjects(getStore()),
4796
5037
  // `vibeflow open <path>` while a host runs: only the host writes the store.
4797
5038
  "projects:record": (projectPath) => {
4798
- const p = path18.resolve(str(projectPath, "projectPath"));
4799
- if (!fs14.existsSync(p) || !fs14.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
5039
+ const p = path20.resolve(str(projectPath, "projectPath"));
5040
+ if (!fs16.existsSync(p) || !fs16.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
4800
5041
  recordRecentProject(getStore(), p);
4801
5042
  return listRecentProjects(getStore());
4802
5043
  },
@@ -4828,7 +5069,7 @@ function createCore({ sessions, bus, version }) {
4828
5069
  },
4829
5070
  // Discard everything the last run left behind — the PTY session, the
4830
5071
  // sub-agent timeline, the conversation id, the code it changed, its artifacts
4831
- // and its decision record — so the next launch starts over as if the card
5072
+ // and its legacy decision record — so the next launch starts over as if the card
4832
5073
  // were new. Launching again is the caller's decision.
4833
5074
  "vibeflow:resetTaskRun": async (taskId) => {
4834
5075
  const task = requireTask(taskId);
@@ -4836,7 +5077,7 @@ function createCore({ sessions, bus, version }) {
4836
5077
  if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
4837
5078
  await teardownTask(task.id);
4838
5079
  await resetWorktreeToBase(task.worktreePath, task.baseBranch);
4839
- const workspacePath = task.workspacePath ?? path18.dirname(task.worktreePath);
5080
+ const workspacePath = task.workspacePath ?? path20.dirname(task.worktreePath);
4840
5081
  deleteArtifacts(workspacePath, task.worktreePath);
4841
5082
  deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
4842
5083
  resetSubAgents(task.worktreePath);
@@ -4875,9 +5116,9 @@ function createCore({ sessions, bus, version }) {
4875
5116
  if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
4876
5117
  await teardownTask(existing.id);
4877
5118
  await removeTaskWorktree(existing);
4878
- const projectName = path18.basename(nextProject);
5119
+ const projectName = path20.basename(nextProject);
4879
5120
  const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
4880
- await fs14.promises.mkdir(workspacePath, { recursive: true });
5121
+ await fs16.promises.mkdir(workspacePath, { recursive: true });
4881
5122
  const result = await provisionWorktree(
4882
5123
  nextProject,
4883
5124
  workspacePath,
@@ -4965,7 +5206,7 @@ function createCore({ sessions, bus, version }) {
4965
5206
  "claude:sessionExists": (taskId) => {
4966
5207
  const task = requireTask(taskId);
4967
5208
  if (!task.worktreePath) return false;
4968
- return fs14.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
5209
+ return fs16.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
4969
5210
  },
4970
5211
  "sessions:list": async () => {
4971
5212
  const keys = await sessions.list();
@@ -5012,16 +5253,19 @@ function createCore({ sessions, bus, version }) {
5012
5253
  const task = requireTask(taskId);
5013
5254
  if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
5014
5255
  const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
5015
- if (!fs14.existsSync(dir)) return "artifacts directory does not exist yet";
5256
+ if (!fs16.existsSync(dir)) return "artifacts directory does not exist yet";
5016
5257
  return getPlatform().openPath(dir);
5017
5258
  },
5018
- "task:getDecisions": (taskId) => {
5259
+ "task:getSpecs": async (taskId) => {
5019
5260
  const task = requireTask(taskId);
5020
- if (!task.workspacePath) return null;
5021
- return readDecisions(task.workspacePath, decisionsKey(task.worktreePath, task.branch));
5261
+ const base = task.baseBranch ?? "main";
5262
+ if (task.worktreePath) return readWorktreeSpecs(task.worktreePath, base);
5263
+ if (!task.projectPath) return [];
5264
+ const changed = task.outcome?.files.filter((f) => f.status !== "D").map((f) => f.path);
5265
+ return readBranchSpecs(task.projectPath, task.branch, base, changed);
5022
5266
  },
5023
5267
  "board:getCliLaunchInfo": () => boardCliLaunchInfo(),
5024
- "library:list": async () => listLibrary(await libraryRoot()),
5268
+ "library:list": async () => listLibrary(await libraryRoot(), builtinSkillsDir()),
5025
5269
  // The source path was picked by the user (native picker or typed path).
5026
5270
  "library:import": async (payload) => {
5027
5271
  const p = obj(payload, "payload");
@@ -5060,6 +5304,11 @@ function createCore({ sessions, bus, version }) {
5060
5304
  deleteEntry(await libraryRoot(), libraryKind(p.kind), str(p.name, "name"));
5061
5305
  return true;
5062
5306
  },
5307
+ "library:restoreBuiltin": async (payload) => {
5308
+ const p = obj(payload, "payload");
5309
+ return restoreBuiltinSkill(await libraryRoot(), builtinSkillsDir(), str(p.name, "name"));
5310
+ },
5311
+ "library:removedBuiltins": async () => removedBuiltinSkills(await libraryRoot(), builtinSkillsDir()),
5063
5312
  // Approve: commit everything in the worktree and push the branch upstream.
5064
5313
  "git:approve": async (payload) => {
5065
5314
  const p = obj(payload, "payload");
@@ -5165,9 +5414,9 @@ function createCore({ sessions, bus, version }) {
5165
5414
  // Watch the containing directory, not the file: JsonStore renames a temp
5166
5415
  // file over the target and so replaces the inode on every save.
5167
5416
  watchStore() {
5168
- const file = path18.basename(getStorePath());
5417
+ const file = path20.basename(getStorePath());
5169
5418
  let debounce = null;
5170
- storeWatcher = fs14.watch(path18.dirname(getStorePath()), (_event, filename) => {
5419
+ storeWatcher = fs16.watch(path20.dirname(getStorePath()), (_event, filename) => {
5171
5420
  if (filename !== file) return;
5172
5421
  if (debounce) clearTimeout(debounce);
5173
5422
  debounce = setTimeout(() => {
@@ -5199,9 +5448,9 @@ function createSessionBackend(sink, choice) {
5199
5448
  }
5200
5449
 
5201
5450
  // packages/core/src/web-server.ts
5202
- import fs15 from "fs";
5451
+ import fs17 from "fs";
5203
5452
  import http from "http";
5204
- import path19 from "path";
5453
+ import path21 from "path";
5205
5454
  import { timingSafeEqual } from "crypto";
5206
5455
  import { WebSocketServer } from "ws";
5207
5456
  var TOKEN_COOKIE = "vf_token";
@@ -5254,14 +5503,14 @@ function resolveStatic(root, urlPath) {
5254
5503
  return null;
5255
5504
  }
5256
5505
  if (decoded.includes("\0")) return null;
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`];
5506
+ const rel = path21.posix.normalize(decoded).replace(/^\/+/, "");
5507
+ const base = path21.resolve(root);
5508
+ const candidates = [rel, path21.posix.join(rel, "index.html"), `${rel}.html`];
5260
5509
  for (const c of candidates) {
5261
- const full = path19.resolve(base, c);
5262
- if (full !== base && !full.startsWith(base + path19.sep)) return null;
5510
+ const full = path21.resolve(base, c);
5511
+ if (full !== base && !full.startsWith(base + path21.sep)) return null;
5263
5512
  try {
5264
- if (fs15.statSync(full).isFile()) return full;
5513
+ if (fs17.statSync(full).isFile()) return full;
5265
5514
  } catch {
5266
5515
  }
5267
5516
  }
@@ -5322,7 +5571,7 @@ async function startWebServer(options) {
5322
5571
  send(res, 404, "Not found");
5323
5572
  return;
5324
5573
  }
5325
- const ext = path19.extname(file).toLowerCase();
5574
+ const ext = path21.extname(file).toLowerCase();
5326
5575
  const immutable = url.pathname.startsWith("/_next/static/");
5327
5576
  res.writeHead(200, {
5328
5577
  "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
@@ -5335,7 +5584,7 @@ async function startWebServer(options) {
5335
5584
  res.end();
5336
5585
  return;
5337
5586
  }
5338
- fs15.createReadStream(file).pipe(res);
5587
+ fs17.createReadStream(file).pipe(res);
5339
5588
  }
5340
5589
  const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
5341
5590
  function reject(socket, status, reason) {
@@ -5460,6 +5709,7 @@ async function startHost(options) {
5460
5709
  return startHost(options);
5461
5710
  }
5462
5711
  try {
5712
+ await syncBuiltinsAtStartup();
5463
5713
  const bus = new EventBus();
5464
5714
  const sessions = createSessionBackend(bus.sink(), {
5465
5715
  prefer: options.backend,
@@ -5506,7 +5756,7 @@ function loginUrl(lock, pathname = "/home/") {
5506
5756
  return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
5507
5757
  }
5508
5758
  function isWebDir(dir) {
5509
- return fs16.existsSync(path20.join(dir, "home", "index.html"));
5759
+ return fs18.existsSync(path22.join(dir, "home", "index.html"));
5510
5760
  }
5511
5761
 
5512
5762
  // packages/cli/src/remote.ts
@@ -5623,8 +5873,8 @@ function connectToHost(lock) {
5623
5873
  }
5624
5874
 
5625
5875
  // packages/cli/src/doctor.ts
5626
- import fs17 from "fs";
5627
- import path21 from "path";
5876
+ import fs19 from "fs";
5877
+ import path23 from "path";
5628
5878
  import { execFile as execFile7 } from "child_process";
5629
5879
  import { promisify as promisify5 } from "util";
5630
5880
  init_env();
@@ -5764,10 +6014,10 @@ async function runDoctor(options) {
5764
6014
  });
5765
6015
  }
5766
6016
  try {
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);
6017
+ fs19.mkdirSync(options.userDataDir, { recursive: true });
6018
+ const probe = path23.join(options.userDataDir, `.doctor-${process.pid}`);
6019
+ fs19.writeFileSync(probe, "");
6020
+ fs19.rmSync(probe);
5771
6021
  checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
5772
6022
  } catch (err) {
5773
6023
  checks.push({
@@ -5821,24 +6071,24 @@ Options:
5821
6071
  `.trim();
5822
6072
  function detectLayout() {
5823
6073
  const file = fileURLToPath(import.meta.url);
5824
- const here = path24.dirname(file);
5825
- const bundledWeb = path24.join(here, "web");
5826
- if (isWebDir(bundledWeb) || path24.basename(here) === "dist") {
6074
+ const here = path26.dirname(file);
6075
+ const bundledWeb = path26.join(here, "web");
6076
+ if (isWebDir(bundledWeb) || path26.basename(here) === "dist") {
5827
6077
  return {
5828
- version: readVersion(path24.join(here, "..", "package.json")),
6078
+ version: readVersion(path26.join(here, "..", "package.json")),
5829
6079
  webDir: isWebDir(bundledWeb) ? bundledWeb : null,
5830
6080
  sourceRoot: null,
5831
6081
  cliEntry: file
5832
6082
  };
5833
6083
  }
5834
- const root = path24.resolve(here, "..", "..", "..");
6084
+ const root = path26.resolve(here, "..", "..", "..");
5835
6085
  const override = process.env.VIBEFLOW_WEB_DIR;
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 };
6086
+ const webDir = override && isWebDir(override) ? override : isWebDir(path26.join(root, "app")) ? path26.join(root, "app") : null;
6087
+ return { version: readVersion(path26.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
5838
6088
  }
5839
6089
  function readVersion(pkgPath) {
5840
6090
  try {
5841
- return JSON.parse(fs19.readFileSync(pkgPath, "utf8")).version;
6091
+ return JSON.parse(fs21.readFileSync(pkgPath, "utf8")).version;
5842
6092
  } catch {
5843
6093
  return "0.0.0";
5844
6094
  }
@@ -6044,8 +6294,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
6044
6294
  console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
6045
6295
  return 1;
6046
6296
  }
6047
- const dir = path24.resolve(target);
6048
- if (!fs19.existsSync(dir) || !fs19.statSync(dir).isDirectory()) {
6297
+ const dir = path26.resolve(target);
6298
+ if (!fs21.existsSync(dir) || !fs21.statSync(dir).isDirectory()) {
6049
6299
  console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
6050
6300
  return 1;
6051
6301
  }