A CLI scaffolding tool that initializes new projects with a complete, opinionated environment: version control, dependency management, code quality tooling, agent behavioral protocols, and optional domain modules. Designed for use by humans and agentic collaborators. Every project produced by project-init is git-backed from the start, has pre-commit hooks enforcing quality gates, carries an agents.md behavioral protocol, and builds a reference.md from the modules installed. Automating the basic setup of a new projects effectively sidesteps forgetting and configuration drift between projects.
git clone https://github.com/ruschenpohler/project-init
cd project-init
uv syncRequires Python 3.11+ and uv.
# base + python (default)
project-init my-project
# base only, no Python tooling
project-init my-project --no-python
# named profile (flag bundle)
project-init my-project --profile ml-research
# explicit flags
project-init my-project --cas --exptracking --security
# specify parent directory
project-init my-project --path ~/projects/Every project receives the following, regardless of flags:
my-project/
├── .git/
│ └── hooks/
│ ├── pre-commit # dispatcher: never overwritten by modules
│ └── pre-commit.d/ # modules drop numbered hook scripts here
├── agents.md # symlink to project-init/modules/base/agents.md.tmpl
├── reference.md # built at init time by appending module fragments
├── .gitignore
├── impl-log.jsonl # append-only JSONL audit log
├── output/
│ └── .gitkeep
└── data/
└── .gitkeep
With the python module (default):
├── pyproject.toml # project name substituted from template
├── .python-version
├── src/
│ └── my_project/
│ └── __init__.py
└── tests/
└── conftest.py
The initial commit is made automatically with --no-verify, since hooks should not fire on the scaffold itself. Every subsequent commit goes through the full hook chain.
- Initializes a git repo with
mainas the default branch. - Installs the pre-commit dispatcher into
.git/hooks/pre-commit. This file is owned bybaseand never overwritten. Modules add checks by dropping numbered scripts into.git/hooks/pre-commit.d/-- naming conventionNN-description.sh, where lower numbers run first. - Creates
agents.mdas a symlink tomodules/base/agents.md.tmplin theproject-initrepo. Updating the template propagates to all symlinked projects immediately. - Creates
reference.mdand appends the base reference fragment. Subsequent modules append their own fragments. - Writes a standard
.gitignorecovering generated artefacts, data directories, secrets, and environment files.
- Writes
pyproject.tomlfrom a template, substituting the project name. - Pins
.python-versionto 3.11. - Creates
src/<project_name>/andtests/with aconftest.pystub. - Installs two pre-commit hooks:
20-ruff.sh(lint and format check) and30-pytest.sh(full test suite). Both run viauv runto ensure the project's own environment is used.
- Creates
cas/store/andcas/shadow/directories. - Writes
src/cas.pywith SHA-256 helpers:hash_file,hash_directory, andstore_artefact. - Content-addressed storage makes silent overwrites of artefacts impossible: a changed file produces a different hash and is stored separately.
- Adds MLflow to the project's dev dependencies via
uv add. - Writes
src/experiment.py, a thin wrapper overmlflow.start_runthat logs all params immediately. - Writes
project_standards.yamlas the single source of truth for project-wide configuration (seeds, thresholds, paths). Agents and scripts read this file; magic numbers do not live in source code. - Includes Pydantic-style schema enforcement conventions via the reference fragment.
- Installs
70-pip-audit.shand71-bandit.shintopre-commit.d/. Both fail on high-severity findings only, keeping the signal-to-noise ratio high. - Creates
audit.log(append-only, gitignored) for file-based audit logging outside the git history.
Profiles are flat files in profiles/ containing one flag per line. They allow named configurations without requiring the caller to remember individual flags.
# profiles/ml-research.flags
--cas
--exptracking
--security
project-init my-project --profile ml-researchProfiles and explicit flags can be combined. Explicit flags take precedence.
Dispatcher pattern. A git repo supports exactly one .git/hooks/pre-commit file. Without coordination, each module would overwrite the previous module's hook. The dispatcher solves this: base installs a single hook that iterates over pre-commit.d/*.sh in numeric order. Modules never touch the dispatcher -- they only add scripts to the directory. Installation order is irrelevant; execution order is controlled by the numeric prefix.
Append-on-install for reference.md. Each module ships a ref-<name>.md fragment. At install time, the fragment is appended to the project's reference.md behind an idempotency sentinel (<!-- module:name -->). Re-running a module does not duplicate its section. The result is a single reference file in every project, scoped exactly to what is installed, built from authoritative per-module sources.
Symlink for agents.md. Rather than copying agents.md into each project, base creates a symlink pointing to modules/base/agents.md.tmpl in the project-init repo. Edits to the template propagate to all projects immediately, with no per-project update step. On Windows without Developer Mode, the symlink falls back to a file copy with a printed warning -- changes to the template will not propagate automatically in that case.
uv as the single environment layer. pyproject.toml owns dependencies. uv.lock is committed as the reproducibility contract. No pip install, no requirements.txt, no manual environment management. Pre-commit hooks invoke tooling via uv run to ensure the project environment is always used.
Initial commit. The scaffold makes the first commit automatically using --no-verify. This prevents the ruff and pytest hooks from firing against an empty or partial project state. Every commit after the first goes through the full hook chain.
- Create
modules/<name>/install.pywith a functioninstall(project_root: Path, project_name: str). - Create
modules/<name>/ref-<name>.mdwith the reference content for this module. - In
install.py, append the fragment toreference.md:from project_init.utils import append_fragment append_fragment(project_root / "reference.md", SELF_DIR / "ref-<name>.md", "<name>")
- If the module installs a pre-commit hook, write it to
pre-commit.d/with a numeric prefix. Do not touch.git/hooks/pre-commit. - Add the module name to
MODULE_ORDERinrunner.py. - Add a
--<name>flag incli.pyand wire it intoresolve_flags. - Write tests in
tests/test_<name>.py. Cover: files created, reference fragment appended, idempotency.
agents.md is created as a symlink on Unix. On Windows, symlinks require either Developer Mode or administrator privileges. If neither is available, base falls back to copying agents.md.tmpl into the project as a regular file with a printed warning. In that case, changes to modules/base/agents.md.tmpl in the project-init repo must be manually propagated to existing projects.
Pre-commit hooks use bash. On Windows, Git Bash or WSL is required for hooks to execute.
Pre-commit hooks live in .git/hooks/, which git does not commit or transfer on clone. If a scaffolded project is cloned to a new machine, the hooks must be reinstalled. The simplest approach is to re-run project-init against the cloned directory, or to copy the pre-commit.d/ scripts manually.