@leafarcade/cli 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Let your own agent play a private board-game room while you use the browser board. The CLI reads positions and submits moves; it does not call a model, collect model credentials or implement its own opponent.
4
4
 
5
- **Scope of 0.5.0:** all 53 multiplayer games in unclocked, unrated rooms that admit agents. Nine games expose complete convenience move lists; Amazons, Lines of Action, The Last Greenhouse, Euchre, Cribbage and Fanorona expose complete legal wire actions. The others expose player-visible state and game-specific JSON action formats, validated by the game service. For these new games, use this release’s CLI with the matching game-service deployment. Local automatic play is available through the watcher below. OAuth login, seat-scoped pairing and resuming a stopped desktop chat are not implemented. Approval-only private tables require the host to approve a joining player before that seat is admitted. Invite-only tables admit players holding the invitation. Spectator and agent admission follow the host’s settings.
5
+ **Scope of 0.6.0:** all 53 multiplayer games in unclocked, unrated rooms that admit agents. Nine games expose complete convenience move lists; Amazons, Lines of Action, The Last Greenhouse, Euchre, Cribbage and Fanorona expose complete legal wire actions. The others expose player-visible state and game-specific JSON action formats, validated by the game service. For these new games, use this release’s CLI with the matching game-service deployment. Local automatic play is available through the watcher below. OAuth login, seat-scoped pairing and resuming a stopped desktop chat are not implemented. Approval-only private tables require the host to approve a joining player before that seat is admitted. Invite-only tables admit players holding the invitation. Spectator and agent admission follow the host’s settings.
6
6
 
7
7
  ## Start a game from your agent
8
8
 
@@ -15,15 +15,19 @@ This creates a private, unrated room with the CLI agent as host, saves its sessi
15
15
  ```sh
16
16
  leafarcade game wait --room ROOMCODE --after-revision R
17
17
  leafarcade game start --room ROOMCODE --revision R
18
- leafarcade game play --room ROOMCODE --revision R --column 4 --wait
18
+ leafarcade game play --room ROOMCODE --revision R --column 4 --wait --brief
19
19
  ```
20
20
 
21
21
  Every JSON response includes `next`: the exact command to run next (wait, start, play, or stop and report). Agents should follow it and never ask the human to report their moves.
22
22
 
23
+ `--brief` (on `status`, `wait`, `start`, `play` and the other position commands) returns only `status` (room, revision, phase, your piece, whose turn, outcome), `board_labeled`, `last_move`, the legal moves and `next`, which repeats the flag. Games without a labeled board also include `visible_state`. A mid-game Connect Four status shrinks from about 1.6 KB to about 0.8 KB.
24
+
23
25
  **Sandboxed agents.** Codex and Claude Code sandboxes block network access by default. Run every command with network access from the start; otherwise `npx` hangs without output and requests fail with `NETWORK_ERROR`. If `~/.config/leafarcade` is not writable, the CLI reports it; allow the write or use the same `LEAFARCADE_CONFIG_DIR` for every command.
24
26
 
25
27
  Omit `--open` on a remote or headless machine and share the returned URL. If launching the browser fails, the room is still valid: open that URL manually or use `leafarcade game open --room ROOMCODE`. No agent credential is placed in the URL. `--capacity N` selects a supported seat count (default: the game's minimum); Go also supports `--go-size 9|13|19`. For a custom service, provide both `--server` and its matching browser frontend `--site`.
26
28
 
29
+ Tables created by the CLI are labeled as CLI tables in the service's aggregate daily counts (no names, codes or moves). Set `LEAFARCADE_TEST_TRAFFIC=1` for test runs; CI runs (`CI=true`) are marked as test traffic automatically.
30
+
27
31
  The CLI stores a persistent creator identity so creating another room does not reset guest limits. An uncertain create keeps its request ID locally; rerunning the same command resumes that request. Do not discard the saved creation or blindly create another room after a network error. CLI-created rooms currently use guest limits; browser account limits and owner exemptions require authenticated browser-hosted creation.
28
32
 
29
33
  ## Install the agent skill
@@ -115,7 +119,7 @@ Use `game pause --room CODE --revision R` to explicitly take a break. The pause
115
119
  | `game rules --game GAME` | Returns rules, board representation, coordinates and instructions offline |
116
120
  | `game status --room CODE` | Returns public board, your seat, turn, revision, outcome, expiry and your legal actions |
117
121
  | `game legal-actions --room CODE` | Returns actions, completeness flag, action types and revision |
118
- | `game play --room CODE … --revision R [--wait]` | Sends one validated move; stale moves fail, with no automatic mutation retry. `--wait` then returns at your next turn, pause or finish |
122
+ | `game play --room CODE … --revision R [--wait] [--brief]` | Sends one validated move; stale moves fail, with no automatic mutation retry. `--wait` then returns at your next turn, pause or finish; `--brief` keeps only the decision fields |
119
123
  | `game wait --room CODE --after-revision R [--timeout S]` | Subscribes by WebSocket; catches an already-played move, reconnects at most twice and returns an event or `waiting` |
120
124
  | `watch --room CODE --agent-command EXECUTABLE` | Persistent subscription and platform-independent decision adapter; see below |
121
125
  | `game pause/resume --room CODE --revision R` | Saves or clears your explicit pause; any paused player blocks play |
package/dist/cli.mjs CHANGED
@@ -4799,9 +4799,9 @@ async function openBrowser(url) {
4799
4799
 
4800
4800
  // packages/cli/src/next.ts
4801
4801
  var defaultWaitSeconds = 90;
4802
- function nextStep(position, event) {
4803
- const room = `--room ${position.room}`, revision = position.revision;
4804
- const wait = `leafarcade game wait ${room} --after-revision ${revision} --timeout ${defaultWaitSeconds}`;
4802
+ function nextStep(position, event, brief = false) {
4803
+ const room = `--room ${position.room}`, revision = position.revision, flag = brief ? " --brief" : "";
4804
+ const wait = `leafarcade game wait ${room} --after-revision ${revision} --timeout ${defaultWaitSeconds}${flag}`;
4805
4805
  if (position.you.left) return "You left this room. Create or join another room only if the human asks.";
4806
4806
  if (position.phase === "finished") return `Match finished${position.outcome?.label ? ` (${position.outcome.label})` : ""}. Tell the human the result. Rematch only if they ask: leafarcade game rematch ${room} --revision ${revision}.`;
4807
4807
  if (position.paused) return `Paused. Stop and tell the human. Resume only when asked: leafarcade game resume ${room} --revision ${revision} (each player who paused must resume).`;
@@ -4810,11 +4810,11 @@ function nextStep(position, event) {
4810
4810
  if (host && position.access_requests?.length) return `A player is asking to join. Approve the person the human expects: leafarcade game approve ${room} --revision ${revision} --request ID.`;
4811
4811
  if (!host) return `Waiting for the host to start. Wait yourself: ${wait}`;
4812
4812
  if (seated < position.capacity) return `Waiting for players (${seated}/${position.capacity}). Wait yourself; do not ask the human to report joining: ${wait}`;
4813
- return `All seats are filled. Start the match: leafarcade game start ${room} --revision ${revision}`;
4813
+ return `All seats are filled. Start the match: leafarcade game start ${room} --revision ${revision}${flag}`;
4814
4814
  }
4815
4815
  if (position.turn?.yours) {
4816
4816
  const move = position.legal_wire_actions_complete ? "'<one legal_wire_actions entry>'" : position.legal_actions_complete ? "'<one legal_actions entry>'" : "'<wire action JSON from game rules>'";
4817
- return `Your move. Check your immediate win and your opponent's immediate threats (every line, including diagonals), then: leafarcade game play ${room} --revision ${revision} --action ${move} --wait`;
4817
+ return `Your move. Check your immediate win and your opponent's immediate threats (every line, including diagonals), then: leafarcade game play ${room} --revision ${revision} --action ${move} --wait${flag}`;
4818
4818
  }
4819
4819
  if (event === "waiting") return `No change yet. If the human is still playing, wait again: ${wait}. After several silent waits, stop and tell the human how to resume.`;
4820
4820
  const outOfTurn = position.legal_actions_complete || position.legal_wire_actions_complete ? "" : " Some phases allow out-of-turn actions; check game rules for this phase first.";
@@ -4822,6 +4822,7 @@ function nextStep(position, event) {
4822
4822
  }
4823
4823
 
4824
4824
  // packages/cli/src/create.ts
4825
+ var testTraffic = (env = process.env) => env.LEAFARCADE_TEST_TRAFFIC === "1" || /^(1|true)$/i.test(env.CI ?? "");
4825
4826
  async function createRoom(options2, signal) {
4826
4827
  if (!supportsAgent(options2.game)) throw Error("Unknown game ID. Run leafarcade help games.");
4827
4828
  const capacity = options2.capacity ?? allowedSeats(options2.game)[0];
@@ -4841,7 +4842,7 @@ async function createRoom(options2, signal) {
4841
4842
  pending = await storeSession(pendingPath, { ...creator, name: name.trim(), creation: { requestId: randomUUID(), game: options2.game, capacity, goSize: options2.goSize, access: options2.access ?? "invite", allowSpectators: options2.allowSpectators ?? true } });
4842
4843
  }
4843
4844
  await rpc(pending, "register", { name: pending.name }, false, signal);
4844
- const reply = await rpc(pending, "create", { ...pending.creation, agent: true }, false, signal);
4845
+ const reply = await rpc(pending, "create", { ...pending.creation, agent: true, client: "cli", ...testTraffic() ? { test: true } : {} }, false, signal);
4845
4846
  if (typeof reply?.code !== "string") throw Error("The room service returned an invalid room code. Your saved creation is retained.");
4846
4847
  const code = roomCode(reply.code);
4847
4848
  const path = sessionPath(options2.server, code);
@@ -4852,7 +4853,7 @@ async function createRoom(options2, signal) {
4852
4853
  const position = agentPosition(await readRoom(session, signal));
4853
4854
  await forgetSession(pendingPath);
4854
4855
  const url = roomUrl(options2.site, options2.game, code);
4855
- return { event: "created", room: code, url, ...options2.open ? { browser: await openBrowser(url) } : {}, position, next: `You are the host. Give the human this url (no second room). ${nextStep(position)}` };
4856
+ return { event: "created", room: code, url, ...options2.open ? { browser: await openBrowser(url) } : {}, position, next: `You are the host. Give the human this url (no second room). ${nextStep(position, void 0, options2.brief)}` };
4856
4857
  }
4857
4858
 
4858
4859
  // packages/cli/src/skill.ts
@@ -4904,7 +4905,7 @@ async function installSkill(agent = "codex", update = false) {
4904
4905
  // packages/cli/package.json
4905
4906
  var package_default = {
4906
4907
  name: "@leafarcade/cli",
4907
- version: "0.5.0",
4908
+ version: "0.6.0",
4908
4909
  private: false,
4909
4910
  description: "Create private browser board games and play with your AI agent. Includes 53 games and an installable Codex/Claude skill.",
4910
4911
  type: "module",
@@ -4956,19 +4957,19 @@ var commands = {
4956
4957
  approve: { usage: "game approve --room CODE --revision R --request ID", description: "As host, approve a player or spectator access request shown by status. Approve only the requested participant.", options: ["room", "revision", "request", "server"] },
4957
4958
  decline: { usage: "game decline --room CODE --revision R --request ID", description: "As host, decline a pending access request.", options: ["room", "revision", "request", "server"] },
4958
4959
  tables: { usage: "game tables [--game GAME_ID]", description: "Find public tables with an open seat that accept AI agents. No automatic joining or polling.", options: ["game", "server"] },
4959
- create: { usage: 'game create --game connect-four --name "My agent" [--open] [--capacity N]', description: "Create a table as the agent host (--access public|invite|private; default invite) and return the human browser link. --open launches the browser. Saved creator identity preserves limits across rooms; uncertain creation can be resumed. Give the human the returned url; never create a second room for the same request.", options: ["game", "name", "open", "capacity", "access", "no-spectators", "go-size", "site", "server"] },
4960
+ create: { usage: 'game create --game connect-four --name "My agent" [--open] [--capacity N]', description: "Create a table as the agent host (--access public|invite|private; default invite) and return the human browser link. --open launches the browser. Saved creator identity preserves limits across rooms; uncertain creation can be resumed. Give the human the returned url; never create a second room for the same request.", options: ["game", "name", "open", "capacity", "access", "no-spectators", "go-size", "site", "brief", "server"] },
4960
4961
  open: { usage: "game open --room ROOMCODE", description: "Reopen the human browser link for a saved room. Does not create another game or share the agent credential.", options: ["room", "site", "server"] },
4961
- start: { usage: "game start --room ROOMCODE --revision R", description: "Start a room you host once the requested players have joined. Other seats cannot start it.", options: ["room", "revision", "server"] },
4962
+ start: { usage: "game start --room ROOMCODE --revision R", description: "Start a room you host once the requested players have joined. Other seats cannot start it.", options: ["room", "revision", "brief", "server"] },
4962
4963
  skill: { usage: "skill install --agent codex|claude [--update]", description: "Install the bundled Leaf Arcade skill into your chosen agent user directory. Explicit command only; never runs during npm install. A differing existing skill is kept unless you pass --update, which moves it to a backup outside the skills directory first. Use skill show to inspect it.", options: ["agent", "update"] },
4963
- join: { usage: 'join ROOMCODE --name "My agent"', description: "Join an open private seat, or restore your saved seat. The human host starts the match. Joining identifies this client as an external agent.", options: ["name", "server"] },
4964
+ join: { usage: 'join ROOMCODE --name "My agent"', description: "Join an open private seat, or restore your saved seat. The human host starts the match. Joining identifies this client as an external agent.", options: ["name", "brief", "server"] },
4964
4965
  rules: { usage: "game rules --game GAME_ID", description: "Read game rules, action payloads and coordinate conventions offline. JSON output is suitable for agents.", options: ["game"] },
4965
- status: { usage: "game status --room ROOMCODE", description: "Read your seat's visible state, phase, turn, revision, outcome and action completeness. Hidden information belonging to other players stays hidden. Includes a labeled board, last_move and a concrete next command.", options: ["room", "server"] },
4966
+ status: { usage: "game status --room ROOMCODE", description: "Read your seat's visible state, phase, turn, revision, outcome and action completeness. Hidden information belonging to other players stays hidden. Includes a labeled board, last_move and a concrete next command.", options: ["room", "brief", "server"] },
4966
4967
  "legal-actions": { usage: "game legal-actions --room ROOMCODE", description: "Read actions and legal_actions_complete. An empty partial list is not proof that no move exists. Use game rules and visible_state for JSON actions.", options: ["room", "server"] },
4967
- play: { usage: "game play --room ROOMCODE --revision R MOVE_FLAGS [--wait]", description: "Submit one move at the exact latest revision. Use convenience flags or --action JSON, never both; --action also accepts an exact legal_actions entry unchanged. --wait then waits for your next turn or the end, like game wait. The service validates legality. On stale or uncertain results, read status before acting again.", options: ["room", "revision", "action", "column", "row", "from", "to", "promotion", "swap", "edge", "pit", "wait", "timeout", "server"] },
4968
- wait: { usage: "game wait --room ROOMCODE --after-revision R [--timeout 90]", description: "Wait for changes through WebSocket (1\u20133600 seconds, default 90). Returns your_turn, changed, paused, finished, left or waiting. Use it after every move instead of asking the human to report theirs. A timeout is normal, not resignation. This command cannot restart a stopped agent.", options: ["room", "after-revision", "timeout", "server"] },
4969
- pause: { usage: "game pause --room ROOMCODE --revision R", description: "Save your explicit pause in an active, unclocked private match. Any paused player blocks moves. Room expiry is not extended.", options: ["room", "revision", "server"] },
4970
- resume: { usage: "game resume --room ROOMCODE --revision R", description: "Clear your own pause. Each player who paused must resume before play continues.", options: ["room", "revision", "server"] },
4971
- rematch: { usage: "game rematch --room ROOMCODE --revision R", description: "Request another round after a normal finish. All players must agree and remain at the table. After resignation or leaving, create a fresh room. Never request a rematch without the human asking.", options: ["room", "revision", "server"] },
4968
+ play: { usage: "game play --room ROOMCODE --revision R MOVE_FLAGS [--wait]", description: "Submit one move at the exact latest revision. Use convenience flags or --action JSON, never both; --action also accepts an exact legal_actions entry unchanged. --wait then waits for your next turn or the end, like game wait. The service validates legality. On stale or uncertain results, read status before acting again.", options: ["room", "revision", "action", "column", "row", "from", "to", "promotion", "swap", "edge", "pit", "wait", "timeout", "brief", "server"] },
4969
+ wait: { usage: "game wait --room ROOMCODE --after-revision R [--timeout 90]", description: "Wait for changes through WebSocket (1\u20133600 seconds, default 90). Returns your_turn, changed, paused, finished, left or waiting. Use it after every move instead of asking the human to report theirs. A timeout is normal, not resignation. This command cannot restart a stopped agent.", options: ["room", "after-revision", "timeout", "brief", "server"] },
4970
+ pause: { usage: "game pause --room ROOMCODE --revision R", description: "Save your explicit pause in an active, unclocked private match. Any paused player blocks moves. Room expiry is not extended.", options: ["room", "revision", "brief", "server"] },
4971
+ resume: { usage: "game resume --room ROOMCODE --revision R", description: "Clear your own pause. Each player who paused must resume before play continues.", options: ["room", "revision", "brief", "server"] },
4972
+ rematch: { usage: "game rematch --room ROOMCODE --revision R", description: "Request another round after a normal finish. All players must agree and remain at the table. After resignation or leaving, create a fresh room. Never request a rematch without the human asking.", options: ["room", "revision", "brief", "server"] },
4972
4973
  resign: { usage: "game resign --room ROOMCODE --revision R", description: "Leave/resign through the service and remove your saved credential after acknowledgement. Returns the final position when available. This ends participation; closing a terminal does not.", options: ["room", "revision", "server"] },
4973
4974
  leave: { usage: "game leave --room ROOMCODE --revision R", description: "Alias for resign. Leaves the server room and removes local access after acknowledgement.", options: ["room", "revision", "server"] },
4974
4975
  forget: { usage: "forget --room ROOMCODE", description: "Delete local access only. Does not resign or revoke a credential copied elsewhere. Use game leave to leave the room.", options: ["room", "server"] }
@@ -4995,6 +4996,7 @@ var options = {
4995
4996
  "after-revision": "--after-revision R Revision you last saw; returns on any later change",
4996
4997
  timeout: "--timeout S Wait limit, 1\u20133600 seconds (default 90)",
4997
4998
  wait: "--wait After the move, wait for your next turn or the end",
4999
+ brief: "--brief Compact JSON: status, board_labeled, last_move, legal moves, next",
4998
5000
  request: "--request ID Access request ID from status access_requests",
4999
5001
  action: "--action JSON Wire action, or an exact legal_actions entry",
5000
5002
  column: "--column N One-based column (Connect Four 1\u20137; with --row for grids)",
@@ -5145,6 +5147,7 @@ Start playing (the agent hosts)
5145
5147
  Repeat step 4 until the match is finished.
5146
5148
  Every response includes "next" with the exact command to run next.
5147
5149
  Wait on the table yourself; never ask the human to report their move.
5150
+ Add --brief to status, play and wait for compact JSON; "next" keeps the flag.
5148
5151
 
5149
5152
  Joining a browser-hosted room instead:
5150
5153
  leafarcade join ROOMCODE --name "My agent", then follow "next".
@@ -5568,6 +5571,46 @@ async function watchRoom(session, options2, signal, emit, dependencies = watchDe
5568
5571
  }
5569
5572
  }
5570
5573
 
5574
+ // packages/cli/src/brief.ts
5575
+ var winner = (position) => {
5576
+ const seat = position.outcome?.winner;
5577
+ return seat === void 0 || seat === null ? null : seat === position.you.seat ? "you" : `seat ${seat + 1}`;
5578
+ };
5579
+ function briefPosition(position) {
5580
+ const turn = position.turn ? position.turn.yours ? "yours" : `seat ${position.turn.seat + 1}` : null;
5581
+ const wire = position.legal_wire_actions_complete ? position.legal_wire_actions ?? [] : null;
5582
+ return {
5583
+ status: {
5584
+ room: position.room,
5585
+ game: position.game,
5586
+ revision: position.revision,
5587
+ phase: position.phase,
5588
+ you: position.you.piece,
5589
+ turn,
5590
+ ...position.paused ? { paused: true } : {},
5591
+ ...position.you.left ? { left: true } : {},
5592
+ ...position.phase === "waiting" ? { seated: position.players.filter((player) => !player.left).length, capacity: position.capacity } : {},
5593
+ ...position.phase === "finished" ? { outcome: position.outcome?.label ?? null, winner: winner(position), terminal_reason: position.terminal_reason } : {}
5594
+ },
5595
+ board_labeled: position.board_labeled,
5596
+ last_move: position.last_move,
5597
+ ...wire ? { legal_wire_actions: wire } : { legal_actions: position.legal_actions, legal_actions_complete: position.legal_actions_complete },
5598
+ // Card, tile and other games without a labeled board still need their seat-visible state.
5599
+ ...position.board_labeled || !position.visible_state ? {} : { visible_state: position.visible_state },
5600
+ ...position.access_requests?.length ? { access_requests: position.access_requests } : {}
5601
+ };
5602
+ }
5603
+ var isPosition = (value) => "protocol_version" in value && "you" in value && "board_size" in value;
5604
+ function briefResult(result) {
5605
+ if (isPosition(result)) {
5606
+ const { next: next2, ...position2 } = result;
5607
+ return { ...briefPosition(position2), next: next2 };
5608
+ }
5609
+ if (!("position" in result) || !result.position || typeof result.position !== "object") return result;
5610
+ const { position, next, ...rest } = result;
5611
+ return { ...rest, ...briefPosition(position), next };
5612
+ }
5613
+
5571
5614
  // packages/cli/src/main.ts
5572
5615
  var agentCliVersion2 = package_default.version;
5573
5616
  function integer(value, label, min, max) {
@@ -5614,6 +5657,7 @@ try {
5614
5657
  "after-revision": { type: "string" },
5615
5658
  timeout: { type: "string" },
5616
5659
  wait: { type: "boolean" },
5660
+ brief: { type: "boolean" },
5617
5661
  update: { type: "boolean" },
5618
5662
  json: { type: "boolean" },
5619
5663
  help: { type: "boolean", short: "h" },
@@ -5650,7 +5694,7 @@ try {
5650
5694
  if (values.server && server !== defaultServer && !values.site) throw Error("Provide --site for your custom server's browser frontend.");
5651
5695
  const goSize = integer(values["go-size"] ?? "9", "go-size", 9, 19);
5652
5696
  if (goSize !== 9 && goSize !== 13 && goSize !== 19) throw Error("go-size must be 9, 13 or 19.");
5653
- result = await createRoom({ server, site: values.site ?? "https://leafarcade.com", game: values.game ?? "connect-four", name: values.name, capacity: values.capacity === void 0 ? void 0 : integer(values.capacity, "capacity", 2, 8), goSize, open: values.open, access: values.access, allowSpectators: !values["no-spectators"] }, controller.signal);
5697
+ result = await createRoom({ server, site: values.site ?? "https://leafarcade.com", game: values.game ?? "connect-four", name: values.name, capacity: values.capacity === void 0 ? void 0 : integer(values.capacity, "capacity", 2, 8), goSize, open: values.open, access: values.access, allowSpectators: !values["no-spectators"], brief: values.brief }, controller.signal);
5654
5698
  } else if (command === "rules") result = agentRules(values.game);
5655
5699
  else {
5656
5700
  const server = serviceUrl(values.server ?? defaultServer);
@@ -5727,13 +5771,13 @@ try {
5727
5771
  await rpc(session, "play", { revision, action }, false, controller.signal);
5728
5772
  const played = agentPosition(await readRoom(session, controller.signal));
5729
5773
  result = values.wait && played.phase === "playing" && !played.paused && !played.turn?.yours && !played.you.left ? { ...await waitForRoom(session, played.revision, waitSeconds, controller.signal), played: { revision: played.revision, last_move: played.last_move } } : { event: "played", position: played };
5730
- } else result = command === "legal-actions" ? { protocol_version: 1, room, revision: position.revision, legal_actions: position.legal_actions, legal_actions_complete: position.legal_actions_complete, action_types: position.action_types, ...position.legal_wire_actions_complete ? { legal_wire_actions_complete: true, legal_wire_actions: position.legal_wire_actions } : {}, next: nextStep(position) } : { ...position, next: nextStep(position) };
5774
+ } else result = command === "legal-actions" ? { protocol_version: 1, room, revision: position.revision, legal_actions: position.legal_actions, legal_actions_complete: position.legal_actions_complete, action_types: position.action_types, ...position.legal_wire_actions_complete ? { legal_wire_actions_complete: true, legal_wire_actions: position.legal_wire_actions } : {}, next: nextStep(position, void 0, values.brief) } : { ...position, next: nextStep(position, void 0, values.brief) };
5731
5775
  }
5732
5776
  }
5733
5777
  }
5734
5778
  }
5735
- if ("position" in result && result.position && !("next" in result)) result = { ...result, next: nextStep(result.position, result.event) };
5736
- output(result);
5779
+ if ("position" in result && result.position && !("next" in result)) result = { ...result, next: nextStep(result.position, result.event, values.brief) };
5780
+ output(values.brief ? briefResult(result) : result);
5737
5781
  }
5738
5782
  } catch (error) {
5739
5783
  const code = error instanceof ServiceError ? error.code : "CLI_ERROR";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leafarcade/cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "private": false,
5
5
  "description": "Create private browser board games and play with your AI agent. Includes 53 games and an installable Codex/Claude skill.",
6
6
  "type": "module",
@@ -20,10 +20,12 @@ leafarcade game create --game connect-four --name "My agent" # add --open on
20
20
 
21
21
  1. Give the human the returned `url`. Do not create a second table, even if something looked wrong. Use `leafarcade game open --room CODE` to reopen it.
22
22
  2. `leafarcade game wait --room CODE --after-revision R` until the human has joined. Then `leafarcade game start --room CODE --revision R`.
23
- 3. On your turn: `leafarcade game play --room CODE --revision R --column 4 --wait`. This moves, then waits until it is your turn again or the match ends. Repeat.
23
+ 3. On your turn: `leafarcade game play --room CODE --revision R --column 4 --wait --brief`. This moves, then waits until it is your turn again or the match ends. Repeat.
24
24
 
25
25
  Every JSON response has a `next` field with the exact command to run next. Follow it.
26
26
 
27
+ Add `--brief` to `status`, `wait`, `start` and `play`. It returns only `status` (room, revision, phase, your piece, whose turn, outcome), `board_labeled`, `last_move`, the legal moves and `next`, which keeps the flag. Games without a labeled board (cards, tiles) also get `visible_state`. Drop `--brief` once if you need the full position: player list, raw state or rules.
28
+
27
29
  `leafarcade help games` lists game IDs. `--capacity N` picks a supported table size; the default is the game's minimum. Each additional agent needs its own `LEAFARCADE_CONFIG_DIR` and seat.
28
30
 
29
31
  ## Joining an existing table