by assafkip
Three hooks that fix Claude Code's attention drift. Cited research, one-line install.
The deep catalog scan for this skill is still queued. Run an instant dependency check now instead.
# Add to your Claude Code skills
git clone https://github.com/assafkip/claude-focusNo comments yet. Be the first to share your thoughts!
Unlocks once the catalog security scan passes (runs nightly).
Three hooks that stop Claude Code from drifting, lying about "done," and burning tokens. ~550 lines of Python. Zero dependencies. MIT. Drop them into ~/.claude/hooks/ and wire three lines of settings.
git clone https://github.com/assafkip/claude-focus.git ~/.claude/hooks/claude-focus
cp ~/.claude/hooks/claude-focus/examples/settings.example.json ~/.claude/settings.json
Open Claude Code. The hooks are live. If you already have a ~/.claude/settings.json, merge the hooks block instead of overwriting.
The three hooks are token-guard.py (circuit breaker), verification-gate.py (catches false "done" reports), and echo-of-prompt.py (re-injects the original task so the model stops drifting). They run as PreToolUse / UserPromptSubmit / Stop hooks. No LLM call sits in the hot path, so they are fast and deterministic.
Version 1.1 - updated 2026-05-28
Add a PreToolUse hook that counts tool calls and blocks the turn when it sees a runaway pattern. token-guard.py is that hook. It catches the model retrying a failed call three times, making 50 tool calls with no user input, spawning subagents that produce nothing, or hammering an MCP server. When a pattern trips, it blocks the turn (exit code 2) and tells the model what to do instead.
| Trigger | What it catches | Action |
|---|---|---|
| Same tool + same input, 3x | Retry loops without diagnosis | Block |
| 50 tool calls since last user message | Runaway execution | Block |
| 25 subagents since last user message | Agent-spawn storm | Block |
| 30 MCP calls in 60s | API hammering | Block |
| 3 edit attempts on same file | Wrong-approach edit spiral | Block |
Attempted edit to .env, .pem, .key |
Accidental secret exposure | Block |
| 15 consecutive reads, no write | Grep drift | Warn |
| Same file read 3x | Re-read loop | Warn |
| 5 greps since last write | Searching instead of working | Warn |
| 3 agents with no output between them | Agents not producing | Warn |
| 2 min + 10 calls since last write | Time-based stall | Warn |
Every threshold is an environment variable (CLAUDE_FOCUS_VOLUME_CEILING, CLAUDE_FOCUS_RETRY_LIMIT, and so on), so you can tune it without touching the code.
The drift has research names. "Lost in the Middle" (Liu et al., 2023) describes how models lose the middle of a long context. Multi-turn drift (Laban et al., 2025) describes how they degrade over a long back-and-forth. The fix is not a better prompt. It is a hook that does not depend on the model behaving.
echo-of-prompt.py fights this directly. Write the task into .claude/task-context.md, and every 15 tool calls (configurable via CLAUDE_FOCUS_ECHO_INTERVAL) the hook re-injects it as additionalContext:
[echo-of-prompt - re-anchoring task context at call 30]
...original task content...
[Re-read this. Verify the current tool call still serves the original task.]
Attention drift stops compounding because the original requirements keep coming back into the model's working context.
Claude self-reports "done" without checking. verification-gate.py does not let it. Drop a JSON contract in .claude/contracts/:
{
"name": "daily-report",
"required_file": "output/report-{date}.json",
"required_keys": ["summary", "action_items", "sources"],
"min_size_bytes": 200
}
On every Stop event (when Claude tries to end its turn), the gate checks every active contract. File missing, keys missing, or empty values? It blocks the turn with a diagnostic:
VERIFICATION FAILED. You reported the work is done. It isn't.
- [daily-report] output/report-2026-05-28.json missing required keys: ['action_items']
Do NOT claim completion until every contract passes.
Self-reports become falsifiable. The turn cannot end until the file on disk actually matches the contract.
A hook is a command Claude Code runs at a lifecycle event: before a tool call (PreToolUse), when you submit a prompt (UserPromptSubmit), when the turn ends (Stop), and others. The hook reads a JSON payload on stdin and signals back with its exit code. Exit 0 allows the action. Exit 2 blocks it and sends stderr back to the model as feedback. All three hooks in this repo follow that contract, which is why they work without any LLM in the loop.
The big free frameworks are kitchen sinks: hundreds of skills, a whole operating system to install when you wanted a seatbelt. The bare guardrail snippets floating around GitHub tend to have no tests, no tuning profiles, and no install path. claude-focus is attention control done well: three small hooks that drop into any setup.
| claude-focus (free) | Kitchen-sink framework | Token Guard Kit (paid) | |
|---|---|---|---|
| Runaway circuit breaker | Yes | Buried in a large system | Yes, hardened |
| False-done verification gate | Yes | No | Yes, deadlock-proof |
| Task re-anchoring | Yes | No | Yes |
| Install footprint | Three files | The whole framework | Two minutes, any repo |
| Tests | No | Varies | 35 pytest unit tests |
| Tuning profiles | No | No | safe / aggressive / paranoid |
| Per-OS setup guides | No | Varies | macOS, Linux, Windows |
In any Claude Code session, ask it to do something that forces a retry loop, like "read a file that doesn't exist, then retry 3 times." You should see a block message from token-guard.py instead of a fourth attempt. If you don't, the hook isn't picking up. Check your ~/.claude/settings.json and confirm the command paths resolve to the cloned files.
Yes. Every threshold is an environment variable. Set them in your shell before launching Claude Code:
export CLAUDE_FOCUS_VOLUME_CEILING=30 # hard stop at 30 tool calls instead of 50
export CLAUDE_FOCUS_RETRY_LIMIT=2 # block after 2 identical calls instead of 3
export CLAUDE_FOCUS_ECHO_INTERVAL=10 # re-inject task every 10 calls
export CLAUDE_FOCUS_CONTRACTS_DIR=.claude/contracts
export CLAUDE_FOCUS_CONTEXT_FILE=.claude/task-context.md
The defaults are sane for everyday coding. Full list lives in each hook's source.
claude-focus is attention control. It catches deterministic patterns. It does not read intent and it will not fix a vague prompt. It stops the spinning, not the thinking.
MIT. See LICENSE.
I built these hooks for my own Claude Code work and open-sourced them. This repo is the free core.
The full Token Guard Kit adds a 35-test pytest suite shipped green, three tuning profiles (safe / aggressive / paranoid), a deadlock-proof Stop gate, an instruction-budget preflight CLI, advanced pipeline reliability modules, and a two-minute installer with per-OS guides: https://claudedaddy.gumroad.com/l/yybwrk
More kits for founders building on Claude Code: https://claudedaddy.io
Want one wired to your own setup, or a larger Claude Code reliability system built around it? I build these for teams. Book a call: https://calendar.app.google/cMFvhvDsfi9iyWYy9