Programmatic Zellij automation for humans, scripts, and agents alike.
zjctl is a CLI + plugin that lets you script Zellij end-to-end (actions,
status, setup, and pane operations) via a single CLI command.
- Target panes by selector (id/title/cmd/regex/focused)
- Operate on panes directly: send, focus, rename, resize, capture, wait-idle
- Launch panes and get back a selector
- JSON output for automation
- Action passthrough for
zellij action
# 1) Install + verify the plugin
zjctl install --load
zjctl doctor
# 2) Launch a shell pane and run a command
pane=$(zjctl pane launch -- "zsh")
zjctl pane send --pane "$pane" -- "ls -la\n"
# 3) Wait, capture, and clean up
zjctl pane wait-idle --pane "$pane" --idle-time 2 --timeout 30
zjctl pane capture --pane "$pane"
zjctl pane close --pane "$pane"Requires Zellij 0.43+.
cargo install zjctl
zjctl install --loadzjctl install downloads the plugin, updates config.kdl, and can load it in
the current session.
# Download CLI binary
curl -L https://github.com/mrshu/zjctl/releases/latest/download/zjctl-x86_64-linux.tar.gz | \
tar -xz -C ~/.local/bin/# Download plugin binary
mkdir -p ~/.config/zellij/plugins
curl -L https://github.com/mrshu/zjctl/releases/latest/download/zrpc.wasm \
-o ~/.config/zellij/plugins/zrpc.wasm# Load the plugin in the current session
zellij action launch-plugin "file:~/.config/zellij/plugins/zrpc.wasm"To auto-load on startup, add to ~/.config/zellij/config.kdl:
load_plugins {
"file:~/.config/zellij/plugins/zrpc.wasm"
}Accept the permissions when prompted. The plugin runs as a background service.
Verify setup:
zjctl doctor
zjctl doctor --json# Prerequisites
rustup target add wasm32-wasip1
# Build and install CLI
cargo build --release -p zjctl
cp target/release/zjctl ~/.local/bin/
# Build and install plugin
cargo build --release -p zjctl-zrpc --target wasm32-wasip1
cp target/wasm32-wasip1/release/zrpc.wasm ~/.config/zellij/plugins/If you prefer crates.io for the plugin:
rustup target add wasm32-wasip1
cargo install zjctl-zrpc --target wasm32-wasip1 --root ~/.local
mkdir -p ~/.config/zellij/plugins
cp ~/.local/bin/zrpc.wasm ~/.config/zellij/plugins/zrpc.wasm| Selector | Description |
|---|---|
id:terminal:N |
Terminal pane with ID N |
id:plugin:N |
Plugin pane with ID N |
focused |
Currently focused pane |
title:substring |
Panes with title containing substring |
title:/regex/ |
Panes with title matching regex |
cmd:substring |
Panes running command containing substring |
cmd:/regex/ |
Panes running command matching regex |
tab:N:index:M |
Pane at index M in tab N |
- Always launch a shell pane (prefer
zsh) before running commands; if a command exits, you can lose output. zjctl pane sendwaits 1s before Enter by default; use--enter=falseor--delay-enter 0for immediate input.zjctl pane closerefuses to close the focused pane unless--force.
pane=$(zjctl pane launch -- "zsh")
zjctl pane send --pane "$pane" -- "python script.py\n"
zjctl pane wait-idle --pane "$pane" --idle-time 2 --timeout 30
zjctl pane capture --pane "$pane"
zjctl pane close --pane "$pane"# Inventory and status
zjctl panes ls
zjctl panes ls --json
zjctl status
zjctl status --json
# Send input
zjctl pane send --pane id:terminal:3 -- "ls -la\n"
zjctl pane send --pane id:terminal:3 --enter=false -- "ls -la"
# Navigation and layout
zjctl pane focus --pane title:server
zjctl pane rename --pane focused "API Server"
zjctl pane resize --pane focused --increase --direction right --step 5
# Capture and wait
zjctl pane capture --pane focused
zjctl pane capture --pane focused --full
zjctl pane wait-idle --pane focused --idle-time 3 --timeout 60
# Signals
zjctl pane interrupt --pane id:terminal:3
zjctl pane escape --pane id:terminal:3
# Close / launch
zjctl pane close --pane id:terminal:3
zjctl pane close --pane focused --force
zjctl pane launch --direction right -- "python"
# Help / passthrough
zjctl help
zjctl action new-pane- Use
zjctl panes ls --jsonfor selection logic. - Prefer
wait-idleinstead of pollingcapture.
zjctl pane send --pane id:terminal:3 -- "analyze this code\n"
zjctl pane wait-idle --pane id:terminal:3 --idle-time 3.0
zjctl pane capture --pane id:terminal:3# Diagnose setup issues
zjctl doctor
# Reinstall and re-load the plugin if needed
zjctl install --force
zjctl install --load┌─────────┐ ┌──────────────┐ ┌─────────────────┐
│ zjctl │─────▶│ zellij pipe │─────▶│ zrpc plugin │
│ (CLI) │◀─────│ (transport) │◀─────│ (WASM) │
└─────────┘ └──────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ Zellij shim API │
│ (pane ops) │
└─────────────────┘
- zjctl: Native CLI binary, sends JSON-RPC requests via
zellij pipe - zrpc: WASM plugin running in Zellij, receives pipe messages, executes pane operations
- Protocol: Newline-delimited JSON (jsonl) with UUID correlation
The plugin requests these Zellij permissions:
ReadApplicationState- to track pane/tab stateWriteToStdin- to send input to panesChangeApplicationState- to focus/rename/resize panesReadCliPipes- to respond to CLI pipe messages
Note: The plugin runs as a hidden background service and won't appear as a visible pane.
# Check all crates
cargo check
# Run tests
cargo test
# Format
cargo fmt
# Lint
cargo clippyMIT