Integrations
Hooks
Lifecycle hooks let you run your own shell commands at key moments in a session — before and after every tool call, when a tool errors, and when a session starts. A PreToolUse hook can even veto a tool call before it runs, so you can enforce policy that lives entirely in your own scripts.
Hooks are the extension point for wiring BharatCode into the tooling you already run by hand — formatters, linters, audit logs, guardrails. They are shell-backed: each hook is just a command (or a script you point at) that BharatCode invokes at the right moment, hands a JSON description of what is happening, and — for the pre-tool case — listens for a decision. No plugin API, no compiled extension. If you can write a shell script, you can write a hook.
The four lifecycle events
A hook subscribes to one of four events. Together they cover the full arc of a tool call, plus the start of a session:
PreToolUse— fires before a tool runs. This is the only event that can block the call; use it for guardrails and policy checks.PostToolUse— fires after a tool completes successfully. Use it for follow-up work like formatting a file the agent just wrote.OnError— fires when a tool returns an error. Use it to log failures or send a notification.OnSession— fires when a session starts. Use it to record context or prime your environment.
OnSession session starts │ ├─ PreToolUse before a tool runs ← can block the call │ │ │ (tool executes) │ │ ├─ PostToolUse after a tool succeeds │ └─ OnError a tool returns an errorMatching a hook to a tool
Each hook carries a match pattern that is tested against the name of the tool involved in the event — for example bash, edit, multiedit, write, web_fetch, and the rest of the built-in tools. The pattern can be a glob (* matches every tool) or a regex (edit|multiedit|write matches all three of the file-writing tools). A hook only fires when its event occurs and the tool name matches its pattern.
What a hook receives
When a hook fires, BharatCode runs its command and writes a JSON payload to the command's stdin. The payload describes the event in full:
{ "event": "PreToolUse", "tool": "bash", "args": { "command": "rm -rf build/" }, "session_id": "01HZX9Q...K3"}event— which lifecycle event fired (PreToolUse,PostToolUse,OnError, orOnSession).tool— the name of the tool the event is about.args— the arguments the tool was called with (forbashthat is thecommand; for the file tools, thepathand edit details).session_id— the id of the current session, so you can correlate hook runs with a single conversation.
Alongside the JSON on stdin, BharatCode exports a set of BHARATCODE_* environment variables into the hook's process, mirroring the same fields. Read whichever is more convenient — pipe the stdin JSON through a parser like jq for structured access, or reach for an environment variable when you just need one value in a one-liner.
Blocking a tool call
A PreToolUse hook can stop a tool from running. Because the hook is handed the call's payload before the tool executes, it can inspect exactly what the agent is about to do and return a block decision. The decision is JSON written to the hook's stdout: set block to true to veto the call, and add an optional reason string that is surfaced back to the agent so it understands why.
{"block": true, "reason": "Refusing to run a recursive force delete."}Allowing is the default
PreToolUse hooks are consulted for a decision. If the hook prints nothing, an empty object, or anything without block: true, the call proceeds. The other three events run for their side effects and cannot stop a tool.Configuring hooks
Hooks live in your configuration as a hooks array. Each entry names the event to subscribe to, the match pattern for the tool, and the command to run. Put hooks in your global config (~/.config/bharatcode/config.json) to apply them everywhere, or in a project's ./.bharatcode.json to scope them to one repository.
Example: block a recursive delete
A common guardrail is to make sure the agent never runs a recursive force delete. Register a PreToolUse hook on the bash tool that inspects the command and vetoes it when it looks like an rm -rf:
{ "hooks": [ { "event": "PreToolUse", "match": "bash", "command": "~/.bharatcode/hooks/guard-bash.sh" } ]}The script reads the JSON payload from stdin, pulls out args.command with jq, and prints a block decision when the command matches:
#!/usr/bin/env bash# guard-bash.sh — veto destructive recursive deletes.# stdin: the PreToolUse JSON payload. stdout: a JSON block decision. payload="$(cat)"command="$(printf '%s' "$payload" | jq -r '.args.command')" if printf '%s' "$command" | grep -Eq 'rm[[:space:]]+(-[a-zA-Z]*r[a-zA-Z]*[[:space:]]+)?-?[a-zA-Z]*f'; then echo '{"block": true, "reason": "Refusing to run a recursive force delete."}' exit 0fi # Nothing printed (or an empty object) means: allow the call.echo '{}'Now whenever the agent tries to shell out to a recursive force delete, the call is vetoed and the agent is told why — without you having to be at the keyboard to deny it.
Hooks complement permissions
PreToolUse hook lets you encode policy that always applies — even in auto or full mode — in your own code.Example: run gofmt after every edit
A PostToolUse hook is the natural place for follow-up work. Here is one that formats a Go file the moment the agent writes to it, so the working tree never drifts from gofmt:
{ "hooks": [ { "event": "PostToolUse", "match": "edit|multiedit|write", "command": "~/.bharatcode/hooks/gofmt-on-write.sh" } ]}The match regex catches all three file-writing tools — edit, multiedit, and write — so the formatter runs no matter how the change was made. The script reads the file path from the payload and only acts on .go files:
#!/usr/bin/env bash# gofmt-on-write.sh — format Go files right after the agent writes them. payload="$(cat)"path="$(printf '%s' "$payload" | jq -r '.args.path')" case "$path" in *.go) gofmt -w "$path" ;;esacExample: a session-start hook
OnSession fires once when a session begins, which makes it handy for recording context. Because no specific tool is involved, match on * and run any command you like:
{ "hooks": [ { "event": "OnSession", "match": "*", "command": "git status --short >> ~/.bharatcode/session-start.log" } ]}Tips for writing hooks
- Parse stdin, don't guess. Read the JSON payload (with
jqor your language of choice) rather than re-deriving values — it is the source of truth for theevent,tool, andargs. - Keep them fast. Pre-tool hooks run on the critical path of every matching call, so a slow script slows the agent. Do the minimum needed to make a decision.
- Fail open, on purpose. A
PreToolUsehook only blocks when it explicitly printsblock: true. Make sure your guardrails print that decision on the paths you care about — and let everything else fall through to allow. - Make them portable. Point
commandat a checked-in script so the same hook works for everyone on the team when it ships in a project's./.bharatcode.json.
Between guardrails on PreToolUse, cleanup on PostToolUse, and logging on OnError and OnSession, hooks give you a precise, scriptable seam to make BharatCode behave the way your codebase needs — entirely in your own shell.