@kw-yeh/vibeflow 4.0.6 → 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 fs18 from "fs";
601
+ import fs19 from "fs";
602
602
  import os6 from "os";
603
- import path22 from "path";
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) {
@@ -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 = fs19.mkdtempSync(path23.join(os6.tmpdir(), "vibeflow-"));
723
+ const file = path23.join(dir, "description.md");
724
724
  try {
725
- fs18.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: fs18.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
730
+ return { text: fs19.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
731
731
  } finally {
732
- fs18.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(path22.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir())));
800
+ if (value2) setProject(path23.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 = 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 ? path22.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) => path22.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");
@@ -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"}/${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`;
@@ -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 path24 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 = path24.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 fs20 from "fs";
1425
+ import path25 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);
@@ -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 fs17 from "fs";
3578
+ import path21 from "path";
3535
3579
  import { randomBytes as randomBytes2 } from "crypto";
3536
3580
 
3537
3581
  // packages/core/src/events.ts
@@ -3557,188 +3601,699 @@ 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;
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, "");
3704
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"],
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 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"],
3742
4297
  {
3743
4298
  name: "xterm-256color",
3744
4299
  cwd: process.cwd(),
@@ -3790,38 +4345,38 @@ 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
4367
  var MAX_DECISION_BYTES = 256 * 1024;
3813
4368
  function decisionsKey(worktreePath, branch) {
3814
- return worktreePath ? path16.basename(worktreePath) : branch.replace(/\//g, "-");
4369
+ return worktreePath ? path18.basename(worktreePath) : branch.replace(/\//g, "-");
3815
4370
  }
3816
4371
  function decisionsPath(workspacePath, key) {
3817
- return path16.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
4372
+ return path18.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
3818
4373
  }
3819
4374
  function readDecisions(workspacePath, key) {
3820
4375
  const target = decisionsPath(workspacePath, key);
3821
4376
  try {
3822
- const stat = fs12.statSync(target);
4377
+ const stat = fs14.statSync(target);
3823
4378
  if (!stat.isFile()) return { path: target, markdown: null };
3824
- const buf = fs12.readFileSync(target);
4379
+ const buf = fs14.readFileSync(target);
3825
4380
  return {
3826
4381
  path: target,
3827
4382
  markdown: buf.subarray(0, MAX_DECISION_BYTES).toString("utf8"),
@@ -3834,7 +4389,7 @@ function readDecisions(workspacePath, key) {
3834
4389
  }
3835
4390
  function deleteDecisions(workspacePath, key) {
3836
4391
  try {
3837
- fs12.rmSync(decisionsPath(workspacePath, key), { force: true });
4392
+ fs14.rmSync(decisionsPath(workspacePath, key), { force: true });
3838
4393
  } catch {
3839
4394
  }
3840
4395
  }
@@ -4037,437 +4592,94 @@ ${attachmentLines.join("\n")}` : text;
4037
4592
  }
4038
4593
  } else if (block.type === "thinking" && block.thinking) {
4039
4594
  pushPhase({
4040
- taskId,
4041
- id: `${taskId}-${seq++}`,
4042
- phaseType: "thinking",
4043
- phaseSummary: "Thinking\u2026",
4044
- 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));
4595
+ taskId,
4596
+ id: `${taskId}-${seq++}`,
4597
+ phaseType: "thinking",
4598
+ phaseSummary: "Thinking\u2026",
4599
+ phaseDetail: block.thinking,
4600
+ done: true
4601
+ });
4602
+ } else if (block.type === "tool_use" && block.name) {
4603
+ const toolId = block.id ?? "";
4604
+ toolNames.set(toolId, block.name);
4605
+ const { summary, detail } = summarizeToolUse(block.name, block.input ?? {});
4606
+ pushPhase({
4607
+ taskId,
4608
+ id: `${taskId}-${seq++}`,
4609
+ phaseType: "tool_use",
4610
+ phaseSummary: summary,
4611
+ phaseDetail: detail,
4612
+ done: true
4613
+ });
4614
+ }
4615
+ }
4616
+ }
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
+ }
4236
4639
  } catch {
4237
- continue;
4238
4640
  }
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
4641
  }
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
4642
  });
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
4643
+ proc.stderr.on("data", (raw) => {
4644
+ const msg = raw.toString().trim();
4645
+ if (msg) push({ taskId, delta: "", done: false, error: msg });
4304
4646
  });
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
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()
4368
4654
  });
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) {
4655
+ const error = code2 !== 0 ? `Process exited with code ${code2}` : void 0;
4656
+ push({ taskId, delta: assistantText, done: true, error });
4657
+ });
4658
+ return () => {
4384
4659
  try {
4385
- fs13.rmSync(linkPath, { recursive: true, force: true });
4386
- attempt();
4387
- return true;
4660
+ proc.kill("SIGTERM");
4388
4661
  } catch {
4389
4662
  }
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
4663
  };
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
4664
  }
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
- };
4665
+ var activeProcs = /* @__PURE__ */ new Map();
4666
+ function cancelChatSend(taskId) {
4667
+ activeProcs.get(taskId)?.();
4668
+ activeProcs.delete(taskId);
4466
4669
  }
4467
- async function libraryLaunchInfo(worktreePath) {
4468
- return libraryLaunchInfoAt(await libraryRoot(), worktreePath);
4670
+ function cancelAllChatSends() {
4671
+ activeProcs.forEach((kill) => kill());
4672
+ activeProcs.clear();
4673
+ }
4674
+ function startChatSend(opts, sender) {
4675
+ cancelChatSend(opts.taskId);
4676
+ const kill = sendChatMessage(opts, sender);
4677
+ activeProcs.set(opts.taskId, kill);
4469
4678
  }
4470
4679
 
4680
+ // packages/core/src/service.ts
4681
+ init_attachments();
4682
+
4471
4683
  // packages/core/src/launch.ts
4472
4684
  var ARTIFACTS_DIR_SUFFIX2 = ".artifacts";
4473
4685
  var SCRATCH_DIR_NAME2 = "scratch";
@@ -4750,13 +4962,13 @@ function createCore({ sessions, bus, version }) {
4750
4962
  }
4751
4963
  function claudeSessionFile(cwd, sessionId) {
4752
4964
  const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
4753
- return path18.join(os5.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
4965
+ return path19.join(os5.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
4754
4966
  }
4755
4967
  async function removeTaskWorktree(task) {
4756
4968
  if (!task.projectPath || !task.worktreePath) return;
4757
4969
  const branch = task.branch || fallbackBranchName(task.id);
4758
4970
  await removeWorktree(task.projectPath, task.worktreePath);
4759
- deleteArtifacts(task.workspacePath ?? path18.dirname(task.worktreePath), task.worktreePath);
4971
+ deleteArtifacts(task.workspacePath ?? path19.dirname(task.worktreePath), task.worktreePath);
4760
4972
  await deleteBranch(task.projectPath, branch);
4761
4973
  }
4762
4974
  const handlers = {
@@ -4795,8 +5007,8 @@ function createCore({ sessions, bus, version }) {
4795
5007
  "projects:listRecent": () => listRecentProjects(getStore()),
4796
5008
  // `vibeflow open <path>` while a host runs: only the host writes the store.
4797
5009
  "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}`);
5010
+ const p = path19.resolve(str(projectPath, "projectPath"));
5011
+ if (!fs15.existsSync(p) || !fs15.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
4800
5012
  recordRecentProject(getStore(), p);
4801
5013
  return listRecentProjects(getStore());
4802
5014
  },
@@ -4836,7 +5048,7 @@ function createCore({ sessions, bus, version }) {
4836
5048
  if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
4837
5049
  await teardownTask(task.id);
4838
5050
  await resetWorktreeToBase(task.worktreePath, task.baseBranch);
4839
- const workspacePath = task.workspacePath ?? path18.dirname(task.worktreePath);
5051
+ const workspacePath = task.workspacePath ?? path19.dirname(task.worktreePath);
4840
5052
  deleteArtifacts(workspacePath, task.worktreePath);
4841
5053
  deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
4842
5054
  resetSubAgents(task.worktreePath);
@@ -4875,9 +5087,9 @@ function createCore({ sessions, bus, version }) {
4875
5087
  if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
4876
5088
  await teardownTask(existing.id);
4877
5089
  await removeTaskWorktree(existing);
4878
- const projectName = path18.basename(nextProject);
5090
+ const projectName = path19.basename(nextProject);
4879
5091
  const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
4880
- await fs14.promises.mkdir(workspacePath, { recursive: true });
5092
+ await fs15.promises.mkdir(workspacePath, { recursive: true });
4881
5093
  const result = await provisionWorktree(
4882
5094
  nextProject,
4883
5095
  workspacePath,
@@ -4965,7 +5177,7 @@ function createCore({ sessions, bus, version }) {
4965
5177
  "claude:sessionExists": (taskId) => {
4966
5178
  const task = requireTask(taskId);
4967
5179
  if (!task.worktreePath) return false;
4968
- return fs14.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
5180
+ return fs15.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
4969
5181
  },
4970
5182
  "sessions:list": async () => {
4971
5183
  const keys = await sessions.list();
@@ -5012,7 +5224,7 @@ function createCore({ sessions, bus, version }) {
5012
5224
  const task = requireTask(taskId);
5013
5225
  if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
5014
5226
  const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
5015
- if (!fs14.existsSync(dir)) return "artifacts directory does not exist yet";
5227
+ if (!fs15.existsSync(dir)) return "artifacts directory does not exist yet";
5016
5228
  return getPlatform().openPath(dir);
5017
5229
  },
5018
5230
  "task:getDecisions": (taskId) => {
@@ -5021,7 +5233,7 @@ function createCore({ sessions, bus, version }) {
5021
5233
  return readDecisions(task.workspacePath, decisionsKey(task.worktreePath, task.branch));
5022
5234
  },
5023
5235
  "board:getCliLaunchInfo": () => boardCliLaunchInfo(),
5024
- "library:list": async () => listLibrary(await libraryRoot()),
5236
+ "library:list": async () => listLibrary(await libraryRoot(), builtinSkillsDir()),
5025
5237
  // The source path was picked by the user (native picker or typed path).
5026
5238
  "library:import": async (payload) => {
5027
5239
  const p = obj(payload, "payload");
@@ -5060,6 +5272,11 @@ function createCore({ sessions, bus, version }) {
5060
5272
  deleteEntry(await libraryRoot(), libraryKind(p.kind), str(p.name, "name"));
5061
5273
  return true;
5062
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()),
5063
5280
  // Approve: commit everything in the worktree and push the branch upstream.
5064
5281
  "git:approve": async (payload) => {
5065
5282
  const p = obj(payload, "payload");
@@ -5165,9 +5382,9 @@ function createCore({ sessions, bus, version }) {
5165
5382
  // Watch the containing directory, not the file: JsonStore renames a temp
5166
5383
  // file over the target and so replaces the inode on every save.
5167
5384
  watchStore() {
5168
- const file = path18.basename(getStorePath());
5385
+ const file = path19.basename(getStorePath());
5169
5386
  let debounce = null;
5170
- storeWatcher = fs14.watch(path18.dirname(getStorePath()), (_event, filename) => {
5387
+ storeWatcher = fs15.watch(path19.dirname(getStorePath()), (_event, filename) => {
5171
5388
  if (filename !== file) return;
5172
5389
  if (debounce) clearTimeout(debounce);
5173
5390
  debounce = setTimeout(() => {
@@ -5199,9 +5416,9 @@ function createSessionBackend(sink, choice) {
5199
5416
  }
5200
5417
 
5201
5418
  // packages/core/src/web-server.ts
5202
- import fs15 from "fs";
5419
+ import fs16 from "fs";
5203
5420
  import http from "http";
5204
- import path19 from "path";
5421
+ import path20 from "path";
5205
5422
  import { timingSafeEqual } from "crypto";
5206
5423
  import { WebSocketServer } from "ws";
5207
5424
  var TOKEN_COOKIE = "vf_token";
@@ -5254,14 +5471,14 @@ function resolveStatic(root, urlPath) {
5254
5471
  return null;
5255
5472
  }
5256
5473
  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`];
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`];
5260
5477
  for (const c of candidates) {
5261
- const full = path19.resolve(base, c);
5262
- if (full !== base && !full.startsWith(base + path19.sep)) return null;
5478
+ const full = path20.resolve(base, c);
5479
+ if (full !== base && !full.startsWith(base + path20.sep)) return null;
5263
5480
  try {
5264
- if (fs15.statSync(full).isFile()) return full;
5481
+ if (fs16.statSync(full).isFile()) return full;
5265
5482
  } catch {
5266
5483
  }
5267
5484
  }
@@ -5322,7 +5539,7 @@ async function startWebServer(options) {
5322
5539
  send(res, 404, "Not found");
5323
5540
  return;
5324
5541
  }
5325
- const ext = path19.extname(file).toLowerCase();
5542
+ const ext = path20.extname(file).toLowerCase();
5326
5543
  const immutable = url.pathname.startsWith("/_next/static/");
5327
5544
  res.writeHead(200, {
5328
5545
  "Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
@@ -5335,7 +5552,7 @@ async function startWebServer(options) {
5335
5552
  res.end();
5336
5553
  return;
5337
5554
  }
5338
- fs15.createReadStream(file).pipe(res);
5555
+ fs16.createReadStream(file).pipe(res);
5339
5556
  }
5340
5557
  const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
5341
5558
  function reject(socket, status, reason) {
@@ -5460,6 +5677,7 @@ async function startHost(options) {
5460
5677
  return startHost(options);
5461
5678
  }
5462
5679
  try {
5680
+ await syncBuiltinsAtStartup();
5463
5681
  const bus = new EventBus();
5464
5682
  const sessions = createSessionBackend(bus.sink(), {
5465
5683
  prefer: options.backend,
@@ -5506,7 +5724,7 @@ function loginUrl(lock, pathname = "/home/") {
5506
5724
  return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
5507
5725
  }
5508
5726
  function isWebDir(dir) {
5509
- return fs16.existsSync(path20.join(dir, "home", "index.html"));
5727
+ return fs17.existsSync(path21.join(dir, "home", "index.html"));
5510
5728
  }
5511
5729
 
5512
5730
  // packages/cli/src/remote.ts
@@ -5623,8 +5841,8 @@ function connectToHost(lock) {
5623
5841
  }
5624
5842
 
5625
5843
  // packages/cli/src/doctor.ts
5626
- import fs17 from "fs";
5627
- import path21 from "path";
5844
+ import fs18 from "fs";
5845
+ import path22 from "path";
5628
5846
  import { execFile as execFile7 } from "child_process";
5629
5847
  import { promisify as promisify5 } from "util";
5630
5848
  init_env();
@@ -5764,10 +5982,10 @@ async function runDoctor(options) {
5764
5982
  });
5765
5983
  }
5766
5984
  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);
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);
5771
5989
  checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
5772
5990
  } catch (err) {
5773
5991
  checks.push({
@@ -5821,24 +6039,24 @@ Options:
5821
6039
  `.trim();
5822
6040
  function detectLayout() {
5823
6041
  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") {
6042
+ const here = path25.dirname(file);
6043
+ const bundledWeb = path25.join(here, "web");
6044
+ if (isWebDir(bundledWeb) || path25.basename(here) === "dist") {
5827
6045
  return {
5828
- version: readVersion(path24.join(here, "..", "package.json")),
6046
+ version: readVersion(path25.join(here, "..", "package.json")),
5829
6047
  webDir: isWebDir(bundledWeb) ? bundledWeb : null,
5830
6048
  sourceRoot: null,
5831
6049
  cliEntry: file
5832
6050
  };
5833
6051
  }
5834
- const root = path24.resolve(here, "..", "..", "..");
6052
+ const root = path25.resolve(here, "..", "..", "..");
5835
6053
  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 };
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 };
5838
6056
  }
5839
6057
  function readVersion(pkgPath) {
5840
6058
  try {
5841
- return JSON.parse(fs19.readFileSync(pkgPath, "utf8")).version;
6059
+ return JSON.parse(fs20.readFileSync(pkgPath, "utf8")).version;
5842
6060
  } catch {
5843
6061
  return "0.0.0";
5844
6062
  }
@@ -6044,8 +6262,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
6044
6262
  console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
6045
6263
  return 1;
6046
6264
  }
6047
- const dir = path24.resolve(target);
6048
- if (!fs19.existsSync(dir) || !fs19.statSync(dir).isDirectory()) {
6265
+ const dir = path25.resolve(target);
6266
+ if (!fs20.existsSync(dir) || !fs20.statSync(dir).isDirectory()) {
6049
6267
  console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
6050
6268
  return 1;
6051
6269
  }