Skip to main content
Both commands are local: no cloud, no build, no model. They exist because the same loop through a real ship is minutes long, and an agent authoring a config needs a loop it can actually iterate in.

bay init

Reads the repository and writes a draft supersonic.json — the monorepo split, the install command from the lockfile, the build command and output directory, the start command bound to $PORT, the runtime version the manifests ask for, and the framework. Then it prints what it could not determine:
It refuses to overwrite an existing config without --force.

bay check

Resolves and validates the config exactly as a deploy would, and prints, per service, the command each phase runs. Non-zero exit on any problem.

Why it is the control plane’s own resolver

Both commands go through a bundle of apps/web/lib/{resolve,app-config, infer-services,repo-facts,lanes,plan-deps}.ts — the control plane’s actual resolver, compiled into the package, not a port of it. A test fails on a stale bundle. That matters because a committed detector once spent two days answering python:3.12 after the runner had moved to 3.14. A second implementation is a second thing to be wrong.