Skip to content

obra/freshell

The Agentic IDE (or, the loving child of tmux and claude code)

https://skillcdn.ai/gh/obra/freshell

Connect this address to your AI to use this skill. How to connect

  • Unverified
  • Default branchmain
  • Commit05c6b1f
  • LicenseNone declared
View on GitHub

freshell-orchestration

Use when interacting with Freshell panes, panels, or tabs from the CLI for tmux-style automation and multi-pane workflows, outside external-browser automation tasks.

Path
.agents/skills/freshell-orchestration/SKILL.md
License
None declared

Freshell tmux-style automation

Start state

Freshell injects FRESHELL_URL and FRESHELL_TOKEN into every spawned terminal. They are already set in your environment.

MCP tool (preferred)

If you have the freshell MCP tool available, use it directly -- it's faster and doesn't require Bash approval for each command.

Quick start:

  • freshell({action: "help"}) -- see all available commands
  • freshell({action: "list-tabs"}) -- see open tabs
  • freshell({action: "new-tab", params: {name: "Work", mode: "claude"}}) -- create a tab
  • freshell({action: "send-keys", params: {target: "p1", keys: "hello\n"}}) -- send input

The MCP tool accepts the same commands as the CLI below but with structured JSON input instead of positional arguments.

CLI fallback

If the MCP tool is not available (e.g., running outside Freshell or in a context without MCP support), use the CLI:

FSH="npx tsx server/cli/index.ts"
$FSH health

Use absolute paths for --cwd and --editor.

Mental model

  • Freshell CLI is an HTTP client over /api/*, not a local tmux socket client.
  • Tabs and pane trees live in layoutStore.
  • Terminal lifecycle + scrollback live in terminalRegistry.
  • Pane kinds: terminal, editor, browser, agent-chat (Claude/Codex), picker (transient).
  • Picker panes are ephemeral. A freshly-created tab without --mode/--browser/--editor starts as a picker pane while the user chooses what to launch. Once they select, the picker is replaced by the real pane with a new pane ID. Never target a picker pane for splits or other mutations — wait until it resolves to its final kind, or use --mode/--browser/--editor flags on new-tab/split-pane to skip the picker entirely.
  • Typical loop: new-tab/split-pane -> send-keys -> wait-for -> capture-pane/screenshot-*.

Choosing the right action and pane type

  • split-pane vs new-tab: When the user says "pane", "split", "alongside", "next to", or "side by side", use split-pane. Use new-tab only when the user explicitly says "tab", "window", or "new [thing]" with no spatial reference. When unsure, split-pane is the safer default.
  • Prefer specialized pane types: Do NOT open a terminal to run cat/vim/nano/curl/wget when a dedicated pane type fits.
    • "open/edit/show a file" -> split-pane --editor /path/to/file
    • "open/show a URL" or "view a webpage" -> split-pane --browser URL or open-browser URL
    • "run a command" or "use a CLI tool" -> split-pane --mode shell or new-tab --mode shell
  • Sending text: Use send-keys -l for natural-language prompts or multi-word text. Do NOT append "ENTER" as literal text — send the command with -l, then send ENTER as a separate call.
  • Default targeting (MCP only): When no target is specified, the MCP tool resolves to your own pane/tab (set by FRESHELL_TAB_ID/FRESHELL_PANE_ID), not the user's active viewport. split-pane without a target splits your own pane.
  • Default direction: split-pane defaults to vertical (top/bottom). Use -h for horizontal (left/right).

Command reference

Output behavior:

  • Most commands print JSON.
  • list-tabs and list-panes print TSV unless --json.
  • list-panes --titles appends a fifth TSV column with the pane title.
  • capture-pane and display print plain text.

Targets:

  • Tab target: tab ID or exact tab title.
  • Pane target: pane ID, pane index in active tab, or tabRef.paneIndex.
  • Omitted target on rename-tab means the active tab.
  • Omitted target on rename-pane means the active pane in the active tab.
  • Omitted target on split-pane (MCP) means your own pane, not the user's active viewport.
  • Omitted target falls back to active pane in active tab when command supports it (CLI only).
  • If a target or name contains spaces, quote it.
  • Use the flagged -t/-n form when you want to make the target and name explicit.

Tab commands:

  • new-tab [-n NAME] [--claude|--codex|--mode MODE] [--shell SHELL] [--cwd DIR] [--browser URL] [--editor FILE] [--resume SESSION_ID] [--prompt TEXT]
  • list-tabs [--json]
  • select-tab [TARGET] or select-tab -t TARGET
  • kill-tab [TARGET] or kill-tab -t TARGET
  • rename-tab NEW_NAME - rename the active tab
  • rename-tab TARGET NEW_NAME
  • rename-tab -t TARGET -n NEW_NAME
  • has-tab TARGET or has-tab -t TARGET
  • next-tab
  • prev-tab

Pane/layout commands:

  • split-pane [-t PANE_TARGET] [-h] [--mode MODE] [--shell SHELL] [--cwd DIR] [--browser URL] [--editor FILE]
  • list-panes [-t TAB_TARGET] [--json] [--titles]
  • select-pane PANE_TARGET or select-pane -t PANE_TARGET
  • rename-pane NEW_NAME - rename the active pane
  • rename-pane TARGET NEW_NAME
  • rename-pane -t TARGET -n NEW_NAME
  • kill-pane PANE_TARGET or kill-pane -t PANE_TARGET
  • resize-pane PANE_TARGET [--x X_PCT] [--y Y_PCT]
  • swap-pane PANE_TARGET --other OTHER_PANE_TARGET
  • respawn-pane PANE_TARGET [--mode MODE] [--shell SHELL] [--cwd DIR]
  • attach TERMINAL_ID [PANE_TARGET] or attach -t TERMINAL_ID -p PANE_TARGET

Terminal interaction:

  • send-keys [-t PANE_TARGET] [-l] KEYS...
  • capture-pane [-t PANE_TARGET] [-S START] [-J] [-e]
  • wait-for [-t PANE_TARGET] [-p PATTERN] [--stable SECONDS] [--exit] [--prompt] [-T TIMEOUT_SECONDS]
  • display -p FORMAT [-t PANE_TARGET] or display FORMAT [PANE_TARGET]
  • run [--capture|-c] [--detach|-d] [-T TIMEOUT_SECONDS] [-n NAME] [--cwd DIR] COMMAND...
  • summarize PANE_TARGET or summarize -t PANE_TARGET
  • list-terminals

Browser/navigation:

  • open-browser URL [-n NAME]
  • navigate URL [PANE_TARGET] or navigate --url URL -t PANE_TARGET

Screenshot commands:

  • screenshot --scope pane|tab|view --name NAME [--path DIR_OR_FILE] [--overwrite] [-t TARGET]
  • Aliases:
    • screenshot-pane -t PANE_TARGET --name NAME [--path ...] [--overwrite]
    • screenshot-tab -t TAB_TARGET --name NAME [--path ...] [--overwrite]
    • screenshot-view --name NAME [--path ...] [--overwrite]
  • --name is required.
  • --path is optional; default output root is OS temp dir.
  • Pane/tab scopes resolve target before capture; view scope captures current app viewport.

Session/service:

  • list-sessions
  • search-sessions QUERY or search-sessions -q QUERY
  • health
  • lan-info

tmux-style aliases:

  • new-window, new-session -> new-tab
  • list-windows -> list-tabs
  • select-window -> select-tab
  • kill-window -> kill-tab
  • rename-window -> rename-tab
  • next-window -> next-tab
  • previous-window, prev-window -> prev-tab
  • split-window -> split-pane
  • display-message -> display
  • screenshot-pane, screenshot-tab, screenshot-view -> screenshot

tmux differences

  • Transport/auth: tmux uses local socket; Freshell uses HTTP API + token auth.
  • Pane types: tmux terminal-only; Freshell supports terminal/editor/browser.
  • Target model: tmux session/window/pane grammar vs Freshell ID/title/index resolution.
  • Runtime model: tmux TTY-local; Freshell browser-first and remote-friendly.
  • Feature model: Freshell adds session indexing/search and AI summary workflows.

Playbook: open file in editor pane

New tab:

FILE="/absolute/path/to/file.ts"
$FSH new-tab -n "Edit $(basename "$FILE")" --editor "$FILE"

Split current tab:

FILE="/absolute/path/to/file.ts"
$FSH split-pane --editor "$FILE"

Playbook: create, split, and rename without UI interaction

FSH="npx tsx server/cli/index.ts"
CWD="/absolute/path/to/repo"
FILE="/absolute/path/to/repo/README.md"

WS="$($FSH new-tab -n 'Triager' --codex --cwd "$CWD")"
TAB_ID="$(printf '%s' "$WS" | jq -r '.data.tabId')"
P0="$(printf '%s' "$WS" | jq -r '.data.paneId')"
P1="$($FSH split-pane -t "$P0" --editor "$FILE" | jq -r '.data.paneId')"

$FSH rename-tab -t "$TAB_ID" -n "Issue 166 work"
$FSH rename-pane -t "$P0" -n "Codex"
$FSH select-pane -t "$P1"
$FSH rename-pane "Editor"

Playbook: parallel Claude panes

FSH="npx tsx server/cli/index.ts"
CWD="/absolute/path/to/repo"
PROMPT="Implement <task>. Run tests. Summarize tradeoffs."

SEED_JSON="$($FSH new-tab -n 'Claude x4 Eval' --claude --cwd "$CWD")"
P0="$(printf '%s' "$SEED_JSON" | jq -r '.data.paneId')"
J1="$($FSH split-pane -t "$P0" --mode claude --cwd "$CWD")"
P1="$(printf '%s' "$J1" | jq -r '.data.paneId')"
J2="$($FSH split-pane -t "$P0" -h --mode claude --cwd "$CWD")"
P2="$(printf '%s' "$J2" | jq -r '.data.paneId')"
J3="$($FSH split-pane -t "$P1" -h --mode claude --cwd "$CWD")"
P3="$(printf '%s' "$J3" | jq -r '.data.paneId')"

for p in "$P0" "$P1" "$P2" "$P3"; do
  $FSH send-keys -t "$p" -l "$PROMPT"
  $FSH send-keys -t "$p" ENTER
  $FSH wait-for -t "$p" --stable 8 -T 1800
  $FSH capture-pane -t "$p" -S -120 > "/tmp/${p}.txt"
done

Screenshot-specific guidance

  • Use a dedicated canary tab when validating screenshot behavior so live project panes are not contaminated.
  • Close temporary tabs/panes after verification unless user asked to keep them open.
  • Browser panes:
    • Same-origin iframe content is captured best-effort.
    • If iframe content is not capturable (for example cross-origin/security), screenshots intentionally render a placeholder message with source URL context instead of a silent blank region.
    • For assertions, allow either explicit page content or the explicit non-capturable placeholder text depending on origin/security context.

REST API patterns

  • Auth header: x-auth-token: <TOKEN> (not Bearer).
  • POST /api/tabs with { name, mode: "shell", shell: "wsl", cwd } creates a tab with a terminal, bypassing the picker.
  • POST /api/panes/:id/split with { direction: "horizontal"|"vertical", browser?, editor?, mode?, cwd? } — defaults to vertical; always 50/50.
  • POST /api/panes/:id/resize with { sizes: [left, right] } (percentages summing to 100) — call immediately after split to fix proportions.
  • Editor panes show "Loading..." until visited. When screenshotting multiple tabs, visit each tab once first to trigger editor loading, then loop back for screenshots.
  • DELETE /api/terminals/:id removes orphaned terminals. Freshell has a 50 PTY limit; orphans from scripted runs accumulate silently.

Gotchas

  • Use send-keys -l for natural-language prompts.
  • wait-for --stable is usually more reliable than prompt heuristics across providers.
  • If target resolution fails, run list-tabs and list-panes --json, then retry with explicit IDs.

Files of this skill

This skill has no supporting files.

Browse all supporting files