A games-first browser playground for the LLM-OS kernel. A 350M-parameter LLM plays Tetris and solves grid-world quests by emitting grammar-constrained ISA opcodes. Strategy markdown cartridges customize how the LLM plays.
Companion to llm_os — same kernel, richer UX, ships in a browser tab.
cd mobile
npm install
npm run devOpen http://localhost:5173. The launcher lists the games and available strategies. Click a strategy card and the page navigates to the demo with that strategy injected into the system prompt. Browser back returns to the launcher.
skillos_mini Svelte launcher (one component, GamesLauncher.svelte)
│
│ list strategies/<game>/*.md, click → /demos/<game>/index.html?strategy=<id>
▼
Bundled demo (vendored from llm_os main)
│ fetches /strategies/<game>/<id>.md, prepends body to SYSTEM_PROMPT
▼
llm_os kernel (token-trie + Sampler + dispatch) — vendored at /demos/_kernel/
│
├─→ game/tetris cartridge
│ Program-layer enumerates 4 rotations × 10 columns of placements,
│ simulates each, scores Dellacherie-style, surfaces best_actions[].
│ LLM ratifies best_actions[0] and emits its sequence.
│
└─→ game/scavenger cartridge
Program-layer runs BFS around walls/pits, surfaces next_step.dir.
LLM ratifies a single direction.
The grammar enforces output validity at the sampler level — strategies can only steer which valid opcode the LLM picks, never produce invalid ones.
Per CLAUDE.md §3: no trade verticals, no backend, no cloud LLMs, no Capacitor / Android / PWA, no CartridgeRunner / blackboard / validators / schema-driven UI, no agentic Recipe loops, no iframe Skill sandbox, no model-management UI. The trade-app vertical (electricista/plomero/pintor) was archived in the games-first pivot on 2026-05-05; recoverable from git history at commit 709104b if needed.
- Launcher fetches
/strategies/index.json, lists each strategy as a card under its game. - User clicks a card. Browser navigates to
/demos/<game>/index.html?strategy=<id>. - The demo's
btnLoad.onclick:- Reads
?strategy=<id>from URL. - Fetches
/strategies/<game>/<id>.md. - Strips frontmatter, gets the prose body.
- Sets
EFFECTIVE_SYSTEM_PROMPT = SYSTEM_PROMPT + body. - Loads the LFM 2.5 350M model and the kernel cartridge.
- Reads
- The model plays under the strategy's prose guidance.
skillos_mini/
├── CLAUDE.md # mission + scope (read first)
├── README.md # this file
├── TESTING.md # how to run end-to-end
├── strategies/ # markdown strategy cartridges
│ ├── index.json # launcher's catalog
│ ├── tetris/{conservative,aggressive,well-filler}.md
│ └── scavenger/{cautious,direct,explorer}.md
└── mobile/
├── package.json # vite + svelte + wllama + gray-matter
├── vite.config.ts # COOP/COEP plugin
├── public/
│ ├── demos/ # bundled llm_os games
│ │ ├── index.html # raw-demos landing
│ │ ├── tetris/index.html
│ │ ├── scavenger/index.html
│ │ ├── _kernel/ # vendored kernel snapshot
│ │ └── _cart/game/ # game cartridge manifests
│ └── strategies/ # built artifact (copy-strategies.mjs)
├── scripts/
│ ├── copy-wllama.mjs
│ └── copy-strategies.mjs # vendors strategies/ → public/
└── src/
├── App.svelte # GamesLauncher root
├── main.ts
└── components/
└── GamesLauncher.svelte # the only Svelte component
Drop a new .md file under strategies/<game>/ with frontmatter:
---
target: tetris
id: my-strategy
name: My Strategy
description: One-line summary for the launcher card.
---
# My Strategy
...prose strategy guidance the LLM reads...Add a single-line entry to strategies/index.json and refresh the launcher. No code changes needed. The grammar enforcement layer guarantees output validity regardless of what the strategy says.
Apache 2.0.