@tabletopwithfriends/cli
v0.1.2
Published
Create, playtest, and deploy Tabletop with Friends games
Readme
Run twf dev from a game repository. It reads the nearest boardengine.json (entry, export, and assets), or falls back to the package's src/game.ts. Use twf dev --config path/to/config.json or an explicit module to override discovery.
Generated projects include .envrc: after installing dependencies and running direnv allow, plain twf uses the repository's CLI. The engine checkout's .envrc runs current CLI source through scripts/bin/twf. To opt into that CLI from another game, put PATH_add /absolute/path/to/boardengine/scripts/bin in its ignored .envrc.local. The game's SDK stays independently pinned. Upgrade the installed CLI explicitly with pnpm add -D --save-exact @tabletopwithfriends/cli@latest.
@tabletopwithfriends/cli
Create, playtest, and deploy games with the twf command.
npx @tabletopwithfriends/[email protected] create my-game
cd my-game
npm install
npx twf dev src/game.tsDocumentation · Version compatibility
Game repositories pin their SDK. The CLI loads that installed SDK for rules execution and worker builds. Older npm versions and their documentation remain available; existing deployed rooms retain their immutable executor and assets.
Agent- and shell-friendly tools for ordinary BoardEngine TypeScript game files.
The CLI is a separate adapter; games continue to depend only on
@tabletopwithfriends/sdk.
From this monorepo:
pnpm --silent game create examples/my-game --title "My Game"
pnpm install
pnpm --silent game dev examples/tic-tac-toe/src/game.ts
pnpm --silent game inspect examples/tic-tac-toe/src/game.ts
pnpm --silent game inspect examples/tic-tac-toe/src/game.ts --state-file examples/tic-tac-toe/scenarios/endgame.json
pnpm --silent game simulate examples/dice-race/src/game.ts --seed playtest-1
pnpm --silent game explore examples/tic-tac-toe/src/game.ts --max-states 10000
pnpm --silent game manufacture examples/tic-tac-toe/src/game.ts
pnpm --silent game build-worker examples/tic-tac-toe/src/game.tsEvery successful command writes one JSON document to stdout. Failures write a
boardengine.error JSON document to stderr and exit nonzero, including nested
runtime error codes, details, causes, and exploration paths when available.
Use --compact for one-line output. The monorepo examples use pnpm --silent
so pnpm's script banner does not precede that JSON; an installed boardengine
binary needs no wrapper flag.
Platform deployments
From an independent Git repository with the SDK and CLI installed:
pnpm exec boardengine init --project my-game --entry src/game.ts
pnpm exec boardengine login --server https://tabletopwithfriends.com
pnpm exec boardengine deploy --dry-run
git add boardengine.json
git commit -m "Configure deployment"
pnpm exec boardengine deploy
pnpm exec boardengine whoami
pnpm exec boardengine logoutinit creates a private boardengine.json. Set visibility to public in that file to make publication public. Deploy requires a clean commit unless --allow-dirty is explicitly supplied. --config selects another repository-local manifest. --dry-run builds and hashes everything without authenticating or uploading.
Login uses a browser-confirmed device code. --no-browser prints the link without opening it. Credentials are scoped by server origin and stored with mode 0600 under $XDG_CONFIG_HOME/boardengine/auth.json (otherwise ~/.config/boardengine/auth.json). CI can use BOARDENGINE_TOKEN; BOARDENGINE_SERVER sets the default host. Logout revokes the session. Never check these credentials into a game repository.
The platform validates uploads and initializes the exact game executor before publication. All artwork goes to R2; no game source is added to the website's registry. Repeating a deployment is idempotent. Existing rooms retain their release. See platform deployments for infrastructure setup and present limits.
Commands
createwrites a tiny, ordinary TypeScript workspace package with one SDK-only game and tests. The initial inputless move is a direct typed method, so there is no action-builder ceremony in the first game file. It derives the game id and title from the directory name unless--idor--titleis supplied, and refuses to touch an existing target. Runpnpm installfrom the monorepo root afterward to link its workspace dependencies; the generated package then hasdev,inspect,simulate,test, andtypecheckscripts.devserves any trusted game module in a generic local browser playtest. Audience projections, legal actions, reset, and seat/spectator switching come from the normal headless runtime. A projected scene renders through Three.js; games without one remain playable through generated legal-action controls. Vite watches the ordinary TypeScript module, so no web registry or second game format is required. Use--host,--port,--players,--seed, or--state-filewhen the local session needs them.inspectcreates the initial authoritative playtest and returns its manifest, action/rule/event/invariant vocabulary, whether it has an automatic end condition, its complete component catalog and snapshot, every player projection (including legal actions), and the spectator projection. Scene generation is therefore validated as part of inspection.simulateruns the deterministic or supplied seed through the normal first-choice policy and returns status, result, final snapshot, projections, and replayable command/rule records.explorebreadth-first checks every finite legal branch up to--max-statesand returns grouped outcomes, dead ends, and replayable shortest paths.manufacturecompiles the exact game version and catalog into the neutral manufacturing artifact. Vendor credentials and uploads remain separate.build-workervalidates the same game export and bundles it with the versioned executor adapter asdist/boardengine-worker/worker.js. The output is a self-contained ES module suitable for an immutable Workers for Platforms user Worker; the authored game still imports only@tabletopwithfriends/sdk.
The loader prefers a default game export, otherwise selects the only game
export in the module. Use --export <name> when a module intentionally exports
more than one game. Games using defineSeats need no player configuration; use
--players '[{"id":"p1","seat":"north"}]' for custom host identities.
Use --state-file <path> with dev, inspect, simulate, or explore to start from
a focused scenario stored as ordinary JSON. The path is resolved from the
current working directory. BoardEngine parses the file and validates its value
against the game's state schema through the normal restoration boundary, so a
malformed or game-invalid scenario produces the same structured CLI error as
other failures. Named game invariants additionally reject schema-valid positions
that contradict the rulebook. Scenario state starts at revision zero while
preserving normal setup randomness. manufacture does not accept session
options such as --state-file.
Loading a game module executes that module, just like importing it from a test or host. The CLI should therefore be run only on trusted project files.
The local browser's left panel uses SDK-authored locations. Number keys (and
optional Cmd/Ctrl-number bindings) move the camera. The separate Rules & actions
window uses actionGuide to provide a folder tree, full-page explanations, search,
filters, and reading history. Its camera links locate pieces without playing a move. Play through the physical components or next-step
controls. inspect still returns the complete machine-readable action vocabulary.
Source for hosted AI
twf deploy uploads tracked game source and rules as a separate release-pinned R2 bundle. The default selection is the game entry directory and root README. Override it with sources in boardengine.json, such as ["src", "README.md", "rules"]; use [] to disable AI source uploads. twf deploy --dry-run lists the files that will be included. Only include source intended for AI players to read.
