Skip to main content
Naming checks validate skill name requirements defined in the Agent Skills specification.

Check List

This dimension includes 1 behavioral check and 2 schema checks (all spec-required).
Check IDSeveritySpecWhat It Validates
naming.requiredERRORYesName field is present in frontmatter
naming.formatERRORYesName follows format rules
naming.matches-directoryERRORYesName matches parent directory

naming.required

Severity: ERROR | Spec Required: Yes | Type: Schema Check Validates that the name field is present in frontmatter.

What It Checks

  1. Verifies frontmatter exists
  2. Checks that name field is present
  3. Checks that name is not empty or whitespace-only

Schema Definition

FieldRule(
    check_id="naming.required",
    check_name="Name Required",
    description="Name field is present in frontmatter",
    severity=Severity.ERROR,
    dimension=EvalDimension.NAMING,
    spec_required=True,
    field_name="name",
    source="metadata",
    required=True,
    no_metadata_context="check name",
    fail_message="Name field is missing or empty in frontmatter",
    pass_message="Name field present: '{value}'",
)

Example

# ❌ Fails - no name field
---
description: My skill
---

# ❌ Fails - empty name
---
name: ""
description: My skill
---

# ✅ Passes
---
name: my-skill
description: My skill
---

naming.format

Severity: ERROR | Spec Required: Yes | Type: Schema Check Validates that the name follows the Agent Skills specification format rules.

Format Rules

  1. Lowercase only - letters and numbers
  2. Hyphens allowed - but not at start or end
  3. No consecutive hyphens - -- is not allowed
  4. Max 64 characters
  5. Pattern: ^[a-z0-9][a-z0-9-]*[a-z0-9]$ or ^[a-z0-9]$ (single char)

Schema Definition

FieldRule(
    check_id="naming.format",
    check_name="Name Format",
    description="Name is lowercase, hyphen-separated, max 64 chars",
    severity=Severity.ERROR,
    dimension=EvalDimension.NAMING,
    spec_required=True,
    field_name="name",
    source="metadata",
    required=True,
    max_length=64,
    regex_pattern=r"^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$",
    regex_fail_message=(
        "Name must be lowercase letters, numbers, and hyphens only, "
        "and must not start or end with a hyphen"
    ),
    no_consecutive="--",
    no_consecutive_message="Name should not contain consecutive hyphens",
    no_metadata_context="validate name",
    fail_message="No name to validate",
    pass_message="Name '{value}' follows format rules",
)

Examples

# ✅ Valid names
---
name: my-skill
---

---
name: api-helper-v2
---

---
name: doc-writer
---

---
name: x
---

# ❌ Invalid names
---
name: My-Skill  # Uppercase not allowed
---

---
name: -my-skill  # Cannot start with hyphen
---

---
name: my-skill-  # Cannot end with hyphen
---

---
name: my--skill  # No consecutive hyphens
---

---
name: my_skill  # Underscores not allowed
---
The format check accumulates multiple errors. If a name violates several rules (e.g., too long AND has consecutive hyphens), all violations will be reported in a single failure message.

naming.matches-directory

Severity: ERROR | Spec Required: Yes | Type: Behavioral Check Validates that the skill name matches the parent directory name (spec requirement).

What It Checks

  1. Extracts name from frontmatter
  2. Gets parent directory name
  3. Normalizes both using NFKC Unicode normalization
  4. Compares for exact match

Why NFKC Normalization?

Unicode normalization ensures consistent handling of special characters across different filesystems:
  • macOS uses NFD (decomposed) normalization
  • Linux/Windows use NFC (composed) normalization
  • NFKC provides compatibility normalization

Implementation

import unicodedata

def run(self, skill: Skill) -> CheckResult:
    if skill.metadata is None or not skill.metadata.name:
        return self._fail(
            "No name to validate",
            location=self._skill_md_location(skill),
        )

    name = unicodedata.normalize("NFKC", skill.metadata.name)
    directory_name = unicodedata.normalize("NFKC", skill.path.name)

    if name != directory_name:
        return self._fail(
            f"Name '{name}' does not match directory name '{directory_name}'",
            details={"name": name, "directory": directory_name},
            location=self._skill_md_location(skill),
        )

    return self._pass(
        f"Name '{name}' matches directory name",
        location=self._skill_md_location(skill),
    )

Example

# ✅ Correct structure
my-skill/
  SKILL.md  # name: my-skill

# ❌ Name mismatch
my-skill/
  SKILL.md  # name: different-name
This check ensures that skill directories are self-describing. The directory name should always match the skill name for easy identification.

File Location

Naming checks are implemented in:
  • Behavioral check: src/skill_lab/checks/static/naming.py (NameMatchesDirectoryCheck)
  • Schema checks: src/skill_lab/checks/static/schema.py (FRONTMATTER_SCHEMA list)

Build docs developers (and LLMs) love