For the complete documentation index, see llms.txt. This page is also available as Markdown.

Examples & Best Practices

Copy-paste hook recipes, security model, and testing tips for Tabnine CLI.

Examples

Blocking dangerous shell commands

.tabnine/agent/settings.json

{
  "hooks": {
    "BeforeTool": [
      {
        "matcher": "run_shell_command",
        "sequential": true,
        "hooks": [
          {
            "type": "command",
            "command": "node ./hooks/block-dangerous.js",
            "name": "block-dangerous",
            "description": "Block dangerous shell commands"
          }
        ]
      }
    ]
  }
}

hooks/block-dangerous.js

Restricting file writes to a directory

hooks/enforce-directory.js

Logging all tool calls

.tabnine/agent/settings.json

hooks/log-tools.js

Adding context to every prompt

.tabnine/agent/settings.json

hooks/add-context.js

Auto-running linter after file changes

.tabnine/agent/settings.json

hooks/auto-lint.js

Security and trust

Best practices

1

Keep hooks fast

Hooks run synchronously and block the agent loop. Aim for sub-second execution. Use the timeout field to prevent hangs.

2

Always output valid JSON to stdout

Any non-JSON output to stdout will cause the hook to be treated as a plain-text system message with "allow" decision.

3

Use stderr for logging

Never use console.log() or echo for debug output — use console.error() or >&2 to write to stderr instead.

4

Prefer exit code 0 with decision: "deny" over exit code 2

For intentional blocks, exit code 0 with JSON output gives you full control over the feedback message and behavior.

5

Use descriptive name fields

The name appears in logs and can be used in hooksConfig.disabled to selectively turn off hooks without removing them.

6

Test hooks independently

Before adding them to settings:

7

Set the sequential flag

Use it when hook execution order matters (e.g., a validation hook must run before a logging hook).

8

Read stdin via the stream API

Use process.stdin.on('data', ...) rather than fs.readFileSync('/dev/stdin') for cross-platform compatibility.

Was this helpful?