DEV Community

Little Pan
Little Pan

Posted on Edited on Originally published at ponymux.com

How to know which Claude Code or Codex agent needs you

You start three agents and go read something. Twenty minutes later one of them has been waiting ten minutes for approval to run npm install. Another finished long ago. The third is still working. You find out by clicking through every tab.

Each agent can tell you when it needs you. The defaults just need a little help:

  • Claude Code: notifications come from your terminal. Pick the channel in /config, or add a Notification hook.
  • Codex: desktop notifications are on by default, but only while the terminal isn't in front. Tune them under [tui] in ~/.codex/config.toml.
  • Grok Build: notifications are on by default too, also only while the terminal isn't in front. Tune them under [ui.notifications] in ~/.grok/config.toml.
  • Name the project: a short hook script puts the folder name in the notification title, so you know which agent it is.
  • The catch: a notification tells you something happened. It doesn't take you back to the right tab.

When do Claude Code, Codex, and Grok notify you?

Claude Code sends a notification when it needs permission to use a tool, and when it has finished and you've been idle for a minute. It sends it through your terminal. In iTerm2, Ghostty, and kitty that's a desktop notification. In Apple Terminal it's the bell. Change the channel with /config inside a session. The Local notifications setting offers the terminal-specific options, the bell, or off.

Codex sends a desktop notification when a turn finishes and when it asks for approval to run a command or edit files. By default it only does this while the terminal is in the background, which is usually what you want. The settings live in ~/.codex/config.toml:

[tui]
notifications = true                 # or a list, such as ["approval-requested"]
notification_condition = "unfocused" # "always" to notify even when the terminal is in front
Enter fullscreen mode Exit fullscreen mode

Grok Build sends a notification when a turn finishes and when it needs approval. Like Claude Code, it goes through your terminal: a desktop notification in iTerm2, Ghostty, kitty, and WezTerm, the bell in Apple Terminal. It also waits until the terminal has been in the background for a few seconds. The settings live in ~/.grok/config.toml:

[ui.notifications]
events = ["turn_complete", "approval_required"]
condition = "unfocused" # "always" to notify even when the terminal is in front, "never" to turn them off
Enter fullscreen mode Exit fullscreen mode

All three work well with one agent. With four, "Claude Code needs your permission" doesn't say which of the four.

How do I see which project a notification is about?

Use a hook. All three agents can run a command on these events and pass it a JSON description that includes the working directory. This script turns that into a notification titled with the agent and folder name:

#!/bin/sh
# ~/bin/agent-notify.sh — usage: agent-notify.sh "Claude Code"
[ -n "$GROK_HOOK_EVENT" ] && set -- Grok # Grok runs the Claude Code hook too
jq -r --arg agent "$1" '
  (.message // (if .hook_event_name == "PermissionRequest" then "Needs your approval" else "Finished its turn" end)) as $body
  | "display notification \($body | @json) with title \("\($agent) · \(.cwd | split("/") | last)" | @json)"
' | osascript >/dev/null
Enter fullscreen mode Exit fullscreen mode

Save it and run chmod +x ~/bin/agent-notify.sh. It needs jq, which ships with macOS 15 and later.

For Claude Code, add a Notification hook to ~/.claude/settings.json:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt|idle_prompt",
        "hooks": [{ "type": "command", "command": "~/bin/agent-notify.sh 'Claude Code'" }]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

For Codex, add Stop and PermissionRequest hooks to ~/.codex/hooks.json:

{
  "hooks": {
    "Stop": [{ "hooks": [{ "type": "command", "command": "~/bin/agent-notify.sh Codex" }] }],
    "PermissionRequest": [{ "hooks": [{ "type": "command", "command": "~/bin/agent-notify.sh Codex" }] }]
  }
}
Enter fullscreen mode Exit fullscreen mode

Codex asks you to review new hooks the next time it starts, and /hooks inside a session shows what's installed. The script prints nothing, so it doesn't change whether a permission request is approved. Now a notification reads "Claude Code · api: Claude needs your permission to use Bash", and you know where to go. If you now get two notifications for the same event, turn the built-in one off: Local notifications in Claude Code's /config, or notifications = false under [tui] for Codex.

Grok Build needs no hook file of its own. It also runs the hooks in ~/.claude/settings.json, so the Claude Code hook above already fires in Grok sessions. Grok sets GROK_HOOK_EVENT when it runs a hook, and the line in the script that checks it makes those notifications say "Grok" instead of "Claude Code". To turn off Grok's built-in notification, set condition = "never" under [ui.notifications].

Why does a notification still leave me hunting for the tab?

Because it comes from the agent, not from the window it runs in. Clicking a display notification banner opens Script Editor. Clicking the terminal's own notification brings the terminal forward, but not necessarily the right tab. With several agents per window, you still scan tabs for the one with the prompt.

A hook can't fix this, and neither can the terminal settings. What's missing is a place that tracks each agent's state next to the terminal it runs in.

Can I see every agent's status at a glance?

That's what PonyMux does. Each Terminal running Claude Code, Codex, or Grok shows the agent's state on its row in the Terminal list:

  • Waiting for you: approval or an answer is needed.
  • Running: the agent is working.
  • Unread: it finished something you haven't looked at yet.
  • Standing by: ready for the next prompt.
  • Will come back: the agent is idle, but background work or a subagent it started is still running.
  • Error: it stopped because something went wrong.

Agent status on each Terminal row: running, waiting for you, standing by, unread, and will come back.

When any Terminal in the list has a status, a badge at the bottom of the list shows the count. Click it to jump to the next one, and click again to cycle through the rest. The list order doesn't change.

To see only the agents, select Agents in the sidebar. It lists every Terminal where Claude Code, Codex, or Grok is running right now, plus the ones whose agent was still active when you last quit PonyMux, so four agents spread across different projects and Lists show up in one place. Running agents come first, then the ones left from your last quit, each group in your usual Terminal order. It isn't a folder you manage: Terminals join and leave it as their agents start and stop.

The Agents collection in the sidebar lists the Terminals running Claude Code, Codex, or Grok, across every project and List.

PonyMux also sends its own notifications for finished turns, permission requests, input requests, and errors, and you can set each kind to Off, Silent, or Sound in Settings → General. Clicking a notification opens PonyMux on the Terminal it's about, which is the step a hook can't do. Agent status needs the Claude Code, Codex, or Grok integration, which you connect once from the bottom of the sidebar.

For every status and notification setting, see Agent Status and Notifications.


Originally published on ponymux.com, where the demos are interactive.

Top comments (0)