skills 1.5.23 → 1.5.25
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 +6 -6
- package/dist/cli.mjs +256 -99
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The CLI for the open agent skills ecosystem.
|
|
4
4
|
|
|
5
5
|
<!-- agent-list:start -->
|
|
6
|
-
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [
|
|
6
|
+
Supports **OpenCode**, **Claude Code**, **Codex**, **Cursor**, and [75 more](#supported-agents).
|
|
7
7
|
<!-- agent-list:end -->
|
|
8
8
|
|
|
9
9
|
[](https://skills.sh/vercel-labs/skills)
|
|
@@ -279,7 +279,7 @@ Skills can be installed to any of these agents:
|
|
|
279
279
|
| IBM Bob | `bob` | `.bob/skills/` | `~/.bob/skills/` |
|
|
280
280
|
| Claude Code | `claude-code` | `.claude/skills/` | `~/.claude/skills/` |
|
|
281
281
|
| OpenClaw | `openclaw` | `skills/` | `~/.openclaw/skills/` |
|
|
282
|
-
| Cline, Dexto, Kimi Code CLI, Loaf, Warp, Zed | `cline`, `dexto`, `kimi-code-cli`, `loaf`, `warp`, `zed` | `.agents/skills/` | `~/.agents/skills/` |
|
|
282
|
+
| Cline, Dexto, Kimi Code CLI, Loaf, Sarvam Code, Warp, Zed | `cline`, `dexto`, `kimi-code-cli`, `loaf`, `sarvam-code`, `warp`, `zed` | `.agents/skills/` | `~/.agents/skills/` |
|
|
283
283
|
| CodeArts Agent | `codearts-agent` | `.codeartsdoer/skills/` | `~/.codeartsdoer/skills/` |
|
|
284
284
|
| CodeBuddy | `codebuddy` | `.codebuddy/skills/` | `~/.codebuddy/skills/` |
|
|
285
285
|
| Codemaker | `codemaker` | `.codemaker/skills/` | `~/.codemaker/skills/` |
|
|
@@ -292,10 +292,11 @@ Skills can be installed to any of these agents:
|
|
|
292
292
|
| Cursor | `cursor` | `.agents/skills/` | `~/.cursor/skills/` |
|
|
293
293
|
| Deep Agents | `deepagents` | `.agents/skills/` | `~/.deepagents/agent/skills/` |
|
|
294
294
|
| Devin for Terminal | `devin` | `.devin/skills/` | `~/.config/devin/skills/` |
|
|
295
|
-
| Droid | `droid` | `.
|
|
295
|
+
| Droid | `droid` | `.agents/skills/` | `~/.factory/skills/` |
|
|
296
296
|
| Eve | `eve` | `agent/skills/` | N/A (project-only) |
|
|
297
297
|
| Firebender | `firebender` | `.agents/skills/` | `~/.firebender/skills/` |
|
|
298
298
|
| ForgeCode | `forgecode` | `.forge/skills/` | `~/.forge/skills/` |
|
|
299
|
+
| fx | `fx` | `.fx/skills/` | `~/.fx/skills/` |
|
|
299
300
|
| Gemini CLI | `gemini-cli` | `.agents/skills/` | `~/.gemini/skills/` |
|
|
300
301
|
| GitHub Copilot | `github-copilot` | `.agents/skills/` | `~/.copilot/skills/` |
|
|
301
302
|
| Goose | `goose` | `.goose/skills/` | `~/.config/goose/skills/` |
|
|
@@ -305,7 +306,7 @@ Skills can be installed to any of these agents:
|
|
|
305
306
|
| Jazz | `jazz` | `.jazz/skills/` | `~/.jazz/skills/` |
|
|
306
307
|
| Junie | `junie` | `.junie/skills/` | `~/.junie/skills/` |
|
|
307
308
|
| iFlow CLI | `iflow-cli` | `.iflow/skills/` | `~/.iflow/skills/` |
|
|
308
|
-
| Kilo Code | `kilo` | `.
|
|
309
|
+
| Kilo Code | `kilo` | `.agents/skills/` | `~/.kilo/skills/` |
|
|
309
310
|
| Kimchi | `kimchi` | `.kimchi/skills/` | `~/.config/kimchi/harness/skills/` |
|
|
310
311
|
| Kiro CLI | `kiro-cli` | `.kiro/skills/` | `~/.kiro/skills/` |
|
|
311
312
|
| Kode | `kode` | `.kode/skills/` | `~/.kode/skills/` |
|
|
@@ -430,9 +431,9 @@ discover `SKILL.md` files outside these container directories (e.g. under
|
|
|
430
431
|
- `.cortex/skills/`
|
|
431
432
|
- `.crush/skills/`
|
|
432
433
|
- `.devin/skills/`
|
|
433
|
-
- `.factory/skills/`
|
|
434
434
|
- `agent/skills/`
|
|
435
435
|
- `.forge/skills/`
|
|
436
|
+
- `.fx/skills/`
|
|
436
437
|
- `.goose/skills/`
|
|
437
438
|
- `.grok/skills/`
|
|
438
439
|
- `.hermes/skills/`
|
|
@@ -440,7 +441,6 @@ discover `SKILL.md` files outside these container directories (e.g. under
|
|
|
440
441
|
- `.jazz/skills/`
|
|
441
442
|
- `.junie/skills/`
|
|
442
443
|
- `.iflow/skills/`
|
|
443
|
-
- `.kilocode/skills/`
|
|
444
444
|
- `.kimchi/skills/`
|
|
445
445
|
- `.kiro/skills/`
|
|
446
446
|
- `.kode/skills/`
|
package/dist/cli.mjs
CHANGED
|
@@ -598,21 +598,30 @@ async function searchMultiselect(options) {
|
|
|
598
598
|
lastRenderHeight = countVisualRowsForLines(lines, process.stdout.columns);
|
|
599
599
|
};
|
|
600
600
|
const cleanup = () => {
|
|
601
|
+
rl.removeListener("close", closeHandler);
|
|
601
602
|
process.stdin.removeListener("keypress", keypressHandler);
|
|
602
603
|
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
603
604
|
rl.close();
|
|
604
605
|
};
|
|
606
|
+
let settled = false;
|
|
605
607
|
const submit = () => {
|
|
608
|
+
if (settled) return;
|
|
606
609
|
if (required && selected.size === 0 && lockedValues.length === 0) return;
|
|
610
|
+
settled = true;
|
|
607
611
|
render("submit");
|
|
608
612
|
cleanup();
|
|
609
613
|
resolve([...lockedValues, ...Array.from(selected)]);
|
|
610
614
|
};
|
|
611
615
|
const cancel = () => {
|
|
616
|
+
if (settled) return;
|
|
617
|
+
settled = true;
|
|
612
618
|
render("cancel");
|
|
613
619
|
cleanup();
|
|
614
620
|
resolve(cancelSymbol);
|
|
615
621
|
};
|
|
622
|
+
const closeHandler = () => {
|
|
623
|
+
cancel();
|
|
624
|
+
};
|
|
616
625
|
const keypressHandler = (_str, key) => {
|
|
617
626
|
if (!key) return;
|
|
618
627
|
const entries = buildSearchEntries(getFiltered(), selectGroups, collapsedGroups);
|
|
@@ -673,6 +682,11 @@ async function searchMultiselect(options) {
|
|
|
673
682
|
}
|
|
674
683
|
};
|
|
675
684
|
process.stdin.on("keypress", keypressHandler);
|
|
685
|
+
rl.on("close", closeHandler);
|
|
686
|
+
if (process.stdin.readableEnded || process.stdin.destroyed) {
|
|
687
|
+
cancel();
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
676
690
|
render();
|
|
677
691
|
});
|
|
678
692
|
}
|
|
@@ -685,6 +699,25 @@ const CLONE_TIMEOUT_MS = (() => {
|
|
|
685
699
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CLONE_TIMEOUT_MS;
|
|
686
700
|
})();
|
|
687
701
|
const execFileAsync = promisify(execFile);
|
|
702
|
+
function isCommitSha(ref) {
|
|
703
|
+
return /^[0-9a-f]{40}$/i.test(ref);
|
|
704
|
+
}
|
|
705
|
+
function isMissingRefError(message) {
|
|
706
|
+
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);
|
|
707
|
+
}
|
|
708
|
+
async function cloneAtSha(url, sha, tempDir, extraEnv) {
|
|
709
|
+
const git = createGitClient(extraEnv);
|
|
710
|
+
await git.cwd(tempDir);
|
|
711
|
+
await git.init();
|
|
712
|
+
await git.addRemote("origin", url);
|
|
713
|
+
await git.fetch([
|
|
714
|
+
"--depth",
|
|
715
|
+
"1",
|
|
716
|
+
"origin",
|
|
717
|
+
sha
|
|
718
|
+
]);
|
|
719
|
+
await git.checkout("FETCH_HEAD");
|
|
720
|
+
}
|
|
688
721
|
var GitCloneError = class extends Error {
|
|
689
722
|
url;
|
|
690
723
|
isTimeout;
|
|
@@ -742,7 +775,7 @@ function isGitHubSsoAuthError(message) {
|
|
|
742
775
|
function isAuthFailure(message) {
|
|
743
776
|
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
777
|
}
|
|
745
|
-
function createGitClient(
|
|
778
|
+
function createGitClient(extraEnv) {
|
|
746
779
|
const git = esm_default({
|
|
747
780
|
timeout: { block: CLONE_TIMEOUT_MS },
|
|
748
781
|
config: [
|
|
@@ -778,7 +811,7 @@ function createGitClient(sshCommand) {
|
|
|
778
811
|
GIT_TERMINAL_PROMPT: "0",
|
|
779
812
|
GIT_ALLOW_PROTOCOL: ALLOWED_GIT_PROTOCOLS,
|
|
780
813
|
GIT_LFS_SKIP_SMUDGE: "1",
|
|
781
|
-
...
|
|
814
|
+
...extraEnv
|
|
782
815
|
});
|
|
783
816
|
return git;
|
|
784
817
|
}
|
|
@@ -846,12 +879,18 @@ async function cloneRepo(url, ref) {
|
|
|
846
879
|
"--branch",
|
|
847
880
|
ref
|
|
848
881
|
] : ["--depth", "1"];
|
|
882
|
+
const refCanBeSha = !!ref && isCommitSha(ref);
|
|
849
883
|
const repo = parseGitHubRepoUrl(url);
|
|
850
884
|
try {
|
|
851
885
|
await createGitClient().clone(url, tempDir, cloneOptions);
|
|
852
886
|
return tempDir;
|
|
853
887
|
} catch (error) {
|
|
854
888
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
889
|
+
if (refCanBeSha && isMissingRefError(errorMessage)) try {
|
|
890
|
+
await resetTempDir(tempDir);
|
|
891
|
+
await cloneAtSha(url, ref, tempDir);
|
|
892
|
+
return tempDir;
|
|
893
|
+
} catch {}
|
|
855
894
|
const isTimeout = errorMessage.includes("block timeout") || errorMessage.includes("timed out");
|
|
856
895
|
const isAuthError = isAuthFailure(errorMessage);
|
|
857
896
|
if (isTimeout) {
|
|
@@ -868,7 +907,16 @@ async function cloneRepo(url, ref) {
|
|
|
868
907
|
} catch {}
|
|
869
908
|
try {
|
|
870
909
|
await resetTempDir(tempDir);
|
|
871
|
-
|
|
910
|
+
const sshEnv = { GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND ?? "ssh -o BatchMode=yes" };
|
|
911
|
+
try {
|
|
912
|
+
await createGitClient(sshEnv).clone(repo.sshUrl, tempDir, cloneOptions);
|
|
913
|
+
} catch (sshError) {
|
|
914
|
+
const sshMessage = sshError instanceof Error ? sshError.message : String(sshError);
|
|
915
|
+
if (refCanBeSha && isMissingRefError(sshMessage)) {
|
|
916
|
+
await resetTempDir(tempDir);
|
|
917
|
+
await cloneAtSha(repo.sshUrl, ref, tempDir, sshEnv);
|
|
918
|
+
} else throw sshError;
|
|
919
|
+
}
|
|
872
920
|
return tempDir;
|
|
873
921
|
} catch {}
|
|
874
922
|
}
|
|
@@ -1105,11 +1153,13 @@ const AGENT_PROJECT_SKILL_DIRS = [
|
|
|
1105
1153
|
".codex/skills",
|
|
1106
1154
|
".commandcode/skills",
|
|
1107
1155
|
".continue/skills",
|
|
1156
|
+
".factory/skills",
|
|
1108
1157
|
".github/skills",
|
|
1109
1158
|
".goose/skills",
|
|
1110
1159
|
".grok/skills",
|
|
1111
1160
|
".iflow/skills",
|
|
1112
1161
|
".junie/skills",
|
|
1162
|
+
".kilo/skills",
|
|
1113
1163
|
".kilocode/skills",
|
|
1114
1164
|
".kimchi/skills",
|
|
1115
1165
|
".kiro/skills",
|
|
@@ -1127,7 +1177,7 @@ const AGENT_PROJECT_SKILL_DIRS = [
|
|
|
1127
1177
|
".zcode/skills",
|
|
1128
1178
|
".zencoder/skills"
|
|
1129
1179
|
];
|
|
1130
|
-
function normalizeSkillName(name) {
|
|
1180
|
+
function normalizeSkillName$1(name) {
|
|
1131
1181
|
return name.toLowerCase().replace(/[\s_]+/g, "-");
|
|
1132
1182
|
}
|
|
1133
1183
|
function normalizeRelativePath(path) {
|
|
@@ -1207,7 +1257,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
1207
1257
|
const seenNames = /* @__PURE__ */ new Set();
|
|
1208
1258
|
const parsedSkillPaths = /* @__PURE__ */ new Set();
|
|
1209
1259
|
const localLock = await readLocalLock(basePath);
|
|
1210
|
-
const lockedSkillNames = new Set(Object.keys(localLock.skills).map(normalizeSkillName));
|
|
1260
|
+
const lockedSkillNames = new Set(Object.keys(localLock.skills).map(normalizeSkillName$1));
|
|
1211
1261
|
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
1262
|
const searchPath = subpath ? join(basePath, subpath) : basePath;
|
|
1213
1263
|
const pluginGroupings = await getPluginGroupings(searchPath);
|
|
@@ -1220,8 +1270,8 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
1220
1270
|
if (lockedSkillNames.size === 0) return false;
|
|
1221
1271
|
const relativeDir = normalizeRelativePath(relative(basePath, skill.path));
|
|
1222
1272
|
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));
|
|
1273
|
+
const skillName = normalizeSkillName$1(skill.name);
|
|
1274
|
+
const directoryName = normalizeSkillName$1(basename(skill.path));
|
|
1225
1275
|
return lockedSkillNames.has(skillName) || lockedSkillNames.has(directoryName);
|
|
1226
1276
|
};
|
|
1227
1277
|
const parseSkillAt = async (skillDir) => {
|
|
@@ -1254,7 +1304,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
1254
1304
|
const tryAddSkillAt = async (skillDir) => {
|
|
1255
1305
|
if (!await hasSkillMd(skillDir)) return false;
|
|
1256
1306
|
let skill = await parseSkillAt(skillDir);
|
|
1257
|
-
if (!skill || seenNames.has(skill.name)) return true;
|
|
1307
|
+
if (!skill || !options?.includeDuplicateNames && seenNames.has(skill.name)) return true;
|
|
1258
1308
|
if (isInstalledProjectSkill(skill)) return true;
|
|
1259
1309
|
skill = enhanceSkill(skill);
|
|
1260
1310
|
skills.push(skill);
|
|
@@ -1277,7 +1327,7 @@ async function discoverSkills(basePath, subpath, options) {
|
|
|
1277
1327
|
const allSkillDirs = await findSkillDirs(searchPath);
|
|
1278
1328
|
for (const skillDir of allSkillDirs) {
|
|
1279
1329
|
let skill = await parseSkillAt(skillDir);
|
|
1280
|
-
if (skill && !seenNames.has(skill.name) && !isInstalledProjectSkill(skill)) {
|
|
1330
|
+
if (skill && (options?.includeDuplicateNames || !seenNames.has(skill.name)) && !isInstalledProjectSkill(skill)) {
|
|
1281
1331
|
skill = enhanceSkill(skill);
|
|
1282
1332
|
skills.push(skill);
|
|
1283
1333
|
seenNames.add(skill.name);
|
|
@@ -1305,6 +1355,7 @@ const vibeHome = process.env.VIBE_HOME?.trim() || join(home, ".vibe");
|
|
|
1305
1355
|
const hermesHome = process.env.HERMES_HOME?.trim() || join(home, ".hermes");
|
|
1306
1356
|
const autohandHome = process.env.AUTOHAND_HOME?.trim() || join(home, ".autohand");
|
|
1307
1357
|
const grokHome = process.env.GROK_HOME?.trim() || join(home, ".grok");
|
|
1358
|
+
const sarvamHome = process.env.SARVAM_HOME?.trim() || join(home, ".sarvam");
|
|
1308
1359
|
const zedAppDataHome = process.env.APPDATA?.trim();
|
|
1309
1360
|
const zedFlatpakConfigHome = process.env.FLATPAK_XDG_CONFIG_HOME?.trim();
|
|
1310
1361
|
function packageJsonHasDependency(packageJsonPath, dependencyName) {
|
|
@@ -1357,6 +1408,7 @@ const agents = {
|
|
|
1357
1408
|
displayName: "Antigravity",
|
|
1358
1409
|
skillsDir: ".agents/skills",
|
|
1359
1410
|
globalSkillsDir: join(home, ".gemini/antigravity/skills"),
|
|
1411
|
+
showInUniversalPrompt: false,
|
|
1360
1412
|
detectInstalled: async () => {
|
|
1361
1413
|
return existsSync(join(home, ".gemini/antigravity"));
|
|
1362
1414
|
}
|
|
@@ -1366,6 +1418,7 @@ const agents = {
|
|
|
1366
1418
|
displayName: "Antigravity CLI",
|
|
1367
1419
|
skillsDir: ".agents/skills",
|
|
1368
1420
|
globalSkillsDir: join(home, ".gemini/antigravity-cli/skills"),
|
|
1421
|
+
showInUniversalPrompt: false,
|
|
1369
1422
|
detectInstalled: async () => {
|
|
1370
1423
|
return existsSync(join(home, ".gemini/antigravity-cli"));
|
|
1371
1424
|
}
|
|
@@ -1528,6 +1581,7 @@ const agents = {
|
|
|
1528
1581
|
displayName: "Deep Agents",
|
|
1529
1582
|
skillsDir: ".agents/skills",
|
|
1530
1583
|
globalSkillsDir: join(home, ".deepagents/agent/skills"),
|
|
1584
|
+
showInUniversalPrompt: false,
|
|
1531
1585
|
detectInstalled: async () => {
|
|
1532
1586
|
return existsSync(join(home, ".deepagents"));
|
|
1533
1587
|
}
|
|
@@ -1554,7 +1608,7 @@ const agents = {
|
|
|
1554
1608
|
droid: {
|
|
1555
1609
|
name: "droid",
|
|
1556
1610
|
displayName: "Droid",
|
|
1557
|
-
skillsDir: ".
|
|
1611
|
+
skillsDir: ".agents/skills",
|
|
1558
1612
|
globalSkillsDir: join(home, ".factory/skills"),
|
|
1559
1613
|
detectInstalled: async () => {
|
|
1560
1614
|
return existsSync(join(home, ".factory"));
|
|
@@ -1589,6 +1643,15 @@ const agents = {
|
|
|
1589
1643
|
return existsSync(join(home, ".forge"));
|
|
1590
1644
|
}
|
|
1591
1645
|
},
|
|
1646
|
+
fx: {
|
|
1647
|
+
name: "fx",
|
|
1648
|
+
displayName: "fx",
|
|
1649
|
+
skillsDir: ".fx/skills",
|
|
1650
|
+
globalSkillsDir: join(home, ".fx/skills"),
|
|
1651
|
+
detectInstalled: async () => {
|
|
1652
|
+
return existsSync(join(home, ".fx"));
|
|
1653
|
+
}
|
|
1654
|
+
},
|
|
1592
1655
|
"gemini-cli": {
|
|
1593
1656
|
name: "gemini-cli",
|
|
1594
1657
|
displayName: "Gemini CLI",
|
|
@@ -1673,10 +1736,10 @@ const agents = {
|
|
|
1673
1736
|
kilo: {
|
|
1674
1737
|
name: "kilo",
|
|
1675
1738
|
displayName: "Kilo Code",
|
|
1676
|
-
skillsDir: ".
|
|
1677
|
-
globalSkillsDir: join(home, ".
|
|
1739
|
+
skillsDir: ".agents/skills",
|
|
1740
|
+
globalSkillsDir: join(home, ".kilo/skills"),
|
|
1678
1741
|
detectInstalled: async () => {
|
|
1679
|
-
return existsSync(join(home, ".kilocode"));
|
|
1742
|
+
return existsSync(join(home, ".kilo")) || existsSync(join(home, ".kilocode"));
|
|
1680
1743
|
}
|
|
1681
1744
|
},
|
|
1682
1745
|
kimchi: {
|
|
@@ -1888,6 +1951,16 @@ const agents = {
|
|
|
1888
1951
|
return existsSync(join(home, ".roo"));
|
|
1889
1952
|
}
|
|
1890
1953
|
},
|
|
1954
|
+
"sarvam-code": {
|
|
1955
|
+
name: "sarvam-code",
|
|
1956
|
+
displayName: "Sarvam Code",
|
|
1957
|
+
skillsDir: ".agents/skills",
|
|
1958
|
+
globalSkillsDir: join(home, ".agents/skills"),
|
|
1959
|
+
showInUniversalPrompt: false,
|
|
1960
|
+
detectInstalled: async () => {
|
|
1961
|
+
return existsSync(sarvamHome);
|
|
1962
|
+
}
|
|
1963
|
+
},
|
|
1891
1964
|
"tabnine-cli": {
|
|
1892
1965
|
name: "tabnine-cli",
|
|
1893
1966
|
displayName: "Tabnine CLI",
|
|
@@ -3837,11 +3910,13 @@ const PRIORITY_PREFIXES = [
|
|
|
3837
3910
|
".codex/skills/",
|
|
3838
3911
|
".commandcode/skills/",
|
|
3839
3912
|
".continue/skills/",
|
|
3913
|
+
".factory/skills/",
|
|
3840
3914
|
".github/skills/",
|
|
3841
3915
|
".goose/skills/",
|
|
3842
3916
|
".grok/skills/",
|
|
3843
3917
|
".iflow/skills/",
|
|
3844
3918
|
".junie/skills/",
|
|
3919
|
+
".kilo/skills/",
|
|
3845
3920
|
".kilocode/skills/",
|
|
3846
3921
|
".kimchi/skills/",
|
|
3847
3922
|
".kiro/skills/",
|
|
@@ -3943,6 +4018,35 @@ function computeSnapshotHash(files) {
|
|
|
3943
4018
|
}
|
|
3944
4019
|
return hash.digest("hex");
|
|
3945
4020
|
}
|
|
4021
|
+
function getSkillFolderPath(skillMdPath) {
|
|
4022
|
+
const pathLower = skillMdPath.toLowerCase();
|
|
4023
|
+
if (pathLower.endsWith("/skill.md")) return skillMdPath.slice(0, -9);
|
|
4024
|
+
if (pathLower === "skill.md") return "";
|
|
4025
|
+
return skillMdPath.slice(0, -9);
|
|
4026
|
+
}
|
|
4027
|
+
const SNAPSHOT_EXCLUDED_FILES = /* @__PURE__ */ new Set(["metadata.json"]);
|
|
4028
|
+
const SNAPSHOT_EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
4029
|
+
".git",
|
|
4030
|
+
"__pycache__",
|
|
4031
|
+
"__pypackages__"
|
|
4032
|
+
]);
|
|
4033
|
+
function isInstallableSnapshotPath(path) {
|
|
4034
|
+
const parts = path.split("/");
|
|
4035
|
+
const fileName = parts.at(-1);
|
|
4036
|
+
if (!fileName || SNAPSHOT_EXCLUDED_FILES.has(fileName)) return false;
|
|
4037
|
+
return parts.slice(0, -1).every((part) => !SNAPSHOT_EXCLUDED_DIRS.has(part));
|
|
4038
|
+
}
|
|
4039
|
+
function hasCompleteNestedSnapshot(tree, skillMdPath, files) {
|
|
4040
|
+
const folderPath = getSkillFolderPath(skillMdPath);
|
|
4041
|
+
if (!folderPath) return true;
|
|
4042
|
+
const folderPrefix = `${folderPath}/`;
|
|
4043
|
+
const snapshotPaths = new Set(files.map((file) => file.path));
|
|
4044
|
+
return tree.tree.every((entry) => {
|
|
4045
|
+
if (entry.type !== "blob" || !entry.path.startsWith(folderPrefix)) return true;
|
|
4046
|
+
const relativePath = entry.path.slice(folderPrefix.length);
|
|
4047
|
+
return !isInstallableSnapshotPath(relativePath) || snapshotPaths.has(relativePath);
|
|
4048
|
+
});
|
|
4049
|
+
}
|
|
3946
4050
|
async function tryBlobInstall(ownerRepo, options = {}) {
|
|
3947
4051
|
if (options.ref !== void 0) return null;
|
|
3948
4052
|
const tree = await fetchRepoTree(ownerRepo, options.ref, options.getToken);
|
|
@@ -3999,10 +4103,10 @@ async function tryBlobInstall(ownerRepo, options = {}) {
|
|
|
3999
4103
|
};
|
|
4000
4104
|
}));
|
|
4001
4105
|
if (!downloads.every((d) => d.download !== null)) return null;
|
|
4106
|
+
if (!downloads.every(({ skill, download }) => hasCompleteNestedSnapshot(tree, skill.mdPath, download.files))) return null;
|
|
4002
4107
|
return {
|
|
4003
4108
|
skills: downloads.map(({ skill, download }) => {
|
|
4004
|
-
const
|
|
4005
|
-
const files = (mdPathLower.endsWith("/skill.md") ? skill.mdPath.slice(0, -9) : mdPathLower === "skill.md" ? "" : skill.mdPath.slice(0, -9)) ? download.files : download.files.filter((file) => file.path.toLowerCase() === "skill.md");
|
|
4109
|
+
const files = getSkillFolderPath(skill.mdPath) ? download.files : download.files.filter((file) => file.path.toLowerCase() === "skill.md");
|
|
4006
4110
|
return {
|
|
4007
4111
|
name: skill.name,
|
|
4008
4112
|
description: skill.description,
|
|
@@ -4017,7 +4121,7 @@ async function tryBlobInstall(ownerRepo, options = {}) {
|
|
|
4017
4121
|
tree
|
|
4018
4122
|
};
|
|
4019
4123
|
}
|
|
4020
|
-
var version$1 = "1.5.
|
|
4124
|
+
var version$1 = "1.5.25";
|
|
4021
4125
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
4022
4126
|
const EVE_AGENT_LABEL = "eve agent";
|
|
4023
4127
|
async function isSourcePrivate(source) {
|
|
@@ -4170,6 +4274,14 @@ function buildResultLines(results, targetAgents) {
|
|
|
4170
4274
|
if (failedSymlinks.length > 0) lines.push(` ${import_picocolors.default.yellow("copied:")} ${formatList$1(failedSymlinks)}`);
|
|
4171
4275
|
return lines;
|
|
4172
4276
|
}
|
|
4277
|
+
function exitInstallationCancelled() {
|
|
4278
|
+
cancel("Installation cancelled");
|
|
4279
|
+
if (!process.stdin.isTTY) {
|
|
4280
|
+
console.error("Interactive prompt required but stdin is not a TTY. Nothing was installed. Use --agent <name> (or --agent '*') and -y to run non-interactively.");
|
|
4281
|
+
process.exit(1);
|
|
4282
|
+
}
|
|
4283
|
+
process.exit(0);
|
|
4284
|
+
}
|
|
4173
4285
|
async function promptForAgents(message, choices) {
|
|
4174
4286
|
let lastSelected;
|
|
4175
4287
|
try {
|
|
@@ -4303,10 +4415,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4303
4415
|
maxVisible: 20,
|
|
4304
4416
|
selectAll: true
|
|
4305
4417
|
});
|
|
4306
|
-
if (isCancelled$1(selected))
|
|
4307
|
-
cancel("Installation cancelled");
|
|
4308
|
-
process.exit(0);
|
|
4309
|
-
}
|
|
4418
|
+
if (isCancelled$1(selected)) exitInstallationCancelled();
|
|
4310
4419
|
selectedSkills = selected;
|
|
4311
4420
|
}
|
|
4312
4421
|
let targetAgents;
|
|
@@ -4336,10 +4445,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4336
4445
|
value: key,
|
|
4337
4446
|
label: config.displayName
|
|
4338
4447
|
})));
|
|
4339
|
-
if (
|
|
4340
|
-
cancel("Installation cancelled");
|
|
4341
|
-
process.exit(0);
|
|
4342
|
-
}
|
|
4448
|
+
if (isCancelled$1(selected)) exitInstallationCancelled();
|
|
4343
4449
|
targetAgents = selected;
|
|
4344
4450
|
}
|
|
4345
4451
|
else if (installedAgents.length === 1 || options.yes) {
|
|
@@ -4350,10 +4456,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4350
4456
|
} else log.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
|
|
4351
4457
|
} else {
|
|
4352
4458
|
const selected = await selectAgentsInteractive({ global: options.global });
|
|
4353
|
-
if (
|
|
4354
|
-
cancel("Installation cancelled");
|
|
4355
|
-
process.exit(0);
|
|
4356
|
-
}
|
|
4459
|
+
if (isCancelled$1(selected)) exitInstallationCancelled();
|
|
4357
4460
|
targetAgents = selected;
|
|
4358
4461
|
}
|
|
4359
4462
|
}
|
|
@@ -4372,10 +4475,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4372
4475
|
hint: "Install in home directory (available across all projects)"
|
|
4373
4476
|
}]
|
|
4374
4477
|
});
|
|
4375
|
-
if (isCancel(scope))
|
|
4376
|
-
cancel("Installation cancelled");
|
|
4377
|
-
process.exit(0);
|
|
4378
|
-
}
|
|
4478
|
+
if (isCancel(scope)) exitInstallationCancelled();
|
|
4379
4479
|
installGlobally = scope;
|
|
4380
4480
|
}
|
|
4381
4481
|
let installMode = options.copy ? "copy" : "symlink";
|
|
@@ -4393,10 +4493,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4393
4493
|
hint: "Independent copies for each agent"
|
|
4394
4494
|
}]
|
|
4395
4495
|
});
|
|
4396
|
-
if (isCancel(modeChoice))
|
|
4397
|
-
cancel("Installation cancelled");
|
|
4398
|
-
process.exit(0);
|
|
4399
|
-
}
|
|
4496
|
+
if (isCancel(modeChoice)) exitInstallationCancelled();
|
|
4400
4497
|
installMode = modeChoice;
|
|
4401
4498
|
} else if (uniqueDirs.size <= 1) installMode = "copy";
|
|
4402
4499
|
const cwd = process.cwd();
|
|
@@ -4426,10 +4523,7 @@ async function handleWellKnownSkills(source, url, options, spinner) {
|
|
|
4426
4523
|
note(summaryLines.join("\n"), "Installation Summary");
|
|
4427
4524
|
if (!options.yes) {
|
|
4428
4525
|
const confirmed = await confirm({ message: "Proceed with installation?" });
|
|
4429
|
-
if (isCancel(confirmed) || !confirmed)
|
|
4430
|
-
cancel("Installation cancelled");
|
|
4431
|
-
process.exit(0);
|
|
4432
|
-
}
|
|
4526
|
+
if (isCancel(confirmed) || !confirmed) exitInstallationCancelled();
|
|
4433
4527
|
}
|
|
4434
4528
|
const sourceIdentifier = wellKnownProvider.getSourceIdentifier(url);
|
|
4435
4529
|
const wellKnownPrivacyPromise = isSourcePrivate(sourceIdentifier).catch(() => null);
|
|
@@ -4758,9 +4852,8 @@ async function runAdd(args, options = {}) {
|
|
|
4758
4852
|
selectAll: true
|
|
4759
4853
|
});
|
|
4760
4854
|
if (isCancelled$1(selected)) {
|
|
4761
|
-
cancel("Installation cancelled");
|
|
4762
4855
|
await cleanup(tempDir);
|
|
4763
|
-
|
|
4856
|
+
exitInstallationCancelled();
|
|
4764
4857
|
}
|
|
4765
4858
|
selectedSkills = selected;
|
|
4766
4859
|
}
|
|
@@ -4791,19 +4884,17 @@ async function runAdd(args, options = {}) {
|
|
|
4791
4884
|
initialValue: true
|
|
4792
4885
|
});
|
|
4793
4886
|
if (isCancel(useEve)) {
|
|
4794
|
-
cancel("Installation cancelled");
|
|
4795
4887
|
await cleanup(tempDir);
|
|
4796
|
-
|
|
4888
|
+
exitInstallationCancelled();
|
|
4797
4889
|
}
|
|
4798
4890
|
if (useEve) {
|
|
4799
4891
|
targetAgents = ["eve"];
|
|
4800
4892
|
log.info(`Installing to: ${import_picocolors.default.cyan(EVE_AGENT_LABEL)}`);
|
|
4801
4893
|
} else {
|
|
4802
4894
|
const selected = await selectAgentsInteractive({ global: options.global });
|
|
4803
|
-
if (
|
|
4804
|
-
cancel("Installation cancelled");
|
|
4895
|
+
if (isCancelled$1(selected)) {
|
|
4805
4896
|
await cleanup(tempDir);
|
|
4806
|
-
|
|
4897
|
+
exitInstallationCancelled();
|
|
4807
4898
|
}
|
|
4808
4899
|
targetAgents = selected;
|
|
4809
4900
|
}
|
|
@@ -4816,10 +4907,9 @@ async function runAdd(args, options = {}) {
|
|
|
4816
4907
|
value: key,
|
|
4817
4908
|
label: config.displayName
|
|
4818
4909
|
})));
|
|
4819
|
-
if (
|
|
4820
|
-
cancel("Installation cancelled");
|
|
4910
|
+
if (isCancelled$1(selected)) {
|
|
4821
4911
|
await cleanup(tempDir);
|
|
4822
|
-
|
|
4912
|
+
exitInstallationCancelled();
|
|
4823
4913
|
}
|
|
4824
4914
|
targetAgents = selected;
|
|
4825
4915
|
}
|
|
@@ -4831,10 +4921,9 @@ async function runAdd(args, options = {}) {
|
|
|
4831
4921
|
} else log.info(`Installing to: ${installedAgents.map((a) => import_picocolors.default.cyan(agents[a].displayName)).join(", ")}`);
|
|
4832
4922
|
} else {
|
|
4833
4923
|
const selected = await selectAgentsInteractive({ global: options.global });
|
|
4834
|
-
if (
|
|
4835
|
-
cancel("Installation cancelled");
|
|
4924
|
+
if (isCancelled$1(selected)) {
|
|
4836
4925
|
await cleanup(tempDir);
|
|
4837
|
-
|
|
4926
|
+
exitInstallationCancelled();
|
|
4838
4927
|
}
|
|
4839
4928
|
targetAgents = selected;
|
|
4840
4929
|
}
|
|
@@ -4860,9 +4949,8 @@ async function runAdd(args, options = {}) {
|
|
|
4860
4949
|
required: true
|
|
4861
4950
|
});
|
|
4862
4951
|
if (isCancel(selectedSubagents)) {
|
|
4863
|
-
cancel("Installation cancelled");
|
|
4864
4952
|
await cleanup(tempDir);
|
|
4865
|
-
|
|
4953
|
+
exitInstallationCancelled();
|
|
4866
4954
|
}
|
|
4867
4955
|
eveSubagentTargets = selectedSubagents.map((s) => s === "" ? void 0 : s);
|
|
4868
4956
|
}
|
|
@@ -4884,9 +4972,8 @@ async function runAdd(args, options = {}) {
|
|
|
4884
4972
|
}]
|
|
4885
4973
|
});
|
|
4886
4974
|
if (isCancel(scope)) {
|
|
4887
|
-
cancel("Installation cancelled");
|
|
4888
4975
|
await cleanup(tempDir);
|
|
4889
|
-
|
|
4976
|
+
exitInstallationCancelled();
|
|
4890
4977
|
}
|
|
4891
4978
|
installGlobally = scope;
|
|
4892
4979
|
}
|
|
@@ -4907,9 +4994,8 @@ async function runAdd(args, options = {}) {
|
|
|
4907
4994
|
}]
|
|
4908
4995
|
});
|
|
4909
4996
|
if (isCancel(modeChoice)) {
|
|
4910
|
-
cancel("Installation cancelled");
|
|
4911
4997
|
await cleanup(tempDir);
|
|
4912
|
-
|
|
4998
|
+
exitInstallationCancelled();
|
|
4913
4999
|
}
|
|
4914
5000
|
installMode = modeChoice;
|
|
4915
5001
|
} else if (uniqueDirs.size <= 1 || allEve) installMode = "copy";
|
|
@@ -4979,9 +5065,8 @@ async function runAdd(args, options = {}) {
|
|
|
4979
5065
|
if (!options.yes) {
|
|
4980
5066
|
const confirmed = await confirm({ message: "Proceed with installation?" });
|
|
4981
5067
|
if (isCancel(confirmed) || !confirmed) {
|
|
4982
|
-
cancel("Installation cancelled");
|
|
4983
5068
|
await cleanup(tempDir);
|
|
4984
|
-
|
|
5069
|
+
exitInstallationCancelled();
|
|
4985
5070
|
}
|
|
4986
5071
|
}
|
|
4987
5072
|
spinner$3.start("Installing skills…");
|
|
@@ -6158,7 +6243,11 @@ async function removeCommand(skillNames, options) {
|
|
|
6158
6243
|
const scanDir = async (dir) => {
|
|
6159
6244
|
try {
|
|
6160
6245
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
6161
|
-
for (const entry of entries)
|
|
6246
|
+
for (const entry of entries) {
|
|
6247
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
6248
|
+
if (!await hasSkillMd(join(dir, entry.name))) continue;
|
|
6249
|
+
skillNamesSet.add(entry.name);
|
|
6250
|
+
}
|
|
6162
6251
|
} catch (err) {
|
|
6163
6252
|
if (err instanceof Error && err.code !== "ENOENT") log.warn(`Could not scan directory ${dir}: ${err.message}`);
|
|
6164
6253
|
}
|
|
@@ -6366,6 +6455,46 @@ function parseRemoveOptions(args) {
|
|
|
6366
6455
|
options
|
|
6367
6456
|
};
|
|
6368
6457
|
}
|
|
6458
|
+
function normalizeSkillName(name) {
|
|
6459
|
+
return name.toLowerCase().replace(/[\s_]+/g, "-");
|
|
6460
|
+
}
|
|
6461
|
+
function normalizeSkillPath(path) {
|
|
6462
|
+
return path.replace(/\\/g, "/").replace(/\/+/g, "/");
|
|
6463
|
+
}
|
|
6464
|
+
function resolveSkillLocations(lockedSkillNames, lockSkills, discovered) {
|
|
6465
|
+
const discoveredPaths = new Set(discovered.map((skill) => normalizeSkillPath(skill.skillPath)));
|
|
6466
|
+
const pathsByName = /* @__PURE__ */ new Map();
|
|
6467
|
+
for (const skill of discovered) {
|
|
6468
|
+
const key = normalizeSkillName(skill.name);
|
|
6469
|
+
const paths = pathsByName.get(key) ?? /* @__PURE__ */ new Set();
|
|
6470
|
+
paths.add(normalizeSkillPath(skill.skillPath));
|
|
6471
|
+
pathsByName.set(key, paths);
|
|
6472
|
+
}
|
|
6473
|
+
const deletedSkills = [];
|
|
6474
|
+
const ambiguousSkills = [];
|
|
6475
|
+
const resolvedPaths = /* @__PURE__ */ new Map();
|
|
6476
|
+
for (const name of lockedSkillNames) {
|
|
6477
|
+
const lockedPath = lockSkills[name]?.skillPath;
|
|
6478
|
+
if (!lockedPath) continue;
|
|
6479
|
+
const normalizedLockedPath = normalizeSkillPath(lockedPath);
|
|
6480
|
+
const candidates = [...pathsByName.get(normalizeSkillName(name)) ?? []];
|
|
6481
|
+
if (candidates.length > 1) {
|
|
6482
|
+
ambiguousSkills.push(name);
|
|
6483
|
+
continue;
|
|
6484
|
+
}
|
|
6485
|
+
if (discoveredPaths.has(normalizedLockedPath)) {
|
|
6486
|
+
resolvedPaths.set(name, normalizedLockedPath);
|
|
6487
|
+
continue;
|
|
6488
|
+
}
|
|
6489
|
+
if (candidates.length === 1) resolvedPaths.set(name, candidates[0]);
|
|
6490
|
+
else deletedSkills.push(name);
|
|
6491
|
+
}
|
|
6492
|
+
return {
|
|
6493
|
+
deletedSkills,
|
|
6494
|
+
ambiguousSkills,
|
|
6495
|
+
resolvedPaths
|
|
6496
|
+
};
|
|
6497
|
+
}
|
|
6369
6498
|
const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
6370
6499
|
const RESET$1 = "\x1B[0m";
|
|
6371
6500
|
const BOLD$1 = "\x1B[1m";
|
|
@@ -6512,14 +6641,15 @@ async function promptDeletions(source, deletedSkills, isGlobal, options) {
|
|
|
6512
6641
|
});
|
|
6513
6642
|
}
|
|
6514
6643
|
}
|
|
6515
|
-
async function checkAndPromptForDeletions(source, allLockedForSource, lockSkills, isGlobal, options,
|
|
6516
|
-
const
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6644
|
+
async function checkAndPromptForDeletions(source, allLockedForSource, lockSkills, isGlobal, options, discovered) {
|
|
6645
|
+
const resolution = resolveSkillLocations(allLockedForSource, lockSkills, discovered);
|
|
6646
|
+
if (resolution.ambiguousSkills.length > 0) {
|
|
6647
|
+
console.log();
|
|
6648
|
+
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:`);
|
|
6649
|
+
for (const name of resolution.ambiguousSkills) console.log(` ${DIM$1}•${RESET$1} ${sanitizeMetadata(name)}`);
|
|
6650
|
+
}
|
|
6651
|
+
await promptDeletions(source, resolution.deletedSkills, isGlobal, options);
|
|
6652
|
+
return resolution;
|
|
6523
6653
|
}
|
|
6524
6654
|
async function checkWellKnownForUpdates(baseUrl, items) {
|
|
6525
6655
|
let indexResult;
|
|
@@ -6703,36 +6833,42 @@ async function updateGlobalSkills(options = {}) {
|
|
|
6703
6833
|
const tree = await fetchRepoTree(source, firstEntry.ref, getGitHubToken);
|
|
6704
6834
|
if (tree) {
|
|
6705
6835
|
const discoveredPaths = tree.tree.filter((entry) => entry.type === "blob").map((entry) => entry.path);
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6836
|
+
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))) {
|
|
6837
|
+
for (const { name: skillName, entry } of itemsForSource) {
|
|
6838
|
+
const latestHash = getSkillFolderHashFromTree(tree, entry.skillPath);
|
|
6839
|
+
if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
|
|
6840
|
+
name: skillName,
|
|
6841
|
+
source,
|
|
6842
|
+
entry
|
|
6843
|
+
});
|
|
6844
|
+
}
|
|
6845
|
+
continue;
|
|
6716
6846
|
}
|
|
6717
|
-
|
|
6718
|
-
}
|
|
6719
|
-
console.log(` ${DIM$1}GitHub API unavailable; checking via Git clone${RESET$1}`);
|
|
6847
|
+
console.log(` ${DIM$1}Skill paths changed; resolving via Git clone${RESET$1}`);
|
|
6848
|
+
} else console.log(` ${DIM$1}GitHub API unavailable; checking via Git clone${RESET$1}`);
|
|
6720
6849
|
}
|
|
6721
6850
|
tempDir = await cloneRepo(sourceUrl, firstEntry.ref);
|
|
6722
|
-
const
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6851
|
+
const discoveredLocations = (await discoverSkills(tempDir, void 0, {
|
|
6852
|
+
fullDepth: true,
|
|
6853
|
+
includeDuplicateNames: true
|
|
6854
|
+
})).map((skill) => ({
|
|
6855
|
+
name: skill.name,
|
|
6856
|
+
skillPath: join(relative(tempDir, skill.path), "SKILL.md").split(sep).join("/")
|
|
6857
|
+
}));
|
|
6858
|
+
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);
|
|
6859
|
+
const deletedSkillSet = new Set(resolution.deletedSkills);
|
|
6727
6860
|
for (const { name: skillName, entry } of itemsForSource) {
|
|
6728
6861
|
if (deletedSkillSet.has(skillName)) continue;
|
|
6729
|
-
const skillPath =
|
|
6730
|
-
if (!
|
|
6862
|
+
const skillPath = resolution.resolvedPaths.get(skillName);
|
|
6863
|
+
if (!skillPath) continue;
|
|
6731
6864
|
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({
|
|
6865
|
+
if (skillPath !== entry.skillPath || latestHash && latestHash !== entry.skillFolderHash) updates.push({
|
|
6733
6866
|
name: skillName,
|
|
6734
6867
|
source,
|
|
6735
|
-
entry
|
|
6868
|
+
entry: {
|
|
6869
|
+
...entry,
|
|
6870
|
+
skillPath
|
|
6871
|
+
}
|
|
6736
6872
|
});
|
|
6737
6873
|
}
|
|
6738
6874
|
} catch (error) {
|
|
@@ -6910,6 +7046,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
6910
7046
|
const allLockedForSource = Object.entries(localLock.skills).filter(([_, entry]) => (entry.sourceUrl || entry.source) === source && entry.ref === ref).map(([name, _]) => name);
|
|
6911
7047
|
let tempDir = null;
|
|
6912
7048
|
let deletedSkills = [];
|
|
7049
|
+
let resolvedPaths = null;
|
|
6913
7050
|
if (cloneSource === null) {
|
|
6914
7051
|
failCount += skillsForSource.length;
|
|
6915
7052
|
console.log(`${DIM$1}✗ Cannot update ${source}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$1}`);
|
|
@@ -6917,27 +7054,43 @@ async function updateProjectSkills(options = {}) {
|
|
|
6917
7054
|
}
|
|
6918
7055
|
try {
|
|
6919
7056
|
tempDir = await cloneRepo(cloneSource, ref);
|
|
6920
|
-
const
|
|
6921
|
-
|
|
6922
|
-
|
|
6923
|
-
|
|
7057
|
+
const discoveredLocations = (await discoverSkills(tempDir, void 0, {
|
|
7058
|
+
fullDepth: true,
|
|
7059
|
+
includeDuplicateNames: true
|
|
7060
|
+
})).map((skill) => ({
|
|
7061
|
+
name: skill.name,
|
|
7062
|
+
skillPath: join(relative(tempDir, skill.path), "SKILL.md").split(sep).join("/")
|
|
7063
|
+
}));
|
|
7064
|
+
const resolution = await checkAndPromptForDeletions(source, allLockedForSource, localLock.skills, false, options, discoveredLocations);
|
|
7065
|
+
deletedSkills = resolution.deletedSkills;
|
|
7066
|
+
resolvedPaths = resolution.resolvedPaths;
|
|
6924
7067
|
} catch (error) {
|
|
6925
7068
|
console.log(`${DIM$1}✗ Failed to check for deleted skills from ${source}${RESET$1}`);
|
|
6926
7069
|
} finally {
|
|
6927
7070
|
if (tempDir) await cleanupTempDir(tempDir);
|
|
6928
7071
|
}
|
|
7072
|
+
if (resolvedPaths === null) {
|
|
7073
|
+
failCount += skillsForSource.length;
|
|
7074
|
+
continue;
|
|
7075
|
+
}
|
|
6929
7076
|
const remainingSkills = skillsForSource.filter((s) => !deletedSkills.includes(s.name));
|
|
6930
7077
|
for (const skill of remainingSkills) {
|
|
6931
7078
|
const safeName = sanitizeMetadata(skill.name);
|
|
7079
|
+
const resolvedPath = resolvedPaths?.get(skill.name);
|
|
7080
|
+
if (resolvedPaths && !resolvedPath) continue;
|
|
7081
|
+
const entry = resolvedPath ? {
|
|
7082
|
+
...skill.entry,
|
|
7083
|
+
skillPath: resolvedPath
|
|
7084
|
+
} : skill.entry;
|
|
6932
7085
|
console.log(`${TEXT$1}Updating ${safeName}…${RESET$1}`);
|
|
6933
|
-
const installUrl = buildLocalUpdateSource(
|
|
7086
|
+
const installUrl = buildLocalUpdateSource(entry);
|
|
6934
7087
|
if (!installUrl) {
|
|
6935
7088
|
failCount++;
|
|
6936
7089
|
console.log(` ${DIM$1}✗ Cannot update ${safeName}: skills-lock.json is missing sourceUrl for this generic Git source${RESET$1}`);
|
|
6937
7090
|
continue;
|
|
6938
7091
|
}
|
|
6939
7092
|
const subagentArgs = skill.entry.subagents?.length ? ["--subagent", ...skill.entry.subagents.map((s) => s === "" ? "root" : s)] : [];
|
|
6940
|
-
const fullDepthArgs = shouldUseFullDepthForUpdate(
|
|
7093
|
+
const fullDepthArgs = shouldUseFullDepthForUpdate(entry) ? ["--full-depth"] : [];
|
|
6941
7094
|
if (spawnSync(process.execPath, [
|
|
6942
7095
|
cliEntry,
|
|
6943
7096
|
"add",
|
|
@@ -6954,7 +7107,7 @@ async function updateProjectSkills(options = {}) {
|
|
|
6954
7107
|
"pipe"
|
|
6955
7108
|
],
|
|
6956
7109
|
encoding: "utf-8",
|
|
6957
|
-
env: getUpdateChildEnv(
|
|
7110
|
+
env: getUpdateChildEnv(entry.sourceType),
|
|
6958
7111
|
shell: false
|
|
6959
7112
|
}).status === 0) {
|
|
6960
7113
|
successCount++;
|
|
@@ -7042,6 +7195,10 @@ const USE_AGENT_CONFIGS = {
|
|
|
7042
7195
|
codex: {
|
|
7043
7196
|
command: "codex",
|
|
7044
7197
|
args: []
|
|
7198
|
+
},
|
|
7199
|
+
"sarvam-code": {
|
|
7200
|
+
command: "sarvam-code",
|
|
7201
|
+
args: []
|
|
7045
7202
|
}
|
|
7046
7203
|
};
|
|
7047
7204
|
const SUPPORTED_USE_AGENTS = Object.keys(USE_AGENT_CONFIGS);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skills",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.25",
|
|
4
4
|
"description": "The open agent skills ecosystem",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"eve",
|
|
65
65
|
"firebender",
|
|
66
66
|
"forgecode",
|
|
67
|
+
"fx",
|
|
67
68
|
"gemini-cli",
|
|
68
69
|
"github-copilot",
|
|
69
70
|
"goose",
|
|
@@ -97,6 +98,7 @@
|
|
|
97
98
|
"reasonix",
|
|
98
99
|
"rovodev",
|
|
99
100
|
"roo",
|
|
101
|
+
"sarvam-code",
|
|
100
102
|
"tabnine-cli",
|
|
101
103
|
"terramind",
|
|
102
104
|
"tinycloud",
|