feat(kyberforge): replace write-skill with spec-compliant skill-write and add skill-audit

Rewrote the skill authoring factory skill from scratch against the agentskills.io
specification. Renamed write-skill → skill-write (name now matches directory per spec).

skill-write:
- Full scaffold via new-skill.sh (annotated templates for SKILL.md, README.md,
  scripts/, references/, assets/)
- validate.sh checks all spec constraints deterministically (name format/length,
  description length, placeholder detection, line count, script rules)
- SKILL.md body includes description rules, body discipline, patterns, and scripts
  guidance with "why" rationale throughout
- Templates usable standalone by agents and humans

skill-audit:
- Structural validation (via validate.sh) + seven qualitative dimensions
- Produces PASS/FAIL/SUGGESTION punch list with per-FAIL fix proposals
- Report-only: does not apply fixes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 18:50:00 +00:00
parent 41c4da31cf
commit 672fd25890
17 changed files with 882 additions and 319 deletions

View File

@@ -0,0 +1,63 @@
# skill-write
Author a new skill conforming to the [agentskills.io](https://agentskills.io) specification.
## What it does
1. Scaffolds a full skill directory from annotated templates
2. Guides filling in `SKILL.md` and supporting files
3. Validates the result against the spec
## Before you start
This skill produces its best output when you arrive with rich context:
- Run `/grill-me` to resolve design decisions (scope, triggers, patterns)
- Collect domain research, examples, and reference docs
- Know the skill name (kebab-case) and destination path
## Usage
Invoke via your agent tool with `/skill-write`, or follow the steps in `SKILL.md` manually.
**Agent invocation:**
```
/skill-write
```
**Manual (human) workflow:**
```bash
# 1. Create the scaffold
bash scripts/new-skill.sh <skill-name> <destination-dir>
# 2. Fill in the templates at <destination-dir>/<skill-name>/
# 3. Validate
bash scripts/validate.sh <destination-dir>/<skill-name>
```
## Files
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `scripts/new-skill.sh` | Copies annotated templates to the destination |
| `scripts/validate.sh` | Validates a skill directory against the spec |
| `assets/templates/SKILL.md` | Annotated SKILL.md template |
| `assets/templates/README.md` | Annotated README template for the new skill |
| `assets/templates/scripts/README.md` | Placeholder for bundled scripts |
| `assets/templates/references/README.md` | Placeholder for reference docs |
| `assets/templates/assets/README.md` | Placeholder for static assets |
## Placement
Skills can be installed in two locations:
| Type | Path |
|------|------|
| Direct (available immediately) | `~/.agents/skills/<name>/` |
| Plugin (installed via marketplace) | `plugins/<plugin>/skills/<name>/` |
## Spec reference
[agentskills.io specification](https://agentskills.io/specification.md)

View File

@@ -0,0 +1,151 @@
---
name: skill-write
description: >
Author a new skill following the agentskills.io specification — scaffold the
directory structure from annotated templates, fill in SKILL.md and supporting
files, then validate the result. Use when the user wants to create a new skill
from scratch, says "write a skill for X", "build a skill that does Y", or
"create a SKILL.md for Z", or wants to make a workflow repeatable or shareable
as a reusable command. Performs best when preceded by a grill session and
domain research. Do not use to update an existing well-formed skill, write
evals, or author agent definition files.
allowed-tools: Bash Read Write
metadata:
category: factory
---
## Prerequisites
Run `/grill-me` on the skill's design and research the target domain first.
Share those outputs in this conversation: grill context, research docs, examples, constraints.
**Before touching the filesystem, verify you have:**
- [ ] A clear purpose — what specific task will this skill handle?
- [ ] Trigger scenarios — when should an agent activate it, including indirect cases?
- [ ] Skill name (kebab-case) and destination path
If any are missing, stop and ask the user before proceeding.
## Step 1 — Scaffold
Run the copy script with the skill name and destination directory:
```bash
bash scripts/new-skill.sh <skill-name> <destination-dir>
```
Examples:
```bash
bash scripts/new-skill.sh my-tool ~/.agents/skills/
bash scripts/new-skill.sh data-analyzer plugins/myplugin/skills/
```
This creates `<destination-dir>/<skill-name>/` with annotated templates ready to fill in.
## Step 2 — Fill in SKILL.md
Open `<destination-dir>/<skill-name>/SKILL.md`. Replace every `FILL IN:` placeholder.
### Frontmatter
**`name`** — already set by the scaffold script. Must exactly match the directory name.
**`description`** — carries the entire triggering burden. Rules:
- Imperative: "Use when..." not "This skill..."
- Specific about capabilities ("parses and validates OpenAPI specs", not "helps with APIs")
- Include indirect triggers: "even if the user doesn't mention X explicitly"
- Add "Do not use when..." only if a near-miss skill exists that could steal activations
- Hard limit: 1024 characters — count before finalizing
**Optional fields** — uncomment and fill in or remove entirely:
- `license` — include when distributing the skill externally
- `compatibility` — include if the skill requires specific tools, runtimes, or network access
- `metadata` — key-value map; use `author`, `version`, `category`
- `allowed-tools` — space-separated pre-approved tools; reduces permission prompts
### Body — include only what the agent lacks
Ask of every sentence: "Would the agent get this wrong without it?" Cut anything that answers "no."
**Include:**
- Non-obvious sequences or ordering constraints — the agent may skip or reorder steps without this
- Domain conventions the agent cannot infer from general knowledge — this is the core value a skill adds
- One default per decision point, plus one escape hatch — never a menu; menus cause the agent to pause or pick arbitrarily
- Gotchas — facts that defy reasonable assumptions; the agent will get these wrong every time without them
**Exclude:**
- Concepts the agent already knows (what JSON is, how HTTP works) — adds tokens without changing behavior
- Exhaustive option lists — pick a default; the agent doesn't benefit from choosing
- Steps the agent handles independently — over-specifying leads agents to follow unproductive paths
- Restatements of the description — it's already in context; repeating it wastes the token budget
### Patterns
**Gotchas** — highest value; place near the top:
```markdown
## Gotchas
- <Fact that defies a reasonable assumption>
- <Non-obvious naming discrepancy or hidden constraint>
```
**Default with escape hatch** (not a menu):
```markdown
Use <X> for <task>. For <edge case>, use <Y> instead.
```
**Prescriptive sequence** (when order is critical or fragile):
```markdown
Run exactly:
\`\`\`bash
<command>
\`\`\`
Do not modify flags.
```
**Checklist** (multi-step workflows):
```markdown
- [ ] Step 1: ...
- [ ] Step 2: ...
```
**Conditional reference** (progressive disclosure — load only when needed):
```
If <condition>, read `references/<file>.md`.
```
### Size budget
Keep `SKILL.md` under 500 lines. When approaching the limit:
- Move reference material to `references/<topic>.md` and load it conditionally
- Bundle repeated executable logic into `scripts/` rather than reinventing each run
## Step 3 — Add scripts (if needed)
Place executable scripts in `scripts/`. Rules for agentic scripts:
- **No interactive prompts** — agents run non-interactive; blocking on TTY input hangs indefinitely. Accept all input via flags, env vars, or stdin.
- **Expose `--help`** — concise usage output; keep it short (it enters the agent's context)
- **Structured output** — data (JSON, CSV) to stdout; diagnostics and progress to stderr
- **Idempotent** — "create if not exists"; agents may retry on failure
- **Meaningful exit codes** — `0` success, non-zero failure; document in `--help`
- **Dry-run support** — add `--dry-run` for destructive operations
If no scripts are needed, delete `scripts/README.md` and the `scripts/` directory.
## Step 4 — Add references and assets (if needed)
**`references/`** — additional documentation loaded on demand. One topic per file.
Reference conditionally from SKILL.md: `If <condition>, read references/<file>.md`.
**`assets/`** — static resources: templates, schemas, lookup tables.
Reference by relative path from SKILL.md.
If not needed, delete the placeholder READMEs and their directories.
## Step 5 — Validate
```bash
bash scripts/validate.sh <destination-dir>/<skill-name>
```
All checks must pass before the skill is considered done.

View File

@@ -0,0 +1,53 @@
# SKILL_NAME
<!-- FILL IN: One sentence describing what this skill does. -->
## What it does
<!-- FILL IN: 2–4 sentences. What task does this skill handle?
What does the agent produce or accomplish when it runs? -->
## Before you start
<!-- FILL IN: List any prerequisites the user should have ready.
Examples: research docs, a grill session, specific input files, credentials.
Delete this section if the skill has no meaningful prerequisites. -->
## Usage
Invoke via your agent tool:
```
/SKILL_NAME
```
<!-- FILL IN: Add any required or common arguments.
If the skill takes no arguments, delete the code block above and just keep the slash command. -->
<!-- OPTIONAL: Manual (human) workflow — include if the skill bundles scripts a human can run directly.
**Manual workflow:**
```bash
# FILL IN: step-by-step commands
```
-->
## Files
<!-- FILL IN: List the files in this skill directory and their purpose.
Remove rows for directories that don't exist in your skill.
Example rows are provided — replace with your actual files. -->
| File | Purpose |
|------|---------|
| `SKILL.md` | Skill instructions for agents |
| `scripts/` | FILL IN: what the scripts do |
| `references/` | FILL IN: what reference docs are here |
| `assets/` | FILL IN: what static resources are here |
<!-- OPTIONAL: Spec reference — include if this skill implements or follows an external standard.
## Spec reference
[FILL IN: Spec name](FILL IN: URL)
-->

View File

@@ -0,0 +1,101 @@
---
# SKILL.md — agentskills.io skill definition
# Fill in all FILL IN: placeholders. Remove comment blocks that don't apply.
name: SKILL_NAME
# Required. Must exactly match the parent directory name.
# Valid characters: lowercase letters, numbers, hyphens.
# Invalid: uppercase, leading/trailing/consecutive hyphens.
# Max length: 64 characters.
# Examples: my-tool, data-analyzer, pdf-processor
description: >
FILL IN: What does this skill do? State capabilities specifically
(e.g. "parses and validates OpenAPI specs", not "helps with APIs").
Use when FILL IN: when should an agent activate this skill?
Include indirect triggers: even if the user doesn't mention X explicitly.
Do not use when FILL IN: near-miss exclusions — remove this line if none apply.
# license: MIT
# Optional. License name (e.g. MIT, Apache-2.0) or relative path to a bundled
# license file. Include when distributing this skill. Omit for private/internal use.
# compatibility: Requires python3 >= 3.10 and uv
# Optional. 1–500 characters. State tool requirements, runtime versions,
# and network access needs. Omit for skills with no special environment requirements.
# metadata:
# author: your-name
# version: "1.0"
# category: general
# Optional. Arbitrary key-value map. Common keys: author, version, category.
# No restrictions on keys or values.
# allowed-tools: Bash Read Write
# Optional (experimental — support varies by client).
# Space-separated list of pre-approved tools.
# Use when tool usage is known and bounded, to reduce permission prompts.
---
<!-- ============================================================
SKILL BODY
Include only what the agent lacks:
- Domain conventions the agent cannot infer from general knowledge
- Non-obvious sequences or ordering constraints
- One default per decision point + one escape hatch (never a menu)
- Gotchas — facts that defy reasonable assumptions
Omit:
- Concepts the agent already knows
- Exhaustive option lists
- Steps the agent handles independently
- Restatements of the description
Size budget: under 500 lines / 5000 tokens.
Move reference material to references/ and load it conditionally.
Bundle repeated executable logic into scripts/.
Delete this comment block before shipping.
============================================================ -->
<!-- OPTIONAL: Gotchas section — highest-value content. Place near the top.
Add facts that defy reasonable assumptions or non-obvious constraints.
## Gotchas
- FILL IN: fact that defies a reasonable assumption
- FILL IN: non-obvious naming discrepancy or hidden constraint
-->
<!-- OPTIONAL: Multi-step workflow checklist.
## Workflow
- [ ] Step 1: FILL IN
- [ ] Step 2: FILL IN
- [ ] Step 3: FILL IN
-->
<!-- OPTIONAL: Output format template — use when the agent must produce a specific format.
## Output format
Use this structure:
```markdown
# [FILL IN: Title]
## FILL IN: Section
FILL IN: what goes here
```
-->
<!-- OPTIONAL: Conditional reference — load documentation only when needed.
If FILL IN: condition, read `references/FILL IN: filename.md`.
-->
## Instructions
FILL IN: Add your skill instructions here. Replace this section with the skill body.

View File

@@ -0,0 +1,29 @@
# assets/
Static resources bundled with this skill: templates, schemas, lookup tables,
sample data, images.
## When to add an asset
Add a file here when the skill needs a static resource that:
- Would be tedious to reproduce in instructions (a full JSON schema, a CSV
lookup table, a binary template)
- Needs to be referenced by path rather than inlined in SKILL.md
## How to reference from SKILL.md
Use a relative path from the skill root:
```markdown
Use the schema at `assets/response-schema.json` to validate output.
```
Or instruct the agent to load it conditionally:
```markdown
If validating output format, use `assets/response-schema.json`.
```
## If no assets are needed
Delete this README and the `assets/` directory entirely.

View File

@@ -0,0 +1,31 @@
# references/
Additional documentation agents load on demand. Files here extend SKILL.md
without bloating its core context.
## When to add a reference file
Move content here when SKILL.md is approaching 500 lines, or when a topic
is only relevant in specific circumstances (error handling, edge cases,
domain-specific sub-procedures).
## How to reference from SKILL.md
Load conditionally — tell the agent exactly when to read each file:
```markdown
If the API returns a non-200 status, read `references/api-errors.md`.
```
Avoid generic "see references/ for details" — the agent loads context on
demand, so give it a precise trigger condition.
## File conventions
- One topic per file — focused files mean less unnecessary context loaded
- Kebab-case filenames (e.g. `api-errors.md`, `output-formats.md`)
- Keep files under 200 lines where possible
## If no reference files are needed
Delete this README and the `references/` directory entirely.

View File

@@ -0,0 +1,47 @@
# scripts/
Executable code bundled with this skill. Agents run scripts in this directory
to perform repeatable operations rather than reinventing the logic each run.
## When to add a script
Add a script when agents independently reinvent the same logic across runs —
building the same parser, chart, or validation routine from scratch each time.
Bundle it here once, tested and reliable.
## Script requirements (agentskills.io)
Scripts must be designed for non-interactive, agentic execution:
- **No interactive prompts** — agents run in non-interactive shells.
Accept all input via flags, env vars, or stdin. A script that blocks on
TTY input hangs indefinitely.
- **Expose `--help`** — this is how agents learn your script's interface.
Keep the output concise; it enters the agent's context window.
- **Structured output** — write data (JSON, CSV, TSV) to stdout.
Write progress, warnings, and diagnostics to stderr.
- **Idempotent** — prefer "create if not exists" over "create and fail on
duplicate". Agents may retry on failure.
- **Meaningful exit codes** — `0` for success, non-zero for failure.
Use distinct codes for different failure types; document them in `--help`.
- **Dry-run support** — add `--dry-run` for destructive operations.
## Self-contained scripts
Bundle dependencies inline so the agent can run the script with a single command.
Python (PEP 723 + uv):
```python
# /// script
# dependencies = ["requests>=2.31,<3"]
# requires-python = ">=3.11"
# ///
import requests
```
```bash
uv run scripts/my-script.py
```
## If no scripts are needed
Delete this README and the `scripts/` directory entirely.

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env bash
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEMPLATES_DIR="$SKILL_DIR/../assets/templates"
usage() {
cat <<EOF
Usage: new-skill.sh <skill-name> <destination-dir>
Create a new skill scaffold by copying annotated templates to the destination.
Arguments:
skill-name Kebab-case skill identifier (e.g. my-tool, data-analyzer).
Must match the directory name exactly.
destination-dir Parent directory to create the skill in.
Examples: ~/.agents/skills/ plugins/myplugin/skills/
Output:
Creates <destination-dir>/<skill-name>/ with annotated templates ready to fill in.
Exit codes:
0 Scaffold created successfully
1 Invalid arguments or destination already exists
EOF
}
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
exit 0
fi
if [[ $# -lt 2 ]]; then
echo "Error: skill-name and destination-dir are required." >&2
echo "" >&2
usage >&2
exit 1
fi
SKILL_NAME="$1"
DEST_DIR="$2"
# Validate skill name format
if ! echo "$SKILL_NAME" | grep -qE '^[a-z0-9]+(-[a-z0-9]+)*$'; then
echo "Error: skill-name must use lowercase letters, numbers, and hyphens only." >&2
echo " No leading, trailing, or consecutive hyphens." >&2
echo " Received: '$SKILL_NAME'" >&2
exit 1
fi
# Validate templates directory exists
if [[ ! -d "$TEMPLATES_DIR" ]]; then
echo "Error: templates directory not found at '$TEMPLATES_DIR'." >&2
echo " Run this script from its original location inside the skill-write skill." >&2
exit 1
fi
# Validate destination exists
if [[ ! -d "$DEST_DIR" ]]; then
echo "Error: destination directory '$DEST_DIR' does not exist." >&2
exit 1
fi
TARGET="$DEST_DIR/$SKILL_NAME"
# Refuse to overwrite existing directory
if [[ -d "$TARGET" ]]; then
echo "Error: '$TARGET' already exists." >&2
echo " Remove it first or choose a different name." >&2
exit 1
fi
# Copy templates to destination
cp -r "$TEMPLATES_DIR" "$TARGET"
# Set skill name in templates
sed -i "s/SKILL_NAME/$SKILL_NAME/g" "$TARGET/SKILL.md"
sed -i "s/SKILL_NAME/$SKILL_NAME/g" "$TARGET/README.md"
echo "Scaffold created: $TARGET"
echo ""
echo "Next steps:"
echo " 1. Fill in $TARGET/SKILL.md — replace all FILL IN: placeholders"
echo " 2. Add scripts to scripts/ if needed (or delete the directory)"
echo " 3. Add docs to references/ if needed (or delete the directory)"
echo " 4. Add resources to assets/ if needed (or delete the directory)"
echo " 5. Validate: bash $(dirname "${BASH_SOURCE[0]}")/validate.sh $TARGET"

View File

@@ -0,0 +1,179 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<EOF
Usage: validate.sh <skill-dir>
Validate a skill directory against the agentskills.io specification.
Arguments:
skill-dir Path to the skill directory containing SKILL.md.
Exit codes:
0 All checks passed
1 One or more checks failed
EOF
}
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
exit 0
fi
if [[ $# -lt 1 ]]; then
echo "Error: skill-dir is required." >&2
echo "" >&2
usage >&2
exit 1
fi
python3 -u - "$1" <<'PYTHON'
import sys
import os
import re
skill_dir = os.path.abspath(sys.argv[1])
skill_md = os.path.join(skill_dir, "SKILL.md")
if not os.path.isfile(skill_md):
print(f"Error: '{skill_md}' not found.", file=sys.stderr)
sys.exit(1)
with open(skill_md) as f:
content = f.read()
failed = False
def ok(msg):
print(f"PASS {msg}")
def fail(msg):
global failed
print(f"FAIL {msg}", file=sys.stderr)
failed = True
# --- Parse frontmatter ---
fm_match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
if not fm_match:
fail("No valid YAML frontmatter block found (expected ---...---)")
sys.exit(1)
fm = fm_match.group(1)
body_start = fm_match.end()
# Extract name
name_m = re.search(r'^name:\s*(\S+)', fm, re.MULTILINE)
name = name_m.group(1).strip('"\'') if name_m else ""
# Extract description — inline or block scalar (> or |)
desc = ""
desc_m = re.search(r'^description:\s*([>|])\n((?:[ \t]+.+\n?)+)', fm, re.MULTILINE)
if desc_m:
raw = desc_m.group(2)
desc = re.sub(r'\s+', ' ', raw).strip()
else:
desc_inline = re.search(r'^description:\s*(.+)', fm, re.MULTILINE)
if desc_inline:
desc = desc_inline.group(1).strip()
dir_name = os.path.basename(skill_dir)
# --- Checks ---
# name present
if name:
ok(f"name present: '{name}'")
else:
fail("name field is missing or empty")
# name matches directory
if name and dir_name:
if name == dir_name:
ok(f"name '{name}' matches directory '{dir_name}'")
else:
fail(f"name '{name}' does not match directory '{dir_name}'")
# name length
if name:
if len(name) <= 64:
ok(f"name length {len(name)} chars (limit: 64)")
else:
fail(f"name '{name}' is {len(name)} chars — exceeds 64-character limit")
# name format
if name:
if re.match(r'^[a-z0-9]+(-[a-z0-9]+)*$', name):
ok(f"name format valid (kebab-case)")
else:
fail(f"name '{name}' is invalid — use lowercase letters, numbers, and hyphens only; no leading, trailing, or consecutive hyphens")
# description present
if desc:
ok(f"description present")
else:
fail("description field is missing or empty")
# description length
if desc:
dlen = len(desc)
if dlen <= 1024:
ok(f"description length {dlen} chars (limit: 1024)")
else:
fail(f"description length {dlen} chars — exceeds 1024-character limit")
# Unfilled placeholder detection — matches FILL IN: followed by actual content,
# but not backtick-quoted references like `FILL IN:` used in instructions.
PLACEHOLDER_RE = re.compile(r'(?<!`)FILL IN:[^`\n]')
# description contains unfilled placeholder
if desc and PLACEHOLDER_RE.search(desc):
fail("description still contains 'FILL IN:' placeholder — replace before shipping")
else:
if desc:
ok("description has no unfilled placeholders")
# SKILL.md line count
line_count = len(content.splitlines())
if line_count <= 500:
ok(f"SKILL.md line count {line_count} (limit: 500)")
else:
fail(f"SKILL.md line count {line_count} — exceeds 500-line limit")
# Body unfilled placeholders
body = content[body_start:]
fill_matches = PLACEHOLDER_RE.findall(body)
if fill_matches:
fail(f"SKILL.md body contains {len(fill_matches)} unfilled 'FILL IN:' placeholder(s)")
else:
ok("SKILL.md body has no unfilled placeholders")
# Scripts checks
scripts_dir = os.path.join(skill_dir, "scripts")
if os.path.isdir(scripts_dir):
scripts = [f for f in os.listdir(scripts_dir)
if os.path.isfile(os.path.join(scripts_dir, f)) and not f.endswith('.md')]
for fname in scripts:
fpath = os.path.join(scripts_dir, fname)
with open(fpath) as f:
sc = f.read()
# Interactive prompt heuristic
if re.search(r'^\s*(read\s|input\()', sc, re.MULTILINE):
fail(f"scripts/{fname}: may use interactive input (read/input detected)")
else:
ok(f"scripts/{fname}: no interactive prompts detected")
# Executable bit
if os.access(fpath, os.X_OK):
ok(f"scripts/{fname}: is executable")
else:
fail(f"scripts/{fname}: not executable — run: chmod +x {fpath}")
# Summary
print()
if not failed:
print("All checks passed.")
sys.exit(0)
else:
print("One or more checks failed.", file=sys.stderr)
sys.exit(1)
PYTHON