spawn/.claude/rules/shell-scripts.md
Ahmed Abushagur 4b62e8735f
chore: rename moved repos (OpenRouterTeam→OpenRouterLabs, sst→anomalyco) (#3445)
* chore: rename moved repos OpenRouterTeam→OpenRouterLabs and sst/opencode→anomalyco

Pure mechanical find/replace across the tree — both repos were renamed on
GitHub and only resolved via 301 redirects (fragile; hijack risk if the old
names are re-registered).

- OpenRouterTeam/spawn → OpenRouterLabs/spawn (141 refs): hardcoded REPO
  consts (manifest.ts, agent-tarball.ts), release-download URLs in every
  sh/{cloud}/{agent}.sh, gh/raw/asset URLs, docs, tests, fixtures.
- sst/opencode → anomalyco/opencode (5 refs): manifest, README, agent-setup.ts
  download cmd, opencode.Dockerfile.

No behavior change. biome clean (205 files). CLI version 1.1.0 → 1.1.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(spawn): deflake cmdRun + hetzner tests (disable telemetry in tests, route hetzner mock by endpoint)

* test(spawn): deflake cmdRun + hetzner tests (disable telemetry in tests, route hetzner mock by endpoint)

* test(spawn): deflake cmdRun + hetzner tests (disable telemetry in tests, route hetzner mock by endpoint)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: spa-the-spawn-maintainer[bot] <286559366+spa-the-spawn-maintainer[bot]@users.noreply.github.com>
2026-06-03 07:29:14 +00:00

2.7 KiB

Shell Script Rules

These rules are non-negotiable — violating them breaks remote execution for all users.

curl|bash Compatibility

Every script MUST work when executed via bash <(curl -fsSL URL):

  • NEVER use relative paths for sourcing (source ./lib/..., source ../shared/...)
  • NEVER rely on $0, dirname $0, or BASH_SOURCE resolving to a real filesystem path

macOS bash 3.x Compatibility

macOS ships bash 3.2. All scripts MUST work on it:

  • NO echo -e — use printf for escape sequences
  • NO source <(cmd) inside bash <(curl ...) — use eval "$(cmd)" instead
  • NO ((var++)) with set -e — use var=$((var + 1)) (avoids falsy-zero exit)
  • NO local keyword inside ( ... ) & subshells — not function scope
  • NO set -u (nounset) — use ${VAR:-} for optional env var checks instead

Conventions

  • #!/bin/bash + set -eo pipefail (no u flag)
  • Use ${VAR:-} for all optional env var checks (OPENROUTER_API_KEY, cloud tokens, etc.)
  • Primary script URL: https://openrouter.ai/labs/spawn/{path} (CDN proxy, maps to repo sh/, e.g., {cloud}/{agent}.sh)
  • Fallback script URL: https://raw.githubusercontent.com/OpenRouterLabs/spawn/main/sh/{path}
  • Version check URL: https://github.com/OpenRouterLabs/spawn/releases/download/cli-latest/version (GitHub release artifact)
  • All env vars documented in the cloud's sh/{cloud}/README.md

Use Bun + TypeScript for Inline Scripting — NEVER python/python3

When shell scripts need JSON processing, HTTP calls, crypto, or any non-trivial logic:

  • ALWAYS use bun -e '...' or write a temp .ts file and bun run it
  • NEVER use python3 -c or python -c for inline scripting — python is not a project dependency
  • Prefer jq for simple JSON extraction; fall back to bun -e when jq is unavailable
  • Pass data to bun via environment variables (e.g., _DATA="${var}" bun -e "...") or temp files — never interpolate untrusted values into JS strings
  • For complex operations (SigV4 signing, API calls with retries), write a heredoc .ts file and bun run it

ESM Only — NEVER use require() or CommonJS

All TypeScript code in packages/cli/src/ MUST use ESM (import/export):

  • NEVER use require() — always use import (static or dynamic await import())
  • NEVER use module.exports — always use export / export default
  • NEVER use createRequire — it's a CJS compatibility hack that triggers Bun bugs
  • The project is "type": "module" in package.json — CJS is not supported
  • For Node.js built-ins: import fs from "fs", import path from "path", etc.
  • For dynamic imports: const mod = await import("./module.ts")