CLI-Games creator guide · embed protocol V3

Package your game

Keep your finished game and its keyboard controls. Add a small manifest and our supplied shim, then upload one self-contained ZIP to your private workspace.

This guide is public. Upload workspaces are invitation-only and require a verified CLI-Games account.

Already have a workspace labelled protocol V2? Keep using the frozen packaging and action-bridge instructions inside that workspace. Do not add this V3 manifest or shim unless CLI-Games moves your invitation to V3.

Send these instructions to your AI assistantcli-games-packaging-instructions.mdDownload the exact host shimcli-games-embed-v3.js

The instructions are a complete engineering handoff. The shim is the exact file your ZIP needs; include it unchanged.

  1. 01Add two filesManifest plus supplied shim
  2. 02Keep keyboard codeNative key down and key up
  3. 03Preview and submitOne exact private fingerprint

The package

Add the manifest and shim

Keep index.html at the ZIP root. Beside it, add cli-games.json and the unchanged cli-games-embed-v3.js file linked above.

{
  "schema": "cli-games.embed-manifest.v1",
  "protocol": 3,
  "title": "Keyboard Check",
  "command": "keyboard-check",
  "description": "A minimal keyboard-capable browser game prepared for review.",
  "category": "arcade",
  "keys": [
    { "key": "ArrowLeft", "label": "LEFT", "aria": "Move left", "hold": true },
    { "key": "ArrowRight", "label": "RIGHT", "aria": "Move right", "hold": true },
    { "key": " ", "label": "ACT", "aria": "Use the main action" },
    { "key": "Escape", "label": "MENU", "aria": "Return to the menu" }
  ],
  "scoring": {
    "maxScore": 30,
    "minDurationMs": 0,
    "maxDurationMs": 1800000,
    "scoreMayDecrease": false
  }
}
  • The manifest is the source of truth. Title, command, description, category, keys, and scoring are read-only in the workspace.
  • Declare every playable key. Labels and descriptions become terminal and phone controls; held movement keys should set hold: true.
  • Keyboard input is required. Pointer-only and gamepad-only games are outside V3.0.
  • Keep creator credit separate. Your name, bio, and optional website stay in the account-bound workspace, not the manifest.

Keep every file local

Use relative paths for scripts, images, fonts, audio, workers, and WASM.

Test it offline

Disconnect the network, open index.html, play, and resize the window.

Fill the frame

Support desktop, portrait phone, and landscape phone shapes.

50 MBZIP
100 MBunpacked
20 MBper file
2,000files

One enclosing folder is accepted. Symlinks, unsafe paths, absolute URLs, other origins, missing local assets, and <base> tags are rejected.

Browser and security details

The game runs in an opaque, cookie-free frame on a separate origin. Network requests, analytics, shared storage, navigation, forms, popups, downloads, and access to the CLI-Games page are blocked.

Local Canvas, WebGL, WebAssembly, Web Workers, blobs, and data assets are supported. Files are served as uploaded; there is no server-side build step.

The controls

Keep your keyboard listeners

The host shim redispatches ordinary KeyboardEvents.Your existing key down and key up handlers remain the game’s input path.

const embed = window.CliGamesEmbed

embed.onStart(({ runId, seed }) => {
  resetGame({ runId, seed })
})

window.addEventListener('keydown', (event) => {
  if (event.key === 'ArrowLeft') moveLeft()
  if (event.key === 'ArrowRight') moveRight()
  if (event.key === ' ') useMainAction()
  if (event.key === 'Escape') finishRun()
})

window.addEventListener('keyup', (event) => {
  releaseHeldKey(event.key)
})

function showPlayableMenu() {
  renderMenu()
  // Required after every completed run or return to the playable menu.
  embed.ready()
}
  • Reset on host start. onStart supplies the run ID and unsigned 32-bit seed.
  • Use the host seed for any gameplay or scoring randomness.
  • Return ready every time. The shim announces the initial DOM-ready state; call ready() after every finished run or return to a playable menu.
  • Match the manifest. Every listed key must appear in the shipped game code and do what its label says.

Optional reporting

Add progress or scoring

An unscored game may skip reporting. If the manifest includes scoring, report scores within its declared bounds and end each run once.

Show reporting code
// Optional progress snapshot. Send after start and meaningful changes.
embed.state({ status: 'playing', level: 2, levelCount: 6, moves: 19, score: 20 })

// Required when cli-games.json includes scoring.
embed.score(20)

// Send once when a scored run ends, then return to the menu and call ready().
embed.gameOver(30)
showPlayableMenu()
  • State has five fields: status, level, levelCount, moves, and score.
  • Status is one of: ready, playing, won, or lost.
  • Scores are whole numbers at or above zero and no higher than the manifest maximum.

Embedded scores are creator-reported and isolated from CLI-Games’ verified competitive economy. This protocol does not promise replay-grade verification.

The release

Upload, play, submit

Your upload stays private until review passes. You inspect the exact saved build before submitting its immutable fingerprint.

  1. 1
    Upload the ZIPFile, manifest, security, control, and score checks run together.
  2. 2
    Review package factsThe workspace shows the manifest title, command, copy, category, keys, and scoring read-only.
  3. 3
    Play the private previewYou see the exact saved bytes before authorizing publication.
  4. 4
    Accept and submitTerms, creator credit, manifest, and ZIP fingerprint become one candidate.
  5. 5
    CLI-Games reviewsIndependent internal review checks safety, rights, controls, scoring, credit, and catalog fit.
  6. 6
    Approved build goes liveSubmission authorizes publication only if that same fingerprint passes review.

Ready to upload?

Five quick checks

  • index.html, cli-games.json, and the unchanged shim are at the root
  • the shim loads before the game’s script
  • the game works offline and fills each frame shape
  • every manifest key works through native keyboard listeners
  • each completed run returns to a playable menu and calls ready()