Install
Install the CLI from npm (Node.js 22 or later):tsx loader rather than a build):
--setup turns the plan into credentials you can run it with, in one pass in your own terminal. For each provider the
plan uses it shows the page where the token is created and the least it needs, takes the token from the environment
or asks for it (hidden input, never written to disk), and checks it with one read-only request. Then, after a yes for
each, it stores the checked tokens as GitHub Actions secrets of your origin with your own gh (the value goes to
gh secret set on stdin, nowhere else), and writes .github/workflows/sponson.yml with the action pinned to the
commit of the installed version’s release tag (an existing file is never overwritten). TODOs the plan still has (a
placeholder such as a project id init could not find) are listed first, and the workflow is not offered until they
are filled in: it would run a plan that cannot work. A default the plan runs with but you should look at (a recipe’s
documented default, such as Stripe’s webhook path, marked # CHECK:) is listed as “please check” and blocks nothing.
Run it again any time, also on an existing plan; --yes accepts every offer for scripted use, --json reports what
init detects (todo and check), each credential’s status without a value, and next: { command, why }, where
command is the command to run next (sponson plan once ready, sponson init --setup when a token only needs to be
typed) or null when the next step is not a command (filling in a TODO, making a new token), and why says what to do.
Agents ask a human to run it rather than handle a token themselves (SKILL.md).
<version> is the installed version; the commit is the one its release tag points at.
Where each provider’s token is created and what to pick is listed per provider in docs/providers.md.
sponson init reads the repository’s files only (no network, no credentials): .vercel/project.json and
vercel.json, netlify.toml and .netlify/state.json, package.json dependencies, Prisma and Drizzle configs,
.neon, and the variable names in
.env.example, .env.local and the other .env* files. It never reads a value out of them, except to recognise a
database host such as *.neon.tech. It writes a line only for what it found (one database: a Neon branch, a PlanetScale branch and
password, or a Supabase preview branch, in that order of preference when several are detected; the Vercel or Netlify preview
variable that references it under the name your code reads; a Clerk redirect or a Supabase Auth redirect for the
preview URL), fills in the project ids it can find, and marks each one it cannot with a TODO comment saying where to
look (vercel link, netlify link, neonctl projects list, supabase link). LaunchDarkly gets a commented example line to fill in with a
flag key and uncomment. A Wrangler configuration (wrangler.toml, wrangler.json) gives a Cloudflare Pages
variables line with the project’s name; Pages preview variables are shared by every preview, so no per-PR value
is wired into them. A provider managed by recipes gets its recipe’s line, ready when the plan has
everything it needs and commented out with a TODO otherwise. With nothing detected it writes the Vercel + Neon
template. Besides the plan, init writes one line, .sponson/ (where local receipts go), into .gitignore.
A service init detects that no adapter or recipe manages yet is listed as “not supported yet”, with where to ask for it (or why no API can manage it), and never silently dropped: today Pusher Channels and Liveblocks.
# TODO comment is gone too: until then it counts as a placeholder. sponson init --json reports the same under detected (also on a re-run with --setup, where todo is what the plan still has):
found and unsupported (each with its evidence), todo (each placeholder’s path and hint) and assumed (true
when the plan starts from the template). Paths are relative to the working directory in text and absolute in JSON.
Which providers are covered today, and what is planned, is measured in docs/coverage.md.
Beyond the built-in adapters, any JSON REST API is reachable with the generic http adapter, and the providers it
has been verified against ship as recipes: one plan line names a provider operation and fills in
its params, for example recipe: cloudflare.dns_cname with a zone_id, a name and a target. Each recipe cites the
API docs it was checked against and runs through create, re-apply, drift and destroy in the test suite.
Third-party adapters and secret sources load as plugins: SPONSON_PLUGINS=sponson-adapter-x,./local-adapter.mjs, each module exporting register(registry).
Credentials come from the providers’ own conventions (VERCEL_TOKEN for Vercel; every adapter’s variable is listed under built-in ops). Sponson has no credential store of its own: sponson init --setup checks them and hands them to GitHub’s.
In GitHub Actions, one workflow with three triggers calls the same action: