fly ssh console with stdin as /dev/null ("ignore") can cause the
connection to hang — flyctl doesn't get a clean EOF signal to know
when to close the transport. Switch to "pipe" and immediately call
stdin.end() so flyctl receives a proper EOF.
Applied to runServer, runServerCapture, and uploadFile.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
git on Ubuntu 24.04 pulls in python3 via recommended packages. Use
--no-install-recommends to install only direct dependencies. Also
added ca-certificates explicitly since it's needed for HTTPS but
won't be auto-pulled without recommends.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fixes:
1. runServer() was inheriting stdin, so commands like `claude install`
that try to read input would hang the terminal indefinitely. Changed
stdin to "ignore" (/dev/null) for non-interactive remote commands.
2. preLaunch failures (e.g. OpenClaw gateway) were silently swallowed,
dropping users into a broken TUI with no gateway. Now preLaunch
errors propagate — users get a clear error instead of a mystery hang.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tzdata (pulled in as a Node.js dependency) tries to run
dpkg-reconfigure interactively, which fails on headless Fly
machines. Set DEBIAN_FRONTEND=noninteractive so apt silently
accepts defaults.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Fly Machines API enforces a [1s, 1m0s] range on
WaitMachineRequest.Timeout. We were passing 90s, which caused an
invalid_argument error and prevented machines from starting.
Lower the default to 60s (the API maximum) and retry up to 3 times
so slow-starting machines still have a full 3-minute window.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
testFlyToken() fallback to /v1/user accepted 404 plain text responses
because hasError() only checks for JSON "error"/"errors" keys. Adding
resp.ok check ensures non-2xx responses are correctly rejected.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
promptSpawnName() used `placeholder` (visual hint only) without `defaultValue`,
so pressing Enter returned an empty string instead of applying the placeholder.
Now generates a unique default like `spawn-a3f2` with a random suffix to avoid
Fly.io global name collisions.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
validateConnectionIP rejected "localhost" (written by local cloud) and
hostnames like "ssh.app.daytona.io" (written by Daytona), causing
mergeLastConnection to silently discard connection data. This broke
spawn list and spawn delete for these providers.
- Add "localhost" to CONNECTION_SENTINELS
- Add HOSTNAME_PATTERN for valid multi-label DNS hostnames
- Update tests: localhost now valid, add hostname acceptance/rejection tests
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Two bugs in reExecWithArgs():
1. args.length === 0 early exit:
Running bare `spawn` (interactive picker) after an auto-update would
print "Run your spawn command again" and exit, requiring the user to
manually re-invoke. Now always re-exec so the new flow triggers
immediately.
2. process.argv[1] stale binary path:
If the installer places the updated binary in a different directory than
the currently running binary (e.g. old: ~/.local/bin, new: /usr/local/bin),
re-exec would run the old stale binary. Fix: add findUpdatedBinary() which
resolves via `which spawn` (PATH lookup) first, falling back to
process.argv[1] only if which fails.
Bump CLI version 0.5.17 → 0.5.18.
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
* fix: replace hardcoded ~/.spawn/history.json path in security.ts error messages
Error messages in security validation functions (validateConnectionIP,
validateUsername, validateServerIdentifier, validateMetadataValue) hardcoded
~/.spawn/history.json as the fix path. This is wrong when SPAWN_HOME is set,
directing users to a nonexistent file. Replace all 9 occurrences with
'spawn list --clear' which works regardless of SPAWN_HOME and is simpler
than manually editing JSON.
Agent: ux-engineer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: bump cli version to 0.5.17
Required by CLAUDE.md: any change to cli/ needs a version bump.
PR #1520 changes security.ts error messages (cli/ change).
Agent: pr-maintainer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The test's runCli() helper used \${process.env.HOME}/.bun/bin/bun as
the subprocess command. The test preload sandboxes HOME to a temp dir,
so this path resolves to a nonexistent file, causing ENOENT and 49/56
test failures.
Fix: use bare "bun" (resolved via PATH), matching the pattern in
cli-version-and-dispatch.test.ts and cmdrun-resolution.test.ts.
All 56 tests in cli-entry-edge-cases.test.ts now pass.
Agent: team-lead
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Two error messages told users to run 'spawn clear-history' when
encountering corrupted history files, but that command does not exist.
The actual command is 'spawn list --clear'. Users got a confusing
"Unknown agent or cloud: clear-history" error when following the advice.
Agent: ux-engineer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Enrich each agent entry with curated metadata fields: creator, repo,
license, created/added dates, GitHub stars, language, runtime, category,
tagline, and tags. This helps users compare and choose agents.
- Extend AgentDef interface with 12 optional metadata fields
- Add metadata to all 6 agents in manifest.json
- Add type validation tests for new fields
- Bump CLI version 0.5.12 → 0.5.13
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Why: `set -eo pipefail` + `output=$(shellcheck ...)` on line 659 of
test/run.sh causes immediate exit when shellcheck finds any warning,
preventing the entire shell test suite from running. 53 CLI tests also
fail due to stale assertions after agents/clouds were removed in recent
PRs.
Fixes:
- test/run.sh:659 — add `|| true` to shellcheck command substitution so
shell test suite runs to completion even when scripts have warnings
- manifest-real-data.test.ts — lower agent count min from 10→5,
matrix count min from 80→40 (now 6 agents, 48 matrix entries)
- agent-env-injection-contract.test.ts — lower script count min
from 70→40 (now 47 implemented scripts)
- script-conventions.test.ts — same script count fix (70→40)
- cloud-lib-source-chain.test.ts — lower cloud lib min from 9→8
(OVH removed, now 8 clouds)
- commands-credential-display-internals.test.ts — add missing
@clack/prompts mock (tests call p.log.error but never mocked it)
- commands-exported-helpers-edges.test.ts — fix environment-dependent
assertion: only check credential-based hintOverrides, not
CLI-installed ones (sprite CLI is installed in CI/dev)
- agent-config-setup.test.ts — fix stale model ID assertion
("openrouter/anthropic/..." → "anthropic/...") and stale mkdir
command ("rm -rf && mkdir" → "mkdir -p")
- agent-info-quickstart.test.ts — remove sprite from singleAuthManifest
fixture (sprite CLI installed causes sprite to be prioritized over
hetzner, breaking 4 tests); update count assertions for single cloud
Agent: team-lead
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Delete 32 agent scripts ({cloud}/{cline,gptme,plandex,continue}.sh across
8 clouds), remove the 4 agents from manifest.json with all their matrix
entries, update README matrix rows, remove stale mock agent binaries and
plandex.ai URL patterns from test harness, update CLI help examples to use
remaining agents, and bump version 0.5.7 → 0.5.8.
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Remove OVH as a cloud provider: delete ovh/ directory (lib + 11 agent
scripts), remove from manifest.json clouds and all ovh/* matrix entries,
update README matrix table, remove OVH destroy case in CLI commands,
and clean up all test harness references (mock.sh, mock-curl-script.sh,
record.sh, e2e.sh, cloud-lib-api-surface.test.ts, test-infra-sync.test.ts)
- Make featured_cloud an array (string[]) so agents can recommend multiple
clouds; update manifest.ts type, all 10 manifest.json values, and the
prioritizeCloudsByCredentials() comparison in commands.ts
- Sandbox OAuth in subprocess tests: add OPENROUTER_API_KEY=sk-or-test-fake
to the default env in cli-entry-edge-cases.test.ts and
cmdrun-resolution.test.ts so get_or_prompt_api_key() never triggers the
real OAuth browser flow during test runs
- Fix upload-file-security.test.ts SSH cloud count (5→4) after OVH removal
- Bump CLI version 0.5.6 → 0.5.7
Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Export isInteractiveTTY from commands.ts and import in index.ts,
removing the duplicate definition that was missing !! boolean coercion
- Remove unused inject_env_vars_ovh function from ovh/lib/common.sh
(all OVH scripts use spawn_agent which calls _spawn_inject_env_vars)
- Bump CLI version to 0.5.6
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The buildDeleteScript function in commands.ts interpolated connection.metadata.zone
and connection.metadata.project directly into a bash script string without validation.
A tampered history file could inject arbitrary shell commands via these fields
(e.g., zone='"; rm -rf /; echo "' would escape the double quotes).
Add validateMetadataValue() to security.ts and call it before interpolating
GCP zone and project values into the delete script.
Agent: security-auditor
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Add --default to KNOWN_FLAGS so it is recognized even if the `spawn pick`
early-return path is bypassed (e.g. due to Bun kqueue/TTY errors on certain
platforms). Also wrap cmdPick in a try/catch so TTY errors produce a clean
error message instead of an unhandled rejection.
Sync test copies of KNOWN_FLAGS that had drifted: unknown-flags.test.ts was
missing --debug, --headless, --output, --clear, -a, -c, --agent, --cloud;
index-dispatch-routing.test.ts had the same gaps. Fix an incorrect test that
expected --output to be flagged as unknown (it has been a known flag since
--headless/--output were added).
Fixes#1447
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- New cli/src/picker.ts: modular picker module with pickToTTY() that renders
an arrow-key UI directly to /dev/tty, works even when stdout is captured by
bash $() subshell substitution and stdin is piped with options.
- New spawn pick subcommand: reads options from stdin as tab-separated lines
(value\tLabel\tHint), shows clack-style picker via /dev/tty, writes selected
value to stdout. Falls back to a numbered list when no TTY is available.
Usage from bash:
zone=$(printf 'us-central1-a\tIowa\nus-east1-b\tVirginia\n' \
| spawn pick --prompt "Select zone" --default "us-central1-a")
- gcp/lib/common.sh: interactive project, zone, and machine-type pickers for
all GCP agent scripts. Each picker respects env var overrides (GCP_PROJECT,
GCP_ZONE, GCP_MACHINE_TYPE) and skips the prompt when already set. Uses
spawn pick for a nice arrow-key UI when available; falls back to
_display_and_select (fzf or numbered list) from shared/common.sh.
- _gcp_machine_type_options(): curated list of 8 popular instance types
- _gcp_zone_options(): 12 curated zones across US / EU / APAC / AU
- _gcp_project_options(): live list via gcloud projects list
- _gcp_pick_{machine_type,zone,project}(): picker wrappers
- _gcp_resolve_project(): now prompts interactively instead of erroring when
no project is configured
- create_server(): now calls pickers before provisioning instead of silently
using defaults
- cli version bump 0.5.2 to 0.5.3
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: clean up test directories after cmdlist integration tests
The cmdlist-integration.test.ts was creating temporary directories in
beforeEach but never cleaning them up in afterEach, leaving 1,560
test directories in /root (spawn-cmdlist-test-*).
Added rmSync cleanup in afterEach to remove the test directory after
each test run. Bumped CLI version to 0.5.2.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat: add update-team skill for managing agent team services
This skill automates updating and restarting agent team services
(discovery, refactor, security, qa-cycle) with the latest configuration
from setup-agent-team.
Features:
- Reads latest setup-agent-team SKILL.md for best practices
- Identifies all deployed services via wrapper scripts
- Validates wrapper scripts have required env vars and correct paths
- Validates systemd service files for compliance
- Updates wrapper scripts and systemd configs as needed
- Restarts services and verifies health
- Supports --check-only for dry-run mode
- Can target specific services or update all
Usage:
- claude /update-team # Update all services
- claude /update-team discovery # Update specific service
- claude /update-team --check-only # Check without changes
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Oracle Cloud is removed as a supported provider. Each agent now has a
`featured_cloud` field in manifest.json that controls cloud sort order
in the CLI picker — featured clouds appear after credential-detected
clouds but before CLI-installed ones, with a "recommended" hint.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add headless SDK mode for programmatic provisioning (#1181)
Add --headless and --output json flags to enable non-interactive
provisioning with structured JSON output on stdout.
- --headless: disables prompts, OAuth browser flows, and SSH sessions
- --output json: outputs structured SpawnResult JSON on stdout
- Exit code contract: 0=success, 1=execution, 2=download, 3=validation
- Upfront credential validation (fail-fast before provisioning)
- Script stdout piped to stderr to keep JSON output clean
- SPAWN_HEADLESS=1 env var set for bash scripts
Closes#1181
-- refactor/ux-engineer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: restore critical test mocks for fly SSH readiness checks
The PR inadvertently removed essential mock logic:
- fly ssh mock no longer responded to 'echo ok' commands
- timeout/gtimeout mocks were removed (needed for SSH polling)
- python3 mock was removed (needed for JSON parsing)
- /tmp/spawn_* cleanup was removed from test teardown
This caused 29 fly/* test failures with 'SSH connectivity failed'.
Restores the exact mock implementations from main branch.
---------
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements spawn name feature (#1372) to improve UX:
- Add optional spawn name prompt in interactive mode
- Pass spawn name via SPAWN_NAME env var to shell scripts
- Shell scripts use spawn name as default for resource names
- Store spawn name in history for future reference
- Bump CLI version to 0.4.0
The spawn name is prompted before agent/cloud selection and
automatically used as the default for platform-specific resource
names (server name on Hetzner, sprite name on Sprite, etc.).
Agent: ux-engineer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Add input validation for SSH connection parameters (IP, username, server_name)
and server identifiers used in delete operations. This prevents command injection
attacks if ~/.spawn/history.json is corrupted or tampered with.
Changes:
- Add validateConnectionIP() - validates IPv4/IPv6 addresses and sentinels
- Add validateUsername() - validates Unix username format
- Add validateServerIdentifier() - validates server names/IDs
- Update cmdConnect() to validate all connection params before use
- Update buildDeleteScript() to validate server IDs before interpolation
- Update mergeLastConnection() to validate data from bash scripts
- Add comprehensive test coverage for all validation functions
- Bump CLI version to 0.3.3 (security patch)
Security impact:
- Prevents HIGH severity command injection via history.ip/user (issue #1381)
- Prevents MEDIUM severity command injection via server_id (issue #1380)
Agent: security-auditor
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes path traversal vulnerability where unvalidated filenames from
GitHub API could write files outside intended directory.
Attack vector: MITM attack or DNS hijacking could inject filenames
like "../../../../../../tmp/evil.ts" to write arbitrary files.
Fix: Validate filenames before download - block "..", "/", and "\\"
to ensure files are written only within ${dest}/cli/src/
Severity: HIGH/CRITICAL
Affects: All users running installer via curl|bash
Agent: security-auditor
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: auto-run gcloud auth login on expired GCP tokens
Instead of telling users to run `gcloud auth login` manually, just
run it automatically when auth check fails or instance creation hits
a reauthentication error, then retry.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: prioritize clouds with CLI installed + hcloud CLI integration
When selecting a cloud provider, clouds are now sorted in 3 tiers:
1. Credentials detected (env vars set) — top priority
2. CLI installed (e.g., gcloud, hcloud, aws) — middle priority
3. Neither — default order
Also adds hcloud CLI-first support for Hetzner operations (server
create/delete/list, SSH key management, auth) with automatic fallback
to the existing REST API when hcloud is not available.
Closes#1370
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: rename aws-lightsail to aws across the project
Simplifies the cloud key from "aws-lightsail" to "aws" — AWS should
have a single entry regardless of the underlying service used.
Renames the directory, updates manifest.json matrix keys, CLI map,
test fixtures, README, and all agent scripts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wire up connection tracking across all 10 clouds so users can reconnect
to and delete previously spawned servers via `spawn list` and `spawn delete`.
Phase 1 - Connection tracking:
- Extend save_vm_connection() with cloud and metadata params
- Add save_vm_connection to create_server() in all cloud libs
- Extend VMConnection with cloud, deleted, deleted_at, metadata fields
Phase 2 - Delete via interactive picker:
- Add "Delete this server" option to spawn list picker
- Build delete scripts that reuse each cloud's destroy_server()
- Confirmation UX with spinner feedback
- Soft-delete marking in history (deleted records show [deleted])
Phase 3 - Standalone delete command:
- spawn delete (aliases: rm, destroy) with interactive picker
- Filter support: spawn delete -a <agent> -c <cloud>
Also improves reconnect hints for Fly (fly ssh console) and
Daytona (daytona ssh) connections.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Tests calling loadManifest(true) with mocked fetch were writing test
manifests (only 2 agents) to the real ~/.cache/spawn/manifest.json.
This caused `spawn` to show only "Claude Code" and "Aider" instead
of all 15 agents.
Root cause: CACHE_DIR/CACHE_FILE were computed once at import time,
so tests setting XDG_CACHE_HOME in beforeEach() had no effect.
Fix:
- Make CACHE_DIR/CACHE_FILE dynamic via getter functions so test
isolation via XDG_CACHE_HOME actually works
- Skip disk writes in test environments unless XDG_CACHE_HOME is
explicitly set (tests that need disk cache use setupTestEnvironment
which sets XDG_CACHE_HOME to a temp dir)
- Bump CLI version to 0.2.88
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Fixed Hetzner installation issue where curl to claude.ai/install.sh
was returning 403 errors. Added fallback to use bun (already installed
by cloud-init) to install Claude Code.
Also added --debug flag to enable verbose bash output (set -x) for
easier troubleshooting.
Changes:
- hetzner/claude.sh: Use bun fallback installation method
- CLI: Added --debug flag support (v0.2.86)
- shared/common.sh: Enable set -x when SPAWN_DEBUG=1
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Adds a new `spawn last` command (with `rerun` alias) that instantly
reruns the most recent spawn from history without requiring the
interactive picker. This improves the workflow for users who frequently
want to restart their last session.
Features:
- `spawn last` or `spawn rerun` to instantly rerun last spawn
- Shows descriptive label and timestamp before rerunning
- Handles empty history gracefully with helpful message
- Preserves prompt from original spawn if it had one
- Updated help text and examples
Agent: ux-engineer
Co-authored-by: spawn-refactor-bot <refactor@openrouter.ai>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes two UX issues identified in #1106:
1. Install script: Raw escape codes weren't rendering in log_info
- Before: "Run \033[1mspawn\033[0m\033[0;32m to get started\033[0m"
- After: Uses printf with proper color variable interpolation
2. Update command: Confusing message after `spawn update`
- Before: "Run your spawn command again to use the new version"
- After: "Run spawn again to use the new version"
- The word "your" implied the user had run some other command,
but they explicitly ran `spawn update`
Agent: ux-engineer
Co-authored-by: Spawn Refactor Service <refactor@spawn.service>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
When spawn scripts fail or are interrupted, error messages now include
the cloud provider's actual dashboard URL instead of generic "check your
cloud provider dashboard" text. This helps users quickly navigate to
their provider to check server status, clean up orphaned resources, or
debug provisioning failures.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The interactive flow (bare `spawn`) was missing the preflight credential
warning that the direct `spawn <agent> <cloud>` path already had. Users
who picked an agent and cloud interactively would not be warned about
missing credentials, leading to confusing failures from the cloud
provider script. Now both paths warn about missing credentials before
launching.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When running `spawn claude` or `spawn hetzner`, the "Loading manifest..."
spinner appeared twice: once in showInfoOrError() and again in
cmdAgentInfo/cmdCloudInfo via validateAndGetEntity(). Pass the
pre-loaded manifest to avoid the redundant load and spinner flash.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a spawn script is killed by a signal (SIGKILL, SIGTERM, SIGHUP, etc.),
Node.js returns exit code null. Previously this produced the confusing message
"Script exited with code null". Now detects the actual signal and shows
signal-specific guidance: OOM suggestions for SIGKILL, terminal reconnection
tips for SIGHUP, spot instance warnings for SIGTERM.
Fixes#1011
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Change 'auth: TOKEN' to 'needs TOKEN' with yellow highlight in spawn clouds
- Always show legend footer explaining ready/needs indicators
- Add --clear hint to spawn list footer
- Show --version/-v and --help/-h aliases in help text
- Document SPAWN_UNICODE=1 env var in help
- Include HTTP status code in update fetch errors
- Bump version to next patch
Fixes#1010
Agent: issue-fixer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Show list-specific flags (-a, -c, --clear) in unknown flag error
- Add specific error for empty prompt files instead of generic validation
- Document SPAWN_UNICODE=1 env var in help text and troubleshooting
- Show filter/clear hints in interactive list picker
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
- Add interactive confirmation prompt before clearing spawn history
(spawn list --clear) to prevent accidental data loss
- Show total prompt length in dry-run preview when prompt exceeds 100
characters, so users can verify the correct prompt was loaded
- Add "Rerun previous" suggestion to non-interactive terminal fallback
- Show "(shown first)" hint when clouds with credentials are detected
in interactive picker, so users understand the sort order
- Add repository URL to spawn version output for discoverability
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Two UX improvements:
1. Dry-run credential status now shows the cloud provider's URL next to
missing cloud-specific auth vars (e.g., HCLOUD_TOKEN), helping users
find where to create their credentials. Previously only
OPENROUTER_API_KEY showed a URL hint.
2. Added `spawn list --clear` command to let users clear their spawn
history. Previously there was no way to reset the 100-entry history
file without manually deleting ~/.spawn/history.json.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix macOS compatibility bug in Atlantic.Net API signature: `base64 -w 0`
fails on macOS (no `-w` flag); add fallback like other providers
- Replace misleading "Use 'csb' CLI dashboard" in CodeSandbox interactive
session with link to the actual web terminal at codesandbox.io/dashboard
- Soften preflight credential check prompt from "will likely fail" to
"will prompt you to authenticate" (scripts have built-in auth flows)
- Bump CLI version to 0.2.72
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Two UX improvements:
1. `spawn clouds` now shows a green "ready" indicator next to clouds where
credentials are already configured in the environment, making it immediately
clear which providers the user can use without additional setup.
2. `spawn list` now shows relative timestamps ("5 min ago", "yesterday",
"3d ago") instead of absolute dates, giving immediate temporal context.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
When users type `spawn claude/hetzner` or `spawn hetzner/claude`,
the CLI now splits on the slash and forwards to the correct handler
with a helpful tip, instead of showing a confusing "invalid characters"
error from identifier validation.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a spawn script fails, the error message now checks which
required environment variables are actually set vs missing, instead
of generically saying "Missing or invalid credentials". This helps
users immediately see which credential they need to add.
- All set: "Credentials appear to be set (invalid or expired?)"
- Some missing: lists only the specific vars that are not set
- None set: lists all required vars
Version bump to 0.2.67.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two UX improvements:
1. `spawn <agent> <cloud> --dry-run` now shows a Credentials section that
checks which env vars (OPENROUTER_API_KEY, cloud auth vars) are set vs
missing, so users can verify readiness before a real run.
2. Script failure guidance (exit code 1 and default) now checks which
specific env vars are unset instead of showing a generic "need X + Y"
message, making it immediately clear what's missing.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>