
利用 Claude Code Hooks 自動化您的 AI 工作流程
本文介紹了 Claude Code 新推出的 Hooks 系統,這是一個用於自動化 AI 代理工作流程的強大功能。文章探討了這些 Hooks 如何實現腳本編寫以增強控制和工作流程的添加,並著重介紹了如何與 GitButler 整合以自動提交工作。
5 months ago by Scott Chacon
Automate Your AI Workflows with Claude Code Hooks
Claude Code's new hooks system is a powerful tool for automating your agent workflows. We'll do a quick overview of what's possible and explore a few fun examples.

A while ago we realized that a lot of our users were taking advantage of our rather unique virtual branches functionality in GitButler to manage the code that their coding agents were producing into multiple branches easily, so we started to work on better ways to interact with them.
What we really wanted to do was inspect what an agent was doing so we could automatically create save points that could easily be edited. Or even better, know what topic the agent was working on so we could try to group the changes into multiple branches.
However, most agents are almost entirely opaque - you can't see anything that Cursor or Windsurf is doing, for example other than the files that they're editing on disk.
Claude Code Hooks
However, recently Anthropic shipped lifecycle hooks for their terminal based coding agent. These provide the ability to tell Claude to run a script whenever one of it's sessions reaches any of these points:
That's a lot of stuff. Which is awesome, because it means that now you can script all sorts of automations and extra workflow additions and controls that you simply cannot do with other code generation agents.
Since we just shipped a way to use GitButler to automatically commit work into one lane per session based on these hooks, and we learned a lot while implementing it, we thought we would show you how you can use this for fun and profit too.
So, here is a very simple tutorial on how you can add a pretty useful hook to your Claude Code workflow in a few minutes as an example of what this can do and how to set it up.
Hooks are a very recent addition to Claude Code, released initially in June 2025. I'm on 1.0.59 and you can easily update by running claude update
A simple example
We're going to add a simple hook that will make Claude send you a desktop notification when a session is completed.
Since we want to know when something is completed, we'll use the Stop hook, which fires when everything in a session is done.
You can set up a hook in one of three places:
Claude Code should look for all of these files, and fire every hook that matches in any of them. Since what we're doing is pretty generic and also platform specific, we'll put it in the user settings (~/.claude/settings.json)
How to do a Desktop notification on Mac
This should be simple, but it's sadly slightly complicated.
On a Mac, you can run osascript to do a notification, but the catch is that if you've never done this before, you need to open up a program called Script Editor, run it manually first, give it system permissions, then you're good going forward.

Find 'Script Editor' under Notifications in your Settings and allow
notifications
Once you have permissions, then you can just run something like this in your terminal to see a cool little notification and sound:

Ok, now we can do a simple desktop notification. Let's make Claude nail it.
Adding the hook
It's possible that you already have something in some of these files, sometimes Claude will add permissions rules to your local project file, but you probably won't have a User settings file yet, or it may be empty.
So, go ahead and make your ~/.claude/settings.json look something like this:
If we then run Claude anywhere, it will give us this little popup every time it's finished with a chat based task.

You can't hear it, but it also made a little ding sound.
A massively complicated example
Ok, we have our cute example and you probably want to stop reading here. I'm sure you can take that, look at the hooks docs and figure out how to do all sorts of pretty cool things, like perhaps:
Whatever, the world is your Claude-based oyster.
I was just going to end here, but I figured it might be fun to really get into the weeds and even abuse Git a little along the way.
What I'll do now is implement a poor-man's version of what the GitButler client does with it's Claude Code hooks integration, which is to create a new commit after every chat session is finished. To make the task much more complicated and abuse Git the way I promised, we'll even store changes from multiple simultaneous running sessions in different branches.
Let's do some fun Git magic.
Version One: Auto Committing
Your should be able to figure out how to do this on a very basic level, since we already are running a command when a session finishes. It's fairly straightforward to change the notification into a git commit -m checkpoint. You can always clean up and edit the commits later, but it gives you rollback points to just about anywhere.
However, let's make it slightly better by using the prompt itself as the commit message.
This introduces a new concept to the hooks, which is hook input. Every hook gets called with json data passed to stdin, the format of which depends on the hook. In the case of the Stop hook, we get something like this:
So, you can see immediately some useful data:
The transcript file is actually a JSONL file (JSON "lines" format), so each line is an individual json object - makes it easy to keep concatenating new json content. Each entry (line) looks something like this:
Another interesting thing to notice here is where it keeps these transcript files. If you go into ~/.claude/projects you can see a list of every project where you've run Claude Code. Inside each project directory is a transcript file for every session you've done. It's actually pretty cool.
Enough blah blah, let's write our auto-committing script
Now, there are a few different ways we could do this auto-committing script.
We could use the UserPromptSubmit to store the latest prompt in a tempfile and then commit using that as the message when we get a Stop call, but instead we're going to just run a single command at the Stop hook that figures out what the last prompt was from the transcript, then add all the files and commit.
So, let's change the ~/.claude/settings.json to run a new script of ours and get that script to do all of that. Instead of the ocascript line, just point it at a script of yours. Let's say we put it in ~/bin/post_chat.rb:
Our new settings file, with our new Stop hook script
I am of course using Ruby here, both because it's the best language in the world, but also because it's pretty pseudo-code-y, so it should be easy to follow.
So, here is my script, which:
Check it out:
Pretty straightforward. Now every time a chat session ends, you should automatically get a commit to whatever branch you're on.
Version Two, Beast Mode: Multiple Branches
Now, this won't exactly work in all circumstances, but let's try to do something crazy, just for fun.
Let's see how close we can get to what GitButler does - figure out what files are changed in parallel sessions and commit only the changes in those files to independent branches.
The plan is:
Now, this will commit changes to the same file to both session's branches because we can't easily do partial hunk staging in the Git CLI (as we can in GitButler) but it's still pretty fun.
Enough blah blah, let's write this beast
Instead of actually embedding the full files here, which I'm sure you don't want to read as it's quite repetitive, I'll just link a GitHub repo with the three files (pre_tool_call.rb, post_tool_call.rb and stop_call.rb) here:

Let's look at the interesting bits.
For the PreToolUse call, it gets the stdin data similar to the previous script, then does this:
Pretty standard, though you need to use the --index-output version of git read-tree to initialize the index file with the contents of the last commit (HEAD). This will work even as we're doing other things in the working directory and other branches.
I'm also writing the current HEAD sha into a file called base_commit, which is sort of unnecessary as I'll just fall back to HEAD later, but it's nice maybe for posterity.
The next thing will be the PostToolUse call, which is really simple - just find that index file again and run git add on whatever file was modified.
Notice that to make sure this goes into our new shadow index, we need to set the GIT_INDEX_FILE env variable so we don't stage in our actual working directory.
Finally, when the chat is finished, we get the Stop call and need to actually create the commit. This is a little more complicated.
First we create the branch name we know we want to commit to:
Then we check if the branch already exists so we can use it as our parent if we need to, otherwise we use the HEAD:
Finally, we write the commit to our session branch without touching our current one:
You may not know about git write-tree and git commit-tree and git update-ref, but essentially we're just doing the plumbing commands that write a tree object from the session shadow index we have, then manually create a commit object from that tree, parent and commit message, then finally update the branch reference with our new commit.
Finally, we need to tell Claude Code about these three hooks, so our settings.json file should now look something like this:
Most of that is pretty straightforward, just three hooks instead of one. The one thing is the matcher entry on the tool use calls, which is set to "Edit|MultiEdit|Write", meaning that it will only get called on file modifications. You can also set this value to things like Read, Bash, Grep, etc - but for the purposes of what we're doing here, we're only interested in file changes.
What does it look like?
Ok, so let say we have this in place and do some chat sessions now. What does this end up looking like?
Let's say that we go into a simple project, fire up claude in each, and ask one to create a file that lists directory contents and the other to show disk usage:

However, running the list_dir.py script, I don't like the file icons it put in, so I'll ask it to remove them.

Great, done. So now what does Git look like?
Hey! We have two new branches, one for each session. Let's check them out:

Nice - on the first branch we have two commits - one that adds the list_dir.py file and another that modifies it, and both with the correct prompt. The second branch just has the one commit that adds the disk_usage.sh file, again with the prompt.
Now, in theory, we could push these to GitHub with something like:
Or turn it into a normal branch locally, or whatever. The work of our two parallel Claude Code sessions are isolated into different branches.
It's worth noting here that since nothing has been committed to the branch we're actually on, the working directory looks dirty. You would need to stash or clean before you could merge these branches back in.
Anyhow, fun exercise.
Fin
That's it for now. A little Git abuse is always fun on a Thursday. Hopefully you've also learned a bit about Claude Code hooks and what you could possibly do with them.
If you want to do the "splitting sessions into branches" thing, but in a way that is much better and simpler, try out GitButler's Claude Code hooks instead of using this method, I promise it's much more robust.

Written by Scott Chacon
Scott Chacon is a co-founder of GitHub and GitButler, where he builds innovative tools for modern version control. He has authored Pro Git and spoken globally on Git and software collaboration.
Table of Contents
Related articles
by Scott Chacon - 6 min read
by Scott Chacon - 2 min read
by Scott Chacon - 5 min read
More to Read

MCP vs RAG: Two Very Different Ways to Gain Context
At first glance RAG and MCP seem similar. In practice, they solve very different problems and lead to very different system designs.

Getting Started With GitButler Agents
GitButler built a new way to integrate AI-powered code generation directly into your version control workflow.

Using the GitButler MCP Server to Build Better AI-Driven Git Workflows
By exposing GitButler functionality through the Model Context Protocol, the MCP server allows AI tools to interact directly with your Git workflow.
Stay in the Loop
Subscribe to get fresh updates, insights, andexclusive content delivered straight to your inbox.No spam, just great reads. 🚀
F💥ck Merge Conflicts.Use GitButler ⧓
©2025 GitButler. All rights reserved.
相關文章