Making Claude Code shut up (and other customizations that earned their keep)
The first thing I did with Claude Code was make it talk less. The small, shareable customizations that survived my own filter — terse output, a statusline, the right hooks — plus the ones I deliberately refuse to automate, and why managing the context window shouldn't be a skill at all.
The first thing I did with Claude Code was make it talk less.
Claude Code is Anthropic’s terminal coding assistant — you talk to it, and it reads, writes, and runs code in your project. Out of the box it’s polite. “Certainly! I’d be happy to help you with that. The issue you’re seeing is likely caused by…” I read fast, I pay per token, and I don’t need the warm-up. I switched that off. I run a community plugin, caveman, that strips responses to the bone: no articles, no pleasantries, every bit of technical substance intact. “Bug in auth middleware. Token expiry check uses < not <=. Fix:” — and then the fix.
It takes adjusting. Some of those compressed sentences make you stop and read twice; a fragment can carry more meaning than it first lets on, and you spend the saved second decoding it. But the trade lands well in my favor. I get to the point faster, the context window fills slower, and if a line is genuinely unclear there’s always the escape hatch: ask it to expand. It never minds.
That mode is the smallest possible example of the thing this whole post is about. Claude Code out of the box is generic. It’s built to be reasonable for everyone, which means it’s optimal for no one. The leverage isn’t the model itself — you can’t change how good it is, only which one you run. The leverage is in bending the tool around your own loop.
The trap
Customization isn’t free, and I want to be honest about the bill. You can pour weeks into a setup nobody else can read, including future you.
The tools that took configurability furthest are the ones everyone reveres: vim and git. Deep, powerful, endlessly tunable. And, I’d argue gently, not actually that accessible. The depth is both the point and the price. Vim is the only tool I know with a meme about how to quit it — that should tell us something.
I hear the objection most about git: “but git is so easy.” Is it, though? It’s easy right up until your first real merge conflict, or the afternoon you rebase the wrong thing and quietly rewrite your own history with no clean way back. Yes, you can see where you’re trying to get to. So can someone holding the box for a fifty-thousand-piece puzzle, the finished picture printed right there on the lid. Seeing the goal is not the same as the path being accessible. Those are different claims, and we keep mixing them up.
Here’s the belief, stated plainly instead of as a complaint: a great tool should be as accessible as it can possibly be, and then, having earned that, offer infinite depth to anyone who wants to dig. The accessible part is the whole job, not a consolation prize for beginners. Depth is the bonus you add on top.
My bar for any Claude Code customization is one question: would I hand this to a teammate without an hour of briefing? If the answer is no, it usually doesn’t survive. My stack stays simple and stupid on purpose. Here’s what’s left after that filter.
What earned a slot
The terse output mode. Covered above. The single highest-leverage change, because it touches every response. Less to read, fewer tokens burned, longer-lived context.
The statusline. This matters more than it sounds. I usually have several things going at once, and I lose track of where I am — which repo, which task, how much budget I’ve got left. My statusline shows it all in one strip: the model I’m on, how full the context window is, where I stand against my 5-hour usage limit (Claude Code caps how much you can run in any rolling five-hour window), and my current directory. No clicking, no guessing. I glance down and I know my situation. For anyone juggling more than one thing at a time, it’s the cheapest sanity I’ve bought.
The settings layer. This is where it gets personal, and where the options never end. My config plays a sound when Claude finishes so I can look away and get pinged back. It fires OS notifications. You can have it log the purpose of each session, track tokens and cost, whatever you decide is worth knowing. There’s no “right” setup — the point is that the hooks exist (little commands the tool runs automatically at set moments, like when it finishes) and you bend them to your purpose. Mine is “tell me when you’re done.” Yours might be something else entirely.
None of these is clever. That’s the point. Each one removes a small, daily friction, and none of them needs explaining.
The actual wiring
The sound is a Stop hook in settings.json:
"hooks": {
"Stop": [{ "hooks": [{
"type": "command",
"command": "afplay ~/.claude/sounds/done.mp3",
"async": true
}]}]
}
async: true so it never blocks the session, and afplay is just the built-in macOS player — swap in whatever you like.
The statusline is barely more than that. Claude hands your script a blob of JSON on every render; the script prints a line. You point at it once:
"statusLine": {
"type": "command",
"command": "bash ~/.claude/statusline.sh"
}
and the script reads whatever fields it cares about off stdin:
input=$(cat) # session state, as JSON
model=$(jq -r '.model.display_name' <<<"$input")
ctx=$( jq -r '.context_window.used_percentage // 0 | floor' <<<"$input")
fh=$( jq -r '.rate_limits.five_hour.used_percentage // 0 | floor' <<<"$input")
dir=$( jq -r '.workspace.current_dir' <<<"$input")
From there it’s just drawing strings. Mine renders a little colored bar for each percentage — green, then yellow, then red as it fills — and right-aligns the path, so the strip ends up looking like this:
Opus 4.8 xhigh · CTX ████░░░░░░ 38% · 5h ██░░░░░░░░ 21% ~/dev/golfnext
(xhigh there is just the reasoning-effort level I keep the model at.) That’s the whole thing. No framework, no dependency to keep current. A shell script and a JSON blob.
What I won’t automate
More automation isn’t always a win. Some of it just moves the cost somewhere you can’t see, and those are the ones I leave switched off.
You can wire Claude to lint and format every file the moment it edits it. Sounds tidy. It’s the opposite. If a hook reformats a file right after the model touched it, the model’s picture of that file is now stale — so the next time it works there, it has to re-read the whole thing to catch up. You’ve traded a clean diff for a slower, token-hungrier session. I’d rather it just run the linter once, at the very end, when the thinking is done.
Same logic on automatic model fallback. You can have it silently switch to a smaller model when your first choice is busy. I don’t want that. I pick a model deliberately, usually because the task needs the bigger reasoning model — the one that pauses to work a problem through before answering. If it’s overloaded, I’d rather come back in two hours and write “pick this up again” than have something quietly hand my hard problem to a model that can’t carry it. Convenience isn’t worth a worse answer I didn’t notice I was getting.
The window should be a non-event
People talk a lot about managing the context window — compacting it (having the tool summarize the conversation so far to free up room), watching it, nursing it along. I try to never be in that conversation.
The way I avoid it is structural: push the heavy lifting onto subagents — separate helper instances of Claude Code, each with its own private context — and keep the main thread clean. A subagent goes off, burns its own context reading and searching, and hands back the short answer. The main session stays light. Done right, I almost never reach for compaction, because I never let the window get full in the first place.
But here’s the honest part: I think the fact that any of us has to know this is a smell. Nobody should need to understand context windows to start being productive with this tool. It’s a current limitation wearing the costume of a skill. A few years from now I’d bet it’s abstracted away entirely, and we’ll talk about it the way we talk about managing our own RAM — which is to say, we don’t. I optimize around it today, and I try not to mistake working around a limitation for something worth teaching.
Can I prove it? Not with numbers. I don’t have a clean before-and-after, and I distrust anyone who quotes a tidy percentage for this. What I have is a feel: the window fills slower, the budget stretches further, sessions stay light longer. It’s subjective, and I’d notice the moment it was gone.
How to find your own
The hard part isn’t configuring anything. It’s knowing what’s even possible.
Claude Code moves fast — it feels like ten new commands a week. So fast that the model itself can’t keep up. I’ll ask it about a real slash command — “what does /goal do, can you explain?” — and it has no idea the command exists. There’s something funny about that: the most capable thing I work with often can’t tell you about features that shipped after its training cutoff. It’s behind on itself, in the most literal way. You can’t just ask the model what it can do. You have to go look — read the changelog, skim a guide, point it at the open web where the fresh stuff actually lives.
The next thing I want to try isn’t a setting at all. I keep half a dozen projects in flight, so I’m going to build one small meta-repo: a single place that points at all of them, that anyone — not just me — can navigate. Same instinct as everything else here: if it only works for me, it doesn’t really work.
That’s the whole filter, in the end. Take the two or three things that kill a real, repeated friction. Leave the rest, however clever it looks. The goal was never the most elaborate setup. It was the one you’d still be happy to hand to someone else.
