Skip to main content
Content checks validate the quality and completeness of the SKILL.md body content, including token budgets, examples, script references, and path validation.

Check List

This dimension includes 11 checks (all quality suggestions, none spec-required).
Check IDSeverityWhat It Validates
content.body-not-emptyWARNINGBody has meaningful content
content.line-budgetWARNINGBody is under 500 lines
content.token-budgetWARNINGBody is under 5000 tokens
content.metadata-token-budgetINFOMetadata under 150 tokens
content.has-examplesINFOContent contains code examples
content.description-actionableINFODescription has activation phrasing
content.reference-depthWARNINGReferences max 1 level deep
content.scripts-referencedWARNINGScripts mentioned in body
content.script-paths-existWARNINGScript paths exist on disk
content.asset-paths-existWARNINGAsset paths exist on disk
content.compatibility-prereqsINFOCommand runners documented

content.body-not-empty

Severity: WARNING Validates that SKILL.md body has meaningful content (quality suggestion; spec allows empty body).

What It Checks

  1. Body is not empty after trimming whitespace
  2. Body has at least 50 characters of content

Implementation

def run(self, skill: Skill) -> CheckResult:
    body = skill.body.strip()

    if not body:
        return self._fail(
            "SKILL.md body is empty",
            location=self._skill_md_location(skill),
        )

    if len(body) < 50:
        return self._fail(
            f"SKILL.md body is too short ({len(body)} characters)",
            details={"length": len(body), "minimum": 50},
            location=self._skill_md_location(skill),
        )

    return self._pass(
        f"SKILL.md body has content ({len(body)} characters)",
        location=self._skill_md_location(skill),
    )

content.line-budget

Severity: WARNING Validates that body content stays under 500 lines for readability.

Constants

MAX_LINE_COUNT = 500

Implementation

def run(self, skill: Skill) -> CheckResult:
    lines = skill.body.split("\n")
    line_count = len(lines)

    if line_count > MAX_LINE_COUNT:
        return self._fail(
            f"Body exceeds {MAX_LINE_COUNT} lines (got {line_count})",
            details={"line_count": line_count, "max_lines": MAX_LINE_COUNT},
            location=self._skill_md_location(skill),
        )

    return self._pass(
        f"Body within line budget ({line_count}/{MAX_LINE_COUNT})",
        location=self._skill_md_location(skill),
    )

content.token-budget

Severity: WARNING Validates that body instructions stay under the spec-recommended 5,000 token budget.

Constants

MAX_BODY_TOKENS = 5000

Implementation

Uses estimate_tokens() from skill_lab.core.tokens for token estimation.
from skill_lab.core.tokens import estimate_tokens

def run(self, skill: Skill) -> CheckResult:
    tokens = estimate_tokens(skill.body)

    if tokens > MAX_BODY_TOKENS:
        return self._fail(
            f"Body exceeds {MAX_BODY_TOKENS} token budget ({tokens} estimated)",
            details={"estimated_tokens": tokens, "max_tokens": MAX_BODY_TOKENS},
            location=self._skill_md_location(skill),
        )

    return self._pass(
        f"Body within token budget ({tokens}/{MAX_BODY_TOKENS})",
        location=self._skill_md_location(skill),
    )
Token estimation uses a simple heuristic (characters / 4). Actual token counts may vary by model.

content.metadata-token-budget

Severity: INFO Validates that metadata (name + description) fits the ~100-token discovery budget for efficient skill matching.

Constants

MAX_METADATA_TOKENS = 150

Implementation

def run(self, skill: Skill) -> CheckResult:
    if skill.metadata is None:
        return self._pass(
            "No metadata to check",
            location=self._skill_md_location(skill),
        )

    combined = f"{skill.metadata.name} {skill.metadata.description}"
    tokens = estimate_tokens(combined)

    if tokens > MAX_METADATA_TOKENS:
        return self._fail(
            f"Metadata exceeds {MAX_METADATA_TOKENS} token budget ({tokens} estimated)",
            details={"estimated_tokens": tokens, "max_tokens": MAX_METADATA_TOKENS},
            location=self._skill_md_location(skill),
        )

    return self._pass(
        f"Metadata within token budget ({tokens}/{MAX_METADATA_TOKENS})",
        location=self._skill_md_location(skill),
    )
Keeping metadata concise improves skill discovery performance when agents scan multiple skills.

content.has-examples

Severity: INFO Validates that content contains code examples to help agents understand usage.

Detection Patterns

CODE_EXAMPLE_PATTERNS = [
    r"```",  # Fenced code blocks
    r"^\s{4,}\S",  # Indented code blocks (4+ spaces)
    r"<example>",  # Example tags
]

Implementation

def run(self, skill: Skill) -> CheckResult:
    body = skill.body

    for pattern in CODE_EXAMPLE_PATTERNS:
        if re.search(pattern, body, re.MULTILINE):
            return self._pass(
                "Content contains code examples",
                location=self._skill_md_location(skill),
            )

    return self._fail(
        "Content does not contain code examples",
        details={"suggestion": "Add code examples using fenced code blocks (```)"},
        location=self._skill_md_location(skill),
    )

Example

# ✅ Has examples (fenced code)

```python
import requests
response = requests.get("https://api.example.com")
```

# ✅ Has examples (indented)

    import requests
    response = requests.get("https://api.example.com")

# ✅ Has examples (example tags)

<example>
usage example here
</example>

content.description-actionable

Severity: INFO Validates that description includes activation phrasing to help agents match tasks to skills.

Activation Phrases

_ACTIVATION_PHRASES = (
    "use when",
    "use for",
    "use this",
    "trigger",
    "activate",
    "invoke",
    "run when",
    "run this",
    "helps with",
    "designed for",
    "intended for",
    "works with",
)

Implementation

def run(self, skill: Skill) -> CheckResult:
    if skill.metadata is None or not skill.metadata.description.strip():
        return self._pass(
            "No description to check (other checks catch this)",
            location=self._skill_md_location(skill),
        )

    desc_lower = skill.metadata.description.lower()
    for phrase in _ACTIVATION_PHRASES:
        if phrase in desc_lower:
            return self._pass(
                f"Description contains activation phrase: '{phrase}'",
                location=self._skill_md_location(skill),
            )

    return self._fail(
        "Description lacks activation phrasing for agent matching",
        details={
            "suggestion": "Add 'Use when...' or 'Designed for...' phrasing "
            "to help agents match tasks to this skill",
        },
        location=self._skill_md_location(skill),
    )

Example

# ❌ Not actionable
---
description: A helper for API calls
---

# ✅ Actionable
---
description: Use when making authenticated REST API calls with retries
---

content.reference-depth

Severity: WARNING Validates that references are not too deeply nested (max 1 level).

Constants

MAX_REFERENCE_DEPTH = 1

Implementation

def run(self, skill: Skill) -> CheckResult:
    references_path = skill.path / "references"

    if not references_path.exists() or not references_path.is_dir():
        return self._pass(
            "No references folder to check",
        )

    deep_paths: list[str] = []

    def check_depth(path: Path, current_depth: int) -> None:
        if current_depth > MAX_REFERENCE_DEPTH:
            deep_paths.append(str(path.relative_to(skill.path)))
            return

        if path.is_dir():
            for item in path.iterdir():
                if item.is_dir():
                    check_depth(item, current_depth + 1)

    check_depth(references_path, 0)

    if deep_paths:
        return self._fail(
            f"References nested too deeply (max {MAX_REFERENCE_DEPTH} level)",
            details={"deep_paths": deep_paths},
            location=str(references_path),
        )

    return self._pass(
        f"References within depth limit ({MAX_REFERENCE_DEPTH} level max)",
        location=str(references_path),
    )

Example

# ✅ Valid structure (1 level deep)
my-skill/
  references/
    api/
      reference.md
    guide.md

# ❌ Too deep (2 levels)
my-skill/
  references/
    api/
      v1/
        reference.md  # Too deep!

content.scripts-referenced

Severity: WARNING Validates that scripts in scripts/ are mentioned in the SKILL.md body.

Implementation

def run(self, skill: Skill) -> CheckResult:
    scripts_path = skill.path / "scripts"

    if not scripts_path.exists() or not scripts_path.is_dir():
        return self._pass("No scripts folder present (optional)")

    script_files = [
        item.name
        for item in scripts_path.iterdir()
        if item.is_file() and item.suffix.lower() in VALID_SCRIPT_EXTENSIONS
    ]

    if not script_files:
        return self._pass(
            "No script files in scripts/",
            location=str(scripts_path),
        )

    body = skill.body
    mentioned = [f for f in script_files if f in body]

    if mentioned:
        return self._pass(
            f"Script(s) mentioned in body: {', '.join(mentioned)}",
            location=self._skill_md_location(skill),
        )

    return self._fail(
        f"Scripts exist but none mentioned in body: {', '.join(sorted(script_files))}",
        details={
            "script_files": sorted(script_files),
            "suggestion": "List available scripts in your SKILL.md so the agent knows they exist",
        },
        location=self._skill_md_location(skill),
    )
If you include scripts but don’t mention them in the body, agents won’t know they exist.

content.script-paths-exist

Severity: WARNING Validates that script paths referenced in body (e.g., scripts/setup.py) exist on disk.

Detection Pattern

_SCRIPT_EXT_PATTERN = "|".join(ext.lstrip(".") for ext in sorted(VALID_SCRIPT_EXTENSIONS))
_SCRIPT_PATH_RE = re.compile(rf"(?<![/\w-])scripts/[\w.-]+\.(?:{_SCRIPT_EXT_PATTERN})\b")
Matches patterns like:
  • scripts/setup.py
  • scripts/build.sh
  • scripts/helper.js

Implementation

def run(self, skill: Skill) -> CheckResult:
    refs = _SCRIPT_PATH_RE.findall(skill.body)

    if not refs:
        return self._pass(
            "No script path references in body",
            location=self._skill_md_location(skill),
        )

    # Deduplicate while preserving order
    seen: set[str] = set()
    unique_refs: list[str] = []
    for ref in refs:
        if ref not in seen:
            seen.add(ref)
            unique_refs.append(ref)

    missing = [ref for ref in unique_refs if not (skill.path / ref).exists()]

    if missing:
        return self._fail(
            f"Script path(s) not found on disk: {', '.join(missing)}",
            details={"missing_paths": missing, "all_references": unique_refs},
            location=self._skill_md_location(skill),
        )

    return self._pass(
        f"All referenced script paths exist ({len(unique_refs)} verified)",
        location=self._skill_md_location(skill),
    )

content.asset-paths-exist

Severity: WARNING Validates that asset paths referenced in body (e.g., assets/logo.png) exist on disk.

Detection Pattern

_ASSET_PATH_RE = re.compile(r"(?<![/\w-])assets/[\w.-]+\.\w+\b")
Matches patterns like:
  • assets/logo.png
  • assets/diagram.svg
  • assets/template.json

Implementation

def run(self, skill: Skill) -> CheckResult:
    refs = _ASSET_PATH_RE.findall(skill.body)

    if not refs:
        return self._pass(
            "No asset path references in body",
            location=self._skill_md_location(skill),
        )

    # Deduplicate while preserving order
    seen: set[str] = set()
    unique_refs: list[str] = []
    for ref in refs:
        if ref not in seen:
            seen.add(ref)
            unique_refs.append(ref)

    missing = [ref for ref in unique_refs if not (skill.path / ref).exists()]

    if missing:
        return self._fail(
            f"Asset path(s) not found on disk: {', '.join(missing)}",
            details={"missing_paths": missing, "all_references": unique_refs},
            location=self._skill_md_location(skill),
        )

    return self._pass(
        f"All referenced asset paths exist ({len(unique_refs)} verified)",
        location=self._skill_md_location(skill),
    )

content.compatibility-prereqs

Severity: INFO Validates that command runners referenced in body are documented in the compatibility field.

Command Runner Mapping

_RUNNER_TO_RUNTIME: dict[str, str] = {
    "npx": "Node.js",
    "uvx": "uv",
    "bunx": "Bun",
    "deno run": "Deno",
    "go run": "Go",
    "pipx run": "pipx",
}

Implementation

def run(self, skill: Skill) -> CheckResult:
    matches = _RUNNER_RE.findall(skill.body)

    if not matches:
        return self._pass(
            "No command runners referenced in body",
            location=self._skill_md_location(skill),
        )

    # Normalize matches to canonical form
    runners_found: dict[str, str] = {}
    for match in matches:
        canonical = match.lower()
        if canonical not in runners_found:
            runners_found[canonical] = _RUNNER_TO_RUNTIME.get(
                canonical, _RUNNER_TO_RUNTIME.get(match, match)
            )

    # Get compatibility field
    compat = ""
    if skill.metadata and skill.metadata.raw.get("compatibility"):
        compat_val = skill.metadata.raw["compatibility"]
        if isinstance(compat_val, str):
            compat = compat_val.lower()

    missing: dict[str, str] = {}
    for runner, runtime in runners_found.items():
        if runtime.lower() not in compat:
            missing[runner] = runtime

    if missing:
        pairs = [f"{r} (needs {rt})" for r, rt in missing.items()]
        return self._fail(
            f"Command runners missing from compatibility: {', '.join(pairs)}",
            details={
                "missing_runners": missing,
                "suggestion": "Add runtime prerequisites to the compatibility frontmatter field",
            },
            location=self._skill_md_location(skill),
        )

    return self._pass(
        "All command runners documented in compatibility field",
        location=self._skill_md_location(skill),
    )

Example

# ❌ Missing compatibility info
---
name: api-helper
description: Use when making API calls
---

Run `npx fetch-api` to test the endpoint.

# ✅ Documented in compatibility
---
name: api-helper
description: Use when making API calls
compatibility: Requires Node.js
---

Run `npx fetch-api` to test the endpoint.

File Location

Content checks are implemented in: src/skill_lab/checks/static/content.py

Build docs developers (and LLMs) love