cli: add interactive cloud selection for spawn <agent> (#1192)

Fixes #1180

When running `spawn <agent>` (e.g., `spawn claude`), now shows an interactive
cloud picker instead of requiring the full command or showing agent info.

- Add cmdAgentInteractive() function for agent-first cloud selection
- Route `spawn <agent>` to interactive picker when in TTY mode
- Fall back to agent info display in non-interactive contexts
- Update help text to reflect new interactive behavior
- Version bump 0.2.83 → 0.2.84

Agent: ux-engineer

Co-authored-by: spawn-refactor-bot <refactor@openrouter.ai>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
A 2026-02-15 17:36:36 -08:00 committed by GitHub
parent d8ac64863d
commit 4e1796230e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 41 additions and 2 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openrouter/spawn",
"version": "0.2.86",
"version": "0.2.87",
"type": "module",
"bin": {
"spawn": "cli.js"

View file

@ -431,6 +431,37 @@ export async function cmdInteractive(): Promise<void> {
await execScript(cloudChoice, agentChoice, undefined, getAuthHint(manifest, cloudChoice), manifest.clouds[cloudChoice].url);
}
/** Interactive cloud selection when agent is already known (e.g. `spawn claude`) */
export async function cmdAgentInteractive(agent: string, prompt?: string, dryRun?: boolean): Promise<void> {
p.intro(pc.inverse(` spawn v${VERSION} `));
const manifest = await loadManifestWithSpinner();
const resolvedAgent = resolveAgentKey(manifest, agent);
if (!resolvedAgent) {
const agentMatch = findClosestKeyByNameOrKey(agent, agentKeys(manifest), (k) => manifest.agents[k].name);
p.log.error(`Unknown agent: ${pc.bold(agent)}`);
if (agentMatch) {
p.log.info(`Did you mean ${pc.cyan(agentMatch)} (${manifest.agents[agentMatch].name})?`);
}
p.log.info(`Run ${pc.cyan("spawn agents")} to see available agents.`);
process.exit(1);
}
const { clouds, hintOverrides } = getAndValidateCloudChoices(manifest, resolvedAgent);
const cloudChoice = await selectCloud(manifest, clouds, hintOverrides);
await preflightCredentialCheck(manifest, cloudChoice);
const agentName = manifest.agents[resolvedAgent].name;
const cloudName = manifest.clouds[cloudChoice].name;
p.log.step(`Launching ${pc.bold(agentName)} on ${pc.bold(cloudName)}`);
p.log.info(`Next time, run directly: ${pc.cyan(`spawn ${resolvedAgent} ${cloudChoice}`)}`);
p.outro("Handing off to spawn script...");
await execScript(cloudChoice, resolvedAgent, prompt, getAuthHint(manifest, cloudChoice), manifest.clouds[cloudChoice].url, dryRun);
}
// ── Run ────────────────────────────────────────────────────────────────────────
/** Resolve display names / casing and log if resolved to a different key */
@ -2058,7 +2089,7 @@ function getHelpUsageSection(): string {
Execute agent with prompt (non-interactive)
spawn <agent> <cloud> --prompt-file <file> (or -f)
Execute agent with prompt from file
spawn <agent> Show available clouds for agent
spawn <agent> Interactive cloud picker for agent
spawn <cloud> Show available agents for cloud
spawn list Browse and rerun previous spawns (aliases: ls, history)
spawn list <filter> Filter history by agent or cloud name

View file

@ -1,6 +1,7 @@
#!/usr/bin/env bun
import {
cmdInteractive,
cmdAgentInteractive,
cmdRun,
cmdList,
cmdListClear,
@ -163,6 +164,13 @@ async function handleDefaultCommand(agent: string, cloud: string | undefined, pr
await suggestCloudsForPrompt(agent);
process.exit(1);
}
// Interactive cloud selection when agent is provided without cloud
if (isInteractiveTTY()) {
await cmdAgentInteractive(agent, prompt, dryRun);
return;
}
await showInfoOrError(agent);
}