Skip to content
ContractorKeith
Go back

how i set up Claude Code so it actually works the way i need it to

Updated:
10 min read

updated september 8, 2026: corrected instruction imports, MCP scopes and commands, permission precedence, subagent fields, and hook behavior against the linked Claude Code docs. the setup examples below are illustrative, not a copy of my current permissions.

i spent three weeks using Claude Code before i realized i was doing it wrong.

not wrong like “it doesn’t work.” wrong like “i was typing the same instructions every single session, approving the same permissions over and over, and watching it forget my preferences the moment i closed the terminal.” i was treating Claude Code like a chatbot instead of a tool i could actually configure.

once i figured out the config system, everything changed. so here’s what i wish someone had told me on day one.

two folders, one system

Claude Code’s configuration lives in two places. understanding the split is the single most important thing to get right before you customize anything.

global (~/.claude/) is your personal defaults. these follow you everywhere. no matter what project you open, your global config comes with you.

project (.claude/ inside any repo) is specific to that particular project. shared settings travel with the repository; personal and managed settings can also apply.

there’s also a local override layer (.claude/settings.local.json) that lets you make personal tweaks within a project without affecting anyone else. i use this constantly for testing new permissions before committing them to the team config.

what goes where

this is the part that confused me for weeks. here’s how i think about it now.

your global ~/.claude/ folder is where you put things about you. your coding style. your preferred tools. your communication preferences. the stuff that doesn’t change from project to project.

your project .claude/ folder is where you put things about the project. the architecture. the build commands. the rules that anyone working on this codebase needs to follow.

simple once you see it. But i didn’t see it for a while.

the files that matter

CLAUDE.md (the memory file)

this is the simplest and most useful file in the whole system. it’s plain markdown that Claude reads at the start of every session. think of it as the brief you’d hand someone on their first day.

global (~/.claude/CLAUDE.md) applies to all projects:

## who i am

construction professional turned builder-coder.
i prefer direct, practical language over jargon.
i work in Python, JavaScript, and Astro.

## how i work

- be concise. skip disclaimers.
- show me working code, not abstract patterns.
- if something is risky, say so directly.

## code standards

- clean, readable code over clever code
- meaningful commit messages
- handle errors explicitly

project (CLAUDE.md at repo root) is specific to one codebase:

## what this is

astro site deployed to Cloudflare Pages.

## architecture

- blog posts in src/data/blog/ as markdown
- components in src/components/
- tests alongside source files

## rules

- all lowercase in titles and headings
- pnpm dev to start; pnpm build to check and build

instruction imports use @path/to/file, such as @docs/architecture.md, on a plain line outside a code block. see Claude Code memory docs.

settings.json (the control panel)

this is where you configure permissions, environment variables, and hooks. the structure is identical at every scope. what changes is the file location.

here’s a practical global config:

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git *)",
      "Bash(python3 *)",
      "Read",
      "Edit",
      "Glob",
      "Grep"
    ],
    "deny": ["Read(./.env)", "Read(./.env.*)", "Bash(rm -rf *)", "Bash(sudo *)"]
  },
  "env": {
    "DISABLE_TELEMETRY": "1"
  }
}

the allow list pre-approves commands so you don’t get prompted every time. the deny list blocks things outright. this example denies rm -rf and sudo; choose rules for your own workflow. it is not a copy of my current setup.

permission rules use pattern syntax with wildcards. Bash(npm run *) means “allow any npm run command.” Read(./.env) blocks matching file-tool access. it does not sandbox arbitrary subprocesses; a Python script can still read a file unless an OS-level boundary prevents it. see the permission docs. takes five minutes to set up, saves you hundreds of permission prompts.

the precedence hierarchy

when settings conflict, here’s who wins (highest to lowest priority):

  1. managed settings
  2. command-line arguments
  3. local project settings (.claude/settings.local.json)
  4. shared project settings (.claude/settings.json)
  5. user settings (~/.claude/settings.json)

that is the ordinary settings order. permission lists merge, and matching deny rules take precedence over allow rules across scopes. a local allow does not cancel a project deny. see settings precedence and permission evaluation.

i use this for experimentation. when i’m trying new permissions or hooks, i put them in settings.local.json first. Claude excludes the local file when it creates it. if you create it by hand, add it to .gitignore; see local settings.

commands, agents, and skills

these three are the extension points that turn Claude Code from a chatbot into a real tool. they’re different from each other in ways that matter.

commands (saved prompts)

commands are markdown files that become slash commands. the filename becomes the command name. they’re the simplest extension.

~/.claude/commands/review.md:

review the code i just changed. focus on:

1. bugs or logic errors
2. missing error handling
3. anything that would bite me in production
   skip style nitpicks unless something is genuinely confusing.

now i type /review and get a focused code review every time. no retyping the instructions. $ARGUMENTS works as a placeholder too, so /estimate the auth flow passes “the auth flow” into the prompt.

agents (isolated workers)

agents are like commands but they run in their own context window. this is the difference that matters: they don’t pollute your main session’s context. use agents for tasks that require deep analysis or that you want to run in parallel.

~/.claude/agents/research-agent.md:

---
name: research-agent
description: research a topic and return a structured summary
model: sonnet
tools: WebSearch, WebFetch, Read
---

you are a research assistant. when given a topic:

1. search for current, authoritative sources
2. cross-reference at least 3 sources
3. return a structured summary with key findings and source URLs
4. flag anything uncertain

the frontmatter selects the model and tools; the description helps Claude choose when to delegate. subagents use tools, while skills use allowed-tools. see the subagent reference.

skills (the power feature)

skills are the most capable extension point. unlike commands (just prompts) and agents (isolated workers), skills are instruction packages that can bundle scripts, templates, and reference materials. Claude loads them on-demand when they’re relevant.

the difference: skills are folders with a SKILL.md file inside. not a single file. this trips everyone up.

~/.claude/skills/proposal-writer/
├── SKILL.md          # the skill instructions
├── scripts/
│   └── calc.js       # bundled calculator
├── templates/
│   └── proposal.md   # document template
└── references/
    └── checklist.md  # scope checklist

the SKILL.md frontmatter tells Claude when to trigger the skill automatically, what tools it can use, and whether it runs in-conversation or as a subagent. skills can also include executable scripts in any language.

i have skills for writing blog posts, generating project plans, and building MVPs. each one bundles everything Claude needs to do the job without me re-explaining the context every session.

hooks (the enforcement layer)

this is the part most people skip. And it’s the part that matters most.

CLAUDE.md instructions are suggestions. Claude follows them most of the time, but it can forget or drift. configured hooks run at matching lifecycle events. they can enforce checks, but matchers, failures, and timeouts affect what happens. test the hook itself before relying on it.

hooks are configured in settings.json under the hooks key:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/block-destructive.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/format-on-save.sh"
          }
        ]
      }
    ]
  }
}

PreToolUse runs before Claude uses a tool. your hook can block the action entirely by returning exit code 2. PostToolUse runs after, which is good for auto-formatting or linting. there’s also SessionStart (when a session begins) and Stop (when Claude finishes responding).

command hooks read JSON from stdin. exit 0 means success and can carry structured decisions. exit 2 blocks a PreToolUse call, but PostToolUse cannot undo a tool that already ran. behavior depends on the event; see the hooks reference.

one note: most hook scripts use jq to parse JSON input. install it with brew install jq on macOS if you don’t have it.

MCP servers (external tools)

MCP servers connect Claude to tools and data. user-scoped servers live in ~/.claude.json; shared project servers live in the repository’s .mcp.json. local-scope servers also live in ~/.claude.json, scoped to that project. ~/.mcp.json is not the user-wide configuration file.

use the CLI to register servers. this illustrates a local stdio server; replace the absolute path with your installed executable:

claude mcp add --transport stdio --scope user my-tool -- /absolute/path/to/server
claude mcp list
claude mcp get my-tool

choose --scope project to share a registration in .mcp.json, or --scope local for private project-specific setup. use /mcp inside Claude to inspect connections. follow the MCP documentation for your server’s transport and authentication.

start with a server you need for the task. every extra integration is another configuration to understand when something stops working.

setting it up from scratch

here’s the order i’d follow if i were starting over today.

step 1: create the global structure.

mkdir -p ~/.claude/{commands,agents,skills,hooks}

step 2: write your global CLAUDE.md. who you are, how you work, what you expect. three to five sections, plain markdown. this is the single highest-value file in the whole system.

step 3: create your global settings.json with sensible permissions. allow the tools you use every day. deny the dangerous ones. takes five minutes.

step 4: add 2-3 global commands for things you do repeatedly. /review and /ship are good starting points.

step 5: add MCP servers only when you have a concrete need. memory is a good first one.

step 6: when you start a new project, run claude in the directory and use /init to generate a starter CLAUDE.md. then build out from there with project-specific commands, agents, and skills.

that’s it. don’t try to build out the entire hierarchy on day one. I built mine up over about two weeks, adding things as i noticed myself repeating work.

tips i learned the hard way

/status inside a Claude Code session shows you which settings files are active and where they’re loaded from. use this when something isn’t working and you can’t figure out why.

skills are folders, not files. ~/.claude/skills/estimate/SKILL.md, not ~/.claude/skills/estimate.md. i got this wrong three times before it stuck.

commands are being folded into skills. Anthropic has been consolidating the two. if you’re starting fresh, you can skip commands entirely and use skills for everything. But commands still work and are simpler for basic saved prompts.

CLAUDE.md imports use @path/to/file. keep the instructions short and put detailed context in the files that need it.

the MCP scope confusion is real. user-wide registrations are in ~/.claude.json; shared project registrations are in .mcp.json. check /mcp when a tool is missing.

the real point

none of this config stuff is exciting. But it’s the difference between using Claude Code as a novelty and using it as a daily tool. i went from re-explaining my preferences every session to having Claude know exactly how i work from the first message.

if you’re a solo builder, a small business owner, or someone who just wants to get things done without fighting the tool, spend an hour on this setup. you’ll get that hour back in your first week.


Share this post on:

Previous Post
i built a subcontractor game for bid day
Next Post
ködade is open source now