@kw-yeh/vibeflow 4.0.6 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +930 -680
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +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-014e95fe86cd1d39.js} +1 -1
- package/dist/web/_next/static/chunks/pages/home-e692e9e0783d2fdf.js +11 -0
- package/dist/web/_next/static/css/{37b782400bf48851.css → c380333c2873a2b8.css} +1 -1
- package/dist/web/_next/static/{nFwNZkZhqIks3G972au7C → lyOZoF78HzRwmQ3DYf88f}/_buildManifest.js +1 -1
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/pages/home-0b1963318ed92bb5.js +0 -11
- /package/dist/web/_next/static/{nFwNZkZhqIks3G972au7C → lyOZoF78HzRwmQ3DYf88f}/_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 fs20 from "fs";
|
|
602
602
|
import os6 from "os";
|
|
603
|
-
import
|
|
603
|
+
import path24 from "path";
|
|
604
604
|
import React, { useEffect, useRef, useState } from "react";
|
|
605
605
|
import { Box, Text, render, useApp, useInput } from "ink";
|
|
606
606
|
function newFormState(defaultAutoMode, agents) {
|
|
@@ -719,17 +719,17 @@ function editorCommand(env, platform) {
|
|
|
719
719
|
return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
|
|
720
720
|
}
|
|
721
721
|
function editInEditor(text) {
|
|
722
|
-
const dir =
|
|
723
|
-
const file =
|
|
722
|
+
const dir = fs20.mkdtempSync(path24.join(os6.tmpdir(), "vibeflow-"));
|
|
723
|
+
const file = path24.join(dir, "description.md");
|
|
724
724
|
try {
|
|
725
|
-
|
|
725
|
+
fs20.writeFileSync(file, text);
|
|
726
726
|
const cmd = editorCommand(process.env, process.platform);
|
|
727
727
|
const r = spawnSync(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
|
|
728
728
|
if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
|
|
729
729
|
if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
|
|
730
|
-
return { text:
|
|
730
|
+
return { text: fs20.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
|
|
731
731
|
} finally {
|
|
732
|
-
|
|
732
|
+
fs20.rmSync(dir, { recursive: true, force: true });
|
|
733
733
|
}
|
|
734
734
|
}
|
|
735
735
|
function cycle(list, current, step) {
|
|
@@ -797,10 +797,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
797
797
|
if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
|
|
798
798
|
else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
|
|
799
799
|
else if (editing === "project") {
|
|
800
|
-
if (value2) setProject(
|
|
800
|
+
if (value2) setProject(path24.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir())));
|
|
801
801
|
} else if (editing === "attachments" && value2) {
|
|
802
|
-
const file =
|
|
803
|
-
if (!
|
|
802
|
+
const file = path24.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir()));
|
|
803
|
+
if (!fs20.existsSync(file) || !fs20.statSync(file).isFile()) {
|
|
804
804
|
setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
|
|
805
805
|
return;
|
|
806
806
|
}
|
|
@@ -870,7 +870,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
870
870
|
case "title":
|
|
871
871
|
return s.title || "\uFF08\u4F8B\u5982\uFF1A\u5BE6\u4F5C\u767B\u5165\u9801\u9762\uFF09";
|
|
872
872
|
case "project": {
|
|
873
|
-
if (projectLocked) return `${s.projectPath ?
|
|
873
|
+
if (projectLocked) return `${s.projectPath ? path24.basename(s.projectPath) : "\u2014"}\uFF08\u4EFB\u52D9\u5DF2\u958B\u59CB\uFF0C\u9396\u5B9A\uFF09`;
|
|
874
874
|
if (!s.projectPath) return ctx.recent.length ? "\u2190/\u2192 \u9078\u64C7\u4F7F\u7528\u904E\u7684\u5C08\u6848\uFF0C\u6216 Enter \u8F38\u5165\u8DEF\u5F91" : "Enter \u8F38\u5165\u5C08\u6848\u8CC7\u6599\u593E\u7684\u7D55\u5C0D\u8DEF\u5F91";
|
|
875
875
|
return `${s.projectPath}${missing ? "\uFF08\u8DEF\u5F91\u907A\u5931\uFF09" : ""}`;
|
|
876
876
|
}
|
|
@@ -881,7 +881,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
881
881
|
case "branch":
|
|
882
882
|
return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
|
|
883
883
|
case "attachments":
|
|
884
|
-
return s.attachments.length ? s.attachments.map((a) =>
|
|
884
|
+
return s.attachments.length ? s.attachments.map((a) => path24.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
|
|
885
885
|
case "description": {
|
|
886
886
|
if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
|
|
887
887
|
const lines = s.description.split("\n");
|
|
@@ -912,7 +912,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
912
912
|
if (info && !info.isRepo) return "\u9019\u500B\u8CC7\u6599\u593E\u4E0D\u662F Git repository\u3002\u8ACB\u6539\u9078\u4E00\u500B Git \u5C08\u6848\uFF0C\u6216\u5728\u4E0B\u65B9\u521D\u59CB\u5316\u3002";
|
|
913
913
|
if (s.mode === "edit" && projectChanged(s)) return "\u66F4\u63DB\u5C08\u6848\u6703\u5728\u65B0\u5C08\u6848\u91CD\u5EFA worktree\uFF08\u6B64\u4EFB\u52D9\u5C1A\u672A\u958B\u59CB\uFF0C\u7121\u8B8A\u66F4\u6703\u907A\u5931\uFF09\u3002";
|
|
914
914
|
if (info?.isRepo && !info.hasRemote) return `\u6B64 repository \u6C92\u6709 remote\uFF0C\u5C07\u4EE5\u76EE\u524D\u5206\u652F (${info.currentBranch ?? "HEAD"}) \u70BA\u57FA\u6E96\u5EFA\u7ACB\u672C\u5730 worktree\u3002`;
|
|
915
|
-
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${
|
|
915
|
+
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path24.basename(s.projectPath)}`;
|
|
916
916
|
}
|
|
917
917
|
if (field === "branch") return info?.hasRemote ? "\u586B\u5BEB\u5F8C\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\uFF1B\u82E5 remote \u5DF2\u6709\u9019\u500B\u5206\u652F\uFF0C\u5247\u76F4\u63A5\u53D6\u56DE\u4E26\u63A5\u7E8C\u4E0A\u9762\u7684\u5DE5\u4F5C\u3002" : "\u586B\u5BEB\u5F8C\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u3002";
|
|
918
918
|
if (field === "effort") return `${EFFORTS.find((x) => x.value === s.effort).description} Claude Code \u8207 Codex \u6703\u5728\u555F\u52D5\u6B64\u4EFB\u52D9\u6642\u5957\u7528\uFF1B\u5BE6\u969B\u53EF\u7528\u7D1A\u8DDD\u4F9D model \u800C\u5B9A\u3002`;
|
|
@@ -1029,7 +1029,7 @@ __export(src_exports, {
|
|
|
1029
1029
|
showDiff: () => showDiff
|
|
1030
1030
|
});
|
|
1031
1031
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1032
|
-
import
|
|
1032
|
+
import path25 from "path";
|
|
1033
1033
|
import React2, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1034
1034
|
import { Box as Box2, Text as Text2, render as render2, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
|
|
1035
1035
|
function findDetachKey(text) {
|
|
@@ -1291,7 +1291,7 @@ function showDiff(task) {
|
|
|
1291
1291
|
});
|
|
1292
1292
|
}
|
|
1293
1293
|
function tmuxAttach(opts, sessionName) {
|
|
1294
|
-
const conf =
|
|
1294
|
+
const conf = path25.join(opts.userDataDir, "tmux.conf");
|
|
1295
1295
|
const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
|
|
1296
1296
|
const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
|
|
1297
1297
|
spawnSync2("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
|
|
@@ -1421,8 +1421,8 @@ var init_src = __esm({
|
|
|
1421
1421
|
});
|
|
1422
1422
|
|
|
1423
1423
|
// packages/cli/src/main.ts
|
|
1424
|
-
import
|
|
1425
|
-
import
|
|
1424
|
+
import fs21 from "fs";
|
|
1425
|
+
import path26 from "path";
|
|
1426
1426
|
import { fileURLToPath } from "url";
|
|
1427
1427
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
1428
1428
|
|
|
@@ -1526,6 +1526,12 @@ function createNodePlatform(options = {}) {
|
|
|
1526
1526
|
userDataDir: () => userData,
|
|
1527
1527
|
sourceRoot: () => root,
|
|
1528
1528
|
cliEntry: () => options.cliEntry ?? null,
|
|
1529
|
+
builtinSkillsDir: () => {
|
|
1530
|
+
if (options.builtinSkillsDir !== void 0) return options.builtinSkillsDir;
|
|
1531
|
+
if (options.cliEntry) return path2.join(path2.dirname(options.cliEntry), "builtin-skills");
|
|
1532
|
+
if (root) return path2.join(root, "packages", "core", "builtin-skills");
|
|
1533
|
+
return null;
|
|
1534
|
+
},
|
|
1529
1535
|
openExternal: async (url) => {
|
|
1530
1536
|
const err = await openWithOs(url);
|
|
1531
1537
|
if (err) throw new Error(err);
|
|
@@ -1808,8 +1814,8 @@ function createBridge(t) {
|
|
|
1808
1814
|
* in-app preview will not inline. Resolves to '' on success, else a reason.
|
|
1809
1815
|
*/
|
|
1810
1816
|
openArtifactsDir: (taskId) => t.invoke("task:openArtifactsDir", taskId),
|
|
1811
|
-
/**
|
|
1812
|
-
|
|
1817
|
+
/** Every spec.md the task's branch added or changed; empty when there is none. */
|
|
1818
|
+
getSpecs: (taskId) => t.invoke("task:getSpecs", taskId),
|
|
1813
1819
|
/** Store dir + CLI paths for launch injection, so an agent can write cards. */
|
|
1814
1820
|
getBoardCliLaunchInfo: () => t.invoke("board:getCliLaunchInfo"),
|
|
1815
1821
|
/** VibeFlow's own skill / prompt / script store. */
|
|
@@ -1821,6 +1827,10 @@ function createBridge(t) {
|
|
|
1821
1827
|
setLibraryEntryDescription: (payload) => t.invoke("library:setDescription", payload),
|
|
1822
1828
|
setLibraryEntryEnabled: (payload) => t.invoke("library:setEnabled", payload),
|
|
1823
1829
|
deleteLibraryEntry: (payload) => t.invoke("library:delete", payload),
|
|
1830
|
+
/** Overwrite or bring back a shipped skill with the version this install carries. */
|
|
1831
|
+
restoreBuiltinSkill: (payload) => t.invoke("library:restoreBuiltin", payload),
|
|
1832
|
+
/** Shipped skills the user deleted, offered for restore. */
|
|
1833
|
+
listRemovedBuiltinSkills: () => t.invoke("library:removedBuiltins"),
|
|
1824
1834
|
approve: (taskId, message) => t.invoke("git:approve", { taskId, message }),
|
|
1825
1835
|
/** Use the task's agent CLI to generate a commit message for branch changes. */
|
|
1826
1836
|
generateCommitMessage: (taskId) => t.invoke("git:generateCommitMessage", taskId),
|
|
@@ -2691,7 +2701,41 @@ async function copyIgnoredEntry(projectPath, worktreePath, entry) {
|
|
|
2691
2701
|
const src = path12.join(projectPath, entry);
|
|
2692
2702
|
const dest = path12.join(worktreePath, entry);
|
|
2693
2703
|
await fs8.mkdir(path12.dirname(dest), { recursive: true });
|
|
2694
|
-
await fs8.cp(src, dest, {
|
|
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 fs18 from "fs";
|
|
3578
|
+
import path22 from "path";
|
|
3535
3579
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
3536
3580
|
|
|
3537
3581
|
// packages/core/src/events.ts
|
|
@@ -3557,190 +3601,701 @@ var EventBus = class {
|
|
|
3557
3601
|
}
|
|
3558
3602
|
};
|
|
3559
3603
|
|
|
3560
|
-
// packages/core/src/
|
|
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
|
-
env: execEnv(),
|
|
3683
|
-
timeout: 1e4,
|
|
3684
|
-
windowsHide: true
|
|
3685
|
-
}
|
|
3686
|
-
);
|
|
3687
|
-
const body = JSON.parse(stdout.toString());
|
|
3688
|
-
return statusFromBody(body);
|
|
3689
|
-
} catch (err) {
|
|
3690
|
-
const maybeStdout = err.stdout;
|
|
3691
|
-
if (maybeStdout) {
|
|
3692
|
-
try {
|
|
3693
|
-
const body = JSON.parse(maybeStdout.toString());
|
|
3694
|
-
return statusFromBody(body);
|
|
3695
|
-
} catch {
|
|
3696
|
-
}
|
|
3697
|
-
}
|
|
3698
|
-
const message = errorMessage2(err);
|
|
3699
|
-
return {
|
|
3700
|
-
installed: !/ENOENT|not found/i.test(message),
|
|
3701
|
-
authenticated: false,
|
|
3702
|
-
error: message
|
|
3703
|
-
};
|
|
3699
|
+
content = fs10.readFileSync(skillMdPath, "utf8");
|
|
3700
|
+
} catch {
|
|
3701
|
+
return void 0;
|
|
3704
3702
|
}
|
|
3703
|
+
const lines = content.split(/\r?\n/);
|
|
3704
|
+
if (lines[0]?.trim() !== "---") return void 0;
|
|
3705
|
+
for (let i = 1; i < lines.length; i += 1) {
|
|
3706
|
+
const line = lines[i];
|
|
3707
|
+
if (line.trim() === "---") break;
|
|
3708
|
+
const match = /^description:\s*(.+)$/.exec(line);
|
|
3709
|
+
if (match) return match[1].trim().replace(/^["']|["']$/g, "");
|
|
3710
|
+
}
|
|
3711
|
+
return void 0;
|
|
3705
3712
|
}
|
|
3706
|
-
|
|
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
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3749
|
+
function listLibrary(root, builtinDir) {
|
|
3750
|
+
const index = readIndex(root);
|
|
3751
|
+
const entries = [];
|
|
3752
|
+
for (const kind of LIBRARY_KINDS) {
|
|
3753
|
+
const dir = path14.join(root, KIND_DIR[kind]);
|
|
3754
|
+
let dirents;
|
|
3755
|
+
try {
|
|
3756
|
+
dirents = fs10.readdirSync(dir, { withFileTypes: true });
|
|
3757
|
+
} catch {
|
|
3758
|
+
continue;
|
|
3759
|
+
}
|
|
3760
|
+
for (const dirent of dirents) {
|
|
3761
|
+
if (dirent.name.startsWith(".")) continue;
|
|
3762
|
+
const wantsDirectory = kind === "skill";
|
|
3763
|
+
let stat;
|
|
3764
|
+
try {
|
|
3765
|
+
stat = fs10.statSync(path14.join(dir, dirent.name));
|
|
3766
|
+
} catch {
|
|
3767
|
+
continue;
|
|
3768
|
+
}
|
|
3769
|
+
if (stat.isDirectory() !== wantsDirectory) continue;
|
|
3770
|
+
const name = dirent.name;
|
|
3771
|
+
const full = path14.join(dir, name);
|
|
3772
|
+
if (wantsDirectory && !fs10.existsSync(path14.join(full, "SKILL.md"))) continue;
|
|
3773
|
+
const key = entryKey(kind, name);
|
|
3774
|
+
const record = index.entries[key] ?? {};
|
|
3775
|
+
entries.push({
|
|
3776
|
+
key,
|
|
3777
|
+
kind,
|
|
3778
|
+
name,
|
|
3779
|
+
path: full,
|
|
3780
|
+
description: wantsDirectory ? readSkillDescription(path14.join(full, "SKILL.md")) : record.description,
|
|
3781
|
+
sourcePath: record.sourcePath,
|
|
3782
|
+
importedAt: record.importedAt,
|
|
3783
|
+
enabled: record.enabled !== false,
|
|
3784
|
+
builtin: wantsDirectory && record.builtinHash && builtinDir !== void 0 ? builtinStatus(full, name, record.builtinHash, builtinDir) : void 0
|
|
3785
|
+
});
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
return entries.sort((a, b) => a.key.localeCompare(b.key));
|
|
3789
|
+
}
|
|
3790
|
+
function annotate(root, key, patch) {
|
|
3791
|
+
const index = readIndex(root);
|
|
3792
|
+
index.entries[key] = { ...index.entries[key], ...patch };
|
|
3793
|
+
writeIndex(root, index);
|
|
3794
|
+
}
|
|
3795
|
+
function importEntry(root, kind, sourcePath) {
|
|
3796
|
+
ensureLibrary(root);
|
|
3797
|
+
const resolved = path14.resolve(sourcePath);
|
|
3798
|
+
const stat = fs10.statSync(resolved);
|
|
3799
|
+
if (kind === "skill") {
|
|
3800
|
+
if (!stat.isDirectory()) throw new Error("skill \u5FC5\u9808\u662F\u542B SKILL.md \u7684\u76EE\u9304");
|
|
3801
|
+
if (!fs10.existsSync(path14.join(resolved, "SKILL.md"))) {
|
|
3802
|
+
throw new Error("\u76EE\u9304\u5167\u627E\u4E0D\u5230 SKILL.md");
|
|
3803
|
+
}
|
|
3804
|
+
} else if (!stat.isFile()) {
|
|
3805
|
+
throw new Error(`${kind} \u5FC5\u9808\u662F\u55AE\u4E00\u6A94\u6848`);
|
|
3806
|
+
}
|
|
3807
|
+
const name = safeLibraryName(path14.basename(resolved));
|
|
3808
|
+
const target = entryPath(root, kind, name);
|
|
3809
|
+
fs10.rmSync(target, { recursive: true, force: true });
|
|
3810
|
+
fs10.cpSync(resolved, target, { recursive: kind === "skill", dereference: true });
|
|
3811
|
+
if (kind === "script") fs10.chmodSync(target, 493);
|
|
3812
|
+
const key = entryKey(kind, name);
|
|
3813
|
+
const index = readIndex(root);
|
|
3814
|
+
const { builtinHash: _hash, builtinDeleted: _deleted, ...kept } = index.entries[key] ?? {};
|
|
3815
|
+
index.entries[key] = { ...kept, sourcePath: resolved, importedAt: Date.now(), enabled: true };
|
|
3816
|
+
writeIndex(root, index);
|
|
3817
|
+
return requireEntry(root, kind, name);
|
|
3818
|
+
}
|
|
3819
|
+
function createEntry(root, kind, rawName, content, description) {
|
|
3820
|
+
ensureLibrary(root);
|
|
3821
|
+
const name = safeLibraryName(rawName);
|
|
3822
|
+
const target = entryPath(root, kind, name);
|
|
3823
|
+
if (fs10.existsSync(target)) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u9805\u76EE\uFF1A${name}`);
|
|
3824
|
+
if (kind === "skill") {
|
|
3825
|
+
fs10.mkdirSync(target, { recursive: true });
|
|
3826
|
+
fs10.writeFileSync(path14.join(target, "SKILL.md"), content);
|
|
3827
|
+
} else {
|
|
3828
|
+
fs10.writeFileSync(target, content);
|
|
3829
|
+
if (kind === "script") fs10.chmodSync(target, 493);
|
|
3830
|
+
}
|
|
3831
|
+
annotate(root, entryKey(kind, name), {
|
|
3832
|
+
importedAt: Date.now(),
|
|
3833
|
+
enabled: true,
|
|
3834
|
+
description
|
|
3835
|
+
});
|
|
3836
|
+
return requireEntry(root, kind, name);
|
|
3837
|
+
}
|
|
3838
|
+
function setEntryDescription(root, kind, name, description) {
|
|
3839
|
+
annotate(root, entryKey(kind, name), { description });
|
|
3840
|
+
}
|
|
3841
|
+
function updateEntry(root, kind, name, content) {
|
|
3842
|
+
const target = entryPath(root, kind, name);
|
|
3843
|
+
if (!fs10.existsSync(target)) throw new Error(`\u627E\u4E0D\u5230\u9805\u76EE\uFF1A${name}`);
|
|
3844
|
+
fs10.writeFileSync(kind === "skill" ? path14.join(target, "SKILL.md") : target, content);
|
|
3845
|
+
return requireEntry(root, kind, name);
|
|
3846
|
+
}
|
|
3847
|
+
function readEntryContent(root, kind, name) {
|
|
3848
|
+
const target = entryPath(root, kind, name);
|
|
3849
|
+
return fs10.readFileSync(kind === "skill" ? path14.join(target, "SKILL.md") : target, "utf8");
|
|
3850
|
+
}
|
|
3851
|
+
function markBuiltinDeleted(root, key) {
|
|
3852
|
+
const index = readIndex(root);
|
|
3853
|
+
index.entries[key] = { builtinDeleted: true };
|
|
3854
|
+
writeIndex(root, index);
|
|
3855
|
+
}
|
|
3856
|
+
function deleteEntry(root, kind, name) {
|
|
3857
|
+
fs10.rmSync(entryPath(root, kind, name), { recursive: true, force: true });
|
|
3858
|
+
const key = entryKey(kind, name);
|
|
3859
|
+
const record = readIndex(root).entries[key];
|
|
3860
|
+
if (record?.builtinHash || record?.builtinDeleted) {
|
|
3861
|
+
markBuiltinDeleted(root, key);
|
|
3862
|
+
return;
|
|
3863
|
+
}
|
|
3864
|
+
const index = readIndex(root);
|
|
3865
|
+
delete index.entries[key];
|
|
3866
|
+
writeIndex(root, index);
|
|
3867
|
+
}
|
|
3868
|
+
function setEntryEnabled(root, kind, name, enabled) {
|
|
3869
|
+
annotate(root, entryKey(kind, name), { enabled });
|
|
3870
|
+
}
|
|
3871
|
+
function requireEntry(root, kind, name) {
|
|
3872
|
+
const found2 = listLibrary(root).find((e) => e.key === entryKey(kind, name));
|
|
3873
|
+
if (!found2) throw new Error(`\u9805\u76EE\u5BEB\u5165\u5F8C\u8B80\u4E0D\u56DE\u4F86\uFF1A${kind}/${name}`);
|
|
3874
|
+
return found2;
|
|
3875
|
+
}
|
|
3876
|
+
function projectSkillsDir(worktreePath) {
|
|
3877
|
+
return path14.join(worktreePath, ".claude", "skills");
|
|
3878
|
+
}
|
|
3879
|
+
function skillNamesIn(dir) {
|
|
3880
|
+
try {
|
|
3881
|
+
return fs10.readdirSync(dir, { withFileTypes: true }).filter((d) => !d.name.startsWith(".")).map((d) => d.name).filter((name) => fs10.existsSync(path14.join(dir, name, "SKILL.md"))).sort();
|
|
3882
|
+
} catch {
|
|
3883
|
+
return [];
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
var PLUGIN_MANIFEST_DIR = ".claude-plugin";
|
|
3887
|
+
function buildPluginDir(root) {
|
|
3888
|
+
const paths = libraryPaths(root);
|
|
3889
|
+
const enabled = listLibrary(root).filter((e) => e.kind === "skill" && e.enabled);
|
|
3890
|
+
fs10.rmSync(path14.join(paths.plugin, "skills"), { recursive: true, force: true });
|
|
3891
|
+
fs10.mkdirSync(path14.join(paths.plugin, PLUGIN_MANIFEST_DIR), { recursive: true });
|
|
3892
|
+
fs10.mkdirSync(path14.join(paths.plugin, "skills"), { recursive: true });
|
|
3893
|
+
fs10.writeFileSync(
|
|
3894
|
+
path14.join(paths.plugin, PLUGIN_MANIFEST_DIR, "plugin.json"),
|
|
3895
|
+
`${JSON.stringify(
|
|
3896
|
+
{
|
|
3897
|
+
name: "vibeflow-library",
|
|
3898
|
+
version: "1.0.0",
|
|
3899
|
+
description: "Skills delivered by VibeFlow for this task."
|
|
3900
|
+
},
|
|
3901
|
+
null,
|
|
3902
|
+
2
|
|
3903
|
+
)}
|
|
3904
|
+
`
|
|
3905
|
+
);
|
|
3906
|
+
for (const entry of enabled) {
|
|
3907
|
+
fs10.cpSync(entry.path, path14.join(paths.plugin, "skills", entry.name), {
|
|
3908
|
+
recursive: true,
|
|
3909
|
+
dereference: true
|
|
3910
|
+
});
|
|
3911
|
+
}
|
|
3912
|
+
return { pluginDir: paths.plugin, skills: enabled.map((e) => e.name) };
|
|
3913
|
+
}
|
|
3914
|
+
function linkOrSkip(target, linkPath) {
|
|
3915
|
+
if (!fs10.existsSync(target)) return false;
|
|
3916
|
+
const source = path14.resolve(target);
|
|
3917
|
+
const attempts = fs10.statSync(source).isDirectory() ? [
|
|
3918
|
+
() => fs10.symlinkSync(source, linkPath, "junction"),
|
|
3919
|
+
() => fs10.cpSync(source, linkPath, { recursive: true, dereference: true })
|
|
3920
|
+
] : [
|
|
3921
|
+
() => fs10.symlinkSync(source, linkPath),
|
|
3922
|
+
() => fs10.linkSync(source, linkPath),
|
|
3923
|
+
() => fs10.copyFileSync(source, linkPath)
|
|
3924
|
+
];
|
|
3925
|
+
for (const attempt of attempts) {
|
|
3926
|
+
try {
|
|
3927
|
+
fs10.rmSync(linkPath, { recursive: true, force: true });
|
|
3928
|
+
attempt();
|
|
3929
|
+
return true;
|
|
3930
|
+
} catch {
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
return false;
|
|
3934
|
+
}
|
|
3935
|
+
function buildCodexHome(root, worktreePath, userCodexHome = path14.join(os3.homedir(), ".codex")) {
|
|
3936
|
+
const paths = libraryPaths(root);
|
|
3937
|
+
const skillsDir = path14.join(paths.codexHome, "skills");
|
|
3938
|
+
fs10.rmSync(skillsDir, { recursive: true, force: true });
|
|
3939
|
+
fs10.mkdirSync(skillsDir, { recursive: true });
|
|
3940
|
+
for (const file of ["auth.json", "config.toml"]) {
|
|
3941
|
+
linkOrSkip(path14.join(userCodexHome, file), path14.join(paths.codexHome, file));
|
|
3942
|
+
}
|
|
3943
|
+
const result = {
|
|
3944
|
+
codexHome: paths.codexHome,
|
|
3945
|
+
project: [],
|
|
3946
|
+
user: [],
|
|
3947
|
+
library: [],
|
|
3948
|
+
shadowed: []
|
|
3949
|
+
};
|
|
3950
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
3951
|
+
const projectDir = worktreePath ? projectSkillsDir(worktreePath) : void 0;
|
|
3952
|
+
if (projectDir) {
|
|
3953
|
+
for (const name of skillNamesIn(projectDir)) {
|
|
3954
|
+
if (linkOrSkip(path14.join(projectDir, name), path14.join(skillsDir, name))) {
|
|
3955
|
+
result.project.push(name);
|
|
3956
|
+
claimed.set(name, "project");
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
const userDir = path14.join(userCodexHome, "skills");
|
|
3961
|
+
for (const name of skillNamesIn(userDir)) {
|
|
3962
|
+
const owner = claimed.get(name);
|
|
3963
|
+
if (owner) {
|
|
3964
|
+
result.shadowed.push({ name, keptFrom: owner, dropped: "user" });
|
|
3965
|
+
continue;
|
|
3966
|
+
}
|
|
3967
|
+
if (linkOrSkip(path14.join(userDir, name), path14.join(skillsDir, name))) {
|
|
3968
|
+
result.user.push(name);
|
|
3969
|
+
claimed.set(name, "user");
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
for (const entry of listLibrary(root)) {
|
|
3973
|
+
if (entry.kind !== "skill" || !entry.enabled) continue;
|
|
3974
|
+
const owner = claimed.get(entry.name);
|
|
3975
|
+
if (owner) {
|
|
3976
|
+
result.shadowed.push({ name: entry.name, keptFrom: owner, dropped: "library" });
|
|
3977
|
+
continue;
|
|
3978
|
+
}
|
|
3979
|
+
fs10.cpSync(entry.path, path14.join(skillsDir, entry.name), {
|
|
3980
|
+
recursive: true,
|
|
3981
|
+
dereference: true
|
|
3982
|
+
});
|
|
3983
|
+
result.library.push(entry.name);
|
|
3984
|
+
}
|
|
3985
|
+
return result;
|
|
3986
|
+
}
|
|
3987
|
+
function libraryLaunchInfoAt(root, worktreePath, userCodexHome) {
|
|
3988
|
+
const entries = listLibrary(root).filter((e) => e.enabled);
|
|
3989
|
+
if (entries.length === 0) return null;
|
|
3990
|
+
const { pluginDir } = buildPluginDir(root);
|
|
3991
|
+
const { codexHome } = buildCodexHome(root, worktreePath, userCodexHome);
|
|
3992
|
+
const scripts = entries.filter((e) => e.kind === "script").map((e) => ({ name: e.name, path: e.path, description: e.description }));
|
|
3993
|
+
const promptBodies = entries.filter((e) => e.kind === "prompt").map((e) => readEntryContent(root, "prompt", e.name).trim()).filter(Boolean);
|
|
3994
|
+
const lines = [...promptBodies];
|
|
3995
|
+
if (scripts.length > 0) {
|
|
3996
|
+
lines.push(
|
|
3997
|
+
"\u53EF\u7528\u7684\u5DE5\u5177\u8173\u672C\uFF08\u9700\u8981\u6642\u81EA\u884C\u57F7\u884C\uFF0C\u4E0D\u9700\u8981\u5C31\u5225\u8DD1\uFF09\uFF1A",
|
|
3998
|
+
...scripts.map((s) => `- ${s.path}${s.description ? ` \u2014 ${s.description}` : ""}`)
|
|
3999
|
+
);
|
|
4000
|
+
}
|
|
4001
|
+
return {
|
|
4002
|
+
pluginDir,
|
|
4003
|
+
codexHome,
|
|
4004
|
+
libraryDir: root,
|
|
4005
|
+
scripts,
|
|
4006
|
+
promptText: lines.join("\n\n")
|
|
4007
|
+
};
|
|
4008
|
+
}
|
|
4009
|
+
async function libraryLaunchInfo(worktreePath) {
|
|
4010
|
+
return libraryLaunchInfoAt(await libraryRoot(), worktreePath);
|
|
4011
|
+
}
|
|
4012
|
+
|
|
4013
|
+
// packages/core/src/library-builtins.ts
|
|
4014
|
+
function builtinSkillsDir() {
|
|
4015
|
+
return getPlatform().builtinSkillsDir?.() ?? null;
|
|
4016
|
+
}
|
|
4017
|
+
function installBuiltin(root, builtinDir, name) {
|
|
4018
|
+
const target = entryPath(root, "skill", name);
|
|
4019
|
+
const staging = entryPath(root, "skill", `.${name}.installing`);
|
|
4020
|
+
fs11.rmSync(staging, { recursive: true, force: true });
|
|
4021
|
+
try {
|
|
4022
|
+
fs11.cpSync(path15.join(builtinDir, name), staging, {
|
|
4023
|
+
recursive: true,
|
|
4024
|
+
dereference: true,
|
|
4025
|
+
filter: (src) => path15.basename(src) !== "__pycache__"
|
|
4026
|
+
});
|
|
4027
|
+
fs11.rmSync(target, { recursive: true, force: true });
|
|
4028
|
+
fs11.renameSync(staging, target);
|
|
4029
|
+
} finally {
|
|
4030
|
+
fs11.rmSync(staging, { recursive: true, force: true });
|
|
4031
|
+
}
|
|
4032
|
+
return hashSkillDir(target);
|
|
4033
|
+
}
|
|
4034
|
+
function syncBuiltinSkills(root, builtinDir) {
|
|
4035
|
+
const result = { installed: [], updated: [] };
|
|
4036
|
+
if (!builtinDir) return result;
|
|
4037
|
+
const names = skillNamesIn(builtinDir);
|
|
4038
|
+
if (names.length === 0) return result;
|
|
4039
|
+
if (indexIsCorrupt(root)) {
|
|
4040
|
+
console.error("Library index.json is unreadable; skipping built-in skill sync");
|
|
4041
|
+
return result;
|
|
4042
|
+
}
|
|
4043
|
+
ensureLibrary(root);
|
|
4044
|
+
for (const name of names) {
|
|
4045
|
+
try {
|
|
4046
|
+
syncOne(root, builtinDir, name, result);
|
|
4047
|
+
} catch (err) {
|
|
4048
|
+
console.error(`Failed to sync built-in skill "${name}":`, err);
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
return result;
|
|
4052
|
+
}
|
|
4053
|
+
function syncOne(root, builtinDir, name, result) {
|
|
4054
|
+
const key = entryKey("skill", name);
|
|
4055
|
+
const record = readIndex(root).entries[key] ?? {};
|
|
4056
|
+
if (record.builtinDeleted) return;
|
|
4057
|
+
const target = entryPath(root, "skill", name);
|
|
4058
|
+
if (!record.builtinHash) {
|
|
4059
|
+
if (fs11.existsSync(target)) return;
|
|
4060
|
+
annotate(root, key, {
|
|
4061
|
+
builtinHash: installBuiltin(root, builtinDir, name),
|
|
4062
|
+
enabled: true,
|
|
4063
|
+
importedAt: Date.now()
|
|
4064
|
+
});
|
|
4065
|
+
result.installed.push(name);
|
|
4066
|
+
return;
|
|
4067
|
+
}
|
|
4068
|
+
if (!fs11.existsSync(path15.join(target, "SKILL.md"))) {
|
|
4069
|
+
markBuiltinDeleted(root, key);
|
|
4070
|
+
return;
|
|
4071
|
+
}
|
|
4072
|
+
if (hashSkillDir(path15.join(builtinDir, name)) === record.builtinHash) return;
|
|
4073
|
+
if (hashSkillDir(target) !== record.builtinHash) return;
|
|
4074
|
+
annotate(root, key, { builtinHash: installBuiltin(root, builtinDir, name) });
|
|
4075
|
+
result.updated.push(name);
|
|
4076
|
+
}
|
|
4077
|
+
function restoreBuiltinSkill(root, builtinDir, name) {
|
|
4078
|
+
if (!builtinDir || safeLibraryName(name) !== name || !skillNamesIn(builtinDir).includes(name)) {
|
|
4079
|
+
throw new Error(`\u4E0D\u662F\u5167\u5EFA skill\uFF1A${name}`);
|
|
4080
|
+
}
|
|
4081
|
+
const key = entryKey("skill", name);
|
|
4082
|
+
if (fs11.existsSync(entryPath(root, "skill", name)) && !readIndex(root).entries[key]?.builtinHash) {
|
|
4083
|
+
throw new Error(`\u5DF2\u6709\u540C\u540D\u7684\u81EA\u8A02\u9805\u76EE\uFF1A${name}`);
|
|
4084
|
+
}
|
|
4085
|
+
ensureLibrary(root);
|
|
4086
|
+
const builtinHash = installBuiltin(root, builtinDir, name);
|
|
4087
|
+
const index = readIndex(root);
|
|
4088
|
+
const record = { ...index.entries[key], builtinHash, enabled: true, importedAt: Date.now() };
|
|
4089
|
+
delete record.builtinDeleted;
|
|
4090
|
+
delete record.sourcePath;
|
|
4091
|
+
index.entries[key] = record;
|
|
4092
|
+
writeIndex(root, index);
|
|
4093
|
+
const entry = listLibrary(root, builtinDir).find((e) => e.key === key);
|
|
4094
|
+
if (!entry) throw new Error(`\u9084\u539F\u5931\u6557\uFF1A${name}`);
|
|
4095
|
+
return entry;
|
|
4096
|
+
}
|
|
4097
|
+
function removedBuiltinSkills(root, builtinDir) {
|
|
4098
|
+
if (!builtinDir) return [];
|
|
4099
|
+
const index = readIndex(root);
|
|
4100
|
+
return skillNamesIn(builtinDir).filter(
|
|
4101
|
+
(name) => index.entries[entryKey("skill", name)]?.builtinDeleted === true && !fs11.existsSync(entryPath(root, "skill", name))
|
|
4102
|
+
);
|
|
4103
|
+
}
|
|
4104
|
+
async function syncBuiltinsAtStartup() {
|
|
4105
|
+
try {
|
|
4106
|
+
const { installed, updated } = syncBuiltinSkills(await libraryRoot(), builtinSkillsDir());
|
|
4107
|
+
if (installed.length || updated.length) {
|
|
4108
|
+
console.log(`Built-in skills \u2014 installed: ${installed.join(", ") || "-"}; updated: ${updated.join(", ") || "-"}`);
|
|
4109
|
+
}
|
|
4110
|
+
} catch (err) {
|
|
4111
|
+
console.error("Failed to sync built-in skills:", err);
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
// packages/core/src/service.ts
|
|
4116
|
+
import fs16 from "fs";
|
|
4117
|
+
import os5 from "os";
|
|
4118
|
+
import path20 from "path";
|
|
4119
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4120
|
+
|
|
4121
|
+
// packages/core/src/agent-models.ts
|
|
4122
|
+
import { execFile as execFile5 } from "child_process";
|
|
4123
|
+
import fs12 from "fs";
|
|
4124
|
+
import os4 from "os";
|
|
4125
|
+
import path16 from "path";
|
|
4126
|
+
import { promisify as promisify3 } from "util";
|
|
4127
|
+
init_env();
|
|
4128
|
+
var pexec3 = promisify3(execFile5);
|
|
4129
|
+
var CODEX_MODELS_TIMEOUT_MS = 2e4;
|
|
4130
|
+
function builtinModels(id) {
|
|
4131
|
+
return (AGENT_CLIS.find((a) => a.id === id) ?? AGENT_CLIS[0]).models;
|
|
4132
|
+
}
|
|
4133
|
+
function defaultCodexHome() {
|
|
4134
|
+
const configured = process.env.CODEX_HOME?.trim();
|
|
4135
|
+
return configured ? configured : path16.join(os4.homedir(), ".codex");
|
|
4136
|
+
}
|
|
4137
|
+
async function defaultRunCodexModels() {
|
|
4138
|
+
const { stdout } = await pexec3("codex", ["debug", "models"], {
|
|
4139
|
+
env: execEnv(),
|
|
4140
|
+
timeout: CODEX_MODELS_TIMEOUT_MS,
|
|
4141
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
4142
|
+
windowsHide: true
|
|
4143
|
+
});
|
|
4144
|
+
return stdout;
|
|
4145
|
+
}
|
|
4146
|
+
function parseCodexCatalog(raw) {
|
|
4147
|
+
const body = JSON.parse(raw);
|
|
4148
|
+
if (!Array.isArray(body.models)) throw new Error("Codex model catalog has no models array");
|
|
4149
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4150
|
+
const models = [];
|
|
4151
|
+
for (const entry of body.models) {
|
|
4152
|
+
if (!entry || typeof entry !== "object") continue;
|
|
4153
|
+
const { slug, display_name: displayName, visibility } = entry;
|
|
4154
|
+
if (typeof slug !== "string" || !slug.trim() || visibility === "hide") continue;
|
|
4155
|
+
const id = slug.trim();
|
|
4156
|
+
if (seen.has(id)) continue;
|
|
4157
|
+
seen.add(id);
|
|
4158
|
+
models.push({
|
|
4159
|
+
id,
|
|
4160
|
+
label: typeof displayName === "string" && displayName.trim() ? displayName.trim() : id
|
|
4161
|
+
});
|
|
4162
|
+
}
|
|
4163
|
+
if (models.length === 0) throw new Error("Codex model catalog lists no selectable models");
|
|
4164
|
+
return models;
|
|
4165
|
+
}
|
|
4166
|
+
function errorMessage(err) {
|
|
4167
|
+
return err instanceof Error ? err.message : String(err);
|
|
4168
|
+
}
|
|
4169
|
+
async function listAgentModels(id, opts = {}) {
|
|
4170
|
+
if (id !== "codex") return { models: builtinModels(id), source: "builtin" };
|
|
4171
|
+
if (opts.refresh) {
|
|
4172
|
+
try {
|
|
4173
|
+
const stdout = await (opts.runCodexModels ?? defaultRunCodexModels)();
|
|
4174
|
+
return { models: parseCodexCatalog(stdout), source: "codex-cli" };
|
|
4175
|
+
} catch (err) {
|
|
4176
|
+
return { models: builtinModels(id), source: "builtin", error: errorMessage(err) };
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
const cachePath = path16.join(opts.codexHome ?? defaultCodexHome(), "models_cache.json");
|
|
4180
|
+
try {
|
|
4181
|
+
const raw = await fs12.promises.readFile(cachePath, "utf8");
|
|
4182
|
+
return { models: parseCodexCatalog(raw), source: "codex-cache" };
|
|
4183
|
+
} catch (err) {
|
|
4184
|
+
const missing = err.code === "ENOENT";
|
|
4185
|
+
return {
|
|
4186
|
+
models: builtinModels(id),
|
|
4187
|
+
source: "builtin",
|
|
4188
|
+
...missing ? {} : { error: errorMessage(err) }
|
|
4189
|
+
};
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
// packages/core/src/github-auth.ts
|
|
4194
|
+
init_env();
|
|
4195
|
+
import { execFile as execFile6 } from "child_process";
|
|
4196
|
+
import { promisify as promisify4 } from "util";
|
|
4197
|
+
import * as nodePty3 from "node-pty";
|
|
4198
|
+
var pexec4 = promisify4(execFile6);
|
|
4199
|
+
var GITHUB_HOST = "github.com";
|
|
4200
|
+
var DEVICE_URL = "https://github.com/login/device";
|
|
4201
|
+
var activeLogin = null;
|
|
4202
|
+
function stripAnsi(value) {
|
|
4203
|
+
return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x07/g, "");
|
|
4204
|
+
}
|
|
4205
|
+
function normalizeCode(code2) {
|
|
4206
|
+
return code2.replace(/\s+/g, "").toUpperCase();
|
|
4207
|
+
}
|
|
4208
|
+
function extractDeviceCode(output) {
|
|
4209
|
+
const cleaned = stripAnsi(output);
|
|
4210
|
+
const explicit = cleaned.match(/one-time code:\s*([A-Z0-9-]+)/i);
|
|
4211
|
+
if (explicit?.[1]) return normalizeCode(explicit[1]);
|
|
4212
|
+
const fallback = cleaned.match(/\b([A-Z0-9]{4}-[A-Z0-9]{4})\b/i);
|
|
4213
|
+
return fallback?.[1] ? normalizeCode(fallback[1]) : null;
|
|
4214
|
+
}
|
|
4215
|
+
function statusFromBody(body) {
|
|
4216
|
+
const accounts = body.hosts?.[GITHUB_HOST] ?? [];
|
|
4217
|
+
const active = accounts.find((account) => account.active) ?? accounts[0];
|
|
4218
|
+
if (!active) return { installed: true, authenticated: false };
|
|
4219
|
+
return {
|
|
4220
|
+
installed: true,
|
|
4221
|
+
authenticated: Boolean(active.login) && !active.error,
|
|
4222
|
+
login: active.login,
|
|
4223
|
+
gitProtocol: active.gitProtocol,
|
|
4224
|
+
error: active.error
|
|
4225
|
+
};
|
|
4226
|
+
}
|
|
4227
|
+
function errorMessage2(err) {
|
|
4228
|
+
if (err instanceof Error) return err.message;
|
|
4229
|
+
return String(err);
|
|
4230
|
+
}
|
|
4231
|
+
async function getGitHubCliAuthStatus() {
|
|
4232
|
+
try {
|
|
4233
|
+
const { stdout } = await pexec4(
|
|
4234
|
+
"gh",
|
|
4235
|
+
["auth", "status", "--hostname", GITHUB_HOST, "--json", "hosts"],
|
|
4236
|
+
{
|
|
4237
|
+
env: execEnv(),
|
|
4238
|
+
timeout: 1e4,
|
|
4239
|
+
windowsHide: true
|
|
4240
|
+
}
|
|
4241
|
+
);
|
|
4242
|
+
const body = JSON.parse(stdout.toString());
|
|
4243
|
+
return statusFromBody(body);
|
|
4244
|
+
} catch (err) {
|
|
4245
|
+
const maybeStdout = err.stdout;
|
|
4246
|
+
if (maybeStdout) {
|
|
4247
|
+
try {
|
|
4248
|
+
const body = JSON.parse(maybeStdout.toString());
|
|
4249
|
+
return statusFromBody(body);
|
|
4250
|
+
} catch {
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
const message = errorMessage2(err);
|
|
4254
|
+
return {
|
|
4255
|
+
installed: !/ENOENT|not found/i.test(message),
|
|
4256
|
+
authenticated: false,
|
|
4257
|
+
error: message
|
|
4258
|
+
};
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
async function logoutGitHubCli() {
|
|
4262
|
+
const status = await getGitHubCliAuthStatus();
|
|
4263
|
+
if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
|
|
4264
|
+
if (!status.login) return status;
|
|
4265
|
+
await pexec4(
|
|
4266
|
+
"gh",
|
|
4267
|
+
["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
|
|
4268
|
+
{
|
|
4269
|
+
env: execEnv(),
|
|
4270
|
+
timeout: 1e4,
|
|
4271
|
+
windowsHide: true
|
|
4272
|
+
}
|
|
4273
|
+
);
|
|
4274
|
+
return getGitHubCliAuthStatus();
|
|
4275
|
+
}
|
|
4276
|
+
function cancelGitHubCliLogin() {
|
|
4277
|
+
if (!activeLogin) return;
|
|
4278
|
+
try {
|
|
4279
|
+
activeLogin.kill();
|
|
4280
|
+
} catch {
|
|
4281
|
+
}
|
|
4282
|
+
activeLogin = null;
|
|
4283
|
+
}
|
|
4284
|
+
function startGitHubCliLogin(onEvent) {
|
|
4285
|
+
cancelGitHubCliLogin();
|
|
4286
|
+
onEvent({ type: "starting" });
|
|
4287
|
+
let output = "";
|
|
4288
|
+
let sentBrowserChoice = false;
|
|
4289
|
+
let emittedCode = null;
|
|
4290
|
+
let continuedAfterCode = false;
|
|
4291
|
+
let completed = false;
|
|
4292
|
+
let proc;
|
|
4293
|
+
try {
|
|
4294
|
+
proc = nodePty3.spawn(
|
|
4295
|
+
"gh",
|
|
4296
|
+
["auth", "login", "--hostname", GITHUB_HOST, "--git-protocol", "https"],
|
|
4297
|
+
{
|
|
4298
|
+
name: "xterm-256color",
|
|
3744
4299
|
cwd: process.cwd(),
|
|
3745
4300
|
env: { ...execEnv(), BROWSER: "true", TERM: "xterm-256color" },
|
|
3746
4301
|
cols: 100,
|
|
@@ -3790,53 +4345,100 @@ function startGitHubCliLogin(onEvent) {
|
|
|
3790
4345
|
}
|
|
3791
4346
|
|
|
3792
4347
|
// packages/core/src/board-cli.ts
|
|
3793
|
-
import
|
|
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
|
-
var MAX_DECISION_BYTES = 256 * 1024;
|
|
3813
4367
|
function decisionsKey(worktreePath, branch) {
|
|
3814
|
-
return worktreePath ?
|
|
4368
|
+
return worktreePath ? path18.basename(worktreePath) : branch.replace(/\//g, "-");
|
|
3815
4369
|
}
|
|
3816
4370
|
function decisionsPath(workspacePath, key) {
|
|
3817
|
-
return
|
|
4371
|
+
return path18.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
|
|
3818
4372
|
}
|
|
3819
|
-
function
|
|
3820
|
-
const target = decisionsPath(workspacePath, key);
|
|
4373
|
+
function deleteDecisions(workspacePath, key) {
|
|
3821
4374
|
try {
|
|
3822
|
-
|
|
3823
|
-
if (!stat.isFile()) return { path: target, markdown: null };
|
|
3824
|
-
const buf = fs12.readFileSync(target);
|
|
3825
|
-
return {
|
|
3826
|
-
path: target,
|
|
3827
|
-
markdown: buf.subarray(0, MAX_DECISION_BYTES).toString("utf8"),
|
|
3828
|
-
updatedAt: stat.mtimeMs,
|
|
3829
|
-
...buf.byteLength > MAX_DECISION_BYTES ? { truncated: true } : {}
|
|
3830
|
-
};
|
|
4375
|
+
fs14.rmSync(decisionsPath(workspacePath, key), { force: true });
|
|
3831
4376
|
} catch {
|
|
3832
|
-
return { path: target, markdown: null };
|
|
3833
4377
|
}
|
|
3834
4378
|
}
|
|
3835
|
-
|
|
4379
|
+
|
|
4380
|
+
// packages/core/src/specs.ts
|
|
4381
|
+
import fs15 from "fs";
|
|
4382
|
+
import path19 from "path";
|
|
4383
|
+
var SPEC_FILE_NAME = "spec.md";
|
|
4384
|
+
var MAX_SPEC_BYTES = 256 * 1024;
|
|
4385
|
+
function isSpecPath(p) {
|
|
4386
|
+
return path19.posix.basename(p) === SPEC_FILE_NAME;
|
|
4387
|
+
}
|
|
4388
|
+
function clip2(buf) {
|
|
4389
|
+
return {
|
|
4390
|
+
markdown: buf.subarray(0, MAX_SPEC_BYTES).toString("utf8"),
|
|
4391
|
+
...buf.byteLength > MAX_SPEC_BYTES ? { truncated: true } : {}
|
|
4392
|
+
};
|
|
4393
|
+
}
|
|
4394
|
+
async function readWorktreeSpecs(worktreePath, baseBranch) {
|
|
4395
|
+
const baseRef = await resolveBaseRef(worktreePath, baseBranch);
|
|
4396
|
+
const entries = await collectDiffEntries(worktreePath, baseRef);
|
|
4397
|
+
const specs = [];
|
|
4398
|
+
for (const entry of entries) {
|
|
4399
|
+
if (entry.status === "D" || !isSpecPath(entry.path)) continue;
|
|
4400
|
+
try {
|
|
4401
|
+
const full = path19.join(worktreePath, entry.path);
|
|
4402
|
+
const stat = fs15.statSync(full);
|
|
4403
|
+
if (!stat.isFile()) continue;
|
|
4404
|
+
specs.push({ path: entry.path, ...clip2(fs15.readFileSync(full)), updatedAt: stat.mtimeMs });
|
|
4405
|
+
} catch {
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
return specs.sort((a, b) => a.path.localeCompare(b.path));
|
|
4409
|
+
}
|
|
4410
|
+
async function readBranchSpecs(projectPath, branch, baseBranch, changedPaths) {
|
|
4411
|
+
const ref = `refs/heads/${branch}`;
|
|
3836
4412
|
try {
|
|
3837
|
-
|
|
4413
|
+
await git(projectPath, ["rev-parse", "--verify", ref]);
|
|
3838
4414
|
} catch {
|
|
4415
|
+
return [];
|
|
4416
|
+
}
|
|
4417
|
+
let paths = changedPaths;
|
|
4418
|
+
if (!paths) {
|
|
4419
|
+
const baseRef = await resolveBaseRef(projectPath, baseBranch);
|
|
4420
|
+
const names = await git(projectPath, [
|
|
4421
|
+
"diff",
|
|
4422
|
+
"--name-only",
|
|
4423
|
+
"--diff-filter=d",
|
|
4424
|
+
`${baseRef}...${ref}`
|
|
4425
|
+
]).catch(() => "");
|
|
4426
|
+
paths = names.split("\n").map((l) => l.trim());
|
|
4427
|
+
}
|
|
4428
|
+
const specs = [];
|
|
4429
|
+
for (const p of paths.filter(isSpecPath)) {
|
|
4430
|
+
try {
|
|
4431
|
+
const content = await git(projectPath, ["show", `${ref}:${p}`]);
|
|
4432
|
+
const ct = await git(projectPath, ["log", "-1", "--format=%ct", ref, "--", p]);
|
|
4433
|
+
specs.push({
|
|
4434
|
+
path: p,
|
|
4435
|
+
...clip2(Buffer.from(content, "utf8")),
|
|
4436
|
+
...ct ? { updatedAt: Number(ct) * 1e3 } : {}
|
|
4437
|
+
});
|
|
4438
|
+
} catch {
|
|
4439
|
+
}
|
|
3839
4440
|
}
|
|
4441
|
+
return specs.sort((a, b) => a.path.localeCompare(b.path));
|
|
3840
4442
|
}
|
|
3841
4443
|
|
|
3842
4444
|
// packages/core/src/chat-session.ts
|
|
@@ -4042,432 +4644,89 @@ ${attachmentLines.join("\n")}` : text;
|
|
|
4042
4644
|
phaseType: "thinking",
|
|
4043
4645
|
phaseSummary: "Thinking\u2026",
|
|
4044
4646
|
phaseDetail: block.thinking,
|
|
4045
|
-
done: true
|
|
4046
|
-
});
|
|
4047
|
-
} else if (block.type === "tool_use" && block.name) {
|
|
4048
|
-
const toolId = block.id ?? "";
|
|
4049
|
-
toolNames.set(toolId, block.name);
|
|
4050
|
-
const { summary, detail } = summarizeToolUse(block.name, block.input ?? {});
|
|
4051
|
-
pushPhase({
|
|
4052
|
-
taskId,
|
|
4053
|
-
id: `${taskId}-${seq++}`,
|
|
4054
|
-
phaseType: "tool_use",
|
|
4055
|
-
phaseSummary: summary,
|
|
4056
|
-
phaseDetail: detail,
|
|
4057
|
-
done: true
|
|
4058
|
-
});
|
|
4059
|
-
}
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
if (event.type === "user" && event.message?.content) {
|
|
4063
|
-
for (const block of event.message.content) {
|
|
4064
|
-
if (block.type === "tool_result") {
|
|
4065
|
-
const toolName = toolNames.get(block.tool_use_id ?? "") ?? "Tool";
|
|
4066
|
-
const raw2 = block.content;
|
|
4067
|
-
const detail = typeof raw2 === "string" ? raw2 : Array.isArray(raw2) ? raw2.map((c) => c.text ?? "").join("\n") : "";
|
|
4068
|
-
pushPhase({
|
|
4069
|
-
taskId,
|
|
4070
|
-
id: `${taskId}-${seq++}`,
|
|
4071
|
-
phaseType: "tool_result",
|
|
4072
|
-
phaseSummary: `Got result from \`${toolName}\``,
|
|
4073
|
-
phaseDetail: detail,
|
|
4074
|
-
done: true
|
|
4075
|
-
});
|
|
4076
|
-
}
|
|
4077
|
-
}
|
|
4078
|
-
}
|
|
4079
|
-
if (event.type === "result" && event.result) {
|
|
4080
|
-
const remaining = event.result.slice(accumulated.length);
|
|
4081
|
-
if (remaining) push({ taskId, delta: remaining, done: false });
|
|
4082
|
-
accumulated = event.result;
|
|
4083
|
-
}
|
|
4084
|
-
} catch {
|
|
4085
|
-
}
|
|
4086
|
-
}
|
|
4087
|
-
});
|
|
4088
|
-
proc.stderr.on("data", (raw) => {
|
|
4089
|
-
const msg = raw.toString().trim();
|
|
4090
|
-
if (msg) push({ taskId, delta: "", done: false, error: msg });
|
|
4091
|
-
});
|
|
4092
|
-
proc.on("close", (code2) => {
|
|
4093
|
-
const assistantText = accumulated || "\uFF08\u7121\u56DE\u61C9\uFF09";
|
|
4094
|
-
appendMessage(taskId, {
|
|
4095
|
-
id: randomUUID3().slice(0, 8),
|
|
4096
|
-
role: "assistant",
|
|
4097
|
-
text: assistantText,
|
|
4098
|
-
ts: Date.now()
|
|
4099
|
-
});
|
|
4100
|
-
const error = code2 !== 0 ? `Process exited with code ${code2}` : void 0;
|
|
4101
|
-
push({ taskId, delta: assistantText, done: true, error });
|
|
4102
|
-
});
|
|
4103
|
-
return () => {
|
|
4104
|
-
try {
|
|
4105
|
-
proc.kill("SIGTERM");
|
|
4106
|
-
} catch {
|
|
4107
|
-
}
|
|
4108
|
-
};
|
|
4109
|
-
}
|
|
4110
|
-
var activeProcs = /* @__PURE__ */ new Map();
|
|
4111
|
-
function cancelChatSend(taskId) {
|
|
4112
|
-
activeProcs.get(taskId)?.();
|
|
4113
|
-
activeProcs.delete(taskId);
|
|
4114
|
-
}
|
|
4115
|
-
function cancelAllChatSends() {
|
|
4116
|
-
activeProcs.forEach((kill) => kill());
|
|
4117
|
-
activeProcs.clear();
|
|
4118
|
-
}
|
|
4119
|
-
function startChatSend(opts, sender) {
|
|
4120
|
-
cancelChatSend(opts.taskId);
|
|
4121
|
-
const kill = sendChatMessage(opts, sender);
|
|
4122
|
-
activeProcs.set(opts.taskId, kill);
|
|
4123
|
-
}
|
|
4124
|
-
|
|
4125
|
-
// packages/core/src/service.ts
|
|
4126
|
-
init_attachments();
|
|
4127
|
-
|
|
4128
|
-
// packages/core/src/library.ts
|
|
4129
|
-
import fs13 from "fs";
|
|
4130
|
-
import os4 from "os";
|
|
4131
|
-
import path17 from "path";
|
|
4132
|
-
var LIBRARY_DIR = "library";
|
|
4133
|
-
var LIBRARY_KINDS = ["skill", "prompt", "script"];
|
|
4134
|
-
var KIND_DIR = {
|
|
4135
|
-
skill: "skills",
|
|
4136
|
-
prompt: "prompts",
|
|
4137
|
-
script: "scripts"
|
|
4138
|
-
};
|
|
4139
|
-
var INDEX_FILE = "index.json";
|
|
4140
|
-
var MAX_NAME_BYTES = 100;
|
|
4141
|
-
function libraryPaths(root) {
|
|
4142
|
-
return {
|
|
4143
|
-
root,
|
|
4144
|
-
skills: path17.join(root, KIND_DIR.skill),
|
|
4145
|
-
prompts: path17.join(root, KIND_DIR.prompt),
|
|
4146
|
-
scripts: path17.join(root, KIND_DIR.script),
|
|
4147
|
-
plugin: path17.join(root, "plugin"),
|
|
4148
|
-
codexHome: path17.join(root, "codex-home")
|
|
4149
|
-
};
|
|
4150
|
-
}
|
|
4151
|
-
async function libraryRoot() {
|
|
4152
|
-
return path17.join(getPlatform().userDataDir(), LIBRARY_DIR);
|
|
4153
|
-
}
|
|
4154
|
-
function truncateUtf82(value, maxBytes) {
|
|
4155
|
-
let result = "";
|
|
4156
|
-
for (const character of value) {
|
|
4157
|
-
if (Buffer.byteLength(result + character) > maxBytes) break;
|
|
4158
|
-
result += character;
|
|
4159
|
-
}
|
|
4160
|
-
return result;
|
|
4161
|
-
}
|
|
4162
|
-
function safeLibraryName(name) {
|
|
4163
|
-
const basename2 = name.replace(/\\/g, "/").split("/").pop() ?? "";
|
|
4164
|
-
const cleaned = truncateUtf82(
|
|
4165
|
-
basename2.replace(/[\u0000-\u001f\u007f]/g, "").trim(),
|
|
4166
|
-
MAX_NAME_BYTES
|
|
4167
|
-
);
|
|
4168
|
-
return cleaned && cleaned !== "." && cleaned !== ".." ? cleaned : "untitled";
|
|
4169
|
-
}
|
|
4170
|
-
function entryKey(kind, name) {
|
|
4171
|
-
return `${kind}/${name}`;
|
|
4172
|
-
}
|
|
4173
|
-
function indexPath(root) {
|
|
4174
|
-
return path17.join(root, INDEX_FILE);
|
|
4175
|
-
}
|
|
4176
|
-
function readIndex(root) {
|
|
4177
|
-
try {
|
|
4178
|
-
const parsed = JSON.parse(fs13.readFileSync(indexPath(root), "utf8"));
|
|
4179
|
-
if (parsed && typeof parsed === "object" && "entries" in parsed) {
|
|
4180
|
-
const entries = parsed.entries;
|
|
4181
|
-
if (entries && typeof entries === "object") return { version: 1, entries };
|
|
4182
|
-
}
|
|
4183
|
-
} catch {
|
|
4184
|
-
}
|
|
4185
|
-
return { version: 1, entries: {} };
|
|
4186
|
-
}
|
|
4187
|
-
function writeIndex(root, index) {
|
|
4188
|
-
fs13.mkdirSync(root, { recursive: true });
|
|
4189
|
-
fs13.writeFileSync(indexPath(root), `${JSON.stringify(index, null, 2)}
|
|
4190
|
-
`);
|
|
4191
|
-
}
|
|
4192
|
-
function ensureLibrary(root) {
|
|
4193
|
-
const paths = libraryPaths(root);
|
|
4194
|
-
for (const kind of LIBRARY_KINDS) {
|
|
4195
|
-
fs13.mkdirSync(path17.join(root, KIND_DIR[kind]), { recursive: true });
|
|
4196
|
-
}
|
|
4197
|
-
return paths;
|
|
4198
|
-
}
|
|
4199
|
-
function readSkillDescription(skillMdPath) {
|
|
4200
|
-
let content;
|
|
4201
|
-
try {
|
|
4202
|
-
content = fs13.readFileSync(skillMdPath, "utf8");
|
|
4203
|
-
} catch {
|
|
4204
|
-
return void 0;
|
|
4205
|
-
}
|
|
4206
|
-
const lines = content.split(/\r?\n/);
|
|
4207
|
-
if (lines[0]?.trim() !== "---") return void 0;
|
|
4208
|
-
for (let i = 1; i < lines.length; i += 1) {
|
|
4209
|
-
const line = lines[i];
|
|
4210
|
-
if (line.trim() === "---") break;
|
|
4211
|
-
const match = /^description:\s*(.+)$/.exec(line);
|
|
4212
|
-
if (match) return match[1].trim().replace(/^["']|["']$/g, "");
|
|
4213
|
-
}
|
|
4214
|
-
return void 0;
|
|
4215
|
-
}
|
|
4216
|
-
function entryPath(root, kind, name) {
|
|
4217
|
-
return path17.join(root, KIND_DIR[kind], name);
|
|
4218
|
-
}
|
|
4219
|
-
function listLibrary(root) {
|
|
4220
|
-
const index = readIndex(root);
|
|
4221
|
-
const entries = [];
|
|
4222
|
-
for (const kind of LIBRARY_KINDS) {
|
|
4223
|
-
const dir = path17.join(root, KIND_DIR[kind]);
|
|
4224
|
-
let dirents;
|
|
4225
|
-
try {
|
|
4226
|
-
dirents = fs13.readdirSync(dir, { withFileTypes: true });
|
|
4227
|
-
} catch {
|
|
4228
|
-
continue;
|
|
4229
|
-
}
|
|
4230
|
-
for (const dirent of dirents) {
|
|
4231
|
-
if (dirent.name.startsWith(".")) continue;
|
|
4232
|
-
const wantsDirectory = kind === "skill";
|
|
4233
|
-
let stat;
|
|
4234
|
-
try {
|
|
4235
|
-
stat = fs13.statSync(path17.join(dir, dirent.name));
|
|
4647
|
+
done: true
|
|
4648
|
+
});
|
|
4649
|
+
} else if (block.type === "tool_use" && block.name) {
|
|
4650
|
+
const toolId = block.id ?? "";
|
|
4651
|
+
toolNames.set(toolId, block.name);
|
|
4652
|
+
const { summary, detail } = summarizeToolUse(block.name, block.input ?? {});
|
|
4653
|
+
pushPhase({
|
|
4654
|
+
taskId,
|
|
4655
|
+
id: `${taskId}-${seq++}`,
|
|
4656
|
+
phaseType: "tool_use",
|
|
4657
|
+
phaseSummary: summary,
|
|
4658
|
+
phaseDetail: detail,
|
|
4659
|
+
done: true
|
|
4660
|
+
});
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4664
|
+
if (event.type === "user" && event.message?.content) {
|
|
4665
|
+
for (const block of event.message.content) {
|
|
4666
|
+
if (block.type === "tool_result") {
|
|
4667
|
+
const toolName = toolNames.get(block.tool_use_id ?? "") ?? "Tool";
|
|
4668
|
+
const raw2 = block.content;
|
|
4669
|
+
const detail = typeof raw2 === "string" ? raw2 : Array.isArray(raw2) ? raw2.map((c) => c.text ?? "").join("\n") : "";
|
|
4670
|
+
pushPhase({
|
|
4671
|
+
taskId,
|
|
4672
|
+
id: `${taskId}-${seq++}`,
|
|
4673
|
+
phaseType: "tool_result",
|
|
4674
|
+
phaseSummary: `Got result from \`${toolName}\``,
|
|
4675
|
+
phaseDetail: detail,
|
|
4676
|
+
done: true
|
|
4677
|
+
});
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
if (event.type === "result" && event.result) {
|
|
4682
|
+
const remaining = event.result.slice(accumulated.length);
|
|
4683
|
+
if (remaining) push({ taskId, delta: remaining, done: false });
|
|
4684
|
+
accumulated = event.result;
|
|
4685
|
+
}
|
|
4236
4686
|
} catch {
|
|
4237
|
-
continue;
|
|
4238
4687
|
}
|
|
4239
|
-
if (stat.isDirectory() !== wantsDirectory) continue;
|
|
4240
|
-
const name = dirent.name;
|
|
4241
|
-
const full = path17.join(dir, name);
|
|
4242
|
-
if (wantsDirectory && !fs13.existsSync(path17.join(full, "SKILL.md"))) continue;
|
|
4243
|
-
const key = entryKey(kind, name);
|
|
4244
|
-
const record = index.entries[key] ?? {};
|
|
4245
|
-
entries.push({
|
|
4246
|
-
key,
|
|
4247
|
-
kind,
|
|
4248
|
-
name,
|
|
4249
|
-
path: full,
|
|
4250
|
-
description: wantsDirectory ? readSkillDescription(path17.join(full, "SKILL.md")) : record.description,
|
|
4251
|
-
sourcePath: record.sourcePath,
|
|
4252
|
-
importedAt: record.importedAt,
|
|
4253
|
-
enabled: record.enabled !== false
|
|
4254
|
-
});
|
|
4255
|
-
}
|
|
4256
|
-
}
|
|
4257
|
-
return entries.sort((a, b) => a.key.localeCompare(b.key));
|
|
4258
|
-
}
|
|
4259
|
-
function annotate(root, key, patch) {
|
|
4260
|
-
const index = readIndex(root);
|
|
4261
|
-
index.entries[key] = { ...index.entries[key], ...patch };
|
|
4262
|
-
writeIndex(root, index);
|
|
4263
|
-
}
|
|
4264
|
-
function importEntry(root, kind, sourcePath) {
|
|
4265
|
-
ensureLibrary(root);
|
|
4266
|
-
const resolved = path17.resolve(sourcePath);
|
|
4267
|
-
const stat = fs13.statSync(resolved);
|
|
4268
|
-
if (kind === "skill") {
|
|
4269
|
-
if (!stat.isDirectory()) throw new Error("skill \u5FC5\u9808\u662F\u542B SKILL.md \u7684\u76EE\u9304");
|
|
4270
|
-
if (!fs13.existsSync(path17.join(resolved, "SKILL.md"))) {
|
|
4271
|
-
throw new Error("\u76EE\u9304\u5167\u627E\u4E0D\u5230 SKILL.md");
|
|
4272
4688
|
}
|
|
4273
|
-
} else if (!stat.isFile()) {
|
|
4274
|
-
throw new Error(`${kind} \u5FC5\u9808\u662F\u55AE\u4E00\u6A94\u6848`);
|
|
4275
|
-
}
|
|
4276
|
-
const name = safeLibraryName(path17.basename(resolved));
|
|
4277
|
-
const target = entryPath(root, kind, name);
|
|
4278
|
-
fs13.rmSync(target, { recursive: true, force: true });
|
|
4279
|
-
fs13.cpSync(resolved, target, { recursive: kind === "skill", dereference: true });
|
|
4280
|
-
if (kind === "script") fs13.chmodSync(target, 493);
|
|
4281
|
-
annotate(root, entryKey(kind, name), {
|
|
4282
|
-
sourcePath: resolved,
|
|
4283
|
-
importedAt: Date.now(),
|
|
4284
|
-
enabled: true
|
|
4285
4689
|
});
|
|
4286
|
-
|
|
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
|
|
4690
|
+
proc.stderr.on("data", (raw) => {
|
|
4691
|
+
const msg = raw.toString().trim();
|
|
4692
|
+
if (msg) push({ taskId, delta: "", done: false, error: msg });
|
|
4304
4693
|
});
|
|
4305
|
-
|
|
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
|
|
4694
|
+
proc.on("close", (code2) => {
|
|
4695
|
+
const assistantText = accumulated || "\uFF08\u7121\u56DE\u61C9\uFF09";
|
|
4696
|
+
appendMessage(taskId, {
|
|
4697
|
+
id: randomUUID3().slice(0, 8),
|
|
4698
|
+
role: "assistant",
|
|
4699
|
+
text: assistantText,
|
|
4700
|
+
ts: Date.now()
|
|
4368
4701
|
});
|
|
4369
|
-
|
|
4370
|
-
|
|
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) {
|
|
4702
|
+
const error = code2 !== 0 ? `Process exited with code ${code2}` : void 0;
|
|
4703
|
+
push({ taskId, delta: assistantText, done: true, error });
|
|
4704
|
+
});
|
|
4705
|
+
return () => {
|
|
4384
4706
|
try {
|
|
4385
|
-
|
|
4386
|
-
attempt();
|
|
4387
|
-
return true;
|
|
4707
|
+
proc.kill("SIGTERM");
|
|
4388
4708
|
} catch {
|
|
4389
4709
|
}
|
|
4390
|
-
}
|
|
4391
|
-
return false;
|
|
4392
|
-
}
|
|
4393
|
-
function buildCodexHome(root, worktreePath, userCodexHome = path17.join(os4.homedir(), ".codex")) {
|
|
4394
|
-
const paths = libraryPaths(root);
|
|
4395
|
-
const skillsDir = path17.join(paths.codexHome, "skills");
|
|
4396
|
-
fs13.rmSync(skillsDir, { recursive: true, force: true });
|
|
4397
|
-
fs13.mkdirSync(skillsDir, { recursive: true });
|
|
4398
|
-
for (const file of ["auth.json", "config.toml"]) {
|
|
4399
|
-
linkOrSkip(path17.join(userCodexHome, file), path17.join(paths.codexHome, file));
|
|
4400
|
-
}
|
|
4401
|
-
const result = {
|
|
4402
|
-
codexHome: paths.codexHome,
|
|
4403
|
-
project: [],
|
|
4404
|
-
user: [],
|
|
4405
|
-
library: [],
|
|
4406
|
-
shadowed: []
|
|
4407
4710
|
};
|
|
4408
|
-
const claimed = /* @__PURE__ */ new Map();
|
|
4409
|
-
const projectDir = worktreePath ? projectSkillsDir(worktreePath) : void 0;
|
|
4410
|
-
if (projectDir) {
|
|
4411
|
-
for (const name of skillNamesIn(projectDir)) {
|
|
4412
|
-
if (linkOrSkip(path17.join(projectDir, name), path17.join(skillsDir, name))) {
|
|
4413
|
-
result.project.push(name);
|
|
4414
|
-
claimed.set(name, "project");
|
|
4415
|
-
}
|
|
4416
|
-
}
|
|
4417
|
-
}
|
|
4418
|
-
const userDir = path17.join(userCodexHome, "skills");
|
|
4419
|
-
for (const name of skillNamesIn(userDir)) {
|
|
4420
|
-
const owner = claimed.get(name);
|
|
4421
|
-
if (owner) {
|
|
4422
|
-
result.shadowed.push({ name, keptFrom: owner, dropped: "user" });
|
|
4423
|
-
continue;
|
|
4424
|
-
}
|
|
4425
|
-
if (linkOrSkip(path17.join(userDir, name), path17.join(skillsDir, name))) {
|
|
4426
|
-
result.user.push(name);
|
|
4427
|
-
claimed.set(name, "user");
|
|
4428
|
-
}
|
|
4429
|
-
}
|
|
4430
|
-
for (const entry of listLibrary(root)) {
|
|
4431
|
-
if (entry.kind !== "skill" || !entry.enabled) continue;
|
|
4432
|
-
const owner = claimed.get(entry.name);
|
|
4433
|
-
if (owner) {
|
|
4434
|
-
result.shadowed.push({ name: entry.name, keptFrom: owner, dropped: "library" });
|
|
4435
|
-
continue;
|
|
4436
|
-
}
|
|
4437
|
-
fs13.cpSync(entry.path, path17.join(skillsDir, entry.name), {
|
|
4438
|
-
recursive: true,
|
|
4439
|
-
dereference: true
|
|
4440
|
-
});
|
|
4441
|
-
result.library.push(entry.name);
|
|
4442
|
-
}
|
|
4443
|
-
return result;
|
|
4444
4711
|
}
|
|
4445
|
-
|
|
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
|
-
};
|
|
4712
|
+
var activeProcs = /* @__PURE__ */ new Map();
|
|
4713
|
+
function cancelChatSend(taskId) {
|
|
4714
|
+
activeProcs.get(taskId)?.();
|
|
4715
|
+
activeProcs.delete(taskId);
|
|
4466
4716
|
}
|
|
4467
|
-
|
|
4468
|
-
|
|
4717
|
+
function cancelAllChatSends() {
|
|
4718
|
+
activeProcs.forEach((kill) => kill());
|
|
4719
|
+
activeProcs.clear();
|
|
4720
|
+
}
|
|
4721
|
+
function startChatSend(opts, sender) {
|
|
4722
|
+
cancelChatSend(opts.taskId);
|
|
4723
|
+
const kill = sendChatMessage(opts, sender);
|
|
4724
|
+
activeProcs.set(opts.taskId, kill);
|
|
4469
4725
|
}
|
|
4470
4726
|
|
|
4727
|
+
// packages/core/src/service.ts
|
|
4728
|
+
init_attachments();
|
|
4729
|
+
|
|
4471
4730
|
// packages/core/src/launch.ts
|
|
4472
4731
|
var ARTIFACTS_DIR_SUFFIX2 = ".artifacts";
|
|
4473
4732
|
var SCRATCH_DIR_NAME2 = "scratch";
|
|
@@ -4493,22 +4752,6 @@ function buildArtifactPrompt(artifactsDir = ARTIFACTS_FALLBACK_DIR2) {
|
|
|
4493
4752
|
"5. Artifact \u8CC7\u6599\u593E\u4F4D\u65BC worktree \u4E4B\u5916\uFF1B\u4E0D\u8981\u628A\u5176\u4E2D\u5167\u5BB9\u52A0\u5165 git commit\uFF0C\u4E5F\u4E0D\u8981\u628A\u6700\u7D42\u4EA4\u4ED8\u7269\u53EA\u7559\u5728\u9019\u88E1\u3002"
|
|
4494
4753
|
].join("\n");
|
|
4495
4754
|
}
|
|
4496
|
-
var DECISIONS_FILE_SUFFIX2 = ".DECISIONS.md";
|
|
4497
|
-
function taskDecisionsPath(worktreePath, workspacePath) {
|
|
4498
|
-
if (!worktreePath || !workspacePath) return null;
|
|
4499
|
-
return `${toShellPath(workspacePath)}/${pathBasename(worktreePath)}${DECISIONS_FILE_SUFFIX2}`;
|
|
4500
|
-
}
|
|
4501
|
-
function buildDecisionPrompt(decisionsPath2) {
|
|
4502
|
-
return [
|
|
4503
|
-
"\u6C7A\u7B56\u66F8\uFF08\u672C\u6B21 session \u5168\u7A0B\u9069\u7528\uFF09\uFF1A",
|
|
4504
|
-
`1. \u672C\u4EFB\u52D9\u7684\u6C7A\u7B56\u66F8\u662F ${decisionsPath2}\uFF1B\u505A\u51FA\u65B0\u6C7A\u7B56\u3001\u6216\u8ABF\u6574\u65E2\u6709\u6C7A\u7B56\u6642\uFF0C\u7576\u4E0B\u5C31\u66F4\u65B0\u5B83\uFF08\u4E0D\u5B58\u5728\u8ACB\u5EFA\u7ACB\uFF09\u3002`,
|
|
4505
|
-
"2. \u53EA\u5BEB\u300C\u9019\u6B21\u65B0\u589E\u4E86\u4EC0\u9EBC\u6C7A\u7B56\u3001\u8ABF\u6574\u4E86\u4EC0\u9EBC\u65E2\u6709\u6C7A\u7B56\u300D\u8207\u7406\u7531\u3002\u9700\u6C42\u8986\u8FF0\u3001\u9032\u5EA6\u56DE\u5831\u3001\u5BE6\u4F5C\u6D41\u6C34\u5E33\u3001\u672C\u4F86\u5C31\u6210\u7ACB\u7684\u6163\u4F8B\u90FD\u4E0D\u8981\u5BEB\u3002",
|
|
4506
|
-
"3. \u6BCF\u5247\u6C7A\u7B56\u4E00\u500B `## <\u4E00\u53E5\u8A71\u8B1B\u5B8C\u7684\u6C7A\u7B56>` \u6BB5\u843D\uFF0C\u5E95\u4E0B 2-4 \u884C\u4EA4\u4EE3\uFF1A\u70BA\u4EC0\u9EBC\u9019\u6A23\u9078\u3001\u5426\u6C7A\u4E86\u4EC0\u9EBC\u66FF\u4EE3\u65B9\u6848\u3001\u8AB0\u4E4B\u5F8C\u6703\u88AB\u9019\u500B\u6C7A\u7B56\u7D81\u4F4F\u3002",
|
|
4507
|
-
"4. \u8981\u8AAA\u660E\u6D41\u7A0B\u3001\u72C0\u614B\u8F49\u63DB\u6216\u6A21\u7D44\u95DC\u4FC2\u6642\uFF0C\u7528 mermaid fence \u756B\u5716\uFF0CVibeFlow \u6703\u76F4\u63A5\u6E32\u67D3\u3002",
|
|
4508
|
-
"5. \u9019\u6B21\u6C92\u6709\u4EFB\u4F55\u6C7A\u7B56\u5C31\u4E0D\u8981\u5EFA\u7ACB\u9019\u500B\u6A94\u6848\uFF1B\u5BE7\u53EF\u7559\u767D\uFF0C\u4E0D\u8981\u6E4A\u5167\u5BB9\u3002",
|
|
4509
|
-
"6. \u7528\u7E41\u9AD4\u4E2D\u6587\u66F8\u5BEB\uFF0C\u7A0B\u5F0F\u8B58\u5225\u5B57\u4FDD\u7559\u82F1\u6587\u3002"
|
|
4510
|
-
].join("\n");
|
|
4511
|
-
}
|
|
4512
4755
|
var AUTO_PERMISSION_MODE = "auto";
|
|
4513
4756
|
function claudePermissionFlag(autoMode) {
|
|
4514
4757
|
return autoMode ? ` --permission-mode ${AUTO_PERMISSION_MODE}` : "";
|
|
@@ -4637,11 +4880,9 @@ function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
|
|
|
4637
4880
|
const model = taskModel(task);
|
|
4638
4881
|
const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? ARTIFACTS_FALLBACK_DIR2;
|
|
4639
4882
|
const builtInPrompt = buildArtifactPrompt(artifactsDir);
|
|
4640
|
-
const decisionsPath2 = taskDecisionsPath(task.worktreePath, workspacePath);
|
|
4641
|
-
const decisionPrompt = decisionsPath2 ? buildDecisionPrompt(decisionsPath2) : "";
|
|
4642
4883
|
const libraryPrompt = opts?.library?.promptText?.trim();
|
|
4643
4884
|
const customPrompt = resolveSystemPrompt(systemPrompt);
|
|
4644
|
-
const sys = [builtInPrompt,
|
|
4885
|
+
const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
|
|
4645
4886
|
const includeTaskPrompt = opts?.includeTaskPrompt !== false;
|
|
4646
4887
|
const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
|
|
4647
4888
|
const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
|
|
@@ -4750,13 +4991,13 @@ function createCore({ sessions, bus, version }) {
|
|
|
4750
4991
|
}
|
|
4751
4992
|
function claudeSessionFile(cwd, sessionId) {
|
|
4752
4993
|
const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
4753
|
-
return
|
|
4994
|
+
return path20.join(os5.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
|
|
4754
4995
|
}
|
|
4755
4996
|
async function removeTaskWorktree(task) {
|
|
4756
4997
|
if (!task.projectPath || !task.worktreePath) return;
|
|
4757
4998
|
const branch = task.branch || fallbackBranchName(task.id);
|
|
4758
4999
|
await removeWorktree(task.projectPath, task.worktreePath);
|
|
4759
|
-
deleteArtifacts(task.workspacePath ??
|
|
5000
|
+
deleteArtifacts(task.workspacePath ?? path20.dirname(task.worktreePath), task.worktreePath);
|
|
4760
5001
|
await deleteBranch(task.projectPath, branch);
|
|
4761
5002
|
}
|
|
4762
5003
|
const handlers = {
|
|
@@ -4795,8 +5036,8 @@ function createCore({ sessions, bus, version }) {
|
|
|
4795
5036
|
"projects:listRecent": () => listRecentProjects(getStore()),
|
|
4796
5037
|
// `vibeflow open <path>` while a host runs: only the host writes the store.
|
|
4797
5038
|
"projects:record": (projectPath) => {
|
|
4798
|
-
const p =
|
|
4799
|
-
if (!
|
|
5039
|
+
const p = path20.resolve(str(projectPath, "projectPath"));
|
|
5040
|
+
if (!fs16.existsSync(p) || !fs16.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
|
|
4800
5041
|
recordRecentProject(getStore(), p);
|
|
4801
5042
|
return listRecentProjects(getStore());
|
|
4802
5043
|
},
|
|
@@ -4828,7 +5069,7 @@ function createCore({ sessions, bus, version }) {
|
|
|
4828
5069
|
},
|
|
4829
5070
|
// Discard everything the last run left behind — the PTY session, the
|
|
4830
5071
|
// sub-agent timeline, the conversation id, the code it changed, its artifacts
|
|
4831
|
-
// and its decision record — so the next launch starts over as if the card
|
|
5072
|
+
// and its legacy decision record — so the next launch starts over as if the card
|
|
4832
5073
|
// were new. Launching again is the caller's decision.
|
|
4833
5074
|
"vibeflow:resetTaskRun": async (taskId) => {
|
|
4834
5075
|
const task = requireTask(taskId);
|
|
@@ -4836,7 +5077,7 @@ function createCore({ sessions, bus, version }) {
|
|
|
4836
5077
|
if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
|
|
4837
5078
|
await teardownTask(task.id);
|
|
4838
5079
|
await resetWorktreeToBase(task.worktreePath, task.baseBranch);
|
|
4839
|
-
const workspacePath = task.workspacePath ??
|
|
5080
|
+
const workspacePath = task.workspacePath ?? path20.dirname(task.worktreePath);
|
|
4840
5081
|
deleteArtifacts(workspacePath, task.worktreePath);
|
|
4841
5082
|
deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
|
|
4842
5083
|
resetSubAgents(task.worktreePath);
|
|
@@ -4875,9 +5116,9 @@ function createCore({ sessions, bus, version }) {
|
|
|
4875
5116
|
if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
|
|
4876
5117
|
await teardownTask(existing.id);
|
|
4877
5118
|
await removeTaskWorktree(existing);
|
|
4878
|
-
const projectName =
|
|
5119
|
+
const projectName = path20.basename(nextProject);
|
|
4879
5120
|
const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
|
|
4880
|
-
await
|
|
5121
|
+
await fs16.promises.mkdir(workspacePath, { recursive: true });
|
|
4881
5122
|
const result = await provisionWorktree(
|
|
4882
5123
|
nextProject,
|
|
4883
5124
|
workspacePath,
|
|
@@ -4965,7 +5206,7 @@ function createCore({ sessions, bus, version }) {
|
|
|
4965
5206
|
"claude:sessionExists": (taskId) => {
|
|
4966
5207
|
const task = requireTask(taskId);
|
|
4967
5208
|
if (!task.worktreePath) return false;
|
|
4968
|
-
return
|
|
5209
|
+
return fs16.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
|
|
4969
5210
|
},
|
|
4970
5211
|
"sessions:list": async () => {
|
|
4971
5212
|
const keys = await sessions.list();
|
|
@@ -5012,16 +5253,19 @@ function createCore({ sessions, bus, version }) {
|
|
|
5012
5253
|
const task = requireTask(taskId);
|
|
5013
5254
|
if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
|
|
5014
5255
|
const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
|
|
5015
|
-
if (!
|
|
5256
|
+
if (!fs16.existsSync(dir)) return "artifacts directory does not exist yet";
|
|
5016
5257
|
return getPlatform().openPath(dir);
|
|
5017
5258
|
},
|
|
5018
|
-
"task:
|
|
5259
|
+
"task:getSpecs": async (taskId) => {
|
|
5019
5260
|
const task = requireTask(taskId);
|
|
5020
|
-
|
|
5021
|
-
|
|
5261
|
+
const base = task.baseBranch ?? "main";
|
|
5262
|
+
if (task.worktreePath) return readWorktreeSpecs(task.worktreePath, base);
|
|
5263
|
+
if (!task.projectPath) return [];
|
|
5264
|
+
const changed = task.outcome?.files.filter((f) => f.status !== "D").map((f) => f.path);
|
|
5265
|
+
return readBranchSpecs(task.projectPath, task.branch, base, changed);
|
|
5022
5266
|
},
|
|
5023
5267
|
"board:getCliLaunchInfo": () => boardCliLaunchInfo(),
|
|
5024
|
-
"library:list": async () => listLibrary(await libraryRoot()),
|
|
5268
|
+
"library:list": async () => listLibrary(await libraryRoot(), builtinSkillsDir()),
|
|
5025
5269
|
// The source path was picked by the user (native picker or typed path).
|
|
5026
5270
|
"library:import": async (payload) => {
|
|
5027
5271
|
const p = obj(payload, "payload");
|
|
@@ -5060,6 +5304,11 @@ function createCore({ sessions, bus, version }) {
|
|
|
5060
5304
|
deleteEntry(await libraryRoot(), libraryKind(p.kind), str(p.name, "name"));
|
|
5061
5305
|
return true;
|
|
5062
5306
|
},
|
|
5307
|
+
"library:restoreBuiltin": async (payload) => {
|
|
5308
|
+
const p = obj(payload, "payload");
|
|
5309
|
+
return restoreBuiltinSkill(await libraryRoot(), builtinSkillsDir(), str(p.name, "name"));
|
|
5310
|
+
},
|
|
5311
|
+
"library:removedBuiltins": async () => removedBuiltinSkills(await libraryRoot(), builtinSkillsDir()),
|
|
5063
5312
|
// Approve: commit everything in the worktree and push the branch upstream.
|
|
5064
5313
|
"git:approve": async (payload) => {
|
|
5065
5314
|
const p = obj(payload, "payload");
|
|
@@ -5165,9 +5414,9 @@ function createCore({ sessions, bus, version }) {
|
|
|
5165
5414
|
// Watch the containing directory, not the file: JsonStore renames a temp
|
|
5166
5415
|
// file over the target and so replaces the inode on every save.
|
|
5167
5416
|
watchStore() {
|
|
5168
|
-
const file =
|
|
5417
|
+
const file = path20.basename(getStorePath());
|
|
5169
5418
|
let debounce = null;
|
|
5170
|
-
storeWatcher =
|
|
5419
|
+
storeWatcher = fs16.watch(path20.dirname(getStorePath()), (_event, filename) => {
|
|
5171
5420
|
if (filename !== file) return;
|
|
5172
5421
|
if (debounce) clearTimeout(debounce);
|
|
5173
5422
|
debounce = setTimeout(() => {
|
|
@@ -5199,9 +5448,9 @@ function createSessionBackend(sink, choice) {
|
|
|
5199
5448
|
}
|
|
5200
5449
|
|
|
5201
5450
|
// packages/core/src/web-server.ts
|
|
5202
|
-
import
|
|
5451
|
+
import fs17 from "fs";
|
|
5203
5452
|
import http from "http";
|
|
5204
|
-
import
|
|
5453
|
+
import path21 from "path";
|
|
5205
5454
|
import { timingSafeEqual } from "crypto";
|
|
5206
5455
|
import { WebSocketServer } from "ws";
|
|
5207
5456
|
var TOKEN_COOKIE = "vf_token";
|
|
@@ -5254,14 +5503,14 @@ function resolveStatic(root, urlPath) {
|
|
|
5254
5503
|
return null;
|
|
5255
5504
|
}
|
|
5256
5505
|
if (decoded.includes("\0")) return null;
|
|
5257
|
-
const rel =
|
|
5258
|
-
const base =
|
|
5259
|
-
const candidates = [rel,
|
|
5506
|
+
const rel = path21.posix.normalize(decoded).replace(/^\/+/, "");
|
|
5507
|
+
const base = path21.resolve(root);
|
|
5508
|
+
const candidates = [rel, path21.posix.join(rel, "index.html"), `${rel}.html`];
|
|
5260
5509
|
for (const c of candidates) {
|
|
5261
|
-
const full =
|
|
5262
|
-
if (full !== base && !full.startsWith(base +
|
|
5510
|
+
const full = path21.resolve(base, c);
|
|
5511
|
+
if (full !== base && !full.startsWith(base + path21.sep)) return null;
|
|
5263
5512
|
try {
|
|
5264
|
-
if (
|
|
5513
|
+
if (fs17.statSync(full).isFile()) return full;
|
|
5265
5514
|
} catch {
|
|
5266
5515
|
}
|
|
5267
5516
|
}
|
|
@@ -5322,7 +5571,7 @@ async function startWebServer(options) {
|
|
|
5322
5571
|
send(res, 404, "Not found");
|
|
5323
5572
|
return;
|
|
5324
5573
|
}
|
|
5325
|
-
const ext =
|
|
5574
|
+
const ext = path21.extname(file).toLowerCase();
|
|
5326
5575
|
const immutable = url.pathname.startsWith("/_next/static/");
|
|
5327
5576
|
res.writeHead(200, {
|
|
5328
5577
|
"Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
|
|
@@ -5335,7 +5584,7 @@ async function startWebServer(options) {
|
|
|
5335
5584
|
res.end();
|
|
5336
5585
|
return;
|
|
5337
5586
|
}
|
|
5338
|
-
|
|
5587
|
+
fs17.createReadStream(file).pipe(res);
|
|
5339
5588
|
}
|
|
5340
5589
|
const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
|
|
5341
5590
|
function reject(socket, status, reason) {
|
|
@@ -5460,6 +5709,7 @@ async function startHost(options) {
|
|
|
5460
5709
|
return startHost(options);
|
|
5461
5710
|
}
|
|
5462
5711
|
try {
|
|
5712
|
+
await syncBuiltinsAtStartup();
|
|
5463
5713
|
const bus = new EventBus();
|
|
5464
5714
|
const sessions = createSessionBackend(bus.sink(), {
|
|
5465
5715
|
prefer: options.backend,
|
|
@@ -5506,7 +5756,7 @@ function loginUrl(lock, pathname = "/home/") {
|
|
|
5506
5756
|
return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
|
|
5507
5757
|
}
|
|
5508
5758
|
function isWebDir(dir) {
|
|
5509
|
-
return
|
|
5759
|
+
return fs18.existsSync(path22.join(dir, "home", "index.html"));
|
|
5510
5760
|
}
|
|
5511
5761
|
|
|
5512
5762
|
// packages/cli/src/remote.ts
|
|
@@ -5623,8 +5873,8 @@ function connectToHost(lock) {
|
|
|
5623
5873
|
}
|
|
5624
5874
|
|
|
5625
5875
|
// packages/cli/src/doctor.ts
|
|
5626
|
-
import
|
|
5627
|
-
import
|
|
5876
|
+
import fs19 from "fs";
|
|
5877
|
+
import path23 from "path";
|
|
5628
5878
|
import { execFile as execFile7 } from "child_process";
|
|
5629
5879
|
import { promisify as promisify5 } from "util";
|
|
5630
5880
|
init_env();
|
|
@@ -5764,10 +6014,10 @@ async function runDoctor(options) {
|
|
|
5764
6014
|
});
|
|
5765
6015
|
}
|
|
5766
6016
|
try {
|
|
5767
|
-
|
|
5768
|
-
const probe =
|
|
5769
|
-
|
|
5770
|
-
|
|
6017
|
+
fs19.mkdirSync(options.userDataDir, { recursive: true });
|
|
6018
|
+
const probe = path23.join(options.userDataDir, `.doctor-${process.pid}`);
|
|
6019
|
+
fs19.writeFileSync(probe, "");
|
|
6020
|
+
fs19.rmSync(probe);
|
|
5771
6021
|
checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
|
|
5772
6022
|
} catch (err) {
|
|
5773
6023
|
checks.push({
|
|
@@ -5821,24 +6071,24 @@ Options:
|
|
|
5821
6071
|
`.trim();
|
|
5822
6072
|
function detectLayout() {
|
|
5823
6073
|
const file = fileURLToPath(import.meta.url);
|
|
5824
|
-
const here =
|
|
5825
|
-
const bundledWeb =
|
|
5826
|
-
if (isWebDir(bundledWeb) ||
|
|
6074
|
+
const here = path26.dirname(file);
|
|
6075
|
+
const bundledWeb = path26.join(here, "web");
|
|
6076
|
+
if (isWebDir(bundledWeb) || path26.basename(here) === "dist") {
|
|
5827
6077
|
return {
|
|
5828
|
-
version: readVersion(
|
|
6078
|
+
version: readVersion(path26.join(here, "..", "package.json")),
|
|
5829
6079
|
webDir: isWebDir(bundledWeb) ? bundledWeb : null,
|
|
5830
6080
|
sourceRoot: null,
|
|
5831
6081
|
cliEntry: file
|
|
5832
6082
|
};
|
|
5833
6083
|
}
|
|
5834
|
-
const root =
|
|
6084
|
+
const root = path26.resolve(here, "..", "..", "..");
|
|
5835
6085
|
const override = process.env.VIBEFLOW_WEB_DIR;
|
|
5836
|
-
const webDir = override && isWebDir(override) ? override : isWebDir(
|
|
5837
|
-
return { version: readVersion(
|
|
6086
|
+
const webDir = override && isWebDir(override) ? override : isWebDir(path26.join(root, "app")) ? path26.join(root, "app") : null;
|
|
6087
|
+
return { version: readVersion(path26.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
|
|
5838
6088
|
}
|
|
5839
6089
|
function readVersion(pkgPath) {
|
|
5840
6090
|
try {
|
|
5841
|
-
return JSON.parse(
|
|
6091
|
+
return JSON.parse(fs21.readFileSync(pkgPath, "utf8")).version;
|
|
5842
6092
|
} catch {
|
|
5843
6093
|
return "0.0.0";
|
|
5844
6094
|
}
|
|
@@ -6044,8 +6294,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
|
|
|
6044
6294
|
console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
|
|
6045
6295
|
return 1;
|
|
6046
6296
|
}
|
|
6047
|
-
const dir =
|
|
6048
|
-
if (!
|
|
6297
|
+
const dir = path26.resolve(target);
|
|
6298
|
+
if (!fs21.existsSync(dir) || !fs21.statSync(dir).isDirectory()) {
|
|
6049
6299
|
console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
|
|
6050
6300
|
return 1;
|
|
6051
6301
|
}
|