sprite

SKILL.md

Sprite Skill

Manage Sprites.dev remote VMs with checkpoint/restore — and critically, control an InnerClaude from OuterClaude.

Quick Start: Ask InnerClaude Something

Copy-paste this. Tested Jan 2026.

# Get token from Mac Keychain (or ask user for one)
TOKEN=$(security find-generic-password -a claude-sprite -s CLAUDE_CODE_OAUTH_TOKEN -w)

# Create tmux session and start Claude (literal paths, no escaping!)
sprite exec -tty bash -c 'tmux kill-session -t innerClaude 2>/dev/null; tmux new-session -d -s innerClaude -x 150 -y 50'
sprite exec -tty bash -c "tmux send-keys -t innerClaude 'source /.sprite/languages/node/nvm/nvm.sh && nvm use default && export CLAUDE_CODE_OAUTH_TOKEN=$TOKEN && export TERM=xterm-256color && claude' Enter"
sprite exec -tty bash -c 'tmux pipe-pane -t innerClaude "cat > /tmp/claude-output.txt"'
sleep 25

# Approve workspace trust dialog
sprite exec -tty bash -c 'tmux send-keys -t innerClaude Enter'
sleep 15

# Send your message (TWO ENTERS to submit!)
sprite exec -tty bash -c 'tmux send-keys -t innerClaude "Write a haiku about recursion" Enter Enter'
sleep 30

# Read response
sprite exec -tty bash -c 'cat /tmp/claude-output.txt | strings | tail -60'

# Cleanup
sprite exec -tty bash -c 'tmux kill-session -t innerClaude'

Key gotchas: Use literal NVM path (no $NVM_DIR), TWO Enters to submit messages, security runs on local Mac not sprite.


When to Use

  • OuterClaude/InnerClaude pattern — Testing workflows, install flows, or any scenario where Claude controls Claude
  • Remote development — Running code on persistent Ubuntu VMs
  • Checkpoint/restore workflows — Snapshotting and restoring VM state
  • Bootstrap new sprites — First-time setup with auth and tools

When NOT to Use

  • Local development — Just use terminal directly
  • Simple sprite commandssprite list, sprite console don't need this skill
  • Claude Code Web — Different product (ephemeral VMs, not Sprites.dev)
  • Non-sprite servers — Use server-checkup skill instead

OuterClaude Pattern (Primary Use Case)

This pattern enables you (OuterClaude) to operate an InnerClaude on a sprite as if you were the human user. Use for testing workflows, install patterns, or Claude-to-Claude interaction.

Mental Model

You (OuterClaude) are the user. InnerClaude is a CLI tool you're operating.

This framing is critical:

  • When you see InnerClaude's output → you're reading what a human would see
  • When you send input → you're typing what a human would type
  • Resist the instinct to "be" InnerClaude — you're operating it

Prerequisites: Authentication

Claude needs a valid OAuth token to start. Without it, Claude exits silently — no error message, no node process, just an empty output file.

Critical insight: Checkpoints don't persist environment variables. Even if you created a checkpoint right after authenticating, the token won't be there on restore. You must export it fresh every time.

Before starting the Working Loop, you need a token. Either:

  1. Retrieve from Keychain (local Mac): security find-generic-password -a claude-sprite -s CLAUDE_CODE_OAUTH_TOKEN -w
  2. Get one from the user: Ask them to provide CLAUDE_CODE_OAUTH_TOKEN
  3. Run setup-token flow on sprite: See Auth Setup section below

To store a token in Keychain (recommended, runs on your Mac not the sprite):

# Run locally on Mac (OuterClaude side) — NOT on the sprite!
security add-generic-password -a "claude-sprite" -s "CLAUDE_CODE_OAUTH_TOKEN" -w "<your-token>" -U

To verify auth will work before starting:

sprite exec -tty bash -c 'source /.sprite/languages/node/nvm/nvm.sh && nvm use default && export CLAUDE_CODE_OAUTH_TOKEN=<token> && claude --version'

If this returns a version number, auth is good. If it says "Invalid API key", the token is expired/invalid.

The Working Loop

ESCAPING WARNING: Variable expansion like $NVM_DIR gets mangled through multiple shell layers. Always use literal paths to avoid escaping hell.

# 1. Create tmux session on sprite (use -tty for proper TTY allocation!)
sprite exec -tty bash -c 'tmux new-session -d -s innerClaude -x 150 -y 50'

# 2. Set up environment + token + start Claude IN ONE COMMAND
# CRITICAL: Use literal path /.sprite/languages/node/nvm/nvm.sh (no $NVM_DIR variable!)
# Fetch token locally on Mac, then interpolate into the sprite command
TOKEN=$(security find-generic-password -a claude-sprite -s CLAUDE_CODE_OAUTH_TOKEN -w)
sprite exec -tty bash -c "tmux send-keys -t innerClaude 'source /.sprite/languages/node/nvm/nvm.sh && nvm use default && export CLAUDE_CODE_OAUTH_TOKEN=$TOKEN && export TERM=xterm-256color && claude' Enter"

# 3. Set up pipe-pane for output capture (capture-pane doesn't work!)
sprite exec -tty bash -c 'tmux pipe-pane -t innerClaude "cat > /tmp/claude-output.txt"'
sleep 25  # Claude needs 20-30 seconds to fully start

# 4. Verify Claude started (should be >1000 bytes if running)
sprite exec -tty bash -c 'wc -c /tmp/claude-output.txt'

# 5. Read captured output (strings filters escape codes into readable text)
sprite exec -tty bash -c 'cat /tmp/claude-output.txt | strings | tail -100'

Diagnostic: If output file is tiny (<500 bytes):

  • Check for node process: sprite exec -tty bash -c 'ps aux | grep node'
  • If no node process → auth failed. Token is invalid/expired.
  • Re-run setup-token flow or get fresh token from user.

Why pipe-pane, Not capture-pane

tmux capture-pane does NOT work for Claude's interactive UI. Claude uses the alternate screen buffer which capture-pane misses entirely.

Method Works? Why
capture-pane -p Misses alternate screen buffer
capture-pane -a Returns "no alternate screen"
pipe-pane "cat > file" Captures all output including alternate screen

Note: Raw output contains ANSI escape codes. Use strings to filter into readable text. The UI content IS captured, just wrapped in terminal control sequences.

Sending Input

TWO-ENTER PATTERN: Claude's input box requires TWO Enters:

  1. First Enter after your text → moves to new line in input box
  2. Second Enter → actually submits the message
# Send text to InnerClaude (types text + newline, but DOES NOT submit yet!)
sprite exec -tty bash -c 'tmux send-keys -t innerClaude "your message here" Enter'

# THEN submit with a second Enter
sprite exec -tty bash -c 'tmux send-keys -t innerClaude Enter'

# Or combine: type, newline, then submit
sprite exec -tty bash -c 'tmux send-keys -t innerClaude "your message here" Enter Enter'

# Navigate options (for dialogs/AskUserQuestion)
sprite exec -tty bash -c 'tmux send-keys -t innerClaude Down'   # Next option
sprite exec -tty bash -c 'tmux send-keys -t innerClaude Up'     # Previous option

# Approve dialog (single Enter works for dialogs - they're already focused)
sprite exec -tty bash -c 'tmux send-keys -t innerClaude Enter'

# Cancel dialog
sprite exec -tty bash -c 'tmux send-keys -t innerClaude Escape'

Recognizing Prompts

Prompt Type Visual Markers How to Respond
Workspace trust "Do you trust the files in this folder?" Enter (select Yes)
AskUserQuestion ☐ {header} + numbered options with Enter (current) or Down/Up then Enter
Write permission "Do you want to create {file}?" Enter (Yes)
Edit permission "Do you want to edit {file}?" Enter (Yes)
Bash permission "Do you want to proceed?" Enter (Yes)
Ready for input prompt at bottom Send your next message

Response Codes

Input Meaning
Enter Select highlighted option (default)
1, 2, 3 Explicit option selection
n No/deny
Escape Cancel dialog

Auth Setup

If InnerClaude shows "OAuth token expired" or "Please run /login":

# Run setup-token in a tmux session
sprite exec -tty bash -c 'tmux send-keys -t innerClaude "/exit" Enter'
sprite exec -tty bash -c 'tmux send-keys -t innerClaude "claude setup-token" Enter'

# Capture the auth URL from output, open it for the user
# After user authorizes, paste the code back:
sprite exec -tty bash -c 'tmux send-keys -t innerClaude "AUTH_CODE_HERE" Enter'

# Or use environment variable for subsequent sessions:
export CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...

Complete Example: Interactive Session

This is the full annotated version of Quick Start. Use when you need to understand what's happening or debug issues.

# 0. Get token from local Mac Keychain (this runs on YOUR machine, not the sprite!)
TOKEN=$(security find-generic-password -a claude-sprite -s CLAUDE_CODE_OAUTH_TOKEN -w)
# If that fails, ask user for token: TOKEN="sk-ant-oat01-..."

# 1. (Optional) Restore to virgin snapshot for clean test
# sprite restore v11

# 2. Create fresh tmux session
sprite exec -tty bash -c 'tmux kill-session -t innerClaude 2>/dev/null; tmux new-session -d -s innerClaude -x 150 -y 50'

# 3. Start Claude with NVM + token IN ONE COMMAND (use literal path, no $NVM_DIR!)
sprite exec -tty bash -c "tmux send-keys -t innerClaude 'source /.sprite/languages/node/nvm/nvm.sh && nvm use default && export CLAUDE_CODE_OAUTH_TOKEN=$TOKEN && export TERM=xterm-256color && claude' Enter"

# 4. Set up pipe-pane capture and wait for startup
sprite exec -tty bash -c 'tmux pipe-pane -t innerClaude "cat > /tmp/claude-output.txt"'
sleep 25

# 5. Verify Claude started (should be >1000 bytes)
sprite exec -tty bash -c 'wc -c /tmp/claude-output.txt'

# 6. Check for workspace trust dialog and approve
sprite exec -tty bash -c 'cat /tmp/claude-output.txt | strings | tail -50'
# If you see "Do you trust the files in this folder?" → approve it:
sprite exec -tty bash -c 'tmux send-keys -t innerClaude Enter'
sleep 15

# 7. Clear output and send your message (TWO ENTERS to submit!)
sprite exec -tty bash -c '> /tmp/claude-output.txt'
sprite exec -tty bash -c 'tmux send-keys -t innerClaude "Your prompt here" Enter Enter'
sleep 30

# 8. Read the response
sprite exec -tty bash -c 'cat /tmp/claude-output.txt | strings | tail -100'

# 9. For multi-turn: approve permission prompts as needed
sprite exec -tty bash -c 'tmux send-keys -t innerClaude Enter'  # Approve permission
# Repeat steps 7-9 for additional turns

# 10. Cleanup
sprite exec -tty bash -c 'tmux kill-session -t innerClaude'

Common Pitfalls

Pitfall Fix
$NVM_DIR escaping breaks Use literal path /.sprite/languages/node/nvm/nvm.sh — no variables!
Message doesn't submit Need TWO Enters: first adds newline, second submits. Use Enter Enter
Token not exported in tmux session Export CLAUDE_CODE_OAUTH_TOKEN INSIDE the tmux session, not just outer shell
Claude starts but output file stays tiny Auth failed silently. Check `ps aux
Using -p mode for interactive dialogs Use tmux + interactive claude instead
Using capture-pane instead of pipe-pane Claude's UI needs pipe-pane
Not sourcing NVM before running claude Add NVM setup to session init
Sending Enter before prompt renders Always sleep then capture before responding
OAuth token expired Long-lived tokens (setup-token) last ~1 year. Store in Keychain
Assuming checkpoint has valid auth Checkpoints don't persist env vars. Export token fresh every time
security command on sprite That's macOS — fetch token locally, then pass to sprite

Quick Reference

Command Purpose
sprite list List all sprites
sprite use <name> Set default sprite for directory
sprite exec <cmd> Run command on active sprite
sprite console Interactive shell (for humans)
sprite checkpoint create Snapshot current state
sprite checkpoint list List checkpoints
sprite restore <id> Restore to checkpoint
sprite proxy <port> Forward port locally

For detailed command reference: See references/commands.md


Setup & Bootstrap

Fresh sprites need authentication and tool setup before use.

Quick bootstrap:

sprite create my-sprite
sprite use my-sprite
sprite exec gh auth login          # GitHub auth (interactive)
sprite exec gh auth setup-git      # Enable credential helper
sprite checkpoint create --comment "Fresh with gh auth"

For full setup guide: See references/setup.md


Troubleshooting

Issue Fix
Output file tiny, no node process Auth failed silently. Token must be exported INSIDE tmux session
capture-pane shows nothing Use pipe-pane instead — see OuterClaude Pattern
Claude won't start in tmux Source NVM first — see Working Loop
"OAuth token expired" or "Invalid API key" Get fresh token. Run setup-token flow or ask user for token
"Permission denied (publickey)" Use HTTPS URLs, run gh auth login
claude -p returns empty Use sprite exec -tty to allocate a PTY

Quick Diagnostic Sequence

When InnerClaude isn't working:

# 1. Is there a node process?
sprite exec -tty bash -c 'ps aux | grep node | grep -v grep'
# No output = Claude never started (usually auth)

# 2. What's in the output file?
sprite exec -tty bash -c 'wc -c /tmp/claude-output.txt'
# <500 bytes = Claude exited immediately

# 3. Can Claude start at all with this token? (use literal path!)
# Fetch token locally (Mac), then pass to sprite
TOKEN=$(security find-generic-password -a claude-sprite -s CLAUDE_CODE_OAUTH_TOKEN -w)
sprite exec -tty bash -c "source /.sprite/languages/node/nvm/nvm.sh && nvm use default && export CLAUDE_CODE_OAUTH_TOKEN=$TOKEN && claude -p 'hello' 2>&1"
# "Invalid API key" = token expired/invalid

For full troubleshooting guide: See references/troubleshooting.md

TTY Allocation: Use -tty Flag (Jan 2026)

The fix: Always use sprite exec -tty when running commands that need TTY allocation (tmux, Claude, interactive tools).

# CORRECT: -tty allocates a PTY
sprite exec -tty bash -c 'tmux new-session -d -s innerClaude ...'

# WRONG: no TTY, pipe-pane won't capture output
sprite exec bash -c 'tmux new-session -d -s innerClaude ...'

What -tty does: Allocates /dev/pts/X so pipe-pane can capture both input AND output. Without it, you see keystrokes going in but Claude's responses are missing.

Diagnostic (if output capture fails):

sprite exec -tty bash -c 'tty'  # Should show /dev/pts/0 or similar

Anti-Patterns

Pattern Problem Fix
Use $NVM_DIR variable in send-keys Escaping hell across shell layers Use literal /.sprite/languages/node/nvm/nvm.sh
Send message with single Enter First Enter is newline, second submits Use Enter Enter (type + submit)
Use capture-pane for Claude UI Alternate screen buffer not captured Use pipe-pane
Run claude without NVM setup Node won't be in PATH Source NVM first in tmux
Use -p mode for interactive testing Can't handle dialogs Use tmux + interactive claude
Assume OAuth persists across restores Checkpoints may have stale tokens Export CLAUDE_CODE_OAUTH_TOKEN
Run security on the sprite security is macOS, sprite is Linux Fetch token locally, pass to sprite
Use SSH URLs for git gh credential helper needs HTTPS Use HTTPS URLs
Skip gh auth setup-git uv/pip need credential helper Always run after gh auth login

Integration with Other Skills

Complements:

  • server-checkup — For non-sprite Linux servers
  • claude-go (google-workspace skill) — Shares interaction patterns (tmux send-keys)

Virgin Snapshot Pattern: Maintain a checkpoint with Anthropic + GitHub auth but no customizations. Restore before each test:

sprite restore v11  # Your virgin checkpoint ID
Weekly Installs
10
First Seen
Feb 21, 2026
Installed on
opencode10
gemini-cli10
claude-code10
github-copilot10
codex10
kimi-cli10