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.
Every ECC hook is defined by a trigger event, an optional matcher (filtering which tools the hook applies to), and one or more action objects. ECC supports three action types: command for shell scripts, http for webhook-style HTTP calls, and prompt/agent for sub-agent invocations. Hooks receive tool input as JSON on stdin and must echo JSON on stdout.
All hooks receive structured input on stdin:
interface HookInput {
tool_name: string; // "Bash", "Edit", "Write", "Read", etc.
tool_input: {
command?: string; // Bash: the command being run
file_path?: string; // Edit/Write/Read: target file
old_string?: string; // Edit: text being replaced
new_string?: string; // Edit: replacement text
content?: string; // Write: file content
};
tool_output?: { // PostToolUse only
output?: string; // Command/tool output
};
}
Action Types
command
http
prompt / agent
Runs a local shell command. This is the most common hook type in ECC — all built-in hooks use Node.js scripts for cross-platform compatibility.{
"type": "command",
"command": "node",
"args": ["scripts/hooks/quality-gate.js"],
"timeout": 30,
"async": false
}
Fields:| Field | Type | Description |
|---|
command | string | Executable to run (e.g., node, bash, python3) |
args | array | Arguments passed to the command |
env | object | Additional environment variables |
timeout | number | Seconds before the hook is killed (default: 10) |
async | boolean | If true, hook runs in background and cannot block |
Exit codes:
0 — Success, continue execution
2 — Block the tool call (PreToolUse only)
- Any other non-zero — Error (logged, does not block)
Async hooks run in the background and cannot block tool execution. Use for slow operations like background analysis:{
"type": "command",
"command": "node",
"args": ["scripts/hooks/build-analysis.js"],
"async": true,
"timeout": 30
}
Posts to a URL. Useful for webhook integrations, external logging, or custom services.{
"type": "http",
"url": "https://my-webhook.example.com/hook",
"method": "POST",
"headers": {
"Authorization": "Bearer ${WEBHOOK_TOKEN}",
"Content-Type": "application/json"
},
"timeout": 5
}
Fields:| Field | Type | Description |
|---|
url | string | Target URL (HTTP or HTTPS) |
method | string | HTTP method (default: POST) |
headers | object | Request headers; use ${ENV_VAR} for secrets |
timeout | number | Seconds before the request is aborted |
The hook body is the JSON-encoded HookInput object. Runs a sub-prompt against a model. Use for AI-powered post-processing, analysis, or decision-making hooks.{
"type": "prompt",
"prompt": "Review the following edit for potential security issues: {{tool_input}}",
"model": "claude-3-5-haiku",
"timeout": 30
}
Fields:| Field | Type | Description |
|---|
prompt | string | Prompt template; use {{tool_input}} for interpolation |
model | string | Model to use (optional; defaults to session model) |
timeout | number | Seconds before the prompt times out |
Built-In Hook Catalog
ECC’s hooks-runtime module ships the following hooks, grouped by event:
| Hook ID | Matcher | Behavior | Blocking |
|---|
pre:bash:dev-server-blocker | Bash | Blocks npm run dev etc. outside tmux — ensures log access | Yes (exit 2) |
pre:bash:tmux-reminder | Bash | Suggests tmux for long-running commands (npm test, cargo build, docker) | No (warn) |
pre:bash:git-push-reminder | Bash | Reminds to review changes before git push | No (warn) |
pre:bash:pre-commit-quality | Bash | Runs quality checks before git commit: lints staged files, validates commit message, detects secrets | Conditional |
pre:write:doc-file-warning | Write | Warns about non-standard .md/.txt files | No (warn) |
pre:edit:strategic-compact | Edit|Write | Suggests /compact at logical intervals (~50 tool calls) | No (warn) |
PostToolUse Hooks
| Hook ID | Matcher | What It Does |
|---|
post:bash:pr-logger | Bash | Logs PR URL and review command after gh pr create |
post:bash:build-analysis | Bash | Background analysis after build commands (async) |
post:edit:quality-gate | Edit|Write|MultiEdit | Runs fast quality checks after edits |
post:edit:design-quality | Edit|Write|MultiEdit | Warns when frontend edits drift toward generic UI |
post:edit:prettier | Edit | Auto-formats JS/TS files with Prettier |
post:edit:typecheck | Edit | Runs tsc --noEmit after editing .ts/.tsx files |
post:edit:console-log-warning | Edit | Warns about console.log statements in edited files |
Lifecycle Hooks
| Hook ID | Event | What It Does |
|---|
session:start | SessionStart | Loads previous context and detects package manager |
pre:compact | PreCompact | Saves state before context compaction |
stop:check-console-log | Stop | Audits all modified files for console.log |
stop:session-summary | Stop | Persists session state when transcript path is available |
stop:pattern-extraction | Stop | Evaluates session for extractable patterns (continuous learning) |
stop:cost-tracker | Stop | Emits lightweight run-cost telemetry markers |
stop:desktop-notify | Stop | Sends macOS desktop notification with task summary |
session:end | SessionEnd | Lifecycle marker and cleanup log |
Custom Hook Recipes
{
"matcher": "Edit",
"hooks": [{
"type": "command",
"command": "node",
"args": ["-e", "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const ns=i.tool_input?.new_string||'';if(/TODO|FIXME|HACK/.test(ns)){console.error('[Hook] New TODO/FIXME added - consider creating an issue')}console.log(d)})"]
}],
"description": "Warn when adding TODO/FIXME comments"
}
Block large file creation
{
"matcher": "Write",
"hooks": [{
"type": "command",
"command": "node",
"args": ["-e", "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const c=i.tool_input?.content||'';const lines=c.split('\\n').length;if(lines>800){console.error('[Hook] BLOCKED: File exceeds 800 lines ('+lines+' lines)');process.exit(2)}console.log(d)})"]
}],
"description": "Block creation of files larger than 800 lines"
}
{
"matcher": "Edit",
"hooks": [{
"type": "command",
"command": "node",
"args": ["-e", "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(/\\.py$/.test(p)){const{execFileSync}=require('child_process');try{execFileSync('ruff',['format',p],{stdio:'pipe'})}catch(e){}}console.log(d)})"]
}],
"description": "Auto-format Python files with ruff after edits"
}
Require test files alongside new source files
{
"matcher": "Write",
"hooks": [{
"type": "command",
"command": "node",
"args": ["-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(/src\\/.*\\.(ts|js)$/.test(p)&&!/\\.test\\.|\\.spec\\./.test(p)){const testPath=p.replace(/\\.(ts|js)$/,'.test.$1');if(!fs.existsSync(testPath)){console.error('[Hook] No test file found for: '+p)}}console.log(d)})"]
}],
"description": "Remind to create tests when adding new source files"
}
Overriding Built-In Hooks
To disable a specific built-in hook without uninstalling the module, override it in ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [],
"description": "Override: allow all .md file creation"
}
]
}
}
Or use environment variables for runtime control (see Lifecycle Events for the full list of ECC_* controls).