Configuration

Lifecycle hooks

Run shell commands automatically at key points in a Bob Shell session to log activity, inject context, or block actions based on your own logic.

Lifecycle hooks let you run shell commands at specific points in a Bob Shell session. Use them to log activity, inject context into the model, gate or block actions, or start follow-up automation, all without modifying Bob Shell itself.

Supported hooks

HookWhen it runsBlockingStdout behaviour
SessionStartOnce when a session beginsNoInjected as context
UserPromptSubmitEach time you submit a promptYes (exit 2)Injected as context
PreToolUseBefore a matched tool runsYes (exit 2)Ignored
PostToolUseAfter a matched tool completesNoIgnored
StopWhen the agent stopsNoIgnored

Configuration

Hooks are defined under the hooks key in your settings.json. Bob Shell merges hooks from two locations:

ScopeFile
Global (all workspaces)~/.bob/settings/settings.json
Workspace (current project).bob/settings.json

Global hooks always run. Workspace hooks are merged on top of global hooks and apply only to the current project.

Important:

Workspace hooks only run in trusted folders. If the current folder is untrusted, the .bob/settings.json file is not loaded and workspace-level hooks are silently skipped. Global hooks in ~/.bob/settings/settings.json are unaffected by folder trust.

For details on how settings files are located and loaded, see Configuring Bob Shell.

Hook schema

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "^write_file$",
        "hooks": [
          {
            "type": "command",
            "command": "sh .bob/hooks/check.sh",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

Configuration fields

FieldTypeDefaultDescription
type"command"(none)Required. Only command is supported.
commandstring(none)Required. The shell command to run. Runs via sh -c on macOS/Linux, cmd /c on Windows.
matcherstring(none)Optional. A regex matched against the tool name (PreToolUse, PostToolUse only). Omit to match all tools.
timeoutnumber10Seconds before the hook is stopped. Set to 0 to disable the timeout.

Hook reference

Runs once when a new session begins, before the first turn.

Stdin schema

{
  "event": "string",
  "session_id": "string"
}

Example payload

{
  "event": "SessionStart",
  "session_id": "ses_01abc123"
}

Stdout: Written to the model's context as additional session information.

Blocking: Exit code 2 is not supported. The session always starts. Other non-zero exits are logged and ignored.

Runs each time you submit a prompt, before it is sent to the model.

Stdin schema

{
  "event": "string",
  "session_id": "string",
  "prompt": "string"
}

Example payload

{
  "event": "UserPromptSubmit",
  "session_id": "ses_01abc123",
  "prompt": "Refactor the auth module"
}

Stdout: Written to the model's context alongside the prompt.

Blocking: Exit code 2 blocks the prompt from being sent. Bob Shell shows an error and the prompt is not submitted.

Runs before a matched tool runs, giving you a chance to inspect or block the action.

Stdin schema

{
  "event": "string",
  "session_id": "string",
  "tool": "string",
  "input": "object"
}

Example payload

{
  "event": "PreToolUse",
  "session_id": "ses_01abc123",
  "tool": "write_file",
  "input": {
    "path": "src/index.ts",
    "content": "..."
  }
}

Stdout: Ignored.

Blocking: Exit code 2 prevents the tool from running. Bob Shell reports the tool as blocked and continues the session.

Runs after a matched tool completes, regardless of whether it succeeded.

Stdin schema

{
  "event": "string",
  "session_id": "string",
  "tool": "string",
  "input": "object",
  "output": "string"
}

Example payload

{
  "event": "PostToolUse",
  "session_id": "ses_01abc123",
  "tool": "write_file",
  "input": {
    "path": "src/index.ts",
    "content": "..."
  },
  "output": "File written successfully"
}

Stdout: Ignored.

Blocking: Exit code 2 has no effect. The tool has already run.

Runs when the agent stops, after the final turn completes.

Stdin schema

{
  "event": "string",
  "session_id": "string"
}

Example payload

{
  "event": "Stop",
  "session_id": "ses_01abc123"
}

Stdout: Ignored.

Blocking: Exit code 2 has no effect. The session has already ended.

Exit codes and blocking

Exit codeBehaviourApplies to
0Success: hook ran without issueAll hooks
2Block: stop the current actionUserPromptSubmit, PreToolUse
Any other non-zeroNon-blocking failure: logged and ignoredAll hooks
Note:

Only UserPromptSubmit and PreToolUse support blocking. Exit code 2 from SessionStart, PostToolUse, or Stop is treated as a non-blocking failure.

Command details

  • Working directory: Commands run from the task working directory (the folder Bob Shell is working in).
  • Default timeout: 10 seconds. Override per-hook with the timeout field. Set timeout to 0 to disable the timeout entirely.
  • Stderr: Written to Bob Shell's logs but does not affect the hook result.
  • Shell: Commands run via sh -c on macOS and Linux, and cmd /c on Windows.

Getting started

Open or create your global settings file at ~/.bob/settings/settings.json.

Add a hooks key with the hook you want to use. The example below runs a script before every write_file call:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "^write_file$",
        "hooks": [
          {
            "type": "command",
            "command": "sh ~/.bob/hooks/log-write.sh"
          }
        ]
      }
    ]
  }
}

Create the script file. This minimal script logs the incoming JSON payload:

#!/bin/sh
# ~/.bob/hooks/log-write.sh
cat >> ~/.bob/hooks/write-log.txt

Start a Bob Shell session and use the matched tool. Check ~/.bob/hooks/write-log.txt to confirm the hook ran and the payload was written.

Examples

Log all hook input

Write every hook's stdin to a file for debugging:

#!/bin/sh
# Append the incoming JSON payload with a timestamp
echo "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> ~/.bob/hooks/debug.log
cat >> ~/.bob/hooks/debug.log
echo "" >> ~/.bob/hooks/debug.log

Configure this under any hook:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [{ "type": "command", "command": "sh ~/.bob/hooks/debug.sh" }]
      }
    ]
  }
}

Inject session context

Return text from a SessionStart hook to add it to the model's context:

#!/bin/sh
# Output project metadata for the model to use
echo "Project: $(basename $PWD)"
echo "Git branch: $(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"
echo "Node version: $(node --version 2>/dev/null || echo 'not installed')"

Block a prompt

Exit with code 2 from a UserPromptSubmit hook to prevent a prompt from being sent:

#!/bin/sh
# Block prompts containing the word "delete"
PROMPT=$(cat | python3 -c "import sys,json; print(json.load(sys.stdin)['prompt'])")
case "$PROMPT" in
  *delete*|*DELETE*)
    echo "Prompt blocked: contains 'delete'" >&2
    exit 2
    ;;
esac

Block a matched tool

Exit with code 2 from a PreToolUse hook to prevent a specific tool from running:

#!/bin/sh
# Block write_file operations on files outside the src/ directory
PATH_VAL=$(cat | python3 -c "import sys,json; print(json.load(sys.stdin)['input'].get('path',''))")
case "$PATH_VAL" in
  src/*) ;;
  *)
    echo "Blocked: writes outside src/ are not allowed" >&2
    exit 2
    ;;
esac

Run follow-up automation from Stop

Use Stop to start cleanup or reporting after a session ends:

#!/bin/sh
# Commit any staged changes after the agent finishes
cd "$PWD"
git diff --cached --quiet || git commit -m "chore: auto-commit from Bob Shell session"

Current limitations

Only command hooks and the five hook types listed above are supported in this release. The following are not yet available:

  • Hook types other than command: function hooks, inline script hooks, and similar are not supported.
  • Scheduled hooks: hooks cannot be set to run on a timer or in response to an external event.
  • Input rewriting: hooks cannot modify the prompt or tool input before it reaches the model.
  • Sandboxed running: hooks run with your full user permissions; no isolation is applied.
  • Dedicated hook telemetry: hook activity is not tracked separately in session analytics.
  • Blocking from PostToolUse or Stop: exit code 2 has no effect for these hooks.
How is this topic?