Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .claude/agents/architecture-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ Never upward. Never cross-resource (router A importing from router B's store/mod
## Review checklist

1. **Boundary purity** — `routes/` files must not contain business logic. Logic = conditionals that derive new values, loops over domain objects, or computations beyond "validate → call store → return". Flag route handlers longer than ~20 lines as a smell.
Run: `rg "^\s+(if|for|while|return [^a])" src/app/routes/`
2. **Upward imports** — `store/` and `models/` must not import from `routes/`. Run: `rg "from app.routes" src/app/store/ src/app/models/`
3. **Cross-router imports** — no router imports from another router's store or models. Run: `rg "from app.routes" src/app/routes/`
4. **Circular imports** — run the import check command above; report any `ImportError` or `ModuleNotFoundError`.
5. **Router registration** — every new router module must be mounted in `main.py` via `app.include_router()`. Check `git diff main...HEAD -- src/app/main.py`.
6. **One resource = one router** — a single router file must not handle multiple unrelated resources.
Run: `rg "^router = APIRouter" src/app/routes/ -l` and verify each file covers one resource.
7. **`main.py` scope** — `main.py` must only wire routers, configure lifespan, mount static files, and register exception handlers. No business logic.
Run: `rg "^\s+(if|for|while)" src/app/main.py`

## Output format

Expand Down
15 changes: 15 additions & 0 deletions .claude/agents/implementer.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,18 @@ If the plan requires changes to any forbidden target, surface this as a blocker
- Tests use `httpx.AsyncClient` with `ASGITransport` — never `TestClient`.

Read CLAUDE.md before acting.

## When blocked or uncertain

- If the plan is ambiguous about a detail, state the ambiguity and your assumed interpretation before proceeding — do not silently guess.
- If implementing a step would require writing to a forbidden target, stop and surface it as a blocker rather than working around it.
- Prefer the smallest change that satisfies the plan over a larger refactor.

## When done

Print a summary:
```
Files created: <list>
Files modified: <list>
Next: run /review to check the diff.
```
24 changes: 14 additions & 10 deletions .claude/agents/planner.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,23 @@ tools:
- WebFetch
---

You are the **planner** agent. Your job is to produce a detailed implementation plan for a feature — nothing more.
You are the **planner** agent. Your job is to produce a detailed, grounded implementation plan for a feature — nothing more. You write one file and stop.

## Workflow

1. Read `CLAUDE.md` before anything else.
2. If `docs/specs/<feature>/` exists, read `requirements.md` and `design.md` there first.
3. Read all existing source files relevant to the feature (`src/app/routes/`, `models/`, `store/`, `main.py`).
4. Identify at least two implementation approaches. Pick one and state why, grounded in the codebase.
5. Write the plan to `docs/plans/<feature>.md` using the output format below.

## Constraints

- **Read-only** against `src/` and `tests/`. Do not edit any source or test files.
- You may write **one output file** only: `docs/plans/<feature>.md`.
- Never speculate about implementation details not derivable from the existing codebase.
- Cite file paths and line numbers when referencing existing code.
- Flag any pre-existing issues (type errors, sync tests) you notice — do not fix them.
- Do not invoke implementer or reviewer agents.

## Output format
Expand All @@ -22,6 +33,7 @@ Your plan document **must** use exactly these H2 headings, in this order, with n

```
## Scope
## Approach
## Endpoints
## Models
## Store interface
Expand All @@ -30,17 +42,9 @@ Your plan document **must** use exactly these H2 headings, in this order, with n
```

- **## Scope** — one paragraph describing what changes and what does not.
- **## Approach** — brief comparison of two options; state which is chosen and why.
- **## Endpoints** — markdown table: Method | Path | Request body | Response body | Status codes.
- **## Models** — Pydantic class sketches (field names + types, no full code).
- **## Store interface** — method signatures only (e.g. `get_by_id(id: str) -> Item`).
- **## Test plan** — bulleted list: one line per test case, format `route · scenario · expected status`.
- **## Open questions** — numbered list of anything that needs human decision before implementing.

## Rules

- Never speculate about implementation details not derivable from the existing codebase.
- Cite file paths and line numbers when referencing existing code.
- Flag any pre-existing issues (type errors, sync tests) you notice — do not fix them.
- Keep the plan under 150 lines.

Read CLAUDE.md before acting.
1 change: 1 addition & 0 deletions .claude/commands/implement.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Read the most recently modified plan in `docs/plans/`, then invoke the **impleme
Instructions for the implementer agent:
- Locate the latest plan: `ls -t docs/plans/*.md | head -1`
- Read the plan in full before writing any code
- If `docs/specs/<feature-name>/` exists, read it for context before starting
- If not already on a feature branch, create one: `git checkout -b feat/<feature-name>` where feature-name matches the plan filename
- Implement in order: models → store → routes → wire into main.py → tests
- Do not modify files outside `src/` and `tests/`
Expand Down
5 changes: 3 additions & 2 deletions .claude/commands/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ Read CLAUDE.md, then invoke the **planner** agent to produce an implementation p
Instructions for the planner agent:
- The feature name is: `$ARGUMENTS`
- Write the plan to `docs/plans/$ARGUMENTS.md`
- Follow the output format in your system prompt exactly (scope, endpoints, models, store interface, test plan, open questions)
- If `docs/specs/$ARGUMENTS/` exists, read `requirements.md` and `design.md` there before planning
- Follow the output format in your system prompt exactly (scope, approach, endpoints, models, store interface, test plan, open questions)
- Identify at least two implementation approaches; choose one and state why in the `## Approach` section
- Read all relevant existing source files before writing
- Keep the plan under 150 lines

If `$ARGUMENTS` ends with `--opus`, use the claude-opus-4-8 model for this planning session (more complex features that need deeper reasoning).

Expand Down
11 changes: 7 additions & 4 deletions .claude/skills/uv-workflows/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ uv sync # install all deps from uv.lock

## Running commands

Run tools directly — never prefix with `uv run`:

```bash
uv run pytest # run tests
uv run mypy src/ # type check
uv run ruff check . # lint
uv run python script.py
pytest # run tests
mypy --strict src/ # type check
ruff check . # lint
ruff format --check . # format check
python script.py # run a script
```

## Upgrading a specific package
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ eggs/

# Type checkers
.ruff_cache/
.mypy_cache/
.mypy_cache/
.dmypy.json
dmypy.json

Expand Down Expand Up @@ -115,6 +115,7 @@ venv.bak/
.claude/settings.local.json
.claude/state/
docs/plans/
docs/specs/

# Project-specific
TODO.md
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ async def client() -> AsyncGenerator[AsyncClient, None]:

---

## Git commit style

- Use `git commit -m "<message>"` with a plain, descriptive one-line message. No HEREDOC.
- **Never** append attribution lines to commit messages.

---

## Forbidden patterns

| Pattern | Replacement |
Expand Down
118 changes: 63 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,55 @@
# agentic-coding-template

Containered FastAPI service with Claude Code patterns: typed agents, auto-trigger skills, inline push gate, and a Spec→Plan→Implement→Review→Ship loop.
Template for a containered FastAPI service with Claude Code support: typed agents, auto-trigger skills, inline push gate, and a Spec→Plan→Implement→Review→Ship loop.


---

## Quick Start
## Features

```bash
docker compose up --build # start dev container
cp .env.template .env # configure environment
```
- **FastAPI 0.136 + Pydantic v2** — fully preconfigured with async route handlers, strict type annotations, dependency injection via `Depends()`, and response models on every endpoint
- **BCE Architecture (Boundary / Control / Entity)** — enforced by parallel reviewer agents (`architecture-reviewer`, `performance-reviewer`, `security-reviewer`) that run on every `/review` call
- **Automated quality gate** — `ruff format`, `ruff check`, `mypy --strict`, and `pytest` must all pass before any `git push`; enforced inline by the `pre_push_quality_gate` hook
- **Docker-based dev environment** — `docker compose up --build` brings up the full dev container; no local Python setup required
- **Guard-rail hook suite** — blocks secrets in `.env`, dangerous Bash patterns (`rm -rf`, force-push), `pip` in favour of `uv`, and auto-formats Python after every edit
- **Spec→Plan→Implement→Review→Ship loop** — structured agentic workflow from fuzzy requirement to merged PR, all driven from the Claude Code chat; no external project-management tool needed
- **`docs/plans/` as the single source of truth** — every feature starts as a plan file that the `planner` agent writes and the `implementer` agent reads; the plan is referenced in the PR body and versioned alongside the code

---

## Agents

| Agent | Model | Write targets | Purpose |
|---|---|---|---|
| `planner` | Sonnet 4.6 | `docs/plans/` | Reads codebase, writes implementation plan |
| `implementer` | Sonnet 4.6 | `src/`, `tests/` | Turns plan into working code + tests |
| `architecture-reviewer` | Haiku 4.5 | none (read-only) |
| `performance-reviewer` | Haiku 4.5 | none (read-only) |
| `security-reviewer` | Haiku 4.5 | none (read-only) | Secrets, OWASP-lite, input validation |


---

## Skills

Skills extend Claude Code with domain-specific context — path-triggered ones load automatically when you open a matching file; manual ones are invoked on demand.

| Skill | `paths:` trigger | Purpose |
|---|---|---|
| `fastapi-conventions` | `src/app/routes/**`, `src/app/models/**`, `src/app/main.py` | Route/DI/response-model conventions |
| `pytest-patterns` | `tests/**`, `**/conftest.py` | AsyncClient fixtures, parametrize patterns |
| `uv-workflows` | `pyproject.toml`, `uv.lock` | `uv add`, `uv sync`, `uv run` idioms |
| `infrastructure` | `Dockerfile`, `docker-compose*`, CI workflows | Container, compose, and CI pipeline patterns |
| `openapi` | `openapi*.yml`, `openapi*.json` | Spec authoring and codegen |
| `frontend` | `*.html`, `ui/**` | Jinja2, Tailwind, HTMX patterns |
| `spec-feature` | manual | 2-phase interview → `docs/specs/<feature>/` before planning |
| `review` | manual | Inline BCE + FastAPI + Python checklist for ad-hoc review |
| `doc` | manual | Reads source → writes project docs into `docs/` |
| `blog-post` | manual | Audience interview → structured technical blog post |
| `infografik` | manual | AI image generation via Hugging Face FLUX.1 → `docs/assets/` |


---

### Agentic loop

Expand All @@ -27,41 +67,20 @@ spec → plan → implement → review → ship

Skip the spec step for well-understood changes (bug fixes, small additive work). Use it when requirements are fuzzy or need alignment before any code is written — the skill runs a structured 6-question interview and records what was agreed.

---

## Stack

| Tool | Version | Purpose |
|---|---|---|
| FastAPI | 0.136 | Async API framework |
| Pydantic v2 | 2.x | Typed models, settings |
| uv | latest | Dependency management |
| pytest-asyncio | latest | Async test runner |
| ruff | latest | Formatter + linter |
| mypy | latest | Strict static typing |
| httpx | latest | Async test client |

---

## Agent Map
## Quick Start

| Agent | Model | Write targets | Purpose |
|---|---|---|---|
| `planner` | Sonnet 4.6 | `docs/plans/` | Reads codebase, writes implementation plan |
| `implementer` | Sonnet 4.6 | `src/`, `tests/` | Turns plan into working code + tests |
| `architecture-reviewer` | Haiku 4.5 | none (read-only) |
| `performance-reviewer` | Haiku 4.5 | none (read-only) |
| `security-reviewer` | Haiku 4.5 | none (read-only) | Secrets, OWASP-lite, input validation |
```bash
git clone <repository-url> # clone the repo
docker compose up --build # start dev container
cp .env.template .env # configure environment
```

---
This template uses Claude Code subscription per default. For API connection add the ANTHROPIC_API_KEY to the .env`.

## Skill Roster

| Skill | `paths:` trigger | Purpose |
|---|---|---|
| `fastapi-conventions` | `src/app/routes/**`, `src/app/models/**`, `src/app/main.py` | Route/DI/response-model conventions |
| `pytest-patterns` | `tests/**`, `**/conftest.py` | AsyncClient fixtures, parametrize patterns |
| `uv-workflows` | `pyproject.toml`, `uv.lock` | `uv add`, `uv sync`, `uv run` idioms |

---

Expand All @@ -77,32 +96,21 @@ Skip the spec step for well-understood changes (bug fixes, small additive work).

See [.claude/hooks/README.md](.claude/hooks/README.md) for exit codes and how to add a hook.

---

## Command Map

| Command | What it does |
|---|---|
| `/plan <feature>` | Invokes `planner` → writes `docs/plans/<feature>.md` |
| `/implement` | Reads latest plan, creates `feat/<name>` branch, builds code |
| `/review` | Invokes `architecture-reviewer`, `architecture-reviewer` and `security-reviewer` in parallel |
| `/ship` | Runs quality gate, then `gh pr create` |

---

## Demo Feature: `/items` CRUD

Five in-memory endpoints proving the full loop works end-to-end:
## Stack

```
POST /items → 201 create item
GET /items → 200 list all
GET /items/{id} → 200 get by id
PUT /items/{id} → 200 update
DELETE /items/{id} → 204 delete
```
| Tool | Version | Purpose |
|---|---|---|
| FastAPI | 0.136 | Async API framework |
| Pydantic v2 | 2.x | Typed models, settings |
| uv | latest | Dependency management |
| pytest-asyncio | latest | Async test runner |
| ruff | latest | Formatter + linter |
| mypy | latest | Strict static typing |
| httpx | latest | Async test client |

Run the 15 tests: `uv run pytest tests/test_items.py -v`

---

Expand Down
4 changes: 2 additions & 2 deletions docs/AGENTIC_WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Never use `exit(1)` — it is reserved for unexpected Python exceptions and will
{
"tool_name": "Bash",
"tool_input": {
"command": "git push origin feat/items-resource"
"command": "git push origin feat/my-feature"
}
}
```
Expand Down Expand Up @@ -132,7 +132,7 @@ Events: `PreToolUse`, `PostToolUse`. Matchers: `Bash`, `Edit`, `Write`, `MultiEd

## Self-Correcting Review Loop

`/review` invokes both reviewer agents in parallel. If either finds violations:
`/review` invokes all three reviewer agents in parallel. If any finds violations:

```
Iteration 1:
Expand Down
Loading