@terminalika/pi-play
terminalika inside pi: retro games in an overlay that pause themselves the moment the agent finishes or needs you.
Package details
Install @terminalika/pi-play from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@terminalika/pi-play- Package
@terminalika/pi-play- Version
0.1.1- Published
- Sep 10, 2026
- Downloads
- not available
- Author
- recepahmetkara
- License
- MIT
- Types
- extension
- Size
- 5.5 MB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./extensions"
],
"video": "https://terminalika.dev/demo-pi.mp4",
"image": "https://raw.githubusercontent.com/terminalika/pi-play/main/.github/demo.gif"
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-play

terminalika as a pi package: the retro games from terminalika-core, running in an overlay inside pi, that pause themselves the moment pi finishes and is waiting for you. No standalone binary, no second pane.
pi install npm:@terminalika/pi-play
Then, while pi works:
/play pick a game
/play snake or name one: snake, tetris, 2048, mines
/play tetris while snake is on: swap it (asks first, until you say don't)
/play stop end the game
Arrows/WASD move, SPACE pauses, R resets. When pi settles (its turn is
over, it is waiting on you) what happens to a running game is asked once,
on the first /play, and remembered in the terminalika config dir
(pi.json):
- Pause the game — freeze it with a notice in pi's colour; resume with
SPACE(default), - Settle the game — the game settles too: parked, hidden, the keyboard
back with pi, and a toast pointing at
/play, - Don't pause — keep playing; pi settling changes nothing.
Skipping the question (Esc) keeps the default for that run and offers it again next time. The file can be edited by hand:
{ "auto_pause": "pause" }
Going back to pi never ends the game: ESC parks it - paused, hidden,
the editor has the keyboard again and the status line remembers it - and
/play brings it back exactly where it was. The only thing that ends a game
is /play stop.
Changing games: /play <other> from the editor, or alt+g from either
side - it opens the picker with the game that is on listed first (so Enter
returns to it); in the game it parks first, and cancelling brings the same
game back. Since swapping closes the game that is on, it asks
Yes / No / Yes, and don't ask again; the last one is remembered in
pi.json as "confirm_switch": false.
On a Mac every alt+… above reads option+… - in the hints, the toasts and
the status line. The key is the same one.
Try it without installing: pi -e /path/to/pi-play.
How it works
This is a thin pi launcher for terminalika-core, the same way the CLI and the website are thin launchers: the games are written once, in Go.
terminalika-core (Go) ──GOOS=js──▶ wasm/terminalika.wasm
│ tcell's web-screen protocol:
│ Go calls resize/clearScreen/drawCell/show,
│ registers onKeyEvent, exposes tkPause/tkResume/tkQuit
▼
src/worker.ts the game's thread: screen.ts keeps the cell
(screen.ts, runtime.ts) grid, runtime.ts runs the wasm; every frame is
posted to the main thread as ANSI lines
│
▼
src/host.ts main thread: keeps the latest frame for a pi
overlay component (ctx.ui.custom({ overlay:
true })), relays keys (src/keys.ts)
│
▼
extensions/terminalika.ts /play, and pi.on("agent_settled") → tkPause.
- Overlay, not a pane. The game floats over the conversation; pi's own
DOOM example is proof the overlay can carry real-time rendering. It takes
exactly as many rows as the game says it needs (
core.Sized: board, status line above, hint lines below), each game reporting its own number, and the full width of the terminal: pi splices an overlay into each row after the text to its left, measured with its own width tables, and a terminal that draws an emoji one cell differently would shift that row. - Its own thread. The wasm runs on a worker thread (
src/worker.ts); pi rendering a streaming answer on the main thread cannot starve the game's ticks or its held-key timers. The main thread only keeps the latest frame (src/host.ts). - Events come straight from pi. No session-file tailing:
agent_settledis the moment pi will not continue on its own. Desktop notifications are not this package's job; pi has extensions for that. - Key releases. Under a terminal that speaks the kitty keyboard protocol pi reports releases and held keys feel exact; elsewhere the engine synthesises releases from auto-repeat, like the CLI does.
Not yet: high scores (the wasm has no filesystem).
Layout
extensions/terminalika.ts the pi extension
src/ the host: worker + host (the game's thread and the main thread's
view of it), screen (cell grid → ANSI), runtime (wasm_exec), keys
go/ the Go side: main + engine port (pause notice, held keys)
wasm/ terminalika.wasm + wasm_exec.js, built from go/ (committed)
scripts/build-wasm.sh npm run build:wasm (Go 1.24+; prefers a sibling ../terminalika-core)
scripts/smoke.ts npm run test:wasm (headless: every game renders, pauses, exits)
Requirements
pi 0.84+ (agent_settled, overlays). Node 22+ or Bun for the host; the
package ships the wasm, so no Go toolchain is needed to use it.
