Claude Cursor Skill

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.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download monte-carlo-data-mc-agent-toolkit-.claude_skills_toolkit-skill-author-bcc7373.zip · 17 KB
Part of monte-carlo-data/mc-agent-toolkit — 20 skills

Install

skills CLI npx skills add https://github.com/monte-carlo-data/mc-agent-toolkit/tree/main/.claude/skills/toolkit-skill-author
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install monte-carlo-data-mc-agent-toolkit@llmmart
Git 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:

  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 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-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:

!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:

!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)

  1. Output artifact — what the skill produces (file type, diff, notebook, text verdict).
  2. Persona / workflow — who invokes this and during what task.
  3. 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:

  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:
    !./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.

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.

No comments yet.

Reviews (0)

No reviews yet.

Related