doneguard 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: doneguard
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Your AI coding agent can't say "done" until the checks pass.
|
|
5
|
+
Project-URL: Homepage, https://github.com/abranchesia/doneguard
|
|
6
|
+
Project-URL: Issues, https://github.com/abranchesia/doneguard/issues
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: ai-agents,aider,claude-code,codex,coding-agent,copilot,cursor,docker,e2e,gemini-cli,harness,hooks,playwright,postgres,verification
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
17
|
+
Classifier: Topic :: Software Development :: Testing
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
<div align="center">
|
|
22
|
+
|
|
23
|
+
# doneguard
|
|
24
|
+
|
|
25
|
+
**Your AI coding agent can't say "done" until the checks pass.**
|
|
26
|
+
|
|
27
|
+

|
|
28
|
+

|
|
29
|
+

|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
<img src="https://raw.githubusercontent.com/abranchesia/doneguard/main/demo/claude.gif" alt="Real Claude Code session: the agent makes total() return a string and tries to stop; doneguard starts a throwaway Postgres, seeds it, runs vitest and Playwright, and blocks it with both failures; the agent fixes the page and the test; doneguard stats shows 1 blocked, 1 fixed" width="820">
|
|
33
|
+
|
|
34
|
+
<sub>A real Claude Code session. The agent can only read and edit files, so it can't run anything itself. When it tries to stop, doneguard starts a throwaway Postgres, seeds it, runs the unit tests and the Playwright tests, and sends back both failures.</sub>
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
Agents love to finish with *"Done! ✅"* while the types are broken and the tests are red. You find out ten minutes later.
|
|
39
|
+
|
|
40
|
+
doneguard is a **stop hook**. When the agent tries to finish, doneguard runs your type checker, linter and *related* tests on the files changed in the session. If anything fails, the errors go straight back to the agent and it keeps working until they pass.
|
|
41
|
+
|
|
42
|
+
- **Tests that need a database?** doneguard starts a throwaway Postgres, MySQL, Redis or MongoDB in Docker, migrates and seeds it, points `DATABASE_URL` at it, runs the tests and removes it. Your real database stays out of it.
|
|
43
|
+
- **Playwright or Cypress in the project?** It runs the end-to-end tests in a real browser and keeps a screenshot and a video of every test in `.doneguard/evidence/`. When one fails, the agent gets the error, the failure screenshot and a snapshot of the page.
|
|
44
|
+
|
|
45
|
+
- [Works with your agent](#works-with-your-agent)
|
|
46
|
+
- [How it works](#how-it-works)
|
|
47
|
+
- [Install](#install)
|
|
48
|
+
- [Add it to an existing project](#add-it-to-an-existing-project) · [Start from zero](#start-a-new-project-from-zero)
|
|
49
|
+
- [Tests that need a database](#tests-that-need-a-database) · [End-to-end tests with evidence](#end-to-end-tests-with-evidence)
|
|
50
|
+
- [Without hooks: CI, git hooks, any agent](#without-hooks-ci-git-hooks-any-agent)
|
|
51
|
+
- [Stats](#stats) · [Day to day](#day-to-day) · [Teams](#teams)
|
|
52
|
+
- [Stacks](#what-init-detects) · [Configuration](#configuration) · [Commands](#commands) · [FAQ](#faq)
|
|
53
|
+
|
|
54
|
+
## Works with your agent
|
|
55
|
+
|
|
56
|
+
Not using Claude Code? doneguard speaks each agent's own hook format. `doneguard init` hooks into the agents it finds, or name them: `doneguard init cursor codex`.
|
|
57
|
+
|
|
58
|
+
| Agent | How doneguard keeps it working | Status |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| **Claude Code** | `Stop` hook → `decision: block` | ✅ tested end to end |
|
|
61
|
+
| **Cursor** (IDE and CLI) | `stop` hook → `followup_message` | 🧪 |
|
|
62
|
+
| **OpenAI Codex CLI** | `Stop` hook → `decision: block` | 🧪 |
|
|
63
|
+
| **Gemini CLI** | `AfterAgent` hook → `decision: block` | 🧪 |
|
|
64
|
+
| **GitHub Copilot** (CLI and VS Code) | `agentStop` hook → `decision: block` | 🧪 |
|
|
65
|
+
| **Google Antigravity** | `Stop` hook → `decision: continue` | 🧪 |
|
|
66
|
+
| **Devin Local** (successor of Windsurf Cascade) | `Stop` hook → `decision: block` | 🧪 |
|
|
67
|
+
| **Kiro CLI** | `Stop` hook → `decision: block` | 🧪 |
|
|
68
|
+
| **Aider** | `lint-cmd` → Aider's own fix loop | 🧪 |
|
|
69
|
+
| **OpenCode** | plugin on `session.idle` → new prompt | ⚗️ experimental |
|
|
70
|
+
| **Cline, Windsurf Cascade, anything else** | a rule: *"run `doneguard check` before saying done"* | manual |
|
|
71
|
+
|
|
72
|
+
✅ the GIF above. 🧪 built from the agent's official hook docs and tested against its documented payloads. ⚗️ experimental. If one misbehaves with your version, please open an issue.
|
|
73
|
+
|
|
74
|
+
Cursor, Copilot and Devin also run the hooks in `.claude/`. When Claude Code is hooked too, `init` doesn't add theirs, so nothing runs twice.
|
|
75
|
+
|
|
76
|
+
## How it works
|
|
77
|
+
|
|
78
|
+
```mermaid
|
|
79
|
+
sequenceDiagram
|
|
80
|
+
participant You
|
|
81
|
+
participant Agent as Agent (Claude Code, Cursor, Codex…)
|
|
82
|
+
participant DG as doneguard
|
|
83
|
+
participant Tools as Your tools (tsc, eslint, vitest, pytest…)
|
|
84
|
+
You->>Agent: "Make total() return a formatted string"
|
|
85
|
+
Note over Agent,DG: session starts → doneguard start (marks the time)
|
|
86
|
+
Agent->>Agent: edits src/cart.ts
|
|
87
|
+
Agent->>DG: "Done!" → stop hook → doneguard verify
|
|
88
|
+
DG->>Tools: checks on files changed since the mark
|
|
89
|
+
Note over DG,Tools: tsc → throwaway Postgres + seed → vitest → Playwright
|
|
90
|
+
Tools-->>DG: vitest fails, Playwright fails (screenshot saved)
|
|
91
|
+
DG-->>Agent: not done yet + the errors + the screenshot path
|
|
92
|
+
Agent->>Agent: fixes server.ts and src/cart.test.ts
|
|
93
|
+
Agent->>DG: "Done!" → doneguard verify
|
|
94
|
+
DG->>Tools: same checks again
|
|
95
|
+
Tools-->>DG: all green
|
|
96
|
+
DG-->>Agent: ok
|
|
97
|
+
Agent->>You: Done, for real
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
1. **`doneguard init`** looks at your project, writes the checks to `.doneguard.conf` and registers two hooks in your agent's config.
|
|
101
|
+
2. **Session starts:** the agent runs `doneguard start`, which notes the time.
|
|
102
|
+
3. **The agent says it's done:** before letting it stop, the agent runs `doneguard verify`.
|
|
103
|
+
4. **verify** lists the files modified since the mark (respecting `.gitignore`), runs only the checks whose globs match them, and only the tests related to them. If a database is needed, it starts one in Docker first and removes it at the end. End-to-end runs leave their evidence in `.doneguard/evidence/`.
|
|
104
|
+
5. **Something fails:** the errors go back to the agent in its own format and it keeps working. **Everything passes:** the agent stops and the mark moves forward.
|
|
105
|
+
6. **Safety valve:** after 3 failed attempts in a row, doneguard lets the agent stop and tells you, so it never loops forever.
|
|
106
|
+
|
|
107
|
+
## Install
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
uv tool install doneguard
|
|
111
|
+
# or: pipx install doneguard
|
|
112
|
+
doneguard --help
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Want to try it first? `uvx doneguard init`, then `uvx doneguard check`, runs it without installing. The agent hooks need it installed, since they call `doneguard` by name.
|
|
116
|
+
|
|
117
|
+
If your shell can't find `doneguard` afterwards, run `uv tool update-shell` (or `pipx ensurepath`) and open a new terminal. The hooks call `doneguard` by name, so it has to be on your `PATH`.
|
|
118
|
+
|
|
119
|
+
Requirements: Python 3.9+ (only to run doneguard itself), macOS or Linux, and the tools your checks call (`tsc`, `ruff`, `cargo`…) installed in the project. git is optional; with it, change detection respects `.gitignore`. Docker is optional, for [test databases](#tests-that-need-a-database). Playwright browsers are optional, for [end-to-end tests](#end-to-end-tests-with-evidence).
|
|
120
|
+
|
|
121
|
+
## Add it to an existing project
|
|
122
|
+
|
|
123
|
+
<img src="https://raw.githubusercontent.com/abranchesia/doneguard/main/demo/init.gif" alt="doneguard init in an existing TypeScript project with Postgres and Playwright detects tsc, a throwaway Postgres with its migrate and seed scripts, vitest and Playwright, and hooks into Claude Code" width="820">
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
cd your-project
|
|
127
|
+
doneguard init # detects the stack and your agents, writes .doneguard.conf, adds the hooks
|
|
128
|
+
doneguard check src/app.ts # optional: dry-run the checks on any file
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Then restart your agent so it loads the hooks, and:
|
|
132
|
+
|
|
133
|
+
1. **Read `.doneguard.conf`.** One line per check, plain text. Delete what you don't want, add what's missing ([format](#configuration)).
|
|
134
|
+
2. **Commit `.doneguard.conf`.** Your team and your CI share the same checks.
|
|
135
|
+
3. **Work as usual.** Old errors elsewhere in the repo don't matter: doneguard only looks at files changed in the session.
|
|
136
|
+
|
|
137
|
+
Using an agent that `init` didn't find (or several)? Name them: `doneguard init codex gemini`, or `doneguard init all`.
|
|
138
|
+
|
|
139
|
+
Already have a `.doneguard.conf`? `init` never rewrites it, but it lists what it detects now and the file doesn't have yet, so upgrading is copy and paste.
|
|
140
|
+
|
|
141
|
+
## Start a new project from zero
|
|
142
|
+
|
|
143
|
+
In an empty folder there's nothing to detect yet. Run `init` once to install the hooks, scaffold the project, then run `init` again: it fills in the checks as soon as it finds a stack.
|
|
144
|
+
|
|
145
|
+
<img src="https://raw.githubusercontent.com/abranchesia/doneguard/main/demo/new.gif" alt="doneguard init in an empty folder writes 0 checks; after npm init and installing typescript and vitest, init again detects 2 checks" width="820">
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
mkdir my-app && cd my-app
|
|
149
|
+
doneguard init # hooks installed, 0 checks for now
|
|
150
|
+
npm init -y && npm i -D typescript vitest && npx tsc --init
|
|
151
|
+
doneguard init # now detects tsc + vitest
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Letting the agent scaffold the project? Tell it: *"once the project is set up, run `doneguard init`"*. The config is read on every stop, so the checks kick in right away.
|
|
155
|
+
|
|
156
|
+
## Tests that need a database
|
|
157
|
+
|
|
158
|
+
Integration tests usually need a database with the schema and some data in it. When `init` sees that your project talks to one, it adds a line for it right above the tests, at the end of the file:
|
|
159
|
+
|
|
160
|
+
```text
|
|
161
|
+
*.ts,*.tsx | @package.json | filter | npx --no -- tsc --noEmit --pretty false -p .
|
|
162
|
+
* | . | postgres=postgres:17-alpine | npm run db:migrate && npm run db:seed
|
|
163
|
+
*.js,*.jsx,*.ts,*.tsx | @package.json | files | npx --no -- vitest related --run --passWithNoTests
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Right before the first check below it runs, doneguard:
|
|
167
|
+
|
|
168
|
+
1. **starts a throwaway database** in Docker, on a free port of `127.0.0.1`. Same image as your `docker-compose.yml` when there is one (pgvector and PostGIS included), so the version and extensions match.
|
|
169
|
+
2. **points your project at it.** For every check below, doneguard overrides `DATABASE_URL`, every variable holding a URL of the same kind in your `.env` files or your shell (`DIRECT_URL`, `POSTGRES_PRISMA_URL`…), and every URL variable your ORM config reads (Prisma's `env("…")`, `process.env.…` in `drizzle.config` and `knexfile`). For Postgres it also sets `PGHOST`, `PGPORT`, `PGUSER`, `PGPASSWORD` and `PGDATABASE`. dotenv, Prisma, Next.js, Vite and pydantic all let a real environment variable win over `.env`.
|
|
170
|
+
3. **migrates and seeds it** with the command on the line, with `NODE_ENV=test`. A broken migration or seed is reported to the agent as the cause, and the tests after it are not run, so it isn't buried under fifty "relation does not exist" errors. If the command can't run at all, the tests below are skipped rather than run on an empty database.
|
|
171
|
+
4. **runs the checks below it**, then **removes the container**. If the run is killed, the container still removes itself after 15 minutes.
|
|
172
|
+
|
|
173
|
+
If no check below it runs (only a README changed, or no related tests), no database starts. With the image cached, Postgres is ready in about a second.
|
|
174
|
+
|
|
175
|
+
| Detected by | Database | Environment |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| `pg`, `postgres`, `psycopg`, `asyncpg`, gem `pg`, Prisma `provider = "postgresql"`, or a `postgres`/`postgis`/`pgvector` image in compose | `postgres` | `DATABASE_URL`, `PG*` |
|
|
178
|
+
| `mysql2`, `mysqlclient`, `pymysql`, Prisma `provider = "mysql"` | `mysql` | `DATABASE_URL` |
|
|
179
|
+
| `redis`, `ioredis`, `bullmq` | `redis` | `REDIS_URL` |
|
|
180
|
+
| `mongodb`, `mongoose`, `pymongo`, `motor`, `mongoid` | `mongo` | `MONGODB_URI`, `MONGO_URL` |
|
|
181
|
+
|
|
182
|
+
The migrate and seed command `init` writes:
|
|
183
|
+
|
|
184
|
+
| Project | Command |
|
|
185
|
+
|---|---|
|
|
186
|
+
| Prisma | `prisma migrate deploy` (or `prisma db push` without migrations), then your `db:seed`/`seed` script or `prisma db seed` |
|
|
187
|
+
| Drizzle | `drizzle-kit push`, then your seed script |
|
|
188
|
+
| Knex | `knex migrate:latest`, then your seed script |
|
|
189
|
+
| Other Node projects | your `db:migrate`/`migrate` and `db:seed`/`seed` scripts |
|
|
190
|
+
| Alembic (when `env.py` reads the environment) | `alembic upgrade head` |
|
|
191
|
+
| Rails | `RAILS_ENV=test bin/rails db:prepare` |
|
|
192
|
+
| Django, Redis, MongoDB | `-` (nothing: Django's test runner builds its own test database) |
|
|
193
|
+
|
|
194
|
+
`init` only writes a migrate and seed command when it can see the connection comes from a variable it redirects: the ORM config reads a URL variable, or, for plain npm scripts, your `.env` files hold a database URL. Otherwise it writes `-` and tells you to fill it in. A config that builds the connection from `DB_HOST`, `DB_USER`… can't be redirected that way, so doneguard never runs a migration through it on its own.
|
|
195
|
+
|
|
196
|
+
**Docker not running?** The database and the checks below it are skipped, never failed, and doneguard says so (Claude Code shows it after the turn). `init` puts these lines last, so your linters and the stacks that don't need a database still run. Your tests don't need a database after all? Delete the line.
|
|
197
|
+
|
|
198
|
+
## End-to-end tests with evidence
|
|
199
|
+
|
|
200
|
+
<img src="https://raw.githubusercontent.com/abranchesia/doneguard/main/demo/check.gif" alt="doneguard check: tsc passes, a throwaway Postgres starts and is seeded, vitest and Playwright fail, and the output lists the failure screenshot, the page snapshot and the HTML report in .doneguard/evidence; after the fix every check passes" width="820">
|
|
201
|
+
|
|
202
|
+
With `@playwright/test` and a `playwright.config.*` that starts the app (`webServer`), `init` adds an `e2e` line. When the agent changes a page, a component or a stylesheet, doneguard runs the suite in a real browser and keeps everything in your project:
|
|
203
|
+
|
|
204
|
+
```text
|
|
205
|
+
.doneguard/
|
|
206
|
+
├── .gitignore # "*": evidence stays out of git unless you delete this
|
|
207
|
+
└── evidence/
|
|
208
|
+
└── 2026-10-30_12-08-37/
|
|
209
|
+
├── report/ # npx playwright show-report .doneguard/evidence/2026-10-30_12-08-37/report
|
|
210
|
+
└── results/
|
|
211
|
+
└── cart-two-mugs-cost-20-00/
|
|
212
|
+
├── test-failed-1.png # what the page looked like when it failed
|
|
213
|
+
├── error-context.md # the page as text, for the agent
|
|
214
|
+
├── video.webm
|
|
215
|
+
└── trace.zip # npx playwright show-trace …
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- **Every test gets a screenshot and a video**, passing or not; failures also get a trace. Your own Playwright config is used as is: doneguard wraps it for the run (a temporary `.doneguard.playwright.config.*` next to yours) only to add the evidence and the output folder.
|
|
219
|
+
- **When a test fails, the agent gets** the error, the paths of the failure screenshot and the page snapshot, and opens them like any other file. Claude Code reads screenshots.
|
|
220
|
+
- **Which server the tests hit:** without a test database, `CI` is unset, like a run in your terminal, so a `webServer` with `reuseExistingServer: !process.env.CI` reuses the dev server you already have running. With a test database, `CI` stays set, so Playwright starts its own server pointed at it; if your dev server holds the port, the check is skipped and doneguard tells you, instead of clicking through your dev database.
|
|
221
|
+
- **The last 10 runs are kept.** Older ones are deleted.
|
|
222
|
+
- **No browsers installed?** The check is skipped until you run `npx playwright install chromium`.
|
|
223
|
+
- **Timeout:** 7 minutes, then the check is skipped.
|
|
224
|
+
|
|
225
|
+
<img src="https://raw.githubusercontent.com/abranchesia/doneguard/main/demo/evidence.png" alt="The failure screenshot Playwright saved: two mugs in the cart, total $10.00 instead of $20.00" width="560">
|
|
226
|
+
|
|
227
|
+
<sub>`test-failed-1.png` from the run above: two mugs in the cart, total $10.00. The agent sees this.</sub>
|
|
228
|
+
|
|
229
|
+
Any other runner works too: put `{evidence}` in the command and doneguard replaces it with that run's folder. pytest-playwright gets one automatically when there is a `tests/e2e` or `e2e` folder. Cypress needs your app running, so `init` suggests a line instead of writing it:
|
|
230
|
+
|
|
231
|
+
```text
|
|
232
|
+
*.py,*.html,*.css,*.js | . | e2e | pytest -q --tb=short tests/e2e --screenshot on --video on --tracing retain-on-failure --output {evidence}
|
|
233
|
+
*.tsx,*.css | . | e2e | npx --no -- start-server-and-test dev http://localhost:3000 'cypress run --config screenshotsFolder={evidence}/screenshots,videosFolder={evidence}/videos,video=true'
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Without hooks: CI, git hooks, any agent
|
|
237
|
+
|
|
238
|
+
`doneguard check` runs the same checks on your git changes (or on the files you pass), one progress line per check, and exits 1 on failure. No agent needed. The GIF in [End-to-end tests](#end-to-end-tests-with-evidence) shows it.
|
|
239
|
+
|
|
240
|
+
**GitHub Actions**, on the files changed in the pull request:
|
|
241
|
+
|
|
242
|
+
```yaml
|
|
243
|
+
on: pull_request
|
|
244
|
+
jobs:
|
|
245
|
+
doneguard:
|
|
246
|
+
runs-on: ubuntu-latest
|
|
247
|
+
steps:
|
|
248
|
+
- uses: actions/checkout@v4
|
|
249
|
+
with:
|
|
250
|
+
fetch-depth: 0
|
|
251
|
+
- run: npm ci # install your project's tools
|
|
252
|
+
- run: npx playwright install --with-deps chromium # if you have e2e tests
|
|
253
|
+
- run: pipx install doneguard
|
|
254
|
+
- run: doneguard check $(git diff --name-only origin/${{ github.base_ref }}...HEAD)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
GitHub's Ubuntu runners have Docker, so the test databases work there too.
|
|
258
|
+
|
|
259
|
+
**Git pre-commit hook:**
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
printf '#!/bin/sh\nexec doneguard check $(git diff --cached --name-only)\n' > .git/hooks/pre-commit
|
|
263
|
+
chmod +x .git/hooks/pre-commit
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Agents without a stop hook** (Cline, Windsurf Cascade…): add to `AGENTS.md` or the agent's rules: *"Before saying you're done, run `doneguard check` and fix everything it reports."*
|
|
267
|
+
|
|
268
|
+
## Stats
|
|
269
|
+
|
|
270
|
+
doneguard keeps a tiny local log of every checked stop. Ask it how many times it caught your agent:
|
|
271
|
+
|
|
272
|
+
```text
|
|
273
|
+
$ doneguard stats
|
|
274
|
+
doneguard stats since 2026-10-30
|
|
275
|
+
stops checked 2 in 1 session(s)
|
|
276
|
+
"done" blocked 1
|
|
277
|
+
fixed by the agent 1 of 1 (100%)
|
|
278
|
+
most failing vitest (1)
|
|
279
|
+
agents claude (2)
|
|
280
|
+
|
|
281
|
+
Caught 1 broken "done" claim(s); the agents fixed 100% on their own.
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
That's the real output of the session in the GIF at the top: one stop blocked by Playwright and vitest, fixed on the next try. The log lives in `~/.local/state/doneguard/log.jsonl` (or `$XDG_STATE_HOME`) and stores only times, agent names, results and tool names: no code, no paths. Delete it to start over.
|
|
285
|
+
|
|
286
|
+
## Day to day
|
|
287
|
+
|
|
288
|
+
- **You don't run anything.** The hooks fire on their own when a session starts and every time the agent tries to stop.
|
|
289
|
+
- **When a check fails**, the agent gets the errors and keeps going. You see it fixing things instead of saying "done".
|
|
290
|
+
- **If it still fails after 3 tries**, the agent stops (Claude Code shows `doneguard: checks still failing after 3 fix attempts`).
|
|
291
|
+
- **After an e2e run**, the screenshots and videos are in `.doneguard/evidence/`, newest last. Open the report with `npx playwright show-report .doneguard/evidence/<run>/report`.
|
|
292
|
+
- **Pause a check** by commenting its line with `#` in `.doneguard.conf`. It takes effect on the next stop.
|
|
293
|
+
- **Check by hand** at any time: `doneguard check`.
|
|
294
|
+
|
|
295
|
+
## Teams
|
|
296
|
+
|
|
297
|
+
Commit `.doneguard.conf`. Each developer installs doneguard and runs `doneguard init`, which keeps the existing config and adds their hooks.
|
|
298
|
+
|
|
299
|
+
`.doneguard/` ignores itself, so evidence never lands in a commit by accident. To keep evidence in the repo (for review, or as a CI artifact), delete `.doneguard/.gitignore`.
|
|
300
|
+
|
|
301
|
+
Hook files that live in the repo (`.cursor/hooks.json`, `.codex/hooks.json`, `.gemini/settings.json`, `.github/hooks/doneguard.json`…) run for everyone who opens the project with that agent. Commit them only if everyone has doneguard installed: without it, their agent reports a failed hook on every stop. Claude Code's hooks go to `.claude/settings.local.json`, which is personal.
|
|
302
|
+
|
|
303
|
+
## What `init` detects
|
|
304
|
+
|
|
305
|
+
| Stack | Detected by | Check |
|
|
306
|
+
|---|---|---|
|
|
307
|
+
| TypeScript | `tsconfig.json` | `tsc --noEmit`, errors in changed files only |
|
|
308
|
+
| ESLint / Biome | `eslint` or `@biomejs/biome` in `package.json` | `eslint --quiet` / `biome lint` on changed files |
|
|
309
|
+
| Vitest / Jest | `vitest` or `jest` in `package.json` | `vitest related` / `jest --findRelatedTests` |
|
|
310
|
+
| Playwright | `playwright.config.*` with a `webServer` | the e2e suite, with [evidence](#end-to-end-tests-with-evidence) (Cypress: a suggested line) |
|
|
311
|
+
| Python | `pyproject.toml`, `setup.py`, `requirements.txt` | `ruff` (syntax errors, undefined names), `pytest` on related tests, pytest-playwright e2e |
|
|
312
|
+
| Ruby | `Gemfile` | `rubocop` (errors only), `bin/rails test` or `rspec` on related tests |
|
|
313
|
+
| Go | `go.mod` | `go vet` (findings in changed files), `go test` on the packages with related tests |
|
|
314
|
+
| Rust | `Cargo.toml` | `cargo check`, errors in changed files only |
|
|
315
|
+
| Java / Kotlin | `pom.xml`, `build.gradle*` + `gradlew` | `mvn compile` / `./gradlew classes`, errors in changed files only |
|
|
316
|
+
| C# | `*.sln`, `*.csproj` | `dotnet build`, errors in changed files only |
|
|
317
|
+
| PHP | `composer.json` | `php -l` on changed files, Pest or PHPUnit on related tests |
|
|
318
|
+
| Deno | `deno.json` | `deno check`, `deno test` on related tests |
|
|
319
|
+
| Swift | `Package.swift` | `swift build`, errors in changed files only |
|
|
320
|
+
| Databases | dependencies, Prisma schema, `docker-compose.yml` | a [throwaway Postgres, MySQL, Redis or MongoDB](#tests-that-need-a-database) |
|
|
321
|
+
|
|
322
|
+
It looks at the root and one folder down, so monorepos work: JS checks run once per package (`@package.json`). For Python it uses `uv run` or `.venv/bin/` when it finds them. Anything else is one line of [configuration](#configuration) away.
|
|
323
|
+
|
|
324
|
+
## Configuration
|
|
325
|
+
|
|
326
|
+
`.doneguard.conf` lives at the root of the project. One check per line, `#` for comments:
|
|
327
|
+
|
|
328
|
+
```text
|
|
329
|
+
# <globs> | <dir> | <mode> | <command>
|
|
330
|
+
*.ts,*.tsx | @package.json | filter | npx --no -- tsc --noEmit --pretty false -p .
|
|
331
|
+
*.py | . | files | .venv/bin/ruff check --select E9,F63,F7,F82 --output-format concise
|
|
332
|
+
* | . | postgres | .venv/bin/alembic upgrade head
|
|
333
|
+
* | . | redis | -
|
|
334
|
+
*.py | . | tests | .venv/bin/pytest -x -q --tb=short {tests}
|
|
335
|
+
*.go | . | tests | go test {test_dirs}
|
|
336
|
+
*.tsx,*.css | web | e2e | npx --no -- playwright test
|
|
337
|
+
*.sql | . | command | ./scripts/check-migrations.sh
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
| Field | Meaning |
|
|
341
|
+
|---|---|
|
|
342
|
+
| `globs` | Comma-separated, matched against the path relative to the root. `*` also crosses folders. |
|
|
343
|
+
| `dir` | Where the command runs, relative to the root. `@<file>` means the nearest folder holding `<file>`, so `@package.json` runs once per package in a monorepo. |
|
|
344
|
+
| `mode` | How the result is judged, see below. |
|
|
345
|
+
| `command` | Any shell command. Pipes are fine. |
|
|
346
|
+
|
|
347
|
+
| Mode | Runs | Fails when |
|
|
348
|
+
|---|---|---|
|
|
349
|
+
| `files` | `command <changed files>` | exit code is not 0 |
|
|
350
|
+
| `filter` | `command` as is | an output line cites a changed file (perfect for whole-project tools like `tsc`) |
|
|
351
|
+
| `tests` | `command` with `{tests}` replaced by the related tests (`test_x.py`, `x_test.go`, `x.test.ts`, `x.spec.ts`, `XTest.php`…), or `{test_dirs}` by their folders (`./cart`) | exit code is not 0; skipped when there are no related tests |
|
|
352
|
+
| `command` | `command` as is | exit code is not 0 |
|
|
353
|
+
| `e2e` | `command` with `{evidence}` replaced by a new folder in `.doneguard/evidence/`; `playwright test` gets screenshots, videos and traces there on its own | exit code is not 0; the failure screenshots are listed for the agent |
|
|
354
|
+
| `postgres`, `mysql`, `redis`, `mongo` | a throwaway database in Docker (`postgres=<image>` picks the image), before the first check below it that runs; then `command` (`-` for none) with `DATABASE_URL` and friends pointing at it | the command's exit code is not 0; the checks below it are then skipped |
|
|
355
|
+
|
|
356
|
+
A tool that isn't installed, Docker that isn't running, missing browsers, or a check that takes longer than 150 s (7 minutes for `e2e`) is skipped, never failed.
|
|
357
|
+
|
|
358
|
+
## Commands
|
|
359
|
+
|
|
360
|
+
| Command | What it does |
|
|
361
|
+
|---|---|
|
|
362
|
+
| `doneguard init [agents…]` | Detect the stack, write `.doneguard.conf` (if it has no checks yet, otherwise list what's new) and add the hooks to the agents found here, or to the ones you name (`all` for every one). Safe to run again. |
|
|
363
|
+
| `doneguard check [files…]` | Run the checks on the given files, or on your git changes, with a progress line per check. Exit 1 on failure. |
|
|
364
|
+
| `doneguard stats` | How many "done" claims were blocked, and how many the agents fixed. |
|
|
365
|
+
| `doneguard start` | Hook: mark the start of a session (reads the event JSON on stdin). |
|
|
366
|
+
| `doneguard verify` | Hook: check the files changed since the mark and answer in the agent's format. |
|
|
367
|
+
|
|
368
|
+
## FAQ
|
|
369
|
+
|
|
370
|
+
**Do I need Claude Code?** No. See [Works with your agent](#works-with-your-agent). Without any agent, `doneguard check` works in CI and git hooks.
|
|
371
|
+
|
|
372
|
+
**Does it slow the agent down?** It runs once, when the agent tries to stop, and only the checks that match the changed files. Usually a few seconds.
|
|
373
|
+
|
|
374
|
+
**Can it touch my real database?** The migrate and seed command only runs after the throwaway database is up, with `DATABASE_URL`, the `PG*` variables, every same-kind URL from your `.env` files and shell, and the URL variables your ORM config reads all pointing at it. If Docker can't start it, the command doesn't run at all. `init` only writes the command when it can see the connection goes through one of those variables, and e2e tests never reuse your dev server while a test database is up. What doneguard can't redirect is a connection hardcoded in your code: read the database line before you commit it.
|
|
375
|
+
|
|
376
|
+
**Docker is off on my laptop half the time.** Then the database line and the checks below it are skipped, with a note, and everything above it still runs (`init` puts them last). Start Docker Desktop, OrbStack or Colima and the next stop runs them again.
|
|
377
|
+
|
|
378
|
+
**Isn't a database per stop slow?** About a second with the image cached, plus your migrations and seed. It only starts when a check below it actually runs, and it uses `fsync=off` because the data is thrown away anyway.
|
|
379
|
+
|
|
380
|
+
**My e2e suite takes 10 minutes.** Then it doesn't fit in a stop hook: give the `e2e` line narrower globs (only the pages that matter), point it at a smoke subset (`npx --no -- playwright test e2e/smoke`), or keep the full suite in CI with `doneguard check`.
|
|
381
|
+
|
|
382
|
+
**My project already has lint errors.** `filter` checks (tsc, cargo, go vet, mvn, swift) only count errors in changed files. `files` checks lint whole files, so the default ruff rules are fatal errors only: syntax errors and undefined names.
|
|
383
|
+
|
|
384
|
+
**The agent changed a function and broke a file it didn't touch. Will doneguard see it?** Related tests will: `vitest related` and `jest --findRelatedTests` follow imports. Type errors in untouched files are ignored on purpose, so old errors never block anyone.
|
|
385
|
+
|
|
386
|
+
**What if I edit files while the agent works?** Your changes count too: doneguard checks every file modified since the agent last finished, whoever changed it.
|
|
387
|
+
|
|
388
|
+
**Does it catch edits the agent makes through shell commands?** Yes. It compares modification times, so `sed`, codegen and scripts count, not just the edit tool.
|
|
389
|
+
|
|
390
|
+
**Does it send my code anywhere?** No. It runs your own tools on your machine. No network calls, no telemetry (Docker may pull a database image the first time). It's one file: [`doneguard.py`](https://github.com/abranchesia/doneguard/blob/main/doneguard.py).
|
|
391
|
+
|
|
392
|
+
**How do I uninstall?** Remove the `doneguard` entries from your agent's hook config, delete `.doneguard.conf`, then `uv tool uninstall doneguard`.
|
|
393
|
+
|
|
394
|
+
**Windows?** macOS and Linux for now: commands run through `sh`.
|
|
395
|
+
|
|
396
|
+
## Contributing
|
|
397
|
+
|
|
398
|
+
Small on purpose: one file, standard library only. New stack detections, agent adapters and reports from agents marked 🧪 are welcome.
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
uvx pytest # run the tests
|
|
402
|
+
vhs demo/check.tape # re-record a GIF (needs vhs, npm and Docker; claude.tape also needs Claude Code)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## License
|
|
406
|
+
|
|
407
|
+
[MIT](https://github.com/abranchesia/doneguard/blob/main/LICENSE)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
doneguard.py,sha256=gp4k_XSz2A3jMNF24W_9EgcVRYQ2UiLjjPynTqeH1oo,45181
|
|
2
|
+
doneguard-0.2.0.dist-info/METADATA,sha256=t-KvpuTfwGYf8MSTq0gy-bl-UKZnKwZkh-YTZOB7VbQ,28506
|
|
3
|
+
doneguard-0.2.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
4
|
+
doneguard-0.2.0.dist-info/entry_points.txt,sha256=IwV6pB6lxB4ykzWKZ7-Xzx1DhheZeHA3Aj8B36Zd-TE,45
|
|
5
|
+
doneguard-0.2.0.dist-info/licenses/LICENSE,sha256=xxImg3YtnYgDi5km6LoSqWfMCnXMg0Q4UjkIwCq39bI,1079
|
|
6
|
+
doneguard-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 doneguard contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|