When a spawn script fails with credential-related errors, the error
message now always includes "Run spawn <cloud> for setup instructions"
alongside the required env var names. Previously, this setup hint was
only shown when the auth env var names were unknown.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bun 1.3.8 on Termux proot doesn't resolve node_modules by walking up
from the source file directory. Changing cwd to ~/.spawn/ (where
node_modules lives) before exec ensures packages are found.
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a spawn script fails with exit code 255 (SSH connection failure),
the CLI now retries up to 2 times with progressive delays (5s, 10s).
Non-retryable failures (syntax errors, permission denied, Ctrl+C, and
generic exit code 1) are not retried and fail immediately as before.
Fixes#705
Agent: issue-fixer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bun 1.3.8 in Termux proot cannot resolve packages with --packages bundle
even with bun.lock present and after --force reinstall. When the bundled
build fails, install source + node_modules to ~/.spawn/ and create a
wrapper script that runs via `bun` directly.
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The non-git download path did not fetch bun.lock, causing bun install
to resolve dependencies from scratch. On older bun versions (e.g. 1.3.8
in Termux proot), this produced a node_modules layout that broke
`bun build --packages bundle`.
- Download bun.lock in the non-git (curl) path
- Add build retry with `bun install --force` fallback
- Enforce minimum bun version (1.2.0) with auto-upgrade
- Bump CLI version to 0.2.60
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The local/opencode.sh script already exists and is functional.
Agent: gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Agents can self-review their PRs (read diff, add comments) but must
never merge. PRs get labeled `needs-team-review` and stay open for
external review by maintainers or a separate review cycle.
Changes across all three scripts:
- refactor.sh: added No Self-Merge Rule section, updated worktree
pattern, issue fix workflow, monitoring loop, and lifecycle mgmt
- discovery.sh: added No Self-Merge Rule section, updated worktree
pattern, git workflow, branch cleaner, and lifecycle mgmt
- qa-cycle.sh: renamed push_and_merge_pr to push_and_create_pr,
removed all gh pr merge calls, added self-review + labeling
Agent: team-lead
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Kamatera: Replace _load_kamatera_config, _validate_kamatera_credentials, and
ensure_kamatera_token (62 lines of custom env/config/prompt/validate/save logic)
with ensure_multi_credentials (5 lines).
OVH: Replace _ovh_prompt_credentials, ensure_ovh_authenticated (72 lines of
custom credential management) with ensure_multi_credentials (8 lines).
Replace wait_for_ovh_instance (38 lines of custom polling with backoff) with
generic_wait_for_instance (8 lines).
Net: -175 lines, same behavior, consistent patterns across providers.
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>
Previously, `spawn list hetzner` always treated the bare positional
argument as an agent filter, returning 0 results since "hetzner" is a
cloud, not an agent. Now resolveListFilters auto-detects: when the
filter doesn't resolve as an agent but does resolve as a cloud, it
reclassifies to a cloud filter. This matches the help text which
promises "Filter history by agent or cloud name".
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds label lifecycle transitions (Pending Review → Under Review → In Progress)
directly into both issue mode and refactor mode prompts per maintainer request
on #546. The community-coordinator now manages labels at each stage of issue
engagement, and the shutdown checklist verifies all open issues are labeled.
Fixes#546
Agent: team-lead
Co-authored-by: Sprite <noreply@sprite.dev>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cover groupByType, buildAgentLines, buildCloudLines, credentialHint,
mapToSelectOptions, buildRecordLabel, buildRecordHint, and
resolveDisplayName edge cases. Uses the established replica pattern
since these functions are not exported.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements Plandex on Netcup VPS using netcup/lib/common.sh primitives.
Updates manifest.json to mark netcup/plandex as implemented.
Updates netcup/README.md with Plandex usage instructions.
Agent: gap-filler-netcup
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Open Interpreter on local machine using local lib/common.sh primitives.
Agent: gap-filler-local
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Exit codes 126 (permission denied) and 137 (killed/OOM) previously
showed terse one-line messages with no suggestions for what to do.
Now they include specific causes and remediation steps, consistent
with all other exit codes in getScriptFailureGuidance.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The `echo ""` on line 351 of get_model_id_interactive() was going to
stdout, causing it to be captured by command substitution into MODEL_ID.
This injected a newline into the openclaw.json config, breaking JSON
parsing with "invalid character '\n' at 15:0".
Fixes#553
Agent: issue-fixer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
13 cloud providers had identical 5-line check_ssh_key functions that
fetch SSH keys from the provider API and grep for the fingerprint.
Extract this pattern into a shared check_ssh_key_by_fingerprint helper
in shared/common.sh, reducing each cloud's function to a single line.
Affected clouds: BinaryLane, Cherry, Civo, Contabo, DigitalOcean,
Genesis Cloud, Hetzner, Hostinger, Latitude, Linode, OVH, Scaleway,
Vultr.
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>
The generic_wait_for_instance timeout message previously just said
"did not become active in time" with no guidance. Now it follows the
same pattern as generic_ssh_wait by telling users what to do next.
Similarly, _validate_token_with_provider now shows the env var name
so users can set it directly instead of re-running interactively.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Support `spawn list claude` as shorthand for `spawn list -a claude`
- Add --agent and --cloud as long-flag aliases for -a and -c
- Fix flaky cmdlist-integration tests by priming manifest cache in
beforeEach and isolating XDG_CACHE_HOME to prevent cross-test leakage
- Export _resetCacheForTesting from manifest.ts for deterministic tests
- Update help text with new filter syntax and examples
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The get_model_id_interactive function returned MODEL_ID from env vars
without calling validate_model_id, bypassing the allowlist check. Also
migrated 13 legacy scripts from raw safe_read to get_model_id_interactive
which includes validation.
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>
The json_escape function already adds quotes around strings, so using
"%s" in printf was adding a second set of quotes, resulting in invalid
JSON like `"OPENROUTER_API_KEY": ""value""`.
Fixed railway/openclaw.sh and koyeb/openclaw.sh to use %s (unquoted)
for API key and token fields, matching the correct pattern used in
fly/openclaw.sh and shared/common.sh.
Fixes#542
Agent: issue-responder
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Cover the untested filter resolution path added in PR #537 where
cmdList resolves display names (e.g., "Claude Code" -> "claude") before
querying history. Also cover buildRecordLabel/buildRecordHint helpers
from PR #531 and the double-quote escaping in rerun prompt suggestions.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The saveSpawnRecord MAX_HISTORY_ENTRIES=100 trimming was completely untested.
These tests cover: trimming at boundary (99->100, 100->101), trimming well
over limit (150+1), prompt preservation through trimming, sequential saves
crossing the limit, filterHistory reverse-chronological ordering, boundary
conditions (empty/missing dir), and file format after trimming.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
The word "list" is ambiguous in a CLI that also has "spawn agents" and
"spawn clouds" -- users naturally expect "spawn list" to list resources,
not show history. Adding "spawn history" as a first-class alias makes
the history command more discoverable. Also documents both aliases (ls,
history) in the help text.
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. _cloud_api_retry_loop: consolidate two duplicate retry branches
(network error + HTTP 429/503) into a single retry path using a
retry_reason variable. Reduces from 47 to 43 lines, eliminates
duplicated _api_should_retry_on_error / _update_retry_interval calls.
2. interactive_pick: extract list display + selection into reusable
_display_and_select helper. The main function is now a thin wrapper
that checks env var, fetches items, then delegates to the helper.
3. generic_ssh_wait: replace inline backoff calculation (3 lines) with
existing _update_retry_interval helper, reducing duplication.
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Replace 25-line custom _binarylane_wait_for_active with 4-line
generic_wait_for_instance call, matching the pattern used by 7 other
clouds (DigitalOcean, Vultr, Linode, etc).
Change log_warn to log_step for status/progress messages in polling
loops across 7 cloud providers (aws-lightsail, exoscale, fly, kamatera,
latitude, ovh, scaleway). These are normal status updates, not warnings.
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
- Resolve display names in `spawn list -a/-c` filters (e.g., "Claude Code" -> "claude")
- Fix dry-run preview to show GitHub raw URL instead of non-existent openrouter.ai/lab URL
- Cap history at 100 entries to prevent unbounded growth
- Rename compact matrix "Missing" column to "Not yet available" for clarity
- Escape double quotes in rerun prompt suggestions to produce valid shell commands
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
18 cloud lib/common.sh files had identical 7-line get_server_name()
functions (get_resource_name + validate_server_name + echo). Added a
shared get_validated_server_name helper to shared/common.sh and replaced
all duplicates with one-line delegations. Net -110 lines.
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>
~1500 progress messages across 481 files were using log_warn (yellow)
for normal status updates like "Installing...", "Setting up...",
"Creating server...", etc. This made users think something was wrong
when everything was proceeding normally.
Changes:
- Replace log_warn with log_step for all progress/status messages
- Keep log_warn only for actual warnings (errors, remediation hints)
- Remove emoji from 3 sprite completion messages
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Remove 2 unnecessary indirection layers (_handle_api_transient_error and
_api_handle_transient_http_error) from the cloud API retry infrastructure.
The old _handle_api_transient_error had a bug where "network" was passed
as the attempt parameter to _api_should_retry_on_error, which expects a
numeric value. The retry logic is now inlined directly in
_cloud_api_retry_loop, calling _api_should_retry_on_error with the
correct arguments.
Also extract duplicated help-flag checking in dispatchCommand into a
hasTrailingHelpFlag helper, reducing nesting and removing repeated code.
Net: -72 lines, 2 fewer functions, 1 bug fix.
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 26 tests covering the cmdList pipeline through the actual
exported function with mock.module for @clack/prompts. Tests the
full path from history file through rendering: empty history,
table rendering with display names, agent/cloud filtering,
prompt display, manifest fallback, footer formatting.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When run in a TTY, `spawn list` (or `spawn ls`) now shows an interactive
picker using @clack/prompts select. Users can navigate the list with
arrow keys and press Enter to immediately rerun a previous spawn.
Non-TTY environments (piped output, CI) continue to show the static
table as before.
Fixes#492
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Cline agent support for RamNode Cloud via OpenStack API.
Uses npm install -g cline, injects OpenRouter credentials via
OPENAI_API_KEY and OPENAI_BASE_URL env vars.
Agent: ramnode-gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Add local machine support for Continue (31K+ GitHub stars).
Installs @continuedev/cli via npm and configures OpenRouter.
Agent: local-gap-filler
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Tests the complete cmdRun success flow: primary/fallback download,
history recording during execScript, SPAWN_PROMPT/SPAWN_MODE env var
passing to bash, dry-run skip, and script content validation.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract duplicated credential-hint logic from case 1/default into
credentialHint() helper, and flatten nested if-blocks in
generic_wait_for_instance using early-continue.
Agent: complexity-hunter
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace log_warn (yellow) with log_step (cyan) for progress messages
like "Installing...", "Setting up environment variables...", and
"Starting..." in recently-added scripts. Yellow warnings should be
reserved for actual warnings, not normal progress output.
Also fix bare `clear` calls to use `clear 2>/dev/null || true` for
robustness on minimal terminals, and improve the misleading
"Appending environment variables to ~/.zshrc..." message in local
scripts to the standard "Setting up environment variables..." phrasing.
Files: local/gptme.sh, local/aider.sh, ramnode/openclaw.sh,
ramnode/gptme.sh, netcup/gptme.sh
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>