Your bin/setup got you this far. Bivvy takes it the rest of the way — with idempotency, change detection, shared configs, and CI parity baked in.
Setup scripts blindly re-run everything. Bivvy's completed_check detects what's already done and skips it. Safe to run a hundred times.
watches tracks your lockfiles. When Gemfile.lock changes, the Ruby step re-runs. When it hasn't, skip. No wasted time.
extends: lets your infra team publish a base config. Every repo inherits it. When the base updates, every project picks it up.
| Capability | Bivvy | bin/setup |
|---|---|---|
| Idempotent | Yes — completed_check skips satisfied steps | Rarely — most scripts re-run everything |
| Change detection | watches detects changed lockfiles | None — runs the same regardless |
| Shared across repos | extends: inherits from shared configs | Each repo has its own script |
| CI support | Same config, --non-interactive flag | Separate workflow file maintained in parallel |
| Error handling | depends_on lets independent steps continue | Stops on first failure |
| Discoverability | bivvy status and bivvy list | Read the script source |
| Template reuse | Built-in and remote template libraries | Copy-paste between repos |
| Interactive recovery | Retry, fix, skip, or debug on failure | Script exits, figure it out |
Every project starts with a fresh bin/setup. Six months later, someone adds a new dependency and forgets to update the script. The README says one thing, the script does another, and CI has its own version of reality.
Bivvy's declarative config is the single source of truth. bivvy status shows what's satisfied and what's not — on any machine, at any time.
❯ bivvy status [✓] brew 2 hours ago [!] ruby stale (Gemfile.lock changed) [✓] node 2 hours ago [✓] docker running [pending] db not yet run
A setup script says "run these commands in order." Bivvy says "ensure this state exists." The difference matters when you run it the second time, the tenth time, or after pulling new changes.
completed_check means bivvy knows whether a step's already done. watches means it knows when something changed. The result: fast, correct re-runs instead of "just run the whole thing again."
steps: ruby: template: bundler watches: [Gemfile.lock] completed_check: command: "bundle check" node: template: yarn watches: [yarn.lock] db: command: "rails db:prepare" depends_on: [ruby, docker]
You don't have to rewrite anything. Run bivvy init in your project and bivvy detects your stack, generates a config from built-in templates, and works immediately.
Your existing setup logic becomes declarative steps with completion checks. Same outcome, better ergonomics. And once it works for one repo, extends: lets you share the pattern across your entire org.
❯ bivvy init ⛺ bivvy init # Detecting project... ✓ Ruby 3.2.2 (bundler) ✓ Node 20.11.0 (yarn) ✓ PostgreSQL (docker-compose) ✓ Created .bivvy/config.yml 3 steps configured from templates Run bivvy run to set up.
Bivvy is what your setup script would be if someone designed it as a product instead of a one-off script.