
Pilotty
- 95 installs
- 150 repo stars
- Updated July 11, 2026
- msmps/pilotty
Helps with ai & agent building tasks.
About
pilotty is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- pilotty
- AI & Agent Building
- AI-coding skill
Pilotty by the numbers
- 95 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #4,580 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/msmps/pilotty --skill pilottyAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 95 |
|---|---|
| repo stars | ★ 150 |
| Last updated | July 11, 2026 |
| Repository | msmps/pilotty ↗ |
What it does
Helps with ai & agent building tasks.
Files
Terminal Automation with pilotty
CRITICAL: Argument Positioning
All flags (`--name`, `-s`, `--format`, etc.) MUST come BEFORE positional arguments:
# CORRECT - flags before command/arguments
pilotty spawn --name myapp vim file.txt
pilotty key -s myapp Enter
pilotty snapshot -s myapp --format text
# WRONG - flags after command (they get passed to the app, not pilotty!)
pilotty spawn vim file.txt --name myapp # FAILS: --name goes to vim
pilotty key Enter -s myapp # FAILS: -s goes nowhere usefulThis is the #1 cause of agent failures. When in doubt: flags first, then command/args.
---
Quick start
pilotty spawn vim file.txt # Start TUI app in managed session
pilotty wait-for "file.txt" # Wait for app to be ready
pilotty snapshot # Get screen state with UI elements
pilotty key i # Enter insert mode
pilotty type "Hello, World!" # Type text
pilotty key Escape # Exit insert mode
pilotty kill # End sessionCore workflow
1. Spawn: pilotty spawn <command> starts the app in a background PTY 2. Wait: pilotty wait-for <text> ensures the app is ready 3. Snapshot: pilotty snapshot returns screen state with detected UI elements 4. Understand: Parse elements[] to identify buttons, inputs, toggles 5. Interact: Use keyboard commands (key, type) to navigate and interact 6. Re-snapshot: Check content_hash to detect screen changes
Commands
Session management
pilotty spawn <command> # Start TUI app (e.g., pilotty spawn htop)
pilotty spawn --name myapp <cmd> # Start with custom session name (--name before command)
pilotty kill # Kill default session
pilotty kill -s myapp # Kill specific session
pilotty list-sessions # List all active sessions
pilotty daemon # Manually start daemon (usually auto-starts)
pilotty shutdown # Stop daemon and all sessions
pilotty examples # Show end-to-end workflow exampleScreen capture
pilotty snapshot # Full JSON with text content and elements
pilotty snapshot --format compact # JSON without text field
pilotty snapshot --format text # Plain text with cursor indicator
pilotty snapshot -s myapp # Snapshot specific session
# Wait for screen to change (eliminates need for sleep!)
HASH=$(pilotty snapshot | jq '.content_hash')
pilotty key Enter
pilotty snapshot --await-change $HASH # Block until screen changes
pilotty snapshot --await-change $HASH --settle 50 # Wait for 50ms stabilityInput
pilotty type "hello" # Type text at cursor
pilotty type -s myapp "text" # Type in specific session
pilotty key Enter # Press Enter
pilotty key Ctrl+C # Send interrupt
pilotty key Escape # Send Escape
pilotty key Tab # Send Tab
pilotty key F1 # Function key
pilotty key Alt+F # Alt combination
pilotty key Up # Arrow key
pilotty key -s myapp Ctrl+S # Key in specific session
# Key sequences (space-separated, sent in order)
pilotty key "Ctrl+X m" # Emacs chord: Ctrl+X then m
pilotty key "Escape : w q Enter" # vim :wq sequence
pilotty key "a b c" --delay 50 # Send a, b, c with 50ms delay
pilotty key -s myapp "Tab Tab Enter" # Sequence in specific sessionInteraction
pilotty click 5 10 # Click at row 5, col 10
pilotty click -s myapp 10 20 # Click in specific session
pilotty scroll up # Scroll up 1 line
pilotty scroll down 5 # Scroll down 5 lines
pilotty scroll up 10 -s myapp # Scroll in specific sessionTerminal control
pilotty resize 120 40 # Resize terminal to 120 cols x 40 rows
pilotty resize 80 24 -s myapp # Resize specific session
pilotty wait-for "Ready" # Wait for text to appear (30s default)
pilotty wait-for "Error" -r # Wait for regex pattern
pilotty wait-for "Done" -t 5000 # Wait with 5s timeout
pilotty wait-for "~" -s editor # Wait in specific sessionGlobal options
| Option | Description |
|---|---|
-s, --session <name> | Target specific session (default: "default") |
--format <fmt> | Snapshot format: full, compact, text |
-t, --timeout <ms> | Timeout for wait-for and await-change (default: 30000) |
-r, --regex | Treat wait-for pattern as regex |
--name <name> | Session name for spawn command |
--delay <ms> | Delay between keys in a sequence (default: 0, max: 10000) |
--await-change <hash> | Block snapshot until content_hash differs |
--settle <ms> | Wait for screen to be stable for this many ms (default: 0) |
Environment variables
PILOTTY_SESSION="mysession" # Default session name
PILOTTY_SOCKET_DIR="/tmp/pilotty" # Override socket directory
RUST_LOG="debug" # Enable debug loggingSnapshot Output
The snapshot command returns structured JSON with detected UI elements:
{
"snapshot_id": 42,
"size": { "cols": 80, "rows": 24 },
"cursor": { "row": 5, "col": 10, "visible": true },
"text": "Settings:\n [x] Notifications [ ] Dark mode\n [Save] [Cancel]",
"elements": [
{ "kind": "toggle", "row": 1, "col": 2, "width": 3, "text": "[x]", "confidence": 1.0, "checked": true },
{ "kind": "toggle", "row": 1, "col": 20, "width": 3, "text": "[ ]", "confidence": 1.0, "checked": false },
{ "kind": "button", "row": 2, "col": 2, "width": 6, "text": "[Save]", "confidence": 0.8 },
{ "kind": "button", "row": 2, "col": 10, "width": 8, "text": "[Cancel]", "confidence": 0.8 }
],
"content_hash": 12345678901234567890
}Use --format text for a plain text view with cursor indicator:
--- Terminal 80x24 | Cursor: (5, 10) ---
bash-3.2$ [_]The [_] shows cursor position. Use the text content to understand screen state and navigate with keyboard commands.
---
Element Detection
pilotty automatically detects interactive UI elements in terminal applications. Elements provide read-only context to help understand UI structure.
Element Kinds
| Kind | Detection Patterns | Confidence | Fields |
|---|---|---|---|
| toggle | [x], [ ], [*], ☑, ☐ | 1.0 | checked: bool |
| button | Inverse video, [OK], <Cancel>, (Submit) | 1.0 / 0.8 | focused: bool (if true) |
| input | Cursor position, ____ underscores | 1.0 / 0.6 | focused: bool (if true) |
Element Fields
| Field | Type | Description |
|---|---|---|
kind | string | Element type: button, input, or toggle |
row | number | Row position (0-based from top) |
col | number | Column position (0-based from left) |
width | number | Width in terminal cells (CJK chars = 2) |
text | string | Text content of the element |
confidence | number | Detection confidence (0.0-1.0) |
focused | bool | Whether element has focus (only present if true) |
checked | bool | Toggle state (only present for toggles) |
Confidence Levels
| Confidence | Meaning |
|---|---|
| 1.0 | High confidence: Cursor position, inverse video, checkbox patterns |
| 0.8 | Medium confidence: Bracket patterns [OK], <Cancel> |
| 0.6 | Lower confidence: Underscore input fields ____ |
Wait for Screen Changes (Recommended)
Stop guessing sleep durations! Use --await-change to wait for the screen to actually update:
# Capture baseline hash
HASH=$(pilotty snapshot | jq '.content_hash')
# Perform action
pilotty key Enter
# Wait for screen to change (blocks until hash differs)
pilotty snapshot --await-change $HASH
# Or wait for screen to stabilize (for apps that render progressively)
pilotty snapshot --await-change $HASH --settle 100Flags:
| Flag | Description |
|---|---|
--await-change <HASH> | Block until content_hash differs from this value |
--settle <MS> | After change detected, wait for screen to be stable for MS |
-t, --timeout <MS> | Maximum wait time (default: 30000) |
Why this is better than sleep:
sleep 1is a guess - too short causes race conditions, too long slows automation--await-changewaits exactly as long as needed - no more, no less--settlehandles apps that render progressively (show partial, then complete)
Waiting for Streaming AI Responses
When interacting with AI-powered TUIs (like opencode, etc.) that stream responses, you need a longer --settle time since the screen keeps updating as tokens arrive:
# 1. Capture hash before sending prompt
HASH=$(pilotty snapshot -s myapp | jq -r '.content_hash')
# 2. Type prompt and submit
pilotty type -s myapp "write me a poem about ai agents"
pilotty key -s myapp Enter
# 3. Wait for streaming response to complete
# - Use longer settle (2-3s) since AI apps pause between chunks
# - Extend timeout for long responses (60s+)
pilotty snapshot -s myapp --await-change "$HASH" --settle 3000 -t 60000
# 4. Response may be scrolled - scroll up if needed to see full output
pilotty scroll -s myapp up 10
pilotty snapshot -s myapp --format textKey parameters for streaming:
--settle 2000-3000: AI responses have pauses between chunks; 2-3 seconds ensures streaming is truly done-t 60000: Extend timeout beyond the 30s default for longer generations- The settle timer resets on each screen change, so it naturally waits until streaming stops
Manual Change Detection
For manual polling (not recommended), use content_hash directly:
# Get initial state
SNAP1=$(pilotty snapshot)
HASH1=$(echo "$SNAP1" | jq -r '.content_hash')
# Perform action
pilotty key Tab
# Check if screen changed
SNAP2=$(pilotty snapshot)
HASH2=$(echo "$SNAP2" | jq -r '.content_hash')
if [ "$HASH1" != "$HASH2" ]; then
echo "Screen changed - re-analyze elements"
fiUsing Elements Effectively
Elements are read-only context for understanding the UI. Use keyboard navigation for reliable interaction:
# 1. Get snapshot to understand UI structure
pilotty snapshot | jq '.elements'
# Output shows toggles (checked/unchecked) and buttons with positions
# 2. Navigate and interact with keyboard (reliable approach)
pilotty key Tab # Move to next element
pilotty key Space # Toggle checkbox
pilotty key Enter # Activate button
# 3. Verify state changed
pilotty snapshot | jq '.elements[] | select(.kind == "toggle")'Key insight: Use elements to understand WHAT is on screen, use keyboard to interact with it.
---
Navigation Approach
pilotty uses keyboard-first navigation, just like a human would:
# 1. Take snapshot to see the screen
pilotty snapshot --format text
# 2. Navigate using keyboard
pilotty key Tab # Move to next element
pilotty key Enter # Activate/select
pilotty key Escape # Cancel/back
pilotty key Up # Move up in list/menu
pilotty key Space # Toggle checkbox
# 3. Type text when needed
pilotty type "search term"
pilotty key Enter
# 4. Click at coordinates for mouse-enabled TUIs
pilotty click 5 10 # Click at row 5, col 10Key insight: Parse the snapshot text and elements to understand what's on screen, then use keyboard commands to navigate. This works reliably across all TUI applications.
---
Example: Edit file with vim
# 1. Spawn vim
pilotty spawn --name editor vim /tmp/hello.txt
# 2. Wait for vim to load and capture baseline hash
pilotty wait-for -s editor "hello.txt"
HASH=$(pilotty snapshot -s editor | jq '.content_hash')
# 3. Enter insert mode
pilotty key -s editor i
# 4. Type content
pilotty type -s editor "Hello from pilotty!"
# 5. Wait for screen to update, then exit (no sleep needed!)
pilotty snapshot -s editor --await-change $HASH --settle 50
pilotty key -s editor "Escape : w q Enter"
# 6. Verify session ended
pilotty list-sessionsAlternative using individual keys:
pilotty key -s editor Escape
pilotty type -s editor ":wq"
pilotty key -s editor EnterExample: Dialog checklist interaction
# 1. Spawn dialog checklist (--name before command)
pilotty spawn --name opts dialog --checklist "Select features:" 12 50 4 \
"notifications" "Push notifications" on \
"darkmode" "Dark mode theme" off \
"autosave" "Auto-save documents" on \
"telemetry" "Usage analytics" off
# 2. Wait for dialog to render (use await-change, not sleep!)
pilotty snapshot -s opts --settle 200 # Wait for initial render to stabilize
# 3. Get snapshot and examine elements, capture hash
SNAP=$(pilotty snapshot -s opts)
echo "$SNAP" | jq '.elements[] | select(.kind == "toggle")'
HASH=$(echo "$SNAP" | jq '.content_hash')
# 4. Navigate to "darkmode" and toggle it
pilotty key -s opts Down # Move to second option
pilotty key -s opts Space # Toggle it on
# 5. Wait for change and verify
pilotty snapshot -s opts --await-change $HASH | jq '.elements[] | select(.kind == "toggle") | {text, checked}'
# 6. Confirm selection
pilotty key -s opts Enter
# 7. Clean up
pilotty kill -s optsExample: Form filling with elements
# 1. Spawn a form application
pilotty spawn --name form my-form-app
# 2. Get snapshot to understand form structure
pilotty snapshot -s form | jq '.elements'
# Shows inputs, toggles, and buttons with positions for click command
# 3. Tab to first input (likely already focused)
pilotty type -s form "myusername"
# 4. Tab to password field
pilotty key -s form Tab
pilotty type -s form "mypassword"
# 5. Tab to remember me and toggle
pilotty key -s form Tab
pilotty key -s form Space
# 6. Tab to Login and activate
pilotty key -s form Tab
pilotty key -s form Enter
# 7. Check result
pilotty snapshot -s form --format textExample: Monitor with htop
# 1. Spawn htop
pilotty spawn --name monitor htop
# 2. Wait for display
pilotty wait-for -s monitor "CPU"
# 3. Take snapshot to see current state
pilotty snapshot -s monitor --format text
# 4. Send commands
pilotty key -s monitor F9 # Kill menu
pilotty key -s monitor q # Quit
# 5. Kill session
pilotty kill -s monitorExample: Interact with AI TUI (opencode, etc.)
AI-powered TUIs stream responses, requiring special handling:
# 1. Spawn the AI app
pilotty spawn --name ai opencode
# 2. Wait for the prompt to be ready
pilotty wait-for -s ai "Ask anything" -t 15000
# 3. Capture baseline hash
HASH=$(pilotty snapshot -s ai | jq -r '.content_hash')
# 4. Type prompt and submit
pilotty type -s ai "explain the architecture of this codebase"
pilotty key -s ai Enter
# 5. Wait for streaming response to complete
# - settle=3000: Wait 3s of no changes to ensure streaming is done
# - timeout=60000: Allow up to 60s for long responses
pilotty snapshot -s ai --await-change "$HASH" --settle 3000 -t 60000 --format text
# 6. If response is long and scrolled, scroll up to see full output
pilotty scroll -s ai up 20
pilotty snapshot -s ai --format text
# 7. Clean up
pilotty kill -s aiGotchas with AI apps:
- Use
--settle 2000-3000because AI responses pause between chunks - Extend timeout with
-t 60000for complex prompts - Long responses may scroll the terminal; use
scroll upto see the beginning - The settle timer resets on each screen update, so it waits for true completion
---
Sessions
Each session is isolated with its own:
- PTY (pseudo-terminal)
- Screen buffer
- Child process
# Run multiple apps (--name must come before the command)
pilotty spawn --name monitoring htop
pilotty spawn --name editor vim file.txt
# Target specific session
pilotty snapshot -s monitoring
pilotty key -s editor Ctrl+S
# List all
pilotty list-sessions
# Kill specific
pilotty kill -s editorThe first session spawned without --name is automatically named default.
Important: The --name flag must come before the command. Everything after the command is passed as arguments to that command.Daemon Architecture
pilotty uses a background daemon for session management:
- Auto-start: Daemon starts on first command
- Auto-stop: Shuts down after 5 minutes with no sessions
- Session cleanup: Sessions removed when process exits (within 500ms)
- Shared state: Multiple CLI calls share sessions
You rarely need to manage the daemon manually.
Error Handling
Errors include actionable suggestions:
{
"code": "SESSION_NOT_FOUND",
"message": "Session 'abc123' not found",
"suggestion": "Run 'pilotty list-sessions' to see available sessions"
}{
"code": "SPAWN_FAILED",
"message": "Failed to spawn process: command not found",
"suggestion": "Check that the command exists and is in PATH"
}---
Common Patterns
Reliable action + wait (recommended)
# The pattern: capture hash, act, await change
HASH=$(pilotty snapshot | jq '.content_hash')
pilotty key Enter
pilotty snapshot --await-change $HASH --settle 50
# This replaces fragile patterns like:
# pilotty key Enter && sleep 1 && pilotty snapshot # BAD: guessingWait then act
pilotty spawn my-app
pilotty wait-for "Ready" # Ensure app is ready
pilotty snapshot # Then snapshotCheck state before action
pilotty snapshot --format text | grep "Error" # Check for errors
pilotty key Enter # Then proceedCheck for specific element
# Check if the first toggle is checked
pilotty snapshot | jq '.elements[] | select(.kind == "toggle") | {text, checked}' | head -1
# Find element at specific position
pilotty snapshot | jq '.elements[] | select(.row == 5 and .col == 10)'Retry on timeout
pilotty wait-for "Ready" -t 5000 || {
pilotty snapshot --format text # Check what's on screen
# Adjust approach based on actual state
}---
Deep-dive Documentation
For detailed patterns and edge cases, see:
| Reference | Description |
|---|---|
| references/session-management.md | Multi-session patterns, isolation, cleanup |
| references/key-input.md | Complete key combinations reference |
| references/element-detection.md | Detection rules, confidence, patterns |
Ready-to-use Templates
Executable workflow scripts:
| Template | Description |
|---|---|
| templates/vim-workflow.sh | Edit file with vim, save, exit |
| templates/dialog-interaction.sh | Handle dialog/whiptail prompts |
| templates/multi-session.sh | Parallel TUI orchestration |
| templates/element-detection.sh | Element detection demo |
Usage:
./templates/vim-workflow.sh /tmp/myfile.txt "File content here"
./templates/dialog-interaction.sh
./templates/multi-session.sh
./templates/element-detection.shElement Detection
pilotty automatically detects interactive UI elements in terminal applications. Elements provide read-only context to help agents understand UI structure.
Overview
pilotty analyzes terminal screen content and detects:
- Toggles: Checkboxes like
[x],[ ],[*],☑,☐ - Buttons: Action elements like
[OK],<Cancel>,(Submit) - Inputs: Text fields marked by underscores
____or cursor position
Each detected element includes:
- Kind, position (row, col), width, text content
- Confidence score (0.0-1.0)
- State information (checked for toggles, focused for inputs/buttons)
Detection Rules
Priority Order (Highest to Lowest)
1. Cursor Position - Input (confidence: 1.0, focused: true) 2. Checkbox Patterns - Toggle (confidence: 1.0) 3. Inverse Video - Button (confidence: 1.0, focused: true) 4. Bracket Patterns - Button (confidence: 0.8) 5. Underscore Fields - Input (confidence: 0.6)
Toggle Detection
Toggles are detected from checkbox patterns:
| Pattern | State | Notes |
|---|---|---|
[x], [X] | checked: true | Standard checked |
[ ] | checked: false | Standard unchecked |
[*] | checked: true | Dialog/ncurses style |
☑, ✓, ✔, ☒ | checked: true | Unicode checkmarks |
☐, □ | checked: false | Unicode unchecked |
Example detection:
{
"kind": "toggle",
"row": 5,
"col": 2,
"width": 3,
"text": "[x]",
"confidence": 1.0,
"checked": true
}Button Detection
Buttons are detected from:
1. Inverse video (highest confidence)
- Text with reversed foreground/background colors
- Common in dialog, whiptail, and ncurses apps
- Confidence: 1.0, focused: true
2. Bracket patterns (medium confidence)
- Square brackets:
[OK],[Cancel],[Save] - Angle brackets:
<Yes>,<No> - Parentheses:
(Submit),(Reset) - Confidence: 0.8
Example detection:
{
"kind": "button",
"row": 10,
"col": 5,
"width": 6,
"text": "[Save]",
"confidence": 0.8
}Input Detection
Inputs are detected from:
1. Cursor position (highest confidence)
- The cell where the cursor is located
- Confidence: 1.0, focused: true
2. Underscore runs (lower confidence)
- 3+ consecutive underscores:
___,__________ - Common in form-style TUIs
- Confidence: 0.6
Example detection:
{
"kind": "input",
"row": 8,
"col": 12,
"width": 10,
"text": "__________",
"confidence": 0.6
}Non-Interactive Patterns (Filtered)
The following patterns are recognized but NOT returned as interactive elements:
| Pattern | Why Filtered |
|---|---|
http://, https:// | Links are not clickable in most TUIs |
[====], [####] | Progress bars |
[ERROR], [WARNING], [INFO] | Status indicators |
[1], [2], 1), a) | Menu prefixes |
├, ┤, │, ┌, ┐ | Box-drawing characters |
Element Fields Reference
| Field | Type | Required | Description |
|---|---|---|---|
kind | string | Yes | button, input, or toggle |
row | number | Yes | Row position (0-based from top) |
col | number | Yes | Column position (0-based from left) |
width | number | Yes | Width in terminal cells |
text | string | Yes | Element text content |
confidence | number | Yes | Detection confidence (0.0-1.0) |
focused | bool | No | Present and true if element has focus |
checked | bool | No | Present for toggles only |
Width Calculation
Element width uses Unicode display width:
- ASCII characters: width 1
- CJK characters (Chinese, Japanese, Korean): width 2
- Emoji: width 2
- Zero-width characters: width 0
This matches terminal column alignment.
Content Hash
Each snapshot includes a content_hash field for change detection:
{
"content_hash": 12345678901234567890,
...
}The hash is computed from the visible screen text content. Use it to:
- Detect if the screen changed between snapshots
- Avoid re-processing unchanged screens
HASH1=$(pilotty snapshot | jq -r '.content_hash')
pilotty key Tab
HASH2=$(pilotty snapshot | jq -r '.content_hash')
[ "$HASH1" != "$HASH2" ] && echo "Screen changed"Best Practices
1. Elements for Understanding, Keyboard for Interaction
Elements tell you WHAT is on screen. Use keyboard to interact:
# See what's on screen
pilotty snapshot | jq '.elements[] | {kind, text, row, col, checked}'
# Navigate with keyboard
pilotty key Tab # Move between elements
pilotty key Space # Toggle checkboxes
pilotty key Enter # Activate buttons2. Check Confidence Levels
Higher confidence means more reliable detection:
# Filter to high-confidence elements only
pilotty snapshot | jq '.elements[] | select(.confidence >= 0.8)'3. Find Elements by Content or Position
# Find element by text content
pilotty snapshot | jq '.elements[] | select(.text | contains("Save"))'
# Find element at specific position
pilotty snapshot | jq '.elements[] | select(.row == 5 and .col == 10)'
# Get first toggle
pilotty snapshot | jq '[.elements[] | select(.kind == "toggle")][0]'Limitations
What Detection Does NOT Find
1. Menu items without markers - Plain text menus need keyboard navigation 2. Custom widgets - Non-standard UI patterns may not be recognized 3. Color-only highlighting - Elements must have text patterns or inverse video 4. Disabled elements - No distinction between enabled/disabled
What Detection Cannot Do
1. Click elements directly by name - Use row/col with click command 2. Track elements across screens - Elements may move; use text content to re-find
Troubleshooting
No Elements Detected
1. Check if the app uses standard patterns:
pilotty snapshot --format text # View raw screen2. Look for inverse video (may show elements on button/input):
pilotty snapshot | jq '.elements[] | select(.confidence == 1.0)'Wrong Element Kind
The classifier uses heuristics. If [x] is detected as a button instead of toggle: 1. Check for surrounding context 2. Use text field to identify element purpose
Elements Missing After Action
Element positions may change between snapshots. Track elements by:
- Text content (most reliable)
- Element kind
- Approximate row/column position
Example: Complete Workflow
#!/bin/bash
SESSION="form"
# 1. Spawn application
pilotty spawn --name $SESSION dialog --checklist "Options:" 15 50 4 \
"opt1" "Feature A" on \
"opt2" "Feature B" off \
"opt3" "Feature C" on \
"opt4" "Feature D" off
sleep 0.5
# 2. Analyze initial state
echo "Initial state:"
pilotty snapshot -s $SESSION | jq '.elements[] | select(.kind == "toggle") | {text, checked}'
# 3. Find unchecked toggles
UNCHECKED=$(pilotty snapshot -s $SESSION | jq '[.elements[] | select(.kind == "toggle" and .checked == false)] | length')
echo "Unchecked toggles: $UNCHECKED"
# 4. Navigate and toggle opt2
pilotty key -s $SESSION Down # Move to opt2
pilotty key -s $SESSION Space # Toggle it
# 5. Verify change via content_hash
HASH1=$(pilotty snapshot -s $SESSION | jq -r '.content_hash')
echo "Hash after toggle: $HASH1"
# 6. Confirm and check final state
pilotty key -s $SESSION Enter
sleep 0.3
echo "Final state:"
pilotty snapshot -s $SESSION | jq '.elements[] | select(.kind == "toggle") | {text, checked}'
# 7. Cleanup
pilotty kill -s $SESSIONKey Input Reference
Complete reference for key combinations supported by pilotty key.
Basic Usage
pilotty key <key> # Send single key to default session
pilotty key -s myapp <key> # Send to specific session
pilotty key "key1 key2 key3" # Send key sequence (space-separated)
pilotty key "key1 key2" --delay 50 # Sequence with 50ms delay between keysNamed Keys
| Key | Aliases | Description |
|---|---|---|
Enter | Return | Enter/Return key |
Tab | Tab key | |
Escape | Esc | Escape key |
Space | Space bar | |
Backspace | Backspace key | |
Delete | Del | Delete key |
Insert | Ins | Insert key |
Arrow Keys
| Key | Aliases | Description |
|---|---|---|
Up | ArrowUp | Up arrow |
Down | ArrowDown | Down arrow |
Left | ArrowLeft | Left arrow |
Right | ArrowRight | Right arrow |
Navigation Keys
| Key | Aliases | Description |
|---|---|---|
Home | Home key | |
End | End key | |
PageUp | PgUp | Page up |
PageDown | PgDn | Page down |
Function Keys
| Key | Description |
|---|---|
F1 | Function key 1 |
F2 | Function key 2 |
F3 | Function key 3 |
F4 | Function key 4 |
F5 | Function key 5 |
F6 | Function key 6 |
F7 | Function key 7 |
F8 | Function key 8 |
F9 | Function key 9 |
F10 | Function key 10 |
F11 | Function key 11 |
F12 | Function key 12 |
Modifier Combinations
Ctrl Combinations
| Key | Aliases | Common Use |
|---|---|---|
Ctrl+C | Control+C | Interrupt/cancel |
Ctrl+D | EOF/exit | |
Ctrl+Z | Suspend process | |
Ctrl+L | Clear screen | |
Ctrl+A | Beginning of line (bash, emacs) | |
Ctrl+E | End of line (bash, emacs) | |
Ctrl+K | Kill to end of line | |
Ctrl+U | Kill to beginning of line | |
Ctrl+W | Kill word backward | |
Ctrl+R | Reverse search (bash) | |
Ctrl+S | Save (many apps) | |
Ctrl+Q | Quit (some apps) | |
Ctrl+X | Cut / prefix key | |
Ctrl+V | Paste / literal next | |
Ctrl+G | Cancel (emacs) | |
Ctrl+O | Open (many apps) | |
Ctrl+N | Next / new | |
Ctrl+P | Previous | |
Ctrl+F | Forward / find | |
Ctrl+B | Backward |
Alt Combinations
| Key | Aliases | Common Use |
|---|---|---|
Alt+F | Meta+F, Option+F | Forward word / File menu |
Alt+B | Backward word | |
Alt+D | Delete word forward | |
Alt+Backspace | Delete word backward | |
Alt+. | Last argument (bash) | |
Alt+Tab | (Usually handled by window manager) |
Shift Combinations
| Key | Description |
|---|---|
Shift+Tab | Reverse tab (previous field) |
Shift+Enter | Shift+Enter (app-specific) |
Shift+Up | Select up (some apps) |
Shift+Down | Select down (some apps) |
Combined Modifiers
| Key | Description |
|---|---|
Ctrl+Alt+C | Ctrl+Alt+C |
Ctrl+Shift+C | Copy (some terminals) |
Ctrl+Shift+V | Paste (some terminals) |
Special Characters
| Key | Description |
|---|---|
Plus | Literal + character |
Key Sequences
Send multiple keys in order with a single command. Keys are space-separated:
# Emacs-style chords
pilotty key "Ctrl+X Ctrl+S" # Save file
pilotty key "Ctrl+X Ctrl+C" # Exit Emacs
pilotty key "Ctrl+X m" # Compose mail
# vim command sequences
pilotty key "Escape : w q Enter" # Save and quit
pilotty key "Escape : q ! Enter" # Quit without saving
pilotty key "g g d G" # Delete entire file
# Navigation sequences
pilotty key "Tab Tab Enter" # Tab twice then Enter
pilotty key "Down Down Space" # Move down twice and selectInter-key Delay
Use --delay for TUIs that need time between keys:
pilotty key "Tab Tab Enter" --delay 100 # 100ms between each key
pilotty key "F9 Down Enter" --delay 50 # htop kill menu navigation| Option | Description |
|---|---|
--delay <ms> | Milliseconds between keys (default: 0, max: 10000) |
When to Use Sequences vs Individual Keys
Use sequences for:
- Emacs/vim chords that must be sent together
- Predictable navigation patterns
- Reducing command overhead
Use individual keys when:
- You need to check screen state between keys
- Timing is unpredictable
- Different paths based on UI state
Common TUI Patterns
Dialog/Whiptail
pilotty key Tab # Move between buttons
pilotty key Enter # Activate button
pilotty key Space # Toggle checkbox
pilotty key Escape # Cancel dialogVim
pilotty key i # Insert mode (use pilotty type for text)
pilotty key Escape # Normal mode
pilotty key Ctrl+C # Also exits insert mode
pilotty type ":wq" # Command (then Enter)
pilotty key Enter
# Using sequences for common operations
pilotty key "Escape : w q Enter" # Save and quit
pilotty key "Escape : q ! Enter" # Force quit
pilotty key "Escape d d" # Delete line
pilotty key "Escape g g" # Go to topHtop
pilotty key F1 # Help
pilotty key F2 # Setup
pilotty key F5 # Tree view
pilotty key F9 # Kill process
pilotty key F10 # Quit
pilotty key q # Also quitLess/More
pilotty key Space # Page down
pilotty key b # Page up
pilotty key q # Quit
pilotty key / # Search (then type pattern)
pilotty key n # Next match
pilotty key N # Previous matchNano
pilotty key Ctrl+O # Save
pilotty key Ctrl+X # Exit
pilotty key Ctrl+K # Cut line
pilotty key Ctrl+U # Paste
pilotty key Ctrl+W # Search
# Using sequences
pilotty key "Ctrl+O Enter" # Save with default filename
pilotty key "Ctrl+X n" # Exit without saving (answer 'n' to save prompt)Tmux (default prefix)
pilotty key Ctrl+B # Prefix key
# Then send the command key:
pilotty key c # New window
pilotty key n # Next window
pilotty key p # Previous window
pilotty key d # Detach
# Using sequences for tmux commands
pilotty key "Ctrl+B c" # Prefix + new window
pilotty key "Ctrl+B n" # Prefix + next window
pilotty key "Ctrl+B d" # Prefix + detachReadline/Bash
pilotty key Ctrl+A # Beginning of line
pilotty key Ctrl+E # End of line
pilotty key Ctrl+U # Clear line
pilotty key Ctrl+R # Reverse search
pilotty key Ctrl+L # Clear screen
pilotty key Up # Previous history
pilotty key Down # Next historyCase Sensitivity
- Named keys are case-insensitive:
Enter,ENTER,enterall work - Letter keys with Ctrl/Alt are case-insensitive:
Ctrl+c=Ctrl+C - Plain letters: Use
pilotty typefor text, notpilotty key
Escaping
The + character is the modifier separator. To type a literal +:
pilotty key Plus # Sends the + character
# Or use type for text:
pilotty type "2+2" # Types "2+2"Troubleshooting
Key Not Recognized
# Check if it's a named key or text
pilotty key Enter # Named key
pilotty type "hello" # Text inputModifier Not Working
Some apps intercept modifiers before the terminal sees them. Try:
# Check raw terminal behavior
pilotty spawn cat
pilotty key Ctrl+C # Should show ^C or exitTiming Issues
Some TUIs need time to process input:
pilotty key F9 # Opens menu
pilotty wait-for "SIGTERM" # Wait for menu
pilotty key Enter # Then selectSession Management
pilotty manages multiple isolated terminal sessions, each running its own TUI application with independent state.
CRITICAL: Flag Positioning
All flags MUST come BEFORE positional arguments. This applies to --name, -s/--session, and all other options:
# CORRECT
pilotty spawn --name myapp vim file.txt
pilotty key -s myapp Enter
pilotty snapshot -s myapp --format text
# WRONG - flags after positional args get passed to the command, not pilotty
pilotty spawn vim file.txt --name myapp # --name goes to vim, session uses "default"
pilotty key Enter -s myapp # -s is ignored, targets wrong session---
Session Basics
Each session has:
- PTY: Pseudo-terminal for the application
- Screen buffer: Terminal emulator state
- Child process: The running application
Creating Sessions
Default Session
The first spawn without --name creates the default session:
pilotty spawn htop
# Creates session named "default"
pilotty snapshot
# Snapshots the "default" sessionNamed Sessions
Use --name for multiple concurrent sessions. Note: --name must come before the command:
pilotty spawn --name monitoring htop
pilotty spawn --name editor vim file.txt
pilotty spawn --name git lazygitSession Naming Rules
- Names are sanitized (alphanumeric, hyphens, underscores)
- Path traversal attempts (
../) are rejected - Names must be unique per daemon instance
Targeting Sessions
Use -s or --session to target a specific session:
# Snapshot specific session
pilotty snapshot -s monitoring
# Send key to specific session
pilotty key -s editor Ctrl+S
# Send key in specific session
pilotty key -s git Enter
# Kill specific session
pilotty kill -s monitoringWithout -s, commands target the most recently used session (or default).
Listing Sessions
pilotty list-sessionsOutput:
{
"sessions": [
{ "id": "abc123", "name": "monitoring", "command": "htop" },
{ "id": "def456", "name": "editor", "command": "vim file.txt" },
{ "id": "ghi789", "name": "git", "command": "lazygit" }
]
}Session Lifecycle
Spawn
pilotty spawn --name myapp my-command arg1 arg21. Daemon creates PTY 2. Forks child process with command 3. Initializes terminal emulator (default: 80x24) 4. Returns session ID
Active Use
While a session is active:
- Screen buffer updates on process output
- Cursor position is tracked
- Terminal size can be changed with
resize
Process Exit
When the child process exits:
- Session is marked for cleanup
- Cleanup happens within 500ms
- Session is removed from list
Manual Kill
pilotty kill -s myappSends SIGTERM to the child process, then cleans up.
Multi-Session Patterns
Parallel Monitoring
Run multiple apps and switch between them:
# Start apps (--name before command)
pilotty spawn --name cpu htop
pilotty spawn --name io iotop
pilotty spawn --name net nethogs
# Check each
pilotty snapshot -s cpu --format text
pilotty snapshot -s io --format text
pilotty snapshot -s net --format text
# Clean up
pilotty kill -s cpu
pilotty kill -s io
pilotty kill -s netEditor + Preview
Edit a file while watching output:
# Start editor (--name before command)
pilotty spawn --name editor vim main.py
# Start file watcher
pilotty spawn --name preview watch -n1 python main.py
# Edit
pilotty key -s editor i
pilotty type -s editor "print('hello')"
pilotty key -s editor Escape
pilotty type -s editor ":w"
pilotty key -s editor Enter
# Check preview
pilotty snapshot -s preview --format textPipeline Workflow
Sequential operations across sessions:
# Setup (--name before command)
pilotty spawn --name worker bash
# Run commands
pilotty type -s worker "curl -s https://api.example.com > data.json"
pilotty key -s worker Enter
pilotty wait-for -s worker "$" # Wait for prompt
pilotty type -s worker "jq '.items[]' data.json"
pilotty key -s worker Enter
pilotty wait-for -s worker "$"
# Get output
pilotty snapshot -s worker --format textSession Isolation
Sessions are fully isolated:
- Separate PTY file descriptors
- Independent screen buffers
- Independent cursor positions
- No shared state between sessions
This means:
- Killing session A doesn't affect session B
- Each session can have different terminal sizes
- Snapshots from one session don't affect others
Daemon Lifecycle
The daemon manages all sessions:
Auto-Start
The daemon starts automatically on the first command:
pilotty spawn vim # Starts daemon if not runningAuto-Stop
After 5 minutes with no active sessions, the daemon shuts down automatically.
Manual Control
pilotty daemon # Manually start daemon
pilotty stop # Stop daemon and all sessionsSocket Location
The daemon creates a Unix socket at (in priority order):
1. $PILOTTY_SOCKET_DIR/pilotty.sock 2. $XDG_RUNTIME_DIR/pilotty/pilotty.sock 3. ~/.pilotty/pilotty.sock 4. /tmp/pilotty/pilotty.sock
Environment Variables
| Variable | Description |
|---|---|
PILOTTY_SESSION | Default session name for all commands |
PILOTTY_SOCKET_DIR | Override socket directory |
Example:
export PILOTTY_SESSION=editor
pilotty snapshot # Targets "editor" session without -s flagError Handling
Session Not Found
{
"code": "SESSION_NOT_FOUND",
"message": "Session 'myapp' not found",
"suggestion": "Run 'pilotty list-sessions' to see available sessions"
}Session Already Exists
Attempting to spawn with a name that's already in use:
{
"code": "SESSION_EXISTS",
"message": "Session 'myapp' already exists",
"suggestion": "Use a different name or kill the existing session first"
}Best Practices
1. Put --name before command: pilotty spawn --name myapp cmd (not after) 2. Use meaningful names: --name editor is better than --name s1 3. Clean up when done: Kill sessions you're finished with 4. Don't rely on default: For multi-session work, always name your sessions 5. Check session exists: Use list-sessions before targeting 6. Handle process exit: Sessions auto-cleanup, but check if your command is still running
#!/bin/bash
# Template: Interact with dialog/whiptail prompts
# Demonstrates handling various dialog types with element detection
#
# Usage: ./dialog-interaction.sh
# Requires: dialog or whiptail installed
set -euo pipefail
SESSION_NAME="dialog-demo"
# Check for dialog
if ! command -v dialog &> /dev/null; then
echo "Error: 'dialog' is not installed"
echo "Install with: brew install dialog (macOS) or apt install dialog (Linux)"
exit 1
fi
# Cleanup on exit
cleanup() {
pilotty kill -s "$SESSION_NAME" 2>/dev/null || true
}
trap cleanup EXIT
echo "=== Dialog Interaction Demo ==="
# --- Yes/No Dialog ---
echo ""
echo "1. Yes/No Dialog"
pilotty spawn --name "$SESSION_NAME" dialog --yesno "Do you want to continue?" 10 40 >/dev/null
# Wait for dialog to render
pilotty wait-for -s "$SESSION_NAME" "continue" -t 5000 >/dev/null
# Show detected elements
echo "Detected elements:"
pilotty snapshot -s "$SESSION_NAME" | jq -r '.elements[] | " \(.kind) \(.text) at (\(.row),\(.col))"'
# Select Yes using keyboard (Enter selects the default button)
pilotty key -s "$SESSION_NAME" Enter >/dev/null
sleep 0.5
echo "Selected: Yes (via Enter)"
# --- Menu Dialog ---
echo ""
echo "2. Menu Dialog"
pilotty spawn --name "$SESSION_NAME" dialog --menu "Choose an option:" 15 50 4 \
1 "Option One" \
2 "Option Two" \
3 "Option Three" \
4 "Exit" >/dev/null
pilotty wait-for -s "$SESSION_NAME" "Choose" -t 5000 >/dev/null
# Navigate with arrow keys
pilotty key -s "$SESSION_NAME" Down >/dev/null # Move to option 2
pilotty key -s "$SESSION_NAME" Down >/dev/null # Move to option 3
pilotty key -s "$SESSION_NAME" Enter >/dev/null # Select
sleep 0.5
echo "Selected: Option Three (via arrow keys + Enter)"
# --- Checklist Dialog with Element Detection ---
echo ""
echo "3. Checklist Dialog (with element detection)"
pilotty spawn --name "$SESSION_NAME" dialog --checklist "Select items:" 15 50 4 \
1 "Item A" off \
2 "Item B" off \
3 "Item C" off \
4 "Item D" off >/dev/null
pilotty wait-for -s "$SESSION_NAME" "Select" -t 5000 >/dev/null
# Show initial toggle states
echo "Initial toggle states:"
pilotty snapshot -s "$SESSION_NAME" | jq -r '.elements[] | select(.kind == "toggle") | " \(.text) at (\(.row),\(.col)) checked=\(.checked)"'
# Toggle items with Space
pilotty key -s "$SESSION_NAME" Space >/dev/null # Toggle Item A
pilotty key -s "$SESSION_NAME" Down >/dev/null
pilotty key -s "$SESSION_NAME" Down >/dev/null
pilotty key -s "$SESSION_NAME" Space >/dev/null # Toggle Item C
# Show updated toggle states
echo "After toggling:"
pilotty snapshot -s "$SESSION_NAME" | jq -r '.elements[] | select(.kind == "toggle") | " \(.text) at (\(.row),\(.col)) checked=\(.checked)"'
pilotty key -s "$SESSION_NAME" Enter >/dev/null # Confirm
sleep 0.5
echo "Selected: Item A, Item C"
# --- Input Dialog ---
echo ""
echo "4. Input Dialog"
pilotty spawn --name "$SESSION_NAME" dialog --inputbox "Enter your name:" 10 40 >/dev/null
pilotty wait-for -s "$SESSION_NAME" "name" -t 5000 >/dev/null
# Show detected input element
echo "Detected input element:"
pilotty snapshot -s "$SESSION_NAME" | jq -r '.elements[] | select(.kind == "input") | " \(.kind) at (\(.row),\(.col)) width=\(.width)"'
# Type input
pilotty type -s "$SESSION_NAME" "Agent Smith"
pilotty key -s "$SESSION_NAME" Enter >/dev/null
sleep 0.5
echo "Entered: Agent Smith"
# --- Message Box (final) ---
echo ""
echo "5. Message Box"
pilotty spawn --name "$SESSION_NAME" dialog --msgbox "Demo complete!" 10 40 >/dev/null
pilotty wait-for -s "$SESSION_NAME" "complete" -t 5000 >/dev/null
# Show button element
echo "Detected button:"
pilotty snapshot -s "$SESSION_NAME" | jq -r '.elements[] | select(.kind == "button" or .kind == "input") | " \(.kind) \(.text) at (\(.row),\(.col))"'
# Dismiss with Enter
pilotty key -s "$SESSION_NAME" Enter >/dev/null
sleep 0.5
echo ""
echo "=== Demo Complete ==="
echo ""
echo "Key takeaways:"
echo " - Use snapshot | jq '.elements' to see detected UI elements"
echo " - Toggles have 'checked' field for state tracking"
echo " - Use keyboard (Tab, Space, Enter, arrows) for reliable navigation"
echo " - content_hash can detect screen changes between snapshots"
#!/bin/bash
# Element Detection Template
# Demonstrates pilotty's element detection and interaction
#
# Usage: ./element-detection.sh
set -e
# Configuration
PILOTTY="${PILOTTY:-pilotty}"
SESSION="element-demo"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Cleanup on exit
cleanup() {
$PILOTTY kill -s "$SESSION" 2>/dev/null || true
}
trap cleanup EXIT
echo -e "${BLUE}=== Element Detection Demo ===${NC}"
echo ""
# -----------------------------------------------------------------------------
# Step 1: Spawn a TUI with UI elements
# -----------------------------------------------------------------------------
echo -e "${YELLOW}Step 1: Spawning dialog checklist...${NC}"
$PILOTTY spawn --name "$SESSION" -- dialog --checklist "Select features to enable:" 15 60 5 \
"notifications" "Push notifications" on \
"darkmode" "Dark mode theme" off \
"autosave" "Auto-save documents" on \
"analytics" "Usage analytics" off \
"updates" "Auto-updates" on >/dev/null
sleep 0.5
# -----------------------------------------------------------------------------
# Step 2: Get snapshot with elements
# -----------------------------------------------------------------------------
echo -e "${YELLOW}Step 2: Getting snapshot with detected elements...${NC}"
echo ""
SNAPSHOT=$($PILOTTY snapshot -s "$SESSION")
# Show element summary
echo -e "${GREEN}Detected elements:${NC}"
echo "$SNAPSHOT" | jq -r '.elements[] | " \(.kind) \(.text) at (\(.row),\(.col)) conf=\(.confidence)"'
echo ""
# -----------------------------------------------------------------------------
# Step 3: Analyze toggles
# -----------------------------------------------------------------------------
echo -e "${YELLOW}Step 3: Analyzing toggle states...${NC}"
echo ""
TOGGLES=$(echo "$SNAPSHOT" | jq '[.elements[] | select(.kind == "toggle")]')
CHECKED=$(echo "$TOGGLES" | jq '[.[] | select(.checked == true)] | length')
UNCHECKED=$(echo "$TOGGLES" | jq '[.[] | select(.checked == false)] | length')
echo -e " Checked toggles: ${GREEN}$CHECKED${NC}"
echo -e " Unchecked toggles: ${RED}$UNCHECKED${NC}"
echo ""
# Show each toggle
echo -e "${GREEN}Toggle details:${NC}"
echo "$TOGGLES" | jq -r '.[] | " \(.text) at (\(.row),\(.col)) checked=\(.checked)"'
echo ""
# -----------------------------------------------------------------------------
# Step 4: Toggle an unchecked option
# -----------------------------------------------------------------------------
echo -e "${YELLOW}Step 4: Toggling 'darkmode' (currently off)...${NC}"
# Get initial hash for change detection
HASH1=$(echo "$SNAPSHOT" | jq -r '.content_hash')
# Navigate to darkmode (second option) and toggle
$PILOTTY key -s "$SESSION" Down >/dev/null # Move to darkmode
$PILOTTY key -s "$SESSION" Space >/dev/null # Toggle it
sleep 0.2
# Get new snapshot and hash
SNAPSHOT2=$($PILOTTY snapshot -s "$SESSION")
HASH2=$(echo "$SNAPSHOT2" | jq -r '.content_hash')
# Verify change
if [ "$HASH1" != "$HASH2" ]; then
echo -e " ${GREEN}Screen changed! (hash: $HASH1 -> $HASH2)${NC}"
else
echo -e " ${RED}No change detected${NC}"
fi
echo ""
# Show updated toggle states
echo -e "${GREEN}Updated toggle states:${NC}"
echo "$SNAPSHOT2" | jq -r '.elements[] | select(.kind == "toggle") | " \(.text) at (\(.row),\(.col)) checked=\(.checked)"'
echo ""
# -----------------------------------------------------------------------------
# Step 5: Find and interact with button
# -----------------------------------------------------------------------------
echo -e "${YELLOW}Step 5: Looking for action button...${NC}"
BUTTON=$(echo "$SNAPSHOT2" | jq -r '.elements[] | select(.kind == "button" or .kind == "input") | "\(.text) at (\(.row),\(.col))"' | head -1)
if [ -n "$BUTTON" ]; then
echo -e " Found button: ${GREEN}$BUTTON${NC}"
else
echo -e " ${YELLOW}No button element detected, using keyboard to confirm${NC}"
fi
echo ""
# -----------------------------------------------------------------------------
# Step 6: Confirm selection
# -----------------------------------------------------------------------------
echo -e "${YELLOW}Step 6: Confirming selection with Enter...${NC}"
$PILOTTY key -s "$SESSION" Enter >/dev/null
sleep 0.3
# Check final state
echo -e "${GREEN}Final screen state:${NC}"
$PILOTTY snapshot -s "$SESSION" --format text 2>/dev/null | head -5 || echo " (dialog closed)"
echo ""
# -----------------------------------------------------------------------------
# Summary
# -----------------------------------------------------------------------------
echo -e "${BLUE}=== Summary ===${NC}"
echo ""
echo "This demo showed how to:"
echo " 1. Spawn a TUI application"
echo " 2. Get snapshot with detected elements"
echo " 3. Analyze element states (toggles, buttons)"
echo " 4. Use content_hash for change detection"
echo " 5. Navigate with keyboard based on element context"
echo ""
echo -e "${GREEN}Demo complete!${NC}"
#!/bin/bash
# Template: Multi-session orchestration
# Run multiple TUI apps in parallel and interact with each
#
# Usage: ./multi-session.sh
# Demonstrates parallel session management
set -euo pipefail
echo "=== Multi-Session Orchestration Demo ==="
# Session names
SESSION_SHELL="worker-shell"
SESSION_MONITOR="system-monitor"
SESSION_EDITOR="file-editor"
cleanup() {
echo ""
echo "Cleaning up sessions..."
pilotty kill -s "$SESSION_SHELL" 2>/dev/null || true
pilotty kill -s "$SESSION_MONITOR" 2>/dev/null || true
pilotty kill -s "$SESSION_EDITOR" 2>/dev/null || true
echo "Done."
}
trap cleanup EXIT
# --- 1. Start all sessions ---
echo ""
echo "1. Starting sessions..."
# Shell for running commands
pilotty spawn --name "$SESSION_SHELL" bash
echo " Started: $SESSION_SHELL (bash)"
# System monitor (top is more portable than htop)
pilotty spawn --name "$SESSION_MONITOR" top
echo " Started: $SESSION_MONITOR (top)"
# Editor for a temp file
TEMP_FILE="/tmp/pilotty-demo-$$.txt"
pilotty spawn --name "$SESSION_EDITOR" vi "$TEMP_FILE"
echo " Started: $SESSION_EDITOR (vi)"
# Wait for all to be ready
pilotty wait-for -s "$SESSION_SHELL" '$' -t 5000 || true
pilotty wait-for -s "$SESSION_MONITOR" "load" -t 5000 || pilotty wait-for -s "$SESSION_MONITOR" "CPU" -t 5000 || true
pilotty wait-for -s "$SESSION_EDITOR" "~" -t 5000 || true
echo " All sessions ready"
# --- 2. List active sessions ---
echo ""
echo "2. Active sessions:"
pilotty list-sessions
# --- 3. Interact with shell ---
echo ""
echo "3. Running command in shell session..."
pilotty type -s "$SESSION_SHELL" 'echo "Hello from pilotty multi-session demo"'
pilotty key -s "$SESSION_SHELL" Enter
# Wait for command to complete
sleep 0.5
pilotty wait-for -s "$SESSION_SHELL" '$' -t 5000
# Capture output
echo " Shell output:"
pilotty snapshot -s "$SESSION_SHELL" --format text | tail -5
# --- 4. Check monitor ---
echo ""
echo "4. Checking system monitor..."
pilotty snapshot -s "$SESSION_MONITOR" --format text | head -10
echo " (truncated)"
# --- 5. Write to editor ---
echo ""
echo "5. Writing to editor..."
# Enter insert mode
pilotty key -s "$SESSION_EDITOR" i
# Type content
pilotty type -s "$SESSION_EDITOR" "# Multi-session demo"
pilotty key -s "$SESSION_EDITOR" Enter
pilotty type -s "$SESSION_EDITOR" "This file was created by pilotty"
pilotty key -s "$SESSION_EDITOR" Enter
pilotty type -s "$SESSION_EDITOR" "Running $(date)"
# Exit insert mode
pilotty key -s "$SESSION_EDITOR" Escape
# Save (but don't quit yet)
pilotty type -s "$SESSION_EDITOR" ":w"
pilotty key -s "$SESSION_EDITOR" Enter
echo " Content written to $TEMP_FILE"
# --- 6. Run another shell command ---
echo ""
echo "6. Running another shell command..."
pilotty type -s "$SESSION_SHELL" "cat $TEMP_FILE"
pilotty key -s "$SESSION_SHELL" Enter
sleep 0.5
pilotty wait-for -s "$SESSION_SHELL" '$' -t 5000
echo " File contents from shell:"
pilotty snapshot -s "$SESSION_SHELL" --format text | grep -A5 "Multi-session" || true
# --- 7. Close editor ---
echo ""
echo "7. Closing editor..."
pilotty type -s "$SESSION_EDITOR" ":q"
pilotty key -s "$SESSION_EDITOR" Enter
sleep 0.5
# --- 8. Stop monitor ---
echo ""
echo "8. Stopping monitor..."
pilotty key -s "$SESSION_MONITOR" q
sleep 0.5
# --- 9. Final shell command ---
echo ""
echo "9. Final shell command..."
pilotty type -s "$SESSION_SHELL" "echo 'Demo complete!'"
pilotty key -s "$SESSION_SHELL" Enter
sleep 0.5
# --- Summary ---
echo ""
echo "=== Demo Summary ==="
echo "Demonstrated:"
echo " - Starting multiple named sessions"
echo " - Interacting with each independently"
echo " - Running commands in a shell session"
echo " - Monitoring system with top"
echo " - Editing files with vi"
echo " - Capturing output from sessions"
echo ""
echo "Sessions will be cleaned up on exit."
# Cleanup handled by trap
#!/bin/bash
# Template: Edit a file with vim
# Creates/edits a file, writes content, saves and exits
#
# Usage: ./vim-workflow.sh <filepath> [content]
# Example: ./vim-workflow.sh /tmp/hello.txt "Hello, World!"
set -euo pipefail
FILE_PATH="${1:?Usage: $0 <filepath> [content]}"
CONTENT="${2:-}"
SESSION_NAME="vim-editor"
echo "Editing: $FILE_PATH"
# 1. Spawn vim with the file
pilotty spawn --name "$SESSION_NAME" vim "$FILE_PATH"
# 2. Wait for vim to be ready (shows filename or new file indicator)
FILENAME=$(basename "$FILE_PATH")
pilotty wait-for -s "$SESSION_NAME" "$FILENAME" -t 10000 || \
pilotty wait-for -s "$SESSION_NAME" "VIM" -t 5000
echo "Vim ready"
# 3. If content provided, enter insert mode and type it
if [ -n "$CONTENT" ]; then
echo "Writing content..."
# Enter insert mode
pilotty key -s "$SESSION_NAME" i
# Type the content
pilotty type -s "$SESSION_NAME" "$CONTENT"
# Exit insert mode
pilotty key -s "$SESSION_NAME" Escape
echo "Content written"
fi
# 4. Save and quit
echo "Saving and quitting..."
pilotty type -s "$SESSION_NAME" ":wq"
pilotty key -s "$SESSION_NAME" Enter
# 5. Wait briefly for vim to exit
sleep 0.5
# 6. Check if session is still alive (it shouldn't be)
if pilotty list-sessions 2>/dev/null | grep -q "$SESSION_NAME"; then
echo "Warning: vim session still active, killing..."
pilotty kill -s "$SESSION_NAME"
fi
echo "Done. File saved: $FILE_PATH"
# Optionally verify content
if [ -f "$FILE_PATH" ]; then
echo "--- File contents ---"
cat "$FILE_PATH"
echo "--- End ---"
fi