skills 1.4.3 → 1.4.4
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 +72 -18
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -21,6 +21,13 @@ import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpat
|
|
|
21
21
|
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
22
22
|
function getOwnerRepo(parsed) {
|
|
23
23
|
if (parsed.type === "local") return null;
|
|
24
|
+
const sshMatch = parsed.url.match(/^git@[^:]+:(.+)$/);
|
|
25
|
+
if (sshMatch) {
|
|
26
|
+
let path = sshMatch[1];
|
|
27
|
+
path = path.replace(/\.git$/, "");
|
|
28
|
+
if (path.includes("/")) return path;
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
24
31
|
if (!parsed.url.startsWith("http://") && !parsed.url.startsWith("https://")) return null;
|
|
25
32
|
try {
|
|
26
33
|
let path = new URL(parsed.url).pathname.slice(1);
|
|
@@ -46,6 +53,11 @@ async function isRepoPrivate(owner, repo) {
|
|
|
46
53
|
return null;
|
|
47
54
|
}
|
|
48
55
|
}
|
|
56
|
+
function sanitizeSubpath(subpath) {
|
|
57
|
+
const segments = subpath.replace(/\\/g, "/").split("/");
|
|
58
|
+
for (const segment of segments) if (segment === "..") throw new Error(`Unsafe subpath: "${subpath}" contains path traversal segments. Subpaths must not contain ".." components.`);
|
|
59
|
+
return subpath;
|
|
60
|
+
}
|
|
49
61
|
function isLocalPath(input) {
|
|
50
62
|
return isAbsolute(input) || input.startsWith("./") || input.startsWith("../") || input === "." || input === ".." || /^[a-zA-Z]:[/\\]/.test(input);
|
|
51
63
|
}
|
|
@@ -53,6 +65,10 @@ const SOURCE_ALIASES = { "coinbase/agentWallet": "coinbase/agentic-wallet-skills
|
|
|
53
65
|
function parseSource(input) {
|
|
54
66
|
const alias = SOURCE_ALIASES[input];
|
|
55
67
|
if (alias) input = alias;
|
|
68
|
+
const githubPrefixMatch = input.match(/^github:(.+)$/);
|
|
69
|
+
if (githubPrefixMatch) return parseSource(githubPrefixMatch[1]);
|
|
70
|
+
const gitlabPrefixMatch = input.match(/^gitlab:(.+)$/);
|
|
71
|
+
if (gitlabPrefixMatch) return parseSource(`https://gitlab.com/${gitlabPrefixMatch[1]}`);
|
|
56
72
|
if (isLocalPath(input)) {
|
|
57
73
|
const resolvedPath = resolve(input);
|
|
58
74
|
return {
|
|
@@ -68,7 +84,7 @@ function parseSource(input) {
|
|
|
68
84
|
type: "github",
|
|
69
85
|
url: `https://github.com/${owner}/${repo}.git`,
|
|
70
86
|
ref,
|
|
71
|
-
subpath
|
|
87
|
+
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
72
88
|
};
|
|
73
89
|
}
|
|
74
90
|
const githubTreeMatch = input.match(/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)$/);
|
|
@@ -95,7 +111,7 @@ function parseSource(input) {
|
|
|
95
111
|
type: "gitlab",
|
|
96
112
|
url: `${protocol}://${hostname}/${repoPath.replace(/\.git$/, "")}.git`,
|
|
97
113
|
ref,
|
|
98
|
-
subpath
|
|
114
|
+
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
99
115
|
};
|
|
100
116
|
}
|
|
101
117
|
const gitlabTreeMatch = input.match(/^(https?):\/\/([^/]+)\/(.+?)\/-\/tree\/([^/]+)$/);
|
|
@@ -130,7 +146,7 @@ function parseSource(input) {
|
|
|
130
146
|
return {
|
|
131
147
|
type: "github",
|
|
132
148
|
url: `https://github.com/${owner}/${repo}.git`,
|
|
133
|
-
subpath
|
|
149
|
+
subpath: subpath ? sanitizeSubpath(subpath) : subpath
|
|
134
150
|
};
|
|
135
151
|
}
|
|
136
152
|
if (isWellKnownUrl(input)) return {
|
|
@@ -486,9 +502,15 @@ async function findSkillDirs(dir, depth = 0, maxDepth = 5) {
|
|
|
486
502
|
return [];
|
|
487
503
|
}
|
|
488
504
|
}
|
|
505
|
+
function isSubpathSafe(basePath, subpath) {
|
|
506
|
+
const normalizedBase = normalize(resolve(basePath));
|
|
507
|
+
const normalizedTarget = normalize(resolve(join(basePath, subpath)));
|
|
508
|
+
return normalizedTarget.startsWith(normalizedBase + sep) || normalizedTarget === normalizedBase;
|
|
509
|
+
}
|
|
489
510
|
async function discoverSkills(basePath, subpath, options) {
|
|
490
511
|
const skills = [];
|
|
491
512
|
const seenNames = /* @__PURE__ */ new Set();
|
|
513
|
+
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.`);
|
|
492
514
|
const searchPath = subpath ? join(basePath, subpath) : basePath;
|
|
493
515
|
const pluginGroupings = await getPluginGroupings(searchPath);
|
|
494
516
|
const enhanceSkill = (skill) => {
|
|
@@ -1751,7 +1773,7 @@ function createEmptyLocalLock() {
|
|
|
1751
1773
|
skills: {}
|
|
1752
1774
|
};
|
|
1753
1775
|
}
|
|
1754
|
-
var version$1 = "1.4.
|
|
1776
|
+
var version$1 = "1.4.4";
|
|
1755
1777
|
const isCancelled$1 = (value) => typeof value === "symbol";
|
|
1756
1778
|
async function isSourcePrivate(source) {
|
|
1757
1779
|
const ownerRepo = parseOwnerRepo(source);
|
|
@@ -2580,7 +2602,7 @@ async function runAdd(args, options = {}) {
|
|
|
2580
2602
|
let skillFolderHash = "";
|
|
2581
2603
|
const skillPathValue = skillFiles[skill.name];
|
|
2582
2604
|
if (parsed.type === "github" && skillPathValue) {
|
|
2583
|
-
const hash = await fetchSkillFolderHash(normalizedSource, skillPathValue);
|
|
2605
|
+
const hash = await fetchSkillFolderHash(normalizedSource, skillPathValue, getGitHubToken());
|
|
2584
2606
|
if (hash) skillFolderHash = hash;
|
|
2585
2607
|
}
|
|
2586
2608
|
await addSkillToLock(skill.name, {
|
|
@@ -3845,6 +3867,22 @@ function readSkillLock() {
|
|
|
3845
3867
|
};
|
|
3846
3868
|
}
|
|
3847
3869
|
}
|
|
3870
|
+
function getSkipReason(entry) {
|
|
3871
|
+
if (entry.sourceType === "local") return "Local path";
|
|
3872
|
+
if (entry.sourceType === "git") return "Git URL (hash tracking not supported)";
|
|
3873
|
+
if (!entry.skillFolderHash) return "No version hash available";
|
|
3874
|
+
if (!entry.skillPath) return "No skill path recorded";
|
|
3875
|
+
return "No version tracking";
|
|
3876
|
+
}
|
|
3877
|
+
function printSkippedSkills(skipped) {
|
|
3878
|
+
if (skipped.length === 0) return;
|
|
3879
|
+
console.log();
|
|
3880
|
+
console.log(`${DIM}${skipped.length} skill(s) cannot be checked automatically:${RESET}`);
|
|
3881
|
+
for (const skill of skipped) {
|
|
3882
|
+
console.log(` ${TEXT}•${RESET} ${skill.name} ${DIM}(${skill.reason})${RESET}`);
|
|
3883
|
+
console.log(` ${DIM}To update: ${TEXT}npx skills add ${skill.sourceUrl} -g -y${RESET}`);
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3848
3886
|
async function runCheck(args = []) {
|
|
3849
3887
|
console.log(`${TEXT}Checking for skill updates...${RESET}`);
|
|
3850
3888
|
console.log();
|
|
@@ -3857,12 +3895,16 @@ async function runCheck(args = []) {
|
|
|
3857
3895
|
}
|
|
3858
3896
|
const token = getGitHubToken();
|
|
3859
3897
|
const skillsBySource = /* @__PURE__ */ new Map();
|
|
3860
|
-
|
|
3898
|
+
const skipped = [];
|
|
3861
3899
|
for (const skillName of skillNames) {
|
|
3862
3900
|
const entry = lock.skills[skillName];
|
|
3863
3901
|
if (!entry) continue;
|
|
3864
|
-
if (
|
|
3865
|
-
|
|
3902
|
+
if (!entry.skillFolderHash || !entry.skillPath) {
|
|
3903
|
+
skipped.push({
|
|
3904
|
+
name: skillName,
|
|
3905
|
+
reason: getSkipReason(entry),
|
|
3906
|
+
sourceUrl: entry.sourceUrl
|
|
3907
|
+
});
|
|
3866
3908
|
continue;
|
|
3867
3909
|
}
|
|
3868
3910
|
const existing = skillsBySource.get(entry.source) || [];
|
|
@@ -3872,9 +3914,10 @@ async function runCheck(args = []) {
|
|
|
3872
3914
|
});
|
|
3873
3915
|
skillsBySource.set(entry.source, existing);
|
|
3874
3916
|
}
|
|
3875
|
-
const totalSkills = skillNames.length -
|
|
3917
|
+
const totalSkills = skillNames.length - skipped.length;
|
|
3876
3918
|
if (totalSkills === 0) {
|
|
3877
3919
|
console.log(`${DIM}No GitHub skills to check.${RESET}`);
|
|
3920
|
+
printSkippedSkills(skipped);
|
|
3878
3921
|
return;
|
|
3879
3922
|
}
|
|
3880
3923
|
console.log(`${DIM}Checking ${totalSkills} skill(s) for updates...${RESET}`);
|
|
@@ -3917,6 +3960,7 @@ async function runCheck(args = []) {
|
|
|
3917
3960
|
console.log();
|
|
3918
3961
|
console.log(`${DIM}Could not check ${errors.length} skill(s) (may need reinstall)${RESET}`);
|
|
3919
3962
|
}
|
|
3963
|
+
printSkippedSkills(skipped);
|
|
3920
3964
|
track({
|
|
3921
3965
|
event: "check",
|
|
3922
3966
|
skillCount: String(totalSkills),
|
|
@@ -3936,12 +3980,18 @@ async function runUpdate() {
|
|
|
3936
3980
|
}
|
|
3937
3981
|
const token = getGitHubToken();
|
|
3938
3982
|
const updates = [];
|
|
3939
|
-
|
|
3983
|
+
const skipped = [];
|
|
3940
3984
|
for (const skillName of skillNames) {
|
|
3941
3985
|
const entry = lock.skills[skillName];
|
|
3942
3986
|
if (!entry) continue;
|
|
3943
|
-
if (
|
|
3944
|
-
|
|
3987
|
+
if (!entry.skillFolderHash || !entry.skillPath) {
|
|
3988
|
+
skipped.push({
|
|
3989
|
+
name: skillName,
|
|
3990
|
+
reason: getSkipReason(entry),
|
|
3991
|
+
sourceUrl: entry.sourceUrl
|
|
3992
|
+
});
|
|
3993
|
+
continue;
|
|
3994
|
+
}
|
|
3945
3995
|
try {
|
|
3946
3996
|
const latestHash = await fetchSkillFolderHash(entry.source, entry.skillPath, token);
|
|
3947
3997
|
if (latestHash && latestHash !== entry.skillFolderHash) updates.push({
|
|
@@ -3951,8 +4001,9 @@ async function runUpdate() {
|
|
|
3951
4001
|
});
|
|
3952
4002
|
} catch {}
|
|
3953
4003
|
}
|
|
3954
|
-
if (
|
|
4004
|
+
if (skillNames.length - skipped.length === 0) {
|
|
3955
4005
|
console.log(`${DIM}No skills to check.${RESET}`);
|
|
4006
|
+
printSkippedSkills(skipped);
|
|
3956
4007
|
return;
|
|
3957
4008
|
}
|
|
3958
4009
|
if (updates.length === 0) {
|
|
@@ -3982,11 +4033,14 @@ async function runUpdate() {
|
|
|
3982
4033
|
installUrl,
|
|
3983
4034
|
"-g",
|
|
3984
4035
|
"-y"
|
|
3985
|
-
], {
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
4036
|
+
], {
|
|
4037
|
+
stdio: [
|
|
4038
|
+
"inherit",
|
|
4039
|
+
"pipe",
|
|
4040
|
+
"pipe"
|
|
4041
|
+
],
|
|
4042
|
+
shell: process.platform === "win32"
|
|
4043
|
+
}).status === 0) {
|
|
3990
4044
|
successCount++;
|
|
3991
4045
|
console.log(` ${TEXT}✓${RESET} Updated ${update.name}`);
|
|
3992
4046
|
} else {
|