Commit graph

974 commits

Author SHA1 Message Date
A
1d4e5b874c
fix: add defense-in-depth for SPAWN_HOME path validation and manifest JSON sanitization (#984)
- Validate SPAWN_HOME is an absolute path, reject relative paths to prevent
  unintended file writes (addresses #980)
- Resolve SPAWN_HOME to canonical form to collapse .. segments
- Strip __proto__, constructor, and prototype keys from parsed manifest JSON
  to prevent prototype pollution (addresses #979)
- Apply sanitization to all manifest ingestion paths (GitHub fetch, disk cache,
  local dev manifest)
- Add 12 tests covering path validation and JSON sanitization

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 11:37:10 -08:00
A
16b9132c7c
fix: add confirmation to history clear and improve UX details (#983)
- Add interactive confirmation prompt before clearing spawn history
  (spawn list --clear) to prevent accidental data loss
- Show total prompt length in dry-run preview when prompt exceeds 100
  characters, so users can verify the correct prompt was loaded
- Add "Rerun previous" suggestion to non-interactive terminal fallback
- Show "(shown first)" hint when clouds with credentials are detected
  in interactive picker, so users understand the sort order
- Add repository URL to spawn version output for discoverability

Agent: ux-engineer

Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
2026-02-13 11:31:01 -08:00
Ahmed Abushagur
d501b5eb1d
fix: CI test summary uses NO_COLOR instead of sed hack (#985)
* fix: strip ANSI colors before grepping test summary

The mock test output uses ANSI escape codes for colored ✓/✗/━━━
characters, so the grep in the Post summary step couldn't match
them. Strip colors with sed first.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: use NO_COLOR standard instead of sed to strip ANSI codes

mock.sh now respects the NO_COLOR env var (https://no-color.org/).
CI sets NO_COLOR=1 so grep matches ✓/✗/━━━ cleanly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 11:26:41 -08:00
Ahmed Abushagur
0ed8a29004
fix: stop QA cycle from auto-merging PRs, only create them (#981)
The QA cycle was auto-merging stale QA PRs that were mergeable.
Now it only closes stale ones — merging is left for human review.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 11:19:24 -08:00
A
3b4444f292
fix: show all auth vars in agent info quick start for multi-credential clouds (#975)
The `spawn <agent>` quick start section was only showing the first auth
env var when the best available cloud requires multiple credentials
(e.g., UpCloud with UPCLOUD_USERNAME + UPCLOUD_PASSWORD). This left
users confused about what other credentials they needed.

Now iterates over all auth vars, consistent with `spawn <cloud>` info.

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 11:18:28 -08:00
A
c7dcbaa5af
fix: validate TARGET_SCRIPT against allowlist in trigger-server (#974)
Add startup validation for the TARGET_SCRIPT env var to prevent
arbitrary script execution. The validation:
- Requires .sh extension
- Checks the file exists
- Resolves symlinks and relative paths via realpathSync
- Verifies the real path is inside the allowed skill directory

Fixes #970

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 11:14:40 -08:00
A
8c8511f9a2
test: add 67 tests for untested shared/common.sh helpers (#978)
Cover 8 functions that had zero test coverage:
- log_install_failed: actionable guidance for agent install failures (added across 126 scripts)
- ensure_jq: cross-platform jq installation
- get_cloud_init_userdata: cloud-init template structure and content
- _multi_creds_validate: credential validation with env var cleanup on failure
- check_python_available: dependency check with install guidance
- calculate_retry_backoff: exponential backoff with jitter bounds
- verify_agent_installed: agent command verification with diagnostic output
- _log_diagnostic: structured error reporting with causes and fixes
- _generate_csrf_state: CSRF token generation with multiple backends
- register_cleanup_trap / track_temp_file / cleanup_temp_files: temp file lifecycle

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 11:11:23 -08:00
Ahmed Abushagur
50b2e98d7d
ci: add mock test workflow for PRs (#977)
Runs `bash test/mock.sh` on every pull request targeting main.
Includes concurrency grouping to cancel stale runs and a 10-minute
timeout. Results are posted to the GitHub Actions step summary.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 11:11:15 -08:00
A
b2628e91c1
fix: add actionable guidance to SSH, API, and dependency error messages (#968)
Improve error messages in shared utilities and cloud providers that
previously showed bare "Failed to..." messages without telling users
how to fix the problem.

Shared (shared/common.sh):
- generate_ssh_key_if_missing: handle ssh-keygen/mkdir failures with
  disk space and permission guidance
- get_ssh_fingerprint: detect missing/corrupt public key files with
  regeneration instructions
- generic_ssh_wait: structured "How to fix" with manual SSH test command
  and firewall check
- _report_api_failure: add DNS/firewall/proxy guidance for network errors
- ensure_jq: platform-specific install commands for unknown package
  managers, hash rehash hint after install
- get_openrouter_api_key_manual: structured guidance after 3 failed
  attempts

Cloud providers:
- Contabo: actionable guidance for OAuth token failures
- Exoscale: guidance for credential validation and CLI download failures
- Netcup: network connectivity hint for API connection failure
- Scaleway: structured guidance for project ID lookup failure

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 11:00:35 -08:00
A
37ae22ddb5
refactor: reduce complexity in Linode create_server and CLI dispatchCommand (#972)
Extract _linode_extract_error helper to deduplicate 3 inline Python
error-extraction blocks, and _linode_handle_create_error to reduce
create_server from 47 to 31 lines.

Extract dispatchListCommand, dispatchSubcommand, dispatchVerbAlias, and
dispatchSlashNotation from the 63-line dispatchCommand function, reducing
it to 15 lines with clear single-responsibility helpers.

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:58:40 -08:00
A
23351585b4
test: add 261 tests for cloud provider error guidance patterns (#973)
Validate that cloud provider lib/common.sh files include actionable
error guidance when operations fail (destroy_server, create_server,
auth). Tests cover dashboard URLs, billing warnings, structured
logging, API error extraction, URL format, timeout messages, and
auth credential references across all 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-13 10:54:37 -08:00
A
f9c491a546
fix: move CloudSigma region validation to API entry point and harden trigger-server issue param (#967)
The SSRF fix in PR #948 added validate_region_name in create_server(),
but cloudsigma_api() is called much earlier via test_cloudsigma_credentials()
and cloudsigma_check_ssh_key(). A crafted CLOUDSIGMA_REGION (e.g.
"evil.com/foo#") could redirect API calls — including Base64-encoded
Basic Auth credentials — to an attacker's server before create_server()
is ever reached.

Move validation to get_cloudsigma_api_base() so every API call validates
the region before constructing the URL.

Also add a 10-digit length cap to the trigger-server issue parameter as
defense-in-depth against path traversal via absurdly long numbers in
worktree directory paths.

Fixes #960

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 10:32:49 -08:00
A
8852bcfc36
refactor: decompose GCP ensure_gcloud and create_server into focused helpers (#964)
ensure_gcloud (36 lines -> 5 lines + 3 helpers):
- _gcp_check_cli_installed: verify gcloud CLI presence
- _gcp_check_auth: verify active authenticated account
- _gcp_resolve_project: resolve and export GCP_PROJECT

create_server (65 lines -> 37 lines + 4 helpers):
- _gcp_write_startup_script: write cloud-init to tracked temp file
- _gcp_invoke_create: run gcloud compute instances create
- _gcp_handle_create_error: actionable error guidance
- _gcp_get_instance_ip: fetch and export instance external 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 10:19:19 -08:00
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