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.

Managing hooks

Run /hooks to open a dialog that lists all configured global and workspace hooks. The dialog shows each hook's event type, matcher, scope, status, and command. You can toggle individual hooks on or off. The list refreshes automatically when hook settings change on disk.

Note:

To enforce hooks across all users in your organization, see EnforcedHooks.

Supported hooks

HookWhen it runsBlockingStdout behavior
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 and 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?