Guides / Hook automation and blocking

Hook automation and blocking

Hooks run checks or notifications at specific points in kernel execution. Definitions live in your project's .lxz/hooks/*.json; the IDE automation / Hook settings can also manage them. Hooks have different responsibilities from MCP tools and A2A tasks.

1. Create a Command hook

Install Node.js and make node available in the kernel process PATH. Use a trusted project directory: project hooks can start processes as your local user, so review scripts before enabling them.

Create .lxz/hooks/check-tool.json. Every field below is required except handler, which defaults to Command.

{
  "name": "check-tool",
  "event": "BeforeToolCall",
  "command": "node",
  "args": [".lxz/scripts/check-tool.cjs"],
  "enabled": true,
  "timeout_secs": 5,
  "handler": "Command"
}

Create .lxz/scripts/check-tool.cjs:

// Input is an environment variable. stdin is closed; never wait for input.
const raw = process.env.LXZ_HOOK_DATA || ''
// Demonstration rule. Parse the particular tool's data for real checks.
const blocked = raw.includes('LXZ_DEMO_BLOCK')
process.stdout.write(JSON.stringify(blocked
  ? { action: 'block', reason: 'The demonstration rule blocked this tool call' }
  : { action: 'continue' }))

Commands run with the project root as their working directory. args contains separate arguments; shell expansion is not automatic. On Windows, explicitly choose an interpreter for .ps1 files or shell built-ins. Write diagnostics to stderr and exactly one decision JSON to stdout.

Check the script from the project directory first:

$env:LXZ_HOOK_DATA = '{"demo":"ok"}'
node .lxz/scripts/check-tool.cjs
$env:LXZ_HOOK_DATA = '{"demo":"LXZ_DEMO_BLOCK"}'
node .lxz/scripts/check-tool.cjs
Remove-Item Env:LXZ_HOOK_DATA

Expect {"action":"continue"}, then block with a nonempty reason. This checks script input/output; trigger a tool call in the IDE to verify its actual integration.

2. Input, decisions, and failures

The working directory provides the Command's project context; call sites may also explicitly supply environment variables. LXZ_HOOK_DATA contains the original string supplied by the current call site. Events need not share a JSON schema or include lifecycle identifiers. Validate data per event and never splice it into commands or treat it as trusted instructions.

Asynchronous Command / Prompt / Mcp decisions accept strict JSON only:

{"action":"continue"}
{"action":"block","reason":"Complete the required check first"}

Do not add Markdown, extra fields, or plain text. Blocking requires a nonempty reason. A nonzero Command exit, timeout, excessive output, or invalid decision fails and blocks execution. stdout and stderr are each capped at 1 MiB. The timeout has a one-second minimum; timeout or cancellation terminates the process tree. Interactive commands are unsuitable.

BeforeToolCall can prevent the subsequent operation. An AfterToolCall check runs after the operation and cannot undo its side effects. The legacy synchronous fire interface's BLOCK: convention does not apply to the IDE's asynchronous runtime.

3. Events currently connected

Execution pathConnected events
ToolsBeforeToolCall, AfterToolCall
Agent / RUSHBeforeAgentRun, AfterAgentRun; the start event also has a fallback path
RUSH stagesBeforeGuide / AfterGuide, BeforePlan / AfterPlan, BeforeCode / AfterCode, BeforeVerify / AfterVerify, BeforeLearn / AfterLearn

The event type also lists file writes, commands, commits, sessions, and errors. An enum variant does not guarantee a trigger at every operation. Do not depend on names such as BeforeFileWrite or BeforeCommit for enforcement in this version; verify events on the actual execution path.

4. Other handlers

Keep the complete outer definition and replace handler with the appropriate object below. For non-command handlers, use an empty command and an empty args array.

Handlerhandler exampleRequirements
HTTP{"Http":{"url":"http://127.0.0.1:8787/hook","timeout_secs":3}}Reachable HTTP service
MCP{"Mcp":{"server":"checks","tool":"validate","arguments":"{}"}}Configured server/tool, runtime executor, and tool approval
Prompt{"Prompt":{"prompt":"Check the call and return only decision JSON"}}Configured model and runtime executor

Mcp.arguments is a JSON string, not an object. Names are case-sensitive and externally tagged: use Http, not type: http. An Agent variant exists in the type, but current file loading and IDE saving reject enabled Agent hooks. It is not an available configuration feature. Plugin loading and fingerprint approval generate PluginCommand; do not fabricate its approval fields.

HTTP sends POST with event, tool_name, args, session_id, run_id, event_id, sequence, and occurred_at. Missing fields may be null, and args remains a string. Return 2xx with {"action":"continue"} or {"action":"block","reason":"Explanation"}. Network errors, timeouts, non-2xx responses, invalid responses, and unknown actions log a warning and continue: HTTP is fail-open, not an enforcement boundary. Its response parsing differs from strict Command decisions.

5. Save, toggle, and reload

Use the actual listener address and local Bearer token as explained in Kernel API. Never put tokens in configuration files or logs.

# Set $kernelBase and $kernelToken to the current kernel's actual values.
$hookHeaders = @{ Authorization = "Bearer $kernelToken" }
$projectRoot = (Get-Location).Path
$rootQuery = [uri]::EscapeDataString($projectRoot)
Invoke-RestMethod "$kernelBase/api/hooks/list?project_root=$rootQuery" -Headers $hookHeaders
$toggle = @{ name = 'check-tool'; enabled = $true; project_root = $projectRoot } | ConvertTo-Json
Invoke-RestMethod "$kernelBase/api/hooks/toggle" -Method Post -Headers $hookHeaders -ContentType 'application/json' -Body $toggle
Invoke-RestMethod "$kernelBase/api/hooks/reload?project_root=$rootQuery" -Method Post -Headers $hookHeaders

POST /api/hooks/save accepts { "hook": <complete definition>, "file": "check-tool.json", "project_root": "absolute project path" }. Save and toggle persist project files. Reload reports the rescan result; it does not guarantee replacement of an already running engine. Start a new task after changes. Some API errors use success: false in the response body even with HTTP 200, so inspect the body.

6. Troubleshooting

SymptomCheck
No hook executionActual project root, .lxz/hooks, enabled flag, connected event, and a new task
Configuration failure blocks executionRequired fields, handler tags, Agent restriction; repair and reload
Process fails to startKernel PATH, Node installation, relative script path, interpreter, and permissions
Repeated timeoutsAccidental stdin reading, interactive commands, network calls without timeouts
Invalid decisionLogs on stdout, strict JSON, and nonempty block reason
HTTP outage continues executionExpected fail-open behavior; inspect hook warnings and service logs
A file changed despite an after-hook blockThe operation already happened; use a connected before-event check

A2A guide · MCP configuration · Kernel API

v1.0.0 · web-2026