bhcli

A TUI for chatting on LE PHP Chats
git clone https://git.dasho.dev/bhcli.git
Log | Files | Refs | README

ai.rs (27499B)


      1 use crate::ai_service::AIService;
      2 use crate::chatops::{ChatCommand, ChatOpError, ChatOpResult, CommandContext};
      3 use std::sync::Arc;
      4 use tokio::runtime::Runtime;
      5 
      6 /// AI-powered message summarization
      7 pub struct SummarizeCommand {
      8    ai_service: Arc<AIService>,
      9    runtime: Arc<Runtime>,
     10 }
     11 
     12 impl SummarizeCommand {
     13    pub fn new(ai_service: Arc<AIService>, runtime: Arc<Runtime>) -> Self {
     14        Self {
     15            ai_service,
     16            runtime,
     17        }
     18    }
     19 }
     20 
     21 impl ChatCommand for SummarizeCommand {
     22    fn name(&self) -> &'static str {
     23        "summarize"
     24    }
     25    fn description(&self) -> &'static str {
     26        "Summarize recent chat activity (AI)"
     27    }
     28    fn usage(&self) -> &'static str {
     29        "/summarize [count]"
     30    }
     31    fn aliases(&self) -> Vec<&'static str> {
     32        vec!["tldr", "summary"]
     33    }
     34 
     35    fn execute(
     36        &self,
     37        args: Vec<String>,
     38        _context: &CommandContext,
     39    ) -> Result<ChatOpResult, ChatOpError> {
     40        if !self.ai_service.is_available() {
     41            return Ok(ChatOpResult::Error(
     42                "AI service not configured. Please check OPENAI_API_KEY environment variable."
     43                    .to_string(),
     44            ));
     45        }
     46 
     47        // Test if AI is actually functional (not just configured)
     48        let ai_service = Arc::clone(&self.ai_service);
     49        let is_functional = self
     50            .runtime
     51            .block_on(async move { ai_service.is_functional().await });
     52 
     53        if !is_functional {
     54            return Ok(ChatOpResult::Error(
     55                "AI service temporarily unavailable. This might be due to:\n\
     56                • API quota exceeded\n\
     57                • Billing issues\n\
     58                • Service outage\n\
     59                Please try again later or contact an admin."
     60                    .to_string(),
     61            ));
     62        }
     63 
     64        let count = if !args.is_empty() {
     65            args[0].parse::<usize>().unwrap_or(50).min(200)
     66        } else {
     67            50
     68        };
     69 
     70        let ai_service = Arc::clone(&self.ai_service);
     71        match self
     72            .runtime
     73            .block_on(async move { ai_service.summarize_chat(Some(count)).await })
     74        {
     75            Some(summary) => {
     76                let mut result = vec![
     77                    "🤖 **Chat Summary**".to_string(),
     78                    "".to_string(),
     79                    format!("**Overview:** {}", summary.summary),
     80                    "".to_string(),
     81                ];
     82 
     83                if !summary.key_points.is_empty() {
     84                    result.push("**Key Points:**".to_string());
     85                    for point in summary.key_points {
     86                        result.push(format!("• {}", point));
     87                    }
     88                    result.push("".to_string());
     89                }
     90 
     91                if !summary.participants.is_empty() {
     92                    result.push(format!(
     93                        "**Active Participants:** {}",
     94                        summary.participants.join(", ")
     95                    ));
     96                }
     97 
     98                if !summary.topics.is_empty() {
     99                    result.push(format!(
    100                        "**Topics Discussed:** {}",
    101                        summary.topics.join(", ")
    102                    ));
    103                }
    104 
    105                result.push(format!("**Overall Mood:** {}", summary.sentiment_overview));
    106                result.push(format!("*(Analyzed {} recent messages)*", count));
    107 
    108                Ok(ChatOpResult::Block(result))
    109            }
    110            None => Ok(ChatOpResult::Error(
    111                "Failed to generate summary. Please try again.".to_string(),
    112            )),
    113        }
    114    }
    115 }
    116 
    117 /// Language detection and translation command
    118 pub struct TranslateCommand {
    119    ai_service: Arc<AIService>,
    120    runtime: Arc<Runtime>,
    121 }
    122 
    123 impl TranslateCommand {
    124    pub fn new(ai_service: Arc<AIService>, runtime: Arc<Runtime>) -> Self {
    125        Self {
    126            ai_service,
    127            runtime,
    128        }
    129    }
    130 }
    131 
    132 impl ChatCommand for TranslateCommand {
    133    fn name(&self) -> &'static str {
    134        "translate"
    135    }
    136    fn description(&self) -> &'static str {
    137        "Translate text to another language"
    138    }
    139    fn usage(&self) -> &'static str {
    140        "/translate <target_lang> <text>"
    141    }
    142    fn aliases(&self) -> Vec<&'static str> {
    143        vec!["tr"]
    144    }
    145 
    146    fn execute(
    147        &self,
    148        args: Vec<String>,
    149        _context: &CommandContext,
    150    ) -> Result<ChatOpResult, ChatOpError> {
    151        if args.len() < 2 {
    152            return Err(ChatOpError::MissingArguments(
    153                "Usage: /translate <language> <text>".to_string(),
    154            ));
    155        }
    156 
    157        let target_lang = &args[0];
    158        let text = args[1..].join(" ");
    159 
    160        // Try AI translation first if available
    161        if self.ai_service.is_available() {
    162            // Check if AI is functional
    163            let ai_service_check = Arc::clone(&self.ai_service);
    164            let is_functional = self
    165                .runtime
    166                .block_on(async move { ai_service_check.is_functional().await });
    167 
    168            if is_functional {
    169                let ai_service = Arc::clone(&self.ai_service);
    170                let target_lang_clone = target_lang.to_string();
    171                let text_clone = text.clone();
    172 
    173                match self.runtime.block_on(async move {
    174                    ai_service
    175                        .translate_text(&text_clone, &target_lang_clone)
    176                        .await
    177                }) {
    178                    Some(translated) => {
    179                        return Ok(ChatOpResult::Message(format!(
    180                            "🌐 **Translation to {}:**\n{}",
    181                            target_lang, translated
    182                        )));
    183                    }
    184                    None => {
    185                        // Fall through to basic translation
    186                    }
    187                }
    188            }
    189        }
    190 
    191        // Fallback to system translator
    192        match std::process::Command::new("trans")
    193            .arg("-b")
    194            .arg("-t")
    195            .arg(target_lang)
    196            .arg(&text)
    197            .output()
    198        {
    199            Ok(output) => {
    200                if output.status.success() {
    201                    let translated = String::from_utf8_lossy(&output.stdout);
    202                    Ok(ChatOpResult::Message(format!(
    203                        "🌐 **Translation to {}:**\n{}",
    204                        target_lang,
    205                        translated.trim()
    206                    )))
    207                } else {
    208                    let encoded_text = text.replace(" ", "%20");
    209                    Ok(ChatOpResult::Message(format!("🌐 Translation failed. Try: https://translate.google.com/?sl=auto&tl={}&text={}", target_lang, encoded_text)))
    210                }
    211            }
    212            Err(_) => {
    213                let encoded_text = text.replace(" ", "%20");
    214                Ok(ChatOpResult::Message(format!("🌐 System translator unavailable. Try: https://translate.google.com/?sl=auto&tl={}&text={}", target_lang, encoded_text)))
    215            }
    216        }
    217    }
    218 }
    219 
    220 /// Language detection command
    221 pub struct DetectCommand {
    222    ai_service: Arc<AIService>,
    223    runtime: Arc<Runtime>,
    224 }
    225 
    226 impl DetectCommand {
    227    pub fn new(ai_service: Arc<AIService>, runtime: Arc<Runtime>) -> Self {
    228        Self {
    229            ai_service,
    230            runtime,
    231        }
    232    }
    233 }
    234 
    235 impl ChatCommand for DetectCommand {
    236    fn name(&self) -> &'static str {
    237        "detect"
    238    }
    239    fn description(&self) -> &'static str {
    240        "Detect the language of text"
    241    }
    242    fn usage(&self) -> &'static str {
    243        "/detect <text>"
    244    }
    245    fn aliases(&self) -> Vec<&'static str> {
    246        vec!["lang"]
    247    }
    248 
    249    fn execute(
    250        &self,
    251        args: Vec<String>,
    252        _context: &CommandContext,
    253    ) -> Result<ChatOpResult, ChatOpError> {
    254        if args.is_empty() {
    255            return Err(ChatOpError::MissingArguments(
    256                "Please specify text to analyze".to_string(),
    257            ));
    258        }
    259 
    260        let text = args.join(" ");
    261 
    262        if self.ai_service.is_available() {
    263            // Check if AI is functional
    264            let ai_service_check = Arc::clone(&self.ai_service);
    265            let is_functional = self
    266                .runtime
    267                .block_on(async move { ai_service_check.is_functional().await });
    268 
    269            if !is_functional {
    270                return Ok(ChatOpResult::Error(
    271                    "AI service temporarily unavailable. Falling back to basic detection."
    272                        .to_string(),
    273                ));
    274            }
    275 
    276            let ai_service = Arc::clone(&self.ai_service);
    277            let text_clone = text.clone();
    278            match self
    279                .runtime
    280                .block_on(async move { ai_service.detect_language(&text_clone).await })
    281            {
    282                Some(detection) => {
    283                    let confidence_emoji = if detection.confidence > 0.8 {
    284                        "🎯"
    285                    } else if detection.confidence > 0.6 {
    286                        "🎲"
    287                    } else {
    288                        "❓"
    289                    };
    290 
    291                    Ok(ChatOpResult::Message(format!(
    292                        "{} **Language Detection:**\n**Language:** {} ({})\n**Confidence:** {:.0}%",
    293                        confidence_emoji,
    294                        detection.language,
    295                        detection.iso_code,
    296                        detection.confidence * 100.0
    297                    )))
    298                }
    299                None => {
    300                    // Fallback to simple detection
    301                    let detection = crate::ai_service::fallback_language_detection(&text);
    302                    Ok(ChatOpResult::Message(format!(
    303                        "🔍 **Language Detection (Basic):**\n**Language:** {} ({})\n**Confidence:** {:.0}%",
    304                        detection.language,
    305                        detection.iso_code,
    306                        detection.confidence * 100.0
    307                    )))
    308                }
    309            }
    310        } else {
    311            let detection = crate::ai_service::fallback_language_detection(&text);
    312            Ok(ChatOpResult::Message(format!(
    313                "🔍 **Language Detection (Basic):**\n**Language:** {} ({})\n**Confidence:** {:.0}%",
    314                detection.language,
    315                detection.iso_code,
    316                detection.confidence * 100.0
    317            )))
    318        }
    319    }
    320 }
    321 
    322 /// Sentiment analysis command
    323 pub struct SentimentCommand {
    324    ai_service: Arc<AIService>,
    325    runtime: Arc<Runtime>,
    326 }
    327 
    328 impl SentimentCommand {
    329    pub fn new(ai_service: Arc<AIService>, runtime: Arc<Runtime>) -> Self {
    330        Self {
    331            ai_service,
    332            runtime,
    333        }
    334    }
    335 }
    336 
    337 impl ChatCommand for SentimentCommand {
    338    fn name(&self) -> &'static str {
    339        "sentiment"
    340    }
    341    fn description(&self) -> &'static str {
    342        "Analyze sentiment and emotions in text"
    343    }
    344    fn usage(&self) -> &'static str {
    345        "/sentiment <text>"
    346    }
    347    fn aliases(&self) -> Vec<&'static str> {
    348        vec!["mood", "feel"]
    349    }
    350 
    351    fn execute(
    352        &self,
    353        args: Vec<String>,
    354        _context: &CommandContext,
    355    ) -> Result<ChatOpResult, ChatOpError> {
    356        if args.is_empty() {
    357            return Err(ChatOpError::MissingArguments(
    358                "Please specify text to analyze".to_string(),
    359            ));
    360        }
    361 
    362        let text = args.join(" ");
    363 
    364        if self.ai_service.is_available() {
    365            // Check if AI is functional
    366            let ai_service_check = Arc::clone(&self.ai_service);
    367            let is_functional = self
    368                .runtime
    369                .block_on(async move { ai_service_check.is_functional().await });
    370 
    371            if !is_functional {
    372                return Ok(ChatOpResult::Error(
    373                    "AI service temporarily unavailable. Cannot analyze sentiment.".to_string(),
    374                ));
    375            }
    376 
    377            let ai_service = Arc::clone(&self.ai_service);
    378            let text_clone = text.clone();
    379            match self
    380                .runtime
    381                .block_on(async move { ai_service.analyze_sentiment(&text_clone).await })
    382            {
    383                Some(analysis) => {
    384                    let sentiment_emoji = match analysis.sentiment.as_str() {
    385                        "positive" => "😊",
    386                        "negative" => "😔",
    387                        _ => "😐",
    388                    };
    389 
    390                    let mut result = vec![
    391                        format!("{} **Sentiment Analysis:**", sentiment_emoji),
    392                        format!(
    393                            "**Sentiment:** {} (Score: {:.2})",
    394                            analysis.sentiment, analysis.score
    395                        ),
    396                        format!("**Confidence:** {:.0}%", analysis.confidence * 100.0),
    397                    ];
    398 
    399                    if !analysis.emotions.is_empty() {
    400                        result.push(format!("**Emotions:** {}", analysis.emotions.join(", ")));
    401                    }
    402 
    403                    Ok(ChatOpResult::Block(result))
    404                }
    405                None => {
    406                    // Fallback to simple analysis
    407                    let analysis = crate::ai_service::fallback_sentiment_analysis(&text);
    408                    let sentiment_emoji = match analysis.sentiment.as_str() {
    409                        "positive" => "😊",
    410                        "negative" => "😔",
    411                        _ => "😐",
    412                    };
    413 
    414                    Ok(ChatOpResult::Message(format!(
    415                        "{} **Sentiment (Basic):** {} (Score: {:.2})",
    416                        sentiment_emoji, analysis.sentiment, analysis.score
    417                    )))
    418                }
    419            }
    420        } else {
    421            let analysis = crate::ai_service::fallback_sentiment_analysis(&text);
    422            let sentiment_emoji = match analysis.sentiment.as_str() {
    423                "positive" => "😊",
    424                "negative" => "😔",
    425                _ => "😐",
    426            };
    427 
    428            Ok(ChatOpResult::Message(format!(
    429                "{} **Sentiment (Basic):** {} (Score: {:.2})",
    430                sentiment_emoji, analysis.sentiment, analysis.score
    431            )))
    432        }
    433    }
    434 }
    435 
    436 /// Chat atmosphere command
    437 pub struct AtmosphereCommand {
    438    ai_service: Arc<AIService>,
    439 }
    440 
    441 impl AtmosphereCommand {
    442    pub fn new(ai_service: Arc<AIService>) -> Self {
    443        Self { ai_service }
    444    }
    445 }
    446 
    447 impl ChatCommand for AtmosphereCommand {
    448    fn name(&self) -> &'static str {
    449        "atmosphere"
    450    }
    451    fn description(&self) -> &'static str {
    452        "Get current chat mood and activity"
    453    }
    454    fn usage(&self) -> &'static str {
    455        "/atmosphere"
    456    }
    457    fn aliases(&self) -> Vec<&'static str> {
    458        vec!["vibe", "mood"]
    459    }
    460 
    461    fn execute(
    462        &self,
    463        _args: Vec<String>,
    464        _context: &CommandContext,
    465    ) -> Result<ChatOpResult, ChatOpError> {
    466        let atmosphere = self.ai_service.get_chat_atmosphere();
    467        Ok(ChatOpResult::Message(format!(
    468            "**Current Chat Atmosphere:**\n{}",
    469            atmosphere
    470        )))
    471    }
    472 }
    473 
    474 /// Advanced moderation test command
    475 pub struct ModCheckCommand {
    476    ai_service: Arc<AIService>,
    477    runtime: Arc<Runtime>,
    478 }
    479 
    480 impl ModCheckCommand {
    481    pub fn new(ai_service: Arc<AIService>, runtime: Arc<Runtime>) -> Self {
    482        Self {
    483            ai_service,
    484            runtime,
    485        }
    486    }
    487 }
    488 
    489 impl ChatCommand for ModCheckCommand {
    490    fn name(&self) -> &'static str {
    491        "modcheck"
    492    }
    493    fn description(&self) -> &'static str {
    494        "Test AI moderation on a message"
    495    }
    496    fn usage(&self) -> &'static str {
    497        "/modcheck <text>"
    498    }
    499    fn aliases(&self) -> Vec<&'static str> {
    500        vec!["checkmod"]
    501    }
    502 
    503    fn execute(
    504        &self,
    505        args: Vec<String>,
    506        _context: &CommandContext,
    507    ) -> Result<ChatOpResult, ChatOpError> {
    508        if !self.ai_service.is_available() {
    509            return Ok(ChatOpResult::Error(
    510                "AI service not configured. Check OPENAI_API_KEY environment variable.".to_string(),
    511            ));
    512        }
    513 
    514        // Test if AI is actually functional (not just configured)
    515        let ai_service_check = Arc::clone(&self.ai_service);
    516        let is_functional = self
    517            .runtime
    518            .block_on(async move { ai_service_check.is_functional().await });
    519 
    520        if !is_functional {
    521            return Ok(ChatOpResult::Error(
    522                "AI moderation service temporarily unavailable. This might be due to:\n\
    523                • API quota exceeded\n\
    524                • Billing issues\n\
    525                • Service outage\n\
    526                Please try again later or contact an admin."
    527                    .to_string(),
    528            ));
    529        }
    530 
    531        if args.is_empty() {
    532            return Err(ChatOpError::MissingArguments(
    533                "Please specify text to check".to_string(),
    534            ));
    535        }
    536 
    537        let text = args.join(" ");
    538        let recent_msgs = self.ai_service.get_recent_messages(5);
    539        let context = recent_msgs
    540            .iter()
    541            .map(|m| format!("{}: {}", m.author, m.content))
    542            .collect::<Vec<_>>()
    543            .join(" | ");
    544 
    545        let ai_service = Arc::clone(&self.ai_service);
    546        match self
    547            .runtime
    548            .block_on(async move { ai_service.advanced_moderation(&text, &context).await })
    549        {
    550            Some(result) => {
    551                let action_emoji = match result.suggested_action.as_str() {
    552                    "ban" => "🔨",
    553                    "kick" => "👢",
    554                    "warn" => "⚠️",
    555                    _ => "✅",
    556                };
    557 
    558                let mut response = vec![
    559                    format!("{} **AI Moderation Check:**", action_emoji),
    560                    format!(
    561                        "**Should Moderate:** {}",
    562                        if result.should_moderate { "YES" } else { "NO" }
    563                    ),
    564                    format!("**Severity:** {}/10", result.severity),
    565                    format!("**Suggested Action:** {}", result.suggested_action),
    566                    format!("**Confidence:** {:.0}%", result.confidence * 100.0),
    567                ];
    568 
    569                if !result.reasons.is_empty() {
    570                    response.push("**Reasons:**".to_string());
    571                    for reason in result.reasons {
    572                        response.push(format!("• {}", reason));
    573                    }
    574                }
    575 
    576                Ok(ChatOpResult::Block(response))
    577            }
    578            None => Ok(ChatOpResult::Error(
    579                "Failed to analyze message. Please try again.".to_string(),
    580            )),
    581        }
    582    }
    583 }
    584 
    585 /// AI service status command
    586 pub struct AIStatusCommand {
    587    ai_service: Arc<AIService>,
    588 }
    589 
    590 impl AIStatusCommand {
    591    pub fn new(ai_service: Arc<AIService>) -> Self {
    592        Self { ai_service }
    593    }
    594 }
    595 
    596 impl ChatCommand for AIStatusCommand {
    597    fn name(&self) -> &'static str {
    598        "aistatus"
    599    }
    600    fn description(&self) -> &'static str {
    601        "Check AI service status and statistics"
    602    }
    603    fn usage(&self) -> &'static str {
    604        "/aistatus"
    605    }
    606    fn aliases(&self) -> Vec<&'static str> {
    607        vec!["aiinfo"]
    608    }
    609 
    610    fn execute(
    611        &self,
    612        _args: Vec<String>,
    613        _context: &CommandContext,
    614    ) -> Result<ChatOpResult, ChatOpError> {
    615        let stats = self.ai_service.get_stats();
    616 
    617        let mut result = vec!["🤖 **AI Service Status:**".to_string(), "".to_string()];
    618 
    619        for (key, value) in stats {
    620            let display_key = match key.as_str() {
    621                "available" => "Service Available",
    622                "message_history" => "Messages in History",
    623                "language_cache" => "Language Cache Size",
    624                "sentiment_cache" => "Sentiment Cache Size",
    625                "max_history" => "Max History Size",
    626                _ => &key,
    627            };
    628            result.push(format!("**{}:** {}", display_key, value));
    629        }
    630 
    631        result.push("".to_string());
    632        result.push("Available Commands: /summarize, /translate, /detect, /sentiment, /atmosphere, /modcheck".to_string());
    633 
    634        Ok(ChatOpResult::Block(result))
    635    }
    636 }
    637 
    638 /// Code fixing command with AI enhancement
    639 pub struct FixCommand {
    640    ai_service: Arc<AIService>,
    641    #[allow(dead_code)]
    642    runtime: Arc<Runtime>,
    643 }
    644 
    645 impl FixCommand {
    646    pub fn new(ai_service: Arc<AIService>, runtime: Arc<Runtime>) -> Self {
    647        Self {
    648            ai_service,
    649            runtime,
    650        }
    651    }
    652 }
    653 
    654 impl ChatCommand for FixCommand {
    655    fn name(&self) -> &'static str {
    656        "fix"
    657    }
    658    fn description(&self) -> &'static str {
    659        "Attempt to fix code issues (AI)"
    660    }
    661    fn usage(&self) -> &'static str {
    662        "/fix <code>"
    663    }
    664 
    665    fn execute(
    666        &self,
    667        args: Vec<String>,
    668        _context: &CommandContext,
    669    ) -> Result<ChatOpResult, ChatOpError> {
    670        if args.is_empty() {
    671            return Err(ChatOpError::MissingArguments(
    672                "Please specify code to fix".to_string(),
    673            ));
    674        }
    675 
    676        let code = args.join(" ");
    677 
    678        // Enhanced basic analysis
    679        let mut suggestions = vec!["🔧 **Code Fix Suggestions:**".to_string()];
    680 
    681        // Language detection
    682        if code.contains("fn ") || code.contains("let ") || code.contains("mut ") {
    683            suggestions.push(
    684                "• **Rust detected**: Use `cargo check` and `clippy` for detailed analysis"
    685                    .to_string(),
    686            );
    687        } else if code.contains("def ") || code.contains("import ") {
    688            suggestions.push(
    689                "• **Python detected**: Check indentation and use `pylint` or `flake8`".to_string(),
    690            );
    691        } else if code.contains("function ") || code.contains("const ") || code.contains("=>") {
    692            suggestions.push(
    693                "• **JavaScript detected**: Use ESLint for comprehensive checking".to_string(),
    694            );
    695        }
    696 
    697        // Common issues
    698        if code.contains("unwrap()") {
    699            suggestions.push(
    700                "• Replace `unwrap()` with proper error handling using `?` or `match`".to_string(),
    701            );
    702        }
    703 
    704        if code.contains("panic!") {
    705            suggestions.push("• Consider returning `Result` instead of using `panic!`".to_string());
    706        }
    707 
    708        if !code.contains("//") && !code.contains("#") && !code.contains("/*") && code.len() > 50 {
    709            suggestions.push("• Add comments to explain complex logic".to_string());
    710        }
    711 
    712        if code.len() > 300 {
    713            suggestions
    714                .push("• Consider breaking this into smaller, more focused functions".to_string());
    715        }
    716 
    717        // AI enhancement note
    718        if self.ai_service.is_available() {
    719            suggestions.push("".to_string());
    720            suggestions
    721                .push("💡 *For detailed AI-powered code review, use `/review <code>`*".to_string());
    722        } else {
    723            suggestions.push("".to_string());
    724            suggestions.push(
    725                "💡 *Enable AI service (OPENAI_API_KEY) for enhanced code analysis*".to_string(),
    726            );
    727        }
    728 
    729        Ok(ChatOpResult::Block(suggestions))
    730    }
    731 }
    732 
    733 /// Enhanced code review command
    734 pub struct ReviewCommand {
    735    ai_service: Arc<AIService>,
    736    runtime: Arc<Runtime>,
    737 }
    738 
    739 impl ReviewCommand {
    740    pub fn new(ai_service: Arc<AIService>, runtime: Arc<Runtime>) -> Self {
    741        Self {
    742            ai_service,
    743            runtime,
    744        }
    745    }
    746 }
    747 
    748 impl ChatCommand for ReviewCommand {
    749    fn name(&self) -> &'static str {
    750        "review"
    751    }
    752    fn description(&self) -> &'static str {
    753        "Get comprehensive code review (AI)"
    754    }
    755    fn usage(&self) -> &'static str {
    756        "/review <code>"
    757    }
    758 
    759    fn execute(
    760        &self,
    761        args: Vec<String>,
    762        _context: &CommandContext,
    763    ) -> Result<ChatOpResult, ChatOpError> {
    764        if args.is_empty() {
    765            return Err(ChatOpError::MissingArguments(
    766                "Please specify code to review".to_string(),
    767            ));
    768        }
    769 
    770        let code = args.join(" ");
    771 
    772        // Check if AI is functional
    773        let ai_service_check = Arc::clone(&self.ai_service);
    774        let is_functional = if self.ai_service.is_available() {
    775            self.runtime
    776                .block_on(async move { ai_service_check.is_functional().await })
    777        } else {
    778            false
    779        };
    780 
    781        if !is_functional {
    782            // Fallback to enhanced basic review
    783            let mut suggestions = vec!["📝 **Code Review (Basic Analysis):**".to_string()];
    784 
    785            if code.len() > 200 {
    786                suggestions.push(
    787                    "• Consider breaking this into smaller functions for better maintainability"
    788                        .to_string(),
    789                );
    790            }
    791 
    792            if code.contains("TODO") || code.contains("FIXME") || code.contains("XXX") {
    793                suggestions
    794                    .push("• Address TODO/FIXME comments before production deployment".to_string());
    795            }
    796 
    797            if !code.contains("//") && !code.contains("#") && !code.contains("/*") {
    798                suggestions.push(
    799                    "• Add descriptive comments explaining the logic and purpose".to_string(),
    800                );
    801            }
    802 
    803            if code.contains("panic!") || code.contains("unwrap()") {
    804                suggestions
    805                    .push("• Implement proper error handling instead of panicking".to_string());
    806            }
    807 
    808            if code.split('\n').count() > 20 {
    809                suggestions.push(
    810                    "• Function appears long - consider extracting helper functions".to_string(),
    811                );
    812            }
    813 
    814            // Variable naming check
    815            if code.chars().filter(|c| c.is_uppercase()).count() as f32 / code.len() as f32 > 0.3 {
    816                suggestions
    817                    .push("• Check variable naming conventions for your language".to_string());
    818            }
    819 
    820            suggestions.push("".to_string());
    821            suggestions
    822                .push("💡 *For AI-powered detailed review, set up OPENAI_API_KEY*".to_string());
    823 
    824            return Ok(ChatOpResult::Block(suggestions));
    825        }
    826 
    827        // AI-powered review
    828        let ai_service = Arc::clone(&self.ai_service);
    829        let prompt = format!(
    830            "Please review this code and provide specific, actionable feedback. Focus on:
    831 - Code quality and best practices
    832 - Potential bugs or issues
    833 - Performance considerations
    834 - Readability and maintainability
    835 - Security concerns if applicable
    836 
    837 Code to review:
    838 ```
    839 {}
    840 ```
    841 
    842 Please format your response as a markdown list of specific suggestions.",
    843            code
    844        );
    845 
    846        match self.runtime.block_on(async move {
    847            ai_service.translate_text(&prompt, "code review").await // Using translate as a general AI completion
    848        }) {
    849            Some(review) => {
    850                let result = vec!["🤖 **AI Code Review:**".to_string(), "".to_string(), review];
    851                Ok(ChatOpResult::Block(result))
    852            }
    853            None => Ok(ChatOpResult::Error(
    854                "AI review failed. Please try again or use basic review.".to_string(),
    855            )),
    856        }
    857    }
    858 }