@trolleroof/tui 0.2.3

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 ADDED
@@ -0,0 +1,349 @@
1
+ # tui-gamepigeon
2
+
3
+ Play familiar single-player games in a lightweight HTML overlay while your agents work. Game engines are shared between the overlay and the recording pipeline so interaction traces stay game-agnostic when recording is enabled.
4
+
5
+ ## Play
6
+
7
+ Build the overlay once, then launch the arcade menu or a specific game:
8
+
9
+ ```bash
10
+ bun install
11
+ bun run build:overlay # web bundle + native Tauri shell (needs Rust)
12
+ bun start # overlay menu
13
+ bun start snake # jump straight into Snake
14
+ # or after `bun run build`: gamepigeon / gamepigeon snake
15
+ ```
16
+
17
+ In the overlay: arrow keys or WASD to move, `Enter` to select, `r` to restart, `Esc` or `q` to hide. Drag the top bar to move the window; use the expand control to resize it.
18
+
19
+ ## Install: local vs. global
20
+
21
+ There are two ways to run the Arcade overlay. **Local** runs it straight from this
22
+ checkout for development. **Global** installs it once so the plain `claude` command
23
+ opens Arcade in every project.
24
+
25
+ Both need [Bun](https://bun.sh) (`>=1.3`), macOS 11+ or Windows 10+, and Rust only
26
+ when building the overlay binary from source.
27
+
28
+ ### Local (from this checkout)
29
+
30
+ Build the overlay and wire the harness adapters to this working copy:
31
+
32
+ ```bash
33
+ bun install
34
+ bun run build:overlay # overlay web bundle + native Tauri shell (needs Rust)
35
+ bun run install:adapters # installs Codex / Claude / OpenCode adapters -> this checkout
36
+ ```
37
+
38
+ Restart your harness. The generated adapters live at `~/.codex/skills/arcade`,
39
+ `~/.claude/skills/arcade`, and `~/.config/opencode/commands/arcade.md`; all execute
40
+ `scripts/overlay.ts` from this checkout.
41
+
42
+ For local harness development, one command builds the current checkout, replaces any
43
+ stale overlay, and points at the debug Tauri binary instead of the packaged release:
44
+
45
+ ```bash
46
+ bun run claude:dev # Claude Code — loads this checkout via --plugin-dir .
47
+ bun run codex:dev # Codex — workspace plugin + $arcade adapter from this checkout
48
+ bun run antigravity:dev # Antigravity — .agents/plugins/ workspace plugin + agy
49
+ ```
50
+
51
+ Claude appears as `/tui-gamepigeon:arcade`. Codex uses the `$arcade` skill (or `/arcade` picker).
52
+ Antigravity picks up `.agents/plugins/tui-gamepigeon/` from the repo root. All three set
53
+ `GAMEPIGEON_SKIP_UPDATE_CHECK=1` so local dev is not fighting the release auto-updater.
54
+
55
+ To open the overlay directly without a harness:
56
+
57
+ ```bash
58
+ bun run start # menu
59
+ bun run start 2048 # a specific game
60
+ bun run preview:overlay # browser preview at 470×650 (no Tauri)
61
+ bun run dev:overlay # rebuild web bundle + debug shell, then show
62
+ ```
63
+
64
+ #### Browser preview
65
+
66
+ Agents (and humans) can iterate on overlay layout without a Rust build:
67
+
68
+ ```bash
69
+ bun run preview:overlay
70
+ # → http://127.0.0.1:4173/ (opens your browser)
71
+ bun run preview:overlay -- --game=connect4
72
+ ```
73
+
74
+ The preview frames the real overlay UI at the production sizes (**470×650** compact, **720×780** expanded). Titlebar **↗ / ↙** toggles the frame; Hide and Shutdown are no-ops in the browser. Open a game from the menu, or pass `--game=<id>` / use `?game=<id>` (optional `&variation=classic`).
75
+
76
+ ### Global (installed once, works everywhere)
77
+
78
+ For Codex (or another supported harness), the one-command install is:
79
+
80
+ ```bash
81
+ npx -y bunx @trolleroof/tui@latest install --codex
82
+ ```
83
+
84
+ Replace `--codex` with `--claude`, `--opencode`, `--hermes`, `--openclaw`, or
85
+ `--all`. This uses npm to fetch Bun for the command, then writes the harness adapter.
86
+ Restart the harness when it finishes.
87
+
88
+ Install the plugin from the GitHub marketplace so the normal `claude` command runs
89
+ the lifecycle hooks in every project:
90
+
91
+ ```bash
92
+ claude plugin marketplace add Trolleroof/tui-gamepigeon
93
+ claude plugin install tui-gamepigeon@tui-gamepigeon --scope user
94
+ ```
95
+
96
+ Optionally enable Claude's marketplace refresh under `/plugin` → **Marketplaces** →
97
+ **tui-gamepigeon**, and point the status line at the Arcade update shim once:
98
+
99
+ ```json
100
+ "statusLine": {
101
+ "type": "command",
102
+ "command": "bun ~/.gamepigeon/status-line.ts"
103
+ }
104
+ ```
105
+
106
+ The packaged plugin exposes `/tui-gamepigeon:arcade` and `/tui-gamepigeon:arcade-update`;
107
+ the standalone adapter provides Claude's shorter `/arcade`.
108
+
109
+ ## Optional account
110
+
111
+ Arcade requires an account. On first launch the plugin opens the shared web login, where a new player can create an account or sign in with email, Google, or GitHub. Later launches reuse the private local session.
112
+
113
+ ```bash
114
+ gamepigeon auth signup
115
+ gamepigeon auth login
116
+ gamepigeon auth oauth google
117
+ gamepigeon auth oauth github
118
+ gamepigeon auth status
119
+ gamepigeon auth logout
120
+ ```
121
+
122
+ Production defaults to `https://runretroarcade.com/login.html`, `https://auth.runretroarcade.com`, and `https://api.runretroarcade.com`. Override them for local development with `GAMEPIGEON_LOGIN_URL`, `GAMEPIGEON_AUTH_URL`, and `GAMEPIGEON_BACKEND_URL`. The browser returns the Better Auth bearer to a localhost callback; it is stored in `auth.json` and atomically mirrored to `backend.conf`, both with user-only permissions. That same bearer authenticates `/v1/me`, trace uploads, and completed-game results. If that validation or an upload fails, Arcade marks the account chip **Sync paused** while keeping gameplay data queued locally. Logout clears both credentials without deleting local games or traces.
123
+
124
+ ## Coding harness adapters
125
+
126
+ The bundled adapters give Codex CLI, Claude Code, and OpenCode the same Arcade launcher. GamePigeon opens a centered, borderless HTML overlay above the active desktop. It uses the operating system's installed webview through a small Tauri shell, not Chrome or Electron.
127
+
128
+ Requirements:
129
+
130
+ - Bun
131
+ - macOS 11+ or Windows 10+
132
+ - A packaged overlay binary, or Rust when building it from source
133
+
134
+ The overlay is a single-instance helper with no Dock icon on macOS and no taskbar or console entry on Windows. Hiding it keeps the game state alive; invoking Arcade again restores and centers the existing window. It works from regular terminals and does not require tmux, a browser, a local server, or an extra setup command after plugin installation.
135
+
136
+ Install the dependencies and all three harness adapters from this checkout:
137
+
138
+ ```bash
139
+ bun install
140
+ bun run build:overlay
141
+ bun run install:adapters
142
+ ```
143
+
144
+ Restart the harness after installation. In Claude Code, submitting any prompt opens or restores Arcade while Claude works. In Codex, invoke `$arcade` directly, or type `/arcade` to filter the picker and select **Arcade**. Claude Code and OpenCode receive a bare `/arcade` command. The packaged Claude plugin uses its required namespace: `/tui-gamepigeon:arcade`. Codex does not register custom top-level slash commands; its `/arcade` interaction is picker discovery for the `$arcade` skill, like other Codex skills.
145
+
146
+ For local harness development, one command builds the current checkout, replaces any stale overlay, and loads this repo instead of an installed copy:
147
+
148
+ ```bash
149
+ bun run claude:dev # Claude Code
150
+ bun run codex:dev # Codex CLI
151
+ bun run antigravity:dev # Antigravity (agy)
152
+ ```
153
+
154
+ `SessionStart` snaps a still-running overlay back to the menu (without popping the window open) so a new session never resumes mid-game, `UserPromptSubmit` shows the overlay, `Stop` pauses it with an `Enter` shortcut back to the coding prompt, and `SessionEnd` shuts down the helper.
155
+
156
+ To enable the hooks for the normal `claude` command, install the GitHub marketplace once:
157
+
158
+ ```bash
159
+ claude plugin marketplace add Trolleroof/tui-gamepigeon
160
+ claude plugin install tui-gamepigeon@tui-gamepigeon --scope user
161
+ ```
162
+
163
+ Claude Code does not silently install plugin updates. Arcade uses a WOZ-style detect-and-nudge flow instead (see **Auto-updates** below).
164
+
165
+ The generated adapters live at `~/.codex/skills/arcade`, `~/.claude/skills/arcade`, and `~/.config/opencode/commands/arcade.md`; all execute `scripts/overlay.ts` from this checkout. The installer refuses to replace an unrelated command with the same name. Press `Esc` or `q` to hide the game, drag its top bar to move it, and use the expand button to resize it.
166
+
167
+ ## Auto-updates
168
+
169
+ Arcade updates in three layers. Claude Code cannot silently install a new plugin package for you — the user still confirms the upgrade — but detection and overlay binaries are automatic.
170
+
171
+ 1. **Detect + nudge (plugin scripts/hooks)**
172
+ On every new session, a `SessionStart` hook runs `scripts/plugin-update.ts --check`. It compares the installed plugin's `gitCommitSha` against `main` on GitHub. When they differ, it writes `~/.gamepigeon/update-available.json` and refreshes `~/.gamepigeon/status-line.ts`. The first prompt of the session also runs `--nudge`, which injects a reminder to run `/tui-gamepigeon:arcade-update`. Local directory marketplaces and `bun run claude:dev` (`GAMEPIGEON_SKIP_UPDATE_CHECK=1`) skip the check.
173
+
174
+ 2. **Manual plugin upgrade**
175
+ Run `/tui-gamepigeon:arcade-update` (or the equivalent `claude plugin marketplace update tui-gamepigeon` + `claude plugin update tui-gamepigeon@tui-gamepigeon`). Then `/reload-plugins` or restart Claude Code so the new scripts load. The skill clears the update flag on success.
176
+
177
+ 3. **Overlay binary download**
178
+ Overlay UI changes ship as new binaries. Pushing anything under `overlay/`, `tauri/`, `scripts/overlay*`, or the shared game code triggers the `Overlay` workflow, which builds and tests macOS and Windows binaries. Publishing is a separate, deliberate step: the workflow cuts a release only when `package.json`'s version names one that does not exist yet (see [Releasing](#releasing)). When it does, it publishes the binaries as `overlay-v<version>` and commits `overlay-release.json` pointing at it. On the next session, `scripts/overlay.ts` compares that manifest against `~/.gamepigeon/bin/current`, downloads the new binary (via `gh` for private repos, plain HTTPS once public), quits any overlay still running the old build, and launches the fresh one. Download failures fall back to the previously installed binary, so offline sessions keep working.
179
+
180
+ One-time status line setup (optional, but closest to the WOZ experience):
181
+
182
+ ```json
183
+ "statusLine": {
184
+ "type": "command",
185
+ "command": "bun ~/.gamepigeon/status-line.ts"
186
+ }
187
+ ```
188
+
189
+ Add that under `~/.claude/settings.json`. The shim is rewritten on each session check and does not hardcode a plugin cache path, so it stays valid across upgrades.
190
+
191
+ The repository also includes native plugin manifests for marketplace distribution. Local dev commands build the checkout and load it directly:
192
+
193
+ ```bash
194
+ bun run claude:dev # Claude Code — --plugin-dir .
195
+ bun run codex:dev # Codex — workspace plugin + adapter
196
+ bun run antigravity:dev # Antigravity — .agents/plugins/ at repo root
197
+ ```
198
+
199
+ Claude namespaces plugin-provided skills, so the packaged forms appear as `/tui-gamepigeon:arcade` and `/tui-gamepigeon:arcade-update`. The standalone adapter provides Claude's shorter `/arcade` form.
200
+
201
+ Codex CLI and OpenCode invoke the overlay through their Arcade adapter. Claude Code additionally exposes lifecycle hooks, which is why prompt-start and response-stop behavior is automatic there.
202
+
203
+ To bypass a harness and open the overlay directly:
204
+
205
+ ```bash
206
+ bun run start
207
+ bun run start 2048
208
+ ```
209
+
210
+ ## Launch games
211
+
212
+ | Game | Status | Controls |
213
+ |---|---|---|
214
+ | 2048 | ✅ playable | arrows/wasd slide |
215
+ | Tetris | ✅ playable | arrows move, z/x rotate, space drop |
216
+ | Snake | ✅ playable | arrows/wasd steer |
217
+ | Minesweeper | ✅ playable | arrows move, space reveal, f flag |
218
+ | Sokoban | ✅ playable | arrows push, z undo |
219
+ | Connect Four | ✅ playable | ←/→ aim, space drop |
220
+ | Sudoku | ✅ playable | arrows move, 1-9 place, 0/x clear |
221
+
222
+ ## Interaction traces
223
+
224
+ The recording layer under `src/recording/` durably journals episodes under `~/.tui-gamepigeon/recordings/` and splits them into immutable upload chunks. Every schema-v2 event includes `event_id`, `user_id`, `chunk_id`, and `game_id`.
225
+
226
+ - Policy-visible events preserve input impulses, timing, score/reward changes, and episode completion.
227
+ - Privileged engine checkpoints and realized random effects support deterministic replay without leaking hidden state into policy input.
228
+ - Local writes continue when upload is disabled or temporarily unavailable; pending chunks retry on later sessions.
229
+ - Completed games also enter a separate durable score outbox. Quits, restarts, and client shutdowns never submit a score.
230
+
231
+ ## Local score history
232
+
233
+ Every naturally completed overlay game appends one JSON object to
234
+ `~/.tui-gamepigeon/scores/history.jsonl`, even without a network connection.
235
+ The directory is user-only (`0700`) and the JSONL file is `0600`. Quits,
236
+ restarts, and shutdowns do not create entries.
237
+
238
+ Each schema-v1 row contains `game_id`, `game_type`, `variation_id`, `score`,
239
+ `success`, `completed_at` (the game-end timestamp), `duration_ms`, and
240
+ `platform`, plus the resulting `personal_best` and `high_score_updated`.
241
+ Personal bests are calculated per `game_type`; duplicate `game_id` writes are
242
+ idempotent.
243
+
244
+ The history is device-only today. A future remote score feature belongs in the
245
+ sync backend, not the auth Worker D1; it should merge records by `game_id` and
246
+ keep local logging independent of account and network availability.
247
+
248
+ Set the opaque per-user bearer token issued by Retro Backend. Requests default
249
+ to the sync backend at `https://api.runretroarcade.com`; set
250
+ `GAMEPIGEON_BACKEND_URL` only when targeting local or staging infrastructure:
251
+
252
+ ```bash
253
+ export GAMEPIGEON_BACKEND_TOKEN=retro_...
254
+ # optional override:
255
+ # export GAMEPIGEON_BACKEND_URL=http://127.0.0.1:8787
256
+ ```
257
+
258
+ The HTML overlay journals each seeded action sequence in local storage, rebuilds replay-safe schema-v2 engine-state traces at episode end, and hands immutable chunks plus completed-game results to Tauri. Tauri validates and durably queues them before any network call, then resolves the canonical `/v1/me` user and drains both outboxes in the background. Chunks live under `~/.tui-gamepigeon/recordings/overlay-pending/` until `/v1/game-event-chunks` accepts them; results live under `~/.tui-gamepigeon/recordings/results/overlay-pending/` until `/v1/game-results` accepts them. Interrupted and offline sessions recover on the next launch, while cloud-acknowledged payloads move to the matching `overlay-completed/` directory.
259
+
260
+ Export policy-visible human demonstrations into leakage-safe BC splits:
261
+
262
+ ```bash
263
+ bun run export:bc -- --output ./bc-dataset
264
+ ```
265
+
266
+ The exporter reads completed terminal and overlay chunks by default, joins each human control to its referenced observation and outcome across chunk boundaries, drops environment actions and no-op controls, and writes `train.jsonl`, `validation.jsonl`, `test.jsonl`, and `manifest.json`. Splits are deterministic by `game_id`, keeping every episode in only one split. Use repeatable `--input <file-or-directory>` arguments for backend downloads or `--include-noops` when those actions are intentionally part of the policy.
267
+
268
+ Before recording, the client finishes local recovery and resolves the token's canonical user through `GET /v1/me`. It uploads only trace chunks and results owned by that user; pending data from another local account is left untouched. If configured identity lookup fails, gameplay starts without recording that episode and retries identity on later games and drains. `GAMEPIGEON_INGEST_URL` and `GAMEPIGEON_INGEST_API_KEY` are deprecated compatibility aliases retained through `0.x` and scheduled for removal in `1.0`. They do not opt into the default backend: a legacy key set without `GAMEPIGEON_INGEST_URL` leaves sync disabled rather than uploading to `https://api.runretroarcade.com`.
269
+
270
+ This repository owns the client-side producer and versioned wire contracts. The ingestion service, score database, queue consumers, and object-storage archive live in the backend repository. The v1 and v2 request fixtures under `contracts/` can be reused by that backend's contract tests.
271
+
272
+ ## Analytics
273
+
274
+ Basic product analytics (DAU / feature usage) via PostHog are separate from interaction traces, which are for policy training rather than usage metrics.
275
+
276
+ ```bash
277
+ export POSTHOG_API_KEY=phc_...
278
+ # optional, defaults to https://us.i.posthog.com
279
+ export POSTHOG_HOST=https://us.i.posthog.com
280
+ ```
281
+
282
+ Without `POSTHOG_API_KEY` set, analytics calls are no-ops — gameplay is never affected. Events currently captured: `app.opened`, `account.signed_up`, `account.signed_in`, `game.started`, `game.ended`. Signed-in events use the account's pseudonymous user ID; guest events use an anonymous per-install ID stored at `~/.tui-gamepigeon/device_id`.
283
+
284
+ The PostHog SDK batches events with its defaults, uses the CloudEvent ID as `$insert_id` for retry deduplication, and flushes during graceful shutdown.
285
+
286
+ Product events use a CloudEvents 1.0 envelope with `specversion`, `id`, `source`, `type`, `time`, `datacontenttype`, optional `subject`, and typed `data`. Client events default to source `/tui-gamepigeon/client`; plugin, website, and backend producers use their own source. Event IDs reuse the recording layer's `evt_` identifier format.
287
+
288
+ | Funnel | Event types | Allowed data |
289
+ | --- | --- | --- |
290
+ | Awareness | `site.visited` | `referrer_host`, `campaign` |
291
+ | Acquisition | `account.signed_up`, `account.signed_in` | `method` |
292
+ | Activation | `game.started`, `game.ended` | `game_type`, `variation_id`, outcome, score, success |
293
+ | Retention | `app.opened`, gameplay events | `app_version`, platform, gameplay data |
294
+ | Revenue | `payment.succeeded` | `plan`, `currency`, `amount_minor` |
295
+ | Referral | `referral.created`, `referral.redeemed` | channel |
296
+ | Plugin lifecycle | `plugin.launched`, `plugin.reattached`, `agent.task_completed` | harness |
297
+
298
+ The website owns `site.visited`; invite and payment services own their corresponding events. Until those producers exist, the names above reserve the shared contract without fabricating client events. Analytics must never include terminal frames, trajectories, prompts, repository contents, credentials, email addresses, or raw provider payloads.
299
+
300
+ The live [GamePigeon AARRR client dashboard](https://us.posthog.com/project/434929/dashboard/1866662) covers the client-owned acquisition, activation, and retention signals. See [`docs/analytics.md`](docs/analytics.md) for event ownership, saved insights, and the CLI verification runbook.
301
+
302
+ ## Architecture
303
+
304
+ - `src/index.ts` — thin CLI: `gamepigeon` opens the overlay; `gamepigeon auth` handles accounts.
305
+ - `src/core/game.ts` — gym-style `Game` interface every title implements (`reset(seed)` / `step(action)` / `state()`), shared by the overlay and recorder.
306
+ - `src/core/rng.ts` — seedable PRNG (mulberry32) for reproducible episodes.
307
+ - `src/games/*/` — pure game engines (unit-tested); HTML rendering lives in `overlay/renderers.ts`.
308
+ - `src/recording/` — causal events, durable chunks, recovery, and upload transport.
309
+ - `src/results/` — completed-game result outbox, recovery, and score delivery.
310
+ - `src/sync/` — authoritative backend identity and the single-flight upload coordinator.
311
+ - `src/analytics/posthog.ts` — PostHog Node client wrapper for product analytics.
312
+ - `scripts/overlay.ts` — launcher, release download, and harness hook entry point.
313
+ - `overlay/` — bundled HTML/CSS/TypeScript frontend and deterministic local game session.
314
+ - `tauri/` — single-instance native shell with hidden Dock/taskbar presence.
315
+
316
+ ## Development
317
+
318
+ ```bash
319
+ bun run test
320
+ bun run test:adapters
321
+ bun run test:launcher
322
+ bun run typecheck
323
+ bun run build # emits dist/, exposes the `gamepigeon` bin
324
+ bun run build:overlay:web # overlay bundle only
325
+ bun run preview:overlay # browser preview (470×650 / 720×780 frame)
326
+ bun run dev:overlay # debug Tauri shell + show overlay
327
+ ```
328
+
329
+ Bun runs the TypeScript source directly in development and builds a Bun-targeted executable for distribution.
330
+
331
+ ## Releasing
332
+
333
+ Releases are gated on the version, not on commits. Pushing to `main` builds and tests the overlay but publishes nothing; a release appears only when `package.json`'s version names one that has no release yet. This keeps routine pushes from minting a release each time.
334
+
335
+ To cut one, bump the version and push:
336
+
337
+ ```bash
338
+ bun run version:bump # 0.2.1 -> 0.2.2
339
+ bun run version:bump minor # 0.2.1 -> 0.3.0
340
+ bun run version:bump major # 0.2.1 -> 1.0.0
341
+ bun run version:bump 1.4.0 # explicit
342
+ bun run version:bump --no-commit
343
+ ```
344
+
345
+ The version lives in four manifests — `package.json`, `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, and `.claude-plugin/marketplace.json` — and the script rewrites all of them together. Bump by hand and they drift, which is why `scripts/version-bump.test.ts` asserts they agree.
346
+
347
+ The script commits but never pushes; publishing stays an explicit action. On the next push to `main`, CI builds the binaries, publishes them as `overlay-v<version>`, and commits an `overlay-release.json` pointing at that tag, which is what installed plugins download on their next session.
348
+
349
+ Adding a new file that carries the version means adding it to `VERSION_FILES` in `scripts/version-bump.ts`, or it silently stops being bumped.