Feature Request: Proactive Hooks for Deterministic Command Chaining
Title: Feature Request: Proactive Hooks for Deterministic Command Chaining
Area: Agent Tooling, Hooks
Is this a bug report or a feature request?
This is a feature request.
Problem Statement
The current hooks system (PreToolUse, PostToolUse, Stop, etc.) as documented in hooks-guide.md and hooks.md is incredibly powerful for validating tool usage, providing feedback, and running post-processing tasks. However, its design is fundamentally reactive. Hooks trigger based on a tool call the model has already decided to make.
This creates a limitation for workflows that require deterministic, sequential execution of commands. For example, in a standard CI or linting workflow, a developer might want to enforce the following logic:
- Run
npm run build. - If and only if
buildsucceeds, runnpm run test. - If and only if
testsucceeds, runnpm run lint.
Currently, achieving this requires complex prompting ("run build, then if it succeeds run test...") or a potentially brittle implementation using Stop/SubagentStop hooks. The model must reason about and decide to run each subsequent step, introducing non-determinism, latency, and additional token cost for what should be a fixed, predictable sequence.
Proposed Solution
I propose the introduction of a new hook type or a new property within the existing PostToolUse hook that enables proactive command chaining. This would allow users to define a "follow-up" action that is automatically executed upon the successful completion of a matched tool call.
A new hook event, perhaps named OnToolSuccess, could be introduced. This hook would only trigger if the initial tool call completes with an exit code of 0.
Here is a conceptual example of how this could look in .claude/settings.json:
{
"hooks": {
"OnToolSuccess": [
{
"matcher": "Bash(npm run build)",
"action": {
"type": "command",
"command": "npm run test"
},
"description": "After a successful build, automatically run the test suite."
},
{
"matcher": "Bash(npm run test)",
"action": {
"type": "command",
"command": "npm run lint"
},
"description": "After all tests pass, automatically run the linter."
}
]
}
}
How it would work:
- Claude is prompted to
build the project. - It calls the
Bashtool with the commandnpm run build. - The command succeeds (exit code 0).
- The
OnToolSuccesshook system finds a match forBash(npm run build). - Claude Code automatically executes the defined follow-up action:
npm run test, without requiring another reasoning step from the model. - The result of
npm run testis then returned to the model for the next turn.
Use Cases & Rationale
This feature would unlock more robust and efficient automation for common developer workflows:
- Reliable CI/CD Sequences: Chain build, test, and lint commands to create simple, deterministic pipelines directly within Claude Code.
- Code Generation & Formatting: Automatically run a code formatter (e.g.,
prettier) and then a linter (e.g.,eslint --fix) after Claude uses theWriteorEdittools on specific file types. - Multi-Step Data Processing: Create sequences like
download_data.sh,unzip_data.sh, and thenprocess_data.py. - Infrastructure Management: Chain Terraform commands like
terraform planfollowed by a custom approval script.
The key benefits are:
- Determinism: Removes model variability from routine, sequential tasks, making automation more reliable.
- Efficiency: Reduces token cost and latency by eliminating the need for the model to "re-think" the next logical step in a fixed workflow.
- Simplicity: Allows users to define simple state-machine logic declaratively in configuration files, rather than through complex prompts or
Stophook hacks. - Empowerment: Gives power users a first-class mechanism for orchestrating more complex, multi-step agentic tasks.
Current Workarounds and Their Limitations
- Complex Prompting: A user can prompt Claude with the entire sequence. This is verbose, must be repeated, and is susceptible to model interpretation errors.
- Using
Stop/SubagentStopHooks: One could use aStophook to check the last command's result from the transcript and then feed a new instruction back to Claude. This is indirect, complex to implement correctly, and not the intended purpose of theStophook.
This proposed feature provides a clean, first-class solution for a very common and valuable pattern in developer automation. Thank you for considering this request
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗