TJ records terminal work in persistent journals. Each shell command becomes a numbered entry containing the command, working directory, terminal output, prompt, exit status, and timing metadata.
Recorded entries can be inspected, searched, pinned, removed, or used as input to later commands. Reading an entry does not run its command again.
curl -s https://example.com/data.json # recorded as entry 1
jq .items "$(tj @1/out)"
tj history
tj cat @1
tj grep error
tj tuiTJ provides two commands:
tjctlcreates, opens, replays, and manages journals.tjworks with journal entries and their resources.
With Homebrew:
brew install vrypan/tap/tjOr build from source with Zig 0.16.0:
git clone https://github.com/vrypan/tj.git
cd tj
make installRelease archives and custom install prefixes are described in Installation.
For zsh, add the plugin to ~/.zshrc:
source ~/.local/share/tj/tj.plugin.zshHomebrew installs the plugin under its own prefix:
source "$(brew --prefix)/share/tj/tj.plugin.zsh"For Fish 4.0.0 or later, add this to ~/.config/fish/config.fish:
source ~/.local/share/tj/tj.plugin.fishWith Homebrew:
source "(brew --prefix)/share/tj/tj.plugin.fish"Start a new shell, then check the setup:
tj --version
tjctl --versionThe shell plugin records command boundaries and enables journal-aware
completion. Zsh also expands bare references such as @12/out when you press
Enter. Inside a journal, Ctrl-X Ctrl-T opens the entry browser. TJ can start
without a plugin, but shell commands will not be recorded.
Create a journal and open a shell inside it:
tjctl new project-workLeave the journal with exit. Open it again later:
tjctl use project-workFor disposable work, use a temporary journal. It is removed on exit unless you save it from the shell:
tjctl new --temp
tjctl saveBy default, use quickly replays the recorded terminal output before opening
a new shell. Use --no-replay to skip it. Continuing a journal does not restore
old processes, environment variables, or shell state.
Use tj-new and tj-use from an interactive zsh or Fish shell. Outside TJ they behave like
tjctl new/use; inside TJ they keep the current shell and move recording to
the selected journal.
Useful journal commands:
tjctl ls -l
tjctl current
tjctl mv project-work new-name
tjctl rm new-nameSee Journals for temporary journals, replay, disk usage, titles, and deletion.
Inside a journal:
tj history # list entries
tj history @12 # show details for one entry
tj history --ids @10..@20 # print IDs for a pipeline
tj cat @12 # show recorded output
tj cat @12/out # show only recorded output
tj grep 'connection refused' @10..@20 # search selected entries
tj pin @12 @15..@18
tj pin --ids | tj tui
tj rm @15..@18Every entry has core resources:
cmd— the command as typedout— recorded terminal output, capped at 1 GiB per entry by defaultcwd— the working directoryprompt— the rendered promptrc— the exit status, when the command finishedmeta.json— timing, recording, and optional expanded-command metadata
"$(tj @REF)" is TJ's canonical POSIX-shell form for an entry filesystem path.
Fish uses its equivalent (tj @REF). In interactive zsh, the plugin rewrites a
valid bare @REF on Enter as a convenience, so cat @12/out runs as
cat "$(tj @12/out)".
Qualified references select another journal: @work.12/out. Commands that
change entries only operate on the current journal.
See Entries and references for ranges, pins, removal, completion, and the TUI.
TJ does not accept pull requests. To report a specific bug or request a specific feature, open an issue and document it clearly. See Contributing.
TJ is released under the MIT License.
- Installation
- Journals
- Entries and references
- Shell integration
- Search and output
- Agents and published resources
- Journal storage
- OSC ELLO protocol
- Development
- Shell recording currently supports zsh and Fish.
- Only one TJ process can write to a journal at a time.
- Automatic retention and pruning are not implemented.
- Journals may contain commands, output, paths, and other sensitive data. Their directories are created with permissions for the current user only.