Skip to content

DiversioTeam/pi-cmux

Repository files navigation

@diversio/pi-cmux

CI Workflow Security License: MIT

Reusable cmux helpers for Pi-aware tooling.

This is a plain TypeScript library, not a Pi package. Install it as a normal npm dependency and import it from TypeScript or JavaScript code that uses Pi's extension APIs.

Why this package exists

This package provides a small, reusable set of cmux helpers for code that needs to:

  • detect whether it is running inside cmux
  • identify the active workspace and surface
  • open splits and workspaces safely
  • build hardened Pi and shell launch commands
  • send native cmux notifications

The point of the package is simple:

make common cmux automation reliable and easy to reuse

The problem it is solving

One common failure this package helps prevent is:

open split
  -> spawn command in fresh pane
  -> pane immediately closes
  -> no clue what failed

That usually happens because a fresh pane is a weaker environment than the Pi session that launched it:

  • PATH may be different
  • the pi binary may not be discoverable by name
  • a short-lived failure may exit before the human can read it

@diversio/pi-cmux hardens that path by:

  • reusing the current session's PATH
  • resolving the safest Pi launcher it can find
  • keeping failed panes open with a readable message
  • centralizing the split/workspace launch flow in one place

Mental model

your code
  ├─ decides when a split/workspace/notification is useful
  └─ calls @diversio/pi-cmux
       ├─ cmux.ts       -> talk to cmux safely
       ├─ launch.ts     -> build reliable shell / Pi commands
       ├─ split.ts      -> open adjacent panes
       ├─ workspace.ts  -> open new workspace tabs
       ├─ notify.ts     -> send native cmux notifications
       └─ escape.ts     -> safe shell argument escaping

Another way to think about it:

Your application code decides when cmux behavior is useful.
pi-cmux handles the low-level terminal mechanics.

Install

npm install @diversio/pi-cmux

The package is published to the public npm registry. No .npmrc setup or auth tokens are needed — it installs the same way as any other public npm package.

Quick usage examples

1. Open a new Pi split

import { buildPiCommand, isInsideCmux, openSplit } from "@diversio/pi-cmux";

if (isInsideCmux()) {
  await openSplit(pi, "right", buildPiCommand(process.cwd(), {
    prompt: "Review the current diff",
  }));
}

Mental model:

current Pi session
  -> ask cmux for the caller surface
  -> create a right-side split
  -> respawn that pane with a hardened Pi command

2. Open a shell-command split

import { buildShellCommand, openSplit } from "@diversio/pi-cmux";

await openSplit(pi, "down", buildShellCommand(process.cwd(), "npm test"));

Use this when you want a helper terminal lane instead of another Pi lane.

3. Open a workspace tab

import { buildPiCommand, openWorkspace } from "@diversio/pi-cmux";

await openWorkspace(pi, {
  cwd: process.cwd(),
  name: "Reviewer",
  command: buildPiCommand(process.cwd(), {
    prompt: "Review the migration for safety",
  }),
});

4. Send a native notification

import { notify } from "@diversio/pi-cmux";

await notify(pi, "Review finished", "backend", "No correctness issues found.");

Why the launcher helpers matter

The most important helpers in this package are the command builders in launch.ts.

resolvePiLauncher()

This answers:

"If I start Pi inside a brand-new shell right now, what command is least likely to fail?"

Resolution order:

1. PI_CLI_PATH override
2. pi binary next to the current Node executable
3. plain `pi`

wrapSpawnedCommand()

This answers:

"How do I run a command in a fresh pane without losing the error instantly if it fails?"

It wraps the real command so the new shell:

  • restores PATH
  • runs the requested command
  • prints a readable failure message if needed
  • drops into an interactive shell on failure instead of closing immediately

buildPiCommand()

This is the usual choice for a Pi-powered helper lane.

It composes:

cd <cwd>
  -> restore PATH
  -> launch Pi
  -> optionally reuse a seeded session file
  -> optionally pass a prompt after `--`

buildShellCommand()

This is the usual choice for commands like:

  • npm test
  • npm run dev
  • lazygit
  • python manage.py shell

Public API

Environment and cmux execution

  • isInsideCmux()
  • identifyCaller(pi)
  • execCmux(pi, args, timeout?)
  • CMUX_TIMEOUT_MS

Launch-command building

  • resolvePiLauncher()
  • wrapSpawnedCommand(cmd, notice)
  • shellEscape(value)
  • buildPiCommand(cwd, { sessionFile?, prompt? })
  • buildShellCommand(cwd, cmd)

Pane and workspace creation

  • openSplit(pi, direction, command)
  • openWorkspace(pi, { cwd, name?, command })
  • SPLIT_BOOT_DELAY_MS

Notifications

  • notify(pi, title, subtitle, body, opts?)

Development

npm install
npm run build
npm pack --dry-run

Useful checks while editing:

# verify the compiled package surface
node --input-type=module -e 'import("./dist/index.js").then((m) => console.log(Object.keys(m).sort()))'

# inspect the exact tarball contents that would be published
npm pack --dry-run

Release

Releases are triggered by pushing a version tag. The publish workflow (defined in .github/workflows/publish.yml) runs automatically and does:

  1. Verify — the git tag (e.g. v0.1.0) must match the version field in package.json. This prevents accidentally publishing a mismatched version.
  2. Build — TypeScript is compiled into dist/.
  3. Idempotency check — if this version already exists on npm, the publish step is skipped. This makes the workflow safe to re-run after transient failures.
  4. Publish — the package is pushed to the public npm registry (registry.npmjs.org) under the @diversio org. Auth is handled by an npm Automation token stored as the NPM_TOKEN GitHub secret.
  5. GitHub Release — a release is created using auto-generated notes (merged PRs since the last tag).

To cut a release:

git tag v0.1.0
git push origin v0.1.0

Notes

  • The package is published to the public npm registry — no auth required.
  • The published tarball intentionally contains only dist/ plus standard package metadata files.
  • This package is intentionally small. Calling code should own UX policy; this package should own the reusable cmux mechanics.

About

Shared cmux primitives for Pi extensions: hardened split/workspace launching, notifications, and shell command builders.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors