@brianli/kimaki 0.4.72-brianli.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/bin.js +2 -0
  2. package/dist/ai-tool-to-genai.js +233 -0
  3. package/dist/ai-tool-to-genai.test.js +267 -0
  4. package/dist/ai-tool.js +6 -0
  5. package/dist/bin.js +87 -0
  6. package/dist/bot-token.js +121 -0
  7. package/dist/bot-token.test.js +134 -0
  8. package/dist/channel-management.js +101 -0
  9. package/dist/cli-parsing.test.js +89 -0
  10. package/dist/cli.js +2529 -0
  11. package/dist/commands/abort.js +82 -0
  12. package/dist/commands/action-buttons.js +257 -0
  13. package/dist/commands/add-project.js +114 -0
  14. package/dist/commands/agent.js +291 -0
  15. package/dist/commands/ask-question.js +223 -0
  16. package/dist/commands/compact.js +120 -0
  17. package/dist/commands/context-usage.js +140 -0
  18. package/dist/commands/create-new-project.js +118 -0
  19. package/dist/commands/diff.js +128 -0
  20. package/dist/commands/file-upload.js +275 -0
  21. package/dist/commands/fork.js +217 -0
  22. package/dist/commands/gemini-apikey.js +70 -0
  23. package/dist/commands/login.js +490 -0
  24. package/dist/commands/mention-mode.js +51 -0
  25. package/dist/commands/merge-worktree.js +124 -0
  26. package/dist/commands/model.js +694 -0
  27. package/dist/commands/permissions.js +163 -0
  28. package/dist/commands/queue.js +217 -0
  29. package/dist/commands/remove-project.js +115 -0
  30. package/dist/commands/restart-opencode-server.js +116 -0
  31. package/dist/commands/resume.js +159 -0
  32. package/dist/commands/run-command.js +79 -0
  33. package/dist/commands/session-id.js +78 -0
  34. package/dist/commands/session.js +192 -0
  35. package/dist/commands/share.js +80 -0
  36. package/dist/commands/types.js +2 -0
  37. package/dist/commands/undo-redo.js +159 -0
  38. package/dist/commands/unset-model.js +152 -0
  39. package/dist/commands/upgrade.js +42 -0
  40. package/dist/commands/user-command.js +148 -0
  41. package/dist/commands/verbosity.js +60 -0
  42. package/dist/commands/worktree-settings.js +50 -0
  43. package/dist/commands/worktree.js +299 -0
  44. package/dist/condense-memory.js +33 -0
  45. package/dist/config.js +110 -0
  46. package/dist/database.js +1050 -0
  47. package/dist/db.js +159 -0
  48. package/dist/db.test.js +49 -0
  49. package/dist/discord-api.js +28 -0
  50. package/dist/discord-auth.js +231 -0
  51. package/dist/discord-auth.test.js +80 -0
  52. package/dist/discord-bot.js +997 -0
  53. package/dist/discord-utils.js +560 -0
  54. package/dist/discord-utils.test.js +115 -0
  55. package/dist/errors.js +167 -0
  56. package/dist/escape-backticks.test.js +429 -0
  57. package/dist/format-tables.js +122 -0
  58. package/dist/format-tables.test.js +199 -0
  59. package/dist/forum-sync/config.js +79 -0
  60. package/dist/forum-sync/discord-operations.js +154 -0
  61. package/dist/forum-sync/index.js +5 -0
  62. package/dist/forum-sync/markdown.js +117 -0
  63. package/dist/forum-sync/sync-to-discord.js +417 -0
  64. package/dist/forum-sync/sync-to-files.js +190 -0
  65. package/dist/forum-sync/types.js +53 -0
  66. package/dist/forum-sync/watchers.js +307 -0
  67. package/dist/gateway-consumer.js +232 -0
  68. package/dist/gateway-consumer.test.js +18 -0
  69. package/dist/genai-worker-wrapper.js +111 -0
  70. package/dist/genai-worker.js +311 -0
  71. package/dist/genai.js +232 -0
  72. package/dist/generated/browser.js +17 -0
  73. package/dist/generated/client.js +35 -0
  74. package/dist/generated/commonInputTypes.js +10 -0
  75. package/dist/generated/enums.js +30 -0
  76. package/dist/generated/internal/class.js +41 -0
  77. package/dist/generated/internal/prismaNamespace.js +239 -0
  78. package/dist/generated/internal/prismaNamespaceBrowser.js +209 -0
  79. package/dist/generated/models/bot_api_keys.js +1 -0
  80. package/dist/generated/models/bot_tokens.js +1 -0
  81. package/dist/generated/models/channel_agents.js +1 -0
  82. package/dist/generated/models/channel_directories.js +1 -0
  83. package/dist/generated/models/channel_mention_mode.js +1 -0
  84. package/dist/generated/models/channel_models.js +1 -0
  85. package/dist/generated/models/channel_verbosity.js +1 -0
  86. package/dist/generated/models/channel_worktrees.js +1 -0
  87. package/dist/generated/models/forum_sync_configs.js +1 -0
  88. package/dist/generated/models/global_models.js +1 -0
  89. package/dist/generated/models/ipc_requests.js +1 -0
  90. package/dist/generated/models/part_messages.js +1 -0
  91. package/dist/generated/models/scheduled_tasks.js +1 -0
  92. package/dist/generated/models/session_agents.js +1 -0
  93. package/dist/generated/models/session_models.js +1 -0
  94. package/dist/generated/models/session_start_sources.js +1 -0
  95. package/dist/generated/models/thread_sessions.js +1 -0
  96. package/dist/generated/models/thread_worktrees.js +1 -0
  97. package/dist/generated/models.js +1 -0
  98. package/dist/heap-monitor.js +95 -0
  99. package/dist/hrana-server.js +416 -0
  100. package/dist/hrana-server.test.js +368 -0
  101. package/dist/image-utils.js +112 -0
  102. package/dist/interaction-handler.js +327 -0
  103. package/dist/ipc-polling.js +251 -0
  104. package/dist/kimaki-digital-twin.e2e.test.js +165 -0
  105. package/dist/limit-heading-depth.js +25 -0
  106. package/dist/limit-heading-depth.test.js +105 -0
  107. package/dist/logger.js +160 -0
  108. package/dist/markdown.js +342 -0
  109. package/dist/markdown.test.js +253 -0
  110. package/dist/message-formatting.js +433 -0
  111. package/dist/message-formatting.test.js +73 -0
  112. package/dist/openai-realtime.js +228 -0
  113. package/dist/opencode-plugin-loading.e2e.test.js +91 -0
  114. package/dist/opencode-plugin.js +536 -0
  115. package/dist/opencode-plugin.test.js +98 -0
  116. package/dist/opencode.js +409 -0
  117. package/dist/privacy-sanitizer.js +105 -0
  118. package/dist/runtime-mode.js +51 -0
  119. package/dist/runtime-mode.test.js +115 -0
  120. package/dist/sentry.js +127 -0
  121. package/dist/session-handler/state.js +151 -0
  122. package/dist/session-handler.js +1874 -0
  123. package/dist/session-search.js +100 -0
  124. package/dist/session-search.test.js +40 -0
  125. package/dist/startup-service.js +153 -0
  126. package/dist/system-message.js +499 -0
  127. package/dist/task-runner.js +282 -0
  128. package/dist/task-schedule.js +191 -0
  129. package/dist/task-schedule.test.js +71 -0
  130. package/dist/thinking-utils.js +35 -0
  131. package/dist/thread-message-queue.e2e.test.js +781 -0
  132. package/dist/tools.js +359 -0
  133. package/dist/unnest-code-blocks.js +136 -0
  134. package/dist/unnest-code-blocks.test.js +641 -0
  135. package/dist/upgrade.js +114 -0
  136. package/dist/utils.js +109 -0
  137. package/dist/voice-handler.js +606 -0
  138. package/dist/voice.js +304 -0
  139. package/dist/voice.test.js +187 -0
  140. package/dist/wait-session.js +94 -0
  141. package/dist/worker-types.js +4 -0
  142. package/dist/worktree-utils.js +727 -0
  143. package/dist/xml.js +92 -0
  144. package/dist/xml.test.js +32 -0
  145. package/package.json +82 -0
  146. package/schema.prisma +246 -0
  147. package/skills/batch/SKILL.md +87 -0
  148. package/skills/critique/SKILL.md +129 -0
  149. package/skills/errore/SKILL.md +589 -0
  150. package/skills/goke/.prettierrc +5 -0
  151. package/skills/goke/CHANGELOG.md +40 -0
  152. package/skills/goke/LICENSE +21 -0
  153. package/skills/goke/README.md +666 -0
  154. package/skills/goke/SKILL.md +458 -0
  155. package/skills/goke/package.json +43 -0
  156. package/skills/goke/src/__test__/coerce.test.ts +411 -0
  157. package/skills/goke/src/__test__/index.test.ts +1798 -0
  158. package/skills/goke/src/__test__/types.test-d.ts +111 -0
  159. package/skills/goke/src/coerce.ts +547 -0
  160. package/skills/goke/src/goke.ts +1362 -0
  161. package/skills/goke/src/index.ts +16 -0
  162. package/skills/goke/src/mri.ts +164 -0
  163. package/skills/goke/tsconfig.json +15 -0
  164. package/skills/jitter/EDITOR.md +219 -0
  165. package/skills/jitter/EXPORT-INTERNALS.md +309 -0
  166. package/skills/jitter/SKILL.md +158 -0
  167. package/skills/jitter/jitter-clipboard.json +1042 -0
  168. package/skills/jitter/package.json +14 -0
  169. package/skills/jitter/tsconfig.json +15 -0
  170. package/skills/jitter/utils/actions.ts +212 -0
  171. package/skills/jitter/utils/export.ts +114 -0
  172. package/skills/jitter/utils/index.ts +141 -0
  173. package/skills/jitter/utils/snapshot.ts +154 -0
  174. package/skills/jitter/utils/traverse.ts +246 -0
  175. package/skills/jitter/utils/types.ts +279 -0
  176. package/skills/jitter/utils/wait.ts +133 -0
  177. package/skills/playwriter/SKILL.md +31 -0
  178. package/skills/security-review/SKILL.md +208 -0
  179. package/skills/simplify/SKILL.md +58 -0
  180. package/skills/termcast/SKILL.md +945 -0
  181. package/skills/tuistory/SKILL.md +250 -0
  182. package/skills/zustand-centralized-state/SKILL.md +582 -0
  183. package/src/__snapshots__/compact-session-context-no-system.md +35 -0
  184. package/src/__snapshots__/compact-session-context.md +41 -0
  185. package/src/__snapshots__/first-session-no-info.md +17 -0
  186. package/src/__snapshots__/first-session-with-info.md +23 -0
  187. package/src/__snapshots__/session-1.md +17 -0
  188. package/src/__snapshots__/session-2.md +5871 -0
  189. package/src/__snapshots__/session-3.md +17 -0
  190. package/src/__snapshots__/session-with-tools.md +5871 -0
  191. package/src/ai-tool-to-genai.test.ts +296 -0
  192. package/src/ai-tool-to-genai.ts +282 -0
  193. package/src/ai-tool.ts +39 -0
  194. package/src/bin.ts +108 -0
  195. package/src/bot-token.test.ts +171 -0
  196. package/src/bot-token.ts +159 -0
  197. package/src/channel-management.ts +172 -0
  198. package/src/cli-parsing.test.ts +132 -0
  199. package/src/cli.ts +3605 -0
  200. package/src/commands/abort.ts +112 -0
  201. package/src/commands/action-buttons.ts +376 -0
  202. package/src/commands/add-project.ts +152 -0
  203. package/src/commands/agent.ts +404 -0
  204. package/src/commands/ask-question.ts +330 -0
  205. package/src/commands/compact.ts +157 -0
  206. package/src/commands/context-usage.ts +199 -0
  207. package/src/commands/create-new-project.ts +179 -0
  208. package/src/commands/diff.ts +165 -0
  209. package/src/commands/file-upload.ts +389 -0
  210. package/src/commands/fork.ts +320 -0
  211. package/src/commands/gemini-apikey.ts +104 -0
  212. package/src/commands/login.ts +634 -0
  213. package/src/commands/mention-mode.ts +77 -0
  214. package/src/commands/merge-worktree.ts +177 -0
  215. package/src/commands/model.ts +961 -0
  216. package/src/commands/permissions.ts +261 -0
  217. package/src/commands/queue.ts +296 -0
  218. package/src/commands/remove-project.ts +155 -0
  219. package/src/commands/restart-opencode-server.ts +162 -0
  220. package/src/commands/resume.ts +242 -0
  221. package/src/commands/run-command.ts +123 -0
  222. package/src/commands/session-id.ts +109 -0
  223. package/src/commands/session.ts +250 -0
  224. package/src/commands/share.ts +106 -0
  225. package/src/commands/types.ts +25 -0
  226. package/src/commands/undo-redo.ts +221 -0
  227. package/src/commands/unset-model.ts +189 -0
  228. package/src/commands/upgrade.ts +52 -0
  229. package/src/commands/user-command.ts +193 -0
  230. package/src/commands/verbosity.ts +88 -0
  231. package/src/commands/worktree-settings.ts +79 -0
  232. package/src/commands/worktree.ts +431 -0
  233. package/src/condense-memory.ts +36 -0
  234. package/src/config.ts +148 -0
  235. package/src/database.ts +1530 -0
  236. package/src/db.test.ts +60 -0
  237. package/src/db.ts +190 -0
  238. package/src/discord-api.ts +35 -0
  239. package/src/discord-bot.ts +1316 -0
  240. package/src/discord-utils.test.ts +132 -0
  241. package/src/discord-utils.ts +767 -0
  242. package/src/errors.ts +213 -0
  243. package/src/escape-backticks.test.ts +469 -0
  244. package/src/format-tables.test.ts +223 -0
  245. package/src/format-tables.ts +145 -0
  246. package/src/forum-sync/config.ts +92 -0
  247. package/src/forum-sync/discord-operations.ts +241 -0
  248. package/src/forum-sync/index.ts +9 -0
  249. package/src/forum-sync/markdown.ts +176 -0
  250. package/src/forum-sync/sync-to-discord.ts +595 -0
  251. package/src/forum-sync/sync-to-files.ts +294 -0
  252. package/src/forum-sync/types.ts +175 -0
  253. package/src/forum-sync/watchers.ts +454 -0
  254. package/src/genai-worker-wrapper.ts +164 -0
  255. package/src/genai-worker.ts +386 -0
  256. package/src/genai.ts +321 -0
  257. package/src/generated/browser.ts +109 -0
  258. package/src/generated/client.ts +131 -0
  259. package/src/generated/commonInputTypes.ts +512 -0
  260. package/src/generated/enums.ts +46 -0
  261. package/src/generated/internal/class.ts +362 -0
  262. package/src/generated/internal/prismaNamespace.ts +2251 -0
  263. package/src/generated/internal/prismaNamespaceBrowser.ts +308 -0
  264. package/src/generated/models/bot_api_keys.ts +1288 -0
  265. package/src/generated/models/bot_tokens.ts +1577 -0
  266. package/src/generated/models/channel_agents.ts +1256 -0
  267. package/src/generated/models/channel_directories.ts +2104 -0
  268. package/src/generated/models/channel_mention_mode.ts +1300 -0
  269. package/src/generated/models/channel_models.ts +1288 -0
  270. package/src/generated/models/channel_verbosity.ts +1224 -0
  271. package/src/generated/models/channel_worktrees.ts +1308 -0
  272. package/src/generated/models/forum_sync_configs.ts +1452 -0
  273. package/src/generated/models/global_models.ts +1288 -0
  274. package/src/generated/models/ipc_requests.ts +1485 -0
  275. package/src/generated/models/part_messages.ts +1302 -0
  276. package/src/generated/models/scheduled_tasks.ts +2320 -0
  277. package/src/generated/models/session_agents.ts +1086 -0
  278. package/src/generated/models/session_models.ts +1114 -0
  279. package/src/generated/models/session_start_sources.ts +1408 -0
  280. package/src/generated/models/thread_sessions.ts +1599 -0
  281. package/src/generated/models/thread_worktrees.ts +1352 -0
  282. package/src/generated/models.ts +29 -0
  283. package/src/heap-monitor.ts +121 -0
  284. package/src/hrana-server.test.ts +428 -0
  285. package/src/hrana-server.ts +547 -0
  286. package/src/image-utils.ts +149 -0
  287. package/src/interaction-handler.ts +461 -0
  288. package/src/ipc-polling.ts +325 -0
  289. package/src/kimaki-digital-twin.e2e.test.ts +201 -0
  290. package/src/limit-heading-depth.test.ts +116 -0
  291. package/src/limit-heading-depth.ts +26 -0
  292. package/src/logger.ts +203 -0
  293. package/src/markdown.test.ts +360 -0
  294. package/src/markdown.ts +410 -0
  295. package/src/message-formatting.test.ts +81 -0
  296. package/src/message-formatting.ts +549 -0
  297. package/src/openai-realtime.ts +362 -0
  298. package/src/opencode-plugin-loading.e2e.test.ts +112 -0
  299. package/src/opencode-plugin.test.ts +108 -0
  300. package/src/opencode-plugin.ts +652 -0
  301. package/src/opencode.ts +554 -0
  302. package/src/privacy-sanitizer.ts +142 -0
  303. package/src/schema.sql +158 -0
  304. package/src/sentry.ts +137 -0
  305. package/src/session-handler/state.ts +232 -0
  306. package/src/session-handler.ts +2668 -0
  307. package/src/session-search.test.ts +50 -0
  308. package/src/session-search.ts +148 -0
  309. package/src/startup-service.ts +200 -0
  310. package/src/system-message.ts +568 -0
  311. package/src/task-runner.ts +425 -0
  312. package/src/task-schedule.test.ts +84 -0
  313. package/src/task-schedule.ts +287 -0
  314. package/src/thinking-utils.ts +61 -0
  315. package/src/thread-message-queue.e2e.test.ts +997 -0
  316. package/src/tools.ts +432 -0
  317. package/src/unnest-code-blocks.test.ts +679 -0
  318. package/src/unnest-code-blocks.ts +168 -0
  319. package/src/upgrade.ts +127 -0
  320. package/src/utils.ts +145 -0
  321. package/src/voice-handler.ts +852 -0
  322. package/src/voice.test.ts +219 -0
  323. package/src/voice.ts +444 -0
  324. package/src/wait-session.ts +147 -0
  325. package/src/worker-types.ts +64 -0
  326. package/src/worktree-utils.ts +988 -0
  327. package/src/xml.test.ts +38 -0
  328. package/src/xml.ts +121 -0
@@ -0,0 +1,250 @@
1
+ ---
2
+ name: tuistory
3
+ description: |
4
+ Playwright for terminal apps. Like tmux but designed for agents — virtual terminals you can
5
+ type into, press keys, wait for text, take snapshots, and screenshot as images.
6
+
7
+ tuistory has **2 modes**:
8
+
9
+ - **CLI** (`tuistory` command) — shell-based. Launch sessions, type, press keys, snapshot, screenshot.
10
+ Runs a background daemon that persists sessions across commands. Install globally or use `npx`/`bunx`.
11
+ **You MUST run `tuistory --help` before using the CLI** to see the latest commands and options.
12
+ - **JS/TS API** (`import { launchTerminal } from 'tuistory'`) — programmatic. Use in vitest/bun:test
13
+ to write Playwright-style tests for CLIs and TUIs with inline snapshots.
14
+
15
+ Use tuistory when you need to:
16
+ - Write e2e tests for CLI/TUI apps (vitest, bun:test) with inline snapshots
17
+ - Automate terminal interactions (launch a REPL, debugger, or TUI and drive it)
18
+ - Screenshot terminal as images to send to users (Discord bots, agent UIs like kimaki/openclaw)
19
+ - Reproduce bugs in interactive CLIs by scripting the exact steps
20
+ - Explore TUI apps progressively with observe-act-observe loops
21
+ ---
22
+
23
+ # tuistory
24
+
25
+ Playwright for terminal user interfaces. Write end-to-end tests for CLI and TUI applications.
26
+
27
+ tuistory has **2 modes**:
28
+
29
+ 1. **CLI** — the `tuistory` shell command. Launch terminal sessions, type text, press keys, take text snapshots or image screenshots. Sessions run in a background daemon and persist across commands.
30
+ 2. **JS/TS API** — `import { launchTerminal } from 'tuistory'`. Use programmatically in test files (vitest, bun:test) to write Playwright-style tests with inline snapshots.
31
+
32
+ ## CLI usage
33
+
34
+ **REQUIREMENT:** You MUST run `tuistory --help` before using the CLI. The CLI evolves and the help output is the source of truth for available commands, options, and syntax. Always check it first.
35
+
36
+ Install globally or use npx/bunx:
37
+
38
+ ```bash
39
+ # global
40
+ bun add -g tuistory
41
+ npm install -g tuistory
42
+
43
+ # or without installing
44
+ npx tuistory --help
45
+ bunx tuistory --help
46
+ ```
47
+
48
+ ### CLI quick reference
49
+
50
+ ```bash
51
+ tuistory launch <command> -s <name> [--cols N] [--rows N] [--env KEY=VAL]
52
+ tuistory -s <name> snapshot --trim
53
+ tuistory -s <name> screenshot -o image.jpg --pixel-ratio 2
54
+ tuistory -s <name> type "text"
55
+ tuistory -s <name> press enter
56
+ tuistory -s <name> press ctrl c
57
+ tuistory -s <name> click "Submit"
58
+ tuistory -s <name> wait "pattern" --timeout 10000
59
+ tuistory -s <name> wait "/regex/" --timeout 10000
60
+ tuistory -s <name> scroll down 5
61
+ tuistory -s <name> resize 120 40
62
+ tuistory -s <name> close
63
+ tuistory sessions
64
+ tuistory daemon-stop
65
+ ```
66
+
67
+ Always run `snapshot --trim` after every action to see the current terminal state.
68
+
69
+ ### Screenshot for agent bots
70
+
71
+ Capture terminal as an image to upload to users (Discord, Slack, web UIs).
72
+ Use `--pixel-ratio 2` for sharp images on social media and messaging apps:
73
+
74
+ ```bash
75
+ tuistory -s myapp screenshot -o /tmp/terminal.jpg --pixel-ratio 2
76
+ # then upload /tmp/terminal.jpg to the user
77
+ ```
78
+
79
+ ## JS/TS API (library)
80
+
81
+ ```bash
82
+ bun add tuistory # or npm install tuistory
83
+ ```
84
+
85
+ ```ts
86
+ import { launchTerminal } from 'tuistory'
87
+
88
+ const session = await launchTerminal({
89
+ command: 'my-cli',
90
+ args: ['--flag'],
91
+ cols: 120,
92
+ rows: 36,
93
+ cwd: '/path/to/dir',
94
+ env: { MY_VAR: 'value' },
95
+ })
96
+
97
+ // observe
98
+ const text = await session.text() // full terminal text
99
+ const text = await session.text({ trimEnd: true }) // trimmed
100
+ const bold = await session.text({ only: { bold: true } }) // style filter
101
+
102
+ // act
103
+ await session.type('hello world') // type character by character
104
+ await session.press('enter') // single key
105
+ await session.press(['ctrl', 'c']) // key chord
106
+ await session.click('Submit') // click on text
107
+
108
+ // wait
109
+ await session.waitForText('Ready', { timeout: 10000 })
110
+ await session.waitForText(/Loading\.\.\./, { timeout: 5000 })
111
+
112
+ // screenshot to image
113
+ const data = session.getTerminalData()
114
+ const { renderTerminalToImage } = await import('ghostty-opentui/image')
115
+ const image = await renderTerminalToImage(data, { format: 'jpeg', devicePixelRatio: 2 })
116
+
117
+ // cleanup
118
+ session.close()
119
+ ```
120
+
121
+ ## Writing tests with vitest
122
+
123
+ tuistory is like Playwright but for CLIs. The workflow is: **observe** (snapshot with inline snapshot), **act** (type/press/click), **observe** again. Build tests progressively.
124
+
125
+ ### Step 1: Launch and observe
126
+
127
+ Start with an empty inline snapshot. Run with `--update` / `-u` to fill it in.
128
+
129
+ ```ts
130
+ import { test, expect } from 'vitest'
131
+ import { launchTerminal } from 'tuistory'
132
+
133
+ test('my CLI shows help', async () => {
134
+ const session = await launchTerminal({
135
+ command: 'my-cli',
136
+ args: ['--help'],
137
+ cols: 120,
138
+ rows: 36,
139
+ })
140
+
141
+ const text = await session.text({ trimEnd: true })
142
+ expect(text).toMatchInlineSnapshot()
143
+ // ^ run `vitest --run -u` to fill this in, then read the file to see what it captured
144
+
145
+ session.close()
146
+ }, 10000)
147
+ ```
148
+
149
+ ### Step 2: Interact and observe again
150
+
151
+ Add actions and more snapshots incrementally:
152
+
153
+ ```ts
154
+ test('bash interaction', async () => {
155
+ const session = await launchTerminal({
156
+ command: 'bash',
157
+ args: ['--norc', '--noprofile'],
158
+ cols: 60,
159
+ rows: 10,
160
+ env: { PS1: '$ ', HOME: '/tmp', PATH: process.env.PATH },
161
+ })
162
+
163
+ // observe initial state
164
+ const initial = await session.text({ trimEnd: true })
165
+ expect(initial).toMatchInlineSnapshot()
166
+
167
+ // act
168
+ await session.type('echo "hello world"')
169
+ await session.press('enter')
170
+
171
+ // wait + observe
172
+ const output = await session.waitForText('hello world')
173
+ expect(output).toMatchInlineSnapshot()
174
+
175
+ // cleanup
176
+ await session.type('exit')
177
+ await session.press('enter')
178
+ session.close()
179
+ }, 10000)
180
+ ```
181
+
182
+ ### Step 3: Run with -u, read back, iterate
183
+
184
+ ```bash
185
+ # fill in snapshots
186
+ vitest --run -u
187
+
188
+ # read the test file to see captured terminal output
189
+ # adjust assertions, add more interactions, repeat
190
+ ```
191
+
192
+ This observe-act-observe loop lets you progressively explore any TUI. Each inline snapshot captures the exact terminal state, making tests readable and easy to update.
193
+
194
+ ### Testing a TUI app (e.g. opencode, claude)
195
+
196
+ ```ts
197
+ test('opencode shows welcome', async () => {
198
+ const session = await launchTerminal({
199
+ command: 'opencode',
200
+ cols: 150,
201
+ rows: 45,
202
+ })
203
+
204
+ await session.waitForText('switch agent', { timeout: 15000 })
205
+ await session.type('hello from tuistory')
206
+
207
+ const text = await session.text({ timeout: 1000 })
208
+ expect(text).toMatchInlineSnapshot()
209
+
210
+ // navigate menus
211
+ await session.press(['ctrl', 'p'])
212
+ const commands = await session.waitForText('Commands', { timeout: 5000 })
213
+ expect(commands).toMatchInlineSnapshot()
214
+
215
+ await session.press('esc')
216
+ session.close()
217
+ }, 30000)
218
+ ```
219
+
220
+ ### Testing a Node.js debugger
221
+
222
+ ```ts
223
+ test('node debugger inspect variables', async () => {
224
+ const session = await launchTerminal({
225
+ command: 'node',
226
+ args: ['inspect', 'app.js'],
227
+ cols: 150,
228
+ rows: 45,
229
+ })
230
+
231
+ await session.waitForText('Break on start', { timeout: 10000 })
232
+ await session.type('cont')
233
+ await session.press('enter')
234
+ await session.waitForText('break in', { timeout: 5000 })
235
+
236
+ const snapshot = await session.text({ trimEnd: true })
237
+ expect(snapshot).toMatchInlineSnapshot()
238
+
239
+ session.close()
240
+ }, 30000)
241
+ ```
242
+
243
+ ## Tips
244
+
245
+ - **Always set a timeout** on `waitForText` for async operations
246
+ - **Use `trimEnd: true`** in `session.text()` to avoid trailing whitespace in snapshots
247
+ - **Set `waitForData: false`** for interactive commands that don't produce output immediately (like `cat`)
248
+ - **Use regex** in `waitForText` for dynamic content: `await session.waitForText(/version \d+/)`
249
+ - **Close sessions** in test teardown to avoid leaked processes
250
+ - **Use `--cols` and `--rows`** to control terminal size — affects TUI layout