{"doc":"cli","title":"CLI reference","markdown":"# CLI reference\n\nThis page lists every `gamestage` command, what it does and what it leaves\nbehind. It follows `gamestage <command> --help`, and where the two disagree the\ntool is correct.\n\nThis page writes every command as `gamestage <command>`. That is the form the\nshell installer puts on your PATH. Running from npm it is `npx gamestage\n<command>`, and running the downloaded bundle it is `node gamestage.mjs\n<command>`. `gamestage start` reports which form it is answering to.\n\n`--json` is a global option and goes before the command: `gamestage --json\nverify`. Use it when a script or an agent will branch on the result instead of\nreading prose.\n\nCommands marked \"Needs sign-in\" reach Backstage, so run `login` first. Signing\nin is public and self-serve, so a new account stops at approval instead: the\ncommands answer `access_pending` until a person approves it.\n\n## Commands\n\n### `start`\n\nReads the current directory and names the right next step. Run it when you do\nnot know which command to run; it works at every stage of a migration.\n\nIt prints the version it is, and reaches nothing over the network to do it. Read\nthat number first when a command refuses something this page says it accepts: a\ncached bundle presents as a documentation error rather than as an old tool.\n\n### `doctor`\n\nChecks Node, the bundled schema, and whether a manifest is present. Exits `1`\nwhen something blocking is wrong, `0` otherwise.\n\n### `inspect [target]`\n\nReads an existing game and produces an inspection report. `target` is a game\nfile or a project directory, defaulting to `.`.\n\n| Option | Meaning |\n| --- | --- |\n| `--write [path]` | Write the generated manifest, defaulting to `gamestage.yaml` |\n| `--force` | Replace an existing manifest, losing whatever it holds |\n\nThe report also names how the prototype already sits on the screen: `screen`,\n`canvas`, `card` or `page`, with the file and line it read that from, and\n\"(possible)\" when it is a guess rather than a certainty. `--write` puts it in\nthe manifest as `experience.layout`, exactly as `create --layout` would have.\nSee [layout and loading](/docs/layout-and-loading) for what each value means.\n\nThe inspection is read-only and registers nothing, however many times you run\nit. Only `--write` touches the disk, and it refuses to overwrite an existing\nmanifest without `--force`, because a later manifest may hold a complete\ncontainer the inspection cannot reconstruct. When the CLI is signed in,\n`--write` also registers the manifest it wrote, after writing it, so the id in\nyour `gamestage.yaml` is the id your workspace holds; a failed registration\ndoes not undo the file.\n\n### `formats [format]`\n\nExplains the formats before you choose one: what the fan does, what it suits,\nwhat the Engine keeps secret, what a producer writes in Studio, and a deployed\ngame of that format to play. No account needed.\n\n```\ngamestage formats          every format you can build, then the ones with no Engine yet\ngamestage formats hunt     one format in full\ngamestage formats trivia   a format with no Engine yet, and how to ask for it\n```\n\nIf none fits, `gamestage suggest \"what you want to build\"` tells us.\n\n### `create`\n\nStarts a new game, choosing its authority before scaffolding anything. Run by\nhand with no `--format` or `--name`, it asks for them, then for who runs the\ngame and their privacy policy for the consent card. An agent, a pipeline or\n`--json` is never asked: with no format it prints the menu and exits.\n\n| Option | Meaning |\n| --- | --- |\n| `--name <name>` | The game's name |\n| `--format <format>` | How the game is played, and so the Engine rules it runs. `gamestage formats` explains each |\n| `--brand <name>` | Who runs the game, named when a fan is asked for consent |\n| `--privacy-url <url>` | Your privacy policy's address, linked from the consent card |\n| `--here` | Write into this folder even when it holds other things |\n| `--host <host>` | Where it runs: `fullscreen` (the default), `article-launch`, `article-embed` or `app-webview`. See [layout and loading](/docs/layout-and-loading) |\n| `--layout <layout>` | How it sits on the screen: `screen`, `canvas`, `launch`, `card`, `page` or `sheet`, defaulting from the host and format |\n| `--write [path]` | Manifest path, defaulting to `gamestage.yaml` |\n| `--force` | Replace files that are already there, losing what they hold |\n\nIt writes `gamestage.yaml`, `index.html`, `gamestage.js`, `gamestage-player.js`\nand `favicon.svg`, and scaffolds a playable page for `hunt`, `push`, `group`,\n`predict`, `bingo` and `shoot`.\n\n**Where it writes.** Into the current folder when that folder is empty or\nalready holds a page (`index.html`) or a manifest. Anywhere else, such as a\nfolder holding another project, it makes a folder named after the game and\nwrites there, so your other files are left alone. `--here` writes into the\ncurrent folder regardless. `--json` reports the folder as `directory`.\n\n**It never replaces a file it did not write.** A file already in the directory\nis kept and named in the output, so running `create` beside a prototype gives\nyou the manifest and the clients while your own page is left alone. A\n`gamestage.yaml` that is already there stops the command outright, because that\nfile holds a game and a second one minted beside it would leave the directory\nwith two. `--force` overrides both.\n\n### `client`\n\nWrites the browser clients into your project in the form a browser can load:\n`gamestage.js`, the Gamestage client, and `gamestage-player.js`, the smaller\nplayer API client. Both are regenerated when the command runs again, so keep\ngame logic out of them.\n\n| Option | Meaning |\n| --- | --- |\n| `--out <dir>` | Where to write them, defaulting to the current directory |\n\n### `dev`\n\nServes your round locally, through the same Engine rules that serve it in the\ncloud. Prints `window.GAMESTAGE_API` and `window.GAMESTAGE_GAME` for the page.\nThere is no watch mode: stop and restart after changing the container. Needs\nsign-in.\n\n| Option | Meaning |\n| --- | --- |\n| `--port <port>` | Local HTTP port, default `4000` |\n| `--manifest <path>` | Manifest the round is read from, default `gamestage.yaml` |\n| `--fixture` | Serve the contract fixture instead of your own round |\n| `--scenario <name>` | Which fixture scenario, for contract work |\n| `--as <player>` | Play as a named signed-in player, with a keypair minted for this run |\n\nWith no `backend.round`, which is the normal case because a game's rounds are\nwritten in Studio, `dev` plays a sample round for the game's format, the same\none `review` plays, and says it is a sample. Nothing is written for it, so a\ndeploy cannot publish it. With no readable manifest at all, `dev` serves the\ncontract fixture and says so. The fixture is useful for wire work. It is not\nyour game.\n`--as` mints a temporary keypair and loopback issuer; the manifest validator\nrefuses that issuer in a deployed game.\n\n### `verify`\n\nRuns the game against a real Engine and proves the migration is done. Needs\nsign-in. It runs a set of checks: whether the round played here is honestly\nthis game's own, whether the attempt cap was chosen rather than defaulted,\nwhether the answers are absent from everything a deploy would publish,\nwhether every producer setting the page offers is read by the game, whether a\ngenerated client is present and the game reaches Monterosa through it rather\nthan around it, whether a deadline comes from the server's clock, and, when a\nbrowser is available, whether the screen shows what the server decided and\nnotices the connection going and coming back.\n\nThe first check walks the publish set rather than the named page, using the same\ncall `deploy --dir` uploads with, so a manifest, data file or source map holding\nthe answers beside a clean page is a fault rather than an oversight. Files are\nread on their contents rather than their extension; binaries and very large files\nare listed but not opened.\n\n| Option | Meaning |\n| --- | --- |\n| `--file <path>` | The game to verify, normally `index.html` |\n| `--manifest <path>` | Manifest the round is read from, default `gamestage.yaml` |\n| `--dir <path>` | The directory a deploy would publish, default the game's own |\n| `--no-browser` | Skip the browser proof |\n\n### `review`\n\nPlays the built game against a local Engine, in a headless browser at phone\nsize, and gives advice on whether fans will come back. Run it from the game's\nfolder before `deploy`:\n\n```bash\nnpx gamestage@latest review --file dist/index.html\n```\n\nEvery line is `ok`, `advice` (with the rule and a fix) or `skipped` (with why).\nIt never fails and always exits 0: these are judgements, not faults. The rules\nand the research behind them are in [Design a game fans come back to](/docs/game-design).\n\n| Check | What it looks at |\n| --- | --- |\n| First answer | Seconds and taps from arriving to the first answer, against 30 seconds and 3 taps |\n| Play next | Whether Play next (or a greyed Come back tomorrow) is the main button after a round |\n| Story | Whether the story opens on its own, whether it runs past 3 slides, and whether a story button opens anything. Found by what it does: a full-screen layer with a row of progress segments, whether it is Gamestage UI's Story or the game's own |\n| Come back | Whether the result screen shows a streak or when the next round opens |\n| Share | Whether the page declares a share card and offers a Share button |\n| Ties | How many distinct scores a round can award, from its scoring rules |\n| Options | Six options for a one-answer pick |\n| Rounds | Rounds ready in the live pool, when you are signed in and the game is deployed |\n\nA game whose rounds live in Studio has no round in its manifest, so the tie\nestimate uses a representative round of its format and says so. The game's own\n`gamestage.settings.json` stands in for Studio, as it does for `dev --serve`.\n\n| Option | Meaning |\n| --- | --- |\n| `--file <path>` | The game's page, default `index.html` |\n| `--manifest <path>` | The manifest, default `gamestage.yaml` |\n\nSet `GAMESTAGE_REVIEW_SCREENSHOT=/tmp/result.png` to save the result screen as\nit was reviewed, to check the advice by eye.\n\n### `suggest [what]`\n\nTells us about a format or feature that does not exist. No account needed, and\nthe right moment is the one where you ran out of road.\n\n**Pipe it in when it runs to more than one line.** A newline inside a quoted\nargument ends the command at your shell, so a report typed across several lines\narrives as its first line and nothing says so:\n\n```\ngamestage suggest --send < suggestion.txt\n```\n\nThe argument still works and is still the shortest way to send one line.\n\nWithout `--send` it prints the exact payload and sends nothing. That payload is\nfour strings: what you were building, the format that came nearest, the CLI\nversion, and a marker saying it is a suggestion. **Never the game.** No\nmanifest, no round, no answers, no source, and there is nowhere in the shape to\nput one.\n\n| Option | Meaning |\n| --- | --- |\n| `--closest <format>` | The format that came nearest, if one did |\n| `--send` | Send it, having seen what it says |\n\n### `issue [what]`\n\nReports something that went wrong. No account needed, and it lands in the same\nbacklog a suggestion does, titled so a bug is not read as a wish.\n\n**Pipe it in when it runs to more than one line.** A newline inside a quoted\nargument ends the command at your shell, so a report typed across several lines\narrives as its first line and nothing says so. That is how two real reports were\nlost. Write the report to a file, or pipe it from whatever wrote it:\n\n```\ngamestage issue --send < report.txt\ntype report.txt | gamestage issue --send    # Windows\n```\n\nThe argument still works and is still the shortest way to send one line. Nothing\nhere opens a file of its own: the pipe carries what you chose to put in it, and\n`--game` is an id you type rather than a file the command reads.\n\nWithout `--send` it prints the exact payload and sends nothing. That payload is\nsix strings: what went wrong, the game you named, the CLI version, the node\nversion, the operating system, and a marker saying it is a bug report. **Never\nthe game.** No manifest, no round, no answers, no source, no file paths, and\nthere is nowhere in the shape to put one. `--game` is the id you type, not a\nfile the command reads.\n\n| Option | Meaning |\n| --- | --- |\n| `--game <id>` | The game it happened on, if it was about one |\n| `--send` | Send it, having seen what it says |\n\n### `manifest validate [path]`\n\nValidates a manifest and reports the build stage its evidence supports. Path\ndefaults to `gamestage.yaml`. `--level <level>` requires a conformance level\nfrom L0 to L5 and exits `1` when the manifest does not reach it.\n\n### `manifest register [path]`\n\nRegisters an existing manifest with Backstage. Safe to repeat, and needed\nwhen a manifest was created while signed out. Needs sign-in.\n\n### `login`, `logout`, `whoami`\n\n`login` signs in, and creates an account when none exists: there is no\nseparate signup and no website step first. It starts a WorkOS device flow, so\na person has to approve it in a browser. `logout` forgets the stored session.\n`whoami` prints who the CLI is signed in as.\n\n| Option | Meaning |\n| --- | --- |\n| `--workspace <id>` | The workspace you expect to land in, from the welcome screen |\n\n### `link github`\n\nLinks your GitHub account through a second device flow, which a first deploy\nrequires. Needs sign-in.\n\n### `open <game> [where]`\n\nOpens the game in the browser. `where` is `play` (the default: the game as a\nfan plays it), `studio` (its Studio project, where rounds and words are\nwritten) or `stage` (its page on gamestage.ai). With `--json`, or on a machine\nwith no browser, it prints the address instead. A game not yet deployed has no\nStudio project, and the command says so.\n\n### `docs [chapter]`\n\nPrints a chapter of this documentation in the terminal, fetched from\ngamestage.ai so it is never out of date. With no chapter it lists them.\n`gamestage docs game-formats` and `gamestage docs cli` are the two most used;\n`formats`, `deploy` and `manifest` work as short names. No account needed.\n\n### A newer version\n\nAfter a command you run by hand, the CLI checks npm at most once a day and,\nwhen a newer `gamestage` is out, prints one line saying so and how to update.\nIt never checks for `--json`, a pipe, CI or `GAMESTAGE_NO_UPDATE_CHECK=1`.\n\n### `status [game]`\n\n`gamestage list` is the same command: with no game named, it lists your games.\n\nWhat is deployed, what state it is in and which limits are near. One game when\nnamed, the whole workspace when not. Needs sign-in.\n\nName a game and it also says whether that game is serving as dev or prod, read\nwith `effectiveStage` rather than inferred from `promote` having succeeded. On\ndev it names which of the two gates is still shut: your own approval\n(`gamestage promote`), your workspace's plan, or both. An archived game is told\nneither, because it serves nobody and `promote` would refuse it.\n\nThe workspace list does not repeat that per row. Every row would carry the same\nsentence, and a game's state and its serving stage are different questions, so\n\"live, dev\" side by side reads as a contradiction rather than as two answers.\n`--json` does carry it for every game, because an agent branches on a field\nrather than reading a column.\n\n### `deploy <game>`\n\nPublishes a snapshot to a Playground, and the round it puts on. Needs sign-in.\n\n| Option | Meaning |\n| --- | --- |\n| `--file <path>` | The game's HTML, uploaded before the deploy gate runs |\n| `--dir <path>` | The game's directory, uploaded with relative paths |\n| `--manifest <path>` | Manifest the round is read from |\n| `--revision <revision>` | Manifest revision being published |\n| `--hash <hash>` | Content hash of the snapshot |\n| `--level <level>` | Evidenced conformance level, L0 to L5 |\n| `--prize` | The game offers a prize, which is a paid plan only |\n| `--notes <text>` | What changed, said in the deploy announcement and in `logs` |\n| `--no-check` | Skip loading the published page in a browser afterwards |\n| `--allow <path...>` | Publish a file the deploy gate suspects; never clears a certain finding |\n\n`--dir` uploads everything under the directory, withholding the manifest\n(`gamestage.yaml` or `.yml`), environment files (`.env`, `.env.local`,\n`.env.*`) and the downloaded CLI (`gamestage.mjs`, `cli.mjs`), and removes\nstale files an older deploy left. The generated clients, `gamestage.js` and\n`gamestage-player.js`, are walked but not uploaded either: whatever imports\nthem has that import rewritten to the hosted client at a pinned version\ninstead. Upload happens before the deployment gate, so a refused deploy can\nleave new files in storage without making the game live. The picture that\nstands for the game in Stage comes from its format and is there from\nregistration, so a deploy has nothing to run and nobody has a brief to write.\n\n**A first deploy needs no round.** It puts the game online and creates its\nproject in Studio, which is where its rounds are then written. With no round\nyet, deploy says so, `No round yet: add one in Studio`, with the link to the\ngame's Studio project, and skips the browser check, because a page with\nnothing on is not a broken page. The round appears without another deploy\nonce a producer writes it.\n\n### `reference push <name> <file>`\n\nUploads the private values a round is scored against. `name` is what a producer\ntypes in Studio, such as `the-limit/careers.json`, and `file` is the JSON file\nholding it. Needs sign-in.\n\nThe file is read and validated on your machine before anything leaves it,\nbecause the server never sees the bytes: they go to a private prefix from a\nsigned URL. One push writes two objects, the pointer at the name a producer\nuses and a copy keyed by the digest of those exact bytes. A round records the\ndigest it was provisioned against, so asking what a round was scored on is a\nlookup rather than an argument.\n\nA round provisioned after the push uses the new values. A round already being\nplayed keeps the values it opened with, because a fan's page load will not\nre-value a round underneath them, and the change goes on at the next deploy or\nexplicit refresh. [Dataset](/docs/data) has the file's shape and what\nmust never go in it.\n\n| Option | Meaning |\n| --- | --- |\n| `--drop-scoring` | Push anyway, when the file would remove a measure's `scoring` block that a live round depends on |\n\nWithout `--drop-scoring`, a push that would stop a measure scoring rounds is\nrefused and names the measure: see [Scoring a push round by how close it\ncame](/docs/data#scoring-a-push-round-by-how-close-it-came).\n\n### `reference check <file> [keys...]`\n\nReports what a round would fail on, before a producer finds out by having one\nrefused. It prints how many values each measure holds, and when you name keys\nit says which of them are absent.\n\nA key the dataset does not hold refuses the whole round rather than\nscoring as nothing, so one typo costs the round. This is that same check, run\nagainst the file, by the person who can fix it. It names a missing key and\nnever prints what a present one is worth. Nothing leaves the machine and no\naccount is needed.\n\n### `sources`\n\nLists where a game's real-world values can come from, and which of them your\nworkspace can use now. Needs an approved account.\n\n```\nnpx gamestage sources\n```\n\nFor each source it prints what it offers, what it covers, what it costs, and\nthe licence position, then its state for you: **connected** (data from it is\nalready in your workspace), **available**, **available, needs a key** (sign up\nwith the provider, then connect your key through the page it gives you), or\n**not available to you**, with the reason. `--json` prints the same for an\nagent.\n\nRun it before designing a game that needs real figures. It never asks for a\nkey, and you should never paste one into a chat: a key goes in through a\nGamestage page.\n\n### `sources connect <source>`\n\nPrints the Stage page where you add your own key for a source, such as\n`sportmonks`. Needs an approved account for the page itself.\n\n```\nnpx gamestage sources connect sportmonks\n```\n\nOpen the link in your browser and paste the key there. Gamestage checks it with\nthe provider once, then stores it in AWS Secrets Manager for your workspace and\nuses it only to fetch your data on a schedule. The command never takes the key,\nnot as an option and not as input, so a coding agent running it never sees it.\nA wrong key is refused on the page with the reason, and nothing is stored.\n\n**At your own terminal, from 1Password.** If you cannot use the page, you can\nread the key from your own 1Password instead:\n\n```\nnpx gamestage sources connect sportmonks --from-op \"op://Private/Sportmonks/credential\"\n```\n\n| Option | Meaning |\n| --- | --- |\n| `--from-op <reference>` | A 1Password secret reference, `op://vault/item/field`. The CLI runs `op read` itself and sends the key straight to Gamestage |\n\nIt needs the 1Password CLI, signed in, and 1Password asks you to approve the\nread. The key is never printed, logged or written to disk: you see\n\"Connected\", its last four characters, and the competitions your plan covers.\nThere is no way to pass a key itself, as an option, on input or in an\nenvironment variable. This route is for a person at their own terminal: a\ncoding agent must never run it or write the reference for you. Every attempt\nto connect a key, by either route, is recorded against your workspace without\nthe key, and five attempts an hour is the limit.\n\n### `sources status <source>`\n\nSays whether your key for a source is connected, when it was connected, and\nwhich competitions your plan covers. It never shows the key.\n\n```\nnpx gamestage sources status sportmonks\n```\n\n### `sources disconnect <source>`\n\nRemoves your key for a source. Its scheduled pulls stop at once; data already\nin your workspace stays. Connect again with `sources connect`.\n\n```\nnpx gamestage sources disconnect sportmonks\n```\n\n### `round push <game>`\n\nWrites the round in your manifest into Studio, where a producer owns it from\nthen on. Needs sign-in.\n\n| Option | Meaning |\n| --- | --- |\n| `--manifest <path>` | Manifest the round is read from, default `gamestage.yaml` |\n| `--yes` | Do not ask before replacing what a producer has typed, default off |\n\nIt prints what is about to go in, then asks. Without a terminal and without\n`--yes` it refuses and writes nothing, because it replaces what a producer\ntyped and nobody was there to be asked. That refusal comes before it reaches\nthe control plane, so an unattended run is turned away whether or not the CLI\nis signed in.\n\nWhat travels differs by format. A `push` round sends only its public half: an\nentry's point value would be broadcast to every connected browser, so the\nvalues stay in your dataset where only the Engine reads them. A `hunt`\nor `group` round sends its answer too, into a field Studio withholds from a\nfan's browser. A `predict` round is refused by name, because a prediction's\nquestion and options are the platform's own and are authored in Studio itself.\nA `shoot` round sends its settings; the keeper is the Engine's and never\ntravels.\n\nThere is a push and there is no pull. Once the round is in Studio a producer's\nedits live there, and a pull would put them back into a manifest, which is the\none thing a manifest must not hold. `status <game>` reports the round a game is\nserving without writing anything.\n\n### `round seed <game> --count <n>`\n\nBuilds candidate rounds from the game's connected dataset, then creates one\nnew element per round in its current Studio edition. Needs sign-in and a game\nthat has been deployed with a dataset connected. Existing rounds are preserved;\na producer reviews and publishes the new elements in Studio, where each is\nnamed and annotated like every other round. This does not publish them or\nrefresh the Engine.\n\n```sh\ngamestage round seed points --count 5 --measure season_points --dry-run\ngamestage round seed points --count 5 --measure season_points --yes\ngamestage round seed almanac --count 3 --measure assists --position Goalkeeper --dry-run\n```\n\n| Option | Meaning |\n| --- | --- |\n| `--count <n>` | Required, 1–80 distinct rounds |\n| `--measure <key>` | Dataset column; required when the dataset has several measures |\n| `--position <position>` | Exact position filter, ignoring case |\n| `--team <team>` | Exact team filter, ignoring case |\n| `--dry-run` | Print complete candidate rounds as JSON, without writing files or Studio elements |\n| `--yes` | Create the rounds without the interactive consent prompt |\n\nDry-run is explicit, including when stdout is redirected. This follows the CLI's\nexisting convention: a non-interactive stdin requires `--yes` to write, while\n`--dry-run` needs no consent. Dry-run still reads the connected dataset through\nthe authenticated control plane. The private dataset stays on the server.\n\nFor **push**, each position gets a slate of up to four entries (at least two,\nat most eight positions). The target has **one to five exact squads**, counting\nall combinations of one pick per slate, including different picks with equal\nvalues. Values never go onto the element; the selected measure and target do.\n\nFor **computed hunt**, each board has four distinct players and asks for the\nhighest figure. “Clearly separates” means the answer is strictly greater than\nthe runner-up and **at least 1.5 times its figure**. A positive answer against\nthree zeroes qualifies; an all-zero board or a tie for highest never does.\nThe element carries dataset keys and the highest-value rule, so the Engine\nrecomputes the answer during provisioning. Dataset changes can invalidate an\nold candidate's separation; the preview describes the data read at that time.\n\nFilters run before selection. Missing names, missing push positions, insufficient\nentries or ambiguous measures produce an explanation. Selection is reproducible\nfor the same dataset and options. Search stops after 5,000 candidate attempts;\nif it cannot fill the requested batch, nothing is written. Reduce `--count` or\nwiden the filters. Re-running a successful seed creates additional elements.\nAn edition's 80-element limit still applies, including existing elements.\nIf a write fails partway through, the command reports how many were created;\ninspect Studio before retrying. The control plane must include this command's\ncandidate endpoint and additive round-writing support.\n\n### `round refresh <game>`\n\nReads Studio and the dataset again now and provisions what it finds, rather\nthan waiting for the next fan's visit. Needs sign-in.\n\nIt changes nothing a producer wrote: it re-reads and re-provisions exactly as a\nfan's visit past the cooldown would, and prints the last lines the game logged\nso you see what it did. Use it after pushing a new dataset with\n`reference push`, or when a producer's edit should reach fans before anyone\nopens the game.\n\n### `graphic templates`\n\nLists the screens a graphic can be cut for. No account needed, no network call.\n\n```\ngamestage graphic templates\n```\n\n| Template | Type | Made for |\n| --- | --- | --- |\n| `lowerthird` | Leaderboard | A lower third over live video, for TV and streaming. Transparent, three rows, keyed |\n| `panel` | Leaderboard | A right-hand panel pulled out over the picture, for TV and CTV. Eight rows |\n| `bigscreen` | Leaderboard | A stadium screen or a Jumbotron. Ten rows at large type. The default |\n| `vertical` | Leaderboard | A portrait panel in a concourse, 9:16 |\n| `promo` | QR | A QR code that sends a crowd to the game. No live data |\n\nA template sets the graphic's size, type scale and safe margins. It does not\nset its colours: that is your stylesheet, and `graphic dev` is how you write\none.\n\n### `graphic create <game>`\n\nPuts the game's leaderboard on a screen. The control plane composes the graphic\nin Monterosa Live Graphics and hands you back an address.\n\n```\ngamestage graphic create kickoff --template bigscreen --consent\n```\n\n```\nOn screen\n  Game      Kick Off (kickoff)\n  Template  bigscreen - A stadium screen or a Jumbotron\n  Made for  A crowd looking up at a screen from a long way away\n  Address   https://<space-domain>/r/embed.html?slug=5aItiwK&bigscreen=true\n```\n\n| Option | What it does |\n| --- | --- |\n| `--template <id>` | Which screen to cut it for. Default `bigscreen`. See `graphic templates` |\n| `--consent` | Confirms the people named agreed to appear on a screen. No default |\n\nOpen the address on the screen. It is a web page, it needs no sign-in, and it\nfollows the game's leaderboard by itself: fans play, the graphic redraws, and\nnothing is redeployed for a new round.\n\n**`--consent` has no default and `--json` does not imply it.** A leaderboard\nputs fans' names on a screen other people can see, and somebody has to say\nthose people agreed to that. Your account is stored on the graphic as the one\nwho confirmed it. A flag that defaulted to true would make that record say\nsomebody agreed when nobody was asked, which is worse than having no gate.\n\n| Refusal | What to do |\n| --- | --- |\n| `Nobody has said these names can be shown` | Run it again with `--consent`, once that is true |\n| `No template called <id>` | Run `gamestage graphic templates` |\n| `This game has not been deployed` | A graphic follows the game's own leaderboard, and a QR code sends people to the game's page. Neither exists before a deploy. Run `gamestage deploy <game> --dir .` |\n| `This deployment cannot make graphics` | The control plane holds no Live Graphics key. That is a deployment setting, not anything about your game |\n\n**Your machine never holds a graphics credential.** Live Graphics takes one key\nper deployment, and that key can write to every graphic in it. The control\nplane holds it, checks the game is yours, and makes the call. Nothing is\nwritten to your game's files and nothing about your game changes.\n\n### `graphic list <game>`\n\nThe graphics this game has, their ids, and where to watch them.\n\n```\ngamestage graphic list kickoff\n```\n\nEach row carries the template, the graphic's id, its address, and who confirmed\nthe names could be shown. The id is what `graphic remove` takes.\n\n### `graphic remove <game> <id>`\n\nTakes a graphic off the air. The address stops answering, so take the screen\ndown first if somebody is watching it.\n\n```\ngamestage graphic remove kickoff 42a57df0-6caf-46a8-ad5f-a7aeb8a7030f\n```\n\nA graphic this game does not hold is refused, whoever asks. That refusal is the\nonly thing between one creator and another customer's screen, because the key\nthat deletes a graphic has no tenancy of its own.\n\n### `graphic dev`\n\nServes the Live Graphics renderer on your own machine, drawing a made-up\nleaderboard with your stylesheet applied, so you can restyle a graphic without\nlooking at a production screen to find out whether a colour was right. Needs no\naccount.\n\n| Option | Meaning |\n| --- | --- |\n| `--css <path>` | The stylesheet to preview, default `graphic.css` |\n| `--port <port>` | Port to serve on, default `4321` |\n| `--rows <rows>` | How many rows to draw, default `10` |\n| `--bigscreen` | Draw it as a big screen rather than an overlay, default off |\n| `--accent <colour>` | Accent colour, as `#rrggbb` |\n| `--renderer <url>` | Where the renderer is served from |\n\n**The page is the real renderer, not a copy of it.** The markup and the script\ncome from the deployed graphic, so what you see is the thing itself rather than\nsomething that resembles it and quietly stops matching.\n\nSave the stylesheet and the page picks it up within five seconds. There is no\nwatcher and no reload: a graphic re-reads its stylesheet on every poll, so this\ngets live editing by not getting in the way of behaviour the renderer already\nhas.\n\nIt reads no graphic and writes none. The rows are invented, so there is no\nproduction board to get wrong.\n\n**The structure is not yours to change, only the paint.** A graphic's type is\nbuilt by Monterosa and its markup is fixed, which is what stops a stadium\nscreen rendering something nobody designed. Your stylesheet is applied over it.\n\n### `graphic push <game>`\n\nHands a producer the address of your stylesheet, so the graphic they composed\ncan follow it. Needs an approved account.\n\n| Option | Meaning |\n| --- | --- |\n| `--css <path>` | The stylesheet to publish, default `graphic.css` |\n\n```\ngamestage graphic push kickoff --css graphic.css\n```\n\n```\nReady for a producer\n\n  Game        Kick Off (kickoff)\n  Stylesheet  graphic.css (3,204 of 10,000 characters)\n  Address     https://<space-domain>/games/kickoff/graphic.css\n```\n\n**Nothing is written to Live Graphics, and that is deliberate rather than a\nlimitation we are working around.** Writing to a graphic needs a key belonging\nto a whole deployment, or a Studio operator's own token, and a creator has\nneither. Giving a creator one would make Gamestage the only thing standing\nbetween them and another customer's stadium screen.\n\nSo the stylesheet travels the way the leaderboard already does. `deploy`\npublishes it along with every other file in the directory, a producer pastes\nits address into the graphic beside the feed address, and the graphic follows\nit from then on. You restyle a live screen by deploying. The producer takes it\nback by clearing one field.\n\nWhat this command does is make that usable: it reads the stylesheet, refuses it\nif it will not fit, checks the address is actually being served, and prints the\nline to hand over.\n\n| It stops with | When |\n| --- | --- |\n| `No stylesheet to publish` | The path does not exist |\n| `The stylesheet is too long for a graphic` | Over 10,000 characters, which is the field Live Graphics stores it in. Minify it |\n| `This game has not been deployed` | Nothing is served yet, so there is no address to give |\n| `The stylesheet is not being served yet` | Nothing answers at the address, usually because the file was written after the last deploy or sits outside the deployed directory. The playground says 403 rather than 404 for a file that is not there, so the command reports whatever it got rather than guessing |\n\n**Deploy first.** The address only starts working once a deploy has published\nthe file, and a deploy publishes the directory it is given, so the stylesheet\nhas to be inside it.\n\n**Live Graphics does not read a stylesheet address.** A producer opens the\naddress this command prints and copies the stylesheet out of it by hand,\nrather than the graphic following it directly. See\n[Live Graphics](/docs/live-graphics) for how a graphic gets its style today.\n\n### `endpoint <game> [which]`\n\nPrints where a deployed game answers. `which` is `player`, `control` or\n`playground`; all three when omitted. Needs sign-in.\n\n### `logs <game>`\n\nTails a game's runtime log. Needs sign-in.\n\n| Option | Meaning |\n| --- | --- |\n| `--limit <n>` | How many lines, newest kept, default `100` |\n| `--since <iso>` | Only lines after this timestamp |\n| `--level <level>` | `info`, `warn` or `error` |\n\n### `plan <plan>`\n\nChanges this workspace's plan, and so its limits. Takes `runtime`, `live` or\n`enterprise`, which a creator reads as Starter, Publisher and Enterprise.\nNeeds sign-in.\n\nOnly `runtime` can be set here. Publisher and Enterprise are arranged with a\nperson and no payment is captured on this command, so asking for either is\nrefused with `operator_required` and your workspace is left as it was.\n\nThis changes a record. It is not evidence that commercial approval, a\nMonterosa org or dedicated infrastructure exists, and none of those can be\ncreated by the service account. See [Plans and limits](/docs/starter-plan).\n\n### `promote <game>`\n\nApproves your own game to move from dev to prod. Needs your account approved.\n\nSucceeds for a game that exists, is not archived and is not already approved:\napproving your own game is not a purchase, so there is nothing for a plan to\nrefuse here. Run it again on an approved game and it reports there is nothing\nto do. It does not put the game live on its own. Reaching fans on prod also\nneeds your workspace on a paid plan, which only Monterosa can do, and the\ncommand tells you which of the two states you are actually in afterwards.\n\nStage carries the same action, under \"Dev and prod\" on a game's own page. It\nshows both gates, which of them is open, and which of the two states the game\nis serving right now.\n\n### `suspend <game>`, `wake <game>`, `archive <game>`\n\nTake a game off the air and free its live slot; put a suspended game back on\nthe air; take a game off the air for good while keeping its record, id and\nfiles. All three change what fans can reach, so they belong to a person rather\nthan an agent. There is no creator-facing delete. Needs sign-in.\n\n### `prune`\n\nDeletes archived games that never deployed. Needs sign-in.\n\n| Option | Meaning |\n| --- | --- |\n| `--yes` | Do not ask before deleting, default off |\n\nIt lists what it found, grouped by name, and then asks. Without a terminal and\nwithout `--yes` it deletes nothing. A game that is live, or that ever deployed,\nis refused by the server rather than by the command, so a mistaken `--yes`\ncannot take a real game away.\n\n### `admin approve <workspace>`, `admin refuse <workspace>`, `admin plan <workspace> <plan>`, `admin refresh <workspace> <dataset> <hours>`, `admin creator-sync`\n\nOurs, not yours. Approving an account and moving it onto a paid plan are\ndecisions we take, so these are refused for anybody who is not on the operator\nallowlist, and the refusal says so rather than pretending the command does not\nexist. They are listed here because they are in `gamestage --help` and an\nunexplained command is worse than a documented one you cannot run.\n\n`admin plan` is how a person moves you when `plan` tells you a plan is arranged\nwith a person. It changes your limits and nothing already running: a live\nround keeps the caps it was given until you deploy the game again.\n\n`admin refresh` sets how often one switched-on source, such as ESPN, is\nfetched for one workspace: a whole number of hours from 1 to 168, or `default`\nto go back to every 12 hours. It is how we answer a game that needs fresher\nnumbers than twice a day.\n\n`admin creator-sync` refreshes the list we use to email creators about their\napplication and Gamestage, now rather than on the hour, and prints only how many\nwere updated.\n\nIf your account is waiting, nothing you type changes that. `gamestage status`\nre-checks it and says where you stand.\n\n## Exit codes\n\nThere are three exit codes.\n\n| Code | Means |\n| --- | --- |\n| `0` | The command did what it said. A refusal is also `0`. |\n| `1` | It failed: a bad manifest, a missing file, a check that is open. |\n| `2` | Nothing failed and nothing was proved either. |\n\n`verify` is where all three appear. It exits `0` only when every check\nverified, `1` when a check is open and the game failed verification, and `2`\nwhen no check failed but one was skipped, which leaves the result unverified.\n`--no-browser` therefore yields `2`, because a skipped browser check leaves\nthe rendered outcome unproved.\n\nStarter turning down a second live game, or a deploy refused because\nGitHub is not linked, is the product working as designed, so it exits `0` and\ncarries a stable `code` in `--json` output. Branch on the code and fix the\ncause rather than retrying.\n"}