lwc 0.15.2

Agent-driven proactive memory CLI for AI agents — autonomously recall, maintain, and evolve persistent, source-grounded knowledge across sessions.
use rusqlite::{Error as SqliteError, ErrorCode};
use serde_json::{Value, json};
use std::fmt;

pub type Result<T> = std::result::Result<T, AppError>;

#[derive(Debug)]
pub struct AppError {
    pub code: &'static str,
    pub message: String,
    pub details: Option<Value>,
}

impl AppError {
    pub fn new(code: &'static str, message: impl Into<String>) -> Self {
        Self {
            code,
            message: message.into(),
            details: None,
        }
    }

    pub fn with_details(mut self, details: Value) -> Self {
        self.details = Some(details);
        self
    }
}

impl fmt::Display for AppError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str(&self.message)
    }
}

impl std::error::Error for AppError {}

impl From<std::io::Error> for AppError {
    fn from(error: std::io::Error) -> Self {
        Self::new("io_error", error.to_string())
    }
}

impl From<rusqlite::Error> for AppError {
    fn from(error: rusqlite::Error) -> Self {
        if matches!(
            &error,
            SqliteError::SqliteFailure(inner, _)
                if matches!(inner.code, ErrorCode::DatabaseBusy | ErrorCode::DatabaseLocked)
        ) {
            return Self::new(
                "database_busy",
                "another short Wiki write is active; retry this exact operation",
            )
            .with_details(json!({
                "retryable": true,
                "retry_after_ms": 100,
                "work_command": "lwc work list",
            }));
        }
        Self::new("database_error", error.to_string())
    }
}