Status (2026-07-24): latest published npm release is
1.3.0(@blit386/kit@1.3.0+create-blit386@1.3.0; dist-taglatest).blit386.engineRangeandBLIT386_RANGEare^1.4.0. Hot-reload content ships in this cut (section 12; kit docs/skillshot-reload.md/use-hot-reload, CatcheronHotReloadexample, starterblit386()Vite plugin). Publishing is manual-only (see the 2026-07-14 policy change below) – there is no CI publish workflow. 24 skill directories underpackages/kit/content/skills/. Claude/Cursor agent adapters are a single shared module (packages/kit/src/adapters.ts,@blit386/kit/adapters) used by both scaffold andblit agents sync/add(BT-350). Full preflight green. (NomainHEAD SHA is pinned here on purpose: it goes stale within days. Rungit log --oneline -1for the current one.)History: first published as
@blit386/kit@0.1.0+create-blit386@0.1.1(section 11); 1.0.0 shipped 2026-06-14 (see below). All Phase 1.x code items merged tomain(PRs #7–#10). All Phase 2 "Agents on tap" work is merged tomain– the fullblit agents syncwrite path (PR #15) plus the review-driven bug fixes (sync baseline / manifest pruning / vars persistence; shared-file note preservation across repeated syncs; test exit-code assertions) and the docs sweep. The fully-qualified docs-sync-path commit (9c37894) is already inmain– it merged as the second parent of the PR #16 merge commit, so there is no pendingagent-docsfollow-up (the earlier "not yet merged" note was stale).blit agents add <claude|cursor>(Round 18, PR #17) and the kit-owned clean-merge drift fix (Round 19, PR #18) are both onmain.Release status: 1.0.0 SHIPPED (2026-06-14). Both packages are published to npm at
1.0.0(dist-taglatest):@blit386/kit@1.0.0andcreate-blit386@1.0.0(the published scaffolder manifest pins@blit386/kit: 1.0.0, confirming the pnpmworkspace:*rewrite). Landed via PR #19 (squash) tomain(0eac7aa); annotated git tag1.0.0(novprefix, matching0.1.0) created on that merged commit. Pre-1.0 features included before cutting:blit agents add(Round 18) and the clean-merge drift fix (Round 19), plus a packaging-metadata pass (repository/bugs/homepage, author object, keywords). End-to-end smoke test from npm passed (blit doctorgreen; engine1.1.1compatible with kit^1.1.1; 7 kit files up to date).BLIT386_RANGEstays^1.1.1. Release notes: the GitHub Release at https://github.com/blit386/create-blit386/releases/tag/1.0.0. Note:mainis protected, so releases land through a PR and the tag is created on the merged commit (not the pre-merge branch SHA).Release status: 1.1.0 SHIPPED (2026-06-14). Minor bump over 1.0.0, bundling everything merged to
mainsince the 1.0.0 tag (PRs #20–#22): (1) theblit migratecodemod feature – typed migration registry + a dependency-free anchored codemod engine +blit upgradewiring, with the auto-vs-review split (Round 21); and (2) the generated-game skills – themigrateAI skill (Round 22) plus the game-author capability skills andshare-the-game(Round 23; 14 at the time; 24 skill directories on disk today), with Claude keeping skill YAML frontmatter and Cursor commands stripping it. Both packages published to npm at1.1.0; the scaffolder manifest pins@blit386/kit: 1.1.0(pnpmworkspace:*rewrite confirmed). Landed via PR #22 (squash) tomain; annotated git tag1.1.0(novprefix) on merged commita9e77fd.engineRangeandBLIT386_RANGEstay^1.1.1. Release notes: the GitHub Release at https://github.com/blit386/create-blit386/releases/tag/1.1.0.Release status: 1.2.0 SHIPPED (2026-06-19). Both packages published to npm at
1.2.0(dist-taglatest):@blit386/kit@1.2.0+create-blit386@1.2.0. Headline change: the package/repo rename fromcreate-blit-tech+@blit-tech/kittocreate-blit386+@blit386/kit(GitHub repoblit386/create-blit386), plus a Claude skills/rules refresh and the design doc landing in-repo (PR #34). This release also moves the engine pin forward:engineRangeandBLIT386_RANGEare now^1.2.0(was^1.1.1), tracking engineblit386@1.2.0. Landed via PR #39 (chore(release): 1.2.0) on merged commitec563aa; annotated git tag1.2.0(novprefix). Release notes: GitHub Release at https://github.com/blit386/create-blit386/releases/tag/1.2.0.Engine update (2026-07-13):
blit386@1.3.0is now live on npm (latest), which is the version carrying the audio subsystem. This opens the release-order gate in section 0 – the kit's audio content (content/docs/audio.md,content/skills/play-a-sound/, plus audio rows added tocontent/AGENTS.md,blit-api-names.md, andshow-debug-overlay) can now be published.Release status: 1.2.1 SHIPPED (2026-07-14). Both packages published to npm at
1.2.1(dist-taglatest):@blit386/kit@1.2.1+create-blit386@1.2.1. Headline change: the audio content gated above ships (PR #56), andengineRange/BLIT386_RANGEmove to^1.3.0(was^1.2.0) – not cosmetic,engineRangefeedsblit doctor's D14 compatibility check against an already-installed engine, and leaving it at^1.2.0would have madeblit doctorreport a false "compatible" for a project still onblit386@1.2.0after syncing in the new audio docs. Landed via PR #60 (chore(release): 1.2.1) on merged commitf19fafc; tag1.2.1(novprefix) on that commit. Smoke test passed (blit doctorgreen;blit386 1.3.0 is compatible with this kit (^1.3.0)). Release notes: the GitHub Release at https://github.com/blit386/create-blit386/releases/tag/1.2.1.Publishing policy change (2026-07-14): this release exposed that the
NPM_TOKENrepository secret the tag-drivenpublish.ymlworkflow depended on was missing, so the workflow failed withENEEDAUTHand1.2.1was published by hand instead (pnpm publish, kit first, per the now-only path inPUBLISHING.md). Rather than re-provision the secret, the decision is to never publish from CI again –.github/workflows/publish.ymlis deleted, and every future release is a manualpnpm publishfrom vancura's machine.PUBLISHING.mdand thecbt-releaseskill are rewritten to describe manual publishing as the only path, not a fallback. Tags are still pushed after a manual publish so the repo history keeps recording releases the same way; they no longer trigger anything.Engine update (2026-07-23):
blit386@1.4.0is live on npm (latest), carrying hot reload /blit386/vite/ asset hot-replace /BT.loadingAssetsCount. That satisfies the release-order gate for the kit's hot-reload content (section 12) the same wayblit386@1.3.0did for audio in section 0.Release status: 1.3.0 SHIPPED (2026-07-24). Both packages published to npm at
1.3.0(dist-taglatest):@blit386/kit@1.3.0+create-blit386@1.3.0. Headline change: hot-reload kit content and the starter Vite plugin for engineblit386@1.4.0(section 12;content/docs/hot-reload.md,use-hot-reload/ loading-screen skills, CatcheronHotReloadexample,blit386()intemplates/base/vite.config.js), andengineRange/BLIT386_RANGEmove to^1.4.0(was^1.3.0). Existing games opt in vianpx blit upgrade/npx blit migrate(migration2026-07-23-hot-reload-vite-plugin). Also in this cut: shared@blit386/kit/adaptersfor scaffold + agents sync, Claudesettings.jsonhooks for guardrail parity with Cursor, and lockstep bump script hardening (pnpm run bump -- <x.y.z>). Manualpnpm publish(kit first) per the 2026-07-14 policy. Landed viachore(release): 1.3.0PR; tag1.3.0(novprefix) on the mergedmaincommit. Release notes: the GitHub Release at https://github.com/blit386/create-blit386/releases (tag1.3.0after publish).Dogfood finding (Round 15, still holds): the kit IR is game-author altitude; the
blit386/blit386-demosrepos are the kit's upstream maintainers, not consumer games – regenerating their.cursor//.claude/from the current IR would delete library-maintenance tooling, so those configs were left untouched (a maintainer-profile IR is future work). Phase 3 migrations (Round 21–22): the kit ships a structured migration registry + codemod engine (packages/kit/src/migrations/), ablit migratecommand (preview by default,--writeto apply), andblit upgraderuns the applicable codemods after a version change. Safe renames auto-apply; ambiguous ones (equals,tick, ...) are reported for review, and themigrateAI skill (content/skills/migrate/) teaches the assistant to resolve them. 24 skill directories undercontent/skills/cover the full renderer / input / palette / timing / audio / post-process / hot-reload surface (plusrun,fix,migrate,share-the-game).npx blitverified (2026-06-14):blit doctor+blit runpass on npm, pnpm, and yarn for a freshly scaffolded project (bun intentionally out of scope; see section 7). Still open (roadmap only – do not treat deleted GitHub issues as live links): generate enginedocs/deprecations.mdfrom the kit migration registry; auto-stampblit386.engineRangeat release; section 7 verification TODOs (StackBlitz, Windows, iPad/Safari); Catcher starter catch/miss sounds (deferred product work). Repo: https://github.com/blit386/create-blit386 (public). Owner: Václav (vancura). First external user: Filipek. Started: 2026-06-07. Purpose: shared source of truth for the BLIT386 project scaffolder. We return to this across sessions so we do not lose decisions, findings, or deferred ideas.
This is the planning doc: roadmap, decisions, and monetization notes. It moved into the scaffolder repo in PR #34 and
now lives at the root of blit386/create-blit386, which is public – so write it as a doc a stranger may read, and keep
anything genuinely private out of it.
Status (2026-07-14): CLOSED – the gate is satisfied and the kit's audio content has shipped.
blit386@1.3.0(the audio-bearing engine release) went live on npm on 2026-07-13; PR #56 (content/docs/audio.md,content/skills/play-a-sound/, plus edits tocontent/AGENTS.md,content/rules/blit-api-names.md,content/skills/show-debug-overlay/SKILL.md,content/skills/share-the-game/SKILL.md,content/skills/structure-a-game/SKILL.md,content/docs/getting-started.md, andcontent/docs/when-something-breaks.md) merged tomain;@blit386/kitandcreate-blit386published to npm at1.2.1on 2026-07-14 (PR #60, manualpnpm publish– see the top status block for why manual). Correction (2026-07-14): the "no version-pin bump needed" call originally made here was wrong forengineRange– see the "why this is safe" paragraph below, now corrected.blit386.engineRangeinpackages/kit/package.jsonandBLIT386_RANGEinscaffold.tsboth shipped at^1.3.0in the1.2.1release. Catcher starter catch + miss sounds did not ship in1.2.1and remain deferred product work (the starter game has noBT.soundPlay/synthPresetcalls yet); tracked historically under closed #50. Those pins later moved to^1.4.0and shipped in the1.3.0release (hot reload) – see the top status block.
The kit now documents the engine's audio subsystem – content/docs/audio.md, the play-a-sound skill, the audio rows
in content/AGENTS.md and content/rules/blit-api-names.md, and the audio overlay flags in show-debug-overlay. All
of it describes API that exists in the blit386 source tree under ## 1.3.0 - Unreleased and, at the time this section
was written, was NOT on npm yet (latest was blit386@1.2.1, which had no audio) – see the status line above for where
that stands now.
The rule:
Do NOT publish
@blit386/kitwith the audio content untilblit386@1.3.0is live on npm. Publish the engine first, then the kit.
Correction (2026-07-14): the paragraph below originally argued no version-pin change was needed. That conflated two different mechanisms and was wrong for one of them.
BLIT386_RANGE (in scaffold.ts) is written into a freshly scaffolded game's package.json as its blit386
dependency range. Leaving it at ^1.2.0 really would have been harmless: npm install always resolves to the latest
version satisfying the range, so a fresh scaffold gets 1.3.0 regardless of whether the pin reads ^1.2.0 or ^1.3.0.
engineRange (in packages/kit/package.json) is a different mechanism entirely – it does not describe what a fresh
install resolves to. blit doctor reads it via kitEngineRange() (packages/kit/src/env.ts) and compares it against
an already-installed blit386 via satisfiesCaretRange(), which only checks "same major, and installed >= floor"
(packages/kit/src/commands/doctor.ts, the D14 compatibility check). Leaving engineRange at ^1.2.0 while the kit's
own content documents 1.3.0-only API means an existing project that syncs in the new audio docs (npx blit agents sync
/ blit upgrade) but still has blit386@1.2.0 installed gets told "blit386 1.2.0 is compatible with this kit (^1.2.0)"
– a false green light, while docs/audio.md describes BT.soundPlay, which does not exist on their installed engine.
That is the exact "docs lied to me" failure D14 exists to catch; the stale engineRange just made D14 blind to this
particular drift. Bumping engineRange to ^1.3.0 puts that same user in the correct "needs update" branch of
blit doctor instead.
Conclusion: BLIT386_RANGE and engineRange are bumped to ^1.3.0 together for the 1.2.1 release – required for
engineRange (the D14 compatibility check needs it), harmless-but-consistent for BLIT386_RANGE.
If the kit shipped first, a kid would follow docs/audio.md, call BT.soundPlay, and get "not a function" – the exact
"the docs lied to me" failure this repo exists to prevent. The audio doc and skill each carry a line telling the reader
to run npx blit upgrade if BT.soundPlay is missing, which covers an existing project that upgrades the kit while
sitting on an older engine, but it is a safety net, not a substitute for the publish order.
- There is no path from "blit386 installed" to "a project." The proof is
filipek-basics.jssitting loose at the workspace root: a single file, nopackage.json, noindex.html, no agent config. It hasvar, commented-out experiments, a dead statement (this.pos.y - 10;computes nothing), and a strayconsole.log('HEY'). A real kid, with no scaffold at all. This is the pain to remove. Filipek's environment (confirmed): no Node.js installed, editor is Zed, no AI agent. So v0.1 must (a) teach installing Node.js in the README, (b) nail the no-agent path, and (c) document running the game from Zed's built-in terminal. - Multi-agent config is already duplicated by hand. Both
blit386andblit386-demoscarry near-identical.claude/(3 rules + 12bt-*skills),.cursor/(.mdcrules +hooks.json+ shell hooks),.zed/, and an empty.agents/..cursor/rules/claude-canonical.mdcdocuments the current strategy: CLAUDE.md is canonical, the rest are hand-written mirrors. No generator exists. The scaffolder must solve a problem the engine repos have not solved for themselves yet. Build it once, reuse it in three places (engine, demos, every scaffolded game). docs/deprecations.mdis already a codemod table. It is a clean one-to-one old-to-new map (BT.keyDown()->BT.isKeyDown(),canvasId->canvasID, ...) with dated@deprecatedmarkers in source. The "migration skill" we want is closer than it looks: that prose table wants to become machine-readable data.
| # | Decision | Choice | Notes |
|---|---|---|---|
| D1 | Versioning model | Independent kit version | Engine on npm semver. The "kit" (AI files, docs, skills, hooks, templates) has its own version, pulled and regenerated by a project-local command. Engine and AI guidance evolve at different speeds. |
| D2 | Cloud strategy | Local-first now, cloud after verify | Ship npm create blit386 for desktop first. StackBlitz is wanted and low-risk (see finding F3). Hosted playground is deferred to Ambilab (section 6). |
| D3 | Agent support | AGENTS.md generic is canonical; generate per-agent files from it | Support all viable agents. Claude Code and Cursor files are generated from the canonical source. Generation is capability-aware, not lowest-common-denominator (Cursor hooks and contextual rules are more powerful than Claude's; use each agent's full power). |
| D4 | Default UX | Short wizard | No silent default. 2–3 questions (JS or TS, which agent or none). Kid-friendly copy for the unsure. Pros pass flags to skip. |
| D5 | Starter game | Catcher | A real tiny game (~80 lines): move a paddle, catch falling items for points, miss and lose a life. Teaches input, score, lose condition, palette. Must not depend on post-process effects (works on software fallback per F3). |
| D6 | Kit packaging + name | @blit386/kit |
Its own independently-versioned package (D1). Name resolved: create a free blit386 npm org (sole owner) for the @blit386/* scope; no collision with the unscoped blit386 engine (F5). Not Ambilab. |
| D7 | MVP build approach | Thinnest no-agent path, on the adapter pipeline | Filipek uses no agent, so v0.1 must nail the no-agent scaffold (JS + Catcher + blit run / blit upgrade + local docs). The generator's only v0.1 target is its cheapest one: emit AGENTS.md (useful as a plain doc) + docs/. Real Claude/Cursor adapters are phase 2. Built on the pipeline so adapters slot in without re-architecture. |
| D8 | Language support | JS in v0.1, TS in phase 2 | Template = shared base + thin language layer, so TS is an added layer later, not a fork. JS path still gets editor type-checking via JSDoc @typedef against the published .d.ts. |
| D9 | Repo layout | Hybrid: new repo for scaffolder + kit | create-blit386 and @blit386/kit live in one new repo. Leave blit386 and blit386-demos as their own repos, histories, and CI. Least disruption to what already publishes. |
| D10 | End-user package manager | Auto-detect; npm default | Scaffolder detects the manager that invoked it (npm_config_user_agent, like create-vite) and uses it for install + lockfile + documented run commands. Docs default to npm (ships with Node; zero extra install for kids). pnpm-only remains Václav's own-repo rule, not forced on end users. |
| D11 | Pre-Node onboarding (chicken-and-egg) | Install instructions live OUTSIDE the project | A scaffolded README cannot teach installing Node, because running the scaffolder already requires Node. The Node install steps live on pages a brand-new user sees first: the create-blit386 GitHub README (done 2026-06-12, including a copy-pasteable "send this to a friend" message) and later a docs site. Once StackBlitz is verified (section 7), the browser path becomes the lead option for users without Node: all onboarding docs must be written with two paths in mind – "in your browser (nothing to install)" first, "on your computer" second. |
| D12 | blit CLI invocation |
Document npx blit ... everywhere; lead with npm run dev |
blit is a bin inside @blit386/kit, a project dependency. Local bins are only on PATH inside package scripts, so plain blit run typed in a terminal fails with "command not found." All shipped docs (template README, AGENTS.md, kit docs, CLAUDE.md template) say npx blit ... and explain why in one beginner-friendly line. The scaffolded README leads with npm run dev; blit is presented as the helper, not the primary interface. Docs updated 2026-06-12; verify npx blit under npm/pnpm/yarn/bun (section 7). |
| D13 | User edits vs blit agents sync |
Ownership model: manifest + three file classes; never clobber | Without a plan, sync destroys user edits and people stop running it. Full spec in section 4.10. Summary: .blit/manifest.json records what the kit generated (with hashes and pristine bases); every emitted file is kit-owned (regenerate freely), shared (managed region between markers; user content preserved), or user-owned (scaffolded once, never touched again). On conflict: three-way merge when git is available, <file>.new + a friendly report otherwise. AGENTS.md managed markers + a "Your notes" section ship from v0.1.x so projects scaffolded today survive future syncs (markers added to kit content 2026-06-12). |
| D14 | Kit-engine compatibility | Kit declares a supported engine range; blit doctor checks the pair |
D1 (independent versioning) is about cadence, not content: the kit's docs describe the engine API, so a stale kit actively misleads an agent after an engine major. The kit's package.json gains a blit386.engineRange semver range; blit doctor and blit upgrade compare it against the installed blit386 and report drift in Tier-1 voice ("Your local guides describe an older BLIT386 than the one installed. Update @blit386/kit, then run npx blit agents sync."). Phase 3 doc generation stamps the range automatically. |
- F1.
npm create blit386@latestis the correct, current convention. Vite, Next, etc. all usecreate-*/npm create. Not dated. SvelteKit's brandedsv createis the only notable alternative pattern. We will supportnpm create,pnpm create, andnpx create-blit386, plus a flags form for non-interactive use. - F2. AGENTS.md is the cross-agent standard. Read natively by Cursor, Copilot, Zed, Gemini CLI, Windsurf, Aider, Codex
(stewarded under the Linux Foundation). Caveat: Claude Code does not read AGENTS.md natively as of early 2026; the
accepted workaround is AGENTS.md as source of truth with
CLAUDE.mdsymlinked or generated from it. Implication: flip the currentclaude-canonicalmodel – AGENTS.md becomes canonical, CLAUDE.md becomes generated. - F3. WebGPU is not a blocker for the cloud story. WebGPU shipped across Chrome, Firefox, Safari, Edge as of Jan 2026, including Safari on iPadOS 26+. More importantly (Václav's correction): WebGPU is a client capability, not something StackBlitz provides or withholds, and BLIT386 already runs without WebGPU via the Canvas 2D software fallback. The only features unavailable on the fallback are fullscreen post-process effects (CRT, bloom, etc.). So the worst case in any sandbox or on any older device is "no post-process effects," never a broken canvas. This de-risks both the StackBlitz path and the iPad path. The starter game must therefore not depend on post-process effects.
- F4. StackBlitz WebContainers can run a Vite dev server in-browser. Whether the preview iframe exposes WebGPU was not confirmed by search, but per F3 it does not matter – software fallback covers it. Still worth a 30-minute manual test before we advertise StackBlitz, recorded in section 7.
- F5. The kit can be a scoped package without an npm "team." Václav owns the unscoped
blit386package under uservancura. A free npm organization can have a single member, and an org namedblit386grants the@blit386scope. The unscoped packageblit386and the scope@blit386are separate namespaces, so there is no collision. Three options: (a) create the freeblit386org and publish@blit386/kit(reserves the whole@blit386/*scope; tidiest); (b) unscoped siblingblit386-kit(zero setup, matches the existing unscoped names); (c) user scope@vancura/blit386-kit(works now, least on-brand). TheblitCLI ships as abininside the kit package, so it needs no npm name of its own.
| Thing | What | Distribution | Cadence |
|---|---|---|---|
| Engine | blit386 npm package (v1.1.1) |
npm, semver | when the API changes |
| Kit | AGENTS.md + local docs + skills + hooks + templates | see open question Q-KIT | when AI guidance or tooling improves |
| Scaffolder | create-blit386 CLI |
npm, run once via npm create |
rarely |
This resolves the "does it pull the package or the scaffolder?" question. The engine updates via npm. The kit updates via a project-local command that re-pulls and regenerates the AI files. They move at different speeds.
The engine's 12 bt-* skills (release, pr, issue-audit, security-run, perf, knip, spellcheck, ...) are
library-maintenance skills. A scaffolded game needs none of them. It needs game-author skills that do not exist yet:
run– start the dev server, open the game.add-sprite,play-a-sound,draw-shapes, ... – guided feature recipes, one per engine capability.fix– diagnose a runtime error (e.g. forgotawait, palette index out of range) using the local docs.migrate– bump blit386, run codemods, resolve the ambiguous renames (section 4.6).share-the-game– ship the game (builddist/, upload it to any static host).
Design these fresh. Do not copy the library skills into the template.
Built out (19 skills, see Phase 3). Two of the original sketch names were deliberately dropped: add-enemy and
add-scene – the engine has no physics, collision, entity, or scene system, so those skills would have to invent one.
add-sound shipped as play-a-sound (the engine's audio subsystem landed in blit386 1.3.0).
The kit holds one canonical, human-readable source. A generator renders per-agent files from it. Because agents differ in capability (D3), generation is capability-aware, not a dumb copy.
Confirmed canonical kit layout (intermediate representation; Q-GROUND-TRUTH-FORMAT resolved 2026-06-07):
kit/
AGENTS.md # canonical prose: persona, lifecycle, hard rules, the "router" map
rules/*.md # one rule per file, frontmatter declares scope (globs, alwaysApply, which agents)
skills/<name>/SKILL.md # game-author skills (name, description, when-to-use, steps)
hooks/ # hook scripts + hooks.manifest.json declaring intent in a neutral schema
docs/*.md # progressive-disclosure deep dives (trimmed from engine docs/api-*.md)
agents.config.json # capability matrix: which adapter renders what, and how
Capability matrix (what each adapter emits from the same source):
| Capability | AGENTS.md (generic) | Claude Code | Cursor | Zed |
|---|---|---|---|---|
| Persona / hard rules | the file itself | CLAUDE.md (symlink or generated copy) + .claude/rules/*.md |
.cursor/rules/*.mdc (globs, alwaysApply) |
reads AGENTS.md |
| On-demand actions (skills) | described in prose | .claude/skills/<name>/SKILL.md |
.cursor/commands or scoped rules |
reads AGENTS.md |
| Deterministic guardrails (hooks) | prose warning only | .claude/settings.json hooks (PreToolUse / PostToolUse) |
.cursor/hooks.json (afterFileEdit, beforeShellExecution, failClosed) – richest |
.zed/settings.json tool_permissions |
| Lockfile / .env block | prose warning | settings.json PreToolUse | hooks.json failClosed |
settings.json always_deny |
This formalizes exactly what the engine repos do by hand today. Reuse the output to clean up the engine repos too.
The ground truth expresses INTENT; each adapter expresses its agent's CAPABILITY. Content differs per agent, not just file location. Worked example – one guardrail ("never let the agent edit lockfiles or secrets"), four renderings:
Canonical intent (kit/hooks.manifest.json):
{
"id": "block-sensitive-writes",
"intent": "Never let the agent edit lockfiles or secrets",
"event": "before-file-write",
"deny": ["pnpm-lock.yaml", "*.lock", ".env*"],
"failClosed": true
}- AGENTS.md (generic): a prose line under hard rules – "Never modify pnpm-lock.yaml, *.lock, or .env files." Instruction only; most generic readers cannot enforce.
- Claude Code: a
.claude/settings.jsonPreToolUse hook matchingWrite|Editthat blocks those paths. - Cursor: a
.cursor/hooks.jsonentry withfailClosed: true– actually refuses the write. Richest enforcement. - Zed:
.zed/settings.jsontool_permissions.{edit_file,write_file}.always_denypatterns.
Same intent; four formats; differing enforcement power (AGENTS.md only instructs, the others truly block). Rules and
skills follow the same pattern: a "rule" becomes an AGENTS.md bullet, a .claude/rules/*.md, and a glob-scoped
.cursor/rules/*.mdc; a "skill" becomes a .claude/skills/<name>/SKILL.md, a .cursor/commands/<name>.md, and a "read
docs/.md" pointer for agents without a skill mechanism. Adding a new agent = writing one adapter that maps these
intent types to that agent's files and capabilities.
- AGENTS.md = thin router (~150 lines). Persona, the four lifecycle methods (
configure,init,update,render), hard rules (integer coords viaVector2i/Rect2i, palette indices,BTnamespace, no floats), and a map: "Need sprites? readdocs/sprites.md. Need input? readdocs/input.md." It points; it does not contain the API. - docs/ = task-scoped deep dives, trimmed from the engine's existing
docs/api-*.mdto game-author altitude. The agent reads one on demand, spending minimal context. - skills = the actions, loaded on demand. This is the proven Claude Code skill mechanism.
Net effect: the agent "just knows" how to start and where to look, without preloading the whole API.
A small CLI (name confirmed: blit, a bin of @blit386/kit) the user runs inside their game. Invocation is
npx blit ... (D12): the bin is project-local, not on the system PATH, so plain blit fails outside package scripts.
Every doc that mentions it uses the npx form and explains why once, kindly.
npx blit run– dev server (thin wrapper overnpm run dev/ vite).npx blit upgrade– bump blit386, detect version delta, run codemods, show a diff, escalate non-mechanical changes to the AI migration skill (section 4.6). Refuses-with-kindness if unversioned (section 4.7).npx blit agents add <name>– generate config for a newly discovered agent from the canonical kit. Implemented Round 18 forclaudeandcursor: regenerates that assistant's adapter output from the installed kit, writes the new files, and records them in the manifest (so latersynckeeps them fresh). All-or-nothing: if any generated file would collide with an existing untracked user file, it writes only<file>.newcopies, leaves the project and manifest untouched, and exits non-zero. (A half-add would let a latersyncregenerate the colliding path, find no manifest entry, and clobber the user file – so the command refuses to partially activate the assistant.) A friendly no-op pointing atsyncwhen the assistant is already set up.npx blit agents sync– regenerate kit-managed files after a kit update, honoring the ownership model (4.10).--checkexits non-zero on drift without writing (CI-friendly; also surfaced byblit doctor).npx blit doctor– environment + project health check (node version, git presence, blit386 version, kit-engine compatibility per D14, agent-file drift per 4.10).
Short commands, copy-pasteable – matches the "I appreciate the commands are short" goal. The scaffolded README leads
with npm run dev; blit is the helper layer on top.
- Ship structured migrations with the engine (or kit): each dated migration exports (a) a machine-applicable codemod for mechanical renames (the one-to-one table – regex or ts-morph/jscodeshift) and (b) a human-readable summary of intent.
- Flip the source of truth:
docs/deprecations.mdbecomes generated FROM the migration data, not hand-written. blit upgraderuns applicable codemods automatically, shows a diff, and for non-mechanical changes invokes the AI migration skill with only the relevant migration notes loaded into context.- The AI skill is the fallback for what codemods cannot do safely. Precedent: React / Next ship
npx ...codemod.
Status (Round 21–22): built kit-side. packages/kit/src/migrations/ holds the typed registry (registry.ts, seeded
from deprecations.md) and a dependency-free, anchored codemod engine (codemod.ts). Each rename is classified auto
(receiver-anchored BT.*, distinctive configure() keys, distinctive method names) or review (generic names that
could match unrelated code: common method words equals/contains/intersects/tick and generic bootstrap keys
canvasId/containerId/waitForDOMReady – reported with a suggestion, never auto-rewritten). blit migrate previews
by default and writes only with --write (kid-safe: warns + confirms when the project is not under git); blit upgrade
runs the applicable codemods after a real version change and offers to apply them. Decisions / still open: (1) the
migration data lives in the kit, not the engine, so the feature ships without an engine release – the long-term "flip
deprecations.md to be generated from this data" is cross-repo and deferred (the two are mirrored by hand for now); (2)
no ts-morph/jscodeshift dependency yet – anchored string matching covers the current one-to-one table; (3) the AI
migration skill (content/skills/migrate/SKILL.md, Round 22) ships into generated games as a Claude skill and a Cursor
command: it runs blit migrate --write for the autos and resolves each review hit by checking the receiver type
(equals/contains/intersects/tick, generic bootstrap keys).
Follow the engine's voice guide Tier 1 (plain English, one sentence on what is wrong, one concrete next step, no jargon,
no emoji, sentence case). Trigger on first run, blit doctor, and especially before blit upgrade.
Draft copy (refine later):
Your game is not saved with version control yet. If a BLIT386 update ever changes something, you could lose your work. Run
git initto start saving snapshots, or keep a copy of your folder somewhere safe before upgrading.
Before an upgrade on an unversioned project: strongly suggest a backup, or proceed only after explicit confirmation. Never block a kid mid-flow without a clear, friendly way forward.
As built in v0.1 (slightly richer than the original three-question sketch):
npm create blit386@latest my-game(or no folder argument – the wizard asks for a name).- "Which language do you want?" – JavaScript (recommended) or TypeScript. Both ship (the TS layer landed in phase 2);
--tsskips this prompt. - "Do you use an AI coding assistant?" – None (recommended to start) / Claude Code (adds
CLAUDE.md) / Cursor (adds.cursor/rules). "None" still emitsAGENTS.md+ localdocs/(see 4.9). - "Add GitHub Actions CI (build + format check)?" – optional, default no.
- Scaffold,
git init+ first commit (skippable), install, print the next steps.
Flags for pros and CI (built): --yes/-y (skip prompts, defaults), --ts (TypeScript layer), --no-install,
--no-git. Still unimplemented: --agent=<name> (the assistant can only be chosen in the wizard, or added afterwards
with blit agents add).
Robustness rules (v0.1.x backlog, required for agent-driven and CI use):
- Non-TTY input: when
stdinorstdoutis not a TTY (an AI agent or CI invoked the scaffolder), do not start the clack wizard – it would hang. Behave as--yesand print one informational line saying defaults were used and which flags exist. This is the single most common way scaffolders break under agents. - Node version gate: check
process.versions.nodeagainst the engine's floor (>= 22.18.0) as the very first step, before any prompt. On failure print one Tier-1 sentence ("BLIT386 needs Node 22.18 or newer; yours is X. Download the LTS from nodejs.org.") and exit. Do not let a kid discover this as anEBADENGINEwall of text mid-install.
A minimal, real game (Catcher per D5), plus:
package.json(blit386 from npm, dev/build scripts),index.html(canvas + container perbootstrapcontract),vite.config,public/, the starter game file,.gitignore,.editorconfig,biome.json,jsconfig.json.README.mdwritten for the chosen persona. Leads withnpm run dev(D12); includes the overlay-key explanation (position-based Backquote + tap-the-corner fallback), a "Share your game" section (npm run build+ dragdist/to a static host), and a pointer todocs/when-something-breaks.md.docs/local copies – always, even in no-AI mode (they are human docs first). Includeswhen-something-breaks.md, the beginner troubleshooting guide (added 2026-06-12).AGENTS.md– always emitted (it doubles as a human doc and carries the managed-region markers per 4.10). Generated agent dirs (.cursor/,CLAUDE.md, ...) only if an agent was chosen.- The
blitproject-local CLI (via the@blit386/kitdependency). - From v0.1.x:
.blit/manifest.jsonseeding the sync ownership model (4.10).
The failure mode to design against: a user (or their agent) customizes AGENTS.md or a generated rule, later runs
sync to get kit improvements, and loses their edits. After that happens once, nobody runs sync again and the whole
"stays fresh" story dies. The fix is an explicit ownership model, enforced by a manifest, with a no-clobber guarantee.
This is the Debian conffile / git merge-file model adapted to kid-friendly output.
Three file classes. Every file the scaffolder or sync emits belongs to exactly one:
| Class | Examples | Sync behavior | How users customize |
|---|---|---|---|
| Kit-owned | docs/*.md (kit names only), .cursor/rules/kit-*.mdc, .claude/skills/kit-*/, generated hook entries |
Regenerated freely when unmodified; never clobbered when modified (see conflict rule) | Don't edit these; add sibling files with your own names (.cursor/rules/my-*.mdc). Kit files carry a one-line header: "Generated by @blit386/kit – your edits will be flagged on sync; put your own rules in a new file." |
| Shared (managed region) | AGENTS.md, generated CLAUDE.md |
Only the content between <!-- blit-kit:managed:start --> and <!-- blit-kit:managed:end --> is rewritten; everything outside (the seeded "Your notes" section and anything else) is preserved byte-for-byte |
Write below the end marker. The file says so in plain language. |
| User-owned | README.md, src/game.js, index.html, vite.config, package.json |
Scaffolded once; sync never touches them (engine bumps go through blit upgrade, not sync) |
Edit freely. |
Reserved namespace. Kit-owned files use a reserved prefix (kit-*) or fixed, documented names so user files can never
collide with a future kit file. Sync refuses to write outside its manifest + reserved names.
The manifest. Sync needs to know "did the user touch this?" without guessing. .blit/manifest.json (committed) records,
per generated file: path, file class, kit version that wrote it, and the SHA-256 of the reconciled on-disk content from
the last sync (at scaffold time this is just the as-generated content). Pristine as-generated copies are kept under
.blit/base/ (small text files; also committed) so a real three-way merge is possible later. Two distinct references,
do not conflate them (Round 19): the recorded SHA-256 is the drift reference – sync --check/doctor compare the
current file against it, so a clean-merged file reads as in-sync rather than drifting forever. The .blit/base/<path>
copy is the pristine merge ancestor and the reference for "did the user change this kit file?" in the full-sync write
path. After a clean three-way merge the on-disk file holds the user's edits, so the recorded SHA-256 becomes the merged
hash while the base copy stays the pristine kit version (shared files already worked this way; Round 19 made kit-owned
files consistent).
Sync algorithm (deterministic, no AI involved):
- For each file the new kit wants to emit:
- Not in manifest and exists on disk -> never touch; report ("exists, not kit-managed, skipped").
- In manifest, current hash == recorded hash (user never modified) -> overwrite with the new version; update manifest + base.
- In manifest, hashes differ (user modified):
- Shared file -> rewrite only the managed region; preserve the rest; update manifest.
- Kit-owned file -> no clobber. If git is available: three-way merge (
git merge-filesemantics) using.blit/base/<file>as the ancestor; clean merge applies, conflict markers do not – fall through to.new. Without git or on conflict: write the incoming version as<file>.new, keep the user's file, and report one friendly line per file ("You changed docs/input.md, so I saved the new version next to it as docs/input.md.new. Compare them and keep what you like.").
- Files in the manifest that the new kit no longer ships -> list them and suggest deletion; never auto-delete.
- Exit summary in Tier-1 voice: N updated, N preserved, N need your eyes (with paths).
Modes. npx blit agents sync --check: report-only, non-zero exit on drift (CI; also run inside blit doctor).
--force <path>: explicit per-file clobber for users who want the kit version back.
Seeding now (v0.1.x), implementing later (phase 2): the markers in AGENTS.md and the manifest writing cost almost
nothing today and make every project scaffolded from now on sync-safe. The merge machinery lands with the real adapters
in phase 2.
- Engine update:
pnpm up blit386(orblit upgrade, which wraps it). Pulls the new npm package. May trigger codemods (4.6). - Kit update: a separate step (
blit agents syncafter pulling a new kit version). Regenerates AGENTS.md, docs, skills, hooks, agent dirs. Independent of the engine version (D1). - Open: how the kit reaches the project (Q-KIT below).
- Cloud editor (StackBlitz): offer a one-click "open in StackBlitz" for a hosted edit + dev-server experience. Low-risk per F3 (software fallback covers any missing WebGPU). Good for the iPad-on-a-train user. Verify once (section 7), then add a button to the README and docs site. Doc rule (D11): once verified, the browser path becomes the FIRST option in all onboarding copy ("in your browser – nothing to install"), with the local path second; it fully sidesteps the pre-Node chicken-and-egg for new users. Note the starter game must be playable without a keyboard for this audience (pointer/touch input, section 9 phase 1.x).
- Hosted BLIT386 playground = Ambilab. This is Václav's monetization strategy, under a separate team and brand: domain
ambilab.games, where users host their games (CodePen-style editor + live WebGPU preview + fork + hosting). Distant
future, separate from the open-source scaffolder. The scaffolder should leave a clean seam so a game can later
"publish to Ambilab" via the
blit publishskill. Keep this out of the publicblit386repo.
- Create the free
blit386npm org. Done 2026-06-07: npmjs.com/org/blit386 is live; engine package left unscoped. Steps in section 10. - Manually confirm a Vite + blit386 project boots in a StackBlitz WebContainer and renders (WebGPU or software fallback). 30 minutes. Record result here. → BT-301
- Confirm Safari on iPadOS 26 runs a scaffolded game (WebGPU or fallback). Requires touch input in Catcher first (phase 1.x) or the game is technically rendering but unplayable. → BT-302
- Verify
npx blit run/npx blit doctorwork in a freshly scaffolded project under each detected package manager – the docs now promise thenpxform (D12). DONE 2026-06-14 (macOS, Node 26.3.0) for npm, pnpm, and yarn. Tested against publishedcreate-blit386@1.0.0->@blit386/kit@1.0.0+blit386@1.1.1. For each managerblit doctordetects the package manager from the lockfile, reports the engine version, the kit-compatibility line (^1.1.1), and "7 kit-managed files up to date";blit runstarts Vite v8.0.16 and serves the game on http://localhost:5173 (HTTP 200, correct<title>, "Starting your game with "). yarn is Corepack classic 1.22.22 and runsyarn dev(norunkeyword) correctly. bun is intentionally not verified – it is a niche choice for this audience and the cost/benefit is poor; if a bun user hits a problem they can file an issue. Two environment notes, NOT product bugs: (1) Vite binds IPv6::1only by default, so127.0.0.1curls refuse whilelocalhost/[::1]return 200. (2) A local pnpmminimumReleaseAgegate madepnpm create blit386@latestresolve the SCAFFOLDER to the oldercreate-blit386@0.1.0; a clean pnpm install of a kit^1.0.0project installs 1.0.0 (auto-recorded inminimumReleaseAgeExclude) anddoctor/runboth pass. - Scaffold and run a game on Windows (PowerShell + cmd): wizard,
git initabsent-git path, install,npm run dev,npx blit doctor. Most beginners are on Windows and nothing has been tested there. Record result here. → BT-300 - Prototype
blit agents syncfrom the canonical kit IR (phase 2), implementing the ownership model in 4.10. Done 2026-06-13 (Round 15): full write path inpackages/kit/src/commands/agents.ts+ generate-to-memorypackages/kit/src/adapters.ts, manifestvarsfor deterministic regeneration, git three-way merge +.newfallback,--force.
Resolved:
- Q-NAME (resolved):
create-blit386(sonpm create blit386); project CLIblit, shipped as abininside the kit package (no npm name of its own). - Q-GAME (resolved -> D5): Catcher.
- Q-KIT (resolved -> D6): its own package. Mechanism = a kit dependency the project installs;
blit agents syncregenerates from the installed version;pnpm upupdates it. (Not bundled in the engine, not GitHub-fetch.) - Q-MVP (resolved -> D7): thinnest, on the adapter pipeline.
- Q-KIT-NAME (resolved -> D6):
@blit386/kitvia a freeblit386npm org. - Q-REPO (resolved -> D9): hybrid – new repo for scaffolder + kit; engine and demos stay separate.
- Q-FILIPEK-AGENT (resolved): Filipek uses no AI agent (and no Node.js yet; editor is Zed). v0.1 ships the no-agent path; no agent dirs emitted; README teaches Node.js install and running from Zed's terminal.
- Q-GROUND-TRUTH-FORMAT (resolved): canonical kit IR =
AGENTS.mdprose +rules/*.md+skills/*/SKILL.md+hooks.manifest.json+agents.config.json(see 4.3). - Q-PKGMGR (resolved -> D10): auto-detect the package manager that invoked
create-blit386(vianpm_config_user_agent, like create-vite); default docs/examples to npm (ships with Node). pnpm-only stays Václav's own-repo rule. - Q-ONBOARDING (resolved -> D11, 2026-06-12): pre-Node install instructions live outside the project (repo README + send-to-a-friend snippet; later the StackBlitz browser path leads).
- Q-CLI-PATH (resolved -> D12, 2026-06-12): all docs use
npx blit ...; README leads withnpm run dev. - Q-SYNC (resolved -> D13, 2026-06-12): sync ownership model specified in 4.10 (manifest + three file classes + no-clobber merge). Markers seeded in the kit's AGENTS.md now.
- Q-KIT-ENGINE-COMPAT (resolved -> D14, 2026-06-12): kit declares
blit386.engineRange; doctor/upgrade check it.
Still open: none.
Phase 0 – Design (now): this doc. Largely done; remaining: Q-GROUND-TRUTH-FORMAT, then a concrete v0.1 spec.
Phase 1 – v0.1 "Filipek can start" (no agent, JS): BUILT 2026-06-07 at /Users/vancura/Repos/_BLIT386_/create-blit386
(own git repo; not published; not committed by Claude).
- Hybrid repo (D9); pnpm monorepo with
@blit386/kit+create-blit386(strict TS, built with tsup). Theblit386npm org is created. - Wizard (clack): language (JS; TS "coming soon"), agent (None; Claude/Cursor "coming soon").
--yesskips prompts. - Emits: the JS Catcher game (uses
BT.systemPrint, so NO font asset is shipped),index.html(pixel-perfect CSS),vite.config.js, a renderedpackage.json(blit386 from npm),.gitignore, a kid-friendlyREADME.md, and the kit'sAGENTS.md+ localdocs/. Autogit init+ first commit (skippable). Package manager auto-detected. blit run,blit doctor(node/git/version),blit upgrade(bump + kind no-git nag + gated confirm),blit agents(friendly stub).- Verified end-to-end: build + typecheck + lint pass; the scaffolder produces a correct tree with all placeholders
rendered; the generated game runs in a browser on the published
blit3861.1.1 (WebGPU backend, clean console, score/lives logic working);blit doctor/upgradenag /agentsstub all behave. The pre-publish test linked the kit via afile:path; the template itself ships@blit386/kit@^0.1.0. - PUBLISHED 2026-06-09:
@blit386/kit@0.1.0thencreate-blit386@0.1.0are both live on npm (verified 2026-06-13 via the registry). Hand Filipek the one-line command once the next release ships (number TBD – see the status header; the Phase 1.x batch may go out as0.1.1,0.2.0, or1.0.0). - Done when:
npm create blit386 my-game-> installs -> runs -> Filipek plays Catcher and edits it. - Built beyond the original v0.1 sketch (recorded 2026-06-12):
--no-install/--no-gitflags, an optional CI question, and light Claude (CLAUDE.md) / Cursor (.cursor/rules) templates in the wizard. These are static templates, not the phase-2 generator.
Phase 1.x – v0.1.x polish (from the 2026-06-12 review; small, do before or right after first real users):
Docs, DONE 2026-06-12 (in the repo, unpublished until the next kit/scaffolder release):
-
docs/when-something-breaks.md– beginner troubleshooting guide (console reading, blank screen, forgottenawait, empty palette slot,command not found, port in use, undo/git restore). Shipped in kitcontent/docs/. - All docs use
npx blit ...with a one-line why (D12); template README leads withnpm run dev. - Template README: overlay key explained by position (below Esc; the symbol printed there varies by layout – on some
keyboards
~sits left of Z) with the Quake-console heritage, plus the no-keyboard fallback (tap the bottom-left corner; phones, tablets, Steam Deck). - Template README: "Share your game" section (
npm run build+ dragdist/to Netlify Drop / Cloudflare Pages). - Repo README: "Never used Node.js before?" section + send-to-a-friend snippet (D11 chicken-and-egg fix).
- Kit
AGENTS.md:blit-kit:managed:start/endmarkers + seeded "Your notes" user section (D13 groundwork).
Code, TODO (each is small and independently shippable):
- Non-TTY guard in
create-blit386(4.8): DONE 2026-06-13. Whenstdin/stdoutis not a TTY, the CLI behaves as--yesand prints one Tier-1 info line naming the defaults used and the flags (--yes,--no-install,--no-git). Covered by an end-to-end test that runs the CLI withstdio: 'ignore'(with a timeout that fails if it ever hangs on a prompt). Logic insrc/env.ts(isInteractive), wired insrc/index.ts. - Node version gate at CLI start (4.8): DONE 2026-06-13. The very first step of
main()checksprocess.versions.nodeagainst the engine floor (NODE_FLOOR = 22.18.0) and, on failure, prints one Tier-1 sentence pointing at nodejs.org and exits before any prompt – noEBADENGINEwall. Pure comparison (meetsNodeFloor) lives insrc/env.tswith unit tests across the boundary, pre-release tags, and short version strings. - Touch/pointer input for Catcher: DONE 2026-06-13. Paddle follows
BT.pointerPos(0)whenBT.isPointerActive(0)is true (mouse or touch); arrow keys / BTN_LEFT/RIGHT remain the fallback when no pointer is active. Beginner-friendly comments explain the slot-0 concept (multiple fingers; 0 = first), the center-under-pointer math, and why each branch exists. File:templates/js/src/game.js. - Write
.blit/manifest.json+.blit/base/at scaffold time (4.10): DONE 2026-06-13. After emitting all project files,scaffold()insrc/scaffold.tscomputes a SHA-256 digest for each file, classifies it (kit-owned/shared/user-ownedvia aclassifyFilehelper), writes.blit/manifest.jsonwith path / class / kitVersion / sha256 per entry, and copies pristine kit-owned + shared files to.blit/base/for future three-way merge.collectTreehandles thecpSyncdocs tree (which returns void). Test asserts manifest presence, AGENTS.md classified asshared, a 64-char sha256, and the.blit/base/AGENTS.mdbase copy. - Kit-engine compatibility check (D14): DONE 2026-06-13. Added
"blit386": { "engineRange": "^1.1.1" }topackages/kit/package.json. New helpers inpackages/kit/src/env.ts:satisfiesCaretRange(installed major matches and version >= floor),exceedsCaretRange(installed major > range major = kit is stale), andkitEngineRange(reads the field from the kit's own package.json viaimport.meta.url).blit doctornow prints a success line when compatible, or a Tier-1 warn + info pair directing tonpx blit upgrade(stale kit) ornpm update blit386(stale engine). No third-party semver package added.
Phase 2 – "Agents on tap" (COMPLETE 2026-06-13, all merged to main):
- TS language layer (
templates/ts/,--tsflag,ScaffoldOptions.language; PR #11). - Claude adapter (generated from kit IR into
CLAUDE.md+.claude/rules/+.claude/skills/; PR #12). Later parity with Cursor guardrails:.claude/settings.json+.claude/hooks/fromhooks.manifest.jsonclaude:blocks (BT-254). - Cursor adapter (generated into
.cursor/rules/*.mdc+.cursor/hooks.json+.cursor/hooks/+.cursor/commands/; PR #13). -
blit agents sync --check: drift detection, report-only, non-zero exit on drift (CI-safe); also integrated intoblit doctor. Manifest shape validation and path-traversal guard added in follow-up (PR #14). - Dogfood: ran the generators against the engine repos (Round 15). Outcome: do not replace. The kit IR is
game-author altitude (2 game rules,
run/fixskills, 5 beginner docs, one shell-safety hook); the engine repos carry library-maintenance config (6+ repo-specific.mdcrules, 12bt-*/ 9demos-*skills, RTK/format hooks). The engine repos are the kit's upstream source, not scaffolded games – overwriting their configs would delete maintenance tooling. A separate "maintainer profile" IR would be needed before any reuse; deferred (Phase 3+). - Full
blit agents syncwrite path (Round 15): regenerates kit output in memory from the installed kit IR, overwrites unmodified kit-owned files, managed-region merge for shared files (AGENTS.md/CLAUDE.md), git three-way merge for user-edited kit-owned files with a<file>.newfallback, manifest + base refresh, and--force [path...]. The scaffolder now records the template vars in.blit/manifest.jsonso regeneration is deterministic. Four new tests (clean Claude/Cursor parity,--forcerestore, shared managed-region merge). - Cut
1.0.0release (2026-06-14, Round 20): both packages published to npm;1.0.0tag on the mergedmaincommit (novprefix); GitHub Release live. End-to-end smoke test green.
Phase 3 – "Stays fresh":
- [~] Structured migrations derived from
deprecations.md;blit upgraderuns codemods + shows a diff; AI migration skill for non-mechanical changes. Round 21: registry + codemod engine +blit migrate+blit upgradewiring shipped kit-side (auto vs review split). Round 22: AI migration skill (content/skills/migrate/) ships into generated games for Claude and Cursor, teaching the assistant to apply--writeautos and resolvereviewhits by receiver type. Remaining (roadmap): generate enginedocs/deprecations.mdfrom this data (cross-repo). → BT-299 - Auto-stamp
blit386.engineRangeduring release + kit docs drift detection CI (replaces the original "generate kit docs FROM enginedocs/api-*.md" plan; full prose generation is not feasible). → BT-293 - More game-author skills. Round 23 shipped 14 capability skills in
content/skills/(structure-a-game,draw-shapes,add-sprite,add-text,use-palette,animate-the-palette,move-and-time,scroll-with-camera,read-keyboard,read-pointer,read-gamepad,add-crt-effect,save-a-screenshot,show-debug-overlay), later joined byplay-a-soundandshare-the-game, and bysmooth-the-motion,design-a-sound, andkeep-it-fastin Round 24, thenuse-hot-reloadandshow-a-loading-screenwith the 1.4.0 hot-reload work. Withrun,fix, andmigrate, that is 24 skill directories today, covering the full renderer / input / palette / timing / audio / post-process / hot-reload surface.share-the-gameIS the publish skill and it shipped: it teachesbuild+preview+ uploaddist/to a static host, which needs no deploy config in the scaffold. Still noadd-enemy/physics skill – the engine has no game systems, and inventing one in a skill would be a lie. The current list is the directory itself, mirrored for humans inpackages/kit/README.md– do not re-enumerate it here.
Phase 4 – "Reach":
- StackBlitz one-click (after the section 7 verify); iPad path. → BT-292 (blocked by BT-301)
- More agents (Zed, Gemini CLI, Windsurf) – cheap once the pipeline exists. → BT-295 (umbrella; sub-issues: BT-296 research, BT-297 Zed, BT-298 Gemini CLI, BT-294 Windsurf)
Separate product (later): Ambilab (ambilab.games) hosted editor + game hosting; blit publish seam (section 6).
- Sign in to npmjs.com as
vancura(the account that ownsblit386). Ensure 2FA is enabled – npm may require it to create an org and to publish. - Open the create-org page: avatar menu (top-right) -> "Add Organization", or go to
npmjs.com/org/create. - Org name:
blit386. This becomes the scope@blit386. Org names share a namespace with usernames, not with package names, so the existingblit386package does not block it. If the org name is somehow taken, fall back to unscopedblit386-kit(no org needed) or a variant likeblittech. - Plan: choose Free ("unlimited public packages"). The paid tier is only for private packages; not needed.
- If npm offers to add the existing
blit386package to the org, skip it. The engine must stay published as unscopedblit386; never let a step rename it to@blit386/blit386orpnpm add blit386breaks for every consumer. - Finish. You now own
@blit386/*. Leave membership as just you. - When publishing the kit: in its
package.jsonset"name": "@blit386/kit"and"publishConfig": { "access": "public" }(scoped packages default to private; public access keeps it free – same pattern already used on the engine). Publish from a logged-in shell.
Status (verified 2026-07-24 against the npm registry): @blit386/kit@1.3.0 and create-blit386@1.3.0 are published and
live (dist-tags.latest = 1.3.0 on both). Prior releases: 0.1.0 / 0.1.1, then 1.0.0 (2026-06-14), then 1.1.0
(2026-06-14; migrate codemods + game-author skills), then 1.2.0 (2026-06-19; create-blit-tech/@blit-tech/kit
rename to create-blit386/@blit386/kit), then 1.2.1 (2026-07-14; audio content + engineRange fix), then 1.3.0
(2026-07-24; hot reload for engine 1.4.0 – see the top status block and section 12). Git tag 1.3.0 (no v prefix) on
the merged main commit; release notes live in the GitHub Release at
https://github.com/blit386/create-blit386/releases (tag 1.3.0 after publish).
Publishing is manual-only (policy change 2026-07-14, see the top status block): there is no
.github/workflows/publish.yml and no NPM_TOKEN secret. Nothing publishes on a tag push. Tags are still cut and
pushed after a manual publish, purely as a release marker for the repo history.
Procedure (repeat for each release; bump versions first – a version, once published, is permanent):
- Commit first (
pnpm publishrefuses a dirty tree; otherwise pass--no-git-checks). npm login, thennpm whoamimust printvancura(sole owner of the freeblit386org).pnpm install && pnpm -r build(theprepackscript also rebuilds on publish).- Publish the KIT FIRST (the scaffolder depends on it):
pnpm --filter @blit386/kit publish(try--dry-runfirst; add--otp=<code>if 2FA). It is scoped;publishConfig.access: publickeeps it a free public package. - Publish the SCAFFOLDER SECOND:
pnpm --filter create-blit386 publish. In--dry-run, confirm the manifest shows"@blit386/kit": "<version>", NOT"workspace:*". - Verify:
npm view @blit386/kit version; smoke testnpm create blit386@latest smoke-test-> install -> run. - Tag the merged commit on
main(git tag <version> && git push origin <version>) and publish a GitHub Release (gh release create <version> --title "Release <version>" --notes-file ...) – this records the release; it does not trigger anything.
Critical rules: ALWAYS pnpm publish, never npm publish (only pnpm rewrites workspace:* to a real version); kit
before scaffolder; with 2FA publish one package at a time (each needs a fresh OTP).
New games from create-blit386 get the blit386() Vite plugin in templates/base/vite.config.js. That plugin is a
subpath export of the engine (blit386/vite). In dev it:
- Appends a tiny snippet to the game entry module (the file that imports
blit386and callsbootstrap(). - Watches
public/and broadcasts asset-change events so images, audio, and.btfontfiles replace in place.
The snippet's job is registration only. The literal import.meta.hot.accept() must appear in the transformed module
source (Vite marks self-accepting modules by static analysis). The engine owns all swap logic after
registerHotReload(import.meta.hot) runs. Game authors never call registerHotReload by hand.
Tiered swap (Defold-style), decided once and kept as the product model:
| Edit | Behavior |
|---|---|
update() / render() / related method bodies |
Prototype swap; instance fields kept |
init() / constructor / class field initializers |
Fresh instance + init(); optional onHotReload can restore a snapshot |
configure() hardware settings |
Full page reload via hot.invalidate() |
Kit docs (content/docs/hot-reload.md), the use-hot-reload skill, AGENTS.md, and the Catcher templates (commented
onHotReload example) teach the same model.
Decision: scaffold template changes apply to new games only. The starter vite.config.js, index.html,
src/game.*, and package.json are user-owned – blit agents sync never rewrites them. Existing projects therefore
keep full-page-reload behavior until the owner opts in.
Rationale: rewriting a user's Vite config from sync would surprise people who customized it, and a forced migrate as part of every kit sync would blur ownership. Documenting a one-line (plus import) opt-in is enough for anyone who wants the feature.
How existing games opt in:
- Prefer: add
import { blit386 } from 'blit386/vite'andplugins: [blit386()]tovite.config.js, then restart the dev server (needs blit386 1.4.0+). - Or:
npx blit migrate --write/npx blit upgrade, which can enable the same plugin on a standarddefineConfigwhen the engine range supports it.
Kit docs/ and skills are kit-owned, so npx blit agents sync delivers the hot-reload documentation to existing
games even before they enable the plugin.
Engine range pins (BLIT386_RANGE, kit blit386.engineRange) are release concerns – bump them when publishing a kit /
scaffolder cut that expects a newer engine floor on npm, not as a silent side effect of a docs PR. The 1.3.0 release
ships both at ^1.4.0.
- 2026-07-24:
1.3.0shipped. Hot-reload kit content and starter Vite plugin for engineblit386@1.4.0(section 12);engineRange/BLIT386_RANGEat^1.4.0; existing games opt in viablit upgrade/blit migrate. Also: shared@blit386/kit/adapters, Claudesettings.jsonhooks, lockstep bump hardening. Top banner, section 0 pin note, section 11 publish status updated; removed a stale claim that a rootRELEASE.mdexists (release notes live only in GitHub Releases). Manual publish after merge, kit first. - 2026-07-23: Status accuracy pass (BT-341). Top banner then stated published npm was still
1.2.1while localengineRange/BLIT386_RANGEonmainwere already^1.4.0(unpublished next cut), hot-reload content/section 12 was onmain, skill count is 24, and tests are 47 (25 scaffolder + 22 kit). Deleted GitHub issue links #23–#33 (HTTP 410) replaced with Linear trackers (BT-300/301/302 for section 7 verifies; BT-299/293 for remaining Phase 3 roadmap; BT-292/295–298/294 for Phase 4). Catcher starter catch/miss sounds stay deferred product work. The 2026-07-13 changelog entry below is historical and superseded by later entries / this pass. Superseded for publish status by the 2026-07-241.3.0entry above. - 2026-07-23: Section 12 added – hot reload for scaffolded games (engine 1.4.0+): snippet ↔ engine contract, tiered swap
model, and the explicit "new games only" delivery decision (existing games opt in via a one-line
vite.config.jsedit orblit migrate). Kit docs/skills (hot-reload.md,use-hot-reload) and the Catcher commentedonHotReloadexample teach the same model. - 2026-07-14:
1.2.1shipped (PR #56 audio content, PR #60 release). The tag-drivenpublish.ymlworkflow failed on the1.2.1tag push withENEEDAUTH– theNPM_TOKENrepository secret was missing. Rather than re-provision it, the decision is publishing is manual-only from now on:.github/workflows/publish.ymlis deleted,PUBLISHING.mdand thecbt-releaseskill are rewritten so manualpnpm publishis the only documented path (not a fallback), and1.2.1itself was published that way. Tags are still cut and pushed after a manual publish as a release marker; they no longer trigger anything. See the top status block and section 11 for details. - 2026-07-14: Correction to the 2026-07-13 entry below and to section 0. That entry's "no version-pin bump needed –
BLIT386_RANGE/engineRangestay^1.2.0" call was wrong forengineRange: it conflated the scaffold-pin mechanism (BLIT386_RANGE, harmless to leave sincenpm installalways resolves to the latest satisfying version) with the kit's ownblit386.engineRangefield, whichblit doctor's D14 compatibility check (packages/kit/src/commands/doctor.ts,env.ts) compares against an already-installed engine. Leaving it at^1.2.0meantblit doctorwould report a false "compatible" for a project onblit386@1.2.0after syncing in the new 1.3.0-only audio docs. Fixed for the1.2.1release:packages/kit/package.json'sblit386.engineRangeandscaffold.ts'sBLIT386_RANGEboth bumped to^1.3.0. See the corrected "why this is safe" paragraph in section 0. - 2026-07-13: Status audit (no code changes). Verified current state against npm and GitHub rather than trusting the doc
at face value. Findings: (1)
blit386@1.3.0(the audio-bearing release) is live on npm aslatest– confirmed vianpm view blit386 dist-tags. This satisfies the section 0 release-order gate for the first time. (2)@blit386/kitandcreate-blit386are still published at1.2.0; the audio content described in section 0 (content/docs/audio.md,content/skills/play-a-sound/, and edits tocontent/AGENTS.md,content/rules/blit-api-names.md,content/skills/show-debug-overlay/SKILL.md,content/skills/share-the-game/SKILL.md,content/skills/structure-a-game/SKILL.md,content/docs/getting-started.md,content/docs/when-something-breaks.md) is committed on the open, unmergedchore/kit-updatebranch (PR #56) – none of it is onmainyet. (3) Tracking issue #50 is open with its four checklist items unchecked. (4) Phase 3 items #26 (generatedeprecations.mdfrom migration data) and #27 (auto-stampengineRange+ drift CI) remain open, as does all of Phase 4 (#23–25, #28–33 – StackBlitz, iPad, Windows, and the Zed/Gemini CLI/Windsurf adapters). (5) #37 (non-interactive scaffolding support) is already implemented and tested in code – theisInteractiveTTY check plus--yes/--ts/--no-install/--no-gitflags insrc/index.ts, covered by the "scaffolds without --yes when no interactive terminal is attached" test inscaffold.test.mjs– the GitHub issue just hasn't been closed yet. Updated section 0's status line and the top status header to record this; otherwise this doc was already current with the/kit→@blit386/kitnaming fixes from a prior session. Next actions for whoever picks this up: merge PR #56, then publish the kit before the scaffolder (section 11); no version-pin bump needed –BLIT386_RANGE/engineRangestay^1.2.0(the existing caret range already admits1.3.0). - 2026-06-07: Created. Round 1 decisions D1-D4 locked. Findings F1-F4 recorded. Architecture drafted. Open questions Q-NAME, Q-GAME, Q-KIT, Q-MVP, Q-REPO queued.
- 2026-06-07: Round 2. Decisions D5 (Catcher), D6 (kit as own package), D7 (thinnest MVP on adapter pipeline). Finding F5 (npm scope without a team). Added the intent-vs-capability worked example to 4.3. Resolved Q-NAME, Q-GAME, Q-KIT, Q-MVP. New open questions: Q-KIT-NAME, Q-GROUND-TRUTH-FORMAT, Q-FILIPEK-AGENT (Q-REPO still open).
- 2026-06-07: Round 3. D6 name resolved (
@blit386/kitvia free org), D7 refined (Filipek = no agent), D8 (JS in v0.1, TS phase 2), D9 (hybrid repo). Added section 9 phase roadmap. Resolved Q-KIT-NAME, Q-REPO, Q-FILIPEK-AGENT. Only Q-GROUND-TRUTH-FORMAT remains open. - 2026-06-07: Round 4. Q-GROUND-TRUTH-FORMAT resolved (kit IR confirmed). Recorded Filipek's environment (no Node.js, Zed editor). Added section 10 (npm org setup steps). New open question Q-PKGMGR (auto-detect package manager, npm-default for end users). Engine requires Node >= 22.18.0.
- 2026-06-07: Round 5. Q-PKGMGR resolved -> D10 (auto-detect, npm default for end users). npm org
blit386created (npmjs.com/org/blit386). All design questions resolved; moving to build planning for v0.1. - 2026-06-07: Round 6. v0.1 BUILT and verified end-to-end at
create-blit386/(two strict-TS packages, the Catcher game, theblitCLI, the kit's AGENTS.md + docs). The generated game was confirmed rendering on WebGPU via Vite against publishedblit3861.1.1, with working score/lives logic and a clean console. Not published; not committed by Claude. Catcher usesBT.systemPrint(no font asset). Remaining: the manual npm publish step. - 2026-06-09: Round 7. Pre-publish polish: added ISC
LICENSE(root + both packages), aREADME.mdper package,prepackbuild scripts, and a hardened.gitignore(re-verified no build/dep artifacts are staged). Documented the npm publish procedure (section 11). Began publishing:@blit386/kit@0.1.0is up (read-API propagating);create-blit386to follow. Repo atcreate-blit386/is git-init'd; initial commit message drafted via/commit-msg(not yet committed by Claude). - 2026-06-09: Round 8. Repo committed and pushed to GitHub: https://github.com/blit386/create-blit386 (public).
- 2026-06-12: Round 9. Design review (all-audiences pass: beginners, experts, no-AI, AI, agents). New decisions D11
(pre-Node onboarding lives outside the project; StackBlitz leads once verified), D12 (
npx blit ...everywhere; README leads withnpm run dev), D13 (sync ownership model – full spec in new section 4.10), D14 (kit declares a supported engine range; doctor checks). Section 4.8 rewritten to match the built wizard + non-TTY and Node-gate rules; 4.9 fixed (AGENTS.md is always emitted) and updated. Section 7 gained Windows,npx blit, and touch-input verification TODOs. Section 9 gained the Phase 1.x backlog. Docs shipped same day:when-something-breaks.md(new kit doc),npx blitsweep across template README / AGENTS.md / getting-started / CLAUDE.md template / package README, overlay-key explanation (position-based Backquote, Quake heritage, tap-the-corner fallback), "Share your game" README section, repo-README Node onboarding + send-to-a-friend snippet, and the AGENTS.md managed markers + "Your notes" section. Code items (non-TTY guard, Node gate, Catcher touch input, manifest seeding, engine-range check) recorded as Phase 1.x TODOs, not yet implemented. - 2026-06-13: Round 10. Verified actual state against npm + the repo: both packages are published at
0.1.0(the Phase 1 "REMAINING: publish" and section 11 "confirm/pending" lines were stale – corrected). Implemented the first two Phase 1.x code items increate-blit386: the Node-version gate (friendly Tier-1 message + exit before any prompt when below22.18.0) and the non-TTY guard (no TTY -> act as--yesand print one info line). Both back pure helpers in a newpackages/create-blit386/src/env.ts(meetsNodeFloor,isInteractive,NODE_FLOOR), emitted as its own dist entry viatsup.config.tsso tests import it without running the CLI. Added unit tests formeetsNodeFloorand an end-to-end non-TTY test (timed, fails if the wizard hangs); addedEBADENGINEtocspell.json. Full preflight green (format, lint, typecheck, spellcheck, knip, docs:links, build, 7/7 tests). Merged tomainas PR #7 (CodeRabbit-approved) and unpublished.package.jsonbumped to a provisional0.1.1, but the release number is deferred until the Phase 1.x batch is done (could be0.1.1/0.2.0/1.0.0). Remaining Phase 1.x code: Catcher touch input,.blit/manifest seeding, engine-range check (D14). - 2026-06-13: Round 11. Implemented the remaining three Phase 1.x code items (see Round 10 for the first two). Catcher
touch/pointer input (PR #8):
update()intemplates/js/src/game.jsnow checksBT.isPointerActive(0)first; when true, centers the paddle onBT.pointerPos(0).x; arrows are the fallback. File-level header and templateREADME.md"Run it" section updated to document pointer-first behavior.biome.jsonextended to covertemplates/**/*.js;cspelllint-staged invocation gained--no-must-find-files(templates are in cspell'signorePaths; without the flag, pre-commit hooks failed with "0 files checked")..blit/manifest seeding (PR #10):scaffold()collects every written file path, classifies askit-owned/shared/user-owned, computes SHA-256, writes.blit/manifest.json+.blit/base/pristine copies. Test strengthened to compute a fresh sha256 from the actual file anddeepStrictEqualthe base copy bytes. D14 engine-range check (PR #9):"blit386": { "engineRange": "^1.1.1" }inpackages/kit/package.json; new helperssatisfiesCaretRange,exceedsCaretRange,kitEngineRange;blit doctorreports compatibility in Tier-1 voice.kitEngineRangeURL path bug fixed (was../../package.jsonresolving to non-existentpackages/package.json; corrected to../package.json→packages/kit/package.json). All four PRs merged tomain. Release version not bumped yet. - 2026-06-13: Round 12. Phase 2 Items 1–3 implemented. Item 1 (TypeScript language layer, PR #11): added
templates/ts/(thin layer withpackage.json.tmpl,tsconfig.json,src/game.ts); wiredlanguage: 'js' | 'ts'throughScaffoldOptions, wizard, and--tsCLI flag;index.htmlandREADME.mdinbase/use{{entryFile}}/{{gameFile}}placeholders. Item 2 (Claude adapter, generated, branchagents-claude): replaced statictemplates/optional/claude/CLAUDE.md.tmplwithgenerateClaudeAdapter()that reads kit IR (content/AGENTS.md,content/rules/*.md,content/skills/*/SKILL.md) and emitsCLAUDE.md(managed region),.claude/rules/,.claude/skills/. Added kit IR source files:content/rules/blit-api-names.md,content/rules/blit-integer-coords.md,content/skills/run/SKILL.md,content/skills/fix/SKILL.md,content/agents.config.json. Item 3 (Cursor adapter, generated, branchagents-cursor): deletedtemplates/optional/cursor/and addedgenerateCursorAdapter()that emits.cursor/rules/*.mdc(frontmatter preserved),.cursor/hooks.json(translated fromcontent/hooks.manifest.json),.cursor/hooks/shell-safety.sh, and.cursor/commands/*.md(one per skill). Addedcontent/hooks.manifest.jsonandcontent/hooks/shell-safety.sh. Both branches merged together; preflight green (9/9 tests). Item 4 (blit agents sync --check) is next. - 2026-06-13: Round 13. Phase 2 Item 4:
blit agents sync --checkimplemented. Replaced the stub inpackages/kit/src/commands/agents.tswith a real drift-detection algorithm: reads.blit/manifest.json, computes SHA-256 of each kit-owned and shared file on disk, compares against the manifest-recorded hash, and reports any that have drifted (user-modified or deleted). Exits non-zero on drift (CI-safe). Full sync (rewriting kit-owned files and managed regions) remains a stub pointing to--check. Integrated drift check intoblit doctor(appended at end of checkup, no exit-code change for doctor). Updatedcli.tshelp text. Added two end-to-end tests (exit 0 on clean project; exit 1 with drifted.claude/rules/blit-api-names.md). Fixed aSyntaxErrorcaused byawait import()in a sync test callback – movedwriteFileSyncto the top-level import. Preflight green (11/11 tests). Phase 2 complete. - 2026-06-13: Round 14. CodeRabbit review fixes; all Phase 2 PRs merged to
main. PR #13 (Cursor adapter,agents-cursor): added python3 availability pre-check toshell-safety.sh(exits non-zero with a deny response if python3 is absent – true fail-closed); addedlength > 0guards before[0]array accesses in the hooks.json test. PR #14 (blit agents sync --check,agents-sync): addedArray.isArray(manifest.files)shape validation after JSON.parse; added path-traversal guard to the entry loop (reject absolute paths and..escapes, usepath.resolve+ root-containment check). All PRs reviewed by CodeRabbit and merged.mainis now atafea44c. Release version still not bumped – next step is1.0.0. - 2026-06-13: Round 15. Release prep + Phase 2 finish. (1) 1.0.0 version bump: both
packages/kit/package.jsonandpackages/create-blit386/package.jsonset to1.0.0;pnpm installsynced the lockfile; full preflight green. Commit,v1.0.0tag, and npm publish are intentionally not done – held for an explicit go (kit first, per PUBLISHING.md). (2) Dogfood (finding, no file changes): ran the scaffolder'sgenerateClaudeAdapter/generateCursorAdapteragainst scratch projects and compared to the engine repos' real.cursor//.claude/. Conclusion: do not replace. The kit IR targets game authors (2 rules,run/fixskills, 5 beginner docs, one shell-safety hook);blit386/blit386-demoscarry maintainer config (6+ repo-specific rules, 12bt-*/ 9demos-*skills, RTK/format hooks) and are the kit's upstream, not consumers. Overwriting would delete maintenance tooling; a future "maintainer profile" IR is the prerequisite (Phase 3+). The sibling repos were left untouched. (3) Fullblit agents syncwrite path: ported the adapter generation into a new generate-to-memory modulepackages/kit/src/adapters.ts(byte-identical to the scaffolder's adapters), then implementedrunFullSyncinpackages/kit/src/commands/agents.ts: regenerate the installed kit's output in memory, then per the 4.10 ownership model – overwrite unmodified kit-owned files, managed-region merge for shared files,git merge-filethree-way merge for user-edited kit-owned files with a<file>.newfallback when git is unavailable or conflicts, refresh.blit/manifest.json+.blit/base/, and support--force [path...]. Tier-1 summary output. The scaffolder now records the scaffold-time template vars in the manifest (BlitManifest.vars) so sync regenerates deterministically regardless of the environment. Four new tests (clean Claude + clean Cursor parity → zero changes,--forcerestore, CLAUDE.md managed-region merge preserving a user note); 15/15 pass, preflight green. (4) Docs sweep: updated all docs for the new sync capability and to clear leftover Phase 2 staleness –packages/kit/README.md(blit agents syncis now real, with--check/--force;addstill pending),packages/create-blit386/README.md(addednpx blit agents sync+--ts+ agent-config note), repoREADME.md(Status now reflects Phase 2 complete; CLI list gainsagents sync), repoCLAUDE.md(scaffold flow incl. adapter generation + manifest step, template layoutts/and removedoptional/cursor|claude, JS-by-default rule, kit-content table points atcontent/rules/*.md, new where-to-find rows for sync/adapters/manifest), and the workspaceCLAUDE.mdcreate-blit386 status. docs:links + spellcheck green. Nothing committed. - 2026-06-13: Round 16. Documentation accuracy audit (full sweep of all 27 markdown/mdc docs + JSON config against the
real code). Fixes: (a)
content/docs/basics.mdconfigure()example importedbootstrap, BTbut usedVector2i– added the missing import. (b)content/agents.config.jsoncursoremitsomitted the generated.cursor/hooks/{script}(shell-safety hook) – added it. (c) repoCLAUDE.mdsaid "five beginner docs" (there are six: getting-started, basics, drawing, input, palette, when-something-breaks) – corrected the count and added the two missing rows to the kit-content audit table. (d) Repo agent rules were stale:.cursor/rules/template-structure.mdc.claudemirror still listedoptional/{cursor,claude}(deleted – those configs are now generated from the kit IR), and the.claudemirror lacked thets/layer – synced both to reality. (e).cursor/rules/docs-sync-required.mdc- mirror pointed at the deleted
templates/optional/cursor/.../blit386-api-names.mdc– repointed atpackages/kit/content/rules/*.mdand added a row for CLI/adapter/agents syncchanges. (f)claude-canonical.mdc"plain JavaScript" line updated to note the opt-in TypeScript layer (and fixed a wrongblit init --tsreference – TS is a scaffolder flag,npm create blit386 ... --ts). (g)create-blit386/src/index.tsheader flag list andpackages/create-blit386/README.md"What you get" now mention--ts/src/game.ts. Full preflight green (15/15 tests). Nothing committed.
- 2026-06-14: Round 23. Game-author skills (Phase 3 "more game-author skills"). Added 14 capability recipes under
packages/kit/content/skills/(the set has since grown to 19 – see Phase 3), grounded in an authoritative audit ofBTAPI.ts+BLIT386.tsand the demo suite (33 demos then; 39 today):structure-a-game,draw-shapes,add-sprite,add-text,use-palette,animate-the-palette,move-and-time,scroll-with-camera,read-keyboard,read-pointer,read-gamepad,add-crt-effect,save-a-screenshot,show-debug-overlay. Together they cover every capability area the engine exposes today and deliberately fold in APIs no demo exercises (paletteFadeRange,paletteClearEffects,BT.preset.amber/green,PixelMosaic,captureFrame,showCursor,effectClear,ticksReset). Each is a short recipe (when to use + minimal code + key calls + gotchas) that points only at the kit's own local docs, never an outside repo. They emit into both.claude/skills/<name>/SKILL.mdand.cursor/commands/<name>.mdvia the existing skill auto-discovery – no generator change. Hard boundaries respected: renderer-only (no physics/collision/entity systems taught), post-process gated onBT.activeBackend === 'webgpu', display-tier effects needdrawingBufferSize, sprite flip/rotate constants documented as not-yet-wired. Also fixed a doc bug incontent/docs/palette.md:Color32.whiteand friends are static getters, not calls (the doc showedColor32.white()). Decisions: noadd-enemy/physics skill (out of engine scope); nopublish/deploy skill (the scaffolder ships no deploy config); kit content is self-contained – skills and docs reference onlyblit386(the engine) and the local kit docs, never theblit386-demosrepo (which may be archived in favor of kit-based demos), so no demo slugs or demo URLs appear in shipped content. - 2026-06-14: Round 22. AI migration skill (closes the Round 21 follow-up). Added
packages/kit/content/skills/migrate/SKILL.md, which the existing skill auto-discovery inscaffold.tsandadapters.tsemits into generated games as a Claude skill (.claude/skills/migrate/SKILL.md) and a Cursor command (.cursor/commands/migrate.md) – no generator code change needed. The skill tells the assistant to preview withblit migrate, apply the safe renames with--write, then resolve eachreviewhit by checking the receiver type (.equals->.isEqual,.contains->.isContaining,.intersects->.isIntersecting,.tick->.fireIfElapsed, and the bootstrap keyscanvasId/containerId/waitForDOMReady), leaving non-engine values alone. It points at the enginedeprecations.mdas the authoritative table and routes verification toblit run/blit doctorand thefixskill. Docs: design doc 4.6 status + Phase 3 line updated. Still open in Phase 3: generatedeprecations.mdfrom this data (cross-repo). - 2026-06-14: Round 21. Started Phase 3 migrations/codemods (section 4.6), the first post-1.0 feature. Built kit-side
and self-contained: a typed migration registry (
packages/kit/src/migrations/registry.ts, seeded by hand from the engine'sdocs/deprecations.md) plus a dependency-free, anchored codemod engine (codemod.ts). Renames are classifiedauto(receiver-anchoredBT.*calls, distinctiveconfigure()keys, distinctive method names likeisIndexized/containsXY/intersectionTo) orreview(generic names that could match unrelated code: common method wordsequals/contains/intersects/tickand generic bootstrap keyscanvasId/containerId/waitForDOMReady– located and reported with a suggested rewrite, never auto-changed). Newblit migratecommand (commands/migrate.ts): previews by default, writes only with--write, and runs the no-git nag + confirm before touching files.blit upgradenow runs the applicable codemods after a real version change and offers to apply them (replacing the bare deprecations link). Wiring: registeredmigrateincli.ts+ help; factored the sharedconfirm()intoprompt.ts; exportedcompareVersionsfromenv.ts; added a second tsup entry so the engine/registry are importable by tests. Tests: 13 codemod unit tests (packages/kit/test/codemod.test.mjs) + two scaffold integration tests (blit migratepreview-no-write, and--writeon a git project that rewrites safe names and reports ambiguous ones). Decisions: (1) migration data lives in the kit, not the engine, so this ships without an engine release – the long-term "flipdeprecations.mdto be generated from this data" is cross-repo and deferred (mirrored by hand for now); (2) nots-morph/jscodeshiftdependency – anchored strings cover the current one-to-one table; (3) the AI migration skill for non-mechanical changes is not built yet (reviewhits are surfaced for a human/assistant). Docs updated: kitREADME.md, kitcontent/AGENTS.md, repoCLAUDE.md(info table). Preflight not yet run this round (shell environment was unavailable); nothing committed. - 2026-06-14: Round 20. Cut and shipped
1.0.0(the first stable release). Bumped@blit386/kitandcreate-blit386to1.0.0(major) and did a packaging-metadata pass on both: addedrepository(with monorepodirectory),bugs,homepage; switchedauthorto object form with a profile URL; broadened npm keywords; aligned the JS templatevitefloor with the TS template (^8.0.16). Also refreshed GitHub repo topics for all three repos (create-blit386,blit386,blit386-demos) and pointedblit386-demos'shomepage/website at the live demos site. Process:mainis protected, so the release landed via PR #19 (squash,0eac7aa); the annotated tag1.0.0(novprefix) was created on the merged commit, not the pre-merge branch (squash rewrites the SHA). Verified: preflight green (22 tests), both publish dry-runs, packed tarball pins@blit386/kit: 1.0.0. After the owner published (kit first, then scaffolder), confirmednpm viewshows both at1.0.0(latest) and an end-to-endnpm create blit386@latestsmoke test passed (blit doctorgreen, engine1.1.1compatible with kit^1.1.1). Release notes live in the GitHub Release (https://github.com/blit386/create-blit386/releases/tag/1.0.0). Saved a workspace memory thatmainis always protected. The earlier "1.0.0 deferred / reverted bump /v1.0.0tag" notes are now superseded. - 2026-06-14: Round 19. Resolved the kit-owned clean-merge drift wrinkle (flagged since Round 17). Decision: a clean
three-way-merged kit file is reconciled state, not drift – it should read as in-sync, exactly like a shared file with
a preserved note. Reporting it as drift was a false positive that made
doctornag forever and re-runningsyncchange nothing (the "nobody trusts sync" failure the ownership model fights). Root cause:entry.sha256was overloaded. After a kit-owned clean merge it was set to the pristine kit hash (so the full-sync fast pathdiskHash === entry.sha256would not mistake a merged file for an untouched one and clobber it), butcheckSyncDriftreads the same field as the on-disk reference, so it saw the merged file (pristine ≠ merged) and flagged drift. Shared files avoided this because Round 17 routes them through the managed-region merge before any fast path, lettingentry.sha256hold the reconciled (merged) hash. Fix (packages/kit/src/commands/agents.ts,runFullSync): give the fast path its own correct reference – the pristine ancestor already saved in.blit/base/<path>. The kit-owned "unmodified" check now comparesdiskHashagainst the base-copy hash (existsSync(basePath) ? sha256(basePath) : entry.sha256for older projects without a base copy), and the clean-merge branch now recordsentry.sha256 = sha256Text(merged)(reconciled on-disk content) instead of the pristine hash. The base copy stays the pristine merge ancestor, so the next sync still detects the user's edits and re-merges them – user edits remain protected. Updated theManifestEntry.sha256doc and section 4.10 manifest description. New test (blit agents sync does not flag a clean-merged kit file as drift, git-guarded): a user edit to a kit rule merges cleanly,--checkreports in-sync (the fix), and the edit survives a second sync. 22/22 pass, full preflight green. Nothing committed. - 2026-06-14: Round 18. Implemented
blit agents add <claude|cursor>(section 4.5), the post-scaffold command to set up one AI assistant in a project that started without one – replacing the friendly "coming soon" stub. NewrunAddAgentinpackages/kit/src/commands/agents.tsreuses the existing ownership-model building blocks: it reads.blit/manifest.json(via a new sharedreadManifesthelper), regenerates the chosen assistant's adapter output in memory from the installed kit (generateClaudeAdapter/generateCursorAdapterinadapters.ts, using the manifest's recordedvarsorfallbackVars), writes the new files, copies pristine.blit/base/ancestors, and adds the entries to the manifest sosync --checkstays clean. Safety: an assistant that is already set up is a friendly no-op pointing atsync; an unknown name exits non-zero listing the supported assistants (claude,cursor). Collision handling is all-or-nothing (review fix, same round): the first draft wrote the non-colliding files and added them to the manifest while saving only the colliding file as<file>.new. That left the assistant half-present, so a latersyncwould regenerate the colliding path (now thathasClaude/hasCursorwas true), find no manifest entry, and overwrite the very user fileaddhad protected. Fixed by detecting all collisions up front: if any exist, write only the<file>.newcopies, touch neither the project nor the manifest, and exit non-zero (the user resolves the originals, then re-runsadd). Wired intorunAgents(add <name>dispatch + updated usage block) and the module JSDoc. Five end-to-end tests (add claude, add cursor + clean follow-up sync, already-present no-op, unknown-name rejection, and the collision test – strengthened to assert no half-add and that a latersyncleaves the user file intact); 21/21 pass, full preflight green. Docs updated: kitREADME.md(realagents adddescription + usage example),packages/create-blit386/README.mdand repoREADME.md(CLI lists + "add later" pointer), repoCLAUDE.md(new where-to-find row), and the kit's canonicalcontent/AGENTS.md(managed region now points projects that started without an assistant atagents add). Also corrected the stale status-header note: the9c37894docs-sync-path commit is already inmain(PR #16 merge), so there is no pendingagent-docsbranch. Release still deferred; nothing committed. - 2026-06-14: Round 17. Review-driven hardening of
blit agents sync, everything landed onmain, and1.0.0deferred. (1) Sync bug fixes (3 review rounds, all merged): (a) the kit-owned clean three-way-merge path recorded the merged result as the baseline/hash, so the next sync misread an untouched merged file as "unmodified" and overwrote the user's edits – now records the kit-generatedincomingas the base +entry.sha256; (b) orphaned manifest entries (files the new kit no longer ships) were tallied but never removed fromentryByPath, so stale entries got rewritten – nowentryByPath.delete(path)for each orphan; (c)regeneratefell back tofallbackVars(root)but never persisted it, re-detecting the package manager every sync – now writes the resolved vars back ontomanifest.varsthe first time. (2) Shared-file note loss (the subtle one): shared files (AGENTS.md,CLAUDE.md) recordedentry.sha256 = sha256Text(merged); on a second sync the untouched file matched that hash, hit the "unmodified" fast path, and was overwritten wholesale, dropping the user's notes outside the managed region. Fix: handle shared files before the kit-owned fast path so they always route throughreplaceManagedRegion(preserving surrounding content), while keepingentry.sha256 = sha256Text(merged)sosync --checkstill treats a preserved note as in-sync. Noteentry.sha256has two consumers –checkSyncDrift(wants reconciled on-disk hash) and the full-sync fast path (wants the pristine kit baseline) – which is why the fix is "route by class first," not "change the hash." Added a two-sync regression test; the single-sync test could not catch it. 16/16 tests pass. (3) Docs path consistency:.cursor/rules/docs-sync-required.mdc+.claudemirror now use fully qualifiedpackages/kit/content/...paths (committed onagent-docsas9c37894, not yet merged – open a PR or fast-forwardmainnext session). (4) Merges: the write path + fixes + docs sweep are onmain(PR #15, then PR #16 for docs;mainHEADb7435fa). (5) Release deferred: reverted the uncommitted1.0.0bump back to0.1.0/0.1.1; no tag cut. The owner is adding a couple more features before1.0.0. Nothing about the release is committed or tagged. - 2026-07-13: Round 24. Three engine-surface skills added to
content/skills/, closing gaps opened by the 1.3.0 engine release rather than by a new product decision. (1)smooth-the-motion–BT.renderAlphaappeared exactly once in the entire kit (a bare name in a getter list inrules/blit-api-names.md) and was explained nowhere, so no scaffolded game interpolated and all three_3RD_games judder against the fixed tick on a high-refresh display; the skill teaches the snapshot-before-move +Vector2i.lerprecipe, anddocs/basics.mdgains the matching section. (2)design-a-sound–play-a-soundstopped at the sixBT.synthPresetfactories, leaving the wholeSynthParamsknob surface undocumented;_3RD_/killer-mathhand-rolled a raw Web Audiobeep()rather than findingBT.synthPreset.blip(), which is a discoverability failure the kit caused. (3)keep-it-fast– the kit had no performance guidance at all; notably, blowing the per-frame budget (about 8,300 sprites, and separately about 8,300 shapes, everything being drawn as quads) silently drops the extra draws with only a console warning, which reads to a beginner as "sprites randomly vanish." Also fixedadd-crt-effectto warn against gating onBT.requestedBackend, which stays'webgpu'after a software fallback and so passes on exactly the machines the guard exists to protect. The renderer-only boundary from Round 23 was reaffirmed, not relaxed: collision, game states, juice, and save/load were considered and deliberately left out, since the engine has no game systems to document. Hygiene:cbt-kit-audithardcoded a 12-skill list that had already gone stale (missing four skills andaudio.md), so its doc and skill steps now glob the directories instead of naming files;packages/kit/README.mdgains the first human-facing table of the shipped skills, andcbt-kit-auditis responsible for keeping it complete. - 2026-07-23: Round 25 (BT-350). Shared agent adapters: deleted the duplicated Claude/Cursor generators from
packages/create-blit386/src/scaffold.ts.packages/kit/src/adapters.tsis now the single source of truth, exported as@blit386/kit/adapters(tsup entry + packageexports). Scaffold importsgenerateClaudeAdapter/generateCursorAdapterand writes the{ path, content }pairs to disk;blit agents sync/addkeep using the same module in memory. Added a scaffold-vs-memory parity test; ownership model / manifest /.blit/base/unchanged. - 2026-07-24: Round 26 (BT-254). Claude adapter hook parity with Cursor:
hooks.manifest.jsongainsclaude:blocks;generateClaudeAdapteremits.claude/settings.json(PreToolUse / PostToolUse) and copiescontent/hooks/into.claude/hooks/.shell-safety.shspeaks both Cursor permission JSON and Claude exit-2 / permissionDecision protocols. Ownership classifiers treat.claude/hooks/as kit-owned; scaffold twin tests cover settings.json.