panes-cli
v0.2.7
Published
Tile multiple commands into iTerm2 split panes
Readme
panes
Tile multiple commands into iTerm2 split panes. Opens a new tab and runs each command in its own pane, arranged top to bottom.
Using Claude Code? This repo ships a
use-panes-cliskill that teaches Claude Code how to wirepanes-cliinto a project (pinned devDependency,.panes/layout,dirresolution rules, wait gates). Drop it into your.claude/skills/and ask Claude to set uppanes.
Install
The npm package is panes-cli; the binary it installs is panes.
Node.js projects (recommended)
Pin panes-cli as a devDependency so every contributor and CI run uses the same version, then invoke it via an npm script:
npm install --save-dev panes-cli@<exact-version>{
"scripts": {
"dev": "panes -c .panes/dev.yaml"
},
"devDependencies": {
"panes-cli": "0.2.5"
}
}Pin the exact version (no ^ / ~) — panes drives iTerm2 via AppleScript, and small changes to pane arrangement or wait-gate semantics can subtly shift a working dev setup.
Global (ad-hoc or non-Node projects)
npm install -g panes-cliFrom source
git clone https://github.com/koyeo/panes.git
cd panes
npm install
npm install -g .Usage
Basic
-e / --exec accepts a plain command string (repeatable):
panes -e "npm run dev" -e "npm run test"For wait conditions (sleep, waitPort, waitFile), use a YAML config file (see below).
Config file (-c / --config)
Load commands from a YAML file. Each entry is either a plain string or a mapping:
dir: .
commands:
- htop
- exec: npm run dev
- exec: npm test
waitPort: 3000Mapping fields:
exec(required): command to executesleep(optional): delay N seconds before executingwaitPort(optional): wait for a TCP port on localhost to become reachablewaitFile(optional): wait for a file to appear before executing
Wait conditions are evaluated by the panes process itself — they never inject sleep/nc/loop snippets into the user's shell. The pane's shell only sees the clean exec string once the gates pass.
Multiple wait conditions can be combined. Order: sleep → waitPort → waitFile → exec
dir is optional; relative paths resolve from the config file's location.
Every pane is driven through a fully synchronized sequence — each step waits
for observed state, never timing: panes first waits until a shell process is
attached to the pane's tty, then writes cd <dir>, then waits until the
shell's working directory has actually changed (verified via ps/lsof),
then evaluates the wait gates, and only then writes the exec command. The
cd and the command are separate lines, but the command is never written
before the cd was demonstrably consumed by the shell — so the two can never
merge into one line, no matter how slowly the shell starts. If a pane is
closed while panes is still waiting, it fails with a diagnostic instead of
hanging.
# Load from file
panes -c panes.yaml
# File + extra commands (file commands first, -e appended)
panes -c panes.yaml -e "htop"Wait conditions example
commands:
- exec: npm run dev
- exec: npm test
waitPort: 3000 # tests wait for server port
- exec: npm start
waitFile: dist/index.js # wait for build artifact
- exec: npm run smoke
sleep: 5 # delay 5 secondsProject layout: keep configs under .panes/
For projects that use panes regularly, colocate per-workflow YAML files under a top-level .panes/ directory:
your-project/
├── .panes/
│ ├── dev.yaml # full dev stack
│ ├── e2e.yaml # services needed for e2e tests
│ └── debug.yaml # extra inspectors (htop, log tail, …)
├── package.json
└── …Commit shared recipes; leave a .panes/local.yaml slot (gitignored) for personal tweaks.
Working directory (-d / dir)
dir controls where each pane is launched. Two rules cause every surprise people hit:
- A relative
dirresolves against the YAML file's directory, NOT the current working directory. - Resolution priority: CLI
-d> YAMLdir> current working directory.
Because YAML files live under .panes/, dir: . points at .panes/, not the project root. Use dir: .. instead:
# .panes/dev.yaml — every pane launches at the project root
dir: ..
commands:
- exec: npm run server
- exec: npm run worker
waitPort: 3000When using -c, do not also pass -d. The CLI -d wins and is itself resolved relative to the YAML file's directory, so panes -c .panes/dev.yaml -d packages/web becomes <repo>/.panes/packages/web — almost always wrong. Pick one source of truth: put dir in the YAML, or use -d only with -e (no -c).
There is no per-pane dir. If panes need different directories, prefix the command:
dir: ..
commands:
- exec: cd packages/api && npm run dev
- exec: cd packages/web && npm run dev
waitPort: 4000For ad-hoc one-off runs without a config file:
panes -d /path/to/project -e "npm run dev" -e "npm test"Requirements
- macOS
- iTerm2
- Node.js >= 18
License
MIT
