Skip to content
Agent Workspaces
Skill

/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

From plugin
sidecar
1.1k19 skills

Claude Code

$ npx -y skills add marcus/sidecar --skill shell-integration --agent claude-code

SKILL.md

shell-integration.SKILL.md
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
            -> adaptive per-feed presentation publication
            -> 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 reseed

Core 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) []string

Key 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. Worksp

Read more
@marcus
Ships withsidecar

Always check if you are running in Sidecar: run sidecar agents for capabilities. You might never open your editor again. Status: Ready for daily use. Please report any issues you encounter. Documentation · Getting Started · Comprehensive List of Features

Stars on GitHub

Momentum

stars in total
1.1k
Star history of marcus/sidecar on GitHub, a chart by star-history.com

Other skills on sidecar.

coordinate-agents

coordinate-agents

Drive another Sidecar-managed agent from a shell — discover targets, create the layout, start a provider, prompt and wait, read…

@marcus@marcusInstall Skill
create-adapter

create-adapter

Create conversation adapters for importing AI chat history from different tools (Claude Code, Cursor, Warp, Codex, etc.). Covers…

@marcus@marcusInstall Skill
create-modal

create-modal

Create declarative modals using the modal library API. Covers modal types (confirm, input, select, form), sections (Text,…

@marcus@marcusInstall Skill
create-plugin

create-plugin

Create new sidecar plugins implementing the plugin.Plugin interface, rendering views with Bubble Tea, handling keyboard input via…

@marcus@marcusInstall Skill
create-prompt

create-prompt

Create prompts for sidecar workspaces. Covers prompt structure (name, ticketMode, body), template variables (ticket with…

@marcus@marcusInstall Skill
create-theme

create-theme

Create custom color themes for Sidecar, including base theme selection, color overrides, gradient borders, tab styles,…

@marcus@marcusInstall Skill

More skills from other plugins.

paseo

paseo

Paseo reference for managing hosts, projects, workspaces, workspace scripts, agents, schedules, and heartbeats.

paseo11d23k
@getpaseo@getpaseoInstall Skill
10x
Auto-invoked

10x

Personalized audit that teaches the advanced Paradev (formerly Superset) features the user isn't using yet (automations, parallel…

paradev2d16k
@superset-sh@superset-shInstall Skill
capabilities

capabilities

Your capability catalog — read this at boot. Lists the temporal date-range skills and the external integrations (reached via the…

@chaitanyagiri@chaitanyagiriInstall Skill

Questions about shell-integration.

Which coding agents can use shell-integration?
shell-integration installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.