Claude Code Hooks

Turn Claude Code into a self-validating agent. When Claude edits a UI component, Buoy automatically checks for design drift and feeds corrections backβ€”creating a closed loop where the AI catches and fixes its own mistakes.

How It Works

Claude Code supports hooksβ€”shell commands that run at specific points in the AI's workflow. Buoy uses the PostToolUse hook to validate files after Claude writes them:

  1. Claude generates or edits a component file
  2. The hook runs buoy drift check on the modified file
  3. If drift is detected, feedback returns to Claude
  4. Claude self-corrects without you asking

Quick Setup

buoy dock hooks --claude

This creates two files:

  • .claude/hooks/buoy-validate.js β€” The validation script
  • .claude/settings.local.json β€” Hook configuration

What Gets Checked

The hook validates these file types:

  • .tsx, .jsx β€” React components
  • .vue β€” Vue components
  • .svelte β€” Svelte components
  • .component.ts, .component.html β€” Angular components

It skips test files, stories, config files, and type definitions.

Example Feedback

When Claude writes a component with hardcoded values, it sees:

⚠️ Design drift detected in Button.tsx:

β€’ hardcoded-value: Component "Button" has 3 hardcoded colors: #3b82f6, #ffffff, #1e40af

β€’ hardcoded-value: Component "Button" has 2 hardcoded size values: 8px, 16px

Run `buoy show drift` for full details.

Claude then fixes the issues automatically in its next edit.

Manual Setup

If you prefer to set up manually, create .claude/settings.local.json:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/buoy-validate.js\""
          }
        ]
      },
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/buoy-validate.js\""
          }
        ]
      }
    ]
  }
}

Then copy the validation script from the Buoy repository.

Configuration

Adjusting Severity

By default, all drift types trigger feedback. To only report critical issues, modify the hook script's buoy drift check call:

npx ahoybuoy drift check --format json --fail-on critical

Quiet Mode

To reduce noise, use the --quiet flag:

npx ahoybuoy drift check --format json --quiet

Combining with Other Hooks

The Buoy hook works alongside other Claude Code hooks. For example, you might have:

  • SessionStart β€” Load design system context
  • PostToolUse β€” Buoy validation (this hook)
  • PreToolUse β€” Custom approval workflows

Troubleshooting

Hook Not Running

Verify the hook is configured:

cat .claude/settings.local.json | jq '.hooks'

No Feedback Appearing

Test the hook manually:

echo '{"tool_name":"Write","tool_input":{"file_path":"src/Button.tsx"},"cwd":"'$(pwd)'"}' | node .claude/hooks/buoy-validate.js

Permission Errors

Ensure the script is executable:

chmod +x .claude/hooks/buoy-validate.js

How the Script Works

The validation script:

  1. Receives JSON from Claude Code via stdin with the modified file path
  2. Checks if it's a UI component file (by extension)
  3. Runs buoy drift check --format json
  4. Filters results to only the modified file
  5. Returns structured feedback via hookSpecificOutput.additionalContext

The script always exits with code 0β€”it provides feedback without blocking Claude's workflow.

Related