---
draft: false
title: Hooks
description: Lifecycle hooks in AgentsMesh trigger shell commands or prompt injections before/after tool use, on notifications, on user prompts, and on subagent lifecycle events.
---

import { Aside } from '@astrojs/starlight/components';

Hooks let you run shell commands or inject prompts at specific points in the AI tool lifecycle. They live in `.agentsmesh/hooks.yaml`.

## File format

```yaml
PreToolUse:
  - matcher: "Edit|Write"
    type: command
    command: "eslint --fix $FILE"
    timeout: 30

PostToolUse:
  - matcher: "Bash"
    type: prompt
    prompt: "Review the command output for security concerns before proceeding."

Notification:
  - matcher: "*"
    type: command
    command: "./scripts/log-notification.sh"

UserPromptSubmit:
  - matcher: "*"
    type: command
    command: "./scripts/prompt-guard.sh"
```

## Hook event types

| Event | When it fires |
|-------|--------------|
| `PreToolUse` | Before a tool call executes. |
| `PostToolUse` | After a tool call completes. |
| `Notification` | When the AI sends a notification. |
| `UserPromptSubmit` | When the user submits a prompt. |
| `SubagentStart` | When a subagent starts. |
| `SubagentStop` | When a subagent stops. |

## Hook entry fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `matcher` | `string` | Yes | Regex pattern for which tool or event triggers the hook. Use `*` to match all. |
| `type` | `string` | Yes | `command` — run a shell command. `prompt` — inject a prompt into the conversation. |
| `command` | `string` | If `type: command` | Shell command to execute. |
| `prompt` | `string` | If `type: prompt` | Prompt text to inject. |
| `timeout` | `number` | No | Timeout in seconds. Default varies by tool. |

## Variable substitution

For `PreToolUse` and `PostToolUse` hooks, the following variables are available in `command`:

| Variable | Description |
|----------|-------------|
| `$FILE` | The file path being edited (for Edit/Write tools). |
| `$TOOL` | The tool name being called. |

<Aside type="note">
  Variable availability depends on the target tool. Not all tools expose all variables to hook commands. Check the individual tool's documentation for supported variables.
</Aside>

## Common hook patterns

### Auto-lint on file save

```yaml
PreToolUse:
  - matcher: "Edit|Write"
    type: command
    command: "npx eslint --fix $FILE"
    timeout: 30
```

### Security review on Bash execution

```yaml
PostToolUse:
  - matcher: "Bash"
    type: prompt
    prompt: "Check the output of the previous command for security issues, unexpected errors, or sensitive data before continuing."
```

### Prompt injection guard

```yaml
UserPromptSubmit:
  - matcher: "*"
    type: command
    command: "./scripts/check-prompt-injection.sh"
    timeout: 5
```

### Log all notifications

```yaml
Notification:
  - matcher: "*"
    type: command
    command: "echo \"$(date): $NOTIFICATION\" >> .agentsmesh/notifications.log"
```

## Tool-specific behavior

See the **Hooks** row in the [supported tools matrix](../../reference/supported-tools/) for per-target support levels (native, partial, or unsupported).

## Windows portability

For targets that embed the canonical `command` verbatim in a JSON config (executed by the agent at runtime), a portable command (`pnpm test`, `npx eslint $FILE`) works identically on Linux, macOS, and Windows.

**Cline and Copilot are different**: their native hook contracts are shell wrapper files. AgentsMesh emits them as `.clinerules/hooks/*.sh` and `.github/hooks/scripts/*.sh` with a `#!/usr/bin/env bash` header. These wrappers will not execute on Windows without a POSIX shell (git-bash or WSL). `agentsmesh lint` warns when hooks are configured for either target. If your team includes Windows developers, prefer a JSON-embedded hook target (see the [hooks row in the support matrix](../../reference/supported-tools/#hooks)) or supply a POSIX shell on the Windows machines.

## Generated wrapper contract (Cline / Copilot)

Generated `.sh` wrappers carry three guarantees beyond the canonical command:

- **`set -eu`** is the first executable line — wrappers abort on any command failure **and** on reference to an unset variable. If your `command` references `$ENV_VAR` and the variable is not exported, the hook now fails fast instead of silently expanding to an empty string. Use `${ENV_VAR:-default}` syntax when an unset value is intentional.
- **Executable mode `0o755`** is set on every emitted `.sh`/`.bash`/`.zsh` file so `runner exec`-style invocation works without an explicit `chmod +x` step.
- **CR/LF stripped from the metadata header** — the `# agentsmesh-matcher:` and `# agentsmesh-command:` comment lines collapse newlines from the canonical YAML, so a multi-line block scalar in the canonical hook cannot break out of the comment header into executable shell.

## Agent-level hooks

Hooks can also be defined per-agent in `agents/*.md` frontmatter:

```markdown
---
name: safe-editor
description: Editor agent with pre-commit validation
hooks:
  PreToolUse:
    - matcher: "Edit|Write"
      type: command
      command: "npx tsc --noEmit"
      timeout: 60
---
```

Agent-level hooks apply only when that agent is active and override project-level hooks for matching events.
