{"slug": "show-hn-agenttab-status-icons-for-claude-code-codex-and-opencode-tabs", "title": "Show HN: Agenttab – Status Icons for Claude Code, Codex and OpenCode Tabs", "summary": "Developer marciob released agenttab, a macOS tool that displays AI agent state as icons in terminal tab titles for Claude Code, Codex, and OpenCode. The tool maps states to icons — a blank title when the agent waits for a prompt, ◐/◑ while working, ⚠ when awaiting permission, and the shell title when the session ends — and installs via a curl script into ~/.local/bin. It requires jq and, for Codex 0.157 or later, lsof, and works by adding hooks to each agent's settings file.", "body_md": "agenttab shows the state of an AI agent in the title of its terminal tab. It\nworks with Claude Code, Codex, and OpenCode. The title is `<state icon> <text>`,\nfor example `◑ Terminal title customization`.\n\n| Icon | State | \n|---|---|\n| (none) | The agent waits for your next prompt. | \n| `◐``◑` in turn | The agent works. | \n| `⚠` | The agent waits for your permission. | \n| (the shell title) | The session ends. | \n\nThe text is the title of the conversation. If the conversation has no title, the text is the name of the directory.\n\n- macOS. Linux can work, but nobody has tested it.\n- `jq` . Install it with`brew install jq` .\n- `lsof` , only for Codex. macOS has it.\n\n1. \nRun the installer: \n\n```\ncurl -fsSL https://raw.githubusercontent.com/marciob/agenttab/main/install.sh | sh\n```\n\n The installer puts `agenttab` in`~/.local/bin` . It does not change the\nsettings of an agent. From a clone, run`./install.sh` . Then`git pull` updates the installed files.\n2. \nIf `~/.local/bin` is not on the PATH, add it to`~/.zshrc` :`export PATH=\"$HOME/.local/bin:$PATH\"`\n3. \nAdd the hooks of your agent: [Claude Code](#claude-code) ,[Codex](#codex) ,\nor[OpenCode](#opencode) .\n4. \nIn Cursor or VS Code, set `\"terminal.integrated.tabs.title\": \"${sequence}\"` .\nThen the tab shows the title that agenttab writes.\n5. \nOptional: add `source ~/.config/agenttab/agenttab.zsh` to`~/.zshrc` . Then a\nterminal with no agent shows the name of the directory, or the running\ncommand. It also shows a[name from Cursor](#a-name-from-cursor) .\n\n1. \nAdd these hooks to `~/.claude/settings.json` . If the file already has a`hooks` object, add each entry to the list of the same event.## The hooks for Claude Code\n\n```\n{\n  \"hooks\": {\n    \"SessionStart\": [\n      { \"matcher\": \"startup|resume|clear|compact\",\n        \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook idle\", \"timeout\": 5 }] }\n    ],\n    \"UserPromptSubmit\": [\n      { \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook busy\", \"timeout\": 5 }] }\n    ],\n    \"PostToolUse\": [\n      { \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook busy\", \"timeout\": 5 }] }\n    ],\n    \"Notification\": [\n      { \"matcher\": \"permission_prompt\",\n        \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook wait\", \"timeout\": 5 }] }\n    ],\n    \"Stop\": [\n      { \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook idle\", \"timeout\": 5 }] }\n    ],\n    \"SessionEnd\": [\n      { \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook end\", \"timeout\": 5 }] }\n    ]\n  }\n}\n```\n\n2. \nAdd `export CLAUDE_CODE_DISABLE_TERMINAL_TITLE=1` to`~/.zshrc` . Then\nClaude Code does not write its own title.\n3. \nStart a new session. Sessions that already run do not load the new hooks.\n\nEach account has its own settings file, in the directory of `CLAUDE_CONFIG_DIR`.\nAdd the hooks to the settings file of each account, for example\n`~/.claude-work/settings.json`. An account with no hooks shows no title,\nbecause `CLAUDE_CODE_DISABLE_TERMINAL_TITLE` applies to all accounts.\n\nType `/rename <name>` in Claude Code. agenttab then shows that name in place of\nthe title that Claude Code made. The name shows at the next change of state.\n\nThis section applies to Codex 0.157 or later.\n\n1. \nAdd these hooks to `~/.codex/hooks.json` :## The hooks for Codex\n\n```\n{\n  \"hooks\": {\n    \"SessionStart\": [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook idle codex\", \"timeout\": 5 }] }],\n    \"UserPromptSubmit\": [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook busy codex\", \"timeout\": 5 }] }],\n    \"PostToolUse\": [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook busy codex\", \"timeout\": 5 }] }],\n    \"PermissionRequest\": [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook wait codex\", \"timeout\": 5 }] }],\n    \"Stop\": [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook idle codex\", \"timeout\": 5 }] }],\n    \"Interrupt\": [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook idle codex\", \"timeout\": 3 }] }],\n    \"SessionEnd\": [{ \"hooks\": [{ \"type\": \"command\", \"command\": \"agenttab hook end codex\", \"timeout\": 5 }] }]\n  }\n}\n```\n\n The timeout of the `Interrupt` hook must be 3 seconds or less.\n2. \nAdd this to `~/.codex/config.toml` . Then Codex does not write its own title.\n\n```\n[tui]\nterminal_title = []\n```\n\n3. \nStart Codex and type `/hooks` . Codex runs a new hook only after you trust\nit, so trust each agenttab hook.\n\nThe text of the title is the name of the thread, from\n`~/.codex/session_index.jsonl`.\n\nThe interactive Codex runs its turns in a shared daemon, and that daemon runs\nthe hooks. The daemon has no terminal. Thus agenttab finds the `codex` process\nthat has a terminal and runs in the directory of the thread. If two Codex\nsessions run in the same directory, agenttab cannot find the correct tab. Then\nit writes no title.\n\nOpenCode has no hooks. The plugin `opencode/agenttab.js` reads the events of\nthe session and runs `agenttab set <state> <text>`.\n\n1. Clone this repository.\n2. Link the plugin: `ln -s \"$PWD/opencode/agenttab.js\" ~/.config/opencode/plugins/`\n3. Add `export OPENCODE_DISABLE_TERMINAL_TITLE=1` to`~/.zshrc` .\n\nThe plugin ignores the busy and idle states of a subagent session. A permission request from a subagent shows the wait icon, because it waits for you too.\n\nThe extension in `cursor/` adds the command **Rename (agenttab)**. To use it,\nright-click the tab of a terminal. The command palette also has it, as\n**Terminal: Rename (agenttab)**. An empty name gives the automatic title back.\n\nThe name comes before `/rename` and before the automatic title. The extension\nwrites the name to `~/.cache/agenttab/<tty>.name` and runs `agenttab refresh`.\nThe zsh hook reads the same file, so a terminal with no agent also shows the\nname.\n\nDo not use the built-in **Rename** item. It makes the name of the tab static.\nThen the tab ignores the title that agenttab writes, and the icon does not\nshow. A rename with an empty name does not make the tab dynamic again (VS Code\nissue #333933). To recover, close that tab and open a new one.\n\nTo build and install the extension:\n\n```\ncd cursor\nnpx @vscode/vsce package --allow-missing-repository\ncursor --install-extension agenttab-0.1.0.vsix\n```\n\nWrite the file `~/.config/agenttab/config`. Each line is optional:\n\n```\nICON_IDLE=\"\"\nICON_BUSY=\"◐ ◑\"         # several frames, separated by spaces, make an animation\nICON_WAIT=\"⚠\"\nSPIN_INTERVAL=\"0.8\"     # seconds between two frames\nTEXT=\"topic\"            # topic: the title of the conversation. folder: the name of the directory.\n```\n\nAn empty icon gives a title with no icon.\n\nThe hook is a shell script. It runs for approximately 60 ms at each change of state, and then it stops. The hook prints nothing, so it adds no tokens to the conversation.\n\nWhile the agent works, one shell loop shows the frames. It uses approximately 2 MB. It stops when the agent stops work, when the agent process ends, or when the terminal closes. A busy icon with one frame starts no loop.\n\nMIT", "url": "https://wpnews.pro/news/show-hn-agenttab-status-icons-for-claude-code-codex-and-opencode-tabs", "canonical_source": "https://github.com/marciob/agenttab", "published_at": "2026-09-27 23:38:44+00:00", "updated_at": "2026-09-28 00:01:22.733380+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["agenttab", "Claude Code", "Codex", "OpenCode", "marciob", "Cursor", "VS Code"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-agenttab-status-icons-for-claude-code-codex-and-opencode-tabs", "markdown": "https://wpnews.pro/news/show-hn-agenttab-status-icons-for-claude-code-codex-and-opencode-tabs.md", "text": "https://wpnews.pro/news/show-hn-agenttab-status-icons-for-claude-code-codex-and-opencode-tabs.txt", "jsonld": "https://wpnews.pro/news/show-hn-agenttab-status-icons-for-claude-code-codex-and-opencode-tabs.jsonld"}}