vskill 1.0.21 → 1.1.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 +2 -0
- package/agents.json +1 -1
- package/dist/commands/add.d.ts +7 -0
- package/dist/commands/add.js +93 -30
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/check.d.ts +41 -4
- package/dist/commands/check.js +112 -25
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/cleanup.d.ts +0 -1
- package/dist/commands/cleanup.js +71 -80
- package/dist/commands/cleanup.js.map +1 -1
- package/dist/commands/remove.js +55 -65
- package/dist/commands/remove.js.map +1 -1
- package/dist/discovery/github-tree.js +56 -0
- package/dist/discovery/github-tree.js.map +1 -1
- package/dist/eval-server/plugin-cli-routes.js +10 -1
- package/dist/eval-server/plugin-cli-routes.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lockfile/local-root.d.ts +7 -0
- package/dist/lockfile/local-root.js +25 -0
- package/dist/lockfile/local-root.js.map +1 -0
- package/dist/marketplace/manifest-conflict.d.ts +63 -0
- package/dist/marketplace/manifest-conflict.js +181 -0
- package/dist/marketplace/manifest-conflict.js.map +1 -0
- package/dist/utils/claude-plugin.js +8 -0
- package/dist/utils/claude-plugin.js.map +1 -1
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -305,6 +305,8 @@ Full changelog → [github.com/anton-abyzov/vskill/releases](https://github.com/
|
|
|
305
305
|
|
|
306
306
|
Issues and PRs welcome at [github.com/anton-abyzov/vskill](https://github.com/anton-abyzov/vskill). To submit a skill to the registry, see [verified-skill.com/docs/submitting](https://verified-skill.com/docs/submitting).
|
|
307
307
|
|
|
308
|
+
Inside this repo dependency install scripts are disabled (`.npmrc`): run `npm ci`, then `npm run setup` (rebuilds esbuild), and `npm run security:scan` before pushing — see [SECURITY.md](SECURITY.md).
|
|
309
|
+
|
|
308
310
|
## License
|
|
309
311
|
|
|
310
312
|
MIT
|
package/agents.json
CHANGED
package/dist/commands/add.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { AgentDefinition } from "../agents/agents-registry.js";
|
|
2
|
+
/**
|
|
3
|
+
* Subdirectories fetched alongside SKILL.md when installing a skill that lives
|
|
4
|
+
* in a repo subdirectory. Must stay a subset of the prefixes the installer
|
|
5
|
+
* accepts in installer/bundle-files.ts, or the write is rejected.
|
|
6
|
+
*/
|
|
7
|
+
export declare const BUNDLE_SUBDIRS: readonly ["agents", "references", "scripts", "assets", "tests"];
|
|
8
|
+
export declare function isBundleTextFile(name: string): boolean;
|
|
2
9
|
interface MarketplaceDetectionResult {
|
|
3
10
|
isMarketplace: boolean;
|
|
4
11
|
manifestContent?: string;
|
package/dist/commands/add.js
CHANGED
|
@@ -36,6 +36,30 @@ import { extractFrontmatterVersion } from "../utils/version.js";
|
|
|
36
36
|
import { claudePluginInstall, claudePluginUninstall, claudePluginMarketplaceAdd, claudePluginMarketplaceList, } from "../utils/claude-plugin.js";
|
|
37
37
|
import { buildPerAgentReport, resolvePluginId, } from "../lib/skill-lifecycle.js";
|
|
38
38
|
// ---------------------------------------------------------------------------
|
|
39
|
+
/**
|
|
40
|
+
* Subdirectories fetched alongside SKILL.md when installing a skill that lives
|
|
41
|
+
* in a repo subdirectory. Must stay a subset of the prefixes the installer
|
|
42
|
+
* accepts in installer/bundle-files.ts, or the write is rejected.
|
|
43
|
+
*/
|
|
44
|
+
export const BUNDLE_SUBDIRS = ["agents", "references", "scripts", "assets", "tests"];
|
|
45
|
+
/**
|
|
46
|
+
* Extensions carried in a skill bundle. Bundle contents are transported as
|
|
47
|
+
* UTF-8 strings, so this is a text-only allowlist — binaries would be corrupted.
|
|
48
|
+
*/
|
|
49
|
+
const BUNDLE_TEXT_EXTENSIONS = [
|
|
50
|
+
".md", ".txt", ".json", ".jsonc", ".yaml", ".yml", ".toml", ".csv",
|
|
51
|
+
".py", ".sh", ".bash", ".zsh", ".js", ".mjs", ".cjs", ".ts", ".sql",
|
|
52
|
+
".excalidraw", ".svg", ".css", ".html",
|
|
53
|
+
];
|
|
54
|
+
/** Per-file and per-skill ceilings, so one fat repo cannot stall an install. */
|
|
55
|
+
const MAX_BUNDLE_FILE_BYTES = 512 * 1024;
|
|
56
|
+
const MAX_BUNDLE_FILES = 100;
|
|
57
|
+
export function isBundleTextFile(name) {
|
|
58
|
+
const lower = name.toLowerCase();
|
|
59
|
+
if (lower.startsWith("."))
|
|
60
|
+
return false; // .DS_Store, dotfiles
|
|
61
|
+
return BUNDLE_TEXT_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
62
|
+
}
|
|
39
63
|
/** Validate that a download_url from GitHub Contents API points to a trusted GitHub domain. */
|
|
40
64
|
function isGitHubDownloadUrl(url) {
|
|
41
65
|
try {
|
|
@@ -2400,41 +2424,80 @@ async function installSingleSkillLegacy(owner, repo, skill, opts, skillSubpathOv
|
|
|
2400
2424
|
}
|
|
2401
2425
|
const branch = refOverride || (await getDefaultBranch(owner, repo));
|
|
2402
2426
|
const sourceCommitSha = await getBranchHeadSha(owner, repo, branch);
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2427
|
+
// Candidate subpaths: explicit override wins; otherwise try the conventional
|
|
2428
|
+
// skills/{name}/ layout first, then root-level {name}/ (e.g. higgsfield-ai/skills).
|
|
2429
|
+
const subpathCandidates = skillSubpathOverride
|
|
2430
|
+
? [skillSubpathOverride]
|
|
2431
|
+
: skill
|
|
2432
|
+
? [`skills/${skill}/SKILL.md`, `${skill}/SKILL.md`]
|
|
2433
|
+
: ["SKILL.md"];
|
|
2434
|
+
// Fetch SKILL.md — first candidate that exists wins
|
|
2435
|
+
let skillSubpath = subpathCandidates[0];
|
|
2436
|
+
let content;
|
|
2437
|
+
for (let i = 0; i < subpathCandidates.length; i++) {
|
|
2438
|
+
const candidate = subpathCandidates[i];
|
|
2439
|
+
const candidateUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${candidate}`;
|
|
2440
|
+
const isLast = i === subpathCandidates.length - 1;
|
|
2441
|
+
if (isLast) {
|
|
2442
|
+
content = await fetchSkillContent(candidateUrl);
|
|
2443
|
+
skillSubpath = candidate;
|
|
2444
|
+
break;
|
|
2445
|
+
}
|
|
2446
|
+
const probe = await githubFetch(candidateUrl);
|
|
2447
|
+
if (probe.ok) {
|
|
2448
|
+
content = await probe.text();
|
|
2449
|
+
skillSubpath = candidate;
|
|
2450
|
+
break;
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
if (content === undefined)
|
|
2454
|
+
return process.exit(1);
|
|
2455
|
+
// Fetch the skill's bundled subdirectories when the skill lives in a
|
|
2456
|
+
// subdirectory of the repo. These are the skill-authoring convention and the
|
|
2457
|
+
// same prefixes the installer accepts (see installer/bundle-files.ts):
|
|
2458
|
+
// agents/ holds sub-agent prompts, references/ the docs SKILL.md links to,
|
|
2459
|
+
// scripts/ the executable tooling, assets/ static files, tests/ its tests.
|
|
2460
|
+
// A skill whose SKILL.md says "run scripts/foo.py" and installs without
|
|
2461
|
+
// scripts/ is not degraded — it is broken.
|
|
2408
2462
|
let legacyAgentFiles;
|
|
2409
2463
|
if (skill) {
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2464
|
+
let fetched = 0;
|
|
2465
|
+
for (const subdir of BUNDLE_SUBDIRS) {
|
|
2466
|
+
try {
|
|
2467
|
+
const basePath = skillSubpath.replace(/\/SKILL\.md$/, `/${subdir}`);
|
|
2468
|
+
// ?ref= keeps the listing on the same branch as SKILL.md
|
|
2469
|
+
// (deep links may pin a non-default branch).
|
|
2470
|
+
const dirUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${basePath}?ref=${encodeURIComponent(branch)}`;
|
|
2471
|
+
const dirRes = await githubFetch(dirUrl, { headers: { Accept: "application/vnd.github.v3+json" } });
|
|
2472
|
+
if (!dirRes.ok)
|
|
2473
|
+
continue;
|
|
2419
2474
|
const entries = (await dirRes.json());
|
|
2420
|
-
const
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2475
|
+
const wanted = entries.filter((e) => e.type !== "dir" &&
|
|
2476
|
+
isBundleTextFile(e.name) &&
|
|
2477
|
+
(e.size ?? 0) <= MAX_BUNDLE_FILE_BYTES &&
|
|
2478
|
+
e.download_url &&
|
|
2479
|
+
isGitHubDownloadUrl(e.download_url));
|
|
2480
|
+
if (wanted.length === 0)
|
|
2481
|
+
continue;
|
|
2482
|
+
legacyAgentFiles ??= {};
|
|
2483
|
+
const budget = wanted.slice(0, Math.max(0, MAX_BUNDLE_FILES - fetched));
|
|
2484
|
+
fetched += budget.length;
|
|
2485
|
+
const fetches = budget.map(async (entry) => {
|
|
2486
|
+
try {
|
|
2487
|
+
const res = await githubFetch(entry.download_url);
|
|
2488
|
+
if (res.ok)
|
|
2489
|
+
legacyAgentFiles[`${subdir}/${entry.name}`] = await res.text();
|
|
2490
|
+
}
|
|
2491
|
+
catch { /* skip */ }
|
|
2492
|
+
});
|
|
2493
|
+
await Promise.allSettled(fetches);
|
|
2494
|
+
if (fetched >= MAX_BUNDLE_FILES)
|
|
2495
|
+
break;
|
|
2435
2496
|
}
|
|
2497
|
+
catch { /* subdir doesn't exist or API error — fine */ }
|
|
2436
2498
|
}
|
|
2437
|
-
|
|
2499
|
+
if (legacyAgentFiles && Object.keys(legacyAgentFiles).length === 0)
|
|
2500
|
+
legacyAgentFiles = undefined;
|
|
2438
2501
|
}
|
|
2439
2502
|
// Platform security check (best-effort, non-blocking on network error)
|
|
2440
2503
|
const platformSecurity = await checkPlatformSecurity(skillName);
|