How to Chain Claude Code Hooks for Automated Workflows
Problem
Every time I edited package.json, I had to manually run the same sequence: npm install, then npm test, then commit the changes. Over and over. Multiply this by dozens of edits per day, and I was losing focus on actual coding.
I knew Claude Code had hooks, but I couldn’t figure out how to chain multiple actions together. The documentation showed single hooks, not pipelines.
Environment
- Claude Code CLI
- Node.js project with npm
- Git repository
- macOS/Linux shell
What Happened
I started with a simple PostToolUse hook in my settings.json:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": ["npm run lint --fix"] } ] }}This worked for linting, but I needed more. After editing package.json, I wanted:
- Run
npm installto sync dependencies - Run
npm testto verify nothing broke - Only commit if tests passed
My first attempt at chaining failed:
{ "hooks": { "PostToolUse": [ { "matcher": "package.json", "hooks": [ "npm install", "npm test", "git add package.json package-lock.json" ] } ] }}The hooks array runs commands in parallel, not sequentially. Tests started before npm install finished.
Solution
I discovered two approaches: using && for inline chaining, and shell scripts for complex workflows.
Inline Chaining with &&
The simplest approach chains commands with &&:
{ "hooks": { "PostToolUse": [ { "matcher": "package.json", "hooks": ["npm install && npm test && git add package.json package-lock.json"] }, { "matcher": "Edit|Write", "hooks": ["npm run lint --fix", "npm run format"] } ] }}The && operator ensures each command only runs if the previous one succeeds. If npm install fails, the chain stops.
Shell Scripts for Conditional Logic
For workflows with conditional logic (like “commit only if tests pass”), shell scripts work better:
#!/bin/bashset -e
# Run linternpm run lint --fix
# Run testsif npm test; then echo "Tests passed" # Commit if on feature branch BRANCH=$(git branch --show-current) if [[ $BRANCH == feature/* ]]; then git add -A git commit -m "Auto-commit: lint and test passed" fielse echo "Tests failed" exit 1fiThen reference it in settings:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": ["bash .claude/hooks/post-edit.sh"] } ] }}Chaining Patterns
| Pattern | Use Case | Example |
|---|---|---|
| Sequential | Linear workflows | edit - lint - format |
| Conditional | Quality gates | edit - test - (if pass) commit |
| Parallel | Independent checks | edit - (lint OR format) |
| File-specific | Targeted workflows | package.json - npm install |
For conditional commits:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit", "hooks": [ "npm run test && git add . && git commit -m 'Auto: tests passed'" ] } ] }}Reason
Hook chaining works because Claude Code treats each hook as a shell command. When you use &&, the shell handles the sequencing, not Claude Code. This means:
- Exit codes matter - A non-zero exit code stops the chain
- Each command runs in order -
&&guarantees sequential execution - Environment variables persist - Set once, available throughout the chain
The hooks array runs commands in parallel for speed. But when you need sequence, use a single command with && or call a shell script.
Why This Matters
- Eliminates repetitive steps - Edit once, run all checks automatically
- Ensures consistency - Same workflow every time, no skipped steps
- Catches issues early - Tests run immediately after changes
- Reduces cognitive load - Focus on code, not process
As one Reddit user put it: “It turns a back-and-forth workflow into something that is less demanding. You set it once and the guardrails are just… there.”
Summary
Chain Claude Code hooks by combining commands with && or using shell scripts. The hooks array runs commands in parallel, but inline && chains them sequentially. For complex conditional logic, extract to a shell script and reference it from your configuration.
Start simple: add one chain, test it, then expand. My current setup handles linting, formatting, testing, and conditional commits automatically. The initial setup took 30 minutes; the time savings compound daily.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments