11 KiB
Spawn CLI
The spawn CLI is a command-line tool for launching AI coding agents on cloud providers, pre-configured with OpenRouter.
Overview
The spawn CLI provides a unified interface to:
- Launch any supported AI agent (Claude Code, Aider, etc.) on any supported cloud provider
- Interactively browse available agents and clouds
- View the agent × cloud compatibility matrix
- Self-update to the latest version
Architecture
Three-Tier Installation Strategy
The CLI uses a progressive fallback installation strategy to maximize compatibility:
┌─────────────────────────────────────────────────────────┐
│ Method 1: Bun (Preferred) │
│ - Fastest execution (native TypeScript runtime) │
│ - Full TypeScript support with minimal overhead │
│ - Falls back to compiled binary if global install fails │
└─────────────────────────────────────────────────────────┘
↓ (if bun not found)
┌─────────────────────────────────────────────────────────┐
│ Method 2: npm │
│ - Standard Node.js package manager │
│ - Transpiles TypeScript to JavaScript at install time │
│ - Requires Node.js runtime │
└─────────────────────────────────────────────────────────┘
↓ (if npm not found)
┌─────────────────────────────────────────────────────────┐
│ Method 3: Bash Fallback │
│ - Pure bash implementation (spawn.sh) │
│ - Zero runtime dependencies except curl + jq/python3 │
│ - Functional subset of TypeScript CLI │
└─────────────────────────────────────────────────────────┘
Why this pattern?
- Universal compatibility: Works on any system with bash and curl
- Optimal performance: Uses the fastest available runtime (bun > node > bash)
- Zero friction: No prerequisite installation required for basic usage
- Graceful degradation: Each tier provides full functionality with varying performance characteristics
Directory Structure
cli/
├── src/
│ ├── index.ts # Entry point (routes commands to handlers)
│ ├── commands.ts # All command implementations
│ ├── manifest.ts # Manifest fetching and caching logic
│ └── version.ts # Version constant
├── install.sh # Multi-tier installer script
├── spawn.sh # Bash fallback CLI (full implementation)
├── package.json # npm package metadata
└── tsconfig.json # TypeScript configuration
TypeScript Implementation
The TypeScript CLI (src/*.ts) provides:
- Interactive mode: Terminal UI with prompts for selecting agents and clouds
- Manifest caching: Local cache with TTL to minimize network requests
- Progress indicators: Spinners and colored output for better UX
- Error handling: Structured error messages and exit codes
Key dependencies:
@clack/prompts— Interactive terminal promptspicocolors— Terminal color support
Bash Fallback Implementation
The bash CLI (spawn.sh) is a standalone script that:
- Implements the same commands as the TypeScript version
- Uses
jqorpython3for JSON parsing (auto-detects which is available) - Provides a numbered menu picker for interactive mode
- Maintains local manifest cache with TTL
- Supports all core commands:
list,agents,clouds,run,improve,update
Why maintain both implementations?
- Portability: Bash version works on minimal systems (CI containers, embedded Linux, etc.)
- Bootstrap: Used by installer when bun/npm aren't available
- Reference: Demonstrates that the protocol is runtime-agnostic
Installation
Quick Install
curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/cli/install.sh | bash
The installer will:
- Check for
bun→ install viabun install -gif found - Check for
npm→ install vianpm install -gif found - Fallback → download
spawn.shto$HOME/.local/binif neither found
Environment Variables
SPAWN_INSTALL_DIR— Override install directory (default:$HOME/.local/binfor fallback method)
Manual Installation (Development)
cd cli
bun install
bun link
Or build a standalone binary:
bun run compile # Creates ./spawn executable
Usage
Interactive Mode
spawn
Launches an interactive picker to select an agent and cloud provider.
Direct Launch
spawn <agent> <cloud>
Examples:
spawn claude sprite # Launch Claude Code on Sprite
spawn aider hetzner # Launch Aider on Hetzner Cloud
Agent Information
spawn <agent>
Show which cloud providers support the specified agent.
Example:
spawn claude
# Output:
# Claude Code — AI coding agent from Anthropic
#
# Available clouds:
# Sprite spawn claude sprite
# Hetzner Cloud spawn claude hetzner
List All Combinations
spawn list
Display the full agent × cloud compatibility matrix.
List Agents
spawn agents
Show all available agents with descriptions.
List Cloud Providers
spawn clouds
Show all available cloud providers with descriptions.
Improve Command
spawn improve [--loop]
Clone (or update) the spawn repository and run the improve.sh script, which uses Claude to autonomously add missing matrix entries or new agents/clouds.
Update CLI
spawn update
- TypeScript version: Displays update instructions (re-run installer)
- Bash version: Self-updates by downloading the latest
spawn.sh
Version
spawn version
Display the current CLI version.
Development
Prerequisites
- Bun 1.0+ (or Node.js 18+ with npm)
- TypeScript 5.0+
Running Locally
bun run dev # Run TypeScript CLI directly
bun run build # Build to cli.js
bun run compile # Compile to standalone binary
Testing
# Test TypeScript version
bun run dev list
bun run dev agents
bun run dev claude sprite
# Test bash version
bash spawn.sh list
bash spawn.sh agents
bash spawn.sh claude sprite
Code Organization
src/index.ts
- Command-line argument parsing
- Routes to appropriate command handler
- Minimal logic (just dispatching)
src/commands.ts
- All command implementations
- Interactive picker UI
- Script execution logic
- Help text
src/manifest.ts
- Manifest fetching from GitHub
- Local caching with TTL
- Offline fallback to stale cache
- Typed manifest structure
src/version.ts
- Single source of truth for version number
- Imported by both TypeScript and bash implementations
Adding a New Command
-
Add command handler in
src/commands.ts:export async function cmdMyCommand() { const manifest = await loadManifest(); // ... implementation } -
Add routing in
src/index.ts:case "mycommand": await cmdMyCommand(); break; -
Update help text in
src/commands.ts→cmdHelp() -
(Optional) Add equivalent implementation to
spawn.shfor bash fallback
Design Rationale
Why TypeScript?
- Type safety: Manifest structure is type-checked at compile time
- Modern async/await: Clean, readable asynchronous code
- Rich ecosystem: Access to high-quality CLI libraries (
@clack/prompts, etc.) - Single codebase: Same code runs on bun, node, or as a compiled binary
Why Bash Fallback?
- Universality: Bash is available on virtually all Unix-like systems
- Zero dependencies: Only requires
curlandjq/python3(one of which is usually installed) - CI/CD friendly: Works in minimal Docker containers, GitHub Actions, etc.
- Educational: Demonstrates the protocol can be implemented in any language
Why Bun → npm → Bash Tiering?
- Performance gradient: Bun is fastest, npm is widely available, bash always works
- User experience: Bun users get instant execution, others get working tool
- Distribution: Can be installed via package manager or curl | bash
- Maintenance: Single TypeScript codebase serves bun and npm, bash is separate but synchronized
Manifest Caching
Both implementations cache the manifest locally to reduce network requests:
- Cache location:
$XDG_CACHE_HOME/spawn/manifest.json(or~/.cache/spawn/manifest.json) - TTL: 1 hour (3600 seconds)
- Offline fallback: If fetch fails, uses stale cache if available
- Invalidation:
spawn updateclears the cache
Script Execution Flow
When you run spawn <agent> <cloud>:
- Load manifest: Fetch from GitHub or use cached version
- Validate combination: Check that
matrix["<cloud>/<agent>"]is"implemented" - Download script: Fetch
https://openrouter.ai/lab/spawn/<cloud>/<agent>.sh- Fallback to GitHub raw URL if OpenRouter CDN fails
- Execute: Pipe script to
bash -cwith inherited stdio - Interactive handoff: User interacts directly with the spawned agent
Contributing
Before Submitting Changes
-
Test both TypeScript and bash versions:
bun run dev --help bash spawn.sh --help -
Ensure version numbers are synchronized:
src/version.ts→VERSIONspawn.sh→SPAWN_VERSIONpackage.json→version
-
Update this README if you add new commands or change behavior
-
Run the installer locally to verify the three-tier strategy works:
# Test with bun bash install.sh # Test without bun (rename temporarily) mv $(which bun) $(which bun).bak bash install.sh mv $(which bun).bak $(which bun)
Release Checklist
- Bump version in all three locations (see above)
- Update CHANGELOG (if exists)
- Test installer on clean system
- Tag release:
git tag -a cli-vX.Y.Z -m "Release vX.Y.Z" - Push tag:
git push --tags
License
See repository root for license information.