Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/affaan-m/ECC/llms.txt

Use this file to discover all available pages before exploring further.

Without hooks, every agent session starts from zero: no context from yesterday’s work, no automatic formatting after edits, no guardrails preventing a blind git push. Hooks are ECC’s event-driven automation layer — shell commands, HTTP calls, or sub-prompts that execute at precise moments in the harness lifecycle, injecting consistent behaviour into every session without requiring manual intervention.

How Hooks Work

The execution pipeline for every tool call passes through the hook graph:
User request
  → Claude picks a tool
    → PreToolUse hooks run   (can block with exit code 2)
      → Tool executes
        → PostToolUse hooks run  (analysis, formatting, feedback)
          → Claude responds
            → Stop hooks run     (audits, session persistence)
Session-boundary hooks (SessionStart, SessionEnd, PreCompact) fire outside the per-tool flow.
  • PreToolUse hooks run before the tool executes. They can block the call (exit code 2) or emit a warning (write to stderr, exit 0).
  • PostToolUse hooks run after the tool completes. They can analyze output but cannot block.
  • Stop hooks run after each complete Claude response.
  • SessionStart / SessionEnd hooks run at session lifecycle boundaries.
  • PreCompact hooks run before context compaction — the right moment to save state.

Supported Hook Events

All 18 hook event types defined in schemas/hooks.schema.json:

SessionStart

Session opens. Used to load bounded prior context and detect project state.

UserPromptSubmit

Fires when a user message is submitted. Runs before Claude processes it.

PreToolUse

Before any tool executes. The primary gate for quality checks and blockers.

PermissionRequest

When the harness requests elevated permissions for an operation.

PostToolUse

After a tool completes successfully. Used for formatting, logging, feedback.

PostToolUseFailure

After a tool fails. Used for error analysis and recovery signals.

Notification

Harness notification events (desktop alerts, external integrations).

SubagentStart

Before a sub-agent begins a delegated task.

Stop

After each complete Claude response. Used for audits and session persistence.

SubagentStop

After a sub-agent finishes its delegated task.

PreCompact

Before context compaction. The correct moment to save session state.

InstructionsLoaded

After CLAUDE.md / rules files are loaded into the session context.

TeammateIdle

A teammate agent becomes idle in a multi-agent session.

TaskCompleted

A discrete task unit is marked complete.

ConfigChange

ECC configuration or settings change during a session.

WorktreeCreate

A new git worktree is created for parallel agent work.

WorktreeRemove

A git worktree is removed after parallel work completes.

SessionEnd

Session closes. Used for final cleanup and end markers.

Three Hook Action Types

Every hook entry uses one of three type values:
Runs a local shell command. The most common type — shell scripts or Node.js one-liners that receive tool input as JSON on stdin and return JSON on stdout.
{
  "type": "command",
  "command": "node scripts/hooks/my-check.js",
  "async": false,
  "timeout": 30
}
Set "async": true for background hooks that should not block the main flow (e.g., build analysis, cost telemetry).

Hook Definition Structure

A complete hooks configuration file maps event names to arrays of matcher entries:
{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "description": "Tmux reminder for long-running commands",
        "hooks": [
          {
            "type": "command",
            "command": "if [ -z \"$TMUX\" ]; then echo '[Hook] Consider tmux for session persistence' >&2; fi"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit",
        "description": "Auto-format JS/TS files with Prettier after edits",
        "hooks": [
          {
            "type": "command",
            "command": "node scripts/hooks/prettier-format.js"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node scripts/hooks/session-start.js"
          }
        ]
      }
    ]
  }
}
Exit codes for command hooks:
CodeMeaning
0Success — continue execution
2Block the tool call (PreToolUse only)
Other non-zeroError — logged but does not block

ECC Built-In Hooks

PreToolUse Hooks

HookMatcherBehaviorExit Code
Dev server blockerBashBlocks npm run dev etc. outside tmux — ensures log access2 (blocks)
Tmux reminderBashSuggests tmux for long-running commands (npm test, cargo build, docker)0 (warns)
Git push reminderBashReminds to review changes before git push0 (warns)
Pre-commit quality checkBashLints staged files, validates commit message format, detects console.log / debugger / secrets2 (blocks critical) / 0 (warns)
Doc file warningWriteWarns about non-standard .md/.txt files; allows README, CLAUDE, CONTRIBUTING, CHANGELOG, LICENSE, SKILL, docs/, skills/0 (warns)
Strategic compactEdit|WriteSuggests manual /compact at logical intervals (~every 50 tool calls)0 (warns)
Config protectionWrite|Edit|MultiEditBlocks modifications to linter/formatter config files — steers agent to fix code instead2 (blocks)

PostToolUse Hooks

HookMatcherWhat It Does
PR loggerBashLogs PR URL and review command after gh pr create
Build analysisBashBackground analysis after build commands (async, non-blocking)
Quality gateEdit|Write|MultiEditRuns fast quality checks after edits
Design quality checkEdit|Write|MultiEditWarns when frontend edits drift toward generic template-looking UI
Prettier formatEditAuto-formats JS/TS files with Prettier after edits
TypeScript checkEditRuns tsc --noEmit after editing .ts/.tsx files
console.log warningEditWarns about console.log statements in edited files

Lifecycle Hooks

HookEventWhat It Does
Session startSessionStartLoads previous context and detects package manager
Pre-compactPreCompactSaves state before context compaction
Console.log auditStopChecks all modified files for console.log after each response
Session summaryStopPersists session state when transcript path is available
Pattern extractionStopEvaluates session for extractable patterns (continuous learning)
Cost trackerStopEmits lightweight run-cost telemetry markers
Desktop notifyStopSends macOS desktop notification with task summary
Session end markerSessionEndLifecycle marker and cleanup log

Installing Hooks

# Install the full hooks-runtime module
npx ecc install --modules hooks-runtime --target claude
# Hooks are written to ~/.claude/hooks/hooks.json

# On Windows
npx ecc install --modules hooks-runtime --target claude
# Hooks are written to %USERPROFILE%\.claude\hooks\hooks.json
Do not paste the raw repo hooks.json directly into ~/.claude/settings.json. The checked-in file is plugin-oriented and uses relative paths. Use the installer so hook commands are rewritten against your actual Claude root.

Runtime Hook Controls

You can tune hook behaviour without editing hooks.json using environment variables:
# Profile: minimal | standard | strict (default: standard)
export ECC_HOOK_PROFILE=standard

# Disable specific hook IDs (comma-separated)
export ECC_DISABLED_HOOKS="pre:bash:tmux-reminder,post:edit:typecheck"

# Disable GateGuard (dev-server blocker) during setup or recovery
export ECC_GATEGUARD=off

# Cap SessionStart additional context (default: 8000 chars)
export ECC_SESSION_START_MAX_CHARS=4000

# Disable SessionStart additional context entirely
export ECC_SESSION_START_CONTEXT=off

# Suppress API-rate cost estimate warnings
export ECC_CONTEXT_MONITOR_COST_WARNINGS=off
ProfileDescription
minimalEssential lifecycle and safety hooks only
standardDefault — balanced quality and safety checks
strictAdditional reminders and stricter guardrails

Writing a Custom Hook

Hooks receive tool input as JSON on stdin and must return JSON on stdout.
// my-hook.js
let data = '';
process.stdin.on('data', chunk => data += chunk);
process.stdin.on('end', () => {
  const input = JSON.parse(data);

  const toolName  = input.tool_name;    // "Edit", "Bash", "Write", etc.
  const toolInput = input.tool_input;   // tool-specific parameters
  // input.tool_output is available in PostToolUse hooks only

  // Warn (non-blocking): write to stderr
  if (/TODO|FIXME/.test(toolInput.new_string || '')) {
    console.error('[Hook] New TODO added — consider creating an issue');
  }

  // Block (PreToolUse only): exit with code 2
  // process.exit(2);

  // Always forward the original data to stdout
  console.log(data);
});
For hooks that should run without blocking the main flow, set "async": true in the hook definition:
{
  "type": "command",
  "command": "node my-slow-hook.js",
  "async": true,
  "timeout": 30
}

Build docs developers (and LLMs) love