---
title: "Model Control with Hooks"
id: "586"
type: "page"
slug: "hooks-control"
published_at: "2026-06-03T14:29:23+00:00"
modified_at: "2026-06-11T14:02:26+00:00"
url: "https://xedant.com/code/docs/validation/hooks-control"
markdown_url: "https://xedant.com/code/docs/validation/hooks-control.md"
excerpt: "Hooks are the enforcement layer of Xedant Code’s validation system. While skills guide the model’s…"
---

# Model Control with Hooks

[https://xedant.com/code/docs/validation/hooks-control.md](https://xedant.com/code/docs/validation/hooks-control.md)

Hooks are the enforcement layer of Xedant Code’s validation system. While skills guide the model’s behavior through instructions, hooks enforce rules by blocking specific tool calls before they execute. The most impactful use of hooks is preventing the model from running build and lint commands manually — since automated builds handle this more efficiently, every manual build the model attempts is wasted time and tokens.

![Hooks dialog](https://xedant.com/wp-content/uploads/2026/06/hooks-dialog-v2.png)## Hook Events and Types

Hooks are organized by event type. Xedant Code supports two events:

- **PreToolUse** — Runs before a tool is executed. This is where deny rules and pre-execution commands are defined. Hooks can target specific tools: Bash, Edit, Write, Read, Glob, Grep, Agent, NotebookEdit, and others
- **UserPromptSubmit** — Runs when the user submits a prompt to the AI. Use this to validate or modify user input before processing

### Deny Hooks

Deny hooks block tool execution by matching patterns against the tool’s input. For Bash tools, the pattern is matched against the command string. For non-Bash tools, use `.*` as a catch-all pattern to deny the tool entirely.

Each deny hook has four fields:

- **Pattern** — The text or regex pattern to match against the tool input
- **Regex** — When enabled, the pattern is treated as a regular expression (case-insensitive). When disabled, it’s matched as a substring
- **Message** — The denial message shown to the user when the hook triggers

### Execute Hooks

Execute hooks run a shell command before the tool executes. They receive context about the tool call via stdin as JSON and can return structured output that influences the model’s behavior. The `$CLAUDE_PROJECT_DIR` environment variable is available in the command.

## Preventing Manual Builds

This is the single most impactful use of hooks in Xedant Code. When automated builds are configured, the model should never attempt to build, lint, or start the project manually. Without these hooks, the model routinely runs build and lint commands after every edit — wasting time and tokens on actions the build system already handles.

```
preToolUse:
  Bash:
    - type: deny
      pattern: "dotnet build"
      message: "Don't build projects manually - it's done automatically"
    - type: deny
      pattern: "npm run build"
      message: "Don't build projects manually - it's done automatically"
    - type: deny
      pattern: "npm run link"
      message: "Don't lint projects manually - it's done automatically"
    - type: deny
      pattern: "npx svelte-check"
      message: "Don't lint projects manually - it's done automatically"
    - type: deny
      pattern: "npm run check"
      message: "Don't lint projects manually - it's done automatically"
    - type: deny
      pattern: "npm run dev"
      message: "Don't start projects manually - it's done automatically"
    - type: deny
      pattern: "dotnet run"
      message: "Don't start projects manually - it's done automatically"
userPromptSubmit: {}
```

When a deny hook blocks a command, the model receives the denial message and adjusts its behavior. Over the course of a session, this saves dozens of unnecessary tool calls that would compound into significant time and cost savings. You must always start with adjusting skills and CLAUDE.md to minimize amount of model-initiated builds, using hooks as a last-resort guardrail when the model forgets about your requirements when the chat gets long or compacted.

## Claude Code Integration

Hook configurations stored in `.xedant/hooks.yml` are automatically synced to Claude Code’s native hooks system. The sync process converts YAML definitions into JSON format and writes them to `.claude/settings.local.json`, so the hooks take effect on the next user prompt in the chat.

The sync flow works as follows:

1. On startup and after each save, `HooksService` reads the YAML configuration
2. Each hook definition is converted to Claude Code’s JSON format with the executable path resolved automatically
3. Hooks are deduplicated by command to avoid conflicts
4. The JSON is written to `.claude/settings.local.json`
5. Currently running sessions use the old hooks until the next user prompt is sent

The executable path for hook processing is resolved automatically by checking several locations: next to the running process binary, in the application base directory, or falling back to `dotnet XedantCode.dll`.

## Other Validation Uses

Beyond preventing manual builds, hooks serve other validation purposes:

- **Blocking destructive operations** — Deny `rm -rf`, force pushes, or other dangerous commands that could cause irreversible damage even inside a container
- **Enforcing project-specific policies** — Block writes to specific protected files, prevent edits to configuration files, or deny modifications to test fixtures
- **Custom validation logic** — Execute hooks can run shell commands before tool execution, enabling pre-commit checks, format validation, or custom guards

Config validation automatically checks `hooks.yml` on save, warning about unknown event types and properties. Errors reference the relevant section in `.xedant/README.md`.

## Effective Validation Patterns

- **Always deny manual builds when automated builds are configured** — This is the highest-impact hook configuration. Add deny rules for every build and lint command the model might try
- **Use substring matching for simple patterns** — `pattern: "npm run build"` with `regex: false` catches any command containing that substring, including with extra flags
- **Use regex for complex patterns** — Enable `regex: true` when you need case-insensitive matching or pattern syntax like `rm\s+-rf`
- **Test hooks after saving** — Hooks take effect on the next user prompt. Send a test prompt and verify the hook blocks the expected commands

For the complete hooks reference, see [Hooks](/code/docs/hooks)
. For the build system hooks prevent manual runs of, see [Build & Deploy](/code/docs/build-deploy)
. For Docker isolation that makes hooks a safety net rather than a gate, see [Docker Isolation & Skip Permissions](/code/docs/validation/docker-isolation)
.

**[← Docker Isolation & Skip Permissions](/code/docs/validation/docker-isolation)**

**[Analytics & Observability →](/code/docs/validation/analytics-observability)**
