@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/README.md +5 -0
- package/dist/builtin-skills/pr/SKILL.md +80 -0
- package/dist/builtin-skills/visual-parity/SKILL.md +119 -0
- package/dist/builtin-skills/visual-parity/scripts/parity.py +449 -0
- package/dist/vibeflow.mjs +864 -646
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/{nFwNZkZhqIks3G972au7C → E9h3ANqgTHA4it6NLyDv5}/_buildManifest.js +1 -1
- package/dist/web/_next/static/chunks/{5426-48abe4a8b4f787e5.js → 8833-76c31db372a29bb8.js} +2 -2
- package/dist/web/_next/static/chunks/pages/{_app-2c12a0c9ae85f245.js → _app-66261c34aef6a4cb.js} +1 -1
- package/dist/web/_next/static/chunks/pages/{home-0b1963318ed92bb5.js → home-e16a7b49ece28424.js} +6 -6
- package/dist/web/_next/static/css/{37b782400bf48851.css → c380333c2873a2b8.css} +1 -1
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- /package/dist/web/_next/static/{nFwNZkZhqIks3G972au7C → E9h3ANqgTHA4it6NLyDv5}/_ssgManifest.js +0 -0
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
|
|
601
|
+
import fs19 from "fs";
|
|
602
602
|
import os6 from "os";
|
|
603
|
-
import
|
|
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 =
|
|
723
|
-
const file =
|
|
722
|
+
const dir = fs19.mkdtempSync(path23.join(os6.tmpdir(), "vibeflow-"));
|
|
723
|
+
const file = path23.join(dir, "description.md");
|
|
724
724
|
try {
|
|
725
|
-
|
|
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:
|
|
730
|
+
return { text: fs19.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
|
|
731
731
|
} finally {
|
|
732
|
-
|
|
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(
|
|
800
|
+
if (value2) setProject(path23.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir())));
|
|
801
801
|
} else if (editing === "attachments" && value2) {
|
|
802
|
-
const file =
|
|
803
|
-
if (!
|
|
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 ?
|
|
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) =>
|
|
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"}/${
|
|
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
|
|
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 =
|
|
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
|
|
1425
|
-
import
|
|
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, {
|
|
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
|
|
3534
|
-
import
|
|
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/
|
|
3561
|
-
import
|
|
3562
|
-
import
|
|
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/
|
|
3567
|
-
import {
|
|
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
|
-
|
|
3572
|
-
|
|
3573
|
-
var
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
}
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
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
|
|
3583
|
-
|
|
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
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
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
|
-
|
|
3609
|
-
return models;
|
|
3641
|
+
return result;
|
|
3610
3642
|
}
|
|
3611
|
-
function
|
|
3612
|
-
|
|
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
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
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
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
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
|
-
|
|
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
|
|
3654
|
-
|
|
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
|
|
3661
|
-
|
|
3662
|
-
const
|
|
3663
|
-
if (
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
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
|
|
3673
|
-
|
|
3674
|
-
|
|
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
|
-
|
|
3696
|
+
function readSkillDescription(skillMdPath) {
|
|
3697
|
+
let content;
|
|
3677
3698
|
try {
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
);
|
|
3687
|
-
const
|
|
3688
|
-
return
|
|
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
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
{
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
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
|
-
|
|
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
|
|
3722
|
-
if (!activeLogin) return;
|
|
3734
|
+
function builtinStatus(full, name, recorded, builtinDir) {
|
|
3723
3735
|
try {
|
|
3724
|
-
|
|
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
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
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
|
|
3794
|
-
import
|
|
4348
|
+
import fs13 from "fs";
|
|
4349
|
+
import path17 from "path";
|
|
3795
4350
|
import { pathToFileURL } from "url";
|
|
3796
4351
|
async function boardCliLaunchInfo() {
|
|
3797
|
-
const storeDir =
|
|
4352
|
+
const storeDir = path17.dirname(getStorePath());
|
|
3798
4353
|
const entry = getPlatform().cliEntry?.();
|
|
3799
|
-
if (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 =
|
|
3803
|
-
const loaderPath =
|
|
3804
|
-
if (!
|
|
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
|
|
3810
|
-
import
|
|
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 ?
|
|
4369
|
+
return worktreePath ? path18.basename(worktreePath) : branch.replace(/\//g, "-");
|
|
3815
4370
|
}
|
|
3816
4371
|
function decisionsPath(workspacePath, key) {
|
|
3817
|
-
return
|
|
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 =
|
|
4377
|
+
const stat = fs14.statSync(target);
|
|
3823
4378
|
if (!stat.isFile()) return { path: target, markdown: null };
|
|
3824
|
-
const buf =
|
|
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
|
-
|
|
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
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
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
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
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
|
-
|
|
4371
|
-
}
|
|
4372
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
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
|
-
|
|
4468
|
-
|
|
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
|
|
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 ??
|
|
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 =
|
|
4799
|
-
if (!
|
|
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 ??
|
|
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 =
|
|
5090
|
+
const projectName = path19.basename(nextProject);
|
|
4879
5091
|
const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
|
|
4880
|
-
await
|
|
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
|
|
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 (!
|
|
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 =
|
|
5385
|
+
const file = path19.basename(getStorePath());
|
|
5169
5386
|
let debounce = null;
|
|
5170
|
-
storeWatcher =
|
|
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
|
|
5419
|
+
import fs16 from "fs";
|
|
5203
5420
|
import http from "http";
|
|
5204
|
-
import
|
|
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 =
|
|
5258
|
-
const base =
|
|
5259
|
-
const candidates = [rel,
|
|
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 =
|
|
5262
|
-
if (full !== base && !full.startsWith(base +
|
|
5478
|
+
const full = path20.resolve(base, c);
|
|
5479
|
+
if (full !== base && !full.startsWith(base + path20.sep)) return null;
|
|
5263
5480
|
try {
|
|
5264
|
-
if (
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
5627
|
-
import
|
|
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
|
-
|
|
5768
|
-
const probe =
|
|
5769
|
-
|
|
5770
|
-
|
|
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 =
|
|
5825
|
-
const bundledWeb =
|
|
5826
|
-
if (isWebDir(bundledWeb) ||
|
|
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(
|
|
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 =
|
|
6052
|
+
const root = path25.resolve(here, "..", "..", "..");
|
|
5835
6053
|
const override = process.env.VIBEFLOW_WEB_DIR;
|
|
5836
|
-
const webDir = override && isWebDir(override) ? override : isWebDir(
|
|
5837
|
-
return { version: readVersion(
|
|
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(
|
|
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 =
|
|
6048
|
-
if (!
|
|
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
|
}
|