Coding tools · 03

Codex app

Configure a Stop hook for Local and Worktree tasks running on this Mac. The script reads only session, turn, and working-directory fields; it does not pass prompts or response text to bluebell.

Check where the task runs

Only for tasks running on this Mac

Codex environment Can it invoke the local bluebellctl?
Local Yes. The task runs directly in the current project directory.
Worktree Yes. The task runs in an isolated worktree on this Mac.
Cloud Not with this guide. The remote environment cannot access the CLI inside your Mac app.

Current official lifecycle

Stop runs when the main thread stops

The Codex Stop hook runs when a response turn in the main thread stops and receives JSON on stdin. Current fields include session_id, turn_id, cwd and the latest response text. This guide uses only the first three fields.

Step 1

Create a Stop adapter script

codex-app-bluebell.py
#!/usr/bin/env python3
import json, os, subprocess, sys

CLI = "/Applications/bluebellMac.app/Contents/Helpers/bluebellctl"

try:
    payload = json.load(sys.stdin)
except Exception:
    sys.exit(0)

if payload.get("hook_event_name") != "Stop":
    sys.exit(0)

session_id = str(payload.get("session_id") or "")
turn_id = str(payload.get("turn_id") or "")
cwd = str(payload.get("cwd") or "")
if not session_id or not turn_id:
    sys.exit(0)

try:
    subprocess.run([
        CLI, "emit",
"--source", "Codex app",
        "--workspace-name", os.path.basename(cwd) or "Codex",
        "--session-id", session_id,
        "--turn-id", turn_id,
        "--dedupe-key", f"codex-app:{session_id}:{turn_id}",
        "--local",
    ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=3, check=False)
except (OSError, subprocess.SubprocessError):
    pass

Replace CLI with the actual CLI path copied from the bluebell Mac home screen. session_id + turn_id form a stable deduplication ID. Retrying the same hook does not create a second logical notification.

Step 2

Add the hook to your Codex configuration

To apply it to all local projects, merge the following into ~/.codex/hooks.json; to apply it to just one project, use <repo>/.codex/hooks.json. Project-level configuration also requires that project to be trusted.

~/.codex/hooks.json
{
  "description": "Send Codex Stop events to bluebell",
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"/absolute/path/codex-app-bluebell.py\"",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

Replace the script’s absolute path. If the file already contains hooks, merge only the new Stop entry. Do not overwrite the whole file or define both hooks.json and config.toml hooks.

Step 3

Review the hook, then complete a local task

  1. After saving, create a Local or Worktree task. Review and trust the command hook when Codex prompts you.
  2. Complete a short turn and confirm that bluebell Mac shows exactly one local “Codex app” event in recent notifications.
  3. Continue with another turn in the same task. Confirm that the new turn_id produces a new event.
  4. If Codex reports an untrusted hook, use /hooks in Codex CLI to inspect its source and trust status.
  5. After verification, remove --local from the script. Then check Mac remote delivery, the delivery queue, and a real Watch.

Sources

OpenAI official references

  • Codex Hooks Configuration locations, trust, Stop input fields, and concurrent execution
  • Codex environments Where Local, Worktree, and Cloud tasks run