Setup
Prerequisites
- Node.js 24+
- Chrome 141+
Install
npm install
npm run build
Install from source
- Open Chrome and navigate to
chrome://extensions - Enable “Developer mode” (toggle in the top-right corner)
- Click “Load unpacked” and select this project’s root directory
- The BGA Assistant icon appears in the Chrome toolbar
Data processing CLI
Two CLI scripts run the extension’s pipeline stages locally for debugging, using the same artifacts saved by the ZIP download.
game-log
Reads raw_data.json, auto-detects the game from the gameName field, runs the game-specific log processor, and writes game_log.json to the same directory. If the file lacks a gameName field (e.g. older exports), specify --game <name> (innovation, azul, thecrewdeepsea, nucleum).
npm run game-log -- data/bgaa_823235522_23/raw_data.json
game-state
Reads game_log.json, runs the engine and serialization pipeline, and writes game_state.json to the same directory. If the file lacks a gameName field, specify --game <name>. With --debug, also creates a game_states/ subfolder with per-entry snapshots named by turn and entry index (0001_0042.json, 0001_0043.json, etc.).
npm run game-state -- data/bgaa_823235522_23/game_log.json [--debug]
Project structure
manifest.json Chrome extension manifest (v3, side panel)
sidepanel.html Side panel page shell (Vite HTML entry point)
scripts/
game-log.ts CLI: raw_data.json → game_log.json
game-state.ts CLI: game_log.json → game_state.json (+ --debug snapshots)
package.ts CLI: build + create Chrome Web Store ZIP
migrate-fixture.ts One-shot migration for legacy fixture player maps
src/
background.ts Service worker: orchestration, side panel management, live tracking
pipeline.ts Pure pipeline logic shared by background.ts and CLI scripts
extract.ts Content script: BGA data extraction (MAIN world)
time-tracking.ts Play-time tracker: sessions, table classification, CSV export/import
sidepanel/
sidepanel.ts Receives data, triggers render, handles downloads
sidepanel.css Dark theme, font declarations, card grids, tooltips
settings.ts localStorage persistence: loadSetting/saveSetting with typed defaults
turn_history_settings.ts Settings shared by every game that shows a turn history, and their menu rows
inpage_settings.ts chrome.storage.local settings for what is changed on BGA's own page
global_menu.ts The help page's eye menu: settings that apply to every BGA table
models/
types.ts Shared BGA types (GameName, RawPacket, RawExtractionData)
engine/
constraint.ts Game-agnostic constraint propagation kernel (shared by Crew and Innovation)
turn_history.ts Game-agnostic TurnAction shape and recent-turns grouping
games/
innovation/
types.ts Innovation types: Card, CardInfo, CardDatabase, enums, actions
process_log.ts Raw BGA packets -> structured Innovation game log
game_state.ts GameState interface (zone data), createGameState(), cardsAt()
game_engine.ts GameEngine class: state tracking + constraint propagation
serialization.ts toJSON/fromJSON serialization for side panel transport
turn_history.ts Innovation action types over the shared turn-history kernel
render.ts GameState + GameEngine -> HTML string via template literals
config.ts Section layout config, visibility/layout defaults
display.ts Innovation display menu: sections, in-page log, cards, police line
compact_header.ts In-page surface: BGA's three header rows folded into one (every game)
compact_header.css The one-row layout those DOM moves make possible
sticky_panels.ts In-page surface: the pinned right column (every game)
sticky_panels.css The sticky rules for both modes, and the panels' backdrop
simplified_cards.ts In-page surface: the panel's compact card on BGA's own table
simplified_cards.css That card's interior at panel scale, and the opponent-hand slot
action_tint.ts In-page surface: hazard stripes while you must act out of turn
action_tint.css The stripes and their animation
mini_card.css The panel's compact card, shared by panel, ZIP export and table
card_tip.css Card tooltip geometry, shared by panel, in-page log and table
azul/
process_log.ts Raw BGA packets -> structured Azul game log
game_state.ts Azul bag/discard/wall tracking
render.ts AzulGameState -> HTML tile count table
display.ts Azul display menu (shimmer toggle)
styles.css Azul-specific CSS (tile table, shimmer animation)
crew/
types.ts Crew types: suit constants, ALL_SUITS, CrewCard, card key helper
process_log.ts Raw BGA packets -> structured Crew game log
game_state.ts CardGuess, Trick, CrewGameState interface, createCrewGameState() factory
game_engine.ts processCrewState() pipeline, void detection, communication constraints, playerSuitStatus()
serialization.ts toJSON/fromJSON serialization for side panel transport
render.ts CrewGameState -> HTML card grid, suit matrix, trick history
styles.css Crew-specific CSS (card grid, suit colors, matrix, trick table)
nucleum/
types.ts Nucleum city names and turn-history action detail types
process_log.ts Raw BGA packets -> structured Nucleum game log
game_state.ts NucleumGameState (the turn history) + toJSON/fromJSON
game_engine.ts Log entries -> turn actions: grouping, undo rewinds, railway links
render.ts NucleumGameState -> HTML turn history
display.ts Nucleum display menu (shared history rows, compact player panels)
styles.css Nucleum-specific CSS (history list, out-of-turn actor)
player_panels.ts In-page surface: BGA's player panels folded onto one line
player_panels.css The fold, every rule scoped under the mount's class
render/
help.ts Help page content
icons.ts Shared icon utilities
player.ts Shared player-color helper: inline --player-color from a PlayerInfo
toggle.ts Shared toggle logic (side panel + ZIP export); tooltips are CSS-only
turn_history_rows.ts Turn-history rows for every game and both surfaces
turn_history.css Row appearance, shared by the panel and the in-page log
inpage_log.ts Mount function for the turn history in BGA's log column
inpage_log.css In-page-only delta over the shared row styles
assets/
bga/
innovation/
card_info.json Card database (base, echoes, cities, and artifacts cards plus specials)
icons/ Resource and hex icon PNGs
cards/ Full card face images (WebP, for tooltips)
sprites/ Card sprite sheets (gitignored)
azul/
tiles/ Tile color SVGs (5 colors)
fonts/ Bundled web fonts (Russo One, Barlow Condensed woff2)
extension/ Extension toolbar icons
docs/
screenshots/
screenshots.json Screenshot manifest: what each frame shows and its replacement policy
capture/
lib/render.ts Renders a shot's subject to standalone HTML from a committed fixture
lib/shoot.py Headless Chromium capture (Playwright), framed to a uniform margin
fixtures/ Game logs the captures render, committed so a shot is reproducible
<id>.sh One script per screenshot, named for its manifest entry
Documentation screenshots
The shots in docs/pages/ that show the side panel are captured by script rather than by hand — bash docs/screenshots/capture/<id>.sh rebuilds one. Each drives the panel’s own renderers against a committed fixture, so a capture needs no BGA session, no live table and no login, and reruns identically on any machine. docs/screenshots/screenshots.json records what every frame shows and whether it may be replaced automatically; the shots of BGA’s own page are marked never, since only a real table can produce them.
Data flow
See Data Flow Architecture for the full data flow architecture, message protocols, and connection management details.
Testing
Tests use vitest and cover the full pipeline: types, log processing, game engine, serialization, rendering, and extension entry points.
npm test # Run all tests
npx vitest run --coverage # Run with coverage report
Release
.github/workflows/build.yml runs on every push to main and on every PR — it lints, tests, packages, and uploads the zip as an Actions artifact (30-day retention). When a v* tag is pushed, it additionally creates a GitHub Release with the zip attached.
To cut a new release, run the project-local /release skill. It bumps manifest.json + package.json, commits and pushes, drafts release notes from commits since the last tag for review, pushes the vX.Y.Z tag, monitors the workflow run, and updates the release notes once the zip is published. The tag must match manifest.json exactly — CI fails fast otherwise.
Then run the /publish-chrome-extension skill. It downloads the release zip, compares the new package against the version the store already holds (the Web Store API reports it as crxVersion), uploads it, and submits it for review. Anything on the dashboard’s Privacy practices or Store listing tabs — permission justifications, single purpose, store description — has to be edited there by hand: the API can neither read nor write it, so the repo deliberately keeps no copy of it.
Local builds via npm run package still work for testing, but the CI build is the canonical artifact for Web Store uploads.