aboutsummaryrefslogtreecommitdiffstats
path: root/crates/atuin-client/src/database.rs
diff options
context:
space:
mode:
authorEllie Huxtable <ellie@atuin.sh>2026-04-11 05:26:52 +0100
committerGitHub <noreply@github.com>2026-04-11 05:26:52 +0100
commit4d81ec537f91ebed0d5498a36596a516dbf7d26b (patch)
tree01c9ed9f3e3e6ecf61461c99fd8fd6998c26a8c3 /crates/atuin-client/src/database.rs
parentfix: ensure daemon is running (#3384) (diff)
downloadatuin-4d81ec537f91ebed0d5498a36596a516dbf7d26b.zip
feat: track coding agent shell usage (#3388)
https://github.com/user-attachments/assets/7868c7a4-6a91-4c93-ac6a-e8665cf1f799 ## 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
Diffstat (limited to '')
-rw-r--r--crates/atuin-client/src/database.rs39
1 files changed, 39 insertions, 0 deletions
diff --git a/crates/atuin-client/src/database.rs b/crates/atuin-client/src/database.rs
index 7c63368d..75ef51c3 100644
--- a/crates/atuin-client/src/database.rs
+++ b/crates/atuin-client/src/database.rs
@@ -5,6 +5,7 @@ use std::{
time::Duration,
};
+use crate::history::{AUTHOR_FILTER_ALL_AGENT, AUTHOR_FILTER_ALL_USER, KNOWN_AGENTS};
use async_trait::async_trait;
use atuin_common::utils;
use fs_err as fs;
@@ -53,6 +54,8 @@ pub struct OptFilters {
pub offset: Option<i64>,
pub reverse: bool,
pub include_duplicates: bool,
+ /// Author filter. Supports special values `$all-user` and `$all-agent`.
+ pub authors: Vec<String>,
}
pub async fn current_context() -> eyre::Result<Context> {
@@ -85,6 +88,38 @@ impl Context {
}
}
+/// Each entry is OR'd: `$all-user` → NOT IN agents, `$all-agent` → IN agents, literal → exact match.
+fn apply_author_filter(sql: &mut SqlBuilder, authors: &[String]) {
+ let mut conditions: Vec<String> = Vec::new();
+ let agent_list: String = KNOWN_AGENTS.iter().map(quote).join(", ");
+ let author_expr = "CASE \
+ WHEN author IS NULL OR trim(author) = '' THEN \
+ CASE \
+ WHEN instr(hostname, ':') > 0 THEN substr(hostname, instr(hostname, ':') + 1) \
+ ELSE hostname \
+ END \
+ ELSE author \
+ END";
+
+ for author in authors {
+ match author.as_str() {
+ AUTHOR_FILTER_ALL_USER => {
+ conditions.push(format!("{author_expr} NOT IN ({agent_list})"));
+ }
+ AUTHOR_FILTER_ALL_AGENT => {
+ conditions.push(format!("{author_expr} IN ({agent_list})"));
+ }
+ literal => {
+ conditions.push(format!("{author_expr} = {}", quote(literal)));
+ }
+ }
+ }
+
+ if !conditions.is_empty() {
+ sql.and_where(format!("({})", conditions.join(" OR ")));
+ }
+}
+
fn get_session_start_time(session_id: &str) -> Option<i64> {
if let Ok(uuid) = Uuid::parse_str(session_id)
&& let Some(timestamp) = uuid.get_timestamp()
@@ -595,6 +630,10 @@ impl Database for Sqlite {
.map(|after| sql.and_where_gt("timestamp", quote(after.unix_timestamp_nanos() as i64)))
});
+ if !filter_options.authors.is_empty() {
+ apply_author_filter(&mut sql, &filter_options.authors);
+ }
+
sql.and_where_is_null("deleted_at");
let query = sql.sql().expect("bug in search query. please report");