sprite
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 commands —
sprite list,sprite consoledon'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:
- Retrieve from Keychain (local Mac):
security find-generic-password -a claude-sprite -s CLAUDE_CODE_OAUTH_TOKEN -w - Get one from the user: Ask them to provide
CLAUDE_CODE_OAUTH_TOKEN - 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:
- First Enter after your text → moves to new line in input box
- 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