settings.json is not a preferences file. It is where your agent's behavior is configured. Themes and model choice live there too, but those are the boring part. The part worth understanding is the one block that changes what the agent does, not how it looks. This is the short version of the file that matters.
Where the Claude Code settings.json file lives
In three places, and precedence is the whole point. .claude/settings.local.json in your project (gitignored, yours on this machine) wins first. Then .claude/settings.json committed in the repo, shared by the team. Then ~/.claude/settings.json, your global default everywhere. The most specific file overrides the broader ones, key by key.
~/.claude/settings.json · your cross-project defaults, local to you.claude/settings.json · committed, so everyone gets the same behavior.claude/settings.local.json · gitignored, machine-specific paths and overridesThis split is the same discipline you use for a CLAUDE.md file. Personal stays local, shared gets committed, and the machine-specific bits never touch git. Get the precedence right and you stop fighting your own config.
The only block that changes behavior is hooks
Every other key in settings.json tunes appearance or access: theme, model, permissions, status line. The hooks block is different. It maps a lifecycle event to a shell command Claude Code runs automatically at that moment. That is the only part of the file that makes the agent act, so it is the only part worth studying. See how hooks work for the full mechanism.
A hook entry has one exact shape. Our installer writes it like this, and so should you:
{ "matcher": "Bash",
"hooks": [{ "type": "command", "command": "node safety-guard.mjs" }] }The matcher decides which tool calls fire the hook. Leave it off and the event fires unconditionally. The command is just a shell string, so a hook can be any script in any language. That is the entire contract.
What kitstarter wires into settings.json, event by event
kitstarter's installer generates the whole hooks block for you, one entry per lifecycle event. Here is the real event-to-script table it writes, straight from the wiring code. Nothing invented, this is what ships.
session-start.mjs, tutor.mjs · load state and the tutor at the top of a sessionclarity.mjs, tutor.mjs · catch a vague ask before the agent runs with itsafety-guard.mjs · warn before a destructive shell commandclarity.mjs · pause before a build the agent never asked aboutslop-check.mjs · scan the file just written for AI-slop tellssubagent-rules.mjs · push the same rules into spawned subagentstutor.mjs · keep the tutor alive across a context compactionstop-reminder.mjs / recap.mjs · gate the finish, then recap what happenedBash for safety, the four edit tools for clarity, Edit|Write for slop-check.Read the matchers closely, because that is where the design lives. Safety-guard listens only on Bash, so it never slows an edit. Clarity listens on all four edit tools plus prompt submit, because a build can start from any of them. Slop-check listens on Edit|Write, since that is when a file changes on disk.
What a wired hook actually does
Each script is small and fails open, meaning any error and it goes silent rather than blocking your work. safety-guard.mjs reads the pending Bash command and matches it against a list of destructive patterns: recursive deletes of a root or home path, force-push, hard reset, dropping a table. It warns, it never blocks. A kit that hard-blocks your commands gets uninstalled.
slop-check.mjs fires after an edit to a UI file. It runs the slop detector on just that one file and, if it finds tells, surfaces them as context the agent must fix before moving on. Both hooks are triggered by nothing more than an entry in the hooks block. That is the leverage the file gives you.
Does wiring settings.json this way actually help?
Yes on the things that matter, and we measured it honestly. In our clean-room benchmark against vanilla Claude Code, the kit asked before building on 5/5 tasks versus 2/5 for vanilla, shipped about 40% less code for the same outcome, and drove slop tells from 5 to 0 on a debug-spiral task where vanilla left five in.
The losses are real and we do not hide them, that is a house rule. On an already-simple todo app the kit was about twice as slow and roughly 75% pricier for near-identical output, and across the suite the hook context adds about a 25% average cost premium, paid for by your existing subscription. Hooks are not free. On vague prompts they pay for themselves in avoided rework, and on already-clear ones they are overhead.
Write your own, or start from one that works
The mechanism is simple: pick an event, write a script, add one entry. The hard part is the judgment, which matchers to listen on, when to warn instead of block, how to fail open so the hook never hangs a session. That is what kitstarter is, a tuned settings.json and the scripts behind it, generated for you and ready to drop in. Build your own with the table above, or install one that already earns its cost.
A settings.json that already works
kitstarter generates the hooks block and ships the scripts behind it, tuned so your agent asks first and stays lean, for Claude Code, Codex, and Antigravity.
