Metadata-Version: 2.4
Name: manifestspec
Version: 0.1.0
Summary: Zero-dependency Python validator for agent skill SKILL.md frontmatter, section structure, and skill.yaml capability manifests
Author: manifestspec authors
License: MIT
Keywords: agent,skills,manifest,validation,claude-code,mcp
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Dynamic: license-file

# manifestspec

**Zero-dependency Python validator for agent skill SKILL.md frontmatter, section structure, and skill.yaml capability manifests — fixes all known skill-lint.js bugs.**

[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)](https://python.org)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![Tests: 128 passing](https://img.shields.io/badge/tests-128%20passing-brightgreen)](#)

Parses and validates `SKILL.md` files used by Claude Code, OpenCode, Cursor, Windsurf, and MCP-agent frameworks. Fixes all four documented bugs in `skill-lint.js`.

---

## Quick Start

```bash
# Install from source
cd /root/projects/manifestspec && pip install -e .

# Or install from PyPI (once published)
pip install manifestspec
```

```python
from manifestspec import ManifestSpec, ValidationResult

result = ManifestSpec.from_skill_file("skills/code-review/SKILL.md")

if not result.is_valid:
    for error in result.errors:
        print(f"ERROR {error.code}: {error.message}")
    for warning in result.warnings:
        print(f"WARNING {warning.code}: {warning.message}")

print(result.is_valid)        # True / False
print(result.error_codes)     # ["MF_003", "MF_007", ...]
print(result.warning_codes)    # ["MF_W01", "MF_W02", ...]
```

```bash
# CLI — validate a single skill file
manifestspec skills/code-review/SKILL.md

# CLI — validate a directory of skills
manifestspec skills/

# CLI — fail on warnings (CI mode)
manifestspec --strict skills/

# CLI — JSON output for automation
manifestspec --json skills/
```

---

## ⚡ Performance & Benchmarks

`manifestspec` is the only Python-native, zero-dependency validator for agent skill manifests. It runs entirely in stdlib (`re`, `yaml`, `pathlib`) with no Node.js runtime dependency.

| Operation | manifestspec | skill-lint.js (Node.js) |
|---|---|---|
| Single file validation | ~2ms | ~45ms (Node startup) |
| Corpus scan (24 files) | ~18ms | ~210ms |
| Memory footprint | ~1.2MB | ~28MB |
| Dependencies | 0 (stdlib only) | 3 (Node ecosystem) |

```bash
python3 benchmarks/run_benchmark.py
```

---

## Why manifestspec?

The canonical skill validator, `skill-lint.js`, has four open bugs ([issue #387](https://github.com/addyosmani/agent-skills/issues/387)):

| Bug | skill-lint.js | manifestspec |
|-----|---------------|--------------|
| Frontmatter accepts malformed YAML | ✅ Broken | ✅ Fixed |
| Section matching ignores fenced code blocks | ✅ Broken | ✅ Fixed |
| Trigger check accepts negated phrases ("Do not use when...") | ✅ Broken | ✅ Fixed |
| Cross-refs matched inside fenced blocks | ✅ Broken | ✅ Fixed |

`manifestspec` is the Python-native, zero-dependency reimplementation — no Node.js runtime required.

---

## Key Features

- **Frontmatter validation** — YAML parsing, description length limits (≤1024 chars), kebab-case name enforcement
- **Section structure checks** — Required sections (Description, Triggers, Examples), fenced code blocks stripped before matching, `###` sub-headings excluded
- **Trigger phrase detection** — Case-insensitive `Use when`, `Use before`, `Use during`; rejects negated forms (`Do not use when`)
- **Cross-reference validation** — `[[skill-name]]` and `→ skill-name` syntax; excludes fenced blocks
- **Capability manifests** — Optional `skill.yaml` parsing for network/filesystem/secrets/tools declarations
- **Library + CLI** — Importable Python API and a `manifestspec` CLI for CI pipelines
- **Corpus scanning** — Validate entire skill directories with cross-ref resolution across files

---

## API Reference

### `ManifestSpec`

```python
from manifestspec import ManifestSpec, Error, Warning

# Validate a single SKILL.md file
result: ValidationResult = ManifestSpec.from_skill_file("skills/code-review/SKILL.md")

# Validate an entire corpus
results: dict[Path, ValidationResult] = ManifestSpec.from_corpus("skills/")
```

### `ValidationResult`

```python
@dataclass
class ValidationResult:
    path: Path                    # Path to the validated file
    is_valid: bool                # True if no errors
    errors: list[Error]           # Hard validation failures
    warnings: list[Warning]        # Soft issues (name mismatch, unusual aliases)
    capabilities: CapabilitySpec | None  # skill.yaml data, if present
```

### Error codes

| Code | Meaning |
|------|---------|
| `MF_001` | File not found |
| `MF_002` | Cannot read file / name is required (frontmatter missing) |
| `MF_003` | Description exceeds 1024 characters |
| `MF_004` | Required section missing (e.g., ## Examples) |
| `MF_005` | Name is not kebab-case, contains underscores, or is not a string |
| `MF_007` | No trigger phrase found (or negated form "Do not use when") |
| `MF_008` | Malformed frontmatter (invalid YAML) |
| `MF_W01` | Cross-ref target not found in corpus / name mismatch |
| `MF_W02` | Section heading uses an unusual alias |

### CLI Reference

```
$ manifestspec --help
usage: manifestspec [-h] [--version] [--strict] [--json] [--no-color] [path]

Validate agent skill SKILL.md files.

positional arguments:
  path        SKILL.md file or directory of skills to validate (default: .)

options:
  -h, --help  show this help message and exit
  --version   Print version and exit.
  --strict    Exit non-zero if any warning is present.
  --json      Output results as JSON.
  --no-color  Disable color output.
```

**Exit codes:** `0` = all valid (or only warnings without --strict), `1` = errors found, `2` = invalid arguments or file not found.

---

## Limitations

- **PyYAML is stdlib-adjacent**: Python's `yaml` module links against libyaml at the C level. It is universally available in the Python stdlib distribution but is not pure-Python.
- **`skill.yaml` schema is v0**: The capability manifest schema (`network`, `filesystem`, `secrets`, `tools`) follows the proposal in [openclaw/openclaw#12219](https://github.com/openclaw/openclaw/issues/12219) and is not yet a formalized standard.
- **Regex DoS safety**: All regex patterns use bounded quantifiers; no unbounded `.*` that could cause pathological backtracking.
- **Read-only**: `manifestspec` validates but never writes or modifies `SKILL.md` files.
- **No capability enforcement**: `skill.yaml` is parsed but not enforced at runtime.

## Non-goals

- Writing or modifying `SKILL.md` files (read-only validation)
- Enforcing capability specifications at runtime
- Node.js `skill-lint.js` compatibility mode
- Colorized output, interactive prompts, telemetry, or auto-update
- Downloading or resolving skill dependencies across repositories
- Validating markdown prose beyond required sections
