Skip to content

Commit 2f0cdaa

Browse files
authored
Add AGENTS.md (#69)
1 parent 19c6f17 commit 2f0cdaa

1 file changed

Lines changed: 215 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
# AGENTS.md
2+
3+
This file provides guidelines for AI coding agents (e.g., GitHub Copilot, Cursor, Codex) working in this repository.
4+
5+
---
6+
7+
## Project Overview
8+
9+
This is a **Python project template** that provides a pre-configured, production-ready starting point for Python applications. It includes out-of-the-box support for:
10+
11+
- **Packaging & dependency management** via [Poetry](https://python-poetry.org)
12+
- **CLI** via [click](https://click.palletsprojects.com)
13+
- **Testing & coverage** via [pytest](https://pytest.org) and [coverage](https://coverage.readthedocs.io)
14+
- **Linting, formatting & import sorting** via [ruff](https://docs.astral.sh/ruff)
15+
- **Type checking** via [pyright](https://microsoft.github.io/pyright)
16+
- **Pre-commit hooks** via [pre-commit](https://pre-commit.com)
17+
- **Documentation** via [MkDocs](https://www.mkdocs.org) with [mkdocstrings](https://mkdocstrings.github.io), auto-deployed to GitHub Pages
18+
- **CI/CD** via GitHub Actions
19+
- **Containerisation** via Docker and Dev Containers
20+
21+
---
22+
23+
## Project Structure
24+
25+
```
26+
├── .devcontainer/ # Dev container configuration
27+
├── .github/ # GitHub-specific files (Actions, templates, Dependabot)
28+
├── .vscode/ # VS Code settings
29+
├── docs/ # Documentation source
30+
│ ├── README.md # Project README and MkDocs home page
31+
│ ├── CONTRIBUTING.md # Contributing guidelines
32+
│ └── reference/ # Auto-generated API reference pages
33+
├── project/ # Main source package (renamed via `make project`)
34+
│ ├── __init__.py
35+
│ └── app.py # CLI entry point
36+
├── tests/ # Test suite
37+
│ ├── conftest.py # Shared pytest fixtures and hooks
38+
│ └── test_app.py # Sample tests
39+
├── compose.yml # Docker Compose file
40+
├── Dockerfile # App container
41+
├── Makefile # Workflow automation targets
42+
├── mkdocs.yml # MkDocs configuration
43+
├── pyproject.toml # Project metadata, dependencies, and tool configuration
44+
└── poetry.lock # Locked dependency versions (do not edit manually)
45+
```
46+
47+
> **Note:** The `project/` folder is the template placeholder. After initialising a real project with `make project NAME=...`, it is renamed to the chosen package name.
48+
49+
---
50+
51+
## Setup
52+
53+
### Prerequisites
54+
55+
- Python 3.12+
56+
- [pipx](https://pipx.pypa.io) (to install Poetry)
57+
- Docker (for Dev Container or containerised runs)
58+
59+
### First-time setup
60+
61+
```bash
62+
# 1. Install Poetry (if not already installed)
63+
make poetry
64+
65+
# 2. Install all dependencies
66+
make install
67+
68+
# 3. Install pre-commit hooks
69+
make precommit
70+
71+
# 4. Activate the virtual environment
72+
make venv
73+
```
74+
75+
### Rename the template for a new project (run once)
76+
77+
```bash
78+
make project NAME="my-project" DESCRIPTION="My app" AUTHOR="Your Name" EMAIL="you@example.com" GITHUB="your-username"
79+
```
80+
81+
---
82+
83+
## Common Commands
84+
85+
| Task | Command |
86+
|---|---|
87+
| Install dependencies | `make install` |
88+
| Update dependencies | `make update` |
89+
| Lint and format | `make lint` |
90+
| Run tests with coverage | `make test` |
91+
| Run app locally | `app` (after `make venv`) or `poetry run app` |
92+
| Run app in Docker | `docker compose run app` |
93+
| Serve docs locally | `make local` |
94+
| Deploy docs to GitHub Pages | `make docs` |
95+
| Full setup from scratch | `make all` |
96+
97+
> Refer to `docs/README.md` for the full list of available targets. Add new targets to `Makefile` as needed.
98+
99+
---
100+
101+
## Code Style
102+
103+
- **Line length**: 120 characters (configured in `pyproject.toml` under `[tool.ruff]`)
104+
- **Linter/formatter**: `ruff` — enforces `E` (pycodestyle errors) and `I` (isort) rules
105+
- **Type checker**: `pyright` — all imports must resolve; missing imports are errors
106+
- **Pre-commit**: hooks run `ruff` automatically before every commit
107+
- Run `make lint` to format, sort imports, and check types manually
108+
- All public functions and classes must have docstrings (used by `mkdocstrings` for API docs)
109+
110+
---
111+
112+
## Testing
113+
114+
- Tests live in the `tests/` directory and mirror the source structure
115+
- Run the full test suite with coverage using `make test` (runs `pytest` via `coverage`)
116+
- Coverage is measured with branch coverage enabled; the report is printed to the terminal and exported as `coverage.xml`
117+
- Shared fixtures belong in `tests/conftest.py`
118+
- Test files must be named `test_*.py`
119+
120+
---
121+
122+
## Git Workflow
123+
124+
- Work on feature branches; open a pull request to `main`
125+
- Pre-commit hooks enforce formatting and linting on every commit
126+
- CI (`.github/workflows/check.yml`) runs lint and tests on every push
127+
- Do not commit directly to `main`
128+
129+
---
130+
131+
## Dependencies
132+
133+
- Always use Poetry for dependency management (`poetry add <package>`)
134+
- Use Pydantic for data models
135+
- Use Pydantic-settings for environment variable configuration in a `settings.py` file
136+
137+
## Testing Guidelines
138+
139+
- Use pytest, not unittest
140+
- Use `pytest` monkeypatch and `pytest-mock` for mocking instead of `unittest.MagicMock`
141+
- Do not cheat! Never modify source code just to make a failing test pass. Fix real bugs in source code and fix incorrect assertions in tests
142+
143+
## Make Targets
144+
145+
Use `make` targets for all common workflows: lint, test, run locally, and deploy. Refer to `docs/README.md` for currently available targets. Add new targets to `Makefile` as needed.
146+
147+
## Notes
148+
149+
- Python 3.12+ required
150+
- Dependencies are managed via `pyproject.toml` and locked in `poetry.lock`
151+
- Do not edit `poetry.lock` directly; use `make update` to update dependencies
152+
153+
## Coding Conventions
154+
155+
### Field descriptions
156+
157+
Every field in a Pydantic model or pydantic-settings class must be documented using `Field(description="...")`. This makes descriptions machine-readable and visible in generated JSON schemas.
158+
159+
```python
160+
from uuid import uuid4
161+
from pydantic import BaseModel, Field
162+
163+
class Item(BaseModel, populate_by_name=True, alias_generator=to_camel):
164+
id: str = Field(description="Unique item identifier.", default_factory=lambda:str(uuid4()))
165+
name: str = Field(description="Human-readable item name.")
166+
```
167+
168+
### camelCase alias convention
169+
170+
All `BaseModel` subclasses must be defined with `populate_by_name=True` and `alias_generator=to_camel` so that JSON payloads can use camelCase while Python attributes use snake_case. Always serialise with `model_dump(by_alias=True, exclude_none=True)` to produce camelCase JSON output and omit unset optional fields.
171+
172+
```python
173+
from uuid import uuid4
174+
from pydantic import BaseModel, Field
175+
from pydantic.alias_generators import to_camel
176+
177+
class Item(BaseModel, populate_by_name=True, alias_generator=to_camel):
178+
item_id: str = Field(description="Unique item identifier.", default_factory=str(uuid4()))
179+
# Accepts {"itemId": "..."} from JSON; attribute is item.item_id
180+
# model_dump() → {"item_id": ...}
181+
# model_dump(by_alias=True, exclude_none=True) → {"itemId": ...}
182+
```
183+
184+
### No `model_config` class attribute
185+
186+
Do not use `model_config = ConfigDict(...)` or `model_config = SettingsConfigDict(...)`. Pass configuration options as keyword arguments to the base class instead.
187+
188+
```python
189+
# Good
190+
class Item(BaseModel, extra="allow", populate_by_name=True, alias_generator=to_camel): ...
191+
class Settings(BaseSettings, case_sensitive=False): ...
192+
193+
# Bad
194+
class Item(BaseModel):
195+
model_config = ConfigDict(extra="allow")
196+
```
197+
198+
### Import style
199+
200+
Do not add unnecessary imports like `from __future__ import annotations`. Always use explicit `from x import y` form:
201+
202+
```python
203+
from json import dumps, loads
204+
from pytest import fixture, main, raises
205+
from aws_cdk.aws_lambda import Code, Function, Runtime
206+
```
207+
208+
### Test file main block
209+
210+
Every test file must end with:
211+
212+
```python
213+
if __name__ == "__main__":
214+
main()
215+
```

0 commit comments

Comments
 (0)