Skip to main content

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.

Learning Focus

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/kitty-dev-setup.sh
#!/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/kitty-monitor.sh
#!/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:

~/.config/kitty/kitty.conf
# 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:

every-hour-cron.sh
#!/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"
crontab entry
# Run every hour, but only if kitty is running
0 * * * * ~/bin/kitty-reminder.sh
Cron Caveats

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/kitty-dashboard.sh
#!/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

PitfallSymptomFix
Script fails when kitty is not running"No running kitty instance" errorCheck kitty @ ls first with guard clause
send-text target ambiguousText sent to wrong windowAlways 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 installedScript fails on JSON parsingInstall jq or use grep/awk for simple parsing
Layout changes don't persistGrid layout resetsCall set-layout again after each launch
Tab title not matchingfocus-tab doesn't find tabTitles 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

What's Next