skillmds 1.2.0 → 1.3.0
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/{chunk-A7BKAUUO.js → chunk-FVWCI37S.js} +17 -3
- package/dist/cli.js +307 -99
- package/dist/mcp.js +1 -1
- package/package.json +2 -1
|
@@ -19,6 +19,12 @@ function parseSource(arg, opts = {}) {
|
|
|
19
19
|
const exists = opts.exists ?? existsSync;
|
|
20
20
|
const a = arg.trim();
|
|
21
21
|
if (!a) throw new Error("a source is required: owner/name, a GitHub URL, or a local path");
|
|
22
|
+
if (/^(plugin|pack):/i.test(a)) {
|
|
23
|
+
const body2 = a.slice(a.indexOf(":") + 1);
|
|
24
|
+
const m2 = body2.match(/^([^/#@\s]+)\/([^/#@\s]+)$/);
|
|
25
|
+
if (!m2) throw new Error(`"${a}" is not a valid plugin source \u2014 expected plugin:owner/slug`);
|
|
26
|
+
return { kind: "plugin", owner: m2[1], slug: m2[2], display: a };
|
|
27
|
+
}
|
|
22
28
|
if (a === "~" || a.startsWith("~/") || a.startsWith("~\\")) {
|
|
23
29
|
return { kind: "local", path: join(homedir(), a.slice(1)), display: a };
|
|
24
30
|
}
|
|
@@ -54,6 +60,10 @@ function sourceId(spec) {
|
|
|
54
60
|
switch (spec.kind) {
|
|
55
61
|
case "slug":
|
|
56
62
|
return `registry:${spec.owner}/${spec.name}`;
|
|
63
|
+
// Members are recorded under their own registry slug, not this one — the
|
|
64
|
+
// lock file tracks what was written, and a plugin is only how they arrived.
|
|
65
|
+
case "plugin":
|
|
66
|
+
return `plugin:${spec.owner}/${spec.slug}`;
|
|
57
67
|
case "github":
|
|
58
68
|
return `github:${spec.owner}/${spec.repo}${spec.subpath ? `/${spec.subpath}` : ""}${spec.ref ? `#${spec.ref}` : ""}${spec.skill ? `@${spec.skill}` : ""}`;
|
|
59
69
|
case "gist":
|
|
@@ -182,13 +192,14 @@ async function fetchBundle(base, slug, token, opts = {}) {
|
|
|
182
192
|
let bytes = 0;
|
|
183
193
|
for (const f of data.files) {
|
|
184
194
|
let contents;
|
|
195
|
+
let fromUrl = false;
|
|
185
196
|
if (f.content_base64 != null) {
|
|
186
197
|
contents = Buffer.from(f.content_base64, "base64");
|
|
187
198
|
} else if (f.source_url && isAllowedSourceUrl(f.source_url)) {
|
|
188
|
-
if (!f.sha256) throw new IntegrityError(`"${f.path}" is delivered by URL but the registry recorded no sha256 for it \u2014 refusing to install unverifiable content`);
|
|
189
199
|
const r = await doFetch(f.source_url).catch(() => null);
|
|
190
200
|
if (!r || !r.ok) throw new Error(`could not fetch companion file "${f.path}" from ${hostOf(f.source_url)}`);
|
|
191
201
|
contents = Buffer.from(await r.arrayBuffer());
|
|
202
|
+
fromUrl = true;
|
|
192
203
|
} else {
|
|
193
204
|
continue;
|
|
194
205
|
}
|
|
@@ -197,10 +208,10 @@ async function fetchBundle(base, slug, token, opts = {}) {
|
|
|
197
208
|
if (f.sha256) {
|
|
198
209
|
const got = createHash("sha256").update(contents).digest("hex");
|
|
199
210
|
if (got !== f.sha256) throw new IntegrityError(`integrity check failed for "${f.path}" (expected ${f.sha256.slice(0, 12)}\u2026, got ${got.slice(0, 12)}\u2026)`);
|
|
200
|
-
} else if (f.path !== "SKILL.md") {
|
|
211
|
+
} else if (!fromUrl && f.path !== "SKILL.md") {
|
|
201
212
|
throw new IntegrityError(`"${f.path}" has no sha256 in the registry bundle \u2014 refusing to install unverifiable content`);
|
|
202
213
|
}
|
|
203
|
-
out.push({ path: f.path, contents });
|
|
214
|
+
out.push({ path: f.path, contents, ...fromUrl && !f.sha256 ? { unverified: true } : {} });
|
|
204
215
|
}
|
|
205
216
|
return out;
|
|
206
217
|
}
|
|
@@ -313,6 +324,9 @@ async function resolveSource(arg, opts = {}) {
|
|
|
313
324
|
if (raw.length > MAX_PACK_BYTES) throw tooBig(raw.length);
|
|
314
325
|
return [{ path: spec.display, raw, slug: `gist-${spec.id.slice(0, 8)}` }];
|
|
315
326
|
}
|
|
327
|
+
if (spec.kind === "plugin") {
|
|
328
|
+
throw new Error(`"${spec.display}" is a plugin, not a single skill \u2014 install it with \`skillmd add ${spec.display}\`.`);
|
|
329
|
+
}
|
|
316
330
|
const gh = asGithub(spec);
|
|
317
331
|
const { dir, cleanup } = await downloadGithub(gh, "skillmd-src-", opts, Boolean(gh.subpath));
|
|
318
332
|
try {
|
package/dist/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
uninstallSkill,
|
|
31
31
|
upsertEntry,
|
|
32
32
|
writeConfig
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-FVWCI37S.js";
|
|
34
34
|
|
|
35
35
|
// src/index.ts
|
|
36
36
|
import { Command as Command13 } from "commander";
|
|
@@ -319,12 +319,12 @@ function lintCommand() {
|
|
|
319
319
|
const s = p.spinner();
|
|
320
320
|
s.start(`Scanning ${target}`);
|
|
321
321
|
try {
|
|
322
|
-
const
|
|
323
|
-
s.stop(`Scanned ${
|
|
324
|
-
console.log(
|
|
325
|
-
const failed =
|
|
322
|
+
const run3 = await runLint(target, opts);
|
|
323
|
+
s.stop(`Scanned ${run3.results.length} skill${run3.results.length === 1 ? "" : "s"}`);
|
|
324
|
+
console.log(run3.output);
|
|
325
|
+
const failed = run3.results.filter((r) => !r.result.ok).length;
|
|
326
326
|
p.outro(failed === 0 ? pc2.green("All good") : pc2.red(`${failed} skill${failed === 1 ? "" : "s"} failed \u2014 details above`));
|
|
327
|
-
process.exitCode =
|
|
327
|
+
process.exitCode = run3.exitCode;
|
|
328
328
|
} catch (err) {
|
|
329
329
|
s.stop(pc2.red("No skills found"));
|
|
330
330
|
p.outro(err instanceof Error ? err.message : String(err));
|
|
@@ -334,9 +334,9 @@ function lintCommand() {
|
|
|
334
334
|
}
|
|
335
335
|
process.stderr.write(`Looking for SKILL.md under ${target} \u2026
|
|
336
336
|
`);
|
|
337
|
-
const
|
|
338
|
-
console.log(
|
|
339
|
-
process.exitCode =
|
|
337
|
+
const run2 = await runLint(target, opts);
|
|
338
|
+
console.log(run2.output);
|
|
339
|
+
process.exitCode = run2.exitCode;
|
|
340
340
|
});
|
|
341
341
|
}
|
|
342
342
|
|
|
@@ -375,9 +375,9 @@ async function runScan(target, flags) {
|
|
|
375
375
|
}
|
|
376
376
|
function scanCommand() {
|
|
377
377
|
return new Command2("scan").description("Security-scan SKILL.md file(s) for scripts, network calls and secret access").argument("[target]", "local path or GitHub source", ".").option("--deny <flags...>", "exit non-zero if any of these flags are present (e.g. executes_scripts)").option("--format <fmt>", "text | json", "text").action(async (target, opts) => {
|
|
378
|
-
const
|
|
379
|
-
console.log(
|
|
380
|
-
process.exitCode =
|
|
378
|
+
const run2 = await runScan(target, opts);
|
|
379
|
+
console.log(run2.output);
|
|
380
|
+
process.exitCode = run2.exitCode;
|
|
381
381
|
});
|
|
382
382
|
}
|
|
383
383
|
|
|
@@ -504,8 +504,8 @@ async function runInit(nameArg, flags, deps = defaultInitDeps(flags)) {
|
|
|
504
504
|
const file = initSkill(name, { dir: flags.dir, description: flags.description, license: flags.license });
|
|
505
505
|
return { exitCode: 0, file, output: flags.json ? JSON.stringify({ ok: true, file }, null, 2) : `Created ${file}` };
|
|
506
506
|
} catch (e) {
|
|
507
|
-
const
|
|
508
|
-
return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error:
|
|
507
|
+
const msg2 = e instanceof Error ? e.message : String(e);
|
|
508
|
+
return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error: msg2 }, null, 2) : pc5.red(msg2) };
|
|
509
509
|
}
|
|
510
510
|
}
|
|
511
511
|
function initCommand() {
|
|
@@ -563,8 +563,8 @@ async function runSearch(query, flags) {
|
|
|
563
563
|
function searchCommand() {
|
|
564
564
|
return new Command5("search").description("Search the SkillMD registry").argument("<query>", "search terms").option("--category <slug>", "filter by category").option("--verified", "only verified skills").option("--type <type>", "single | pack").option("--min-rating <n>", "minimum average rating (0-5)").option("--limit <n>", "max results", "20").option("--json", "machine-readable output").action(async (query, opts, cmd) => {
|
|
565
565
|
const merged = { ...cmd.parent?.opts(), ...opts };
|
|
566
|
-
const
|
|
567
|
-
console.log(
|
|
566
|
+
const run2 = await runSearch(query, merged);
|
|
567
|
+
console.log(run2.output);
|
|
568
568
|
});
|
|
569
569
|
}
|
|
570
570
|
|
|
@@ -613,19 +613,99 @@ async function runInfo(slug, flags, deps = {}) {
|
|
|
613
613
|
function infoCommand() {
|
|
614
614
|
return new Command6("info").description("Show registry details for a skill: verification, security flags, provenance, rating").argument("<slug>", "owner/name, e.g. anthropic/pdf").action(async (slug, _opts, cmd) => {
|
|
615
615
|
const flags = { ...cmd.parent?.opts() };
|
|
616
|
-
const
|
|
617
|
-
if (
|
|
618
|
-
process.exitCode =
|
|
616
|
+
const run2 = await runInfo(slug, flags);
|
|
617
|
+
if (run2.output) (run2.ok ? console.log : console.error)(run2.output);
|
|
618
|
+
process.exitCode = run2.exitCode;
|
|
619
619
|
});
|
|
620
620
|
}
|
|
621
621
|
|
|
622
622
|
// src/commands/add.ts
|
|
623
623
|
import { Command as Command7, Option } from "commander";
|
|
624
|
-
import { homedir as
|
|
624
|
+
import { homedir as homedir4 } from "os";
|
|
625
625
|
import * as p3 from "@clack/prompts";
|
|
626
626
|
import { dirname, resolve as resolve3 } from "path";
|
|
627
627
|
import pc8 from "picocolors";
|
|
628
628
|
import { lint as lint2, parseSkillMd as parseSkillMd2 } from "@skillmds/core";
|
|
629
|
+
|
|
630
|
+
// src/claudePlugin.ts
|
|
631
|
+
import { execFile } from "child_process";
|
|
632
|
+
import { existsSync as existsSync2 } from "fs";
|
|
633
|
+
import { homedir as homedir3 } from "os";
|
|
634
|
+
import { delimiter, join as join2 } from "path";
|
|
635
|
+
import { promisify } from "util";
|
|
636
|
+
var run = promisify(execFile);
|
|
637
|
+
function resolveClaudeBin(env = process.env) {
|
|
638
|
+
const override = env.SKILLMD_CLAUDE_BIN;
|
|
639
|
+
if (override) return existsSync2(override) ? override : null;
|
|
640
|
+
const win = process.platform === "win32";
|
|
641
|
+
const exts = win ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
642
|
+
const dirs = (env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
643
|
+
const home = env.USERPROFILE || env.HOME || homedir3();
|
|
644
|
+
dirs.push(join2(home, ".local", "bin"), join2(home, ".claude", "local"));
|
|
645
|
+
for (const dir of dirs) {
|
|
646
|
+
for (const ext of exts) {
|
|
647
|
+
const candidate = join2(dir, `claude${ext.toLowerCase()}`);
|
|
648
|
+
if (existsSync2(candidate)) return candidate;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
var needsShell = (bin) => /\.(cmd|bat)$/i.test(bin);
|
|
654
|
+
var MARKETPLACE_URL = "https://skillmd.com/.claude-plugin/marketplace.json";
|
|
655
|
+
var MARKETPLACE_NAME = "skillmd";
|
|
656
|
+
var TIMEOUT_MS = 12e4;
|
|
657
|
+
function exec(deps) {
|
|
658
|
+
return deps.exec ?? ((file, args) => {
|
|
659
|
+
const bin = file === "claude" ? resolveClaudeBin() : file;
|
|
660
|
+
if (!bin) return Promise.reject(new Error("claude not found on PATH"));
|
|
661
|
+
return run(bin, args, { timeout: TIMEOUT_MS, windowsHide: true, shell: needsShell(bin) }).then((r) => ({
|
|
662
|
+
stdout: String(r.stdout ?? ""),
|
|
663
|
+
stderr: String(r.stderr ?? "")
|
|
664
|
+
}));
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
function marketplacePluginName(owner, slug) {
|
|
668
|
+
const clean = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
669
|
+
const base = clean(slug) || "plugin";
|
|
670
|
+
return owner.toLowerCase() === "skillmd" ? base : `${clean(owner)}-${base}`;
|
|
671
|
+
}
|
|
672
|
+
async function claudeCliAvailable(deps = {}) {
|
|
673
|
+
try {
|
|
674
|
+
await exec(deps)("claude", ["--version"]);
|
|
675
|
+
return true;
|
|
676
|
+
} catch {
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
async function installClaudePlugin(owner, slug, deps = {}) {
|
|
681
|
+
const sh = exec(deps);
|
|
682
|
+
const qualified = `${marketplacePluginName(owner, slug)}@${MARKETPLACE_NAME}`;
|
|
683
|
+
try {
|
|
684
|
+
await sh("claude", ["plugin", "marketplace", "add", MARKETPLACE_URL]);
|
|
685
|
+
} catch (e) {
|
|
686
|
+
return { ok: false, reason: `could not add the SkillMD marketplace: ${msg(e)}` };
|
|
687
|
+
}
|
|
688
|
+
let out;
|
|
689
|
+
try {
|
|
690
|
+
const r = await sh("claude", ["plugin", "install", qualified]);
|
|
691
|
+
out = `${r.stdout}
|
|
692
|
+
${r.stderr}`;
|
|
693
|
+
} catch (e) {
|
|
694
|
+
return { ok: false, reason: msg(e) };
|
|
695
|
+
}
|
|
696
|
+
if (/Successfully installed plugin/i.test(out)) return { ok: true, name: qualified, marketplace: MARKETPLACE_NAME };
|
|
697
|
+
const failed = /Failed to install plugin[^:]*:\s*(.+)/i.exec(out);
|
|
698
|
+
return { ok: false, reason: (failed?.[1] ?? out).trim().split(/\r?\n/)[0] ?? "install did not report success" };
|
|
699
|
+
}
|
|
700
|
+
function msg(e) {
|
|
701
|
+
if (e && typeof e === "object" && "stderr" in e) {
|
|
702
|
+
const s = String(e.stderr ?? "").trim();
|
|
703
|
+
if (s) return s.split(/\r?\n/)[0];
|
|
704
|
+
}
|
|
705
|
+
return e instanceof Error ? e.message.split(/\r?\n/)[0] : String(e);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/commands/add.ts
|
|
629
709
|
var infoNotice = (text3) => ({ text: text3, style: "info" });
|
|
630
710
|
var warnNotice = (text3) => ({ text: text3, style: "warn" });
|
|
631
711
|
var skipNotice = (text3) => ({ text: text3, style: "skip" });
|
|
@@ -662,6 +742,10 @@ async function promptAgentsInteractive(ctx) {
|
|
|
662
742
|
});
|
|
663
743
|
return p3.isCancel(picked) ? null : picked;
|
|
664
744
|
}
|
|
745
|
+
function unverifiedFilesNote(n) {
|
|
746
|
+
const [f, it] = n === 1 ? ["file", "it"] : ["files", "them"];
|
|
747
|
+
return `${n} companion ${f} came straight from the source repo \u2014 the registry holds no checksum for ${it}, so only the connection was verified`;
|
|
748
|
+
}
|
|
665
749
|
function registryFallbackNote(host) {
|
|
666
750
|
return `installed from GitHub because the SkillMD registry (${host}) was unreachable \u2014 this copy is not the registry-reviewed version`;
|
|
667
751
|
}
|
|
@@ -689,6 +773,77 @@ function provenanceFor(arg) {
|
|
|
689
773
|
return `unknown:${arg}`;
|
|
690
774
|
}
|
|
691
775
|
}
|
|
776
|
+
async function registryCandidate(owner, name, source, client) {
|
|
777
|
+
const registrySlug = `${owner}/${name}`;
|
|
778
|
+
const skill = await client.api(`/api/skills/${owner}/${name}`);
|
|
779
|
+
const skillName = skill.slug.split("/")[1] || name;
|
|
780
|
+
const meta = {
|
|
781
|
+
name: skillName,
|
|
782
|
+
slug: skillName,
|
|
783
|
+
registrySlug,
|
|
784
|
+
source,
|
|
785
|
+
commit_sha: skill.commit_sha ?? void 0,
|
|
786
|
+
verified: skill.verified,
|
|
787
|
+
type: skill.type,
|
|
788
|
+
securityFlags: skill.security_flags
|
|
789
|
+
};
|
|
790
|
+
if (skill.type === "pack") {
|
|
791
|
+
const bundle = await fetchBundle(client.base, registrySlug, client.token);
|
|
792
|
+
if (bundle && bundle.length) {
|
|
793
|
+
const sk = bundle.find((file) => file.path === "SKILL.md");
|
|
794
|
+
const unverified = bundle.filter((file) => file.unverified).length;
|
|
795
|
+
return {
|
|
796
|
+
...meta,
|
|
797
|
+
raw: sk ? sk.contents.toString("utf8") : skillMdFor(skill),
|
|
798
|
+
files: bundle,
|
|
799
|
+
...unverified ? { note: unverifiedFilesNote(unverified) } : {}
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
const pack = await resolvePackFiles(skill);
|
|
803
|
+
if (pack) return { ...meta, ...pack };
|
|
804
|
+
}
|
|
805
|
+
return { ...meta, raw: skillMdFor(skill) };
|
|
806
|
+
}
|
|
807
|
+
async function fetchPackMembers(api, owner, slug) {
|
|
808
|
+
const PAGE = 200;
|
|
809
|
+
const members = [];
|
|
810
|
+
let name = slug;
|
|
811
|
+
let total = Infinity;
|
|
812
|
+
for (let offset = 0; members.length < total; offset += PAGE) {
|
|
813
|
+
const page = await api(`/api/packs/${owner}/${slug}?limit=${PAGE}&offset=${offset}`);
|
|
814
|
+
name = page.name || name;
|
|
815
|
+
total = Number(page.item_total ?? 0);
|
|
816
|
+
const got = (page.items ?? []).map((i) => ({ slug: String(i.slug ?? ""), type: i.type, verified: Boolean(i.verified) })).filter((i) => i.slug);
|
|
817
|
+
if (!got.length) break;
|
|
818
|
+
members.push(...got);
|
|
819
|
+
}
|
|
820
|
+
return { name, members };
|
|
821
|
+
}
|
|
822
|
+
function packDirNames(slugs) {
|
|
823
|
+
const used = /* @__PURE__ */ new Set();
|
|
824
|
+
return slugs.map((slug) => {
|
|
825
|
+
const [owner, name] = slug.split("/");
|
|
826
|
+
let dir = name ?? slug;
|
|
827
|
+
if (used.has(dir)) dir = `${owner}-${name}`;
|
|
828
|
+
used.add(dir);
|
|
829
|
+
return dir;
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
async function fetchPackArchive(base, owner, slug, token, doFetch = fetch) {
|
|
833
|
+
const headers = { accept: "application/zip" };
|
|
834
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
835
|
+
const res = await doFetch(`${base}/api/packs/${owner}/${slug}/download`, { headers });
|
|
836
|
+
if (!res.ok) throw new RegistryError(`SkillMD API ${res.status} on /api/packs/${owner}/${slug}/download`, res.status);
|
|
837
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
838
|
+
const { unzipSync, strFromU8 } = await import("fflate");
|
|
839
|
+
const entries = unzipSync(bytes);
|
|
840
|
+
const out = /* @__PURE__ */ new Map();
|
|
841
|
+
for (const [path, data] of Object.entries(entries)) {
|
|
842
|
+
const m = /^(.+)\/SKILL\.md$/.exec(path);
|
|
843
|
+
if (m) out.set(m[1], strFromU8(data));
|
|
844
|
+
}
|
|
845
|
+
return out;
|
|
846
|
+
}
|
|
692
847
|
function makeDefaultDeps(flags) {
|
|
693
848
|
const canPrompt = () => isInteractive(flags, {
|
|
694
849
|
stdinTTY: Boolean(process.stdin.isTTY),
|
|
@@ -699,6 +854,55 @@ function makeDefaultDeps(flags) {
|
|
|
699
854
|
async resolve(arg, f) {
|
|
700
855
|
const spec = parseSource(arg, { ref: f.ref });
|
|
701
856
|
const source = sourceId(spec);
|
|
857
|
+
if (spec.kind === "plugin") {
|
|
858
|
+
const { api: api2, base: apiBase2, token: token2, fetch: doFetch } = createClient(f);
|
|
859
|
+
const { name: packName, members } = await fetchPackMembers(api2, spec.owner, spec.slug);
|
|
860
|
+
if (!members.length) throw new Error(`plugin ${spec.owner}/${spec.slug} has no installable skills`);
|
|
861
|
+
const archive = await fetchPackArchive(apiBase2, spec.owner, spec.slug, token2, doFetch);
|
|
862
|
+
const dirs = packDirNames(members.map((m) => m.slug));
|
|
863
|
+
const out = [];
|
|
864
|
+
const missing = [];
|
|
865
|
+
members.forEach((m, i) => {
|
|
866
|
+
const raw = archive.get(dirs[i]);
|
|
867
|
+
if (!raw) {
|
|
868
|
+
missing.push(m.slug);
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
const name = dirs[i];
|
|
872
|
+
out.push({
|
|
873
|
+
name,
|
|
874
|
+
slug: name,
|
|
875
|
+
registrySlug: m.slug,
|
|
876
|
+
source,
|
|
877
|
+
raw,
|
|
878
|
+
verified: m.verified,
|
|
879
|
+
type: m.type
|
|
880
|
+
});
|
|
881
|
+
});
|
|
882
|
+
const multi = out.filter((c) => c.type === "pack");
|
|
883
|
+
if (multi.length) {
|
|
884
|
+
let n = 0;
|
|
885
|
+
await Promise.all(Array.from({ length: Math.min(4, multi.length) }, async () => {
|
|
886
|
+
for (let i = n++; i < multi.length; i = n++) {
|
|
887
|
+
const c = multi[i];
|
|
888
|
+
try {
|
|
889
|
+
const bundle = await fetchBundle(apiBase2, c.registrySlug, token2);
|
|
890
|
+
if (!bundle?.length) continue;
|
|
891
|
+
const sk = bundle.find((file) => file.path === "SKILL.md");
|
|
892
|
+
const unverified = bundle.filter((file) => file.unverified).length;
|
|
893
|
+
c.files = bundle;
|
|
894
|
+
if (sk) c.raw = sk.contents.toString("utf8");
|
|
895
|
+
if (unverified) c.note = unverifiedFilesNote(unverified);
|
|
896
|
+
} catch {
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}));
|
|
900
|
+
}
|
|
901
|
+
if (!out.length) throw new Error(`plugin ${spec.owner}/${spec.slug} returned no readable skills`);
|
|
902
|
+
const first = out[0];
|
|
903
|
+
first.note = `${out.length} skill${out.length === 1 ? "" : "s"} from the ${packName} plugin` + (missing.length ? ` (${missing.length} member${missing.length === 1 ? "" : "s"} were not in the archive)` : "");
|
|
904
|
+
return out;
|
|
905
|
+
}
|
|
702
906
|
if (spec.kind !== "slug") {
|
|
703
907
|
const rs = await resolveSource(argWithRef(arg, f.ref));
|
|
704
908
|
return rs.map((r) => ({
|
|
@@ -720,28 +924,7 @@ function makeDefaultDeps(flags) {
|
|
|
720
924
|
})();
|
|
721
925
|
const registrySlug = `${spec.owner}/${spec.name}`;
|
|
722
926
|
try {
|
|
723
|
-
|
|
724
|
-
const skillName = skill.slug.split("/")[1] || spec.name;
|
|
725
|
-
const meta = {
|
|
726
|
-
name: skillName,
|
|
727
|
-
slug: skillName,
|
|
728
|
-
registrySlug,
|
|
729
|
-
source,
|
|
730
|
-
commit_sha: skill.commit_sha ?? void 0,
|
|
731
|
-
verified: skill.verified,
|
|
732
|
-
type: skill.type,
|
|
733
|
-
securityFlags: skill.security_flags
|
|
734
|
-
};
|
|
735
|
-
if (skill.type === "pack") {
|
|
736
|
-
const bundle = await fetchBundle(apiBase, registrySlug, token);
|
|
737
|
-
if (bundle && bundle.length) {
|
|
738
|
-
const sk = bundle.find((file) => file.path === "SKILL.md");
|
|
739
|
-
return [{ ...meta, raw: sk ? sk.contents.toString("utf8") : skillMdFor(skill), files: bundle }];
|
|
740
|
-
}
|
|
741
|
-
const pack = await resolvePackFiles(skill);
|
|
742
|
-
if (pack) return [{ ...meta, ...pack }];
|
|
743
|
-
}
|
|
744
|
-
return [{ ...meta, raw: skillMdFor(skill) }];
|
|
927
|
+
return [await registryCandidate(spec.owner, spec.name, source, { api, base: apiBase, token })];
|
|
745
928
|
} catch (e) {
|
|
746
929
|
if (e instanceof IntegrityError) throw e;
|
|
747
930
|
if (e instanceof RegistryError && e.status !== 404) registryDown = e;
|
|
@@ -775,7 +958,7 @@ function makeDefaultDeps(flags) {
|
|
|
775
958
|
};
|
|
776
959
|
}
|
|
777
960
|
async function resolveScope(flags, deps) {
|
|
778
|
-
const home = resolve3(flags.home ??
|
|
961
|
+
const home = resolve3(flags.home ?? homedir4());
|
|
779
962
|
const cwd = resolve3(flags.cwd ?? process.cwd());
|
|
780
963
|
const base = { cwd: flags.cwd, home: flags.home, env: flags.env };
|
|
781
964
|
if (flags.global) return { scope: { global: true, ...base } };
|
|
@@ -798,7 +981,7 @@ function defaultNeedsPrompt(flags) {
|
|
|
798
981
|
const env = flags.env ?? process.env;
|
|
799
982
|
if (flags.yes || flags.json || flags.global || flags.project || flags.list) return false;
|
|
800
983
|
if (detectHostAgent(env)) return false;
|
|
801
|
-
if (resolve3(flags.cwd ?? process.cwd()) === resolve3(flags.home ??
|
|
984
|
+
if (resolve3(flags.cwd ?? process.cwd()) === resolve3(flags.home ?? homedir4())) return false;
|
|
802
985
|
return !isInteractive(flags, { stdinTTY: Boolean(process.stdin.isTTY), stdoutTTY: Boolean(process.stdout.isTTY), env });
|
|
803
986
|
}
|
|
804
987
|
var CANCELLED_TEXT = "installation cancelled \u2014 nothing was installed";
|
|
@@ -813,7 +996,7 @@ function finish2(flags, outcome) {
|
|
|
813
996
|
...outcome.notices,
|
|
814
997
|
...outcome.records.flatMap((r) => r.kind === "installed" ? r.notices : [])
|
|
815
998
|
].map((n) => n.text);
|
|
816
|
-
const exitCode = blocked.length > 0 || installed.length === 0 ? 1 : 0;
|
|
999
|
+
const exitCode = blocked.length > 0 || installed.length === 0 && !outcome.managed ? 1 : 0;
|
|
817
1000
|
const doc = { ok: exitCode === 0, scope: outcome.scope, installed, blocked, warnings };
|
|
818
1001
|
return {
|
|
819
1002
|
written,
|
|
@@ -975,9 +1158,28 @@ async function installOne(c, ctx) {
|
|
|
975
1158
|
...out.replaced ? { replaced: out.replaced } : {}
|
|
976
1159
|
};
|
|
977
1160
|
}
|
|
1161
|
+
async function tryPluginInstall(arg, flags) {
|
|
1162
|
+
let spec;
|
|
1163
|
+
try {
|
|
1164
|
+
spec = parseSource(arg, { ref: flags.ref });
|
|
1165
|
+
} catch {
|
|
1166
|
+
return null;
|
|
1167
|
+
}
|
|
1168
|
+
if (spec.kind !== "plugin") return null;
|
|
1169
|
+
if (flags.agent?.length || flags.skill?.length || flags.list || flags.mode === "copy") return null;
|
|
1170
|
+
if (!await claudeCliAvailable()) return null;
|
|
1171
|
+
const res = await installClaudePlugin(spec.owner, spec.slug);
|
|
1172
|
+
if (!res.ok) return null;
|
|
1173
|
+
return {
|
|
1174
|
+
agent: "claude-code",
|
|
1175
|
+
name: res.name,
|
|
1176
|
+
note: `installed ${res.name} as a Claude Code plugin \u2014 manage it with \`claude plugin\` or /plugin`
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
978
1179
|
async function runAdd(arg, flags, deps) {
|
|
979
1180
|
const d = deps ?? makeDefaultDeps(flags);
|
|
980
1181
|
const env = flags.env ?? process.env;
|
|
1182
|
+
const managed = await tryPluginInstall(arg, flags);
|
|
981
1183
|
let candidates;
|
|
982
1184
|
try {
|
|
983
1185
|
candidates = await resolveCandidates(arg, flags, d);
|
|
@@ -994,14 +1196,20 @@ async function runAdd(arg, flags, deps) {
|
|
|
994
1196
|
if (!resolved) return finish2(flags, { kind: "cancel" });
|
|
995
1197
|
const scope = resolved.scope;
|
|
996
1198
|
const notices = resolved.note ? [infoNotice(resolved.note)] : [];
|
|
997
|
-
|
|
1199
|
+
if (managed) notices.push(infoNotice(managed.note));
|
|
1200
|
+
const detected = detectAgents(scope).filter((id) => id !== managed?.agent);
|
|
1201
|
+
if (managed && !detected.length) {
|
|
1202
|
+
const paths2 = { home: flags.home ?? homedir4(), cwd: flags.cwd ?? process.cwd() };
|
|
1203
|
+
return finish2(flags, { kind: "install", scope: scope.global ? "global" : "project", records: [], notices, paths: paths2, managed: managed.name });
|
|
1204
|
+
}
|
|
1205
|
+
const chosen = await chooseTargets(flags, d, scope, detected);
|
|
998
1206
|
if (!chosen) return finish2(flags, { kind: "cancel" });
|
|
999
1207
|
notices.push(...chosen.notices);
|
|
1000
1208
|
const ctx = { arg, flags, deny: new Set(flags.deny ?? []), scope, targets: chosen.targets, env, deps: d };
|
|
1001
1209
|
const records = [];
|
|
1002
1210
|
for (const c of candidates) records.push(await installOne(c, ctx));
|
|
1003
|
-
const paths = { home: flags.home ??
|
|
1004
|
-
return finish2(flags, { kind: "install", scope: scope.global ? "global" : "project", records, notices, paths });
|
|
1211
|
+
const paths = { home: flags.home ?? homedir4(), cwd: flags.cwd ?? process.cwd() };
|
|
1212
|
+
return finish2(flags, { kind: "install", scope: scope.global ? "global" : "project", records, notices, paths, ...managed ? { managed: managed.name } : {} });
|
|
1005
1213
|
}
|
|
1006
1214
|
function addFlags(opts, parent) {
|
|
1007
1215
|
const merged = { ...parent, ...opts };
|
|
@@ -1011,12 +1219,12 @@ function addFlags(opts, parent) {
|
|
|
1011
1219
|
function addCommand() {
|
|
1012
1220
|
return new Command7("add").description("Install a skill from the registry, a GitHub repo, or a local path (lints before writing)").argument("<source>", "registry slug (owner/name), GitHub source, or local path").option("-g, --global", "install to your user-level agent folders (available in every project)").option("-p, --project", "install into the current directory's agent folders (committed with the project)").option("-a, --agent <agents...>", "target specific agents (default: every agent detected on this machine)").option("-s, --skill <names...>", "install only specific skills by name ('*' for all)").option("--ref <ref>", "git ref (branch, tag or commit) for GitHub sources").option("-l, --list", "list the skills a source contains without installing").addOption(new Option("--mode <mode>", "link (junction/symlink into each agent dir, default) or copy").choices(["link", "copy"]).default("link")).option("--force", "replace a skill installed from a different source, or an untracked directory").addOption(new Option("--copy", "alias of --mode copy").hideHelp()).option("-y, --yes", "skip prompts; scope is auto-detected (project if this folder looks like a project, else global)").addOption(new Option("--allow-unverified").hideHelp()).option("--skip-lint", "install even if the skill fails lint").option("--deny <flags...>", "refuse skills carrying any of these security flags (opt-in)").action(async (source, opts, cmd) => {
|
|
1013
1221
|
const merged = addFlags(opts, cmd.parent?.opts());
|
|
1014
|
-
const { run, printed } = await runAddWithUI(source, merged);
|
|
1222
|
+
const { run: run2, printed } = await runAddWithUI(source, merged);
|
|
1015
1223
|
if (!printed) {
|
|
1016
|
-
if (
|
|
1017
|
-
else console.log(
|
|
1224
|
+
if (run2.exitCode === 1 && !merged.json) console.error(run2.output);
|
|
1225
|
+
else console.log(run2.output);
|
|
1018
1226
|
}
|
|
1019
|
-
process.exitCode =
|
|
1227
|
+
process.exitCode = run2.exitCode;
|
|
1020
1228
|
});
|
|
1021
1229
|
}
|
|
1022
1230
|
var clackUI = {
|
|
@@ -1033,7 +1241,7 @@ async function runAddWithUI(source, flags, opts = {}) {
|
|
|
1033
1241
|
const ui = opts.ui ?? clackUI;
|
|
1034
1242
|
ui.intro(banner());
|
|
1035
1243
|
try {
|
|
1036
|
-
const
|
|
1244
|
+
const run2 = await runAdd(source, flags, {
|
|
1037
1245
|
// NOTE: spreading `base` reads makeDefaultDeps()'s promptScope/promptAgents
|
|
1038
1246
|
// getters right here, freezing their values for this run. That is exactly
|
|
1039
1247
|
// what we want (interactivity is decided once, up front), but it does mean
|
|
@@ -1053,13 +1261,13 @@ async function runAddWithUI(source, flags, opts = {}) {
|
|
|
1053
1261
|
}
|
|
1054
1262
|
}
|
|
1055
1263
|
});
|
|
1056
|
-
if (
|
|
1264
|
+
if (run2.cancelled) {
|
|
1057
1265
|
ui.cancel("Installation cancelled \u2014 nothing was installed.");
|
|
1058
|
-
return { run, printed: true };
|
|
1266
|
+
return { run: run2, printed: true };
|
|
1059
1267
|
}
|
|
1060
|
-
ui.message(
|
|
1061
|
-
ui.outro(
|
|
1062
|
-
return { run, printed: true };
|
|
1268
|
+
ui.message(run2.output);
|
|
1269
|
+
ui.outro(run2.exitCode === 0 ? `Done. Run ${pc8.cyan("skillmd list")} to see everything installed.` : pc8.red("Some skills were not installed."));
|
|
1270
|
+
return { run: run2, printed: true };
|
|
1063
1271
|
} catch (e) {
|
|
1064
1272
|
ui.outro(pc8.red("Nothing was installed."));
|
|
1065
1273
|
throw e;
|
|
@@ -1068,8 +1276,8 @@ async function runAddWithUI(source, flags, opts = {}) {
|
|
|
1068
1276
|
|
|
1069
1277
|
// src/commands/list.ts
|
|
1070
1278
|
import { Command as Command8 } from "commander";
|
|
1071
|
-
import { existsSync as
|
|
1072
|
-
import { join as
|
|
1279
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
1280
|
+
import { join as join4 } from "path";
|
|
1073
1281
|
import pc9 from "picocolors";
|
|
1074
1282
|
import { parseSkillMd as parseSkillMd3 } from "@skillmds/core";
|
|
1075
1283
|
function runList(flags = {}) {
|
|
@@ -1077,9 +1285,9 @@ function runList(flags = {}) {
|
|
|
1077
1285
|
let items = [];
|
|
1078
1286
|
for (const global of scopes) {
|
|
1079
1287
|
for (const s of listInstalled({ global, cwd: flags.cwd, home: flags.home })) {
|
|
1080
|
-
const md =
|
|
1288
|
+
const md = join4(s.path, "SKILL.md");
|
|
1081
1289
|
let description = "";
|
|
1082
|
-
if (
|
|
1290
|
+
if (existsSync3(md)) {
|
|
1083
1291
|
const parsed = parseSkillMd3(readFileSync(md, "utf8"));
|
|
1084
1292
|
description = "error" in parsed ? `(invalid SKILL.md \u2014 ${parsed.error})` : parsed.description;
|
|
1085
1293
|
}
|
|
@@ -1110,12 +1318,12 @@ function listCommand() {
|
|
|
1110
1318
|
// src/commands/remove.ts
|
|
1111
1319
|
import { Command as Command9 } from "commander";
|
|
1112
1320
|
import { rmSync } from "fs";
|
|
1113
|
-
import { join as
|
|
1321
|
+
import { join as join5, resolve as resolve4 } from "path";
|
|
1114
1322
|
import * as p4 from "@clack/prompts";
|
|
1115
1323
|
import pc10 from "picocolors";
|
|
1116
1324
|
var defaultDeps = (flags) => isInteractive(flags) ? {
|
|
1117
|
-
confirm: async (
|
|
1118
|
-
const a = await p4.confirm({ message:
|
|
1325
|
+
confirm: async (msg2) => {
|
|
1326
|
+
const a = await p4.confirm({ message: msg2, initialValue: false });
|
|
1119
1327
|
return !p4.isCancel(a) && a;
|
|
1120
1328
|
},
|
|
1121
1329
|
pick: async ({ names }) => {
|
|
@@ -1198,7 +1406,7 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
|
|
|
1198
1406
|
acted = true;
|
|
1199
1407
|
continue;
|
|
1200
1408
|
}
|
|
1201
|
-
const pth =
|
|
1409
|
+
const pth = join5(dir, name);
|
|
1202
1410
|
if (isSkillDirOrLink(pth)) {
|
|
1203
1411
|
rmSync(pth, { recursive: true, force: true });
|
|
1204
1412
|
removed.push(pth);
|
|
@@ -1232,9 +1440,9 @@ async function runRemove(names, flags, deps = defaultDeps(flags)) {
|
|
|
1232
1440
|
}
|
|
1233
1441
|
function removeCommand() {
|
|
1234
1442
|
return new Command9("remove").alias("rm").description("Remove installed skills (links, canonical copy and lock entry)").argument("[names...]", "skill names (omit to pick interactively)").option("-g, --global", "only user-level skills").option("-p, --project", "only this project's skills").option("-a, --agent <agents...>", "remove only from these agents").option("--all", "remove every skill in the scope").option("-y, --yes", "skip the confirmation").option("--json", "machine-readable output").action(async (names, opts, cmd) => {
|
|
1235
|
-
const
|
|
1236
|
-
console.log(
|
|
1237
|
-
process.exitCode =
|
|
1443
|
+
const run2 = await runRemove(names, { ...cmd.parent?.opts(), ...opts });
|
|
1444
|
+
console.log(run2.output);
|
|
1445
|
+
process.exitCode = run2.exitCode;
|
|
1238
1446
|
});
|
|
1239
1447
|
}
|
|
1240
1448
|
|
|
@@ -1368,16 +1576,16 @@ function scopeOptions(cmd) {
|
|
|
1368
1576
|
}
|
|
1369
1577
|
function updateCommand() {
|
|
1370
1578
|
return scopeOptions(new Command10("update").description("Update installed skills from the source recorded in the lock file (re-lints before writing)").argument("[names...]", "specific skills (default: all tracked)").option("--check", "only report which skills have updates")).action(async (names, opts, cmd) => {
|
|
1371
|
-
const
|
|
1372
|
-
console.log(
|
|
1373
|
-
process.exitCode =
|
|
1579
|
+
const run2 = await runUpdate(names, { ...cmd.parent?.opts(), ...opts });
|
|
1580
|
+
console.log(run2.output);
|
|
1581
|
+
process.exitCode = run2.exitCode;
|
|
1374
1582
|
});
|
|
1375
1583
|
}
|
|
1376
1584
|
function checkCommand() {
|
|
1377
1585
|
return scopeOptions(new Command10("check").description("Report installed skills that have updates (alias of `update --check`)").argument("[names...]", "specific skills (default: all tracked)")).action(async (names, opts, cmd) => {
|
|
1378
|
-
const
|
|
1379
|
-
console.log(
|
|
1380
|
-
process.exitCode =
|
|
1586
|
+
const run2 = await runUpdate(names, { ...cmd.parent?.opts(), ...opts, check: true });
|
|
1587
|
+
console.log(run2.output);
|
|
1588
|
+
process.exitCode = run2.exitCode;
|
|
1381
1589
|
});
|
|
1382
1590
|
}
|
|
1383
1591
|
|
|
@@ -1425,9 +1633,9 @@ ${pc12.green(`\u2713 Published ${res.slug ?? src.slug} (status: ${res.status ??
|
|
|
1425
1633
|
function publishCommand() {
|
|
1426
1634
|
return new Command11("publish").alias("submit").description("Publish a skill to the registry (refuses to publish on lint errors)").argument("[target]", "local path to the skill", ".").option("--type <type>", "single | pack", "single").option("--force", "publish despite warnings (never bypasses errors)").option("--dry-run", "lint only; do not publish").action(async (target, opts, cmd) => {
|
|
1427
1635
|
const merged = { ...cmd.parent?.opts(), ...opts };
|
|
1428
|
-
const
|
|
1429
|
-
console.log(
|
|
1430
|
-
process.exitCode =
|
|
1636
|
+
const run2 = await runPublish(target, merged);
|
|
1637
|
+
console.log(run2.output);
|
|
1638
|
+
process.exitCode = run2.exitCode;
|
|
1431
1639
|
});
|
|
1432
1640
|
}
|
|
1433
1641
|
|
|
@@ -1463,8 +1671,8 @@ async function runLogin(flags, deps = defaultLoginDeps(flags)) {
|
|
|
1463
1671
|
try {
|
|
1464
1672
|
host = hostOf(resolveApi({ api: flags.api, insecureHttp: flags.insecureHttp }, { env, config: { ...cfg, api: flags.api ?? cfg.api } }));
|
|
1465
1673
|
} catch (e) {
|
|
1466
|
-
const
|
|
1467
|
-
return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error:
|
|
1674
|
+
const msg2 = e instanceof Error ? e.message : String(e);
|
|
1675
|
+
return { exitCode: 1, output: flags.json ? JSON.stringify({ ok: false, error: msg2 }, null, 2) : pc13.red(msg2) };
|
|
1468
1676
|
}
|
|
1469
1677
|
cfg.token = token;
|
|
1470
1678
|
if (flags.api) cfg.api = flags.api;
|
|
@@ -1566,9 +1774,9 @@ async function browseOneResult(item) {
|
|
|
1566
1774
|
}
|
|
1567
1775
|
p6.log.step(`Installing ${slugTxt}\u2026`);
|
|
1568
1776
|
try {
|
|
1569
|
-
const
|
|
1570
|
-
console.log(
|
|
1571
|
-
if (
|
|
1777
|
+
const run2 = await runAdd(item.slug, action === "global" ? { global: true } : { project: true });
|
|
1778
|
+
console.log(run2.output);
|
|
1779
|
+
if (run2.exitCode === 0) p6.log.success("Installed.");
|
|
1572
1780
|
else p6.log.warn("Not installed.");
|
|
1573
1781
|
} catch (err) {
|
|
1574
1782
|
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
@@ -1603,10 +1811,10 @@ async function runInteractive() {
|
|
|
1603
1811
|
const s = p6.spinner();
|
|
1604
1812
|
s.start(action === "lint" ? "Linting" : "Scanning");
|
|
1605
1813
|
try {
|
|
1606
|
-
const
|
|
1814
|
+
const run2 = action === "lint" ? await runLint(target, { format: "text" }) : await runScan(target, { format: "text" });
|
|
1607
1815
|
s.stop("Done");
|
|
1608
|
-
console.log(
|
|
1609
|
-
process.exitCode =
|
|
1816
|
+
console.log(run2.output);
|
|
1817
|
+
process.exitCode = run2.exitCode;
|
|
1610
1818
|
} catch (err) {
|
|
1611
1819
|
s.stop(pc14.red(err instanceof Error ? err.message : String(err)));
|
|
1612
1820
|
process.exitCode = 1;
|
|
@@ -1621,13 +1829,13 @@ async function runInteractive() {
|
|
|
1621
1829
|
const s = p6.spinner();
|
|
1622
1830
|
s.start("Searching");
|
|
1623
1831
|
try {
|
|
1624
|
-
const
|
|
1625
|
-
s.stop(`${
|
|
1626
|
-
if (
|
|
1832
|
+
const run2 = await runSearch(String(query), { verified: Boolean(verified), limit: "20" });
|
|
1833
|
+
s.stop(`${run2.items.length} result${run2.items.length === 1 ? "" : "s"}`);
|
|
1834
|
+
if (run2.items.length === 0) {
|
|
1627
1835
|
p6.log.info("No results.");
|
|
1628
1836
|
break;
|
|
1629
1837
|
}
|
|
1630
|
-
await browseSearchResults(
|
|
1838
|
+
await browseSearchResults(run2.items);
|
|
1631
1839
|
} catch (err) {
|
|
1632
1840
|
s.stop(pc14.red(err instanceof Error ? err.message : String(err)));
|
|
1633
1841
|
process.exitCode = 1;
|
|
@@ -1652,9 +1860,9 @@ async function runInteractive() {
|
|
|
1652
1860
|
const src = await p6.text({ message: "Skill source", placeholder: "owner/name, https://github.com/\u2026, or ./path" });
|
|
1653
1861
|
if (bail(src)) break;
|
|
1654
1862
|
try {
|
|
1655
|
-
const
|
|
1656
|
-
console.log(
|
|
1657
|
-
process.exitCode =
|
|
1863
|
+
const run2 = await runAdd(String(src), {});
|
|
1864
|
+
console.log(run2.output);
|
|
1865
|
+
process.exitCode = run2.exitCode;
|
|
1658
1866
|
} catch (err) {
|
|
1659
1867
|
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
1660
1868
|
process.exitCode = 1;
|
|
@@ -1667,9 +1875,9 @@ async function runInteractive() {
|
|
|
1667
1875
|
}
|
|
1668
1876
|
case "update": {
|
|
1669
1877
|
try {
|
|
1670
|
-
const
|
|
1671
|
-
console.log(
|
|
1672
|
-
process.exitCode =
|
|
1878
|
+
const run2 = await runUpdate([], {});
|
|
1879
|
+
console.log(run2.output);
|
|
1880
|
+
process.exitCode = run2.exitCode;
|
|
1673
1881
|
} catch (err) {
|
|
1674
1882
|
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
1675
1883
|
process.exitCode = 1;
|
|
@@ -1678,9 +1886,9 @@ async function runInteractive() {
|
|
|
1678
1886
|
}
|
|
1679
1887
|
case "remove": {
|
|
1680
1888
|
try {
|
|
1681
|
-
const
|
|
1682
|
-
console.log(
|
|
1683
|
-
process.exitCode =
|
|
1889
|
+
const run2 = await runRemove([], {});
|
|
1890
|
+
console.log(run2.output);
|
|
1891
|
+
process.exitCode = run2.exitCode;
|
|
1684
1892
|
} catch (err) {
|
|
1685
1893
|
p6.log.error(err instanceof Error ? err.message : String(err));
|
|
1686
1894
|
process.exitCode = 1;
|
package/dist/mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillmds",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "SkillMD for agents: the `skillmds` MCP server + the `skillmd` CLI (lint, scan, search, install, publish Agent Skills) from skillmd.com.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "SkillMD",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"@modelcontextprotocol/sdk": "^1.13.0",
|
|
60
60
|
"@skillmds/core": "^1.0.0",
|
|
61
61
|
"commander": "^12.1.0",
|
|
62
|
+
"fflate": "^0.8.3",
|
|
62
63
|
"giget": "^3.3.0",
|
|
63
64
|
"picocolors": "^1.1.0"
|
|
64
65
|
},
|