How to Implement a `console.log` Detection Hook in Everything-Claude-Code

Add a PostToolUse hook in hooks/hooks.json that matches Edit calls on JS/TS files and runs a Node script to scan for console.log statements.

The Everything-Claude-Code repository provides a powerful hook system that runs custom commands automatically after Claude Code tools finish executing. Implementing a console.log detection hook helps catch stray debug statements before they reach production, enforcing clean JavaScript and TypeScript codebases without interrupting your development flow.


Understanding the Hook Architecture

Claude Code hooks are defined in hooks/hooks.json and triggered by lifecycle events. The PostToolUse event fires after any tool completes, making it ideal for lint-style checks on file modifications.

Each hook entry contains three components:

  • Matcher – a boolean expression determining when the hook fires
  • Command – the executable to run (shell command or inline script)
  • Description – human-readable explanation of the hook's purpose

The hook receives tool execution data via stdin as a JSON payload, allowing inspection of which file was edited and what operation occurred.


Step-by-Step Implementation

Step 1: Define the Matcher Expression

The matcher restricts execution to Edit operations on web-convention files:

"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.(ts|tsx|js|jsx)$\""

This pattern ensures the hook only processes relevant file types, avoiding unnecessary computation on non-JavaScript assets.

Step 2: Create the Detection Command

The command reads the edited file from disk, scans line-by-line for the /console\.log/ regex, and reports matches to stderr:

// hooks/hooks.json – complete PostToolUse entry
{
  "matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.(ts|tsx|js|jsx)$\"",
  "hooks": [
    {
      "type": "command",
      "command": "node -e \"const fs=require('fs');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path;if(p&&fs.existsSync(p)){const c=fs.readFileSync(p,'utf8');const lines=c.split('\\n');const matches=[];lines.forEach((l,idx)=>{if(/console\\.log/.test(l))matches.push((idx+1)+': '+l.trim())});if(matches.length){console.error('[Hook] WARNING: console.log found in '+p);matches.slice(0,5).forEach(m=>console.error(m));console.error('[Hook] Remove console.log before committing')}}console.log(d)})\""
    }
  ],
  "description": "Warn about console.log statements after edits"
}

The Node script performs four operations:

  1. Buffer stdin – accumulates the JSON payload from Claude Code
  2. Extract file path – parses tool_input.file_path from the payload
  3. Scan for matches – tests each line against /console\.log/
  4. Emit warnings – prints line numbers and snippets (limited to first 5 matches)
  5. Passthrough – echoes original payload to stdout for downstream processing

Step 3: Test the Hook Manually

Before relying on automatic execution, simulate the Claude Code payload:


# Simulate a Claude-code Edit tool payload

echo '{"tool":"Edit","tool_input":{"file_path":"src/example.js"}}' \
  | node -e "const fs=require('fs');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path;if(p&&fs.existsSync(p)){const c=fs.readFileSync(p,'utf8');const lines=c.split('\\n');const matches=[];lines.forEach((l,idx)=>{if(/console\\.log/.test(l))matches.push((idx+1)+': '+l.trim())});if(matches.length){console.error('[Hook] WARNING: console.log found in '+p);matches.slice(0,5).forEach(m=>console.error(m));console.error('[Hook] Remove console.log before committing')}}console.log(d)})"

Given a file src/example.js containing:

function processData(data) {
  console.log('debug start');
  const result = transform(data);
  console.log(result);
  return result;
}

The hook outputs:


[Hook] WARNING: console.log found in src/example.js
2: console.log('debug start');
4: console.log(result);
[Hook] Remove console.log before committing


Extending the Detection Pattern

The same architecture supports additional lint-style checks by modifying the regex or adding parallel hook entries:

Pattern Regex Modification Use Case
debugger statements /\bdebugger\b/ Prevent accidental breakpoints in production
alert() calls /\balert\(/ Block debugging dialogs
Hardcoded secrets `/password secret
TODO/FIXME comments `/TODO FIXME/`

Each extension follows the identical three-step pattern: update the matcher if needed, modify the detection regex in the command, and adjust the description accordingly.


Key Files Reference

File Purpose
hooks/hooks.json Central configuration for all Claude Code hooks; the PostToolUse array holds detection logic
scripts/hooks/pre-compact.js Reference implementation showing Node-based hook scripts in action
tests/hooks/hooks.test.js Test suite for hook behavior—extend here to add detection coverage
README.md Architecture documentation and hook lifecycle details

The hook system in WorldFlowAI/everything-claude-code operates entirely through configuration, requiring no modifications to Claude Code's core—making it safe to upgrade without losing custom checks.


Summary

  • Hooks live in hooks/hooks.json under the PostToolUse section
  • Matchers filter by tool type and file extension using boolean expressions
  • Commands receive JSON via stdin and must passthrough data to stdout
  • console.log detection uses /console\.log/ regex with line-by-line scanning
  • Warnings go to stderr to surface issues without breaking the tool chain

Frequently Asked Questions

Does the console.log detection hook block file edits or commits?

No. The hook runs after the Edit tool completes and only writes to stderr. It surfaces warnings without modifying the exit code or preventing the operation from succeeding. This design keeps development velocity high while maintaining visibility into code quality issues.

Can I detect other console methods like console.warn or console.error?

Yes. Modify the regex in the command from /console\.log/ to /console\.(log|warn|error|info|debug)/ to catch all console method calls. You can also create separate hooks with distinct descriptions if you want differentiated handling for production-appropriate methods versus debug methods.

How do I test hooks without triggering actual Claude Code edits?

Use the manual testing approach shown above: construct a JSON payload matching Claude Code's PostToolUse format and pipe it through the Node command. The tests/hooks/hooks.test.js file contains additional patterns for automated testing that you can adapt for your detection logic.

What happens if the hook script throws an error?

Claude Code's hook system generally logs hook failures but proceeds with the operation. However, always ensure your command ends with console.log(d) to passthrough the original payload—failure to do so may break downstream processing. The inline Node script includes error-adjacent checks like fs.existsSync(p) to avoid crashes on missing files.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →