skilldrop-cli 1.12.0 → 1.13.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.
Files changed (57) hide show
  1. package/bin/skilldrop.js +46 -3
  2. package/package.json +1 -1
  3. package/packs/ai-engineering/skills/agent-config-review/evals/files/1/dot.gitignore +5 -0
  4. package/packs/ai-engineering/skills/agent-config-review/examples/inputs/tidewater-ingest/dot.gitignore +3 -0
  5. package/packs/ai-engineering/skills/agent-config-review/examples/python-repo-tighten.md +3 -3
  6. package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/dot.npmrc +3 -0
  7. package/packs/experience-design/skills/locale-check/SKILL.md +1 -1
  8. package/packs/experience-design/skills/locale-check/manifest.json +2 -2
  9. package/packs/frontend/skills/bundle-size-review/evals/files/9/dot.gitignore +5 -0
  10. /package/packs/ai-engineering/skills/agent-config-review/evals/files/1/{.claude → dot.claude}/agents/db-migrator.md +0 -0
  11. /package/packs/ai-engineering/skills/agent-config-review/evals/files/1/{.claude → dot.claude}/agents/reviewer.md +0 -0
  12. /package/packs/ai-engineering/skills/agent-config-review/evals/files/1/{.claude → dot.claude}/settings.json +0 -0
  13. /package/packs/ai-engineering/skills/agent-config-review/evals/files/1/{.claude → dot.claude}/settings.local.json +0 -0
  14. /package/packs/ai-engineering/skills/agent-config-review/evals/files/1/{.mcp.json → dot.mcp.json} +0 -0
  15. /package/packs/ai-engineering/skills/agent-config-review/evals/files/2/{.cursor → dot.cursor}/mcp.json +0 -0
  16. /package/packs/ai-engineering/skills/agent-config-review/evals/files/4/quillstack/{.claude → dot.claude}/agents/release-notes.md +0 -0
  17. /package/packs/ai-engineering/skills/agent-config-review/evals/files/4/quillstack/{.claude → dot.claude}/settings.json +0 -0
  18. /package/packs/ai-engineering/skills/agent-config-review/evals/files/4/quillstack/{.mcp.json → dot.mcp.json} +0 -0
  19. /package/packs/ai-engineering/skills/agent-config-review/evals/files/5/{.github → dot.github}/workflows/codex-triage.yml.fixture +0 -0
  20. /package/packs/ai-engineering/skills/agent-config-review/examples/inputs/tidewater-ingest/{.claude → dot.claude}/settings.json +0 -0
  21. /package/packs/ai-engineering/skills/agent-config-review/examples/inputs/tidewater-ingest/{.mcp.json → dot.mcp.json} +0 -0
  22. /package/packs/ai-engineering/skills/agent-incident-response/evals/files/6/{.mcp.json → dot.mcp.json} +0 -0
  23. /package/packs/ai-engineering/skills/agent-incident-response/evals/files/7/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  24. /package/packs/ai-engineering/skills/mcp-pairing/evals/files/4/{.mcp.json → dot.mcp.json} +0 -0
  25. /package/packs/api-builder/skills/eval-harness-generator/evals/files/1/{SKILL.md → SKILL.md.fixture} +0 -0
  26. /package/packs/api-builder/skills/eval-harness-generator/evals/files/2/{SKILL.md → SKILL.md.fixture} +0 -0
  27. /package/packs/core/skills/context-graph/evals/files/9/{.env.example → dot.env.example} +0 -0
  28. /package/packs/dev-team/skills/agents-md-generator/evals/files/1/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  29. /package/packs/dev-team/skills/agents-md-generator/evals/files/5/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  30. /package/packs/dev-team/skills/codebase-tour/evals/files/1/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  31. /package/packs/dev-team/skills/codebase-tour/evals/files/2/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  32. /package/packs/dev-team/skills/codebase-tour/evals/files/3/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  33. /package/packs/dev-team/skills/codebase-tour/evals/files/4/{.env.example → dot.env.example} +0 -0
  34. /package/packs/dev-team/skills/codebase-tour/evals/files/4/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  35. /package/packs/dev-team/skills/codebase-tour/evals/files/5/{.env.example → dot.env.example} +0 -0
  36. /package/packs/dev-team/skills/codebase-tour/evals/files/5/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  37. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/1/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  38. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  39. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/2/{.github → dot.github}/workflows/release.yml.fixture +0 -0
  40. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/4/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  41. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  42. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/{.github → dot.github}/workflows/release.yml.fixture +0 -0
  43. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/5/{.nvmrc → dot.nvmrc} +0 -0
  44. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  45. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/{.github → dot.github}/workflows/integration.yml.fixture +0 -0
  46. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  47. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/{.lintwright.json → dot.lintwright.json} +0 -0
  48. /package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/{.nvmrc → dot.nvmrc} +0 -0
  49. /package/packs/dev-team/skills/sonar-onboard/evals/files/2/{.github → dot.github}/workflows/ci.yml.fixture +0 -0
  50. /package/packs/dev-team/skills/sonar-onboard/evals/files/2/{.gitlab-ci.yml → dot.gitlab-ci.yml} +0 -0
  51. /package/packs/frontend/skills/bundle-size-review/evals/files/5/{.browserslistrc → dot.browserslistrc} +0 -0
  52. /package/packs/frontend/skills/bundle-size-review/evals/files/6/{.browserslistrc → dot.browserslistrc} +0 -0
  53. /package/packs/frontend/skills/bundle-size-review/evals/files/8/{.browserslistrc → dot.browserslistrc} +0 -0
  54. /package/packs/frontend/skills/bundle-size-review/evals/files/9/{.github → dot.github}/workflows/size.yml.fixture +0 -0
  55. /package/packs/skill-engineering/skills/skill-review/evals/files/1/skills/changelog-writer/{SKILL.md → SKILL.md.fixture} +0 -0
  56. /package/packs/skill-engineering/skills/skill-review/evals/files/1/skills/pr-summary/{SKILL.md → SKILL.md.fixture} +0 -0
  57. /package/packs/skill-engineering/skills/skill-review/evals/files/2/invoice-chaser/{SKILL.md → SKILL.md.fixture} +0 -0
package/bin/skilldrop.js CHANGED
@@ -133,6 +133,8 @@ Options:
133
133
  --json machine-readable output for list | info | packs | agents | outdated | doctor —
134
134
  one JSON object on stdout, nothing else (for agents, scripts, CI)
135
135
  --with-related also install each skill's related companions (one level)
136
+ --replace replace a skill installed from a different catalogue that has the same
137
+ name (install refuses otherwise); the old folder is removed first
136
138
  --with-hooks also wire any hooks a skill declares (RFC-0006) — git pre-commit
137
139
  reminders and Claude Code session-start context; degrades where the
138
140
  target has no hook mechanism. Off by default so installs never touch
@@ -189,6 +191,15 @@ function splitSource(source) {
189
191
  const i = source.lastIndexOf("#");
190
192
  return i > 0 ? [source.slice(0, i), source.slice(i + 1)] : [source, ""];
191
193
  }
194
+ /* Two catalogue sources are the same catalogue when they name the same place, whatever ref or
195
+ spelling: git+https://host/o/r.git#abc, https://host/o/r and https://host/o/r/ are one source. */
196
+ function sourceId(source) {
197
+ if (!source || source === BUNDLED) return BUNDLED;
198
+ let s = splitSource(String(source))[0].trim().replace(/^git\+/, "").replace(/\/+$/, "").replace(/\.git$/, "");
199
+ if (/^[a-z]+:\/\//i.test(s)) s = s.replace(/^([a-z]+:\/\/[^/]+)/i, (h) => h.toLowerCase());
200
+ else s = path.resolve(s);
201
+ return s;
202
+ }
192
203
  function isPinned(source) { return !!source && SHA_RE.test(splitSource(source)[1]); }
193
204
 
194
205
  function fetchCatalog(source) {
@@ -1471,11 +1482,15 @@ function installUnits(args) {
1471
1482
  const policy = enforcePolicy(args.flags, cat, names, "skill");
1472
1483
  const { dest, ide } = target(args.flags);
1473
1484
  const contain = containmentFor(args.flags, cat, names, dest, ide, policy);
1474
- if (args.flags["dry-run"]) { planInstall(cat, names, dest, ide, args.flags); if (contain) applyContainment(contain, true); return; }
1485
+ const clashes = sourceClashes(cat, names, dest);
1486
+ if (args.flags["dry-run"]) { planInstall(cat, names, dest, ide, args.flags, clashes); if (contain) applyContainment(contain, true); return; }
1487
+ if (clashes.length && !args.flags.replace) die(clashMessage(clashes, cat, dest));
1475
1488
  fs.mkdirSync(dest, { recursive: true });
1476
1489
  const l = ledger(dest);
1477
1490
  const pipDeps = [], suggestions = new Set(), notes = [];
1478
1491
  for (const s of names) {
1492
+ // A replaced skill's folder goes first, so no file of the other catalogue's skill is left mixed in.
1493
+ if (clashes.some((c) => c.name === s)) fs.rmSync(path.join(dest, s), { recursive: true, force: true });
1479
1494
  const m = copyOne(cat, s, dest, ide, l, notes);
1480
1495
  if (fs.existsSync(path.join(skillDir(cat, s), "requirements.txt"))) pipDeps.push(s);
1481
1496
  for (const r of m.related || []) if (!names.includes(r) && !l.data[r]) suggestions.add(r);
@@ -1537,14 +1552,39 @@ function installUnits(args) {
1537
1552
  if (pk && pk["first-value"]) console.log("\n" + firstValueLines(pk).join("\n"));
1538
1553
  }
1539
1554
 
1555
+ /* Two catalogues can each ship a skill with the same name, and they are different skills. Copying
1556
+ one over the other would replace it silently and leave the first one's extra files mixed in, so
1557
+ install refuses unless asked (--replace), and says where each came from. */
1558
+ function sourceClashes(cat, names, dest) {
1559
+ const l = ledger(dest), out = [];
1560
+ for (const s of names) {
1561
+ const have = l.data[s];
1562
+ if (!have || isLoopEntry(have)) continue;
1563
+ if (sourceId(lsrc(have)) !== sourceId(cat.source)) out.push({ name: s, from: lsrc(have) });
1564
+ }
1565
+ return out;
1566
+ }
1567
+ function clashMessage(clashes, cat, dest) {
1568
+ const what = (src) => src === BUNDLED ? "skilldrop" : `'${src}'`;
1569
+ return clashes.map((c) => `'${c.name}' in ${dest} came from ${what(c.from)}; this install is from ${what(cat.source)}, a different skill with the same name.`).join("\n ") +
1570
+ "\n Nothing was installed. Keep the one you have, or replace it: rerun with --replace (the old folder is removed first)," +
1571
+ "\n or install into a separate folder with --dest <dir>.";
1572
+ }
1573
+
1540
1574
  /* install --dry-run: everything install would write, and nothing written. */
1541
- function planInstall(cat, names, dest, ide, flags) {
1575
+ function planInstall(cat, names, dest, ide, flags, clashes = []) {
1542
1576
  const l = ledger(dest);
1543
1577
  console.log(`dry run — nothing will be written. Target: ${dest} (${ide})\n`);
1544
1578
  for (const s of names) {
1545
1579
  const m = manifestOf(cat, s);
1546
1580
  const have = l.data[s];
1547
- const verb = !have ? "install" : lver(have) === m.version ? "reinstall" : `replace ${lver(have)} with`;
1581
+ const clash = clashes.find((c) => c.name === s);
1582
+ if (clash && !flags.replace) {
1583
+ console.log(`would refuse ${s}: installed from ${clash.from === BUNDLED ? "skilldrop" : clash.from}, a different catalogue (add --replace to replace it)`);
1584
+ continue;
1585
+ }
1586
+ const verb = clash ? `replace ${clash.from === BUNDLED ? "skilldrop" : clash.from}'s ${s} with` :
1587
+ !have ? "install" : lver(have) === m.version ? "reinstall" : `replace ${lver(have)} with`;
1548
1588
  const edits = editedFiles(dest, s, have);
1549
1589
  console.log(`would ${verb} ${s}@${m.version} -> ${path.join(dest, s)}`);
1550
1590
  if (edits.length) console.log(` overwrites your edits in: ${edits.join(", ")} (update keeps them; install does not)`);
@@ -1818,6 +1858,9 @@ function doctor(args) {
1818
1858
  for (const n of names) {
1819
1859
  const note = lsrc(data[n]) === BUNDLED && retiredNote(resolveCatalog(), n, isLoopEntry(data[n]));
1820
1860
  if (note) add("warn", note, `skilldrop uninstall ${n}${targetHint(args.flags)}, or keep it as it is; it will not update`);
1861
+ if (lsrc(data[n]) !== BUNDLED && !isLoopEntry(data[n]) && skillExists(resolveCatalog(), n))
1862
+ add("info", `${n} here came from '${lsrc(data[n])}'; skilldrop has its own ${n}, a different skill with the same name`,
1863
+ `keep it, or switch with: skilldrop install ${n} --replace${targetHint(args.flags)} (or install skilldrop's into another --dest)`);
1821
1864
  if (!fs.existsSync(path.join(dest, n))) add("error", `${n} is in the ledger but its folder is gone`, `skilldrop install ${n} (or uninstall ${n} to drop the record)`);
1822
1865
  else if (!data[n].file) {
1823
1866
  const loop = isLoopEntry(data[n]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skilldrop-cli",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "Portable AI-agent skills, measured against the agent without them. ADRs, PRDs, runbooks, decks and reviews, installable into Claude Code, Cursor, Kiro, and any IDE that takes custom instructions. One command per skill or per role-based pack.",
5
5
  "author": {
6
6
  "name": "Sanjay Ananthanarayan",
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ coverage/
4
+ .env
5
+ .env.*
@@ -0,0 +1,3 @@
1
+ .env
2
+ .claude/settings.local.json
3
+ __pycache__/
@@ -5,9 +5,9 @@
5
5
  > We're about to roll this Claude Code setup out to the whole data team for tidewater-ingest.
6
6
  > Anything we should change first?
7
7
 
8
- Files: [`inputs/tidewater-ingest/.claude/settings.json`](inputs/tidewater-ingest/.claude/settings.json),
9
- [`inputs/tidewater-ingest/.mcp.json`](inputs/tidewater-ingest/.mcp.json),
10
- [`inputs/tidewater-ingest/.gitignore`](inputs/tidewater-ingest/.gitignore).
8
+ Files: [`inputs/tidewater-ingest/dot.claude/settings.json`](inputs/tidewater-ingest/dot.claude/settings.json),
9
+ [`inputs/tidewater-ingest/.mcp.json`](inputs/tidewater-ingest/dot.mcp.json),
10
+ [`inputs/tidewater-ingest/.gitignore`](inputs/tidewater-ingest/dot.gitignore).
11
11
 
12
12
  ## What the scan listed (abridged, real output)
13
13
 
@@ -0,0 +1,3 @@
1
+ registry=https://registry.npmjs.org/
2
+ save-exact=true
3
+ engine-strict=true
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: locale-check
3
- description: Check a product's translations against the source strings before they ship — every key present, every placeholder ({name}, %s, %1$d, ICU plural and select) carried over intact, the plural forms each language needs, markup tags balanced, and strings over their character budget — with a script that reads JSON, ARB, gettext .po, Android strings.xml and iOS .strings, and lists each problem with its key and locale. Use when someone asks to "check the translations", "QA these locale files", "will the German fit", "why does the French build crash on this string", or hands over locale files before a release.
3
+ description: Check a product's translations against the source strings before they ship — every key present, every placeholder (named, %s, %1$d, ICU plural and select) carried over intact, the plural forms each language needs, markup tags balanced, and strings over their character budget — with a script that reads JSON, ARB, gettext .po, Android strings.xml and iOS .strings, and lists each problem with its key and locale. Use when someone asks to "check the translations", "QA these locale files", "will the German fit", "why does the French build crash on this string", or hands over locale files before a release.
4
4
  ---
5
5
 
6
6
  # locale-check
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "locale-check",
3
- "version": "0.1.0",
4
- "description": "Check a product's translations against the source strings before they ship — every key present, every placeholder ({name}, %s, %1$d, ICU plural and select) carried over intact, the plural forms each language needs, markup tags balanced, and strings over their character budget — with a script that reads JSON, ARB, gettext .po, Android strings.xml and iOS .strings, and lists each problem with its key and locale. Use when someone asks to \"check the translations\", \"QA these locale files\", \"will the German fit\", \"why does the French build crash on this string\", or hands over locale files before a release.",
3
+ "version": "0.1.1",
4
+ "description": "Check a product's translations against the source strings before they ship — every key present, every placeholder (named, %s, %1$d, ICU plural and select) carried over intact, the plural forms each language needs, markup tags balanced, and strings over their character budget — with a script that reads JSON, ARB, gettext .po, Android strings.xml and iOS .strings, and lists each problem with its key and locale. Use when someone asks to \"check the translations\", \"QA these locale files\", \"will the German fit\", \"why does the French build crash on this string\", or hands over locale files before a release.",
5
5
  "entrypoint": "SKILL.md",
6
6
  "deps": {
7
7
  "npm": [],
@@ -0,0 +1,5 @@
1
+ node_modules/
2
+ dist/
3
+ meta.json
4
+ # generated from the design team's emoji export, not committed
5
+ src/emoji/emoji-data.json