skilldrop-cli 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -285,7 +285,21 @@ npx skilldrop-cli install --agent code-quality --project # .claude/agents/, s
285
285
 
286
286
  Then delegate by name: *"use the devils-advocate agent on this diff."*
287
287
 
288
- Only **plain-copy targets** ship today — Claude Code (whose format the file already is) and `--dest <dir>`. Copilot needs a filename change, Kiro a generated JSON wrapper, Codex a schema the survey never confirmed; each of those is a projection waiting on [RFC-0010](docs/rfcs/0010-install-target-table.md), and the CLI says so rather than guessing a path. [`agents/README.md`](agents/README.md) has the manual route for all of them.
288
+ Four targets, each projecting only as much as the tool's format demands:
289
+
290
+ | Target | Writes | Projection |
291
+ |---|---|---|
292
+ | *(default)* | `~/.claude/agents/<name>.md` | none — the file already is Claude Code's format |
293
+ | `--ide copilot` | `.github/agents/<name>.agent.md` | a rename |
294
+ | `--ide kiro` | `.kiro/agents/<name>.json` | generated JSON; tool names mapped to Kiro's built-ins |
295
+ | `--ide codex` | `~/.codex/agents/<name>.toml` (`--project` for repo) | generated TOML |
296
+ | `--dest <dir>` | `<dir>/<name>.md` | none |
297
+
298
+ The Kiro emitter maps `Read`/`Grep`/`Glob`/`Bash` to `read`/`grep`/`glob`/`shell` against [Kiro's built-in tool reference](https://kiro.dev/docs/cli/reference/built-in-tools/), and **names any tool it can't map instead of dropping it silently** — a mistranslated permission is worse than a missing one. It omits `allowedTools` so you're prompted per tool call.
299
+
300
+ Each generated target omits the permission field it cannot map safely rather than guessing one: Kiro's `allowedTools` and Codex's `sandbox_mode` are both left unset, so an agent inherits the session's permissions and is prompted per call. A widened permission nobody asked for is worse than an extra prompt.
301
+
302
+ Only **Antigravity** still refuses — its subagents exist solely inside a plugin bundle, which is a container rather than a file drop. Design is worked out in [RFC-0013](docs/rfcs/0013-antigravity-plugin-bundle.md), unimplemented pending demand. [`agents/README.md`](agents/README.md) has the manual route.
289
303
 
290
304
  ### Third-party catalogs — publish your own skills through the same CLI
291
305
 
package/agents/README.md CHANGED
@@ -40,9 +40,11 @@ Cursor has no subagent file format. Two options:
40
40
 
41
41
  ### Codex (native subagents)
42
42
 
43
- Codex shipped subagents with GPT-5.5 in April 2026 — `.toml` agent definitions under `.codex/`. That superseded the old advice here, which was to paste the body into a section of `AGENTS.md` because Codex "has no subagent concept."
43
+ ```bash
44
+ npx skilldrop-cli install --agent devils-advocate --ide codex # ~/.codex/agents/<name>.toml
45
+ ```
44
46
 
45
- The exact filename and schema were **not confirmed** in the survey, so check Codex's own docs rather than trusting a shape written here. The fallback still works: keep the file at `agents/<name>.md` and open the review turn with "Follow the instructions in `agents/<name>.md` for the diff below."
47
+ Standalone TOML in `~/.codex/agents/` or `.codex/agents/` (`--project`). Codex requires `name`, `description`, and `developer_instructions`, and **rejects unknown fields** — so the emitter writes exactly those three. It leaves `sandbox_mode` unset, which means the agent inherits the parent session's permissions; Codex has no `tools` field, and guessing a sandbox value would either break the agent or silently widen what it can do.
46
48
 
47
49
  ### Kiro (native custom agents)
48
50
 
package/bin/skilldrop.js CHANGED
@@ -49,9 +49,13 @@ Install/update/uninstall targets (pick one):
49
49
  .github/skills (Copilot), Continue / Cline / Aider
50
50
 
51
51
  Options:
52
- --agent operate on subagents instead of skills. Plain-copy targets only:
53
- Claude Code (~/.claude/agents, --project for repo scope) or --dest.
54
- Copilot/Kiro/Codex need a projection — see agents/README.md.
52
+ --agent operate on subagents instead of skills. Targets:
53
+ (default) ~/.claude/agents Claude Code (--project for repo scope)
54
+ --ide kiro ./.kiro/agents generated JSON, tools mapped
55
+ --ide copilot ./.github/agents <name>.agent.md
56
+ --ide codex ~/.codex/agents generated TOML (--project for repo)
57
+ --dest <dir> any directory copied as-is
58
+ Antigravity needs a plugin bundle (RFC-0013) — see agents/README.md.
55
59
  --with-related also install each skill's related companions (one level)
56
60
  --with-hooks also wire any hooks a skill declares (RFC-0006) — git pre-commit
57
61
  reminders and Claude Code session-start context; degrades where the
@@ -175,11 +179,47 @@ function target(flags) {
175
179
  die(`unknown --ide '${ide}' (claude | cursor | kiro; use --dest for anything else)`);
176
180
  }
177
181
 
182
+ /* Claude Code tool name -> Kiro built-in tool name. Confirmed against
183
+ kiro.dev/docs/cli/reference/built-in-tools/ — not inferred. Agent configs use these
184
+ simplified names; Kiro's *hook* matchers use internal names (fs_read, execute_bash)
185
+ instead, and mixing the two silently grants nothing. */
186
+ const KIRO_TOOLS = {
187
+ Read: "read", Grep: "grep", Glob: "glob", Bash: "shell",
188
+ Write: "write", Edit: "write", WebSearch: "web_search", WebFetch: "web_fetch",
189
+ };
190
+
191
+ function kiroAgentJSON(meta, body) {
192
+ const declared = meta.tools ? meta.tools.split(",").map((s) => s.trim()).filter(Boolean) : [];
193
+ const tools = [], unmapped = [];
194
+ for (const d of declared) (KIRO_TOOLS[d] ? tools : unmapped).push(KIRO_TOOLS[d] || d);
195
+ // The prompt is inlined rather than a file:// reference: the docs' example is
196
+ // relative, but whether it resolves against the workspace root or the JSON file is
197
+ // not stated, and a wrong path fails silently. One self-contained file cannot.
198
+ const agent = { name: meta.name, description: meta.description, prompt: body, tools };
199
+ // allowedTools is deliberately omitted — it is auto-approval, so leaving it out means
200
+ // the user is prompted per tool call. Also sidesteps kirodotdev/Kiro#6714, where
201
+ // allowedTools does not load as configured.
202
+ return { json: JSON.stringify(agent, null, 2) + "\n", unmapped: [...new Set(unmapped)] };
203
+ }
204
+
205
+ /* Codex agent TOML. Schema confirmed at learn.chatgpt.com/docs/agent-configuration/subagents:
206
+ name, description and developer_instructions are required; unknown fields are rejected.
207
+ sandbox_mode is deliberately NOT emitted — Codex has no `tools` field, permissions come
208
+ from sandbox_mode, and its permitted values are unconfirmed. Omitting it inherits the
209
+ parent session, which is the only behaviour that can neither break the agent nor
210
+ silently widen what it may do. */
211
+ function codexAgentTOML(meta, body) {
212
+ const esc = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
213
+ const safe = body.includes('"""') ? body.replace(/"""/g, '\\"\\"\\"') : body;
214
+ return `name = "${esc(meta.name)}"\n` +
215
+ `description = "${esc(meta.description)}"\n` +
216
+ `developer_instructions = """\n${safe}\n"""\n`;
217
+ }
218
+
178
219
  /* Only plain-copy targets ship in RFC-0012. Everything else needs a projection the
179
220
  install-target model (RFC-0010) has not settled — so say so instead of guessing a path. */
180
221
  const AGENT_MANUAL = {
181
- copilot: ".github/agents/<name>.agent.md (rename needed)",
182
- kiro: ".kiro/agents/<name>.json (JSON wrapper around a file:// prompt)",
222
+ antigravity: "an agents/ directory inside a plugin bundle you own — see RFC-0013",
183
223
  cursor: "a custom mode — Cursor has no agent file format",
184
224
  };
185
225
  function agentTarget(flags) {
@@ -189,11 +229,42 @@ function agentTarget(flags) {
189
229
  return flags.project
190
230
  ? { dest: path.resolve(".claude", "agents"), ide }
191
231
  : { dest: path.join(os.homedir(), ".claude", "agents"), ide };
232
+ // Kiro and Copilot agent dirs are repo-relative by convention, so --project is implied.
233
+ if (ide === "kiro") return { dest: path.resolve(".kiro", "agents"), ide };
234
+ if (ide === "copilot") return { dest: path.resolve(".github", "agents"), ide };
235
+ if (ide === "codex")
236
+ return flags.project
237
+ ? { dest: path.resolve(".codex", "agents"), ide }
238
+ : { dest: path.join(os.homedir(), ".codex", "agents"), ide };
192
239
  const hint = AGENT_MANUAL[ide];
193
- die(`--agent has no plain-copy target for '${ide}'${hint ? ` — it needs ${hint}` : ""}.\n` +
240
+ die(`--agent has no target for '${ide}'${hint ? ` — it needs ${hint}` : ""}.\n` +
194
241
  ` Copy it by hand (see agents/README.md), or use --dest <dir>. Tracked in RFC-0012.`);
195
242
  }
196
243
 
244
+ /* Write one agent into a target, projecting only where the target's format demands it.
245
+ Returns the filename written plus any warning worth printing. */
246
+ function writeAgent(cat, a, dest, ide) {
247
+ const src = agentPath(cat, a);
248
+ if (ide === "kiro") {
249
+ const raw = fs.readFileSync(src, "utf8");
250
+ const body = raw.split("---").slice(2).join("---").trim();
251
+ const { json, unmapped } = kiroAgentJSON(agentMeta(cat, a), body);
252
+ fs.writeFileSync(path.join(dest, `${a}.json`), json);
253
+ return { file: `${a}.json`, warn: unmapped.length
254
+ ? ` ${a}: no Kiro equivalent for ${unmapped.join(", ")} — dropped from tools, add by hand if needed`
255
+ : null };
256
+ }
257
+ if (ide === "codex") {
258
+ const raw = fs.readFileSync(src, "utf8");
259
+ const body = raw.split("---").slice(2).join("---").trim();
260
+ fs.writeFileSync(path.join(dest, `${a}.toml`), codexAgentTOML(agentMeta(cat, a), body));
261
+ return { file: `${a}.toml`, warn: null };
262
+ }
263
+ const name = ide === "copilot" ? `${a}.agent.md` : `${a}.md`;
264
+ fs.copyFileSync(src, path.join(dest, name));
265
+ return { file: name, warn: null };
266
+ }
267
+
197
268
  function ledger(dest) {
198
269
  const p = path.join(dest, LEDGER);
199
270
  return { path: p, data: fs.existsSync(p) ? readJSON(p) : {} };
@@ -500,14 +571,20 @@ function installAgents(args) {
500
571
  const { dest, ide } = agentTarget(args.flags);
501
572
  fs.mkdirSync(dest, { recursive: true });
502
573
  const l = ledger(dest);
574
+ const warns = [];
503
575
  for (const a of names) {
504
- fs.copyFileSync(agentPath(cat, a), path.join(dest, `${a}.md`));
505
- l.data[a] = { version: null, source: cat.source }; // agents carry no version yet (RFC-0012)
506
- console.log(`installed ${a} -> ${dest}`);
576
+ const { file, warn } = writeAgent(cat, a, dest, ide);
577
+ if (warn) warns.push(warn);
578
+ l.data[a] = { version: null, source: cat.source, file }; // agents carry no version yet (RFC-0012)
579
+ console.log(`installed ${a} -> ${path.join(dest, file)}`);
507
580
  }
508
581
  saveLedger(l);
509
582
  console.log(`\n${names.length} agent(s) installed (${ide}).`);
583
+ if (warns.length) console.log(`\nTool mapping:\n${warns.join("\n")}`);
510
584
  if (ide === "claude") console.log("Delegate by name, e.g. \"use the devils-advocate agent on this diff\".");
585
+ if (ide === "kiro") console.log("Kiro grants `tools` but not auto-approval — you are prompted per call by design.");
586
+ if (ide === "copilot") console.log("Invoke with: copilot --agent <name>");
587
+ if (ide === "codex") console.log("sandbox_mode is left unset, so each agent inherits the parent session's permissions.");
511
588
  if (cat.source !== BUNDLED)
512
589
  console.log(`\nWARNING: third-party catalog '${cat.source}'. An agent is a system prompt your tool will adopt — read it before first use. Install copied files only; nothing was executed.`);
513
590
  }
@@ -517,7 +594,9 @@ function uninstallAgents(args) {
517
594
  const { dest } = agentTarget(args.flags);
518
595
  const l = ledger(dest);
519
596
  for (const a of args._) {
520
- fs.rmSync(path.join(dest, `${a}.md`), { force: true });
597
+ const rec = l.data[a];
598
+ for (const f of new Set([rec && rec.file, `${a}.md`, `${a}.json`, `${a}.agent.md`, `${a}.toml`].filter(Boolean)))
599
+ fs.rmSync(path.join(dest, f), { force: true });
521
600
  delete l.data[a];
522
601
  console.log(`removed ${a} from ${dest}`);
523
602
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skilldrop-cli",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
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
  "bin": {
6
6
  "skilldrop": "bin/skilldrop.js"