vskill 0.2.44 → 0.2.46
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/commands/add.js +133 -100
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/add.test.js +37 -9
- package/dist/commands/add.test.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -297,40 +297,47 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
297
297
|
for (const unreg of selectedUnregistered) {
|
|
298
298
|
const pluginPath = unreg.source.replace(/^\.\//, "");
|
|
299
299
|
try {
|
|
300
|
-
// Discover skills
|
|
300
|
+
// Discover skills: try nested {pluginPath}/skills/ first, fall back to flat {pluginPath}/SKILL.md
|
|
301
|
+
const skillsToSubmit = [];
|
|
301
302
|
const skillsUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${pluginPath}/skills`;
|
|
302
303
|
const skillsRes = await fetch(skillsUrl, { headers: { "User-Agent": "vskill-cli" } });
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
304
|
+
if (skillsRes.ok) {
|
|
305
|
+
const skillDirs = (await skillsRes.json())
|
|
306
|
+
.filter((e) => e.type === "dir");
|
|
307
|
+
for (const sd of skillDirs) {
|
|
308
|
+
skillsToSubmit.push({ name: sd.name, path: `${pluginPath}/skills/${sd.name}/SKILL.md` });
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
// Flat layout: SKILL.md at plugin root
|
|
313
|
+
skillsToSubmit.push({ name: unreg.name, path: `${pluginPath}/SKILL.md` });
|
|
314
|
+
}
|
|
315
|
+
for (const skill of skillsToSubmit) {
|
|
309
316
|
try {
|
|
310
|
-
const sub = await submitSkill({ repoUrl, skillName:
|
|
317
|
+
const sub = await submitSkill({ repoUrl, skillName: skill.name, skillPath: skill.path });
|
|
311
318
|
if (sub.alreadyVerified) {
|
|
312
|
-
const skillUrl = `https://verified-skill.com/skills/${
|
|
313
|
-
console.log(green(` ${bold(
|
|
319
|
+
const skillUrl = `https://verified-skill.com/skills/${encodeURIComponent(skill.name)}`;
|
|
320
|
+
console.log(green(` ${bold(skill.name)} is already verified.`));
|
|
314
321
|
console.log(dim(" View: ") + link(skillUrl, skillUrl));
|
|
315
322
|
}
|
|
316
323
|
else if (sub.blocked) {
|
|
317
|
-
console.log(red(` ${bold(
|
|
324
|
+
console.log(red(` ${bold(skill.name)} is blocked.`));
|
|
318
325
|
}
|
|
319
326
|
else {
|
|
320
327
|
const subId = sub.id ?? sub.submissionId;
|
|
321
328
|
const trackUrl = `https://verified-skill.com/submit/${subId}`;
|
|
322
329
|
if (sub.duplicate) {
|
|
323
|
-
console.log(yellow(` ${bold(
|
|
330
|
+
console.log(yellow(` ${bold(skill.name)} is already in the queue.`));
|
|
324
331
|
}
|
|
325
332
|
else {
|
|
326
|
-
console.log(green(` Submitted ${bold(
|
|
333
|
+
console.log(green(` Submitted ${bold(skill.name)} for scanning.`));
|
|
327
334
|
}
|
|
328
335
|
console.log(dim(" Track: ") + link(trackUrl, trackUrl));
|
|
329
336
|
}
|
|
330
337
|
}
|
|
331
338
|
catch {
|
|
332
339
|
const submitUrl = `https://verified-skill.com/submit`;
|
|
333
|
-
console.log(yellow(` Could not submit ${
|
|
340
|
+
console.log(yellow(` Could not submit ${skill.name} automatically.`));
|
|
334
341
|
console.log(dim(" Submit manually: ") + link(submitUrl, submitUrl));
|
|
335
342
|
}
|
|
336
343
|
}
|
|
@@ -352,95 +359,91 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
352
359
|
selectedUnregistered = [];
|
|
353
360
|
}
|
|
354
361
|
}
|
|
362
|
+
// ── Step 1: Optional Claude Code native plugin install ──────────────
|
|
355
363
|
let marketplaceRegistered = false;
|
|
356
|
-
if (hasClaude) {
|
|
357
|
-
// Register marketplace via git URL — Claude Code clones to its own
|
|
358
|
-
// persistent location, avoiding the stale-temp-dir bug.
|
|
359
|
-
const gitUrl = `https://github.com/${owner}/${repo}`;
|
|
360
|
-
const regSpin = spinner("Registering marketplace with Claude Code");
|
|
361
|
-
let regResult = registerMarketplace(gitUrl);
|
|
362
|
-
// Retry once after deregistering stale entry
|
|
363
|
-
if (!regResult.success) {
|
|
364
|
-
deregisterMarketplace(gitUrl);
|
|
365
|
-
regResult = registerMarketplace(gitUrl);
|
|
366
|
-
}
|
|
367
|
-
marketplaceRegistered = regResult.success;
|
|
368
|
-
regSpin.stop();
|
|
369
|
-
if (!marketplaceRegistered) {
|
|
370
|
-
console.log(yellow(" Failed to register marketplace — will use extraction fallback."));
|
|
371
|
-
if (regResult.stderr) {
|
|
372
|
-
console.log(dim(` Reason: ${regResult.stderr}`));
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
// Install registered plugins
|
|
377
364
|
const results = [];
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
installSpin.stop();
|
|
384
|
-
if (ok) {
|
|
385
|
-
console.log(green(` ✓ ${bold(plugin.name)}`) + dim(` (${marketplaceName}:${plugin.name})`));
|
|
386
|
-
results.push({ name: plugin.name, installed: true, method: "native" });
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
console.log(red(` ✗ ${bold(plugin.name)}`) + dim(" — native install failed, trying extraction..."));
|
|
390
|
-
// Fallback to extraction
|
|
391
|
-
try {
|
|
392
|
-
await installRepoPlugin(`${owner}/${repo}`, plugin.name, opts);
|
|
393
|
-
results.push({ name: plugin.name, installed: true, method: "extraction" });
|
|
394
|
-
}
|
|
395
|
-
catch {
|
|
396
|
-
results.push({ name: plugin.name, installed: false, method: "failed" });
|
|
397
|
-
}
|
|
398
|
-
}
|
|
365
|
+
if (hasClaude && selectedPlugins.length > 0) {
|
|
366
|
+
let wantNative = false;
|
|
367
|
+
if (isTTY() && !opts.yes) {
|
|
368
|
+
const prompter = createPrompter();
|
|
369
|
+
wantNative = await prompter.promptConfirm("Claude Code detected. Install selected plugins as native Claude Code plugins?", true);
|
|
399
370
|
}
|
|
400
371
|
else {
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
372
|
+
// Non-interactive or --yes: install as native plugin by default
|
|
373
|
+
wantNative = true;
|
|
374
|
+
}
|
|
375
|
+
if (wantNative && marketplaceName) {
|
|
376
|
+
const gitUrl = `https://github.com/${owner}/${repo}`;
|
|
377
|
+
const regSpin = spinner("Registering marketplace with Claude Code");
|
|
378
|
+
let regResult = registerMarketplace(gitUrl);
|
|
379
|
+
if (!regResult.success) {
|
|
380
|
+
deregisterMarketplace(gitUrl);
|
|
381
|
+
regResult = registerMarketplace(gitUrl);
|
|
405
382
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
383
|
+
marketplaceRegistered = regResult.success;
|
|
384
|
+
regSpin.stop();
|
|
385
|
+
if (marketplaceRegistered) {
|
|
386
|
+
for (const plugin of selectedPlugins) {
|
|
387
|
+
const installSpin = spinner(`Installing ${bold(plugin.name)} via Claude Code plugin system`);
|
|
388
|
+
const ok = installNativePlugin(plugin.name, marketplaceName, opts.global ? "user" : "project");
|
|
389
|
+
installSpin.stop();
|
|
390
|
+
if (ok) {
|
|
391
|
+
console.log(green(` ✓ ${bold(plugin.name)}`) + dim(` (${marketplaceName}:${plugin.name})`));
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
console.log(yellow(` ⚠ ${bold(plugin.name)}`) + dim(" — native install failed"));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
console.log(yellow(" Failed to register marketplace — skipping native install."));
|
|
409
400
|
}
|
|
410
401
|
}
|
|
411
402
|
}
|
|
412
|
-
//
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
403
|
+
// ── Step 2: Agent selection + skill file install (ALWAYS) ──────────
|
|
404
|
+
const branch = await getDefaultBranch(owner, repo);
|
|
405
|
+
let agents = await detectInstalledAgents();
|
|
406
|
+
const selections = await promptInstallOptions(agents, opts);
|
|
407
|
+
agents = selections.agents;
|
|
408
|
+
if (selections.global)
|
|
409
|
+
opts.global = true;
|
|
410
|
+
if (!selections.symlink)
|
|
411
|
+
opts.copy = true;
|
|
412
|
+
// Combine all selected plugins (registered + unregistered) for skill file install
|
|
413
|
+
const allPluginsToInstall = [
|
|
414
|
+
...selectedPlugins.map((p) => ({
|
|
415
|
+
name: p.name,
|
|
416
|
+
source: getPluginSource(p.name, manifestContent || "") || "",
|
|
417
|
+
isUnregistered: false,
|
|
418
|
+
})),
|
|
419
|
+
...selectedUnregistered.map((u) => ({
|
|
420
|
+
name: u.name,
|
|
421
|
+
source: u.source,
|
|
422
|
+
isUnregistered: true,
|
|
423
|
+
})),
|
|
424
|
+
];
|
|
425
|
+
for (const plugin of allPluginsToInstall) {
|
|
426
|
+
const pluginPath = plugin.source.replace(/^\.\//, "");
|
|
427
|
+
if (!pluginPath) {
|
|
428
|
+
results.push({ name: plugin.name, installed: false, method: "failed" });
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
const installSpin = spinner(`Installing skills: ${bold(plugin.name)}`);
|
|
432
|
+
try {
|
|
433
|
+
// Discover skills: try {pluginPath}/skills/ first, then fall back to {pluginPath}/SKILL.md
|
|
434
|
+
const installedSkillNames = [];
|
|
435
|
+
const skillsUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${pluginPath}/skills`;
|
|
436
|
+
const skillsRes = await fetch(skillsUrl, { headers: { "User-Agent": "vskill-cli" } });
|
|
437
|
+
if (skillsRes.ok) {
|
|
438
|
+
// Nested layout: {pluginPath}/skills/{skillName}/SKILL.md
|
|
431
439
|
const skillDirs = (await skillsRes.json())
|
|
432
440
|
.filter((e) => e.type === "dir");
|
|
433
|
-
if (skillDirs.length === 0)
|
|
434
|
-
throw new Error(`No skill directories in ${pluginPath}/skills`);
|
|
435
|
-
// Fetch each skill's SKILL.md and install directly to agent dirs
|
|
436
|
-
let installedSkillNames = [];
|
|
437
441
|
for (const sd of skillDirs) {
|
|
438
442
|
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${pluginPath}/skills/${sd.name}/SKILL.md`;
|
|
439
443
|
const contentRes = await fetch(rawUrl);
|
|
440
444
|
if (!contentRes.ok)
|
|
441
445
|
continue;
|
|
442
446
|
const content = await contentRes.text();
|
|
443
|
-
// Write to each agent's skills dir: {agent-dir}/{skillName}/SKILL.md
|
|
444
447
|
for (const agent of agents) {
|
|
445
448
|
const baseDir = resolveInstallBase(opts, agent);
|
|
446
449
|
const skillDir = join(baseDir, sd.name);
|
|
@@ -449,22 +452,38 @@ async function installMarketplaceRepo(owner, repo, manifestContent, opts, preSel
|
|
|
449
452
|
}
|
|
450
453
|
installedSkillNames.push(sd.name);
|
|
451
454
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
// Flat layout: {pluginPath}/SKILL.md directly in the plugin root
|
|
458
|
+
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${pluginPath}/SKILL.md`;
|
|
459
|
+
const contentRes = await fetch(rawUrl);
|
|
460
|
+
if (contentRes.ok) {
|
|
461
|
+
const content = await contentRes.text();
|
|
462
|
+
for (const agent of agents) {
|
|
463
|
+
const baseDir = resolveInstallBase(opts, agent);
|
|
464
|
+
const skillDir = join(baseDir, plugin.name);
|
|
465
|
+
mkdirSync(skillDir, { recursive: true });
|
|
466
|
+
writeFileSync(join(skillDir, "SKILL.md"), content, "utf-8");
|
|
467
|
+
}
|
|
468
|
+
installedSkillNames.push(plugin.name);
|
|
460
469
|
}
|
|
461
470
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
471
|
+
installSpin.stop();
|
|
472
|
+
if (installedSkillNames.length > 0) {
|
|
473
|
+
const tier = plugin.isUnregistered ? "unverified" : "verified";
|
|
474
|
+
console.log(green(` ✓ ${bold(plugin.name)}`) + dim(` (${installedSkillNames.join(", ")}) [${tier}]`));
|
|
475
|
+
results.push({ name: plugin.name, installed: true, method: plugin.isUnregistered ? "extraction-unregistered" : "extraction" });
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
console.log(red(` ✗ ${bold(plugin.name)}`) + dim(" — no SKILL.md found"));
|
|
479
|
+
results.push({ name: plugin.name, installed: false, method: "failed" });
|
|
466
480
|
}
|
|
467
481
|
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
installSpin.stop();
|
|
484
|
+
console.error(red(` ✗ ${plugin.name}: ${err.message}`));
|
|
485
|
+
results.push({ name: plugin.name, installed: false, method: "failed" });
|
|
486
|
+
}
|
|
468
487
|
}
|
|
469
488
|
// Update lockfile
|
|
470
489
|
const lockForWrite = ensureLockfile(lockDir);
|
|
@@ -1305,6 +1324,7 @@ async function installRepoPlugin(ownerRepo, pluginName, opts, overrideSource) {
|
|
|
1305
1324
|
// Discover skills via GitHub Contents API
|
|
1306
1325
|
const discoverSpin = spinner(`Discovering skills in ${pluginName}`);
|
|
1307
1326
|
let skillEntries = [];
|
|
1327
|
+
let flatLayout = false;
|
|
1308
1328
|
try {
|
|
1309
1329
|
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${pluginPath}/skills`, { headers: { "User-Agent": "vskill-cli" } });
|
|
1310
1330
|
if (res.ok) {
|
|
@@ -1312,6 +1332,17 @@ async function installRepoPlugin(ownerRepo, pluginName, opts, overrideSource) {
|
|
|
1312
1332
|
}
|
|
1313
1333
|
}
|
|
1314
1334
|
catch { /* skills dir might not exist */ }
|
|
1335
|
+
// Flat layout fallback: check for SKILL.md directly at plugin root
|
|
1336
|
+
if (skillEntries.length === 0) {
|
|
1337
|
+
try {
|
|
1338
|
+
const res = await fetch(`https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${pluginPath}/SKILL.md`);
|
|
1339
|
+
if (res.ok) {
|
|
1340
|
+
flatLayout = true;
|
|
1341
|
+
skillEntries = [{ name: pluginName, type: "dir" }];
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
catch { /* flat layout not available */ }
|
|
1345
|
+
}
|
|
1315
1346
|
let cmdEntries = [];
|
|
1316
1347
|
try {
|
|
1317
1348
|
const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${pluginPath}/commands`, { headers: { "User-Agent": "vskill-cli" } });
|
|
@@ -1337,7 +1368,9 @@ async function installRepoPlugin(ownerRepo, pluginName, opts, overrideSource) {
|
|
|
1337
1368
|
const allContent = [];
|
|
1338
1369
|
const skills = [];
|
|
1339
1370
|
for (const entry of skillEntries) {
|
|
1340
|
-
const rawUrl =
|
|
1371
|
+
const rawUrl = flatLayout
|
|
1372
|
+
? `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${pluginPath}/SKILL.md`
|
|
1373
|
+
: `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${pluginPath}/skills/${entry.name}/SKILL.md`;
|
|
1341
1374
|
try {
|
|
1342
1375
|
const res = await fetch(rawUrl);
|
|
1343
1376
|
if (res.ok) {
|