Launch price: $49 one-time for all 55 specialists. Get it →
Blog

Easiest Way to Generate settings.json for Claude Code Hooks

Updated November 2, 2026 · 12 min read

Short answer: The easiest way to generate a valid settings.json for Claude Code hooks that run a linter after every edit is to use a form-based hooks builder, then review the output and save it as .claude/settings.json. This works best if you do not want to memorize event names and matcher syntax; if you already know the schema, hand-writing a PostToolUse hook with an Edit|Write matcher is just as fast.

You want ESLint or Ruff to run every time Claude Code edits a file, so lint errors show up before they reach a pull request. You open the hooks docs and find nested arrays, event names, matcher patterns and a command field. One misplaced bracket or a wrong event name, and the hook silently does nothing.

This article compares three ways to get a working file: a form-based builder, asking Claude to write it, and hand-writing it from the docs. For each, you get the trade-offs for a small team, plus a checklist to confirm the hook is valid and actually fires after an edit.

What users report

Community threads suggest there is no single generator for this job. Users mostly describe writing the hook config by hand and then tuning it, though some share tools or examples to make it easier.

What works

  • One Reddit post describes hooks as "if then" rules made of an event, a matcher and a command, written in plain JSON. Its examples include running a formatter after every write and running tests after file edits.
  • Another poster lists running your linter after every file edit as a practical use for hooks.
  • Users who wanted an easier way to manage several hooks shared A tool to manage Claude Code Hooks.
  • Set up these 4 Claude Code hooks gives copyable patterns for the event, matcher and command structure.

What goes wrong

  • Users ask whether several PostToolUse hooks run in a fixed order. They do not: Claude Code's documentation says all matching hooks run in parallel.
  • One user who blocks Claude from editing its own skills and hooks asks whether the model can still work around those guards, in Any Way for Claude Code to Circumvent.

Tips users share

  • Scope hooks to specific files with matchers instead of running a command on everything, as a monorepo thread describes.
  • Use a PreToolUse hook when you need to stop a tool call before it runs. PostToolUse fires after the edit, so it can report problems but not undo them.
  • Don't rely on the order of several hooks in one array, because they run in parallel.

What's the easiest way to generate a valid settings.json for Claude Code linter hooks?

For a first valid file, a form-based hooks builder is usually the easiest route, because it removes the need to remember event names, matcher syntax and the JSON structure. Once that file exists, hand-edit it for rules specific to your team. The right choice depends on how well you already know the hooks schema.

Claude Code hooks are configured in a settings file as events (such as PostToolUse) with a matcher (such as Edit or Write) and a command to run. A linter hook is a small config, but a misplaced bracket, a misspelled event name or a wrong matcher can leave it silently doing nothing.

The three options compared

The three options compared
CriteriaForm-based hooks builderAsking Claude to write itHand-writing from the official docs
Setup timeShort: fill in fields, copy outputShort: one prompt, then reviewLongest: read docs, write, test
Schema validity riskLow, since structure is generated from fieldsMedium: output can look plausible but use wrong keys or nestingDepends on care; typos are the main risk
Needs knowledge of event names and matchersLittle; options are presented to youSome; you need enough to spot mistakesYes, fully
Best forGetting a first working file quicklyDevelopers who want to iterate in conversation and can verify the resultPeople who want full control and already know the schema
Not forHighly custom setups beyond the form's fieldsAnyone who can't check the output against the docsAnyone in a hurry or new to hooks

Who each option fits

  • Builder: developers new to hooks, or anyone who wants a valid file without reading the schema first. It is a weaker fit when you need unusual logic.
  • Asking Claude: people comfortable reviewing JSON who want to tweak the result through follow-up prompts. Treat the output as a draft and check it against the documentation.
  • Hand-writing: those who know the event and matcher model, or who need to understand every line they maintain.

Recommendation

Start with a builder to get a valid baseline that runs your linter after edits. Then edit the file by hand to add team-specific rules, such as limiting the matcher to certain tools or changing the command. Validating the JSON and making a test edit confirms the hook actually fires.

What does a valid settings.json for a lint-after-edit hook look like?

A valid file has a top-level hooks key, a PostToolUse event inside it, a matcher that names the editing tools, and a hook entry of type command. The file normally lives at .claude/settings.json in the project.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx eslint --fix",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

How the pieces fit

  • hooks: the top-level key that holds all hook definitions.
  • PostToolUse: the event that fires after a tool call finishes. It maps to an array of matcher groups.
  • matcher: a pattern for tool names. Edit|Write covers edits to existing files and new files.
  • type: "command": tells Claude Code to run a shell command.
  • command: the string that gets executed. This one reads the edited file's path from the hook input with jq and lints only that file.
  • timeout: optional, in seconds, so a stuck linter cannot hang the session.

Note the nesting: each matcher group contains its own hooks array. A common mistake is putting the command object directly under PostToolUse.

Example linter commands

  • JavaScript or TypeScript: npx eslint --fix followed by the file path
  • Python: ruff check --fix followed by the file path

The hook receives its input as JSON on stdin. That JSON describes the tool call, including the edited file path in tool_input.file_path. Linting only that file keeps each edit fast; a whole-project command such as npx eslint . also works but slows down on large repos. The example needs jq installed.

Why PostToolUse and not PreToolUse

PreToolUse runs before the edit happens, so the linter would check the file as it was, not as it will be. Linting at that point finds nothing about the change that was just proposed.

PostToolUse runs after the file is written, so the linter sees the new contents. Any --fix changes are applied to the edited file, and linter output can surface problems right after the change that caused them.

Which Claude Code hook event and exit code should a linter use?

Use a PostToolUse hook matched to the file-editing tools, and have the linter exit with code 2 when it finds problems. PostToolUse fires after a tool call succeeds, so the file is already written when the linter runs. Exit code 2 sends stderr back to Claude, which can then fix the issues.

Exit codes

Exit codes
Exit codeBehavior
0Success. Claude does not see the output.
2Stderr is sent to Claude so it can fix the problem. On PostToolUse this cannot block anything, because the edit already happened.
Other non-zeroNon-blocking error. You see a hook error notice, Claude does not get the output, and the session continues.

Many linters exit with 1 on violations. That shows a notice but does not give the output to Claude, so a small wrapper script that converts failures to exit 2 is often needed:

#!/bin/bash
file=$(jq -r '.tool_input.file_path')
npx eslint --fix "$file" >&2 || exit 2

Save it as .claude/hooks/lint.sh, make it executable, and set the hook's command to that path.

Hook types

Claude Code supports several hook types, including command, http, prompt and agent. A linter should use command: it runs a shell command, is deterministic, and its exit code and stderr map directly to the behavior above. Prompt and agent hooks rely on a model's judgment, which suits fuzzy checks but adds latency and variability to a task a linter already does.

Blocking with PreToolUse

PostToolUse cannot undo an edit. To stop one before it happens, use PreToolUse. A command hook can read the tool input from stdin, check the file path or command, and exit 2 to block the call and send the reason to Claude.

#!/bin/bash
input=$(cat)
if echo "$input" | grep -qE '\.env|package-lock\.json|rm -rf'; then
  echo "Blocked: protected file or destructive command." >&2
  exit 2
fi
exit 0

Related events

  • Stop runs when Claude finishes responding. It suits a final, whole-project check, not per-edit linting.
  • Notification runs when Claude Code sends a notification, such as when it needs permission or is waiting for input. It suits desktop alerts or sounds, not lint feedback.

How do you configure hooks in Claude Code step by step?

Create a .claude/settings.json file in your project, add a hooks block with a PostToolUse entry, then confirm it with the /hooks command and trigger an edit to watch it run. The steps below follow that order.

  1. Create the file. In the project root, make a .claude folder and add settings.json inside it. Committing this file shares the hook with everyone working in the repository.
  1. Paste the hooks block. Add a hooks object with a PostToolUse array, an Edit|Write matcher so the hook fires after file changes, and a command hook that calls your own linter, such as npx eslint --fix or ruff check --fix, on the edited file.
  1. Let Claude Code pick it up. Edits to settings files are normally picked up automatically while a session is running. If the hook doesn't appear in the next step, restart the session.
  1. Confirm registration. Run /hooks inside Claude Code. It lists configured hook events and their matchers, so you can check that PostToolUse shows your entry.
  1. Trigger an edit. Ask Claude to change a file that matches your linter's scope. The linter should run right after the edit, and its output appears in the session.

If nothing runs, check the JSON for syntax errors first, such as trailing commas or mismatched braces. Then verify the matcher spelling, since tool names are case-sensitive, and run the linter command by hand in a terminal to make sure it works outside Claude Code.

Should you commit settings.json or keep it local?

Keep personal preferences separate from the shared project configuration. That split keeps individual preferences from leaking into the shared configuration.

Claude Code reads settings from several levels:

Should you commit settings.json or keep it local?
FileScopeCommit it?
.claude/settings.jsonProject, shared with the team via gitYes
.claude/settings.local.jsonPer-user overrides for one projectNo
~/.claude/settings.jsonUser-level, applies across all reposNo (lives in your home directory)
Managed settingsOrganization-wide enforcementDeployed by admins

What belongs where

Put the shared linter hook in the project file. Anyone who clones the repo gets the same behavior after every edit, with no setup steps to forget.

Use the local file for things like a different timeout, an extra personal hook, or experiments you don't want to impose on others. Use the user-level file for habits you want in every repo, such as a notification hook.

If your organization needs rules that individuals can't skip, managed settings are the place to enforce them.

Make sure the linter is actually installed

A committed hook only works if the command it calls exists on every machine. Make sure the linter is installed for everyone who uses the repo, so the hook behaves the same way on each machine.

If you are also standardizing agents, a subagent file generator can help you produce consistent definitions to commit alongside your settings.

How can you use a hooks builder to generate the file?

A hooks builder lets you assemble the hooks visually and outputs a valid settings.json, so you skip hand-writing the JSON. The Claude Code Hooks Builder does this, and it is one of several free Claude Code tools available.

The workflow is short:

  1. Pick a starting point in the builder, such as the auto-format preset, which already runs a command after every edit.
  2. Change the command to your linter, so the PostToolUse hook runs it after file edits.
  3. Save the output as .claude/settings.json in your project.

The tools are free, need no signup, and nothing leaves your browser, so your linter commands and project paths stay on your machine.

Review before you commit

A generated file is a starting point, not a guarantee that it fits your repo. Before committing, check two things:

  • The linter command: confirm it matches what you run locally, such as the right binary, flags and file arguments.
  • The matcher: make sure it targets only the editing tools you want, so the linter does not fire on unrelated tool calls.

Run one edit in Claude Code afterward and confirm the hook fires as expected. Then the file is ready to share with your team.

How do you debug a linter hook that does not run?

Start by checking the file itself, then the matcher, then the command. Most silent hook failures come from invalid JSON, a matcher that does not match the tool name, or a linter the hook shell cannot find.

1. Validate the JSON

A trailing comma, a missing quote or a comment will make the whole settings file invalid. Run it through a JSON validator or a command such as python -m json.tool .claude/settings.json. If it prints an error with a line number, fix that first.

2. Check the matcher and tool name

Tool names are capitalized. Edit|Write will match those tools, while edit or write will not. Confirm the event is PostToolUse, since a lint-after-edit hook needs to fire once the file has changed.

3. Confirm the linter is on PATH

The hook runs in a non-interactive shell, so it may not load the same profile as your terminal. A linter installed through a version manager or a virtual environment can work by hand and fail in the hook. Use an absolute path, or call it through your project runner such as npx or python -m.

4. Run the command manually with sample JSON

Hook commands receive event data as JSON on stdin. Reproduce that by piping a sample payload into the command:

echo '{"tool_input":{"file_path":"src/app.py"}}' | ./lint-hook.sh
echo $?

Check the output and the exit code. If the script parses the file path with a tool like jq, make sure that tool is installed too.

5. Keep slow linters from blocking edits

A linter that scans the whole repository after every edit slows each change. Lint only the file named in the payload, or only changed files, and move full runs to CI or a pre-commit step. Set the hook's timeout field, in seconds, so a stuck linter cannot stall the session.

Related articles

Generate your linter hook file with the free Claude Code Hooks Builder, then explore Agentsforce's one-time $49 team of Claude Code specialists when you want consistent subagents across repos.

Frequently asked questions

Can one settings.json run different linters for different file types?

Yes. The hook command receives JSON on stdin, so a small script can read tool_input.file_path and branch on the file extension. For example, it can run eslint for .ts files and ruff for .py files, and skip anything else.

Will a linter hook slow Claude Code down on large repos?

It can if the hook lints the whole project, so lint only the edited file instead. Also set a timeout on the hook so a stuck linter cannot stall the session. Speed varies by linter, config and machine, so measure your own single-file run with time before deciding whether it is fast enough.

How do I keep hooks consistent across several repos?

Commit a project-level settings.json in each repo so every teammate gets the same hooks. Keep hooks in ~/.claude/settings.json only if they need to apply on your own machine, since user-level files are not shared with the team. Name the one file teammates should never edit locally, so everyone knows which copy is the source of truth.

Is it safe to let Claude edit its own settings.json hooks?

Treat it with care. Hooks run shell commands with your user permissions, so a bad command can do real damage. Review any generated command line by line, and avoid anything containing rm, curl piped to sh, or network calls before you commit the file.

Sources

  1. A tool to manage Claude Code Hooks
  2. Set up these 4 Claude Code hooks
  3. Any Way for Claude Code to Circumvent
  4. Hooks reference (Claude Code docs)
10 min readCustom Shopify Storefront with Claude Code vs AgencyRead →12 min readBest Claude Code Tools for WordPress DevelopersRead →12 min readBest Claude Code Setup for Shopify Stores (Step by Step)Read →

Give Claude Code a whole team. 55 specialists, one payment of $49.

Get Agentsforce