Claude Code works the moment you install it. Then it spends your first week asking permission for npm test and forgetting how your repo builds.
Fixing both takes about fifteen minutes and four plain-text files. I have done this on nine repos now, so here is the version without the ceremony, plus four things I had wrong for longer than I would like.
The clock
| Min | Step |
|---|---|
| 0–2 |
npm install -g @anthropic-ai/claude-code, run claude once in your repo to sign in |
| 2–5 | Copy CLAUDE.md, .claude/ and .mcp.json.example into your repo root |
| 5–10 | Fill in your four commands and your never-do list in CLAUDE.md |
| 10–12 | Edit permissions.allow to match your real test and lint commands |
| 12–15 | Run claude, then your ship-check skill. It should run tests and lint and report PASS/FAIL |
Minutes 5 to 10 are the job. The rest is typing.
Minute 5 to 10: the only step that matters
Claude Code starts every session knowing nothing about your repo. CLAUDE.md is what it reads first, every time. Most people write an essay here. The essay is what makes it useless.
Four commands and a short list of things not to do beats three paragraphs of architecture:
# My Project
## Commands
- install: `pnpm install`
- dev: `pnpm dev`
- test: `pnpm test`
- lint: `pnpm lint --fix`
## Layout
- `src/` app code · `src/db/` migrations, hand-written · `e2e/` Playwright
## Never
- Never edit anything in `src/generated/`
- Never commit to `main`; branch first
- Migrations are forward-only. No editing an applied one
If you do one thing from this post, write the four commands. Everything downstream is about commands Claude already knows how to run.
Minute 10 to 12: why it stops asking
Approving a command approves that run. It is not remembered. That is why you can approve npm test forty times and get asked a forty-first.
{
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git diff:*)",
"Bash(npm test:*)",
"Bash(npm run lint:*)"
],
"deny": [
"Bash(rm -rf:*)",
"Bash(git push --force:*)",
"Read(./.env*)"
]
}
}
The bit that catches everyone: Bash(npm test) matches that exact string, so npm test -- --watch still prompts. Bash(npm test:*) covers the arguments too.
And do not allow everything. The point of an allowlist is that you can stop reading the prompts, which is only safe while the prompts you would want to read still show up.
The four things I had wrong
1. You do not need to restart after editing settings
I restarted Claude Code after every settings change for weeks. You do not have to. It watches the files. If a change looks like it did not take, the configuration is usually wrong rather than stale.
2. A hook matcher is not a glob
I wrote Edit(*) and wondered why nothing fired. A plain matcher value is an exact, case-sensitive tool name, or a pipe-separated list of them. Only a value containing regex characters gets treated as a regex. So "Edit|Write" works and Edit(*) matches nothing at all.
The Bash(npm test:*) shape is permission syntax, which is a different mechanism with different rules. I had been mixing them.
3. Exit 1 does not block anything
A hook returns exit 2 to block a tool call. exit 0 is success and exit 1 is a non-blocking error. So the Unix reflex of returning 1 on failure gets you a hook that reports a problem and then lets the action through. Mine "worked" for a week in the sense that it never blocked anything.
4. A failing hook says nothing where you can see it
Hook stderr goes to the debug log, never to the transcript. So a hook that crashes on line one and a hook that was never wired up look identical from the chat. This is the single most confusing thing in the whole setup.
claude --debug hooks
That prints hook resolution, the source file for each hook, the process spawn, and the error. /hooks in-session lists what is actually loaded. If it is not in that list it is a config problem; if it is, it is a script problem. Two different afternoons.
What you can skip on day one
Four words get used as one thing, and three of them can wait:
-
A skill — yes, one. It is a
SKILL.mdand it is also the slash command. Start with a ship-check. - Hooks — later. Useful once you know what should be enforced rather than requested.
- An MCP server — only if Claude needs to reach something outside your machine. Words cannot query your database.
- A plugin — no. Plugins are about handing a setup to other people. Nothing to package on day one.
Copy a working one
All four files, filled in, MIT, nothing held back: kit-claude-code-starter.
git clone https://github.com/sdvsignal/kit-claude-code-starter
cp -r kit-claude-code-starter/{CLAUDE.md,.claude,.mcp.json.example} your-repo/
cd your-repo && claude
There is no paid tier of that repo. If you already know your build commands, this post plus that clone is the whole job.
Longer version of this with the tables and the fix pages it links to: the 15-minute Claude Code setup. We do also sell the fifteen minutes done for you, which is on that site, but the free repo is the honest recommendation for most people and I would rather say so than not.
Checked against Claude Code 2.1.278.
Top comments (0)