skillmds 1.3.1 → 1.3.3
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.js +132 -9
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
|
|
35
35
|
// src/index.ts
|
|
36
36
|
import { Command as Command13 } from "commander";
|
|
37
|
-
import { readFileSync as
|
|
37
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
38
38
|
import pc15 from "picocolors";
|
|
39
39
|
|
|
40
40
|
// src/commands/lint.ts
|
|
@@ -623,7 +623,7 @@ function infoCommand() {
|
|
|
623
623
|
import { Command as Command7, Option } from "commander";
|
|
624
624
|
import { homedir as homedir4 } from "os";
|
|
625
625
|
import * as p3 from "@clack/prompts";
|
|
626
|
-
import { dirname, resolve as resolve3 } from "path";
|
|
626
|
+
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
627
627
|
import pc8 from "picocolors";
|
|
628
628
|
import { lint as lint2, parseSkillMd as parseSkillMd2 } from "@skillmds/core";
|
|
629
629
|
|
|
@@ -633,6 +633,8 @@ import { existsSync as existsSync2 } from "fs";
|
|
|
633
633
|
import { homedir as homedir3 } from "os";
|
|
634
634
|
import { delimiter, join as join2 } from "path";
|
|
635
635
|
import { promisify } from "util";
|
|
636
|
+
import { mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync3 } from "fs";
|
|
637
|
+
import { dirname } from "path";
|
|
636
638
|
var run = promisify(execFile);
|
|
637
639
|
function resolveClaudeBin(env = process.env) {
|
|
638
640
|
const override = env.SKILLMD_CLAUDE_BIN;
|
|
@@ -653,6 +655,7 @@ function resolveClaudeBin(env = process.env) {
|
|
|
653
655
|
var needsShell = (bin) => /\.(cmd|bat)$/i.test(bin);
|
|
654
656
|
var MARKETPLACE_URL = "https://skillmd.com/.claude-plugin/marketplace.json";
|
|
655
657
|
var MARKETPLACE_NAME = "skillmd";
|
|
658
|
+
var MARKETPLACE_SCHEMA = "https://anthropic.com/claude-code/marketplace.schema.json";
|
|
656
659
|
var TIMEOUT_MS = 12e4;
|
|
657
660
|
function exec(deps) {
|
|
658
661
|
return deps.exec ?? ((file, args) => {
|
|
@@ -708,6 +711,123 @@ function msg(e) {
|
|
|
708
711
|
}
|
|
709
712
|
return e instanceof Error ? e.message.split(/\r?\n/)[0] : String(e);
|
|
710
713
|
}
|
|
714
|
+
var SITE_URL = "https://skillmd.com";
|
|
715
|
+
function readJson(path) {
|
|
716
|
+
let text3;
|
|
717
|
+
try {
|
|
718
|
+
text3 = readFileSync(path, "utf8");
|
|
719
|
+
} catch {
|
|
720
|
+
return "absent";
|
|
721
|
+
}
|
|
722
|
+
try {
|
|
723
|
+
const v = JSON.parse(text3);
|
|
724
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : "unparsable";
|
|
725
|
+
} catch {
|
|
726
|
+
return "unparsable";
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function writeJson(path, value) {
|
|
730
|
+
mkdirSync2(dirname(path), { recursive: true });
|
|
731
|
+
writeFileSync3(path, `${JSON.stringify(value, null, 2)}
|
|
732
|
+
`);
|
|
733
|
+
}
|
|
734
|
+
var obj = (v) => v && typeof v === "object" && !Array.isArray(v) ? { ...v } : {};
|
|
735
|
+
async function installPluginNatively(owner, slug, deps = {}) {
|
|
736
|
+
const home = deps.home ?? (process.env.USERPROFILE || process.env.HOME || homedir3());
|
|
737
|
+
const claudeDir = join2(home, ".claude");
|
|
738
|
+
if (!existsSync2(claudeDir)) return { ok: false, reason: "no Claude Code directory on this machine" };
|
|
739
|
+
const name = marketplacePluginName(owner, slug);
|
|
740
|
+
const qualified = `${name}@${MARKETPLACE_NAME}`;
|
|
741
|
+
const doFetch = deps.fetch ?? fetch;
|
|
742
|
+
let entries;
|
|
743
|
+
try {
|
|
744
|
+
const res = await doFetch(`${SITE_URL}/plugins/${owner}/${slug}/plugin.zip`, { headers: { accept: "application/zip" } });
|
|
745
|
+
if (!res.ok) return { ok: false, reason: `the plugin archive answered ${res.status}` };
|
|
746
|
+
const { unzipSync } = await import("fflate");
|
|
747
|
+
entries = unzipSync(new Uint8Array(await res.arrayBuffer()));
|
|
748
|
+
} catch (e) {
|
|
749
|
+
return { ok: false, reason: msg(e) };
|
|
750
|
+
}
|
|
751
|
+
let version2 = "1.0.0";
|
|
752
|
+
const manifest = entries[".claude-plugin/plugin.json"];
|
|
753
|
+
if (manifest) {
|
|
754
|
+
try {
|
|
755
|
+
const v = JSON.parse(new TextDecoder().decode(manifest));
|
|
756
|
+
if (typeof v.version === "string" && v.version) version2 = v.version;
|
|
757
|
+
} catch {
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (!manifest) return { ok: false, reason: "the plugin archive has no .claude-plugin/plugin.json" };
|
|
761
|
+
const pluginsDir = join2(claudeDir, "plugins");
|
|
762
|
+
const installPath = join2(pluginsDir, "cache", MARKETPLACE_NAME, name, version2);
|
|
763
|
+
const knownPath = join2(pluginsDir, "known_marketplaces.json");
|
|
764
|
+
const installedPath = join2(pluginsDir, "installed_plugins.json");
|
|
765
|
+
const settingsPath = join2(claudeDir, "settings.json");
|
|
766
|
+
const known = readJson(knownPath);
|
|
767
|
+
if (known === "unparsable") return { ok: false, reason: "known_marketplaces.json is not readable JSON \u2014 left untouched" };
|
|
768
|
+
const installed = readJson(installedPath);
|
|
769
|
+
if (installed === "unparsable") return { ok: false, reason: "installed_plugins.json is not readable JSON \u2014 left untouched" };
|
|
770
|
+
const settings = readJson(settingsPath);
|
|
771
|
+
if (settings === "unparsable") return { ok: false, reason: "settings.json is not readable JSON \u2014 left untouched" };
|
|
772
|
+
try {
|
|
773
|
+
for (const [path, data] of Object.entries(entries)) {
|
|
774
|
+
if (path.endsWith("/")) continue;
|
|
775
|
+
const dest = join2(installPath, ...path.split("/"));
|
|
776
|
+
mkdirSync2(dirname(dest), { recursive: true });
|
|
777
|
+
writeFileSync3(dest, data);
|
|
778
|
+
}
|
|
779
|
+
writeFileSync3(join2(installPath, ".in_use"), "");
|
|
780
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
781
|
+
const source = { source: "url", url: MARKETPLACE_URL };
|
|
782
|
+
const marketplaceDir = join2(pluginsDir, "marketplaces", MARKETPLACE_NAME);
|
|
783
|
+
const cachedPath = join2(marketplaceDir, ".claude-plugin", "marketplace.json");
|
|
784
|
+
const cached = readJson(cachedPath);
|
|
785
|
+
if (cached === "unparsable") return { ok: false, reason: "the cached marketplace manifest is not readable JSON \u2014 left untouched" };
|
|
786
|
+
const cachedDoc = cached === "absent" ? {
|
|
787
|
+
$schema: MARKETPLACE_SCHEMA,
|
|
788
|
+
name: MARKETPLACE_NAME,
|
|
789
|
+
owner: { name: "SkillMD", email: "hi@skillmd.com" },
|
|
790
|
+
description: "Agent Skill plugins from the SkillMD registry.",
|
|
791
|
+
plugins: []
|
|
792
|
+
} : cached;
|
|
793
|
+
const list = Array.isArray(cachedDoc.plugins) ? cachedDoc.plugins : [];
|
|
794
|
+
cachedDoc.plugins = [
|
|
795
|
+
...list.filter((e) => e?.name !== name),
|
|
796
|
+
{
|
|
797
|
+
name,
|
|
798
|
+
version: version2,
|
|
799
|
+
homepage: `${SITE_URL}/plugins/${owner}/${slug}`,
|
|
800
|
+
author: { name: owner === "skillmd" ? "SkillMD" : `@${owner}` },
|
|
801
|
+
source: { source: "archive", url: `${SITE_URL}/plugins/${owner}/${slug}/plugin.zip` }
|
|
802
|
+
}
|
|
803
|
+
];
|
|
804
|
+
writeJson(cachedPath, cachedDoc);
|
|
805
|
+
const knownDoc = known === "absent" ? {} : known;
|
|
806
|
+
if (!knownDoc[MARKETPLACE_NAME]) {
|
|
807
|
+
knownDoc[MARKETPLACE_NAME] = { source, installLocation: marketplaceDir, lastUpdated: now };
|
|
808
|
+
writeJson(knownPath, knownDoc);
|
|
809
|
+
}
|
|
810
|
+
const installedDoc = installed === "absent" ? { version: 2, plugins: {} } : installed;
|
|
811
|
+
const plugins = obj(installedDoc.plugins);
|
|
812
|
+
const prior = Array.isArray(plugins[qualified]) ? plugins[qualified] : [];
|
|
813
|
+
plugins[qualified] = [
|
|
814
|
+
...prior.filter((p7) => p7?.scope !== "user"),
|
|
815
|
+
{ scope: "user", installPath, version: version2, installedAt: now, lastUpdated: now }
|
|
816
|
+
];
|
|
817
|
+
installedDoc.plugins = plugins;
|
|
818
|
+
if (typeof installedDoc.version !== "number") installedDoc.version = 2;
|
|
819
|
+
writeJson(installedPath, installedDoc);
|
|
820
|
+
const settingsDoc = settings === "absent" ? {} : settings;
|
|
821
|
+
const declared = obj(settingsDoc.extraKnownMarketplaces);
|
|
822
|
+
if (!declared[MARKETPLACE_NAME]) declared[MARKETPLACE_NAME] = { source };
|
|
823
|
+
settingsDoc.extraKnownMarketplaces = declared;
|
|
824
|
+
settingsDoc.enabledPlugins = { ...obj(settingsDoc.enabledPlugins), [qualified]: true };
|
|
825
|
+
writeJson(settingsPath, settingsDoc);
|
|
826
|
+
} catch (e) {
|
|
827
|
+
return { ok: false, reason: msg(e) };
|
|
828
|
+
}
|
|
829
|
+
return { ok: true, name: qualified, marketplace: MARKETPLACE_NAME };
|
|
830
|
+
}
|
|
711
831
|
|
|
712
832
|
// src/commands/add.ts
|
|
713
833
|
var infoNotice = (text3) => ({ text: text3, style: "info" });
|
|
@@ -914,7 +1034,7 @@ function makeDefaultDeps(flags) {
|
|
|
914
1034
|
raw: r.raw,
|
|
915
1035
|
slug: r.slug,
|
|
916
1036
|
source,
|
|
917
|
-
...spec.kind === "local" && r.file ? { files: collectFiles(
|
|
1037
|
+
...spec.kind === "local" && r.file ? { files: collectFiles(dirname2(r.file), dirname2(r.file)) } : {}
|
|
918
1038
|
}));
|
|
919
1039
|
}
|
|
920
1040
|
let registryDown = null;
|
|
@@ -1171,9 +1291,12 @@ async function tryPluginInstall(arg, flags) {
|
|
|
1171
1291
|
}
|
|
1172
1292
|
if (spec.kind !== "plugin") return null;
|
|
1173
1293
|
if (flags.agent?.length || flags.skill?.length || flags.list || flags.mode === "copy") return null;
|
|
1174
|
-
|
|
1175
|
-
const res = await
|
|
1176
|
-
if (!res.ok)
|
|
1294
|
+
const viaCli = await claudeCliAvailable() ? await installClaudePlugin(spec.owner, spec.slug) : null;
|
|
1295
|
+
const res = viaCli?.ok ? viaCli : await installPluginNatively(spec.owner, spec.slug, { home: flags.home });
|
|
1296
|
+
if (!res.ok) {
|
|
1297
|
+
if (!viaCli && /no Claude Code directory/.test(res.reason)) return null;
|
|
1298
|
+
return { ok: false, reason: viaCli && !viaCli.ok ? viaCli.reason : res.reason };
|
|
1299
|
+
}
|
|
1177
1300
|
return {
|
|
1178
1301
|
ok: true,
|
|
1179
1302
|
agent: "claude-code",
|
|
@@ -1297,7 +1420,7 @@ async function runAddWithUI(source, flags, opts = {}) {
|
|
|
1297
1420
|
|
|
1298
1421
|
// src/commands/list.ts
|
|
1299
1422
|
import { Command as Command8 } from "commander";
|
|
1300
|
-
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
1423
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
1301
1424
|
import { join as join4 } from "path";
|
|
1302
1425
|
import pc9 from "picocolors";
|
|
1303
1426
|
import { parseSkillMd as parseSkillMd3 } from "@skillmds/core";
|
|
@@ -1309,7 +1432,7 @@ function runList(flags = {}) {
|
|
|
1309
1432
|
const md = join4(s.path, "SKILL.md");
|
|
1310
1433
|
let description = "";
|
|
1311
1434
|
if (existsSync3(md)) {
|
|
1312
|
-
const parsed = parseSkillMd3(
|
|
1435
|
+
const parsed = parseSkillMd3(readFileSync2(md, "utf8"));
|
|
1313
1436
|
description = "error" in parsed ? `(invalid SKILL.md \u2014 ${parsed.error})` : parsed.description;
|
|
1314
1437
|
}
|
|
1315
1438
|
items.push({ ...s, description: safeText(description, 80) });
|
|
@@ -1941,7 +2064,7 @@ async function runInteractive() {
|
|
|
1941
2064
|
// src/index.ts
|
|
1942
2065
|
var version = "0.0.0";
|
|
1943
2066
|
try {
|
|
1944
|
-
version = JSON.parse(
|
|
2067
|
+
version = JSON.parse(readFileSync3(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
1945
2068
|
} catch {
|
|
1946
2069
|
}
|
|
1947
2070
|
var program = new Command13("skillmd").version(version).description("Lint, validate, and install Agent Skills from the SkillMD registry.").option("--json", "machine-readable output").option("--token <token>", "personal access token (prefer `skillmd login` or SKILLMD_TOKEN \u2014 command-line tokens show up in process lists)").option("--api <url>", "override the registry API base URL").option("--insecure-http", "allow an http:// --api base (local development only)");
|
package/package.json
CHANGED