[FEATURE] Sequential Hook Execution Option
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Claude Code currently executes all matching hooks in parallel. While this is efficient for independent hooks, it prevents use cases where hooks need to run in a specific order:
- Dependent transformations: Hook B needs to process the output of Hook A
- Ordered validation: Security check must pass before format check runs
- Pipeline processing: Data flows through multiple transformation stages
- Priority-based execution: Critical hooks should complete before optional ones start
- Resource constraints: Some hooks may conflict if run simultaneously (e.g., both trying to modify the same file)
From the Claude Code hooks reference:
"Parallelization: All matching hooks run in parallel"
There's no option to run hooks sequentially.
Proposed Solution
Add a sequential option to hook matcher configuration.
Configuration
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"sequential": true,
"hooks": [
{
"type": "command",
"command": "/path/to/format-code.sh"
},
{
"type": "command",
"command": "/path/to/run-linter.sh"
},
{
"type": "command",
"command": "/path/to/update-docs.sh"
}
]
}
]
}
}
Behavior
| Setting | Behavior |
|---------|----------|
| sequential: false (default) | All hooks in the array run in parallel |
| sequential: true | Hooks run one at a time, in array order |
Sequential Execution Details
When sequential: true:
- Hooks execute in the order they appear in the
hooksarray - Each hook must complete (success or failure) before the next starts
- If a hook fails (exit code 2), subsequent hooks are skipped
- The
tool_inputortool_responsepassed to each hook reflects any modifications from previous hooks - Total timeout is the sum of individual hook timeouts
Output Chaining
For sequential hooks, the output of one hook can optionally be passed to the next:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"sequential": true,
"chainOutput": true,
"hooks": [
{
"type": "command",
"command": "/path/to/sanitize-command.py"
},
{
"type": "command",
"command": "/path/to/validate-command.py"
}
]
}
]
}
}
When chainOutput: true, each hook receives an additional field:
{
"previous_hook_output": {
"exitCode": 0,
"stdout": "...",
"updatedInput": {...}
}
}
Alternative Solutions
- Single wrapper script: Combine multiple hooks into one script that runs them sequentially. This works but loses the modularity and individual timeout/error handling of separate hooks.
- External orchestration: Use a separate tool to orchestrate hook execution. This adds complexity and doesn't integrate with Claude Code's configuration.
- File-based coordination: Hooks write to temporary files to coordinate. This is fragile and error-prone.
Priority
Medium - Would be very helpful
Enables more sophisticated hook workflows without sacrificing modularity.
Feature Category
Configuration and settings
Use Case Example
Scenario: Code quality pipeline
- Developer edits a TypeScript file
PostToolUsehook withsequential: trueruns:
- First:
prettier --writeto format the file - Second:
eslint --fixto fix linting issues (must run after formatting) - Third:
tsc --noEmitto type-check (must run after lint fixes)
- Each step depends on the previous step's modifications
- If formatting fails, linting is skipped (no point running on unformatted code)
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"sequential": true,
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs -I{} sh -c 'if [[ \"{}\" == *.ts ]]; then npx prettier --write \"{}\"; fi'"
},
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs -I{} sh -c 'if [[ \"{}\" == *.ts ]]; then npx eslint --fix \"{}\"; fi'"
},
{
"type": "command",
"command": "npx tsc --noEmit"
}
]
}
]
}
}
Scenario: Security validation chain
- User submits a bash command
PreToolUsehook withsequential: trueruns:
- First: Check command against blocklist (fast, critical)
- Second: Analyze command for injection patterns (slower, thorough)
- Third: Log command for audit (should only run if previous checks pass)
- If blocklist check fails, no point running deeper analysis
- Audit log only records commands that passed validation
Scenario: Input transformation pipeline
PreToolUsehook for Write tool:
- First hook: Normalize line endings (CRLF → LF)
- Second hook: Strip trailing whitespace
- Third hook: Ensure final newline
- Each transformation builds on the previous
- Final modified content is what gets written
Additional Context
Prior Art
Gemini CLI supports sequential execution:
{
"matcher": "pattern",
"sequential": true,
"hooks": [...]
}
Their documentation notes:
"Sequential vs. parallel execution"
as a configuration option for hook matchers.
Implementation Considerations
- Timeout handling: Total timeout for sequential hooks should be configurable (sum of individual timeouts, or a separate
totalTimeout) - Error propagation: Clear rules for how errors in one hook affect subsequent hooks
- Output passing: Decide whether hook outputs are automatically chained or require explicit opt-in
- Mixed mode: Consider whether a matcher can have some parallel and some sequential hooks
- Debugging: Sequential execution should be clearly visible in debug output
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗