coderlib 0.1.0

A Rust library for AI-powered code assistance and agentic system
Documentation
//! Plugin manager for Edit integration
//!
//! This module manages the lifecycle of CoderLib as a plugin within
//! Microsoft Edit, handling initialization, configuration, and cleanup.

use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::{mpsc, RwLock};
use tracing::info;

use crate::core::{CoderLib, CoderLibConfig, CoderLibError};
use crate::integration::{EditHost, EditConfig, EditCommand, EditResponse, EditState};
use crate::llm::ProviderFactory;

/// Plugin state
#[derive(Debug, Clone, PartialEq)]
pub enum PluginState {
    /// Plugin is not initialized
    Uninitialized,
    /// Plugin is initializing
    Initializing,
    /// Plugin is ready and active
    Active,
    /// Plugin is paused
    Paused,
    /// Plugin encountered an error
    Error(String),
    /// Plugin is shutting down
    ShuttingDown,
}

/// Plugin manager for Edit integration
pub struct EditPluginManager {
    /// Current plugin state
    state: Arc<RwLock<PluginState>>,
    /// CoderLib instance
    coderlib: Option<Arc<CoderLib>>,
    /// Edit host integration
    edit_host: Option<Arc<EditHost>>,
    /// Configuration
    config: Option<CoderLibConfig>,
    /// Edit-specific configuration
    edit_config: EditConfig,
    /// Command receiver from Edit
    command_receiver: Option<mpsc::UnboundedReceiver<EditCommand>>,
    /// Response sender to Edit
    response_sender: Option<mpsc::UnboundedSender<EditResponse>>,
    /// Plugin directory
    plugin_dir: PathBuf,
}

/// Plugin initialization parameters
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginInitParams {
    /// Plugin configuration file path
    pub config_path: Option<PathBuf>,
    /// Edit configuration
    pub edit_config: EditConfig,
    /// Plugin directory
    pub plugin_dir: PathBuf,
    /// Initial editor state
    pub initial_state: EditState,
}

/// Plugin capabilities that Edit can query
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginCapabilities {
    /// Plugin name
    pub name: String,
    /// Plugin version
    pub version: String,
    /// Supported Edit version range
    pub edit_version_range: String,
    /// Available LLM providers
    pub providers: Vec<String>,
    /// Available tools
    pub tools: Vec<String>,
    /// Supported file types
    pub supported_file_types: Vec<String>,
    /// Required permissions
    pub required_permissions: Vec<String>,
}

impl EditPluginManager {
    /// Create a new plugin manager
    pub fn new() -> Self {
        Self {
            state: Arc::new(RwLock::new(PluginState::Uninitialized)),
            coderlib: None,
            edit_host: None,
            config: None,
            edit_config: EditConfig::default(),
            command_receiver: None,
            response_sender: None,
            plugin_dir: PathBuf::from("."),
        }
    }

    /// Get plugin capabilities
    pub fn get_capabilities() -> PluginCapabilities {
        PluginCapabilities {
            name: "CoderLib".to_string(),
            version: env!("CARGO_PKG_VERSION").to_string(),
            edit_version_range: ">=1.0.0".to_string(),
            providers: ProviderFactory::available_providers().into_iter().map(|s| s.to_string()).collect(),
            tools: vec![
                "file_read".to_string(),
                "file_write".to_string(),
                "file_list".to_string(),
                "shell_command".to_string(),
                "file_search".to_string(),
                "content_search".to_string(),
                "code_analysis".to_string(),
                "git".to_string(),
            ],
            supported_file_types: vec![
                "rust".to_string(),
                "python".to_string(),
                "javascript".to_string(),
                "typescript".to_string(),
                "go".to_string(),
                "java".to_string(),
                "c".to_string(),
                "cpp".to_string(),
                "csharp".to_string(),
                "html".to_string(),
                "css".to_string(),
                "json".to_string(),
                "yaml".to_string(),
                "toml".to_string(),
                "markdown".to_string(),
            ],
            required_permissions: vec![
                "file_read".to_string(),
                "file_write".to_string(),
                "command_execute".to_string(),
                "network_access".to_string(),
            ],
        }
    }

    /// Initialize the plugin
    pub async fn initialize(&mut self, params: PluginInitParams) -> Result<(), CoderLibError> {
        info!("Initializing CoderLib plugin for Edit");
        
        // Set state to initializing
        {
            let mut state = self.state.write().await;
            *state = PluginState::Initializing;
        }

        // Store configuration
        self.edit_config = params.edit_config.clone();
        self.plugin_dir = params.plugin_dir;

        // Load CoderLib configuration
        let config = if let Some(config_path) = params.config_path {
            CoderLibConfig::load_from_file(&config_path)?
        } else {
            // Create default configuration
            let mut config = CoderLibConfig::default();
            config.debug = false;
            config.log_level = "info".to_string();
            config
        };

        self.config = Some(config.clone());

        // Create Edit host integration
        let mut edit_host = EditHost::new(params.edit_config);
        edit_host.update_state(params.initial_state);

        // Set up communication channels
        let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
        let (resp_tx, resp_rx) = mpsc::unbounded_channel();
        
        edit_host.set_event_sender(cmd_tx);
        self.command_receiver = Some(cmd_rx);
        self.response_sender = Some(resp_tx);

        let edit_host = Arc::new(edit_host);
        self.edit_host = Some(edit_host.clone());

        // Initialize CoderLib
        let coderlib = CoderLib::new(config).await?;
        self.coderlib = Some(Arc::new(coderlib));

        // Set state to active
        {
            let mut state = self.state.write().await;
            *state = PluginState::Active;
        }

        info!("CoderLib plugin initialized successfully");
        Ok(())
    }

    /// Get current plugin state
    pub async fn get_state(&self) -> PluginState {
        self.state.read().await.clone()
    }

    /// Pause the plugin
    pub async fn pause(&mut self) -> Result<(), CoderLibError> {
        let mut state = self.state.write().await;
        if *state == PluginState::Active {
            *state = PluginState::Paused;
            info!("CoderLib plugin paused");
        }
        Ok(())
    }

    /// Resume the plugin
    pub async fn resume(&mut self) -> Result<(), CoderLibError> {
        let mut state = self.state.write().await;
        if *state == PluginState::Paused {
            *state = PluginState::Active;
            info!("CoderLib plugin resumed");
        }
        Ok(())
    }

    /// Shutdown the plugin
    pub async fn shutdown(&mut self) -> Result<(), CoderLibError> {
        info!("Shutting down CoderLib plugin");
        
        {
            let mut state = self.state.write().await;
            *state = PluginState::ShuttingDown;
        }

        // Clean up resources
        self.coderlib = None;
        self.edit_host = None;
        self.command_receiver = None;
        self.response_sender = None;

        info!("CoderLib plugin shutdown complete");
        Ok(())
    }

    /// Handle a command from Edit
    pub async fn handle_command(&self, command: EditCommand) -> Result<EditResponse, CoderLibError> {
        let state = self.get_state().await;
        if state != PluginState::Active {
            return Ok(EditResponse::Error {
                message: format!("Plugin not active (state: {:?})", state),
            });
        }

        match command {
            EditCommand::ShowAIAssistant { initial_prompt } => {
                self.handle_ai_assistant_request(initial_prompt).await
            }
            EditCommand::OpenFile { path } => {
                // Update Edit host state
                if let Some(edit_host) = &self.edit_host {
                    let mut state = edit_host.get_state();
                    state.current_file = Some(path.clone());
                    if !state.open_files.contains(&path) {
                        state.open_files.push(path);
                    }
                    edit_host.update_state(state);
                }
                Ok(EditResponse::Success)
            }
            EditCommand::CloseFile { path } => {
                // Update Edit host state
                if let Some(edit_host) = &self.edit_host {
                    let mut state = edit_host.get_state();
                    state.open_files.retain(|f| f != &path);
                    if state.current_file.as_ref() == Some(&path) {
                        state.current_file = state.open_files.first().cloned();
                    }
                    edit_host.update_state(state);
                }
                Ok(EditResponse::Success)
            }
            EditCommand::MoveCursor { position } => {
                // Update Edit host state
                if let Some(edit_host) = &self.edit_host {
                    let mut state = edit_host.get_state();
                    state.cursor_position = position;
                    edit_host.update_state(state);
                }
                Ok(EditResponse::Success)
            }
            EditCommand::SetSelection { range } => {
                // Update Edit host state
                if let Some(edit_host) = &self.edit_host {
                    let mut state = edit_host.get_state();
                    state.selection = Some(range);
                    edit_host.update_state(state);
                }
                Ok(EditResponse::Success)
            }
            _ => {
                // For other commands, just acknowledge
                Ok(EditResponse::Success)
            }
        }
    }

    /// Handle AI assistant request
    async fn handle_ai_assistant_request(&self, initial_prompt: Option<String>) -> Result<EditResponse, CoderLibError> {
        if let (Some(_coderlib), Some(edit_host)) = (&self.coderlib, &self.edit_host) {
            // Gather context
            let context = edit_host.gather_intelligent_context().await?;

            // Prepare the prompt
            let _prompt = if let Some(initial) = initial_prompt {
                format!("{}\n\nContext:\n{}", initial, context)
            } else {
                format!("How can I help you with your code?\n\nContext:\n{}", context)
            };

            // This would typically trigger the AI assistant UI
            // For now, we'll just return success
            Ok(EditResponse::Success)
        } else {
            Ok(EditResponse::Error {
                message: "CoderLib not properly initialized".to_string(),
            })
        }
    }

    /// Get the CoderLib instance
    pub fn get_coderlib(&self) -> Option<Arc<CoderLib>> {
        self.coderlib.clone()
    }

    /// Get the Edit host
    pub fn get_edit_host(&self) -> Option<Arc<EditHost>> {
        self.edit_host.clone()
    }

    /// Update editor state
    pub async fn update_editor_state(&self, state: EditState) -> Result<(), CoderLibError> {
        if let Some(edit_host) = &self.edit_host {
            edit_host.update_state(state);
        }
        Ok(())
    }
}

impl Default for EditPluginManager {
    fn default() -> Self {
        Self::new()
    }
}