Agent Skills: Skillify

Convert a working prototype or ad-hoc solution into a permanent, tested, registered skill. Run after something works and you want it to stick.

UncategorizedID: matteocervelli/llms/skillify

Install this agent skill to your local

pnpm dlx add-skill https://github.com/matteocervelli/llms/tree/HEAD/.codex/skills/skillify

Skill Files

Browse the full folder contents for skillify.

Download Skill

Loading file tree…

.codex/skills/skillify/SKILL.md

Skill Metadata

Name
skillify
Description
Convert a working prototype or ad-hoc solution into a permanent, tested, registered skill. Run after something works and you want it to stick.

Skillify

Converts a working solution into a permanent skill with routing tests, registry entry, and DRY compliance. The complement of /hookify (which blocks failures) — this crystallises successes.

Usage

/skillify <name>              # Full 8-step pipeline for a new skill
/skillify <name> --check      # Audit an existing skill (steps 3-8 only)
/skillify <name> --step 3     # Run a single step

When to use

  • You just solved something ad-hoc and want it reusable
  • A workaround worked 3+ times → time to make it a skill
  • /memory extract surfaces a recurring pattern worth formalising
  • After /quick or a raw session where something clicked

8-Step Pipeline

Run steps in order. Stop and report if any step fails — do not skip.


Step 1 — SKILL.md contract

If ~/.claude/skills/<name>/SKILL.md does not exist, create it.

Required frontmatter:

---
name: <name>
description: "<one sentence, < 200 chars, no quotes inside>"
allowed-tools: Read, Write, Edit, Bash, Grep, Glob # trim to what's needed
---

If it exists, validate:

  • name: matches directory name
  • description: present and < 200 characters
  • allowed-tools: present

Verify: parse frontmatter with Python — must not raise yaml.YAMLError.

python3 -c "
import yaml, sys
fm = open('$HOME/.claude/skills/<name>/SKILL.md').read()
end = fm.find('---', 3)
d = yaml.safe_load(fm[3:end])
assert 'name' in d and 'description' in d, 'missing fields'
assert len(d['description']) < 200, f'description too long: {len(d[\"description\"])}'
print('OK:', d['name'], '-', d['description'][:60])
"

Step 2 — Deterministic extraction

Read the SKILL.md body. Identify logic that is:

  • Computing / parsing / transforming data (same input → same output)
  • Making API calls and parsing structured responses
  • Searching files or running commands and collecting results

If found: create ~/.claude/skills/<name>/scripts/<name>.py (or .sh) and replace the prose description with a Run: block that calls the script.

If not found: note "prompt-only skill — no script needed" and continue.

See rules/skill-architecture.md for the deterministic-vs-latent decision table.

Verify: if a script was created, it runs without error on sample input.


Step 3 — Routing test

Create ~/.claude/routing-eval/fixtures/claude/<name>.jsonl with:

  • 5+ positive triggers (natural language, NOT copies of the description)
  • 3+ negative triggers ("expected_skill": null, "should_match": false)
  • Mix Italian and English inputs

Then run Layer A check:

cd ~/.claude/routing-eval
uv run python -m framework.runner --skill <name> --verbose

Verify: 0 FAIL results. AMBIGUOUS is acceptable (Layer B resolves at runtime). If the skill collides with an existing skill, revise the description to disambiguate.


Step 4 — Functional test

Script-backed skill: create ~/.claude/skills/<name>/tests/test_<name>.py with at least one test for happy path and one for error handling. Run with:

cd ~/.claude/skills/<name> && uv run pytest tests/ -q

Prompt-only skill: invoke the skill with a known input and verify the output matches the expected structure (sections present, no empty output, no error). Document the test case in a ## Smoke Test section of SKILL.md.

Verify: test passes.


Step 5 — DRY check

Scan the SKILL.md body for content that duplicates existing rules:

python3 - <<'EOF'
import re
from pathlib import Path

skill_md = Path.home() / ".claude/skills/<name>/SKILL.md"
rules_dir = Path.home() / ".claude/rules"
body = skill_md.read_text()

violations = []
for rule_file in sorted(rules_dir.glob("*.md")):
    # Extract first heading as key phrase
    m = re.search(r'^#+\s+(.+)', rule_file.read_text(), re.MULTILINE)
    if not m:
        continue
    phrase = m.group(1).strip().lower()
    # Check if phrase appears in body without a reference to the rule file
    if phrase in body.lower() and rule_file.name not in body:
        # Suppress if rule filename mentioned within 40 lines of the phrase
        lines = body.split('\n')
        for i, line in enumerate(lines):
            if phrase in line.lower():
                window = '\n'.join(lines[max(0,i-40):i+40])
                if rule_file.name not in window:
                    violations.append(f"{rule_file.name}: '{phrase}' inlined without reference")
                    break

if violations:
    for v in violations: print("⚠ DRY:", v)
else:
    print("OK: no DRY violations")
EOF

If violations found: replace inlined content with a one-liner reference: See \rules/<rule-file>.md``.

Verify: script outputs "OK: no DRY violations".


Step 6 — Registry entry

Update ~/.claude/docs/development/registry.yaml:

skills:
  <name>:
    category: <appropriate category>
    why: "<why this skill exists>"
    intended_impact: "<what it should do>"
    actual_effect: "New — not yet tested in production"
    pdca_status: check
    last_manual_test: "<today YYYY-MM-DD>"
    depends_on: []
    subcommands: []

Use /registry update to auto-add, then manually fill why and intended_impact.

Verify: grep -A5 "^ <name>:" ~/.claude/docs/development/registry.yaml shows the entry.


Step 7 — Smoke test

Invoke the skill with a real input and observe the output:

/skillify <name> --step 7
→ invoke: /<name> <real-world input>
→ verify: output is non-empty, no errors, matches expected structure

Document the result in the registry actual_effect field.

Verify: skill completes without error on a real input.


Step 8 — Convention reference

Check that the SKILL.md references the relevant rules for its domain:

| Skill type | Required rule references | | ---------------------- | --------------------------------------- | | Any code-writing skill | rules/tdd.md, rules/code-quality.md | | Security-adjacent | rules/security-gate.md | | Memory/knowledge | rules/memory-system.md | | Deployment/infra | rules/cicd-split.md | | Script-backed | rules/skill-architecture.md |

If a relevant rule is not referenced: add a one-liner to the SKILL.md.

Verify: grep confirms rule references present.


Cross-sistema: Codex port

After all 8 steps pass, port to Codex:

# Copy SKILL.md (adapt paths if needed)
mkdir -p ~/.codex/skills/<name>
cp ~/.claude/skills/<name>/SKILL.md ~/.codex/skills/<name>/SKILL.md

# Copy fixtures
cp ~/.claude/routing-eval/fixtures/claude/<name>.jsonl \
   ~/.claude/routing-eval/fixtures/codex/<name>.jsonl

# Update Codex registry
# Add entry to ~/.codex/docs/system-registry.yaml

Completion report

After all 8 steps:

✓ Step 1: SKILL.md valid (name, description < 200 chars)
✓ Step 2: [script created | prompt-only]
✓ Step 3: routing.jsonl — Layer A pass rate X/Y
✓ Step 4: [N tests passing | smoke test documented]
✓ Step 5: no DRY violations
✓ Step 6: registry entry — pdca_status: check
✓ Step 7: smoke test passed
✓ Step 8: rule references confirmed

Skill <name> is permanent. Run /registry routing-check to verify global routing.