Scripting with Kitty
Scripting kitty means writing shell scripts that use kitty @ commands to create, arrange, and manage windows and tabs automatically. This is the DevOps power-user tier — one script sets up your entire monitoring dashboard.
Write idempotent scripts that check if kitty is running, create the layout, and optionally target a specific instance. Use environment variables to make scripts context-aware.
Base Pattern: Launching Windows and Tabs
#!/bin/bash
# Ensure kitty is running
if ! kitty @ ls &>/dev/null; then
echo "No running kitty instance found. Start kitty first."
exit 1
fi
# Open a new tab named "dev"
kitty @ launch --type=tab --title "dev" --cwd ~/projects/myapp
# Split into two windows
kitty @ launch --type=window --title "editor" --cwd ~/projects/myapp
kitty @ launch --type=window --title "server" --cwd ~/projects/myapp
# Start processes
kitty @ send-text --match 'title:server' "npm run dev\n"
kitty @ send-text --match 'title:editor' "vim .\n"
Make it executable:
chmod +x ~/bin/kitty-dev-setup.sh
Idempotent Script Pattern
Check if the target tab exists before creating it:
#!/bin/bash
TAB_NAME="monitor"
# Check if tab already exists
if kitty @ ls | jq -e ".[] | select(.tabs[].title == \"$TAB_NAME\")" &>/dev/null; then
# Tab exists — focus it
kitty @ focus-tab --match "title:$TAB_NAME"
exit 0
fi
# Create new monitoring tab
kitty @ launch --type=tab --title "$TAB_NAME" --cwd /var/log
kitty @ launch --type=window --title "htop" htop
kitty @ launch --type=window --title "disk" bash -c "watch df -h"
kitty @ launch --type=window --title "memory" bash -c "watch free -h"
Command-Line Kitty Options
# Launch kitty with a specific directory
kitty --directory /var/log
# Execute a command on launch
kitty bash -c "tail -f syslog"
# Set initial window title
kitty --title "Log Viewer"
# Open a specific config
kitty --config ~/.config/kitty/monitor.conf
# Attach to an existing OS window (if applicable)
kitty --listen-on unix:/tmp/my-kitty-socket
# Pass environment variables to spawned processes
kitty --env "APP_ENV=production" bash
Environment Variables in Kitty
Kitty sets several environment variables that scripts can read:
# Inside a kitty window, check:
echo $KITTY_WINDOW_ID # unique window ID
echo $KITTY_TAB_ID # tab ID
echo $KITTY_OS_WINDOW_ID # OS window ID
# Use in scripts:
kitty @ focus-window --match "id:$KITTY_WINDOW_ID"
kitty @ close-window --match "id:$KITTY_WINDOW_ID"
The listen_on Directive
Instead of relying on the default socket, you can specify a custom socket path:
# Listen on a custom socket path
listen_on unix:/tmp/kitty-mysocket
Then target it explicitly:
kitty @ --to unix:/tmp/kitty-mysocket ls
This is useful for multiple independent kitty instances — each can have its own socket and be controlled separately.
Automation: Cron Jobs and Scripts
Use kitty @ in cron jobs to affect a running kitty instance:
#!/bin/bash
# ~/bin/kitty-reminder.sh — called from cron
kitty @ launch --type=tab --title "Reminder" \
bash -c "echo 'Top of the hour — check logs in /var/log'; read"
# Run every hour, but only if kitty is running
0 * * * * ~/bin/kitty-reminder.sh
Cron runs with a limited environment. Make sure kitty is in the crontab PATH, and DISPLAY is set if kitty needs to open windows on X11.
# Safer cron wrapper
0 * * * * PATH="$HOME/.local/bin:$PATH" DISPLAY=:0 ~/bin/kitty-reminder.sh
Full Monitoring Dashboard Script
#!/bin/bash
# Kitty Monitoring Dashboard — creates a 4-pane system monitor
DASHBOARD_TAB="system-monitor"
# Idempotent check
if kitty @ ls | jq -e ".[] | select(.tabs[].title == \"$DASHBOARD_TAB\")" &>/dev/null; then
kitty @ focus-tab --match "title:$DASHBOARD_TAB"
exit 0
fi
# Create tab
kitty @ launch --type=tab --title "$DASHBOARD_TAB"
# Create 2x2 grid
kitty @ launch --type=window
kitty @ launch --type=window
kitty @ launch --type=window
# Apply grid layout
kitty @ set-layout grid
# Send commands to each window (0-indexed from ls)
kitty @ send-text --match "tab_title:$DASHBOARD_TAB and index:0" "htop\n"
kitty @ send-text --match "tab_title:$DASHBOARD_TAB and index:1" "watch -n 2 df -h\n"
kitty @ send-text --match "tab_title:$DASHBOARD_TAB and index:2" "watch -n 2 free -h\n"
kitty @ send-text --match "tab_title:$DASHBOARD_TAB and index:3" "tail -f /var/log/syslog\n"
echo "Dashboard created in tab: $DASHBOARD_TAB"
Common Pitfalls
| Pitfall | Symptom | Fix |
|---|---|---|
| Script fails when kitty is not running | "No running kitty instance" error | Check kitty @ ls first with guard clause |
send-text target ambiguous | Text sent to wrong window | Always use specific id:N or title:... match |
| Cron can't find kitty | "command not found" | Set full PATH in crontab or use absolute path /usr/bin/kitty |
jq not installed | Script fails on JSON parsing | Install jq or use grep/awk for simple parsing |
| Layout changes don't persist | Grid layout resets | Call set-layout again after each launch |
| Tab title not matching | focus-tab doesn't find tab | Titles are set at creation; use set-tab-title to update |
Hands-On Practice
# 1) Create a simple dev setup script
cat > /tmp/kitty-practice.sh << 'SCRIPT'
#!/bin/bash
TAB="practice-$(date +%s)"
kitty @ launch --type=tab --title "$TAB"
kitty @ launch --type=window --title "shell"
kitty @ launch --type=window --title "info"
kitty @ send-text --match "title:info" "date; uname -a\n"
SCRIPT
chmod +x /tmp/kitty-practice.sh
bash /tmp/kitty-practice.sh
# 2) Check environment variables inside a kitty window
echo "Window ID: $KITTY_WINDOW_ID"
echo "Tab ID: $KITTY_TAB_ID"
echo "OS Window ID: $KITTY_OS_WINDOW_ID"
# 3) Practice with match criteria
kitty @ ls | jq '.[] | {tabs: [.tabs[] | {title, windows: [.windows[] | {id, title}]}]}'
# 4) Clean up practice tabs
kitty @ ls | jq -r '.[].tabs[] | select(.title | startswith("practice")) | .title' | \
while read tab; do
kitty @ close-tab --match "title:$tab"
done