Before you start
- Node.js 22 or newer. The skill runs the CLI through
npx. - Claude Code or the Codex CLI.
- Access to the
get-wreckedorganization on GitHub. During early access the plugin installs from the privateget-wrecked/medal-playrepository, and the CLI is a private package on GitHub Packages. - A web game that runs from static files, with an
index.html.
1. Give npm access to the CLI
The CLI is @get-wrecked/medal-play on GitHub Packages. npm needs to know where the
@get-wrecked scope lives, and it needs a GitHub token with the read:packages scope. With the
GitHub CLI signed in:
gh auth refresh -s read:packages npm config set @get-wrecked:registry https://npm.pkg.github.com npm config set //npm.pkg.github.com/:_authToken "$(gh auth token)"
Without the GitHub CLI, create a classic personal access token with read:packages and add these two
lines to ~/.npmrc:
@get-wrecked:registry=https://npm.pkg.github.com //npm.pkg.github.com/:_authToken=<your token>
npm view @get-wrecked/medal-play version prints the latest version once this works. If you skip this
step, the skill notices the failed install on your first publish and walks you through it.
2. Install the plugin
The plugin and its skill are the same in both agents. Install it in the one you use.
Claude Code
From a terminal:
claude plugin marketplace add get-wrecked/medal-play claude plugin install medal-play@medal-play
Or inside Claude Code:
/plugin marketplace add get-wrecked/medal-play /plugin install medal-play@medal-play
Claude Code clones the marketplace with your git credentials, so your machine has to be able to clone
https://github.com/get-wrecked/medal-play. Plugins load when a session starts, so start a new one.
claude plugin list shows medal-play@medal-play as enabled.
Codex
codex plugin marketplace add https://github.com/get-wrecked/medal-play codex plugin add medal-play@medal-play
codex plugin list shows medal-play@medal-play as installed and enabled. Start a new Codex
session to load the skill.
Publishing uploads files, so it needs the network. When Codex runs commands in a sandbox without network access, it asks to run the publish command outside the sandbox. Approve it.
3. Publish a game
Open your agent in the game's folder and ask in plain words:
publish this on Medal
Ask for more when you want it:
publish this on Medal, clip when the player wins, and post the high score
The agent then:
- Runs the project's build, if it has one, and finds the folder with
index.html: the first ofdist/,build/,out/, the project root andpublic/. - Adds
window.medalcalls only where you asked for them. Asked only to publish, it leaves your code alone. The clip button works on every game anyway. - Writes
medal.jsonwith the game's name, description, clip events and leaderboard order. - Runs
npx --yes @get-wrecked/medal-play@latest publish --json. - Tells you the live URL, the claim link and which calls it added.
On success the CLI prints:
{
"url": "https://star-dodge.medal.games",
"claimUrl": "https://medal.games/claim/…",
"slug": "star-dodge",
"version": "…"
}
Publishing needs no account. The first publish creates a publisher key for this machine and saves it in
~/.config/medal-play/credentials.json.
4. Claim your game
A new game is live but unlisted, and it expires after 7 days unless you claim it. Open the claim link, sign in with Medal and press Claim. The game then stays up under your Medal account.
To skip claim links, link this machine to your Medal account once:
npx --yes @get-wrecked/medal-play@latest login
It prints a code and a link to the approval page. Sign in there and approve the code, and the
CLI finishes on its own. The games this machine already published become yours, and later ones start claimed, with a
claimUrl of null. Only approve a code you started yourself.
5. Update a game
Publish again from the same folder on the same machine, through your agent or the CLI. The CLI wrote the game's
slug into medal.json on the first publish, and the publisher key shows the game is yours, so
the new version replaces the old one at the same address. Keep medal.json in the project and keep the
credentials file.
To list a claimed game on the games page, set "visibility": "public" in
medal.json and publish again.
Publish from the terminal
The skill runs the same CLI you can run yourself, from the project root:
npx --yes @get-wrecked/medal-play@latest publish npx --yes @get-wrecked/medal-play@latest login
--dir <folder>- The folder with the built
index.html. Default: the first ofdist/,build/,out/,.andpublic/that has one. --json- Prints the result as JSON. On failure it prints
{"error": {"code", "message", "hint"}}and exits non-zero. --env prod|staging|test- The Medal Play environment to publish to. Default:
prod, which is medal.games.
Add clips and scores
Medal Play injects its SDK into every page of a hosted game as window.medal. Call it through
window.medal?. so the game still works anywhere else, and don't bundle the SDK or add a script tag for it.
The overlay draws the clip button, sign-in and the leaderboard, so the game doesn't need its own.
// Clip the moment the player wins, with a marker on the clip's timeline.
window.medal?.event('win', { title: 'Level 3 cleared' })
// Save the last 25 seconds after a big play.
window.medal?.clip({ title: 'Five-hit combo', seconds: 25 })
// At the end of a run, post the score and show the leaderboard.
window.medal?.scores.submit(score)
window.medal?.scores.show()
event(name, { title, seconds, clip })- Marks a moment. It clips when
medal.jsonlists the name inclipEvents, whenclipis true, or when there is noclipEventslist. clip({ title, seconds })- Saves the last
secondsof play, 15 by default, and shows the clip toast. scores.submit(score)- Posts a score under the signed-in player and keeps each player's best. A signed-out player is asked to sign in first.
scores.top({ limit }),scores.show()- Reads the top scores, or opens the leaderboard.
init({ canvas, bufferSeconds, overlay, hotkey })- Optional. Picks the canvas to record when the page has several, keeps 5 to 30 seconds of video (30 by default),
and turns off the overlay or the
F8clip hotkey. user(),signIn()- The signed-in Medal player, or a sign-in popup. Call
signIn()from a click or key press.
The recorder captures the game's canvas and its Web Audio at up to 1280×720 and 30 fps. Nothing leaves the player's
browser unless they choose Post to Medal. TypeScript projects get the types from the CLI package with
import type {} from '@get-wrecked/medal-play/sdk'.
medal.json
Optional, in the project root. The CLI fills in anything missing and writes the slug back after the
first publish.
{
"name": "Star Dodge",
"description": "Dodge the falling stars for as long as you can.",
"clipEvents": ["win"],
"leaderboard": { "order": "desc" },
"input": ["keyboard", "mouse", "touch"],
"orientation": "portrait",
"visibility": "public"
}
name- At most 60 characters. Default: the page's
<title>, else the folder name. slug- The game's address: 3 to 40 characters of
a-z,0-9and-. Default: made from the name. description- At most 280 characters. Default: the page's meta description.
clipEvents- The
medal.event()names that clip. Without the list, every event clips. leaderboard.order"desc"when a higher score is better, the default."asc"for times and other scores where lower wins.input- Any of
keyboard,mouse,touchandgamepad. Default: read from the game's event listeners. orientationlandscape,portraitorany, the default.visibilityunlisted, the default, orpublic. Claimed public games are listed on medal.games.
Limits
- 100 MB and 2,000 files per build.
- 25 MB per file, and 5 MB per HTML file.
- Dotfiles,
node_modules/and.git/are never uploaded. - Static files only: no server code, databases or secrets.
Troubleshooting
npxfails with 401, 403 or 404 for@get-wrecked/medal-play- npm can't read the private package. Redo step 1, and check that the token has
read:packagesand that your GitHub account can see the get-wrecked organization. - Adding the marketplace fails
- Your git can't clone the private repository. Try
git clone https://github.com/get-wrecked/medal-play.gh auth loginfollowed bygh auth setup-gitsets up access over HTTPS. - Publishing fails with
slug_taken - The slug in
medal.jsonbelongs to another game, or to one published from another machine. Use the slug the error's hint suggests, or removeslugto get a free one. - Clips fail with
no_canvas - Only games that draw to a
<canvas>can be clipped for now. When a page has several canvases, callwindow.medal?.init({ canvas })with the main one.
Keep the plugin current
Every publish runs the latest CLI, so CLI fixes need no update. To pick up a newer skill in Claude Code, then restart it:
claude plugin marketplace update medal-play claude plugin update medal-play@medal-play
In Codex:
codex plugin marketplace upgrade codex plugin add medal-play@medal-play