<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Lukas Fryc</title>
    <description>The latest articles on Forem by Lukas Fryc (@lukaszfryc).</description>
    <link>https://forem.com/lukaszfryc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F188385%2F26724dde-fc8e-4151-9e33-5e59ecbfa422.png</url>
      <title>Forem: Lukas Fryc</title>
      <link>https://forem.com/lukaszfryc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lukaszfryc"/>
    <language>en</language>
    <item>
      <title>Claude Code Hooks: Complete Guide with 20+ Ready-to-Use Examples (2026)</title>
      <dc:creator>Lukas Fryc</dc:creator>
      <pubDate>Mon, 09 Feb 2026 12:41:32 +0000</pubDate>
      <link>https://forem.com/lukaszfryc/claude-code-hooks-complete-guide-with-20-ready-to-use-examples-2026-dcg</link>
      <guid>https://forem.com/lukaszfryc/claude-code-hooks-complete-guide-with-20-ready-to-use-examples-2026-dcg</guid>
      <description>&lt;p&gt;&lt;strong&gt;Claude Code hooks&lt;/strong&gt; are shell commands, LLM prompts, or subagents that execute automatically at specific points in Claude Code's lifecycle. They let you enforce formatting on every file write, block dangerous commands before they run, inject context after compaction, and automate any repetitive workflow — without relying on the AI to remember.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaway:&lt;/strong&gt; Hooks give you &lt;em&gt;deterministic&lt;/em&gt; control over a &lt;em&gt;probabilistic&lt;/em&gt; system. Instead of hoping Claude Code remembers to lint your files, a PostToolUse hook runs Prettier automatically, every single time.&lt;/p&gt;

&lt;p&gt;This guide covers everything: setup, all 12 hook events, 20+ ready-to-use configurations, advanced prompt/agent hooks, and troubleshooting. Whether you've never configured a hook or want to build production-grade automation, start here.&lt;/p&gt;

&lt;p&gt;New to Claude Code? Start with &lt;a href="https://aiorg.dev/blog/claude-code-best-practices" rel="noopener noreferrer"&gt;Claude Code best practices&lt;/a&gt; first — hooks build on top of a solid CLAUDE.md setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What hooks are and why they matter&lt;/li&gt;
&lt;li&gt;Your first hook in 2 minutes&lt;/li&gt;
&lt;li&gt;All 12 hook events explained&lt;/li&gt;
&lt;li&gt;20+ ready-to-use configurations&lt;/li&gt;
&lt;li&gt;Advanced: prompt hooks and agent hooks&lt;/li&gt;
&lt;li&gt;When to use hooks vs. CLAUDE.md vs. MCP&lt;/li&gt;
&lt;li&gt;Troubleshooting common issues&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What Are Claude Code Hooks?
&lt;/h2&gt;

&lt;p&gt;Every Claude Code session follows a lifecycle: session starts, user prompts, tools execute, agent responds. Hooks let you inject your own code at any point in that cycle.&lt;/p&gt;

&lt;p&gt;Think of them like Git hooks (&lt;code&gt;pre-commit&lt;/code&gt;, &lt;code&gt;post-merge&lt;/code&gt;), but for AI-assisted development. Git hooks run before/after Git operations. Claude Code hooks run before/after &lt;em&gt;any&lt;/em&gt; Claude Code action — file writes, bash commands, even agent decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why not just put instructions in CLAUDE.md?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CLAUDE.md is a suggestion. Claude Code &lt;em&gt;usually&lt;/em&gt; follows it, but it's not guaranteed. Hooks are deterministic — they &lt;em&gt;always&lt;/em&gt; run. Use CLAUDE.md for guidelines ("prefer Bun over npm"). Use hooks for rules that must never be broken ("always format with Prettier", "never touch .env files").&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three types of hooks:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Command&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs a shell script&lt;/td&gt;
&lt;td&gt;Formatting, linting, logging, security checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Asks an LLM a yes/no question&lt;/td&gt;
&lt;td&gt;Complex decisions that shell scripts can't handle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spawns a multi-turn subagent&lt;/td&gt;
&lt;td&gt;Verification tasks requiring file reads + command execution&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Your First Hook: Auto-Format Every File Edit
&lt;/h2&gt;

&lt;p&gt;Let's set up a hook that runs Prettier on every file Claude Code writes or edits. Two minutes, zero complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Open your project's hook settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Project-level (shared with team via git)&lt;/span&gt;
.claude/settings.json

&lt;span class="c"&gt;# Or user-level (applies to all projects)&lt;/span&gt;
~/.claude/settings.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Add this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jq -r '.tool_input.file_path' | xargs npx prettier --write 2&amp;gt;/dev/null; exit 0"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; That's it. Every file Claude Code writes or edits now gets auto-formatted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's happening:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Claude Code writes a file (triggers &lt;code&gt;PostToolUse&lt;/code&gt; for &lt;code&gt;Write&lt;/code&gt; or &lt;code&gt;Edit&lt;/code&gt; tools)&lt;/li&gt;
&lt;li&gt;Hook receives JSON with the file path via stdin&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jq&lt;/code&gt; extracts the path, Prettier formats the file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exit 0&lt;/code&gt; ensures the hook never blocks Claude Code (even if Prettier fails on a non-supported file)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Verify it works:&lt;/strong&gt; Ask Claude Code to create any file. Check git diff — you'll see Prettier's formatting applied automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Hooks Work: Input, Output, Exit Codes
&lt;/h2&gt;

&lt;p&gt;Every hook receives JSON context on stdin and communicates through exit codes and stdout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; JSON describing the current event. For a &lt;code&gt;PreToolUse&lt;/code&gt; Bash hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"session_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cwd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/your/project"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hook_event_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tool_input"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rm -rf node_modules"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Exit codes determine what happens:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exit code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;td&gt;Action proceeds. Stdout parsed as JSON or added as context.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Block&lt;/td&gt;
&lt;td&gt;Action is blocked. Stderr shown as feedback.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Other&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Non-blocking error&lt;/td&gt;
&lt;td&gt;Action proceeds. Stderr shown in verbose mode.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;JSON output&lt;/strong&gt; (optional, on stdout) controls behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hookSpecificOutput"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hookEventName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"permissionDecision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"permissionDecisionReason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This command is blocked by project policy"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  All 12 Hook Events
&lt;/h2&gt;

&lt;p&gt;Claude Code has 12 lifecycle events where hooks can fire. Here's the complete reference, grouped by category.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session Lifecycle
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;Can block?&lt;/th&gt;
&lt;th&gt;Matcher values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SessionStart&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Session begins or resumes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;startup&lt;/code&gt;, &lt;code&gt;resume&lt;/code&gt;, &lt;code&gt;compact&lt;/code&gt;, &lt;code&gt;clear&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PreCompact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Before context compaction&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;manual&lt;/code&gt;, &lt;code&gt;auto&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SessionEnd&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Session terminates&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;clear&lt;/code&gt;, &lt;code&gt;logout&lt;/code&gt;, &lt;code&gt;other&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;SessionStart&lt;/strong&gt; is particularly useful for injecting dynamic context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SessionStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"compact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo 'Reminder: Use Bun, not npm. Current sprint: auth refactor. Run bun test before committing.'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This injects reminders after every compaction — so Claude Code never forgets your project context, even in long sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Use &lt;code&gt;SessionStart&lt;/code&gt; with the &lt;code&gt;startup&lt;/code&gt; matcher to set environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# .claude/hooks/set-env.sh&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLAUDE_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export NODE_ENV=development"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLAUDE_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export DEBUG=true"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLAUDE_ENV_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tool Lifecycle
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;Can block?&lt;/th&gt;
&lt;th&gt;Matcher values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PreToolUse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Before tool executes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Tool name: &lt;code&gt;Bash&lt;/code&gt;, &lt;code&gt;Edit&lt;/code&gt;, &lt;code&gt;Write&lt;/code&gt;, &lt;code&gt;Read&lt;/code&gt;, &lt;code&gt;Glob&lt;/code&gt;, &lt;code&gt;Grep&lt;/code&gt;, &lt;code&gt;WebFetch&lt;/code&gt;, &lt;code&gt;mcp__*&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PostToolUse&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;After tool succeeds&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Same as PreToolUse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PostToolUseFailure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;After tool fails&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Same as PreToolUse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PermissionRequest&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Permission dialog appears&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Same as PreToolUse&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;PreToolUse&lt;/strong&gt; is the workhorse. Use it to block, modify, or auto-approve tool calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hookSpecificOutput"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hookEventName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"permissionDecision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"permissionDecisionReason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Auto-approved by project policy"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three permission decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"allow"&lt;/code&gt; — bypass permission system, auto-approve&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"deny"&lt;/code&gt; — block the tool call&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"ask"&lt;/code&gt; — show the normal permission prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;PostToolUse&lt;/strong&gt; is great for reactions: formatting, logging, running tests. It can't block (the action already happened), but it can give Claude Code feedback that influences its next steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Lifecycle
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;Can block?&lt;/th&gt;
&lt;th&gt;Matcher values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SubagentStart&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subagent spawns&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Agent type: &lt;code&gt;Bash&lt;/code&gt;, &lt;code&gt;Explore&lt;/code&gt;, &lt;code&gt;Plan&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SubagentStop&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subagent finishes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Same as SubagentStart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stop&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Main agent finishes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;None (always fires)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Stop&lt;/strong&gt; hooks are powerful — they can prevent Claude Code from finishing until conditions are met:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$CLAUDE_PROJECT_DIR/.claude/hooks/verify-tests.sh"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; Always check &lt;code&gt;stop_hook_active&lt;/code&gt; to prevent infinite loops:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.stop_hook_active'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0  &lt;span class="c"&gt;# Let Claude stop — we already verified&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="c"&gt;# Your verification logic here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  User Interaction
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;Can block?&lt;/th&gt;
&lt;th&gt;Matcher values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UserPromptSubmit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User submits a prompt&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;None (always fires)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Notification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Claude sends notification&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;permission_prompt&lt;/code&gt;, &lt;code&gt;idle_prompt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;UserPromptSubmit&lt;/strong&gt; can validate, transform, or block user prompts before Claude Code processes them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ready-to-Use Hook Configurations
&lt;/h2&gt;

&lt;p&gt;Copy-paste these into your &lt;code&gt;.claude/settings.json&lt;/code&gt;. Each one solves a real workflow problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security and Protection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Block destructive commands&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'CMD=$(jq -r &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.tool_input.command&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;&amp;lt;&amp;lt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(cat)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;); for p in &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rm -rf /&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;rm -rf ~&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;drop table&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;DROP TABLE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;truncate&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;TRUNCATE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;--force&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;push.*--force&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; do if echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CMD&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -qiE &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$p&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; then echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Blocked: pattern &lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;$p&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt; detected&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;&amp;amp;2; exit 2; fi; done; exit 0'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Protect sensitive files from writes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Save as &lt;code&gt;.claude/hooks/protect-files.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.file_path // empty'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;PROTECTED&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="s2"&gt;".env.local"&lt;/span&gt; &lt;span class="s2"&gt;"secrets/"&lt;/span&gt; &lt;span class="s2"&gt;".git/"&lt;/span&gt; &lt;span class="s2"&gt;"package-lock.json"&lt;/span&gt; &lt;span class="s2"&gt;"pnpm-lock.yaml"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;pattern &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROTECTED&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Protected file: &lt;/span&gt;&lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;2
  &lt;span class="k"&gt;fi
done
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/hooks/protect-files.sh"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Audit log all bash commands&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'jq -r &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.tool_input.command&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;&amp;lt;&amp;lt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(cat)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | while read cmd; do echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(date +%Y-%m-%dT%H:%M:%S) $cmd&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/command-audit.log; done; exit 0'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Code Quality
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;4. Auto-format with Prettier (already shown above)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Auto-lint with ESLint and fix&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'FILE=$(jq -r &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.tool_input.file_path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;&amp;lt;&amp;lt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(cat)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;); if [[ &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; == *.ts || &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; == *.tsx || &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; == *.js || &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; == *.jsx ]]; then npx eslint --fix &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;/dev/null; fi; exit 0'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Run type checking after TypeScript changes&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'FILE=$(jq -r &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.tool_input.file_path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;&amp;lt;&amp;lt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(cat)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;); if [[ &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; == *.ts || &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; == *.tsx ]]; then npx tsc --noEmit 2&amp;gt;&amp;amp;1 | head -20; fi; exit 0'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Enforce test coverage — prevent stopping without tests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Save as &lt;code&gt;.claude/hooks/verify-tests.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Prevent infinite loop&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.stop_hook_active'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Run tests&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--silent&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Tests are failing. Fix them before finishing."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/hooks/verify-tests.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Workflow Automation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;8. Inject context after compaction&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SessionStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"compact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Post-compaction context: Use Bun (not npm). Run bun test before committing. Current branch: $(git -C &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; branch --show-current 2&amp;gt;/dev/null || echo unknown). Last commit: $(git -C &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; log --oneline -1 2&amp;gt;/dev/null || echo none).&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;9. Set environment variables on session start&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SessionStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"startup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'if [ -n &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_ENV_FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; ]; then echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;export NODE_ENV=development&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_ENV_FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;export NEXT_TELEMETRY_DISABLED=1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_ENV_FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi; exit 0'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;10. Auto-run tests when test files change&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'FILE=$(jq -r &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.tool_input.file_path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;&amp;lt;&amp;lt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(cat)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;); if [[ &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; == *.test.* || &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; == *.spec.* ]]; then npx vitest run &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$FILE&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; 2&amp;gt;&amp;amp;1 | tail -5; fi; exit 0'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"async"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;11. Notify on branch switch (inject branch context)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SessionStart"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"startup|resume"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'BRANCH=$(git -C &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; branch --show-current 2&amp;gt;/dev/null); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Current branch: $BRANCH. Recent commits: $(git -C &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; log --oneline -3 2&amp;gt;/dev/null)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Notifications
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;12. Desktop notification on permission request (macOS)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Notification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission_prompt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"osascript -e 'display notification &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Claude Code needs your input&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; with title &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Claude Code&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; sound name &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Ping&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;13. Desktop notification on task complete (macOS)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Notification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"idle_prompt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"osascript -e 'display notification &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Task complete — ready for next instruction&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; with title &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Claude Code&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; sound name &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Glass&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;14. Linux notifications&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Notification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"permission_prompt|idle_prompt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notify-send 'Claude Code' 'Needs your attention' --urgency=normal"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  MCP Tool Hooks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;15. Log all MCP operations&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp__.*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'INPUT=$(cat); TOOL=$(echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$INPUT&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | jq -r &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;.tool_name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$(date +%H:%M:%S) $TOOL&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/mcp-audit.log; exit 0'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;16. Rate-limit specific MCP tools&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# .claude/hooks/rate-limit-mcp.sh&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;TOOL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_name'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;LOGFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="s2"&gt;/.claude/mcp-rate.log"&lt;/span&gt;

&lt;span class="c"&gt;# Count calls in last 60 seconds&lt;/span&gt;
&lt;span class="nv"&gt;RECENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TOOL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOGFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;0&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$TOOL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOGFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RECENT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 10 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Rate limit: &lt;/span&gt;&lt;span class="nv"&gt;$TOOL&lt;/span&gt;&lt;span class="s2"&gt; called &lt;/span&gt;&lt;span class="nv"&gt;$RECENT&lt;/span&gt;&lt;span class="s2"&gt; times in the last minute"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom Permission Policies
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;17. Auto-approve WebSearch and WebFetch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tired of approving every domain one by one? This hook eliminates the nagging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WebFetch|WebSearch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo '{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;decision&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;allow&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WebSearch and WebFetch are read-only — auto-approving them is safe. See &lt;a href="https://aiorg.dev/blog/claude-code-stop-websearch-permission-prompts" rel="noopener noreferrer"&gt;the full writeup&lt;/a&gt; for why this works and how to clean up your existing domain allowlist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;18. Auto-approve safe read operations&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read|Glob|Grep"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;{&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;hookSpecificOutput&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;:{&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;hookEventName&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;PreToolUse&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;permissionDecision&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;allow&lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;}}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;19. Deny web access in offline mode&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WebFetch|WebSearch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bash -c 'echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Web access disabled by project policy&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;&amp;amp;2; exit 2'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced: Prompt Hooks and Agent Hooks
&lt;/h2&gt;

&lt;p&gt;Most articles only cover command hooks (shell scripts). But Claude Code supports two more powerful types that almost nobody talks about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt Hooks: LLM-Powered Decisions
&lt;/h3&gt;

&lt;p&gt;When your validation logic is too complex for a bash script, let an LLM decide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Review the conversation. Did the user's request get fully completed? Check: all files created, tests passing, no TODO comments left. Respond with {&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true} if done, or {&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: false, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;reason&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;what remains&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;} if not."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The LLM must respond with &lt;code&gt;{"ok": true}&lt;/code&gt; or &lt;code&gt;{"ok": false, "reason": "..."}&lt;/code&gt;. If &lt;code&gt;ok&lt;/code&gt; is false, Claude Code continues working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use prompt hooks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code review quality gates ("does this follow our patterns?")&lt;/li&gt;
&lt;li&gt;Semantic validation ("is this commit message descriptive enough?")&lt;/li&gt;
&lt;li&gt;Complex decision-making that can't be reduced to grep/regex&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Agent Hooks: Multi-Turn Verification
&lt;/h3&gt;

&lt;p&gt;Agent hooks can read files, run commands, and make multi-step decisions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Verify the work is complete: 1) Run the test suite. 2) Check for any TypeScript errors. 3) Verify no console.log statements were left in production code. Report your findings. $ARGUMENTS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agent hooks can use up to 50 tool turns — they can read files, grep for patterns, run bash commands, and make complex assessments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use agent hooks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;End-of-task verification (tests pass, types check, no debug code)&lt;/li&gt;
&lt;li&gt;Multi-file consistency checks&lt;/li&gt;
&lt;li&gt;Complex pre-deployment validations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Combining All Three Types
&lt;/h3&gt;

&lt;p&gt;A production-grade setup might layer all three:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jq -r '.tool_input.file_path' | xargs npx prettier --write 2&amp;gt;/dev/null; exit 0"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/hooks/verify-tests.sh"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Check if the implementation matches what the user asked for. Are there any edge cases missed? Respond with {&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: true} or {&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: false, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;reason&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Command hooks handle the deterministic stuff (formatting). Prompt hooks handle the semantic stuff (does this match the request?). Each runs in sequence — if the command hook blocks (exit 2), the prompt hook doesn't run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks vs. CLAUDE.md vs. MCP: When to Use What
&lt;/h2&gt;

&lt;p&gt;Claude Code has several extension points. Here's when to use each one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Always format files on save"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Hook&lt;/strong&gt; (PostToolUse)&lt;/td&gt;
&lt;td&gt;Must happen every time, no exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Prefer Bun over npm"&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A preference, not a hard rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Never modify .env files"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Hook&lt;/strong&gt; (PreToolUse)&lt;/td&gt;
&lt;td&gt;Hard block, not a suggestion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Our API routes follow this pattern"&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.claude/rules/&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Contextual guidance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Run /deploy to ship"&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Custom command&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reusable workflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Access our Jira board"&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;MCP server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;External service integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Log every command to audit file"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Hook&lt;/strong&gt; (PostToolUse)&lt;/td&gt;
&lt;td&gt;Side effect, transparent to Claude&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Verify tests pass before stopping"&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Hook&lt;/strong&gt; (Stop)&lt;/td&gt;
&lt;td&gt;Enforcement gate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; If it's a &lt;em&gt;suggestion&lt;/em&gt;, use CLAUDE.md. If it's a &lt;em&gt;requirement&lt;/em&gt;, use hooks. If it's an &lt;em&gt;external service&lt;/em&gt;, use MCP. If it's a &lt;em&gt;reusable workflow&lt;/em&gt;, use custom commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  "My hook isn't firing"
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check configuration loaded:&lt;/strong&gt; Run &lt;code&gt;/hooks&lt;/code&gt; in Claude Code to see active hooks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matcher is case-sensitive:&lt;/strong&gt; &lt;code&gt;bash&lt;/code&gt; won't match &lt;code&gt;Bash&lt;/code&gt;. Tool names are PascalCase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong event:&lt;/strong&gt; &lt;code&gt;PostToolUse&lt;/code&gt; fires after success only. For failures, use &lt;code&gt;PostToolUseFailure&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File permissions:&lt;/strong&gt; Ensure your script is executable: &lt;code&gt;chmod +x .claude/hooks/your-script.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  "JSON validation failed" error
&lt;/h3&gt;

&lt;p&gt;Your shell profile (&lt;code&gt;.bashrc&lt;/code&gt;, &lt;code&gt;.zshrc&lt;/code&gt;) is printing output that corrupts the JSON. Fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# In your .bashrc/.zshrc — wrap interactive-only output&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$-&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;i&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Welcome!"&lt;/span&gt;  &lt;span class="c"&gt;# Only runs in interactive shells&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  "Hook blocks everything" (infinite Stop loop)
&lt;/h3&gt;

&lt;p&gt;Your Stop hook keeps blocking Claude Code from finishing. Always check &lt;code&gt;stop_hook_active&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.stop_hook_active'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0  &lt;span class="c"&gt;# Allow stop — we already had our chance&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="c"&gt;# Your logic here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  "jq: command not found"
&lt;/h3&gt;

&lt;p&gt;Install jq — it's required for parsing JSON input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;jq

&lt;span class="c"&gt;# Ubuntu/Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;jq

&lt;span class="c"&gt;# Or use Python as fallback&lt;/span&gt;
python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import sys,json; print(json.load(sys.stdin)['tool_input']['file_path'])"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing hooks manually
&lt;/h3&gt;

&lt;p&gt;Don't guess — test directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Simulate a Bash PreToolUse event&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"tool_name":"Bash","tool_input":{"command":"rm -rf /"}}'&lt;/span&gt; | ./.claude/hooks/block-dangerous.sh
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Exit code: &lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Expected: exit code 2 (blocked)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Viewing hook execution in real-time
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start Claude Code with debug output&lt;/span&gt;
claude &lt;span class="nt"&gt;--debug&lt;/span&gt;

&lt;span class="c"&gt;# Or toggle verbose mode during a session&lt;/span&gt;
&lt;span class="c"&gt;# Press Ctrl+O&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What are Claude Code hooks?
&lt;/h3&gt;

&lt;p&gt;Hooks are user-defined shell commands, LLM prompts, or subagents that run automatically at specific points in Claude Code's lifecycle. They provide deterministic control — ensuring actions like formatting, linting, or security checks always happen, rather than relying on the LLM to remember.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where do I configure Claude Code hooks?
&lt;/h3&gt;

&lt;p&gt;In JSON settings files at three levels: &lt;code&gt;~/.claude/settings.json&lt;/code&gt; (all projects), &lt;code&gt;.claude/settings.json&lt;/code&gt; (single project, shareable via git), and &lt;code&gt;.claude/settings.local.json&lt;/code&gt; (project-specific, gitignored). Use the &lt;code&gt;/hooks&lt;/code&gt; menu in Claude Code to view and manage your hooks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can hooks block dangerous commands?
&lt;/h3&gt;

&lt;p&gt;Yes. &lt;code&gt;PreToolUse&lt;/code&gt; hooks can block any tool call by returning exit code 2 or outputting a JSON decision with &lt;code&gt;permissionDecision&lt;/code&gt; set to &lt;code&gt;"deny"&lt;/code&gt;. This is commonly used to prevent destructive commands like &lt;code&gt;rm -rf&lt;/code&gt;, &lt;code&gt;DROP TABLE&lt;/code&gt;, or force pushes.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the difference between command, prompt, and agent hooks?
&lt;/h3&gt;

&lt;p&gt;Command hooks run shell scripts — best for deterministic tasks like formatting or logging. Prompt hooks use an LLM for yes/no decisions when logic is too complex for shell scripts. Agent hooks spawn a multi-turn subagent that can read files, run commands, and verify complex conditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do hooks slow down Claude Code?
&lt;/h3&gt;

&lt;p&gt;Command hooks add minimal overhead — typically milliseconds. Prompt and agent hooks are slower (they call the LLM), but you can set timeouts. Use &lt;code&gt;async: true&lt;/code&gt; for long-running hooks that don't need to block the current action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use hooks with MCP tools?
&lt;/h3&gt;

&lt;p&gt;Yes. Use regex matchers like &lt;code&gt;mcp__github__.*&lt;/code&gt; to target specific MCP server tools, or &lt;code&gt;mcp__.*&lt;/code&gt; to match all MCP tools.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Lukas — I build AI-powered dev tools for solo founders. More on &lt;a href="https://aiorg.dev/blog" rel="noopener noreferrer"&gt;Claude Code workflows and AI-native development&lt;/a&gt; on my blog.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>automation</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Cursor vs Claude Code: Which Ships Faster? (2026)</title>
      <dc:creator>Lukas Fryc</dc:creator>
      <pubDate>Mon, 09 Feb 2026 10:00:16 +0000</pubDate>
      <link>https://forem.com/lukaszfryc/cursor-vs-claude-code-which-ships-faster-2026-79j</link>
      <guid>https://forem.com/lukaszfryc/cursor-vs-claude-code-which-ships-faster-2026-79j</guid>
      <description>&lt;p&gt;&lt;strong&gt;Cursor vs Claude Code&lt;/strong&gt; — which one should you actually use? The short answer: use Claude Code for building features and large changes. Use Cursor for editing, reviewing, and exploring code. Use both if you can afford it.&lt;/p&gt;

&lt;p&gt;I've been shipping production code with both tools for over a year. Not testing them on toy projects — running 6 real SaaS products simultaneously. Here's what I've learned about when each tool shines and where it falls short.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Cursor?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cursor&lt;/strong&gt; is a code editor built as a fork of VS Code with AI deeply integrated into the editing experience. You write code in a familiar IDE, and AI assists with autocomplete, inline edits, and chat-based code generation.&lt;/p&gt;

&lt;p&gt;Think of Cursor as VS Code with an AI copilot sitting next to you. You're still driving — the AI helps you type faster, understand code, and catch mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Claude Code?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Claude Code&lt;/strong&gt; is Anthropic's terminal-based AI coding agent. Instead of sitting inside an editor, it runs as a command-line tool that autonomously reads your codebase, plans changes, writes code across multiple files, runs tests, and iterates until the task is done.&lt;/p&gt;

&lt;p&gt;Think of Claude Code as an autonomous developer you delegate tasks to. You describe what you need, and it plans the approach and executes across your entire project.&lt;/p&gt;

&lt;p&gt;This distinction — &lt;strong&gt;AI-enhanced editor vs. autonomous coding agent&lt;/strong&gt; — is the fundamental difference that everything else flows from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Side-by-Side Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Cursor&lt;/th&gt;
&lt;th&gt;Claude Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;VS Code (GUI)&lt;/td&gt;
&lt;td&gt;Terminal (CLI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple (GPT-4o, Claude, etc.)&lt;/td&gt;
&lt;td&gt;Claude only (Anthropic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Workflow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You write, AI assists&lt;/td&gt;
&lt;td&gt;AI writes, you review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Autocomplete&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast Tab completion&lt;/td&gt;
&lt;td&gt;No autocomplete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-file edits&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One file at a time (mostly)&lt;/td&gt;
&lt;td&gt;Plans across entire codebase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Autonomous execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited agent mode&lt;/td&gt;
&lt;td&gt;Full autonomy — runs tests, fixes errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Project memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Per-session context&lt;/td&gt;
&lt;td&gt;CLAUDE.md + persistent rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;VS Code extensions&lt;/td&gt;
&lt;td&gt;Custom commands, skills, kits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Price&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$20/mo (Pro)&lt;/td&gt;
&lt;td&gt;$100–$200/mo (Max)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Editing, exploring, small changes&lt;/td&gt;
&lt;td&gt;Building, refactoring, debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When Cursor Wins
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Inline Edits and Autocomplete
&lt;/h3&gt;

&lt;p&gt;Cursor's Tab completion is genuinely fast. You start typing, it predicts the next 5–10 lines, and you tab through. For writing boilerplate, implementing interfaces, or filling in repetitive patterns, nothing beats it.&lt;/p&gt;

&lt;p&gt;Claude Code doesn't do autocomplete. It's not trying to. Different tool, different job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Exploration
&lt;/h3&gt;

&lt;p&gt;When you're reading unfamiliar code, Cursor lets you highlight a function and ask "what does this do?" with full IDE context. You see the answer inline, next to the code. The visual feedback loop is tight.&lt;/p&gt;

&lt;p&gt;With Claude Code, you'd ask the same question in the terminal. It works, but you're mentally switching between terminal and editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Small, Precise Edits
&lt;/h3&gt;

&lt;p&gt;Need to rename a variable in 3 files? Fix a typo in a CSS class? Change an error message? Cursor handles these in seconds with Cmd+K inline edits.&lt;/p&gt;

&lt;p&gt;Claude Code can do it too, but it's overkill. You're launching an agent to change one word.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visual Diffs and Code Review
&lt;/h3&gt;

&lt;p&gt;Cursor shows AI-generated changes as inline diffs. You see exactly what changed, highlighted in your editor with full surrounding context. For reviewing generated code, this visual feedback helps you catch issues faster than scanning terminal output.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Claude Code Wins
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Multi-File Features
&lt;/h3&gt;

&lt;p&gt;"Add authentication with magic links, create the API routes, update the database schema, add the UI components, and write tests."&lt;/p&gt;

&lt;p&gt;Cursor would need you to do this file by file, prompting in each one. Claude Code takes the whole task, plans it, and executes across 10–15 files in one shot. It reads your existing code, understands your patterns, and follows them.&lt;/p&gt;

&lt;p&gt;This is where the productivity gap is massive. What takes 45 minutes of back-and-forth in Cursor takes one prompt in Claude Code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refactoring at Scale
&lt;/h3&gt;

&lt;p&gt;"Migrate all API routes from Pages Router to App Router." "Convert all class components to hooks." "Replace Moment.js with date-fns across the entire codebase."&lt;/p&gt;

&lt;p&gt;These are Claude Code's bread and butter. It reads the full codebase, understands the patterns, and makes consistent changes everywhere. In Cursor, you'd be doing this file by file, hoping you don't miss one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complex Debugging
&lt;/h3&gt;

&lt;p&gt;When a bug spans multiple files — a state management issue that touches the store, a component, and an API route — Claude Code can read all relevant files, form a hypothesis, and fix it. It follows the data flow across your entire project.&lt;/p&gt;

&lt;p&gt;Cursor gives you AI in one file at a time. You can reference other files, but you're still driving the investigation manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Autonomous Iteration
&lt;/h3&gt;

&lt;p&gt;Claude Code runs your tests, sees failures, and fixes them. It runs the linter, fixes the warnings. It builds the project, catches type errors, and resolves them. This loop happens automatically.&lt;/p&gt;

&lt;p&gt;In Cursor, you run the test, copy the error, paste it into chat, wait for a fix, apply it, run the test again. Each cycle is 30–60 seconds of manual work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Persistent Project Context
&lt;/h3&gt;

&lt;p&gt;Claude Code reads your &lt;code&gt;CLAUDE.md&lt;/code&gt; file and &lt;code&gt;.claude/rules/&lt;/code&gt; directory at the start of every session. It remembers your architecture, coding standards, and project rules without you re-explaining anything.&lt;/p&gt;

&lt;p&gt;This is the biggest productivity unlock for projects you work on daily. Configure it once, and Claude Code follows your patterns consistently forever. For a full guide on how to set this up properly, see my &lt;a href="https://aiorg.dev/blog/claude-code-best-practices" rel="noopener noreferrer"&gt;15 Claude Code best practices&lt;/a&gt; — especially tips #1–4 on project setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing: The Real Math
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free:&lt;/strong&gt; 2,000 completions + 50 slow premium requests/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro ($20/mo):&lt;/strong&gt; Unlimited completions + 500 fast premium requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business ($40/mo):&lt;/strong&gt; Admin controls, enforced privacy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Max Plan ($100/mo):&lt;/strong&gt; 5x usage cap — good for moderate daily use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Max Plan ($200/mo):&lt;/strong&gt; 20x usage cap — heavy daily use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Direct:&lt;/strong&gt; Pay per token — no cap, but costs add up fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reality check:&lt;/strong&gt; If you're coding 4–6 hours daily with Claude Code, the $200/month plan is the sweet spot. Cursor Pro at $20/month is 10x cheaper, but you'll hit the 500 request limit in 2–3 days of heavy use.&lt;/p&gt;

&lt;p&gt;For solo founders on a budget: start with Cursor Pro. When you're shipping features and need speed over savings, add Claude Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Both" Workflow
&lt;/h2&gt;

&lt;p&gt;Most productive developers I know use both. Here's the pattern:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Code for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starting new features from scratch&lt;/li&gt;
&lt;li&gt;Large refactors across multiple files&lt;/li&gt;
&lt;li&gt;Complex debugging sessions&lt;/li&gt;
&lt;li&gt;Setting up infrastructure (CI/CD, configs, deployments)&lt;/li&gt;
&lt;li&gt;Writing tests for existing code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cursor for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Day-to-day editing and code review&lt;/li&gt;
&lt;li&gt;Quick fixes and small changes&lt;/li&gt;
&lt;li&gt;Reading and understanding unfamiliar code&lt;/li&gt;
&lt;li&gt;Inline documentation and comments&lt;/li&gt;
&lt;li&gt;Pair programming (you + AI side by side)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A typical day looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Morning: Open terminal, describe today's feature to Claude Code&lt;/li&gt;
&lt;li&gt;Claude Code builds the scaffolding across 10+ files&lt;/li&gt;
&lt;li&gt;Open Cursor to review, tweak, and polish the generated code&lt;/li&gt;
&lt;li&gt;Use Claude Code again for the next big chunk&lt;/li&gt;
&lt;li&gt;Cursor for final cleanup and small fixes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What Makes Claude Code Different
&lt;/h2&gt;

&lt;p&gt;Beyond the terminal-vs-IDE distinction, Claude Code has properties that set it apart from any IDE-based AI tool:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Persistent context.&lt;/strong&gt; Claude Code reads your &lt;code&gt;.claude/&lt;/code&gt; directory — project rules, coding standards, architecture decisions. Every session starts with full project understanding, not from zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool use.&lt;/strong&gt; It runs shell commands, reads files, searches codebases, runs tests. It's not just generating text — it's executing a workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Planning ability.&lt;/strong&gt; For complex tasks, Claude Code creates a plan, breaks it into steps, and executes them in order. Cursor's agent mode does some of this, but Claude Code's planning is more thorough for multi-step tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extensibility.&lt;/strong&gt; You can add custom commands, skills, and knowledge files that change how Claude Code works for your specific project. I've built domain-specific kits that turn Claude Code into an expert for &lt;a href="https://aiorg.dev/blog/claude-code-best-practices" rel="noopener noreferrer"&gt;marketing&lt;/a&gt;, QA, product management, and more — complete with specialized commands and workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;There's no single "better" tool. But here's the decision tree:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose Cursor if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want AI to enhance your existing editing workflow&lt;/li&gt;
&lt;li&gt;You prefer visual feedback and inline suggestions&lt;/li&gt;
&lt;li&gt;Budget is tight ($20/month vs $100–$200/month)&lt;/li&gt;
&lt;li&gt;You work mostly on small-to-medium changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose Claude Code if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want AI to build entire features autonomously&lt;/li&gt;
&lt;li&gt;You're comfortable working in the terminal&lt;/li&gt;
&lt;li&gt;You value speed over cost&lt;/li&gt;
&lt;li&gt;You frequently do multi-file changes and refactors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose both if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You ship code daily and want maximum productivity&lt;/li&gt;
&lt;li&gt;You're a solo founder wearing multiple hats&lt;/li&gt;
&lt;li&gt;Different tasks genuinely need different tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tools aren't competing for the same job. Cursor makes you a faster editor. Claude Code gives you an autonomous engineering partner. The best setup is having both in your toolkit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's your setup? Cursor, Claude Code, or both?&lt;/strong&gt; I'm curious how other developers split the work between them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Lukas — I run 6 SaaS projects solo with Claude Code. I write about &lt;a href="https://aiorg.dev/blog" rel="noopener noreferrer"&gt;AI-native development workflows&lt;/a&gt; on my blog.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>cursor</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Replaced My Marketing Team with Claude Code</title>
      <dc:creator>Lukas Fryc</dc:creator>
      <pubDate>Mon, 09 Feb 2026 09:57:41 +0000</pubDate>
      <link>https://forem.com/lukaszfryc/i-replaced-my-marketing-team-with-claude-code-2k6k</link>
      <guid>https://forem.com/lukaszfryc/i-replaced-my-marketing-team-with-claude-code-2k6k</guid>
      <description>&lt;p&gt;I don't have a marketing team. I have 49 slash commands.&lt;/p&gt;

&lt;p&gt;I run 6 SaaS projects solo. I'm a developer — 15 years of writing code. Marketing? I couldn't tell you the difference between TOFU and tofu until my AI explained it. (Top-of-funnel, apparently.)&lt;/p&gt;

&lt;p&gt;But my blog is publishing SEO-optimized content, tracking keyword rankings, building backlinks, running outreach campaigns, and monitoring Reddit and Hacker News for opportunities — all from my terminal.&lt;/p&gt;

&lt;p&gt;Here's exactly what I built and how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Solo founders have two options for marketing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Learn it yourself&lt;/strong&gt; — spend months studying SEO, content strategy, funnel design, and email marketing. Time you could be building product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hire someone&lt;/strong&gt; — $3K-5K/month for a part-time marketing person. Multiply that by 6 projects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wanted option 3: &lt;strong&gt;build an AI that does it for me.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a chatbot that suggests blog topics. An actual system — with strategy, data pipelines, content production, and analytics — that runs from my terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Built
&lt;/h2&gt;

&lt;p&gt;I built Marketing OS — a Claude Code project that turns slash commands into an autonomous marketing team. The file structure looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;marketing-os/
├── .claude/
│   ├── commands/        # 49 marketing commands
│   ├── guides/          # Step-by-step workflows
│   ├── knowledge/       # SEO, GEO, conversion expertise
│   └── settings.json    # Permissions and hooks
├── config/
│   ├── strategy.md              # 90-day marketing plan
│   ├── product-context.md       # Brand voice, audience, positioning
│   ├── content-inventory.json   # Keyword tracking (14 keywords, 3 funnels)
│   └── brand-voice-examples.md  # Real writing patterns
├── content/
│   ├── drafts/          # Content pipeline
│   └── published/       # Archive
├── data/
│   ├── keywords/        # Ahrefs research cache
│   ├── rankings/        # Google Search Console data
│   └── competitors/     # Competitive analysis
└── outreach/
    ├── templates/       # Email templates
    └── campaigns/       # Outreach tracking
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't a toy. It's a production system with real integrations — Ahrefs for SEO intelligence, Google Search Console for rankings, ForumScout for social listening. Every piece of data is cached, tracked, and version-controlled in git.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Roles It Replaces
&lt;/h2&gt;

&lt;p&gt;Here's what each "marketing person" does and the commands that replace them.&lt;/p&gt;

&lt;h3&gt;
  
  
  SEO Specialist → /keywords, /rankings, /opportunities
&lt;/h3&gt;

&lt;p&gt;When I type &lt;code&gt;/keywords "claude code"&lt;/code&gt;, the system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks my domain's competitive position (DR 0 — brand new)&lt;/li&gt;
&lt;li&gt;Pulls keyword data from Ahrefs (volume, difficulty, traffic potential)&lt;/li&gt;
&lt;li&gt;Filters for keywords I can actually rank for (winnability score)&lt;/li&gt;
&lt;li&gt;Shows me the 3 best options with reasoning&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Real output from my system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Priority 1 — Start Now
┌──────────────────────────┬────────┬────────┬─────┬─────────────┐
│ Keyword                  │ Vol US │ Global │ KD  │ Winnability  │
├──────────────────────────┼────────┼────────┼─────┼─────────────┤
│ claude code vs cursor    │ 3,300  │ 11,000 │  2  │ 92 ✅       │
│ claude code hooks        │ 1,700  │  4,900 │  —  │ 78 ✅       │
│ claude code best practices│ 1,100 │  3,300 │  —  │ 85 ✅       │
│ seo for developers       │   600  │  1,000 │ 20  │ 80 ✅       │
└──────────────────────────┴────────┴────────┴─────┴─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's real data. "Claude code vs cursor" has 3,300 monthly searches in the US, keyword difficulty of 2, and a DR 17 blog sitting at #3 in Google. Beatable.&lt;/p&gt;

&lt;p&gt;I didn't research any of this. The AI did. I just typed &lt;code&gt;/keywords&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Writer → /draft
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/draft&lt;/code&gt; is a 12-step content pipeline in a single command:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check if I already wrote about this topic (dedup)&lt;/li&gt;
&lt;li&gt;Load my product context, brand voice, and claims&lt;/li&gt;
&lt;li&gt;Research the keyword with Ahrefs (I choose the depth: 5, 15, or 30 credits)&lt;/li&gt;
&lt;li&gt;Analyze search intent from the SERP&lt;/li&gt;
&lt;li&gt;Find authoritative external sources and verify every URL exists&lt;/li&gt;
&lt;li&gt;Generate an outline for my approval&lt;/li&gt;
&lt;li&gt;Write the full post with SEO optimization&lt;/li&gt;
&lt;li&gt;Add GEO elements (answer boxes, FAQ, citable stats) for AI chatbot citations&lt;/li&gt;
&lt;li&gt;Verify every single link — internal and external&lt;/li&gt;
&lt;li&gt;Enforce hard limits: title ≤60 chars, description ≤160 chars, 2+ internal links&lt;/li&gt;
&lt;li&gt;Save draft and update the content inventory&lt;/li&gt;
&lt;li&gt;Find a cover image on Unsplash&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The output is publish-ready MDX. Not a rough draft I need to rewrite — actual content that matches my brand voice, follows my SEO rules, and links to my existing posts.&lt;/p&gt;

&lt;p&gt;If you're curious about the kind of content guidelines I use, I wrote up my &lt;a href="https://aiorg.dev/blog/claude-code-best-practices" rel="noopener noreferrer"&gt;15 best practices for Claude Code&lt;/a&gt; — tip #1 (write a CLAUDE.md file) is what makes all of this work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content Strategist → /setup, /mode, /report
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/setup&lt;/code&gt; runs a marketing interview:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analyzes my landing page and existing content&lt;/li&gt;
&lt;li&gt;Asks 7 questions about my target audience and value proposition&lt;/li&gt;
&lt;li&gt;Extracts brand voice patterns from my real writing&lt;/li&gt;
&lt;li&gt;Creates a 90-day content calendar with Ahrefs-validated keywords&lt;/li&gt;
&lt;li&gt;Generates &lt;code&gt;strategy.md&lt;/code&gt; — a complete marketing plan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then &lt;code&gt;/mode&lt;/code&gt; lets me switch between three strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traffic mode&lt;/strong&gt; — 50% educational content, grow audience fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conversion mode&lt;/strong&gt; — 50% bottom-of-funnel, get customers now&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Balanced mode&lt;/strong&gt; — full-funnel, sustainable growth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;/report&lt;/code&gt; shows where I am:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Progress Report
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Mode: BALANCED ⚖️

Keywords: 14 tracked (Ahrefs-validated)
Posts:    7 (5 published, 2 drafts)

├── BOFU: 0/0
├── MOFU: 2/6 published (33%)
└── TOFU: 3/8 published (38%)

Next recommended: "claude code CLAUDE.md"
Reason: 300/mo, winnability 80, follow-up to best practices post.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don't decide what to write. The system tells me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Social Media → /listen, /respond
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/listen&lt;/code&gt; scans Reddit, Hacker News, and Twitter for conversations where people discuss problems my product solves. It scores each opportunity by relevance and timing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/respond&lt;/code&gt; generates a genuinely helpful reply — not spam, not "check out my product." Value-first responses that happen to come from someone who built a solution.&lt;/p&gt;

&lt;p&gt;I still post these manually. Authenticity matters. But finding the right conversations and drafting responses? Automated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outreach Manager → /outreach, /cold-campaign
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/outreach&lt;/code&gt; finds link-building opportunities: guest post targets, resource pages, broken links I can replace. It generates personalized email templates.&lt;/p&gt;

&lt;p&gt;For scale, &lt;code&gt;/cold-campaign&lt;/code&gt; integrates with Apollo.io (contact database) and Instantly.ai (email warmup + sending) to run multi-step email sequences.&lt;/p&gt;

&lt;p&gt;I still review every email before it goes out. But the research, personalization, and tracking are handled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product Marketing → /positioning, /launch, /changelog
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/positioning&lt;/code&gt; generates messaging frameworks — value prop, elevator pitches, differentiators. &lt;code&gt;/launch&lt;/code&gt; creates a full Product Hunt campaign with pre-launch, launch day, and post-launch checklists. &lt;code&gt;/changelog&lt;/code&gt; turns a feature update into multi-channel announcements.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Real Session Looks Like
&lt;/h2&gt;

&lt;p&gt;Monday morning. I open my terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ claude

Marketing OS loaded.
✓ 2 drafts ready to publish. Run /approve.
⏳ 5 days since last post. Ready to write? /draft
📊 14 keywords tracked.

&amp;gt; /next

Your single most important action:
Publish the "SEO for Developers" draft.
Reason: 600/mo keyword, article is ready, 5 days since last publish.

&amp;gt; /approve seo-for-developers

✓ Created PR to aiorg.dev repo
✓ Content inventory updated
✓ Velocity: 1 post every 3.2 days (healthy)

&amp;gt; /draft

Recommended: "claude code CLAUDE.md" (300/mo, winnability 80)
Write it? [Yes]

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. I voice-command this through &lt;a href="https://wispr.flow" rel="noopener noreferrer"&gt;Wispr Flow&lt;/a&gt; — I don't even type. My hands are for code review.&lt;/p&gt;

&lt;p&gt;I also run 2-3 Claude Code sessions in parallel using &lt;a href="https://warp.dev" rel="noopener noreferrer"&gt;Warp&lt;/a&gt; — one session writing content, another refactoring code in a different project, a third running tests. Each session stays focused on its task.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Can't Do
&lt;/h2&gt;

&lt;p&gt;I want to be honest. Here's what the AI still can't handle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creative judgment.&lt;/strong&gt; It writes good content. Not great content. The difference between "good enough to publish" and "people will share this" still requires a human eye. I review every piece.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relationship building.&lt;/strong&gt; Outreach emails get opened. But building genuine connections — the follow-ups, the DMs, the "I saw your talk" messages — that's still manual. And it should be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Taste.&lt;/strong&gt; The system doesn't know that this particular blog post title is cringe, or that this hook sounds like every AI article on the internet. I catch that in review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Novel strategy.&lt;/strong&gt; It's excellent at executing established marketing playbooks. But inventing a new go-to-market approach, or spotting a cultural moment to capitalize on? That's still human territory.&lt;/p&gt;

&lt;p&gt;The honest ratio: AI does 85% of the work. I do 15%. But my 15% is what separates "content mill" from "content marketing."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data Integration Stack
&lt;/h2&gt;

&lt;p&gt;The system isn't just Claude Code with good prompts. It relies on real data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ahrefs&lt;/strong&gt; ($99-199/mo) — keyword volumes, competitor analysis, backlink tracking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Search Console&lt;/strong&gt; (free) — real ranking positions, click data, indexing status&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ForumScout&lt;/strong&gt; (~$30/mo) — social listening across Reddit, HN, Twitter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firecrawl&lt;/strong&gt; ($9/mo) — JavaScript-heavy page scraping fallback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without Ahrefs, &lt;code&gt;/keywords&lt;/code&gt; still works — just with less accurate data. Without GSC, you get estimates instead of real positions. The system degrades gracefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Set This Up
&lt;/h2&gt;

&lt;p&gt;The foundation of this entire system is two things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. A CLAUDE.md file&lt;/strong&gt; that teaches Claude Code your project structure, commands, and rules. I wrote a &lt;a href="https://aiorg.dev/blog/claude-code-best-practices" rel="noopener noreferrer"&gt;complete guide with examples&lt;/a&gt; on how to write one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Custom slash commands&lt;/strong&gt; — markdown files in &lt;code&gt;.claude/commands/&lt;/code&gt; that define multi-step workflows. Each command is a detailed specification: what data to load, what questions to ask, what output to produce, what limits to enforce.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/draft&lt;/code&gt; command alone is 950 lines of specification. That's not bloat — it's the difference between "write a blog post" and a repeatable system that produces consistent, SEO-optimized, brand-aligned content every time.&lt;/p&gt;

&lt;p&gt;If you want to go deeper on the automation layer, I wrote about &lt;a href="https://aiorg.dev/blog/claude-code-hooks" rel="noopener noreferrer"&gt;Claude Code hooks&lt;/a&gt; — shell commands that auto-format, auto-lint, and auto-validate every file Claude Code touches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Beats Hiring
&lt;/h2&gt;

&lt;p&gt;A junior marketing person costs $3K-5K/month and handles one project. They need onboarding, management, and context on your product.&lt;/p&gt;

&lt;p&gt;My system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs across 6 projects simultaneously&lt;/li&gt;
&lt;li&gt;Has zero onboarding (it reads the CLAUDE.md)&lt;/li&gt;
&lt;li&gt;Works at 2am when I have an idea&lt;/li&gt;
&lt;li&gt;Never forgets the brand voice or SEO rules&lt;/li&gt;
&lt;li&gt;Costs ~$250/month (Ahrefs + Claude Max + minor tools)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It won't replace a CMO with 10 years of experience and genuine strategic insight. But for a solo developer who needs marketing to happen? It's not even close.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Small
&lt;/h2&gt;

&lt;p&gt;You don't need 49 commands. Start with three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write a CLAUDE.md&lt;/strong&gt; with your product, audience, and voice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create one custom command&lt;/strong&gt; — &lt;code&gt;/draft&lt;/code&gt; that loads your context and writes a blog post&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track what you write&lt;/strong&gt; in a content-inventory.json file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That gets you 60% of the value. The other 39 commands are refinements.&lt;/p&gt;




&lt;p&gt;I wrote an &lt;a href="https://aiorg.dev/blog/ai-marketing-agent" rel="noopener noreferrer"&gt;expanded deep-dive on my blog&lt;/a&gt; covering the full architecture, data integrations, and Marketing as Code philosophy behind this system.&lt;/p&gt;




&lt;p&gt;I'm building in public, so all of this evolves weekly. The system I described today is version 1.22 — it looked very different 3 months ago and will look different 3 months from now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does your marketing setup look like? Anyone else running marketing from the terminal?&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Lukas — I run 6 SaaS projects solo with Claude Code. I write about &lt;a href="https://aiorg.dev/blog" rel="noopener noreferrer"&gt;AI-native development and marketing workflows&lt;/a&gt; on my blog.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>marketing</category>
      <category>startup</category>
    </item>
    <item>
      <title>Claude Code Best Practices: 15 Tips from Running 6 Projects (2026)</title>
      <dc:creator>Lukas Fryc</dc:creator>
      <pubDate>Mon, 09 Feb 2026 09:44:28 +0000</pubDate>
      <link>https://forem.com/lukaszfryc/claude-code-best-practices-15-tips-from-running-6-projects-2026-9eb</link>
      <guid>https://forem.com/lukaszfryc/claude-code-best-practices-15-tips-from-running-6-projects-2026-9eb</guid>
      <description>&lt;p&gt;I've been running 6 production projects with Claude Code for the past year. Not toy projects — real SaaS products with users, payments, and deployments.&lt;/p&gt;

&lt;p&gt;These 15 practices are the ones that survived. I tried dozens more that didn't make the cut. Some I learned the hard way (tip #9 after losing 3 hours of work).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The single highest-impact thing?&lt;/strong&gt; Writing a &lt;code&gt;CLAUDE.md&lt;/code&gt; file. 10 minutes of setup saves hours of repeated explanations every session.&lt;/p&gt;

&lt;p&gt;Still deciding between AI coding tools? I wrote an &lt;a href="https://aiorg.dev/blog/cursor-vs-claude-code" rel="noopener noreferrer"&gt;honest Cursor vs Claude Code comparison&lt;/a&gt; — spoiler: I use both.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Quick summary of all 15:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a CLAUDE.md file&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;.claude/rules/&lt;/code&gt; for domain-specific rules&lt;/li&gt;
&lt;li&gt;Add custom commands&lt;/li&gt;
&lt;li&gt;Set up &lt;code&gt;.claudeignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start with "what" and "why", not "how"&lt;/li&gt;
&lt;li&gt;Break large tasks into phases&lt;/li&gt;
&lt;li&gt;Reference existing patterns&lt;/li&gt;
&lt;li&gt;Ask for plans before execution&lt;/li&gt;
&lt;li&gt;Commit before big changes&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;/compact&lt;/code&gt; regularly&lt;/li&gt;
&lt;li&gt;Let Claude Code run tests&lt;/li&gt;
&lt;li&gt;One task per session&lt;/li&gt;
&lt;li&gt;Use hooks for automation&lt;/li&gt;
&lt;li&gt;Build project-specific kits&lt;/li&gt;
&lt;li&gt;Review before approving&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Write a CLAUDE.md File
&lt;/h3&gt;

&lt;p&gt;This is the single highest-impact thing you can do. &lt;code&gt;CLAUDE.md&lt;/code&gt; lives in your project root and Claude Code reads it at the start of every session.&lt;/p&gt;

&lt;p&gt;Without it, every session starts from zero. With it, Claude Code immediately knows your stack, patterns, and rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project Name&lt;/span&gt;

Brief description of what this project does.

&lt;span class="gu"&gt;## Tech Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Next.js 15 (App Router)
&lt;span class="p"&gt;-&lt;/span&gt; Supabase (auth + database)
&lt;span class="p"&gt;-&lt;/span&gt; Stripe (payments)
&lt;span class="p"&gt;-&lt;/span&gt; Tailwind CSS v4

&lt;span class="gu"&gt;## Architecture&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; /src/app — pages and API routes
&lt;span class="p"&gt;-&lt;/span&gt; /src/lib — shared utilities and services
&lt;span class="p"&gt;-&lt;/span&gt; /src/components — React components (use shadcn/ui)

&lt;span class="gu"&gt;## Rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Always use TypeScript strict mode
&lt;span class="p"&gt;-&lt;/span&gt; Use server components by default, 'use client' only when needed
&lt;span class="p"&gt;-&lt;/span&gt; Error handling: use Result pattern, not try/catch
&lt;span class="p"&gt;-&lt;/span&gt; Tests: use Vitest, co-locate with source files

&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Dev: pnpm dev
&lt;span class="p"&gt;-&lt;/span&gt; Test: pnpm test
&lt;span class="p"&gt;-&lt;/span&gt; Build: pnpm build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;10 minutes to write. Hours saved in every subsequent session.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use .claude/rules/ for Domain Rules
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; covers the whole project. For domain-specific rules, use &lt;code&gt;.claude/rules/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/rules/
├── api-routes.md      # How to write API routes
├── database.md        # Migration and query patterns
├── testing.md         # Testing conventions
└── components.md      # Component patterns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each file loads when relevant. Claude Code reads &lt;code&gt;api-routes.md&lt;/code&gt; when working on API routes, not when styling a button. Focused context = better results.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Add Custom Commands
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.anthropic.com/en/docs/claude-code/tutorials#create-custom-slash-commands" rel="noopener noreferrer"&gt;Custom commands&lt;/a&gt; turn repetitive prompts into one-word shortcuts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# .claude/commands/new-feature.md&lt;/span&gt;

Create a new feature following our standard pattern:
&lt;span class="p"&gt;1.&lt;/span&gt; Create the API route in /src/app/api/
&lt;span class="p"&gt;2.&lt;/span&gt; Create the database migration in /supabase/migrations/
&lt;span class="p"&gt;3.&lt;/span&gt; Create the React component in /src/components/
&lt;span class="p"&gt;4.&lt;/span&gt; Add a Vitest test file next to each new file
&lt;span class="p"&gt;5.&lt;/span&gt; Update the sidebar navigation if needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;/new-feature&lt;/code&gt; does it all. I have about 15 custom commands across my projects — they're the single biggest time saver after CLAUDE.md.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Set Up .claudeignore
&lt;/h3&gt;

&lt;p&gt;Like &lt;code&gt;.gitignore&lt;/code&gt; for Claude Code. Reduces noise, keeps context focused:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
.next/
dist/
*.lock
*.log
coverage/
.env*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Less context to scan = faster, more accurate responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompting Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5. Start with "What" and "Why"
&lt;/h3&gt;

&lt;p&gt;Bad prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a file called auth.ts in src/lib with a function called verifyToken that takes a JWT string and returns the decoded payload using jose library&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I need JWT verification for my API routes. Users authenticate via Supabase, but I need to verify tokens in edge functions where the Supabase client isn't available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second gives Claude Code room to think. It might suggest a different approach. It might notice you already have a verification pattern elsewhere. Let it make architectural decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Break Large Tasks into Phases
&lt;/h3&gt;

&lt;p&gt;Don't ask for an entire feature in one prompt:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Plan:&lt;/strong&gt; "I need to add billing with Stripe. What's your plan?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review the plan&lt;/strong&gt;, adjust if needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute phase 1:&lt;/strong&gt; "Let's start with the webhook handler"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify&lt;/strong&gt; (run tests, check the code)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execute phase 2:&lt;/strong&gt; "Now add the pricing page"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each phase gets full attention. Claude Code won't rush step 5 because it's trying to remember step 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Reference Existing Patterns
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Add a new API route for /api/projects. Follow the same pattern as /api/teams — same error handling, same auth middleware, same response format.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude Code reads the referenced file and replicates the pattern. Far more reliable than describing it in words.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Ask for Plans First
&lt;/h3&gt;

&lt;p&gt;For any task touching more than 3 files:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Plan how you'd implement user notifications. Don't write code yet — just outline the approach.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Review the plan. Catch problems before 15 files change. Then say "looks good, execute it."&lt;/p&gt;

&lt;p&gt;Saves significant time on reverts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow Habits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  9. Commit Before Big Changes
&lt;/h3&gt;

&lt;p&gt;Before any significant refactor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"checkpoint before refactor"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it goes wrong: one &lt;code&gt;git reset&lt;/code&gt; away from safety. Without this, you're manually undoing changes across 20 files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I learned this the hard way&lt;/strong&gt; — lost 3 hours of work to a botched migration that touched every model file. Now I checkpoint religiously.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Use /compact When Context Gets Long
&lt;/h3&gt;

&lt;p&gt;Long conversations degrade quality. Claude Code spends tokens processing old, irrelevant context instead of focusing on the current task.&lt;/p&gt;

&lt;p&gt;Rule of thumb: compact after every major task completion. When responses get slower or less accurate, that's your signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Let Claude Code Run Tests
&lt;/h3&gt;

&lt;p&gt;Don't copy-paste test output. Put your test command in CLAUDE.md:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Test: pnpm vitest run
&lt;span class="p"&gt;-&lt;/span&gt; Test watch: pnpm vitest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then: "Run the tests and fix any failures." Claude Code runs the suite, reads failures, fixes them, re-runs. This loop is 5x faster than manual copy-paste.&lt;/p&gt;

&lt;h3&gt;
  
  
  12. One Task Per Session
&lt;/h3&gt;

&lt;p&gt;Don't do this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fix the login bug, then add dark mode, then refactor the API routes, then write tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead: fix the login bug. Compact or start fresh. Add dark mode. Each task gets focused context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  13. Use Hooks for Automation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.anthropic.com/en/docs/claude-code/hooks" rel="noopener noreferrer"&gt;Hooks&lt;/a&gt; run shell commands automatically before or after Claude Code actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jq -r '.tool_input.file_path' | xargs npx prettier --write 2&amp;gt;/dev/null; exit 0"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This auto-formats every file Claude Code writes. No more "fix the linting errors" follow-ups.&lt;/p&gt;

&lt;p&gt;I wrote a &lt;a href="https://aiorg.dev/blog/claude-code-hooks" rel="noopener noreferrer"&gt;complete hooks guide with 20+ examples&lt;/a&gt; if you want to go deeper — hooks are the most underused Claude Code feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  14. Build Project-Specific Kits
&lt;/h3&gt;

&lt;p&gt;If you repeat the same setup across projects, package it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/
├── commands/
│   ├── new-feature.md
│   ├── deploy.md
│   └── test-all.md
├── rules/
│   ├── api-patterns.md
│   └── testing.md
├── knowledge/
│   └── architecture.md
└── CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy this &lt;code&gt;.claude/&lt;/code&gt; directory to any new project and Claude Code instantly knows your standards. I've packaged mine into reusable kits — one for each domain I work in (development, marketing, QA, product).&lt;/p&gt;

&lt;h3&gt;
  
  
  15. Review Before Approving
&lt;/h3&gt;

&lt;p&gt;Claude Code is fast, not infallible. Before accepting large changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;git diff&lt;/code&gt; — see everything that changed&lt;/li&gt;
&lt;li&gt;Check for hardcoded values, missing error handling, security issues&lt;/li&gt;
&lt;li&gt;Run the full test suite&lt;/li&gt;
&lt;li&gt;Build the project to catch type errors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Trust but verify. Claude Code handles 90% of the work. Your job is catching the 10% it misses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: What My Actual Workflow Looks Like
&lt;/h2&gt;

&lt;p&gt;Here's the thing — I don't type any of this anymore.&lt;/p&gt;

&lt;p&gt;Not the git commands. Not the CLAUDE.md file. Not even these prompts. I use &lt;a href="https://wispr.flow" rel="noopener noreferrer"&gt;Wispr Flow&lt;/a&gt; for voice input, so my "coding" sessions look like me talking to my laptop.&lt;/p&gt;

&lt;p&gt;"Commit what we have and push to staging." Done.&lt;/p&gt;

&lt;p&gt;"Write a CLAUDE.md for this project, check the tech stack from package.json." Done.&lt;/p&gt;

&lt;p&gt;"Run the tests, fix whatever fails, then run them again." Done.&lt;/p&gt;

&lt;p&gt;My hands are for code review. That's it.&lt;/p&gt;

&lt;p&gt;I run multiple Claude Code sessions in parallel using &lt;a href="https://warp.dev" rel="noopener noreferrer"&gt;Warp&lt;/a&gt; — one session refactoring the API, another writing tests, a third updating docs. Each session has its own context, its own task. I switch between them, review diffs, approve or reject.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My actual workflow looks like this:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Warp with 2-3 Claude Code sessions&lt;/li&gt;
&lt;li&gt;Voice-assign each session a task via Wispr Flow&lt;/li&gt;
&lt;li&gt;Switch between sessions, reviewing output&lt;/li&gt;
&lt;li&gt;Approve good work, redirect bad work — by voice&lt;/li&gt;
&lt;li&gt;Commit and move on&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I write maybe 10-20 lines of code per day. By hand, I mean. Claude Code writes thousands. My job is architecture decisions and code review.&lt;/p&gt;

&lt;p&gt;This isn't the future. This is Tuesday.&lt;/p&gt;

&lt;p&gt;The 15 tips above get you started. But if you really want to see what's possible — stop typing and start directing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Compound Effect
&lt;/h2&gt;

&lt;p&gt;Any single tip saves a few minutes. Combined, they transform the workflow. A well-configured project with CLAUDE.md, custom commands, hooks, and proper prompting ships features 5-10x faster than vanilla Claude Code.&lt;/p&gt;

&lt;p&gt;Add voice input and parallel sessions on top, and you're not 5x faster — you're operating at a completely different scale.&lt;/p&gt;

&lt;p&gt;The setup takes an afternoon. The savings compound every day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's your Claude Code setup? Do you use voice input?&lt;/strong&gt; I'm always looking for patterns from other practitioners.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Lukas — I run 6 SaaS projects solo using Claude Code. I write about &lt;a href="https://aiorg.dev/blog" rel="noopener noreferrer"&gt;Claude Code workflows and developer productivity&lt;/a&gt; on my blog.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
