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

pi-iterm2

v0.1.3

Published

Color-code iTerm2 tabs by host, session, and live agent status for the Pi coding agent.

Readme

pi-iterm2

Never lose a Pi session after an iTerm2 or system restart. The macOS companion daemon remembers the remote host, working directory, and Pi session ID for every tab. When iTerm2 restores a tab, it prints exactly what was running there:

Last Pi session in this tab was 01abc... (idle) on devvm123 /home/me/project, 2m ago.
Run ssh -t -- me@devvm123 'cd -- /home/me/project && exec pi --session 01abc...' to restore this session.

The hostname is printed in that host's tab color, so it remains recognizable at a glance. The message tells you which host to reconnect to, which working directory to enter, and which exact Pi session to resume, followed by a shell-quoted command ready to copy and paste. Remote tabs without a Pi session get a command that reconnects and changes to their last directory; local shell tabs need no command because iTerm2 restores their cwd itself. The command is shown in bold without literal backticks, so copying it cannot accidentally invoke shell command substitution. The shell integration reads the newest matching state record while the shell rc file is sourced and prints it before Powerlevel10k's instant-prompt preamble, making it normal selectable startup output. Remote sessions are launched through build_remote_launch_argv (ssh -t -- by default), so a deployment can override that function for another launcher. Recovery is independent of SSH, mosh, devserver tooling, or any other connection method because pi-iterm2 records session identity rather than managing the connection.

The extension also ties iTerm2 tabs to the live Pi sessions running in them:

  • Tab color is derived from hostname (primary), session id (secondary nudge), and live agent status (idle/working/waiting/error) — so tabs on different machines are visually distinct, sessions on the same machine stay in the same color family, and a glance at the tab tells you whether that session needs attention.
  • Tab title gets a status icon prefixed while there's something to flag (working/waiting/error), on top of pi's own default title.
  • cwd, session name and ID, status, resolved host color, and a per-Pi-instance token are published as iTerm2 user-defined variables (\(user.pi_cwd), \(user.pi_session), \(user.pi_session_id), \(user.pi_status), \(user.pi_host_color), \(user.pi_instance)), so the companion daemon and custom iTerm2 badges or titles can use them.
  • Native cwd tracking (CurrentDir/RemoteHost) is also enabled, so iTerm2's own directory-inheriting new-tab/split behavior and semantic history work for the session's project directory.

Install

Install directly from GitHub:

pi install git:github.com/dreveman/pi-iterm2

To try a local checkout without installing it:

pi --no-extensions -e /path/to/pi-iterm2

After installing or changing configuration in a running Pi session, run /reload or restart Pi.

Requirements

iTerm2 only. The extension auto-detects iTerm2 via TERM_PROGRAM=iTerm.app and does nothing on other terminals. It also does nothing outside interactive TUI mode (RPC, print, and JSON modes have no terminal to address) and outside a real TTY.

Configuration

Defaults apply with no configuration file. Override in:

~/.pi/agent/pi-iterm2.json
{
  "enabled": "auto",
  "tabColor": true,
  "tabTitle": true,
  "currentDir": true,
  "userVars": true,
  "promptRestore": false,
  "vscodeColor": true
}
  • enabled"auto" (default) detects iTerm2 via TERM_PROGRAM; true/false force it on or off (true is useful for terminals that also implement these sequences, and required on hosts reached over plain SSH — see below).
  • tabColor — color-code the tab background by host/session/status.
  • tabTitle — prefix a status icon on the tab title (see How the tab title is chosen).
  • currentDir — emit iTerm2's native CurrentDir/RemoteHost sequences.
  • userVars — publish pi_cwd, pi_session, pi_session_id, pi_status, pi_host_color, and the internal pi_instance liveness token as iTerm2 user-defined variables.
  • promptRestore — after printing a restore command, ask Run it now? [y/N]; a single y/Y runs it immediately without Enter, and any other key declines (default false; /iterm2-install can enable it).
  • vscodeColor — take the host's hue from the VS Code window color when one is set for this machine (see Matching the VS Code window color). Set it to false to ignore that and use the palette or hash instead.
  • palette, hostColors, sessionHueSpread — shape or override the automatic colors (see Choosing your own colors).

SSH and enabled: "auto"

TERM_PROGRAM is a local environment variable your shell doesn't forward over SSH, so "auto" never activates the extension on a remote host — even though your client is genuinely iTerm2. Set "enabled": true in that host's ~/.pi/agent/pi-iterm2.json to force it on there.

This applies to Pi's own activation only. The shell integration never consults TERM_PROGRAM, precisely because it can't be consulted usefully over SSH; installing it is the consent, and "enabled": false or "tabColor": false turns its color off.

Missing configuration uses the defaults. Invalid JSON, unknown fields, and invalid option types produce a warning (shown once at session start) and fall back to the defaults.

How tab color is chosen

Color is HSL, composed from three factors in priority order:

  1. Host sets the hue. In order: a hostColors pin for this machine, else the VS Code window color if one is set for it, else a palette entry, else os.hostname() hashed over the full wheel — the dominant, most visible difference between tabs on different machines.
  2. Session id nudges that hue by up to ±20° — sessions on the same host land in the same color family but stay distinguishable.
  3. Status sets saturation/lightness only, never hue: dim while idle, brighter while the agent is working, brightest when a dialog needs your input, and desaturated-but-marked when the most recently completed turn ended in a tool error (cleared as soon as a later turn succeeds, so a self-corrected run ends up looking normal, not stuck red).

Choosing your own colors

By default every hue is hashed, so you get whatever the wheel gives you. Three options change that, from loosest to tightest:

{
  "palette": ["#4a7ba7", "#a74a5c", "#4aa76b", 45],
  "hostColors": { "devbox": "#7a4aa7", "laptop": 200 },
  "sessionHueSpread": 0
}
  • palette — hosts are assigned from these colors instead of the full 0–360° wheel, so everything stays in a set you picked while still being automatic for new machines.
  • hostColors — pins named hosts explicitly. Wins over palette, and any host not listed still falls back to palette, then to the hash.
  • sessionHueSpread — degrees of per-session nudge around the host hue (default 40, i.e. ±20°). Set it to 0 to pin every session on a host to exactly that hue — worth doing if you pin colors and want precisely the color you named.

Colors are accepted either as "#rrggbb" or as a bare hue number (0359). Only the hue is used from a hex color: saturation and lightness stay reserved for conveying status, so a pinned host still visibly brightens while the agent works. If you want the exact literal color instead, that trade-off isn't available — status signalling is the reason the tab is colored at all.

Rather than editing JSON and reloading to see the result, two commands change the live tab immediately and save the result to the same config file:

/iterm2-color                          show this host's current color and where it came from
/iterm2-color #4a7ba7                  pin this host (also accepts a bare hue, e.g. 208)
/iterm2-color clear                    unpin it

/iterm2-palette                        show the current palette
/iterm2-palette #4a7ba7 #a74a5c 45     set it
/iterm2-palette clear                  back to the full hue wheel

Each reported color is preceded by a swatch of the actual tab color it produces, so you can see the result inline — which matters most for /iterm2-palette, where a whole palette can be previewed at once even though a tab can only show one color at a time:

Palette set: ██ 0°  ██ 120°  ██ 240°

They rewrite ~/.pi/agent/pi-iterm2.json in place, preserving any other settings in it, and store what you typed (#4a7ba7 stays #4a7ba7) rather than the derived hue. sessionHueSpread has no command — it's a set-once preference, so edit the file for that.

Settings are per host: the file lives in the home directory of whichever machine pi runs on, and applies to every session there. Entries are keyed by hostname inside it, so one synced dotfile can carry a color per machine. Pi re-reads the file when a session is replaced, so /new, /resume, and /fork pick up hand-edits without restarting pi.

The tab color and user variables reset on session shutdown.

How the tab title is chosen

The title follows pi's own default format exactly: π - session - cwd once the session has been given a name with /name, or just π - cwd until then — pi has no auto-generated session title, so an unnamed session's raw id isn't shown as one. Either way, a status icon is prefixed when there's something to flag:

| Status | Icon | |---|---| | idle | (none) | | working | ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏ (Pi's Working spinner, 80ms per frame) | | waiting (needs your input) | ◆ | | error (most recent turn failed) | ✖ |

It's set at the same status-change points as the tab color (agent_start, agent_settled, ui_prompt_start/ui_prompt_end, plus /name), not at session start — pi's own default title at that point is already identical to what this would produce for an idle session, so there's nothing to add there. Pi manages the tab title itself only at session start, rename, and shutdown, and never during a turn, so this never fights with pi's own title updates.

The spin is a plain setInterval started at agent_start and stopped the moment status stops being "working" (settled, a dialog opens, session ends) — it never keeps ticking once idle.

Matching the VS Code window color

If you color VS Code windows per machine — with Peacock, your platform's own tooling, or by hand — the tab picks up the same hue, so the terminal tab and the editor window agree without configuring anything twice.

The color is read from VS Code's machine-scope settings, whichever of these exists:

~/.vscode-remote/data/Machine/settings.json
~/.vscode-server/data/Machine/settings.json

from workbench.colorCustomizations, taking the first of titleBar.activeBackground, titleBar.inactiveBackground, activityBar.background that holds a hex color. Machine scope is the one that describes the host: unlike user or workspace settings it doesn't follow you between machines, which is what makes it a sensible source for a per-host hue. Every hex form VS Code accepts works (#rgb, #rgba, #rrggbb, #rrggbbaa); alpha is ignored, as are comments and trailing commas in the file.

As everywhere else, only the hue is taken — saturation and lightness stay reserved for agent status, so a machine matched to its editor still brightens while working. A hostColors pin outranks this, and anything unusable (no file, no color set, an empty workbench.colorCustomizations, an unparseable file) falls back to the palette and then the hash, silently.

The file is only ever read, never written, and only once per session — it describes the machine, so it effectively never changes mid-session. If you do change it while a session is open, /iterm2-color refresh re-reads it and recolors the tab.

To ignore VS Code entirely, set "vscodeColor": false in ~/.pi/agent/pi-iterm2.json.

Note that a fully grey window color (some "black"/"charcoal" presets) has no hue at all and resolves to 0°, i.e. red. Pin the host with /iterm2-color if you'd rather have something else.

Shell tabs without Pi

The installed shell integration treats ordinary shell tabs as the same recoverable tabs with optional Pi metadata. It applies the host's resting identity color and publishes RemoteHost and CurrentDir at each prompt, allowing the Mac recorder to store the remote host and cwd without Pi or Python. It uses the same ~/.pi/agent/pi-iterm2.json precedence as Pi (hostColors → VS Code window color → palette → hostname hash).

Both halves are shell builtins only — the hue hash, the VS Code settings lookup, and the HSL conversion are all computed in the rc file, with hues carried as integer millidegrees because bash has no floating-point arithmetic. So a remote host colors its own tabs with no Python runtime, no iTerm2 Python API, and no daemon, and it does so under the same single condition that publishes host and cwd: the marker file /iterm2-install writes. TERM_PROGRAM is deliberately not part of that condition, since SSH does not forward it and testing it would rule out every remote shell; a terminal that isn't iTerm2 discards these sequences the same way it already discards the location ones.

pi-iterm2-identity reports what that resolution produced — the host, its hue, which rule chose it, and the resulting color — and is the way to check the color on a host with no Pi and no Python:

pi-iterm2-identity

Because the shell works in exact integer arithmetic where Pi and the recorder use floats, a channel value can land one unit away from theirs when the exact result sits precisely on a rounding boundary. That is at most 1/255 of one channel, and never a visible difference.

One caveat is worth knowing: the hue is derived from the hostname, and the hook reads the shell's own HOSTNAME/HOST — the same value it publishes as RemoteHost. Both bash and zsh set that from gethostname(), which is what Pi and the recorder use, so the derivation agrees. If some host reports two different names anyway, pin it with hostColors (or /iterm2-color) and the pin settles it.

After a restart, a tab with Pi metadata gets the exact pi --session resume command. A remote tab without a Pi session ID gets a command that reconnects, changes to the recorded directory, and opens an interactive login shell. A local shell tab needs no restore command because iTerm2 restores its cwd. Agent status brightness is naturally available only while Pi is running.

tmux

Sequences are wrapped in tmux's DCS passthrough envelope (ESC Ptmux; ... ESC \) whenever $TMUX is set, so tab color and user vars work inside a tmux session running in iTerm2. This does not require allow-passthrough to be configured in tmux.

Displaying host, cwd, and session id

pi_cwd, pi_session, pi_session_id, pi_status, pi_host_color, and pi_instance are ordinary iTerm2 user-defined variables, referenceable as \(user.pi_cwd) etc. pi_cwd, pi_session, pi_session_id, pi_host_color, and pi_instance are set at session start and when their values change; pi_status tracks the live agent status (idle/working/waiting/error), updating at the same moments as the tab color. pi_session is the display name when one is set (otherwise the ID), while pi_session_id is always the immutable ID accepted by pi --session. pi_host_color is the resolved resting host color as #rrggbb, before the per-session nudge and live-status brightness. pi_instance is a random token used by the companion daemon to distinguish a freshly started Pi from session variables restored after a force quit. All six are cleared on session shutdown.

For cwd and host specifically, prefer iTerm2's built-in variables instead: \(session.path) and \(session.hostname) are auto-populated from the same CurrentDir/RemoteHost sequences this extension already sends, so pi_cwd is redundant with session.path. (session.hostname/session.username normally require iTerm2's own shell-integration script to be installed on the remote host — this extension's RemoteHost sequence populates them without that.) pi_session and pi_session_id have no iTerm2 built-in equivalents, since iTerm2 has no concept of a Pi session.

Put whichever combination you want in Settings → Profiles → General → Badge, a tab title format, or a status bar "Interpolated String" component, e.g.:

\(session.hostname) — \(user.pi_session)

pi_cwd/session.path reflect the session's project root (ctx.cwd), not a live shell $PWD: Pi's bash tool always runs from that same fixed directory, so there is no in-session directory to track — a cd inside a bash command only affects that one subprocess. The project root only changes when the session itself changes (/new, /resume, /fork), which is when it's refreshed.

Note that none of badge/title/status-bar text is mouse-selectable (it's UI chrome, not real terminal content).

macOS companion daemon

macos/pi_iterm2_daemon.py is a standalone script that runs on the Mac, separately from the Pi extension. It records each tab's built-in host and path plus any Pi session metadata into ~/.pi-iterm2/state.json (keyed by iTerm2 session id, capped at the 200 most recent), so restored Pi and ordinary shell tabs can show an appropriate resume command. The restored reminder and check reports render each hostname in its recorded host color.

On a new iTerm2 application launch, the daemon merges completed state.json records into state.previous.json before recording the new launch. Records for tabs that were not resumed remain available across later restarts. Check reports combine both files, preferring a new record when the same tab has already been seen again and marking that record (active); previous-only records are left unmarked.

The reminder is printed while a newly launched restored shell sources its rc file, before Powerlevel10k's instant-prompt preamble. Ordinary tabs print nothing. The reminder includes a shell-quoted command that changes to the recorded cwd and launches pi --session with the recorded session ID. Local sessions run directly; remote sessions use build_remote_launch_argv, which defaults to ssh -t -- against the recorded username@hostname and can be replaced for other remote launchers.

Prerequisites (once, on the Mac)

  1. Settings → General → Magic → Enable Python API.
  2. Scripts → Install Python Runtime (renamed to Check for Updated Runtime once this has been done before). This provisions iTerm2's bundled Python interpreter under ~/Library/Application Support/iTerm2/iterm2env/versions/.
  3. Add the installed shell integration to the local Mac shell configuration:
    test -e "${HOME}/.pi-iterm2/shell.sh" && source "${HOME}/.pi-iterm2/shell.sh"
    Put it in ~/.zshrc for zsh or ~/.bashrc for bash. /iterm2-install places it before Powerlevel10k's instant-prompt preamble and can configure either or both files. A login bash setup must already source ~/.bashrc from ~/.bash_profile. The hook runs only in local, non-tmux iTerm2 shells and checks once per shell.

If pi-iterm2 is installed locally on the Mac

One Pi command is registered when Pi runs on macOS:

  • /iterm2-install — separately prompts to install or update the AutoLaunch recorder and the shell integration. Shell installation copies ~/.pi-iterm2/shell.sh and its Python helper, can apply the same host identity to ordinary shell tabs, can enable the default-no restore execution prompt, and can add the guarded test -e ... && source ... line to ~/.zshrc and ~/.bashrc. Existing guarded lines are detected and unguarded source lines are upgraded, so rerunning it is safe.

Until /iterm2-install has been run, a one-line hint at session start points to it, since the terminal integration is otherwise easy to miss. The hint records the package version it was run for in ~/.pi-iterm2/installed-version and then stays quiet; after the package is upgraded it reappears once, worded as an update rather than a first install. It shows only in the interactive TUI, and (like the command itself) on any host, so a remote shell reached over SSH still surfaces it even where Pi's own tab coloring is inactive.

Sourcing the hook adds three ordinary shell commands, available without starting Pi:

  • pi-iterm2-check — report the current tab's stored record and reminder preview.
  • pi-iterm2-check-all — report every stored tab record.
  • pi-iterm2-identity — report this host's resting color and where its hue came from.

The first two read state.json and state.previous.json directly; they do not require the recorder to be running. pi-iterm2-identity needs neither the recorder nor Python and works on a remote host too.

On macOS, /iterm2-install offers the recorder, shell integration, and optional ordinary-shell host identity. On remote Linux hosts it skips the macOS recorder and installs only the shell hook, which resolves the host color and publishes RemoteHost and CurrentDir using shell builtins—no Python runtime is required. After installation, reload the configured shell file or open a new shell. Restart iTerm2 after updating the macOS recorder.

Otherwise

None of this requires the pi extension — the daemon is a plain, standalone file. Download it straight from GitHub, from the Mac's own terminal:

mkdir -p ~/Library/Application\ Support/iTerm2/Scripts/AutoLaunch

curl -fsSL https://raw.githubusercontent.com/dreveman/pi-iterm2/main/macos/pi_iterm2_daemon.py \
  -o ~/Library/Application\ Support/iTerm2/Scripts/AutoLaunch/pi_iterm2_daemon.py
mkdir -p ~/.pi-iterm2
cp ~/Library/Application\ Support/iTerm2/Scripts/AutoLaunch/pi_iterm2_daemon.py \
  ~/.pi-iterm2/pi_iterm2.py
curl -fsSL https://raw.githubusercontent.com/dreveman/pi-iterm2/main/shell/pi_iterm2_restore.sh \
  -o ~/.pi-iterm2/shell.sh
python3=$(ls -t ~/Library/Application\ Support/iTerm2/iterm2env/versions/*/bin/python3 2>/dev/null | head -1)
"$python3" ~/.pi-iterm2/pi_iterm2.py --refresh-record-index

On a remote host only the rc file matters, since the color and the location sequences are pure shell:

mkdir -p ~/.pi-iterm2
curl -fsSL https://raw.githubusercontent.com/dreveman/pi-iterm2/main/shell/pi_iterm2_restore.sh \
  -o ~/.pi-iterm2/shell.sh
touch ~/.pi-iterm2/remote-location-enabled ~/.pi-iterm2/shell-identity-enabled

Drop the shell-identity-enabled marker to publish host and cwd without coloring ordinary tabs.

Back on the Mac, mkdir -p comes first because that folder doesn't exist until iTerm2's Scripts/Python API has been used at least once. Add test -e "${HOME}/.pi-iterm2/shell.sh" && source "${HOME}/.pi-iterm2/shell.sh" to ~/.zshrc or ~/.bashrc, then restart iTerm2. A login bash setup must source ~/.bashrc from ~/.bash_profile. AutoLaunch scripts aren't hot-reloaded, so re-run the downloads and restart iTerm2 again any time the daemon is updated. See the file's own header comment for how it works.

After sourcing the hook, check the current tab or every stored session directly from the shell:

pi-iterm2-check
pi-iterm2-check-all
pi-iterm2-identity

All three are read-only. pi-iterm2-check --session <id> targets a different tab.

Development

npm install
npm run verify

Individually:

npm run check   # tsc --noEmit
npm test        # runs test/run.ts under pi
npm run smoke   # loads the extension under pi

Before publishing, verify the tarball includes every runtime dependency:

npm pack --dry-run

License

MIT — see LICENSE.