cmdAgentInfo now displays the cloud type (api, cli, sandbox, etc.) next
to each cloud provider, helping users understand what kind of
infrastructure they're choosing. Also shows agent notes when present.
cmdCloudInfo now shows the cloud type beneath the description for
quick reference.
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>
Runs `bash -n` on every shell script in the repo:
- shared/common.sh (core library)
- All 21 cloud lib/common.sh files
- All 264+ implemented agent scripts
This is the automated equivalent of the CLAUDE.md rule:
"Run bash -n on every changed .sh file before committing."
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 agent/cloud lookup logic into showInfoOrError() and
replace repetitive switch cases with dispatch tables, reducing main()
from 85 to 65 lines and handleDefaultCommand() from 52 to 16 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>
Previously, unknown flags like --json or --verbose were silently ignored
or misinterpreted as positional arguments (agent/cloud names), leading to
confusing error messages. Now the CLI detects unrecognized flags and shows
a clear error listing the supported flags.
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>
Tests the actual CLI entry point through subprocess execution:
- help/version command routing (help, --help, -h, version, --version, -v, -V)
- Subcommand --help routing (list --help, agents --help, clouds --help, etc.)
- ls alias routing
- Non-TTY mode behavior (shows help when no args)
- handleError formatting for invalid identifiers
- extractFlagValue in actual CLI (--prompt/--prompt-file missing values)
- --prompt and --prompt-file mutual exclusion
- --prompt-file with nonexistent file
- Prompt-only-without-cloud error messaging
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>
Previously, `spawn hetzne` (typo of cloud name "hetzner") would show
"Unknown agent: hetzne" with no useful suggestion, because it only
searched agent names for typo matches. Now when a single argument
matches neither an agent nor a cloud, the error searches both pools
and shows "Did you mean hetzner (cloud)?" — guiding the user to the
right command.
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>
cmdCloudInfo was the only major command function with zero test coverage.
Tests cover happy paths, cloud notes display, empty agents state, error
paths (invalid identifiers, unknown clouds), and typo 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 bun build command was failing on proot-distro ubuntu because it couldn't
resolve node_modules dependencies. Added --packages bundle flag to explicitly
bundle all dependencies into the output file.
Fixes#209
Agent: issue-responder
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Validates that manifest.json is internally consistent and that every
"implemented" matrix entry has a corresponding script file. Catches
configuration drift between manifest.json and the actual file tree.
Tests cover:
- Manifest structure (agents, clouds, matrix top-level keys)
- Agent/cloud definition required fields and naming conventions
- Matrix consistency (valid statuses, complete coverage, no duplicates)
- Script file existence for all implemented entries
- Cloud lib/common.sh existence for clouds with implementations
- Script content basics (shebang, set -eo pipefail, sourcing lib)
- Orphaned script detection (files exist but marked "missing")
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Users who know their preferred cloud but not which agents are available
had no way to find out. Now `spawn hetzner` shows all agents available
on Hetzner, mirroring how `spawn claude` shows all clouds for Claude.
- Add cmdCloudInfo() showing cloud details + available agents
- handleDefaultCommand detects cloud names and routes to cloud info
- Update help text and clouds list footer to document the new command
- Bump CLI version to 0.2.15
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Rename the GitHub workflow, scripts, and service from "improve" to
"discovery" to better reflect what the automation does. Remove the
`spawn improve` CLI command entirely — the discovery/refactor loops
are internal automation, not user-facing CLI features.
File renames:
- .github/workflows/improve.yml → discovery.yml
- .claude/skills/.../improve.sh → discovery.sh
- .claude/skills/.../start-improve.sh → start-discovery.sh
- Service: improve-trigger → discovery-trigger
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When users type "spawn sprite claude" instead of "spawn claude sprite",
the CLI now detects the swap and suggests the correct order instead of
showing a confusing "Unknown agent" error. Also fixes grammar in
"spawn agents" and "spawn clouds" output (1 cloud vs 1 clouds).
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add "Did you mean?" suggestions when agent/cloud names have typos
(using Levenshtein distance, max 3 edits)
- Handle "spawn <agent> --help" to show agent info instead of failing
with "invalid characters" error on the --help flag
- Handle "--help" after subcommands (spawn list --help, spawn agents --help)
to show general help instead of silently ignoring the flag
- Bump CLI version to 0.2.13
- Add 15 tests for levenshtein and findClosestMatch functions
Agent: ux-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
The compareVersions function decides whether auto-update runs, but had
no direct test coverage. Tests cover: basic comparisons, precedence
ordering (major > minor > patch), missing/extra segments, zero and
boundary versions, non-numeric input handling, parseSemver helper,
realistic spawn version scenarios, and full checkForUpdates integration
with edge-case version strings.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
The spawn scripts themselves use curl|bash to install agents (e.g.
Claude Code). The validateScriptContent check was blocking our own
legitimate scripts. Removed curl|bash and wget|bash from the
dangerous patterns list since the scripts are already fetched from
our trusted GitHub repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The unicode-detect module only modifies TERM, never LANG. Two tests
incorrectly expected LANG to be set to en_US.UTF-8 - fixed to match
actual module behavior.
Agent: team-lead
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: Bun has a known bug (oven-sh/bun#25767) where --target=node
causes UTF-8 string literals to be double-encoded as Latin-1, producing
mojibake (â instead of ◆/│/✔). Switching to --target=bun avoids this
encoding path entirely.
Also removes the ineffective stdout.write monkey-patch that was
attempting to work around this issue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- `spawn update` now performs auto-update instead of printing curl command
- `--prompt` without both agent and cloud now errors instead of being silently dropped
- Update banner box uses dynamic padding to align correctly with any version length
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>
- Extract resolvePrompt() from main() in cli/src/index.ts (98 -> 62 lines)
Separates prompt flag parsing/file-reading from command dispatch
- Extract _validate_oauth_server_args() and _generate_oauth_html() from
start_oauth_server() in shared/common.sh (81 -> 52 lines)
Separates validation/HTML generation from server startup
- Bump CLI version to 0.2.11
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 16 tests for unicode-detect.ts which had zero coverage.
Tests verify ASCII/Unicode detection based on TERM, SSH,
and SPAWN_UNICODE/SPAWN_ASCII/SPAWN_NO_UNICODE env vars.
Also tests LANG setting and debug output behavior.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Explicitly convert string chunks to Buffer.from(chunk, 'utf8') before
writing to process.stdout. This fixes UTF-8 mojibake (â instead of ◆/│)
seen in some Bun + terminal combinations (e.g. Ghostty on macOS) where
process.stdout.write(string) doesn't encode as UTF-8 by default.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added explicit LANG=en_US.UTF-8 and stdout.setEncoding('utf8')
when Unicode mode is enabled. This should prevent UTF-8 mojibake
(â appearing instead of ◆) in terminals like Ghostty.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds debug logging to unicode-detect.ts to help troubleshoot why
Unicode rendering isn't working in Ghostty terminal. When SPAWN_DEBUG=1
is set, the CLI will show:
- Current TERM value (e.g., xterm-ghostty)
- SSH environment variables (SSH_CONNECTION, SSH_CLIENT, SSH_TTY)
- Whether ASCII mode is being forced
This will help identify if SSH detection is incorrectly triggering
ASCII fallback for local Ghostty sessions.
Usage: SPAWN_DEBUG=1 spawn list
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Reverses the overly conservative ASCII-only approach. Now defaults to
Unicode (beautiful spinners, checkmarks, symbols) on local macOS
Terminal, iTerm2, and modern Linux terminals, while still forcing
ASCII fallback for problematic environments:
- SSH sessions (encoding mismatches)
- Dumb terminals (TERM=dumb)
- Users can override with SPAWN_UNICODE=1 or SPAWN_NO_UNICODE=1
This fixes rendering issues while maintaining compatibility across
all environments.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Set both TERM=linux and CI=true in unicode-detect.ts
- CI env var provides additional Unicode disabling for @clack/prompts
- Fix test imports to use package.json instead of deleted version.ts
- Bump to 0.2.6
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Bumped CLI version from 0.2.4 to 0.2.5
- Added rule to CLAUDE.md: ANY change to cli/ requires a version bump
- Uses semantic versioning (patch for fixes, minor for features, major for breaking)
- Auto-update ensures users get latest version immediately
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Changed update banner to use +, -, | instead of Unicode box characters
- Replaced arrow (→) with ASCII arrow (->)
- Changed p.cancel() to plain console.error to avoid Unicode bullet
- Ensures clean rendering on all terminals regardless of Unicode support
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Removed CHECK_INTERVAL and cache-based update checking
- Now fetches latest version on every spawn invocation
- 5-second timeout keeps it fast
- Simplified tests to match new behavior (8 tests instead of 12)
- Users who don't want update checks can set SPAWN_NO_UPDATE_CHECK=1
This ensures users always get the latest version immediately when available.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Changed unicode detection to default to ASCII mode (TERM=linux)
- Users can opt-in to unicode with SPAWN_UNICODE=1
- Auto-detects SSH sessions and forces ASCII (common rendering issues)
- Fixes bullet point display issues in interactive selectors
To enable unicode: export SPAWN_UNICODE=1
To force ASCII: export SPAWN_ASCII=1 (or SPAWN_NO_UNICODE=1)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Removed src/version.ts file
- Updated src/commands.ts, src/index.ts, and src/update-check.ts to import version from package.json
- Using JSON import assertion: `import pkg from "../package.json" with { type: "json" }`
- Eliminates need to maintain version in two places
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Bumped version from 0.1.0 to 0.2.0
- Changed update-check mechanism to auto-install updates instead of just notifying
- checkForUpdates() now blocks and runs install.sh automatically when update is available
- Added executor wrapper for testability of execSync calls
- Updated all tests to mock executor.execSync instead of child_process.execSync
- Auto-update runs on every spawn invocation (24-hour cache prevents excessive checks)
- On update failure, shows error message but continues with original command
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements a non-intrusive update notification system that:
- Checks for new versions once per day
- Shows a styled notification when updates are available
- Stores check results in cache to minimize network requests
- Can be disabled with SPAWN_NO_UPDATE_CHECK=1
Implementation:
- New update-check.ts module with background version checking
- Comprehensive test suite (16 tests) covering all scenarios
- Integrated into main CLI entry point
- Updated documentation in README.md
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The 663-line bash CLI (spawn.sh) has drifted from the TypeScript CLI,
missing --prompt, security validation, download fallback, and other
features. Rather than maintaining two implementations, the installer
now auto-installs bun (~5 seconds) when it's not present, ensuring
every user gets the full-featured TypeScript CLI.
- Remove cli/spawn.sh (663 lines)
- Simplify install.sh: remove npm method, add bun auto-install
- Extract build_and_install() helper to deduplicate build logic
- Update cli/README.md and CLAUDE.md to reflect bun-only strategy
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes#132 - Some terminals can't render the unicode characters used by
@clack/prompts for bullets, selectors, and box drawing. This adds a
unicode detection module that runs before @clack/prompts is imported,
forcing ASCII mode when:
- SPAWN_NO_UNICODE=1 or SPAWN_ASCII=1 is set (explicit override)
- TERM is "dumb" or unset (common in basic terminals)
Also adds a troubleshooting hint 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>
Tests the actual exported cmdRun and cmdAgentInfo functions (not inline
replicas) for their process.exit error paths: identifier validation
(injection, path traversal, length), unknown agent/cloud handling,
unimplemented combination suggestions, prompt security validation,
and empty input handling.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests the actual exported command functions end-to-end using mock.module
for @clack/prompts and loadManifest(true) to force cache refresh with
mocked fetch data. Covers matrix display, agent/cloud listing, agent
info output, and help text content verification.
Agent: test-engineer
Co-authored-by: A <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
- OAuth failures now explain WHY they failed (timeout, port conflict,
no runtime, network) and suggest specific fixes
- Add duration hints to long-running operations (SSH wait: 30-90s,
OAuth: 10-30s) so users know what to expect
- validateImplementation shows exact `spawn <agent> <cloud>` commands
users can run instead of just listing cloud names
- SSH wait failure suggests checking cloud provider dashboard
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 legend to `spawn list` matrix output (+ implemented, - not yet available)
- Show cloud identifier keys in `spawn <agent>` info output for easy copy-paste
- Add CLI shortcut hint in interactive mode after selection
- Add agent descriptions to `spawn agents` output
- Add agent counts to `spawn clouds` output for consistency
- Fix misleading "Updating" spinner in `spawn update` (it only checks)
- Add `spawn help` to help text command listing
- Improve footer hints in agents/clouds output with actionable commands
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>
Tests the extractFlagValue generic function and the full CLI flag
extraction pipeline (--prompt/-p and --prompt-file). Existing tests
in index-parsing.test.ts and index-edge-cases.test.ts use simplified
re-implementations; these tests cover the exact behavior including
error messages, process.exit on missing values, startsWith("-") guard,
sequential two-pass extraction, and edge cases with flag-like values.
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>
- `spawn agents` now shows the key users need to type (e.g., `claude`)
alongside the display name and cloud count
- `spawn clouds` now shows the key (e.g., `sprite`) alongside the display
name and description
- Both commands show a usage hint at the bottom
- Error when both --prompt and --prompt-file are provided (was silently
overwriting)
- Remove duplicate agent validation in handleDefaultCommand (was loading
manifest twice without spinner, showing different error format)
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>