Claude Code: Lint Every Edit Without Leaving the Terminal
Claude Code linting: reminders or an automatic hook?
My least favorite coding-agent loop is discovering six edits later that the first edit introduced an unused import. The fix is usually tiny. Reconstructing which change caused it is not. Running the linter immediately after each edit keeps that feedback close to the decision that created the problem.
There are two straightforward approaches: tell Claude Code to run ESLint after editing, or configure a PostToolUse hook that runs it automatically. Both can catch the same errors. The difference is whether linting remains an instruction the agent must remember or becomes part of the tool lifecycle.
Prompt-only linting beside PostToolUse enforcement
- Prompt-only approach: Put “run ESLint after every edit” in your project instructions, with the exact command developers should use.
- Hook approach: Match successful Edit and Write tool calls, then execute a local script that checks the changed file.
- Prompt-only feedback: The agent chooses when to invoke the command, so checks can drift into occasional batches.
- Hook feedback: Claude Code invokes the configured command after matching tool calls, without another reminder from you.
- Prompt-only maintenance: Instructions are easy to change, but they do not provide deterministic execution.
- Hook maintenance: A small script needs ownership, dependency installation, and explicit decisions about which files qualify.
I use instructions for judgment calls and hooks for mechanical checks. “Consider whether this abstraction is necessary” belongs in prose. “Run the repository’s linter on this TypeScript file” is a better fit for automation.
A PostToolUse hook for ESLint on edited files
For a JavaScript or TypeScript repository with ESLint already configured and installed locally, add this entry to .claude/settings.json. Merge it with existing settings rather than replacing them. The example requires Bash and jq.
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/lint-edit.sh\"" } ] } ] } }
Create .claude/hooks/lint-edit.sh with the following script. It is written on one line here so it can be pasted directly; Bash does not require a shebang when invoked explicitly.
file=$(jq -r '.tool_input.file_path // empty') || exit 2; case "$file" in *.js|*.jsx|*.ts|*.tsx) ;; *) exit 0 ;; esac; cd "$CLAUDE_PROJECT_DIR" || exit 2; output=$(./node_modules/.bin/eslint -- "$file" 2>&1); status=$?; if [ "$status" -ne 0 ]; then printf '%s\n' "$output" >&2; exit 2; fi
The hook reads the tool’s JSON input from standard input, extracts the file path, and skips unrelated extensions. Quoting preserves paths containing spaces. Calling the installed binary avoids downloading a missing dependency during an editing session.
Exit code 2 sends the error output back to Claude Code as hook feedback. Because this is PostToolUse, the edit has already happened: the hook reports a problem; it does not undo or prevent the change. Restart the session after configuration changes if necessary, then introduce an unused variable to verify the complete loop.
Per-file ESLint checks beside whole-repository checks
Per-file linting wins on feedback speed. Whole-repository linting wins on coverage. Running the entire repository after every small edit can turn a useful guardrail into terminal noise, especially when unrelated files already fail.
Keep this hook read-only: do not add automatic fixes initially. Silent rewrites make it harder to understand what the agent changed. Configure generated-file exclusions in ESLint, and retain full linting, type checking, and tests before merging.
The matcher also defines a real boundary: edits made through shell commands are not covered by Edit|Write. This is reliable automation for matching tools, not proof that every filesystem change was checked.
Fluxo routing stays separate from local lint enforcement
In a Fluxo-backed workflow, model routing and local hooks solve different problems. Fluxo provides one key and one base URL, https://api.fluxorelay.com, across providers; Claude Sonnet 5.5 and GPT-6.1 Sol are two concrete options. It works with Claude Code, Codex, Gemini CLI, and OpenAI SDK clients. Billing follows real tokens, including cache reads and writes, with no monthly fee; invited users’ payments can also earn you a referral share. If that fits your terminal workflow, you can create a Fluxo account.
The verdict: use PostToolUse for immediate lint feedback
Choose the hook for repeatable, per-edit ESLint feedback. Keep project instructions for explaining expectations, and keep repository-wide checks as the final gate. The result is less remembering, faster corrections, and fewer lint surprises after the agent finishes.