agents/skills/writing-claude-md-files/SKILL.md
Use when creating or updating CLAUDE.md / AGENTS.md files for a project or a subdirectory. Covers the split between top-level (how to work here) and domain-level (why this exists, what it promises), and the freshness date convention.
npx skillsauth add timofreiberg/dotfiles writing-claude-md-filesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
These files exist because agents start each session without context. They preserve the things you'd otherwise re-explain every time: how to build, what the conventions are, why a domain is shaped the way it is.
For directive-writing fundamentals (token economy, motivation framing, discovery), see writing-claude-directives.
Tools differ in which file they read — Claude Code reads CLAUDE.md (not
AGENTS.md); most other agents read AGENTS.md. Keep one real file per
repo and bridge with a symlink or an @AGENTS.md import line so every
tool finds it.
The two flavors do different jobs:
Hierarchy: the root CLAUDE.md loads at session start; subdirectory files
load lazily when the agent reads files in that directory (and aren't
re-injected after compaction). So a file in src/domains/auth/ gets
src/domains/auth/CLAUDE.md plus the root one — no need to repeat root
content in domain files, but don't put session-critical rules only in a
subdirectory file.
Depth heuristic: typically zero or one level of subdirectory CLAUDE.md
files. Two levels (e.g. auth/oauth2/CLAUDE.md) is rare and only when the
subdomain has its own non-obvious contracts.
Claude Code alternative: path-scoped rule files in .claude/rules/ with
paths: frontmatter do the same job as subdirectory CLAUDE.md files and
keep the source tree free of doc files. Prefer them in Claude Code-only
repos; subdirectory CLAUDE.md files are the portable form.
# [Project Name]
Last verified: <run `date +%Y-%m-%d`>
## Tech Stack
- Language: TypeScript 5.x
- Framework: Next.js 14
- Database: PostgreSQL
- Testing: Vitest
## Commands
- `npm run dev` — start dev server
- `npm run test` — run tests
- `npm run build` — production build
## Project Structure
- `src/domains/` — domain modules (auth, billing, etc.)
- `src/shared/` — cross-cutting utilities
- `src/infrastructure/` — external adapters (DB, APIs)
## Conventions
- Functional core, imperative shell
- Domains are self-contained — no cross-domain imports
- Domain-specific guidance lives in each domain's CLAUDE.md
## Boundaries
- Safe to edit: `src/`
- Don't touch: `migrations/` (immutable history), `*.lock`
What to leave out of the top-level file:
package.json / justfile instead).# [Domain Name]
Last verified: <run `date +%Y-%m-%d`>
## Purpose
[1-2 sentences: why this domain exists, what problem it owns.]
## Contracts
- **Exposes:** [public interface — what callers can use]
- **Guarantees:** [promises this domain keeps]
- **Expects:** [what callers must provide]
## Dependencies
- **Uses:** [domains/services this depends on]
- **Used by:** [what depends on this]
- **Boundary:** [what should NOT be imported here]
## Key Decisions
- [Decision]: [Rationale]
## Invariants
- [Things that must always be true]
## Key Files
- `index.ts` — public exports
- `service.ts` — main implementation
## Gotchas
- [Non-obvious thing that will bite you]
Worked example — auth domain:
# Auth Domain
Last verified: 2025-12-17
## Purpose
Verifies user identity once at the system edge. Downstream services trust
the token without re-validating.
## Contracts
- **Exposes:** `validateToken(token) → User | null`, `createSession(creds) → Token`
- **Guarantees:** Tokens expire after 24h. User objects always include roles.
- **Expects:** Valid JWT format. Database connection available.
## Dependencies
- **Uses:** Database (users), Redis (session cache)
- **Used by:** All API routes, billing (user identity only)
- **Boundary:** Don't import from billing, notifications, or other domains
## Key Decisions
- JWT over session cookies — stateless auth for horizontal scaling
- bcrypt cost 12 — legacy; argon2 migration tracked in ADR-007
## Invariants
- Every user has exactly one primary email
- Deleted users are soft-deleted (is_deleted), never hard deleted
- User IDs are UUIDs, never sequential
## Key Files
- `service.ts` — AuthService implementation
- `tokens.ts` — JWT creation/validation
- `types.ts` — User, Token, Session
## Gotchas
- `validateToken` returns null on invalid input, doesn't throw
- Never include raw password hashes in serialized User objects
Every CLAUDE.md should have a Last verified date near the top. The date
signals when the contracts were last confirmed against the code; without
it, a six-month-old file looks identical to one written yesterday.
Get the actual date from the shell rather than guessing:
date +%Y-%m-%d
Stale CLAUDE.md is worse than missing CLAUDE.md — it's wrong with confidence. When you touch a domain whose contracts changed, update the date in the same change.
The claim-level version of the freshness concern: a precise claim sourced from prose rather than primary evidence — version numbers, dates, counts, authorship, fork points — rots over time AND propagates forward, because downstream agents cite the number as authoritative without re-verifying. "A long time ago" beats "forked from 2.1.4" unless 2.1.4 is grounded in git log, release notes, or package metadata. Applies to all agent-facing prose: CLAUDE.md, AGENTS.md, team READMEs, PR descriptions.
Add one when the domain has:
Skip it for:
When you change a domain that has a CLAUDE.md:
Last verified to today.Plain prose is enough — name the files inline:
## Key Files
- `index.ts` — public exports
- `service.ts` — main implementation
@file syntax force-loads the file into context at session start whether
or not the agent needs it. Use it only when that's the point (e.g.
@AGENTS.md as an interop shim); for everything else, naming the file
lets the agent read it on demand.
| Mistake | Fix |
|--------------------------------------------|----------------------------------------------------|
| Describing what code does | Focus on why it exists and what it promises |
| Missing or guessed freshness date | Use date +%Y-%m-%d from the shell |
| Casual @-file references | Force-load only deliberately (e.g. @AGENTS.md shim) |
| Subdirectory file > 100 lines | Push detail into code comments or split the file |
| Repeating top-level content in subdirs | Subdirectories inherit; don't duplicate |
| Docs unchanged after the domain changed | Update Last verified and check the contracts |
Top-level:
Subdirectory:
databases
Use when a judgment forms during work that a future session would benefit from — a fork you resolved, a correction from the user, a wrong assumption about the environment, something you had to rediscover. Appends one timestamped entry to the journal staging dir.
development
Use when starting your work day: groom the todo list to a trusted state, archive finished work, surface today's candidates, and propose a concrete first move. Stab-then-confirm, ~5 min.
data-ai
Use when reviewing local changes — the working-copy diff, a branch, a commit, or a GitHub PR by number — with a fresh subagent that returns a structured findings report.
tools
Use when a question needs current internet information — docs, news, releases, prices. Prefer a built-in web search tool for quick lookups if the harness has one; this script returns a model-summarized answer with source URLs and works without one.