@devnazim/pi-cmux
cmux notifications and status integration for pi.
Package details
Install @devnazim/pi-cmux from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@devnazim/pi-cmux- Package
@devnazim/pi-cmux- Version
0.1.2- Published
- May 27, 2026
- Downloads
- 227/mo · 227/wk
- Author
- devnazim
- License
- MIT
- Types
- extension
- Size
- 24.7 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-cmux
cmux notifications and status integration for pi.
pi-cmux is a standalone pi extension/package. It sends generic pi lifecycle updates to cmux and exposes an optional in-process notifier API that other pi extensions can use for semantic notifications.
Package name: @devnazim/pi-cmux.
Install
After publishing to npm:
pi install npm:@devnazim/pi-cmux
From a local checkout:
pi install /path/to/pi-cmux
Or try without installing:
pi -e /path/to/pi-cmux
What it does
| pi event | cmux action |
|---|---|
| Agent starts | mark the active cmux surface as running (surface.report_shell_state) |
| Agent ends with no queued messages | desktop notification (including session name when set) + mark surface as prompt/idle |
| Agent ends with queued messages | keep/report the active surface as running |
| Session shuts down/reloads | mark surface as prompt/idle |
| Optional extension notification | popup/status/log best-effort, controlled by the caller |
All cmux calls are best-effort. If cmux is unavailable or a command fails, pi continues normally.
cmux, SSH, and tmux behavior
pi-cmux detects cmux with:
CMUX_WORKSPACE_IDCMUX_TAB_ID- any non-empty
CMUX_SOCKET_PATH(including remote relay values like127.0.0.1:<port>) /tmp/cmux.sockas a final fallback
It resolves the cmux CLI with CMUX_BUNDLED_CLI_PATH, falling back to cmux on PATH.
For SSH/tmux/surface-aware notifications, it targets the active cmux surface by preferring explicit env vars:
CMUX_SURFACE_IDCMUX_PANEL_ID
If neither is present but CMUX_WORKSPACE_ID or CMUX_TAB_ID exists, pi-cmux asks cmux for that workspace's surfaces with surface.list and chooses the focused surface, then the selected surface, then the first surface.
When a surface is available, notifications use:
cmux rpc notification.create_for_surface '{"surface_id":"...","title":"..."}'
Otherwise they fall back to:
cmux rpc notification.create '{"title":"..."}'
If TMUX_PANE is set, pi-cmux asks tmux for a readable pane label and prefixes notification bodies with it, e.g. [dev:1 %2] Ready for input. If tmux lookup fails, it falls back to the raw pane id.
This avoids terminal OSC notifications and works through SSH/tmux when the cmux shell integration exposes the needed env/socket/CLI access in the remote environment. Without that cmux environment, the extension silently no-ops.
Current cmux builds expose notifications and surface shell-state RPCs, but may not expose older set-status, clear-status, or log CLI commands. pi-cmux uses the shell-state RPC for surface activity, probes cmux --help before calling optional legacy status/log commands, and keeps legacy status calls off the critical agent lifecycle path.
Configuration
Create ~/.config/pi-cmux/config.json or set PI_CMUX_CONFIG to another path.
{
"notifications": {
"done": true,
"error": true,
"xplan": true
},
"status": true,
"logs": true
}
| Option | Default | Description |
|---|---|---|
notifications.done |
true |
Show generic “Pi done” notifications. |
notifications.error |
true |
Allow error-level popup notifications from optional callers. |
notifications.xplan |
true |
Allow popup notifications from source: "xplan". |
status |
true |
Report active cmux surface activity, or use supported legacy status commands when available. |
logs |
true |
Write cmux log entries when the installed cmux CLI exposes cmux log; otherwise no-op. |
Malformed or omitted values fall back to defaults.
Optional notifier API
Other extensions can request cmux notifications without importing or depending on pi-cmux:
const notify = (globalThis as any)[Symbol.for("pi.cmux.notify.v1")];
if (typeof notify === "function") {
await notify({
source: "xplan",
type: "step_ready",
title: "xplan step ready",
body: "S2 is ready for review",
level: "success",
status: { key: "xplan", text: "review", icon: "check", color: "#22c55e" },
});
}
If pi-cmux is not installed, the symbol is absent. Callers should treat notifications as optional and never require them for workflow state.
Supported payload fields:
title— required notification titlesubtitle,body— optional body parts, joined with—source— log/status source, e.g.xplantype— caller-defined event typelevel—info,success,warning,error, orwarnnotify: false— skip popup notificationlog: false— skip cmux log entrystatus— optional keyed status set/clear request; used only when the installed cmux CLI supports legacy status commands
Development
npm install
npm test
npm run check
The implementation no-ops outside cmux, targets notifications and shell-state reports to the active surface when possible, infers SSH/tmux surfaces with surface.list, adds tmux pane labels and pi session names for disambiguation, probes optional legacy commands before using them, keeps legacy status calls best-effort in the background, and executes commands without shell interpolation.