Tests expected old error wording after PR #387 changed the messages.
Agent: team-lead
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract resolveAndLog() from cmdRun to handle argument resolution
- Extract detectAndFixSwappedArgs() from cmdRun for swap detection
- Extract printInfoHeader() shared by cmdAgentInfo and cmdCloudInfo
- Extract groupByType() used by cmdAgentInfo, cmdCloudInfo, cmdClouds
- Extract printGroupedList() for displaying grouped items with hints
All tests pass (3260/3260). No functional changes.
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
- Fix auto-update unicode symbols (checkmark/cross) that bypassed unicode
detection, causing garbled output in SSH sessions and dumb terminals
- Use log_info (green) instead of log_warn (yellow) for OAuth progress
messages, so normal authentication flow doesn't look like a warning
- Add install path to `spawn version` output for easier debugging when
multiple versions are installed
- Improve --prompt-file errors to distinguish file-not-found, permission
denied, and is-a-directory cases with actionable guidance
- Bump CLI version to 0.2.30
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Validates CLI helper functions (getImplementedClouds, parseAuthEnvVars,
resolveAgentKey, resolveCloudKey, calculateColumnWidth, fuzzy matching)
against the real manifest.json with all 21 clouds and 14 agents. Unlike
mock-based tests, these catch real-world issues like the local cloud's
auth: "none" pattern, multi-var auth strings, cloud type grouping, and
matrix key consistency. Includes dedicated tests for the new local cloud
provider (PR #383).
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
- Fix cmdCloudInfo quick-start showing "none" as a command for local cloud
- Always show OPENROUTER_API_KEY in cloud quick-start (needed for all agents)
- Update local scripts to explicitly say "Appending environment variables to ~/.zshrc"
- Update local README with spawn CLI usage and "What It Does" section
- Add 3 tests for quick-start auth display (none auth, env var auth)
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
OVH: Extract _ovh_extract_public_ipv4() and _ovh_extract_status() from
wait_for_ovh_instance() to reduce inline Python and improve readability.
Latitude: Extract _latitude_extract_error(), _latitude_get_ssh_key_ids(),
and _latitude_build_server_body() from create_server() and
latitude_register_ssh_key(). Deduplicates error extraction logic used in
two places.
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Adds a "local" cloud provider that installs and runs agents directly on the
user's machine without any cloud provisioning. This is useful for local
development and testing.
- local/lib/common.sh: Cloud lib with local execution functions
- local/claude.sh: Claude Code agent script
- local/openclaw.sh: OpenClaw agent script
- local/nanoclaw.sh: NanoClaw agent script
- manifest.json: Added local cloud + matrix entries
- test/: Updated record.sh and mock.sh for local cloud support
Fixes#378
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>
Adds a "local" cloud provider that installs and runs agents directly on the
user's machine without any cloud provisioning. This is useful for local
development and testing.
- local/lib/common.sh: Cloud lib with local execution functions
- local/claude.sh: Claude Code agent script
- local/openclaw.sh: OpenClaw agent script
- local/nanoclaw.sh: NanoClaw agent script
- manifest.json: Added local cloud + matrix entries
- test/: Updated record.sh and mock.sh for local cloud support
Fixes#378
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>
The showInfoOrError function loaded the manifest without a spinner,
causing no visual feedback on cold cache. Now uses loadManifestWithSpinner
so users see a loading indicator while the manifest fetches.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace vulnerable heredoc patterns across 27 continue.sh scripts with
setup_continue_config() helper that uses json_escape() + upload_config_file()
to safely handle API keys containing special characters like quotes or braces.
Also fix _save_token_to_config() in shared/common.sh which had the same
unescaped heredoc vulnerability for local token storage.
Relates to #104
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>
Show actionable auth setup commands in `spawn <cloud>` and `spawn <agent>`
output so users can get started immediately without reading README docs.
For clouds with env var auth (e.g. Hetzner), shows exact export commands:
Quick start:
export HCLOUD_TOKEN=your-hcloud-token-here
spawn claude hetzner
For agents, shows OpenRouter API key and example launch command:
Quick start:
export OPENROUTER_API_KEY=sk-or-v1-...
spawn claude sprite
Also adds parseAuthEnvVars() utility with 6 tests and 8 quick-start
integration tests.
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>
Export and test 7 previously-unexported utility functions from commands.ts:
getTerminalWidth, getMissingClouds, getImplementedAgents, getImplementedClouds,
getErrorMessage, calculateColumnWidth, getStatusDescription.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover previously untested code paths:
- cmdCloudInfo "Not yet available" missing agents text (<=5 threshold)
- cmdCloudInfo setup URL and auth field display
- cmdCloudInfo agent count (N of M) format
- cmdAgentInfo URL and notes display
- cmdAgentInfo cloud count and type grouping
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Split the two longest functions in the codebase into focused sub-functions:
Contabo create_server (108 -> 52 lines):
- _contabo_get_ssh_secret_ids: fetch SSH secret IDs from API
- _contabo_build_instance_body: construct API request JSON
- _contabo_wait_for_instance: poll until instance is running
Genesis Cloud create_server (91 -> 42 lines):
- _genesis_get_ssh_key_ids: fetch SSH key IDs from API
- _genesis_build_instance_body: construct API request JSON
- _genesis_wait_for_instance: poll until instance is active
No behavioral changes - pure restructuring for readability.
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
- Manifest load failure now shows specific troubleshooting steps including
cache directory path for manual cleanup
- Non-TTY mode explains why interactive picker is unavailable and suggests
the direct launch syntax instead of silently falling through to help
- `spawn update` network failure now includes recovery steps and manual
update command
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement contabo/nanoclaw.sh with WhatsApp-based AI agent support.
- Uses Contabo VPS provisioning via OAuth API
- Installs tsx, clones nanoclaw repo, builds with npm
- Configures OpenRouter API credentials via env vars and .env file
- Launches interactive session with WhatsApp QR code auth
Agent: gap-filler-contabo-1
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Implements Kilo Code deployment on Contabo VPS:
- Uses Contabo OAuth API for server provisioning
- Installs @kilocode/cli via npm
- Injects OpenRouter API key as KILO_OPEN_ROUTER_API_KEY
- Sets KILO_PROVIDER_TYPE=openrouter for native OpenRouter support
- Launches interactive Kilo Code CLI session
Agent: gap-filler-remaining
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
- Agent info (spawn <agent>) now shows "X of Y" cloud count and groups
clouds by type (api, cli, sandbox) for easier scanning
- Cloud info (spawn <cloud>) now shows "X of Y" agent count, auth method,
and lists missing agents when there are 5 or fewer
- Cloud listing (spawn clouds) groups providers by type with X/Y ratio
counts instead of singular/plural text
- Remove unused TYPE_COLUMN_WIDTH constant
- Bump CLI version to 0.2.26
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>
Validates ALL implemented scripts against CLAUDE.md shell script rules,
covering shebang, set -eo pipefail, lib/common.sh sourcing, macOS bash
3.x compatibility (no echo -e, no source <(), no set -u), and remote
fallback patterns. Unlike manifest-integrity.test.ts which samples 20
scripts, this checks every implemented script exhaustively.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Hostinger: HOSTINGER_OS_TEMPLATE was interpolated into Python code
without validation, allowing Python code injection via env var.
Added validate_resource_name check.
Contabo: CONTABO_PRODUCT_ID, CONTABO_REGION, CONTABO_IMAGE_ID were
interpolated into Python strings without validation. CONTABO_PERIOD
was interpolated as bare Python (not even quoted), allowing arbitrary
code execution. Added validate_resource_name, validate_region_name,
and integer validation checks.
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>
Cover the previously untested branch in validateAgent (lines 150-158) and
validateCloud (lines 186-194) where findClosestMatch against display names
is used when key-based suggestion fails. Also tests findClosestMatch with
display name arrays and end-to-end through cmdRun/cmdAgentInfo/cmdCloudInfo.
22 new tests, all pass.
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>
- Show agent/cloud URLs in info pages (spawn <agent>, spawn <cloud>)
- Add setup instructions link to cloud info pages
- Suggest available clouds when --prompt is used without a cloud arg
- Fix help text alignment for spawn list
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The log file is cumulative — result events from previous cycles persist.
The watchdog was grepping the entire file, so every new cycle immediately
matched an old result event and killed itself after 13 seconds.
Now records log file size at cycle start (LOG_START_SIZE) and only
searches content written since then via `tail -c +OFFSET | grep`.
Applied to both refactor.sh and discovery.sh.
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ports the same three improvements from refactor.sh (#340, #355):
1. Capture claude's actual PID via subshell wrapper (not tee's)
2. Detect stream-json "result" event and exit in 30s instead of 10min
3. kill_claude() kills the full process tree (TERM→5s→KILL)
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: Automated improvements
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: Kill claude process tree reliably via captured PID
The watchdog was killing tee (last in the pipe), not claude. Killing
tee doesn't propagate — claude and its agent subprocesses stay alive
as zombies, blocking the trigger server slot.
Now captures claude's actual PID via a subshell wrapper and kills the
full tree: SIGTERM children → SIGTERM claude → 5s grace → SIGKILL.
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 Sonnet 4.5 <noreply@anthropic.com>
Add Hostinger VPS hosting as a new cloud provider with REST API
provisioning and cloud-init support. Budget VPS starting at $4.95/mo
with hourly billing.
Implementation:
- hostinger/lib/common.sh: Full API wrapper with auth, provisioning,
SSH management, and cleanup functions
- hostinger/claude.sh: Claude Code on Hostinger VPS
- hostinger/aider.sh: Aider on Hostinger VPS
- hostinger/openclaw.sh: OpenClaw on Hostinger VPS
- hostinger/README.md: Complete usage documentation
Added to manifest.json:
- New cloud entry with API details and defaults
- 15 matrix entries (hostinger/claude through hostinger/continue)
- 3 implemented, 12 marked as "missing"
Agent: cloud-scout-2
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Contabo is a budget European VPS provider with affordable CPU instances
starting at $4.95/month. Ideal for AI agents using remote API inference.
Key features:
- Budget-friendly pricing ($4.95-$59/mo)
- Full REST API with OAuth2 authentication
- Cloud-init/user_data support for provisioning
- Full root access via SSH
- European data centers (GDPR-friendly)
- Unlimited traffic included
Implementation:
- contabo/lib/common.sh: OAuth2 token flow, instance provisioning, SSH access
- contabo/claude.sh: Claude Code deployment (implemented)
- contabo/aider.sh: Aider deployment (implemented)
- contabo/openclaw.sh: OpenClaw deployment (implemented)
- manifest.json: Added Contabo cloud + 15 matrix entries (3 implemented)
- contabo/README.md: Complete usage guide with API setup
Authentication uses OAuth2 password grant requiring 4 credentials from
https://my.contabo.com/api/details: Client ID, Client Secret, API User,
API Password.
Agent: cloud-scout-1
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Implements exoscale/continue matrix entry using Exoscale cloud primitives
and Continue installation pattern from existing implementations.
Agent: gap-filler-exoscale-4
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>