The linter was running in CI with --warn-only, meaning it never
blocked anything — effectively vaporware. This removes --warn-only
to make it a real gate.
Also adds rules for bash 4.0+ features that were documented in
CLAUDE.md but not enforced:
- MC014: readarray/mapfile (bash 4.0+)
- MC015: coproc (bash 4.0+)
- MC016: &>> redirect (bash 4.0+)
- MC017: relative source paths (breaks curl|bash)
- MC018: wait -n (bash 4.3+)
- MC019: declare -g (bash 4.2+)
Excludes .claude/worktrees/ from scanning (temp copies, not
committed code).
Co-authored-by: spawn-bot <spawn-bot@openrouter.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Restructure the repo as a Bun workspace monorepo:
- Move cli/ → packages/cli/
- Create packages/shared/ (@openrouter/spawn-shared) with type-guards and parse utilities
- Add root package.json with workspace configuration
- Update all CLI imports to use @openrouter/spawn-shared
- Deduplicate toRecord/toObjectArray helpers from 4 cloud modules
- Update SPA (slack-bot) to use shared package instead of local toObj()
- Update 48 agent shell scripts for new packages/cli/ path
- Update install.sh, install.ps1, e2e, and test scripts
- Update all GitHub workflows, .gitignore, pre-commit hooks
- Update CLAUDE.md, README.md, and skill prompt references
- Pin all dependency versions (no ^ ranges)
- Bump CLI version 0.9.1 → 0.10.0
All 1908 tests pass. Lint clean. All 8 cloud bundles build.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reorganizes the project so all shell scripts live under a dedicated
/sh directory, enabling the OpenRouter rewrite URL to point at /sh/
instead of the repository root.
Moves:
- cli/install.sh → sh/cli/install.sh
- shared/*.sh → sh/shared/*.sh
- {cloud}/{agent}.sh → sh/{cloud}/{agent}.sh (48 scripts)
- {cloud}/README.md → sh/{cloud}/README.md
- e2e/*.sh → sh/e2e/*.sh
- test/macos-compat.sh → sh/test/macos-compat.sh
- test/fixtures/**/*.sh → sh/test/fixtures/**/*.sh
Updates all references:
- RAW_BASE path construction in commands.ts, update-check.ts
- GitHub auth URL in agent-setup.ts
- Self-referencing URLs in install.sh, github-auth.sh
- CI workflow paths in lint.yml, cli-release.yml
- Test file paths in install-script-validation, manifest-integrity
- Documentation in README.md, cli/README.md, CLAUDE.md
- QA scripts in .claude/skills/
Co-authored-by: lab <6723574+louisgv@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Run `biome format --write` on all 98 source files (38 needed fixes).
The main change: object literals and long argument lists are now expanded
onto separate lines per Biome's `"expand": "always"` setting, making
code much easier to scan on narrow screens.
Add `biome format` check step to CI lint workflow so formatting
regressions are caught on every PR.
Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: Result monad for retry logic — prevent duplicate server creation
SSH exit 255 after an interactive session caused runWithRetries to retry
the entire bash script, creating duplicate servers. The old withRetry
also blindly retried all errors including timeouts where the remote
command may have already completed.
Introduces a Result<T> monad (Ok/Err) so callers explicitly signal
whether a failure is retryable (return Err) or fatal (throw). Adds
wrapSshCall() that classifies SSH errors: transient connection failures
are retryable, timeouts are not. Removes retry loop from the top-level
script runner entirely since it spans server creation + interactive
session.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: mandate draft-PR-first workflow for all changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add biome lint to CI and pre-commit hook, fix lint violations
- Add Biome lint job to .github/workflows/lint.yml
- Add TypeScript lint check to .githooks/pre-commit
- Fix useBlockStatements violations in ui.ts and tests
- Add biome lint to CLAUDE.md "After Each Change" checklist
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: rename Result.value to Result.data
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: clean up stale pre-commit hook
- Remove dead check for deleted functions (write_oauth_response_file,
create_oauth_response_html) — they no longer exist in the codebase
- Fix early exit skipping Biome lint when no .sh files are staged
- Replace echo -e with printf (the hook was using the pattern it bans)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: resolve biome lint errors blocking CI
- Fix useImportType: import { type Result } → import type { Result }
- Fix noUnusedImports: remove unused KNOWN_FLAGS import
- Fix noUnusedTemplateLiteral: template literal → string literal
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>
* feat: add QA upgrade — macOS compat linter, per-agent mock assertions
Layer 1: macOS compat linter (test/macos-compat.sh)
- 12 rules (MC001–MC012) catching bash 3.2 incompatibilities
- Detects: base64 -w0 file args, non-portable echo flags, source <(),
((var++)), read -d, nounset flag, sed -i, date %N, local -n,
declare -A, ${var,,}, and |&
- Added to CI lint.yml in warn-only mode for burn-in
- Integrated as Phase 0.5 in qa-dry-run.sh
Layer 2: Per-agent mock assertions
- test/fixtures/_shared_agent_assertions.sh with install checks
for all 15 agents (claude, openclaw, aider, goose, etc.)
- Integrated into test/mock.sh via _run_agent_assertions()
Also includes branch fixes:
- Fix base64 -w0 to use stdin redirect (aws, daytona, fly)
- Fix fly/openclaw to use npm install instead of broken curl|bash
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add E2E test harness and integrate into QA pipeline
Add test/e2e.sh — a full E2E test harness that provisions real servers,
installs agents, and verifies setup across all clouds. Features:
- Smoke test (one canary agent per cloud) and full matrix modes
- Credential auto-detection for 8 clouds
- Per-cloud preflight validation (sequential) then parallel agent tests
- Stale server cleanup, timing history, cross-cloud comparison
- Auto-fix and optimization phases via Claude agents
- macOS bash 3.2 compatible
Integrate E2E as Phase 5 in both qa-cycle.sh and qa-dry-run.sh:
- Runs after mock tests pass, gated on cloud credentials
- Phase 5b auto-fixes failures using per-agent worktree branches
- Parses results and includes in QA summary
Also fixes:
- shared/common.sh: honour SPAWN_NON_INTERACTIVE=1 in safe_read()
- aws/lib/common.sh: fix SSH key import (use cat instead of base64,
handle race condition on concurrent imports)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Added shellcheck to catch bash anti-patterns across 115 scripts:
- Created .shellcheckrc configuration
- Added GitHub Actions workflow (.github/workflows/lint.yml)
- Documented shellcheck usage in README
Currently found 3,598 warnings (expected for unlinted codebase).
Using || true temporarily to not block PRs - warnings will be fixed
incrementally in follow-up tasks.
Common issues: SC2250 (missing braces), SC2162 (read without -r),
SC2312 (command substitution masking), SC1091 (sourcing pattern).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>