skilldrop-cli 0.9.0 → 0.9.1
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/README.md +1 -0
- package/bin/skilldrop.js +179 -3
- package/package.json +1 -1
- package/skills/adr-generator/evals/eval_queries.json +34 -0
- package/skills/adr-generator/evals/evals.json +17 -0
- package/skills/api-contract-draft/evals/eval_queries.json +34 -0
- package/skills/api-contract-draft/evals/evals.json +18 -0
- package/skills/architecture-diagrams/evals/eval_queries.json +34 -0
- package/skills/architecture-diagrams/evals/evals.json +16 -0
- package/skills/audience-profile/evals/eval_queries.json +34 -0
- package/skills/audience-profile/evals/evals.json +15 -0
- package/skills/brief-intake/evals/eval_queries.json +34 -0
- package/skills/brief-intake/evals/evals.json +16 -0
- package/skills/council-review/evals/eval_queries.json +38 -0
- package/skills/council-review/evals/evals.json +18 -0
- package/skills/data-contract/evals/eval_queries.json +34 -0
- package/skills/data-contract/evals/evals.json +16 -0
- package/skills/db-schema-design/evals/eval_queries.json +34 -0
- package/skills/db-schema-design/evals/evals.json +16 -0
- package/skills/decision-log/evals/eval_queries.json +34 -0
- package/skills/decision-log/evals/evals.json +17 -0
- package/skills/deck-builder/evals/eval_queries.json +34 -0
- package/skills/deck-builder/evals/evals.json +17 -0
- package/skills/design-doc/evals/eval_queries.json +34 -0
- package/skills/design-doc/evals/evals.json +17 -0
- package/skills/devils-advocate/evals/eval_queries.json +38 -0
- package/skills/devils-advocate/evals/evals.json +19 -0
- package/skills/doc-critique/evals/eval_queries.json +38 -0
- package/skills/doc-critique/evals/evals.json +17 -0
- package/skills/exec-summary/evals/eval_queries.json +34 -0
- package/skills/exec-summary/evals/evals.json +17 -0
- package/skills/figma-diagrams/evals/eval_queries.json +34 -0
- package/skills/figma-diagrams/evals/evals.json +15 -0
- package/skills/incident-comms/evals/eval_queries.json +34 -0
- package/skills/incident-comms/evals/evals.json +16 -0
- package/skills/nfr-spec/evals/eval_queries.json +34 -0
- package/skills/nfr-spec/evals/evals.json +16 -0
- package/skills/observability-plan/evals/eval_queries.json +34 -0
- package/skills/observability-plan/evals/evals.json +16 -0
- package/skills/postmortem-generator/evals/eval_queries.json +34 -0
- package/skills/postmortem-generator/evals/evals.json +17 -0
- package/skills/prd-draft/evals/eval_queries.json +34 -0
- package/skills/prd-draft/evals/evals.json +17 -0
- package/skills/requirements-interview/evals/eval_queries.json +34 -0
- package/skills/requirements-interview/evals/evals.json +16 -0
- package/skills/reverse-architecture/evals/eval_queries.json +34 -0
- package/skills/reverse-architecture/evals/evals.json +16 -0
- package/skills/runbook-generator/evals/eval_queries.json +34 -0
- package/skills/runbook-generator/evals/evals.json +17 -0
- package/skills/slide-outliner/evals/eval_queries.json +34 -0
- package/skills/slide-outliner/evals/evals.json +16 -0
- package/skills/sonar-onboard/evals/eval_queries.json +34 -0
- package/skills/sonar-onboard/evals/evals.json +16 -0
- package/skills/sonar-review/evals/eval_queries.json +34 -0
- package/skills/sonar-review/evals/evals.json +16 -0
- package/skills/threat-model/evals/eval_queries.json +34 -0
- package/skills/threat-model/evals/evals.json +18 -0
package/README.md
CHANGED
|
@@ -264,6 +264,7 @@ npx skilldrop-cli install --agent devils-advocate # -> ~/.claude/agents/ (
|
|
|
264
264
|
npx skilldrop-cli install --panel review # the whole review fleet: 3 subagents + the pre-merge-review orchestrator (RFC-0020)
|
|
265
265
|
npx skilldrop-cli outdated && npx skilldrop-cli update # skills improve; cp -R never tells you
|
|
266
266
|
npx skilldrop-cli list | skilldrop info <skill> | skilldrop packs | skilldrop uninstall <skill>
|
|
267
|
+
npx skilldrop-cli list --json # machine-readable: list/info/packs/agents/outdated (RFC-0021)
|
|
267
268
|
```
|
|
268
269
|
|
|
269
270
|
`--with-related` also pulls each skill's companions. From a clone (or before the package is published): `node bin/skilldrop.js <same args>`. Scope and design: [RFC-0002](docs/rfcs/0002-skilldrop-cli.md), full command surface in [`docs/designs/skilldrop-cli-design.md`](docs/designs/skilldrop-cli-design.md).
|
package/bin/skilldrop.js
CHANGED
|
@@ -48,6 +48,9 @@ Usage:
|
|
|
48
48
|
skilldrop uninstall <skill...> remove skills (and wiring files this tool wrote)
|
|
49
49
|
skilldrop uninstall --agent <name...> remove subagents
|
|
50
50
|
skilldrop validate [--from <src>] structural check of a catalog (for catalog authors)
|
|
51
|
+
skilldrop scan [<skill...>] [--from <src>] supply-chain scan — flag network/exec/credential
|
|
52
|
+
patterns in scripts and injection-shaped instructions
|
|
53
|
+
in SKILL.md before you trust a catalog (RFC-0022)
|
|
51
54
|
|
|
52
55
|
Catalogs:
|
|
53
56
|
(default) the catalog bundled with this package
|
|
@@ -72,6 +75,8 @@ Options:
|
|
|
72
75
|
--ide antigravity ~/.gemini/config/agents markdown, subagent: true
|
|
73
76
|
--dest <dir> any directory copied as-is
|
|
74
77
|
Cursor has no agent format — use a custom mode (agents/README.md).
|
|
78
|
+
--json machine-readable output for list | info | packs | agents | outdated —
|
|
79
|
+
one JSON object on stdout, nothing else (for agents, scripts, CI)
|
|
75
80
|
--with-related also install each skill's related companions (one level)
|
|
76
81
|
--with-hooks also wire any hooks a skill declares (RFC-0006) — git pre-commit
|
|
77
82
|
reminders and Claude Code session-start context; degrades where the
|
|
@@ -82,6 +87,11 @@ Options:
|
|
|
82
87
|
function die(msg) { console.error("error: " + msg); process.exit(1); }
|
|
83
88
|
function readJSON(p) { return JSON.parse(fs.readFileSync(p, "utf8")); }
|
|
84
89
|
|
|
90
|
+
/* --json (RFC-0021): the read commands emit one machine-readable object on stdout and nothing
|
|
91
|
+
else, so an agent or script can consume them without scraping aligned columns. Human output is
|
|
92
|
+
unchanged by default — this is additive. Errors still go to stderr and exit non-zero. */
|
|
93
|
+
function emitJSON(obj) { console.log(JSON.stringify(obj, null, 2)); }
|
|
94
|
+
|
|
85
95
|
function parseArgs(argv) {
|
|
86
96
|
const out = { _: [], flags: {} };
|
|
87
97
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -276,6 +286,117 @@ function gate(cat, names) {
|
|
|
276
286
|
if (bad) die(`${bad} skill(s) failed the structural check — nothing was installed`);
|
|
277
287
|
}
|
|
278
288
|
|
|
289
|
+
/* ---------- supply-chain scan (RFC-0022) ----------
|
|
290
|
+
A skill is instruction prose an agent will obey, and its scripts are code that runs on the
|
|
291
|
+
user's machine — so installing a third-party catalog is a supply-chain event. Anthropic's own
|
|
292
|
+
Skills guidance says to audit a skill's files for "unexpected network calls, file access
|
|
293
|
+
patterns, or operations that don't match the Skill's stated purpose"; this automates that first
|
|
294
|
+
pass. It is a HEURISTIC that reports — it never blocks and never replaces reading the skill.
|
|
295
|
+
|
|
296
|
+
Two rule sets, deliberately different:
|
|
297
|
+
- SCRIPT_RULES run over executable files (real code): network, exec, credential, obfuscation.
|
|
298
|
+
- PROSE_RULES run over SKILL.md and only match *instructions to misbehave* — not security
|
|
299
|
+
vocabulary, so a skill that legitimately discusses injection (threat-model) isn't flagged. */
|
|
300
|
+
|
|
301
|
+
const SCRIPT_EXT = new Set([".py", ".js", ".mjs", ".cjs", ".sh", ".bash", ".zsh", ".rb", ".pl", ".ps1"]);
|
|
302
|
+
|
|
303
|
+
const SCRIPT_RULES = [
|
|
304
|
+
{ id: "exec-remote", sev: "🟥", why: "downloads and executes remote content — the classic supply-chain payload",
|
|
305
|
+
re: /(curl|wget)[^\n|]*\|\s*(sudo\s+)?(ba|z|)sh|eval\s*\(\s*(requests|urllib|fetch)|base64\s+(-d|--decode)[^\n|]*\|\s*(ba|z|)sh/i },
|
|
306
|
+
{ id: "shell-exec", sev: "🟧", why: "executes shell commands",
|
|
307
|
+
re: /\b(os\.system|subprocess\.(run|call|Popen|check_output)|child_process|execSync|spawnSync|shell_exec|`[^`\n]*\$\()/ },
|
|
308
|
+
{ id: "network", sev: "🟧", why: "makes outbound network calls",
|
|
309
|
+
re: /\b(requests\.(get|post|put)|urllib\.request|httpx\.|axios\.|node-fetch|\bfetch\s*\(\s*["'`]https?:|curl\s+https?:|wget\s+https?:)/i },
|
|
310
|
+
{ id: "credentials", sev: "🟧", why: "reads credentials or secret material",
|
|
311
|
+
re: /\b(os\.environ|process\.env)\b[^\n]{0,40}(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL)|~\/\.(aws|ssh|npmrc|netrc)|\.env\b|id_rsa/i },
|
|
312
|
+
{ id: "broad-fs", sev: "🟨", why: "writes outside the skill's own directory",
|
|
313
|
+
re: /\b(shutil\.rmtree|rm\s+-rf\s+[~/]|open\s*\(\s*["'`]\/(etc|usr|bin)|fs\.(unlink|rmSync)\s*\([^)]*\.\.\/)/ },
|
|
314
|
+
];
|
|
315
|
+
|
|
316
|
+
const PROSE_RULES = [
|
|
317
|
+
{ id: "instruction-override", sev: "🟥", why: "tells the agent to ignore its prior instructions",
|
|
318
|
+
re: /ignore\s+(all\s+)?(previous|prior|earlier|above|preceding)\s+(instructions|prompts|rules)/i },
|
|
319
|
+
{ id: "conceal-from-user", sev: "🟥", why: "tells the agent to hide actions from the user",
|
|
320
|
+
re: /(do\s*n[o']?t|never|without)\s+(tell|telling|inform|informing|notify|notifying|mention|mentioning)\s+(the\s+)?(user|human)/i },
|
|
321
|
+
{ id: "memory-overwrite", sev: "🟥", why: "instructs the agent to rewrite its own persistent memory/identity",
|
|
322
|
+
re: /(update|overwrite|append\s+to|modify)\s+your\s+(own\s+)?(SOUL|MEMORY|CLAUDE|AGENTS)\.md/i },
|
|
323
|
+
{ id: "prose-exfil", sev: "🟧", why: "instructs the agent to send data to an external endpoint",
|
|
324
|
+
re: /\b(POST|send|upload|transmit)\b[^\n.]{0,60}\bto\s+https?:\/\//i },
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
function walkFiles(dir, out = []) {
|
|
328
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
329
|
+
const p = path.join(dir, e.name);
|
|
330
|
+
if (e.isDirectory()) walkFiles(p, out);
|
|
331
|
+
else out.push(p);
|
|
332
|
+
}
|
|
333
|
+
return out;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* Scan one skill. Returns [{sev, id, why, file, line, text}] — worst first. */
|
|
337
|
+
function scanSkill(cat, s) {
|
|
338
|
+
const dir = skillDir(cat, s);
|
|
339
|
+
const findings = [];
|
|
340
|
+
if (!dir || !fs.existsSync(dir)) return findings;
|
|
341
|
+
for (const file of walkFiles(dir)) {
|
|
342
|
+
const rel = path.relative(dir, file);
|
|
343
|
+
const ext = path.extname(file).toLowerCase();
|
|
344
|
+
const isScript = SCRIPT_EXT.has(ext);
|
|
345
|
+
const isProse = path.basename(file) === "SKILL.md";
|
|
346
|
+
if (!isScript && !isProse) continue;
|
|
347
|
+
let body = "";
|
|
348
|
+
try { body = fs.readFileSync(file, "utf8"); } catch (e) { continue; }
|
|
349
|
+
const rules = isScript ? SCRIPT_RULES : PROSE_RULES;
|
|
350
|
+
body.split(/\r?\n/).forEach((line, i) => {
|
|
351
|
+
if (line.length > 400) return; // minified/data line — not reviewable prose or code
|
|
352
|
+
for (const r of rules) {
|
|
353
|
+
if (r.re.test(line))
|
|
354
|
+
findings.push({ sev: r.sev, id: r.id, why: r.why, file: rel, line: i + 1, text: line.trim().slice(0, 120) });
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
const order = { "🟥": 0, "🟧": 1, "🟨": 2, "⚪": 3 };
|
|
359
|
+
return findings.sort((a, b) => order[a.sev] - order[b.sev]);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function printScan(cat, names, { compact = false } = {}) {
|
|
363
|
+
let total = 0, worst = null;
|
|
364
|
+
for (const s of names) {
|
|
365
|
+
const f = scanSkill(cat, s);
|
|
366
|
+
if (!f.length) continue;
|
|
367
|
+
total += f.length;
|
|
368
|
+
if (!worst || f[0].sev === "🟥") worst = worst === "🟥" ? worst : f[0].sev;
|
|
369
|
+
console.log(`\n${s}`);
|
|
370
|
+
for (const x of (compact ? f.slice(0, 3) : f))
|
|
371
|
+
console.log(` ${x.sev} ${x.id} — ${x.why}\n ${x.file}:${x.line} ${x.text}`);
|
|
372
|
+
if (compact && f.length > 3) console.log(` … ${f.length - 3} more (skilldrop scan ${s} --from ${cat.source})`);
|
|
373
|
+
}
|
|
374
|
+
return { total, worst };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function scan(args) {
|
|
378
|
+
const cat = resolveCatalog(args.flags.from);
|
|
379
|
+
const names = args._.length ? args._.slice() : skillsIn(cat);
|
|
380
|
+
for (const s of names) if (!skillExists(cat, s)) die(`unknown skill '${s}' in catalog '${cat.source}'`);
|
|
381
|
+
|
|
382
|
+
if (args.flags.json) {
|
|
383
|
+
const out = names.map((s) => ({ skill: s, findings: scanSkill(cat, s) })).filter((r) => r.findings.length);
|
|
384
|
+
return emitJSON({
|
|
385
|
+
catalog: cat.source, scanned: names.length,
|
|
386
|
+
flagged: out.length, findingCount: out.reduce((n, r) => n + r.findings.length, 0), skills: out,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
console.log(`Scanning ${names.length} skill(s) in catalog '${cat.source}' for supply-chain patterns…`);
|
|
390
|
+
const { total } = printScan(cat, names);
|
|
391
|
+
if (!total) {
|
|
392
|
+
console.log("\nNo flagged patterns found.");
|
|
393
|
+
} else {
|
|
394
|
+
console.log(`\n${total} flagged pattern(s). These are HEURISTICS, not verdicts — a match can be` +
|
|
395
|
+
` entirely legitimate (a skill that is *about* security, or a script that genuinely needs the network).`);
|
|
396
|
+
console.log("Read the cited lines before installing. Nothing here is executed by skilldrop; installs copy files only.");
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
279
400
|
/* ---------- install targets & ledger ---------- */
|
|
280
401
|
|
|
281
402
|
function target(flags) {
|
|
@@ -643,8 +764,15 @@ function install(args) {
|
|
|
643
764
|
console.log(`\n${withHooks.join(", ")} declare hooks — re-run with --with-hooks to wire them (git pre-commit reminders / session-start context).`);
|
|
644
765
|
}
|
|
645
766
|
|
|
646
|
-
if (cat.source !== BUNDLED)
|
|
767
|
+
if (cat.source !== BUNDLED) {
|
|
647
768
|
console.log(`\nWARNING: third-party catalog '${cat.source}'. Skills are instructions your AI agent will follow — review each installed SKILL.md under ${dest} before first use. Install copied files only; nothing was executed.`);
|
|
769
|
+
// Supply-chain scan (RFC-0022): surface risky patterns at the moment of trust. Reports only —
|
|
770
|
+
// the files are already copied, and a match is a prompt to read, not a verdict.
|
|
771
|
+
const { total } = printScan(cat, names, { compact: true });
|
|
772
|
+
console.log(total
|
|
773
|
+
? `\n${total} pattern(s) worth reading before you trust these skills. Full detail: skilldrop scan --from ${cat.source}`
|
|
774
|
+
: "\nSupply-chain scan: no flagged patterns.");
|
|
775
|
+
}
|
|
648
776
|
if (ide === "generic")
|
|
649
777
|
console.log("wiring: attach each skill's SKILL.md to your agent (Continue/Cline: @file, Aider: /add, Codex: reference it from AGENTS.md) — see the repo README's per-IDE steps.");
|
|
650
778
|
for (const s of pipDeps)
|
|
@@ -687,8 +815,18 @@ function update(args) {
|
|
|
687
815
|
|
|
688
816
|
function outdated(args) {
|
|
689
817
|
const { dest, rows } = installedRows(args.flags);
|
|
690
|
-
if (!rows.length) return console.log(`nothing installed at ${dest}`);
|
|
691
818
|
const stale = rows.filter((r) => r.current && r.current !== r.installed);
|
|
819
|
+
if (args.flags.json)
|
|
820
|
+
return emitJSON({
|
|
821
|
+
dest,
|
|
822
|
+
count: rows.length,
|
|
823
|
+
outdatedCount: stale.length,
|
|
824
|
+
skills: rows.map((r) => ({
|
|
825
|
+
name: r.s, installed: r.installed, current: r.current,
|
|
826
|
+
source: r.src, outdated: !!(r.current && r.current !== r.installed),
|
|
827
|
+
})),
|
|
828
|
+
});
|
|
829
|
+
if (!rows.length) return console.log(`nothing installed at ${dest}`);
|
|
692
830
|
for (const r of stale) console.log(`${r.s}: installed ${r.installed}, current ${r.current} (${r.src})`);
|
|
693
831
|
console.log(stale.length ? `\n${stale.length} outdated — run: skilldrop update` : "everything up to date.");
|
|
694
832
|
}
|
|
@@ -712,6 +850,15 @@ function uninstall(args) {
|
|
|
712
850
|
function listAgents(args) {
|
|
713
851
|
const cat = resolveCatalog(args.flags.from);
|
|
714
852
|
const names = agentsIn(cat);
|
|
853
|
+
if (args.flags.json)
|
|
854
|
+
return emitJSON({
|
|
855
|
+
catalog: cat.source,
|
|
856
|
+
count: names.length,
|
|
857
|
+
agents: names.map((a) => {
|
|
858
|
+
const m = agentMeta(cat, a);
|
|
859
|
+
return { name: a, description: m.description || null, tools: m.tools || null, model: m.model || null };
|
|
860
|
+
}),
|
|
861
|
+
});
|
|
715
862
|
if (!names.length) return console.log(`catalog '${cat.source}' ships no agents.`);
|
|
716
863
|
const w = Math.max(...names.map((n) => n.length));
|
|
717
864
|
for (const a of names) {
|
|
@@ -779,6 +926,12 @@ function list(args) {
|
|
|
779
926
|
try { const m = manifestOf(cat, s); return [s, m.version || "?", (m.model && m.model.tier) || "?"]; }
|
|
780
927
|
catch (e) { return [s, "?", "?"]; }
|
|
781
928
|
});
|
|
929
|
+
if (args.flags.json)
|
|
930
|
+
return emitJSON({
|
|
931
|
+
catalog: cat.source,
|
|
932
|
+
count: rows.length,
|
|
933
|
+
skills: rows.map(([name, version, tier]) => ({ name, version, tier })),
|
|
934
|
+
});
|
|
782
935
|
const w = Math.max(...rows.map((r) => r[0].length));
|
|
783
936
|
for (const [s, v, t] of rows) console.log(`${s.padEnd(w)} ${v} ${t}`);
|
|
784
937
|
console.log(`\n${rows.length} skills in catalog '${cat.source}'. Details: skilldrop info <skill>`);
|
|
@@ -791,6 +944,21 @@ function info(args) {
|
|
|
791
944
|
const m = manifestOf(cat, s);
|
|
792
945
|
const ps = packsOf(cat) || {};
|
|
793
946
|
const inPacks = Object.entries(ps).filter(([, p]) => p.skills.includes(s)).map(([n]) => n);
|
|
947
|
+
const needsPip = ((m.deps || {}).pip || []).length > 0 || fs.existsSync(path.join(skillDir(cat, s), "requirements.txt"));
|
|
948
|
+
if (args.flags.json)
|
|
949
|
+
return emitJSON({
|
|
950
|
+
catalog: cat.source,
|
|
951
|
+
name: m.name,
|
|
952
|
+
version: m.version || null,
|
|
953
|
+
description: m.description || null,
|
|
954
|
+
tier: (m.model && m.model.tier) || null,
|
|
955
|
+
packs: inPacks,
|
|
956
|
+
related: m.related || [],
|
|
957
|
+
tags: m.tags || [],
|
|
958
|
+
deps: { pip: (m.deps || {}).pip || [], npm: (m.deps || {}).npm || [], requirementsTxt: needsPip },
|
|
959
|
+
env: { required: (m.env || {}).required || [], optional: (m.env || {}).optional || [] },
|
|
960
|
+
hooks: m.hooks || [],
|
|
961
|
+
});
|
|
794
962
|
console.log(`${m.name}@${m.version} (tier: ${(m.model && m.model.tier) || "n/a"})\n\n${m.description}\n`);
|
|
795
963
|
console.log(`catalog: ${cat.source}`);
|
|
796
964
|
console.log(`packs: ${inPacks.join(", ") || "-"}`);
|
|
@@ -803,6 +971,14 @@ function info(args) {
|
|
|
803
971
|
function listPacks(args) {
|
|
804
972
|
const cat = resolveCatalog(args.flags.from);
|
|
805
973
|
const ps = packsOf(cat);
|
|
974
|
+
if (args.flags.json)
|
|
975
|
+
return emitJSON({
|
|
976
|
+
catalog: cat.source,
|
|
977
|
+
count: ps ? Object.keys(ps).length : 0,
|
|
978
|
+
packs: Object.entries(ps || {}).map(([name, p]) => ({
|
|
979
|
+
name, description: p.description || null, skills: p.skills || [],
|
|
980
|
+
})),
|
|
981
|
+
});
|
|
806
982
|
if (!ps) return console.log(`catalog '${cat.source}' defines no packs.`);
|
|
807
983
|
const w = Math.max(...Object.keys(ps).map((n) => n.length));
|
|
808
984
|
for (const [n, p] of Object.entries(ps))
|
|
@@ -830,7 +1006,7 @@ function validateCmd(args) {
|
|
|
830
1006
|
|
|
831
1007
|
const args = parseArgs(process.argv.slice(2));
|
|
832
1008
|
const cmd = args._.shift();
|
|
833
|
-
const commands = { list, info, packs: listPacks, agents: listAgents, install, update, outdated, uninstall, validate: validateCmd };
|
|
1009
|
+
const commands = { list, info, packs: listPacks, agents: listAgents, install, update, outdated, uninstall, validate: validateCmd, scan };
|
|
834
1010
|
if (!cmd || cmd === "help" || args.flags.help) console.log(HELP);
|
|
835
1011
|
else if (commands[cmd]) commands[cmd](args);
|
|
836
1012
|
else die(`unknown command '${cmd}' — run: skilldrop help`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skilldrop-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Portable AI-agent skills for the deliverables knowledge workers ship \u2014 diagrams, ADRs, PRDs, runbooks, decks, reviews \u2014 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,34 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Write an ADR for this decision",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "Document the 'we decided X because Y' moment as an architecture decision record",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Capture this architectural decision in MADR format",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "We picked Postgres over Dynamo — record the decision properly",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Extract the decisions and owners from these meeting notes",
|
|
20
|
+
"should_trigger": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Write a full engineering design doc for this feature",
|
|
24
|
+
"should_trigger": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Critique the ADR I already wrote",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"query": "Convene a council to decide which database we should use",
|
|
32
|
+
"should_trigger": false
|
|
33
|
+
}
|
|
34
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "adr-generator",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "Write an ADR: we decided to use Postgres as our primary datastore instead of DynamoDB, because our access patterns are relational and the team already operates Postgres.",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"Status is one of Proposed / Accepted / Deprecated / Superseded by ADR-NNNN, defaulting to Proposed unless the user says it was agreed",
|
|
9
|
+
"Title is a noun phrase naming the decision, not a verb phrase naming the activity",
|
|
10
|
+
"Consequences include real trade-offs and downsides, not only upsides",
|
|
11
|
+
"The alternatives considered each carry a why-not, not just a description",
|
|
12
|
+
"The chosen format is MADR by default; Nygard only when explicitly requested",
|
|
13
|
+
"No invented context — facts not supplied by the user are marked rather than fabricated"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Draft the OpenAPI spec for this API",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "Write the REST contract for these endpoints",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "I need an API contract with a proper error catalog and pagination decided",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "Turn this user story into an OpenAPI 3.1 definition",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Define the contract for this Kafka topic our team publishes",
|
|
20
|
+
"should_trigger": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Design the Postgres schema and indexes for this feature",
|
|
24
|
+
"should_trigger": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "What latency and availability targets should this service have?",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"query": "Review this API implementation for security bugs",
|
|
32
|
+
"should_trigger": false
|
|
33
|
+
}
|
|
34
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "api-contract-draft",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "Draft the OpenAPI contract for our orders API — clients need to create an order, fetch one, and list a customer's orders.",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"The emitted YAML is parseable OpenAPI 3.1 and every $ref resolves",
|
|
9
|
+
"Paths are resource-oriented rather than RPC-style verbs in the URL",
|
|
10
|
+
"Every operation enumerates its 4xx set with stable machine-switchable code values — not a bare '400: Bad Request'",
|
|
11
|
+
"Errors follow RFC 9457 problem-details shape",
|
|
12
|
+
"Pagination, idempotency, and versioning are each decided explicitly rather than deferred",
|
|
13
|
+
"Every operation carries at least one example",
|
|
14
|
+
"Each decision in the log has a rationale, and any deviation from the skill's conventions is logged rather than made silently"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Draw an architecture diagram for this system",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "Generate a Mermaid diagram of these services",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Create a C4 container diagram from this description",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "Diagram the sequence flow I just described",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Reverse-engineer the architecture from our Terraform and repo",
|
|
20
|
+
"should_trigger": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Read the frames in our Figma file",
|
|
24
|
+
"should_trigger": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Threat model this design",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"query": "Write the design doc for this system",
|
|
32
|
+
"should_trigger": false
|
|
33
|
+
}
|
|
34
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "architecture-diagrams",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "Draw the architecture for a checkout service: an API gateway, an orders service, a payments service calling Stripe, and a Postgres primary with a read replica.",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"The diagram fits on one screen; beyond roughly 15 nodes it is split into separate Context and Container diagrams rather than crammed",
|
|
9
|
+
"Arrows show direction of data flow, not undirected connections",
|
|
10
|
+
"Every arrow crossing a trust boundary is labelled",
|
|
11
|
+
"The diagram is generated from the written description provided — no components are invented beyond it",
|
|
12
|
+
"A named format is produced (Mermaid, PlantUML, or C4) and renders as valid syntax"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Who is my audience and what rules should the deck follow?",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "What are the structural rules for presenting to a board versus engineers?",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Profile this audience so I know how technical to go",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "We have a mixed exec and technical room — how should I pitch it?",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Build the PowerPoint for the board meeting",
|
|
20
|
+
"should_trigger": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Outline the slides for this deck",
|
|
24
|
+
"should_trigger": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Write the one-page executive summary",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"query": "Map the customer's journey through our product",
|
|
32
|
+
"should_trigger": false
|
|
33
|
+
}
|
|
34
|
+
]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "audience-profile",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "We're presenting our platform strategy to a mixed room — the board plus two engineering leads. What audience rules should we design to?",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"Output is concrete and structural (slide count ranges, density, tone, must-have sections, things to avoid) rather than advice like 'keep it short'",
|
|
9
|
+
"A mixed audience resolves to the least-technical archetype, with the more-technical material moved to an appendix",
|
|
10
|
+
"No new archetype is invented for a one-off case — the closest existing one is chosen and the divergence noted",
|
|
11
|
+
"The profile is reusable across deliverables rather than being a deck or summary itself"
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Turn this Slack thread into a structured brief",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "I have a messy transcript — get it into shape for the design-doc skill",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Structure this raw context into a brief before I run the next skill",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "Take this half-written Notion page and extract a clean brief",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Extract the decisions, owners, and due dates from this discussion",
|
|
20
|
+
"should_trigger": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Write the ADR itself from this context",
|
|
24
|
+
"should_trigger": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Critique this brief's quality",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"query": "Summarize this thread for the executive team",
|
|
32
|
+
"should_trigger": false
|
|
33
|
+
}
|
|
34
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "brief-intake",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "Here's a messy Slack thread about our caching problem — turn it into a brief I can hand to design-doc.",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"Extracted fields marked [explicit] or [implied] carry a short verbatim snippet from the source so nothing is hallucinated",
|
|
9
|
+
"Relative dates are converted to absolute YYYY-MM-DD, marked implied where computed",
|
|
10
|
+
"Gaps are named as missing rather than filled in with plausible invention",
|
|
11
|
+
"The output names the downstream skill the brief is shaped for",
|
|
12
|
+
"The brief is structured into the fields that downstream skill needs, not a summary"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Convene the council on this architecture decision",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "I want multiple senior perspectives to deliberate this change",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Get an architect, security, and SRE view on this before we commit",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "Run a pre-mortem style multi-perspective review of this RFC",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "This is risky and load-bearing — I need more than one reviewer's lens",
|
|
20
|
+
"should_trigger": true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Review this code for edge cases the first pass missed",
|
|
24
|
+
"should_trigger": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Critique this document against its quality bar",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"query": "Score Postgres vs DynamoDB on weighted criteria",
|
|
32
|
+
"should_trigger": false
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"query": "Write the ADR recording the decision we already made",
|
|
36
|
+
"should_trigger": false
|
|
37
|
+
}
|
|
38
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "council-review",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "We're considering moving our monolith's checkout flow onto a separate service before Black Friday. Convene the council and give me a verdict.",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"Each of the five standing seats takes an independent stance (support / conditions / oppose / abstain) before any cross-talk",
|
|
9
|
+
"Every seat point is concrete and attached to evidence — file:line for code, or the named assumption for a decision",
|
|
10
|
+
"Genuine disagreements are surfaced as named cruxes; agreement is reported honestly as unanimous rather than manufactured into conflict",
|
|
11
|
+
"A Chair issues a single reconciled verdict (PROCEED / WITH CONDITIONS / REVISE / RECONSIDER / SPLIT)",
|
|
12
|
+
"The verdict names the tradeoff chosen, records dissent, and states what would change the decision",
|
|
13
|
+
"Bench seats are added only when the change warrants them, and benching is stated explicitly",
|
|
14
|
+
"No persona cosplay — the persona is used as a lens, not costume prose"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"query": "Write a data contract for this table",
|
|
4
|
+
"should_trigger": true
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"query": "We have downstream consumers of this dataset — define the contract and its SLAs",
|
|
8
|
+
"should_trigger": true
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"query": "Define the schema and quality guarantees for this event stream",
|
|
12
|
+
"should_trigger": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"query": "What should our producers promise consumers about this data?",
|
|
16
|
+
"should_trigger": true
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "Draft the OpenAPI spec for our REST API",
|
|
20
|
+
"should_trigger": false
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"query": "Design the database schema and indexes from our access patterns",
|
|
24
|
+
"should_trigger": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"query": "Write the product requirements document",
|
|
28
|
+
"should_trigger": false
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"query": "Plan our observability and alerting",
|
|
32
|
+
"should_trigger": false
|
|
33
|
+
}
|
|
34
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "data-contract",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "We publish an `orders_daily` table that three downstream teams consume. Write the data contract.",
|
|
7
|
+
"assertions": [
|
|
8
|
+
"Every field carries a meaning plus a unit or allowed-values, not merely a type — the cents-vs-dollars class of bug is designed out",
|
|
9
|
+
"Every quality SLA states a threshold, a check, and a breach action; 'high quality' is rejected as an SLA",
|
|
10
|
+
"The evolution policy explicitly names meaning-change-under-a-stable-name as a breaking change",
|
|
11
|
+
"The contract names the owner and the consumers it is a promise to",
|
|
12
|
+
"Freshness, completeness, validity, and distribution are each addressed or explicitly marked not-applicable"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|