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.
Files changed (57) hide show
  1. package/README.md +1 -0
  2. package/bin/skilldrop.js +179 -3
  3. package/package.json +1 -1
  4. package/skills/adr-generator/evals/eval_queries.json +34 -0
  5. package/skills/adr-generator/evals/evals.json +17 -0
  6. package/skills/api-contract-draft/evals/eval_queries.json +34 -0
  7. package/skills/api-contract-draft/evals/evals.json +18 -0
  8. package/skills/architecture-diagrams/evals/eval_queries.json +34 -0
  9. package/skills/architecture-diagrams/evals/evals.json +16 -0
  10. package/skills/audience-profile/evals/eval_queries.json +34 -0
  11. package/skills/audience-profile/evals/evals.json +15 -0
  12. package/skills/brief-intake/evals/eval_queries.json +34 -0
  13. package/skills/brief-intake/evals/evals.json +16 -0
  14. package/skills/council-review/evals/eval_queries.json +38 -0
  15. package/skills/council-review/evals/evals.json +18 -0
  16. package/skills/data-contract/evals/eval_queries.json +34 -0
  17. package/skills/data-contract/evals/evals.json +16 -0
  18. package/skills/db-schema-design/evals/eval_queries.json +34 -0
  19. package/skills/db-schema-design/evals/evals.json +16 -0
  20. package/skills/decision-log/evals/eval_queries.json +34 -0
  21. package/skills/decision-log/evals/evals.json +17 -0
  22. package/skills/deck-builder/evals/eval_queries.json +34 -0
  23. package/skills/deck-builder/evals/evals.json +17 -0
  24. package/skills/design-doc/evals/eval_queries.json +34 -0
  25. package/skills/design-doc/evals/evals.json +17 -0
  26. package/skills/devils-advocate/evals/eval_queries.json +38 -0
  27. package/skills/devils-advocate/evals/evals.json +19 -0
  28. package/skills/doc-critique/evals/eval_queries.json +38 -0
  29. package/skills/doc-critique/evals/evals.json +17 -0
  30. package/skills/exec-summary/evals/eval_queries.json +34 -0
  31. package/skills/exec-summary/evals/evals.json +17 -0
  32. package/skills/figma-diagrams/evals/eval_queries.json +34 -0
  33. package/skills/figma-diagrams/evals/evals.json +15 -0
  34. package/skills/incident-comms/evals/eval_queries.json +34 -0
  35. package/skills/incident-comms/evals/evals.json +16 -0
  36. package/skills/nfr-spec/evals/eval_queries.json +34 -0
  37. package/skills/nfr-spec/evals/evals.json +16 -0
  38. package/skills/observability-plan/evals/eval_queries.json +34 -0
  39. package/skills/observability-plan/evals/evals.json +16 -0
  40. package/skills/postmortem-generator/evals/eval_queries.json +34 -0
  41. package/skills/postmortem-generator/evals/evals.json +17 -0
  42. package/skills/prd-draft/evals/eval_queries.json +34 -0
  43. package/skills/prd-draft/evals/evals.json +17 -0
  44. package/skills/requirements-interview/evals/eval_queries.json +34 -0
  45. package/skills/requirements-interview/evals/evals.json +16 -0
  46. package/skills/reverse-architecture/evals/eval_queries.json +34 -0
  47. package/skills/reverse-architecture/evals/evals.json +16 -0
  48. package/skills/runbook-generator/evals/eval_queries.json +34 -0
  49. package/skills/runbook-generator/evals/evals.json +17 -0
  50. package/skills/slide-outliner/evals/eval_queries.json +34 -0
  51. package/skills/slide-outliner/evals/evals.json +16 -0
  52. package/skills/sonar-onboard/evals/eval_queries.json +34 -0
  53. package/skills/sonar-onboard/evals/evals.json +16 -0
  54. package/skills/sonar-review/evals/eval_queries.json +34 -0
  55. package/skills/sonar-review/evals/evals.json +16 -0
  56. package/skills/threat-model/evals/eval_queries.json +34 -0
  57. 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.0",
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
+ }