Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ atuin-server-sqlite SQLite implementation (sqlx)

- Rust 2024 edition, toolchain 1.93.1.
- Errors: `eyre::Result` in binaries, `thiserror` for typed errors in libraries.
- Derive boilerplate: `derive_more` (workspace dep) for `Display`, `From`, `Into`, `AsRef`, `Deref`, `Debug` on newtypes and simple enums. Prefer `derive_more` over manual `impl` when the formatting/conversion is a straight delegation. Use `thiserror` (not `derive_more`) for error types. Use `#[as_ref(forward)]` on string newtypes for `AsRef<str>`.
- Async: tokio. Client uses `current_thread`; server uses `multi_thread`.
- `#![deny(unsafe_code)]` on client/common, `#![forbid(unsafe_code)]` on server.
- Clippy: `pedantic` + `nursery` on main crate. CI enforces `-D warnings -D clippy::redundant_clone`.
Expand Down
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ time = { version = "0.3.47", features = [
] }
clap = { version = "4.5.7", features = ["derive"] }
config = { version = "0.15.8", default-features = false, features = ["toml"] }
derive_more = { version = "2", features = ["full"] }
directories = "6.0.0"
eyre = "0.6"
fs-err = "3.1"
Expand Down
1 change: 1 addition & 0 deletions crates/atuin-ai/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tree-sitter = ["dep:tree-sitter-lib", "dep:tree-sitter-bash", "dep:tree-sitter-f
[dependencies]
async-trait = { workspace = true }
atuin-client = { workspace = true }
derive_more = { workspace = true }
atuin-common = { workspace = true }
atuin-daemon = { workspace = true }
tokio = { workspace = true }
Expand Down
5 changes: 1 addition & 4 deletions crates/atuin-ai/src/context_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@ const FROZEN_PREFIX_TURNS: usize = 1;

/// Builds API messages from conversation events while respecting a character
/// budget using frozen prefix + live tail truncation.
#[derive(derive_more::Constructor)]
pub(crate) struct ContextWindowBuilder {
budget: usize,
}

impl ContextWindowBuilder {
pub fn new(budget: usize) -> Self {
Self { budget }
}

pub fn with_default_budget() -> Self {
Self::new(DEFAULT_BUDGET_CHARS)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/atuin-ai/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl ViewState {
}

pub fn is_busy(&self) -> bool {
matches!(self.agent_state, AgentState::Turn { .. })
self.agent_state.is_turn()
}

pub fn has_confirmation(&self) -> bool {
Expand Down
6 changes: 3 additions & 3 deletions crates/atuin-ai/src/fsm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use tools::{ToolManager, ToolState};
// ============================================================================

/// The discrete states of the agent FSM.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, derive_more::IsVariant)]
pub(crate) enum AgentState {
/// Waiting for user input.
Idle {
Expand All @@ -44,7 +44,7 @@ pub(crate) enum AgentState {
}

/// Stream connection lifecycle within a Turn.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, derive_more::IsVariant)]
pub(crate) enum StreamPhase {
/// Request sent, awaiting first stream frame.
Connecting,
Expand Down Expand Up @@ -541,7 +541,7 @@ impl AgentFsm {
let mut effects = Vec::new();

// Abort stream if still active
if !matches!(stream, StreamPhase::Done) {
if !stream.is_done() {
Comment thread
markovejnovic marked this conversation as resolved.
Outdated
effects.push(Effect::AbortStream);
}

Expand Down
4 changes: 2 additions & 2 deletions crates/atuin-ai/src/fsm/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub(crate) enum InterruptReason {
}

/// Per-tool lifecycle state.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, derive_more::IsVariant)]
pub(crate) enum ToolState {
/// Permission resolver is running asynchronously.
CheckingPermission,
Expand Down Expand Up @@ -62,7 +62,7 @@ pub(crate) struct TrackedTool {
impl TrackedTool {
/// Whether this tool has reached a terminal state.
pub fn is_resolved(&self) -> bool {
matches!(self.state, ToolState::Completed | ToolState::Denied)
self.state.is_completed() || self.state.is_denied()
Comment thread
markovejnovic marked this conversation as resolved.
Outdated
}

/// Extract shell preview data (for TurnBuilder compatibility).
Expand Down
12 changes: 2 additions & 10 deletions crates/atuin-ai/src/permissions/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,23 @@ use eyre::Result;

use crate::{permissions::file::RuleFile, tools::PermissibleToolCall};

#[derive(derive_more::Constructor)]
Comment thread
markovejnovic marked this conversation as resolved.
Outdated
pub(crate) struct PermissionRequest<'t> {
call: &'t (dyn PermissibleToolCall + Send + Sync),
}

impl<'t> PermissionRequest<'t> {
pub fn new(call: &'t (dyn PermissibleToolCall + Send + Sync)) -> Self {
Self { call }
}
}

pub(crate) enum PermissionResponse {
Allowed,
Denied,
Ask,
}

#[derive(derive_more::Constructor)]
pub(crate) struct PermissionChecker {
files: Vec<RuleFile>,
}

impl PermissionChecker {
pub fn new(files: Vec<RuleFile>) -> Self {
Self { files }
}

pub async fn check<'t>(
&self,
request: &'t PermissionRequest<'t>,
Expand Down
4 changes: 2 additions & 2 deletions crates/atuin-ai/src/tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub(crate) struct ToolPreview {
}

/// A tool call from the server, with parsed input parameters.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, derive_more::From, derive_more::IsVariant)]
pub(crate) enum ClientToolCall {
Read(ReadToolCall),
Edit(EditToolCall),
Expand Down Expand Up @@ -295,7 +295,7 @@ impl PermissibleToolCall for ClientToolCall {
/// Returns true if this tool call should bypass the permission system entirely.
impl ClientToolCall {
pub(crate) fn is_auto_approved(&self) -> bool {
matches!(self, ClientToolCall::LoadSkill(_))
self.is_load_skill()
}
}

Expand Down
6 changes: 1 addition & 5 deletions crates/atuin-ai/src/tui/view/turn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ pub(crate) enum ToolGroupKind {
///
/// Each variant carries the data a per-tool renderer component needs.
/// Built by TurnBuilder from ToolTracker + ConversationEvent data.
#[derive(Debug)]
#[derive(Debug, derive_more::IsVariant)]
pub(crate) enum ToolRenderData {
/// Shell command with live/cached VT100 output preview.
Shell {
Expand Down Expand Up @@ -158,10 +158,6 @@ pub(crate) enum ToolRenderData {
}

impl ToolRenderData {
pub(crate) fn is_remote(&self) -> bool {
matches!(self, ToolRenderData::Remote)
}

/// The group kind this tool should collapse into, if any.
///
/// Returns `None` for tools that render as individual `UiEvent::ToolCall`s
Expand Down
1 change: 1 addition & 0 deletions crates/atuin-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ check-update = []

[dependencies]
atuin-common = { path = "../atuin-common", version = "18.16.1" }
derive_more = { workspace = true }

log = { workspace = true }
base64 = { workspace = true }
Expand Down
17 changes: 2 additions & 15 deletions crates/atuin-client/src/history.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use core::fmt::Formatter;
use rmp::decode::DecodeStringError;
use rmp::decode::ValueReadError;
use rmp::{Marker, decode::Bytes};
use std::env;
use std::fmt::Display;

use atuin_common::record::DecryptedData;
use atuin_common::utils::uuid_v7;
Expand Down Expand Up @@ -45,21 +43,10 @@ pub const HISTORY_TAG: &str = "history";
const HISTORY_AUTHOR_ENV: &str = "ATUIN_HISTORY_AUTHOR";
const HISTORY_INTENT_ENV: &str = "ATUIN_HISTORY_INTENT";

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[derive(Clone, Debug, Eq, PartialEq, Hash, derive_more::Display, derive_more::From)]
#[display("{_0}")]
pub struct HistoryId(pub String);

impl Display for HistoryId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

impl From<String> for HistoryId {
fn from(s: String) -> Self {
Self(s)
}
}

/// Client-side history entry.
///
/// Client stores data unencrypted, and only encrypts it before sending to the server.
Expand Down
10 changes: 1 addition & 9 deletions crates/atuin-client/src/history/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use atuin_common::record::{DecryptedData, Host, HostId, Record, RecordId, Record

use super::{HISTORY_TAG, HISTORY_VERSION, HISTORY_VERSION_V0, History, HistoryId};

#[derive(Debug, Clone)]
#[derive(Debug, Clone, derive_more::Constructor)]
pub struct HistoryStore {
pub store: SqliteStore,
pub host_id: HostId,
Expand Down Expand Up @@ -109,14 +109,6 @@ impl HistoryRecord {
}

impl HistoryStore {
pub fn new(store: SqliteStore, host_id: HostId, encryption_key: [u8; 32]) -> Self {
HistoryStore {
store,
host_id,
encryption_key,
}
}

async fn push_record(&self, record: HistoryRecord) -> Result<(RecordId, RecordIdx)> {
let bytes = record.serialize()?;
let idx = self
Expand Down
20 changes: 7 additions & 13 deletions crates/atuin-client/src/settings.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::HashMap, fmt, io::prelude::*, path::PathBuf, str::FromStr, sync::OnceLock};
use std::{collections::HashMap, io::prelude::*, path::PathBuf, str::FromStr, sync::OnceLock};
use tokio::sync::OnceCell;

use atuin_common::record::HostId;
Expand Down Expand Up @@ -29,6 +29,8 @@ pub(crate) mod meta;
mod scripts;
pub mod watcher;

#[derive(derive_more::AsRef)]
#[as_ref(forward)]
Comment thread
markovejnovic marked this conversation as resolved.
Outdated
pub struct HubEndpoint(String);

/// Default sync address for Atuin's hosted service
Expand All @@ -43,12 +45,6 @@ impl Default for HubEndpoint {
}
}

impl AsRef<str> for HubEndpoint {
fn as_ref(&self) -> &str {
&self.0
}
}

#[derive(Clone, Debug, Deserialize, Copy, ValueEnum, PartialEq, Serialize)]
pub enum SearchMode {
#[serde(rename = "prefix")]
Expand Down Expand Up @@ -164,13 +160,11 @@ impl From<Dialect> for interim::Dialect {
/// multithreaded runtime, otherwise it will fail on most Unix systems.
///
/// See: <https://github.com/atuinsh/atuin/pull/1517#discussion_r1447516426>
#[derive(Clone, Copy, Debug, Eq, PartialEq, DeserializeFromStr, Serialize)]
#[derive(
Clone, Copy, Debug, Eq, PartialEq, DeserializeFromStr, Serialize, derive_more::Display,
)]
#[display("{_0}")]
pub struct Timezone(pub UtcOffset);
impl fmt::Display for Timezone {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
/// format: <+|-><hour>[:<minute>[:<second>]]
static OFFSET_FMT: &[FormatItem<'_>] = format_description!(
"[offset_hour sign:mandatory padding:none][optional [:[offset_minute padding:none][optional [:[offset_second padding:none]]]]]"
Expand Down
Loading
Loading