How to Build a Claude Code Mod: A Complete Example (With a Pixel Pet) A developer published a complete four-file example of a Claude Code mod, consisting of a manifest at .claude-plugin/plugin.json, a hooks/hooks.json file, a hooks/register.tsx hooks module, and a types/index.d.ts state contract, that draws an animated 12×8 pixel cat above the prompt and adds a /pet slash command in both the terminal and desktop app. The tutorial, built on the open-source pixel-pet project at github.com/eranltd/pixel-pet, explains that values a drawing reads must live in $.state rather than module variables because a reload re-runs the module and resets its variables while host-held state survives. The same pattern scales to Bit, a pixel puppy that runs a status board for every Claude Code session on the author's machine. A working Claude Code mod in four files: a live band above the prompt, a slash command, and state that survives reloads. Plus the open-source mod I built on top of it. A mock of a running Claude Code session with the pixel-pet mod. Every name and message is made up. If you searched for a Claude Code mod example and found mostly fragments, this is the full thing. By the end you will have a mod that draws an animated pixel cat above your prompt and adds a /pet command. It works in the terminal and in the desktop app. I’ll also show what the same pattern grows into: Bit , a pixel puppy that runs a status board for every Claude Code session on my machine. It is open source at github.com/eranltd/pixel-pet https://github.com/eranltd/pixel-pet . A Claude Code mod is a plugin with a hooks module : a TypeScript file that Claude Code loads into its own interface. It can draw UI above the prompt, open side panes, add slash commands, and react to what the session does: tool calls, prompts, turns starting and ending. Three things make it different from a script you run next to your editor: my-pet/├── .claude-plugin/plugin.json the manifest├── hooks/hooks.json names the hooks module├── hooks/register.tsx the module: hooks, drawing, commands└── types/index.d.ts the state contract .claude-plugin/plugin.json: { "name": "my-pet", "version": "0.1.0", "description": "A pixel cat above the prompt", "types": "./types/index.d.ts"} hooks/hooks.json: { "modules": "./register.tsx" } Values a drawing reads belong in $.state, not in module variables. A reload runs your module again and resets its variables, but $.state is held by the host and survives. Every value is declared under the mod's name. types/index.d.ts: declare module 'claude-code' { interface PluginState { 'my-pet': { frame: number isHidden: boolean } }} Keep this file a plain declaration. An export {} at the end makes the validator miss the contract. hooks/register.tsx is the whole mod: a 12×8 pixel cat that flicks its tail, drawn as a Raster in the terminal and an animated Svg in the desktop app, plus a /pet command that hides it. python import { atom, read, update } from 'claude-code'import type { Register } from 'claude-code' js // One letter per pixel; '.' is transparent. Two frames: the tail flicks.const FRAMES = '..k.....k...', '.kkkkkkkkk..', '.kwkkkkkwk..', '.kkkkpkkkk..', '..kkkkkkkk.k', '..kkkkkkkkk.', '..k.k..k.k..', '............' , '..k.....k...', '.kkkkkkkkk..', '.kwkkkkkwk..', '.kkkkpkkkk..', '..kkkkkkkk..', '..kkkkkkkkkk', '..k.k..k.k..', '............' , const WIDTH = 12const HEIGHT = 8const PALETTE: Record