DEV Community

SDVSignal
SDVSignal

Posted on

The 15-minute Claude Code setup, and the four things people get wrong

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
Enter fullscreen mode Exit fullscreen mode

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*)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.md and 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
Enter fullscreen mode Exit fullscreen mode

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)