Long labels (e.g. "Claude Code on GCP Compute Engine -- spawn-trial-000-ahmed")
wrap to multiple rows, but the redraw logic uses a fixed line count to cursor-up.
This causes old content to pile up on every arrow-key press.
Query terminal width via `stty size` and truncate all lines to fit within
a single row, with a 1-char margin to prevent auto-wrap edge cases.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
discovery.sh used python3 for manifest parsing and string replacement,
violating the CLAUDE.md rule to use jq/bun instead. Replace all three
python3 call sites with jq equivalents and sed.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pressing `d` in the server picker now shows a sub-menu:
- Destroy server: hard delete (destroys cloud VM + marks deleted)
- Remove from history: soft delete (removes entry, no cloud API call)
- Cancel: go back to picker
Also adds `kill` as an alias for `spawn delete`.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agent installation, config upload, env setup, and agent configuration
can all fail transiently due to network flakiness or SSH instability
on fresh VMs. Add a shared withRetry() helper and wrap these operations
with 2-attempt retries to improve reliability without over-engineering.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 6 cloud providers interpolated process.env.TERM directly into shell
commands without validation. A malicious TERM value (e.g., containing
$(cmd)) would execute on the remote server, potentially exfiltrating
OPENROUTER_API_KEY and other credentials.
Add sanitizeTermValue() allowlist (alphanumeric, dots, hyphens, underscores)
to cli/src/shared/ui.ts and apply it in all interactiveSession functions.
Agent: security-auditor
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Both `spawn list` and `spawn delete` now share a single interactive
picker (`activeServerPicker`) backed by `getActiveServers()`. Pressing
`d` in the picker triggers inline delete-and-refresh without leaving
the list. Failed deletions now mark entries as deleted so users aren't
stuck with phantom servers they can't clear.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 6 other cloud providers (Fly, Hetzner, DigitalOcean, AWS, Sprite, Daytona)
validate remotePath with an allowlist regex before passing it to scp. GCP's
uploadFile had no validation at all, breaking the defense-in-depth pattern.
Adds the same allowlist check (^[a-zA-Z0-9/_.~$-]+$) plus dotdot check.
The regex includes $ to allow $HOME prefix paths used by agent-setup.ts.
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
KNOWN_FLAGS in unknown-flags.test.ts was copy-pasted from index.ts and
was missing the --name flag, causing silent test gaps. Extract
KNOWN_FLAGS, findUnknownFlag, and expandEqualsFlags into a new flags.ts
module so tests import the real source of truth.
- Create cli/src/flags.ts with KNOWN_FLAGS, findUnknownFlag, expandEqualsFlags
- Update index.ts to import from flags.ts (checkUnknownFlags now uses findUnknownFlag)
- Update unknown-flags.test.ts to import from flags.ts instead of copy-pasting
- Add tests for --name flag, KNOWN_FLAGS completeness, and expandEqualsFlags
- Bump CLI version to 0.6.15
Fixes#1744
Agent: test-engineer
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add ServerAliveInterval=15 + ServerAliveCountMax=3 to SSH_OPTS on all
clouds (DO, Hetzner, AWS, GCP) to prevent silent TCP drops during long
idle periods (e.g. waiting on slow LLM API calls). Daytona already had
these.
- Increase DigitalOcean cloud-init fallback poll from 6×5s (30s) to
20×5s (100s) so full-tier installs (build-essential + bun + node)
have time to finish when the streaming tail path fails.
- Replace `source ~/.zshrc` with explicit PATH export in openclaw launch
command to avoid side effects from zshrc inside bash -l.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the CLI collects a display name (SPAWN_NAME), each cloud now shows
the kebab-case derivative as the default in the resource name prompt
instead of silently accepting it. Users can hit Enter to accept or type
an override. Non-interactive mode still skips the prompt.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: eliminate duplicate name prompts, use cloud-native terminology
Users were prompted for a name up to 4 times per spawn. Now each cloud
has a single prompt using its native resource terminology (e.g. "Hetzner
server name", "Fly machine name") and getServerName() returns the
already-collected name silently instead of re-prompting.
Closes#1753
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: never use bare "spawn" as default name, always append random suffix
Extract defaultSpawnName() helper to shared/ui.ts that generates
"spawn-xxxx" with a random suffix. All cloud modules now use it
instead of bare "spawn" for every fallback path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
clone_cli() now uses rm -rf "${canonical_repo}" (the resolved real
path) instead of "${repo_dir}" for safer cleanup. Test assertion
updated to match.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SSH interactive sessions ran the agent command in a non-login,
non-interactive shell — .bashrc/.profile weren't sourced and TERM
wasn't always set, making the shell feel broken (no colors, bad
line editing, missing env).
Fix for all 6 SSH-based clouds (DO, Hetzner, AWS, GCP, Fly, Daytona):
- Forward local TERM (default xterm-256color) to the remote
- Use `exec bash -l -c` for a proper login shell
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace blocklist filename validation with a strict allowlist regex
(^[a-zA-Z0-9_-]+\.ts$) to prevent path traversal via encoding tricks
in GitHub API responses (#1749).
Use pwd -P for symlink-resolving canonicalization and delete via the
canonical path instead of the original variable to close the TOCTOU
gap in cleanup logic (#1750).
Agent: security-auditor
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
After installing bun via curl in cloud-init userdata, bun lives in
~/.bun/bin/bun which isn't on the system PATH. Agent scripts use
#!/usr/bin/env bun and fail with "bun: not found". Symlink it into
/usr/local/bin so it's immediately available system-wide.
Applies to: AWS, DigitalOcean, GCP, Hetzner
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add ~/.bun/bin to shell rc files so spawn finds bun after install
The install script was only adding ~/.local/bin to shell profile files
(bashrc/zshrc/bash_profile), but not ~/.bun/bin. Since the spawn binary
uses #!/usr/bin/env bun as its shebang, bun must be in PATH for spawn
to work. After exec $SHELL, only dirs in rc files are available.
Now ensure_in_path() patches shell rc files for both ~/.local/bin (for
spawn) and ~/.bun/bin (for bun), and correctly checks both when deciding
whether to show "Run spawn" vs "exec $SHELL" instructions.
Fixes#1747
Agent: code-health
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: quote dir in fish_add_path to prevent command injection
Address security review feedback on PR #1748 — unquoted ${dir} in
fish command string could allow injection if HOME/BUN_INSTALL env
vars contain metacharacters.
Agent: code-health
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
apt-get install nodejs npm pulls in hundreds of node-* packages
(libhwasan, node-jsonify, node-eslint-utils, etc.) adding 60-90s
to cloud-init. We immediately replace it with Node 22 via n anyway.
Fix: bootstrap n directly from curl and install Node 22 in one step.
No apt nodejs/npm needed.
Before: apt install nodejs npm → npm install -g n → n 22 (slow)
After: curl n | bash -s install 22 (fast, no apt bloat)
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
spawn uses #!/usr/bin/env bun shebang, so bun must be in PATH for it
to run. When ~/.local/bin isn't in PATH and we symlink spawn into
/usr/local/bin, bun (typically at ~/.bun/bin/bun) isn't reachable,
causing "bun: not found" on first run. Now symlink bun too.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add build-essential to node cloud-init tier
The "node" tier (used by claude, codex, kilocode) was missing
build-essential. Native npm packages that compile C/C++ addons
fail without it. The "full" tier had it but no agent uses "full".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: upgrade openclaw to full cloud-init tier
Openclaw needs the most dependencies (build-essential, nodejs, npm,
bun) but was on the "bun" tier which only installed curl/unzip/git/zsh.
Switch to "full" which includes everything.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`spawn delete` was broken for all non-Fly clouds (hetzner, digitalocean,
gcp, aws, daytona, sprite) because buildDeleteScript sourced
{cloud}/lib/common.sh files that were removed in the TypeScript rewrite.
Fix by removing buildDeleteScript entirely and rewriting execDeleteServer
to call each cloud's TypeScript destroyServer/destroyInstance directly,
following the same pattern already used for Fly.io. All clouds now use
their native TS module for auth + deletion with no shell dependency.
Fixes#1729
Agent: issue-fixer
Signed-off-by: L <6723574+louisgv@users.noreply.github.com>
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: skip /usr/local/bin symlink when ~/.local/bin is already in PATH
Only symlink when actually needed, and never prompt for a password:
- If ~/.local/bin is in PATH, skip symlink (not needed)
- If not, try direct write to /usr/local/bin first
- Fall back to sudo only if passwordless (NOPASSWD, cached creds,
or macOS Touch ID via pam_tid)
- Otherwise patch rc files and show `exec $SHELL`
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: prompt for password as last resort when symlinking spawn
Symlink priority: direct write → passwordless sudo (NOPASSWD/Touch ID)
→ prompt for password → fall back to exec $SHELL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: make spawn immediately available after install on fresh machines
The install script modified PATH internally to find bun, then checked
that same modified PATH to decide where to install and whether to warn.
On a fresh machine, ~/.local/bin wasn't in the user's real PATH, but
the script thought it was — so spawn was installed there with no
warning, and `spawn` was not found after install.
Fix: always install to ~/.local/bin, then symlink into /usr/local/bin
(which is universally in PATH). Also patch shell rc files for future
sessions. If symlinking fails, fall back to showing `exec $SHELL`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: update install.sh tests for new symlink-based install
Remove find_install_dir tests (function removed), update ensure_in_path
tests for symlink + rc-patching behavior, fix section header reference.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: replace [[ ]] with case for macOS bash 3.x compatibility
Addresses security review: [[ ... == */pattern ]] is bash 4.x syntax
that fails on macOS's default bash 3.2. Use POSIX case statements.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DigitalOcean's cloud-init environment doesn't set HOME. Combined
with set -e, any $HOME or ~ reference (bun install, .bashrc writes)
fails with "HOME: unbound variable" and cloud-init silently aborts.
Fixed in both DigitalOcean and Hetzner (same pattern). AWS doesn't
use set -e so is unaffected.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
.join("; ") produced invalid bash: &; after background command,
do; after for, then; after if. Use newline-joined string instead.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 60×5s blind poll loop ("Cloud-init in progress N/60") with
real-time streaming of /var/log/cloud-init-output.log via tail -f
over SSH. Users now see every apt-get, curl, and error as it happens.
Background checker exits as soon as .cloud-init-complete marker
appears. 5min timeout. Brief 30s fallback poll if streaming fails.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agents declare their dependency tier (minimal/node/bun/full), and
cloud-init only installs what's needed. Lightweight agents like
OpenCode and ZeroClaw skip Node.js upgrade, Bun install, and
build-essential — saving 60-90s on boot and eliminating the
DigitalOcean cloud-init timeout.
- Add CloudInitTier type + cloudInitTier field to AgentConfig
- Add shared/cloud-init.ts: tier-to-packages mapping
- Update all 6 clouds (DO, Hetzner, AWS, GCP, Fly, Daytona)
- Bump CLI version to 0.6.8
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: prevent refactor team from closing PRs — security team only
The refactor team-lead incorrectly closed human-created PR #1728,
claiming it touched off-limits files. Multiple fixes:
- Remove pr-maintainer's ability to close PRs via "comment-based triage"
- Add explicit rule: NEVER close or modify human-created PRs
- Reinforce in 5 places: only security team can close PRs
- Only exception: superseding with a replacement PR (must cite number)
- Add guard: only interact with PRs containing `-- refactor/` signature
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add global comment dedup rule for all refactor teammates
Before posting ANY comment on a PR or issue, every teammate must
check for existing `-- refactor/` signatures to avoid duplicate
comments with the same intent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: don't re-prompt name for failed spawns, improve retry hint
- Reuse existing spawn name when rerunning from `spawn list` or
`spawn last` instead of prompting for a new name (#1712)
- Include --name flag in retry command hint when a spawn name
was used, e.g. `spawn claude hetzner --name my-box` (#1709)
- Bump CLI version to 0.6.5
Fixes#1712Fixes#1709
Agent: ux-engineer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* test: add unit tests for buildRetryCommand --name flag
Cover the spawnName parameter added for issue #1709:
- with name and no prompt
- with name and short prompt
- with name and long prompt (prompt-file fallback)
- with undefined/empty name (no --name flag)
- verify --name appears before --prompt in output
Agent: issue-fixer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: quote --name value when spawn name contains spaces
Handle edge case where spawn names may contain spaces or quotes
by properly quoting and escaping the --name flag value.
Agent: issue-fixer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: always quote --name value to prevent shell injection
Always wrap spawn names in double quotes in the retry command hint,
not just when names contain spaces. This prevents shell metacharacters
(;, |, &, etc.) in spawn names from being interpreted if users copy
the displayed retry command.
Agent: issue-fixer
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
shared/common.sh (3852 lines) was dead code — the entire architecture
was rewritten to TypeScript in cli/src/. No agent scripts source it
anymore. The only consumer was github-auth.sh which just needed 4
log functions (now inlined).
Remove 27 test files that spawned ~800+ real bash/bun subprocesses per
run (the root cause of slow bun test). Every shared-common-*.test.ts
file forked a real bash shell per test case to source shared/common.sh.
CLI subprocess tests spawned `bun run index.ts` per assertion. These
were integration tests, not unit tests.
Also removes:
- mock-tests CI job from test.yml (ran test/mock.sh which opens browser)
- Stale plan files referencing deleted infrastructure
- All CLAUDE.md/README.md references to the old lib/common.sh pattern
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When both OAuth flows open browser tabs back-to-back, the user may
reactively close the second tab thinking it's a duplicate. Add a 5-second
pause with a message after DO OAuth completes, only when browser auth
was actually used (skipped for env var / saved token paths).
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Prevent recursive update check during install (SPAWN_NO_UPDATE_CHECK=1)
- Increase fetch timeout from 5s to 10s for slow/cold connections
- Add 1-hour failure backoff to avoid repeated failed update attempts
- Bump CLI version to 0.6.6
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OAuth token validation calls GET /v2/account which requires the
account:read scope. Without it, the token exchange succeeds but
validation fails with 403, falling through to manual token entry.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reuse a single readline interface across prompt() calls instead of
creating and closing a new one each time. In Bun, repeatedly calling
createInterface/close on the same stdin causes the "close" event to
fire immediately on subsequent interfaces, which resolved the prompt
with an empty string before the user could type — triggering "Token
cannot be empty".
Fixes#1707
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace the 1,190-line qa-cycle.sh monolith with the security.sh
pattern: thin bash launcher (qa.sh) + separate .md prompt files for
each run mode.
Three modes:
- quality (schedule/manual): agent team with test-runner, dedup-scanner,
and code-quality-reviewer
- fixtures (reason=fixtures): single-agent API fixture collector
- issue (reason=issues): single-agent issue investigator/fixer
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep workflow_dispatch for manual testing. Re-enable cron when the
QA VM is back online.
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add DigitalOcean OAuth2 flow for automatic token provisioning
Implements the OAuth2 authorization code flow for DigitalOcean as an
alternative to manual API token entry. The flow mirrors the existing
OpenRouter OAuth pattern using Bun.serve() for the local callback.
Changes:
- Add tryDoOAuth() with local Bun.serve callback, CSRF state, and
code-for-token exchange via DO's /v1/oauth/token endpoint
- Add tryRefreshDoToken() for refreshing expired tokens without
re-authorization
- Extend config persistence with refresh_token, expires_at, auth_method
- Modify ensureDoToken() flow: env var -> saved config (with refresh) ->
OAuth browser flow -> manual paste fallback
- OAuth is gated on DO_OAUTH_CLIENT_ID and DO_OAUTH_CLIENT_SECRET env vars
- Add 37 tests covering config persistence, CSRF generation, code
validation, token expiry, URL construction, and feature toggle
- Bump CLI version to 0.6.5
Closes#1715
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: hardcode DO OAuth credentials, remove env var gate
Embed client_id and client_secret as constants (same pattern as gh CLI,
doctl, gcloud). OAuth is now always available — no env vars needed.
Public CLI clients cannot keep secrets confidential; security comes from
the authorization code flow itself (user consent, localhost redirect,
CSRF state, single-use codes).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add droplet:delete scope for spawn delete support
The spawn CLI's destroyServer() calls DELETE /droplets/{id} which
requires the droplet:delete scope. All its required sub-scopes
(droplet:read, regions:read, sizes:read, actions:read, image:read)
were already present.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The npm-global prefix fix from PR #1699 was lost when agent configs
were refactored from gcp/agents.ts into shared/agent-setup.ts. Without
`npm config set prefix ~/.npm-global`, npm install -g uses the system
prefix (/usr/lib/node_modules) which fails with EACCES for non-root
users on GCP.
This also fixes kilocode's postinstall script (postinstall.mjs) which
uses require.resolve() to find @kilocode/cli-linux-x64 — when npm
writes to the system prefix, the postinstall can't write the binary
symlink into the package's bin/ directory.
Fixes#1698
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Delete 5 entirely-duplicate test files and trim 9 others where the same
bash functions were tested identically in multiple places. Every removed
test has a surviving canonical copy — zero coverage lost.
Deleted (all content duplicated elsewhere):
- shared-common-decomposed-helpers.test.ts
- shared-common-oauth-retry.test.ts
- shared-common-oauth-security.test.ts
- shared-common-server-retry.test.ts
- shared-common-token-provider.test.ts
79 files / 38k lines → 74 files / 33k lines
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All clouds now use TypeScript. Convert the last holdout (AWS) from bash
lib fallback to the JS bundle download pattern, then delete all remaining
cloud bash libs and clean up stale test code.
- Convert 6 AWS agent scripts to JS bundle fallback (matching hetzner)
- Delete aws/lib/common.sh and hetzner/lib/common.sh
- Delete orphaned test/fixtures/ovh/
- Stub out dead functions in test/e2e.sh that sourced deleted libs
- Delete 3 test files that only tested cloud bash libs
- Remove dead describe blocks from 3 remaining test files
- Bump CLI version 0.6.3 → 0.6.4
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove `render: shell` from the "What happened?" textarea so users can
paste screenshots, drag & drop files, and use markdown formatting
instead of being forced into a plain-text code block.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move all inline heredoc prompts out of orchestrator scripts into
standalone .md files. Rename discovery-team-prompt.txt to .md.
Orchestrators now read from the .md templates and sed-substitute
placeholders at runtime — same behavior, cleaner separation.
- discovery: .txt → .md rename + reference update
- refactor: 2 prompts (issue + team) extracted to .md
- security: 4 prompts (team-building, triage, review-all, scan) extracted to .md
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: replace python3 with bun/jq in shared scripts (#1697)
Replace python3 -c inline scripting with jq (preferred) and bun -e
fallbacks per project policy. Python is not a declared dependency;
jq and bun are the project's scripting runtimes.
Changes:
- shared/common.sh: Replace all 9 python3 -c calls with jq/bun -e
- shared/key-request.sh: Replace all 4 python3 -c calls with jq/bun -e
- check_python_available: Now checks for jq or bun instead of python3
- Update test expectations for JS semantics (true/false vs True/False,
bracket access vs .get(), null handling)
Fixes#1697
Agent: security-auditor
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: replace eval() with safe property access, rename check_python_available
Security: eliminate eval() from _extract_json_field() — use regex-based
bracket-notation parser to traverse JSON paths safely. The function now
extracts ['key'] and [N] segments from the expression string and
iterates through them, preventing arbitrary code execution.
Also rename check_python_available() → check_json_processor_available()
throughout the codebase (shared/common.sh, local/lib/common.sh, and
tests) since the function now checks for jq/bun, not python3.
Agent: security-auditor
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Automated refactor/discovery agents occasionally run tests from outside
the cli/ directory, where bunfig.toml is not loaded and this preload
never activates. When that happens, HOME stays as the real home dir
(/root on CI), so any subprocess-test-*.txt written by tests leaks
there instead of the sandbox.
Added cleanupStrayTestFiles() which runs both on preload init and on
process exit. This retroactively removes any leftover files from past
runs and prevents accumulation in future ones.
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
No scripts reference this file since local was converted to TypeScript.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agents now open draft PRs immediately after first commit and convert
to non-draft when work is complete, enabling visibility into in-progress
work. Security reviewer skips draft PRs. Stale drafts (7+ days) are
auto-closed. Refactor pr-maintainer picks up stale non-draft PRs (3+ days).
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert gcp, daytona, digitalocean, hetzner, sprite, and local clouds
to use shared/agent-setup.ts and shared/orchestrate.ts, matching the
pattern established by AWS and Fly. Each cloud's agents.ts is now a
~26-line thin wrapper; each main.ts uses runOrchestration().
- Delete gcp/lib/common.sh (406 lines of dead bash code)
- Delete cli/src/fly/oauth.ts and cli/src/fly/ui.ts re-export wrappers
- Fix all fly/oauth and fly/ui imports to use shared/ directly
- Update test thresholds for reduced bash cloud count
- Bump CLI version to 0.6.3
Net reduction: ~2,850 lines removed.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GCP VMs run as a non-root user, so `npm install -g` fails when the npm
prefix points to a system directory. Ensure ~/.npm-global is configured
as the npm prefix before global installs for kilocode, codex, and
openclaw (npm fallback).
Fixes#1698
Agent: code-health
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>