Commit graph

260 commits

Author SHA1 Message Date
A
7731306f37
test: add local cloud provider pattern tests (239 tests) (#911)
Adds comprehensive test coverage for the local cloud provider, which
runs agents directly on the user's machine without cloud provisioning.
Previously had zero dedicated tests despite 14 implemented agent scripts.

Tests cover:
- local/lib/common.sh API surface (no-op destroy, bash -c exec, cp uploads)
- All 14 local agent scripts follow local-specific patterns
- No SSH/SCP patterns leak into local scripts
- OpenRouter API key handling with OAuth fallback
- SPAWN_PROMPT handling for interactive/non-interactive modes
- Installation verification (command -v checks)
- Safety checks (no sudo, no rm -rf system dirs)
- Manifest consistency for local cloud entries

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>
2026-02-13 04:02:45 -08:00
A
7a441813fd
fix: detect slash notation and suggest correct syntax (#859)
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>
2026-02-13 02:15:59 -08:00
A
fa5b4979e8
fix: upgrade SSH to StrictHostKeyChecking=accept-new (TOFU) and randomize temp paths (#849)
- Change SSH default from StrictHostKeyChecking=no to accept-new, which
  accepts host keys on first connection but rejects if they change later
  (Trust On First Use). This protects against MITM attacks on subsequent
  connections. Requires OpenSSH 7.6+ (released Oct 2017).
- Replace predictable $$-based temp file path in upload_config_file with
  $RANDOM to prevent symlink attacks on the remote server.

Addresses findings from issue #763.

Agent: security-auditor

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-13 02:11:47 -08:00
A
bfb125c028
test: add cloud lib API surface tests (#852)
Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 02:09:56 -08:00
A
ebdab346df
fix: warn about missing credentials before running spawn scripts (#851)
Previously, users would run `spawn claude hetzner` without HCLOUD_TOKEN
set, the CLI would download and start executing the script, and it would
fail mid-execution after potentially provisioning resources. Now the CLI
checks for missing credentials before running and warns the user upfront.

In interactive mode, shows a confirmation prompt so the user can abort
or continue. In non-interactive mode, shows a warning without blocking.

- Add preflightCredentialCheck() that inspects cloud auth env vars
- Call it in cmdRun before script execution
- 9 tests covering all credential states (all set, partial, missing,
  multi-var, CLI-based auth, none auth)
- Version bump to 0.2.69

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>
2026-02-13 01:52:41 -08:00
A
7b5f84141f
fix: show specific missing credentials in script failure messages (#813)
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>
2026-02-13 01:45:01 -08:00
A
d9a18b49d3
fix: show credential-aware quick start in spawn <agent> and spawn <cloud> info (#817)
Prioritize clouds with detected credentials in spawn <agent> info pages.
Skip showing export instructions for env vars already set. Show credential
status in spawn <cloud> info header and available clouds 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>
2026-02-13 01:33:19 -08:00
A
2f671d8edf
test: add 66 tests for OAuth security functions in shared/common.sh (#814)
Cover previously untested security-critical OAuth functions:
- _generate_oauth_html: HTML generation for success/error pages
- _validate_oauth_server_args: port validation + CSRF state file
- _generate_oauth_server_script: Node.js server script generation
- cleanup_oauth_session: temp resource cleanup
- exchange_oauth_code: JSON injection prevention via json_escape
- execute_agent_non_interactive: prompt escaping with printf %q
- wait_for_oauth_code: timeout behavior
- _check_oauth_prerequisites: connectivity + runtime detection
- find_node_runtime: bun/node discovery

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>
2026-02-13 01:24:33 -08:00
A
1725fa79d4
test: add cloud lib security convention regression tests (69 tests) (#816)
Validates that all cloud provider lib/common.sh files follow security
conventions from the security audit. Tests cover SSH key encoding
(json_escape or python json.dumps), config file permissions, Python
code injection prevention, API body JSON safety, heredoc injection
prevention, shared/common.sh sourcing, and credential handling patterns.

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>
2026-02-13 01:23:20 -08:00
A
8446e785cf
test: add 88 tests for OAuth flow functions in shared/common.sh (#843)
The OAuth flow is the primary authentication mechanism for spawn users,
yet its component functions had zero test coverage. This adds tests for:

- validate_oauth_port: port range validation (boundary values, injection)
- _generate_csrf_state: CSRF token generation (entropy, uniqueness)
- _generate_oauth_html: success/error HTML page generation
- _generate_oauth_server_script: Node.js callback server (CSRF, ports)
- _validate_oauth_server_args: prerequisite validation (port, state, runtime)
- _init_oauth_session: temp directory and CSRF state file creation
- cleanup_oauth_session: PID and directory cleanup
- exchange_oauth_code: OAuth code-to-key exchange with json_escape security
- check_openrouter_connectivity: network reachability fallback chain
- Integration: session lifecycle and CSRF security properties

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>
2026-02-13 01:22:11 -08:00
A
6182348641
fix: show credential status in dry-run and specify missing env vars on failure (#841)
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>
2026-02-13 01:20:21 -08:00
A
b1a576a52a
test: add 51 tests for _classify_api_result and _report_api_failure (#834)
These helpers were extracted from _cloud_api_retry_loop in PR #821 to
reduce cyclomatic complexity but had zero test coverage. They are
invoked on every cloud API call across all providers:

- _classify_api_result: Classifies curl/HTTP results into retry reasons
  (network error, rate limit 429, service unavailable 503) or empty
  (success/non-retryable error). Tests cover all branches including
  curl exit codes 1/6/7/28, HTTP 429/503, success codes 200/201/204,
  non-retryable errors 400-502, and edge cases.

- _report_api_failure: Generates user-facing error messages after
  retries are exhausted. Differentiates network vs HTTP errors,
  outputs API response body only for HTTP errors. Tests cover
  retry count display, response body handling, and special chars.

Also includes integration tests verifying the classify-then-report
pipeline and realistic cloud provider scenarios (Hetzner, DigitalOcean,
DNS failures, auth errors, validation errors).

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 01:19:31 -08:00
A
087a14c276
test: add agent env injection contract tests (128 tests) (#838)
Validates the critical contract that every implemented agent script
correctly injects the environment variables from manifest.json.
Catches silent breakage where an agent starts but cannot reach the
LLM API due to missing OPENROUTER_API_KEY or provider-specific vars.

Tests cover:
- OPENROUTER_API_KEY presence in all scripts
- Provider-specific env vars (ANTHROPIC_BASE_URL, OPENAI_BASE_URL, etc.)
- OpenRouter API key acquisition patterns (env check, OAuth, manual)
- Agent install and launch command references
- Cloud lib env injection infrastructure
- Base URL values pointing to openrouter.ai
- No hardcoded API keys (security)
- Full coverage statistics across all agents and clouds

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>
2026-02-13 01:19:14 -08:00
A
813089def7
test: add 67 tests for shared/github-auth.sh (zero prior coverage) (#832)
Add comprehensive test coverage for the standalone GitHub auth helper
(shared/github-auth.sh) merged in PR #824 with no tests.

Coverage includes:
- Source pattern and function availability (9 tests)
- Fallback log functions when common.sh unavailable (3 tests)
- ensure_gh_cli: detection, installation paths, error handling (7 tests)
- _install_gh_binary: OS/arch detection, error paths, cleanup (11 tests)
- ensure_gh_auth: token auth, interactive login, post-login checks (8 tests)
- ensure_github_auth: combined wrapper success/failure (4 tests)
- Direct execution mode and set -eo pipefail (2 tests)
- Script conventions: bash 3.x compat, no echo -e, safe var access (10 tests)
- Installation path coverage: macOS/Linux/APT/DNF/Homebrew (4 tests)
- Error handling edge cases: curl failure, tar failure, auth failures (6 tests)
- GITHUB_TOKEN security: piped via printf, not CLI arg (2 tests)
- Shebang check (1 test)

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>
2026-02-13 01:17:57 -08:00
A
9f76af00d2
fix: show credential status in quick-start sections (#823)
The quick-start sections in `spawn <cloud>` and `spawn <agent>` now show
whether required env vars are already set (green with "set" indicator)
or still need to be configured (cyan "export" instruction). This helps
users immediately see what credentials are missing 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>
2026-02-12 23:59:57 -08:00
A
4d3c54a11e
refactor: extract helpers from execScript and _cloud_api_retry_loop (#821)
Reduce cyclomatic complexity in the two highest-scoring functions:

- cli/src/commands.ts: Extract `handleUserInterrupt` and `runWithRetries`
  from `execScript` (complexity score 6 -> 2 for execScript, retry logic
  now independently testable)

- shared/common.sh: Extract `_classify_api_result` and `_report_api_failure`
  from `_cloud_api_retry_loop` (complexity score 9 -> 4, removes duplicated
  error-classification logic from loop body)

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>
2026-02-12 23:57:20 -08:00
A
e73d6b9793
fix: support --flag=value syntax in CLI argument parsing (#826)
Previously, `spawn --prompt="Fix bugs" claude sprite` or
`spawn list --agent=claude` would fail with "Unknown flag" because
the CLI only recognized `--flag value` (space-separated) syntax.
Now `--flag=value` is expanded to `--flag value` early in the
arg parsing pipeline, supporting the common GNU-style convention.

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-12 23:55:46 -08:00
A
716da5d43b
fix: auto re-exec command after CLI auto-update (fixes #780) (#830)
When a CLI auto-update triggers mid-command (e.g. `spawn claude sprite`),
the updated binary now automatically re-runs with the original arguments
instead of asking the user to manually re-run. Sets SPAWN_NO_UPDATE_CHECK=1
on re-exec to prevent infinite update loops. Falls back to the old "run
again" message when no arguments were provided (bare `spawn`).

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>
2026-02-12 23:54:49 -08:00
A
317d931e87
test: add 32 tests for extract_api_error_message in shared/common.sh (#820)
This function parses JSON error responses from cloud provider APIs (used
by Hetzner, DigitalOcean, Vultr, and Contabo) and had zero test coverage.
Tests cover: field priority order, fallback behavior, realistic cloud
provider responses, and edge cases (non-object JSON, null/empty fields).

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>
2026-02-12 23:52:27 -08:00
A
fbea9303f0
test: add 48 tests for SSH key lifecycle functions (#828)
Cover ensure_ssh_key_with_provider (zero prior coverage), plus edge cases
for generate_ssh_key_if_missing, get_ssh_fingerprint, extract_ssh_key_ids,
and check_ssh_key_by_fingerprint. Tests validate the callback-based SSH
key registration flow used by all cloud providers.

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>
2026-02-12 23:52:22 -08:00
A
5169350feb
fix: use buildRetryCommand in spawn list footer to avoid truncated prompts (#819)
The "Rerun last" hint in `spawn list` was truncating prompts at 30
characters and appending "...", producing broken copy-paste commands.
Now delegates to the existing buildRetryCommand helper which properly
handles long prompts by suggesting --prompt-file instead of truncating.

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>
2026-02-12 23:52:08 -08:00
A
3f28d5f29f
test: add 52 tests for SSH helpers and instance polling in shared/common.sh (#822)
Cover critical infrastructure functions that had zero dedicated test coverage:
- ssh_run_server, ssh_upload_file, ssh_interactive_session (SSH command construction)
- ssh_verify_connectivity (ConnectTimeout, max_attempts, test command)
- generic_ssh_wait (exponential backoff, success/failure, elapsed time logging)
- wait_for_cloud_init (argument delegation, cloud-init file check)
- generic_wait_for_instance (API polling, status matching, IP export, timeout)
- extract_api_error_message (all 5 error field patterns + fallbacks)
- SSH_USER default behavior (root fallback across all helpers)

Uses mock SSH/SCP/sleep commands via PATH override to test argument
construction and behavior without requiring network connectivity.

Agent: test-engineer

-- refactor/test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 23:51:46 -08:00
L
6633873ccc
refactor: replace Python with jq in Hetzner lib, fix /lab → /labs URLs (#827)
Hetzner lib: replace all Python JSON parsing with jq. Uses the
/datacenters API as the authoritative source for server type
availability (server_types.available), cross-referenced with
/server_types for specs and pricing. jq is auto-installed if missing.

URLs: update openrouter.ai/lab/spawn → openrouter.ai/labs/spawn
across all READMEs and CLI source.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 23:14:11 -08:00
A
0fe83fe311
fix: improve CLI error messages for retry commands and unknown names (#777)
- buildRetryCommand: suggest --prompt-file for long prompts instead of
  truncating into a non-functional command (threshold raised to 80 chars)
- showUnknownCommandError: change "Unknown command" to "Unknown agent or cloud"
  since users are passing agent/cloud names, not commands
- Bump CLI version to 0.2.66

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 17:19:46 -08:00
A
ff0ccfdbd0
refactor: reduce complexity in ramnode picker and cmdInteractive (#756)
- Replace RamNode's custom _pick_flavor (37 lines) with shared
  interactive_pick helper (1 line), eliminating duplicated picker logic
- Extract credential sorting from cmdInteractive into reusable
  prioritizeCloudsByCredentials helper for testability and clarity

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>
2026-02-12 16:47:43 -08:00
A
f4b3d99cff
test: add 73 tests for logging, temp-file, cloud-init, and SSH key helpers (#765)
Add comprehensive test coverage for previously untested utility functions
in shared/common.sh that are used pervasively across all cloud providers:

- log_step: cyan progress messages (added PR #757)
- _log_diagnostic: structured error output (header + causes + numbered fixes)
- check_python_available: Python 3 dependency detection with install hints
- find_node_runtime: bun/node runtime discovery
- track_temp_file + cleanup_temp_files: secure credential temp file cleanup
- register_cleanup_trap: EXIT/INT/TERM signal handlers
- get_cloud_init_userdata: cloud-init YAML generation for provisioning
- calculate_retry_backoff: jittered exponential backoff
- generate_ssh_key_if_missing: ed25519 key generation with directory creation
- get_ssh_fingerprint: MD5 fingerprint extraction
- opencode_install_cmd: opencode install script content
- POLL_INTERVAL / SSH_OPTS: configurable constants and defaults
- All 4 log functions: stderr-only output verification

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>
2026-02-12 16:46:13 -08:00
A
a290815108
test: add 111 tests for trigger-server security and validation logic (#774)
Add comprehensive test coverage for the trigger-server HTTP service
(.claude/skills/setup-agent-team/trigger-server.ts), which had zero
test coverage despite recent security-critical changes (PRs #745, #747).

Tests cover:
- Timing-safe Bearer token auth (17 tests including injection attempts)
- VALID_REASONS allowlist enforcement (13 tests including injection)
- Issue parameter validation regex (17 tests including shell injection)
- Issue dedup logic (8 tests)
- Capacity checking (6 tests)
- reapAndEnforce process cleanup (9 tests including boundary cases)
- Health response structure (4 tests)
- Streaming response metadata (4 tests)
- Environment variable parsing (5 tests)
- Route matching logic (10 tests)
- Full validation flow with priority ordering (8 tests)

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-12 16:46:10 -08:00
A
cdf6f1dba5
fix: use log_step (cyan) for in-progress messages instead of log_info (green) (#768)
In-progress actions (installing, starting, connecting...) should use
log_step (cyan) to visually distinguish them from completion messages
which use log_info (green). This makes it easier for users to see at a
glance what is happening vs what has finished.

Changes:
- cli/install.sh: add log_step function, use it for install progress
- shared/common.sh: OAuth flow and non-interactive exec messages
- Cloud libs: interactive_session, auth, and cleanup messages
- Agent scripts: gateway startup and session opening 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>
2026-02-12 16:45:58 -08:00
A
fa5fe26d31
test: add 57 tests for credential-based cloud prioritization (PR #752) (#758)
Tests cover parseAuthEnvVars, hasCloudCredentials, cloud sorting by
detected credentials, mapToSelectOptions with hintOverrides, getAuthHint,
getImplementedClouds, and the full interactive picker prioritization flow.

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-12 15:49:29 -08:00
A
4e33cc39cd
fix: address medium security findings from #753 (#755)
- Replace `echo -e` with `printf` in cli/install.sh for macOS bash 3.x compat
- Remove `-u` (nounset) from test/run.sh — use `${VAR:-}` pattern instead
- Replace `source <(curl ...)` with `eval "$(curl ...)"` in test/run.sh for curl|bash compat
- Add .gitignore patterns for sensitive files (.env, *.pem, *.key, credentials)

Refs #753

Agent: security-auditor

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-12 15:48:52 -08:00
A
4bd5f2205f
test: add 71 tests for cloud API helper functions in shared/common.sh (#754)
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>
2026-02-12 15:48:46 -08:00
A
e36e087029
feat: prioritize clouds with detected credentials in interactive picker (#752)
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>
2026-02-12 15:33:14 -08:00
A
242eb1dde0
test: add 54 tests for env injection, JSON extraction, SSH key, and opencode helpers (#743)
Cover previously untested shared/common.sh functions:
- inject_env_vars_ssh: env var injection via SSH (argument passing, content, permissions)
- inject_env_vars_local: env var injection for container providers (local arg format)
- _extract_json_field: Python-based JSON field extraction with defaults
- check_ssh_key_by_fingerprint: SSH key fingerprint lookup via API
- opencode_install_cmd: robust OpenCode install command generation
- track_temp_file/cleanup_temp_files: secure temp file lifecycle
- validate_resource_name: resource name validation edge cases

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-12 15:18:57 -08:00
A
149a1feac5
test: add 41 tests for cmdMatrix, cmdAgents, cmdClouds listing output (#732)
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>
2026-02-12 15:02:55 -08:00
A
e103a6f2af
test: add CLI version output, dispatch routing, and flag validation tests (#674)
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>
2026-02-12 15:02:52 -08:00
A
cbffb856aa
test: add 46 tests for install.sh helper functions (#720)
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>
2026-02-12 15:02:49 -08:00
A
52b91f4c43
fix: redirect 'spawn agents <name>' and 'spawn clouds <name>' to info pages (#709)
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>
2026-02-12 15:02:23 -08:00
A
a626faf619
test: add 75 tests for resolveListFilters and related functions (#724)
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>
2026-02-12 15:02:06 -08:00
A
f131eb82a6
test: add coverage for compact matrix view and footer rendering (#708)
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>
2026-02-12 15:01:59 -08:00
A
6ca02f9362
test: add 55 tests for credential management functions in shared/common.sh (#714)
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>
2026-02-12 15:01:44 -08:00
A
26829b1065
test: add 83 tests for install.sh script validation (#716)
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>
2026-02-12 15:01:43 -08:00
A
30c61aa809
test: add 66 tests for ensure_api_token_with_provider credential flow (#729)
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>
2026-02-12 15:01:03 -08:00
A
b33b3cf6ea
fix: restore --prompt in retry command after script failure (#731)
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>
2026-02-12 15:00:46 -08:00
A
96d96b3ef6
fix: show cleanup warning when script is interrupted by Ctrl+C (#723)
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>
2026-02-12 15:00:30 -08:00
L
d961947983
fix: download pre-built CLI from GitHub release when local build fails (#728)
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>
2026-02-12 13:48:45 -08:00
A
f576661f3e
fix: show setup instructions in script failure credential hints (#683)
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>
2026-02-12 13:34:25 -08:00
L
ecfd8e2f4e
fix: source-mode wrapper must cd into ~/.spawn for package resolution (#710)
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>
2026-02-12 12:17:06 -08:00
A
2e02ab71e0
fix: retry script execution on transient SSH failures (exit 255) (#706)
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>
2026-02-12 12:13:38 -08:00
L
db72074e0a
fix: fall back to source-mode install when bundled build fails (#707)
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>
2026-02-12 12:13:19 -08:00
L
ac1f8239c8
fix: install.sh fails on Termux proot due to missing bun.lock (#704)
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>
2026-02-12 11:49:20 -08:00