trec records terminal sessions into binary .trec files. It can replay a recording or generate a small C or Python script from the recorded input.
The project is still early. The commands below are meant to be run from the repository checkout. C examples include headers with -Iinclude; Python examples use PYTHONPATH=python and TREC_LIBRARY=$PWD/libtrec.so so they load the local binding and library.
- Linux
makegcc- Python 3 for tests, the dump tool, and the Python binding
clang-formatformake format
makeThis builds:
./trec./libtrec.a./libtrec.so
Other common targets:
make test
make format
make cleanRecord a shell session:
./trec record --file session.trecrecord starts $SHELL. Use the shell as usual, then press Ctrl-D to stop recording.
Replay the recorded input in a fresh shell:
./trec replay --file session.trecPrint the recorded output without running the commands again:
./trec replay --output-replay --file session.trecGenerate and run a C script:
./trec codegen --file session.trec --output session.c
cc -Wall -Wextra -g -Iinclude -L. -o session session.c -ltrec -lutil
./sessionGenerate and run a Python script:
./trec codegen --language python --file session.trec --output session.py
PYTHONPATH=python TREC_LIBRARY=$PWD/libtrec.so python3 session.py./trec record [OPTIONS]| Option | Meaning |
|---|---|
-f, --file <file> |
Output file. Default: session.trec |
-h, --help |
Show help |
Recording stores input, output, resize events, and the shell exit status. It always starts $SHELL; if $SHELL is not set, it uses /bin/sh.
./trec replay [OPTIONS]| Option | Meaning |
|---|---|
-f, --file <file> |
Input .trec file. Required |
--output-replay |
Print recorded output instead of injecting input into a shell |
-h, --help |
Show help |
Default replay starts a new $SHELL and sends the recorded input to it. It runs against the current filesystem, commands, and environment, so output can differ from the original session.
--output-replay prints the recorded output with recorded timing. It does not start a shell.
./trec codegen [OPTIONS]| Option | Meaning |
|---|---|
-f, --file <file> |
Input .trec file. Required |
-o, --output <file> |
Output file. Required |
-l, --language <lang> |
Output language: c or python. Default: c |
--timing-mode <mode> |
Timing mode: merge, interval, recorded, or none |
--merge-threshold-ms <ms> |
Merge action gaps below this value. Default: 500 |
--interval-ms <ms> |
Fixed interval for interval timing mode |
--no-timing |
Alias for --timing-mode none |
--include-output-comments |
Include recorded output snippets as comments |
--include-raw-comments |
Include undecoded input comments |
--record-file <file> |
Make the generated script record its own run |
--strict |
Fail if an input sequence cannot be decoded |
-h, --help |
Show help |
Basic C output:
./trec codegen --file demo.trec --output demo.c
cc -Wall -Wextra -g -Iinclude -L. -o demo demo.c -ltrec -lutil
./demoBasic Python output:
./trec codegen --language python --file demo.trec --output demo.py
PYTHONPATH=python TREC_LIBRARY=$PWD/libtrec.so python3 demo.pyRecord a generated script's run:
./trec codegen --file demo.trec --output demo.c --record-file demo-rerun.trecIf --record-file is omitted, generated scripts do not record their run.
Generated C sets the record file before starting the session:
struct trec_session s = {0};
trec_session_set_record_file(&s, "demo-rerun.trec");
trec_session_start_shell(&s, rows, cols);Generated Python uses the same order:
with Session(record_file="demo-rerun.trec") as s:
s.start_shell(rows, cols)Decoded input becomes script calls:
trec_send_text(&s, "echo hello");
trec_send_key(&s, TREC_KEY_ENTER);
trec_click_at(&s, 10, 5, TREC_MOUSE_LEFT);
trec_drag(&s, 2, 3, 8, 9, TREC_MOUSE_LEFT);
trec_resize(&s, 40, 120);
trec_sleep_ms(&s, 250);with Session() as s:
s.start_shell()
s.send_text("echo hello")
s.send_key(Key.ENTER)
s.click_at(10, 5, MouseButton.LEFT)If input cannot be decoded, codegen writes raw bytes unless --strict is set.
The default mode is merge. It drops short gaps below 500 ms and combines adjacent text input:
./trec codegen --file demo.trec --output demo.cUse a different merge threshold:
./trec codegen --timing-mode merge --merge-threshold-ms 1000 --file demo.trec --output demo.cUse a fixed delay between actions:
./trec codegen --timing-mode interval --interval-ms 50 --file demo.trec --output demo.cPreserve recorded action gaps:
./trec codegen --timing-mode recorded --file demo.trec --output demo.cEmit no sleeps:
./trec codegen --timing-mode none --file demo.trec --output demo.c
./trec codegen --no-timing --file demo.trec --output demo.cScripts can wait for text, find text, click a found position, or read the visible screen.
C:
uint16_t x = 0;
uint16_t y = 0;
if (trec_expect_text(&s, "Submit", 3000) == 1 &&
trec_find_text(&s, "Submit", &x, &y) == 1) {
trec_click_at(&s, x, y, TREC_MOUSE_LEFT);
}Python:
with Session() as s:
s.start_shell()
if s.expect_text("Submit", 3000):
pos = s.find_text("Submit")
if pos is not None:
x, y = pos
s.click_at(x, y)
for x, y in s.find_all_text("Submit"):
print("Submit at", x, y)trec_find_text and Session.find_text() return the first match. trec_find_all_text and Session.find_all_text() return all visible matches. trec_snapshot_text and Session.snapshot_text() return the visible screen as plain text. trec_snapshot and Session.snapshot() return cell data, including size, cursor position, text, width, attributes, and supported colors.
The Python binding uses ctypes and loads libtrec.so.
make
PYTHONPATH=python TREC_LIBRARY=$PWD/libtrec.so python3 script.pyExample:
from trec import Key, Session
with Session() as session:
session.start_shell()
session.send_text("printf 'button\\n'\n")
session.expect_text("button", 3000)
pos = session.find_text("button")
if pos is not None:
x, y = pos
session.click_at(x, y)
print(session.snapshot_text())
session.send_text("exit\n")
raise SystemExit(session.close())Use Session as a context manager. If the with block raises, the session terminates the child process and restores the terminal state.
Use tools/trec_dump.py:
python3 tools/trec_dump.py demo.trec
python3 tools/trec_dump.py --payload-limit 80 demo.trec
python3 tools/trec_dump.py --no-hex demo.trec
python3 tools/trec_dump.py --format json demo.trec
python3 tools/trec_dump.py --format yaml demo.trecMouse input is recorded when the application inside the shell enables mouse reporting. Decoded mouse input can produce trec_click_at, trec_mouse_down, trec_mouse_up, trec_drag, and trec_mouse_scroll calls.
- Linux-only alpha
- unstable
.trecfile format - unstable generated script APIs
- no package manager installation yet
- recording launches
$SHELL; it does not accept a target command - terminal behavior is tested mainly against xterm-like terminals
- snapshot image export
- terminal profiles for iTerm2, Ghostty, WezTerm, kitty, and others
- image protocols such as Sixel and kitty image protocol
- Windows and macOS support
trec is licensed under Apache-2.0. The vendored vendor/libvterm dependency uses the MIT license in vendor/libvterm/LICENSE.