Magically control your panes in Zellij
Conjure is a Zellij plugin that lets you send commands to panes using tab:pane name format. It supports both CLI (via pipe) and an interactive UI, providing a better experience than tmux's send-keys -t.
- Why "Conjure"?
- Quick Start
- Features
- Installation
- Usage
- Use Cases
- Project Structure
- Development
- Documentation
- Contributing
- License
conjure = "to summon, to make appear as if by magic"
- 🎯 Magically summon any pane and send commands
- ✨ Instantly recall panes through interactive UI
- 🚀 Use intuitive names instead of numeric IDs
- Add plugin to config.kdl:
plugins {
conjure location="https://github.com/cellfusion/conjure/releases/download/v0.1.0/conjure.wasm"
}- Add keybinding:
keybinds {
shared_except "locked" {
bind "Ctrl p" "c" {
LaunchOrFocusPlugin "conjure" {
floating true
}
}
}
}- Restart Zellij and press
Ctrl+p, cto open Conjure!
For detailed installation instructions, see Installation below.
# Send command to a pane
zellij action pipe --plugin conjure --name send_command \
-- '{"pane_name":"terminal","text":"npm run build"}'
# List all panes (returns JSON)
zellij action pipe --plugin conjure --name list_panes
# Search panes by name/process/tab (fuzzy match, AND condition)
zellij action pipe --plugin conjure --name search_panes \
-- '{"pane_name":"terminal"}'
zellij action pipe --plugin conjure --name search_panes \
-- '{"process_name":"nvim","tab_name":"Editor"}'All CLI commands return JSON responses:
{"success":true,"data":[{"pane_id":3,"pane_title":"terminal","tab_name":"Editor","tab_position":0,"is_focused":true,"process_name":"bash"}]}- Pane Selector: Fuzzy search for quick pane discovery
- Command Input: Enter commands on the fly with history
- Presets: Execute frequently-used commands with one click
- History: Quick access to recently used panes
┌─ Conjure ────────────────────────────────┐
│ Search: term█ │
├──────────────────────────────────────────┤
│ [Tab: Editor] │
│ > terminal (zsh) - /path/to/project │
│ nvim (nvim file.rs) │
│ [Tab: Tests] │
│ watch (cargo watch) │
└──────────────────────────────────────────┘
Download the latest release from GitHub Releases:
# Download the latest release
wget https://github.com/cellfusion/conjure/releases/download/v0.1.0/conjure.wasm
# Install to Zellij plugins directory
mkdir -p ~/.config/zellij/plugins
mv conjure.wasm ~/.config/zellij/plugins/Or configure directly from URL in config.kdl (Zellij will download automatically):
plugins {
conjure location="https://github.com/cellfusion/conjure/releases/download/v0.1.0/conjure.wasm" {
plugin_select_up "Ctrl p"
plugin_select_down "Ctrl n"
}
}For developers or those who want the latest development version:
# Add wasm target (first time only)
rustup target add wasm32-wasip1
# Clone the repository
git clone https://github.com/YOUR_USERNAME/conjure.git
cd conjure
# Build
cargo build --target wasm32-wasip1 --release
# Install plugin
mkdir -p ~/.config/zellij/plugins
cp target/wasm32-wasip1/release/conjure.wasm ~/.config/zellij/plugins/Add the following to your ~/.config/zellij/config.kdl:
keybinds clear-defaults=true {
// ... your other keybindings ...
shared_except "locked" {
// Open Conjure UI
bind "Ctrl p" "c" {
LaunchOrFocusPlugin "conjure" {
floating true
move_to_focused_tab true
}
}
// Show presets
bind "Ctrl p" "C" {
MessagePlugin "conjure" {
name "show_presets"
}
}
// Show history
bind "Ctrl p" "h" {
MessagePlugin "conjure" {
name "show_history"
}
}
}
}
plugins {
conjure location="https://github.com/cellfusion/conjure/releases/download/v0.1.0/conjure.wasm" {
// UI keybindings (inside the plugin)
plugin_select_up "Ctrl p"
plugin_select_down "Ctrl n"
plugin_navigate_to "Enter"
plugin_hide "Esc"
}
}If you downloaded or built the plugin locally:
plugins {
conjure location="file:~/.config/zellij/plugins/conjure.wasm" {
plugin_select_up "Ctrl p"
plugin_select_down "Ctrl n"
plugin_navigate_to "Enter"
plugin_hide "Esc"
}
}Important: By defining the plugin in the plugins section, you can use the short name conjure in CLI commands:
# Short form (after config.kdl setup)
zellij action pipe --plugin conjure --name send_command -- '...'
# vs. Long form (without config.kdl)
zellij action pipe --plugin file:$HOME/.config/zellij/plugins/conjure.wasm --name send_command -- '...'Create ~/.config/zellij/conjure_presets.json:
{
"presets": [
{
"name": "Run Tests",
"target": "Tests:watch",
"command": "cargo test --all",
"description": "Run all unit tests"
},
{
"name": "Build Project",
"target": "Editor:terminal",
"command": "npm run build",
"description": "Production build"
}
]
}When you define the plugin in config.kdl under the plugins section, you can use the plugin name directly instead of the full path.
# Pane name only (searches all tabs)
zellij action pipe --plugin conjure --name send_command \
-- '{"pane_name":"terminal","text":"ls -la"}'
# Tab:Pane format (narrow search to specific tab)
zellij action pipe --plugin conjure --name send_command \
-- '{"tab_name":"Editor","pane_name":"terminal","text":"npm run build"}'
# Without pressing Enter (send_enter defaults to true)
zellij action pipe --plugin conjure --name send_command \
-- '{"pane_name":"terminal","text":"git status","send_enter":false}'Note: If the plugin is not defined in config.kdl, use the full path:
zellij action pipe --plugin file:$HOME/.config/zellij/plugins/conjure.wasm --name send_command \
-- '{"pane_name":"terminal","text":"echo hello"}'zellij action pipe --plugin conjure --name list_panesReturns JSON with all available panes:
{"success":true,"data":[{"pane_id":3,"pane_title":"terminal","tab_name":"Editor","tab_position":0,"is_focused":true,"process_name":"bash"}]}# Search by pane name (fuzzy match)
zellij action pipe --plugin conjure --name search_panes \
-- '{"pane_name":"term"}'
# Search by process name
zellij action pipe --plugin conjure --name search_panes \
-- '{"process_name":"nvim"}'
# Search by multiple fields (AND condition)
zellij action pipe --plugin conjure --name search_panes \
-- '{"process_name":"nvim","tab_name":"Editor"}'- ↑↓ / Ctrl p/n: Navigate through panes
- Enter: Select pane and open command input
- Esc: Close
- Type: Fuzzy search filter
- Enter: Send command
- ↑↓: Navigate command history
- Esc: Cancel
| Keybinding | Description | Config Key |
|---|---|---|
| Ctrl p, c | Open Conjure UI (pane selector) | LaunchOrFocusPlugin |
| Ctrl p, C | Show presets list | MessagePlugin "show_presets" |
| Ctrl p, h | Show history | MessagePlugin "show_history" |
| Keybinding | Description | Config Key |
|---|---|---|
| Up/Down | Move selection | plugin_select_up/plugin_select_down |
| Enter | Select and navigate | plugin_navigate_to |
| Esc | Close plugin | plugin_hide |
Modify the plugins section in config.kdl:
plugins {
conjure location="file:~/.config/zellij/plugins/conjure.wasm" {
plugin_select_up "Ctrl p" # Default: Up
plugin_select_down "Ctrl n" # Default: Down
plugin_navigate_to "Enter" # Default: Enter
plugin_hide "Esc" # Default: Esc
}
}Use an empty string to disable a keybinding:
plugins {
conjure location="file:~/.config/zellij/plugins/conjure.wasm" {
plugin_select_up "" # Disabled
}
}# Start dev server
zellij action pipe --plugin conjure --name send_command \
-- '{"tab_name":"Server","pane_name":"dev","text":"npm run dev","send_enter":true}'
# Run tests
zellij action pipe --plugin conjure --name send_command \
-- '{"tab_name":"Tests","pane_name":"unit","text":"cargo test","send_enter":true}'#!/bin/bash
# deploy.sh
# Run tests first
zellij action pipe --plugin conjure --name send_command \
-- '{"tab_name":"Tests","pane_name":"unit","text":"cargo test --all","send_enter":true}'
sleep 5
# Build project
zellij action pipe --plugin conjure --name send_command \
-- '{"tab_name":"Build","pane_name":"prod","text":"cargo build --release","send_enter":true}'
sleep 10
# Deploy
zellij action pipe --plugin conjure --name send_command \
-- '{"tab_name":"Deploy","pane_name":"terminal","text":"./scripts/deploy.sh","send_enter":true}'conjure/
├── src/
│ ├── main.rs # Plugin entry point
│ ├── state.rs # State management
│ ├── matcher.rs # Pane search logic
│ ├── pipe_handler.rs # CLI message handling
│ ├── ui/ # UI components
│ │ ├── pane_selector.rs
│ │ ├── command_input.rs
│ │ ├── preset_list.rs
│ │ └── history.rs
│ ├── history.rs # History management
│ ├── presets.rs # Preset management
│ └── config.rs # Configuration
├── examples/
│ ├── conjure_presets.json
│ ├── config.kdl
│ └── automation.sh
└── README.md
For developers who want to contribute or build from source.
- Rust 1.75+
- wasm32-wasip1 target
# Add wasm target (first time only)
rustup target add wasm32-wasip1
# Clone the repository
git clone https://github.com/YOUR_USERNAME/conjure.git
cd conjure
# Build
cargo build --target wasm32-wasip1 --release
# The compiled plugin will be at:
# target/wasm32-wasip1/release/conjure.wasm# Install to Zellij plugins directory
mkdir -p ~/.config/zellij/plugins
cp target/wasm32-wasip1/release/conjure.wasm ~/.config/zellij/plugins/# Test the plugin
zellij plugin -- file:$HOME/.config/zellij/plugins/conjure.wasm
# Test CLI mode (after adding plugin to config.kdl)
zellij action pipe --plugin conjure --name send_command \
-- '{"pane_name":"terminal","text":"echo hello","send_enter":true}'
# Test CLI mode (without config.kdl definition)
zellij action pipe --plugin file:$HOME/.config/zellij/plugins/conjure.wasm --name send_command \
-- '{"pane_name":"terminal","text":"echo hello","send_enter":true}'For detailed development information, see CLAUDE.md.
- SPECIFICATION.md - Complete specification
- CLAUDE.md - AI developer guide
This project is inspired by:
- zellij-send-keys - pane_id-based command sending
- zellij-pane-picker - Pane selection UI
- harpoon - Quick navigation
- zellij-playbooks - Command books
Contributions are welcome!
- Fork this repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - See LICENSE file for details
- Zellij team - Amazing terminal multiplexer
- awesome-zellij community
Control your panes magically ✨