Production-ready project scaffold with a containerized dev environment, GitHub automation, and Claude Code as a development workflow agent.
Python-first: linting, typing, tests, packaging, Docker, and docs are wired up and active out of the box (Node/JS is a supported second). Using another stack? Everything Python is stubbed and clearly deletable — see the checklist below.
Run /onboard in Claude Code to set up this template for your project. It will interview you, configure all the files, and tell you which manual steps remain.
You can still pull in later improvements to the template. How depends on how your repository started.
# One-time, either way: add the template as an 'upstream' remote
git remote add upstream https://github.com/Jartan-LLC/scaffold.git # this template's repo
git fetch upstreamIf you used Use this template — the button on this repository — GitHub started your history
fresh, so there is nothing to merge: git merge upstream/main stops at fatal: refusing to merge unrelated histories. Port the change instead, and name the upstream commit in your PR body so it
stays possible to ask which repositories took it:
git log --oneline main..upstream/main # what the template has that you do not
git show <sha> # the change to port; apply the equivalent by handIf you forked this repository, the history is shared and the merge works:
git checkout -b template-update
git merge upstream/main # resolve conflicts, keeping your customizationsOpen a PR either way, so CI runs before the changes land.
| Area | Contents |
|---|---|
.devcontainer/ |
Reproducible dev environment — Python 3.12, Node.js LTS, Docker, GitHub CLI, desktop-lite; plus Claude Code CLI and codebase-memory-mcp (structural code graph, best-effort), both installed via post-create.sh |
.claude/ |
Claude Code configuration — enabled plugins (skills & agents from the grimoire marketplace) and the /onboard setup command |
.github/ |
CI pipeline (active lint incl. workflow security lint via actionlint/zizmor, + Python typecheck/test/build + dependency audit + docs build; Node steps + Docker job commented), Claude Code as CI agent (@claude in issues/PRs), Dependabot auto-patching, publish/release + OpenSSF Scorecard + weekly external-link-check workflows, issue/PR + code-of-conduct + security templates |
pyproject.toml, ci/requirements.txt |
Python packaging + tool config (ruff, pytest, pyright, codespell) — minimal src-layout stub; rename or delete. ci/requirements.txt exact-pins the tools that only run the gate, and the one uv version CI, the devcontainer and make all use |
src/app/, tests/ |
Placeholder package (CLI entry point + logging setup, PEP 561 typed) + smoke/logging tests so CI is green on first fork |
Makefile, .pre-commit-config.yaml |
Task runner (make install/lint/test/check/docs, backed by uv and a project-local .venv) + the single lint source (ruff, codespell, shellcheck, markdownlint, lychee, actionlint, zizmor, hygiene) that make lint and CI both run |
docs/, .readthedocs.yaml.example |
Sphinx docs site (Markdown via MyST, API reference from docstrings); make docs builds it. Publish via pages.yml.example (GitHub Pages) or ReadTheDocs |
AGENTS.md |
Symlink to CLAUDE.md so vendor-neutral agent tools (Cursor, Copilot, …) read the same rules |
Dockerfile, .dockerignore |
Minimal Python image stub — pairs with publish-docker.yml |
CHANGELOG.md, CONTRIBUTING.md |
Keep-a-Changelog skeleton and a Python contributor guide |
.env.example, .prettierrc |
Env-var template and Prettier config (for JS/TS work) |
.editorconfig |
Language-aware formatting — 4-space Python, 2-space JS/TS, tabs for Makefiles |
.gitattributes |
Syntax-aware diffs for 20+ languages, binary normalization for lock files |
.gitignore |
Comprehensive patterns for Node, Python, Docker, IDEs, env files, build artifacts |
CLAUDE.md |
Project rules, anti-patterns, verification commands, skill index |
LICENSE.* |
License templates (MIT, Apache-2.0, AGPL-3.0, proprietary) — pick one during onboarding |
If you prefer to set up manually instead of using /onboard:
- Update
CLAUDE.md— replace placeholder comments:- Project name and description (the
# Project Nameheading and the<!-- ONE LINE: … -->comment under it) - Corrections with any version-specific overrides for your stack
- Project name and description (the
- Update
CLAUDE.mdSkills section — add project-specific skills/conventions as they emerge - Update
.devcontainer/devcontainer.json— change the desktop-lite password, add/remove language features and extensions for your stack - Update
.devcontainer/post-create.sh— add dependency installation for your stack - Update
.devcontainer/post-start.sh— add commands that should run on each container start (Docker socket fix and Codespaces env overrides are included) - Update
.gitignore— add language-specific patterns for your stack - Update
.editorconfig— adjust formatting rules for your language (e.g., tabs for Go) - Update
.github/CODEOWNERS— uncomment and set owner usernames/teams - Update
.github/SECURITY.md— set supported versions, response timeline, and the private security contact. That contact is the reporter's only channel if the advisory form is unavailable, and it is published publicly, so use an address you are willing to publish - Enable private vulnerability reporting (Settings > Security) — until it is on, the advisory form
.github/SECURITY.mdsends every reporter to does not exist - Update
.github/CODE_OF_CONDUCT.md— set the enforcement contact (replace[INSERT CONTACT METHOD]) - Update
.github/ISSUE_TEMPLATE/config.yml— replaceORG/REPOin contact link URLs with your GitHub org and repo name - Update
CHANGELOG.md— replaceORG/REPOin the[Unreleased]link with your GitHub org and repo (otherwise the link 404s) - Tidy
.lycheeignore— delete only thehttps://github.com/ORG/REPOline, once ORG/REPO is real (delete rather than replace, or the pattern would ignore your own repo's links). Leave every other line: the file marks which are permanent, and deleting thefile://advisory-form pattern makes the link check fail on your security policy - Update
.github/dependabot.yml— remove ecosystems you don't use, add ones you need, adjust directories if not at root - Create the
dependencylabel —gh label create dependency --color 0366d6 --description "Dependency updates"(required by the dependabot and link-check configs) - Rename the Python package (
/onboarddoes all this; skip thepackagesedit andpython -m buildfails) — setpyproject.tomlname+description, rename thesrc/app/directory, update[tool.hatch.build.targets.wheel]packagesto match, update theappimports insrc/app/__main__.pyandtests/(from app.log import …in__main__.py/test_log.py,from app.__main__ import …intest_smoke.py), and thepython -m appreferences (__main__.pyprog=,DockerfileCMDhint). - Not a Python project? Delete
pyproject.toml,src/,tests/, the docs stack (docs/,.readthedocs.yaml.example,.github/workflows/pages.yml.example),.github/workflows/publish-pypi.yml, and — if not containerized —Dockerfile,.dockerignore,.github/workflows/publish-docker.yml; adaptCONTRIBUTING.md(rewrite themake installsetup and the "Requires Python 3.12+ and uv" line); trim theruffhook from.pre-commit-config.yaml(keep the language-agnostic hooks — codespell, shellcheck, markdownlint, lychee, actionlint, zizmor, hygiene; move codespell's[tool.codespell]skip config to a.codespellrcbefore deletingpyproject.toml); repoint theMakefiletargets at your stack's lint/format/typecheck/test/build commands somake checkstays your one verify gate; in.github/workflows/ci.ymlremove thetypecheck/test/build/audit/docsjobs (and theircheck.needs+ results entries) — thelintjob just runs pre-commit and stays. - Replace
tests/test_smoke.pywith real tests — it only exists so thetestCI job is green out of the box - Review
.github/workflows/ci.yml— thelint/typecheck/test/build/audit/docsjobs are ACTIVE and pass against the shipped stubs. Delete jobs you don't need (and their entries in thecheckaggregator). To enable extras, uncomment thedocker/integration-testsjobs and add each tocheck.needs+ the results array; the Node checks are commented steps inside thelintjob (uncomment them there — nocheckchange needed) - Docs — set
project/author/project_copyrightindocs/conf.py; write thedocs/index.mdlanding page (replace the# Project Docstitle +TODO(/onboard)); update thepip install appline indocs/getting-started.mdto the renamed package; after renaming the package, update theautomodulemodule names indocs/reference.md(/onboarddoes this; the docs build fails if they're left stale) - Create a
LICENSEfile — rename one of the included templates (LICENSE.MIT,LICENSE.Apache-2.0,LICENSE.AGPL-3.0,LICENSE.proprietary) toLICENSE, fill in[year]and[fullname], delete the others - Add
skillOverridesto.claude/settings.json— disable installed plugin skills that don't match your stack - Add secrets to your repo:
ANTHROPIC_API_KEY— for the Claude Code workflowAPP_ID— GitHub App IDAPP_PRIVATE_KEY— GitHub App private key- GitHub App setup:
- Create a GitHub App at https://github.com/settings/apps
- Under Permissions, grant Contents, Issues, and Pull Requests (Read & Write)
- Under Webhook, uncheck "Active" (not needed for this workflow)
- Install the app on your repo
- Store the App ID and a generated private key as repo secrets
- Set up publishing — the release/publish workflows are opt-in: nothing runs until you push a
v*tag.release.yml— creates a GitHub Release with auto-generated notes on everyv*tag. Language-agnostic; keep it even if you publish no package or image.publish-pypi.yml— a stubpyproject.tomlis included (rename the package first). Create thepypiEnvironment —gh api -X PUT repos/{owner}/{repo}/environments/pypi(this also clears the "environmentpypiis not valid" warning the GitHub Actions VS Code extension shows until it exists) — then configure PyPI Trusted Publishing (OIDC) for it (no token secret). Optionally uncomment the tag-vs-version check.publish-docker.yml— a stubDockerfileis included (give it a real entrypoint). Publishes multi-arch images toghcr.io/OWNER/REPOusing the built-inGITHUB_TOKEN— no secret needed. Create theghcrEnvironment (gh api -X PUT repos/{owner}/{repo}/environments/ghcr) and add required reviewers to gate image publishing onv*tags. Each release is taggedX.Y.ZandX.Y;latestmoves only when the pushed tag is the highest release, so a patch cut on an older line can't drag it backwards.- All trigger on
v*tags.publish-pypi.ymlstill fails until you rename the package and configure PyPI Trusted Publishing; delete whichever publish workflow (and its stub) you don't need.
- Enable GitHub Discussions (Settings > General > Features) — issue template config links to it
- Enable CodeQL default setup (Settings > Security > Code scanning)
- OpenSSF Scorecard (
.github/workflows/scorecard.yml) needs a public repo to publish its score/badge — on a private fork it skips automatically, so delete it only if you don't want it at all - Publish docs (optional) — GitHub Pages: set Settings > Pages > Source = "GitHub Actions", then rename
.github/workflows/pages.yml.example→pages.yml(single-version). Versioned: rename.readthedocs.yaml.example→.readthedocs.yamland import the repo at readthedocs.org. Pick one; the docs build is already checked on every PR either way - Enable secret scanning with push protection (Settings > Security > Secret Protection)
- Configure branch ruleset for
main— require PR reviews, require CI to pass, block force pushes - Enable auto-merge (Settings > General > Allow auto-merge) — Dependabot minor/patch PRs auto-merge after CI passes
- Review
.github/workflows/claude.yml— uses--dangerously-skip-permissionswhich grants Claude unrestricted tool access in CI
- Replace this README with your own
- Delete
.claude/commands/onboard.md