Skip to content

Commit 5dbf45c

Browse files
committed
Revise AGENTS.md to streamline editing policy and documentation structure. Emphasize concise project-specific guidance and link to detailed agent documentation in docs/agents/.
1 parent 1e41c6d commit 5dbf45c

File tree

5 files changed

+193
-50
lines changed

5 files changed

+193
-50
lines changed

AGENTS.md

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,22 @@
11
# AGENTS.md
22

3-
## Cloud-specific instructions
3+
This file is included in full context for every agent conversation. Keep it
4+
tiny and stable.
45

5-
### Overview
6+
## Editing policy
67

7-
This is Kent C. Dodds' personal website (kentcdodds.com) — a React Router v7 app
8-
with Express, SQLite (via Prisma), and extensive MSW mocks for all external
9-
APIs.
8+
- Avoid adding operational details to `AGENTS.md`.
9+
- Only update `AGENTS.md` to reference other docs/files.
10+
- Put project callouts and workflow details in `docs/agents/` and link them from
11+
here.
1012

11-
### Prerequisites
13+
## Agent docs (source of truth)
1214

13-
- **Node.js 24** is required (`engines` field in `package.json`). Install via
14-
`nvm install 24 && nvm alias default 24`.
15-
- The `.npmrc` sets `legacy-peer-deps=true`; `npm install` respects this
16-
automatically.
15+
- `docs/agents/project-context.md` (setup, commands, project-specific caveats)
16+
- `docs/agents/code-style.md`
17+
- `docs/agents/testing-principles.md`
1718

18-
### Key commands
19-
20-
Standard dev commands are documented in `README.md` and `CONTRIBUTING.md`. Quick
21-
reference:
22-
23-
| Task | Command |
24-
| --------------- | --------------------------------------------------------------------------------- |
25-
| Dev server | `npm run dev` (starts on port 3000 with `MOCKS=true`) |
26-
| Lint | `npm run lint` |
27-
| Typecheck | `npm run typecheck` |
28-
| Unit tests | `npm run test -- --watch=false` |
29-
| E2E tests | `npm run test:e2e:dev` (requires Playwright browsers: `npm run test:e2e:install`) |
30-
| DB reset + seed | `npx prisma@7 migrate reset --force` then `npx tsx other/runfile prisma/seed.ts` |
31-
32-
### Non-obvious caveats
33-
34-
- **Dev server is not a TTY**: `server/dev-server.js` detects non-TTY and
35-
disables keyboard shortcuts. This is expected in cloud agent terminals.
36-
- **All external APIs are mocked** via MSW when `MOCKS=true` (the default in
37-
dev). No real API keys are needed for local development — the `.env.example`
38-
values are sufficient.
39-
- **SQLite is file-based**: The database file lives at `prisma/sqlite.db`. No
40-
external database server is required.
41-
- **Cache database**: A separate SQLite cache DB is created at `other/cache.db`.
42-
It's populated on first request or via `npm run prime-cache:mocks`.
43-
- **Patch-package**: Three Remix packages are patched during `postinstall`
44-
(patches in `other/patches/`). If you see patch errors after dependency
45-
changes, check those patches.
46-
- **Content is filesystem-based**: Blog posts are MDX files in `content/blog/`.
47-
Changes to content files are auto-detected by the dev server's file watcher.
19+
If you discover a new sharp edge, workflow, or non-obvious project behavior,
20+
update the relevant doc(s) in `docs/agents/` so future agent runs are faster and
21+
more correct. Keep callouts organized under clear headings and prefer concise,
22+
project-specific guidance over generic advice.

docs/agents/code-style.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Code style
2+
3+
Apply these rules to all new or edited code. When in doubt, match the existing
4+
file style first, then run the formatter.
5+
6+
## Function forms
7+
8+
- Prefer function declarations for named, reusable functions.
9+
- Use arrow functions for callbacks and inline handlers.
10+
- Use object method shorthand for multi-line object methods.
11+
12+
## Array types
13+
14+
- Prefer `Array<T>` and `ReadonlyArray<T>` over `T[]`.
15+
- This avoids precedence pitfalls in union types and keeps type reads clearer.
16+
17+
## Exports
18+
19+
- Prefer named exports.
20+
- Use default exports only when a framework contract requires them.
21+
22+
## Imports
23+
24+
- Prefer repo-root `#...` imports (configured via `package.json` `"imports"`)
25+
over parent-relative `../...` paths.
26+
- Keep `./...` imports for same-folder files.
27+
- Generated files (for example `types/worker-configuration.d.ts`) are allowed to
28+
be exceptions; do not edit them by hand.
29+
30+
## Type conventions
31+
32+
- Prefer `type` aliases for object shapes and unions.
33+
- Use `interface` only when you need declaration merging or public extension
34+
points.
35+
- Prefer inline type definitions in parameters over named types unless sharing
36+
is necessary. When a one-off named type is useful, consider `Parameters<>` (or
37+
similar utility types) instead.
38+
- Use `satisfies` when exporting objects that must match framework contracts.
39+
40+
## Absence values
41+
42+
- Use `null` for explicit "no value" in local state or API responses.
43+
- Use `undefined` for optional or omitted fields, and avoid mixing within one
44+
API.
45+
46+
## References
47+
48+
- https://kentcdodds.com/blog/function-forms
49+
- https://tkdodo.eu/blog/array-types-in-type-script

docs/agents/project-context.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Project context
2+
3+
This is Kent C. Dodds' personal website (kentcdodds.com) — a React Router v7 app
4+
with Express, SQLite (via Prisma), and extensive MSW mocks for all external APIs.
5+
6+
## Prerequisites
7+
8+
- Node.js 24 is required (`engines` in `package.json`).
9+
- Install via `nvm install 24 && nvm alias default 24`.
10+
11+
## Key commands
12+
13+
Standard dev commands are documented in `README.md` and `CONTRIBUTING.md`. Quick
14+
reference:
15+
16+
| Task | Command |
17+
| --------------- | --------------------------------------------------------------------------------- |
18+
| Dev server | `npm run dev` (starts on port 3000 with `MOCKS=true`) |
19+
| Lint | `npm run lint` |
20+
| Typecheck | `npm run typecheck` |
21+
| Unit tests | `npm run test` |
22+
| E2E tests | `npm run test:e2e:dev` (requires Playwright browsers: `npm run test:e2e:install`) |
23+
| DB reset + seed | `npx prisma@7 migrate reset --force` then `npm run runfile -- prisma/seed.ts` |
24+
25+
## Non-obvious caveats
26+
27+
- All external APIs are mocked via MSW when `MOCKS=true` (the default in dev). No
28+
real API keys are needed for local development; `.env.example` values are
29+
sufficient.
30+
- SQLite is file-based: the database file lives at `prisma/sqlite.db`. No
31+
external database server is required.
32+
- Cache database: a separate SQLite cache DB is created at `other/cache.db`.
33+
It's populated on first request or via `npm run prime-cache:mocks`.
34+
- Content is filesystem-based: blog posts are MDX files in `content/blog/`.
35+
Changes to content files are auto-detected by the dev server's file watcher.
36+

docs/agents/testing-principles.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Testing principles
2+
3+
This codebase favors small, readable tests with explicit setup and minimal
4+
magic.
5+
6+
## Principles
7+
8+
- Prefer flat test files: use top-level `test(...)` and avoid `describe`
9+
nesting.
10+
- Avoid shared setup like `beforeEach`/`afterEach`; inline setup per test.
11+
- Don't write tests for what the type system already guarantees.
12+
- Use disposable objects only when there is real cleanup. If no cleanup, skip
13+
`using` and `Symbol.dispose`.
14+
- Build helpers that return ready-to-run objects (factory pattern), not globals.
15+
- Keep test intent obvious in the name: "auth handler returns 400 for invalid
16+
JSON".
17+
- Write tests so they could run offline if necessary: avoid relying on the
18+
public internet and third-party services; prefer local fakes/fixtures.
19+
- Prefer fast unit tests for server logic; keep e2e tests focused on journeys.
20+
- Run server tests with `bun test server` to avoid Playwright spec discovery.
21+
22+
## Examples
23+
24+
### `Symbol.dispose` with `using`
25+
26+
```ts
27+
import { test, expect } from 'bun:test'
28+
29+
const createTempFile = () => {
30+
const path = `/tmp/test-${crypto.randomUUID()}.txt`
31+
Bun.write(path, 'hello')
32+
33+
return {
34+
path,
35+
[Symbol.dispose]: () => {
36+
try {
37+
Bun.file(path).delete()
38+
} catch {
39+
// Cleanup should never fail the test.
40+
}
41+
},
42+
}
43+
}
44+
45+
test('reads a temp file', () => {
46+
using tempFile = createTempFile()
47+
const contents = Bun.file(tempFile.path).text()
48+
return contents.then((text) => expect(text).toBe('hello'))
49+
})
50+
```
51+
52+
### `Symbol.asyncDispose` with `await using`
53+
54+
```ts
55+
import { test, expect } from 'bun:test'
56+
57+
const createDisposableServer = async () => {
58+
const server = Bun.serve({
59+
port: 0,
60+
fetch: () => new Response('ok'),
61+
})
62+
63+
return {
64+
url: `http://localhost:${server.port}`,
65+
[Symbol.asyncDispose]: async () => {
66+
await server.stop()
67+
},
68+
}
69+
}
70+
71+
test('fetches from a disposable server', async () => {
72+
await using server = await createDisposableServer()
73+
const response = await fetch(server.url)
74+
expect(await response.text()).toBe('ok')
75+
})
76+
```

server/dev-server.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import * as readline from 'node:readline'
2+
import { stripVTControlCharacters, styleText } from 'node:util'
23
import { execa } from 'execa'
34

5+
process.env.NODE_ENV ??= 'development'
6+
47
const tryRunCommand = async (command, args, input) => {
58
try {
69
await execa(command, args, input ? { input } : undefined)
@@ -38,7 +41,8 @@ const copyToClipboard = async (value) => {
3841
return tryRunCommand('xclip', ['-selection', 'clipboard'], value)
3942
}
4043

41-
const stripAnsi = (value) => value.replace(/\x1b\[[0-9;]*m/g, '')
44+
const canStyle = Boolean(process.stdout.isTTY && process.stdout.hasColors?.())
45+
const color = (style, text) => (canStyle ? styleText(style, text) : text)
4246

4347
if (process.env.NODE_ENV === 'production') {
4448
// the file may not be there yet
@@ -52,7 +56,7 @@ if (process.env.NODE_ENV === 'production') {
5256
let lastLocalUrl = `http://localhost:${process.env.PORT || 3000}`
5357

5458
const extractLocalUrl = (text) => {
55-
const cleaned = stripAnsi(text)
59+
const cleaned = stripVTControlCharacters(text)
5660
const localMatch = cleaned.match(/Local:\s+(\S+)/)
5761
if (localMatch?.[1]) {
5862
return localMatch[1]
@@ -123,22 +127,25 @@ if (process.env.NODE_ENV === 'production') {
123127
startChild()
124128
}
125129

126-
const printHelp = () => {
130+
function printHelp() {
127131
console.log(
128132
[
129133
'Supported keys:',
130-
` o - open app (${lastLocalUrl})`,
131-
` c - copy url (${lastLocalUrl})`,
132-
' r - restart app',
133-
' h - help',
134-
' q - exit (or Ctrl+C)',
135-
].join('\n'),
134+
` ${color('green', 'o')} - open app (${lastLocalUrl})`,
135+
` ${color('cyan', 'c')} - copy url (${lastLocalUrl})`,
136+
` ${color('magenta', 'r')} - restart app`,
137+
` ${color('yellow', 'h')} - help`,
138+
` ${color('red', 'q')} - exit (or Ctrl+C)`,
139+
].join('\n')
136140
)
137141
}
138142

139143
startChild()
140144

141-
if (process.stdin.isTTY && process.stdout.isTTY) {
145+
const shortcutsEnabled = Boolean(process.stdin.isTTY && process.stdout.isTTY)
146+
147+
if (shortcutsEnabled) {
148+
printHelp()
142149
readline.emitKeypressEvents(process.stdin)
143150
process.stdin.setRawMode(true)
144151
process.stdin.resume()

0 commit comments

Comments
 (0)