Borrowing it
Nothing to install: this file belongs to kyh/vibedgames. Take a copy, put it at the same path in your own repository, and replace the rules that are about this project with yours.
curl -O https://raw.githubusercontent.com/kyh/vibedgames/main/CLAUDE.mdgit clone --depth 1 https://github.com/kyh/vibedgamesWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/instructions/kyh/vibedgames/claude-md)<a href="https://agentmods.dev/instructions/kyh/vibedgames/claude-md"><img src="https://agentmods.dev/badge/instructions/kyh/vibedgames/claude-md/github.svg" alt="Measured on agentmods" height="20"></a>Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.
<a href="https://agentmods.dev/instructions/kyh/vibedgames/claude-md"><img src="https://agentmods.dev/badge/instructions/kyh/vibedgames/claude-md.svg" alt="Reviewed on agentmods" width="80" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5.1 | $0.06303 | $0.06303 |
| Opus 5.5 | $0.02521 | $0.02521 |
| Sonnet 5.5 | $0.01261 | $0.01261 |
| Haiku 4.5 | $0.00630 | $0.00630 |
Grade A, and why
vibedgames CLAUDE.md scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 13d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 142 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Agent Instructions
Agent-driven development
AGENTS.md is the runnable guide — quickstart, env files, seeded logins, headless auth, the pnpm verify gate, an agent-browser recipe, and which surfaces are checkable at runtime versus which need a human. Reach for it when you need to run, sign in, or verify something; a small edit does not need it. This file carries the product context, architectural decisions and conventions that sit on top of it.
pnpm lintis a clean gate.oxlint.config.tsextends the ultracite presets (core, react, anti-slop); every rule is an error. Fix the code, don't add config overrides; a// oxlint-disable-next-line rule -- whyneeds a stated reason.
What This Is
vibedgames — infrastructure platform for deploying, hosting, and adding multiplayer to browser games. Users build games locally, deploy via CLI (vg deploy), and their game is served at {slug}.vibedgames.com. The web app is the central hub for discovering and playing games.
The primary user of the vg CLI is a coding agent, not a human. A human prompts their agent ("build me a bomberman game"), and the agent uses vg + the bundled skills to scaffold, generate assets, add multiplayer, and deploy. Optimise CLI UX accordingly: machine-readable output (--json), self-describing errors, deterministic exit codes, skills that document the exact commands the agent should run. Friction that a human would tolerate ("now open this URL...") blocks an agent.
Product & Business Context
- The bet (why us): Seed your coding agent with the abilities of a full game studio. The wedge isn't "another hosting platform" — it's that an agent, armed with
vg+ the bundled skills, can do everything a studio does (scaffold, generate art/audio, add multiplayer, ship) without the human assembling an engine, art pipeline, or server stack. The differentiator is being agent-native and end-to-end.
Key Architectural Decisions
- Games are untrusted user code. Session cookies are scoped to apex domain only (
vibedgames.com). Games on{slug}.vibedgames.comsubdomains cannot access auth cookies. CSPframe-ancestorsrestricts embedding. Never weaken these boundaries. - Single active deployment per game. No version history, no rollback. New deploy replaces old. R2 keys are
games/{gameId}/{deploymentId}/{path}— immutable per deployment, enabling long cache (1yr) for assets and short cache (60s) for index.html. - CLI auth uses device-code flow. CLI shows 6-char code → user confirms in browser → CLI polls for token. Not OAuth.
- better-auth 1.7 scopes account identity by
(issuer, accountId).account.issueris NOT NULL with a unique(issuer, accountId)index (account_issuer_account_id_uidxinpackages/db/src/drizzle-schema-auth.ts). Credential accounts carrylocal:credentialwithaccountId= the owning user's id; an OAuth provider with no real issuer carrieslocal:oauth:<providerId>. Keepbetter-auth,@better-auth/api-keyand@better-auth/expoon the same minor. Nothing inpnpm verifytouches D1, so a schema/column mismatch surfaces only as a failed sign-in against the live database — any further change to these tables needs a matchingdb:pushplus a backfill of existing rows. - Never regenerate the auth schema with
@better-auth/cli. It is stuck at 1.4.21:generate:auth-schemaemits noissuer, and it strips the hand-curatedindex(...)calls and the hand-addedrate_limittable. Editdrizzle-schema-auth.tsby hand. - Declare uniques as named unique indexes, never
.unique(), and keep the composite PK nameddeployment_file_pk. Production D1 was pushed by drizzle-kit 0.31, which spelled every.unique()as aCREATE UNIQUE INDEX <table>_<column>_uniqueand left thedeployment_filePK unnamed (introspected asdeployment_file_pk). drizzle-kit 1.0 spells.unique()as an inlineUNIQUEconstraint and names PKs, and treats either difference as a table recreate:PRAGMA foreign_keys=OFF; CREATE __new; INSERT; DROP; RENAME. D1 ignores that PRAGMA, so theDROPcascades through everyON DELETE CASCADEchild (account,session,game, ...). WithuniqueIndex("<table>_<column>_unique")in the table's extra config and that PK name,drizzle-kit push --explainagainst a 0.31-shaped database reports no changes.drizzle-kit pushapplies a recreate without asking, sodb:push-remoteprints the--explainplan first and applies only on a typedy— refuse a plan that recreates a table. - Every PR gets Cloudflare Worker Previews, Vercel-style, driven by Actions rather than Workers Builds.
.github/workflows/preview.ymlruns onworkflow_runafter CI is green (same-repo branches only, since that trigger holds secrets), deployscf previews deploy pr-<n>for web and party when the PR touches them, keeps one sticky status comment plus a GitHub deployment per Worker (.github/scripts/worker-preview.mjs), and deletes the previews when the PR closes. Previews inherit no bindings: eachcloudflare.config.tsbinds, whenisPreview,vibedgames-preview(D1) andvibedgames-games-preview(R2) by name, which the workflow creates on first use. PR code never reaches prod D1 or the prod bucket. The games Worker has no preview because it routes by{slug}.vibedgames.com. Preview secrets live in Cloudflare's preview base config (wrangler preview base-config secret put --worker-name <worker>;cfhas no preview secret command yet), not in GitHub. - Multiplayer is host-authoritative, last-write-wins. No conflict resolution. First player becomes host; if host leaves, reassigns. Good for turn-based and host-controlled games. Real-time feel comes from the SDK's netcode helpers, never a hand-rolled throttle or lerp.
FixedRatedrives send clocks.Interpolatorrenders remotes from sender-stamped updates. A guest's own body is predicted locally and corrected byReconciler. Intents go to the host only, and the host handles its own locally. The party server relays a host'sstate_patch(path ops: the leaves that changed) to everyone but the host. Beyond relaying, the server runs only game-agnostic room services: server time (serverNow(), the timebase every stamp uses), first-come claims, ordered input ticks for lockstep/rollback, radius interest filtering, player-state limits, persistence of shared state and claims, host-set room locks and meta, and lobbies (VgLobby) that list the rooms naming them and quick-match players into the fullest open one. No back-compat in this protocol: SDK and server ship together, and a game that changes its own wire format versions its room ids. - Plugins ship from a mirror repo.
kyh/vibedgames-pluginsholds onlyplugins/,.claude-plugin/andLICENSE, synced by.github/workflows/sync-plugins.yml(deploy key in thePLUGINS_MIRROR_DEPLOY_KEYsecret).vg initand the plugin directory listing point at the mirror because both download a whole repo archive and this one is ~140 MiB compressed. Edit skills here; anything committed straight to the mirror is overwritten on the next sync. vgpublishes as one esbuild bundle with no dependencies.apps/cli/scripts/build.mjsinlines every dependency (their licences go todist/THIRD_PARTY_NOTICES.txt) and splits each command into a chunk loaded when it runs, so a coldnpx vibedgamesis one small download that no transitive package can print a deprecation warning into. A runtime dependency of the CLI goes indevDependencies. Source (src/lib/) and chunks (dist/) sit at different depths, so a file the package ships is found fromPACKAGE_ROOT(src/lib/package-root.ts), never spelled as../... Every request carriesx-vg-cli: <version>(@repo/contract/cli). Builds before it (≤0.6.3, oRPC 2.0.0-beta.31) refuse an error body without oRPC's oldinferablefield, soapps/web's RPC handler adds it back for Node requests without the header; delete that once those installs are gone.- Deploy on push to main. GitHub Actions detects changed apps and deploys via
cf deploy --prebuilt. Never runcf deploylocally. Changed example games (games/*, plus anything downstream of a changed package) deploy the same way throughvg deploy, authenticated by theVG_TOKENrepo secret — an API key from Settings. - Signup is open; credit is bought or redeemed. New accounts start at $0. Credit arrives three ways, all as ledger entries: a Stripe Checkout purchase (
credits.checkoutreturns a hosted URL; only the signedcheckout.session.completed/async_payment_succeededwebhook at/api/stripe/webhookcredits it, keyedpurchase:{checkoutSessionId}, $1 paid = $1 credit), a credit code redeemed in Settings or withvg credits redeem(code:{codeId}:{userId}, once per account per code, within the code'smax_uses), or an admin grant. Credit codes live in theinvite_codetable — it predates launch, when the same codes gated signup, and renaming it would be a recreate. Accounts that signed up with an invite before launch (user.invited_by_codeset) still get the old $20signup:{userId}grant, lazily on first credit access; nobody else does. Either ofSTRIPE_SECRET_KEY/STRIPE_WEBHOOK_SECRETunset turns purchases off, nothing else:credits.mereportspurchasesEnabled: false, Settings shows only the code button, checkout answers 412 and theinsufficient_creditsmessage offers codes only. Neither is required to deploy:apps/web/cloudflare.config.tsleaves them out of production builds, and a key set later withcf workers secrets updatebinds anyway. - Per-user generation credits (micro-USD ledger).
credit_entryis an append-only ledger of integer micro-USD deltas — balance isSUM(delta_micro), there is no cached balance column, and idempotency lives in deterministic entry ids (signup:{userId},code:{codeId}:{userId},purchase:{sessionId},hold:{requestId}, ...).generationtracks the per-request lifecycle:generate.forwardblocks a non-admin's queue submits at balance ≤ 0 (insufficient_credits) or to an endpointGET /v1/models/pricinglists no price for (unknown_endpoint; an admin's are metered but never blocked), debits an estimated hold at submit (provider historical per-call estimate, clamped $0.01–$5), settles to actual cost from thex-fal-billable-unitsresult-fetch header and the endpoint's unit price, and refunds holds when a status poll reports FAILED/CANCELLED. Pricing that cannot answer (network error, 5xx, 429) never blocks a submit: it holds the estimate, else a flat $0.10, and settles at the hold; only a priced answer is cached (packages/service/src/credits/endpoint-pricing.ts). The ledger reads a queue hop's endpoint and request id offpathas sent, while fal percent-decodes the path and finds its application whatever the case, so the contract holds a queue path to lowercase letters, digits and-._~/, and every queue POST bills as a submit whatever its path: otherwise the ledger prices or settles a hop other than the one fal runs. Only generation is metered; deploys/hosting are free. Never bypass the gate or write ledger rows outsidepackages/service/src/credits/. - Playtest decisions go through TypeSafe Jev (internal only).
vg playtest runlets a model play a game. The loop runs inside the game's page (apps/cli/src/lib/playtest/agent.ts, injected as source): several times a second it reads__GAME_DIAGNOSTICS__, POSTs to/api/playtest/decideand holds the answer as real input, with an optional per-framereflexfrom the game'swindow.__GAME_PLAYTEST__manifest for fast games. Every decision also asks ascorequestion — the model's own 0–1 read of progress towards the goal — which the report carries asdecisions.progress; it is advisory, never a gate. That endpoint is cross-origin and cookie-blind by design — the page is untrusted user code — and honours only a 15-minute HMAC token minted byplaytest.sessionfor that run (packages/service/src/playtest/session-token.ts, signed withBETTER_AUTH_SECRET). The server holdsTYPESAFE_API_KEYand forwards exactly one System One request shape — state plus typed questions — so neither the token nor the key can be used for anything else. Not metered: a run costs a fraction of a cent, and the per-call caps (64 KB state, 32 questions) bound it.games/pongis the reference manifest, reflex included. The globals are the contract;@vibedgames/playtest(packages/playtest) only types and publishes them — a game may set them by hand, and the CLI never depends on the package. - Media goes through fal (internal only).
vg generateexposesrun,models,schema,upload,pricing,status,docs. The server holdsFAL_API_KEY; the CLI proxies through the API. fal is a gateway to OpenAI, Veo, Sora, Kling, Flux, ElevenLabs, Retro Diffusion, etc. — there's no per-provider routing. End-user-facing surfaces (thevg generateCLI help and the skills underplugins/vibedgames/skills/) must not name fal as a brand. To the user this is just a CLI that generates assets; "fal" stays an implementation detail. The one exception is model endpoint IDs: they're passed through verbatim (e.g.fal-ai/flux/dev,bytedance/seedance-2.0/...), exactly as the upstream API expects — the CLI does no id rewriting. Keep branding out of prose and help text, but never alter an endpoint ID. /mcpis the platform as MCP tools, bearer-only. One tool pervgcommand that reaches the server, named as the agent thinks of it (me,credits,deployments,fork,generate,skills, …), inapps/web/src/lib/mcp-tools.ts;mcp.tsserves them with@orpc/mcp'sMcpRegistraron a freshMcpServerper request (stateless Streamable HTTP). Most tools areappRouter's own procedures; thegenerate_*family isvg generate's subcommands overgenerate.forward, reached through oRPC'scall()so the session check, input rules and credit ledger run exactly as for the CLI; the dedicated asset tools (generate_image,generate_video,generate_3d, …) are the same path on the defaultsMEDIA_MODELSnames, whichmcp.test.tsholds to the model-catalog skill.deployments_uploadisdeploy.upload, which writes inline files through the R2 binding into a staged deployment beside the live one and, onpublish, points the game at it before removing every other deployment of the game: unlikedeploy.create, nothing goes dark mid-upload. Aurlfile is fetched by the Worker only from the media hostsisMediaUrl(@repo/contract/generate/media-hosts, shared with the CLI's downloads) names, so it is no general fetch proxy;source: truetars the published files in the Worker (source-archive.ts) into the samesources/archivevg deploy --sourceuploads, so an uploaded game forks like any other. Themcp.toolmeta lives inapps/web, never in@repo/contract: the CLI bundles the contract and@orpc/mcpwould drag the MCP SDK in. The cookie is dropped before the context is built, so only avg_…key or session bearer signs a call: a game page is same-site with the apex, and SameSite=Lax would otherwise carry the session into a tool call. Not tools: what only touches the caller's machine (init,login,new,update, …) andplaytest run, which drives a local browser.mcp.test.tsholds the tool list, the annotations, the cookie drop and that no prose names fal.
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 13d ago Changed · +92 tokens per session 42e2be799b9c
- 14d ago Changed · +1 lines · +424 tokens per session 08149e77bec6
- 28d ago Changed · +1 lines · +337 tokens per session af3b24a49815
- 29d ago Changed · +164 tokens per session 784ddd5635fd
- 1mo ago Changed · +1 lines · +234 tokens per session a9ebe6a15259
- 1mo ago Changed · +6 tokens per session d91a42196643
- 1mo ago Changed · +167 tokens per session 952f21b2fd23
- 1mo ago Changed · +5 lines · +561 tokens per session 559ca395e2fa
- 1mo ago Changed · +1 lines · +234 tokens per session 437dc6352368
- 1mo ago First seen · 133 lines · 4,084 tokens per session scan A 4a375c1fb29c
vibedgames CLAUDE.md is an instructions file published in the GitHub repository kyh/vibedgames (61 stars, last pushed 2d ago), licensed MIT. It adds 6,303 tokens to every session, about $0.0252 per session on Opus 5.5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.
vibedgames CLAUDE.md compared with instructions from other repositories
| Mod | Type | Stars | Last push | Licence | Scan | Tokens | Works with |
|---|---|---|---|---|---|---|---|
| vibedgames CLAUDE.mdkyh/vibedgames | Instructions file | 61 | 2d ago | MIT | A | 6,303 | Claude Code |
| vscode buildNext.instructions.mdmicrosoft/vscode | Instructions file | 195k | yesterday | MIT | A | 9,719 | GitHub Copilot |
| next.js AGENTS.mdvercel/next.js | Instructions file | 143k | yesterday | MIT | A | 7,461 | Codex, OpenCode |
| langchain AGENTS.mdlangchain-ai/langchain | Instructions file | 149k | yesterday | MIT | A | 4,680 | Codex, OpenCode |
| vscode oss-third-party-notices.instructions.mdmicrosoft/vscode | Instructions file | 195k | yesterday | MIT | A | 5,516 | GitHub Copilot |
| vscode copilot-instructions.mdmicrosoft/vscode | Instructions file | 195k | yesterday | MIT | A | 3,093 | GitHub Copilot |
| deepseek-harness AGENTS.mddeepseek-ai/deepseek-harness | Instructions file | 274k | 2d ago | MIT | A | 4,313 | Codex, OpenCode |