skillmds 1.3.4 → 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 +9 -0
- package/dist/{chunk-6JVWN3IG.js → chunk-HGVEADWS.js} +43 -1
- package/dist/cli.js +45 -7
- package/dist/mcp.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
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
|
+
|
|
10
|
+
## 1.3.5
|
|
11
|
+
|
|
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?`
|
|
13
|
+
|
|
5
14
|
## 1.3.4
|
|
6
15
|
|
|
7
16
|
- **A companion file that its source repo no longer serves no longer fails the install.** When a registry pack links to a file the upstream repo has since deleted or moved, `add` (and the MCP install tool) now installs everything else and warns which files are missing, e.g. `1 companion file is gone from the source repo and was not installed (scripts/gone.ts)`. Before, one such link failed the whole bundle and `add owner/name` fell back to fetching the slug as a GitHub repo, ending in a misleading 404 with nothing installed. `update` stays strict: it never replaces a full install with a partial one.
|
|
@@ -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-
|
|
34
|
+
} from "./chunk-HGVEADWS.js";
|
|
35
35
|
|
|
36
36
|
// src/index.ts
|
|
37
37
|
import { Command as Command13 } from "commander";
|
|
@@ -856,11 +856,14 @@ async function promptScopeInteractive(ctx) {
|
|
|
856
856
|
});
|
|
857
857
|
return p3.isCancel(choice) ? null : choice;
|
|
858
858
|
}
|
|
859
|
+
function agentsPromptMessage(managed) {
|
|
860
|
+
return managed ? `Claude Code already has ${managed} as a plugin. Which other agents should get its skills too?` : "Which agents do you want to install to?";
|
|
861
|
+
}
|
|
859
862
|
async function promptAgentsInteractive(ctx) {
|
|
860
863
|
if (ctx.detected.length < 2) return ctx.detected;
|
|
861
864
|
const remembered = (readConfig().lastAgents ?? []).filter((id) => ctx.detected.includes(id));
|
|
862
865
|
const picked = await p3.multiselect({
|
|
863
|
-
message:
|
|
866
|
+
message: agentsPromptMessage(ctx.managed),
|
|
864
867
|
options: ctx.detected.map((id) => ({ value: id, label: id, hint: ctx.dirFor(id) })),
|
|
865
868
|
initialValues: remembered.length ? remembered : ctx.detected,
|
|
866
869
|
required: true
|
|
@@ -1134,7 +1137,9 @@ function finish2(flags, outcome) {
|
|
|
1134
1137
|
written,
|
|
1135
1138
|
blocked,
|
|
1136
1139
|
exitCode,
|
|
1137
|
-
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)))]
|
|
1138
1143
|
};
|
|
1139
1144
|
}
|
|
1140
1145
|
case "list": {
|
|
@@ -1210,7 +1215,7 @@ function gate(flags, deps, candidates) {
|
|
|
1210
1215
|
}
|
|
1211
1216
|
return null;
|
|
1212
1217
|
}
|
|
1213
|
-
async function chooseTargets(flags, deps, scope, detected) {
|
|
1218
|
+
async function chooseTargets(flags, deps, scope, detected, managed) {
|
|
1214
1219
|
const notices = [];
|
|
1215
1220
|
let targets = flags.agent?.length ? flags.agent : detected.length ? detected : ["claude-code"];
|
|
1216
1221
|
if (scope.global) {
|
|
@@ -1222,7 +1227,7 @@ async function chooseTargets(flags, deps, scope, detected) {
|
|
|
1222
1227
|
targets = kept;
|
|
1223
1228
|
}
|
|
1224
1229
|
if (!flags.agent?.length && deps.promptAgents && !flags.yes && !flags.json && !detectHostAgent(flags.env ?? process.env)) {
|
|
1225
|
-
const picked = await deps.promptAgents({ detected: targets, dirFor: (id) => agentDir(id, scope) });
|
|
1230
|
+
const picked = await deps.promptAgents({ detected: targets, dirFor: (id) => agentDir(id, scope), ...managed ? { managed } : {} });
|
|
1226
1231
|
if (picked === null) return null;
|
|
1227
1232
|
targets = picked;
|
|
1228
1233
|
if (flags.home === void 0 && picked.length) {
|
|
@@ -1354,7 +1359,7 @@ async function runAdd(arg, flags, deps) {
|
|
|
1354
1359
|
const notices = resolved.note ? [infoNotice(resolved.note)] : [];
|
|
1355
1360
|
notices.push(...managedNotices);
|
|
1356
1361
|
const detected = detectAgents(scope).filter((id) => id !== managed?.agent);
|
|
1357
|
-
const chosen = await chooseTargets(flags, d, scope, detected);
|
|
1362
|
+
const chosen = await chooseTargets(flags, d, scope, detected, managed?.name);
|
|
1358
1363
|
if (!chosen) return finish2(flags, { kind: "cancel" });
|
|
1359
1364
|
notices.push(...chosen.notices);
|
|
1360
1365
|
const ctx = { arg, flags, deny: new Set(flags.deny ?? []), scope, targets: chosen.targets, env, deps: d };
|
|
@@ -1384,8 +1389,40 @@ var clackUI = {
|
|
|
1384
1389
|
spinner: () => p3.spinner(),
|
|
1385
1390
|
message: (m) => p3.log.message(m),
|
|
1386
1391
|
outro: (m) => p3.outro(m),
|
|
1387
|
-
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
|
+
}
|
|
1388
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
|
+
}
|
|
1389
1426
|
async function runAddWithUI(source, flags, opts = {}) {
|
|
1390
1427
|
const term = opts.term ?? { ...realTerminal(), env: flags.env ?? process.env };
|
|
1391
1428
|
const base = opts.deps ?? makeDefaultDeps(flags);
|
|
@@ -1418,6 +1455,7 @@ async function runAddWithUI(source, flags, opts = {}) {
|
|
|
1418
1455
|
return { run: run2, printed: true };
|
|
1419
1456
|
}
|
|
1420
1457
|
ui.message(run2.output);
|
|
1458
|
+
await offerFindSkills(source, flags, run2, ui, base);
|
|
1421
1459
|
ui.outro(run2.exitCode === 0 ? `Done. Run ${pc8.cyan("skillmd list")} to see everything installed.` : pc8.red("Some skills were not installed."));
|
|
1422
1460
|
return { run: run2, printed: true };
|
|
1423
1461
|
} catch (e) {
|
package/dist/mcp.js
CHANGED
package/package.json
CHANGED