Cahier is a terminal session recorder and notebook written in Rust.
It runs your commands through your shell inside a PTY, captures command output, exit status, and execution time, and stores the session in a structured SQLite database. The result is a project-local notebook you can browse in a TUI and export to Markdown.
Standard shell history records command lines. Cahier records the working session around them:
- command text
- stdout and stderr
- exit codes
- execution duration
- annotations
- ordering and separators
- reusable snippets
This makes it useful for keeping development notes, reconstructing terminal sessions, and exporting clean project logs.
- Records shell commands with output, exit status, and duration.
- Stores session data in SQLite.
- Exports history as Markdown or plain command lists.
- Opens an interactive TUI to review, reorder, annotate, and delete entries.
- Supports project and global snippets.
- Redirects oversized command output to external files instead of bloating the database.
- Lets you skip persistence for a command with the
nrprefix. - Can import aliases from your interactive shell at startup.
- Uses restrictive Unix permissions for sensitive local files where supported.
cargo install cahiergit clone https://github.com/bistace/cahier.git
cd cahier
cargo install --path .cargo build --release
cp target/release/cahier /usr/local/bin/Start Cahier in the project you want to track:
cahierOn first use, Cahier creates a project-local notebook under ./cahier_logs/:
cahier_logs/
|-- cahier.db
|-- cahier_history.txt
|-- outputs/
`-- env_state.json # only used when restore_env is enabled
Run commands as usual inside the REPL. Later, export the notebook:
cahier export --output session.mdcahier starts the REPL wrapper with the default output capture limit of 16384 bytes.
cahierYou can also start it explicitly and change the output threshold:
cahier start --max-output-size 1048576Commands are executed through your current shell from $SHELL using -c, inside a pseudo-terminal. Cahier also provides built-ins for stateful shell-like behavior where needed.
The REPL supports these built-ins:
cdjobsfgaliasunaliaseditexit
All other commands are executed through your shell.
Prefix a command with nr to execute it without storing it in the notebook or writing captured output to disk:
nr export API_KEY="12345"
nr echo "temporary secret"Export the recorded notebook as Markdown:
cahier exportWrite the export to a file:
cahier export --output session_log.mdExport only the command lines:
cahier export --only-commandsMarkdown export includes:
- optional annotations
- a status line in the form
(exit_code - duration_ms) - the command prefixed with
$ - captured output, or a reference to an external output file
Open the TUI from the command line:
cahier editOr from inside the REPL:
editHistory view key bindings:
j/Down: next entryk/Up: previous entryEnter: toggle fullscreen output viewp: collapse or expand the preview panea: annotate entryb: save selected command as a snippetd: delete entryJ: move entry downK: move entry upSpace: insert a separators: send selected command back to the REPLS: open the snippet browserq: quit
Snippet browser key bindings:
j/Down: next snippetk/Up: previous snippets: send selected snippet back to the REPLd: delete snippetq: return to history view
When creating a snippet, Cahier lets you set:
namedescriptionscope(projectorglobal)tags
Project snippets are stored in the current notebook database. Global snippets are stored in ~/.cahier/snippets.db.
Cahier uses two storage scopes:
- Project-local data in
./cahier_logs/- session database
- REPL history file
- redirected output files
- optional persisted environment state
- User-level data in
~/.cahier/config.jsonsnippets.dbfor global snippets
This split keeps notebooks tied to the current project while allowing shared configuration and reusable snippets.
Cahier loads configuration from ~/.cahier/config.json. If the file does not exist, Cahier creates it automatically.
Default configuration:
{
"ignored_outputs": [
"nano",
"vim",
"vi",
"emacs",
"hx",
"atom",
"gedit",
"geany",
"kate",
"kwrite",
"nvim",
"htop",
"top",
"atop",
"less",
"more",
"man",
"ssh",
"tmux",
"screen"
],
"theme": "Solarized (dark)",
"load_aliases": true,
"restore_env": false
}Configuration fields:
ignored_outputs: commands whose output should not be captured and whose executions should not be persisted to the notebook.theme: syntax highlighting theme used by the REPL highlighter.load_aliases: whether Cahier should load aliases from your interactive shell at startup.restore_env: whether Cahier should persist environment state between sessions.
When restore_env is enabled, Cahier stores environment variables to ./cahier_logs/env_state.json and attempts to restore them, including PWD, on the next start.
When it is disabled, environment changes still persist for the lifetime of the current Cahier session, but they are not written for reuse in later sessions.
nr ...executes a command without logging it to the database.- Oversized output is redirected to a file under
./cahier_logs/outputs/when capture is enabled. - Commands listed in
ignored_outputsare executed without output capture and are not persisted to the notebook. - On Unix, Cahier uses restrictive permissions for sensitive files and directories where it creates them:
- notebook directories and output directories:
0700 - config, env-state, and redirected output files:
0600
- notebook directories and output directories:
If you enable restore_env, be aware that environment values may be written to disk. Do not enable it casually on machines or workflows that handle sensitive secrets.
Cahier is built with:
clapfor the CLIportable-ptyfor PTY-backed command executionreedlinefor the REPL editor, completion, and history integrationratatuifor the interactive TUIrusqlitefor SQLite persistence
Run the test suite with:
cargo testThis project is licensed under the MIT License. See LICENSE.