[00:00] (0.00s)
When Enthropic announced hooks for
[00:01] (1.36s)
Claude code, there was this comment on
[00:03] (3.12s)
the Claude AI subreddit. I'm going to
[00:04] (4.96s)
use this to make Claude meow. I've
[00:06] (6.88s)
always wanted a cat, but I'm allergic.
[00:09] (9.28s)
That felt like as good of a use case as
[00:11] (11.04s)
any to get started with. So, this is
[00:13] (13.12s)
what your Claude code can sound like
[00:14] (14.48s)
when you're running it in parallel.
[00:21] (21.92s)
Or maybe you prefer the old school
[00:29] (29.68s)
Or maybe you want Cloud Code to talk to
[00:31] (31.76s)
editing ready for you bashing. Let me
[00:35] (35.44s)
quickly walk you through how I built
[00:36] (36.80s)
this. And I think this is a really great
[00:38] (38.32s)
project for getting started with cloud
[00:40] (40.16s)
code and hooks because when you assign
[00:42] (42.40s)
sounds to all of the different events,
[00:44] (44.40s)
you will start to understand when
[00:46] (46.16s)
they're getting triggered, which will
[00:47] (47.84s)
then help you come up with new ideas for
[00:49] (49.68s)
a bit more pragmatic ways that you can
[00:51] (51.52s)
use hooks, which is an incredibly
[00:53] (53.20s)
powerful addition to Claude Code. The
[00:55] (55.36s)
general idea here is that Claude has a
[00:57] (57.76s)
whole bunch of different types of
[00:59] (59.12s)
events, and you can set up hooks to run
[01:01] (61.44s)
custom commands before or after or on
[01:04] (64.48s)
those different events. So some of those
[01:06] (66.40s)
events would be a pre-tool use. So
[01:09] (69.20s)
before it uses a tool, the tool includes
[01:12] (72.00s)
bash which you'll find out if you do
[01:13] (73.76s)
this is a lot of cloud code use. Uh it
[01:16] (76.80s)
is also editing files. It is reading
[01:18] (78.80s)
files. It is uh fetching stuff from the
[01:21] (81.60s)
web. Uh you can also do post tool use.
[01:24] (84.40s)
So this is letting you know after it's
[01:25] (85.92s)
finished that task. There's
[01:27] (87.28s)
notification. There's always been the
[01:28] (88.96s)
notification with cloud code that you
[01:30] (90.56s)
could set up with iTerm for instance to
[01:32] (92.40s)
beep when it's waiting for your approval
[01:34] (94.40s)
on something. But I found that it was
[01:36] (96.24s)
easy to miss this and didn't always work
[01:37] (97.84s)
great and I've really enjoyed being able
[01:39] (99.20s)
to customize that. There's stop. So this
[01:41] (101.60s)
is when cloud code's completed as its
[01:43] (103.12s)
task. I actually use this a lot in lie
[01:45] (105.36s)
of the notification. Uh and then there's
[01:48] (108.16s)
uh pre-ompact. So something that it's
[01:49] (109.76s)
going to do before it autocompacts all
[01:51] (111.52s)
of your history. Uh so you can set up uh
[01:54] (114.96s)
claude to run custom commands before it
[01:57] (117.76s)
does any of these actions. And the way
[02:00] (120.08s)
you're going to set that up is in your
[02:01] (121.28s)
settings.json. Now anytime you're
[02:03] (123.44s)
editing your settings.json, JSON, you
[02:04] (124.88s)
got to figure out which one do you edit.
[02:06] (126.56s)
And I think this is a little confusing
[02:07] (127.84s)
for folks and probably worth a deep dive
[02:09] (129.44s)
later on. Uh, but generally for this use
[02:12] (132.64s)
case, I've been doing it in my
[02:13] (133.92s)
settings.json in my project. This way,
[02:17] (137.04s)
it is committed to my repo and uh, it
[02:20] (140.24s)
will be included on all of the uh, work
[02:24] (144.00s)
trees that I'm running this on. I also
[02:26] (146.24s)
have created inside mycloud directory a
[02:29] (149.92s)
hooks directory where I'm storing all of
[02:32] (152.24s)
the files associated with these hooks
[02:34] (154.96s)
here. Uh, and I suspect that I might end
[02:37] (157.84s)
up moving some of this stuff out to my
[02:40] (160.24s)
user settings, my uh, user/.cloud
[02:43] (163.36s)
directory so that this is included in
[02:44] (164.96s)
all of my uh, cloud usage across all my
[02:48] (168.00s)
projects. But for right now, I'm just
[02:49] (169.04s)
doing this on a single project. I did
[02:50] (170.80s)
not want to do it in my
[02:51] (171.68s)
settings.local.json because then it's
[02:53] (173.52s)
not committed to the repo. And then I
[02:55] (175.76s)
don't get all of this across all the
[02:57] (177.12s)
different work trees. You've edited your
[02:58] (178.88s)
settings.json before. You've probably
[03:01] (181.28s)
just looked at the permissions. Uh, and
[03:03] (183.52s)
this is a lot of permissions that I've
[03:05] (185.44s)
given Claude. Now, we'll minimize those
[03:07] (187.20s)
for now. Uh, and now we have hooks. So,
[03:10] (190.72s)
we're going to define the type of event
[03:12] (192.72s)
that we're going to trigger a hook on.
[03:14] (194.64s)
Hooks are a list and then uh a list of
[03:18] (198.72s)
uh dictionaries. And there's really I
[03:21] (201.20s)
think actually command is the only type
[03:23] (203.20s)
of hook that you can run. And for now
[03:25] (205.44s)
the only thing I'm doing is running a
[03:27] (207.44s)
custom Python script. And so I'm running
[03:29] (209.92s)
Python 3 and then I'm passing the full
[03:32] (212.16s)
path of that script. So you can see that
[03:35] (215.92s)
here in my project I have claude hooks
[03:39] (219.60s)
and then the script called hook handler.
[03:41] (221.68s)
And so I'm doing Python 3 users Greg
[03:44] (224.64s)
code YouTube tracker claude hooks etc.
[03:47] (227.84s)
All right. So this means that anytime
[03:49] (229.68s)
that cla code needs to run the
[03:51] (231.28s)
notification, it's running hook handler.
[03:53] (233.36s)
It means that anytime it's running stop,
[03:56] (236.00s)
it triggers a stop event, it's running
[03:57] (237.68s)
hook handler. Anytime it's running
[03:59] (239.36s)
pre-tool use, it's running the hook
[04:01] (241.04s)
handler. I tried this a few different
[04:02] (242.96s)
ways. I tried writing different scripts
[04:05] (245.20s)
for different um events. Uh I have also
[04:09] (249.28s)
tried uh using the same script but
[04:11] (251.28s)
passing different arguments in uh like
[04:13] (253.92s)
command line arguments in. I found that
[04:16] (256.00s)
difficult to debug. Uh, I found that
[04:17] (257.92s)
what I want to do is keep my
[04:19] (259.52s)
settings.json as simple as possible and
[04:22] (262.88s)
then handle all of the logic in the
[04:25] (265.68s)
Python script because there I have much
[04:27] (267.52s)
better debugging, etc. All right, so
[04:29] (269.68s)
let's take a look at the Python script.
[04:31] (271.92s)
This Python script, by the way, uh, I
[04:34] (274.24s)
generated with cloud code. I basically
[04:36] (276.24s)
passed it the URL for the hooks, uh,
[04:39] (279.36s)
documentation and told it what I want to
[04:41] (281.52s)
do. I went through several different
[04:42] (282.56s)
iterations and then I had it um uh
[04:46] (286.48s)
refactor it several times to make it as
[04:48] (288.32s)
legible as possible. And you can find
[04:50] (290.00s)
this script at uh hih high.ai/hooks.
[04:53] (293.76s)
Um but basically what's going to happen
[04:55] (295.52s)
here first and foremost let's just run
[04:58] (298.32s)
and log the data that cloud code is
[05:01] (301.60s)
passing into this script when it gets
[05:04] (304.88s)
triggered because that data is what will
[05:07] (307.76s)
help you decide what you want to do. You
[05:10] (310.64s)
can see under hook input hooks receive
[05:12] (312.56s)
JSON data via standard in containing
[05:14] (314.80s)
session information and event specific
[05:16] (316.56s)
data and it goes through some of the
[05:19] (319.20s)
specifics of what that looks like.
[05:20] (320.80s)
Another little tip that I found for
[05:22] (322.72s)
working with cloud code is you can just
[05:24] (324.48s)
say to cloud code uh do some stuff
[05:29] (329.44s)
to test out my hooks integration
[05:36] (336.00s)
and then it will just do stuff. Uh, and
[05:39] (339.52s)
I think right now I have the beeps
[05:40] (340.72s)
turned on.
[05:43] (343.04s)
So, you can hear the various beeps that
[05:44] (344.48s)
it's doing. And then as that runs, you
[05:47] (347.20s)
can see that it was updating its to-dos
[05:49] (349.52s)
and it's reading a file now. So, I'm
[05:52] (352.00s)
going to come over here and I'm going to
[05:53] (353.60s)
look into my hook handler.json l uh
[05:57] (357.68s)
because I have a function in my hook
[05:59] (359.84s)
handler called log hook data. It's right
[06:03] (363.04s)
here. Claw's going to keep making sounds
[06:05] (365.28s)
in the background as we go.
[06:07] (367.12s)
um which I might need to turn off. Uh
[06:09] (369.52s)
and so this is basically just uh writing
[06:12] (372.24s)
out the uh data that's coming in via
[06:14] (374.40s)
standard in. So this is what that looks
[06:16] (376.08s)
like. So you can see there's a session
[06:18] (378.00s)
ID. You can see that uh we have an event
[06:20] (380.56s)
name. We have the tool name. So in this
[06:22] (382.56s)
case it's it's pre-tool use. So we have
[06:24] (384.88s)
the tool name and then the tool input.
[06:26] (386.96s)
So this gives us all the information we
[06:29] (389.36s)
need to know about what Claude is doing
[06:31] (391.76s)
in order to branch the logic to to do
[06:35] (395.28s)
the various stuff. Um so let's uh come
[06:38] (398.88s)
back in here and we'll look at then what
[06:41] (401.28s)
my hook handler is doing. Uh basically I
[06:43] (403.84s)
have a uh
[06:47] (407.12s)
a few different directories here. I have
[06:49] (409.52s)
a sounds directory and I have um uh
[06:52] (412.88s)
beeps and I have voice and I think on a
[06:55] (415.12s)
different branch I had my meow sounds
[06:56] (416.96s)
although I may have actually
[06:59] (419.20s)
accidentally reverted that out after I
[07:01] (421.60s)
recorded the demo but okay we'll go dig
[07:03] (423.76s)
through uh git for that later. Uh the
[07:05] (425.76s)
way that I got these is I use uh
[07:07] (427.84s)
Epidemic Sound. Uh I use it in my
[07:10] (430.08s)
various YouTube videos. Um they have
[07:12] (432.32s)
music uh and then I also they also have
[07:15] (435.60s)
a sound effects section here. And so I
[07:17] (437.76s)
just came in here and I got there was a
[07:19] (439.44s)
section for uh beeps. And so I just
[07:23] (443.60s)
all sorts of fun beeps that you can play
[07:26] (446.32s)
with. Uh oh my god, that one's Let's
[07:29] (449.12s)
make that one stop right now. Sorry
[07:30] (450.48s)
about that. Uh they've got one for uh
[07:33] (453.28s)
you know, they've got cats.
[07:38] (458.40s)
That's kind of nice.
[07:40] (460.80s)
All right. I feel like I only use sad
[07:43] (463.84s)
meows in mine. I should change that. Uh
[07:46] (466.64s)
I did some construction equipment once
[07:48] (468.56s)
for the tool use. Uh they also have this
[07:50] (470.88s)
new feature called voices which I played
[07:52] (472.96s)
around with. So you can they have these
[07:54] (474.88s)
voice actors who have donated their
[07:57] (477.36s)
voice to the AI and then you can make
[07:59] (479.76s)
them say things. So uh I used Shawn here
[08:04] (484.64s)
because he's British. Seemed like Claude
[08:06] (486.08s)
might be British. uh uh
[08:10] (490.00s)
say committing and you can create the
[08:13] (493.44s)
voice over
[08:16] (496.16s)
commiting.
[08:17] (497.20s)
Commiting. Okay. Uh I guess I did spell
[08:19] (499.84s)
that wrong. Let's try that again.
[08:24] (504.80s)
Committing.
[08:25] (505.76s)
There we go. That's better. Uh so you
[08:27] (507.76s)
can see how I did those. One, if you do
[08:30] (510.00s)
end up using these, one um just tip is
[08:33] (513.12s)
say they often will cram a whole bunch
[08:35] (515.20s)
of different uh sound effects on the
[08:37] (517.92s)
same one. And so you can see it here.
[08:42] (522.16s)
All right. So then if you go to
[08:43] (523.52s)
download, you can come to segment and
[08:45] (525.84s)
you can select just the segment of the
[08:47] (527.84s)
sound effect that you want to download.
[08:49] (529.44s)
So I'll take that down. You can get just
[08:51] (531.44s)
that. So I did that. I played with a
[08:53] (533.68s)
different a few different ways of doing
[08:55] (535.44s)
this. The first iteration I did, um, I
[08:57] (537.92s)
just threw a bunch of random sound
[08:59] (539.12s)
effects into a folder and then I just
[09:01] (541.28s)
let it pick randomly. I do like
[09:03] (543.12s)
assigning specific sounds to specific
[09:04] (544.80s)
actions because I do find that it helps
[09:06] (546.56s)
me understand when Claude is doing
[09:11] (551.52s)
For instance, I didn't realize how often
[09:13] (553.76s)
Claude is updating its to-do list. Uh,
[09:16] (556.08s)
which I I thought was actually kind of
[09:17] (557.44s)
cool. I didn't realize initially before
[09:19] (559.68s)
I started doing this how many of the
[09:21] (561.28s)
commands that Claude runs is a bash
[09:23] (563.04s)
command. And so initially I just had it
[09:25] (565.68s)
uh playing a sound for bash and then it
[09:27] (567.60s)
was the same sound over and over again
[09:28] (568.96s)
and then I realized that I needed to do
[09:30] (570.40s)
a pattern match on uh you know the
[09:33] (573.04s)
specific commands such as editing files
[09:35] (575.04s)
or reading files um or even you know it
[09:37] (577.68s)
runs bash for uh using the GitHub CLI um
[09:41] (581.12s)
and so doing all those different things
[09:42] (582.56s)
I think it just helped me understand
[09:44] (584.24s)
cloud code and how it worked better. So
[09:46] (586.40s)
I have here um I've renamed these
[09:49] (589.68s)
various uh sounds to the command. So
[09:52] (592.72s)
here you can see with uh speaking
[09:56] (596.00s)
committing
[09:57] (597.60s)
or I have one for bash
[10:02] (602.16s)
bashing
[10:03] (603.28s)
or testing
[10:06] (606.24s)
running tests.
[10:07] (607.76s)
You go the PR
[10:10] (610.40s)
creating pull request.
[10:12] (612.16s)
See? So uh pretty cool. I I thought that
[10:14] (614.80s)
was great. And I mean you can imagine
[10:16] (616.64s)
what this Python script's doing here. Um
[10:18] (618.64s)
so basically we just play the sound. I
[10:20] (620.88s)
have a little toggle up here, sounds
[10:23] (623.68s)
type, that I can set this to meows or
[10:26] (626.64s)
voices or beeps. Uh, and then I just
[10:29] (629.04s)
have a map uh telling it to map the
[10:31] (631.92s)
different event to the various uh sound
[10:34] (634.88s)
files. Uh, and then it will uh match go
[10:39] (639.12s)
in depth on uh the different bash
[10:40] (640.88s)
commands to figure out which sound it
[10:42] (642.80s)
should play for those. Um, and uh this
[10:45] (645.36s)
was really fun and pretty easy to create
[10:47] (647.20s)
with Claude. And I think that this was
[10:50] (650.16s)
uh a really cool foray into using hooks
[10:54] (654.08s)
because it helped me understand the
[10:55] (655.76s)
different event types by assigning the
[10:57] (657.20s)
different sounds which has then given me
[10:59] (659.36s)
a lot more thoughts about different ways
[11:00] (660.88s)
that I can use hooks. I've seen a lot of
[11:03] (663.52s)
folks use them for instance to um
[11:06] (666.48s)
prevent some of the commands that
[11:08] (668.00s)
they're most concerned about. So for
[11:09] (669.28s)
instance, you could set up a hook to
[11:11] (671.12s)
look for a bash command that's running
[11:15] (675.04s)
for instance and to uh you know have it
[11:17] (677.84s)
not do that. Uh you could um a lot of
[11:21] (681.76s)
folks are using it to run llinters. A
[11:23] (683.84s)
lot of folks are using it to ensure that
[11:26] (686.16s)
the test suite is being run before uh
[11:29] (689.28s)
you open up a pull request. Um, and so
[11:31] (691.44s)
there's just a lot of really interesting
[11:33] (693.60s)
things you can do here that gives you a
[11:35] (695.04s)
lot a little bit more deterministic
[11:36] (696.96s)
control over Cloud Code's behavior. Uh,
[11:40] (700.08s)
but I did find that getting started by
[11:42] (702.24s)
doing the fun thing with sounds was a
[11:44] (704.08s)
great way to get started here. If you
[11:45] (705.60s)
want to see all this code, you can check
[11:46] (706.88s)
out hih high.ai/hooks
[11:49] (709.20s)
and I put the code up there and would
[11:50] (710.80s)
love to hear what you're using hooks