title: "Hooks and Commands" last_updated: 2026-03-21 status: proven difficulty: intermediate prerequisites: [03-prompting-for-agents]
Hooks and Commands
Customizing agent behavior with event-driven workflows.
The Core Question: "How Do I Customize Agent Behavior?"
By now you have a working agent, a project memory file that teaches it your conventions, and prompting skills that get good results. But you have probably noticed a recurring frustration: you keep correcting the same things. The agent forgets to run the linter. It commits to the wrong branch. It edits a file you told it not to touch — three sessions ago, in a conversation that no longer exists.
Project memory helps. Writing "always run eslint after editing TypeScript files" in your CLAUDE.md or AGENTS.md reduces the frequency of these mistakes. But it does not eliminate them. Instructions in natural language are suggestions. The agent reads them, tries to follow them, and sometimes does not. There is no enforcement mechanism. No guarantee.
Hooks change that. A hook is a piece of code — usually a shell command or a short script — that runs automatically when the agent does something specific. It is not a suggestion. It is not a prompt. It is code that executes. If you want the linter to run after every file edit, a hook makes that happen every single time, regardless of what the agent remembers or forgets.
This is the difference between asking someone to wash their hands and installing a sensor that dispenses soap automatically. Both aim for the same outcome. One relies on memory and compliance. The other relies on a mechanism.
The Event-Driven Model
Agentic coding tools are built on a loop: the agent thinks, chooses an action, executes it, observes the result, and repeats. Each step in this loop is an event. The agent is about to call a tool — that is an event. The agent just finished calling a tool — that is an event. The agent is about to end its session — that is an event.
Hooks let you attach behavior to these events. You are not modifying the agent's reasoning or changing its model. You are inserting your own code at well-defined points in the agent's workflow. The agent decides to edit a file. Before that edit is applied, your hook runs. It might validate the change, log it, or block it entirely. After the edit is applied, another hook could run the formatter, execute tests, or send a notification.
This is the same pattern that powers Git hooks, CI/CD pipelines, and browser event listeners. If you have ever written a pre-commit hook or a GitHub Actions workflow, you already understand the model. The only difference is that the events come from an AI agent instead of a version control system or a browser.
The event model means hooks are composable. You can have one hook that logs all bash commands, another that blocks commits to main, and a third that runs tests after code changes. They operate independently, each responding to the events it cares about and ignoring the rest.
Why Hooks Matter: The Automation Progression
There is a natural progression in how developers address recurring problems with their agent:
Level 1: Manual correction. The agent does something wrong. You notice it during review. You tell the agent to fix it. This works, but it costs you time and attention every time it happens. It scales poorly — you catch the same issue across dozens of sessions.
Level 2: Project memory instruction. You add a line to your CLAUDE.md or AGENTS.md: "Always run tests after modifying code in the src/ directory." The agent reads this at the start of each session and usually follows it. Compliance is high but not perfect. The agent might skip it when the context window is full or when it is focused on a complex chain of reasoning.
Level 3: Hook. You write a PostToolUse hook that runs npm test whenever a file in src/ is modified. The tests run every time. The agent cannot forget, skip, or rationalize its way out of it. The behavior is guaranteed by code, not by prompt compliance.
Each level is appropriate for different situations. Manual correction is fine for one-off issues. Project memory is right for conventions that benefit from flexibility — the agent should usually follow them but might have good reasons to deviate. Hooks are right for non-negotiable requirements — things that must happen every time, with no exceptions.
The mistake most people make is staying at Level 1 for too long. If you find yourself correcting the same behavior more than three times, it is time to escalate to Level 2 or Level 3.
Types of Hooks
Hooks fall into four broad categories based on when they run and what they do:
Pre-action hooks run before the agent performs an action. They can inspect what the agent is about to do and optionally block it. A pre-action hook might prevent the agent from executing a dangerous bash command, block edits to protected files, or validate that certain conditions are met before a destructive operation proceeds.
Post-action hooks run after the agent has completed an action. They cannot block the action (it has already happened), but they can respond to it. A post-action hook might run a linter after a file edit, execute tests after code changes, or format code that the agent just wrote.
Notification hooks run when the agent reaches certain milestones — completing a task, encountering an error, or needing human input. They are useful for monitoring long-running tasks or integrating with external systems like Slack or email.
Validation hooks are a specialized form of pre-action hooks focused on checking conditions. They verify that the environment is in the right state before the agent proceeds — the correct branch is checked out, required environment variables are set, or certain files exist.
In practice, the boundaries between these categories blur. A single hook might validate, log, and conditionally block. The categories are useful for thinking about what you need, not for strict classification.
The Principle: Hooks Enforce What Project Memory Requests
The best way to think about the relationship between hooks and project memory is as two layers of the same system. Project memory describes your intent. Hooks enforce it.
Your CLAUDE.md says "run tests after modifying source files." That is a statement of policy. Your PostToolUse hook that executes the test suite after edits to src/ is the enforcement mechanism. The project memory instruction tells the agent why this matters (so it can make intelligent decisions when edge cases arise). The hook ensures it actually happens (so you do not need to trust the agent's compliance).
This layered approach is more robust than either mechanism alone. Project memory without hooks is aspirational. Hooks without project memory are opaque — the agent does not understand why things are happening around it, which makes it harder for it to work with the system rather than against it.
Custom Commands: Shortcuts for Complex Workflows
Beyond hooks, most agentic tools support custom commands — named shortcuts that trigger specific workflows. If you find yourself typing the same complex prompt repeatedly ("run the full test suite, report any failures, and if tests pass then run the linter"), a custom command turns that into a single invocation.
Custom commands differ from hooks in a key way: hooks are automatic (triggered by events), while commands are manual (triggered by you). Hooks are for things that should always happen. Commands are for things you want to happen on demand, with a convenient shortcut.
Think of hooks as cron jobs and commands as shell aliases. Both reduce repetition. They just operate at different levels of automation.
When to Use Hooks vs. Project Memory Instructions
Not everything should be a hook. Here is a practical decision framework:
Use a project memory instruction when:
- The behavior benefits from flexibility. The agent should usually follow it but might have good reasons to deviate.
- The requirement is about style, approach, or preference rather than correctness.
- Enforcement is not critical — if the agent skips it once, nothing breaks.
Use a hook when:
- The behavior must happen every time, with no exceptions.
- The requirement can be verified programmatically (lint passes, tests pass, file exists).
- You have corrected the agent about this more than three times and the pattern keeps recurring.
- The behavior involves external tools (formatters, linters, notifiers) that the agent would need to remember to invoke.
Use both when:
- You want the agent to understand why the requirement exists (project memory) and you want to guarantee it is met (hook).
The Cost of Over-Hooking
Hooks are powerful, and that power invites overuse. Every hook adds latency. A PostToolUse hook that runs a full test suite after every file edit means the agent waits for tests to complete before it can continue. If your test suite takes 30 seconds, and the agent edits 10 files in a task, you have added 5 minutes of waiting.
More subtly, too many hooks create confusion. If the agent is receiving output from five different hooks after every action, its context window fills with hook output instead of task-relevant information. The signal-to-noise ratio drops. The agent may start responding to hook output instead of focusing on the actual task.
There is also a maintenance cost. Hooks are code. Code has bugs. A hook that worked fine with your old project structure may break after a refactor. A hook that checks for a specific file might fail when that file is renamed. Hooks that call external services can time out. Every hook you add is a piece of infrastructure you need to maintain.
The practical guidance is: start with zero hooks. Add them one at a time, in response to specific problems you have observed. After each addition, use the agent for a few sessions and evaluate whether the hook is helping or hindering. Remove hooks that cause more friction than they prevent.
Security Considerations
Hooks run with your permissions. A hook attached to a PreToolUse event executes as your user, with access to your filesystem, your environment variables, your network, and your credentials. This is by design — hooks need access to your tools to be useful — but it means you should treat hook configuration with the same care you treat shell scripts.
Do not copy-paste hook configurations from untrusted sources without reading them. A malicious hook could exfiltrate environment variables, modify files outside your project, or install software. This is no different from the risk of running an untrusted shell script, but hooks have the additional property of running automatically and silently.
Keep hooks in version-controlled project configuration where your team can review them. Avoid hooks that download and execute remote code. If a hook needs elevated permissions, think carefully about whether it really needs to be a hook or whether it would be safer as a manual step.
Key Takeaways
- Hooks turn recurring corrections into permanent automation. If you keep fixing the same agent behavior, a hook fixes it once and for all.
- The automation progression is: manual correction, project memory instruction, hook. Escalate when the frequency of a problem justifies the investment.
- Hooks are event-driven. They attach to specific points in the agent's workflow — before an action, after an action, on notification, or on session boundaries.
- Hooks enforce what project memory requests. Use both layers together for the most robust configuration.
- Custom commands are manual shortcuts. Use them for frequently repeated workflows that you want on demand rather than automatic.
- Do not over-hook. Each hook adds latency, complexity, and maintenance burden. Add them one at a time, in response to observed problems.
- Hooks run with your permissions. Treat hook configuration as seriously as you treat shell scripts. Review before you run.