Customize Claude Code's Behavior with Undocumented Features
Job to be done: Customize Claude Code's behavior using undocumented features, e.g., adding --dry-run to git push
🇳🇬 Ways to use this in Nigeria
Ideas to get you started, adapt to your situation.
- 9-5 employee
Prevent accidental code pushes by adding --dry-run to git push commands executed by Claude Code.
- Student
Automate adding --dry-run to git push commands for class projects using Claude Code hooks.
What you’ll get
A way to bend Claude Code (the paid AI coding assistant) to your will using “hooks”, small scripts it runs at set moments. The worked example makes Claude Code add --dry-run to every git push it tries, so a push is only rehearsed, never actually sent, a safety net against accidental pushes. Once you see how one hook works, you can rewrite or gate other commands too. It is intermediate: you write a short shell script and edit a settings file.
Note: these features come from reading Claude Code’s source code, so they are “undocumented” and could change in a future version.
Tools you need
- Claude Code (paid): the AI coding assistant whose behavior you are customizing. Needs a paid plan or credits.
- jq (free): a small command-line tool for reading and writing JSON (a structured data format). The hook uses it to read and edit the command.
- git (free): used in the example, where the hook rewrites
git push.
Steps
-
Know where the config lives: Claude Code keeps personal settings in
~/.claude/(on Windows that path isC:\Users\YourName\.claude\), and per-project settings in a.claude/folder inside a project. You will work in the personal one. (The shell commands below are written for macOS or Linux; on Windows, run them in Git Bash or WSL, where~means your home folder.) -
Make a folder for hooks:
# macOS or Linux (Windows: use Git Bash or WSL) mkdir -p ~/.claude/hooks/You should have a new
hooksfolder inside~/.claude/. (-pjust means “create parent folders if missing”.) -
Write the hook script: create a file
~/.claude/hooks/dry-run-pushes.shwith this content. It reads the command Claude Code is about to run, and if it is agit push, hands back the same command with--dry-runadded:#!/bin/bash INPUT=$(jq -r '.tool_input.command' /dev/stdin) if echo "$INPUT" | grep -q 'git push'; then jq -n --arg cmd "$INPUT" '{ updatedInput: { command: ($cmd + " --dry-run") } }' else jq -n --arg cmd "$INPUT" '{ updatedInput: { command: $cmd } }' fi -
Make the script runnable: give it permission to execute:
# macOS or Linux (Windows: use Git Bash or WSL) chmod +x ~/.claude/hooks/dry-run-pushes.shNo output means it worked.
-
Tell Claude Code when to run it: open (or create)
~/.claude/settings.jsonand add this. APreToolUsehook runs before a tool is used; thematcher“Bash” means “before any Bash command”:{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "~/.claude/hooks/dry-run-pushes.sh" } ] } ] } }If
settings.jsonalready has content, merge thishookssection into it rather than overwriting. -
Test it: in Claude Code, ask it to run a push (for example
git push origin main). It should run with--dry-runadded, so nothing is actually pushed. That confirms the hook works.
Original source
Based on ankitg12’s Hacker News post and the article “I Read the Claude Code Source Code” on buildingbetter.tech, documenting configuration options for Claude Code found by reading its source.
Notes & variations
- Free-tier alternatives: this hook mechanism is specific to Claude Code. For plain command tweaks outside an AI assistant, shell aliases or functions in your
.bashrcor.zshrcdo something similar, but without the AI integration. - Common pitfall: forgetting
chmod +x. If Claude Code reports “permission denied” on the hook, the script is not executable. - Tip for better results: explore other hook types from the source, like
SessionStart(inject standing context) andPostToolUse(act on a tool’s output), and return fields likeadditionalContextorpermissionDecisionto auto-approve or block actions. Since these are undocumented, expect them to shift between versions.