ESLint + Jest for agent-config files: lint, sync and scaffold SKILL.md / CLAUDE.md / AGENTS.md / .cursor rules / copilot-instructions.
Project description
skillcraft
ESLint + Jest for agent-config files. skillcraft lints, syncs and scaffolds the fragmented ecosystem of SKILL.md, CLAUDE.md and AGENTS.md — one canonical source, many managed targets, drift detection in CI.
Agent-config files are copy-pasted, drift apart across tools, and silently fail to load.
skillcraftgives them the same lint/test/sync workflow that code already enjoys.
30-second tour
uv tool install skillcraft # or: pip install skillcraft
cd your-repo
skillcraft init --name my-skill # writes a canonical AGENTS.md + .skillcraft.toml
# ...edit AGENTS.md...
skillcraft sync # generates managed SKILL.md + CLAUDE.md
skillcraft lint # validates every agent-config file in the repo
In CI, lock it down:
skillcraft sync --check # exit 1 if SKILL.md/CLAUDE.md drifted from AGENTS.md
skillcraft lint --check # exit 1 on any ERROR; --format=github annotates the PR
Why
- One source of truth. Write
AGENTS.mdonce;skillcraft syncregeneratesSKILL.mdandCLAUDE.md. Edit a target by hand andsync --checkcatches the drift. - Lint that knows the formats. Kebab-case names, frontmatter presence, import cycles, token budgets, merge-conflict markers — see the rule table.
- PR annotations.
--format=githubemits::error file=…,line=…::…so findings render inline on pull requests. - Plugin-friendly. Add a rule or a format converter in one file, no core changes. See Contributing.
Commands
| Command | Purpose |
|---|---|
skillcraft lint [--check] [-f plain|json|github] |
Run the rule set over discovered config files; exit 1 on any ERROR. |
skillcraft sync [--check] [--diff] [--adopt <file>] |
Regenerate managed targets from AGENTS.md; detect or rewrite drift. |
skillcraft init [--name <name>] |
Scaffold a minimal AGENTS.md + .skillcraft.toml. |
skillcraft version |
Print the version. |
Rules (v0.1)
| ID | Scope | Rule | Severity |
|---|---|---|---|
| SC101 | SKILL | name is kebab-case, ≤64 chars |
error |
| SC102 | SKILL | in a skills/<name>/ folder, name matches the folder |
error |
| SC103 | SKILL | description present, ≤1024 chars |
error |
| SC104 | SKILL | body ≈ <5000 tokens | warn |
| SC201 | CLAUDE | @path imports resolve, no cycles, ≤4 hops |
error/warn |
| SC202 | CLAUDE | line count <200 (warn), <500 (error) | warn/error |
| SC301 | ALL | required frontmatter present iff the format requires it | error |
| SC302 | ALL | no merge-conflict markers in the body | error |
Rule IDs are stable and never renumbered — SC1xx = SKILL, SC2xx = CLAUDE, SC3xx = universal, SC4xx = .cursor (v0.2). Every spec'd rule not yet implemented is an open good-first-issue.
How it works
AGENTS.md is the canonical source — vendor-neutral, schema-less. Richer metadata (name, description, scope, license, …) rides in invisible HTML comments that every markdown consumer ignores but skillcraft reads:
<!-- skillcraft:meta {"name":"my-skill","description":"…"} -->
# my-skill
…
Every format parses into a single ConfigDoc IR and renders back out. Same-format parse→render is lossless; an extra_frontmatter escape hatch guarantees no field is ever silently dropped. Managed targets carry a marker:
<!-- skillcraft:managed-source path=AGENTS.md sha=30b2058ede77 -->
sync --check compares each managed target against a fresh render and fails CI on any difference. See ARCHITECTURE.md for the full model.
Plugins
from skillcraft.plugins.api import Rule, Diagnostic
from skillcraft.plugins.registry import register_rule
@register_rule
class SkillNameLength(Rule):
id = "SC101"
formats = ("skill",)
severity = "error"
def check(self, doc):
if doc.meta.name and len(doc.meta.name) > 64:
yield Diagnostic(self.id, self.severity, "skill name >64 chars",
str(doc.meta.source_path))
Ship it as a package with one entry-point — skillcraft lint discovers and runs it automatically:
[project.entry-points."skillcraft.rules"]
my_rules = "my_plugin.rules"
Converters (new formats) use the identical shape under the skillcraft.converters group. Full guide: CONTRIBUTING.md.
Dogfooding
skillcraft is its own first user. This repo's SKILL.md and CLAUDE.md are generated from AGENTS.md by skillcraft sync, and CI runs skillcraft lint --check + skillcraft sync --check on every push — if the project's own configs drift or violate a rule, the build fails.
Roadmap
- v0.1 — lint (8 rules, 3 formats) + sync + init + version +
--format=github. Plugin API frozen. (this release) - v0.2 —
.cursor/rules,.claude/rules, copilot-instructions, legacy.cursorrulesmigration; statictest(fixture-based, no model calls); autofix for SC101/SC102. - v1.0 — semver-frozen API,
--fixeverywhere,skillcraft doctor, pre-commit hook,--reversepromotion, PyPI trusted publishing. - v2 — live model evals (
skillcraft test --eval).
Contributing
PRs welcome — especially new rules and format converters (each is a self-contained good-first-issue). See CONTRIBUTING.md and ARCHITECTURE.md. By contributing you agree to the Code of Conduct.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file skillcraft-0.1.0.tar.gz.
File metadata
- Download URL: skillcraft-0.1.0.tar.gz
- Upload date:
- Size: 17.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2345719f36890a49f8904da570c99eacb689ea4ccaf90b8520e25770816caaa
|
|
| MD5 |
31270add46a8abbb2fe59c1187a68f79
|
|
| BLAKE2b-256 |
2e8a136016339675d8a03636f6cca130f462011164474f3c9537e9718aa9cf0f
|
Provenance
The following attestation bundles were made for skillcraft-0.1.0.tar.gz:
Publisher:
release.yml on cloudroad-io/skillcraft
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
skillcraft-0.1.0.tar.gz -
Subject digest:
d2345719f36890a49f8904da570c99eacb689ea4ccaf90b8520e25770816caaa - Sigstore transparency entry: 2124580011
- Sigstore integration time:
-
Permalink:
cloudroad-io/skillcraft@1f8d962c35d688947ab08f235e5352c372b4eeeb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/cloudroad-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1f8d962c35d688947ab08f235e5352c372b4eeeb -
Trigger Event:
push
-
Statement type:
File details
Details for the file skillcraft-0.1.0-py3-none-any.whl.
File metadata
- Download URL: skillcraft-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bce81bd632467b7b2471c3bd7766eeb7ed76449446e0f6519169a398ad24adc5
|
|
| MD5 |
f8f9cd363c92f728833d60bc97c6db5a
|
|
| BLAKE2b-256 |
ba0bb21b9189c6e9014c2c8ca9b234b87dd1d2f05c23635d62a2a3c3bd79efc2
|
Provenance
The following attestation bundles were made for skillcraft-0.1.0-py3-none-any.whl:
Publisher:
release.yml on cloudroad-io/skillcraft
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
skillcraft-0.1.0-py3-none-any.whl -
Subject digest:
bce81bd632467b7b2471c3bd7766eeb7ed76449446e0f6519169a398ad24adc5 - Sigstore transparency entry: 2124580035
- Sigstore integration time:
-
Permalink:
cloudroad-io/skillcraft@1f8d962c35d688947ab08f235e5352c372b4eeeb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/cloudroad-io
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1f8d962c35d688947ab08f235e5352c372b4eeeb -
Trigger Event:
push
-
Statement type: