A TypeScript starter for AI-agent development that keeps you out of trouble.
Like the bumpers in a bowling lane, it keeps you and your agents out of the gutter without taking over the throw. The guardrails are deterministic and enforced, not advisory. Formatting, linting, types, tests, and secret scanning run as hooks and CI checks that block, so an agent (or a person) can't skip them by forgetting.
- Download a release: the latest
bumpers-vX.Y.Z.zipfrom Releases. It's a versioned snapshot with no git history, tested to pass the quick start below before it was published. - Use the template: click "Use this template" on GitHub to create your own repository from the latest
main.
Then follow the quick start.
| Guardrail | When it runs | What it catches |
|---|---|---|
Edit hook: .claude/hooks/format-and-lint.ts |
Claude Code, after every Edit or Write | Formats the file with Biome (code, JSON, Markdown, YAML) and applies safe fixes. Remaining lint errors go back to Claude (exit code 2) so it fixes them in the same turn. For Markdown, also parses every Mermaid diagram, so one that won't render is caught at once. |
Stop hook: .claude/hooks/typecheck-on-stop.ts |
Claude Code, before a turn ends | Type errors anywhere in the workspace, including the hooks and scripts. Claude can't finish the turn until pnpm typecheck passes; it gets one retry before the hook lets it stop, so it can't loop. |
Prompt hook: .claude/hooks/snapshot-on-prompt.ts |
Claude Code, when a prompt is sent | Nothing by itself. Commits the working tree to the turn history (below), so the Stop hook can see every file the turn changed. |
Stop hook: .claude/hooks/review-turn-on-stop.ts |
Claude Code, before a turn that changed files ends | A final message with no Unrequested decisions: section; source changed with no diagram change and no Diagrams: line explaining why; files changed without an edit tool (by a command, or out of band by an editor or watcher) that the message doesn't name; and any diagram check failure. Patterns in .claude/turn-review-ignore are left out. Without a turn-start snapshot it falls back to the transcript's edit-tool calls. One retry, like the typecheck hook. |
Pre-commit: Biome (lefthook.yml) |
git commit, on staged files |
Formatting and lint errors in code, JSON, Markdown, and YAML. Fixes what it safely can and restages it. |
| Pre-commit: typecheck | git commit, when TypeScript files are staged |
Type errors. |
Pre-commit: diagrams (scripts/check-diagrams.ts --staged) |
git commit |
Diagrams that don't parse, packages missing from the architecture diagram, services in no sequence diagram, and a staged diagram change with no docs/diagrams/CHANGELOG.md change. |
| Pre-commit: gitleaks | git commit, if gitleaks is installed |
Secrets in staged changes: API keys, tokens, private keys. Skipped with a message if gitleaks isn't installed; CI still scans. |
| Pre-push: tests | git push |
Failing Vitest tests. |
CI: .github/workflows/ci.yml |
Push to main and every pull request |
biome ci, typecheck, tests, the diagram check against the base branch, and gitleaks over the full history. On pull requests, the description must have an Unrequested decisions section (.github/pull_request_template.md). The backstop for anything run with --no-verify or outside Claude Code. |
Node version: .nvmrc, engines, .npmrc |
pnpm install, and every Claude Code hook |
Running on anything older than Node 26. pnpm install stops with an "unsupported engine" error, and the hooks say which Node they found and how to switch. |
TypeScript strict config: tsconfig.base.json |
Every typecheck | strict, plus noUncheckedIndexedAccess, exactOptionalPropertyTypes, and verbatimModuleSyntax. The root tsconfig.json adds erasableSyntaxOnly for the hooks and scripts, which Node runs directly. |
| pnpm strict dependencies | Install and import | A package importing something it didn't declare. Services can only reach each other's code if they declare a dependency on it. |
Zod contracts: packages/contracts |
Runtime, at every service boundary | Malformed requests, responses, and events, rejected where they enter instead of deep inside a service. |
Some rules in AGENTS.md are conventions, not checks: contracts first, services talking over HTTP, and code deciding facts while models make judgment calls. Review enforces those. Two are checked only in part: the hooks can require that diagrams render, cover every package, and are logged, and that an agent says which decisions it made unasked, but not that a diagram is accurate or that the list of decisions is complete. Review covers the rest.
New to TypeScript or to working with coding agents? Each step says what it does.
-
Install Node 26 with a version manager, so each project can use the Node it needs. With nvm, run
nvm installin this folder; it reads.nvmrc. To make Node 26 your default, also runnvm alias default 26. -
Install pnpm, the package manager:
npm install --global pnpm. (Node 26 no longer includes Corepack, the older way to get pnpm.) -
Install the project:
git init # only for the zip; a template or clone is already a repo pnpm install # dependencies, git hooks, and the turn history pnpm setup:tools # offers to install gitleaks and the GitHub CLI pnpm check # lint, typecheck, check diagrams, and test
pnpm installlists any optional tools you're missing;pnpm setup:toolsasks before installing each one, with Homebrew on macOS and Linux or winget on Windows. -
Open the folder in Claude Code from a terminal where
node -vshows v26. The hooks in.claude/settings.jsonload from the project and run on Node.
Each Claude Code turn is committed twice, when it starts and when it ends, to a separate git repository in .claude/turn-history/. It's gitignored, local to your clone, and never touches the project's own .git. The Stop hook uses it to see every file a turn changed. You can use it to see what any turn did:
git --git-dir=.claude/turn-history for-each-ref refs/sessions # one branch per session
git --git-dir=.claude/turn-history log --stat refs/sessions/<id> # its turns, newest firstSessions with no activity for 30 days are pruned. Delete the folder to start over; the next pnpm install or prompt recreates it.
packages/contracts: Zod schemas and their inferred types, shared by every service. The only package other packages may import.services/example: one service that imports only@app/contractsand parses its input at the boundary.docs/diagrams/: Mermaid architecture and sequence diagrams, and a changelog of every change to them.scripts/: repo tooling in TypeScript, run directly by Node: the diagram check,prepare(runs afterpnpm install), andsetup:tools.scripts/tools.tslists the command-line tools the repo uses..claude/hooks/: the Claude Code hooks, in TypeScript..claude/settings.jsonwires them up throughrun.sh, which checks the Node version first.docs/intent.md: why the project exists.docs/spec.md: what it must do, with requirement IDs.docs/decisions/: choices and their reasons.AGENTS.md: rules for agents and people.CLAUDE.mdimports it.
- Rename the root package in
package.jsonand the@app/scope in eachpackage.jsonand import. - Fill in the placeholders (
<LIKE THIS>) inAGENTS.md,docs/intent.md, anddocs/spec.md. - Replace the greeting schemas in
packages/contractswith your own, andservices/examplewith your first service. Keep the test next to each schema. - Adding an
apps/folder or another workspace root: add it topnpm-workspace.yamland toprojectsinvitest.config.ts. Every package needs atypecheckscript, whichpnpm typecheckruns. - Replace the diagrams in
docs/diagrams/with your own, and log it in itsCHANGELOG.md. - Exclude generated files or vendored code from Biome with
"!path"infiles.includesinbiome.json. - Add the names of any keys to
.env.examplewith no values, and copy it to.env. - Record each significant choice in
docs/decisions/, starting from0000-template.md. - Change
.nvmrc,engines, and@types/nodetogether when you move to the next Node LTS. - Add a command-line tool the repo needs to
scripts/tools.ts, sopnpm installreports it andpnpm setup:toolsoffers it.
Apache License 2.0. See LICENSE and NOTICE.