toolkit-skill-author
Authors or extends a skill in mc-agent-toolkit. Gates for forbidden buckets and name collisions, applies CONTRIBUTING's extend-or-split rules, then edits a peer skill or hands off to Anthropic's skill-creator and walks the registration checklist.
Install
npx skills add https://github.com/monte-carlo-data/mc-agent-toolkit/tree/main/.claude/skills/toolkit-skill-author
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install monte-carlo-data-mc-agent-toolkit@llmmart
git clone https://github.com/monte-carlo-data/mc-agent-toolkit.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole monte-carlo-data/mc-agent-toolkit collection as a plugin from our marketplace. Git is the plain clone.
README
toolkit-skill-author
A meta-skill for authoring or extending skills in mc-agent-toolkit. Run it from the repo root via /toolkit-skill-author. It interviews you, applies the extend-or-split rules from CONTRIBUTING.md, hands off to Anthropic's skill-creator for the actual SKILL.md generation, and walks the registration checklist that keeps the orchestration layer, /mc catalog, evals, and version manifest in sync.
This skill is for toolkit contributors, not end users of mc-agent-toolkit.
Why this exists
Adding a skill to mc-agent-toolkit is more than dropping a SKILL.md into skills/. A new skill must be:
- Justified against the extend-or-split decision rules so we don't grow a soup of overlapping skills.
- Named without colliding with existing skills (exact or near-match).
- Registered in the orchestration layer (
signal-definitions,/mccatalog) so context detection can find it. - Eval'd so activation is measurable.
- Symlinked into all six editor plugins (
claude-code,cursor,codex,copilot,opencode,cortex-code). - Versioned via
scripts/bump-version.shso the marketplace ships the change.
Doing all of that by hand is error-prone. This skill enforces the workflow and gates against the common mistakes (forbidden buckets, name collisions, skipping registration steps).
When to use it
Run /toolkit-skill-author when you want to:
- Add a new skill to the toolkit.
- Extend an existing skill with new capabilities, phrasings, or references.
Do not use it for:
- Editing skill internals that aren't activation-surface changes (typo fixes, minor rewording). Just edit the file and run a
patchbump.
Prerequisites
- Run from the
mc-agent-toolkitrepo. - Anthropic's
skill-creatorplugin must be installed and enabled in the current session:
Restart the session after install. The skill aborts if/plugin install skill-creator@claude-plugins-officialskill-creatorisn't available — the handoff is core to the workflow, and there is no manual fallback. - Clean working tree recommended (the skill stages changes but does not commit).
How it works
/toolkit-skill-author
│
├── Pre-load: verify repo + skill-creator availability,
│ dump CONTRIBUTING.md and existing skill peers
│
├── Phase 0 — Parse intent + apply gates
│ ├── Gate A: forbidden bucket (Agent-routing) → halt
│ ├── Gate B: name collision (exact or token-overlap) → ask before proceeding
│ └── Gate C: clear extend → fast-path to Phase 2a
│
├── Phase 1 — Decision survey (4 questions, one at a time)
│ bucket · MCP surface · purpose · example prompts
│
├── Decision — apply 4-step test from decision-rules.md
│ verdict: EXTEND <peer> or NEW SKILL
│
├── Phase 2a — Extend Phase 2b — New skill
│ hand off to skill-creator extended survey + name proposal
│ in IMPROVE_EXISTING mode hand off to skill-creator NEW_SKILL
│ run lint-skill.py run lint-skill.py
│ partial registration checklist full registration checklist
│
└── Shared — version bump
propose patch/minor/major per CONTRIBUTING § Version bumping
run scripts/bump-version.sh
What's in this directory
| Path | Purpose |
|---|---|
SKILL.md |
The skill itself — the workflow router. |
references/decision-rules.md |
Source-of-truth for the 4-step extend-or-split test. Linked from CONTRIBUTING.md. |
references/handoff-preamble.md |
Template the skill passes to skill-creator so the generated SKILL.md matches toolkit conventions. |
references/registration-checklist.md |
Post-handoff steps (signal-definitions, /mc catalog, evals, symlinks). |
scripts/find-peers.sh |
Dumps name + description + when_to_use for every existing skill — the candidate set the decision tree reasons over. |
scripts/check-prereqs.sh |
Pre-flight check used by the pre-load. |
scripts/lint-skill.py |
Validates frontmatter (name, description ≤250 chars, prefix) after skill-creator returns. |
scripts/tests/ |
Fixture tests for the helper scripts. |
evals/ |
Activation evals for this skill. |
Notes for SPEDD reviewers
- The skill is read-only through Phase 1 — no files are touched until after the decision verdict is confirmed.
- The skill never auto-commits. Every modification is staged for explicit review.
- Forbidden-bucket and name-collision gates run before any survey questions, so a refusal is fast and unambiguous.
- The four-step decision-rules test is documented in
references/decision-rules.mdand referenced byCONTRIBUTING.md. If the rule needs to change, update both. lint-skill.pyenforces the constraints from.claude/rules/skills.md(description length, name prefix). Failures block the registration checklist.- Version bumps go through
scripts/bump-version.sh, which updates all six plugin manifests + changelogs in lockstep — no partial-bump risk.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
skill-creator plugin is required |
Install skill-creator@claude-plugins-official, enable it, restart the session. |
CONTRIBUTING.md missing — run from repo root |
You're not at the repo root. cd to mc-agent-toolkit/. |
| Gate B fires unexpectedly on a name | Token-overlap is intentional — pick a more distinct name, or switch to extend. |
| Lint ERROR after handoff | Either fix manually or ask skill-creator to regenerate the offending field. The registration checklist will not proceed until lint is clean. |
Skill manifest
toolkit-skill-author
Pre-load
!test -f CONTRIBUTING.md || (echo "CONTRIBUTING.md missing — run from repo root." && exit 1)
Verify skill-creator is callable in this session. Scan the available-skills list for a skill named skill-creator (bare or namespaced, e.g. skill-creator:skill-creator). If absent, abort with exactly this message:
skill-creatorplugin is required. Run/plugin install skill-creator@claude-plugins-official, enable it, then restart this session and re-run/toolkit-skill-author.
Do not fall back to manually scaffolding SKILL.md — the handoff is core to the workflow.
Load authoritative context:
!cat CONTRIBUTING.md
!bash .claude/skills/toolkit-skill-author/scripts/find-peers.sh skills
- @.claude/skills/toolkit-skill-author/references/decision-rules.md
- @.claude/skills/toolkit-skill-author/references/handoff-preamble.md
- @.claude/skills/toolkit-skill-author/references/registration-checklist.md
Phase 0 — Parse intent and apply gates
From the contributor's initial prompt, extract (without asking yet):
target_name— did they name a specific skill (e.g., "extendmonitoring-advisor", "create a skill calledfoo-bar")?action—extend,new, orunknown.bucket— did they state a capability bucket?
Apply the gates below before any survey question. Each gate halts when it fires.
Gate A — Forbidden bucket
If bucket is Agent-routing (or a synonym like "agent routing", "routing skill"), refuse:
Agent-routing skills are owned by the toolkit core team per
CONTRIBUTING § Capability buckets— not authored via/toolkit-skill-author. Halting.
Do not proceed. If bucket is unstated, this gate re-applies after Q1.
Gate B — Name collision (new-skill intent)
If action is new and target_name is given, lowercase both target_name and each existing skill directory name, then check:
- Exact match (equal strings): refuse with "
<target_name>already exists inskills/. Pick a different name, or switch to extend." Do not proceed. - Near match. Split both names on
-into token lists. Fire if either (a) the two lists share any token, or (b) any token in one list is a substring of any token in the other list. Example:monitor-advisor↔monitoring-advisorfires via (a) sharedadvisorand (b)monitor⊂monitoring. When fired, surface the overlap and ask: "<target_name>overlaps with existing<existing>. Did you mean to extend<existing>, or proceed with a new name?" Wait for an answer before continuing.
Gate C — Fast-path for clear extend
If action is extend and target_name names an existing skill:
- Re-check the bucket. If
target_nameis one of the agent-routing skills (context-detection,incident-response,proactive-monitoring), fire Gate A's refusal — those are owned by the core team and cannot be extended via/toolkit-skill-authoreither. - Otherwise: skip the Phase 1 survey, use the initial prompt as the extension description, and jump directly to Phase 2a — Extend. If the prompt is missing fields the handoff preamble needs (purpose, phrasings, output artifact), ask only for those specific fields — do not re-run the full Q1–Q4 survey.
Otherwise, continue to Phase 1. New-skill requests always run the decision tree — the gate against hidden collisions is the tree itself.
Phase 1 — Decision survey
Ask these four questions one at a time, waiting for each answer.
- Capability bucket. Trust / Incident Response / Monitoring / Prevent / Optimize / Setup. Agent-routing is not offered. If the contributor writes in
Agent-routing, fire Gate A and halt. - Primary MCP surface or data input. E.g. Monte Carlo GraphQL, BigQuery INFORMATION_SCHEMA, Sentry issues. One or two items.
- One-line purpose. Plain language — no pushy triggers.
- 2–3 example user prompts. Quote them literally.
Decision
Apply the 4-step test from decision-rules.md against the peers dumped by find-peers.sh. The dump shows every skill's name + description + when_to_use; reason directly about which skills could plausibly activate on the Q4 prompts.
Filter agent-routing skills out of the candidate set — contributors can't author those, so they can't be extension targets. Current list: context-detection, incident-response, proactive-monitoring. If the toolkit adds new routing skills, update both this list and Gate C's list to match.
Present the verdict:
EXTEND <peer>orNEW SKILL.- The step that decided, and why.
- "Proceed with this verdict, or override?"
If the user overrides, capture their reason verbatim — it becomes part of the PR description per CONTRIBUTING.
Phase 2a — Extend
Invoke skill-creator via the Skill tool in improve-existing mode, passing the handoff preamble from references/handoff-preamble.md with MODE=IMPROVE_EXISTING, PEER_NAME=<peer>, and the survey answers (or the initial prompt for the fast-path case).
skill-creator runs its full loop; if the contributor wants a lighter-touch edit, they can tell it mid-flow to skip iteration.
When skill-creator returns:
!rm -rf skills/<peer>/evals/ skills/<peer>-workspace/
!python3 .claude/skills/toolkit-skill-author/scripts/lint-skill.py <peer>
Scratch-artifact cleanup removes skill-creator's iteration files (they drove the loop but aren't the repo's eval format). Lint surfaces any frontmatter violations.
Walk the partial checklist from references/registration-checklist.md via TodoWrite:
- Signal-definitions update — only if phrasings shifted.
/mccatalog update — only if user-facing surface changed.- Eval entry update — only if activation surface expanded.
Continue to the shared version-bump step.
Phase 2b — New skill
Extended survey (one at a time)
- Output artifact — what the skill produces (file type, diff, notebook, text verdict).
- Persona / workflow — who invokes this and during what task.
- Disambiguation — how it differs from the nearest peer (by bucket), even if no peer forced a split.
Propose name
Suggest 2–3 kebab-case directory names. Short is better. Re-run Gate B against each candidate:
- Exact match → drop the candidate.
- Near match (token-overlap with any existing skill) → drop or explicitly flag.
Only present candidates that survive Gate B. Wait for the contributor to pick one. Before handoff, re-verify skills/<chosen>/ does not exist.
Note the two-level naming: the directory is skills/<chosen>/, but the name field inside the generated SKILL.md frontmatter should be monte-carlo-<chosen> — the canonical prefixed form. The handoff preamble passes both, and lint-skill.py verifies them after scaffold.
Handoff to skill-creator
Invoke via the Skill tool with the handoff preamble from references/handoff-preamble.md, MODE=NEW_SKILL, and all survey answers.
When skill-creator returns:
!rm -rf skills/<name>/evals/ skills/<name>-workspace/
!python3 .claude/skills/toolkit-skill-author/scripts/lint-skill.py <name>
If lint prints ERROR lines, surface them and wait for the contributor to fix (manually or via a regenerate pass) before proceeding.
Full registration checklist
Walk the full checklist from references/registration-checklist.md via TodoWrite, one item per step:
- Read the relevant existing file.
- Draft the addition.
- Show the diff and confirm.
- Apply with
Edit, or create new files withWrite.
If Q1 = Setup, confirm: "Setup skills are exempt from signal-definitions and /mc catalog registration per CONTRIBUTING. Skip those two steps? [Y/n]" — default Y.
Shared — version bump
Both phases converge here.
- Propose a level based on what actually changed:
- New skill → minor per
CONTRIBUTING § Version bumping. - Extend changed activation surface (phrasings in
descriptionorwhen_to_use) → minor. - Extend did not touch activation surface → patch.
- New skill → minor per
- "Proceed with this level, or override?" Valid overrides:
patch,minor,major. - Run:
The script opens!./scripts/bump-version.sh <level>$EDITORfor the changelog, then updates all 6 plugin configs and all 6CHANGELOG.mdfiles.
Done
Tell the contributor:
All changes are staged. Review with
git status/git diff --staged, then commit and run/shipto open the PR.
Do not auto-commit or push.
Files (mc-agent-toolkit)
-
references
-
decision-rules.md 3.2 KB
# Extend-or-split decision rules **Canonical source.** `CONTRIBUTING.md § Skill authoring standards → Extend or split?` summarizes and links here; if the two disagree, this file wins. **Default:** extend an existing peer skill. Split only when a rule forces it. **Forbidden buckets.** Agent-routing skills are owned by the toolkit core team per `CONTRIBUTING § Capability buckets` — do not author them via `/toolkit-skill-author`. If the contributor's Q1 answer is `Agent-routing`, halt and refuse before peer search. Do not proceed. ## The 4-step test Apply in order. Stop at the first step that decides. ### Step 1 — Find the nearest peer `find-peers.sh` (run in Pre-load) dumps every skill's `name + description + when_to_use`. Read the dump and reason about which skills could plausibly activate on the new skill's Q4 prompts. For each plausible candidate, ask: *can you write a realistic user prompt that should route to the new skill but could plausibly activate the candidate instead?* - If no candidate survives this test → **there is no routing collision. Go to new-skill path. Stop.** - If at least one survives → that is the peer. Continue to Step 2. ### Step 2 — Budget check Open the peer's `SKILL.md`. Count the combined character length of `description` + `when_to_use`. If adding one sentence to `description` or one bullet to `when_to_use` would push the combined length past **1,400 characters** → **split.** Stop. (The hard ceiling is 1,536; 1,400 leaves headroom.) ### Step 3 — Surface check Does the new behavior: - hit a different MCP surface / data input, **or** - produce a different output artifact, **or** - belong to a different capability bucket …than the peer? If yes → **split.** Stop. ### Step 4 — Otherwise, extend Phrasing overlap, "it feels like its own thing," or wanting a cleaner file are not reasons to split. Add a bullet to the peer's `when_to_use`, add a `references/` file if the workflow needs more room. ## PR requirement If the verdict is SPLIT, the contributor must name the peer(s) considered and cite which step forced the split. `toolkit-skill-author` captures this as the override reason and surfaces it for the PR description. ## Worked example This example walks the tree for an **ambiguous new-skill** request — the case where Phase 0's Gate C doesn't fast-path, so the full decision applies. (Clear-extend requests skip the tree via Gate C; clear name collisions halt at Gate B.) - Survey answers: bucket = Monitoring, surface = BigQuery INFORMATION_SCHEMA, purpose = "detect stale partitions," phrasings = ["find stale partitions", "partition freshness"]. - Peer dump shows: `monitoring-advisor`, `tune-monitor`, and others. - Step 1: `monitoring-advisor`'s current scope ("create monitors for warehouse tables") plausibly activates on "find stale partitions." Peer survives. Continue. - Step 2: measure `monitoring-advisor`'s actual combined `description + when_to_use` length and add the proposed bullet. If the sum stays under 1,400, no budget pressure. Continue. - Step 3: same bucket (Monitoring), same primary surface (warehouse table reads, BigQuery is a subset), output overlaps (monitor recommendations). No surface pressure. - Step 4: **extend** `monitoring-advisor` with the new phrasings. -
handoff-preamble.md 3.4 KB
# Handoff preamble for Anthropic's `skill-creator` This is the template `toolkit-skill-author` uses when invoking Anthropic's `skill-creator` via the `Skill` tool. Claude fills in the bracketed placeholders from survey answers. ## Template You are being invoked by mc-agent-toolkit's `/toolkit-skill-author` with pre-collected context. **Use the pre-filled answers below to skip your initial interview. Then run your full workflow — test cases, iterate loop, and description optimizer — using the answers as the starting draft. If the contributor tells you mid-flow to skip iteration ("just vibe with me"), honor that.** **Mode:** {{NEW_SKILL | IMPROVE_EXISTING}} **Target path:** `skills/{{NAME}}/` in the mc-agent-toolkit repo (not your default workspace). **Pre-filled answers:** - What should this skill enable Claude to do? {{PURPOSE}} - When should this skill trigger? {{PHRASINGS as bullet list}} - Expected output format: {{OUTPUT}} **Body context (inform the SKILL.md body, do NOT copy verbatim into frontmatter):** - Persona / workflow: {{PERSONA_WORKFLOW}} - Disambiguation from nearest peer `{{PEER_NAME}}`: {{DISAMBIGUATION}} - Bucket: {{BUCKET}} (also emitted as the `bucket` frontmatter field — see below) **MC-specific voice and length rules (override your defaults):** - `description` ≤ 1,024 characters. - Combined `description` + `when_to_use` ≤ 1,400 characters (headroom under the 1,536 truncation ceiling). - Third-person voice. Describe what the skill does, not what "this skill" does. - Do **not** open with "This skill…" or "Use this skill when…". - Do **not** use the "pushy" voice pattern ("Make sure to use this skill whenever…"); use concrete triggers instead. **Required frontmatter fields (and fields NOT to emit):** - `name`: `monte-carlo-{{NAME}}` — canonical prefixed form; `{{NAME}}` is the directory name. - `description`: per the length rule above. - `when_to_use`: required (not optional). - `bucket`: `{{BUCKET}}` — one of Trust / Incident Response / Monitoring / Prevent / Optimize / Setup. Tracks which capability bucket the skill belongs to in the public docs. - Do **not** emit a `version` field. Versions live in the plugin manifests (`plugins/*/.*-plugin/plugin.json`), not in SKILL.md. `toolkit-skill-author` will bump them separately via `scripts/bump-version.sh` after you return. **Eval artifacts are scratch, not shipped:** - Your `skills/{{NAME}}/evals/evals.json` and sibling `skills/{{NAME}}-workspace/` drive the iteration loop but are not the repo's eval format. `toolkit-skill-author` will delete them after you return. - mc-agent-toolkit's real evals live at `plugins/claude-code/evals/{{NAME}}/live-evals-dev.yaml` — a YAML schema with `cases: [{ id, turns: [{ prompt, criteria: { must_call, must_not_call } }], criteria: { judge_rubric } }]`. Authoring that file is handled by `toolkit-skill-author` in the registration checklist — don't attempt to write it yourself, and don't produce a `trigger-evals.json` or any JSON variant. **When done:** return control to `toolkit-skill-author`. It will lint the generated SKILL.md, clean up scratch artifacts, and walk the registration checklist. ## Improve-existing mode differences When `{{MODE}} == IMPROVE_EXISTING`: - **Target path:** the existing `skills/{{PEER_NAME}}/` (edit in place, not new scaffold). - **Pre-filled answers** describe the *extension*, not a new skill. - Otherwise identical: same voice rules, same full workflow, same scratch-artifact cleanup. -
registration-checklist.md 3 KB
# Registration checklist After Phase 2a (extend) or Phase 2b (new skill), walk the relevant subset of these steps. `toolkit-skill-author` tracks each as a TodoWrite item and confirms before writing any file. ## Full checklist (new skill) Use when Phase 2b concludes with a scaffolded `skills/<name>/SKILL.md`. 1. **Signal definition.** Append a row to `skills/context-detection/references/signal-definitions.md` under *Conversation Signals* and/or *Workspace Signals*. Describe the keywords, artifacts, and user phrasings that should route to the new skill. *Skipped if bucket = Setup (after user confirmation).* 2. **`/mc` catalog entry.** Append a row to `plugins/claude-code/commands/catalog/mc.md`. *Skipped if bucket = Setup (after user confirmation).* 3. **Eval scaffold.** Create `plugins/claude-code/evals/<name>/live-evals-dev.yaml` using the YAML schema the repo already ships for every other skill. Read `plugins/claude-code/evals/monitoring-advisor/live-evals-dev.yaml` or `plugins/claude-code/evals/context-detection/live-evals-dev.yaml` as templates before writing. Schema: ```yaml cases: - id: <slug-id> turns: - prompt: "<realistic user prompt>" criteria: must_call: [mcp_tool_1, mcp_tool_2] # optional must_not_call: [forbidden_tool] # optional criteria: judge_rubric: | <free-form description of desired behavior> ``` Seed with the Q4 phrasings plus 2–3 should-not-trigger near-misses. Do **not** create `trigger-evals.json` or any other JSON variant — YAML is the canonical format. 4. **Editor plugin symlinks.** For each editor in `plugins/` (claude-code, cursor, opencode, codex), add a relative symlink: ``` plugins/<editor>/skills/<name> -> ../../../skills/<name> ``` 5. **Claude Code commands entry.** Create `plugins/claude-code/commands/<name>/` with at least one `.md` command file. Add the directory name to the `commands` array in `plugins/claude-code/.claude-plugin/plugin.json`. ## Partial checklist (extend) Use when Phase 2a edits an existing peer. 1. **Signal definition.** Update only if phrasings shifted (new user-facing routing language). 2. **`/mc` catalog.** Update only if the user-facing surface changed (new capability described in the catalog row). 3. **Eval entry.** Update the peer's existing `plugins/claude-code/evals/<peer>/` entry only if the activation surface expanded. No symlink or commands changes for extend (they already exist for the peer). ## Setup-bucket confirmation prompt When Q1 = Setup, `toolkit-skill-author` prompts: > Setup skills are exempt from signal-definitions and `/mc` catalog registration per `CONTRIBUTING § Capability buckets`. Skip these steps? [Y/n] Default Y. On N, proceed with steps 1 and 2 as normal (user opted back in). ## Version bump (always) After the relevant registration subset, run: ``` ./scripts/bump-version.sh <patch|minor> ``` See `CONTRIBUTING.md § Version bumping` for semver rules. `toolkit-skill-author` proposes the level; user can override.
-
-
scripts
-
tests
-
find-peers.bats 1.2 KB · in bundle
-
lint-skill.bats 5.9 KB · in bundle
-
smoke-test.sh 2.4 KB
#!/usr/bin/env bash # Verify every path and script that toolkit-skill-author's SKILL.md references still exists # and behaves as expected. Catches drift when registration targets move or scripts # are renamed — without this, toolkit-skill-author would fail silently mid-flow. # # Run from repo root: bash .claude/skills/toolkit-skill-author/scripts/smoke-test.sh # Exits 0 on pass, 1 on any failure. set -u SKILL_DIR=".claude/skills/toolkit-skill-author" fail=0 assert_file() { if [ ! -f "$1" ]; then echo "FAIL: missing file $1" fail=1 fi } assert_dir() { if [ ! -d "$1" ]; then echo "FAIL: missing dir $1" fail=1 fi } assert_script_runs() { local label="$1"; shift if ! "$@" >/dev/null 2>&1; then echo "FAIL: $label — '$*' exited non-zero" fail=1 fi } # Core toolkit-skill-author files assert_file "CONTRIBUTING.md" assert_file "$SKILL_DIR/SKILL.md" assert_file "$SKILL_DIR/references/decision-rules.md" assert_file "$SKILL_DIR/references/handoff-preamble.md" assert_file "$SKILL_DIR/references/registration-checklist.md" assert_file "$SKILL_DIR/scripts/find-peers.sh" assert_file "$SKILL_DIR/scripts/lint-skill.py" # Registration-checklist targets (referenced in registration-checklist.md) assert_file "skills/context-detection/references/signal-definitions.md" assert_file "plugins/claude-code/commands/catalog/mc.md" assert_file "plugins/claude-code/.claude-plugin/plugin.json" assert_file "scripts/bump-version.sh" # Editor-plugin dirs for symlinks (registration-checklist step 4) for editor in claude-code cursor codex opencode; do assert_dir "plugins/$editor/skills" done # Scripts run without crashing on valid input assert_script_runs "find-peers.sh dumps skills" bash "$SKILL_DIR/scripts/find-peers.sh" skills # Lint against a synthetic compliant skill (real skills in skills/ have legacy # version/bucket debt that the linter correctly flags — not what this test # measures). FIXTURE=$(mktemp -d) mkdir -p "$FIXTURE/smoke-fixture" cat > "$FIXTURE/smoke-fixture/SKILL.md" <<'EOF' --- name: monte-carlo-smoke-fixture description: A fixture skill for smoke-testing lint-skill.py. when_to_use: When smoke-testing. bucket: Monitoring --- EOF assert_script_runs "lint-skill.py accepts a compliant skill" python3 "$SKILL_DIR/scripts/lint-skill.py" smoke-fixture "$FIXTURE" rm -rf "$FIXTURE" if [ "$fail" -eq 0 ]; then echo "toolkit-skill-author smoke test: OK" exit 0 else echo "toolkit-skill-author smoke test: FAILED" exit 1 fi
-
-
find-peers.sh 1020 B
#!/usr/bin/env bash # Dump every skill's frontmatter (name + description + when_to_use) so Claude can # reason about which skills are peers to the new/extended one. Replaces the old # bucket+keyword filtering, which was too brittle to be useful — baseline evals # showed false positives from substring bucket matches and false negatives from # stopword-dominated keyword ranking. # # Output is the raw frontmatter block for each skill, separated by `=== <name> ===` # headers. Claude reads it and applies the 4-step decision-rules test itself. set -euo pipefail SKILLS_DIR="${1:-skills}" if [ ! -d "$SKILLS_DIR" ]; then echo "find-peers.sh: skills dir '$SKILLS_DIR' not found" >&2 exit 2 fi found=0 for sm in "$SKILLS_DIR"/*/SKILL.md; do [ -f "$sm" ] || continue found=1 name="$(basename "$(dirname "$sm")")" echo "=== $name ===" awk '/^---[[:space:]]*$/{c++; if(c==2)exit; next} c==1' "$sm" echo done [ "$found" -eq 1 ] || { echo "find-peers.sh: no SKILL.md files in '$SKILLS_DIR'" >&2; exit 2; } -
lint-skill.py 5.6 KB
#!/usr/bin/env python3 """Lint a customer-facing skill's SKILL.md frontmatter against mc-agent-toolkit standards. Scope: skills under `skills/`. Dev-only skills under `.claude/skills/` (e.g., toolkit-skill-author itself) are out of scope and not required to follow the prefix rule — don't point this script at them. Parses YAML frontmatter (handles scalar, block, and folded forms), then checks: - name equals `monte-carlo-<dir>` (canonical) and is kebab-case - description present, <= 1024 chars, no first-person opener - when_to_use present (strongly recommended per CONTRIBUTING) - combined description + when_to_use <= 1400 chars (headroom under 1536 truncation) - bucket present and is one of the canonical capability buckets - `version` field WARN (ignored by Claude — plugin versions live in manifests — but many legacy skills carry it; don't fail lint on that). Exits 0 if clean, 1 on any ERROR, 2 on usage / file errors. """ import re import sys from pathlib import Path MAX_DESCRIPTION = 1024 MAX_COMBINED = 1400 FIRST_PERSON_RE = re.compile(r"^\s*(this skill|use this skill)", re.IGNORECASE) KEBAB_RE = re.compile(r"^[a-z][a-z0-9]*(-[a-z0-9]+)*$") VALID_BUCKETS = { "Trust", "Incident Response", "Monitoring", "Prevent", "Optimize", "Setup", # Agent-routing skills are outside the user-facing capability buckets # (orchestrators, owned by the toolkit core team). /toolkit-skill-author blocks # new ones at Gate A, but existing routers still carry this bucket value. "Agent-routing", } def parse_frontmatter(text: str) -> dict[str, str]: m = re.match(r"^---\s*\n(.*?)\n---", text, re.DOTALL) if not m: return {} lines = m.group(1).split("\n") result: dict[str, str] = {} i = 0 while i < len(lines): line = lines[i] if not line.strip() or line.lstrip().startswith("#"): i += 1 continue km = re.match(r"^([a-zA-Z_][a-zA-Z0-9_-]*)\s*:\s*(.*)$", line) if not km: i += 1 continue key, rest = km.group(1), km.group(2).rstrip() if rest in ("|", ">", "|-", "|+", ">-", ">+"): i += 1 block: list[str] = [] while i < len(lines): bline = lines[i] if bline.startswith(" "): block.append(bline[2:]) i += 1 elif bline.strip() == "": block.append("") i += 1 else: break joined = "\n".join(block).rstrip("\n") if rest.startswith(">"): joined = re.sub(r"\n(?!\n)", " ", joined) result[key] = joined else: val = rest if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")): val = val[1:-1] result[key] = val i += 1 return result def lint(name: str, skills_root: Path) -> tuple[list[str], list[str]]: path = skills_root / name / "SKILL.md" if not path.is_file(): print(f"ERROR: {path} not found", file=sys.stderr) sys.exit(2) text = path.read_text() fm = parse_frontmatter(text) errors: list[str] = [] warnings: list[str] = [] actual_name = fm.get("name", "") expected = f"monte-carlo-{name}" if actual_name != expected: errors.append( f"name '{actual_name}' should be '{expected}' " f"(canonical form: 'monte-carlo-<directory>')" ) if actual_name and not KEBAB_RE.match(actual_name): errors.append(f"name '{actual_name}' is not kebab-case") desc = fm.get("description", "") if not desc: errors.append("description is missing (required)") else: if len(desc) > MAX_DESCRIPTION: errors.append(f"description is {len(desc)} chars (max {MAX_DESCRIPTION})") if FIRST_PERSON_RE.match(desc): errors.append(f"description opens with first-person phrasing (\"{desc[:40]}...\")") wtu = fm.get("when_to_use", "") if not wtu: warnings.append("when_to_use is missing (strongly recommended per CONTRIBUTING)") combined = len(desc) + len(wtu) if combined > MAX_COMBINED: errors.append( f"description + when_to_use = {combined} chars " f"(max {MAX_COMBINED} for headroom under 1536 truncation)" ) bucket = fm.get("bucket", "").strip() if not bucket: errors.append( f"bucket is missing (required; one of: {', '.join(sorted(VALID_BUCKETS))})" ) elif bucket not in VALID_BUCKETS: errors.append( f"bucket '{bucket}' is not a valid capability bucket " f"(expected one of: {', '.join(sorted(VALID_BUCKETS))})" ) if "version" in fm: warnings.append( "version field in SKILL.md is ignored by Claude — plugin versions " "live in plugins/*/.*-plugin/plugin.json (new skills don't need it)" ) print(f"skill: {name}") print(f" description: {len(desc)} chars") print(f" when_to_use: {len(wtu)} chars") print(f" combined: {combined} chars (limit {MAX_COMBINED})") for w in warnings: print(f" WARN: {w}") for e in errors: print(f" ERROR: {e}") return errors, warnings def main() -> None: if len(sys.argv) < 2 or len(sys.argv) > 3: print("Usage: lint-skill.py <skill-name> [skills-root]", file=sys.stderr) sys.exit(2) name = sys.argv[1] skills_root = Path(sys.argv[2]) if len(sys.argv) == 3 else Path("skills") errors, _ = lint(name, skills_root) sys.exit(1 if errors else 0) if __name__ == "__main__": main()
-
-
README.md 5.7 KB
# toolkit-skill-author A meta-skill for **authoring or extending skills in `mc-agent-toolkit`**. Run it from the repo root via `/toolkit-skill-author`. It interviews you, applies the extend-or-split rules from `CONTRIBUTING.md`, hands off to Anthropic's `skill-creator` for the actual SKILL.md generation, and walks the registration checklist that keeps the orchestration layer, `/mc` catalog, evals, and version manifest in sync. This skill is for **toolkit contributors**, not end users of mc-agent-toolkit. ## Why this exists Adding a skill to `mc-agent-toolkit` is more than dropping a `SKILL.md` into `skills/`. A new skill must be: 1. **Justified** against the extend-or-split decision rules so we don't grow a soup of overlapping skills. 2. **Named** without colliding with existing skills (exact or near-match). 3. **Registered** in the orchestration layer (`signal-definitions`, `/mc` catalog) so context detection can find it. 4. **Eval'd** so activation is measurable. 5. **Symlinked** into all six editor plugins (`claude-code`, `cursor`, `codex`, `copilot`, `opencode`, `cortex-code`). 6. **Versioned** via `scripts/bump-version.sh` so the marketplace ships the change. Doing all of that by hand is error-prone. This skill enforces the workflow and gates against the common mistakes (forbidden buckets, name collisions, skipping registration steps). ## When to use it Run `/toolkit-skill-author` when you want to: - **Add a new skill** to the toolkit. - **Extend an existing skill** with new capabilities, phrasings, or references. Do **not** use it for: - **Editing skill internals that aren't activation-surface changes** (typo fixes, minor rewording). Just edit the file and run a `patch` bump. ## Prerequisites - Run from the **`mc-agent-toolkit` repo**. - Anthropic's `skill-creator` plugin must be installed and enabled in the current session: ``` /plugin install skill-creator@claude-plugins-official ``` Restart the session after install. The skill aborts if `skill-creator` isn't available — the handoff is core to the workflow, and there is no manual fallback. - Clean working tree recommended (the skill stages changes but does not commit). ## How it works ``` /toolkit-skill-author │ ├── Pre-load: verify repo + skill-creator availability, │ dump CONTRIBUTING.md and existing skill peers │ ├── Phase 0 — Parse intent + apply gates │ ├── Gate A: forbidden bucket (Agent-routing) → halt │ ├── Gate B: name collision (exact or token-overlap) → ask before proceeding │ └── Gate C: clear extend → fast-path to Phase 2a │ ├── Phase 1 — Decision survey (4 questions, one at a time) │ bucket · MCP surface · purpose · example prompts │ ├── Decision — apply 4-step test from decision-rules.md │ verdict: EXTEND <peer> or NEW SKILL │ ├── Phase 2a — Extend Phase 2b — New skill │ hand off to skill-creator extended survey + name proposal │ in IMPROVE_EXISTING mode hand off to skill-creator NEW_SKILL │ run lint-skill.py run lint-skill.py │ partial registration checklist full registration checklist │ └── Shared — version bump propose patch/minor/major per CONTRIBUTING § Version bumping run scripts/bump-version.sh ``` ## What's in this directory | Path | Purpose | | --- | --- | | `SKILL.md` | The skill itself — the workflow router. | | `references/decision-rules.md` | Source-of-truth for the 4-step extend-or-split test. Linked from `CONTRIBUTING.md`. | | `references/handoff-preamble.md` | Template the skill passes to `skill-creator` so the generated SKILL.md matches toolkit conventions. | | `references/registration-checklist.md` | Post-handoff steps (signal-definitions, `/mc` catalog, evals, symlinks). | | `scripts/find-peers.sh` | Dumps name + description + when_to_use for every existing skill — the candidate set the decision tree reasons over. | | `scripts/check-prereqs.sh` | Pre-flight check used by the pre-load. | | `scripts/lint-skill.py` | Validates frontmatter (name, description ≤250 chars, prefix) after `skill-creator` returns. | | `scripts/tests/` | Fixture tests for the helper scripts. | | `evals/` | Activation evals for this skill. | ## Notes for SPEDD reviewers - The skill is **read-only** through Phase 1 — no files are touched until after the decision verdict is confirmed. - The skill **never auto-commits**. Every modification is staged for explicit review. - Forbidden-bucket and name-collision gates run **before** any survey questions, so a refusal is fast and unambiguous. - The four-step decision-rules test is documented in `references/decision-rules.md` and referenced by `CONTRIBUTING.md`. If the rule needs to change, update both. - `lint-skill.py` enforces the constraints from `.claude/rules/skills.md` (description length, name prefix). Failures block the registration checklist. - Version bumps go through `scripts/bump-version.sh`, which updates all six plugin manifests + changelogs in lockstep — no partial-bump risk. ## Troubleshooting | Symptom | Cause / fix | | --- | --- | | `skill-creator plugin is required` | Install `skill-creator@claude-plugins-official`, enable it, restart the session. | | `CONTRIBUTING.md missing — run from repo root` | You're not at the repo root. `cd` to `mc-agent-toolkit/`. | | Gate B fires unexpectedly on a name | Token-overlap is intentional — pick a more distinct name, or switch to extend. | | Lint ERROR after handoff | Either fix manually or ask `skill-creator` to regenerate the offending field. The registration checklist will not proceed until lint is clean. | -
SKILL.md 8.9 KB
--- name: toolkit-skill-author description: Authors or extends a skill in mc-agent-toolkit. Gates for forbidden buckets and name collisions, applies CONTRIBUTING's extend-or-split rules, then edits a peer skill or hands off to Anthropic's skill-creator and walks the registration checklist. disable-model-invocation: true when_to_use: | Invoked explicitly as /toolkit-skill-author when a contributor wants to add, extend, or draft a new skill for mc-agent-toolkit. Not auto-routed. Requires Anthropic's skill-creator plugin. --- # toolkit-skill-author ## Pre-load ```bash !test -f CONTRIBUTING.md || (echo "CONTRIBUTING.md missing — run from repo root." && exit 1) ``` **Verify `skill-creator` is callable in this session.** Scan the available-skills list for a skill named `skill-creator` (bare or namespaced, e.g. `skill-creator:skill-creator`). If absent, abort with **exactly this message**: > `skill-creator` plugin is required. Run `/plugin install skill-creator@claude-plugins-official`, enable it, then restart this session and re-run `/toolkit-skill-author`. Do **not** fall back to manually scaffolding SKILL.md — the handoff is core to the workflow. Load authoritative context: ```bash !cat CONTRIBUTING.md !bash .claude/skills/toolkit-skill-author/scripts/find-peers.sh skills ``` - @.claude/skills/toolkit-skill-author/references/decision-rules.md - @.claude/skills/toolkit-skill-author/references/handoff-preamble.md - @.claude/skills/toolkit-skill-author/references/registration-checklist.md ## Phase 0 — Parse intent and apply gates From the contributor's initial prompt, extract (without asking yet): - **`target_name`** — did they name a specific skill (e.g., *"extend `monitoring-advisor`"*, *"create a skill called `foo-bar`"*)? - **`action`** — `extend`, `new`, or `unknown`. - **`bucket`** — did they state a capability bucket? Apply the gates below **before** any survey question. Each gate halts when it fires. ### Gate A — Forbidden bucket If `bucket` is `Agent-routing` (or a synonym like "agent routing", "routing skill"), refuse: > Agent-routing skills are owned by the toolkit core team per `CONTRIBUTING § Capability buckets` — not authored via `/toolkit-skill-author`. Halting. Do not proceed. If `bucket` is unstated, this gate re-applies after Q1. ### Gate B — Name collision (new-skill intent) If `action` is `new` and `target_name` is given, lowercase both `target_name` and each existing skill directory name, then check: - **Exact match** (equal strings): refuse with *"`<target_name>` already exists in `skills/`. Pick a different name, or switch to extend."* Do not proceed. - **Near match.** Split both names on `-` into token lists. Fire if either (a) the two lists share any token, or (b) any token in one list is a substring of any token in the other list. Example: `monitor-advisor` ↔ `monitoring-advisor` fires via (a) shared `advisor` and (b) `monitor` ⊂ `monitoring`. When fired, surface the overlap and ask: *"`<target_name>` overlaps with existing `<existing>`. Did you mean to extend `<existing>`, or proceed with a new name?"* Wait for an answer before continuing. ### Gate C — Fast-path for clear extend If `action` is `extend` and `target_name` names an existing skill: - **Re-check the bucket.** If `target_name` is one of the agent-routing skills (`context-detection`, `incident-response`, `proactive-monitoring`), fire Gate A's refusal — those are owned by the core team and cannot be extended via `/toolkit-skill-author` either. - Otherwise: skip the Phase 1 survey, use the initial prompt as the extension description, and jump directly to **Phase 2a — Extend**. If the prompt is missing fields the handoff preamble needs (purpose, phrasings, output artifact), ask only for those specific fields — do not re-run the full Q1–Q4 survey. Otherwise, continue to Phase 1. **New-skill requests always run the decision tree** — the gate against hidden collisions is the tree itself. ## Phase 1 — Decision survey Ask these four questions **one at a time**, waiting for each answer. 1. **Capability bucket.** Trust / Incident Response / Monitoring / Prevent / Optimize / Setup. Agent-routing is not offered. If the contributor writes in `Agent-routing`, fire Gate A and halt. 2. **Primary MCP surface or data input.** E.g. Monte Carlo GraphQL, BigQuery INFORMATION_SCHEMA, Sentry issues. One or two items. 3. **One-line purpose.** Plain language — no pushy triggers. 4. **2–3 example user prompts.** Quote them literally. ## Decision Apply the 4-step test from `decision-rules.md` against the peers dumped by `find-peers.sh`. The dump shows every skill's name + description + when_to_use; reason directly about which skills could plausibly activate on the Q4 prompts. Filter agent-routing skills out of the candidate set — contributors can't author those, so they can't be extension targets. Current list: `context-detection`, `incident-response`, `proactive-monitoring`. If the toolkit adds new routing skills, update both this list and Gate C's list to match. Present the verdict: - `EXTEND <peer>` or `NEW SKILL`. - The step that decided, and why. - "Proceed with this verdict, or override?" If the user overrides, capture their reason verbatim — it becomes part of the PR description per `CONTRIBUTING`. ## Phase 2a — Extend Invoke `skill-creator` via the `Skill` tool in **improve-existing mode**, passing the handoff preamble from `references/handoff-preamble.md` with `MODE=IMPROVE_EXISTING`, `PEER_NAME=<peer>`, and the survey answers (or the initial prompt for the fast-path case). `skill-creator` runs its full loop; if the contributor wants a lighter-touch edit, they can tell it mid-flow to skip iteration. When `skill-creator` returns: ```bash !rm -rf skills/<peer>/evals/ skills/<peer>-workspace/ !python3 .claude/skills/toolkit-skill-author/scripts/lint-skill.py <peer> ``` Scratch-artifact cleanup removes skill-creator's iteration files (they drove the loop but aren't the repo's eval format). Lint surfaces any frontmatter violations. Walk the **partial** checklist from `references/registration-checklist.md` via TodoWrite: - Signal-definitions update — only if phrasings shifted. - `/mc` catalog update — only if user-facing surface changed. - Eval entry update — only if activation surface expanded. Continue to the shared version-bump step. ## Phase 2b — New skill ### Extended survey (one at a time) 5. **Output artifact** — what the skill produces (file type, diff, notebook, text verdict). 6. **Persona / workflow** — who invokes this and during what task. 7. **Disambiguation** — how it differs from the nearest peer (by bucket), even if no peer forced a split. ### Propose name Suggest 2–3 kebab-case **directory names**. Short is better. Re-run Gate B against each candidate: - Exact match → drop the candidate. - Near match (token-overlap with any existing skill) → drop or explicitly flag. Only present candidates that survive Gate B. Wait for the contributor to pick one. Before handoff, re-verify `skills/<chosen>/` does not exist. **Note the two-level naming:** the directory is `skills/<chosen>/`, but the `name` field inside the generated SKILL.md frontmatter should be `monte-carlo-<chosen>` — the canonical prefixed form. The handoff preamble passes both, and `lint-skill.py` verifies them after scaffold. ### Handoff to `skill-creator` Invoke via the `Skill` tool with the handoff preamble from `references/handoff-preamble.md`, `MODE=NEW_SKILL`, and all survey answers. When `skill-creator` returns: ```bash !rm -rf skills/<name>/evals/ skills/<name>-workspace/ !python3 .claude/skills/toolkit-skill-author/scripts/lint-skill.py <name> ``` If lint prints ERROR lines, surface them and wait for the contributor to fix (manually or via a regenerate pass) before proceeding. ### Full registration checklist Walk the full checklist from `references/registration-checklist.md` via TodoWrite, one item per step: 1. Read the relevant existing file. 2. Draft the addition. 3. Show the diff and confirm. 4. Apply with `Edit`, or create new files with `Write`. If Q1 = Setup, confirm: *"Setup skills are exempt from signal-definitions and `/mc` catalog registration per CONTRIBUTING. Skip those two steps? [Y/n]"* — default Y. ## Shared — version bump Both phases converge here. 1. Propose a level based on what actually changed: - New skill → **minor** per `CONTRIBUTING § Version bumping`. - Extend changed activation surface (phrasings in `description` or `when_to_use`) → **minor**. - Extend did not touch activation surface → **patch**. 2. *"Proceed with this level, or override?"* Valid overrides: `patch`, `minor`, `major`. 3. Run: ```bash !./scripts/bump-version.sh <level> ``` The script opens `$EDITOR` for the changelog, then updates all 6 plugin configs and all 6 `CHANGELOG.md` files. ## Done Tell the contributor: > All changes are staged. Review with `git status` / `git diff --staged`, then commit and run `/ship` to open the PR. Do **not** auto-commit or push.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.