Type-Safe Claude Code Hooks with Bun and TypeScript

John Lindquist
InstructorJohn Lindquist

Social Share Links

Tweet

Type-safe hooks turn Claude Code's JSON payloads into structured TypeScript. Use Bun to parse stdin, the official SDK for types, and skip the jq gymnastics.

The shell hook problem

Basic hooks work but lack structure:

  • JSON parsing needs extra tools
  • No autocompletion or type checking
  • Complex logic gets messy fast

Bun + TypeScript solution

Set up a typed hook environment:

mkdir .claude/hooks && cd .claude/hooks
bun init
bun i @anthropic-ai/claude-code

Write the hook

Create .claude/hooks/UserPromptSubmit.ts:

import { type UserPromptSubmitHookInput } from "@anthropic-ai/claude-code";

const input = await Bun.stdin.json() as UserPromptSubmitHookInput;
const { cwd, prompt } = input;

// Example: log hook data
await Bun.write(
  ".claude/hooks/last-prompt.json",
  JSON.stringify({ prompt, cwd }, null, 2)
);

Connect it

Update .claude/settings.local.json:

{
  "hooks": {
    "UserPromptSubmit": [{
      "matcher": "*",
      "hooks": [{
        "type": "command",
        "command": "bun run .claude/hooks/UserPromptSubmit.ts"
      }]
    }]
  }
}

Test:

claude
hello world

Why this pattern scales

  • Type safety: SDK types catch errors at write-time
  • One-line parsing: Bun.stdin.json() replaces complex pipes
  • NPM ecosystem: Use any package for advanced automation

[00:00] When you run the slash hooks command, you'll be prompted to create a hook. I'll select the user prompt submit hook, hit enter to add a new hook, and we'll just have our command be echo hello. We'll add this to our local settings. So then if we look at our project, you'll see we now have a clod directory and in that clod directory a settings local json which has hooks, user prompt submit, matching on everything, and assigning the hook to echo hello. Now what we're going to do to make it much easier to consume the input from the hook is create a .clod hooks directory.

[00:38] And in this clod hooks directory I already have bun installed globally, and I'm going to bun init in this directory with a blank project. Now from here I'm going to bun install the anthropic-ai slash clod code and this will bring the clod code SDK into that directory. So if you look at our hooks directory we have a bun project and I'm going to rename this index file to user prompt submit so that it lines up with the name of the hook itself. So now instead of echo hello we're going to bind this to bun run and pass in the path to our user prompt submit file so that anytime we submit a user prompt it triggers this. So now we can play with our user prompt submit file and configure how we handle the input.

[01:27] Now the amazing thing about bun is that it provides us with a bun.standardIn.json method which we can await from top level and get the input from this, meaning that the payload that is coming in when this is called will automatically be an object we can play with. And because we have the Cloud SDK installed in our project we can grab the user prompt submit hook input type and cast this JSON as that input, which means that we now have the type information if we want to grab something like the current working directory, or we want to grab the prompt that was sent in, we can destructure that from the input and then just as a demo we'll use bun.write to write a file and we'll just call this file user.prompt.submit and we'll just read JSON stringify this and we'll put together a message like message is the prompt was prompt in the current working directory and then jump into json stringify and change this message. Let's word wrap in here so you can see everything. So now every time the user submits a message in Clawd it will invoke this hook, we'll get the input, we'll rip out the current working directory in the prompt, and then write it to a file.

[02:40] Now if we just go back up to our project root, our Clawd hook button project, invoke Clawd, I'll say hello world, hit enter, and you'll see this JSON file get generated. The prompt was hello world in this directory.