The Bridge

A fully customizable dynamic hook system for deep integration.

Every time your agent uses a tool, submits a prompt, or requests a permission, it fires a hook event. Ragtime’s Bridge functionality provides a simple shell integration rt hook and rt statusline that is used to intercept those commands and provide responses if desired. This allows us to implement advanced telemetry, especially with Claude Code which also provides statusline functionality we can intercept, as well as integrate with the same Starlark rule system used in The Score virtual filesystem to customize the agent environment on the fly.

How It Works#

When you configure your agent’s hooks to call rt hook, the flow is:

  1. The agent serializes the event to JSON and passes it to rt hook via stdin
  2. rt hook forwards the event to the ragtime daemon over a Unix socket
  3. The daemon evaluates all matching rules in priority order
  4. Each rule’s Starlark script can read the event and write to the response
  5. The final response is serialized to JSON and written to stdout
  6. The agent reads the response and acts on it

The whole round-trip happens within the agent’s hook timeout window — typically a few hundred milliseconds.

Setting Up The Bridge#

Claude Code#

Add to .claude/settings.local.json (project-level) or ~/.claude/settings.json (global):

{
  "hooks": {
    "PreToolUse":       [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "rt hook --agent claude --event pre-tool-use" }] }],
    "PostToolUse":      [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "rt hook --agent claude --event post-tool-use" }] }],
    "PermissionRequest":[{ "matcher": ".*", "hooks": [{ "type": "command", "command": "rt hook --agent claude --event permission-request" }] }],
    "Stop":             [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "rt hook --agent claude --event stop" }] }],
    "SubagentStop":     [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "rt hook --agent claude --event subagent-stop" }] }],
    "Notification":     [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "rt hook --agent claude --event notification" }] }],
    "SessionStart":     [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "rt hook --agent claude --event session-start" }] }],
    "UserPromptSubmit": [{ "matcher": ".*", "hooks": [{ "type": "command", "command": "rt hook --agent claude --event user-prompt-submit" }] }]
  },
  "statusLine": {
    "type": "command",
    "command": "rt statusline --agent claude"
  }
}

The statusLine entry is separate from hooks — it records per-turn cost, token usage, and context window percentage to SQLite and is what populates ~/.ragtime/fs/active/. Note the camelCase: statusLine, not statusline.

An Example: Interactive Tool Approval#

The most immediate thing The Bridge enables is interactive control over what your agent is allowed to do. Here’s how it works with Claude Code’s PermissionRequest event — the event that fires when the agent’s built-in permission system would prompt the user for approval.

Step 1: The agent hits a permission boundary#

Say the agent tries to run a Bash command it hasn’t been explicitly allowed before. Claude Code fires a PermissionRequest hook event with the tool name and input:

{
  "event": "permission-request",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf ./build"
  }
}

rt hook receives this and dispatches it to the daemon.

Step 2: The rule matches and evaluates#

name: review-permission
match:
  event: permission-request
actions:
  - type: starlark
    script: |
      tool = event.tool_name
      cmd = event.tool_input.get("command", "")
      path = event.tool_input.get("file_path", "")

      detail = cmd if cmd else path
      if detail:
          text = "## Permission Request\n\n**Tool:** `" + tool + "`\n\n```\n" + detail + "\n```\n\nAllow this?"
      else:
          text = "## Permission Request\n\n**Tool:** `" + tool + "`\n\nAllow this?"

      if tui.connected():
          answer = response.prompt(
              text=text,
              type="approve_deny_cancel",
              default="deny",
              timeout=30,
          )
          if answer == "approve":
              response.approve()
          elif answer == "deny":
              response.deny("denied via TUI review")
      # If no TUI or PWA, fall through to agent's default behavior

What happens: The rule builds a formatted message describing the tool and input, then calls response.prompt(). This opens an interactive modal in the Ragtime TUI or PWA dashboard and blocks until you respond — or the timeout elapses.