Skip to content

Hooks & Enforcement

HOOKS The enforcement layer · block native primitives, force fsuite usage
TypeClaude Code PreToolUse hooks
TargetsRead · Write · Edit · Grep · Glob
Effectreject + redirect message
Pairs withMCP adapter

By default, coding agents reach for their native tools — Read, Write, Edit, Grep, Glob, Bash. Those tools flood context, read entire files, and fail on whitespace drift. fsuite exists to fix that, but only if the agent actually uses fsuite.

If you install fsuite and don’t enforce its use, the agent will reach for Read first because that’s what its training reinforced. The MCP exposure tells it fread exists. The hooks layer is what makes fread the default.

Claude Code hooks intercept tool calls before they run. A PreToolUse hook can inspect the tool name, reject the call with a message, and the agent sees that message in its tool result. Reject Read with "Use fsuite fread instead" and the agent immediately tries fread — exactly what you want.

The hook does not need to know anything about fsuite. It just needs to refuse the native primitive with a useful redirect message.

Add to ~/.claude/settings.json:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "echo 'Use fsuite fread instead of native Read. Use fsuite structured reads with symbol/range control. Example: fread --symbol NAME path' >&2; exit 2"
}
]
}
]
}
}

Apply the same pattern to Write, Edit, Grep, and Glob:

Native toolfsuite redirectWhy
Readfread --symbol NAME pathreads exactly one symbol
Writefwrite path (MCP) or fedit --create path (CLI)atomic with safety nets
Editfedit --symbol NAME --replace x --with ysymbol-scoped, dry-run by default
Grepfcontent "pattern"token-capped, ranked
Globfsearch '*.py'fd-aware, suppresses noise dirs

Bash you leave alone — fbash exists, but bash itself is occasionally still the right choice. Don’t block it.

Hooks block native tools. They cannot route or translate calls. MCP exposes fsuite tools with schemas. An agent under both:

  1. Reaches for Read → hook blocks → agent sees the error message
  2. Agent tries again with fread from the fsuite MCP → succeeds

The two layers are complementary, not redundant. Hooks alone would block native tools without offering an alternative the agent can find. MCP alone would expose fsuite without preventing the agent from sliding back to its trained reflexes.

The example above uses exit 2 — that’s the protocol. Exit code 2 from a PreToolUse hook tells Claude Code “block this call, but show the message to the agent instead of the user.” Exit code 0 would silently allow it. Exit code 1 would fail with the message visible to the user.

Always use 2 for redirects. The agent reads the message, learns, and switches tools.

After adding the hooks, ask your agent in a fresh session:

Read /tmp/test.txt

You should see the agent immediately try fread /tmp/test.txt instead. If it still uses native Read, the hook isn’t loading — check ~/.claude/settings.json syntax and restart the agent.

The plan is to ship scripts/install-hooks.sh that adds the correct hook config to ~/.claude/settings.json idempotently — read existing config, merge in the five PreToolUse blocks, write back without clobbering anything else. Until then, hand-edit per the example above.

  • MCP adapter — exposes fsuite tools that the hook redirects to
  • First contact — verify your enforcement is live with a known prompt