Skip to content

benjibromberg/claude-code-customizations

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

claude-code-customizations

Hooks and rules I've built for my own Claude Code setup. Sharing them in case any are useful to you.

├── hooks/
│   └── check-command-antipatterns.sh   # PreToolUse Bash hook that blocks diagnostic-output anti-patterns
└── rules/
    ├── claude-md-hygiene.md            # What belongs in CLAUDE.md vs. issue tracker / MEMORY.md
    ├── command-output.md               # Don't truncate diagnostic output (the rule enforced by the hook above)
    ├── conventional-commits.md         # Conventional Commits + Angular flavor
    ├── gh-cli-github-fallback.md       # Use `gh` CLI when WebFetch 404s on github.com
    ├── gh-watch.md                     # Prefer `gh run watch` / `gh pr checks --watch` over polling
    ├── rate-limit-external-apis.md     # Default to 5–10s between requests to research APIs
    ├── secrets.md                      # Never put secret values in transcript-visible output
    ├── testing.md                      # Tests are a gate, not optional
    └── verify-citations.md             # Read the actual source before adding citations

check-command-antipatterns.sh — the centerpiece

A PreToolUse hook for the Bash tool. It catches four diagnostic-output anti-patterns that are the single most common way Claude Code sessions truncate the answer they were about to find:

Anti-pattern Carve-out (allowed)
cmd | head ... / cmd | tail ... tail -f, tail -F, tail --follow (live log following)
cmd | grep ... / ... | rg ... / ... | egrep ... / ... | ag ... none — even cat log | grep STR hides which subsystem the match came from. Direct grep file.log (no pipe) is fine — that's explicit search intent against a static target.
cmd 2>/dev/null Optional-tool probes: command -v X 2>/dev/null, which X 2>/dev/null, X --version 2>/dev/null
cmd || true / cmd || : Cleanup contexts: rm -f /tmp/..., kill %1, pkill X, teardown hooks

When a command matches, the hook returns permissionDecision: "deny" and an additionalContext string instructing Claude to surface three options to the user via AskUserQuestion: run as-is, modify the command, or cancel.

The override mechanism

"Run as-is" is intentionally narrow: Claude writes a single-use token at /tmp/claude-cmd-override-<sha16-of-cmd> and immediately re-runs the same command. The hook detects the token (mtime within 60s), removes it, and passes the command through. Because the token's filename is a SHA of the exact command (whitespace-sensitive) and TTLs out after 60 seconds, it cannot silently authorize subsequent similar commands.

Architecture

┌──────────────────┐    ┌──────────────┐    ┌─────────────────┐    ┌──────────────┐
│ override token?  │ →  │  tripwire    │ →  │ LLM disambig.   │ →  │ JSON deny    │
│ (single-use TTL) │    │  (substring) │    │ (claude -p)     │    │ + ctx string │
└──────────────────┘    └──────────────┘    └─────────────────┘    └──────────────┘
                              ↓ no match           ↓ "OK"
                              pass through         pass through

Every error path fails open. A broken hook must never brick bash — if claude isn't on PATH, if jq errors, if the LLM times out, the command is allowed through. The cost of a missed catch is much lower than the cost of unrunnable bash.

The tripwire (a single grep -qE against the command string) eliminates ~99% of commands in microseconds without invoking the LLM, so the hook is free in the common case.


Install

1. Drop the files in

# From a clone of this repo
cp hooks/check-command-antipatterns.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/check-command-antipatterns.sh
mkdir -p ~/.claude/rules
cp rules/*.md ~/.claude/rules/

(Or symlink, or add this repo as a submodule of your own ~/.claude/ — whatever your dotfiles setup prefers.)

2. Wire the hook into ~/.claude/settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$HOME/.claude/hooks/check-command-antipatterns.sh"
          }
        ]
      }
    ]
  }
}

3. Reference the rule from ~/.claude/CLAUDE.md

The hook's deny message points Claude back to ~/.claude/rules/command-output.md for context, so make sure that file is installed and (ideally) imported by your CLAUDE.md so Claude internalizes the rule before the hook ever has to fire.


Dependencies

The hook shells out to:

  • bash (4+)
  • jq — parsing the tool_input JSON from the hook event
  • claude (Claude Code CLI) — for the LLM disambiguation step. Fails open if unavailable, so the hook still degrades gracefully without it.
  • shasum — hashing the command for the override token. Pre-installed on macOS; on Linux substitute sha256sum if needed.
  • timeout (GNU coreutils) — bounds the LLM call to 12s. On macOS, install via brew install coreutils (already present if you use Homebrew).

License

MIT. See LICENSE.

About

Hooks and rules I use with Claude Code, including a PreToolUse Bash hook that catches diagnostic-output anti-patterns (pipe-to-head/tail/grep, 2>/dev/null, || true) via a tripwire + LLM disambiguator + single-use override token.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages