Skip to content
Open
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
13 changes: 13 additions & 0 deletions .claude/commands/api-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
description: Review the diff against this repo's API conventions checklist
---

Run `git diff $ARGUMENTS` (default to `main` if no argument is given, i.e. `git diff main`) and review the changed `routes/`, `db/store.js`, and `tests/` code against this project's checklist:

- Data access goes through `db/store.js` only — routes never hold state directly.
- Bad/missing input returns `400`; a missing record returns `404`.
- Every error response is JSON shaped `{ "error": "message" }`.
- Every new/changed route has a matching test in `tests/` covering the success path and the `400`/`404` paths.
- `docs/api.md` is updated to match any new or changed endpoint.

Report each violation with the file and line, and call out anything that looks correct but is untested. Do not edit any files — this is a read-only review.
21 changes: 21 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"permissions": {
"allow": [
"mcp__docs__list_directory",
"mcp__docs__read_text_file"
]
},
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "npm run lint -- --fix"
}
]
}
]
}
}
15 changes: 15 additions & 0 deletions .claude/skills/add-route/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
name: add-route
description: Use when adding a brand-new HTTP endpoint or resource to this Express API (e.g. "add a DELETE /users/:id route", "add a posts resource", "add an endpoint to list active users"). Encodes this repo's route convention so new endpoints match the existing ones. Do not use for editing an existing route's logic, for non-route changes, or for other projects.
---

Add the new endpoint following this repo's convention exactly:

1. **Router file** — add the handler to the existing resource's file in `routes/` (e.g. `routes/users.js`), or create a new file per resource if it's a new resource. Export an Express router.
2. **Mount it** — if it's a new resource file, mount it in `server.js` under its base path, matching how `users.js` and `health.js` are mounted.
3. **Data access** — read/write only through `db/store.js`. Add a helper function there if the store doesn't already expose what you need; the route itself must not hold state.
4. **Validation** — return `400` with a JSON body on bad/missing input, and `404` with a JSON body when a referenced record doesn't exist. Use the existing routes as the template for exactly when each applies.
5. **Error shape** — every error response is `{ "error": "message" }`, matching the existing routes.
6. **Tests** — add cases to the matching file in `tests/` using `node:test` + `supertest`, following the existing `describe`-free flat style and the `test.beforeEach(() => store.reset())` pattern already in `tests/users.test.js`. Cover the success path and the `400`/`404` paths.
7. **Docs** — add or update the endpoint's section in `docs/api.md`, matching the format of the existing entries (method + path heading, one-line description, request/response shapes, status codes).
8. Run `npm test` and `npm run lint` before considering the work done.
9 changes: 9 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"mcpServers": {
"docs": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "${CLAUDE_PROJECT_DIR:-.}/docs"]
}
}
}
16 changes: 16 additions & 0 deletions NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# NOTES

## Server
Connected the official `@modelcontextprotocol/server-filesystem` at project scope (`.mcp.json`), pointed at this repo's `docs/` folder and named `docs`. It's credential-free and useful here because `docs/api.md` is the canonical reference for what each endpoint does — Claude can read it directly instead of re-deriving it from the route code every time. The permission rule in `.claude/settings.json` allows only `mcp__docs__list_directory` and `mcp__docs__read_text_file` — the two read-only tools actually used — so the server can't write, move, or delete anything even though the underlying package supports it. Verified by running it headless: it read `docs/api.md` and correctly listed all five documented endpoints.

## Skill
`.claude/skills/add-route/SKILL.md` captures the repo's route convention: handler in `routes/`, mounted in `server.js`, data access only through `db/store.js`, `400`/`404` validation, `{ "error": "message" }` error shape, tests in `tests/` with the `store.reset()` pattern, and a matching `docs/api.md` entry. The description names concrete trigger phrases ("add a DELETE /users/:id route", "add a posts resource") and explicitly excludes editing existing route logic, so it fires for new-endpoint requests and nothing else. Confirmed live: asked headless Claude to "Add a DELETE /users/:id endpoint that removes a user" (no mention of the skill), and the trace showed `Launching skill: add-route`. It correctly touched `routes/users.js`, `db/store.js`, `tests/users.test.js`, and `docs/api.md`; all 7 tests passed and lint was clean. Reverted afterward since this run was only to confirm the skill fires, not a deliverable feature.

## Command
`/api-review [ref]` (`.claude/commands/api-review.md`) runs `git diff` against `ref` (default `main`) and checks the changed `routes/`, `db/store.js`, and `tests/` code against the same checklist the skill encodes, flagging violations by file and line without editing anything. It's worth a shortcut because it's the review I'd otherwise do by hand on every PR to this API. Verified by deliberately adding a broken `DELETE /users/:id` handler (wrong error shape, no validation, a call to a store function that doesn't exist, no tests, no docs) and running `/api-review main` headless with only `Bash(git diff:*) Read Grep Glob` allowed — it caught all five issues, including the `store.deleteUser` crash, and made no edits. Reverted afterward.

## Hook
A `PostToolUse` hook on the `Edit|Write|MultiEdit` matcher runs `npm run lint -- --fix` (`.claude/settings.json`). It reacts rather than prevents, because the standard it holds — lint-clean code — is something to repair immediately after every edit, not something to block an edit over. Triggered on purpose by having headless Claude append a line with an unused variable to `routes/health.js`; the hook log shows `PostToolUse:Edit` firing and running the lint command (exit 0), with eslint reporting the `no-unused-vars` warning back. Reverted the test edit afterward.

## Headless run
Ran one task with `claude -p` and `--allowedTools "Bash(npm test:*) Bash(npm run lint:*)"`: "run the test suite and the linter, and report whether the repo is green." Locked it down to exactly those two read-style commands — no `Edit`, `Write`, or unscoped `Bash`, so the run can't change anything in the repo, only report on it. It came back clean: 5/5 tests passing, lint clean, zero permission denials.
Loading