feat(cli): spawn export — capture a claude session into a github repo#3377
Merged
la14-1 merged 6 commits intoOpenRouterTeam:mainfrom May 2, 2026
Merged
feat(cli): spawn export — capture a claude session into a github repo#3377la14-1 merged 6 commits intoOpenRouterTeam:mainfrom
la14-1 merged 6 commits intoOpenRouterTeam:mainfrom
Conversation
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
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.
…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.
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.
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.
Review follow-ups to OpenRouterTeam#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.
Member
|
Pushed review follow-ups to
Filed #3381 as a non-blocking follow-up to broaden the secret-scan regex (OpenRouter non-v1 prefixes, Slack, Stripe, Discord, GCP JSON blocks). Biome clean, |
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
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`. Today `spawn claude hetzner --repo user/template` consumes a repo; after this lands, `spawn export` produces one.
```
$ spawn export
✓ Exported to https://github.com/alice/my-vm
Re-spawn with:
spawn claude hetzner --repo alice/my-vm
```
What gets exported
Scope decisions
Files
Test plan
Followups
🤖 Generated with Claude Code