atuin module
Status: opt-in. Source:
src/modules/atuin.zig
Fish/zsh-autosuggestion-style ghost text driven by your Atuin history.
How it works
Suggest path:
- Every keystroke updates the line model;
onInputcopies the current buffer into the worker’s latest-wins query slot and signals. - The worker calls
atuin search --search-mode prefix --filter-mode global --limit <list_count_max> --cmd-only <query>(newest match wins; no--reverse— that flag flips the default and gives the oldest match instead).list_count_maxdefaults to 9 so the inline ghost uses entry 0 and the multi-row pick list (Ctrl+1..9) consumes the rest. provideGhostTextreads the latest result under a mutex; if it still starts with the current input, the trailing portion is rendered after the cursor.onTickoptionally expires the suggestion aftersuggestion_ttl_msof keyboard inactivity (0= disabled, suggestion persists until it no longer prefix-matches — fish-style).
Record path:
onLineCommitfires when the user presses Enter on a non-empty, certain line (the proxy snapshots the pre-submit buffer fromLineState.lastCommitted()).- The module pushes the line into the worker’s bounded FIFO
record queue (
record_queue_capacityslots; default 16). The FIFO preserves commit order across bursts — multi-line paste, slowatuin history start, or rapid LLM-assisted submits no longer overwrite earlier commits. At capacity the producer drops the newest entry (the oldest is already in flight to atuin’s local store) and incrementsrec_dropped; the status bar surfaces this asatuin (N dropped)until detach. - The worker drains one record per loop iteration and shells out
to
atuin history start <cmd>(we don’t capture the entry ID, so there’s nohistory end— entries land with no exit code or duration; atuin handles that gracefully). - After
sync_after_recordsrecords orsync_interval_msms,atuin syncruns on a detachedstd.Thread, so the worker never blocks on the network. The clock starts on the first record handled rather than at attach, so a session that commits no lines never syncs. - Final sync on detach is JOINED (up to
sync_on_detach_timeout_ms, default 3 s). Worker shutdown first drains any queued records, then blocks on the sync thread so an interactive session’s exit leaves no commits stranded locally. The thread is leaked on timeout — bounded shutdown beats waiting for atuin’s offline backoff.
Accept path:
Right-arrow / End / Ctrl-F (configured in bindings[]) replace the
keystroke with the current ghost-overlay text before line state sees
the CSI — see the Keymap docs.
Configuration
pub const Atuin = atty.modules.atuin.configure(.{
.backend = .subprocess,
.atuin_binary = "atuin",
.socket_path = "",
.search_mode = .prefix,
.filter_mode = .global,
.suggestion_ttl_ms = 0,
.max_query = 256,
.max_result = 4096,
.list_count_max = 9,
.record = true,
.record_queue_capacity = 16,
.sync_after_records = 10,
.sync_interval_ms = 60_000,
.sync_on_detach = true,
.sync_on_detach_timeout_ms = 3_000,
.delete_scope = .exact,
.tag_llm_author = false,
.author_tag_prefix = "atty",
.tag_llm_intent = false,
});
| Field | Default | Values |
|---|---|---|
backend |
.subprocess |
.subprocess, .socket (stub) |
atuin_binary |
"atuin" |
path to atuin executable |
socket_path |
"" |
path to atuin’s IPC socket; consumed only when backend = .socket (stub) — ignored otherwise |
search_mode |
.prefix |
.prefix, .full_text, .fuzzy |
filter_mode |
.global |
.global, .host, .session, .directory |
suggestion_ttl_ms |
0 | ms of idleness before suggestion fades; 0 disables |
max_query, max_result |
256 / 4096 | comptime buffer sizes (max_result sized for ~9 suggestions) |
list_count_max |
9 | per-query result cap; entry 0 → ghost text, rest → pick list |
record |
true |
shell out to atuin history start on Enter |
record_queue_capacity |
16 | bounded FIFO depth; overflow drops newest + bumps rec_dropped |
sync_after_records |
10 | sync after N records; 0 disables |
sync_interval_ms |
60000 | sync if at least this much time elapsed; 0 disables |
sync_on_detach |
true |
one final sync on shutdown |
sync_on_detach_timeout_ms |
3000 | max ms to wait for the final sync to complete (0 = forever) |
delete_scope |
.exact |
.exact / .prefix / .full_text / .fuzzy for Ctrl+Shift+D |
tag_llm_author |
false |
tag LLM-authored commits with --author <prefix>:llm (atuin v18.3+) |
author_tag_prefix |
"atty" |
prefix for the author tag (<prefix>:llm) |
tag_llm_intent |
false |
pass the LLM’s intent text via --intent (atuin v18.5+) |
Backends
.subprocess— shells out toatuin search. Robust, used today..socket— talks to the Atuin daemon socket. Stub; the symbols are wired throughconfigureso swapping backends is a one-field change once Atuin’s IPC protocol stabilises. When.subprocessis selected, the socket path is comptime-eliminated from the binary, and vice versa.
Status bar segment
Atuin’s statusText segment is "atuin" in the steady state. When
the record FIFO has overflowed during the current session, it
renders "atuin (N dropped)" instead, where N is the cumulative
rec_dropped counter — gives the operator an at-a-glance signal
that some commits were lost to a burst the worker couldn’t drain
in time. The segment is omitted entirely if the user has disabled
the status bar (statusbar.enabled = false).
deleteHistoryMatch
Implemented via atuin search --search-mode <mode> --delete <query>.
Atuin v18 has no per-entry-ID delete CLI, and its built-in modes
all over-match for “remove just this line.” The implementation
exploits fuzzy mode’s fzf-style anchors to express true exact
match without the side effects:
All variants share the same --filter-mode <filter_mode> argument
(.global by default) — omitted from the argv column for brevity:
delete_scope |
atuin CLI args | Removes |
|---|---|---|
.exact (default) |
--search-mode fuzzy --delete '^<line>$' |
Only commands equal to <line> |
.prefix |
--search-mode prefix --delete <line> |
<line> and any command starting with it (echo asd also removes echo asdf) |
.full_text |
--search-mode full-text --delete <line> |
Any command containing <line> as a substring |
.fuzzy |
--search-mode fuzzy --delete <line> |
Typo-tolerant fuzzy match; broadest collateral |
Runs synchronously on the proxy thread (single std.process.run,
typically <200 ms). Deliberately not routed through the worker
mailbox — the user just pressed a destructive key, the prompt is
already cleared (the proxy sent Ctrl+U first), and a brief
blocking window is preferable to a third worker-mailbox slot.
Performance
onInputdoes onememcpy+ a cv-signal, no I/O. Zero allocations.- The worker thread runs at most one
atuin searchper keystroke (coalesced — typing 5 chars quickly results in 1–2 lookups, not 5). provideGhostTextis a mutex+memcpy, no allocations beyond the per-dispatchctx.scratch.