skillmds 1.3.1 → 1.3.2

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.
Files changed (2) hide show
  1. package/dist/cli.js +110 -9
  2. 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 readFileSync2 } from "fs";
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;
@@ -708,6 +710,102 @@ function msg(e) {
708
710
  }
709
711
  return e instanceof Error ? e.message.split(/\r?\n/)[0] : String(e);
710
712
  }
713
+ var SITE_URL = "https://skillmd.com";
714
+ function readJson(path) {
715
+ let text3;
716
+ try {
717
+ text3 = readFileSync(path, "utf8");
718
+ } catch {
719
+ return "absent";
720
+ }
721
+ try {
722
+ const v = JSON.parse(text3);
723
+ return v && typeof v === "object" && !Array.isArray(v) ? v : "unparsable";
724
+ } catch {
725
+ return "unparsable";
726
+ }
727
+ }
728
+ function writeJson(path, value) {
729
+ mkdirSync2(dirname(path), { recursive: true });
730
+ writeFileSync3(path, `${JSON.stringify(value, null, 2)}
731
+ `);
732
+ }
733
+ var obj = (v) => v && typeof v === "object" && !Array.isArray(v) ? { ...v } : {};
734
+ async function installPluginNatively(owner, slug, deps = {}) {
735
+ const home = deps.home ?? (process.env.USERPROFILE || process.env.HOME || homedir3());
736
+ const claudeDir = join2(home, ".claude");
737
+ if (!existsSync2(claudeDir)) return { ok: false, reason: "no Claude Code directory on this machine" };
738
+ const name = marketplacePluginName(owner, slug);
739
+ const qualified = `${name}@${MARKETPLACE_NAME}`;
740
+ const doFetch = deps.fetch ?? fetch;
741
+ let entries;
742
+ try {
743
+ const res = await doFetch(`${SITE_URL}/plugins/${owner}/${slug}/plugin.zip`, { headers: { accept: "application/zip" } });
744
+ if (!res.ok) return { ok: false, reason: `the plugin archive answered ${res.status}` };
745
+ const { unzipSync } = await import("fflate");
746
+ entries = unzipSync(new Uint8Array(await res.arrayBuffer()));
747
+ } catch (e) {
748
+ return { ok: false, reason: msg(e) };
749
+ }
750
+ let version2 = "1.0.0";
751
+ const manifest = entries[".claude-plugin/plugin.json"];
752
+ if (manifest) {
753
+ try {
754
+ const v = JSON.parse(new TextDecoder().decode(manifest));
755
+ if (typeof v.version === "string" && v.version) version2 = v.version;
756
+ } catch {
757
+ }
758
+ }
759
+ if (!manifest) return { ok: false, reason: "the plugin archive has no .claude-plugin/plugin.json" };
760
+ const pluginsDir = join2(claudeDir, "plugins");
761
+ const installPath = join2(pluginsDir, "cache", MARKETPLACE_NAME, name, version2);
762
+ const knownPath = join2(pluginsDir, "known_marketplaces.json");
763
+ const installedPath = join2(pluginsDir, "installed_plugins.json");
764
+ const settingsPath = join2(claudeDir, "settings.json");
765
+ const known = readJson(knownPath);
766
+ if (known === "unparsable") return { ok: false, reason: "known_marketplaces.json is not readable JSON \u2014 left untouched" };
767
+ const installed = readJson(installedPath);
768
+ if (installed === "unparsable") return { ok: false, reason: "installed_plugins.json is not readable JSON \u2014 left untouched" };
769
+ const settings = readJson(settingsPath);
770
+ if (settings === "unparsable") return { ok: false, reason: "settings.json is not readable JSON \u2014 left untouched" };
771
+ try {
772
+ for (const [path, data] of Object.entries(entries)) {
773
+ if (path.endsWith("/")) continue;
774
+ const dest = join2(installPath, ...path.split("/"));
775
+ mkdirSync2(dirname(dest), { recursive: true });
776
+ writeFileSync3(dest, data);
777
+ }
778
+ writeFileSync3(join2(installPath, ".in_use"), "");
779
+ mkdirSync2(join2(pluginsDir, "marketplaces", MARKETPLACE_NAME), { recursive: true });
780
+ const now = (/* @__PURE__ */ new Date()).toISOString();
781
+ const source = { source: "url", url: MARKETPLACE_URL };
782
+ const knownDoc = known === "absent" ? {} : known;
783
+ knownDoc[MARKETPLACE_NAME] = {
784
+ ...obj(knownDoc[MARKETPLACE_NAME]),
785
+ source,
786
+ installLocation: join2(pluginsDir, "marketplaces", MARKETPLACE_NAME),
787
+ lastUpdated: now
788
+ };
789
+ writeJson(knownPath, knownDoc);
790
+ const installedDoc = installed === "absent" ? { version: 2, plugins: {} } : installed;
791
+ const plugins = obj(installedDoc.plugins);
792
+ const prior = Array.isArray(plugins[qualified]) ? plugins[qualified] : [];
793
+ plugins[qualified] = [
794
+ ...prior.filter((p7) => p7?.scope !== "user"),
795
+ { scope: "user", installPath, version: version2, installedAt: now, lastUpdated: now }
796
+ ];
797
+ installedDoc.plugins = plugins;
798
+ if (typeof installedDoc.version !== "number") installedDoc.version = 2;
799
+ writeJson(installedPath, installedDoc);
800
+ const settingsDoc = settings === "absent" ? {} : settings;
801
+ settingsDoc.extraKnownMarketplaces = { ...obj(settingsDoc.extraKnownMarketplaces), [MARKETPLACE_NAME]: { source } };
802
+ settingsDoc.enabledPlugins = { ...obj(settingsDoc.enabledPlugins), [qualified]: true };
803
+ writeJson(settingsPath, settingsDoc);
804
+ } catch (e) {
805
+ return { ok: false, reason: msg(e) };
806
+ }
807
+ return { ok: true, name: qualified, marketplace: MARKETPLACE_NAME };
808
+ }
711
809
 
712
810
  // src/commands/add.ts
713
811
  var infoNotice = (text3) => ({ text: text3, style: "info" });
@@ -914,7 +1012,7 @@ function makeDefaultDeps(flags) {
914
1012
  raw: r.raw,
915
1013
  slug: r.slug,
916
1014
  source,
917
- ...spec.kind === "local" && r.file ? { files: collectFiles(dirname(r.file), dirname(r.file)) } : {}
1015
+ ...spec.kind === "local" && r.file ? { files: collectFiles(dirname2(r.file), dirname2(r.file)) } : {}
918
1016
  }));
919
1017
  }
920
1018
  let registryDown = null;
@@ -1171,9 +1269,12 @@ async function tryPluginInstall(arg, flags) {
1171
1269
  }
1172
1270
  if (spec.kind !== "plugin") return null;
1173
1271
  if (flags.agent?.length || flags.skill?.length || flags.list || flags.mode === "copy") return null;
1174
- if (!await claudeCliAvailable()) return null;
1175
- const res = await installClaudePlugin(spec.owner, spec.slug);
1176
- if (!res.ok) return { ok: false, reason: res.reason };
1272
+ const viaCli = await claudeCliAvailable() ? await installClaudePlugin(spec.owner, spec.slug) : null;
1273
+ const res = viaCli?.ok ? viaCli : await installPluginNatively(spec.owner, spec.slug, { home: flags.home });
1274
+ if (!res.ok) {
1275
+ if (!viaCli && /no Claude Code directory/.test(res.reason)) return null;
1276
+ return { ok: false, reason: viaCli && !viaCli.ok ? viaCli.reason : res.reason };
1277
+ }
1177
1278
  return {
1178
1279
  ok: true,
1179
1280
  agent: "claude-code",
@@ -1297,7 +1398,7 @@ async function runAddWithUI(source, flags, opts = {}) {
1297
1398
 
1298
1399
  // src/commands/list.ts
1299
1400
  import { Command as Command8 } from "commander";
1300
- import { existsSync as existsSync3, readFileSync } from "fs";
1401
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
1301
1402
  import { join as join4 } from "path";
1302
1403
  import pc9 from "picocolors";
1303
1404
  import { parseSkillMd as parseSkillMd3 } from "@skillmds/core";
@@ -1309,7 +1410,7 @@ function runList(flags = {}) {
1309
1410
  const md = join4(s.path, "SKILL.md");
1310
1411
  let description = "";
1311
1412
  if (existsSync3(md)) {
1312
- const parsed = parseSkillMd3(readFileSync(md, "utf8"));
1413
+ const parsed = parseSkillMd3(readFileSync2(md, "utf8"));
1313
1414
  description = "error" in parsed ? `(invalid SKILL.md \u2014 ${parsed.error})` : parsed.description;
1314
1415
  }
1315
1416
  items.push({ ...s, description: safeText(description, 80) });
@@ -1941,7 +2042,7 @@ async function runInteractive() {
1941
2042
  // src/index.ts
1942
2043
  var version = "0.0.0";
1943
2044
  try {
1944
- version = JSON.parse(readFileSync2(new URL("../package.json", import.meta.url), "utf8")).version;
2045
+ version = JSON.parse(readFileSync3(new URL("../package.json", import.meta.url), "utf8")).version;
1945
2046
  } catch {
1946
2047
  }
1947
2048
  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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillmds",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
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",