CLI

Fat package, thin wiring.

The logic lives in the packages and upgrades with them. What lands in your repository is thin enough to read in a sitting — a config file, a wrangler file, and a mount. And every command is drivable by an agent.

Add a capability, and the wiring is done.

It installs with your project's own package manager, reads the real manifest out of node_modules rather than trusting a catalog, resolves what the capability composes against as a graph, and writes the bindings into every environment stanza your wrangler file declares.

terminal
$ pithy add payments --with-prerequisites
▸ secrets  installed, wired, migrated
▸ auth     installed, wired, migrated
▸ payments installed, wired, migrated
▸ wrangler.jsonc  updated: staging, prod
Done.

Every command speaks JSON, so an agent can drive it.

This is the part most CLIs bolt on later and never finish. Every command supports --json, with structured errors that name the failure and the fix — so a coding agent can install a capability, read what happened, and decide what to do next without parsing prose.

pithy add auth --json
{
  "error": {
    "code": "cli/prerequisites_missing",
    "message": "auth requires secrets and email, which myapp does not compose.",
    "action": "Run pithy add auth --with-prerequisites, or compose them first."
  }
}

A whole environment per branch, and then gone.

One command cuts a git worktree and its branch, reserves a port block so several features run at once, installs, migrates and seeds a local backend. A second provisions that branch its own ephemeral Cloudflare resources — its D1, its KV, its R2, its own master key.

Every name is derived from the branch, so an already-provisioned resource is recovered by looking it up rather than by anything stored. That is what makes it idempotent, resumable, and safe to run from CI. And the ids land in a build artifact, never in your tracked config, so git add -A cannot commit them and a branch abandoned without teardown strands nothing.

terminal
$ pithy feature create checkout-flow --issue 214
▸ worktree   .worktrees/214-checkout-flow
▸ branch     feature/214-checkout-flow
▸ ports      reserved 8110-8119
▸ backend    migrated, seeded

$ cd .worktrees/214-checkout-flow
$ pithy provision --feature
▸ d1         myapp-214-checkout-flow
▸ kv         myapp-214-checkout-flow-sessions
▸ r2         myapp-214-checkout-flow-uploads
Done.

Which is exactly the shape an agent needs.

An isolated worktree, a real environment, and structured output on every command is the loop a coding agent already wants: take an issue, get a branch and infrastructure of its own, build, deploy a preview, and tear the whole thing down when the pull request closes.

Teardown deletes by recomputed name regardless of what anyone recorded, so a half-finished run cleans up too. Put the same two commands in CI and every pull request gets a real environment rather than a shared staging everyone queues for.

.github/workflows/preview.yml
$ pithy feature sync --json
$ pithy provision --feature --json
$ pithy deploy --env feature --json

# and when the pull request closes
$ pithy feature destroy --json

One command refuses to be automated, on purpose.

pithy remove is destructive, so it is interactive only. Passing --json fast-fails with a clear error before anything changes, and its confirmations are typed at a real terminal.

Tearing down an ephemeral environment is a different command, because that is a different intent.

terminal
$ pithy remove payments --drop --json
error  remove is interactive only; --json is refused.
       Nothing has changed.
       Run it in a terminal, or use the feature lifecycle
       to tear down an environment.

It refuses rather than guessing.

In a terminal it asks one question for the whole cascade. With --with-prerequisites it proceeds. Anything non-interactive exits and names the exact commands to run, in order — and nothing is wired.

Composing something nobody asked for is not a thing to do behind your back, and reporting success on a project that cannot boot is worse.

terminal
$ pithy deploy --env prod
▸ typecheck  ok
▸ test       ok
▸ migrate    3 applied
▸ deploy     myapp-api → prod
Done.

The surface

Everything it does.

Start

init · add · remove · worker · ui · alias

Run

dev · migrate · seed · deploy · env · doctor

Operate

secrets · token · provision · feature · upgrade · dashboard

Per capability

payments · email · storage · media · vector · support · testers · turnstile

Every one of them takes --json, except the one that deletes things.

One command, and the wiring is done.

Install a capability, wire its bindings, run its migrations. Then deploy.