* feat(cli): spawn export — capture a claude session into a github repo
Adds `spawn export [name|id]` as a top-level subcommand. Captures a
running claude spawn into a redistributable github repo whose README
contains the canonical re-spawn command — the symmetric inverse of
`--repo`.
What gets exported:
- `~/project/` working tree (with aggressive .gitignore)
- `~/.claude/` sanitized agent system dir: skills, commands,
hooks, CLAUDE.md, AGENTS.md, settings.json
(with token-shaped fields stripped)
- `spawn.md` generated re-spawn metadata
- `README.md` generated; renders a re-auth checklist on github
The export runs over the existing SshRunner. v1 is claude-only; non-claude
agents return a clear "not yet supported" error. Bumps CLI 1.0.27 -> 1.1.0
because this is a real new surface, not a fix.
Followups (not in v1):
- claude introspects its own session (MCP servers, OAuth providers) and
writes them into spawn.md's setup steps
- local cloud target uses a direct branch (currently routed through SSH)
- in-session `:export` slash command
* fix(cli): use patch bump (1.0.28) to match team versioning cadence
The update-check tests hardcode 1.0.x patch-bump scenarios as test fixtures
(e.g. mocking "latest" as 1.0.99 to verify patch auto-install policy). A
1.1.0 bump made those mocked versions look like downgrades and failed CI.
Realign with the team's recent patch-bump cadence — every recent feature
PR has shipped under 1.0.x.
* feat(export): list picker, claude picks the slug, pre-commit secrets scan
Three changes per review feedback:
1. **Picker.** When the user has multiple exportable claude spawns and
no positional target arg, show a clack `select` listing them. Auto-pick
on a single match. Filter out non-claude / no-connection / deleted /
sprite-console records up front.
2. **Claude decides the repo name.** No more slug prompt. The on-VM script
runs `claude -p` with a name-suggestion prompt asking for a kebab-case
project name (max 40 chars, [a-z0-9-]). Falls back to basename(~/project)
then a timestamp slug if claude is unavailable or returns garbage.
`gh api user --jq .login` provides the username; missing gh auth aborts
with a structured JSON failure the CLI surfaces verbatim.
3. **Pre-commit secrets scan.** After `git add`, scan all staged files for
known API-key shapes — Anthropic (sk-ant-api...), OpenRouter (sk-or-v1-),
OpenAI (sk-proj-), GitHub PAT/OAuth/server (gh[ops]_), AWS (AKIA...),
Hetzner (hcloud_), DigitalOcean (dop_v1_), and PEM private keys. Any
match aborts the export with `{"ok":false,"error":"..."}` to the result
file. The settings.json scrubber now recurses; previously it only
stripped top-level + env keys.
Also expands the .gitignore deny-list with .spawnrc, .bash_history,
.aws/, .config/spawn/, .config/gcloud/, .gnupg/, *.token, *.credentials.
Bumps CLI 1.0.28 -> 1.0.29.
* feat(export): default to public visibility
Exports are share-friendly artifacts — public by default makes the
'spawn link' (the printed re-spawn command) usable by anyone the user
hands it to. Override path stays via options.visibility for callers
that need private.
Bumps CLI 1.0.29 -> 1.0.30.
* feat(export): bake --steps into the spawn link for zero-prompt respawn
The printed spawn link is now:
spawn claude <cloud> --repo <slug> --steps <list>
Source of the steps list:
1. Parse `--steps <value>` (or `--steps=<value>`) out of the original
record.connection.launch_cmd.
2. Fall back to 'github,auto-update,security-scan' when the launch_cmd
doesn't carry it (older spawns, or interactive launches that didn't
pass the flag).
The respawn consumer reads SPAWN_ENABLED_STEPS from --steps and skips
the interactive setup picker entirely, so handing someone the spawn
link is a true zero-choice replay.
Adds parseStepsFromLaunchCmd + resolveSteps helpers, both exported
from the barrel for testability. README template grows a __STEPS__
placeholder; bash sed adds it to the substitution pass.
Bumps CLI 1.0.30 -> 1.0.31.
* fix(export): safe defaults and tighter flag parsing
Review follow-ups to #3377:
- Visibility: default private; interactive "make public?" confirm when
the caller doesn't force one. Prior default-public + one-shot `gh repo
create --push` was a public-leak footgun when the secret regex missed.
- `parseStepsFromLaunchCmd`: anchor both regexes to start-or-whitespace
so `--no-steps=foo` no longer over-matches and returns `foo`.
- `--exclude=.git` on the claude/{skills,commands,hooks} rsync so a
nested git checkout inside a skill doesn't leak its history.
- Replace `record!` / `conn!` non-null assertions with explicit
narrowing — matches the project's type-safety rule (no `as`, no `!`).
- Tests: lock in private default, the .git exclude, and the negative
`--no-steps=` regex case (14 new expects, 32/32 pass).
- Bump CLI to 1.0.32.
---------
Co-authored-by: Claude <claude@anthropic.com>
|
||
|---|---|---|
| .. | ||
| src | ||
| .gitignore | ||
| build-clouds.ts | ||
| bun.lock | ||
| bunfig.toml | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
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, Codex, 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
Installation Strategy
The installer uses bun to build the TypeScript CLI into a standalone JavaScript file. If bun is not already installed, the installer auto-installs it first (~5 seconds).
Why bun?
- Fast: Native TypeScript runtime, instant builds
- Universal: Auto-installed if missing, works on any system with bash and curl
- Zero friction: No prerequisite installation required
- Single implementation: One codebase, always feature-complete
Directory Structure
cli/
├── src/
│ ├── index.ts # Entry point (routes commands to handlers)
│ ├── commands/ # Per-command modules (interactive, list, run, etc.)
│ │ └── index.ts # Barrel re-export
│ ├── manifest.ts # Manifest fetching and caching logic
│ ├── update-check.ts # Auto-update check (once per day)
│ └── __tests__/ # Test suite (Bun test runner)
├── ../sh/cli/install.sh # Installer (auto-installs bun if needed, lives in sh/cli/)
├── package.json # Package metadata and dependencies
└── 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
- Auto-update check: Non-intrusive daily version check with notifications
- 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
Installation
Quick Install
curl -fsSL https://raw.githubusercontent.com/OpenRouterTeam/spawn/main/sh/cli/install.sh | bash
The installer will:
- Install
bunif not already present - Clone the CLI source
- Build and install the
spawnbinary to~/.local/bin
Environment Variables
SPAWN_INSTALL_DIR— Override install directory (default:$HOME/.local/bin)
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 codex hetzner # Launch Codex CLI 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.
Update CLI
spawn update
Displays update instructions (re-run installer).
Auto-update check: The CLI automatically checks for updates once per day and displays a notification if a newer version is available. To disable this, set SPAWN_NO_UPDATE_CHECK=1.
Version
spawn version
Display the current CLI version.
Development
Prerequisites
- Bun 1.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
bun run dev list
bun run dev agents
bun run dev claude sprite
Code Organization
src/index.ts
- Command-line argument parsing
- Routes to appropriate command handler
- Minimal logic (just dispatching)
src/commands/
- Per-command modules:
interactive.ts,list.ts,run.ts,delete.ts,update.ts, etc. shared.ts— helpers, entity resolution, fuzzy matching, credential hintsindex.ts— barrel re-export for backward compatibility with existing imports
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
Adding a New Command
-
Add a new file
src/commands/mycommand.ts:export async function cmdMyCommand() { const manifest = await loadManifest(); // ... implementation } -
Re-export from
src/commands/index.ts:export { cmdMyCommand } from "./mycommand.js"; -
Add routing in
src/index.ts:case "mycommand": await cmdMyCommand(); break; -
Update help text in
src/commands/help.ts→cmdHelp()
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 Auto-install Bun?
- Single implementation: No need to maintain a separate bash CLI
- Feature parity: Every user gets the full TypeScript CLI with all features
- Fast install: Bun installs in ~5 seconds via
curl -fsSL https://bun.sh/install | bash - Simple maintenance: One codebase, one source of truth
Manifest Caching
The CLI caches 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/labs/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 the CLI:
bun run dev --help -
Ensure version numbers are synchronized:
src/version.ts→VERSIONpackage.json→version
-
Update this README if you add new commands or change behavior
-
Run the installer locally to verify it works:
bash install.sh
Release Checklist
- Bump version in both 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.