From zjctl
Use the shipped Rust zjctl CLI to inspect and safely control Zellij sessions, tabs, and panes from agents.
npx claudepluginhub mrmans0n/zjctl --plugin zjctlThis skill uses the workspace's default tool permissions.
Use the shipped Rust `zjctl` CLI to inspect and control Zellij sessions, tabs, and panes safely from agents. Prefer `zjctl` over raw `zellij action` commands when the operation is supported.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Processes PDFs: extracts text/tables/images, merges/splits/rotates pages, adds watermarks, creates/fills forms, encrypts/decrypts, OCRs scans. Activates on PDF mentions or output requests.
Share bugs, ideas, or general feedback.
Use the shipped Rust zjctl CLI to inspect and control Zellij sessions, tabs, and panes safely from agents. Prefer zjctl over raw zellij action commands when the operation is supported.
Use this skill for:
Do not use this skill to:
--help first--no-guard requires intentional usezellij action when zjctl supports the operationBefore using zjctl in a fresh session:
zjctl --help
$PATH, install the canonical root crate from the zjctl repository:
git clone https://github.com/mrmans0n/zjctl /tmp/zjctl && cargo install --path /tmp/zjctl
--help output.If updating this skill, verify against source files:
src/cli.rs — command and flag surfacesrc/models.rs — JSON response structssrc/commands/ — command behavior and dry-run mappingsrc/identity.rs — tab and pane identity resolutionsrc/safety.rs — self-write guard behaviorsrc/error.rs — error names, fields, and exit codes| Flag | Default | Description |
|---|---|---|
-f, --format <json|table|quiet> | json | Output format |
-n, --dry-run | off | Show command without executing |
-q, --quiet | off | Suppress non-error output |
-s, --session <SESSION> | — | Target session (overrides ZELLIJ_SESSION_NAME) |
--no-guard | off | Bypass self-write safety guard |
Commands under tabs and panes require a session. Resolution order:
--session <name> flagZELLIJ_SESSION_NAME environment variablenot_in_session, exit code 4sessions list does not require a session.
zjctl sessions list
zjctl tabs list
zjctl tabs focus <index-or-name>
zjctl tabs open [--name NAME] [--layout LAYOUT] [-- COMMAND...]
zjctl panes list [--tab <index-or-name>]
zjctl panes read <pane> [--full] [--ansi]
zjctl panes write <pane> <text>
zjctl panes send-keys <pane> <keys...>
zjctl panes focus <pane>
zjctl panes open [--direction DIR] [--floating] [--name NAME] [--cwd CWD] [--tab-id ID] [-- COMMAND...]
Tabs accept:
0, 2)"main")Panes accept:
terminal_N or plugin_NN — normalized to terminal_NAmbiguous or missing identifiers return invalid_target, exit code 1.
Sessions list:
{"sessions":[{"name":"main"}]}
Tabs list:
{"tabs":[{"index":0,"name":"main","active":true,"tab_id":0}]}
Panes list (note: command, cwd, title can be null):
{"panes":[{"id":"terminal_0","command":"zsh","cwd":"/repo","title":"main","focused":true,"floating":false,"tab_id":0,"tab_name":"main"}]}
Pane read:
{"pane_id":"terminal_7","content":"$ echo hello\nhello\n$"}
Successful mutation (write, send-keys, focus):
{"ok":true}
Tab open:
{"tab_id":1}
Pane open:
{"pane_id":"terminal_12"}
Dry run (any mutation):
{"dry_run":true,"command":["zellij","action","write-chars","--pane-id","terminal_7","hello"]}
Errors are emitted as JSON on stderr:
{"error":"invalid_target","message":"Pane not found: foo","exit_code":1}
Known errors:
| Error | Exit | Extra fields | When |
|---|---|---|---|
unknown_command | 1 | — | Unrecognized command |
missing_argument | 1 | — | Required argument missing |
invalid_target | 1 | — | Tab/pane identifier not found or ambiguous |
zellij_error | 2 | command | Zellij subprocess failed |
self_write_blocked | 3 | target, self | Write/send-keys to own pane blocked |
not_in_session | 4 | — | No session resolved |
parse_error | 5 | — | Failed to parse zellij output |
panes write and panes send-keys include self-write protection:
ZELLIJ_PANE_ID.self_write_blocked (exit 3).--dry-run — dry-run output is only emitted after the guard passes.--no-guard bypasses the protection. Use only when the agent has verified the target is intentional or the user explicitly requests it.ZELLIJ_PANE_ID is not set, the guard is skipped (no self to protect).Self-write block example:
{"error":"self_write_blocked","message":"Refusing to write to own pane (terminal_8). Use --no-guard to override.","exit_code":3,"target":"terminal_8","self":"terminal_8"}
# 1. Find the target pane
zjctl panes list | jq '.panes[] | select(.title == "build-runner")'
# → {"id":"terminal_5", ...}
# 2. Write a command to it
zjctl panes write terminal_5 "cargo test --quiet"
# 3. Send Enter to execute
zjctl panes send-keys terminal_5 Enter
# 4. Wait, then read output
sleep 3
zjctl panes read terminal_5
# → {"pane_id":"terminal_5","content":"...test results..."}
# 1. List tabs to confirm the name exists
zjctl tabs list
# → {"tabs":[{"index":0,"name":"editor","active":true,...},{"index":1,"name":"tests",...}]}
# 2. Focus the target tab
zjctl tabs focus tests
# → {"ok":true}
# 3. List panes in that tab
zjctl panes list --tab tests
# → {"panes":[{"id":"terminal_3",...},{"id":"terminal_4",...}]}
# 1. Open a floating pane with a working directory
zjctl panes open --floating --name "scratch" --cwd /tmp
# → {"pane_id":"terminal_12"}
# 2. Write a command to the new pane using the returned ID
zjctl panes write terminal_12 "ls -la"
zjctl panes send-keys terminal_12 Enter
# 1. Preview what zjctl would execute
zjctl --dry-run panes write terminal_7 "rm -rf build/"
# → {"dry_run":true,"command":["zellij","action","write-chars","--pane-id","terminal_7","rm -rf build/"]}
# 2. If the preview looks correct, run for real
zjctl panes write terminal_7 "rm -rf build/"
# → {"ok":true}
# 1. Attempt to read a pane that may have been closed
zjctl panes read terminal_99
# stderr: {"error":"invalid_target","message":"Pane not found: terminal_99","exit_code":1}
# 2. Relist panes to find the correct target — don't retry blindly
zjctl panes list
# → {"panes":[{"id":"terminal_0",...},{"id":"terminal_3",...}]}
# 3. Use the correct ID
zjctl panes read terminal_3
zjctl over raw zellij action when zjctl supports the operation.--help output in a fresh session before using commands.zjctl, say so plainly and consider raw zellij action only as a last resort.--no-guard as a deliberate override, not a default workaround.