Commit graph

961 commits

Author SHA1 Message Date
A
118be14a23
test: add 70 tests for time formatting, auth parsing, and record helpers (#963)
Direct unit tests for exported functions in commands.ts that were
previously only exercised through replicas or integration paths:
formatRelativeTime, formatTimestamp, getImplementedAgents,
getImplementedClouds, parseAuthEnvVars, hasCloudCredentials,
resolveDisplayName, buildRecordLabel, and buildRecordHint.

Agent: test-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-13 10:18:38 -08:00
A
f3d2384392
refactor: decompose GCP and Cherry create_server functions (#965)
GCP create_server was 64 lines (largest function across all cloud libs).
Cherry create_server was 54 lines. Both are now under 30 lines each
by extracting focused helpers:

GCP (64 -> 25 lines):
- _gcp_prepare_instance_files: startup script + SSH key temp files
- _gcp_run_create: gcloud command execution with error diagnostics
- _gcp_get_instance_ip: IP extraction from instance describe

Cherry (54 -> 27 lines):
- _cherry_build_server_body: JSON payload construction
- _cherry_submit_create: API call with error handling

Agent: complexity-hunter

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 10:15:47 -08:00
A
0f60a2b082
fix: add actionable guidance to agent installation failures across 126 scripts (#966)
Add log_install_failed helper to shared/common.sh that provides
structured troubleshooting for agent install failures: possible causes,
SSH debug command (when server IP available), manual install command,
and re-run suggestion. Also improve SSH key registration error message.

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 10:14:03 -08:00
A
5d69dc4192
fix: add actionable guidance to error messages across 10 cloud providers (#962)
Improve error messages in cloud provider lib/common.sh files to include
specific troubleshooting steps, dashboard URLs, and environment variable
hints instead of bare "Failed" messages.

Providers improved: Netcup, IONOS, CloudSigma, Northflank, UpCloud,
Fly.io, RamNode, OVH, Civo, Scaleway.

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 10:10:45 -08:00
A
e163b38f09
test: add 44 tests for credential display functions in dry-run path (#961)
Adds unit tests for buildCredentialStatusLines, formatAuthVarLine, and
the credential section allSet detection in showDryRunPreview. These
functions had zero direct test coverage despite being in the critical
dry-run preview path. Tests cover:

- formatAuthVarLine: env var set/missing display, URL hints, indentation
- buildCredentialStatusLines: OPENROUTER_API_KEY always present, single
  and multi-var auth, URL hint placement, partial credentials, no-auth
  clouds, all-set scenarios
- Dry-run allSet detection: all creds set, partial, multi-var, none auth
- credentialHints allSet branch: the "appear to be set" path when all
  env vars are present but the error may be invalid/expired credentials
- credentialHints partial credentials: mixed set/missing env var states

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 09:51:49 -08:00
A
5ebe3e5a13
fix: add actionable guidance to destroy_server failures and service timeouts (#959)
When server destruction fails, users are left with a bare error message and
no indication that they may still be billed for a running server. This adds
dashboard URLs and clear warnings to destroy_server errors across 9 clouds
(Hetzner, UpCloud, Contabo, Netcup, RamNode, Hostinger, HOSTKEY, OVH,
Latitude). Also improves error messages for Koyeb (app creation, service
deployment, deployment timeout, instance ID), GitHub Codespaces (creation
failure, readiness timeout), and E2B (sandbox creation failure).

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 09:38:58 -08:00
A
3c3c697ea5
fix: json_escape SSH key names and fix GCP metadata injection (#958)
SSH key registration in 11 cloud providers used unescaped key_name
directly in JSON request bodies. If the hostname (used to generate
key names) contained JSON-special characters like double-quotes, it
could break out of the JSON string and inject arbitrary JSON fields.

Fix: use json_escape for key_name in all providers, matching the
pattern already used by Scaleway.

Also fix GCP create_server which embedded the startup script inline
in --metadata with comma delimiters. Commas in the script could break
metadata parsing or inject additional metadata keys. Fix: use
--metadata-from-file for the startup script.

Affected providers: Hetzner, DigitalOcean, Vultr, BinaryLane,
Hostinger, Contabo, Cherry, HOSTKEY, Civo, Linode, Genesis Cloud, GCP.

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 09:03:35 -08:00
A
fd80f1992c
fix: improve error messages for GCP, AWS Lightsail, Cherry, and Oracle (#957)
- GCP: capture gcloud stderr on failure, add common issues guidance,
  use _log_diagnostic for ensure_gcloud errors
- AWS Lightsail: add common issues for create_server failure,
  use _log_diagnostic for ensure_aws_cli errors,
  improve instance timeout message with actionable steps
- Cherry Servers: use extract_api_error_message instead of raw response
  dump, add common issues for server creation failure
- Oracle Cloud: capture OCI CLI stderr on instance launch failure,
  add common issues for VCN, subnet, and instance creation errors

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-13 09:00:10 -08:00
A
df27dfec17
docs: Sync README matrix with manifest.json (#956)
Agent: team-lead

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 08:49:21 -08:00
A
221ed67f2e
test: add 27 tests for clearHistory and cmdListClear (#955)
Add comprehensive test coverage for the previously untested clearHistory
(history.ts) and cmdListClear (commands.ts) functions invoked via
`spawn list --clear`.

Tests cover:
- Basic clearing: return value, file deletion, directory preservation
- Edge cases: corrupted JSON, non-array values, empty files, null
- Interaction: save-after-clear, filterHistory-after-clear, idempotency
- cmdListClear: log.info vs log.success output, singular/plural grammar,
  file deletion, corrupted file handling, large history counts

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 08:48:39 -08:00
A
68122a6f70
fix: remove duplicate ensure_jq and fix help text alias placement (#954)
- Remove duplicate ensure_jq() function in shared/common.sh (lines 2341-2372)
  that was accidentally left after extracting it to the shared lib in #946
- Move "Aliases: ls, history" onto the "spawn list" help line so it no longer
  appears to describe "spawn list --clear"

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 08:38:17 -08:00
A
0897f64f61
refactor: decompose Atlantic.Net and HOSTKEY create_server into focused helpers (#952)
- Atlantic.Net create_server (59 lines -> 30 lines):
  - Extract _atlanticnet_extract_error for API error message parsing
  - Extract _atlanticnet_check_create_error for error checking + diagnostics
  - Extract _atlanticnet_parse_instance_response for response parsing
  - Replace inline python3 with shared _extract_json_field helper
  - Reuse _atlanticnet_extract_error in atlanticnet_register_ssh_key

- HOSTKEY create_server (52 lines -> 24 lines):
  - Extract _hostkey_build_order_body for JSON body construction
  - Extract _hostkey_check_create_error for error checking + diagnostics
  - Extract _hostkey_parse_instance_response for response parsing

- Update atlanticnet-provider.test.ts to check extracted helpers

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-13 08:35:24 -08:00
A
ea39c8bf28
fix: prevent command injection in update-check reExecWithArgs (#951)
Replace execSync with execFileSync in reExecWithArgs() to prevent shell
metacharacter injection via binary path. execFileSync bypasses the shell
entirely, executing the binary directly with an argv array.

The performAutoUpdate() call retains execSync since it legitimately needs
a shell for piping (curl | bash).

Fixes #950

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 08:34:04 -08:00
A
793dee20ae
fix: improve CloudSigma error messages and update RamNode README (#947)
CloudSigma UX fixes:
- Use log_error consistently for remediation hints (was log_warn)
- Add "Common issues" block to create_server failure
- Add actionable hints to server timeout error
- Extract API error message instead of dumping raw response
- Fix README: VNC password is random, not hardcoded

RamNode README:
- Update implemented agents list from 9 to all 15

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 07:54:31 -08:00
A
8dbe5d0710
fix: validate CLOUDSIGMA_REGION to prevent SSRF via URL injection (#948)
CloudSigma was the only cloud provider that did not validate its region
env var before interpolating it into the API base URL. A crafted
CLOUDSIGMA_REGION value (e.g. "evil.com/foo#") could redirect all API
calls — including HTTP Basic Auth credentials — to an attacker's server.

Adds validate_region_name check in create_server, matching the pattern
used by all other providers (DigitalOcean, Vultr, Hetzner, Fly, etc.).

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 07:51:07 -08:00
A
65127e2e24
refactor: extract ensure_jq to shared lib and decompose CloudSigma helpers (#946)
- Extract duplicated _ensure_jq from hetzner and hostkey into shared
  ensure_jq in shared/common.sh (removes ~60 lines of duplication)
- Replace CloudSigma hand-rolled ensure_cloudsigma_credentials with
  ensure_multi_credentials (removes ~20 lines of inline Python)
- Replace _wait_for_cloudsigma_server (63 lines) with
  generic_wait_for_instance + _resolve_cloudsigma_ip helper
- Extract _find_ubuntu_image_uuid and _clone_drive from
  create_cloudsigma_drive (60 -> 23 lines)
- Extract _get_ssh_key_uuid from create_server inline Python
- Replace hand-rolled SSH functions with shared SSH helpers
  (SSH_USER=cloudsigma)

Net reduction: ~158 lines across 4 files.

Agent: complexity-hunter
-- refactor/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-13 07:34:36 -08:00
A
51d217add1
refactor: deduplicate _ensure_jq and decompose DO create_server (#943)
- Extract `ensure_jq()` from hetzner and hostkey into shared/common.sh,
  eliminating 64 lines of identical duplicated code
- Decompose DigitalOcean `create_server()` by extracting error handling
  into `_do_check_create_error()` helper, and using the shared
  `extract_api_error_message` instead of inline Python parsing
- Use shared `_extract_json_field` for droplet ID extraction

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-13 07:25:08 -08:00
A
904bebfb70
test: add 68 tests for run retry logic and display formatting helpers (#945)
Cover previously untested internal functions:
- formatCacheAge (index.ts): cache age to human-readable string conversion
- handleUserInterrupt (commands.ts): Ctrl+C detection in error messages
- runWithRetries (commands.ts): SSH failure retry logic with MAX_RETRIES
- printInfoHeader (commands.ts): agent/cloud info page header formatting
- printGroupedList (commands.ts): grouped display with type labels
- renderListTable (commands.ts): spawn history table output formatting

Includes boundary transition tests, edge cases, and integration scenarios.

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 07:24:49 -08:00
A
527e701676
feat: Add ramnode/codex (#796)
* feat: Add ramnode/codex script

Agent: gap-filler
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: address security review feedback for ramnode/codex

- Use inject_env_vars_ssh instead of raw heredoc (fixes command injection)
- Restore wait_for_cloud_init call after verify_server_connectivity
- Use .zshrc instead of .bashrc for consistency with other ramnode scripts
- Restore server info in success message

Agent: pr-maintainer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 07:19:00 -08:00
A
2e7b083f8f
fix: show cloud URL for missing credentials in dry-run and add spawn list --clear (#944)
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>
2026-02-13 07:16:14 -08:00
A
78d95d0399
fix: use safe inject_env_vars helpers in 4 missed scripts (#941)
Replaces unsafe direct shell interpolation of OPENROUTER_API_KEY with
the inject_env_vars_ssh/inject_env_vars_local helpers that use
single-quoted values to prevent shell injection.

Affected scripts:
- codesandbox/codex.sh
- codesandbox/interpreter.sh
- codesandbox/gptme.sh
- atlanticnet/goose.sh

This is the same class of fix applied in PR #937 to 3 other scripts,
but these 4 were missed.

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 07:00:26 -08:00
A
eacaf6fd03
refactor: replace hand-rolled polling with generic_wait_for_instance in 3 clouds (#850)
Cherry, RamNode, and Scaleway each had custom IP-polling loops (30-38 lines)
that duplicated the logic already in shared/common.sh's generic_wait_for_instance.
Replace them with single-call delegates, removing ~100 lines and 2 helper functions.

- cherry: _cherry_wait_for_ip -> generic_wait_for_instance, remove _cherry_extract_primary_ip
- ramnode: _ramnode_wait_for_ip -> generic_wait_for_instance
- scaleway: _scaleway_power_on_and_wait polling -> generic_wait_for_instance, remove _scaleway_extract_ip

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-13 06:52:22 -08:00
A
1124af265c
feat: add CloudSigma cloud provider (#860)
* feat: add CloudSigma cloud provider

Add CloudSigma as a new cloud provider with API-first architecture:

- Create cloudsigma/lib/common.sh with HTTP Basic Auth support
- Implement cloudsigma/claude.sh and cloudsigma/aider.sh agent scripts
- Add CloudSigma to manifest.json (38th cloud provider)
- Add matrix entries for all 15 agents (2 implemented, 13 missing)
- Update test/record.sh with CloudSigma endpoints and auth handling
- Update test/mock.sh with URL-stripping for CloudSigma API
- Add cloudsigma/README.md with usage documentation

CloudSigma features:
- API v2.0 with HTTP Basic Auth (email:password)
- Regions: ZRH (Zurich), WDC (Washington DC), LVS (Las Vegas)
- Granular resource control (CPU/RAM/Disk independently configurable)
- Ubuntu 24.04 cloned from public library drives
- SSH access via cloudsigma user
- Pay-as-you-go pricing starting at ~$14/month

Agent: cloud-scout

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: address security review comments for CloudSigma provider

- [CRITICAL] Fix command injection in credential saving: use sys.argv
  instead of raw shell interpolation in Python strings
- [CRITICAL] Fix shell injection in create_cloudsigma_drive: pass name
  and size via sys.argv instead of inline interpolation
- [CRITICAL] Fix shell injection in SSH key fingerprint lookups: pass
  fingerprint via sys.argv
- [HIGH] Replace hardcoded VNC password with random generation via
  openssl rand -hex 8
- [MEDIUM] Fix config file path injection: pass via sys.argv

Agent: pr-maintainer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 06:50:25 -08:00
A
a74a8cebcb
refactor: decompose Daytona create_server and test_daytona_token into focused helpers (#939)
Break the 94-line create_server into smaller single-responsibility functions:
- _daytona_create_with_resources: explicit CPU/memory/disk creation path
- _daytona_create_with_class: class-based creation path
- _resolve_sandbox_id: sandbox ID extraction from daytona info
- _is_snapshot_conflict: snapshot/resource conflict detection

Extract duplicated auth error handling from test_daytona_token:
- _is_daytona_auth_error: auth error pattern matching
- _daytona_auth_error: formatted auth error message

No functional changes. All public function signatures preserved.

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-13 06:46:13 -08:00
A
f806085bb6
fix: improve UX of error messages and status output (#938)
- Remove redundant "Warning:" prefix from API key format message (log_warn
  already conveys warning status)
- Fix incorrect `export VAR=token spawn ...` syntax in auth failure hint
  (export makes it persistent, inline env var syntax is correct)
- Replace attempt/retry jargon with elapsed time in SSH wait and instance
  polling messages (users care about time, not internal retry counts)
- Show instance IP in friendlier "ready (IP: x.x.x.x)" format
- Move HTTP status codes from error title to body in download failures
  (cleaner error headline, details still available)
- Simplify dry-run credential warning (remove confusing double-negative
  "without --dry-run")
- Remove redundant "Warning:" prefix from extra arguments message

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-13 06:45:04 -08:00
A
fa52d5978a
fix: use safe inject_env_vars helpers in 3 missed scripts (#937)
Three scripts were missed in the previous inject_env_vars migration
(commit d785571): atlanticnet/amazonq.sh, codesandbox/cline.sh, and
civo/continue.sh. These still used raw heredoc/echo patterns that
embed OPENROUTER_API_KEY directly into shell command strings without
escaping, allowing potential command injection if the API key contained
shell metacharacters (quotes, backticks, dollar signs).

Replace with inject_env_vars_ssh (Atlantic.Net, Civo) and
inject_env_vars_local (CodeSandbox) which use generate_env_config
to safely single-quote all values.

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 06:42:13 -08:00
L
13575a7a20
feat: Add PR comment triage to security reviewer and refactor pr-maintainer (#940)
Both the security team's pr-reviewer and the refactor team's pr-maintainer
now read PR comments before passing verdict. If comments indicate a PR is
superseded, duplicate, or abandoned, they close it instead of reviewing.

Security reviewer: new step 2 (comment-based triage) before staleness check,
steps renumbered 3→4 through 11→12, verdict options include closed-duplicate.

Refactor pr-maintainer: reads comments first, closes when clearly indicated,
rule updated from "NEVER close" to "only close when comments justify it".

Also updates CLAUDE.md dual-mode table: Agents → Teammates.

Co-authored-by: Security Reviewer <security-reviewer@spawn.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-13 06:25:45 -08:00
A
f696f57129
test: add 44 tests for extract_api_error_message and generic_wait_for_instance (#798)
These two critical shared/common.sh functions had zero test coverage despite
being used across 4+ and 9 cloud providers respectively (10+ call sites each).

extract_api_error_message tests cover:
- All JSON error field patterns (message, error, error.message, error_message, reason)
- Field priority ordering
- Fallback behavior for invalid JSON, empty input, unrecognized fields
- Real-world API response formats (Hetzner, DigitalOcean, Vultr, Contabo)
- Edge cases (special characters, unicode, arrays, null)

generic_wait_for_instance tests cover:
- Successful polling (first attempt and multi-attempt)
- IP extraction from flat and deeply nested JSON
- Timeout behavior when status never reaches target
- Continued polling when API returns errors or invalid JSON
- Polling when status matches but IP is empty
- Logging output (progress, success, timeout)

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 06:25:25 -08:00
A
0fa2b630df
docs: Sync README matrix with manifest.json (#884)
Agent: team-lead

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 06:23:55 -08:00
A
71e5eb6ab7
feat: Add codesandbox/kilocode (#874)
Agent: gap-filler-4

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 06:23:50 -08:00
A
c0cb32f9ce
test: add 97 tests for list command output helpers (#846)
* test: add 97 tests for list command output helpers

Cover buildRetryCommand (prompt truncation at 80 chars, quote escaping,
prompt-file fallback), resolveDisplayName (null manifest fallback),
buildRecordLabel/buildRecordHint (30-char hint truncation, picker
formatting), parseAuthEnvVars (multi-var parsing, validation),
hasCloudCredentials (multi-var auth, empty/unset vars),
getImplementedClouds/getImplementedAgents (manifest filtering),
isRetryableExitCode (SSH 255 detection), formatTimestamp (edge cases),
and getStatusDescription (404 special case).

Agent: test-engineer
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: import actual functions instead of duplicating them in tests

- Export formatTimestamp, buildRecordLabel, buildRecordHint from commands.ts
- Replace 11 duplicated function implementations with imports from commands.ts
- Add @clack/prompts mock (required when importing commands.ts)
- All 97 tests still pass against the real production code

Agent: pr-maintainer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: resolve rebase conflicts and update tests for formatRelativeTime

Merged formatRelativeTime from main, exported formatTimestamp and
buildRecordHint, updated tests to use relative time assertions.

Agent: pr-maintainer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 06:22:18 -08:00
A
4bb0165d59
feat: Add codesandbox/gptme (#872)
* feat: Add codesandbox/gptme

Agent: gap-filler
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: use safe quoting pattern for API key injection in gptme

Agent: pr-maintainer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 06:21:50 -08:00
A
c2ed7a21ad
feat: Add cline on Atlantic.Net (#892)
* feat: Add cline on Atlantic.Net

Agent: gap-filler

* fix: use inject_env_vars_ssh, fix README markdown, fix trailing comma

Agent: pr-maintainer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-13 06:21:44 -08:00
A
ce904383d2
feat: Add interpreter on Atlantic.Net (#890)
* feat: Add interpreter on Atlantic.Net

Agent: gap-filler

* fix: use inject_env_vars_ssh, fix README markdown, use zshrc

Agent: pr-maintainer
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-13 06:16:59 -08:00
L
58dcc6405c
refactor: Replace 'agent' with 'teammate' terminology in team prompts (#936)
Team leaders were outputting "Continue waiting for agent results" because
the prompts used "agent" language throughout. Updated all 4 team scripts
to use "teammate" and "spawn team" terminology per the Anthropic agent
teams documentation (https://code.claude.com/docs/en/agent-teams).

Changes across refactor.sh, security.sh, discovery.sh, qa-cycle.sh:
- "agents" → "teammates" when referring to team members
- "agent teams" → "spawn teams" when referring to the coordination concept
- Preserved: Agent: commit trailers, AGENT-NAME sign-offs, subagent_type,
  agent-teams URL, and "agent" as software product (AI agents)

Co-authored-by: Security Reviewer <security-reviewer@spawn.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-02-13 06:14:14 -08:00
A
c72b442411
feat: Add codex on CodeSandbox (#889)
Agent: gap-filler

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
2026-02-13 06:11:54 -08:00
A
de04724c6d
feat: Add local/amazonq (#871)
Implements Amazon Q CLI on local machine with OpenRouter integration.

Agent: gap-filler-5

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-13 06:11:13 -08:00
A
35ef679a38
feat: Add goose on Atlantic.Net (#886)
Agent: gap-filler

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
2026-02-13 06:11:10 -08:00
A
bdde11c356
feat: Add interpreter on CodeSandbox (#891)
Agent: gap-filler

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
2026-02-13 06:10:50 -08:00
A
c833f4ed3e
fix: improve UX with macOS compat fix, clearer messages, and less alarming prompts (#934)
- 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>
2026-02-13 06:10:47 -08:00
A
75d7be29a4
fix: improve UX for stale manifest cache, list rerun hints, and version info (#805)
- Show warning when manifest is loaded from stale cache (offline fallback)
  so users know the data may be outdated
- Fix list footer rerun command: reuse buildRetryCommand instead of
  truncating prompts with "..." which produced broken copy-paste commands
- Show manifest cache age in "spawn version" output for troubleshooting
- Bump CLI version 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 06:09:56 -08:00
A
4c1a344a7a
test: add 59 tests for JSON extraction helpers in shared/common.sh (#804)
Cover _extract_json_field and extract_api_error_message functions that
were recently extracted (PRs #673, #767) but had zero test coverage.
These are critical infrastructure used by Hetzner, DigitalOcean, Vultr,
and Contabo for API error parsing and by generic_wait_for_instance for
status polling.

Tests cover:
- _extract_json_field: basic extraction, nested fields, default values,
  complex Python expressions, real-world cloud provider patterns, edge cases
- extract_api_error_message: all standard error field patterns (message,
  error, error.message, error.error_message, reason), field priority order,
  fallback behavior, real-world cloud provider error formats, edge cases

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 06:07:50 -08:00
A
28ed8d09b7
feat: Add amazonq on Atlantic.Net (#888)
Agent: gap-filler

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
2026-02-13 06:05:05 -08:00
A
099d8020cc
test: add 343 cloud lib source chain verification tests (#935)
Verify that every cloud provider's lib/common.sh correctly sources
shared/common.sh and exposes required shared functions. Tests run
each cloud's lib in a real bash subprocess to catch source chain
breaks, syntax errors, and missing function definitions.

Coverage:
- Source chain integrity for all 36 cloud lib files
- Required shared function availability (logging, OAuth, API, SSH)
- json_escape behavior (quotes, newlines, backslashes, tabs)
- validate_api_token and validate_server_name security
- calculate_retry_backoff bounds
- extract_api_error_message parsing
- Cross-cloud consistency (SSH_OPTS, API helpers)
- bash -n syntax check on all lib files

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 06:02:06 -08:00
A
8c1183cdbc
feat: Add cline on CodeSandbox (#885)
Agent: gap-filler

Co-authored-by: B (Discovery Team) <6723574+louisgv@users.noreply.github.com>
2026-02-13 06:01:30 -08:00
A
5948de15b8
fix: show 'ready to go' in quick start when all credentials are set (#866)
When all required credentials (OPENROUTER_API_KEY + cloud auth vars) are
already configured, the Quick start section in `spawn <agent>` and
`spawn <cloud>` now shows a concise "credentials detected -- ready to go"
message with just the launch command, instead of showing export instructions
the user doesn't need.

Previously, the `hasCreds` variable was computed but unused in both
`printCloudQuickStart` and `cmdAgentInfo`. This change puts it to use
to give users a clear signal when they're ready to launch.

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 05:58:22 -08:00
A
d785571026
fix: use safe inject_env_vars helpers instead of raw shell interpolation (#932)
Replace unsafe heredoc/echo patterns with inject_env_vars_ssh (Atlantic.Net)
and inject_env_vars_local (CodeSandbox) for API key injection. The previous
patterns embedded OPENROUTER_API_KEY values directly into shell command strings
without escaping, allowing potential command injection if the API key contained
shell metacharacters (quotes, backticks, dollar signs).

Affected scripts (11 total):
- atlanticnet: codex, continue, gemini, gptme, kilocode, opencode
- codesandbox: amazonq, gemini, goose, opencode, plandex

The safe helpers (generate_env_config) properly single-quote all values and
escape embedded single quotes, preventing shell interpretation of special chars.

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 05:57:06 -08:00
A
bcb2d63f35
refactor: extract shared SDK wrapper in codesandbox/lib/common.sh (#862)
The file had 5 nearly-identical inline Node.js scripts, each repeating
the same require/init/async-IIFE/try-catch boilerplate. Extract two
shared helpers:

- _csb_sdk_eval: runs arbitrary JS with an authenticated SDK instance
- _csb_run_cmd: connects to a sandbox and runs a command (used by
  run_server and interactive_session)

interactive_session was a verbatim copy of run_server — it now delegates
to run_server directly.

Net result: -96 lines, +49 lines (287 → 241 lines total).

Agent: complexity-hunter

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-13 05:56:35 -08:00
A
1d73f8e04b
fix: improve local script UX -- add SPAWN_PROMPT support, fix prompt escaping, standardize messages (#863)
- local/cline.sh: Add missing SPAWN_PROMPT non-interactive mode support,
  replace manual sed -i.bak env var handling with inject_env_vars_local
  (eliminates leftover .bak files), add installation verification
- local/plandex.sh: Replace manual shell config handling with
  inject_env_vars_local for consistency, fix printf '%q' prompt escaping
  that corrupted prompt text with literal backslashes
- local/aider.sh: Fix printf '%q' prompt escaping -- pass SPAWN_PROMPT
  directly as quoted argument instead of shell-escaping it
- local/interpreter.sh: Same printf '%q' fix as aider.sh
- 7 local scripts: Standardize "Appending environment variables to
  ~/.zshrc..." to "Setting up environment variables..." for consistency
  with all other cloud providers

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 05:56:29 -08:00
A
dd224612e9
refactor: replace raw curl calls in render/lib/common.sh with render_api wrapper (#933)
Add a centralized `render_api` function that delegates to `generic_cloud_api`,
giving Render the same automatic retry logic (429/503/network errors with
exponential backoff) that all other providers already have.

- `_render_create_service`: raw curl POST -> `render_api POST`
- `_render_wait_for_service`: raw curl GET -> `render_api GET` + `_extract_json_field`
- `cleanup_server`: raw curl DELETE -> `render_api DELETE`

Also improves the wait loop with `INSTANCE_STATUS_POLL_DELAY` support and
better timeout messaging matching the standard provider pattern.

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-13 05:56:20 -08:00