npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

terminal-tab-status

v1.0.1

Published

Terminal tab title sync for AI CLI tools (OpenCode, GitLab Duo). Animated spinner while AI processes, checkmark when done, auto-clears on tab focus.

Downloads

232

Readme

terminal-tab-status

See at a glance which AI session is running, finished, or idle -- right in your terminal tab.

Works with OpenCode and GitLab Duo CLI.

| State | Tab title | When | |-------|-----------|------| | Idle | Session Title | Default | | Running | ⠋ Session Title | AI is processing (animated Braille spinner) | | Done | ✓ Session Title | AI finished (auto-clears when you focus the tab) |

Quick start

# Both OpenCode and Duo CLI
npx terminal-tab-status

# OpenCode only
npx terminal-tab-status opencode

# Duo CLI only
npx terminal-tab-status duo

Restart OpenCode and/or your terminal to activate.

How each tool is configured

OpenCode

The installer copies a TypeScript plugin file into the OpenCode plugins directory:

~/.config/opencode/plugins/terminal-tab-status.ts

OpenCode auto-loads all .ts files from this directory on startup. The plugin listens for session lifecycle events (session.created, session.updated, session.status, session.idle, session.error) and writes OSC escape sequences directly to /dev/tty to set the terminal tab title. Writing to /dev/tty bypasses the TUI that owns process.stdout.

No config files to edit. Just install and restart OpenCode.

GitLab Duo CLI

The installer does two things:

  1. Copies a CommonJS hook module to ~/.config/opencode/bin/duo-hook.cjs
  2. Appends a NODE_OPTIONS line to ~/.zshrc:
    export NODE_OPTIONS="${NODE_OPTIONS} --require ~/.config/opencode/bin/duo-hook.cjs"

The hook is loaded into every Node.js process via --require, but immediately bails out unless it detects a @gitlab/duo-cli process (checked via process.argv[1]). When active, it:

  • Intercepts fs.writeSync to detect Duo's TerminalProgressService OSC 9;4 progress sequences (busy / idle / error)
  • Intercepts process.stderr.write to capture the session title set via OSC 0
  • Writes enhanced titles (spinner/checkmark) to its own /dev/tty file descriptor

No modifications to Duo CLI source. The hook reads the same escape sequences Duo already writes, then overlays its own. This approach is necessary because the Duo CLI has no external plugin or event API -- the LSP server only runs inside IDE extensions (VS Code, JetBrains), not the CLI, leaving raw escape sequence interception as the only integration path (feature request).

Shared: focus detection (macOS)

Both tools share a compiled Swift binary that queries the macOS Accessibility API to detect which terminal tab is currently focused. When the AI finishes:

  • If the tab is focused: title resets immediately (you're already looking at it)
  • If the tab is not focused: shows prefix, polls every 500ms, clears when you switch to it

The binary auto-detects the frontmost terminal and uses the appropriate strategy:

| Terminal | Detection method | |----------|-----------------| | JetBrains IDEs (PyCharm, IntelliJ, etc.) | AXDescription from Terminal tool window group | | Terminal.app | AXTitle from focused window | | iTerm2 | AXTitle from focused window | | VS Code | AXTitle from focused window | | Ghostty | AXTitle from focused window | | Kitty | AXTitle from focused window | | Wezterm | AXTitle from focused window | | Other | Falls back to frontmost app's window title |

Install options

# Global (default) -- installs to ~/.config/opencode/
npx terminal-tab-status
npx terminal-tab-status opencode
npx terminal-tab-status duo
npx terminal-tab-status opencode duo

# Project-local -- installs to .opencode/ in current directory
npx terminal-tab-status --local
npx terminal-tab-status opencode --local

Uninstall

npx terminal-tab-status uninstall              # Both
npx terminal-tab-status uninstall opencode     # OpenCode only
npx terminal-tab-status uninstall duo          # Duo only
npx terminal-tab-status uninstall --local      # Project-local

What gets installed

Global install

~/.config/opencode/
├── plugins/
│   └── terminal-tab-status.ts        # OpenCode plugin (auto-loaded)
└── bin/
    ├── active-terminal-tab            # Compiled Swift binary
    ├── active-terminal-tab.swift      # Swift source
    └── duo-hook.cjs                   # Duo CLI hook

Plus a NODE_OPTIONS line in ~/.zshrc (Duo only).

Project-local install

.opencode/
├── plugins/
│   └── terminal-tab-status.ts
└── bin/
    ├── active-terminal-tab
    ├── active-terminal-tab.swift
    └── duo-hook.cjs

Terminal setup

JetBrains IDEs (PyCharm, IntelliJ, etc.)

JetBrains terminals require one setting to allow processes to set tab titles:

  1. Settings > Advanced Settings
  2. Search terminal.show.application.title
  3. Enable the checkbox
  4. Restart the terminal

All other terminals work out of the box.

macOS Accessibility (optional)

For the checkmark to auto-clear on tab focus, grant Accessibility access:

  1. System Settings > Privacy & Security > Accessibility
  2. Enable your terminal application

If you skip this, the checkmark clears on your next message instead of on tab switch. Everything else works normally.

Compatibility

| Terminal | Title sync | Spinner | Focus detection | |----------|-----------|---------|-----------------| | PyCharm / IntelliJ (macOS) | Yes | Yes | Yes | | Terminal.app | Yes | Yes | Yes | | iTerm2 | Yes | Yes | Yes | | Ghostty | Yes | Yes | Yes | | Kitty | Yes | Yes | Yes | | Wezterm | Yes | Yes | Yes | | VS Code terminal | Yes | Yes | Yes | | Linux terminals | Yes | Yes | No* | | CI environments | No | No | No |

* Accessibility API is macOS-only. See Contributing if you'd like to add Linux support.

Troubleshooting

Swift compilation fails

The binary is compiled automatically during install. If it fails, compile manually:

swiftc -O \
  -o ~/.config/opencode/bin/active-terminal-tab \
  ~/.config/opencode/bin/active-terminal-tab.swift \
  -framework Cocoa -framework ApplicationServices

Requires Xcode Command Line Tools (xcode-select --install).

Duo hook causes issues with other Node.js tools

The hook only activates for @gitlab/duo-cli processes (strict process.argv[1] check). If you still experience issues, temporarily disable it:

NODE_OPTIONS="" npm install   # or any other command

To remove permanently: npx terminal-tab-status uninstall duo

Testing the Swift binary

# Auto-detect frontmost terminal
~/.config/opencode/bin/active-terminal-tab

# Force a specific terminal
~/.config/opencode/bin/active-terminal-tab jetbrains
~/.config/opencode/bin/active-terminal-tab apple_terminal
~/.config/opencode/bin/active-terminal-tab iterm2

Contributing

Contributions are welcome. The canonical repository is on GitLab -- the GitHub repo is a read-only mirror.

Source of truth: https://gitlab.com/yaruchyk.o/terminal-tab-status

  1. Fork on GitLab
  2. Create a feature branch
  3. Submit a merge request

The GitHub mirror is updated automatically via GitLab push mirroring. Do not open pull requests on GitHub.

Development

git clone https://gitlab.com/yaruchyk.o/terminal-tab-status.git
cd terminal-tab-status
npm install
npm run build

# Test install
node scripts/cli.js              # both
node scripts/cli.js opencode     # opencode only
node scripts/cli.js duo          # duo only

# Test uninstall
node scripts/cli.js uninstall

Areas where help is needed

  • Linux focus detection -- xdotool or similar for detecting the active terminal tab on X11/Wayland
  • Bash/fish shell support -- the Duo hook currently only writes to ~/.zshrc
  • Additional AI CLI tools -- the architecture supports any tool that writes progress/title escape sequences

Security

  • The Swift binary reads only the active tab/window title via the Accessibility API. It cannot read terminal content, keystrokes, or data from other apps.
  • The Duo hook strips all C0 and C1 control characters from titles before writing to the terminal, preventing escape sequence injection.
  • The Duo hook uses the original fs.writeSync reference for its own writes, avoiding infinite recursion with its own interceptor.
  • NODE_OPTIONS --require loads the hook into every Node.js process, but the hook exits immediately for non-Duo processes (strict process.argv[1] check).

License

MIT