claude-skill-forge 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_skill_forge-0.1.0/.github/workflows/ci.yml +39 -0
- claude_skill_forge-0.1.0/.gitignore +29 -0
- claude_skill_forge-0.1.0/CHANGELOG.md +28 -0
- claude_skill_forge-0.1.0/CONTRIBUTING.md +48 -0
- claude_skill_forge-0.1.0/LICENSE +21 -0
- claude_skill_forge-0.1.0/PKG-INFO +178 -0
- claude_skill_forge-0.1.0/README.md +147 -0
- claude_skill_forge-0.1.0/SPEC.md +328 -0
- claude_skill_forge-0.1.0/examples/README.md +25 -0
- claude_skill_forge-0.1.0/examples/demo.sh +32 -0
- claude_skill_forge-0.1.0/examples/sample_tool/pyproject.toml +8 -0
- claude_skill_forge-0.1.0/examples/sample_tool/sample_tool/__init__.py +11 -0
- claude_skill_forge-0.1.0/examples/sample_tool/sample_tool/cli.py +28 -0
- claude_skill_forge-0.1.0/pyproject.toml +55 -0
- claude_skill_forge-0.1.0/skill_forge/__init__.py +56 -0
- claude_skill_forge-0.1.0/skill_forge/analyzers/__init__.py +66 -0
- claude_skill_forge-0.1.0/skill_forge/analyzers/base.py +203 -0
- claude_skill_forge-0.1.0/skill_forge/analyzers/cli_help.py +99 -0
- claude_skill_forge-0.1.0/skill_forge/analyzers/docs.py +35 -0
- claude_skill_forge-0.1.0/skill_forge/analyzers/generic.py +47 -0
- claude_skill_forge-0.1.0/skill_forge/analyzers/node.py +73 -0
- claude_skill_forge-0.1.0/skill_forge/analyzers/python.py +354 -0
- claude_skill_forge-0.1.0/skill_forge/cli.py +166 -0
- claude_skill_forge-0.1.0/skill_forge/config.py +32 -0
- claude_skill_forge-0.1.0/skill_forge/describe.py +101 -0
- claude_skill_forge-0.1.0/skill_forge/errors.py +27 -0
- claude_skill_forge-0.1.0/skill_forge/frontmatter.py +77 -0
- claude_skill_forge-0.1.0/skill_forge/generate.py +84 -0
- claude_skill_forge-0.1.0/skill_forge/llm.py +121 -0
- claude_skill_forge-0.1.0/skill_forge/models.py +56 -0
- claude_skill_forge-0.1.0/skill_forge/skill.py +39 -0
- claude_skill_forge-0.1.0/skill_forge/slug.py +41 -0
- claude_skill_forge-0.1.0/skill_forge/templates.py +151 -0
- claude_skill_forge-0.1.0/skill_forge/validate.py +147 -0
- claude_skill_forge-0.1.0/tests/conftest.py +104 -0
- claude_skill_forge-0.1.0/tests/test_analyzers.py +65 -0
- claude_skill_forge-0.1.0/tests/test_cli.py +75 -0
- claude_skill_forge-0.1.0/tests/test_forge.py +57 -0
- claude_skill_forge-0.1.0/tests/test_llm.py +31 -0
- claude_skill_forge-0.1.0/tests/test_review_regressions.py +131 -0
- claude_skill_forge-0.1.0/tests/test_units.py +149 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Lint & test (py${{ matrix.python-version }})
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Install (core + dev, no provider SDKs)
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install -e ".[dev]"
|
|
29
|
+
|
|
30
|
+
- name: Ruff
|
|
31
|
+
run: ruff check .
|
|
32
|
+
|
|
33
|
+
- name: Pytest
|
|
34
|
+
run: pytest -q
|
|
35
|
+
|
|
36
|
+
- name: Self-forge (dogfood — generate a skill from this repo and lint it)
|
|
37
|
+
run: |
|
|
38
|
+
python -m skill_forge.cli forge . --name skill-forge -o /tmp/sf-skills --force
|
|
39
|
+
python -m skill_forge.cli lint /tmp/sf-skills
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
wheels/
|
|
9
|
+
|
|
10
|
+
# Envs
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
.env
|
|
15
|
+
.env.*
|
|
16
|
+
!.env.example
|
|
17
|
+
|
|
18
|
+
# Tooling caches
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
.coverage
|
|
23
|
+
htmlcov/
|
|
24
|
+
|
|
25
|
+
# OS / editor
|
|
26
|
+
.DS_Store
|
|
27
|
+
*.swp
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Initial release of `skill-forge`, a CLI that generates a valid Claude `SKILL.md` from a
|
|
12
|
+
codebase, package, or doc.
|
|
13
|
+
- **Deterministic, offline, zero-dependency core** — `analyze → draft → validate` uses only
|
|
14
|
+
the standard library and never executes the target code.
|
|
15
|
+
- **Valid by construction** — every generated skill passes the bundled linter; the tool
|
|
16
|
+
refuses to write a skill that does not lint clean.
|
|
17
|
+
- Analyzers:
|
|
18
|
+
- **Python** — `pyproject.toml` / `setup.cfg`, `__all__` / public defs via `ast`, and
|
|
19
|
+
argparse / click / typer subcommands.
|
|
20
|
+
- **Node** — `package.json` (name, description, keywords, `bin`) + README, TS/JS detection.
|
|
21
|
+
- **Docs** — a single markdown/rst/txt file (title, summary, headings, code blocks).
|
|
22
|
+
- **Generic** — README + a language histogram of the file tree.
|
|
23
|
+
- **CLI help** — `--from-cli "<cmd>"` captures and parses a tool's `--help` (no shell,
|
|
24
|
+
timeout-guarded, explicit opt-in).
|
|
25
|
+
- CLI subcommands: `forge`, `lint`, `check` (CI drift guard), and `version`.
|
|
26
|
+
- Optional `--llm` refinement via Claude (the only extra, lazily imported and fail-safe:
|
|
27
|
+
it never produces a worse skill than the offline path).
|
|
28
|
+
- GitHub Actions CI: ruff + pytest on Python 3.10–3.13, plus a self-forge dogfood step.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Contributing to skill-forge
|
|
2
|
+
|
|
3
|
+
Thanks for helping make Claude skills easier to author. `skill-forge` has one job: take a
|
|
4
|
+
source and emit a `SKILL.md` that is **valid by construction** — so the bar for every change
|
|
5
|
+
is "the output still lints clean, deterministically, offline."
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- **Deterministic and offline by default.** The `analyze → draft → validate` path must never
|
|
10
|
+
hit the network and must never import or execute the target code. The Python analyzer uses
|
|
11
|
+
`ast` only. The single exception is the explicit `--from-cli` flag.
|
|
12
|
+
- **Zero runtime dependencies in the core.** `anthropic` is the only optional extra. If a
|
|
13
|
+
change needs a new hard dependency, it almost certainly belongs behind an extra — or not at
|
|
14
|
+
all. Prefer the standard library.
|
|
15
|
+
- **Valid by construction.** If you touch the generators, the property tests in
|
|
16
|
+
`tests/test_forge.py` must still show every fixture rendering to a skill that passes
|
|
17
|
+
`lint_text` with zero errors.
|
|
18
|
+
- **The SPEC is the contract.** [`SPEC.md`](SPEC.md) pins the public API and behavior. Change
|
|
19
|
+
the spec in the same PR when you change the contract; don't let them drift.
|
|
20
|
+
|
|
21
|
+
## Development
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m venv .venv && source .venv/bin/activate
|
|
25
|
+
pip install -e ".[dev]"
|
|
26
|
+
ruff check .
|
|
27
|
+
pytest -q
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
CI runs ruff + pytest on Python 3.10–3.13 and a self-forge dogfood step on every push and PR.
|
|
31
|
+
|
|
32
|
+
## Adding an analyzer
|
|
33
|
+
|
|
34
|
+
1. Add `skill_forge/analyzers/<kind>.py` exposing `analyze(path) -> SourceSignals`.
|
|
35
|
+
2. Register it in `analyzers/__init__.py` (`_ANALYZERS`) and teach `detect()` when to pick it.
|
|
36
|
+
3. Add a fixture in `tests/conftest.py` and a test that asserts the extracted signals, plus a
|
|
37
|
+
`test_forge_valid_by_construction` case so the generated skill is proven valid.
|
|
38
|
+
4. Keep it pure: no network, no executing the target.
|
|
39
|
+
|
|
40
|
+
## Reporting issues
|
|
41
|
+
|
|
42
|
+
Found a source that produces a bad (or invalid) skill? Open an issue with a **minimal repro**
|
|
43
|
+
source and the generated `SKILL.md`. A generated skill that fails `skill-forge lint` is a bug
|
|
44
|
+
and the most valuable kind of report.
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
By contributing you agree your work is released under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shaxzodbek Sobirov / Blaze
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: claude-skill-forge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Turn a codebase, package, or doc into a valid Claude SKILL.md — offline, deterministic, and valid by construction. Optional Claude refinement. Zero runtime dependencies.
|
|
5
|
+
Project-URL: Homepage, https://github.com/shaxzodbek-uzb/skill-forge
|
|
6
|
+
Project-URL: Repository, https://github.com/shaxzodbek-uzb/skill-forge
|
|
7
|
+
Project-URL: Issues, https://github.com/shaxzodbek-uzb/skill-forge/issues
|
|
8
|
+
Author-email: Shaxzodbek Sobirov <shaxzodbek@blaze.uz>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,anthropic,claude,cli,codegen,linter,mcp,scaffold,skill-md,skills
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
22
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Provides-Extra: anthropic
|
|
26
|
+
Requires-Dist: anthropic>=0.40; extra == 'anthropic'
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# skill-forge
|
|
33
|
+
|
|
34
|
+
**Point it at your code. Get a valid Claude skill. No API key required.**
|
|
35
|
+
|
|
36
|
+
`skill-forge` turns a codebase, package, or doc into a well-formed Claude
|
|
37
|
+
[`SKILL.md`](https://docs.claude.com/en/docs/agents-and-tools/agent-skills) — and the
|
|
38
|
+
skill it writes is **valid by construction**.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install claude-skill-forge # the CLI it installs is `skill-forge`
|
|
42
|
+
skill-forge forge ./my-tool # writes .claude/skills/my-tool/SKILL.md
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
[](https://github.com/shaxzodbek-uzb/skill-forge/actions/workflows/ci.yml)
|
|
46
|
+
[](https://pypi.org/project/claude-skill-forge/)
|
|
47
|
+

|
|
48
|
+

|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Why
|
|
54
|
+
|
|
55
|
+
Writing a good skill is fiddly: the frontmatter has to be exactly right, the `name` has to
|
|
56
|
+
match its directory, and the `description` — the one field an agent actually reads to
|
|
57
|
+
decide *whether to load the skill* — has to say **when** to trigger, inside a tight
|
|
58
|
+
character budget. Get any of it wrong and the skill is silently undiscoverable.
|
|
59
|
+
|
|
60
|
+
Most "ask an LLM to write my SKILL.md" approaches are non-reproducible, need an API key,
|
|
61
|
+
and still emit invalid frontmatter. `skill-forge` is different on two axes:
|
|
62
|
+
|
|
63
|
+
1. **Offline & deterministic by default.** It reads your source with static analysis — no
|
|
64
|
+
code execution, no network, no key — and emits the skill. Same input → same output.
|
|
65
|
+
The optional `--llm` flag only *refines* the prose; it never owns the structure.
|
|
66
|
+
2. **Valid by construction.** Every generated skill passes the built-in linter (the same
|
|
67
|
+
rules a skill must satisfy to be discoverable). `skill-forge` refuses to write a skill
|
|
68
|
+
that doesn't lint clean, so you never ship a broken one.
|
|
69
|
+
|
|
70
|
+
It's the *forge* half of a pair: **forge generates, `skillcheck` checks.** The linter is
|
|
71
|
+
bundled here too (`skill-forge lint`) so the tool stands alone.
|
|
72
|
+
|
|
73
|
+
## Install
|
|
74
|
+
|
|
75
|
+
The package is published on PyPI as **`claude-skill-forge`**; it installs a CLI named
|
|
76
|
+
`skill-forge` (and the import package is `skill_forge`).
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install claude-skill-forge # core: zero runtime dependencies
|
|
80
|
+
pip install 'claude-skill-forge[anthropic]' # adds the optional --llm refiner
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Quickstart (30 seconds)
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# From a Python/Node project, a package, or a single doc:
|
|
87
|
+
skill-forge forge ./my-tool # -> .claude/skills/my-tool/SKILL.md
|
|
88
|
+
skill-forge forge ./README.md # generate from docs
|
|
89
|
+
skill-forge forge ./pkg --name my-skill # override the skill name
|
|
90
|
+
skill-forge forge ./my-tool --stdout # preview, don't write
|
|
91
|
+
skill-forge forge ./my-tool --llm # sharpen the prose with Claude
|
|
92
|
+
|
|
93
|
+
# Validate any skill / folder of skills (the bundled checker):
|
|
94
|
+
skill-forge lint .claude/skills
|
|
95
|
+
|
|
96
|
+
# CI drift guard — fail if the skill no longer matches the code:
|
|
97
|
+
skill-forge check ./my-tool --name my-tool
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### What it extracts
|
|
101
|
+
|
|
102
|
+
| Source | What it reads |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| **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** |
|
|
105
|
+
| **Node** | `package.json` (name, description, keywords, `bin`), TS/JS detection, README |
|
|
106
|
+
| **Docs** | A markdown/rst/txt file: H1 title, first paragraph, section headings, fenced code blocks |
|
|
107
|
+
| **Any directory** | README + a language histogram of the file tree |
|
|
108
|
+
| **A CLI tool** | `skill-forge forge --from-cli "mytool"` captures and parses `mytool --help` |
|
|
109
|
+
|
|
110
|
+
The result is a complete `SKILL.md`: trigger-oriented `description`, a `## When to use`
|
|
111
|
+
section, an overview, and `## Commands` / `## API` / `## Usage` sections built from what was
|
|
112
|
+
found.
|
|
113
|
+
|
|
114
|
+
## Use it from Python
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from skill_forge import forge, render_skill, write_skill
|
|
118
|
+
|
|
119
|
+
draft = forge("./my-tool") # a validated SkillDraft
|
|
120
|
+
print(render_skill(draft)) # the SKILL.md text
|
|
121
|
+
write_skill(draft, ".claude/skills")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## The `--llm` refiner (optional)
|
|
125
|
+
|
|
126
|
+
`--llm` sends the *extracted signals* (not your source) to Claude to sharpen the
|
|
127
|
+
description's trigger phrasing and tighten the body. It is fail-safe: if the model is
|
|
128
|
+
unavailable it tells you how to fix it, and if its output is anything but a valid
|
|
129
|
+
improvement, `skill-forge` keeps the deterministic draft. You never get a worse skill than
|
|
130
|
+
the offline path. Set `ANTHROPIC_API_KEY` and install the extra to use it.
|
|
131
|
+
|
|
132
|
+
## CI: catch stale skills
|
|
133
|
+
|
|
134
|
+
`skill-forge check` regenerates the skill in memory and diffs it against the one on disk,
|
|
135
|
+
exiting non-zero if they differ — so a skill that drifted from the code it describes fails
|
|
136
|
+
the build:
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
- run: pip install claude-skill-forge
|
|
140
|
+
- run: skill-forge check ./my-tool --name my-tool
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## What this is **not**
|
|
144
|
+
|
|
145
|
+
- **Not a replacement for judgment.** Generated skills are a strong *first draft*. The body
|
|
146
|
+
is assembled from your structure, not written from deep understanding — read it, trim it,
|
|
147
|
+
and add the hard-won "do this, not that" guidance only you know.
|
|
148
|
+
- **Not a runtime.** It writes `SKILL.md` files; it does not execute skills.
|
|
149
|
+
- **Not magic prose.** The offline path is deterministic and a little formulaic by design.
|
|
150
|
+
Reach for `--llm` when you want the description polished.
|
|
151
|
+
- **It does not run your code.** The only time it executes anything is the explicit
|
|
152
|
+
`--from-cli` flag, which runs `<cmd> --help` with no shell and a timeout.
|
|
153
|
+
|
|
154
|
+
## Configuration
|
|
155
|
+
|
|
156
|
+
Environment overrides (all optional):
|
|
157
|
+
|
|
158
|
+
| Variable | Default | Purpose |
|
|
159
|
+
| --- | --- | --- |
|
|
160
|
+
| `SKILL_FORGE_OUTDIR` | `.claude/skills` | Default output directory |
|
|
161
|
+
| `SKILL_FORGE_VERSION` | `0.1.0` | Version stamped on generated skills |
|
|
162
|
+
| `SKILL_FORGE_MODEL_ID` | `claude-haiku-4-5` | Model used by `--llm` |
|
|
163
|
+
| `ANTHROPIC_API_KEY` | — | Required for `--llm` |
|
|
164
|
+
|
|
165
|
+
## Development
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
pip install -e ".[dev]"
|
|
169
|
+
ruff check .
|
|
170
|
+
pytest -q
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The design is pinned in [`SPEC.md`](SPEC.md) — the single source of truth for the public
|
|
174
|
+
API and behavior. See [`CONTRIBUTING.md`](CONTRIBUTING.md) before opening a PR.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# skill-forge
|
|
2
|
+
|
|
3
|
+
**Point it at your code. Get a valid Claude skill. No API key required.**
|
|
4
|
+
|
|
5
|
+
`skill-forge` turns a codebase, package, or doc into a well-formed Claude
|
|
6
|
+
[`SKILL.md`](https://docs.claude.com/en/docs/agents-and-tools/agent-skills) — and the
|
|
7
|
+
skill it writes is **valid by construction**.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install claude-skill-forge # the CLI it installs is `skill-forge`
|
|
11
|
+
skill-forge forge ./my-tool # writes .claude/skills/my-tool/SKILL.md
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
[](https://github.com/shaxzodbek-uzb/skill-forge/actions/workflows/ci.yml)
|
|
15
|
+
[](https://pypi.org/project/claude-skill-forge/)
|
|
16
|
+

|
|
17
|
+

|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Why
|
|
23
|
+
|
|
24
|
+
Writing a good skill is fiddly: the frontmatter has to be exactly right, the `name` has to
|
|
25
|
+
match its directory, and the `description` — the one field an agent actually reads to
|
|
26
|
+
decide *whether to load the skill* — has to say **when** to trigger, inside a tight
|
|
27
|
+
character budget. Get any of it wrong and the skill is silently undiscoverable.
|
|
28
|
+
|
|
29
|
+
Most "ask an LLM to write my SKILL.md" approaches are non-reproducible, need an API key,
|
|
30
|
+
and still emit invalid frontmatter. `skill-forge` is different on two axes:
|
|
31
|
+
|
|
32
|
+
1. **Offline & deterministic by default.** It reads your source with static analysis — no
|
|
33
|
+
code execution, no network, no key — and emits the skill. Same input → same output.
|
|
34
|
+
The optional `--llm` flag only *refines* the prose; it never owns the structure.
|
|
35
|
+
2. **Valid by construction.** Every generated skill passes the built-in linter (the same
|
|
36
|
+
rules a skill must satisfy to be discoverable). `skill-forge` refuses to write a skill
|
|
37
|
+
that doesn't lint clean, so you never ship a broken one.
|
|
38
|
+
|
|
39
|
+
It's the *forge* half of a pair: **forge generates, `skillcheck` checks.** The linter is
|
|
40
|
+
bundled here too (`skill-forge lint`) so the tool stands alone.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
The package is published on PyPI as **`claude-skill-forge`**; it installs a CLI named
|
|
45
|
+
`skill-forge` (and the import package is `skill_forge`).
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install claude-skill-forge # core: zero runtime dependencies
|
|
49
|
+
pip install 'claude-skill-forge[anthropic]' # adds the optional --llm refiner
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quickstart (30 seconds)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# From a Python/Node project, a package, or a single doc:
|
|
56
|
+
skill-forge forge ./my-tool # -> .claude/skills/my-tool/SKILL.md
|
|
57
|
+
skill-forge forge ./README.md # generate from docs
|
|
58
|
+
skill-forge forge ./pkg --name my-skill # override the skill name
|
|
59
|
+
skill-forge forge ./my-tool --stdout # preview, don't write
|
|
60
|
+
skill-forge forge ./my-tool --llm # sharpen the prose with Claude
|
|
61
|
+
|
|
62
|
+
# Validate any skill / folder of skills (the bundled checker):
|
|
63
|
+
skill-forge lint .claude/skills
|
|
64
|
+
|
|
65
|
+
# CI drift guard — fail if the skill no longer matches the code:
|
|
66
|
+
skill-forge check ./my-tool --name my-tool
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### What it extracts
|
|
70
|
+
|
|
71
|
+
| Source | What it reads |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| **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** |
|
|
74
|
+
| **Node** | `package.json` (name, description, keywords, `bin`), TS/JS detection, README |
|
|
75
|
+
| **Docs** | A markdown/rst/txt file: H1 title, first paragraph, section headings, fenced code blocks |
|
|
76
|
+
| **Any directory** | README + a language histogram of the file tree |
|
|
77
|
+
| **A CLI tool** | `skill-forge forge --from-cli "mytool"` captures and parses `mytool --help` |
|
|
78
|
+
|
|
79
|
+
The result is a complete `SKILL.md`: trigger-oriented `description`, a `## When to use`
|
|
80
|
+
section, an overview, and `## Commands` / `## API` / `## Usage` sections built from what was
|
|
81
|
+
found.
|
|
82
|
+
|
|
83
|
+
## Use it from Python
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from skill_forge import forge, render_skill, write_skill
|
|
87
|
+
|
|
88
|
+
draft = forge("./my-tool") # a validated SkillDraft
|
|
89
|
+
print(render_skill(draft)) # the SKILL.md text
|
|
90
|
+
write_skill(draft, ".claude/skills")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## The `--llm` refiner (optional)
|
|
94
|
+
|
|
95
|
+
`--llm` sends the *extracted signals* (not your source) to Claude to sharpen the
|
|
96
|
+
description's trigger phrasing and tighten the body. It is fail-safe: if the model is
|
|
97
|
+
unavailable it tells you how to fix it, and if its output is anything but a valid
|
|
98
|
+
improvement, `skill-forge` keeps the deterministic draft. You never get a worse skill than
|
|
99
|
+
the offline path. Set `ANTHROPIC_API_KEY` and install the extra to use it.
|
|
100
|
+
|
|
101
|
+
## CI: catch stale skills
|
|
102
|
+
|
|
103
|
+
`skill-forge check` regenerates the skill in memory and diffs it against the one on disk,
|
|
104
|
+
exiting non-zero if they differ — so a skill that drifted from the code it describes fails
|
|
105
|
+
the build:
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
- run: pip install claude-skill-forge
|
|
109
|
+
- run: skill-forge check ./my-tool --name my-tool
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## What this is **not**
|
|
113
|
+
|
|
114
|
+
- **Not a replacement for judgment.** Generated skills are a strong *first draft*. The body
|
|
115
|
+
is assembled from your structure, not written from deep understanding — read it, trim it,
|
|
116
|
+
and add the hard-won "do this, not that" guidance only you know.
|
|
117
|
+
- **Not a runtime.** It writes `SKILL.md` files; it does not execute skills.
|
|
118
|
+
- **Not magic prose.** The offline path is deterministic and a little formulaic by design.
|
|
119
|
+
Reach for `--llm` when you want the description polished.
|
|
120
|
+
- **It does not run your code.** The only time it executes anything is the explicit
|
|
121
|
+
`--from-cli` flag, which runs `<cmd> --help` with no shell and a timeout.
|
|
122
|
+
|
|
123
|
+
## Configuration
|
|
124
|
+
|
|
125
|
+
Environment overrides (all optional):
|
|
126
|
+
|
|
127
|
+
| Variable | Default | Purpose |
|
|
128
|
+
| --- | --- | --- |
|
|
129
|
+
| `SKILL_FORGE_OUTDIR` | `.claude/skills` | Default output directory |
|
|
130
|
+
| `SKILL_FORGE_VERSION` | `0.1.0` | Version stamped on generated skills |
|
|
131
|
+
| `SKILL_FORGE_MODEL_ID` | `claude-haiku-4-5` | Model used by `--llm` |
|
|
132
|
+
| `ANTHROPIC_API_KEY` | — | Required for `--llm` |
|
|
133
|
+
|
|
134
|
+
## Development
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
pip install -e ".[dev]"
|
|
138
|
+
ruff check .
|
|
139
|
+
pytest -q
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The design is pinned in [`SPEC.md`](SPEC.md) — the single source of truth for the public
|
|
143
|
+
API and behavior. See [`CONTRIBUTING.md`](CONTRIBUTING.md) before opening a PR.
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT — see [LICENSE](LICENSE).
|