Référence de la CLI bo
bo est l’outil de développement du dépôt : base de données, serveurs, tests, captures. Chaque commande accepte --help et --json ; club, platform, stripe connect et stripe reconcile acceptent aussi --target staging (la commande tourne sur la machine de staging, voir Déploiement). Page générée : elle liste exactement les commandes du code.
bo doctor
Section intitulée « bo doctor »Check the machine + show this worktree’s env (ports, db)
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
Databases: one per worktree, on the shared local Postgres
bo db up
Section intitulée « bo db up »Start the local Postgres and the Mailpit dev inbox (shared containers)
bo db migrate
Section intitulée « bo db migrate »Create the worktree DB if needed and apply pending SQL migrations
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
bo db status
Section intitulée « bo db status »Show this worktree’s database and pending migrations
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
bo db reset
Section intitulée « bo db reset »Drop this worktree’s database and test databases (then bo db migrate + bo e2e setup)
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
--yes |
boolean |
bo db psql
Section intitulée « bo db psql »Interactive psql on this worktree’s database (–app for the RLS role)
| Option | Type | Rôle |
|---|---|---|
--app |
boolean | connect as the RLS-subject app role |
bo db url
Section intitulée « bo db url »Print the connection URL (owner by default, –app for the RLS-subject role)
| Option | Type | Rôle |
|---|---|---|
--app |
boolean |
The end-to-end dataset (specs, journeys, screenshots)
bo e2e setup
Section intitulée « bo e2e setup »Reset this worktree’s bo and demo clubs to the e2e dataset (also the dev one): the bo preset plus named test people (known password), bookings, money. Local only
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
bo e2e durations
Section intitulée « bo e2e durations »Write e2e/durations.json (seconds per spec, from the latest green bo ci main, or an e2e.yml run’s blob reports): the e2e pick balances shards by it
| Option | Type | Rôle |
|---|---|---|
--run |
string | an e2e.yml run id whose blob reports are still kept (default: the latest green bo ci main, else main’s latest green e2e.yml run that has them) |
--json |
boolean | Machine-readable output |
Run api, worker, mcp, desk, player on this worktree’s ports (–only api,desk). Ctrl-C or bo dev down stops.
| Option | Type | Rôle |
|---|---|---|
--only |
string | comma-separated: api,worker,mcp,desk,player |
--club |
string | dev only: this club answers on the bare localhost too (MCP clients can OAuth over http) |
bo dev down
Section intitulée « bo dev down »Stop this worktree’s dev servers, including orphans left by a closed terminal
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
bo dev status
Section intitulée « bo dev status »Which dev servers listen on this worktree’s ports, with their URLs, plus the shared docker services
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
Tests. bo test all is the pre-commit gate.
bo test all
Section intitulée « bo test all »biome ci → typecheck → unit → e2e → site check, stop at first failure (CI: bo ci, the same steps on a merge tree, docs/review.md § 2)
bo test lint
Section intitulée « bo test lint »biome ci: lint + format check, a warning fails it, read-only
bo test typecheck
Section intitulée « bo test typecheck »tsc in every package (turbo, cached)
bo test unit
Section intitulée « bo test unit »Vitest in packages and apps (domain, api auth, mcp OAuth vs this worktree’s test DB; then desk + player model/hook tests + stories in Chromium, after the DB suites, not alongside)
| Option | Type | Rôle |
|---|---|---|
--only |
string | only these packages, comma-separated short names (e.g. desk,player) |
--skip |
string | every package but these (e.g. desk,player) |
--at |
string | run with the tests’ clock moved to this date, e.g. 2027-11-05 (JS clock only: Postgres now() is unchanged, #630) |
bo test e2e
Section intitulée « bo test e2e »Playwright against api + desk on this worktree’s ports (re-seeds first)
| Option | Type | Rôle |
|---|---|---|
--headed |
boolean | |
--shard |
string | run one part of the spec files, e.g. 2/4 (Playwright –shard) |
--only |
string | only these spec files, comma-separated names (e.g. booking,money for tests/booking.spec.ts…) |
--bail |
boolean | stop at the first failed test |
Local CI (replaces the GitHub workflows): the candidate (the PR squashed onto origin/main) in a throwaway worktree, fresh install + databases, ci.yml’s jobs, e2e (AI pick, cache, failed-first, shards, a red spec once more alone; main: full suite), a Claude Code review once per PR (OCR with a Console key); posts the gate + merge-ready statuses and a PR comment. A tree a green run tested today on the same main isn’t run again. One run at a time on this machine (FIFO, main first)
| Argument | Type | Rôle |
|---|---|---|
target |
string | a PR number, or main (default: this branch’s PR) |
| Option | Type | Rôle |
|---|---|---|
--review |
boolean | review again even if this PR was reviewed already (after a fix or a rebase) |
--no-review |
boolean | skip the review |
--no-cache |
boolean | no cache: every picked e2e spec, even those that passed before on identical inputs, and every unit/stories suite (TURBO_FORCE) |
--keep |
boolean | keep the run’s worktrees and databases (debugging) |
--json |
boolean | Machine-readable output |
bo ci status
Section intitulée « bo ci status »What bo ci runs now, what waits, the merge queue, the last runs
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
bo merge
Section intitulée « bo merge »Merge a PR through the local merge queue: its own bo ci green on its head, then it waits its turn; the queue squashes up to 4 queued PRs onto main as one candidate, bo ci tests the tip (a tree already tested isn’t run again) and main fast-forwards to it. Red: half the batch next time; a PR red alone leaves the queue with a comment. Closes each PR « Merged as
| Argument | Type | Rôle |
|---|---|---|
pr |
integer | the PR to merge (none: drain what is queued) |
| Option | Type | Rôle |
|---|---|---|
--ci-main |
boolean | run bo ci main once the queue is drained (the full suite, the daily net) |
--withdraw |
boolean | take the PR out of the merge queue instead (Ctrl-C on a waiting bo merge leaves it queued) |
--json |
boolean | Machine-readable output |
Talk to this worktree’s api
bo api query
Section intitulée « bo api query »Run a GraphQL query against this worktree’s api, signed in as the club owner (dev session)
| Option | Type | Rôle |
|---|---|---|
--query |
string | |
--as |
string | acting user: owner | none | bo auth login) |
--club |
string | |
--json |
boolean |
bo api codegen
Section intitulée « bo api codegen »After a GraphQL schema change: write apps/api/schema.graphql and regenerate apps/{desk,player}/src/gql
Dev identities (Better Auth sessions on club hosts)
bo auth login
Section intitulée « bo auth login »Dev session for a user on a club host: cookie for scripts + a sign-in URL for the browser
| Argument | Type | Rôle |
|---|---|---|
email |
string | user to sign in as (default: list the club’s identities) |
| Option | Type | Rôle |
|---|---|---|
--club |
string | |
--json |
boolean |
bo stripe
Section intitulée « bo stripe »Stripe helpers: test mode, nightly reconciliation by hand
bo stripe listen
Section intitulée « bo stripe listen »stripe listen –latest → this worktree’s /webhooks/stripe (checkout + account.updated, test mode)
bo stripe connect
Section intitulée « bo stripe connect »Make a club chargeable (–target staging: the box’s sandbox): attach (–account) or create the sandbox’s Standard account (audited when the club’s account changes), wait for the terms click
| Option | Type | Rôle |
|---|---|---|
--club |
string | club slug |
--account |
string | attach this existing Standard account (acct_…) instead of creating one |
--email |
string | the email of an account this command creates (default: the club’s contact email); Stripe mails it |
--replace |
boolean | a new account for a club whose account never went live (not the shared BO_STRIPE_TEST_ACCOUNT); the old one is deleted from the sandbox once the new one is attached |
--no-wait |
boolean | print the terms link and stop (the row stays pending) |
--json |
boolean |
bo stripe reconcile
Section intitulée « bo stripe reconcile »Compare each club’s Stripe charges with our card payments: record a charge of ours left unrecorded, keep any other difference (what the worker does nightly). –target staging
| Option | Type | Rôle |
|---|---|---|
--at |
string | run as of this instant (ISO, e.g. 2026-12-01T06:00+01:00); default now |
--club |
string | one club (slug) |
--days |
integer | look back this many days (default 3, at most 90) |
--json |
boolean |
Design-pass tooling
bo ui shots
Section intitulée « bo ui shots »Screenshot every route of an app (light + dark) into demos/ui/
| Option | Type | Rôle |
|---|---|---|
--app |
string | |
--out |
string |
bo ui grid
Section intitulée « bo ui grid »Upload this branch’s shots (+ –before) to an unlisted gist and print the PR’s before/after grid
| Option | Type | Rôle |
|---|---|---|
--app |
string | |
--before |
string | shots of the screens before the change (a bo ui shots --out dir) |
--yes |
boolean | confirm the upload: the gist is unlisted, anyone with its URL sees the shots |
bo ui storybook
Section intitulée « bo ui storybook »Storybook of an app (screens on a fake client + @booker/ui) on this worktree’s STORYBOOK_PORT (desk), STORYBOOK_PLAYER_PORT (player) or STORYBOOK_SITE_PORT (site); –app all runs the three in the desk’s sidebar; –build makes the static composed one (dist/design) for bo deploy design
| Option | Type | Rôle |
|---|---|---|
--app |
choice | (choices: desk, player, site, all) |
--build |
boolean | build the static Storybook of the three apps, composed, into dist/design (what bo deploy design uploads); –app is ignored |
Clubs (tenants). –target staging: on the box
bo club create
Section intitulée « bo club create »Create a club: legal details (required), subdomain, one site with default opening hours, optional owner (audited, invited by email)
| Argument | Type | Rôle |
|---|---|---|
slug |
string | subdomain: |
| Option | Type | Rôle |
|---|---|---|
--name |
string | default: the slug |
--owner |
string | owner’s email (user created if new); –target staging: owner. |
--primary |
string | #rrggbb |
--accent |
string | #rrggbb |
--legal-name |
string | raison sociale, e.g. « Bø Sports SAS » |
--siret |
string | 14 digits |
--legal-address |
string | siège social |
--preset |
choice | bo: Bø: 7 padel, 4 squash, 2 badminton · pickleball, its prices and formules · padel: Padel only: 4 indoor courts, 9h–22h, one price · tennis: Tennis only: 2 clay outside, 2 covered, hourly, a yearly membership · squash: Squash only: 3 courts, 45-min créneaux, a monthly unlimited · demo: the bo preset + ~90 fictitious players (@ |
--test-password |
boolean | the owner starts with BO_TEST_ACCOUNT_PASSWORD (env file; local or staging only; a password it has is kept). Test accounts only |
--json |
boolean |
bo club invite-owner
Section intitulée « bo club invite-owner »Invite a club’s owner by email (audited): the link sets a first password, never resets one; –test-password –reset replaces this account’s
| Argument | Type | Rôle |
|---|---|---|
slug |
string | subdomain: |
email |
string | the owner’s email |
| Option | Type | Rôle |
|---|---|---|
--test-password |
boolean | the owner starts with BO_TEST_ACCOUNT_PASSWORD (env file; local or staging only; a password it has is kept). Test accounts only |
--reset |
boolean | with –test-password: replace the password this account already has (the account named here only) |
--json |
boolean |
bo club setup
Section intitulée « bo club setup »Set up a club without courts from a preset: site, hours, sports, courts, prices, formules (audited); demo also fills it with fictitious players and activity
| Argument | Type | Rôle |
|---|---|---|
slug |
string | subdomain: |
| Option | Type | Rôle |
|---|---|---|
--preset |
choice | bo: Bø: 7 padel, 4 squash, 2 badminton · pickleball, its prices and formules · padel: Padel only: 4 indoor courts, 9h–22h, one price · tennis: Tennis only: 2 clay outside, 2 covered, hourly, a yearly membership · squash: Squash only: 3 courts, 45-min créneaux, a monthly unlimited · demo: the bo preset + ~90 fictitious players (@ |
--json |
boolean |
bo club test-accounts
Section intitulée « bo club test-accounts »One account per desk role besides the owner (staff, coach), members without an invitation and signing in with BO_TEST_ACCOUNT_PASSWORD (audited; local or staging only). Test accounts only
| Argument | Type | Rôle |
|---|---|---|
slug |
string | subdomain: |
| Option | Type | Rôle |
|---|---|---|
--reset |
boolean | replace the password these accounts already have |
--json |
boolean |
bo platform
Section intitulée « bo platform »Platform admin (no UI yet). –target staging: on the box
bo platform clubs
Section intitulée « bo platform clubs »List every club with member and verified custom-domain counts
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
bo platform change-email
Section intitulée « bo platform change-email »Change a user’s sign-in email, for every club they belong to (audited): password, sessions and memberships stay, the new address is unverified, pending invitations follow
| Argument | Type | Rôle |
|---|---|---|
from |
string | the user’s current email |
to |
string | the new email (must be free) |
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
bo platform grant-admin
Section intitulée « bo platform grant-admin »Make an existing user a platform admin: every club’s desk with the owner’s rights (audited); –test-password for a test admin
| Argument | Type | Rôle |
|---|---|---|
email |
string | an existing user’s email (signed in once, or invited and claimed); with –test-password, made if new |
| Option | Type | Rôle |
|---|---|---|
--test-password |
boolean | a test admin (staging’s « comptes de test » page): created if new, signs in with BO_TEST_ADMIN_PASSWORD (local or staging only; never an existing account without a password or a club member’s; –reset replaces its password) |
--reset |
boolean | with –test-password: replace the password this account already has (the account named here only) |
--json |
boolean |
bo feedback
Section intitulée « bo feedback »Feedback notes from the clubs (the desk’s toolbar; the player app’s form comes with RBD-197; COLLECT_FEEDBACK). –target staging: on the box
bo feedback list
Section intitulée « bo feedback list »Feedback notes sent from the desks and player apps, newest first (open ones by default; audited)
| Option | Type | Rôle |
|---|---|---|
--status |
choice | open (default), handled or all (choices: open, handled, all) |
--club |
string | one club’s slug |
--limit |
integer | |
--json |
boolean |
bo feedback show
Section intitulée « bo feedback show »One Feedback note, ready to paste to an agent: who, when, URL, release, each element pointed at with its test id and comment
| Argument | Type | Rôle |
|---|---|---|
id |
string | the note’s id (bo feedback list) |
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
bo feedback handled
Section intitulée « bo feedback handled »Mark a Feedback note handled, once: it leaves bo feedback list
| Argument | Type | Rôle |
|---|---|---|
id |
string | the note’s id (bo feedback list) |
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
bo feedback file
Section intitulée « bo feedback file »File a Feedback note as a Linear issue now, once (a note sent while filing was off, or whose job gave up; audited)
| Argument | Type | Rôle |
|---|---|---|
id |
string | the note’s id (bo feedback list) |
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
bo import
Section intitulée « bo import »Import a CSV/XLSX export (preview; –apply to write) through the api as the club owner. docs/import.md
| Argument | Type | Rôle |
|---|---|---|
entity |
choice | what the file holds (recommended order: resources tariffs memberships entitlements wallets bookings) |
file |
string | CSV or XLSX export of the previous software |
| Option | Type | Rôle |
|---|---|---|
--apply |
boolean | write to the club (default: preview only) |
--template |
boolean | print the CSV template of |
--source |
string | previous software: the column mapping is saved per source |
--map |
string | column mapping overrides: “lastName=Nom client,email=Mail” (field keys: –template) |
--club |
string | |
--as |
string | acting owner: owner (the club’s) | |
--json |
boolean |
bo emails
Section intitulée « bo emails »Email templates (react-email, packages/emails): preview and export
bo emails preview
Section intitulée « bo emails preview »react-email preview server on this worktree’s EMAILS_PORT: every message, Bø + demo, fr + en
bo emails export
Section intitulée « bo emails export »Static HTML + text + 360 px light/dark shots of every message into demos/emails/
| Option | Type | Rôle |
|---|---|---|
--out |
string | output dir (default demos/emails/ |
--json |
boolean |
The Mailpit dev inbox (everything bo dev and e2e send)
bo mail last
Section intitulée « bo mail last »Latest message in the Mailpit dev inbox (–to, –since, –subject, –wait)
| Option | Type | Rôle |
|---|---|---|
--to |
string | recipient email or phone |
--since |
string | ignore messages before this ISO time |
--tag |
string | only what one worktree sent (its db name, e.g. booker_main) |
--subject |
string | only messages whose subject contains this text |
--wait |
integer | seconds to wait for a matching message (the worker sends async) |
--json |
boolean |
The public site + docs (apps/site, Astro + Starlight, ADR-0013)
bo site dev
Section intitulée « bo site dev »Astro dev server for apps/site on this worktree’s SITE_PORT
bo site build
Section intitulée « bo site build »Static build of the site into apps/site/dist (shots/journeys if generated)
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
bo site check
Section intitulée « bo site check »Site gate: docs coverage, astro check, build, internal links, axe + smoke on the built pages
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
bo site shots
Section intitulée « bo site shots »bo ui shots for desk + player and the journeys, into apps/site/src/generated (needs bo dev)
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | |
--part |
choice | only one part: desk shots, player shots or the journeys (choices: desk, player, journeys) |
bo site journeys
Section intitulée « bo site journeys »Run the site’s journeys on the real app (e2e spec) and capture light/dark stills + a webm per journey
| Option | Type | Rôle |
|---|---|---|
--only |
string | one journey id (e.g. reserver-un-terrain) |
--json |
boolean |
bo site publish
Section intitulée « bo site publish »Run site.yml on main (captures, build, links, Netlify deploy when SITE_PUBLISH is on); CI never runs it on its own
| Option | Type | Rôle |
|---|---|---|
--json |
boolean |
bo site media
Section intitulée « bo site media »Put a changelog video on the site’s media bucket (R2): web-sized H.264, a poster, a content-hashed name; prints the entry’s frontmatter
| Argument | Type | Rôle |
|---|---|---|
file |
string | the rendered video (mp4, mov…) |
| Option | Type | Rôle |
|---|---|---|
--name |
string | the slug, kebab-case: court-sponsors → changelog/court-sponsors- |
--dry-run |
boolean | encode and name only, upload nothing (files kept in the temp folder printed) |
--json |
boolean |
bo site cli-docs
Section intitulée « bo site cli-docs »Write the site’s CLI reference page from the bo command tree
| Option | Type | Rôle |
|---|---|---|
--check |
boolean | fail if the committed page differs (CI) instead of writing it |
--json |
boolean |
bo subscriptions
Section intitulée « bo subscriptions »Subscriptions (ADR-0012): the renewal engine by hand, the support list
bo subscriptions tick
Section intitulée « bo subscriptions tick »Run the renewal engine once (what the worker does every 15 min): renew, collect, retry, suspend, notices
| Option | Type | Rôle |
|---|---|---|
--at |
string | run as of this instant (ISO, e.g. 2026-12-01T06:00+01:00); default now |
--club |
string | one club (slug) |
--simulate-card |
choice | dev: answer off-session card charges locally instead of calling Stripe (nothing leaves the machine) (choices: succeed, decline, requires_action) |
--json |
boolean |
bo subscriptions list
Section intitulée « bo subscriptions list »Support: a club’s subscriptions (default bo), filtered by –status
| Option | Type | Rôle |
|---|---|---|
--club |
string | one club (slug) |
--status |
choice | all · active · unpaid · suspended · ending (choices: all, active, unpaid, suspended, ending) |
--json |
boolean |
bo money
Section intitulée « bo money »Money jobs by hand: check the money rules, recover paid checkouts
bo money check
Section intitulée « bo money check »Check every club’s money rules (the worker does it nightly and records a failing club); exits non-zero on a violation
| Option | Type | Rôle |
|---|---|---|
--club |
string | one club (slug) |
--json |
boolean |
bo money recover-checkouts
Section intitulée « bo money recover-checkouts »Record hosted checkouts paid at Stripe whose webhook never came (what the worker does every 15 min)
| Option | Type | Rôle |
|---|---|---|
--at |
string | run as of this instant (ISO, e.g. 2026-12-01T06:00+01:00); default now |
--club |
string | one club (slug) |
--json |
boolean |
bo deploy
Section intitulée « bo deploy »The deployed environment: one box, Docker Compose over ssh (ADR-0016)
bo deploy init
Section intitulée « bo deploy init »Create (or find) the box, its firewall, backups and the
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
bo deploy up
Section intitulée « bo deploy up »Build this checkout on the box, apply migrations, restart what changed, wait for the api’s health, then remove our other images (keeps this release and the previous one) and the build cache past 4gb; under 20% free disk, prunes before building
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
bo deploy prune
Section intitulée « bo deploy prune »Free the box’s disk: remove our images except the running release and the newest other one (to roll back to), dangling images, and the build cache past 4gb
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
bo deploy site
Section intitulée « bo deploy site »Upload the public site to
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
--run |
string | A site.yml run (an id, or latest: main’s last successful one): its site artefact instead of the local apps/site/dist |
bo deploy design
Section intitulée « bo deploy design »Upload the composed Storybook (design system, desk, player, site) to design.bo ci main’s), or –run <id|latest> for a dispatched ci.yml run’s artefact
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
--run |
string | A ci.yml run (an id, or latest: main’s last successful one): its design artefact instead of the local dist/design |
bo deploy webhooks
Section intitulée « bo deploy webhooks »Register the Stripe webhook endpoints (platform + Connect) on api.
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
--recreate |
boolean | Delete and create both endpoints (new secrets), e.g. after a leaked secret |
bo deploy notify
Section intitulée « bo deploy notify »Brevo for real sends: the sender of BO_EMAIL_FROM, the email and SMS delivery webhooks on api.
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
--check |
boolean | Report only: change nothing, in Brevo or in the file |
--recreate |
boolean | A new token on both webhooks: a leaked token, or the api answering 401 to Brevo |
bo deploy status
Section intitulée « bo deploy status »The box’s services, their images, the api’s health, its free disk (warns under 20%); fails unless the firewall lets in 22/80/443 only and only the edge publishes ports
| Option | Type | Rôle |
|---|---|---|
--json |
boolean | Machine-readable output |
bo deploy logs
Section intitulée « bo deploy logs »A service’s logs on the box (–follow, –tail
| Argument | Type | Rôle |
|---|---|---|
service |
string | api · worker · mcp · edge · postgres (default: all) |
| Option | Type | Rôle |
|---|---|---|
--follow |
boolean | |
--tail |
integer |
bo deploy psql
Section intitulée « bo deploy psql »psql on the box’s database, as the owner