skilldrop-cli 0.11.7 → 0.11.9

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 CHANGED
@@ -424,6 +424,9 @@ npx skilldrop-cli install --pack dev-team --project # .claude/skills — als
424
424
  npx skilldrop-cli install prfaq --ide cursor # + writes .cursor/rules/prfaq.mdc
425
425
  npx skilldrop-cli install --pack sre-oncall --ide kiro # .kiro/skills — Kiro IDE + Kiro CLI, discovered natively
426
426
  npx skilldrop-cli install adr-generator --dest .agents/skills # Codex + Copilot CLI (see below)
427
+ npx skilldrop-cli loops # the five loops, their stages and gates
428
+ npx skilldrop-cli install --loop build # a loop + every stage skill it sequences (RFC-0028)
429
+ npx skilldrop-cli install --loop --pack sre-oncall # every loop that pack declares
427
430
  npx skilldrop-cli agents # the reviewer subagents
428
431
  npx skilldrop-cli install --agent devils-advocate # -> ~/.claude/agents/ (RFC-0012)
429
432
  npx skilldrop-cli install --panel review # the whole review fleet: 3 subagents + the pre-merge-review orchestrator (RFC-0020)
@@ -432,7 +435,7 @@ npx skilldrop-cli list | skilldrop info <skill> | skilldrop packs | skilldrop un
432
435
  npx skilldrop-cli list --json # machine-readable: list/info/packs/agents/outdated (RFC-0021)
433
436
  ```
434
437
 
435
- `--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).
438
+ `--loop` installs a loop as an invokable skill (its `LOOP.md` is already `SKILL.md`-shaped) plus the skills its stages name — add `--no-skills` for the loop alone, which still runs because each stage degrades through its declared `fallback`. `--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).
436
439
 
437
440
  ### Or: the Claude Code plugin marketplace
438
441
 
@@ -460,16 +463,18 @@ The six **pack plugins** work differently, because a plugin's skills have to sit
460
463
 
461
464
  `.claude-plugin/{marketplace,plugin}.json` are generated from `package.json` + `packs.json` by [`build_marketplace.py`](build_marketplace.py) (`--check` guards drift in CI; `--dist` renders the branch). Use the CLI above when you want per-skill granularity, another IDE, or hooks; use the marketplace when you're in Claude Code. Rationale: [RFC-0027](docs/rfcs/0027-retire-agentbundle-export.md).
462
465
 
463
- ### Hooks (opt-in) — wire a skill to an event
466
+ ### Everything else about installing
464
467
 
465
- Some loop-shaped skills declare **hooks** — event-triggered nudges the CLI wires into your environment when you pass `--with-hooks` ([RFC-0006](docs/rfcs/0006-per-ide-hooks.md)). It's off by default, so a plain install never touches your git repo or editor settings.
468
+ The long-form install material moved to [`guides/`](guides/) so this page stays scannable:
466
469
 
467
- ```bash
468
- npx skilldrop-cli install devils-advocate --with-hooks --project
469
- # → appends a marker-fenced reminder to .git/hooks/pre-commit: "run /devils-advocate on staged changes"
470
- ```
471
-
472
- The CLI emits per target and **degrades gracefully** — a `pre-commit-review` hook becomes an IDE-agnostic git hook (needs a git repo); a `session-start` hook becomes a Claude Code `settings.json` entry, and is cleanly skipped where the target has no equivalent (Cursor, Kiro, plain `--dest`), printing what it did and where. Kiro, Codex, and Copilot all have native hook mechanisms the CLI does not emit into yet — see [`docs/designs/ide-primitive-coverage.md`](docs/designs/ide-primitive-coverage.md) for the per-tool survey. Hooks are reminders/context, not autonomous execution — skilldrop skills are agent instructions, so the hook prompts *you* to run the review, it doesn't silently run an AI pass. `skilldrop uninstall` removes any hook artifacts it wrote. Vocabulary and the per-target mapping are in the RFC.
470
+ | If you want to | Read |
471
+ |---|---|
472
+ | Install by hand into a specific IDE | [Install a skill into your IDE](guides/how-to/install-per-ide.md) |
473
+ | Wire a skill to an event (opt-in hooks) | [Wire a skill to an event](guides/how-to/wire-a-hook.md) |
474
+ | Publish your own catalogue for `--from` | [Publish your own catalogue](guides/how-to/publish-a-catalogue.md) |
475
+ | Use the two skills that ship scripts | [Skills that ship scripts](guides/reference/skills-with-scripts.md) |
476
+ | Add a skill or a loop to this repo | [Author a skill](guides/how-to/author-a-skill.md) · [Author a loop](guides/how-to/author-a-loop.md) |
477
+ | Understand why it is built this way | [Why loops](guides/explanation/loops.md) · [ARCHITECTURE.md](ARCHITECTURE.md) |
473
478
 
474
479
  ### Reviewer subagents
475
480
 
@@ -500,145 +505,6 @@ Each generated target omits the permission field it cannot map safely rather tha
500
505
 
501
506
  **Every surveyed tool now installs.** Only Cursor is absent, because it has no agent file format at all — use a custom mode ([`agents/README.md`](agents/README.md) has the steps).
502
507
 
503
- ### Third-party catalogs — publish your own skills through the same CLI
504
-
505
- Any git repo or directory shaped like this one is a **catalog**: `skills/<name>/` folders each holding `SKILL.md` + `manifest.json`, optionally a root `packs.json`. That's the whole contract ([RFC-0003](docs/rfcs/0003-third-party-catalogs.md)):
506
-
507
- ```bash
508
- npx skilldrop-cli list --from https://github.com/you/your-skills
509
- npx skilldrop-cli install my-skill --from https://github.com/you/your-skills#v1.2 # #ref pins a branch/tag
510
- npx skilldrop-cli install --pack starter --from ../local-catalog
511
- npx skilldrop-cli update # updates bundled and third-party skills side by side — the ledger remembers each skill's source
512
- ```
513
-
514
- The CLI also reads **agentbundle-shaped catalogs** ([agent-ready-repo](https://github.com/eugenelim/agent-ready-repo)) — `packs/<pack>/.apm/skills/<name>/SKILL.md` with a `pack.toml` per pack — so you can install *its* packs through the same command ([RFC-0014](docs/rfcs/0014-agentbundle-interop.md)). This is one-directional by design: skilldrop reads his shape, and no longer publishes a generated catalogue back into it ([RFC-0027](docs/rfcs/0027-retire-agentbundle-export.md)). Both shapes share the agentskills.io `SKILL.md`, so the reader just maps his packs onto the accessors above:
515
-
516
- ```bash
517
- npx skilldrop-cli packs --from https://github.com/eugenelim/agent-ready-repo
518
- npx skilldrop-cli install --pack contracts --from https://github.com/eugenelim/agent-ready-repo --dest .agents/skills
519
- ```
520
-
521
- Safety model: installs **copy files only — nothing from a catalog is ever executed**; every skill passes a structural check before copying (broken folders are refused with reasons); and third-party installs print a review-before-use warning, because skills are instructions your AI agent will follow — read a stranger's `SKILL.md` before letting your agent obey it.
522
-
523
- **Authoring a catalog:** mirror the layout above, then check it with `npx skilldrop-cli validate --from <your-repo-or-path>` before publishing. `related`, `packs.json`, and `requirements.txt` all work in third-party catalogs exactly as they do here.
524
-
525
- ### Manual install
526
-
527
- Each skill is a plain directory. Installation is always the same two steps: (1) copy the skill folder into your IDE's skills/rules location, then (2) install the skill's dependencies (the commands are in `manifest.json` under `deps`, or run the install line from the skill's SKILL.md). Optionally, also copy the companions listed under `related` in the skill's `manifest.json` — skills reference each other, and while a hand-off to an uninstalled sibling degrades gracefully to inline guidance, the pipelines work best complete.
528
-
529
- ### Claude Code
530
-
531
- Claude Code reads skills from two locations:
532
-
533
- - **User-scope** (available in every project): `~/.claude/skills/<skill-name>/`
534
- - **Project-scope** (tracked with the repo): `<project>/.claude/skills/<skill-name>/`
535
-
536
- Install a skill by copying its folder — drop the directory directly into the skills location, **not** its parent category folder:
537
-
538
- ```bash
539
- # user-scope (recommended for personal use)
540
- mkdir -p ~/.claude/skills
541
- cp -R skills/architecture-diagrams ~/.claude/skills/
542
- cp -R skills/figma-diagrams ~/.claude/skills/
543
-
544
- # project-scope (recommended when sharing with a team)
545
- mkdir -p .claude/skills
546
- cp -R skills/architecture-diagrams .claude/skills/
547
- cp -R skills/figma-diagrams .claude/skills/
548
- ```
549
-
550
- Claude Code discovers the skill via its `SKILL.md` frontmatter `name` field. Invoke it in chat with `/<skill-name>` or by describing the task — Claude will route to the matching skill automatically.
551
-
552
- ### Cursor
553
-
554
- Cursor does not have a native "skills" concept, but you can install a skill as a **project rule**:
555
-
556
- 1. Copy the skill folder somewhere in the repo (e.g. `.cursor/skills/<skill-name>/`):
557
- ```bash
558
- mkdir -p .cursor/skills
559
- cp -R skills/architecture-diagrams .cursor/skills/
560
- ```
561
-
562
- 2. Create `.cursor/rules/<skill-name>.mdc` that points Cursor at it:
563
- ```markdown
564
- ---
565
- description: <paste the skill's description from manifest.json>
566
- globs:
567
- alwaysApply: false
568
- ---
569
- Follow the instructions in .cursor/skills/<skill-name>/SKILL.md when the user requests this task.
570
- ```
571
-
572
- 3. In chat, attach `SKILL.md` with `@` or simply describe the task — the rule will fire when the description matches.
573
-
574
- ### Kiro (IDE and CLI)
575
-
576
- Kiro has native **Agent Skills**, and Kiro IDE and Kiro CLI read the same directories. Copy the folder in — that's the whole install:
577
-
578
- ```bash
579
- mkdir -p .kiro/skills # workspace scope
580
- cp -R skills/figma-diagrams .kiro/skills/
581
-
582
- mkdir -p ~/.kiro/skills # global scope, every project
583
- cp -R skills/figma-diagrams ~/.kiro/skills/
584
- ```
585
-
586
- Kiro matches the skill by its `SKILL.md` frontmatter `name` (which must equal the folder name) and `description` — the same contract every other tool uses.
587
-
588
- **No steering file needed.** Earlier versions of the CLI also wrote `.kiro/steering/<skill-name>.md` pointing back at the skill. That predates native Agent Skills, and because a steering file without frontmatter is *always* loaded, it pinned one description per installed skill into every session's context — to point at a folder Kiro already reads. The CLI no longer writes them, and `install`/`uninstall` remove any it wrote before. A steering file it didn't author is left alone, with a note.
589
-
590
- ### Codex and GitHub Copilot
591
-
592
- Both read `SKILL.md` folders, and both deliberately read *other* tools' directories — so a skilldrop install often already works with no extra step:
593
-
594
- | Path | Read by |
595
- |---|---|
596
- | `.claude/skills/` | Claude Code, **Copilot CLI** |
597
- | `.agents/skills/` | **Codex** (project), **Copilot CLI** |
598
- | `.github/skills/` | **Copilot** |
599
- | `~/.codex/skills/` | Codex (personal) |
600
- | `~/.copilot/skills/` | Copilot (personal) |
601
-
602
- **If you already ran `skilldrop install --project`, Copilot CLI can use every skill you installed** — `.claude/skills/` is one of its discovery paths. Otherwise pick the path your tool reads:
603
-
604
- ```bash
605
- npx skilldrop-cli install --pack dev-team --dest .agents/skills # Codex + Copilot CLI
606
- npx skilldrop-cli install --pack dev-team --dest .github/skills # Copilot
607
- npx skilldrop-cli install --pack dev-team --dest ~/.codex/skills # Codex, all projects
608
- ```
609
-
610
- There is no `--ide codex` or `--ide copilot` flag yet, and `--dest` is not a workaround here — it writes the identical folder the native flags would. Both tools also read a repo-root `AGENTS.md`, which this repo has.
611
-
612
- ### Continue, Cline, Aider, and other agents
613
-
614
- These tools don't have a standard skills directory yet. Two patterns work:
615
-
616
- - **Context attachment.** Copy the skill folder anywhere in the repo, then attach `SKILL.md` to your prompt (Continue: `@file`, Cline: `@file`, Aider: `/add <path>`) and tell the agent to follow it.
617
- - **Custom prompt / agent.** Paste `SKILL.md` into the IDE's custom-agent or system-prompt configuration. The skill's `manifest.json` `description` field is a good seed for the agent's name/summary.
618
-
619
- In all cases, the scripts are invoked from the **copied** folder, so keep the directory structure intact — don't flatten `scripts/` or `templates/` out of the skill folder.
620
-
621
- ### VS Code (Continue / Cline extensions)
622
-
623
- These behave like the "Other agents" path above. For Continue, you can also add the skill folder to `.continue/config.json` under `contextProviders` so `SKILL.md` shows up in `@` suggestions.
624
-
625
- ## Installing dependencies
626
-
627
- Each skill declares its deps in `manifest.json`:
628
-
629
- - **`deps.npm`** → run `npm install <packages>` before using the skill (or let `SKILL.md` step 1 install them on demand).
630
- - **`deps.pip`** → run `python3 -m pip install -r <skill>/requirements.txt`.
631
-
632
- Per-skill quick reference:
633
-
634
- | Skill | Install command (run from inside the copied skill folder) |
635
- |---|---|
636
- | `figma-diagrams` | `python3 -m pip install -r requirements.txt` + `export FIGMA_TOKEN=figd_...` |
637
- | `deck-builder` | `python3 -m pip install -r requirements.txt` (installs `python-pptx`) |
638
- | _all other skills_ | _no runtime deps — pure markdown skills_ |
639
-
640
- For `figma-diagrams`, you also need a [Figma Personal Access Token](https://www.figma.com/developers/api#access-tokens) exported as the `FIGMA_TOKEN` env var.
641
-
642
508
  ## Reviewer agents
643
509
 
644
510
  The [`agents/`](agents/) folder ships **portable reviewer personas** — single-file agents you delegate code and test review to, packaged in Claude Code subagent format (frontmatter + system prompt) but written so the body pastes into Cursor, Codex, Kiro, Continue, Cline, or Aider just as cleanly.
@@ -647,65 +513,10 @@ The [`agents/`](agents/) folder ships **portable reviewer personas** — single-
647
513
  |---|---|---|
648
514
  | [`devils-advocate`](agents/devils-advocate.md) | Correctness — edge cases, broken assumptions, staff-engineer pushback, test gaps | "Will this break?" |
649
515
  | [`code-quality`](agents/code-quality.md) | Craft — naming, structure, duplication, complexity, readability | "Will the next engineer hate this?" |
650
-
651
516
  | [`security-reviewer`](agents/security-reviewer.md) | Exploitability — injection, authz gaps, secret handling, unsafe deserialisation | "How would someone abuse this?" |
652
517
 
653
518
  No tool auto-discovers a folder named `agents/`; it's the canonical source of truth. See [`agents/README.md`](agents/README.md) for where to copy each file in your tool — `.claude/agents/` for Claude Code, a custom mode for Cursor, an `AGENTS.md` section for Codex, a steering file or custom agent for Kiro.
654
519
 
655
- ## Skill Usage
656
-
657
- All skills are invoked in chat. Arguments are passed as plain text after the skill's trigger phrase (or via `$ARGUMENTS` when invoked as a slash command in Claude Code).
658
-
659
- ### `architecture-diagrams`
660
-
661
- Natural-language trigger (works in any IDE that has the skill installed):
662
-
663
- > Draw me a Mermaid diagram of a three-tier web app on AWS with an ALB, two ECS services, and an RDS Postgres backend.
664
-
665
- Slash-command form (Claude Code):
666
-
667
- ```
668
- /architecture-diagrams three-tier web app on AWS with ALB, two ECS services, RDS Postgres
669
- ```
670
-
671
- Everything after the slash command becomes `$ARGUMENTS` inside the skill.
672
-
673
- ### `figma-diagrams`
674
-
675
- Natural-language trigger:
676
-
677
- > Inspect the structure of this Figma file: https://figma.com/file/abc123/MyArchitecture
678
-
679
- Slash-command form (Claude Code):
680
-
681
- ```
682
- /figma-diagrams inspect https://figma.com/file/abc123/MyArchitecture
683
- /figma-diagrams post-comment https://figma.com/file/abc123/MyArchitecture "Looks good — ship it."
684
- ```
685
-
686
- The skill parses `$ARGUMENTS` to figure out which Figma URL you mean and which action to take.
687
-
688
- ## Adding a new skill
689
-
690
- Full contributor guide — the three lanes, the PR gates, and the release flow — is in [CONTRIBUTING.md](CONTRIBUTING.md). The short version:
691
-
692
- 0. Write a one-page RFC first — copy [`docs/rfcs/0000-template.md`](docs/rfcs/0000-template.md) to `docs/rfcs/NNNN-<slug>.md` and record the problem, the fit check, and the alternatives. New skills and structural changes need one; fixes to existing skills don't.
693
- 1. Create `skills/<your-skill>/SKILL.md` with this frontmatter:
694
- ```yaml
695
- ---
696
- name: your-skill
697
- description: One sentence, use-case-first. AI agents match this against user prompts to decide when to invoke.
698
- ---
699
- ```
700
- 2. Add `skills/<your-skill>/manifest.json` with the same `name` + `description` plus declared `deps` and required env vars — this is what makes the skill portable across IDEs.
701
- 3. Keep `SKILL.md` short (under ~500 lines). Move long reference material into sibling files like `reference.md`, `examples.md`, or `templates/`.
702
- 4. If your skill needs scripts, drop them in `scripts/` and reference them with a path relative to the skill folder — **avoid hard-coding `${CLAUDE_SKILL_DIR}` only**; show both paths so non–Claude-Code users aren't stuck.
703
- 5. Add an `evals/` folder: `evals.json` (at least one realistic prompt with a list of assertions the output must satisfy) and `eval_queries.json` (phrases that should and should **not** trigger the skill). These double as the checklist for the manual test pass and keep the `description` honest about when the skill fires.
704
- 6. Add an entry to the **Skills in this repo** table above and to the **Installing dependencies** table.
705
- 7. Add the skill to at least one pack **and** at least one outcome in `packs.json`.
706
- 8. If the change is going out in a release, add a bullet to [`CHANGELOG.md`](CHANGELOG.md) under the new version — the site build fails without one.
707
- 9. Run `python3 validate.py` from the repo root — it checks name consistency, the tier sync with `model-routing.json`, the `related`↔SKILL.md reference sync, description sync, pack and outcome membership, and eval file shape.
708
-
709
520
  ## License
710
521
 
711
522
  MIT — see [LICENSE](LICENSE).
package/bin/skilldrop.js CHANGED
@@ -37,16 +37,20 @@ Usage:
37
37
  skilldrop info <skill> [--from <src>] description, related, packs, deps
38
38
  skilldrop packs [--from <src>] role-based packs
39
39
  skilldrop agents [--from <src>] reviewer subagents in a catalog
40
+ skilldrop loops [--from <src>] loops — sequenced stages over skills (RFC-0028)
40
41
  skilldrop install <skill...> install skills (default: Claude Code, user scope)
41
42
  skilldrop install --pack <name> install a whole pack
42
43
  skilldrop install --all install every skill in the catalog
43
44
  skilldrop install --agent <name...> install reviewer subagents (RFC-0012)
45
+ skilldrop install --loop <name...> install loops + the stage skills they sequence
46
+ (--no-skills for the loop alone) (RFC-0028)
44
47
  skilldrop install --panel review install the review panel — the 3 reviewer subagents +
45
48
  the pre-merge-review orchestrator that fires them (RFC-0020)
46
49
  skilldrop update re-copy installed skills whose version changed
47
50
  skilldrop outdated show installed vs current versions, change nothing
48
51
  skilldrop uninstall <skill...> remove skills (and wiring files this tool wrote)
49
52
  skilldrop uninstall --agent <name...> remove subagents
53
+ skilldrop uninstall --loop <name...> remove loops (stage skills are left in place)
50
54
  skilldrop validate [--from <src>] structural check of a catalog (for catalog authors)
51
55
  skilldrop scan [<skill...>] [--from <src>] supply-chain scan — flag network/exec/credential
52
56
  patterns in scripts and injection-shaped instructions
@@ -740,6 +744,7 @@ function installPanel(args) {
740
744
 
741
745
  function install(args) {
742
746
  if (args.flags.panel) return installPanel(args);
747
+ if (args.flags.loop) return installLoops(args);
743
748
  if (args.flags.agent) return installAgents(args);
744
749
  const cat = resolveCatalog(args.flags.from);
745
750
  let names = expandNames(args, cat);
@@ -846,6 +851,7 @@ function outdated(args) {
846
851
 
847
852
  function uninstall(args) {
848
853
  if (args.flags.agent) return uninstallAgents(args);
854
+ if (args.flags.loop) return uninstallLoops(args);
849
855
  if (!args._.length) die("pass skill names to uninstall");
850
856
  const { dest, ide } = target(args.flags);
851
857
  const l = ledger(dest);
@@ -881,6 +887,136 @@ function listAgents(args) {
881
887
  console.log(`\n${names.length} agent(s). Install one: skilldrop install --agent <name>`);
882
888
  }
883
889
 
890
+ /* Loops (RFC-0028): an ordered sequence of stages over existing skills, with a gate between
891
+ them. LOOP.md's frontmatter is already SKILL.md's shape (name + description), so a loop
892
+ projects into the target's skill directory as <name>/SKILL.md and becomes invokable exactly
893
+ like a skill. loop.json travels beside it so stages, gates and cap stay machine-readable
894
+ after install. A loop never contains a skill — installing one also installs the skills its
895
+ stages name, unless --no-skills. Optional in a catalog. */
896
+ function loopsIn(cat) {
897
+ if (cat.shape === "apm") return [];
898
+ const d = path.join(cat.dir, "loops");
899
+ if (!fs.existsSync(d)) return [];
900
+ return fs.readdirSync(d).filter((x) => fs.existsSync(path.join(d, x, "loop.json"))).sort();
901
+ }
902
+ function loopDir(cat, n) { return path.join(cat.dir, "loops", n); }
903
+ function loopSpec(cat, n) { return readJSON(path.join(loopDir(cat, n), "loop.json")); }
904
+
905
+ function loopSkills(cat, n) {
906
+ const seen = [];
907
+ for (const st of loopSpec(cat, n).stages || [])
908
+ for (const sk of st.skills || [])
909
+ if (sk !== "*" && !seen.includes(sk)) seen.push(sk);
910
+ return seen;
911
+ }
912
+
913
+ function checkLoop(cat, n) {
914
+ const problems = [];
915
+ if (!fs.existsSync(path.join(loopDir(cat, n), "LOOP.md"))) problems.push("no LOOP.md — it is the entrypoint");
916
+ let spec;
917
+ try { spec = loopSpec(cat, n); } catch (e) { return problems.concat("loop.json is missing or unparseable"); }
918
+ if (spec.name !== n) problems.push(`loop.json name '${spec.name}' != folder '${n}'`);
919
+ if (!Array.isArray(spec.stages) || !spec.stages.length) problems.push("no stages — a loop with none sequences nothing");
920
+ for (const sk of loopSkills(cat, n))
921
+ if (!skillExists(cat, sk)) problems.push(`stage names skill '${sk}', which is not in this catalog`);
922
+ return problems;
923
+ }
924
+
925
+ function listLoops(args) {
926
+ const cat = resolveCatalog(args.flags.from);
927
+ const names = loopsIn(cat);
928
+ if (args.flags.json)
929
+ return emitJSON({
930
+ catalog: cat.source, count: names.length,
931
+ loops: names.map((n) => {
932
+ const sp = loopSpec(cat, n);
933
+ return { name: n, kind: sp.kind, cap: sp.cap ?? 3, description: sp.description,
934
+ stages: (sp.stages || []).map((st) => ({ id: st.id, type: st.type, skills: st.skills,
935
+ gate: st.gate ? { id: st.gate.id, kind: st.gate.kind } : null })) };
936
+ }),
937
+ });
938
+ if (!names.length) return console.log(`catalog '${cat.source}' ships no loops.`);
939
+ for (const n of names) {
940
+ const sp = loopSpec(cat, n);
941
+ const gates = (sp.stages || []).filter((st) => st.gate).map((st) => `${st.gate.id}:${st.gate.kind}`);
942
+ console.log(`${n} [${sp.kind}, cap ${sp.cap ?? 3}]`);
943
+ console.log(` ${(sp.stages || []).map((st) => st.id).join(" -> ")}`);
944
+ console.log(` gates: ${gates.join(", ") || "none"}`);
945
+ }
946
+ console.log(`\n${names.length} loop(s). Install one: skilldrop install --loop <name>`);
947
+ }
948
+
949
+ function installLoops(args) {
950
+ const cat = resolveCatalog(args.flags.from);
951
+ const available = loopsIn(cat);
952
+ if (!available.length) die(`catalog '${cat.source}' ships no loops`);
953
+ let names = args._.slice();
954
+ if (!names.length && args.flags.pack) {
955
+ const ps = packsOf(cat);
956
+ if (!ps) die(`catalog '${cat.source}' has no packs.json`);
957
+ const pk = ps[args.flags.pack];
958
+ if (!pk) die(`unknown pack '${args.flags.pack}' in catalog '${cat.source}'`);
959
+ names = (pk.loops || []).slice();
960
+ if (!names.length) die(`pack '${args.flags.pack}' declares no loops`);
961
+ }
962
+ if (!names.length && args.flags.all) names = available.slice();
963
+ if (!names.length) die("nothing to install — pass loop names, --pack <name>, or --all");
964
+ for (const n of names) if (!available.includes(n)) die(`unknown loop '${n}' in catalog '${cat.source}'`);
965
+
966
+ let bad = 0;
967
+ for (const n of names) {
968
+ const problems = checkLoop(cat, n);
969
+ for (const pr of problems) console.error(`refused ${n}: ${pr}`);
970
+ if (problems.length) bad++;
971
+ }
972
+ if (bad) die(`${bad} loop(s) failed the structural check — nothing was installed`);
973
+
974
+ const { dest, ide } = target(args.flags);
975
+ fs.mkdirSync(dest, { recursive: true });
976
+ const l = ledger(dest);
977
+ const notes = [];
978
+ for (const n of names) {
979
+ const sp = loopSpec(cat, n);
980
+ const out = path.join(dest, n);
981
+ fs.mkdirSync(out, { recursive: true });
982
+ fs.copyFileSync(path.join(loopDir(cat, n), "LOOP.md"), path.join(out, "SKILL.md"));
983
+ fs.copyFileSync(path.join(loopDir(cat, n), "loop.json"), path.join(out, "loop.json"));
984
+ const note = writeWiring(ide, dest, n, sp.description);
985
+ if (note) notes.push(note);
986
+ l.data[n] = { version: sp.version || null, source: cat.source, loop: true };
987
+ console.log(`installed loop ${n} -> ${out}`);
988
+ }
989
+ saveLedger(l);
990
+
991
+ const wanted = [];
992
+ for (const n of names) for (const sk of loopSkills(cat, n)) if (!wanted.includes(sk)) wanted.push(sk);
993
+ if (args.flags["no-skills"]) {
994
+ console.log(`\n${names.length} loop(s) installed (${ide}). Skipped ${wanted.length} stage skill(s) (--no-skills).`);
995
+ console.log(`A loop whose stage skills are absent still runs — each stage degrades via its declared fallback.`);
996
+ } else if (wanted.length) {
997
+ console.log(`\nInstalling ${wanted.length} stage skill(s) the loop sequences.\n`);
998
+ const flags = Object.assign({}, args.flags);
999
+ delete flags.loop; delete flags.pack; delete flags.all;
1000
+ install({ _: wanted, flags });
1001
+ }
1002
+ if (notes.length) console.log(`\n${notes.join("\n")}`);
1003
+ console.log(`\nRun a loop by name — e.g. "run the ${names[0]} loop on this". Its SKILL.md is the script; honour each gate and the cap.`);
1004
+ }
1005
+
1006
+ function uninstallLoops(args) {
1007
+ if (!args._.length) die("pass loop names to uninstall");
1008
+ const { dest, ide } = target(args.flags);
1009
+ const l = ledger(dest);
1010
+ for (const n of args._) {
1011
+ fs.rmSync(path.join(dest, n), { recursive: true, force: true });
1012
+ const w = wiringPath(ide, dest, n);
1013
+ if (w) fs.rmSync(w, { force: true });
1014
+ delete l.data[n];
1015
+ console.log(`removed loop ${n} from ${dest} (stage skills left in place)`);
1016
+ }
1017
+ saveLedger(l);
1018
+ }
1019
+
884
1020
  function installAgents(args) {
885
1021
  const cat = resolveCatalog(args.flags.from);
886
1022
  const available = agentsIn(cat);
@@ -1019,7 +1155,7 @@ function validateCmd(args) {
1019
1155
 
1020
1156
  const args = parseArgs(process.argv.slice(2));
1021
1157
  const cmd = args._.shift();
1022
- const commands = { list, info, packs: listPacks, agents: listAgents, install, update, outdated, uninstall, validate: validateCmd, scan };
1158
+ const commands = { list, info, packs: listPacks, agents: listAgents, loops: listLoops, install, update, outdated, uninstall, validate: validateCmd, scan };
1023
1159
  if (!cmd || cmd === "help" || args.flags.help) console.log(HELP);
1024
1160
  else if (commands[cmd]) commands[cmd](args);
1025
1161
  else die(`unknown command '${cmd}' — run: skilldrop help`);
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/sananthanarayan/skilldrop/contracts/agent.schema.json",
4
+ "title": "skilldrop reviewer subagent frontmatter",
5
+ "$comment": "RFC-0012 / RFC-0029. agents/<name>.md is a single markdown file whose YAML frontmatter is already Claude Code's format; the body is the system prompt. The CLI projects this into each target's own shape (Kiro JSON, Codex TOML, Copilot .agent.md), so these four fields are the portable core.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["name", "description"],
9
+ "properties": {
10
+ "name": {
11
+ "type": "string",
12
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
13
+ "description": "Must equal the filename stem. A rename that misses one silently breaks every delegation pointing here."
14
+ },
15
+ "description": {
16
+ "type": "string",
17
+ "description": "What routes a delegation to this agent. Required — without it the agent is unreachable."
18
+ },
19
+ "tools": {
20
+ "type": "string",
21
+ "description": "Comma-separated tool names. Recommended; the CLI maps these per target and warns where a tool has no equivalent."
22
+ },
23
+ "model": {
24
+ "type": "string",
25
+ "description": "Recommended. A hint the target may honour or ignore."
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/sananthanarayan/skilldrop/contracts/guide.schema.json",
4
+ "title": "skilldrop guide frontmatter",
5
+ "$comment": "RFC-0029. Diátaxis `kind` is a PAGE CONTRACT, not a directory requirement — the directory is a convenience, the frontmatter is the truth. That keeps the tree shallow while still forcing each page to declare what kind of document it is, which is the discipline Diátaxis actually provides.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["title", "summary", "kind"],
9
+ "properties": {
10
+ "title": { "type": "string", "description": "Sentence case. Becomes the page's single h1." },
11
+ "summary": { "type": "string", "description": "One line, shown in the guides index." },
12
+ "kind": {
13
+ "enum": ["tutorial", "how-to", "reference", "explanation"],
14
+ "description": "tutorial = learning by doing. how-to = a task with a goal. reference = look it up. explanation = why it is this way. A page that cannot pick one is doing two jobs and should be split."
15
+ },
16
+ "status": { "enum": ["draft", "review", "stable", "deprecated"], "default": "stable" }
17
+ }
18
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/sananthanarayan/skilldrop/contracts/pack.schema.json",
4
+ "title": "skilldrop packs.json",
5
+ "$comment": "RFC-0001 / RFC-0026 / RFC-0029. Packs and outcomes are METADATA ONLY — skills never move out of flat skills/<name> folders. Packs answer WHO needs a skill; outcomes answer WHY someone opened the catalogue. Both get the same two-way membership check in validate.py.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["version", "packs", "outcomes"],
9
+ "properties": {
10
+ "$comment": { "type": "string" },
11
+ "version": { "type": "string" },
12
+ "packs": {
13
+ "type": "object",
14
+ "description": "Role-based bundles. Keys are pack names.",
15
+ "additionalProperties": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "required": ["description", "skills"],
19
+ "properties": {
20
+ "description": { "type": "string", "description": "One line — who this pack is for." },
21
+ "skills": { "type": "array", "items": { "type": "string" }, "minItems": 1,
22
+ "description": "Real skill folders. Every skill must appear in at least one pack." },
23
+ "loops": { "type": "array", "items": { "type": "string" },
24
+ "description": "RFC-0028. Real loops/ folders. Every loop must appear in at least one pack." }
25
+ }
26
+ }
27
+ },
28
+ "outcomes": {
29
+ "type": "object",
30
+ "description": "The catalogue's second browse axis. A browse aid only — never an install unit; the CLI takes no --outcome.",
31
+ "additionalProperties": {
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "required": ["description", "skills"],
35
+ "properties": {
36
+ "description": { "type": "string", "description": "The chip's tooltip on the catalogue site." },
37
+ "skills": { "type": "array", "items": { "type": "string" }, "minItems": 1 }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,95 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/sananthanarayan/skilldrop/contracts/skill.schema.json",
4
+ "title": "skilldrop skill manifest",
5
+ "$comment": "RFC-0029. This shape lived only as prose in AGENTS.md, so the one authoritative statement of what a manifest is was a paragraph a contributor had to read and obey. It is now data. Checked by hand-rolled stdlib code in validate.py — skilldrop ships zero runtime dependencies and this file must not become a reason to add `jsonschema`.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["name", "version", "description", "entrypoint", "deps", "env", "related", "tags", "model"],
9
+ "properties": {
10
+ "name": {
11
+ "type": "string",
12
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
13
+ "description": "Kebab-case. Must equal the folder name AND the SKILL.md frontmatter name — the triple-match in golden rule 1."
14
+ },
15
+ "version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
16
+ "description": {
17
+ "type": "string",
18
+ "description": "The most-read string in the skill. Use-case-first: what it does, then the trigger phrases an agent matches on. Must match the SKILL.md frontmatter description."
19
+ },
20
+ "entrypoint": { "const": "SKILL.md" },
21
+ "deps": {
22
+ "type": "object",
23
+ "additionalProperties": false,
24
+ "required": ["npm", "pip"],
25
+ "properties": {
26
+ "npm": { "type": "array", "items": { "type": "string" } },
27
+ "pip": { "type": "array", "items": { "type": "string" } }
28
+ }
29
+ },
30
+ "env": {
31
+ "type": "object",
32
+ "additionalProperties": false,
33
+ "required": ["required", "optional"],
34
+ "properties": {
35
+ "required": { "type": "array", "items": { "type": "string" },
36
+ "description": "Vars the skill cannot work without, e.g. FIGMA_TOKEN." },
37
+ "optional": { "type": "array", "items": { "type": "string" },
38
+ "description": "Vars that change behaviour but do not block." }
39
+ }
40
+ },
41
+ "related": {
42
+ "type": "array",
43
+ "items": { "type": "string" },
44
+ "description": "Undirected: every sibling this SKILL.md references, in either direction. validate.py enforces the sync both ways."
45
+ },
46
+ "handoff": {
47
+ "type": "array",
48
+ "description": "RFC-0028. The directed companion to `related`. Every `to` must also appear in `related`.",
49
+ "items": {
50
+ "type": "object",
51
+ "additionalProperties": false,
52
+ "required": ["to", "when", "purpose", "fallback"],
53
+ "properties": {
54
+ "to": { "type": "string", "description": "A skill folder name. Never this skill." },
55
+ "when": { "type": "string", "description": "The condition under which the hand-off fires." },
56
+ "purpose": { "type": "string", "description": "What the target does with what it receives." },
57
+ "fallback": { "type": "string", "description": "What to do when the target is not installed. Required — this is what makes an advisory hand-off degrade in a stated way instead of dead-ending." }
58
+ }
59
+ }
60
+ },
61
+ "tags": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
62
+ "model": {
63
+ "type": "object",
64
+ "additionalProperties": false,
65
+ "required": ["tier", "rationale"],
66
+ "properties": {
67
+ "tier": {
68
+ "enum": ["light", "standard", "heavy"],
69
+ "description": "Abstract and provider-neutral. Never a vendor model name — the provider map in model-routing.json is the only place concrete models live. Must agree with this skill's entry there."
70
+ },
71
+ "rationale": { "type": "string", "description": "One sentence: why this tier fits what the skill does." }
72
+ }
73
+ },
74
+ "install": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "description": "Human-readable install lines for a skill with runtime deps.",
78
+ "properties": { "npm": { "type": "string" }, "pip": { "type": "string" } }
79
+ },
80
+ "hooks": {
81
+ "type": "array",
82
+ "description": "RFC-0006. Only for loop-shaped skills that benefit from event automation; artifact generators carry none.",
83
+ "items": {
84
+ "type": "object",
85
+ "additionalProperties": false,
86
+ "required": ["event", "action", "description"],
87
+ "properties": {
88
+ "event": { "enum": ["session-start", "pre-commit-review", "on-demand"] },
89
+ "action": { "type": "string", "description": "A real skill folder this hook points at." },
90
+ "description": { "type": "string" }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skilldrop-cli",
3
- "version": "0.11.7",
3
+ "version": "0.11.9",
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",
package/packs.json CHANGED
@@ -4,6 +4,7 @@
4
4
  "packs": {
5
5
  "solution-architect": {
6
6
  "description": "Design-phase artifacts: diagrams, decision records, design docs, contracts, schemas, threat models, and the reviews that gate them.",
7
+ "loops": ["design", "ship-a-draft"],
7
8
  "skills": [
8
9
  "brief-intake",
9
10
  "architecture-diagrams",
@@ -26,6 +27,7 @@
26
27
  },
27
28
  "product-manager": {
28
29
  "description": "Direction to requirements: PR/FAQs, strategy frameworks, OKRs, PRDs, journey maps, story splitting, and success measurement.",
30
+ "loops": ["discover", "ship-a-draft"],
29
31
  "skills": [
30
32
  "brief-intake",
31
33
  "prfaq",
@@ -44,6 +46,7 @@
44
46
  },
45
47
  "dev-team": {
46
48
  "description": "Build-and-ship loop: story implementation with adversarial review, test plans, triage, migrations, release notes, agent-policy files, and code-quality gates.",
49
+ "loops": ["build", "ship-a-draft"],
47
50
  "skills": [
48
51
  "agents-md-generator",
49
52
  "user-story-splitter",
@@ -63,6 +66,7 @@
63
66
  },
64
67
  "sre-oncall": {
65
68
  "description": "Operate the service: runbooks, observability design, incident communications, postmortems, and capacity/cost models.",
69
+ "loops": ["operate"],
66
70
  "skills": [
67
71
  "runbook-generator",
68
72
  "observability-plan",
@@ -73,6 +77,7 @@
73
77
  },
74
78
  "stakeholder-comms": {
75
79
  "description": "Non-technical audiences: audience profiling, deck outlines and real .pptx decks, exec summaries, decision logs, and guides.",
80
+ "loops": ["ship-a-draft"],
76
81
  "skills": [
77
82
  "brief-intake",
78
83
  "audience-profile",
@@ -87,6 +92,7 @@
87
92
  },
88
93
  "ai-engineering": {
89
94
  "description": "Adopt and build AI systems: readiness assessment, use-case triage, rollout and acceptable-use policy, plus agent loop design, subagent orchestration, spend budgets, agent security review, eval harnesses, AI-usage reporting, and the data contracts AI pipelines depend on.",
95
+ "loops": ["design", "build"],
90
96
  "skills": [
91
97
  "agent-loop-design",
92
98
  "subagent-design",