/shell-integration
Interactive shell/TTY integration with tmux session management, shell command execution, control-mode output capture with polling fallback, native cursor rendering, lazy scrollback, selection, paste handling, and inline editing. Use when working on shell integration, tmux
$ npx -y skills add marcus/sidecar --skill shell-integration --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/shell-integration
Context preview
The summary Claude sees to decide when to auto-load this skill.
Interactive shell/TTY integration with tmux session management, shell command execution, control-mode output capture with polling fallback, native cursor rendering, lazy scrollback, selection, paste handling, and inline editing. Use when working on shell integration, tmux
SKILL.md
shell-integration.SKILL.mdname: shell-integration
description: >
Interactive shell/TTY integration with tmux session management, shell command
execution, control-mode output capture with polling fallback, native cursor
rendering, lazy scrollback, selection, paste handling, and inline editing.
Use when working on shell integration, tmux features, command execution, or
interactive mode.
user-invocable: false
Shell Integration
Sidecar's interactive shell allows users to type directly into tmux sessions from within the TUI. Tmux remains the PTY backend. Sidecar renders ordered control-mode bytes through the shared `tty.Model`, whose VT behavior is behind the `screenmodel` adapter rather than implemented in plugin code.
Package Structure
internal/tty/ # Shared tmux terminal abstraction
tty.go # Core Model and State types
keymap.go # Bubble Tea -> tmux key translation
messages.go # Owner/target/generation-scoped messages
session.go # tmux operations (send-keys, capture-pane, resize)
scheduler.go # Keyed fallback-poll generation ownership
control_*.go # Session-keyed tmux -C transport and manager
capture_range.go # Atomic bounded history capture
cursor.go # Cursor positioning helpers
paste.go # Paste handling (clipboard, bracketed paste)
terminal_mode.go # Capture-fallback mode recovery
output_buffer.go # Absolute, bounded live/history buffer
editor_session.go # Shared inline-editor tmux lifecycle
internal/plugins/workspace/
interactive.go # Workspace-specific interactive mode logic
interactive_selection.go # Text selection in interactive mode
terminal_viewport.go # Pure shared terminal viewport renderer
terminal_control.go # Workspace target/layout policy for tty.Model
terminal_history.go # Lazy absolute scrollback loading
terminal_search.go # Loaded-history search
terminal_links.go # Safe URL/path detection and activation
native_terminal.go # Native cursor and contextual mouse mode
view_preview.go # Agent/shell preview composition
mouse.go # Scroll handling
types.go # InteractiveState type
internal/plugins/filebrowser/
inline_edit.go # Inline editor mode using tty.Model
handlers.go # Message handling for inline edit
Data Flow
User Keypress -> handleInteractiveKeys() -> tty.MapKeyToTmux() -> tmux send-keys
Pane output -> tmux -C ordered %output bytes
-> session-pooled control actor
-> seeded screenmodel adapter
-> owner/target/generation-scoped Tea message
-> OutputBuffer + cursor/modes/history
-> pure terminal viewport + native Bubble Tea cursor
Open/resync/history -> bounded capture seed/range
Control unavailable/dead -> one scoped capture-poll fallback + clean reseedCore Abstractions
tty.Model
Embeddable component for interactive tmux functionality:
type Model struct {
Config Config // Exit key, copy/paste keys, scrollback lines
State *State // Current interactive state
Width int
Height int
OnExit func() tea.Cmd
OnAttach func() tea.Cmd
}
// Usage:
p.inlineEditor = tty.New(&tty.Config{
ExitKey: "ctrl+\\",
ScrollbackLines: 600,
})
cmd := p.inlineEditor.Enter(sessionName, paneID)tty.State
type State struct {
Active bool
TargetPane string // tmux pane ID (e.g., "%12")
TargetSession string
LastKeyTime time.Time // Input timing and fallback polling decay
CursorRow, CursorCol int
CursorVisible bool
PaneHeight, PaneWidth int
BracketedPasteEnabled bool
MouseReportingEnabled bool
OutputBuf *OutputBuffer
PollGeneration int // For invalidating stale fallback polls
}tty.OutputBuffer
Thread-safe bounded buffer with hash-based change detection:
func (b *OutputBuffer) Update(content string) bool {
rawHash := maphash.String(seed, content)
if rawHash == b.lastRawHash { return false } // Skip ALL processing
content = mouseEscapeRegex.ReplaceAllString(content, "")
b.lines = strings.Split(content, "\n")
return true
}
func (b *OutputBuffer) LinesRange(start, end int) []stringKey Mapping (`keymap.go`)
func MapKeyToTmux(msg tea.KeyPressMsg) (key string, useLiteral bool) {
if msg.Mod.Contains(tea.ModCtrl) && msg.Code >= 'a' && msg.Code <= 'z' {
return "C-" + string(msg.Code), false
}
switch msg.Code {
case tea.KeyEnter: return "Enter", false
case tea.KeyBackspace: return "BSpace", false
case tea.KeyTab: return "Tab", false
case tea.KeyUp: return "Up", false
}
if msg.Text != "" {
return msg.Text, true // Literal mode
}
return "", true
}Modified keys use CSI sequences:
case "shift+up": return "\x1b[1;2A", true
case "ctrl+up": return "\x1b[1;5A", true
case "alt+up": return "\x1b[1;3A", true
case "shift+tab": return "\x1b[Z", true
For printable characters, `tmux send-keys -l` prevents interpretation.
Capture fallback and semantic observation
const (
PollingDecayFast = 50ms // During active typing
PollingDecayMedium = 200ms // After 2s inactivity
PollingDecaySlow = 250ms // After 10s inactivity
KeystrokeDebounce = 20ms // Delay after keystroke
)Control-mode bytes are the ordinary presentation source for every visible terminal surface. Adaptive capture polling exists only until the first seeded frame and after control/model failure. Workspace agent and shell observation continues independently fo
Read more
name: shell-integration description: > Interactive shell/TTY integration with tmux session management, shell command execution, control-mode output capture with polling fallback, native cursor rendering, lazy scrollback, selection, paste handling, and inline editing. Use when working on shell integration, tmux features, command execution, or interactive mode. user-invocable: false
Shell Integration
Sidecar's interactive shell allows users to type directly into tmux sessions from within the TUI. Tmux remains the PTY backend. Sidecar renders ordered control-mode bytes through the shared `tty.Model`, whose VT behavior is behind the `screenmodel` adapter rather than implemented in plugin code.
Package Structure
internal/tty/ # Shared tmux terminal abstraction tty.go # Core Model and State types keymap.go # Bubble Tea -> tmux key translation messages.go # Owner/target/generation-scoped messages session.go # tmux operations (send-keys, capture-pane, resize) scheduler.go # Keyed fallback-poll generation ownership control_*.go # Session-keyed tmux -C transport and manager capture_range.go # Atomic bounded history capture cursor.go # Cursor positioning helpers paste.go # Paste handling (clipboard, bracketed paste) terminal_mode.go # Capture-fallback mode recovery output_buffer.go # Absolute, bounded live/history buffer editor_session.go # Shared inline-editor tmux lifecycle internal/plugins/workspace/ interactive.go # Workspace-specific interactive mode logic interactive_selection.go # Text selection in interactive mode terminal_viewport.go # Pure shared terminal viewport renderer terminal_control.go # Workspace target/layout policy for tty.Model terminal_history.go # Lazy absolute scrollback loading terminal_search.go # Loaded-history search terminal_links.go # Safe URL/path detection and activation native_terminal.go # Native cursor and contextual mouse mode view_preview.go # Agent/shell preview composition mouse.go # Scroll handling types.go # InteractiveState type internal/plugins/filebrowser/ inline_edit.go # Inline editor mode using tty.Model handlers.go # Message handling for inline edit
Data Flow
User Keypress -> handleInteractiveKeys() -> tty.MapKeyToTmux() -> tmux send-keys
Pane output -> tmux -C ordered %output bytes
-> session-pooled control actor
-> seeded screenmodel adapter
-> owner/target/generation-scoped Tea message
-> OutputBuffer + cursor/modes/history
-> pure terminal viewport + native Bubble Tea cursor
Open/resync/history -> bounded capture seed/range
Control unavailable/dead -> one scoped capture-poll fallback + clean reseedCore Abstractions
tty.Model
Embeddable component for interactive tmux functionality:
type Model struct {
Config Config // Exit key, copy/paste keys, scrollback lines
State *State // Current interactive state
Width int
Height int
OnExit func() tea.Cmd
OnAttach func() tea.Cmd
}
// Usage:
p.inlineEditor = tty.New(&tty.Config{
ExitKey: "ctrl+\\",
ScrollbackLines: 600,
})
cmd := p.inlineEditor.Enter(sessionName, paneID)tty.State
type State struct {
Active bool
TargetPane string // tmux pane ID (e.g., "%12")
TargetSession string
LastKeyTime time.Time // Input timing and fallback polling decay
CursorRow, CursorCol int
CursorVisible bool
PaneHeight, PaneWidth int
BracketedPasteEnabled bool
MouseReportingEnabled bool
OutputBuf *OutputBuffer
PollGeneration int // For invalidating stale fallback polls
}tty.OutputBuffer
Thread-safe bounded buffer with hash-based change detection:
func (b *OutputBuffer) Update(content string) bool {
rawHash := maphash.String(seed, content)
if rawHash == b.lastRawHash { return false } // Skip ALL processing
content = mouseEscapeRegex.ReplaceAllString(content, "")
b.lines = strings.Split(content, "\n")
return true
}
func (b *OutputBuffer) LinesRange(start, end int) []stringKey Mapping (`keymap.go`)
func MapKeyToTmux(msg tea.KeyPressMsg) (key string, useLiteral bool) {
if msg.Mod.Contains(tea.ModCtrl) && msg.Code >= 'a' && msg.Code <= 'z' {
return "C-" + string(msg.Code), false
}
switch msg.Code {
case tea.KeyEnter: return "Enter", false
case tea.KeyBackspace: return "BSpace", false
case tea.KeyTab: return "Tab", false
case tea.KeyUp: return "Up", false
}
if msg.Text != "" {
return msg.Text, true // Literal mode
}
return "", true
}Modified keys use CSI sequences:
case "shift+up": return "\x1b[1;2A", true case "ctrl+up": return "\x1b[1;5A", true case "alt+up": return "\x1b[1;3A", true case "shift+tab": return "\x1b[Z", true
For printable characters, `tmux send-keys -l` prevents interpretation.
Capture fallback and semantic observation
const (
PollingDecayFast = 50ms // During active typing
PollingDecayMedium = 200ms // After 2s inactivity
PollingDecaySlow = 250ms // After 10s inactivity
KeystrokeDebounce = 20ms // Delay after keystroke
)Control-mode bytes are the ordinary presentation source for every visible terminal surface. Adaptive capture polling exists only until the first seeded frame and after control/model failure. Workspace agent and shell observation continues independently fo
You might never open your editor again. Status: Ready for daily use. Please report any issues you encounter.
Other skills on sidecar.
- /create-adapter
Create conversation adapters for importing AI chat history from different tools (Claude Code, Cursor, Warp, Codex, etc.). Covers the adapter.Adapter interface, caching strategies, incremental parsing, watch/FD management, and performance standards. Use when creating a new
Open skill - /create-modal
Create declarative modals using the modal library API. Covers modal types (confirm, input, select, form), sections (Text, Buttons, Input, Textarea, Checkbox, List, When, Custom), rendering with OverlayModal, and keyboard/mouse handling. Use when adding modals or dialogs to the
Open skill - /create-plugin
Create new sidecar plugins implementing the plugin.Plugin interface, rendering views with Bubble Tea, handling keyboard input via keymap contexts, and integrating with the app shell (footer hints, event bus, adapters). Use when creating a new plugin, modifying plugin
Open skill - /create-prompt
Create prompts for sidecar workspaces. Covers prompt structure (name, ticketMode, body), template variables (ticket with fallbacks), config file locations (global vs project), and scope overrides. Use when creating or modifying prompts in sidecar config files.
Open skill - /create-theme
Create custom color themes for Sidecar, including base theme selection, color overrides, gradient borders, tab styles, per-project themes, community themes, and programmatic theme registration. Use when creating or modifying themes, adjusting UI appearance, or debugging
Open skill - /drag-pane
Drag-and-drop pane resizing implementation for two-pane plugin layouts. Covers mouse event handling via the internal/mouse package, hit region registration, drag delta calculation, width clamping, state persistence, and pane layout management. Use when working on pane resizing,
Open skill

