smart-tree 8.0.0

Smart Tree - An intelligent, AI-friendly directory visualization tool
Documentation
//! File History Tracking Module - The Ultimate Context-Driven System!
//!
//! Tracks all AI file manipulations in ~/.mem8/.filehistory/
//! with hash-based change detection and append-first operations.
//!
//! 🎸 The Cheet says: "Every file tells a story, let's remember them all!"

use anyhow::Result;
use chrono::{DateTime, Datelike, Timelike, Utc};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::fs::{self, OpenOptions};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};

pub mod operations;
pub mod tracker;

pub use operations::*;
pub use tracker::*;

/// File history configuration
pub struct FileHistoryConfig {
    /// Base directory for file history (default: ~/.mem8/.filehistory/)
    pub base_dir: PathBuf,
    /// Whether to auto-create directories
    pub auto_create: bool,
    /// Prefer append operations when possible
    pub prefer_append: bool,
}

impl Default for FileHistoryConfig {
    fn default() -> Self {
        let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
        Self {
            base_dir: cwd.join(".st").join("filehistory"),
            auto_create: true,
            prefer_append: true,
        }
    }
}

/// Get timestamp at 10-minute resolution
pub fn get_time_bucket() -> (String, u64) {
    let now = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs();

    let datetime = DateTime::<Utc>::from(UNIX_EPOCH + std::time::Duration::from_secs(now));
    let minute = datetime.minute() / 10 * 10; // Round down to 10-minute bucket

    let filename = format!(
        "{:04}{:02}{:02}_{:02}{:02}",
        datetime.year(),
        datetime.month(),
        datetime.day(),
        datetime.hour(),
        minute
    );

    (filename, now)
}

/// Calculate SHA256 hash of file contents
pub fn hash_file(path: &Path) -> Result<String> {
    let contents = fs::read(path)?;
    let mut hasher = Sha256::new();
    hasher.update(&contents);
    Ok(format!("{:x}", hasher.finalize()))
}

/// Get project ID from path (uses canonical path as ID)
pub fn get_project_id(path: &Path) -> Result<String> {
    let canonical = path.canonicalize()?;
    let mut hasher = Sha256::new();
    hasher.update(canonical.to_string_lossy().as_bytes());
    Ok(format!("{:x}", hasher.finalize())[..16].to_string())
}