Public: anyone can read this; the crew can change it.
forked from trevato/cs · proposes
The terminal boots headlessly: src/tty.ts takes its streams, and a session is under test #175
Check · Review · Land · Live done — Landed and live in production.
`src/tty.ts` is now a seam: `run(io)` takes an input that can go raw, an output that takes strings, a resize source, an exit, an open database and the environment as one plain object typed by the members it uses — the way `src/client/main.ts` types its host — and `process` and `openDatabase()` appear only in the last lines, under `import.meta.main`, so importing the file touches no real terminal.…
shipped production is serving it deploy ae07c2d
Sign in to join this conversation.
new above
offline
src/tty.tsis now a seam:run(io)takes an input that can go raw, an output that takes strings, a resize source, an exit, an open database and the environment as one plain object typed by the members it uses — the waysrc/client/main.tstypes its host — andprocessandopenDatabase()appear only in the last lines, underimport.meta.main, so importing the file touches no real terminal. Behaviour on the pty is unchanged except in one place: a draw that throws now restores raw mode, clears, printsthe terminal stopped: …and exits 1, instead of leaving a raw shell behind.src/tty.test.tsruns whole sessions headlessly against plain objects and a real SQLite database under a temporary directory: the first paint,tabcycling all three tabs,j/kstopping at both ends,rpicking up a row written behind the session's back,xremoving the match under the cursor with the note that names it,qleaving — and the terminal handed back exactly once onq, on ctrl-c and on a throw, asserted against the recording stream.FORGE_APP_SSHis printed verbatim when set and the session says the terminal is off when it is not, with the test checking no address is invented in its place.The escaping test found a real hole and it is fixed: a name out of the database went into the line of ANSI unescaped, so a player called
\x1b[2Jcleared the screen of anyone looking at the leaderboard.src/terminal/screen.tsgainsprintable()— every control character becomes a middle dot, one character for one so the fixed columns still count — and every string the screen did not write itself (name, timestamp, note, environment pill, ssh command) goes through it;screen()andpress()keep their signatures and all their old tests. What I did not do: no games tab (#84), no identity check on the delete (#40), and no width-aware layout — the columns arescreen.ts's and do not move, so the 20- and 200-column test pins that neither extreme throws and neither changes the layout.bun testpasses (767 tests), and I ranFORGE_APP_DATA=./data bun run src/tty.tsby hand over a pipe: it paints, redraws on a key, andqclears the screen and exits 0.🤖 Generated with Claude Code
Closes #109