Skip to content

About

Cookie-cutter project template for an agentic development workflow: Claude Code architect + engineer subagents, Pi-based multi-model code review (Deepseek + Kimi), strict branch hygiene, declarative apply-standard script.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

reference_project

A cookie-cutter Claude Code project template that enforces a strict architect → engineer → commit → multi-model review → squash-merge loop. Every commit on a feature branch is automatically reviewed by Pi running Deepseek and Kimi (in parallel, with different lenses); the engineer agent waits for verdict: approve from every reviewer before proceeding.


Using it

Prerequisites

On the machine that will host derived projects:

Tool Why
git All workflow is git-driven.
bash Hooks and helper scripts are bash.
python3 Only needed for the bootstrap script (stdlib only — no pip deps).
pi Runs the code reviewers. Must be configured with providers for the models you choose (default: Deepseek + Moonshot/Kimi). Verify with pi --list-models.
gh Optional — only needed if you want the bootstrap to create the GitHub repo for you.

Set git identity once globally (recommended) so the bootstrap commit can run without prompting you:

git config --global user.name  "Your Name"
git config --global user.email "you@example.com"

Apply the standard to a project (fresh or existing)

# Direct execution (recommended — uv resolves the right Python via PEP 723)
/path/to/reference_project/apply-standard.py <target_dir> [options]

# Or explicit:
uv run /path/to/reference_project/apply-standard.py <target_dir> [options]

# Or fallback (no uv available):
python3 /path/to/reference_project/apply-standard.py <target_dir> [options]

The script declares its Python requirement (≥3.10) via PEP 723 inline metadata, so uv run handles version management automatically. It has zero external dependencies, so python3 also works for anyone without uv.

This single command brings any project — greenfield or pre-existing — up to the agentic development standard. It is idempotent: run it as many times as you like. The script asserts a declarative spec ("the target should look like this") and makes it true.

Action Behaviour
Managed files (CLAUDE.md, .claude/agents/, .claude/hooks/, .claude/scripts/, .claude/settings.json) Copied/overwritten from this reference; files deleted from the reference are deleted from the target too
Runtime dirs (.plans/, .reviews/, references/) Created if missing; contents never touched
.gitignore Required entries appended if missing; existing lines preserved (additive only)
.claude/settings.local.json Per-user state, never touched
Git git init -b main if no .git/; core.hooksPath set to .claude/hooks; hooks and shell scripts marked executable
Identity Verified; warns if missing, never modifies your git config
Alignment commit Made on the current branch (with ALLOW_MAIN_COMMIT=1 REVIEW_SKIP=1) only if managed files actually changed
GitHub repo Created via gh only with --gh-create, only if no remote exists, only after a first commit

Options

Flag Effect
--dry-run Report what would change; apply nothing. Always safe to run first.
--no-commit Skip the alignment commit even if files changed (so you can review with git diff before committing yourself)
--gh-create Create a private GitHub repo if no remote exists
--gh-public With --gh-create, public visibility
--gh-name NAME With --gh-create, repo name (default: target dir basename)

Idempotency contract

Run the script. Edit something in this reference. Run the script again — only that file gets propagated. Re-run with no further changes — output is "Target is already at the standard. Nothing changed." That is the contract.

Anything you put inside a managed directory (.claude/agents/, .claude/hooks/, .claude/scripts/) becomes part of the standard's scope: the script will delete it on re-apply if it's not in the reference. Keep project-local extensions outside those directories.

Starting your first feature in the new project

cd <target_dir>
git switch -c feat/initial-scaffolding
# Then ask Claude: "Use the architect role to plan <thing>."

From there, the loop runs itself — see CLAUDE.md in the new project for the full workflow.


How it works

The template installs a workflow that is enforced by a combination of:

  • Claude Code subagent definitions (.claude/agents/) — architect and engineer roles, each with their own contract
  • Git hooks (.claude/hooks/) activated via core.hooksPath
  • Shell helpers (.claude/scripts/) that implement the review-wait loop and branch-completion checks
  • Per-project instructions (CLAUDE.md) loaded into Claude's context every session
  • Permission rules (.claude/settings.json) that block the worst bypass patterns at the harness layer

Roles

Role What it does
Architect A Claude subagent. Designs strategy, splits work into commit-sized units, writes a plan to .plans/<id>.md, updates .plans/ACTIVE, commits, and drives the plan-review loop to approve. Produces a plan, not code.
Engineer A Claude subagent. Implements exactly one commit-sized unit per turn, self-reviews against .claude/scripts/self-review-checklist.md, commits, and drives the code-review loop to approve per commit. When every unit is approved, performs the squash-merge to main.
Reviewers External Pi processes invoked by the post-commit hook. By default: Deepseek (correctness lens) and Kimi (design/maintainability lens). Each runs concurrently with different system prompts depending on commit type.

Branch workflow

  • Never commit to main. The pre-commit hook blocks it. Override only for the final squash-merge: ALLOW_MAIN_COMMIT=1.
  • One feature branch per plan. The branch holds many small commits (one per unit + any fix: address review follow-ups).
  • When every commit is approved, the engineer squash-merges into main as a single human-readable commit. The squash itself goes through an integration-level review (separate prompts focused on whole-feature coherence rather than per-commit detail).

Commit lifecycle

git commit
   │
   ▼
┌─────────────────────────────────────────────────────────────┐
│ pre-commit hook                                             │
│   • blocks if on main (unless ALLOW_MAIN_COMMIT=1)          │
│   • greps staged ADDED lines for:                           │
│       - debug prints (console.log, debugger, pdb,           │
│         breakpoint, dbg!)                                   │
│       - TODO without owner (use TODO(name) or TODO @name)   │
│       - obvious secret patterns (AWS, GitHub, OpenAI,       │
│         Anthropic, Slack, PEM private keys)                 │
│   • fast (<1s), deterministic, language-agnostic            │
│   • override: SKIP_PRECOMMIT_CHECKS=1 (rare)                │
└─────────────────────────────────────────────────────────────┘
   │ commit lands
   ▼
┌─────────────────────────────────────────────────────────────┐
│ post-commit hook                                            │
│   • classifies the commit:                                  │
│       - touches only .plans/  →  plan-review prompts        │
│       - on main branch        →  main-merge (integration)   │
│       - otherwise             →  code-review prompts        │
│   • spawns one Pi process per reviewer (setsid-detached),   │
│     so `git commit` returns immediately                     │
│   • appends a diagnostic line to .reviews/.hook.log         │
└─────────────────────────────────────────────────────────────┘
   │ reviewers run in background
   ▼
┌─────────────────────────────────────────────────────────────┐
│ Each reviewer writes one immutable file to .reviews/        │
│   .reviews/<YYYY-MM-DD-HH-MM-SS>_<commit>_<agent>.md        │
│   with a `verdict: approve | request_changes | error` line  │
└─────────────────────────────────────────────────────────────┘
   │
   ▼
┌─────────────────────────────────────────────────────────────┐
│ The engineer subagent calls:                                │
│   bash .claude/scripts/wait-for-reviews.sh                  │
│ which polls .reviews/ until every expected reviewer has     │
│ landed a file for HEAD, then exits:                         │
│   0 = all approve   3 = some request_changes                │
│   1 = timeout       4 = some error (Pi failure)             │
│ Exit 3 ⇒ engineer reads the open reviews, addresses each,   │
│ makes a NEW commit (never --amend, never --no-verify),      │
│ which triggers a fresh review cycle. Loop continues until 0.│
└─────────────────────────────────────────────────────────────┘

Three review contexts, three sets of prompts

The post-commit hook classifies each commit and dispatches the matching prompt files:

Commit type When Prompts
plan Commit touches only .plans/ plan-review-{agent}.md — review approach & decomposition
code Any other commit on a feature branch code-review-{agent}.md — review correctness & design per commit
main-merge Commit is on main (the squash-merge) main-merge-review-{agent}.md — integration review of the whole feature

Each prompt explicitly differentiates lenses (Deepseek = correctness; Kimi = design) so two reviews on the same diff produce non-redundant signal.

Per-commit-type reviewer filter

An agent participates in a given commit type iff the matching prompt file exists in .claude/scripts/prompts/. To remove an agent from a commit type, delete the corresponding <type>-review-<agent>.md file. The dispatcher, the wait script, and the branch-completion check all honour the same rule.

Current roster:

Agent plan code main-merge
deepseek ✓ ✓ ✓
kimi — ✓ ✓

Plan commits go through a single reviewer (deepseek) — see .claude/scripts/prompts/plan-review-deepseek.md, which carries the full four-tier status semantics (Approve / Approve with changes / Needs rework / Reject) collapsed to the binary verdict: line.

Iteration limit

wait-for-reviews.sh counts cumulative request_changes verdicts across every commit on the current branch. When the count crosses ITERATION_WARN (default 5), it prints a stderr warning. Iteration is not blocked — the warning surfaces to the engineer (and to you), prompting a step back and likely a hand-off to the architect for a plan revision rather than yet another patch commit.

Closing out a branch

bash .claude/scripts/branch-all-approved.sh   # exits 0 iff
     # every commit on the branch is fully approved by every reviewer
     # AND every `**Subject:**` line in the active plan has a matching
     # commit subject on the branch

git switch main
git merge --squash <branch>
ALLOW_MAIN_COMMIT=1 git commit -m '<type>(<scope>): <user-facing summary>'
# triggers the main-merge integration review

Override env vars

Variable Purpose
ALLOW_MAIN_COMMIT=1 Allow a commit directly on main (intended use: the squash-merge step only).
SKIP_PRECOMMIT_CHECKS=1 Bypass pre-commit grep checks. Rare — defeats the safety net.
REVIEW_SKIP=1 Skip Pi review for one commit. Intended for the bootstrap commit; nothing else.
ITERATION_WARN=N Override the request_changes warning threshold (default 5).
PI_THINKING=<level> Override per-agent thinking level for one invocation (off/minimal/low/medium/high/xhigh).
PI_TIMEOUT=<seconds> Override the per-Pi-call timeout (default 300s).

Debugging

.reviews/.hook.log (gitignored) captures one line per dispatcher and reviewer event: when the dispatcher fires, what reviewers were spawned, and the final verdict each reviewer wrote (or the error it hit). If a review file fails to appear for a commit you just made, this is where to look first — the wait script would otherwise just time out without telling you why.


File layout

reference_project/
├── README.md                       this file
├── bootstrap-new-project.py        interactive scaffolder
├── CLAUDE.md                       project-instructions loaded into every Claude session
├── .gitignore                      template defaults
├── .plans/                         architect plans (tracked; .ACTIVE per branch)
├── .reviews/                       reviewer output (gitignored, including .hook.log)
├── references/                     reading material (gitignored)
└── .claude/
    ├── settings.json               permissions (allow/deny) for Claude Code
    ├── agents/
    │   ├── architect.md            architect subagent definition
    │   └── engineer.md             engineer subagent definition
    ├── hooks/                      git hooks (activated via core.hooksPath)
    │   ├── pre-commit              branch check + grep-based content checks
    │   └── post-commit             classify commit + spawn reviewers
    └── scripts/
        ├── install-hooks.sh        one-time setup (run on derived projects)
        ├── hooklib.sh              shared log helper
        ├── reviewers.txt           roster: name:pi_model:thinking
        ├── review-dispatch.sh      spawns one detached reviewer per agent
        ├── run-reviewer.sh         single reviewer invocation (Pi → review file)
        ├── wait-for-reviews.sh     blocks until HEAD is fully approved
        ├── open-reviews.sh         lists request_changes/error files for HEAD
        ├── branch-all-approved.sh  branch-level gate before squash-merge
        ├── self-review-checklist.md  walk-through for engineers pre-commit
        └── prompts/
            ├── code-review-{deepseek,kimi}.md
            ├── plan-review-{deepseek,kimi}.md
            └── main-merge-review-{deepseek,kimi}.md

Where to customize a derived project

Want to change… Edit
Roster, models, or thinking levels .claude/scripts/reviewers.txt
What each reviewer is told to look for .claude/scripts/prompts/*.md
Architect / engineer behaviour or model .claude/agents/{architect,engineer}.md
Pre-commit checks (add lint, typecheck) .claude/hooks/pre-commit
Override default thinking / timeout env vars (see above) or per-row in reviewers.txt
Workflow conventions CLAUDE.md

Design choices worth knowing

  • Per-agent immutable files, not a shared ledger. Two reviewers run concurrently and each writes its own file — no concurrent-write hazard, no locking, an inherent audit trail.
  • Verdict, not state machine. Each review file has verdict: approve | request_changes | error and is never mutated. Iteration happens through new commits (which produce new review files), not by reviewers updating prior verdicts.
  • Hard prohibitions enforced at the permissions layer. --no-verify, --amend, --force pushes, deletion of .reviews/, and edits to hook files are denied in .claude/settings.json — Claude Code refuses these as tool calls, not just by convention.
  • Plan reviewed before code. A bad plan is far cheaper to fix at plan-review time than after five commits embody it.
  • Lens differentiation per reviewer. Same diff, different prompts: Deepseek looks at correctness/security/regressions; Kimi looks at scope/naming/maintainability/commit-message quality. Two reviews on identical input produce non-redundant signal.
  • Integration review for the squash-merge. Per-commit review can't see whole-feature concerns (intermediate-state leakage, public-surface coherence, plan fidelity, squash-message-as-changelog). A separate prompt class handles that exactly once, when the feature lands on main.

About

Cookie-cutter project template for an agentic development workflow: Claude Code architect + engineer subagents, Pi-based multi-model code review (Deepseek + Kimi), strict branch hygiene, declarative apply-standard script.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages