skills 1.5.23 → 1.5.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +145 -47
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -685,6 +685,25 @@ const CLONE_TIMEOUT_MS = (() => {
|
|
|
685
685
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CLONE_TIMEOUT_MS;
|
|
686
686
|
})();
|
|
687
687
|
const execFileAsync = promisify(execFile);
|
|
688
|
+
function isCommitSha(ref) {
|
|
689
|
+
return /^[0-9a-f]{40}$/i.test(ref);
|
|
690
|
+
}
|
|
691
|
+
function isMissingRefError(message) {
|
|
692
|
+
return /Remote branch .* not found in upstream origin/i.test(message) || /couldn't find remote ref/i.test(message) || /upload-pack: not our ref/i.test(message);
|
|
693
|
+
}
|
|
694
|
+
async function cloneAtSha(url, sha, tempDir, extraEnv) {
|
|
695
|
+
const git = createGitClient(extraEnv);
|
|
696
|
+
await git.cwd(tempDir);
|
|
697
|
+
await git.init();
|
|
698
|
+
await git.addRemote("origin", url);
|
|
699
|
+
await git.fetch([
|
|
700
|
+
"--depth",
|
|
701
|
+
"1",
|
|
702
|
+
"origin",
|
|
703
|
+
sha
|
|
704
|
+
]);
|
|
705
|
+
await git.checkout("FETCH_HEAD");
|
|
706
|
+
}
|
|
688
707
|
var GitCloneError = class extends Error {
|
|
689
708
|
url;
|
|
690
709
|
isTimeout;
|
|
@@ -742,7 +761,7 @@ function isGitHubSsoAuthError(message) {
|
|
|
742
761
|
function isAuthFailure(message) {
|
|
743
762
|
return message.includes("Authentication failed") || message.includes("could not read Username") || message.includes("Permission denied") || message.includes("Repository not found") || message.includes("requested URL returned error: 403") || isGitHubSsoAuthError(message);
|
|
744
763
|
}
|
|
745
|
-
function createGitClient(
|
|
764
|
+
function createGitClient(extraEnv) {
|
|
746
765
|
const git = esm_default({
|
|
747
766
|
timeout: { block: CLONE_TIMEOUT_MS },
|
|
748
767
|
config: [
|
|
@@ -778,7 +797,7 @@ function createGitClient(sshCommand) {
|
|
|
778
797
|
GIT_TERMINAL_PROMPT: "0",
|
|
779
798
|
GIT_ALLOW_PROTOCOL: ALLOWED_GIT_PROTOCOLS,
|
|
780
799
|
GIT_LFS_SKIP_SMUDGE: "1",
|
|
781
|
-
...
|
|
800
|
+
...extraEnv
|
|
782
801
|
});
|
|
783
802
|
return git;
|
|
784
803
|
}
|
|
@@ -846,12 +865,18 @@ async function cloneRepo(url, ref) {
|
|
|
846
865
|
"--branch",
|
|
847
866
|
ref
|
|
848
867
|
] : ["--depth", "1"];
|
|
868
|
+
const refCanBeSha = !!ref && isCommitSha(ref);
|
|
849
869
|
const repo = parseGitHubRepoUrl(url);
|
|
850
870
|
try {
|
|
851
871
|
await createGitClient().clone(url, tempDir, cloneOptions);
|
|
852
872
|
return tempDir;
|
|
853
873
|
} catch (error) {
|
|
854
874
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
875
|
+
if (refCanBeSha && isMissingRefError(errorMessage)) try {
|
|
876
|
+
await resetTempDir(tempDir);
|
|
877
|
+
await cloneAtSha(url, ref, tempDir);
|
|
878
|
+
return tempDir;
|
|
879
|
+
} catch {}
|
|
855
880
|
const isTimeout = errorMessage.includes("block timeout") || errorMessage.includes("timed out");
|
|
856
881
|
const isAuthError = isAuthFailure(errorMessage);
|
|
857
882
|
if (isTimeout) {
|
|
@@ -868,7 +893,16 @@ async function cloneRepo(url, ref) {
|
|
|
868
893
|
} catch {}
|
|
869
894
|
try {
|
|
870
895
|
await resetTempDir(tempDir);
|
|
871
|
-
|
|
896
|
+
const sshEnv = { GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND ?? "ssh -o BatchMode=yes" };
|
|
897
|
+
try {
|
|
898
|
+
await createGitClient(sshEnv).clone(repo.sshUrl, tempDir, cloneOptions);
|
|
899
|
+
} catch (sshError) {
|
|
900
|
+
const sshMessage = sshError instanceof Error ? sshError.message : String(sshError);
|
|
901
|
+
if (refCanBeSha && isMissingRefError(sshMessage)) {
|
|
902
|
+
await resetTempDir(tempDir);
|
|
903
|
+
await cloneAtSha(repo.sshUrl, ref, tempDir, sshEnv);
|
|
904
|
+
} else throw sshError;
|
|
905
|
+
}
|
|
872
906
|
return tempDir;
|
|
873
907
|
} catch {}
|
|
874
908
|
}
|
|
@@ -1127,7 +1161,7 @@ const AGENT_PROJECT_SKILL_DIRS = [
|
|
|
1127
1161
|
".zcode/skills",
|
|
1128
1162
|
".zencoder/skills"
|
|
1129
1163
|
];
|
|
1130
|
-
function normalizeSkillName(name) {
|
|
1164
|
+
function normalizeSkillName$1(name) {
|
|
1131
1165
|
return name.toLowerCase().replace(/[\s_]+/g, "-");
|
|
1132
1166
|
}
|
|
1133
1167
|
function normalizeRelativePath(path) {
|
|
@@ -1207,7 +1241,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
1207
1241
|
const seenNames = /* @__PURE__ */ new Set();
|
|
1208
1242
|
const parsedSkillPaths = /* @__PURE__ */ new Set();
|
|
1209
1243
|
const localLock = await readLocalLock(basePath);
|
|
1210
|
-
const lockedSkillNames = new Set(Object.keys(localLock.skills).map(normalizeSkillName));
|
|
1244
|
+
const lockedSkillNames = new Set(Object.keys(localLock.skills).map(normalizeSkillName$1));
|
|
1211
1245
|
if (subpath && !isSubpathSafe(basePath, subpath)) throw new Error(`Invalid subpath: "${subpath}" resolves outside the repository directory. Subpath must not contain ".." segments that escape the base path.`);
|
|
1212
1246
|
const searchPath = subpath ? join(basePath, subpath) : basePath;
|
|
1213
1247
|
const pluginGroupings = await getPluginGroupings(searchPath);
|
|
@@ -1220,8 +1254,8 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
1220
1254
|
if (lockedSkillNames.size === 0) return false;
|
|
1221
1255
|
const relativeDir = normalizeRelativePath(relative(basePath, skill.path));
|
|
1222
1256
|
if (!AGENT_PROJECT_SKILL_DIRS.some((dir) => relativeDir === dir || relativeDir.startsWith(`${dir}/`))) return false;
|
|
1223
|
-
const skillName = normalizeSkillName(skill.name);
|
|
1224
|
-
const directoryName = normalizeSkillName(basename(skill.path));
|
|
1257
|
+
const skillName = normalizeSkillName$1(skill.name);
|
|
1258
|
+
const directoryName = normalizeSkillName$1(basename(skill.path));
|
|
1225
1259
|
return lockedSkillNames.has(skillName) || lockedSkillNames.has(directoryName);
|
|
1226
1260
|
};
|
|
1227
1261
|
const parseSkillAt = async (skillDir) => {
|
|
@@ -1254,7 +1288,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
1254
1288
|
const tryAddSkillAt = async (skillDir) => {
|
|
1255
1289
|
if (!await hasSkillMd(skillDir)) return false;
|
|
1256
1290
|
let skill = await parseSkillAt(skillDir);
|
|
1257
|
-
if (!skill || seenNames.has(skill.name)) return true;
|
|
1291
|
+
if (!skill || !options?.includeDuplicateNames && seenNames.has(skill.name)) return true;
|
|
1258
1292
|
if (isInstalledProjectSkill(skill)) return true;
|
|
1259
1293
|
skill = enhanceSkill(skill);
|
|
1260
1294
|
skills.push(skill);
|
|
@@ -1277,7 +1311,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
1277
1311
|
const allSkillDirs = await findSkillDirs(searchPath);
|
|
1278
1312
|
for (const skillDir of allSkillDirs) {
|
|
1279
1313
|
let skill = await parseSkillAt(skillDir);
|
|
1280
|
-
if (skill && !seenNames.has(skill.name) && !isInstalledProjectSkill(skill)) {
|
|
1314
|
+
if (skill && (options?.includeDuplicateNames || !seenNames.has(skill.name)) && !isInstalledProjectSkill(skill)) {
|
|
1281
1315
|
skill = enhanceSkill(skill);
|
|
1282
1316
|
skills.push(skill);
|
|
1283
1317
|
seenNames.add(skill.name);
|
|
@@ -4017,7 +4051,7 @@ async function tryBlobInstall(ownerRepo, options = {}) {
|
|
|
4017
4051
|
tree
|
|
4018
4052
|
};
|
|
4019
4053
|
}
|
|
4020
|
-
var version$1 = "1.5.
|
|
4054
|
+
var version$1 = "1.5.24";
|
|
4021
4055
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
4022
4056
|
const EVE_AGENT_LABEL = "eve agent";
|
|
4023
4057
|
async function isSourcePrivate(source) {
|
|
@@ -6366,6 +6400,46 @@ function parseRemoveOptions(args) {
|
|
|
6366
6400
|
options
|
|
6367
6401
|
};
|
|
6368
6402
|
}
|
|
6403
|
+
function normalizeSkillName(name) {
|
|
6404
|
+
return name.toLowerCase().replace(/[\s_]+/g, "-");
|
|
6405
|
+
}
|
|
6406
|
+
function normalizeSkillPath(path) {
|
|
6407
|
+
return path.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
6408
|
+
}
|
|
6409
|
+
function resolveSkillLocations(lockedSkillNames, lockSkills, discovered) {
|
|
6410
|
+
const discoveredPaths = new Set(discovered.map((skill) => normalizeSkillPath(skill.skillPath)));
|
|
6411
|
+
const pathsByName = /* @__PURE__ */ new Map();
|
|
6412
|
+
for (const skill of discovered) {
|
|
6413
|
+
const key = normalizeSkillName(skill.name);
|
|
6414
|
+
const paths = pathsByName.get(key) ?? /* @__PURE__ */ new Set();
|
|
6415
|
+
paths.add(normalizeSkillPath(skill.skillPath));
|
|
6416
|
+
pathsByName.set(key, paths);
|
|
6417
|
+
}
|
|
6418
|
+
const deletedSkills = [];
|
|
6419
|
+
const ambiguousSkills = [];
|
|
6420
|
+
const resolvedPaths = /* @__PURE__ */ new Map();
|
|
6421
|
+
for (const name of lockedSkillNames) {
|
|
6422
|
+
const lockedPath = lockSkills[name]?.skillPath;
|
|
6423
|
+
if (!lockedPath) continue;
|
|
6424
|
+
const normalizedLockedPath = normalizeSkillPath(lockedPath);
|
|
6425
|
+
const candidates = [...pathsByName.get(normalizeSkillName(name)) ?? []];
|
|
6426
|
+
if (candidates.length > 1) {
|
|
6427
|
+
ambiguousSkills.push(name);
|
|
6428
|
+
continue;
|
|
6429
|
+
}
|
|
6430
|
+
if (discoveredPaths.has(normalizedLockedPath)) {
|
|
6431
|
+
resolvedPaths.set(name, normalizedLockedPath);
|
|
6432
|
+
continue;
|
|
6433
|
+
}
|
|
6434
|
+
if (candidates.length === 1) resolvedPaths.set(name, candidates[0]);
|
|
6435
|
+
else deletedSkills.push(name);
|
|
6436
|
+
}
|
|
6437
|
+
return {
|
|
6438
|
+
deletedSkills,
|
|
6439
|
+
ambiguousSkills,
|
|
6440
|
+
resolvedPaths
|
|
6441
|
+
};
|
|
6442
|
+
}
|
|
6369
6443
|
const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
6370
6444
|
const RESET$1 = "\x1B[0m";
|
|
6371
6445
|
const BOLD$1 = "\x1B[1m";
|
|
@@ -6512,14 +6586,15 @@ async function promptDeletions(source, deletedSkills, isGlobal, options) {
|
|
|
6512
6586
|
});
|
|
6513
6587
|
}
|
|
6514
6588
|
}
|
|
6515
|
-
async function checkAndPromptForDeletions(source, allLockedForSource, lockSkills, isGlobal, options,
|
|
6516
|
-
const
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6589
|
+
async function checkAndPromptForDeletions(source, allLockedForSource, lockSkills, isGlobal, options, discovered) {
|
|
6590
|
+
const resolution = resolveSkillLocations(allLockedForSource, lockSkills, discovered);
|
|
6591
|
+
if (resolution.ambiguousSkills.length > 0) {
|
|
6592
|
+
console.log();
|
|
6593
|
+
console.log(`${DIM$1}Warning:${RESET$1} Multiple current paths match these skills from ${DIM$1}${source}${RESET$1}; skipping them rather than deleting or migrating the wrong skill:`);
|
|
6594
|
+
for (const name of resolution.ambiguousSkills) console.log(` ${DIM$1}•${RESET$1} ${sanitizeMetadata(name)}`);
|
|
6595
|
+
}
|
|
6596
|
+
await promptDeletions(source, resolution.deletedSkills, isGlobal, options);
|
|
6597
|
+
return resolution;
|
|
6523
6598
|
}
|
|
6524
6599
|
async function checkWellKnownForUpdates(baseUrl, items) {
|
|
6525
6600
|
let indexResult;
|
|
@@ -6703,36 +6778,42 @@ async function updateGlobalSkills(options = {}) {
|
|
|
6703
6778
|
const tree = await fetchRepoTree(source, firstEntry.ref, getGitHubToken);
|
|
6704
6779
|
if (tree) {
|
|
6705
6780
|
const discoveredPaths = tree.tree.filter((entry) => entry.type === "blob").map((entry) => entry.path);
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6781
|
+
if (!Object.entries(lock.skills).filter(([_, entry]) => entry.source === source && entry.ref === firstEntry.ref).map(([name, _]) => name).some((name) => lock.skills[name]?.skillPath && !discoveredPaths.includes(lock.skills[name].skillPath))) {
|
|
6782
|
+
for (const { name: skillName, entry } of itemsForSource) {
|
|
6783
|
+
const latestHash = getSkillFolderHashFromTree(tree, entry.skillPath);
|
|
6784
|
+
if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
|
|
6785
|
+
name: skillName,
|
|
6786
|
+
source,
|
|
6787
|
+
entry
|
|
6788
|
+
});
|
|
6789
|
+
}
|
|
6790
|
+
continue;
|
|
6716
6791
|
}
|
|
6717
|
-
|
|
6718
|
-
}
|
|
6719
|
-
console.log(` ${DIM$1}GitHub API unavailable; checking via Git clone${RESET$1}`);
|
|
6792
|
+
console.log(` ${DIM$1}Skill paths changed; resolving via Git clone${RESET$1}`);
|
|
6793
|
+
} else console.log(` ${DIM$1}GitHub API unavailable; checking via Git clone${RESET$1}`);
|
|
6720
6794
|
}
|
|
6721
6795
|
tempDir = await cloneRepo(sourceUrl, firstEntry.ref);
|
|
6722
|
-
const
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6796
|
+
const discoveredLocations = (await discoverSkills(tempDir, void 0, {
|
|
6797
|
+
fullDepth: true,
|
|
6798
|
+
includeDuplicateNames: true
|
|
6799
|
+
})).map((skill) => ({
|
|
6800
|
+
name: skill.name,
|
|
6801
|
+
skillPath: join(relative(tempDir, skill.path), "SKILL.md").split(sep).join("/")
|
|
6802
|
+
}));
|
|
6803
|
+
const resolution = await checkAndPromptForDeletions(source, Object.entries(lock.skills).filter(([_, entry]) => entry.source === source && entry.ref === firstEntry.ref).map(([name, _]) => name), lock.skills, true, options, discoveredLocations);
|
|
6804
|
+
const deletedSkillSet = new Set(resolution.deletedSkills);
|
|
6727
6805
|
for (const { name: skillName, entry } of itemsForSource) {
|
|
6728
6806
|
if (deletedSkillSet.has(skillName)) continue;
|
|
6729
|
-
const skillPath =
|
|
6730
|
-
if (!
|
|
6807
|
+
const skillPath = resolution.resolvedPaths.get(skillName);
|
|
6808
|
+
if (!skillPath) continue;
|
|
6731
6809
|
const latestHash = isGitHubSource && /^[0-9a-f]{40}$/i.test(entry.skillFolderHash) ? await getGitTreeHash(tempDir, skillPath) : await computeSkillFolderHash(join(tempDir, dirname(skillPath)));
|
|
6732
|
-
if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
|
|
6810
|
+
if (skillPath !== entry.skillPath || latestHash && latestHash !== entry.skillFolderHash) updates.push({
|
|
6733
6811
|
name: skillName,
|
|
6734
6812
|
source,
|
|
6735
|
-
entry
|
|
6813
|
+
entry: {
|
|
6814
|
+
...entry,
|
|
6815
|
+
skillPath
|
|
6816
|
+
}
|
|
6736
6817
|
});
|
|
6737
6818
|
}
|
|
6738
6819
|
} catch (error) {
|
|
@@ -6910,6 +6991,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
6910
6991
|
const allLockedForSource = Object.entries(localLock.skills).filter(([_, entry]) => (entry.sourceUrl || entry.source) === source && entry.ref === ref).map(([name, _]) => name);
|
|
6911
6992
|
let tempDir = null;
|
|
6912
6993
|
let deletedSkills = [];
|
|
6994
|
+
let resolvedPaths = null;
|
|
6913
6995
|
if (cloneSource === null) {
|
|
6914
6996
|
failCount += skillsForSource.length;
|
|
6915
6997
|
console.log(`${DIM$1}✗ Cannot update ${source}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$1}`);
|
|
@@ -6917,27 +6999,43 @@ async function updateProjectSkills(options = {}) {
|
|
|
6917
6999
|
}
|
|
6918
7000
|
try {
|
|
6919
7001
|
tempDir = await cloneRepo(cloneSource, ref);
|
|
6920
|
-
const
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
7002
|
+
const discoveredLocations = (await discoverSkills(tempDir, void 0, {
|
|
7003
|
+
fullDepth: true,
|
|
7004
|
+
includeDuplicateNames: true
|
|
7005
|
+
})).map((skill) => ({
|
|
7006
|
+
name: skill.name,
|
|
7007
|
+
skillPath: join(relative(tempDir, skill.path), "SKILL.md").split(sep).join("/")
|
|
7008
|
+
}));
|
|
7009
|
+
const resolution = await checkAndPromptForDeletions(source, allLockedForSource, localLock.skills, false, options, discoveredLocations);
|
|
7010
|
+
deletedSkills = resolution.deletedSkills;
|
|
7011
|
+
resolvedPaths = resolution.resolvedPaths;
|
|
6924
7012
|
} catch (error) {
|
|
6925
7013
|
console.log(`${DIM$1}✗ Failed to check for deleted skills from ${source}${RESET$1}`);
|
|
6926
7014
|
} finally {
|
|
6927
7015
|
if (tempDir) await cleanupTempDir(tempDir);
|
|
6928
7016
|
}
|
|
7017
|
+
if (resolvedPaths === null) {
|
|
7018
|
+
failCount += skillsForSource.length;
|
|
7019
|
+
continue;
|
|
7020
|
+
}
|
|
6929
7021
|
const remainingSkills = skillsForSource.filter((s) => !deletedSkills.includes(s.name));
|
|
6930
7022
|
for (const skill of remainingSkills) {
|
|
6931
7023
|
const safeName = sanitizeMetadata(skill.name);
|
|
7024
|
+
const resolvedPath = resolvedPaths?.get(skill.name);
|
|
7025
|
+
if (resolvedPaths && !resolvedPath) continue;
|
|
7026
|
+
const entry = resolvedPath ? {
|
|
7027
|
+
...skill.entry,
|
|
7028
|
+
skillPath: resolvedPath
|
|
7029
|
+
} : skill.entry;
|
|
6932
7030
|
console.log(`${TEXT$1}Updating ${safeName}…${RESET$1}`);
|
|
6933
|
-
const installUrl = buildLocalUpdateSource(
|
|
7031
|
+
const installUrl = buildLocalUpdateSource(entry);
|
|
6934
7032
|
if (!installUrl) {
|
|
6935
7033
|
failCount++;
|
|
6936
7034
|
console.log(` ${DIM$1}✗ Cannot update ${safeName}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$1}`);
|
|
6937
7035
|
continue;
|
|
6938
7036
|
}
|
|
6939
7037
|
const subagentArgs = skill.entry.subagents?.length ? ["--subagent", ...skill.entry.subagents.map((s) => s === "" ? "root" : s)] : [];
|
|
6940
|
-
const fullDepthArgs = shouldUseFullDepthForUpdate(
|
|
7038
|
+
const fullDepthArgs = shouldUseFullDepthForUpdate(entry) ? ["--full-depth"] : [];
|
|
6941
7039
|
if (spawnSync(process.execPath, [
|
|
6942
7040
|
cliEntry,
|
|
6943
7041
|
"add",
|
|
@@ -6954,7 +7052,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
6954
7052
|
"pipe"
|
|
6955
7053
|
],
|
|
6956
7054
|
encoding: "utf-8",
|
|
6957
|
-
env: getUpdateChildEnv(
|
|
7055
|
+
env: getUpdateChildEnv(entry.sourceType),
|
|
6958
7056
|
shell: false
|
|
6959
7057
|
}).status === 0) {
|
|
6960
7058
|
successCount++;
|