Scaffold
Creates well-formed batterie-de-savoir Python repos from a checklist derived from building 10+ repos. Every new repo starts at the same baseline — git, hatchling, src layout, tests, CLAUDE.md, bon, GitHub remote.
Iron Law: Structure is deterministic. Meaning is not. The init script creates the skeleton; you fill in the CLAUDE.md content that makes the repo navigable.
When to Use
- Creating a new Python package from scratch
- Promoting scratch/noodling code into a proper repo
- Extracting a module from an existing repo into its own package
/closesuggests this when detecting code in a non-repo directory
Boundaries
- Python repos only (TypeScript is a future extension)
- Does not create plugin structure (
.claude-plugin/,skills/) — that's a different concern - Does not handle monorepo setups
Workflow
Step 1: Interview
Determine the mode and gather inputs.
Ask the user:
- What's the project name? (kebab-case, becomes the repo name)
- One-line description?
- What's the bon prefix? (3-4 lowercase letters)
Detect the mode:
- Greenfield — target directory is empty or doesn't exist
- Adopt — target directory has code but no
.git/ - Extract — user mentions pulling code from another repo
For adopt, list existing files and confirm they'll be preserved (moved into src/<pkg>/).
For extract, identify the source module and confirm what's moving.
Step 2: Plan
Show the user what will be created:
Scaffold plan for <name>:
Mode: greenfield / adopt / extract
Package: src/<pkg>/
Bon prefix: <prefix>
GitHub: spm1001/<name> (public)
Files to create:
pyproject.toml — hatchling, src layout, pytest config
src/<pkg>/__init__.py — importlib.metadata version
.gitignore — Python standard
tests/__init__.py — empty
tests/conftest.py — stub
CLAUDE.md — [TODO] sections for you to fill
.bon/ — work tracker
.bon/understanding.md — seed
[adopt only] Existing files to relocate:
<list>
Confirm with user before proceeding.
Step 3: Execute
Greenfield:
uv run --script ${CLAUDE_SKILL_DIR}/scripts/init_repo.py \
--name <name> --prefix <prefix> --description "<desc>"
Adopt:
- Move existing code into
src/<pkg>/(preserve structure) - Run
init_repo.pywith--dirpointing to the working directory- The script will warn about non-empty directory — that's expected
- Manually adjust if the script created files that conflict with existing ones
Extract:
- Create target directory
- Copy source module into
src/<pkg>/ - Run
init_repo.py - Update source repo to remove extracted code and add dependency
Step 4: Fill CLAUDE.md
The init script leaves [TODO] markers. Replace each one with project-specific content:
Module Map — list every module under src/<pkg>/ with a one-line role description. Use a table:
| Module | Role |
|--------|------|
| `parsing` | JSONL loading, entry type classification |
| `content` | System tag stripping, assistant content extraction |
Key Conventions — document the things a future Claude needs to know:
- What dependencies are allowed (stdlib only? specific packages?)
- What patterns to follow (re-exports from
__init__.py?) - What NOT to "fix" (deliberate quirks)
- Quick commands beyond the standard ones
For adopt/extract mode, also note the origin of the code and any context about why it was extracted.
Step 5: Validate
uv run --script ${CLAUDE_SKILL_DIR}/scripts/validate_repo.py
Review the report. Fix any FAIL items. Re-run until all checks pass.
If validate_repo.py doesn't exist yet, manually verify:
- [ ] Git repo on
mainbranch (not detached HEAD) - [ ]
.gitignorepresent with Python patterns - [ ]
pyproject.toml: hatchling backend, src layout,[dependency-groups]dev, pytest config - [ ]
src/<pkg>/__init__.pywith__version__viaimportlib.metadata - [ ]
tests/withconftest.py - [ ]
CLAUDE.mdpresent, no remaining[TODO]markers - [ ]
.bon/initialized with correct prefix - [ ] GitHub remote configured and pushed
The Checklist
Derived from cross-repo survey of 10 batterie repos (April 2026) and every mistake made building deglacer:
| Item | Why |
|------|-----|
| git init -b main | Never detached HEAD |
| .gitignore | Python bytecode, build artifacts, venv |
| pyproject.toml with hatchling | Single version source, src layout |
| [dependency-groups] dev | Separate dev deps from runtime |
| [tool.pytest.ini_options] | pythonpath = ["src"] avoids import errors |
| src/<pkg>/__init__.py | importlib.metadata version — no hardcoded version |
| tests/__init__.py + conftest.py | Test infrastructure ready from day one |
| CLAUDE.md | Orientation, quick commands, module map, conventions |
| bon init --prefix | Work tracker from the start |
| .bon/understanding.md | Knowledge accumulation seed |
| gh repo create | HTTPS remote, ready to push |
Common Mistakes
| Mistake | What Happens | Better |
|---------|-------------|--------|
| Hardcoded version in pyproject.toml | Dual-maintenance drift | importlib.metadata pattern |
| Missing pythonpath = ["src"] in pytest config | Import errors in tests | Always include it |
| No .gitignore | __pycache__ committed | Create before first commit |
| setuptools instead of hatchling | Inconsistent with suite | Hatchling everywhere |
| Flat layout instead of src/ | Import confusion in tests | Always src/<pkg>/ |
| Skipping bon init | No work tracker | Init with prefix immediately |
| CLAUDE.md with just the name | Useless orientation | Fill in module map and conventions |
Integration
- Bon: Every scaffolded repo gets bon initialized. Actions filed here continue in the new repo.
- Skill-forge: If the new repo will have skills, use skill-forge after scaffold completes.
- /close: Can suggest scaffold when detecting code in a non-repo directory (bds-tipuji).