diff options
| author | Michelle Tilley <michelle@michelletilley.net> | 2026-04-23 13:29:58 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-23 13:29:58 -0700 |
| commit | b121b73d07df389d324b3a8f27066661a6609618 (patch) | |
| tree | f66b69655b6d425ff642200224b3b2a1fdcd0df8 /crates/atuin-ai/src/user_context/mod.rs | |
| parent | fix: minor issues with fish's vim mode(s) (#3362) (diff) | |
| download | atuin-b121b73d07df389d324b3a8f27066661a6609618.zip | |
feat: Send user-defined context with `TERMINAL.md` (#3443)
This PR adds the ability to inject user-defined content into Atuin AI
requests, a la `AGENTS.md` or `CLAUDE.md`.
* `.atuin/TERMINAL.md` (or alternatively just `TERMINAL.md`) is checked in every directory from the cwd up to the root
* `~/.config/atuin/TERMINAL.md` (or equivalent config dir) is also
checked
* Supports Claude-style ``` !`` ``` and ```` ```!...``` ```` style shell
interpolation
Diffstat (limited to 'crates/atuin-ai/src/user_context/mod.rs')
| -rw-r--r-- | crates/atuin-ai/src/user_context/mod.rs | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/crates/atuin-ai/src/user_context/mod.rs b/crates/atuin-ai/src/user_context/mod.rs new file mode 100644 index 00000000..295efdec --- /dev/null +++ b/crates/atuin-ai/src/user_context/mod.rs @@ -0,0 +1,68 @@ +//! User-authored context files (`TERMINAL.md`). +//! +//! Context files are markdown documents that can embed shell commands for +//! dynamic content. Before each API request, context files are discovered +//! by walking the filesystem, commands are executed, and the interpolated +//! content is sent to the server as `config.user_contexts`. + +mod interpolate; +mod walker; + +use std::path::Path; + +pub(crate) use walker::global_context_path; + +/// A fully resolved user context, ready to include in an API request. +#[derive(Debug, Clone, serde::Serialize)] +pub(crate) struct UserContext { + /// The path to the context file on disk. + pub path: String, + /// The interpolated content. + pub data: String, +} + +/// Discover context files and interpolate embedded commands. +/// +/// Walks from `start` up to the filesystem root looking for +/// `.atuin/ai-context.md`, then checks `global_path`. Returns contexts +/// ordered from most general (global/root) to most specific (deepest). +pub(crate) async fn gather( + start: &Path, + global_path: Option<&Path>, + shell: &str, +) -> Vec<UserContext> { + let raw_files = match walker::walk(start, global_path).await { + Ok(files) => files, + Err(e) => { + tracing::warn!("Failed to walk for context files: {e}"); + return Vec::new(); + } + }; + + if raw_files.is_empty() { + return Vec::new(); + } + + // Interpolate all files in parallel. + let mut handles = Vec::with_capacity(raw_files.len()); + for file in raw_files { + let shell = shell.to_string(); + handles.push(tokio::spawn(async move { + let data = interpolate::interpolate(&file.content, &shell).await; + UserContext { + path: file.path.to_string_lossy().to_string(), + data, + } + })); + } + + let mut contexts = Vec::with_capacity(handles.len()); + for handle in handles { + match handle.await { + Ok(ctx) => contexts.push(ctx), + Err(e) => tracing::warn!("Context interpolation task failed: {e}"), + } + } + + contexts +} |
