Agent skill

Bringup

by jenissimo in jenissimo/bottleship

Drive and observe the BottleShip emulator to bring up a game, using the AI-agent harness (window.BS.harness + bun tools/harness.ts).

Apache-2.0Auto-check passedAgent Workflows

Install Bringup

skills CLI
$ npx skills add jenissimo/bottleship --skill bringup -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install jenissimo/bottleship bringup --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/jenissimo/bottleship.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/bringup .claude/skills/bringup && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
bringup
GitHub stars
132
Token cost
~2.9k tokens
SKILL.md length
1,292 words
Files
1
Skills in repo
2
Repo updated
First seen
Licence
Apache-2.0

At a glance

Drive and observe the BottleShip emulator to bring up a game, using the AI-agent harness (window.BS.harness + bun tools/harness.ts).

  • Works in 6 steps: Preconditions — harness up → Drive → Observe → …
  • Making it reach a menu/level
  • SKILL.md covers 1. Preconditions — harness up, 2. Drive, 3. Observe and 4. Diagnose, plus 4 more sections
  • Calls bun

What it does

Bringup is an agent skill from jenissimo/bottleship. Drive and observe the BottleShip emulator to bring up a game, using the AI-agent harness (window.BS.harness + bun tools/harness.ts). Use when loading a game, making it reach a menu/level, diagnosing why it crashes/hangs/renders black, or writing a repeatable bring-up/regression script. Operationalizes CLAUDE.md's debugging workflow on top of the harness verbs. Use the project's CDP harness, not a browser MCP (chrome-devtools MCP is disabled for this project).

Its SKILL.md is about 2.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Agent Workflows, covering Browser testing and Agent instruction files. It works with Model Context Protocol, Chrome DevTools, TypeScript and WebAssembly. The repository describes itself as: Run classic Windows games in your browser. A high-level-emulation engine that boots real x86 Windows executables and reimplements Win32, COM and DirectDraw/Direct3D on WebGPU…. The licence is Apache-2.0.

When your agent uses it

  • Making it reach a menu/level
  • Diagnosing why it crashes/hangs/renders black
  • Writing a repeatable bring-up/regression script

Example prompts

  • “s debugging workflow on top of the harness verbs. Use the project”
  • “/bringup”

Workflow steps

6 steps, taken from the step headings in SKILL.md.

  1. Preconditions — harness up
  2. Drive
  3. Observe
  4. Diagnose
  5. Hypothesis from DATA, not reasoning
  6. Fix → re-run → keep tools, drop probes

What it can do on your machine

Read from SKILL.md and the folder at commit 77fe4f4. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • bun

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Bringup loads about 2.9k tokens when it runs. Until then it costs about 119 tokens; SKILL.md has 1,292 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~119
When it runs · the whole SKILL.md, loaded when a task matches
~2.9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from jenissimo/bottleship at commit 77fe4f4, republished under its Apache-2.0 licence (© jenissimo). 1,292 words, ~2,854 tokens.

Download SKILL.mdSave it as .claude/skills/bringup/SKILL.md (or your agent's skills folder).
name
bringup
description
Drive and observe the BottleShip emulator to bring up a game, using the AI-agent harness (window.__BS__.harness + bun tools/harness.ts). Use when loading a game, making it reach a menu/level, diagnosing why it crashes/hangs/renders black, or writing a repeatable bring-up/regression script. Operationalizes CLAUDE.md's debugging workflow on top of the harness verbs. Use the project's CDP harness, not a browser MCP (chrome-devtools MCP is disabled for this project).

BottleShip Bring-up

The harness turns bring-up from "poke dbg.* by hand, grep logs, guess from screenshots" into fluent, self-judging automation. It is headless (CLI/CDP) and in-page (window.__BS__.harness). Logic lives in the worker HarnessService; the page facade and CLI are thin transports over one harness_rpc {id,cmd,args}→{id,ok,result|error} contract.

1. Preconditions — harness up

bun tools/harness.ts up

Launches/attaches Chrome with --autoplay-policy=no-user-gesture-required (so audio unlocks with no gesture — no canvas click needed in automation), opens http://localhost:5174/?game=dev, arms log streaming, and probes Vite(:5174 /health) + dev-sidecar(:3001 /health) + Chrome(:9333). Bring the dev servers up first: bun run dev and bun run dev:sidecar (dev:logs still works; start the server BEFORE streaming). bun tools/harness.ts health re-probes.

Parallel bring-up — one tab per agent

Set BS_TAB=<name> (once, for every harness command you run) when another agent is already using the emulator:

BS_TAB=alpha bun tools/harness.ts up     # opens/claims ?game=dev&bs=alpha
BS_TAB=alpha bun tools/harness.ts run my.harness.ts
BS_TAB=alpha bun tools/harness.ts report

The name picks that tab and ONLY that tab, and re-roots this run's evidence under logs/alpha/ — screenshots, run-N.harness.ts journals, dumpSurface/shot({save}) PNGs, and the sidecar's log archive. Never read logs/ at the top level while a session is set; that is somebody else's guest.

Rules: pick a name nobody else is using; never close a tab you did not open; and do not measure — parallel guests share the CPU, so trace refuses to run while a second guest tab is open, and A/B timing needs a single tab. With BS_TAB unset everything behaves exactly as it always has.

2. Drive

A fluent chain (bun tools/harness.ts run <script.harness.ts>, or in the browser console await window.__BS__.harness.chain()....run()):

ts
import { harness } from "../harness";
await harness()
  .streamLogs(["SYSTEM","DDRAW"])
  .openWgb(process.env.WGB ?? "/apps/external-wgb/<id>.wgb") // abs path (streamed off disk) or drop-folder URL — take it from env, don't hardcode local paths
  .waitForEvent("dialogShow")              // event-driven wait (HarnessEventBus)
  .click("Play Game")                      // faithful click by label (global coords)
  .tickFrames(120)                         // wait N presents after the click
  .waitUntil(() => read32(0x6b7bf8) === 0) // predicate evaluated IN the worker (spin-loop games)
  .expectSurfaceNonBlack("primary")        // assertion → aborts + auto fault snapshot on fail
  .state(["surfaces","threads"])
  .run();                                  // → one POJO (also written to logs/harness/)

Skip intros with the bundle's skipVideo. audioGesture() exists only for a manually-opened browser; automation uses the autoplay flag.

Touch/mobile runs the same way — .device('phone-landscape'|'tablet-landscape'|'desktop') then .tap(x,y) / .touchDrag(x0,y0,x1,y1,ms) / .longPress(x,y,ms) / .twoFingerTap(x,y) / .pinch(x,y,scale), all in GUEST px. These execute CDP-side, so keep .device() and its gestures in ONE chain: the emulation override is owned by the CDP session and a separate CLI invocation reconnects without it.

3. Observe

  • state([...]) — windows/surfaces/memory/threads/rings/audio/video/modules/cpu/screen as one POJO.
  • shot({save}) — PNG of the SCREEN: the frame that reached the canvas, every overlay (video plane, live GDI dialog rects, stats) composited, read from the mirror the present path keeps. shot({source:'layer'}) asks for the presenter's pre-composite game layer instead — the split between "which layer holds the pixels" and "does the composite show it" — and is always labelled composited:false. A capture that cannot see the screen errors out; it never returns a plausible substitute.
  • bun tools/harness.ts shot [file] --verify — the browser's own capture of the canvas, plus a cross-check of every worker-side route against it (with the screen's own churn as the noise floor). Run it when a screenshot and the tab seem to disagree.
  • screenPixels({x,y,w,h,legend}) — a rect as one string per row, colours quantised to legend; anything outside it reads ? and is tallied, so a chrome-geometry assertion (is the etched line present HERE and absent THERE) cannot pass on pixels it did not recognise.
  • screenMark() … screenChangeSince({allow}) — WHICH pixels a transition touched. Mark from a REPAIRED screen: a mark taken over an already-damaged one reports "nothing changed" and the scope assertion then passes on the very bug it exists to catch. outside.changed answers "what repainted that had no business repainting" (an over-wide invalidate, a stamp with nothing erased under it); each allow rect's own count is the positive control, so a run where the click missed fails instead of passing. Both sides are ours, so it is exact — no reference image, nothing to tune.
  • textures() + dumpSurface(ptr|'primary') — gallery + per-surface PNG to logs/debug/.
  • surfacePixels(sel) / expectSurfaceNonBlack(sel) — cheap liveness from a subsampled readback.
  • Dump PNGs preserve ALPHA: an area that looks WHITE in a viewer but BLACK on the canvas is transparent (a=0), not white — sample the RGBA (readSurfaceRGBA) before concluding a color.
  • A Win32 FRONT-END presents nothing — its dialogs run before the render device does. Gate on waitForControl("New Game"), never tickFrames, or you wait on presents that never come and it reads exactly like a hang.
  • DEAD control (the click does nothing) → hitTest(x,y) before anything else. It prints the window a mouse message is ADDRESSED to next to the control the container hit-test finds, and agrees:false is a routing bug the pixels cannot show: every control we drive ourselves keeps working off the container hit-test, while one the guest SUBCLASSED needs the address and gets nothing. wmTrace then confirms it on the wire (the hwnd on WM_LBUTTONDOWN).
  • BLANK control / unpainted dialog → paintTrace("start") … paintTrace("read"). The chain has many links (posted → pump filter → dispatched → BeginPaint/EndPaint+flush → owner-draw chain with its task counts → per-flush child-window exclusions) and the pixels look identical whichever one dropped it; the trace names the link and its reason.
Show full SKILL.md (549 more words)Show less

4. Diagnose

  • Prefer API breakpoints (breakOnApi("d3d9.*")) — JS layer, no JIT-off, resolves on first hit with args + caller. Best for "where does it first touch X".
  • breakOnExport("d3d9.dll!Direct3DCreate9"), breakOnSymbol("core!UInput::ReadInput") (needs loadSymbols(module, {name:rva}) from the RE layer first), watchMem(addr), breakOn(eip) — all require JIT OFF (auto-enabled; perf collapses while armed — clearBreaks() to restore). Addresses inside the async-park spin loop are refused (CLAUDE.md §3.5).
  • "WHO calls this guest function, and with what?" — arm the function ENTRY and read callsite off the hit: retAddr + retAddrSym, a retAddrTrust verdict, the module-labelled backtrace, a stack window, and capture.reads ({reg:'esi',offset:12,size:4} — add deref to follow the pointer). Present in EVERY mode, continuous included. Trust the caller only on verdict:"verified" (the E8 before [ESP] targets the armed eip); untrusted/unreadable means the armed address is not a function entry and retAddr names nobody.
  • Hits also land in a WORKER-side ring — read them with breakEvents({since,limit}), from any process, at any later time. Never accumulate hits in a script and print at the end: a 60s pageEval/RPC timeout takes the whole run's evidence with it, the ring does not. It reports evicted/gap instead of silently returning a shorter list, and says out loud that 0 events is not evidence the code did not run (block-entry rule).
  • Read the streamed log; events(n) shows recent harness events; on a WASM trap a fault event carries the fault-grade snapshot.
  • The ring holds a fixed number of ENTRIES, so on a ddraw/d3d title the per-frame spam overwrites init-time evidence in ~20s and it is gone before a late crash fires. Quiet the firehose category first — logLevel("DDRAW","WARN") (logLevel() resets) — rather than just enlarging the ring, which only postpones losing the same lines.
  • A guest blocked on a MessageBox looks exactly like a freeze: the host draws it as DOM, so no canvas capture shows it and the one string naming the problem is invisible. report().pendingModals lists them (text, caption, how long it has waited); dismissModal() / onModal() answer them.

5. Hypothesis from DATA, not reasoning

Confirm with a dump / a logged value (a dumpSurface PNG, a state field, a breakOnApi snapshot) before theorizing about DC topology / vtable layout — the canvas-vs-selected-bitmap distinction and multi-DC composites are easy to mis-model (CLAUDE.md diagnostic discipline).

6. Fix → re-run → keep tools, drop probes

Every .run() writes a re-runnable logs/harness/run-N.harness.ts (journal). Turn the winning chain into a checked-in *.harness.ts regression script under tools/harness/regression/ only if it self-judges (throws/sets exitCode with a stated reason, not "look at the screenshot") and isn't tied to one closed bug (see tools/harness/README.md). Remove one-off probes; keep only reusable harness verbs.

Hard rules (don't relearn these)

  • Quality gate order (CLAUDE.md): bun tools/generate-index.ts → bun tools/validate-signatures.ts → bun tools/validate-struct-offsets.ts → bun tools/validate-guest-code-writes.ts → bun tools/validate-stub-tables.ts → bun run typecheck.
  • Reload, not HMR, after editing src/worker (HMR doesn't reload the worker entry and hangs the game). The harness ships in the worker bundle — iterate via a page reload.
  • JIT-off collapses perf — only EIP/export/symbol/watch breakpoints need it; prefer API breakpoints. clearBreaks() when done.
  • Audio needs the autoplay flag (harness up) or a real transport click; a synthetic event won't satisfy the browser autoplay policy.

Division of labor

The skill = workflow/checklist; the harness (src/worker/harness/, src/harness/, tools/harness.ts) = capability/verbs; CLAUDE.md = invariants.

Templates: tools/harness/templates/bringup.harness.ts (bring-up starting point), tools/harness/templates/diagnose-eip.harness.ts (API-breakpoint + waitUntil). Checked-in per-game regression scenarios live in tools/harness/regression/ (run the whole batch with bun tools/harness.ts regress); see tools/harness/README.md for what earns a spot there.

© jenissimo, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/bringup of jenissimo/bottleship.

Open the folder on GitHubat commit 77fe4f4

Compare with similar skills

Bringup next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Bringup compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Bringup this skilljenissimo/bottleship132—~2.9kAutomated safety check: PassApache-2.0
Browser Testing With Devtoolsshashankswe2020-ux/whoop-mcp165—~3kAutomated safety check: WarnMIT
Chrome Devtools MCPmanagedcode/dotnet-skills486—~2.2kAutomated safety check: PassMIT
Spider Kingaoyunyang/spider-king-skill507—~7.3kAutomated safety check: PassMIT
Clawmemyoloshii/ClawMem210—~7.5kAutomated safety check: PassMIT
Review Agents Mddominik1001/caldav-mcp103—~1.2kAutomated safety check: PassMIT

Similar skills

  • Browser Testing With Devtools

    shashankswe2020-ux/whoop-mcp

    Tests in real browsers. An agent skill from shashankswe2020-ux/whoop-mcp.

    165 GitHub stars~3k tokensUpdated yesterday
    Testing & QAAuto-check: warnings
  • Chrome Devtools MCP

    managedcode/dotnet-skills

    Use Chrome DevTools MCP from .NET agents and .NET-focused repos to inspect, debug, and automate Chrome through an MCP client.

    486 GitHub stars~2.2k tokensUpdated yesterday
    DevelopmentAuto-check passed
  • Spider King

    aoyunyang/spider-king-skill

    Pure-web protocol reverse skill: turn hostile browser clients into browser-free Python collectors.

    507 GitHub stars~7.3k tokensUpdated 1 mo ago
    Backend & APIsAuto-check passed
  • Clawmem

    yoloshii/ClawMem

    ClawMem operational reference for agents at query time — the 3-rule escalation gate, MCP tool routing, the 4 query-optimization levers, pipeline behavior (query vs intentsearch), composite scoring…

    210 GitHub stars~7.5k tokensUpdated 2 days ago
    Agent WorkflowsAuto-check passed
  • Review Agents Md

    dominik1001/caldav-mcp

    Reviews an AGENTS.md or CLAUDE.md file against best practices and reports concrete fixes.

    103 GitHub stars~1.2k tokensUpdated 3 days ago
    Agent WorkflowsAuto-check passed
  • Handoff

    anombyte93/prd-taskmaster

    Phase 3 of the prd-taskmaster pipeline: smart mode selection and user handoff.

    604 GitHub stars~5k tokensUpdated 1 mo ago
    Agent WorkflowsAuto-check passed

More from jenissimo/bottleship

  • Repack

    jenissimo/bottleship

    Turn a game installer/archive/disc (InstallShield, PackageForTheWeb/WinZip-SFX, Inno/GOG, ISO/BIN-CUE, CAB, FreeArc, plain ZIP) into a .wgb bundle using BottleShip's OWN self-hosted format readers —…

    132 GitHub stars~2k tokensUpdated today
    Auto-check passed

Questions about Bringup

What does Bringup do?

Drive and observe the BottleShip emulator to bring up a game, using the AI-agent harness (window.BS.harness + bun tools/harness.ts). Bringup is an agent skill from jenissimo/bottleship.ts).

When should I use Bringup?

Bringup fits situations like: making it reach a menu/level; diagnosing why it crashes/hangs/renders black; writing a repeatable bring-up/regression script.

How do I install Bringup in Claude Code?

Run `npx skills add jenissimo/bottleship --skill bringup -a claude-code`. Or copy the skill folder (.claude/skills/bringup in jenissimo/bottleship) into .claude/skills/bringup in your project. Claude Code loads it when a task matches its description.

How do I install Bringup in Codex?

Run `npx skills add jenissimo/bottleship --skill bringup -a codex`. Or copy the skill folder (.claude/skills/bringup in jenissimo/bottleship) into .agents/skills/bringup in your project. Codex loads it when a task matches its description.

Can I use Bringup in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add jenissimo/bottleship --skill bringup -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/bringup, .gemini/skills/bringup, .github/skills/bringup and .opencode/skills/bringup in your project.

What does Bringup need to run?

Going by SKILL.md and its folder, Bringup needs the command-line tools its instructions call (bun).

Does Bringup access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Bringup safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Bringup use?

Bringup is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Bringup use?

About 2.9k tokens (SKILL.md is roughly 11k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Bringup?

Skills that share tags, products or a category with Bringup: Browser Testing With Devtools (shashankswe2020-ux/whoop-mcp, 165 stars), Chrome Devtools MCP (managedcode/dotnet-skills, 486 stars), Spider King (aoyunyang/spider-king-skill, 507 stars) and Clawmem (yoloshii/ClawMem, 210 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Bringup?

jenissimo (a GitHub user) maintains it in jenissimo/bottleship, which has 132 GitHub stars. The repository holds 2 skills in this directory. The repository was last updated on October 7, 2026.

Source: jenissimo/bottleship on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.