Skip to main content

skill-forge

Point it at your code. Get a valid Claude skill. No API key required.

skill-forge turns a codebase, package, or doc into a well-formed Claude SKILL.md — and the skill it writes is valid by construction.

pip install claude-skill-forge       # the CLI it installs is `skill-forge`
skill-forge forge ./my-tool          # writes .claude/skills/my-tool/SKILL.md

PyPI Python License Dependencies


Why

Writing a good skill is fiddly: the frontmatter has to be exactly right, the name has to match its directory, and the description — the one field an agent actually reads to decide whether to load the skill — has to say when to trigger, inside a tight character budget. Get any of it wrong and the skill is silently undiscoverable.

Most "ask an LLM to write my SKILL.md" approaches are non-reproducible, need an API key, and still emit invalid frontmatter. skill-forge is different on two axes:

  1. Offline & deterministic by default. It reads your source with static analysis — no code execution, no network, no key — and emits the skill. Same input → same output. The optional --llm flag only refines the prose; it never owns the structure.
  2. Valid by construction. Every generated skill passes the built-in linter (the same rules a skill must satisfy to be discoverable). skill-forge refuses to write a skill that doesn't lint clean, so you never ship a broken one.

It's the forge half of a pair: forge generates, skillcheck checks. The linter is bundled here too (skill-forge lint) so the tool stands alone.

Install

The package is published on PyPI as claude-skill-forge; it installs a CLI named skill-forge (and the import package is skill_forge).

pip install claude-skill-forge               # core: zero runtime dependencies
pip install 'claude-skill-forge[anthropic]'  # adds the optional --llm refiner

Quickstart (30 seconds)

# From a Python/Node project, a package, or a single doc:
skill-forge forge ./my-tool                 # -> .claude/skills/my-tool/SKILL.md
skill-forge forge ./README.md               # generate from docs
skill-forge forge ./pkg --name my-skill     # override the skill name
skill-forge forge ./my-tool --stdout        # preview, don't write
skill-forge forge ./my-tool --llm           # sharpen the prose with Claude

# Validate any skill / folder of skills (the bundled checker):
skill-forge lint .claude/skills

# CI drift guard — fail if the skill no longer matches the code:
skill-forge check ./my-tool --name my-tool

# Refresh a drifted skill, keeping the parts you wrote by hand:
skill-forge update ./my-tool
skill-forge update ./my-tool --diff         # preview the merge

What it extracts

Source What it reads
Python pyproject.toml / setup.cfg (name, description, keywords, [project.scripts]), __all__ and public defs/classes via ast, and argparse / click / typer subcommands — never importing or running your code
Node package.json (name, description, keywords, bin), TS/JS detection, README
Docs A markdown/rst/txt file: H1 title, first paragraph, section headings, fenced code blocks
Any directory README + a language histogram of the file tree
A CLI tool skill-forge forge --from-cli "mytool" captures and parses mytool --help

The result is a complete SKILL.md: trigger-oriented description, a ## When to use section, an overview, and ## Commands / ## API / ## Usage sections built from what was found.

Use it from Python

from skill_forge import forge, render_skill, write_skill

draft = forge("./my-tool")          # a validated SkillDraft
print(render_skill(draft))          # the SKILL.md text
write_skill(draft, ".claude/skills")

The --llm refiner (optional)

--llm sends the extracted signals (not your source) to Claude to sharpen the description's trigger phrasing and tighten the body. It is fail-safe: if the model is unavailable it tells you how to fix it, and if its output is anything but a valid improvement, skill-forge keeps the deterministic draft. You never get a worse skill than the offline path. Set ANTHROPIC_API_KEY and install the extra to use it.

CI: catch stale skills

skill-forge check regenerates the skill in memory and diffs it against the one on disk, exiting non-zero if they differ — so a skill that drifted from the code it describes fails the build:

- run: pip install claude-skill-forge
- run: skill-forge check ./my-tool --name my-tool

Updating a skill you've edited

check tells you a skill drifted. The problem is what comes next: forge --force overwrites the file, taking every hand edit with it — and hand edits are the point, since a generated skill is a first draft.

skill-forge update regenerates and merges, section by section:

$ skill-forge update ./my-tool
✓ updated .claude/skills/my-tool/SKILL.md
  refreshed: description, Notes
  added: Configuration
  kept your edits: When to use, Gotchas

Use --diff to see the merge before it's written.

To do that it has to tell the generator changed this from a human changed this, and a two-way diff can't. So forge records a .skill-forge.json beside the skill holding a hash of each section as generated. Then each section has an unambiguous answer:

on disk outcome
matches the manifest untouched take the regenerated version
differs from the manifest you edited it keep yours
absent new from the generator add it
not in the regenerated output you wrote it keep it

Commit .skill-forge.json. Without it — a skill forged before this existed, or the file deleted — everything present is treated as hand-edited, so update only adds and never silently reverts prose you wrote. That's the safe direction to be wrong in, and it says so in the output.

Your edits stay marked as edits across repeated updates: re-baselining records the newly generated content but preserves the original hash for every section it kept, so an edit isn't quietly adopted as generated output on the next run.

What this is not

  • Not a replacement for judgment. Generated skills are a strong first draft. The body is assembled from your structure, not written from deep understanding — read it, trim it, and add the hard-won "do this, not that" guidance only you know.
  • Not a runtime. It writes SKILL.md files; it does not execute skills.
  • Not magic prose. The offline path is deterministic and a little formulaic by design. Reach for --llm when you want the description polished.
  • It does not run your code. The only time it executes anything is the explicit --from-cli flag, which runs <cmd> --help with no shell and a timeout.

Configuration

Environment overrides (all optional):

Variable Default Purpose
SKILL_FORGE_OUTDIR .claude/skills Default output directory
SKILL_FORGE_VERSION 0.1.0 Version stamped on generated skills
SKILL_FORGE_MODEL_ID claude-haiku-4-5 Model used by --llm
ANTHROPIC_API_KEY — Required for --llm

Development

pip install -e ".[dev]"
ruff check .
pytest -q

The design is pinned in SPEC.md — the single source of truth for the public API and behavior. See CONTRIBUTING.md before opening a PR.

License

MIT — see LICENSE.

Release files for claude-skill-forge 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for claude-skill-forge 0.2.0
File Size Uploaded
claude_skill_forge-0.2.0.tar.gz 48.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for claude-skill-forge 0.2.0
File Interpreter ABI Platform
claude_skill_forge-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 88.8 kB

Release files / claude_skill_forge-0.2.0.tar.gz

Download URL claude_skill_forge-0.2.0.tar.gz
Size 48.5 kB
Tags Source
SHA-256 checksum
How to use checksums
d04bd5bccca941bb245711faa511d8ad4904d5515683437131331bc4cf9e0891
BLAKE2b-256 checksum
How to use checksums
998b00c61e7c8b018bb28d45682bf210b5fca038209ba9d9d389a12d87fc4510
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.3

Release files / claude_skill_forge-0.2.0-py3-none-any.whl

Download URL claude_skill_forge-0.2.0-py3-none-any.whl
Size 40.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8f46c4fe8241b20bdbc4234a291749bea8ebcdcce20e95f50753a83838244fe5
BLAKE2b-256 checksum
How to use checksums
4a1c9573efb1ec91236a2f0c5a15490f9b9b63693baf778a331dfaa5923a2819
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.3

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page