Cover _parse_api_response, _update_retry_interval, _api_should_retry_on_error,
calculate_retry_backoff, _cloud_api_retry_loop, generic_cloud_api,
generic_cloud_api_custom_auth, _make_api_request, _make_api_request_custom_auth,
and _curl_api -- all recently refactored with zero prior test coverage.
Agent: test-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` interactively, clouds where the user already has
auth env vars set (e.g. HCLOUD_TOKEN, DO_API_TOKEN) now appear first
in the cloud selection list with a "credentials detected" hint. This
reduces friction by surfacing the most likely-to-succeed options.
Fixes#685
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Cover the grid/compact matrix views, agent/cloud listing content,
type grouping, auth hints, footer statistics, edge cases, and
cross-command consistency for the three main listing commands.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 62 subprocess-based integration tests that exercise the actual index.ts
entry point, catching issues that unit tests with mocked modules miss:
- showVersion output format (version string, runtime, platform, arch)
- Version/help flag aliases (--version, -v, -V, --help, -h)
- Trailing help flags on subcommands (agents --help, matrix -h, etc.)
- handleNoCommand error paths (--dry-run, --prompt without agent/cloud)
- Unknown flag detection and error messaging
- Flag value requirements (--prompt, -p, --prompt-file, -f)
- --prompt and --prompt-file mutual exclusion
- Verb alias routing (run, launch, start, deploy, exec)
- Extra arguments warning
- Prompt file error handling (nonexistent, directory)
- Non-interactive terminal detection
- Subcommand alias routing (m for matrix, ls/history for list)
- List command -a/-c flag validation
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
install.sh was modified in 3 of the last 5 commits but had zero test
coverage for its core helper functions. This adds tests for:
- version_gte: semver comparison (22 tests covering equal, greater,
lesser versions, segment edge cases, realistic bun version checks)
- find_install_dir: PATH-aware install directory resolution (6 tests
covering SPAWN_INSTALL_DIR override, PATH heuristics, fallback)
- ensure_in_path: PATH detection and shell-specific instructions (8 tests
covering bash/zsh/fish detection, partial prefix matching, long PATHs)
- install.sh syntax and structure validation (10 tests)
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
When users type 'spawn agents claude' or 'spawn clouds hetzner', they
intuitively expect to see info about that agent/cloud. Previously, the
extra argument was silently ignored with a warning, and the full list was
shown instead. Now these commands redirect to the info page for the
given name, with a tip suggesting the shorter 'spawn <name>' form.
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>
Cover the previously untested resolveListFilters function in commands.ts
which resolves display names to keys, handles case-insensitive matching,
and intelligently swaps bare positional args from agent to cloud filter.
Also adds tests for resolveAgentKey, resolveCloudKey, resolveDisplayName,
getImplementedClouds, getImplementedAgents, and cmdList integration with
filter resolution including table rendering and footer display.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 57 tests covering renderCompactList and renderMatrixFooter functions
which had zero test coverage. Tests cover compact list agent/cloud counts,
missing cloud display, "all clouds supported" logic, matrix footer legends
for compact vs grid modes, implementation counts, and consistency between
rendering helpers (getMissingClouds, getImplementedClouds, calculateColumnWidth).
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add comprehensive test coverage for the untested credential management
pipeline (_load_token_from_env, _load_token_from_config,
_validate_token_with_provider, _save_token_to_config,
_multi_creds_all_env_set, _multi_creds_load_config,
_multi_creds_validate) plus save/load roundtrip integration tests.
These functions are used by every cloud provider script but had zero
test coverage. Tests run in real bash subprocesses sourcing
shared/common.sh to catch actual shell behavior.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
install.sh is the critical entry point for new users (curl | bash) and
has been modified in 3 recent PRs but had zero test coverage. These tests
validate structure, conventions, security, curl|bash compatibility, the
source-mode fallback wrapper, clone_cli logic, find_install_dir, and
ensure_in_path behavior.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add comprehensive test coverage for the single-token credential management
functions in shared/common.sh that previously had zero test coverage:
- _load_token_from_env (env var detection, edge cases)
- _load_token_from_config (JSON config loading, error handling)
- _validate_token_with_provider (validation callback, env var cleanup)
- _save_token_to_config (secure file creation, JSON escaping, roundtrips)
- ensure_api_token_with_provider (full flow integration tests)
These functions are used by every single-token cloud provider (Hetzner,
DigitalOcean, Vultr, Lambda, Linode, etc.) and are security-critical
for credential handling.
Agent: test-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 (e.g., SSH timeout, credentials issue), the
retry command shown to the user was `spawn <agent> <cloud>`, dropping
the --prompt argument the user originally provided. This was a regression
from PR #683 which accidentally removed the buildRetryCommand function
and prompt parameter that PR #712 had added.
Restores buildRetryCommand (truncates to 60 chars, escapes quotes) and
passes prompt through reportScriptFailure so users can copy-paste the
full retry command without reconstructing it from memory.
Adds 7 tests for buildRetryCommand covering truncation, quote escaping,
empty/undefined prompt, and boundary cases.
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>
Previously, when a user hit Ctrl+C during script execution, the CLI
silently exited with code 130. This left users unaware that a server
may have already been created and could still be running, potentially
incurring charges.
Now shows a warning about orphaned resources before exiting.
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>
Root cause: bun install creates empty directories in proot (Termux)
because proot can't intercept bun's symlink/hardlink/copy_file_range
syscalls. This breaks both local build and source-mode fallback.
Fix: when `bun run build` fails, download the pre-built cli.js from
the `cli-latest` GitHub release. The bundled binary is self-contained
(80KB, all deps inlined) and only needs the bun runtime.
- Add CI workflow (.github/workflows/cli-release.yml) that builds and
uploads cli.js to a rolling `cli-latest` release on every push to main
- Replace broken source-mode fallback with GitHub release download
- Bump CLI version to 0.2.63
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a spawn script fails with credential-related errors, the error
message now always includes "Run spawn <cloud> for setup instructions"
alongside the required env var names. Previously, this setup hint was
only shown when the auth env var names were unknown.
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>
bun 1.3.8 on Termux proot doesn't resolve node_modules by walking up
from the source file directory. Changing cwd to ~/.spawn/ (where
node_modules lives) before exec ensures packages are found.
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a spawn script fails with exit code 255 (SSH connection failure),
the CLI now retries up to 2 times with progressive delays (5s, 10s).
Non-retryable failures (syntax errors, permission denied, Ctrl+C, and
generic exit code 1) are not retried and fail immediately as before.
Fixes#705
Agent: issue-fixer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bun 1.3.8 in Termux proot cannot resolve packages with --packages bundle
even with bun.lock present and after --force reinstall. When the bundled
build fails, install source + node_modules to ~/.spawn/ and create a
wrapper script that runs via `bun` directly.
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The non-git download path did not fetch bun.lock, causing bun install
to resolve dependencies from scratch. On older bun versions (e.g. 1.3.8
in Termux proot), this produced a node_modules layout that broke
`bun build --packages bundle`.
- Download bun.lock in the non-git (curl) path
- Add build retry with `bun install --force` fallback
- Enforce minimum bun version (1.2.0) with auto-upgrade
- Bump CLI version to 0.2.60
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, `spawn list hetzner` always treated the bare positional
argument as an agent filter, returning 0 results since "hetzner" is a
cloud, not an agent. Now resolveListFilters auto-detects: when the
filter doesn't resolve as an agent but does resolve as a cloud, it
reclassifies to a cloud filter. This matches the help text which
promises "Filter history by agent or cloud name".
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>
Cover groupByType, buildAgentLines, buildCloudLines, credentialHint,
mapToSelectOptions, buildRecordLabel, buildRecordHint, and
resolveDisplayName edge cases. Uses the established replica pattern
since these functions are not exported.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exit codes 126 (permission denied) and 137 (killed/OOM) previously
showed terse one-line messages with no suggestions for what to do.
Now they include specific causes and remediation steps, consistent
with all other exit codes in getScriptFailureGuidance.
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>
- Support `spawn list claude` as shorthand for `spawn list -a claude`
- Add --agent and --cloud as long-flag aliases for -a and -c
- Fix flaky cmdlist-integration tests by priming manifest cache in
beforeEach and isolating XDG_CACHE_HOME to prevent cross-test leakage
- Export _resetCacheForTesting from manifest.ts for deterministic tests
- Update help text with new filter syntax and examples
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>
Cover the untested filter resolution path added in PR #537 where
cmdList resolves display names (e.g., "Claude Code" -> "claude") before
querying history. Also cover buildRecordLabel/buildRecordHint helpers
from PR #531 and the double-quote escaping in rerun prompt suggestions.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The saveSpawnRecord MAX_HISTORY_ENTRIES=100 trimming was completely untested.
These tests cover: trimming at boundary (99->100, 100->101), trimming well
over limit (150+1), prompt preservation through trimming, sequential saves
crossing the limit, filterHistory reverse-chronological ordering, boundary
conditions (empty/missing dir), and file format after trimming.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
The word "list" is ambiguous in a CLI that also has "spawn agents" and
"spawn clouds" -- users naturally expect "spawn list" to list resources,
not show history. Adding "spawn history" as a first-class alias makes
the history command more discoverable. Also documents both aliases (ls,
history) in the help text.
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>
- Resolve display names in `spawn list -a/-c` filters (e.g., "Claude Code" -> "claude")
- Fix dry-run preview to show GitHub raw URL instead of non-existent openrouter.ai/lab URL
- Cap history at 100 entries to prevent unbounded growth
- Rename compact matrix "Missing" column to "Not yet available" for clarity
- Escape double quotes in rerun prompt suggestions to produce valid shell commands
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove 2 unnecessary indirection layers (_handle_api_transient_error and
_api_handle_transient_http_error) from the cloud API retry infrastructure.
The old _handle_api_transient_error had a bug where "network" was passed
as the attempt parameter to _api_should_retry_on_error, which expects a
numeric value. The retry logic is now inlined directly in
_cloud_api_retry_loop, calling _api_should_retry_on_error with the
correct arguments.
Also extract duplicated help-flag checking in dispatchCommand into a
hasTrailingHelpFlag helper, reducing nesting and removing repeated code.
Net: -72 lines, 2 fewer functions, 1 bug fix.
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 26 tests covering the cmdList pipeline through the actual
exported function with mock.module for @clack/prompts. Tests the
full path from history file through rendering: empty history,
table rendering with display names, agent/cloud filtering,
prompt display, manifest fallback, footer formatting.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When run in a TTY, `spawn list` (or `spawn ls`) now shows an interactive
picker using @clack/prompts select. Users can navigate the list with
arrow keys and press Enter to immediately rerun a previous spawn.
Non-TTY environments (piped output, CI) continue to show the static
table as before.
Fixes#492
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>
Tests the complete cmdRun success flow: primary/fallback download,
history recording during execScript, SPAWN_PROMPT/SPAWN_MODE env var
passing to bash, dry-run skip, and script content validation.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract duplicated credential-hint logic from case 1/default into
credentialHint() helper, and flatten nested if-blocks in
generic_wait_for_instance using early-continue.
Agent: complexity-hunter
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 with exit code 1 (the most common failure),
the error message now shows which specific environment variables are
needed (e.g., "need HCLOUD_TOKEN + OPENROUTER_API_KEY") instead of
the generic "run spawn <cloud> for setup" which required a second
command to discover the needed credentials.
Also adds a "Retry:" hint with the exact rerun command at the end of
all script failure error messages.
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>
Extract renderMatrixFooter, renderListTable, and suggestTypoCorrection
to reduce function complexity and improve readability.
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Users coming from Docker, kubectl, or other CLIs naturally try
"spawn run claude sprite" or "spawn launch aider hetzner". Previously
these would show a confusing "Unknown command: run" error. Now the CLI
transparently strips these verb prefixes and forwards to the correct
agent/cloud handler. Bare verbs like "spawn run" show a helpful message
explaining the correct syntax.
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>
Cover the integration of cmdList with manifest-resolved display names,
table header/separator rendering, prompt preview truncation in rows
vs footer, filtered result counts, timestamp formatting, and the
resolveDisplayName exported utility.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
- Add "Availability Matrix (N agents, M clouds)" title to spawn matrix output
- Shorten compact view column header from "Not available on" to "Missing"
- Show display names (e.g. "Claude Code") instead of raw keys in spawn list
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 agent-config-setup test that expected "does not run correctly" but
shared/common.sh now says "returned an error". Add 25 new tests verifying
checkEntity's user-facing messages for wrong-type detection, same-kind
fuzzy match, cross-kind fuzzy match (PR #510), and no-match fallback.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover recently extracted helpers from PR #506 that had zero direct tests:
- suggestFilterCorrection: agent/cloud filter typo suggestions (9 tests)
- showEmptyListMessage: empty list messaging with filters (11 tests)
- showListFooter: rerun hints, prompt truncation, filter info (14 tests)
- showUnknownCommandError: fuzzy match suggestions for bad commands (12 tests)
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
When a user types `spawn htzner claude` (cloud name typo as first arg),
checkEntity now detects that "htzner" is close to cloud "hetzner" and
suggests the user may have swapped agent and cloud arguments. Previously,
this only worked for exact cloud names; typos would produce a generic
"Unknown agent" error with no helpful suggestion.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Cover untested code paths in cmdCloudInfo and cmdAgentInfo:
- printCloudQuickStart with multi-auth env vars (e.g., UPCLOUD_USERNAME + UPCLOUD_PASSWORD)
- printCloudQuickStart URL hint only shown on first auth var (not repeated)
- printCloudQuickStart with OAuth auth (no parseable env vars)
- printCloudQuickStart with "none" auth (no extra export lines)
- printCloudQuickStart when no implemented agents (no example command)
- printAgentList "Not yet available" shown when missingAgents <= 5
- printAgentList "Not yet available" hidden when missingAgents > 5
- printAgentList boundary at exactly 5 missing agents
- cmdAgentInfo Quick start with multi-auth cloud as first available
- cmdAgentInfo Quick start with "none" auth cloud
- cmdAgentInfo no Quick start when no clouds implemented
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Users had no way to discover SPAWN_HOME, SPAWN_DEBUG, or SPAWN_UNICODE
env vars without reading source code. The help text now documents all
SPAWN_* environment variables in a dedicated section between
TROUBLESHOOTING and MORE INFO.
Also bumps CLI version to 0.2.50.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>