If you use Claude Code regularly you have probably seen a lot of this:
This command requires approval. Do you want to proceed?
Most of it is avoidable. Claude Code has native file editing tools (Edit,
Write) that require zero Bash execution and zero approval prompts. But by
default, Claude tends to reach for Python scripts or sed out of habit —
even for simple file edits — which routes everything through Bash and
triggers the approval flow.
This CLAUDE.md fixes that by giving Claude explicit guidance on the right
tool for the job, and pre-approving a set of safe, well-scoped tools in
settings.json so the ones that do use Bash do not interrupt your flow.
| Approach | Goes through Bash? | Approval prompt? |
|---|---|---|
| Claude Code Edit tool | No | Never |
| Claude Code Write tool | No | Never |
sd, rg, jq, yq, yamllint (pre-approved) |
Yes | No |
sed, Python scripts |
Yes | Yes, every time |
The Edit tool works on the full file buffer — newlines are just characters,
multiline matches just work. sed and sd process line by line by default,
so multiline patterns silently fail and Claude falls back to Python. Telling
Claude to Read the file first, then Edit, eliminates most of that entirely.
Even with pre-approved tools, you can still get approval prompts if Claude wraps them in diagnostic shell expressions:
# This triggers an approval prompt — pattern match fails
fastmod 'old' 'new' src/ 2>&1
echo "exit: $?"
# This does not — matches the pre-approved pattern cleanly
fastmod 'old' 'new' src/The reason: Claude Code's permission matching is prefix-based. The moment a command becomes a compound shell expression, it no longer matches the allow pattern for that tool.
The good news: Claude Code's Bash tool already returns stdout, stderr, and
the exit code automatically as part of every tool result. Claude does not
need to echo $? or redirect stderr — it already has all of that. The
CLAUDE.md in this Gist tells Claude to run commands directly and trust
the tool result rather than adding defensive scaffolding.
brew install sd fastmod ripgrep jq yq yamllintmkdir -p ~/.claude
curl -o ~/.claude/CLAUDE.md \
https://gist.githubusercontent.com/rayjohnson/7b9cef2cb69b76d80eb72ad5103ca58f/raw/CLAUDE.mdOr copy the contents of CLAUDE.md from this Gist manually.
Add the following to ~/.claude/settings.json (create it if it does not exist):
{
"permissions": {
"allow": [
"Bash(sd *)",
"Bash(rg *)",
"Bash(jq *)",
"Bash(yq *)",
"Bash(yamllint *)",
"Bash(shellcheck *)",
"Bash(fastmod *)",
"Bash(which *)",
"Bash(brew install *)"
]
}
}If you already have a settings.json, just merge the allow entries in.
Start a new Claude Code session. Ask it to edit any file. It should reach for the Edit tool directly rather than writing a Python script or sed command.
- rg — read-only, cannot modify files
- jq — read-only unless explicitly redirected
- yamllint — read-only linter, cannot modify files
- shellcheck — read-only linter, cannot modify files
- sd — text replacement only, no execution mode, no
-eflag escape hatch - fastmod — purpose-built codemod tool, no shell execution capability
- which — read-only system lookup
- brew install — will still prompt interactively in your terminal
Compare to sed -e which can be chained with shell execution, or
python * which can run arbitrary code. Those stay unapproved for good reason.
The CLAUDE.md in this Gist is intentionally minimal — just the tool
preference and approval-avoidance guidance. Add your own conventions on
top: language preferences, commit message rules, project-specific patterns.
If you manage dotfiles with chezmoi, this fits naturally:
chezmoi add ~/.claude/CLAUDE.md after any changes.