# 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](/main/getting-started/tabnine-cli/features/hooks/configuration.md) — Settings schema, matchers, multiple hooks, and CLI commands.
* [Event Reference](/main/getting-started/tabnine-cli/features/hooks/event-reference.md) — Detailed input/output fields for all 11 events.
* [Examples & Best Practices](/main/getting-started/tabnine-cli/features/hooks/examples-and-best-practices.md) — Copy-paste recipes, security model, and testing tips.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tabnine.com/main/getting-started/tabnine-cli/features/hooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
