ccswarm 0.9.2

AI Agent Workflow DevOps toolchain complementing Claude Code Agent Teams
Documentation
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use tokio::fs;
use tokio::io::AsyncWriteExt;

use super::context::SessionContext;
use super::types::ExecutionSessionId;

pub struct PersistenceManager {
    storage_path: PathBuf,
}

impl PersistenceManager {
    pub fn new(storage_path: PathBuf) -> Self {
        Self { storage_path }
    }

    pub async fn save_session(
        &self,
        session_id: &ExecutionSessionId,
        state: &SessionState,
    ) -> Result<()> {
        let session_dir = self.storage_path.join(session_id.to_string());
        fs::create_dir_all(&session_dir).await?;
        let state_file = session_dir.join("state.json");
        let data = serde_json::to_vec_pretty(state)?;
        let mut file = fs::File::create(&state_file).await?;
        file.write_all(&data).await?;
        file.sync_all().await?;
        Ok(())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionState {
    pub session_id: ExecutionSessionId,
    pub status: ExecutionSessionStatus,
    pub context: SessionContext,
    #[serde(default)]
    pub command_history: Vec<CommandRecord>,
    pub metadata: SessionMetadata,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum ExecutionSessionStatus {
    #[default]
    Running,
    Completed,
    Error,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommandRecord {
    pub command: String,
    pub timestamp: chrono::DateTime<chrono::Utc>,
    pub exit_code: Option<i32>,
    pub output_preview: String,
    pub duration_ms: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionMetadata {
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub last_accessed: chrono::DateTime<chrono::Utc>,
    pub total_tokens: usize,
    pub custom: HashMap<String, serde_json::Value>,
}

impl Default for SessionMetadata {
    fn default() -> Self {
        let now = chrono::Utc::now();
        Self {
            created_at: now,
            last_accessed: now,
            total_tokens: 0,
            custom: HashMap::new(),
        }
    }
}