skillmds 1.3.5 → 1.3.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  All notable changes to `skillmds` are documented here. Versions follow [semver](https://semver.org).
4
4
 
5
+ ## 1.3.6
6
+
7
+ - **Installed skills name where they came from.** A skill installed from the registry (with `add`, `update` or the MCP install tool) gets one line in its frontmatter, `metadata.source: https://skillmd.com/skills/<owner>/<name>`, so an agent reading it knows its SkillMD page. Every other byte is the author's: an existing `metadata` block gains that one line, and an author's own `metadata.source` is never replaced. The line is added after the integrity checks, and the lock file's digest still covers the bytes the registry served, so `update` is unaffected. GitHub, gist and local installs are written exactly as fetched.
8
+ - **find-skills, offered once.** After your first successful install in a terminal, `add` asks once whether to also add `skillmd/find-skills`, a skill that tells your agent to look on SkillMD when a task needs know-how it doesn't have, and to vet what it finds before suggesting it. Yes or no, it never asks again; an agent running the CLI, `-y` and `--json` are never asked.
9
+
5
10
  ## 1.3.5
6
11
 
7
12
  - **The agent picker now says why Claude Code isn't in it.** `add plugin:owner/slug` installs the plugin into Claude Code as a real plugin first, then asks which other agents should get its skills, leaving Claude Code out so it doesn't get the same skills twice. The question read "Which agents do you want to install to?", and the confirmation only printed after the run, so the list looked as if Claude Code hadn't been detected. It now asks: `Claude Code already has <plugin> as a plugin. Which other agents should get its skills too?`
@@ -622,6 +622,48 @@ import { createHash as createHash2 } from "crypto";
622
622
  import { cpSync, existsSync as existsSync5, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync2, renameSync as renameSync2, rmSync as rmSync3, symlinkSync, writeFileSync as writeFileSync3 } from "fs";
623
623
  import { platform } from "os";
624
624
  import { dirname as dirname3, join as join7, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
625
+
626
+ // src/provenance.ts
627
+ import { parseSkillMd } from "@skillmds/core";
628
+ var SKILLMD_ORIGIN = "https://skillmd.com";
629
+ var FRONTMATTER = /^(?---\r?\n)([\s\S]*?)(\r?\n---[ \t]*(?:\r?\n|$))/;
630
+ function withRegistrySource(md, slug) {
631
+ const m = FRONTMATTER.exec(md);
632
+ if (!m) return md;
633
+ const [whole, open, block, close] = m;
634
+ const nl = open.endsWith("\r\n") ? "\r\n" : "\n";
635
+ const source = `${SKILLMD_ORIGIN}/skills/${slug}`;
636
+ const lines = block.split(/\r?\n/);
637
+ const at = lines.findIndex((l) => /^metadata[ \t]*:/.test(l));
638
+ if (at === -1) {
639
+ lines.push("metadata:", ` source: ${source}`);
640
+ } else {
641
+ if (!/^metadata[ \t]*:[ \t]*(#.*)?$/.test(lines[at])) return md;
642
+ let end = at + 1;
643
+ while (end < lines.length && (/^[ \t]/.test(lines[end]) || lines[end].trim() === "")) end++;
644
+ const children = lines.slice(at + 1, end);
645
+ if (children.some((l) => /^[ \t]+source[ \t]*:/.test(l))) return md;
646
+ const indent = /^([ \t]+)\S/.exec(children.find((l) => l.trim() !== "") ?? "")?.[1] ?? " ";
647
+ lines.splice(at + 1, 0, `${indent}source: ${source}`);
648
+ }
649
+ const out = open + lines.join(nl) + close + md.slice(whole.length);
650
+ const before = parseSkillMd(md);
651
+ const after = parseSkillMd(out);
652
+ return "error" in after && !("error" in before) ? md : out;
653
+ }
654
+ function filesForInstall(files, source) {
655
+ const slug = /^registry:([^/\s]+\/[^/\s]+)$/.exec(source)?.[1];
656
+ if (!slug) return files;
657
+ return files.map((f) => {
658
+ if (f.path.replace(/\\/g, "/") !== "SKILL.md") return f;
659
+ const text = typeof f.contents === "string" ? f.contents : f.contents.toString("utf8");
660
+ const next = withRegistrySource(text, slug);
661
+ if (next === text) return f;
662
+ return { ...f, contents: typeof f.contents === "string" ? next : Buffer.from(next, "utf8") };
663
+ });
664
+ }
665
+
666
+ // src/installer.ts
625
667
  var NAME_OK = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
626
668
  function assertSafeName(name) {
627
669
  if (!NAME_OK.test(name) || name.includes("..")) throw new Error(`refused unsafe skill name: "${name}"`);
@@ -712,7 +754,7 @@ async function installSkill(req) {
712
754
  const staging = `${canonical}.tmp-${process.pid}`;
713
755
  rmSync3(staging, { recursive: true, force: true });
714
756
  try {
715
- writeTree(staging, req.files);
757
+ writeTree(staging, filesForInstall(req.files, req.source));
716
758
  } catch (e) {
717
759
  rmSync3(staging, { recursive: true, force: true });
718
760
  throw e;
package/dist/cli.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  uninstallSkill,
32
32
  upsertEntry,
33
33
  writeConfig
34
- } from "./chunk-6JVWN3IG.js";
34
+ } from "./chunk-HGVEADWS.js";
35
35
 
36
36
  // src/index.ts
37
37
  import { Command as Command13 } from "commander";
@@ -1137,7 +1137,9 @@ function finish2(flags, outcome) {
1137
1137
  written,
1138
1138
  blocked,
1139
1139
  exitCode,
1140
- output: json ? JSON.stringify(doc, null, 2) : renderHuman(outcome.notices, outcome.records, outcome.paths)
1140
+ output: json ? JSON.stringify(doc, null, 2) : renderHuman(outcome.notices, outcome.records, outcome.paths),
1141
+ scope: outcome.scope,
1142
+ agents: [...new Set(installed.flatMap((d) => d.targets.map((t) => t.agent)))]
1141
1143
  };
1142
1144
  }
1143
1145
  case "list": {
@@ -1387,8 +1389,40 @@ var clackUI = {
1387
1389
  spinner: () => p3.spinner(),
1388
1390
  message: (m) => p3.log.message(m),
1389
1391
  outro: (m) => p3.outro(m),
1390
- cancel: (m) => p3.cancel(m)
1392
+ cancel: (m) => p3.cancel(m),
1393
+ confirm: async (m) => {
1394
+ const answer = await p3.confirm({ message: m, initialValue: true });
1395
+ return p3.isCancel(answer) ? null : answer;
1396
+ }
1391
1397
  };
1398
+ var FIND_SKILLS = "skillmd/find-skills";
1399
+ async function offerFindSkills(source, flags, run2, ui, deps) {
1400
+ if (!ui.confirm || run2.exitCode !== 0 || !run2.written.length || !run2.scope || !run2.agents?.length) return;
1401
+ if (source.replace(/^registry:/, "").toLowerCase() === FIND_SKILLS) return;
1402
+ const cfg = readConfig(flags.home);
1403
+ if (cfg.findSkillsOffered) return;
1404
+ const global = run2.scope === "global";
1405
+ const lock = readLock({ global, cwd: flags.cwd, home: flags.home });
1406
+ if (Object.values(lock.skills).some((e) => e.source === `registry:${FIND_SKILLS}`)) return;
1407
+ try {
1408
+ writeConfig({ ...cfg, findSkillsOffered: true }, flags.home);
1409
+ } catch {
1410
+ }
1411
+ const yes = await ui.confirm("Also add find-skills, so your agent can look for more skills on SkillMD when a task needs one?");
1412
+ if (!yes) return;
1413
+ const r = await runAdd(FIND_SKILLS, {
1414
+ ...flags,
1415
+ yes: true,
1416
+ json: false,
1417
+ skill: void 0,
1418
+ list: void 0,
1419
+ ref: void 0,
1420
+ global,
1421
+ project: !global,
1422
+ agent: run2.agents
1423
+ }, deps);
1424
+ ui.message(r.exitCode === 0 ? `${pc8.green(GLYPH.ok)} find-skills added` : pc8.yellow(`${GLYPH.warn} find-skills could not be added. Try later: npx skillmds add ${FIND_SKILLS}`));
1425
+ }
1392
1426
  async function runAddWithUI(source, flags, opts = {}) {
1393
1427
  const term = opts.term ?? { ...realTerminal(), env: flags.env ?? process.env };
1394
1428
  const base = opts.deps ?? makeDefaultDeps(flags);
@@ -1421,6 +1455,7 @@ async function runAddWithUI(source, flags, opts = {}) {
1421
1455
  return { run: run2, printed: true };
1422
1456
  }
1423
1457
  ui.message(run2.output);
1458
+ await offerFindSkills(source, flags, run2, ui, base);
1424
1459
  ui.outro(run2.exitCode === 0 ? `Done. Run ${pc8.cyan("skillmd list")} to see everything installed.` : pc8.red("Some skills were not installed."));
1425
1460
  return { run: run2, printed: true };
1426
1461
  } catch (e) {
package/dist/mcp.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  safeText,
14
14
  stripTerminalEscapes,
15
15
  telemetryDisabled
16
- } from "./chunk-6JVWN3IG.js";
16
+ } from "./chunk-HGVEADWS.js";
17
17
 
18
18
  // src/mcp/server.ts
19
19
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillmds",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
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",