opencode-skills-deck 0.2.0 → 0.3.1
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 +70 -56
- package/dist/gui-experimental.cjs +61 -0
- package/dist/index.js +1468 -118
- package/dist/index.js.map +4 -4
- package/dist/rpc.js +19929 -0
- package/dist/rpc.js.map +7 -0
- package/dist/tui.js +181 -57
- package/dist/tui.js.map +3 -3
- package/package.json +11 -4
package/dist/tui.js
CHANGED
|
@@ -19790,16 +19790,36 @@ function normalizeOpenKey(value) {
|
|
|
19790
19790
|
// packages/core/src/exec.ts
|
|
19791
19791
|
import { spawn } from "node:child_process";
|
|
19792
19792
|
var MAX_BUFFER = 16 * 1024 * 1024;
|
|
19793
|
-
|
|
19794
|
-
|
|
19793
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
19794
|
+
function createRunner(options = {}) {
|
|
19795
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
19796
|
+
return (command, args, options2) => new Promise((resolve2, reject) => {
|
|
19795
19797
|
const child = spawn(command, args, {
|
|
19796
|
-
cwd:
|
|
19798
|
+
cwd: options2?.cwd,
|
|
19797
19799
|
env: process.env,
|
|
19798
19800
|
stdio: ["ignore", "pipe", "pipe"]
|
|
19799
19801
|
});
|
|
19800
19802
|
let stdout = "";
|
|
19801
19803
|
let stderr = "";
|
|
19802
19804
|
let overflow = false;
|
|
19805
|
+
let settled = false;
|
|
19806
|
+
const fail = (error62) => {
|
|
19807
|
+
if (settled) return;
|
|
19808
|
+
settled = true;
|
|
19809
|
+
clearTimeout(timer);
|
|
19810
|
+
reject(error62);
|
|
19811
|
+
};
|
|
19812
|
+
const succeed = (result) => {
|
|
19813
|
+
if (settled) return;
|
|
19814
|
+
settled = true;
|
|
19815
|
+
clearTimeout(timer);
|
|
19816
|
+
resolve2(result);
|
|
19817
|
+
};
|
|
19818
|
+
const timer = setTimeout(() => {
|
|
19819
|
+
child.kill("SIGTERM");
|
|
19820
|
+
fail(new Error(`${command} ${args.join(" ")} timed out after ${timeoutMs}ms`));
|
|
19821
|
+
}, timeoutMs);
|
|
19822
|
+
timer.unref?.();
|
|
19803
19823
|
child.stdout?.setEncoding("utf8");
|
|
19804
19824
|
child.stderr?.setEncoding("utf8");
|
|
19805
19825
|
child.stdout?.on("data", (chunk) => {
|
|
@@ -19819,67 +19839,78 @@ function createRunner() {
|
|
|
19819
19839
|
}
|
|
19820
19840
|
});
|
|
19821
19841
|
child.on("error", (error62) => {
|
|
19822
|
-
|
|
19842
|
+
fail(new Error(`${command} ${args.join(" ")} failed: ${error62.message}`));
|
|
19823
19843
|
});
|
|
19824
19844
|
child.on("close", (code) => {
|
|
19845
|
+
if (settled) return;
|
|
19825
19846
|
if (overflow) {
|
|
19826
|
-
|
|
19847
|
+
fail(new Error(`${command} ${args.join(" ")} failed: \u51FA\u529B\u304C\u5927\u304D\u3059\u304E\u307E\u3059`));
|
|
19827
19848
|
return;
|
|
19828
19849
|
}
|
|
19829
19850
|
if (code === 0) {
|
|
19830
|
-
|
|
19851
|
+
succeed({ stdout, stderr });
|
|
19831
19852
|
return;
|
|
19832
19853
|
}
|
|
19833
19854
|
const detail = stderr.trim() || `exit code ${code ?? "unknown"}`;
|
|
19834
|
-
|
|
19855
|
+
fail(new Error(`${command} ${args.join(" ")} failed: ${detail}`));
|
|
19835
19856
|
});
|
|
19836
19857
|
});
|
|
19837
19858
|
}
|
|
19838
19859
|
|
|
19839
19860
|
// packages/core/src/paths.ts
|
|
19840
19861
|
import { homedir } from "node:os";
|
|
19841
|
-
import {
|
|
19862
|
+
import { posix, win32 } from "node:path";
|
|
19842
19863
|
var APP_DIR = "opencode-skills-deck";
|
|
19864
|
+
function pathFor(platform) {
|
|
19865
|
+
return platform === "win32" ? win32 : posix;
|
|
19866
|
+
}
|
|
19867
|
+
function styleOf(path) {
|
|
19868
|
+
return /^[A-Za-z]:[\\/]/.test(path) || path.startsWith("\\\\") ? "win32" : "linux";
|
|
19869
|
+
}
|
|
19843
19870
|
function configRootOf(platform, env, home) {
|
|
19844
|
-
|
|
19871
|
+
const path = pathFor(platform);
|
|
19872
|
+
return env.XDG_CONFIG_HOME ?? (platform === "win32" ? env.APPDATA ?? path.join(home, "AppData", "Roaming") : path.join(home, ".config"));
|
|
19845
19873
|
}
|
|
19846
19874
|
function stateRootOf(platform, env, home) {
|
|
19847
|
-
|
|
19875
|
+
const path = pathFor(platform);
|
|
19876
|
+
return env.XDG_STATE_HOME ?? (platform === "win32" ? env.LOCALAPPDATA ?? path.join(home, "AppData", "Local") : path.join(home, ".local", "state"));
|
|
19848
19877
|
}
|
|
19849
19878
|
function opencodeConfigDir(options = {}) {
|
|
19850
19879
|
const platform = options.platform ?? process.platform;
|
|
19851
19880
|
const env = options.env ?? process.env;
|
|
19852
19881
|
const home = options.home ?? homedir();
|
|
19853
|
-
return
|
|
19882
|
+
return pathFor(platform).join(configRootOf(platform, env, home), "opencode");
|
|
19854
19883
|
}
|
|
19855
19884
|
function opencodeSkillsDir(options = {}) {
|
|
19856
|
-
return
|
|
19885
|
+
return pathFor(options.platform ?? process.platform).join(opencodeConfigDir(options), "skills");
|
|
19857
19886
|
}
|
|
19858
19887
|
function repoSessionSlug(repo) {
|
|
19859
19888
|
return repo.replace(/[^a-zA-Z0-9._-]+/g, "__");
|
|
19860
19889
|
}
|
|
19861
19890
|
function sessionsDirFor(paths, repo) {
|
|
19862
|
-
|
|
19891
|
+
if (!repo) return paths.sessionsDir;
|
|
19892
|
+
return pathFor(styleOf(paths.sessionsDir)).join(paths.sessionsDir, repoSessionSlug(repo));
|
|
19863
19893
|
}
|
|
19864
19894
|
function resolvePaths(options = {}) {
|
|
19865
19895
|
const platform = options.platform ?? process.platform;
|
|
19866
19896
|
const env = options.env ?? process.env;
|
|
19867
19897
|
const home = options.home ?? homedir();
|
|
19898
|
+
const path = pathFor(platform);
|
|
19868
19899
|
const configRoot = configRootOf(platform, env, home);
|
|
19869
19900
|
const stateRoot = stateRootOf(platform, env, home);
|
|
19870
|
-
const configDir =
|
|
19871
|
-
const stateDir =
|
|
19901
|
+
const configDir = path.join(configRoot, APP_DIR);
|
|
19902
|
+
const stateDir = path.join(stateRoot, APP_DIR);
|
|
19872
19903
|
return {
|
|
19873
19904
|
configDir,
|
|
19874
19905
|
stateDir,
|
|
19875
|
-
sessionsDir:
|
|
19876
|
-
issueCacheDir:
|
|
19906
|
+
sessionsDir: path.join(stateDir, "sessions"),
|
|
19907
|
+
issueCacheDir: path.join(stateDir, "issues")
|
|
19877
19908
|
};
|
|
19878
19909
|
}
|
|
19879
19910
|
|
|
19880
19911
|
// packages/core/src/repo.ts
|
|
19881
19912
|
import { execFileSync } from "node:child_process";
|
|
19882
|
-
import { join as
|
|
19913
|
+
import { join as join2, posix as posix2, win32 as win322 } from "node:path";
|
|
19883
19914
|
var REMOTE_PATTERNS = [
|
|
19884
19915
|
/^git@[^:]+:(?<owner>[^/]+)\/(?<name>[^/]+?)(?:\.git)?$/,
|
|
19885
19916
|
/^(?:ssh|git|https?):\/\/(?:[^@/]+@)?[^/]+\/(?<owner>[^/]+)\/(?<name>[^/]+?)(?:\.git)?\/?$/
|
|
@@ -19904,7 +19935,7 @@ function branchName(issueNumber, title) {
|
|
|
19904
19935
|
return slug ? `issue/${issueNumber}-${slug}` : `issue/${issueNumber}`;
|
|
19905
19936
|
}
|
|
19906
19937
|
function issueWorktreePath(repoRoot, issueNumber) {
|
|
19907
|
-
return
|
|
19938
|
+
return join2(repoRoot, ".worktrees", `issue-${issueNumber}`);
|
|
19908
19939
|
}
|
|
19909
19940
|
function issueKey(issueNumber) {
|
|
19910
19941
|
return `issue-${issueNumber}`;
|
|
@@ -19960,10 +19991,10 @@ import {
|
|
|
19960
19991
|
watch,
|
|
19961
19992
|
writeFileSync
|
|
19962
19993
|
} from "node:fs";
|
|
19963
|
-
import { join as
|
|
19994
|
+
import { join as join3 } from "node:path";
|
|
19964
19995
|
var STARTUP_GRACE_MS = 2e4;
|
|
19965
19996
|
function sessionFilePath(dir, key) {
|
|
19966
|
-
return
|
|
19997
|
+
return join3(dir, `${key}.json`);
|
|
19967
19998
|
}
|
|
19968
19999
|
function readSessions(dir) {
|
|
19969
20000
|
if (!existsSync(dir)) return [];
|
|
@@ -20090,7 +20121,7 @@ function migrateLegacySessions(input2) {
|
|
|
20090
20121
|
// packages/core/src/terminal.ts
|
|
20091
20122
|
import { spawn as spawn2 } from "node:child_process";
|
|
20092
20123
|
import { existsSync as existsSync2 } from "node:fs";
|
|
20093
|
-
import { delimiter, join as
|
|
20124
|
+
import { delimiter, join as join4 } from "node:path";
|
|
20094
20125
|
var DARWIN_BINARIES = {
|
|
20095
20126
|
alacritty: "/Applications/Alacritty.app/Contents/MacOS/alacritty",
|
|
20096
20127
|
kitty: "/Applications/kitty.app/Contents/MacOS/kitty"
|
|
@@ -20099,7 +20130,7 @@ function whichInPath(command, env) {
|
|
|
20099
20130
|
const pathValue = env.PATH ?? "";
|
|
20100
20131
|
for (const directory of pathValue.split(delimiter)) {
|
|
20101
20132
|
if (!directory) continue;
|
|
20102
|
-
const candidate =
|
|
20133
|
+
const candidate = join4(directory, command);
|
|
20103
20134
|
if (existsSync2(candidate)) return candidate;
|
|
20104
20135
|
}
|
|
20105
20136
|
return null;
|
|
@@ -20174,8 +20205,8 @@ function launchInTerminal(input2) {
|
|
|
20174
20205
|
}
|
|
20175
20206
|
|
|
20176
20207
|
// packages/core/src/worktree.ts
|
|
20177
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
20178
|
-
import { join as
|
|
20208
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
20209
|
+
import { join as join5, resolve, sep } from "node:path";
|
|
20179
20210
|
async function defaultBranch(input2) {
|
|
20180
20211
|
if (input2.configured) return input2.configured;
|
|
20181
20212
|
try {
|
|
@@ -20192,22 +20223,54 @@ async function ensureWorktreesExcluded(input2) {
|
|
|
20192
20223
|
const { stdout } = await input2.runner("git", ["rev-parse", "--git-path", "info/exclude"], {
|
|
20193
20224
|
cwd: input2.repoRoot
|
|
20194
20225
|
});
|
|
20195
|
-
const path =
|
|
20226
|
+
const path = join5(input2.repoRoot, stdout.trim());
|
|
20196
20227
|
const existing = existsSync3(path) ? readFileSync3(path, "utf8") : "";
|
|
20197
20228
|
if (existing.split("\n").some((line) => line.trim() === ".worktrees/")) return;
|
|
20198
20229
|
const suffix = existing.endsWith("\n") || existing === "" ? "" : "\n";
|
|
20199
20230
|
writeFileSync2(path, `${existing}${suffix}.worktrees/
|
|
20200
20231
|
`);
|
|
20201
20232
|
}
|
|
20233
|
+
async function readWorktreeEntries(input2) {
|
|
20234
|
+
const { stdout } = await input2.runner("git", ["worktree", "list", "--porcelain"], {
|
|
20235
|
+
cwd: input2.repoRoot
|
|
20236
|
+
});
|
|
20237
|
+
const entries = [];
|
|
20238
|
+
let current = null;
|
|
20239
|
+
for (const line of stdout.split("\n")) {
|
|
20240
|
+
if (line.startsWith("worktree ")) {
|
|
20241
|
+
if (current) entries.push(current);
|
|
20242
|
+
current = {
|
|
20243
|
+
directory: line.slice("worktree ".length).trim(),
|
|
20244
|
+
branch: null,
|
|
20245
|
+
detached: false,
|
|
20246
|
+
bare: false
|
|
20247
|
+
};
|
|
20248
|
+
continue;
|
|
20249
|
+
}
|
|
20250
|
+
if (!current) continue;
|
|
20251
|
+
if (line.startsWith("branch ")) {
|
|
20252
|
+
current.branch = line.slice("branch ".length).replace(/^refs\/heads\//, "").trim();
|
|
20253
|
+
} else if (line === "detached") {
|
|
20254
|
+
current.detached = true;
|
|
20255
|
+
} else if (line === "bare") {
|
|
20256
|
+
current.bare = true;
|
|
20257
|
+
}
|
|
20258
|
+
}
|
|
20259
|
+
if (current) entries.push(current);
|
|
20260
|
+
return entries;
|
|
20261
|
+
}
|
|
20202
20262
|
async function registeredWorktrees(input2) {
|
|
20203
|
-
|
|
20204
|
-
return stdout.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.slice("worktree ".length).trim());
|
|
20263
|
+
return (await readWorktreeEntries(input2)).map((entry) => entry.directory);
|
|
20205
20264
|
}
|
|
20206
20265
|
function normalized(path) {
|
|
20207
|
-
|
|
20266
|
+
try {
|
|
20267
|
+
return realpathSync(path).replace(/\/+$/, "");
|
|
20268
|
+
} catch {
|
|
20269
|
+
return resolve(path).replace(/\/+$/, "");
|
|
20270
|
+
}
|
|
20208
20271
|
}
|
|
20209
20272
|
async function findOrphanWorktrees(input2) {
|
|
20210
|
-
const root = normalized(
|
|
20273
|
+
const root = normalized(join5(input2.repoRoot, ".worktrees"));
|
|
20211
20274
|
const known = new Set(
|
|
20212
20275
|
input2.records.map((record2) => record2.worktree).filter((value) => Boolean(value)).map(normalized)
|
|
20213
20276
|
);
|
|
@@ -20372,8 +20435,8 @@ function dispatchDialogue(input2) {
|
|
|
20372
20435
|
}
|
|
20373
20436
|
|
|
20374
20437
|
// packages/core/src/issue.ts
|
|
20375
|
-
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
20376
|
-
import { dirname, join as
|
|
20438
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
20439
|
+
import { dirname, join as join6 } from "node:path";
|
|
20377
20440
|
var SUMMARY_HEADING = /summary|要約|概要/i;
|
|
20378
20441
|
var CRITERIA_HEADING = /acceptance criteria|受け入れ基準|完了条件|acceptance/i;
|
|
20379
20442
|
var BULLET = /^\s*(?:[-*+]|\d+\.)\s+(?:\[[ xX]\]\s*)?(.+)$/;
|
|
@@ -20431,42 +20494,103 @@ async function fetchIssues(input2) {
|
|
|
20431
20494
|
return rows.map(toIssue).sort((left, right) => left.number - right.number);
|
|
20432
20495
|
}
|
|
20433
20496
|
function issueCachePath(paths, repo) {
|
|
20434
|
-
return
|
|
20497
|
+
return join6(paths.issueCacheDir, `${repo.owner}__${repo.name}.json`);
|
|
20498
|
+
}
|
|
20499
|
+
function normalizeCachedIssue(value) {
|
|
20500
|
+
if (!value || typeof value !== "object") return null;
|
|
20501
|
+
const issue2 = value;
|
|
20502
|
+
if (typeof issue2.number !== "number" || typeof issue2.title !== "string") return null;
|
|
20503
|
+
return {
|
|
20504
|
+
number: issue2.number,
|
|
20505
|
+
title: issue2.title,
|
|
20506
|
+
labels: Array.isArray(issue2.labels) ? issue2.labels.filter((label) => typeof label === "string") : [],
|
|
20507
|
+
state: typeof issue2.state === "string" ? issue2.state : "OPEN",
|
|
20508
|
+
updatedAt: typeof issue2.updatedAt === "string" ? issue2.updatedAt : (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
20509
|
+
summary: typeof issue2.summary === "string" ? issue2.summary : "",
|
|
20510
|
+
criteria: Array.isArray(issue2.criteria) ? issue2.criteria.filter((criterion) => typeof criterion === "string") : []
|
|
20511
|
+
};
|
|
20435
20512
|
}
|
|
20436
20513
|
function readIssueCache(path) {
|
|
20437
20514
|
try {
|
|
20438
20515
|
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
20439
|
-
|
|
20516
|
+
if (!Array.isArray(parsed.issues) || typeof parsed.fetchedAt !== "number") {
|
|
20517
|
+
rmSync2(path, { force: true });
|
|
20518
|
+
return null;
|
|
20519
|
+
}
|
|
20520
|
+
const issues = parsed.issues.map(normalizeCachedIssue);
|
|
20521
|
+
if (issues.some((issue2) => issue2 === null)) {
|
|
20522
|
+
rmSync2(path, { force: true });
|
|
20523
|
+
return null;
|
|
20524
|
+
}
|
|
20525
|
+
return { fetchedAt: parsed.fetchedAt, issues };
|
|
20440
20526
|
} catch {
|
|
20527
|
+
try {
|
|
20528
|
+
rmSync2(path, { force: true });
|
|
20529
|
+
} catch {
|
|
20530
|
+
}
|
|
20441
20531
|
return null;
|
|
20442
20532
|
}
|
|
20443
20533
|
}
|
|
20444
20534
|
function writeIssueCache(path, cache) {
|
|
20445
20535
|
mkdirSync2(dirname(path), { recursive: true });
|
|
20446
|
-
|
|
20536
|
+
const temporary = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
20537
|
+
writeFileSync3(temporary, `${JSON.stringify(cache, null, 2)}
|
|
20538
|
+
`);
|
|
20539
|
+
renameSync2(temporary, path);
|
|
20447
20540
|
}
|
|
20448
20541
|
var CACHE_TTL_MS = 6e4;
|
|
20449
|
-
|
|
20542
|
+
function classifyGhError(error62) {
|
|
20543
|
+
const message = error62 instanceof Error ? error62.message : String(error62);
|
|
20544
|
+
if (/not authenticated|authentication|401|gh auth login/i.test(message)) {
|
|
20545
|
+
return { code: "auth", message };
|
|
20546
|
+
}
|
|
20547
|
+
if (/rate limit|secondary rate|403/i.test(message)) {
|
|
20548
|
+
return { code: "rate_limit", message };
|
|
20549
|
+
}
|
|
20550
|
+
if (/ENOTFOUND|ECONNREFUSED|ETIMEDOUT|fetch failed|network/i.test(message)) {
|
|
20551
|
+
return { code: "network", message };
|
|
20552
|
+
}
|
|
20553
|
+
if (/^gh\b|gh issue/i.test(message)) return { code: "gh", message };
|
|
20554
|
+
return { code: "unknown", message };
|
|
20555
|
+
}
|
|
20556
|
+
async function loadIssuesResult(input2) {
|
|
20450
20557
|
const path = issueCachePath(input2.paths, input2.repo);
|
|
20451
20558
|
const now = input2.now ?? Date.now();
|
|
20452
|
-
|
|
20453
|
-
|
|
20454
|
-
|
|
20559
|
+
const cache = readIssueCache(path);
|
|
20560
|
+
if (!input2.force && cache && now - cache.fetchedAt < CACHE_TTL_MS) {
|
|
20561
|
+
return { issues: cache.issues, cached: true, stale: false, fetchedAt: cache.fetchedAt, error: null };
|
|
20455
20562
|
}
|
|
20456
|
-
|
|
20457
|
-
|
|
20458
|
-
|
|
20459
|
-
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
|
|
20463
|
-
|
|
20563
|
+
try {
|
|
20564
|
+
const issues = await fetchIssues({
|
|
20565
|
+
repo: input2.repo,
|
|
20566
|
+
config: input2.config,
|
|
20567
|
+
runner: input2.runner,
|
|
20568
|
+
cwd: input2.cwd
|
|
20569
|
+
});
|
|
20570
|
+
writeIssueCache(path, { fetchedAt: now, issues });
|
|
20571
|
+
return { issues, cached: false, stale: false, fetchedAt: now, error: null };
|
|
20572
|
+
} catch (error62) {
|
|
20573
|
+
const failure2 = classifyGhError(error62);
|
|
20574
|
+
if (cache) {
|
|
20575
|
+
return {
|
|
20576
|
+
issues: cache.issues,
|
|
20577
|
+
cached: true,
|
|
20578
|
+
stale: true,
|
|
20579
|
+
fetchedAt: cache.fetchedAt,
|
|
20580
|
+
error: failure2
|
|
20581
|
+
};
|
|
20582
|
+
}
|
|
20583
|
+
return { issues: [], cached: false, stale: false, fetchedAt: null, error: failure2 };
|
|
20584
|
+
}
|
|
20585
|
+
}
|
|
20586
|
+
async function loadIssues(input2) {
|
|
20587
|
+
return (await loadIssuesResult(input2)).issues;
|
|
20464
20588
|
}
|
|
20465
20589
|
|
|
20466
20590
|
// packages/core/src/skills.ts
|
|
20467
20591
|
import { createHash } from "node:crypto";
|
|
20468
|
-
import { cpSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync as
|
|
20469
|
-
import { join as
|
|
20592
|
+
import { cpSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
20593
|
+
import { join as join7, relative, sep as sep2 } from "node:path";
|
|
20470
20594
|
var MANIFEST_NAME = ".skills-deck.json";
|
|
20471
20595
|
function hashSkillDir(dir) {
|
|
20472
20596
|
const hash2 = createHash("sha256");
|
|
@@ -20475,7 +20599,7 @@ function hashSkillDir(dir) {
|
|
|
20475
20599
|
(left, right) => left.name.localeCompare(right.name)
|
|
20476
20600
|
);
|
|
20477
20601
|
for (const entry of entries) {
|
|
20478
|
-
const absolute =
|
|
20602
|
+
const absolute = join7(current, entry.name);
|
|
20479
20603
|
const key = relative(dir, absolute).split(sep2).join("/");
|
|
20480
20604
|
if (entry.isDirectory()) {
|
|
20481
20605
|
hash2.update(`d:${key}
|
|
@@ -20494,7 +20618,7 @@ function hashSkillDir(dir) {
|
|
|
20494
20618
|
}
|
|
20495
20619
|
function readManifest(targetDir) {
|
|
20496
20620
|
try {
|
|
20497
|
-
const parsed = JSON.parse(readFileSync5(
|
|
20621
|
+
const parsed = JSON.parse(readFileSync5(join7(targetDir, MANIFEST_NAME), "utf8"));
|
|
20498
20622
|
if (!parsed || typeof parsed.version !== "string" || typeof parsed.skills !== "object") return null;
|
|
20499
20623
|
return parsed;
|
|
20500
20624
|
} catch {
|
|
@@ -20502,19 +20626,19 @@ function readManifest(targetDir) {
|
|
|
20502
20626
|
}
|
|
20503
20627
|
}
|
|
20504
20628
|
function writeManifest(targetDir, manifest) {
|
|
20505
|
-
writeFileSync4(
|
|
20629
|
+
writeFileSync4(join7(targetDir, MANIFEST_NAME), `${JSON.stringify(manifest, null, 2)}
|
|
20506
20630
|
`);
|
|
20507
20631
|
}
|
|
20508
20632
|
function readBundledVersion(sourceDir) {
|
|
20509
20633
|
try {
|
|
20510
|
-
return readFileSync5(
|
|
20634
|
+
return readFileSync5(join7(sourceDir, "VERSION"), "utf8").trim() || "unknown";
|
|
20511
20635
|
} catch {
|
|
20512
20636
|
return "unknown";
|
|
20513
20637
|
}
|
|
20514
20638
|
}
|
|
20515
20639
|
function skillNames(sourceDir) {
|
|
20516
20640
|
if (!existsSync4(sourceDir)) return [];
|
|
20517
|
-
return readdirSync2(sourceDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => existsSync4(
|
|
20641
|
+
return readdirSync2(sourceDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((name) => existsSync4(join7(sourceDir, name, "SKILL.md"))).sort();
|
|
20518
20642
|
}
|
|
20519
20643
|
function installBundledSkills(input2) {
|
|
20520
20644
|
const result = { installed: [], updated: [], skipped: [], unchanged: [] };
|
|
@@ -20524,8 +20648,8 @@ function installBundledSkills(input2) {
|
|
|
20524
20648
|
const manifest = readManifest(input2.targetDir) ?? { version: version2, skills: {} };
|
|
20525
20649
|
const now = new Date(input2.now ?? Date.now()).toISOString();
|
|
20526
20650
|
for (const name of skillNames(input2.sourceDir)) {
|
|
20527
|
-
const source =
|
|
20528
|
-
const target =
|
|
20651
|
+
const source = join7(input2.sourceDir, name);
|
|
20652
|
+
const target = join7(input2.targetDir, name);
|
|
20529
20653
|
const sourceHash = hashSkillDir(source);
|
|
20530
20654
|
if (!existsSync4(target)) {
|
|
20531
20655
|
cpSync(source, target, { recursive: true });
|
|
@@ -20552,7 +20676,7 @@ function installBundledSkills(input2) {
|
|
|
20552
20676
|
result.unchanged.push(name);
|
|
20553
20677
|
continue;
|
|
20554
20678
|
}
|
|
20555
|
-
|
|
20679
|
+
rmSync3(target, { recursive: true, force: true });
|
|
20556
20680
|
cpSync(source, target, { recursive: true });
|
|
20557
20681
|
manifest.skills[name] = { hash: sourceHash, updatedAt: now };
|
|
20558
20682
|
result.updated.push(name);
|