| Commit message (Collapse) | Author |
|
|
|
|
|
That helps remove duplicated code and rustc/cargo will now also show
dead code correctly.
|
|
|
|
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
## 18.16.1
### Bug Fixes
- *(shell/xonsh)* Use os.devnull instead of hard-coded /dev/null
([#3464](https://github.com/atuinsh/atuin/issues/3464))
- Atuin update on windows
([#3453](https://github.com/atuinsh/atuin/issues/3453))
- Ensure local key matches remote data before syncing
([#3474](https://github.com/atuinsh/atuin/issues/3474))
### Documentation
- Add related projects section to README
### Features
- *(ui)* Prominent banner for wrong-key errors at login/sync
([#3475](https://github.com/atuinsh/atuin/issues/3475))
### Miscellaneous Tasks
- Generate LLM-optimized docs
([#3468](https://github.com/atuinsh/atuin/issues/3468))
- Rename 'atuin hex' to 'atuin pty-proxy'
([#3473](https://github.com/atuinsh/atuin/issues/3473))
|
|
|
|
|
|
Overhaul of how AI tool calls are modeled, rendered, and displayed in
the Atuin AI TUI. Fixes bugs in shell command output capture, implements
the `edit_file` tool with full safety infrastructure, and adds a diff
preview for edits.
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
## 18.15.2
### Bug Fixes
- Tab doesn't insert suggested command
([#3420](https://github.com/atuinsh/atuin/issues/3420))
|
|
|
|
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
|
|
Adds client-side tool execution to Atuin AI, starting with
`atuin_history`. The server can request tool calls, which are executed
locally with a permission system, and results are sent back to continue
the conversation.
|
|
|
|
|
|
|
|
|
|
Rename crates (nucleo β atuin-nucleo, nucleo-matcher β atuin-nucleo-matcher),
add to workspace members and dependencies, update all import paths, remove
vendored CI workflow, and suppress upstream clippy warnings.
format
codespell fixes
clippy clappy
|
|
I tried adding a windows arm64 build to the last release, which was
perhaps a little naive of me. I thought it working on x86 windows and
arm everything else would be enough π
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
We were using buildjet as our runners previously, but it looks like they
have gone under. Luckily, GitHub now provides the arm runners we need
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
Our crates used thiserror 1.x while the ratatui ecosystem already
pulled in thiserror 2.x, resulting in both versions in the binary.
The API is compatible; this consolidates to a single version.
|
|
Adds strip = "symbols" to the cargo dist profile, removing debug
symbols and symbol tables from the release binary (~5MiB savings).
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.38.44
to 1.1.4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/bytecodealliance/rustix/releases">rustix's
releases</a>.</em></p>
<blockquote>
<h2>1.0.0</h2>
<p>This release introduces the <a
href="https://docs.rs/rustix/1.0.0/rustix/buffer/trait.Buffer.html"><code>Buffer</code>
trait</a>, which is used in <a
href="https://docs.rs/rustix/1.0.0/rustix/io/fn.read.html"><code>read</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/io/fn.pread.html"><code>pread</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/net/fn.recv.html"><code>recv</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/net/fn.recvfrom.html"><code>recvfrom</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/rand/fn.getrandom.html"><code>getrandom</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/fs/fn.readlinkat_raw.html"><code>readlinkat_raw</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/event/epoll/fn.wait.html"><code>epoll::wait</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/x86_64-unknown-freebsd/rustix/event/kqueue/fn.kevent.html"><code>kevent</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/x86_64-unknown-illumos/rustix/event/port/fn.getn.html"><code>port::getn</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/fs/fn.getxattr.html"><code>getxattr</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/fs/fn.lgetxattr.html"><code>lgetxattr</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/fs/fn.fgetxattr.html"><code>fgetxattr</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/fs/fn.listxattr.html"><code>listxattr</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/fs/fn.llistxattr.html"><code>llistxattr</code></a>,
and <a
href="https://docs.rs/rustix/1.0.0/rustix/fs/fn.flistxattr.html"><code>flistxattr</code></a>,
and adds support for reading data into uninitialized buffers, as well as
safely reading data into the spare capacity of <code>Vec</code>s.</p>
<p>This release also simplifies the way network addresses are handled.
Instead of having separate functions with <code>_v4</code>,
<code>_v6</code>, <code>_unix</code>, <code>_xdp</code>, and now
<code>_netlink</code> suffixes, rustix now uses a <a
href="https://docs.rs/rustix/1.0.0/rustix/net/trait.SocketAddrArg.html"><code>SocketAddrArg</code>
trait</a> so that functions such as <a
href="https://docs.rs/rustix/1.0.0/rustix/net/fn.bind.html"><code>bind</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/net/fn.connect.html"><code>connect</code></a>,
<a
href="https://docs.rs/rustix/1.0.0/rustix/net/fn.sendto.html"><code>sendto</code></a>,
and <a
href="https://docs.rs/rustix/1.0.0/rustix/net/fn.sendmsg_addr.html"><code>sendmsg_addr</code></a>
can accept any type of address, and are easier to extend to new address
types in the future.</p>
<p>And, this release simplifies the <code>ioctl</code> API, replacing
opcode wrapper types with const generics.</p>
<p>This updates several APIs to add Linux 6.13 features, and raw
linux-raw-sys types are no longer exposed in the public API, so it
should be easier to stay up to date with new Linux releases.</p>
<p>And many more new features, bug fixes, and cleanups. See the <a
href="https://github.com/bytecodealliance/rustix/blob/main/CHANGES.md#changes-from-038x-to-1x">CHANGES.md
file</a> for the full list of breaking changes.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/c4caf5caaa7e93828a2e4a4cdba1dd0171e45717"><code>c4caf5c</code></a>
chore: Release rustix version 1.1.4</li>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/5953a2c6bc7bc97c308a8e6a0fd4a8bf79997117"><code>5953a2c</code></a>
Prune pins in CI that are no longer needed. (<a
href="https://redirect.github.com/bytecodealliance/rustix/issues/1588">#1588</a>)</li>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/9116c05d2eab3484748a629e72bdff17117c4f5b"><code>9116c05</code></a>
Bump dependencies (<a
href="https://redirect.github.com/bytecodealliance/rustix/issues/1567">#1567</a>)</li>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/5ee0ca360f41b3699b7c543d1153e94c65988610"><code>5ee0ca3</code></a>
hurd: Fix l_type and l_whence types (<a
href="https://redirect.github.com/bytecodealliance/rustix/issues/1569">#1569</a>)</li>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/89505893fc3b4b9b9a22625cd3a670f6d6cf2f44"><code>8950589</code></a>
Clobber vector registers and do not use preserves_flags in riscv64
syscalls (...</li>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/7b0d2ae013976c959627598c057644ae8922708e"><code>7b0d2ae</code></a>
Update pins for MSRV compatibility (<a
href="https://redirect.github.com/bytecodealliance/rustix/issues/1585">#1585</a>)</li>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/99458d830840dafb8a8c5b8b54cf05beabc2e581"><code>99458d8</code></a>
feat(redox): <code>renameat</code> and <code>renameat_with</code> (<a
href="https://redirect.github.com/bytecodealliance/rustix/issues/1586">#1586</a>)</li>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/a9c8dcbbb74df7a7c4ec6cf50629a810bab6500d"><code>a9c8dcb</code></a>
Remove reference to yanked crate in README.md (<a
href="https://redirect.github.com/bytecodealliance/rustix/issues/1587">#1587</a>)</li>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/8bf15a0eb444087e4c3ed04e01ed488cc429af2d"><code>8bf15a0</code></a>
Drop custom makedev implementation for Redox (<a
href="https://redirect.github.com/bytecodealliance/rustix/issues/1582">#1582</a>)</li>
<li><a
href="https://github.com/bytecodealliance/rustix/commit/74b886d40d7b5209a8d448550e4595e8e06158a1"><code>74b886d</code></a>
Update pins for MSRV compatibility (<a
href="https://redirect.github.com/bytecodealliance/rustix/issues/1584">#1584</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/bytecodealliance/rustix/compare/v0.38.44...v1.1.4">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
## Summary
This PR adds a persistent, in-memory search index to the Atuin daemon,
enabling fast fuzzy search without the startup delay of building an
index each time the TUI opens.
### Key Changes
- **Daemon search service**: A new gRPC service that maintains a Nucleo
fuzzy search index in memory
- **Real-time index updates**: The daemon listens for history events
(new commands, synced records) and updates the index immediately
- **Filter mode support**: All existing filter modes work (Global, Host,
Session, Directory, Workspace)
- **New search engine**: `daemon-fuzzy` search mode that queries the
daemon instead of building a local index
- **Paged history loading**: Database pagination support for efficient
initial index loading
- **Configurable logging**: New `[logs]` settings section for daemon and
search log configuration
- **Component-based daemon architecture**: Refactored daemon internals
into a modular, event-driven system
- **Fallback to DB search for regex**: Since Nucleo doesn't support
regex matching
## Daemon Architecture
The daemon has been refactored to use a component-based, event-driven
architecture that makes it easier to add new functionality and reason
about the system.
### Core Concepts
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Atuin Daemon β
β β
β βββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Daemon β β Components β β
β β Handle ββββββΆβ β β
β β β β βββββββββββββββ βββββββββββββββ ββββββββββββββ β β
β β β’ emit() β β β History β β Search β β Sync β β β
β β β’ subscribe β β β Component β β Component β β Component β β β
β β β’ settings β β β β β β β β β β
β β β’ databases β β β gRPC serviceβ β gRPC serviceβ β background β β β
β βββββββββββββββ β β WIP history β β Nucleo indexβ β sync β β β
β β β βββββββββββββββ βββββββββββββββ ββββββββββββββ β β
β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β² β
β βΌ β β
β βββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββ β
β β Event Bus (broadcast) β β
β β β β
β β HistoryStarted β HistoryEnded β RecordsAdded β SyncCompleted β ... β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β² β
β βββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββ β
β β Control Service (gRPC) β β
β β External event injection from CLI commands β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
### DaemonHandle
A lightweight, cloneable handle that provides access to shared daemon
resources:
- **Event emission**: `handle.emit(DaemonEvent::...)` broadcasts to all
components
- **Event subscription**: `handle.subscribe()` returns a receiver for
the event bus
- **Settings**: `handle.settings()` for configuration access
- **Databases**: `handle.history_db()` and `handle.store()` for data
access
### Component Trait
Components implement a simple lifecycle:
```rust
#[async_trait]
trait Component: Send + Sync {
fn name(&self) -> &'static str;
async fn start(&mut self, handle: DaemonHandle) -> Result<()>;
async fn handle_event(&mut self, event: &DaemonEvent) -> Result<()>;
async fn stop(&mut self) -> Result<()>;
}
```
### Event-Driven Design
Components communicate via events rather than direct coupling:
| Event | Emitted By | Consumed By |
|-------|-----------|-------------|
| `HistoryStarted` | History gRPC | Search (logging) |
| `HistoryEnded` | History gRPC | Search (index update) |
| `RecordsAdded` | Sync | Search (index update) |
| `HistoryPruned` | CLI (via Control) | Search (index rebuild) |
| `HistoryDeleted` | CLI (via Control) | Search (index rebuild) |
| `ForceSync` | CLI (via Control) | Sync |
| `ShutdownRequested` | Signal handler | All (graceful shutdown) |
### External Event Injection
CLI commands can inject events into a running daemon:
```rust
// After `atuin history prune`
emit_event(DaemonEvent::HistoryPruned).await?;
// After deleting specific items
emit_event(DaemonEvent::HistoryDeleted { ids }).await?;
// Request immediate sync
emit_event(DaemonEvent::ForceSync).await?;
```
This ensures the daemon's search index stays in sync with database
changes made by CLI commands.
## Search Architecture
The search service uses a [forked version of
Nucleo](https://github.com/atuinsh/nucleo-ext) that adds filter and
scorer callbacks, enabling efficient filtering and frecency-based
ranking.
```
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Atuin Daemon β
β β
β βββββββββββββββββββ ββββββββββββββββββββββββββββββββββββ β
β β Event System βββββΆβ Search Component β β
β β β β β β
β β β’ RecordsAdded β β ββββββββββββββββββββββββββββββ β β
β β β’ HistoryEnded β β β Deduplicated Index β β β
β β β’ HistoryPruned β β β β β β
β βββββββββββββββββββ β β CommandData per command: β β β
β β β β’ Global frecency β β β
β βββββββββββββββββββ β β β’ Filter indexes (sets) β β β
β β Background Task β β β β’ Invocation history β β β
β β β β ββββββββββββββββββββββββββββββ β β
β β Rebuilds β β β β β
β β frecency map β β βΌ β β
β β every 60s βββββΆβ ββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββ β β Nucleo (forked) β β β
β β β β β β
β β β β’ Filter callback β β β
β β β β’ Scorer callback β β β
β β β β’ Fuzzy matching β β β
β β ββββββββββββββββββββββββββββββ β β
β ββββββββββββββββββββββββββββββββββββ β
β β β
β β gRPC (Unix socket) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Search TUI (Client) β
β β
β 1. Send query + filter mode + context to daemon β
β 2. Receive matching history IDs (ranked by frecency) β
β 3. Hydrate full records from local SQLite database β
β 4. Display results in TUI β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
### Nucleo Fork
The [nucleo-ext fork](https://github.com/atuinsh/nucleo-ext) adds two
key features to Nucleo:
1. **Filter callback**: Pre-filter items before fuzzy matching (used for
directory/host/session filtering)
2. **Scorer callback**: Compute custom scores after matching (used for
frecency ranking)
```rust
// Filter: only include commands run in current directory
nucleo.set_filter(Some(Arc::new(|cmd: &String| {
passing_commands.contains(cmd)
})));
// Scorer: combine fuzzy score with frecency
nucleo.set_scorer(Some(Arc::new(|cmd: &String, fuzzy_score: u32| {
let frecency = frecency_map.get(cmd).unwrap_or(0);
fuzzy_score + (frecency * 10)
})));
```
### Deduplicated Index
Commands are stored once per unique command text, with metadata tracking
all invocations:
```rust
struct CommandData {
command: String,
invocations: Vec<Invocation>, // All times this command was run
global_frecency: FrecencyData, // Precomputed frecency score
// O(1) filter indexes
directories: HashSet<String>, // All cwds where command was run
hosts: HashSet<String>, // All hostnames
sessions: HashSet<String>, // All session IDs
}
```
This deduplication means:
- **Fewer items to match**: ~13K unique commands vs ~62K history entries
- **O(1) filter checks**: HashSet lookups instead of scanning
invocations
- **Single frecency score**: Global frecency computed once, used for all
filter modes
### Frecency Scoring
Frecency (frequency + recency) scoring prioritizes recently and
frequently used commands:
```rust
fn compute_frecency(count: u32, last_used: i64, now: i64) -> u32 {
let age_hours = (now - last_used) / 3600;
// Recency: decays over time (half-life ~24 hours)
let recency = (100.0 * (-age_hours as f64 / 24.0).exp()) as u32;
// Frequency: logarithmic scaling
let frequency = (count.ln() * 20.0).min(100.0) as u32;
recency + frequency
}
```
The frecency map is:
- **Precomputed by background task** every 60 seconds
- **Never computed inline** during search (no latency impact)
- **Graceful fallback**: If unavailable, search works without frecency
ranking
### Filter Mode Implementation
| Filter Mode | Implementation |
|-------------|----------------|
| Global | No filter (all commands) |
| Directory | `command.directories.contains(cwd)` |
| Workspace | `command.directories.any(\|d\| d.starts_with(git_root))` |
| Host | `command.hosts.contains(hostname)` |
| Session | `command.sessions.contains(session_id)` |
Filters are pre-computed into a HashSet before the search, making the
filter callback O(1).
### Search Flow
1. **Daemon startup**: Loads history from SQLite in pages, builds
deduplicated index
2. **Frecency precompute**: Background task builds frecency map after
history loads
3. **Search request**: Client sends query with filter mode and context
4. **Filter**: Pre-computed HashSet determines which commands pass the
filter
5. **Match**: Nucleo fuzzy matches the query against command text
6. **Score**: Frecency scorer ranks results (fuzzy score + frecency *
10)
7. **Response**: Returns history IDs for the most recent invocation of
each matching command
8. **Hydration**: Client fetches full records from local SQLite
### Configuration
```toml
# Enable daemon + autostart
[daemon]
enabled = true
autostart = true
# Enable daemon-based fuzzy search
[search]
search_mode = "daemon-fuzzy"
```
## Performance
Performance varies based on several factors, but in most initial testing
with the new architecture shows improvement:
* **Nucleo performs searches up to 4.5x faster**: direct DB search
averages 18.07ms, but the daemon completes the same queries in 3.99ms.
* **IPC overhead is significant, but acceptable**: a significant amount
of wall-time is taken up by the transfer of data over IPC (via UDS in
this case). This averages to about ~7.8ms and accounts for 66% of
client-side wall time.
* **Tail latency improves at every layer**: p99 times correspond to
initial requests, worst-case query patterns, etc. but the average p99
daemon-based response time is 3.6x better than the associated DB-based
search p99 time
* **Query complexity no longer impacts performance**: the Nucleo-based
search shows consistent 2-7ms times regardless of query pattern. The
DB-based search had a 17x variance (3.59ms to 62.46ms).
Interestingly, @ellie - who has a larger history store than I do - gets
even better performance on the IPC layer. This could use a lot more
testing in various edge cases and on various hardware, but seems
promising.
### Regular DB search
```
Individual calls for: db_search
--------------------------------------------------------------------------------------------------------------
# Wall Busy Idle Fields
--------------------------------------------------------------------------------------------------------------
1 32.25ms 32.20ms 47.70Β΅s {"mode":"Fuzzy","query":"^"}
2 19.48ms 19.40ms 84.20Β΅s {"mode":"Fuzzy","query":"^c"}
3 20.40ms 20.10ms 297.00Β΅s {"mode":"Fuzzy","query":"^ca"}
4 13.07ms 13.00ms 69.90Β΅s {"mode":"Fuzzy","query":"^car"}
5 12.17ms 12.10ms 67.10Β΅s {"mode":"Fuzzy","query":"^carg"}
6 20.78ms 20.70ms 76.60Β΅s {"mode":"Fuzzy","query":"^cargo"}
7 9.15ms 9.10ms 53.20Β΅s {"mode":"Fuzzy","query":"^cargo "}
8 10.24ms 10.00ms 237.00Β΅s {"mode":"Fuzzy","query":"^cargo b"}
9 10.01ms 9.68ms 325.00Β΅s {"mode":"Fuzzy","query":"^cargo bu"}
10 5.89ms 5.83ms 57.20Β΅s {"mode":"Fuzzy","query":"^cargo bui"}
11 8.85ms 8.28ms 568.00Β΅s {"mode":"Fuzzy","query":"^cargo buil"}
12 7.70ms 7.49ms 212.00Β΅s {"mode":"Fuzzy","query":"^cargo build"}
13 3.59ms 3.53ms 57.00Β΅s {"mode":"Fuzzy","query":"^cargo build$"}
14 6.50ms 6.44ms 63.60Β΅s {"mode":"Fuzzy","query":"^cargo "}
15 6.48ms 6.38ms 100.00Β΅s {"mode":"Fuzzy","query":"!"}
16 31.68ms 31.60ms 75.90Β΅s {"mode":"Fuzzy","query":"!g"}
17 62.46ms 62.40ms 58.90Β΅s {"mode":"Fuzzy","query":"!gi"}
18 30.35ms 30.30ms 46.90Β΅s {"mode":"Fuzzy","query":"!git"}
19 53.84ms 53.80ms 40.80Β΅s {"mode":"Fuzzy","query":"!git "}
20 19.24ms 19.20ms 39.70Β΅s {"mode":"Fuzzy","query":"!git c"}
21 22.03ms 22.00ms 34.70Β΅s {"mode":"Fuzzy","query":"!git co"}
22 17.13ms 17.00ms 133.00Β΅s {"mode":"Fuzzy","query":"!git com"}
23 16.14ms 15.90ms 242.00Β΅s {"mode":"Fuzzy","query":"!git comm"}
24 5.11ms 5.08ms 28.60Β΅s {"mode":"Fuzzy","query":"!git commi"}
25 7.31ms 7.26ms 52.70Β΅s {"mode":"Fuzzy","query":"!git commit"}
Summary: 25 calls
Wall: avg=18.07ms, min=3.59ms, max=62.46ms, p50=13.07ms, p99=62.46ms
Busy: avg=17.95ms, min=3.53ms, max=62.40ms, p50=13.00ms, p99=62.40ms
```
### Daemon-based search
**Client**
```
Individual calls for: daemon_search
--------------------------------------------------------------------------------------------------------------
# Wall Busy Idle Fields
--------------------------------------------------------------------------------------------------------------
1 13.05ms 2.55ms 10.50ms {"query":"^"}
2 10.65ms 1.40ms 9.25ms {"query":"^c"}
3 10.72ms 1.18ms 9.54ms {"query":"^ca"}
4 5.54ms 485.00Β΅s 5.06ms {"query":"^car"}
5 15.02ms 1.02ms 14.00ms {"query":"^carg"}
6 9.49ms 840.00Β΅s 8.65ms {"query":"^cargo"}
7 5.53ms 555.00Β΅s 4.97ms {"query":"^cargo "}
8 8.56ms 717.00Β΅s 7.84ms {"query":"^cargo b"}
9 12.34ms 1.24ms 11.10ms {"query":"^cargo bu"}
10 8.38ms 650.00Β΅s 7.73ms {"query":"^cargo bui"}
11 13.07ms 770.00Β΅s 12.30ms {"query":"^cargo buil"}
12 17.11ms 709.00Β΅s 16.40ms {"query":"^cargo build"}
13 15.41ms 907.00Β΅s 14.50ms {"query":"^cargo build$"}
14 8.19ms 665.00Β΅s 7.52ms {"query":"^cargo "}
15 7.98ms 1.72ms 6.26ms {"query":"!"}
16 13.56ms 856.00Β΅s 12.70ms {"query":"!g"}
17 8.11ms 624.00Β΅s 7.49ms {"query":"!gi"}
18 14.57ms 775.00Β΅s 13.80ms {"query":"!git"}
19 14.18ms 779.00Β΅s 13.40ms {"query":"!git "}
20 9.62ms 802.00Β΅s 8.82ms {"query":"!git c"}
21 15.50ms 1.50ms 14.00ms {"query":"!git co"}
22 11.58ms 1.48ms 10.10ms {"query":"!git com"}
23 13.82ms 2.12ms 11.70ms {"query":"!git comm"}
24 17.48ms 2.18ms 15.30ms {"query":"!git commi"}
25 14.81ms 1.71ms 13.10ms {"query":"!git commit"}
Summary: 25 calls
Wall: avg=11.77ms, min=5.53ms, max=17.48ms, p50=12.34ms, p99=17.48ms
Busy: avg=1.13ms, min=485.00Β΅s, max=2.55ms, p50=856.00Β΅s, p99=2.55ms
```
**Daemon**
```
Individual calls for: daemon_search_query
--------------------------------------------------------------------------------------------------------------
# Wall Busy Idle Fields
--------------------------------------------------------------------------------------------------------------
1 1.75ms 250ns 1.75ms {"query":"^","query_id":1}
2 4.58ms 125ns 4.58ms {"query":"^c","query_id":2}
3 4.39ms 250ns 4.39ms {"query":"^ca","query_id":3}
4 2.52ms 125ns 2.52ms {"query":"^car","query_id":4}
5 4.44ms 250ns 4.44ms {"query":"^carg","query_id":5}
6 3.66ms 167ns 3.66ms {"query":"^cargo","query_id":6}
7 2.38ms 84ns 2.38ms {"query":"^cargo ","query_id":7}
8 4.13ms 84ns 4.13ms {"query":"^cargo b","query_id":8}
9 4.40ms 167ns 4.40ms {"query":"^cargo bu","query_id":9}
10 3.87ms 125ns 3.87ms {"query":"^cargo bui","query_id":10}
11 4.36ms 84ns 4.36ms {"query":"^cargo buil","query_id":11}
12 3.96ms 333ns 3.96ms {"query":"^cargo build","query_id":12}
13 4.61ms 167ns 4.61ms {"query":"^cargo build$","query_id":13}
14 4.20ms 209ns 4.20ms {"query":"^cargo ","query_id":14}
15 238.17Β΅s 167ns 238.00Β΅s {"query":"!","query_id":15}
16 4.44ms 125ns 4.44ms {"query":"!g","query_id":16}
17 3.47ms 83ns 3.47ms {"query":"!gi","query_id":17}
18 4.57ms 125ns 4.57ms {"query":"!git","query_id":18}
19 7.15ms 167ns 7.15ms {"query":"!git ","query_id":19}
20 4.27ms 250ns 4.27ms {"query":"!git c","query_id":20}
21 5.19ms 292ns 5.19ms {"query":"!git co","query_id":21}
22 4.29ms 417ns 4.29ms {"query":"!git com","query_id":22}
23 4.08ms 125ns 4.08ms {"query":"!git comm","query_id":23}
24 4.50ms 167ns 4.50ms {"query":"!git commi","query_id":24}
25 4.35ms 208ns 4.35ms {"query":"!git commit","query_id":25}
Summary: 25 calls
Wall: avg=3.99ms, min=238.17Β΅s, max=7.15ms, p50=4.29ms, p99=7.15ms
Busy: avg=182ns, min=83ns, max=417ns, p50=167ns, p99=417ns
```
**Nucleo matching time (in daemon)**
```
Individual calls for: nucleo_match
--------------------------------------------------------------------------------------------------------------
# Wall Busy Idle Fields
--------------------------------------------------------------------------------------------------------------
1 1.73ms 125ns 1.73ms {"query":"^","query_id":1}
2 4.57ms 167ns 4.57ms {"query":"^c","query_id":2}
3 4.37ms 125ns 4.37ms {"query":"^ca","query_id":3}
4 2.51ms 84ns 2.51ms {"query":"^car","query_id":4}
5 4.43ms 125ns 4.43ms {"query":"^carg","query_id":5}
6 3.64ms 125ns 3.64ms {"query":"^cargo","query_id":6}
7 2.37ms 84ns 2.37ms {"query":"^cargo ","query_id":7}
8 4.11ms 125ns 4.11ms {"query":"^cargo b","query_id":8}
9 4.36ms 208ns 4.36ms {"query":"^cargo bu","query_id":9}
10 3.85ms 125ns 3.85ms {"query":"^cargo bui","query_id":10}
11 4.35ms 125ns 4.35ms {"query":"^cargo buil","query_id":11}
12 3.94ms 250ns 3.94ms {"query":"^cargo build","query_id":12}
13 4.59ms 125ns 4.59ms {"query":"^cargo build$","query_id":13}
14 4.18ms 84ns 4.18ms {"query":"^cargo ","query_id":14}
15 220.13Β΅s 125ns 220.00Β΅s {"query":"!","query_id":15}
16 4.43ms 125ns 4.43ms {"query":"!g","query_id":16}
17 3.45ms 125ns 3.45ms {"query":"!gi","query_id":17}
18 4.55ms 125ns 4.55ms {"query":"!git","query_id":18}
19 7.12ms 209ns 7.12ms {"query":"!git ","query_id":19}
20 4.25ms 166ns 4.25ms {"query":"!git c","query_id":20}
21 5.18ms 125ns 5.18ms {"query":"!git co","query_id":21}
22 4.27ms 125ns 4.27ms {"query":"!git com","query_id":22}
23 4.06ms 292ns 4.06ms {"query":"!git comm","query_id":23}
24 4.46ms 166ns 4.46ms {"query":"!git commi","query_id":24}
25 4.31ms 208ns 4.31ms {"query":"!git commit","query_id":25}
Summary: 25 calls
Wall: avg=3.97ms, min=220.13Β΅s, max=7.12ms, p50=4.27ms, p99=7.12ms
Busy: avg=147ns, min=84ns, max=292ns, p50=125ns, p99=292ns
```
|
|
This PR refines the system created in #3178 to be suitable for a v1
release.
---
## Overview
`atuin-ai` is a separate binary that allows for generating commands and
asking questions from the command line.
It is fully opt-in.
## Usage
`atuin ai init` will output bindings for your shell. Currently, bash,
zsh, and fish are supported.
```bash
eval "$(atuin ai init)"
```
Once the hooks are installed, just press `?` on an empty prompt line to
call up the TUI.
`atuin ai` requires an account on [Atuin Hub](https://hub.atuin.sh/);
you will be prompted to log in on first use.
## Features
### Command generation
Prompt the LLM to create a command, and get one back, no fuss. Press
`enter` to run, or `tab` to insert.
```
βAsk questions or generate a command:βββββββββββββββββββββββββββ
β β
β > Get a list of running docker containers β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β $ docker ps β
β β
βββββ[Enter]: Run [Tab]: Insert [f]: Follow-up [Esc]: Cancelβ
```
### Follow-up
You can follow-up with `f` to specify a refinement prompt to update the
command that will be inserted.
```
βAsk questions or generate a command:βββββββββββββββββββββββββββ
β β
β > Get a list of running docker containers β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β $ docker ps β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β > Actually I want to get all docker containers β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β $ docker ps -a β
β β
βββββ[Enter]: Run [Tab]: Insert [f]: Follow-up [Esc]: Cancelβ
```
You can also follow-up with questions to get responses in natural
language.
```
βAsk questions or generate a command:βββββββββββββββββββββββββββ
β β
β > Get a list of running docker containers β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β $ docker ps β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β > Actually I want to get all docker containers β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β $ docker ps -a β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β > What other useful flags to `docker ps` should I know? β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Here are some handy `docker ps` flags: β
β β
β - `-q` β Only show container IDs (great for piping to β
β other commands) β
β - `-s` β Show container sizes β
β - `-n 5` β Show the last 5 created containers β
β - `-l` β Show only the latest created container β
β - `--no-trunc` β Don't truncate output (shows full IDs and β
β commands) β
β - `-f` or `--filter` β Filter by condition, e.g.: β
β - `-f status=exited` β only exited containers β
β - `-f name=myapp` β filter by name β
β - `-f ancestor=nginx` β filter by image β
β - `--format` β Custom output using Go templates, e.g.: β
β `--format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"` β
β β
β A common combo is `docker ps -aq` to get all container β
β IDs, useful for bulk operations like `docker rm $(docker β
β ps -aq)`. β
β β
βββββ[Enter]: Run [Tab]: Insert [f]: Follow-up [Esc]: Cancelβ
```
You can use `enter` or `tab` at any time to run or insert the last
suggested command, even if it was suggested in a previous turn.
### Conversational and search usage
If you prompt the LLM with a question that doesn't imply you want to
generate a command, it can respond in natural language, and use web
search if necessary to fetch the data it needs.
```
βAsk questions or generate a command:βββββββββββββββββββββββββββ
β β
β > What is the latest version of atuin? β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β β Used 2 tools β
β β
β The latest version of Atuin is **v18.12.0**, available on β
β the [GitHub releases β
β page](https://github.com/atuinsh/atuin/releases). β
β β
ββββββββββββββββββββββββββββββββββ[f]: Follow-up [Esc]: Cancelβ
```
### Dangerous or low-confidence command detection
The LLM scores its confidence in the command, as well as how dangerous
the command is. This information is shown if a threshold is exceeded,
and requires an extra confirmation step before running automatically
with `enter`.
The Atuin Hub server also monitors suggested commands for dangerous
patterns the LLM didn't catch, and appends its own assessment at the end
of the LLM's own assessment.
```
βAsk questions or generate a command:βββββββββββββββββββββββββββ
β β
β > Delete all files from $HOME β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β $ rm -rf $HOME/* β
β β
β ! β οΈ This will PERMANENTLY delete ALL files and directories β
β in your home directory, including documents, downloads, β
β configurations, SSH keys, and everything else. This is β
β irreversible and will likely break your system. Also note β
β this won't delete hidden (dot) files β if you want those β
β too, that's even more destructive.; [Server] Recursive β
β delete of critical directory β
β β
βββββ[Enter]: Run [Tab]: Insert [f]: Follow-up [Esc]: Cancelβ
```
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
|
|
Resolves breaking checks on `main`
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
- time >= 0.3.47: RUSTSEC-2026-0009
- bytes >= 1.11.1: RUSTSEC-2026-0007
Did not dive deeply in if this is actually impacting atuin, just picked
up the CVE patches that were reported in Fedora downstream.
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [x] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [x] I have checked that there are no existing pull requests for the
same thing
Supersedes #3124
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
In the [2.0.0](https://github.com/ardaku/whoami/releases/tag/v2.0.0)
series, `whoami` removed all infallible function variants, and removed
the `fallible` module, moving those functions to the root module.
Therefore, I replaced `whoami::fallible::hostname` with
`whoami::hostname` (the same function with the same signature, just
moved to the root module).
For `whoami::username`, the infallible function that `atuin` was using
before is gone, and we must add error handling. I chose to fall back to
the string `"unknown-user"` if getting the username fails, just as
`"unknown-host"` is already the fallback when getting the hostname
fails. This seemed reasonable to me, but itβs worth double-checking if
there could be any unintended consequences, especially if `unknown-user`
happens to be a real, valid username on the system. The alternatives I
can see would be to panic on failure or to amend the signature of
`get_username()` and all of its call sites with some kind of more
graceful error handling (what?).
## Checks
- [x] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [x] I have checked that there are no existing pull requests for the
same thing
|
|
<!-- Thank you for making a PR! Bug fixes are always welcome, but if
you're adding a new feature or changing an existing one, we'd really
appreciate if you open an issue, post on the forum, or drop in on
Discord -->
## Checks
- [ ] I am happy for maintainers to push small adjustments to this PR,
to speed up the review cycle
- [ ] I have checked that there are no existing pull requests for the
same thing
|