What settings.json actually controls
The settings.json file is where you configure Claude Code at the project and user level. Two parts of it matter most in day-to-day work: the permission allowlist - the rules that say which tools and shell commands the agent may run without asking - and the hook bindings that point Claude at the hooks you want to fire on specific events. There are other keys (model, environment variables, MCP servers), but permissions and hooks are the ones that change how a session feels.
It helps to keep the lanes separate. Permissions decide whether an action is allowed to run unattended. Hooks decide what happens around an action - formatting a file after an edit, blocking a destructive command outright. The allowlist is about friction; hooks are about determinism.
The allowlist is what makes autonomous work possible
Every time the agent wants to run a command that is not on the allowlist, it stops and asks. That is the right default for safety, but if your allowlist is too narrow you spend the session clicking approve on pnpm test for the hundredth time. The point of a good allowlist is to pre-approve the commands you would always say yes to anyway, so the agent can run a real loop - edit, test, read output, fix - without a human in the path of every step.
Permissions are expressed as rules with three buckets: allow, ask, and deny. A rule like Bash(pnpm test:*) in allow lets the agent run any pnpm test invocation silently. A rule in deny is a hard wall the agent cannot cross, and deny wins over allow when both match. The skill is drawing the line between the two.
Broad but safe: how to draw the line
A useful allowlist is broad on the commands that are common and reversible, and silent only on those. Reads are almost always safe to allow: git status, git diff, git log, ls, cat. So are the everyday dev verbs in most projects - your package manager’s install, build, test, and lint commands, and the type-checker. These are the commands that make up an honest working loop, and pre-approving them is what lets the agent stay in flow.
What stays off the allowlist is anything genuinely destructive or hard to undo: rm -rf, git push --force, a production deploy, anything that rewrites history or touches a remote. You do not have to enumerate every danger by hand - put the everyday verbs in allow and let the truly irreversible commands fall through to ask, where you see them before they run. For the handful of commands that should never run unattended, an explicit deny rule is clearer than hoping nobody approves them by reflex.
- Allow - reads, install/build/test/lint, type-check, formatters, scoped git reads. The commands you would approve every time.
- Ask - the default for anything not listed, so novel or unusual commands still surface for a human glance.
- Deny - force-push, history rewrites, recursive deletes, production actions. A wall, not a speed bump.
Project settings vs. local settings
Claude Code reads more than one settings file, and knowing which is which keeps your team sane. .claude/settings.json is the project file: it is committed, it travels with the repo, and everyone who clones the project inherits the same allowlist and the same hook bindings. This is where the shared, reviewable baseline lives - the rules you want enforced for every contributor and every session.
.claude/settings.local.json is the local file: it is gitignored and never leaves your machine. It is the right home for personal preferences and anything machine-specific - a path that only exists on your laptop, an experimental permission you are still trying out. Keeping these out of the committed file means your personal tweaks never become everyone else’s problem, and it keeps the shared allowlist clean enough to actually review. This split mirrors the same instinct behind good CLAUDE.md hygiene: shared intent lives in version control, personal noise stays out of it.
The one-command version
Tuning a permission allowlist by hand is the kind of fiddly, easy-to-get-wrong work that claudesetup handles for you. It ships a project settings.json with a broad-but-safe allowlist already drawn - the common dev commands pre-approved so autonomous work flows, the genuinely destructive ones left out - plus the local-settings split set up correctly and hook bindings wired in. You start with permissions that have already been reasoned through, and every choice is explained so you can adjust them with confidence.