# Hooks

Hooks allow you to run custom scripts at specific points during Tabnine CLI's agent loop, enabling you to intercept and customize behavior without modifying the CLI itself.

## What are hooks?

Hooks run synchronously as part of the agent loop — when a hook event fires, Tabnine CLI waits for all matching hooks to complete before continuing.

With hooks, you can:

* **Add context**: Inject relevant information (like git history or project state) before the model processes a request.
* **Validate actions**: Review tool arguments and block potentially dangerous operations.
* **Enforce policies**: Implement security scanners and compliance checks.
* **Modify inputs**: Rewrite tool arguments before execution (e.g., enforcing file path restrictions).
* **Log interactions**: Track tool usage and model responses for auditing.

## Quick start

Add two files to any git-initialized project:

**`.tabnine/agent/settings.json`**

```json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "node ./hooks/announce.js",
            "name": "announce"
          }
        ]
      }
    ]
  }
}
```

**`hooks/announce.js`**

```javascript
let data = '';
process.stdin.on('data', (chunk) => { data += chunk; });
process.stdin.on('end', () => {
  const input = JSON.parse(data);
  process.stdout.write(JSON.stringify({
    systemMessage: '🪝 Hooks are working! Session started.'
  }));
});
```

Start Tabnine CLI in that project — you'll see `🪝 Hooks are working! Session started.` printed in the terminal.

## Communication protocol

Hooks communicate with Tabnine CLI via JSON on standard I/O:

* **Input**: JSON is written to the hook's **stdin**.
* **Output**: The hook writes JSON to **stdout**.
* **Logging**: Use **stderr** for all debug output. Tabnine CLI never parses stderr as JSON.

{% hint style="warning" %}
**Important**: Your hook must not print anything to stdout other than the final JSON object. Even a single stray `echo` before the JSON will break parsing. If stdout contains invalid JSON, the CLI defaults to "allow" and treats the entire output as a `systemMessage`.
{% endhint %}

### Exit codes

| Exit Code | Label            | Behavior                                                                                                            |
| --------- | ---------------- | ------------------------------------------------------------------------------------------------------------------- |
| **0**     | **Success**      | stdout is parsed as JSON. Preferred for all logic, including intentional blocks (use `"decision": "deny"` in JSON). |
| **2**     | **System Block** | The action is blocked. stderr is used as the rejection reason.                                                      |
| **Other** | **Warning**      | Non-fatal failure. A warning is shown, but execution proceeds normally.                                             |

### Base input schema

All hooks receive these common fields via stdin:

```json
{
  "session_id": "uuid-string",
  "transcript_path": "/absolute/path/to/session.json",
  "cwd": "/project/root",
  "hook_event_name": "BeforeTool",
  "timestamp": "2025-01-01T00:00:00.000Z"
}
```

### Common output fields

| Field            | Type      | Description                                                           |
| ---------------- | --------- | --------------------------------------------------------------------- |
| `decision`       | `string`  | `"allow"` or `"deny"` (alias `"block"`). Impact depends on the event. |
| `reason`         | `string`  | Feedback message when `decision` is `"deny"`.                         |
| `systemMessage`  | `string`  | Displayed immediately to the user in the terminal.                    |
| `continue`       | `boolean` | If `false`, stops the entire agent loop immediately.                  |
| `stopReason`     | `string`  | Displayed to the user when `continue` is `false`.                     |
| `suppressOutput` | `boolean` | If `true`, hides internal hook metadata from logs/telemetry.          |

## Environment variables

Hooks are executed with a sanitized environment. The following variables are always available:

| Variable              | Description                                     |
| --------------------- | ----------------------------------------------- |
| `TABNINE_PROJECT_DIR` | Absolute path to the project root.              |
| `CLAUDE_PROJECT_DIR`  | Alias of `TABNINE_PROJECT_DIR` (compatibility). |

Additionally, these variables are expanded in the `command` string before execution:

* `$TABNINE_PROJECT_DIR`
* `$GEMINI_PROJECT_DIR` (expanded to the same value)
* `$CLAUDE_PROJECT_DIR` (expanded to the same value)

Custom environment variables can be added per-hook using the `env` field in hook configuration.

## Hook events

Tabnine CLI supports 11 hook events:

| Event                 | When it fires                                  | Can block? | Common use cases                        |
| --------------------- | ---------------------------------------------- | ---------- | --------------------------------------- |
| `SessionStart`        | When a session begins (startup, resume, clear) | No         | Initialize resources, inject context    |
| `SessionEnd`          | When a session ends (exit, clear)              | No         | Clean up, save state                    |
| `BeforeAgent`         | After user submits prompt, before planning     | Yes        | Add context, validate prompts           |
| `AfterAgent`          | After the model responds                       | Yes        | Review output, force retry              |
| `BeforeModel`         | Before sending request to the LLM              | Yes        | Modify prompts, mock responses          |
| `AfterModel`          | After receiving LLM response                   | Yes        | Filter/redact responses                 |
| `BeforeToolSelection` | Before the LLM selects tools                   | No         | Filter available tools                  |
| `BeforeTool`          | Before a tool executes                         | Yes        | Validate arguments, block dangerous ops |
| `AfterTool`           | After a tool executes                          | Yes        | Process results, inject context         |
| `PreCompress`         | Before context compression                     | No         | Save state, notify user                 |
| `Notification`        | When a system notification occurs              | No         | Forward alerts, logging                 |

## Next steps

* [Configuration](https://docs.tabnine.com/main/getting-started/tabnine-cli/features/hooks/configuration) — Settings schema, matchers, multiple hooks, and CLI commands.
* [Event Reference](https://docs.tabnine.com/main/getting-started/tabnine-cli/features/hooks/event-reference) — Detailed input/output fields for all 11 events.
* [Examples & Best Practices](https://docs.tabnine.com/main/getting-started/tabnine-cli/features/hooks/examples-and-best-practices) — Copy-paste recipes, security model, and testing tips.
