diff --git a/src/apps/cli/src/modes/chat.rs b/src/apps/cli/src/modes/chat.rs index cd20e4f0..6b974e5b 100644 --- a/src/apps/cli/src/modes/chat.rs +++ b/src/apps/cli/src/modes/chat.rs @@ -248,7 +248,6 @@ impl ChatMode { Ok(exit_reason) } - #[allow(clippy::too_many_arguments)] fn handle_key_event( &self, key: KeyEvent, diff --git a/src/apps/cli/src/session.rs b/src/apps/cli/src/session.rs index 9c72b898..eb170fc4 100644 --- a/src/apps/cli/src/session.rs +++ b/src/apps/cli/src/session.rs @@ -167,13 +167,20 @@ impl Session { /// Add or update text flow of the last message pub fn update_last_message_text_flow(&mut self, content: String, is_streaming: bool) { if let Some(last_message) = self.messages.last_mut() { - if let Some(FlowItem::Text { - content: ref mut c, - is_streaming: ref mut s, - }) = last_message.flow_items.last_mut() - { - *c = content.clone(); - *s = is_streaming; + if let Some(last_item) = last_message.flow_items.last_mut() { + if let FlowItem::Text { + content: ref mut c, + is_streaming: ref mut s, + } = last_item + { + *c = content.clone(); + *s = is_streaming; + } else { + last_message.flow_items.push(FlowItem::Text { + content: content.clone(), + is_streaming, + }); + } } else { last_message.flow_items.push(FlowItem::Text { content: content.clone(), diff --git a/src/apps/cli/src/ui/chat.rs b/src/apps/cli/src/ui/chat.rs index fa2f1bc2..fab52e24 100644 --- a/src/apps/cli/src/ui/chat.rs +++ b/src/apps/cli/src/ui/chat.rs @@ -131,7 +131,7 @@ impl ChatView { fn render_messages(&mut self, frame: &mut Frame, area: Rect) { let title = if self.browse_mode { - " Conversation [Browse Mode ↕] ".to_string() + format!(" Conversation [Browse Mode ↕] ") } else { " Conversation ".to_string() }; diff --git a/src/apps/cli/src/ui/markdown.rs b/src/apps/cli/src/ui/markdown.rs index 0a3cce21..7dd99fb8 100644 --- a/src/apps/cli/src/ui/markdown.rs +++ b/src/apps/cli/src/ui/markdown.rs @@ -242,7 +242,7 @@ impl MarkdownRenderer { } // Remove trailing empty lines - while lines.last().is_some_and(|line| { + while lines.last().map_or(false, |line| { line.spans.is_empty() || (line.spans.len() == 1 && line.spans[0].content.is_empty()) }) { lines.pop(); diff --git a/src/apps/cli/src/ui/startup.rs b/src/apps/cli/src/ui/startup.rs index a679d6ba..d1cea763 100644 --- a/src/apps/cli/src/ui/startup.rs +++ b/src/apps/cli/src/ui/startup.rs @@ -309,12 +309,14 @@ impl StartupPage { lines.push(Line::from("")); if use_fancy_logo { - let logo = [" ██████╗ ██╗████████╗███████╗██╗ ██╗███╗ ██╗", + let logo = vec![ + " ██████╗ ██╗████████╗███████╗██╗ ██╗███╗ ██╗", " ██╔══██╗██║╚══██╔══╝██╔════╝██║ ██║████╗ ██║", " ██████╔╝██║ ██║ █████╗ ██║ ██║██╔██╗ ██║", " ██╔══██╗██║ ██║ ██╔══╝ ██║ ██║██║╚██╗██║", " ██████╔╝██║ ██║ ██║ ╚██████╔╝██║ ╚████║", - " ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝"]; + " ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝", + ]; let colors = [ Color::Rgb(255, 0, 100), @@ -334,11 +336,13 @@ impl StartupPage { ))); } } else { - let logo = [" ____ _ _ _____ ", + let logo = vec![ + " ____ _ _ _____ ", " | __ )(_) |_| ___| _ _ __ ", " | _ \\| | __| |_ | | | | '_ \\ ", " | |_) | | |_| _|| |_| | | | |", - " |____/|_|\\__|_| \\__,_|_| |_|"]; + " |____/|_|\\__|_| \\__,_|_| |_|", + ]; let colors = [ Color::Cyan, @@ -883,9 +887,7 @@ impl StartupPage { PageState::Finished(StartupResult::Exit), ); - - - match page_state { + let result = match page_state { PageState::MainMenu => { self.page_state = PageState::MainMenu; self.handle_main_menu_key(key) @@ -954,7 +956,9 @@ impl StartupPage { self.page_state = PageState::Finished(result); Ok(()) } - } + }; + + result } fn handle_main_menu_key(&mut self, key: KeyEvent) -> Result<()> { @@ -1087,8 +1091,9 @@ impl StartupPage { let path = path.trim(); // Handle paths starting with ~ - if let Some(rest) = path.strip_prefix('~') { + if path.starts_with('~') { if let Some(home) = dirs::home_dir() { + let rest = &path[1..]; return home .join(rest.trim_start_matches('/')) .to_string_lossy() diff --git a/src/apps/cli/src/ui/string_utils.rs b/src/apps/cli/src/ui/string_utils.rs index 3a2358d4..25553732 100644 --- a/src/apps/cli/src/ui/string_utils.rs +++ b/src/apps/cli/src/ui/string_utils.rs @@ -1,4 +1,4 @@ -//! String processing utilities +/// String processing utilities /// Safely truncate string to specified byte length pub fn truncate_str(s: &str, max_bytes: usize) -> String { diff --git a/src/apps/desktop/src/api/commands.rs b/src/apps/desktop/src/api/commands.rs index 39e0d1a9..2bc322f5 100644 --- a/src/apps/desktop/src/api/commands.rs +++ b/src/apps/desktop/src/api/commands.rs @@ -809,7 +809,9 @@ pub async fn open_remote_workspace( let stable_workspace_id = remote_workspace_stable_id(&ssh_host, &remote_path); let display_name = remote_path - .split('/').rfind(|s| !s.is_empty()) + .split('/') + .filter(|s| !s.is_empty()) + .last() .unwrap_or(remote_path.as_str()) .to_string(); @@ -1594,10 +1596,8 @@ pub async fn write_file_content( } let full_path = request.file_path; - let options = FileOperationOptions { - backup_on_overwrite: false, - ..FileOperationOptions::default() - }; + let mut options = FileOperationOptions::default(); + options.backup_on_overwrite = false; match state .filesystem_service @@ -2000,7 +2000,7 @@ pub async fn reveal_in_explorer(request: RevealInExplorerRequest) -> Result<(), } else { let normalized_path = request.path.replace("/", "\\"); bitfun_core::util::process_manager::create_command("explorer") - .args(["/select,", &normalized_path]) + .args(&["/select,", &normalized_path]) .spawn() .map_err(|e| format!("Failed to open explorer: {}", e))?; } diff --git a/src/apps/desktop/src/api/computer_use_api.rs b/src/apps/desktop/src/api/computer_use_api.rs index 71def918..4f674186 100644 --- a/src/apps/desktop/src/api/computer_use_api.rs +++ b/src/apps/desktop/src/api/computer_use_api.rs @@ -83,7 +83,7 @@ pub async fn computer_use_open_system_settings( #[cfg(target_os = "windows")] { let _ = request; - Err("Open system settings is not wired for Windows yet.".to_string()) + return Err("Open system settings is not wired for Windows yet.".to_string()); } #[cfg(target_os = "linux")] { diff --git a/src/apps/desktop/src/api/config_api.rs b/src/apps/desktop/src/api/config_api.rs index 2362bd4f..104f997b 100644 --- a/src/apps/desktop/src/api/config_api.rs +++ b/src/apps/desktop/src/api/config_api.rs @@ -249,7 +249,7 @@ pub async fn get_mode_configs(_state: State<'_, AppState>) -> Result) -> Result) -> Result Result { - let supported = ["zh-CN", "en-US"]; + let supported = vec!["zh-CN", "en-US"]; if !supported.contains(&request.language.as_str()) { return Err(format!("Unsupported language: {}", request.language)); } diff --git a/src/apps/desktop/src/api/mcp_api.rs b/src/apps/desktop/src/api/mcp_api.rs index b14df51f..d825a02d 100644 --- a/src/apps/desktop/src/api/mcp_api.rs +++ b/src/apps/desktop/src/api/mcp_api.rs @@ -176,7 +176,11 @@ pub async fn get_mcp_servers(state: State<'_, AppState>) -> Result Result<(), String> { } drop(guard); - let config = RemoteConnectConfig { - mobile_web_dir: detect_mobile_web_dir(), - ..RemoteConnectConfig::default() - }; + let mut config = RemoteConnectConfig::default(); + config.mobile_web_dir = detect_mobile_web_dir(); let service = RemoteConnectService::new(config).map_err(|e| format!("init remote connect: {e}"))?; *holder.write().await = Some(service); @@ -480,10 +478,8 @@ pub async fn remote_connect_configure_custom_server(url: String) -> Result<(), S let holder = get_service_holder(); let mut guard = holder.write().await; if guard.is_none() { - let config = RemoteConnectConfig { - custom_server_url: Some(url), - ..RemoteConnectConfig::default() - }; + let mut config = RemoteConnectConfig::default(); + config.custom_server_url = Some(url); let service = RemoteConnectService::new(config).map_err(|e| format!("init: {e}"))?; *guard = Some(service); } @@ -534,23 +530,13 @@ pub async fn remote_connect_configure_bot(request: ConfigureBotRequest) -> Resul }; if guard.is_none() { - let config = match bot_config { - BotConfig::Feishu { .. } => RemoteConnectConfig { - mobile_web_dir: detect_mobile_web_dir(), - bot_feishu: Some(bot_config), - ..RemoteConnectConfig::default() - }, - BotConfig::Telegram { .. } => RemoteConnectConfig { - mobile_web_dir: detect_mobile_web_dir(), - bot_telegram: Some(bot_config), - ..RemoteConnectConfig::default() - }, - BotConfig::Weixin { .. } => RemoteConnectConfig { - mobile_web_dir: detect_mobile_web_dir(), - bot_weixin: Some(bot_config), - ..RemoteConnectConfig::default() - }, - }; + let mut config = RemoteConnectConfig::default(); + config.mobile_web_dir = detect_mobile_web_dir(); + match &bot_config { + BotConfig::Feishu { .. } => config.bot_feishu = Some(bot_config), + BotConfig::Telegram { .. } => config.bot_telegram = Some(bot_config), + BotConfig::Weixin { .. } => config.bot_weixin = Some(bot_config), + } let service = RemoteConnectService::new(config).map_err(|e| format!("init: {e}"))?; *guard = Some(service); } else if let Some(service) = guard.as_mut() { diff --git a/src/apps/desktop/src/api/skill_api.rs b/src/apps/desktop/src/api/skill_api.rs index b8dc8c58..47af3005 100644 --- a/src/apps/desktop/src/api/skill_api.rs +++ b/src/apps/desktop/src/api/skill_api.rs @@ -890,14 +890,18 @@ async fn fill_market_descriptions(client: &Client, base_url: &str, items: &mut [ }); if join_set.len() >= MARKET_DESC_FETCH_CONCURRENCY { - if let Some(Ok((skill_id, Some(desc)))) = join_set.join_next().await { - fetched.insert(skill_id, desc); + if let Some(result) = join_set.join_next().await { + if let Ok((skill_id, Some(desc))) = result { + fetched.insert(skill_id, desc); + } } } } - while let Some(Ok((skill_id, Some(desc)))) = join_set.join_next().await { - fetched.insert(skill_id, desc); + while let Some(result) = join_set.join_next().await { + if let Ok((skill_id, Some(desc))) = result { + fetched.insert(skill_id, desc); + } } if fetched.is_empty() { diff --git a/src/apps/desktop/src/api/snapshot_service.rs b/src/apps/desktop/src/api/snapshot_service.rs index 4d4ad4a2..3eeccca0 100644 --- a/src/apps/desktop/src/api/snapshot_service.rs +++ b/src/apps/desktop/src/api/snapshot_service.rs @@ -890,7 +890,7 @@ pub async fn get_file_change_history( .await .map_err(|e| format!("Failed to get file change history: {}", e))?; - serde_json::to_value(changes).map_err(|e| format!("Serialization failed: {}", e)) + Ok(serde_json::to_value(changes).map_err(|e| format!("Serialization failed: {}", e))?) } #[tauri::command] diff --git a/src/apps/desktop/src/api/storage_commands.rs b/src/apps/desktop/src/api/storage_commands.rs index 550e83cd..8fcef076 100644 --- a/src/apps/desktop/src/api/storage_commands.rs +++ b/src/apps/desktop/src/api/storage_commands.rs @@ -81,7 +81,7 @@ pub async fn cleanup_storage(state: State<'_, AppState>) -> Result Result<(), String> { return Err("Name cannot be empty".to_string()); } let mut chars = name.chars(); - if !chars.next().is_some_and(|c| c.is_ascii_alphabetic()) { + if !chars.next().map_or(false, |c| c.is_ascii_alphabetic()) { return Err("Name must start with a letter".to_string()); } for c in chars { @@ -214,10 +214,11 @@ pub async fn create_subagent( validate_agent_name(name)?; let workspace = workspace_root_from_request(request.workspace_path.as_deref()); - if request.level == SubagentLevel::Project - && workspace.is_none() { + if request.level == SubagentLevel::Project { + if workspace.is_none() { return Err("Project-level Agent requires opening a workspace first".to_string()); } + } let modes = state.agent_registry.get_modes_info().await; let subagents = state diff --git a/src/apps/desktop/src/api/system_api.rs b/src/apps/desktop/src/api/system_api.rs index 97584001..df82f43c 100644 --- a/src/apps/desktop/src/api/system_api.rs +++ b/src/apps/desktop/src/api/system_api.rs @@ -102,7 +102,7 @@ pub async fn run_system_command( .env .map(|vars| vars.into_iter().map(|v| (v.key, v.value)).collect()); - let env_ref: Option<&[(String, String)]> = env_vars.as_deref(); + let env_ref: Option<&[(String, String)]> = env_vars.as_ref().map(|v| v.as_slice()); let result = system::run_command( &request.command, diff --git a/src/apps/desktop/src/api/terminal_api.rs b/src/apps/desktop/src/api/terminal_api.rs index 42bb007a..bc60a781 100644 --- a/src/apps/desktop/src/api/terminal_api.rs +++ b/src/apps/desktop/src/api/terminal_api.rs @@ -67,8 +67,8 @@ impl TerminalState { *initialized = true; } - TerminalApi::from_singleton() - .map_err(|e| format!("Terminal API not initialized: {}", e)) + Ok(TerminalApi::from_singleton() + .map_err(|e| format!("Terminal API not initialized: {}", e))?) } /// Get the scripts directory path for shell integration diff --git a/src/apps/desktop/src/computer_use/desktop_host.rs b/src/apps/desktop/src/computer_use/desktop_host.rs index 2313795f..7f092a20 100644 --- a/src/apps/desktop/src/computer_use/desktop_host.rs +++ b/src/apps/desktop/src/computer_use/desktop_host.rs @@ -180,8 +180,8 @@ fn coord_blit_glyph( } let iw = img.width() as i32; let ih = img.height() as i32; - let xmin = metrics.xmin; - let ymin = metrics.ymin; + let xmin = metrics.xmin as i32; + let ymin = metrics.ymin as i32; for row in 0..h { for col in 0..w { let alpha = bitmap[row * w + col] as u32; @@ -313,7 +313,7 @@ fn draw_som_labels( // Draw label text centered in badge let text_x = bx0 + SOM_PAD_X; - let baseline_y = by0 + SOM_PAD_Y + text_h - m_rep.ymin.max(0); + let baseline_y = by0 + SOM_PAD_Y + text_h - (m_rep.ymin.max(0) as i32); coord_draw_text_h(frame, text_x, baseline_y, &label_text, SOM_FG, SOM_LABEL_PX); } } @@ -809,12 +809,6 @@ impl std::fmt::Debug for DesktopComputerUseHost { } } -impl Default for DesktopComputerUseHost { - fn default() -> Self { - Self::new() - } -} - impl DesktopComputerUseHost { pub fn new() -> Self { Self { @@ -836,7 +830,7 @@ impl DesktopComputerUseHost { } #[cfg(target_os = "windows")] { - Self::session_snapshot_windows() + return Self::session_snapshot_windows(); } #[cfg(target_os = "linux")] { @@ -1366,7 +1360,6 @@ end tell"#]) /// Pointer position in **scaled image** pixels, if it lies inside the captured display. #[cfg(not(target_os = "macos"))] - #[allow(clippy::too_many_arguments)] fn pointer_in_scaled_image( origin_x: i32, origin_y: i32, @@ -2165,7 +2158,7 @@ impl ComputerUseHost for DesktopComputerUseHost { .map_err(|e| BitFunError::tool(format!("lock: {}", e)))?; s.screenshot_cache = Some(ScreenshotCacheEntry { rgba: rgba.clone(), - screen, + screen: screen.clone(), capture_time: Instant::now(), }); } diff --git a/src/apps/desktop/src/computer_use/screen_ocr.rs b/src/apps/desktop/src/computer_use/screen_ocr.rs index 09de6ea5..caf1872c 100644 --- a/src/apps/desktop/src/computer_use/screen_ocr.rs +++ b/src/apps/desktop/src/computer_use/screen_ocr.rs @@ -53,10 +53,10 @@ pub fn find_text_matches( /// If unset or non-zero: write the exact JPEG passed to OCR into `computer_use_debug` under the app data dir (see implementation). Set `BITFUN_COMPUTER_USE_OCR_DEBUG=0` to disable. fn ocr_debug_save_enabled() -> bool { - !matches!( - std::env::var("BITFUN_COMPUTER_USE_OCR_DEBUG"), - Ok(v) if v == "0" || v.eq_ignore_ascii_case("false") - ) + match std::env::var("BITFUN_COMPUTER_USE_OCR_DEBUG") { + Ok(v) if v == "0" || v.eq_ignore_ascii_case("false") => false, + _ => true, + } } /// Same directory as agent `screenshot` debug (`workspace/.bitfun/computer_use_debug`), when PathManager is available. @@ -164,11 +164,13 @@ fn levenshtein_chars(a: &str, b: &str) -> usize { } let mut prev: Vec = (0..=m).collect(); let mut curr = vec![0usize; m + 1]; - for (i, a_ch) in a.iter().enumerate().take(n) { + for i in 0..n { curr[0] = i + 1; for j in 0..m { - let cost = usize::from(*a_ch != b[j]); - curr[j + 1] = (prev[j] + cost).min(prev[j + 1] + 1).min(curr[j] + 1); + let cost = usize::from(a[i] != b[j]); + curr[j + 1] = (prev[j] + cost) + .min(prev[j + 1] + 1) + .min(curr[j] + 1); } std::mem::swap(&mut prev, &mut curr); } @@ -180,7 +182,7 @@ fn fuzzy_max_distance(query_len_chars: usize) -> usize { match query_len_chars { 0 => 0, 1 => 0, - 2..=4 => 1, + 2 | 3 | 4 => 1, 5..=8 => 2, _ => 3, } @@ -228,7 +230,11 @@ fn rank_matches(mut matches: Vec, query: &str) -> Vec std::cmp::Ordering { +fn compare_match( + a: &OcrTextMatch, + b: &OcrTextMatch, + normalized_query: &str, +) -> std::cmp::Ordering { let sa = match_score(a, normalized_query); let sb = match_score(b, normalized_query); sb.cmp(&sa) @@ -392,9 +398,7 @@ pub fn crop_shot_to_ocr_region( let crop_w = px1 - px0; let crop_h = py1 - py0; if px0.saturating_add(crop_w) > img_w || py0.saturating_add(crop_h) > img_h { - return Err(BitFunError::tool( - "OCR crop rectangle is out of image bounds.".to_string(), - )); + return Err(BitFunError::tool("OCR crop rectangle is out of image bounds.".to_string())); } let cropped_view = image::imageops::crop_imm(&rgb, px0, py0, crop_w, crop_h); let cropped = cropped_view.to_image(); @@ -402,13 +406,14 @@ pub fn crop_shot_to_ocr_region( const OCR_CROP_JPEG_QUALITY: u8 = 75; let mut buf = Vec::new(); let mut enc = JpegEncoder::new_with_quality(&mut buf, OCR_CROP_JPEG_QUALITY); - enc.encode( - cropped.as_raw(), - cropped.width(), - cropped.height(), - image::ColorType::Rgb8, - ) - .map_err(|e| BitFunError::tool(format!("OCR crop: encode JPEG: {}", e)))?; + enc + .encode( + cropped.as_raw(), + cropped.width(), + cropped.height(), + image::ColorType::Rgb8, + ) + .map_err(|e| BitFunError::tool(format!("OCR crop: encode JPEG: {}", e)))?; // Affine mapping must match `image_box_to_global_match`: global = origin + (jpeg_px - content_left) / content_w * native_capture. // Do **not** use ix0/ix1 (intersection clip) as display_origin/native size — they disagree with floor/ceil JPEG bounds px0..px1. @@ -470,9 +475,9 @@ mod macos { use objc2::AnyThread; use objc2_foundation::{NSArray, NSData, NSDictionary, NSError, NSString}; use objc2_vision::{ - VNImageOption, VNImageRectForNormalizedRect, VNImageRequestHandler, VNRecognizeTextRequest, - VNRecognizeTextRequestRevision3, VNRecognizedTextObservation, VNRequest, - VNRequestTextRecognitionLevel, + VNImageOption, VNImageRectForNormalizedRect, VNImageRequestHandler, + VNRecognizeTextRequest, VNRecognizeTextRequestRevision3, VNRecognizedTextObservation, + VNRequest, VNRequestTextRecognitionLevel, }; /// Top-N candidates per observation; Chinese matches often appear below rank 1. @@ -692,8 +697,12 @@ mod windows_backend { // Windows.Media.Ocr requires STA thread let mut co_init = None; if unsafe { CoIncrementMTAUsage() }.is_err() { - let hr = - unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) }; + let hr = unsafe { + CoInitializeEx( + None, + COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE, + ) + }; if hr.is_err() { return Err(BitFunError::tool(format!( "Windows OCR COM initialization failed: {:?}", @@ -721,9 +730,9 @@ mod windows_backend { Ok(e) => e, Err(_) => { // Fallback to English if user profile languages fail - let lang = w(windows::Globalization::Language::CreateLanguage( - &HSTRING::from("en-US"), - ))?; + let lang = w(windows::Globalization::Language::CreateLanguage(&HSTRING::from( + "en-US", + )))?; if !w(OcrEngine::IsLanguageSupported(&lang))? { return Err(BitFunError::tool( "Windows OCR: No supported language packs installed.".to_string(), @@ -760,7 +769,8 @@ mod windows_backend { if ranked.is_empty() { return Err(BitFunError::tool(format!( "No OCR text matched {:?} on screen (Windows OCR found {} text regions total).", - text_query, line_count + text_query, + line_count ))); } Ok(ranked) @@ -800,7 +810,13 @@ mod windows_backend { let height = f64::from(rect.Height); Some(image_box_to_global_match( - shot, text, 0.8, local_left, local_top, width, height, + shot, + text, + 0.8, + local_left, + local_top, + width, + height, )) } } @@ -873,7 +889,9 @@ mod linux_backend { let boxa = api .get_component_images(TessPageIteratorLevel_RIL_WORD, true) .ok_or_else(|| { - BitFunError::tool("Linux OCR: Tesseract did not return word regions.".to_string()) + BitFunError::tool( + "Linux OCR: Tesseract did not return word regions.".to_string(), + ) })?; let word_region_count = boxa.get_n(); @@ -927,10 +945,7 @@ mod linux_backend { text_query: &str, text: &str, confidence: f32, - x1: i32, - y1: i32, - x2: i32, - y2: i32, + x1: i32, y1: i32, x2: i32, y2: i32, content_left: u32, content_top: u32, _content_width: u32, diff --git a/src/apps/desktop/src/computer_use/ui_locate_common.rs b/src/apps/desktop/src/computer_use/ui_locate_common.rs index 9c954789..af8dbdec 100644 --- a/src/apps/desktop/src/computer_use/ui_locate_common.rs +++ b/src/apps/desktop/src/computer_use/ui_locate_common.rs @@ -5,16 +5,8 @@ use bitfun_core::util::errors::{BitFunError, BitFunResult}; use screenshots::display_info::DisplayInfo; pub fn validate_query(q: &UiElementLocateQuery) -> BitFunResult<()> { - let t = q - .title_contains - .as_ref() - .map(|s| !s.trim().is_empty()) - .unwrap_or(false); - let r = q - .role_substring - .as_ref() - .map(|s| !s.trim().is_empty()) - .unwrap_or(false); + let t = q.title_contains.as_ref().map(|s| !s.trim().is_empty()).unwrap_or(false); + let r = q.role_substring.as_ref().map(|s| !s.trim().is_empty()).unwrap_or(false); let i = q .identifier_contains .as_ref() @@ -114,7 +106,10 @@ pub fn role_substring_matches_ax_role(ax_role: &str, want: &str) -> bool { } fn combine_is_any(query: &UiElementLocateQuery) -> bool { - matches!(query.filter_combine.as_deref(), Some("any") | Some("or")) + matches!( + query.filter_combine.as_deref(), + Some("any") | Some("or") + ) } /// OR semantics: element matches if **at least one** non-empty filter matches. @@ -201,7 +196,6 @@ pub fn matches_filters( } #[allow(dead_code)] // Used by windows_ax_ui / linux_ax_ui (not compiled on macOS) -#[allow(clippy::too_many_arguments)] pub fn ok_result( gx: f64, gy: f64, @@ -214,22 +208,12 @@ pub fn ok_result( matched_identifier: Option, ) -> BitFunResult { ok_result_with_context( - gx, - gy, - bounds_left, - bounds_top, - bounds_width, - bounds_height, - matched_role, - matched_title, - matched_identifier, - None, - 1, - vec![], + gx, gy, bounds_left, bounds_top, bounds_width, bounds_height, + matched_role, matched_title, matched_identifier, + None, 1, vec![], ) } -#[allow(clippy::too_many_arguments)] pub fn ok_result_with_context( gx: f64, gy: f64, @@ -246,14 +230,7 @@ pub fn ok_result_with_context( ) -> BitFunResult { let (nx, ny) = global_to_native_center(gx, gy)?; let (nminx, nminy, nmaxx, nmaxy) = if bounds_width > 0.0 && bounds_height > 0.0 { - global_bounds_to_native_minmax( - gx, - gy, - bounds_left, - bounds_top, - bounds_width, - bounds_height, - )? + global_bounds_to_native_minmax(gx, gy, bounds_left, bounds_top, bounds_width, bounds_height)? } else { (nx, ny, nx, ny) }; diff --git a/src/apps/relay-server/src/relay/room.rs b/src/apps/relay-server/src/relay/room.rs index 7ed8d1e4..592b3299 100644 --- a/src/apps/relay-server/src/relay/room.rs +++ b/src/apps/relay-server/src/relay/room.rs @@ -1,4 +1,4 @@ -//! Room management for the relay server. +//! Room management for the relay server. //! //! Each room holds a single desktop participant connected via WebSocket. //! Mobile clients interact through HTTP requests that the relay bridges @@ -181,7 +181,7 @@ impl RoomManager { if room .desktop .as_ref() - .is_some_and(|d| d.conn_id == conn_id) + .map_or(false, |d| d.conn_id == conn_id) { info!("Desktop disconnected from room {room_id}"); room.desktop = None; @@ -203,7 +203,7 @@ impl RoomManager { let is_match = room .desktop .as_ref() - .is_some_and(|d| d.conn_id == conn_id); + .map_or(false, |d| d.conn_id == conn_id); if is_match { let now = Utc::now().timestamp(); room.last_activity = now; @@ -245,7 +245,7 @@ impl RoomManager { pub fn has_desktop(&self, room_id: &str) -> bool { self.rooms .get(room_id) - .is_some_and(|r| r.desktop.is_some()) + .map_or(false, |r| r.desktop.is_some()) } pub fn room_count(&self) -> usize { diff --git a/src/apps/relay-server/src/routes/websocket.rs b/src/apps/relay-server/src/routes/websocket.rs index 0843160e..3e456121 100644 --- a/src/apps/relay-server/src/routes/websocket.rs +++ b/src/apps/relay-server/src/routes/websocket.rs @@ -1,4 +1,4 @@ -//! WebSocket handler for the relay server. +//! WebSocket handler for the relay server. //! //! Only desktop clients connect via WebSocket. Mobile clients use HTTP. //! The relay bridges HTTP requests to the desktop via WebSocket using @@ -82,10 +82,11 @@ async fn handle_socket(socket: WebSocket, state: AppState) { let write_task = tokio::spawn(async move { while let Some(msg) = out_rx.recv().await { - if !msg.text.is_empty() - && ws_sender.send(Message::Text(msg.text)).await.is_err() { + if !msg.text.is_empty() { + if ws_sender.send(Message::Text(msg.text)).await.is_err() { break; } + } } }); diff --git a/src/crates/core/src/agentic/agents/agentic_mode.rs b/src/crates/core/src/agentic/agents/agentic_mode.rs index 1713afc6..25b6f0be 100644 --- a/src/crates/core/src/agentic/agents/agentic_mode.rs +++ b/src/crates/core/src/agentic/agents/agentic_mode.rs @@ -6,12 +6,6 @@ pub struct AgenticMode { default_tools: Vec, } -impl Default for AgenticMode { - fn default() -> Self { - Self::new() - } -} - impl AgenticMode { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/agents/claw_mode.rs b/src/crates/core/src/agentic/agents/claw_mode.rs index 087a1963..c50dd177 100644 --- a/src/crates/core/src/agentic/agents/claw_mode.rs +++ b/src/crates/core/src/agentic/agents/claw_mode.rs @@ -6,12 +6,6 @@ pub struct ClawMode { default_tools: Vec, } -impl Default for ClawMode { - fn default() -> Self { - Self::new() - } -} - impl ClawMode { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/agents/cowork_mode.rs b/src/crates/core/src/agentic/agents/cowork_mode.rs index 5a3c222f..2ccd3269 100644 --- a/src/crates/core/src/agentic/agents/cowork_mode.rs +++ b/src/crates/core/src/agentic/agents/cowork_mode.rs @@ -9,12 +9,6 @@ pub struct CoworkMode { default_tools: Vec, } -impl Default for CoworkMode { - fn default() -> Self { - Self::new() - } -} - impl CoworkMode { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/agents/custom_subagents/custom_subagent.rs b/src/crates/core/src/agentic/agents/custom_subagents/custom_subagent.rs index 88e4dbf7..858bb9ae 100644 --- a/src/crates/core/src/agentic/agents/custom_subagents/custom_subagent.rs +++ b/src/crates/core/src/agentic/agents/custom_subagents/custom_subagent.rs @@ -197,6 +197,6 @@ impl CustomSubagent { } let metadata = Value::Mapping(metadata); FrontMatterMarkdown::save(&self.path, &metadata, &self.prompt) - .map_err(BitFunError::Agent) + .map_err(|e| BitFunError::Agent(e)) } } diff --git a/src/crates/core/src/agentic/agents/custom_subagents/custom_subagent_loader.rs b/src/crates/core/src/agentic/agents/custom_subagents/custom_subagent_loader.rs index 508876ff..0b2929df 100644 --- a/src/crates/core/src/agentic/agents/custom_subagents/custom_subagent_loader.rs +++ b/src/crates/core/src/agentic/agents/custom_subagents/custom_subagent_loader.rs @@ -103,7 +103,7 @@ impl CustomSubagentLoader { }; for e in rd.flatten() { let p = e.path(); - if p.is_file() && p.extension().is_some_and(|ext| ext == "md") { + if p.is_file() && p.extension().map_or(false, |ext| ext == "md") { out.push(p); } } diff --git a/src/crates/core/src/agentic/agents/debug_mode.rs b/src/crates/core/src/agentic/agents/debug_mode.rs index 24f3fc7e..9c460479 100644 --- a/src/crates/core/src/agentic/agents/debug_mode.rs +++ b/src/crates/core/src/agentic/agents/debug_mode.rs @@ -14,12 +14,6 @@ pub struct DebugMode; include!(concat!(env!("OUT_DIR"), "/embedded_agents_prompt.rs")); -impl Default for DebugMode { - fn default() -> Self { - Self::new() - } -} - impl DebugMode { pub fn new() -> Self { Self @@ -153,9 +147,9 @@ impl DebugMode { let mut section = format!("## {} Instrumentation\n\n", template.display_name); section.push_str("```"); section.push_str(lang_hint); - section.push('\n'); + section.push_str("\n"); section.push_str(&template.region_start); - section.push('\n'); + section.push_str("\n"); section.push_str( &template .instrumentation_template @@ -165,7 +159,7 @@ impl DebugMode { .replace("{HYPOTHESIS_ID}", "X") .replace("{RUN_ID}", "pre-fix"), ); - section.push('\n'); + section.push_str("\n"); section.push_str(&template.region_end); section.push_str("\n```\n\n"); @@ -174,7 +168,7 @@ impl DebugMode { for note in &template.notes { section.push_str(&format!("- {}\n", note)); } - section.push('\n'); + section.push_str("\n"); } section diff --git a/src/crates/core/src/agentic/agents/explore_agent.rs b/src/crates/core/src/agentic/agents/explore_agent.rs index 42899ffe..bd1bc7c1 100644 --- a/src/crates/core/src/agentic/agents/explore_agent.rs +++ b/src/crates/core/src/agentic/agents/explore_agent.rs @@ -4,12 +4,6 @@ pub struct ExploreAgent { default_tools: Vec, } -impl Default for ExploreAgent { - fn default() -> Self { - Self::new() - } -} - impl ExploreAgent { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/agents/file_finder_agent.rs b/src/crates/core/src/agentic/agents/file_finder_agent.rs index c44d8e14..8f56ea98 100644 --- a/src/crates/core/src/agentic/agents/file_finder_agent.rs +++ b/src/crates/core/src/agentic/agents/file_finder_agent.rs @@ -5,12 +5,6 @@ pub struct FileFinderAgent { default_tools: Vec, } -impl Default for FileFinderAgent { - fn default() -> Self { - Self::new() - } -} - impl FileFinderAgent { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/agents/generate_doc_agent.rs b/src/crates/core/src/agentic/agents/generate_doc_agent.rs index c8c97a70..c1af554a 100644 --- a/src/crates/core/src/agentic/agents/generate_doc_agent.rs +++ b/src/crates/core/src/agentic/agents/generate_doc_agent.rs @@ -5,12 +5,6 @@ pub struct GenerateDocAgent { default_tools: Vec, } -impl Default for GenerateDocAgent { - fn default() -> Self { - Self::new() - } -} - impl GenerateDocAgent { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/agents/init_agent.rs b/src/crates/core/src/agentic/agents/init_agent.rs index a34d107e..5e95c7a4 100644 --- a/src/crates/core/src/agentic/agents/init_agent.rs +++ b/src/crates/core/src/agentic/agents/init_agent.rs @@ -5,12 +5,6 @@ pub struct InitAgent { default_tools: Vec, } -impl Default for InitAgent { - fn default() -> Self { - Self::new() - } -} - impl InitAgent { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/agents/plan_mode.rs b/src/crates/core/src/agentic/agents/plan_mode.rs index c6d19bc6..dc77b8d2 100644 --- a/src/crates/core/src/agentic/agents/plan_mode.rs +++ b/src/crates/core/src/agentic/agents/plan_mode.rs @@ -6,12 +6,6 @@ pub struct PlanMode { default_tools: Vec, } -impl Default for PlanMode { - fn default() -> Self { - Self::new() - } -} - impl PlanMode { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/agents/prompt_builder/mod.rs b/src/crates/core/src/agentic/agents/prompt_builder/mod.rs index 6d45db5c..affb34c8 100644 --- a/src/crates/core/src/agentic/agents/prompt_builder/mod.rs +++ b/src/crates/core/src/agentic/agents/prompt_builder/mod.rs @@ -1,3 +1,3 @@ -mod prompt_builder_impl; +mod prompt_builder; -pub use prompt_builder_impl::{PromptBuilder, PromptBuilderContext, RemoteExecutionHints}; +pub use prompt_builder::{PromptBuilder, PromptBuilderContext, RemoteExecutionHints}; diff --git a/src/crates/core/src/agentic/agents/prompt_builder/prompt_builder_impl.rs b/src/crates/core/src/agentic/agents/prompt_builder/prompt_builder.rs similarity index 100% rename from src/crates/core/src/agentic/agents/prompt_builder/prompt_builder_impl.rs rename to src/crates/core/src/agentic/agents/prompt_builder/prompt_builder.rs diff --git a/src/crates/core/src/agentic/agents/registry.rs b/src/crates/core/src/agentic/agents/registry.rs index 4c22c80a..d8466a3c 100644 --- a/src/crates/core/src/agentic/agents/registry.rs +++ b/src/crates/core/src/agentic/agents/registry.rs @@ -196,12 +196,6 @@ pub struct AgentRegistry { project_subagents: RwLock>>, } -impl Default for AgentRegistry { - fn default() -> Self { - Self::new() - } -} - impl AgentRegistry { fn read_agents(&self) -> std::sync::RwLockReadGuard<'_, HashMap> { match self.agents.read() { @@ -386,14 +380,14 @@ impl AgentRegistry { /// check if a subagent exists with specified source (used for duplicate check before adding) pub fn has_subagent(&self, agent_id: &str, source: SubAgentSource) -> bool { - if self.read_agents().get(agent_id).is_some_and(|e| { + if self.read_agents().get(agent_id).map_or(false, |e| { e.category == AgentCategory::SubAgent && e.subagent_source == Some(source) }) { return true; } self.read_project_subagents().values().any(|entries| { - entries.get(agent_id).is_some_and(|entry| { + entries.get(agent_id).map_or(false, |entry| { entry.category == AgentCategory::SubAgent && entry.subagent_source == Some(source) }) }) @@ -523,7 +517,7 @@ impl AgentRegistry { result.extend( project_entries .values() - .map(AgentInfo::from_agent_entry), + .map(|entry| AgentInfo::from_agent_entry(entry)), ); } } diff --git a/src/crates/core/src/agentic/coordination/coordinator.rs b/src/crates/core/src/agentic/coordination/coordinator.rs index 9e24cddc..bdea500d 100644 --- a/src/crates/core/src/agentic/coordination/coordinator.rs +++ b/src/crates/core/src/agentic/coordination/coordinator.rs @@ -856,7 +856,6 @@ Update the persona files and delete BOOTSTRAP.md as soon as bootstrap is complet /// Note: Events are sent to frontend via EventLoop, no Stream returned. /// Submission behavior is controlled by `submission_policy`, which provides /// default per-source behavior while still allowing selective overrides. - #[allow(clippy::too_many_arguments)] pub async fn start_dialog_turn( &self, session_id: String, @@ -882,7 +881,6 @@ Update the persona files and delete BOOTSTRAP.md as soon as bootstrap is complet .await } - #[allow(clippy::too_many_arguments)] pub async fn start_dialog_turn_with_image_contexts( &self, session_id: String, @@ -942,7 +940,7 @@ Update the persona files and delete BOOTSTRAP.md as soon as bootstrap is complet let needs_restore = if context_messages.is_empty() { true } else { - context_messages.len() == 1 && !session.dialog_turn_ids.is_empty() + context_messages.len() == 1 && session.dialog_turn_ids.len() > 0 }; if needs_restore { @@ -1067,7 +1065,6 @@ Update the persona files and delete BOOTSTRAP.md as soon as bootstrap is complet } } - #[allow(clippy::too_many_arguments)] async fn start_dialog_turn_internal( &self, session_id: String, @@ -1193,7 +1190,7 @@ Update the persona files and delete BOOTSTRAP.md as soon as bootstrap is complet session_id ); true - } else if context_messages.len() == 1 && !session.dialog_turn_ids.is_empty() { + } else if context_messages.len() == 1 && session.dialog_turn_ids.len() > 0 { debug!( "Session {} has {} turns but only {} messages, restoring history", session_id, @@ -1882,10 +1879,8 @@ Update the persona files and delete BOOTSTRAP.md as soon as bootstrap is complet "workspace_path is required when creating a subagent session".to_string(), ) })?; - let subagent_config = SessionConfig { - workspace_path: Some(workspace_path), - ..SessionConfig::default() - }; + let mut subagent_config = SessionConfig::default(); + subagent_config.workspace_path = Some(workspace_path); let session = self .create_subagent_session( format!("Subagent: {}", task_description), @@ -2145,7 +2140,6 @@ Update the persona files and delete BOOTSTRAP.md as soon as bootstrap is complet } /// Persist a completed `/btw` side-question turn into an existing child session. - #[allow(clippy::too_many_arguments)] pub async fn persist_btw_turn( &self, workspace_path: &Path, diff --git a/src/crates/core/src/agentic/coordination/scheduler.rs b/src/crates/core/src/agentic/coordination/scheduler.rs index faf3f2da..208bfeec 100644 --- a/src/crates/core/src/agentic/coordination/scheduler.rs +++ b/src/crates/core/src/agentic/coordination/scheduler.rs @@ -214,7 +214,6 @@ impl DialogScheduler { /// - Session error → queue cleared, dispatched immediately. /// /// Returns `Err(String)` if the queue is full or the coordinator returns an error. - #[allow(clippy::too_many_arguments)] pub async fn submit( &self, session_id: String, diff --git a/src/crates/core/src/agentic/core/message.rs b/src/crates/core/src/agentic/core/message.rs index 66d328c6..acd8263b 100644 --- a/src/crates/core/src/agentic/core/message.rs +++ b/src/crates/core/src/agentic/core/message.rs @@ -4,7 +4,6 @@ use crate::util::types::{Message as AIMessage, ToolCall as AIToolCall, ToolImage use crate::util::TokenCounter; use log::warn; use serde::{Deserialize, Serialize}; -use std::fmt::{self, Display}; use std::time::SystemTime; use uuid::Uuid; @@ -248,7 +247,7 @@ impl From for AIMessage { // Convert serde_json::Value to HashMap let arguments = if let serde_json::Value::Object(map) = tc.arguments { - map.into_iter().collect() + map.into_iter().map(|(k, v)| (k, v)).collect() } else { std::collections::HashMap::new() }; @@ -496,8 +495,8 @@ impl Message { }) .unwrap_or((1024, 1024)); - let tiles_w = width.div_ceil(512); - let tiles_h = height.div_ceil(512); + let tiles_w = (width + 511) / 512; + let tiles_h = (height + 511) / 512; let tiles = (tiles_w.max(1) * tiles_h.max(1)) as usize; 50 + tiles * 200 } @@ -562,12 +561,11 @@ impl Message { } } -impl Display for MessageContent { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl ToString for MessageContent { + fn to_string(&self) -> String { match self { - MessageContent::Text(text) => write!(f, "{}", text), - MessageContent::Multimodal { text, images } => write!( - f, + MessageContent::Text(text) => text.clone(), + MessageContent::Multimodal { text, images } => format!( "Multimodal: text_length={}, images={}", text.len(), images.len() @@ -579,34 +577,36 @@ impl Display for MessageContent { result_for_assistant, is_error, image_attachments, - } => write!( - f, - "ToolResult: tool_id={}, tool_name={}, result={}, result_for_assistant={:?}, is_error={}, images={}", - tool_id, - tool_name, - result, - result_for_assistant, - is_error, - image_attachments.as_ref().map(|v| v.len()).unwrap_or(0) - ), + } => { + format!( + "ToolResult: tool_id={}, tool_name={}, result={}, result_for_assistant={:?}, is_error={}, images={}", + tool_id, + tool_name, + result, + result_for_assistant, + is_error, + image_attachments.as_ref().map(|v| v.len()).unwrap_or(0) + ) + } MessageContent::Mixed { reasoning_content, text, tool_calls, - } => write!( - f, - "Mixed: reasoning_content={:?}, text={}, tool_calls={}", - reasoning_content, - text, - tool_calls - .iter() - .map(|tc| format!( - "ToolCall: tool_id={}, tool_name={}, arguments={}", - tc.tool_id, tc.tool_name, tc.arguments - )) - .collect::>() - .join(", ") - ), + } => { + format!( + "Mixed: reasoning_content={:?}, text={}, tool_calls={}", + reasoning_content, + text, + tool_calls + .iter() + .map(|tc| format!( + "ToolCall: tool_id={}, tool_name={}, arguments={}", + tc.tool_id, tc.tool_name, tc.arguments + )) + .collect::>() + .join(", ") + ) + } } } } diff --git a/src/crates/core/src/agentic/core/messages_helper.rs b/src/crates/core/src/agentic/core/messages_helper.rs index 281af38e..8e5871d8 100644 --- a/src/crates/core/src/agentic/core/messages_helper.rs +++ b/src/crates/core/src/agentic/core/messages_helper.rs @@ -5,7 +5,7 @@ pub struct MessageHelper; impl MessageHelper { pub fn compute_keep_thinking_flags( - messages: &mut [Message], + messages: &mut Vec, enable_thinking: bool, support_preserved_thinking: bool, ) { @@ -69,7 +69,7 @@ impl MessageHelper { } pub fn convert_messages(messages: &[Message]) -> Vec { - messages.iter().map(AIMessage::from).collect() + messages.iter().map(|m| AIMessage::from(m)).collect() } pub fn group_messages_by_turns(mut messages: Vec) -> Vec> { @@ -112,9 +112,11 @@ impl MessageHelper { mid_idx = Some(idx); } - if message.role == MessageRole::Assistant && delta < min_delta0 { - min_delta0 = delta; - mid_assistant_msg_idx = Some(idx); + if message.role == MessageRole::Assistant { + if delta < min_delta0 { + min_delta0 = delta; + mid_assistant_msg_idx = Some(idx); + } } // Delta will only get larger going forward, so can exit early diff --git a/src/crates/core/src/agentic/core/session.rs b/src/crates/core/src/agentic/core/session.rs index 7992e079..7523d81b 100644 --- a/src/crates/core/src/agentic/core/session.rs +++ b/src/crates/core/src/agentic/core/session.rs @@ -57,7 +57,6 @@ pub struct Session { /// Context compression state #[derive(Debug, Clone, Serialize, Deserialize)] -#[derive(Default)] pub struct CompressionState { /// Time of last compression pub last_compression_at: Option, @@ -65,6 +64,14 @@ pub struct CompressionState { pub compression_count: usize, } +impl Default for CompressionState { + fn default() -> Self { + Self { + last_compression_at: None, + compression_count: 0, + } + } +} impl CompressionState { pub fn increment_compression_count(&mut self) { diff --git a/src/crates/core/src/agentic/execution/execution_engine.rs b/src/crates/core/src/agentic/execution/execution_engine.rs index dd6c5111..3acced74 100644 --- a/src/crates/core/src/agentic/execution/execution_engine.rs +++ b/src/crates/core/src/agentic/execution/execution_engine.rs @@ -447,7 +447,6 @@ impl ExecutionEngine { } /// Compress context, will emit compression events (Started, Completed, and Failed) - #[allow(clippy::too_many_arguments)] pub async fn compress_messages( &self, session_id: &str, @@ -587,7 +586,6 @@ impl ExecutionEngine { /// Compact the current session context outside the normal dialog execution loop. /// Always emits compression started/completed/failed events for the provided turn. - #[allow(clippy::too_many_arguments)] pub async fn compact_session_context( &self, session_id: &str, diff --git a/src/crates/core/src/agentic/execution/stream_processor.rs b/src/crates/core/src/agentic/execution/stream_processor.rs index 97ea7df3..782f40ac 100644 --- a/src/crates/core/src/agentic/execution/stream_processor.rs +++ b/src/crates/core/src/agentic/execution/stream_processor.rs @@ -26,12 +26,16 @@ use tokio::sync::mpsc; /// SSE log collector configuration #[derive(Debug, Clone)] -#[derive(Default)] pub struct SseLogConfig { /// Maximum number of SSE data entries to output on error, None means unlimited pub max_output: Option, } +impl Default for SseLogConfig { + fn default() -> Self { + Self { max_output: None } + } +} /// SSE log collector - Collects raw SSE data, outputs only on error pub struct SseLogCollector { @@ -462,8 +466,8 @@ impl StreamProcessor { session_id: ctx.session_id.clone(), turn_id: ctx.dialog_turn_id.clone(), tool_event: ToolEventData::EarlyDetected { - tool_id, - tool_name, + tool_id: tool_id, + tool_name: tool_name, }, subagent_parent_info: ctx.event_subagent_parent_info.clone(), }, @@ -614,7 +618,6 @@ impl StreamProcessor { /// * `round_id` - Model round ID /// * `subagent_parent_info` - Subagent parent info /// * `cancellation_token` - Cancellation token - #[allow(clippy::too_many_arguments)] pub async fn process_stream( &self, mut stream: futures::stream::BoxStream<'static, Result>, diff --git a/src/crates/core/src/agentic/image_analysis/enhancer.rs b/src/crates/core/src/agentic/image_analysis/enhancer.rs index 39a86ad3..3d24e799 100644 --- a/src/crates/core/src/agentic/image_analysis/enhancer.rs +++ b/src/crates/core/src/agentic/image_analysis/enhancer.rs @@ -37,7 +37,7 @@ impl MessageEnhancer { if !analysis.detected_elements.is_empty() { enhanced.push_str("• Key elements: "); enhanced.push_str(&analysis.detected_elements.join(", ")); - enhanced.push('\n'); + enhanced.push_str("\n"); } enhanced.push_str(&format!( @@ -45,7 +45,7 @@ impl MessageEnhancer { analysis.confidence * 100.0 )); - enhanced.push('\n'); + enhanced.push_str("\n"); } } @@ -55,10 +55,10 @@ impl MessageEnhancer { for ctx in other_contexts { if let Some(formatted) = Self::format_context(ctx) { enhanced.push_str(&formatted); - enhanced.push('\n'); + enhanced.push_str("\n"); } } - enhanced.push('\n'); + enhanced.push_str("\n"); } enhanced.push_str("The above image analysis has already been performed. Do NOT suggest the user to view or re-analyze the image. Respond directly to the user's question based on the analysis.\n\n"); diff --git a/src/crates/core/src/agentic/insights/collector.rs b/src/crates/core/src/agentic/insights/collector.rs index 98404eea..be698d2b 100644 --- a/src/crates/core/src/agentic/insights/collector.rs +++ b/src/crates/core/src/agentic/insights/collector.rs @@ -299,7 +299,7 @@ impl InsightsCollector { if let Some(prev) = last_assistant_time { if let Ok(duration) = msg.timestamp.duration_since(prev) { let secs = duration.as_secs(); - if (2..=ACTIVITY_GAP_THRESHOLD_SECS).contains(&secs) { + if secs >= 2 && secs <= ACTIVITY_GAP_THRESHOLD_SECS { base_stats.response_times_raw.push(secs as f64); } } @@ -564,7 +564,11 @@ fn smart_truncate_parts(parts: &[String], max_chars: usize, tail_reserve: usize) } tail_parts.reverse(); - let omitted = tail_start_idx.saturating_sub(head_end_idx); + let omitted = if tail_start_idx > head_end_idx { + tail_start_idx - head_end_idx + } else { + 0 + }; let mut result = head_parts.join("\n"); if omitted > 0 { @@ -629,7 +633,7 @@ fn compute_response_time_stats(raw: &[f64]) -> (f64, f64) { let mut sorted = raw.to_vec(); sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); - let median = if sorted.len().is_multiple_of(2) { + let median = if sorted.len() % 2 == 0 { let mid = sorted.len() / 2; (sorted[mid - 1] + sorted[mid]) / 2.0 } else { diff --git a/src/crates/core/src/agentic/insights/html.rs b/src/crates/core/src/agentic/insights/html.rs index 44896a54..9eab26f2 100644 --- a/src/crates/core/src/agentic/insights/html.rs +++ b/src/crates/core/src/agentic/insights/html.rs @@ -1085,10 +1085,11 @@ fn find_closing_double_star(chars: &[char], start: usize) -> Option { let len = chars.len(); let mut i = start; while i + 1 < len { - if chars[i] == '*' && chars[i + 1] == '*' - && i > start { + if chars[i] == '*' && chars[i + 1] == '*' { + if i > start { return Some(i); } + } i += 1; } None @@ -1098,11 +1099,13 @@ fn find_closing_single_star(chars: &[char], start: usize) -> Option { let len = chars.len(); let mut i = start; while i < len { - if chars[i] == '*' - && (i + 1 >= len || chars[i + 1] != '*') - && i > start { + if chars[i] == '*' { + if i + 1 >= len || chars[i + 1] != '*' { + if i > start { return Some(i); } + } + } i += 1; } None diff --git a/src/crates/core/src/agentic/insights/service.rs b/src/crates/core/src/agentic/insights/service.rs index 09279282..ace6c46a 100644 --- a/src/crates/core/src/agentic/insights/service.rs +++ b/src/crates/core/src/agentic/insights/service.rs @@ -525,7 +525,7 @@ impl InsightsService { suggestions_handle, "Suggestions", || async { Self::generate_suggestions(ai_client, aggregate, lang_instruction).await }, - default_suggestions, + || default_suggestions(), ).await; let areas = Self::resolve_with_retry( @@ -793,23 +793,28 @@ impl InsightsService { .as_array() .map(|arr| { arr.iter() - .map(|v| UsagePattern { - pattern: v["pattern"] - .as_str() - .or(v["title"].as_str()) - .unwrap_or("") - .to_string(), - description: v["description"] - .as_str() - .or(v["suggestion"].as_str()) - .unwrap_or("") - .to_string(), - detail: v["detail"].as_str().unwrap_or("").to_string(), - suggested_prompt: v["suggested_prompt"] - .as_str() - .or(v["copyable_prompt"].as_str()) - .unwrap_or("") - .to_string(), + .filter_map(|v| { + Some(UsagePattern { + pattern: v["pattern"] + .as_str() + .or(v["title"].as_str()) + .unwrap_or("") + .to_string(), + description: v["description"] + .as_str() + .or(v["suggestion"].as_str()) + .unwrap_or("") + .to_string(), + detail: v["detail"] + .as_str() + .unwrap_or("") + .to_string(), + suggested_prompt: v["suggested_prompt"] + .as_str() + .or(v["copyable_prompt"].as_str()) + .unwrap_or("") + .to_string(), + }) }) .collect() }) @@ -1155,7 +1160,6 @@ impl InsightsService { // ============ Stage 5: Assembly ============ - #[allow(clippy::too_many_arguments)] fn assemble_report( _base_stats: BaseStats, aggregate: InsightsAggregate, diff --git a/src/crates/core/src/agentic/persistence/manager.rs b/src/crates/core/src/agentic/persistence/manager.rs index 7a175170..0e4ced62 100644 --- a/src/crates/core/src/agentic/persistence/manager.rs +++ b/src/crates/core/src/agentic/persistence/manager.rs @@ -640,7 +640,7 @@ impl PersistenceManager { custom_metadata: existing.and_then(|value| value.custom_metadata.clone()), todos: existing.and_then(|value| value.todos.clone()), workspace_path: Some(workspace_root), - workspace_hostname, + workspace_hostname: workspace_hostname, } } @@ -1743,7 +1743,7 @@ impl PersistenceManager { metadata.workspace_path = metadata.workspace_path.clone().or_else(|| { turns .first() - .and(None::) + .and_then(|_| None::) .or_else(|| Some(workspace_path.to_string_lossy().to_string())) }); self.save_session_metadata(workspace_path, &metadata).await diff --git a/src/crates/core/src/agentic/session/context_store.rs b/src/crates/core/src/agentic/session/context_store.rs index 3e486e97..9023ccdf 100644 --- a/src/crates/core/src/agentic/session/context_store.rs +++ b/src/crates/core/src/agentic/session/context_store.rs @@ -12,12 +12,6 @@ pub struct SessionContextStore { session_contexts: Arc>>, } -impl Default for SessionContextStore { - fn default() -> Self { - Self::new() - } -} - impl SessionContextStore { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/session/session_manager.rs b/src/crates/core/src/agentic/session/session_manager.rs index 25ddf159..e02c00a7 100644 --- a/src/crates/core/src/agentic/session/session_manager.rs +++ b/src/crates/core/src/agentic/session/session_manager.rs @@ -1010,7 +1010,6 @@ impl SessionManager { // ============ Dialog Turn Management ============ - #[allow(clippy::too_many_arguments)] async fn start_persisted_turn( &self, session_id: &str, @@ -1411,7 +1410,6 @@ impl SessionManager { } /// Persist a completed `/btw` side-question turn into an existing child session. - #[allow(clippy::too_many_arguments)] pub async fn persist_btw_turn( &self, workspace_path: &Path, diff --git a/src/crates/core/src/agentic/side_question.rs b/src/crates/core/src/agentic/side_question.rs index 41293e14..68c2f733 100644 --- a/src/crates/core/src/agentic/side_question.rs +++ b/src/crates/core/src/agentic/side_question.rs @@ -24,12 +24,6 @@ pub struct SideQuestionRuntime { tokens: Arc>>, } -impl Default for SideQuestionRuntime { - fn default() -> Self { - Self::new() - } -} - impl SideQuestionRuntime { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/tools/implementations/ask_user_question_tool.rs b/src/crates/core/src/agentic/tools/implementations/ask_user_question_tool.rs index 2d8d6baf..b831c08a 100644 --- a/src/crates/core/src/agentic/tools/implementations/ask_user_question_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/ask_user_question_tool.rs @@ -40,12 +40,6 @@ pub struct AskUserQuestionInput { /// AskUserQuestion tool pub struct AskUserQuestionTool; -impl Default for AskUserQuestionTool { - fn default() -> Self { - Self::new() - } -} - impl AskUserQuestionTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/bash_tool.rs b/src/crates/core/src/agentic/tools/implementations/bash_tool.rs index 1fe4dbaa..47312af8 100644 --- a/src/crates/core/src/agentic/tools/implementations/bash_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/bash_tool.rs @@ -80,12 +80,6 @@ struct ResolvedShell { /// Bash tool pub struct BashTool; -impl Default for BashTool { - fn default() -> Self { - Self::new() - } -} - impl BashTool { pub fn new() -> Self { Self @@ -783,7 +777,6 @@ Usage notes: impl BashTool { /// Execute a command in a new background terminal session. /// Returns immediately with the new session ID. - #[allow(clippy::too_many_arguments)] async fn call_background( &self, command_str: &str, diff --git a/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_click_tool.rs b/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_click_tool.rs index 7581835f..bd7fb0d7 100644 --- a/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_click_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_click_tool.rs @@ -10,12 +10,6 @@ use serde_json::{json, Value}; pub struct ComputerUseMouseClickTool; -impl Default for ComputerUseMouseClickTool { - fn default() -> Self { - Self::new() - } -} - impl ComputerUseMouseClickTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_precise_tool.rs b/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_precise_tool.rs index 6757d262..d93b72d7 100644 --- a/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_precise_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_precise_tool.rs @@ -10,12 +10,6 @@ use serde_json::{json, Value}; pub struct ComputerUseMousePreciseTool; -impl Default for ComputerUseMousePreciseTool { - fn default() -> Self { - Self::new() - } -} - impl ComputerUseMousePreciseTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_step_tool.rs b/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_step_tool.rs index e05508d7..07145ecd 100644 --- a/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_step_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/computer_use_mouse_step_tool.rs @@ -10,12 +10,6 @@ use serde_json::{json, Value}; pub struct ComputerUseMouseStepTool; -impl Default for ComputerUseMouseStepTool { - fn default() -> Self { - Self::new() - } -} - impl ComputerUseMouseStepTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/computer_use_tool.rs b/src/crates/core/src/agentic/tools/implementations/computer_use_tool.rs index 441e6961..61227040 100644 --- a/src/crates/core/src/agentic/tools/implementations/computer_use_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/computer_use_tool.rs @@ -88,12 +88,6 @@ const COMPUTER_USE_DEBUG_SUBDIR: &str = ".bitfun/computer_use_debug"; pub struct ComputerUseTool; -impl Default for ComputerUseTool { - fn default() -> Self { - Self::new() - } -} - impl ComputerUseTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/create_plan_tool.rs b/src/crates/core/src/agentic/tools/implementations/create_plan_tool.rs index d11d53df..f315e5c8 100644 --- a/src/crates/core/src/agentic/tools/implementations/create_plan_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/create_plan_tool.rs @@ -208,7 +208,7 @@ Additional guidelines: .workspace_root() .ok_or(BitFunError::tool("Workspace path not set".to_string()))?; let path_manager = get_path_manager_arc(); - let plans_dir = path_manager.project_plans_dir(workspace_path); + let plans_dir = path_manager.project_plans_dir(&workspace_path); let plan_file_path = plans_dir.join(&plan_file_name); path_manager .ensure_dir(&plans_dir) diff --git a/src/crates/core/src/agentic/tools/implementations/cron_tool.rs b/src/crates/core/src/agentic/tools/implementations/cron_tool.rs index 17483353..98d588ae 100644 --- a/src/crates/core/src/agentic/tools/implementations/cron_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/cron_tool.rs @@ -113,7 +113,7 @@ impl CronTool { ) })?; self.resolve_workspace( - workspace.to_string_lossy().as_ref(), + &workspace.to_string_lossy().to_string(), Some(context), ) } diff --git a/src/crates/core/src/agentic/tools/implementations/delete_file_tool.rs b/src/crates/core/src/agentic/tools/implementations/delete_file_tool.rs index 2a72a3ba..2f5164e3 100644 --- a/src/crates/core/src/agentic/tools/implementations/delete_file_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/delete_file_tool.rs @@ -13,12 +13,6 @@ use tokio::fs; /// This tool automatically integrates with the snapshot system, all deletion operations are recorded and support rollback pub struct DeleteFileTool; -impl Default for DeleteFileTool { - fn default() -> Self { - Self::new() - } -} - impl DeleteFileTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/file_edit_tool.rs b/src/crates/core/src/agentic/tools/implementations/file_edit_tool.rs index 811270b5..5933b785 100644 --- a/src/crates/core/src/agentic/tools/implementations/file_edit_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/file_edit_tool.rs @@ -6,12 +6,6 @@ use tool_runtime::fs::edit_file::edit_file; pub struct FileEditTool; -impl Default for FileEditTool { - fn default() -> Self { - Self::new() - } -} - impl FileEditTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/file_read_tool.rs b/src/crates/core/src/agentic/tools/implementations/file_read_tool.rs index ae4f1385..bcd7b459 100644 --- a/src/crates/core/src/agentic/tools/implementations/file_read_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/file_read_tool.rs @@ -16,12 +16,6 @@ pub struct FileReadTool { max_total_chars: usize, } -impl Default for FileReadTool { - fn default() -> Self { - Self::new() - } -} - impl FileReadTool { pub fn new() -> Self { Self { @@ -328,7 +322,7 @@ Usage: self.max_line_chars, self.max_total_chars, ) - .map_err(BitFunError::tool)? + .map_err(|e| BitFunError::tool(e))? }; let file_rules = match get_global_ai_rules_service().await { diff --git a/src/crates/core/src/agentic/tools/implementations/file_write_tool.rs b/src/crates/core/src/agentic/tools/implementations/file_write_tool.rs index 2ee516dd..42895ee6 100644 --- a/src/crates/core/src/agentic/tools/implementations/file_write_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/file_write_tool.rs @@ -10,12 +10,6 @@ use tokio::fs; pub struct FileWriteTool; -impl Default for FileWriteTool { - fn default() -> Self { - Self::new() - } -} - impl FileWriteTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/get_file_diff_tool.rs b/src/crates/core/src/agentic/tools/implementations/get_file_diff_tool.rs index 08c1e2a3..f63c9dde 100644 --- a/src/crates/core/src/agentic/tools/implementations/get_file_diff_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/get_file_diff_tool.rs @@ -23,12 +23,6 @@ use std::path::Path; /// 3. Return full file content pub struct GetFileDiffTool; -impl Default for GetFileDiffTool { - fn default() -> Self { - Self::new() - } -} - impl GetFileDiffTool { pub fn new() -> Self { Self @@ -483,7 +477,7 @@ Usage: // Priority 1: Try baseline diff let path = Path::new(&resolved_path); if let Some(result) = self - .try_baseline_diff(path, context.workspace_root()) + .try_baseline_diff(&path, context.workspace_root()) .await { match result { @@ -507,7 +501,7 @@ Usage: } // Priority 2: Try git diff - if let Some(result) = self.try_git_diff(path).await { + if let Some(result) = self.try_git_diff(&path).await { match result { Ok(data) => { debug!("GetFileDiff tool using git diff"); @@ -530,7 +524,7 @@ Usage: // Priority 3: Return full file content debug!("GetFileDiff tool returning full file content"); - let data = self.return_full_content(path)?; + let data = self.return_full_content(&path)?; let result_for_assistant = self.render_tool_result_message(&data); Ok(vec![ToolResult::Result { diff --git a/src/crates/core/src/agentic/tools/implementations/git_tool.rs b/src/crates/core/src/agentic/tools/implementations/git_tool.rs index 82cc9bfd..eb1e38d3 100644 --- a/src/crates/core/src/agentic/tools/implementations/git_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/git_tool.rs @@ -374,7 +374,7 @@ impl GitTool { .collect(); let params = GitPushParams { - remote: parts.first().map(|s| s.to_string()), + remote: parts.get(0).map(|s| s.to_string()), branch: parts.get(1).map(|s| s.to_string()), force: Some(args_str.contains("--force") || args_str.contains("-f")), set_upstream: Some(args_str.contains("-u") || args_str.contains("--set-upstream")), @@ -402,7 +402,7 @@ impl GitTool { .collect(); let params = GitPullParams { - remote: parts.first().map(|s| s.to_string()), + remote: parts.get(0).map(|s| s.to_string()), branch: parts.get(1).map(|s| s.to_string()), rebase: Some(args_str.contains("--rebase")), }; @@ -427,13 +427,17 @@ impl GitTool { // Extract branch name let branch_name = args_str - .split_whitespace().rfind(|s| !s.starts_with('-')) + .split_whitespace() + .filter(|s| !s.starts_with('-')) + .last() .ok_or_else(|| BitFunError::tool("Branch name is required".to_string()))?; let result = if create_branch { // Create and switch to new branch let start_point = args_str - .split_whitespace().rfind(|s| !s.starts_with('-') && *s != branch_name); + .split_whitespace() + .filter(|s| !s.starts_with('-') && *s != branch_name) + .last(); GitService::create_branch(repo_path, branch_name, start_point).await } else { // Switch to existing branch @@ -489,7 +493,9 @@ impl GitTool { // Delete branch let force = args_str.contains("-D"); let branch_name = args_str - .split_whitespace().find(|s| !s.starts_with('-')) + .split_whitespace() + .filter(|s| !s.starts_with('-')) + .next() .ok_or_else(|| { BitFunError::tool("Branch name is required for deletion".to_string()) })?; diff --git a/src/crates/core/src/agentic/tools/implementations/glob_tool.rs b/src/crates/core/src/agentic/tools/implementations/glob_tool.rs index dd06459c..c0487806 100644 --- a/src/crates/core/src/agentic/tools/implementations/glob_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/glob_tool.rs @@ -150,7 +150,7 @@ fn build_fallback_matcher(relative_pattern: &str) -> Result fn match_relative_path(matcher: &GlobMatcher, relative_path: &str, is_dir: bool) -> bool { if is_dir { - matcher.is_match(relative_path) || matcher.is_match(format!("{}/", relative_path)) + matcher.is_match(relative_path) || matcher.is_match(&format!("{}/", relative_path)) } else { matcher.is_match(relative_path) } @@ -390,12 +390,6 @@ fn build_remote_find_command(search_dir: &str, pattern: &str, limit: usize) -> S pub struct GlobTool; -impl Default for GlobTool { - fn default() -> Self { - Self::new() - } -} - impl GlobTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/grep_tool.rs b/src/crates/core/src/agentic/tools/implementations/grep_tool.rs index c81e5676..afa3ada7 100644 --- a/src/crates/core/src/agentic/tools/implementations/grep_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/grep_tool.rs @@ -1,8 +1,7 @@ -use crate::agentic::tools::framework::{Tool, ToolResult, ToolUseContext}; +use crate::agentic::tools::framework::{Tool, ToolResult, ToolUseContext}; use crate::util::errors::{BitFunError, BitFunResult}; use async_trait::async_trait; use serde_json::{json, Value}; -use std::str::FromStr; use std::sync::Arc; use tool_runtime::search::grep_search::{ grep_search, GrepOptions, GrepSearchResult, OutputMode, ProgressCallback, @@ -12,12 +11,6 @@ const DEFAULT_HEAD_LIMIT: usize = 250; pub struct GrepTool; -impl Default for GrepTool { - fn default() -> Self { - Self::new() - } -} - impl GrepTool { pub fn new() -> Self { Self @@ -246,7 +239,7 @@ impl GrepTool { .get("output_mode") .and_then(|v| v.as_str()) .unwrap_or("files_with_matches"); - let output_mode = OutputMode::from_str(output_mode_str).unwrap_or(OutputMode::FilesWithMatches); + let output_mode = OutputMode::from_str(output_mode_str); let show_line_numbers = input .get("-n") .and_then(|v| v.as_bool()) diff --git a/src/crates/core/src/agentic/tools/implementations/log_tool.rs b/src/crates/core/src/agentic/tools/implementations/log_tool.rs index 93d7bfa9..4dcc1b2b 100644 --- a/src/crates/core/src/agentic/tools/implementations/log_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/log_tool.rs @@ -25,12 +25,6 @@ pub struct LogToolInput { pub level: Option, // Log level filter: "error", "warn", "info", "debug" } -impl Default for LogTool { - fn default() -> Self { - Self::new() - } -} - impl LogTool { pub fn new() -> Self { Self @@ -96,7 +90,7 @@ impl LogTool { } if results.is_empty() { - Ok("No matching log records found".to_string()) + Ok(format!("No matching log records found")) } else { Ok(format!( "Found {} matching records:\n{}", diff --git a/src/crates/core/src/agentic/tools/implementations/ls_tool.rs b/src/crates/core/src/agentic/tools/implementations/ls_tool.rs index ae35f392..f8a79477 100644 --- a/src/crates/core/src/agentic/tools/implementations/ls_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/ls_tool.rs @@ -23,12 +23,6 @@ pub struct LSTool { default_limit: usize, } -impl Default for LSTool { - fn default() -> Self { - Self::new() - } -} - impl LSTool { pub fn new() -> Self { Self { default_limit: 200 } @@ -277,7 +271,7 @@ Usage: .collect::>() }); - let entries = list_files(path, limit, ignore_patterns).map_err(BitFunError::tool)?; + let entries = list_files(path, limit, ignore_patterns).map_err(|e| BitFunError::tool(e))?; let entries_json = entries .iter() diff --git a/src/crates/core/src/agentic/tools/implementations/mcp_tools.rs b/src/crates/core/src/agentic/tools/implementations/mcp_tools.rs index abdae47b..32d7d207 100644 --- a/src/crates/core/src/agentic/tools/implementations/mcp_tools.rs +++ b/src/crates/core/src/agentic/tools/implementations/mcp_tools.rs @@ -200,12 +200,6 @@ fn render_prompt_catalog(prompts: &[MCPPrompt]) -> String { pub struct ListMCPResourcesTool; -impl Default for ListMCPResourcesTool { - fn default() -> Self { - Self::new() - } -} - impl ListMCPResourcesTool { pub fn new() -> Self { Self @@ -307,12 +301,6 @@ pub struct ReadMCPResourceTool { max_render_chars: usize, } -impl Default for ReadMCPResourceTool { - fn default() -> Self { - Self::new() - } -} - impl ReadMCPResourceTool { pub fn new() -> Self { Self { @@ -422,12 +410,6 @@ impl Tool for ReadMCPResourceTool { pub struct ListMCPPromptsTool; -impl Default for ListMCPPromptsTool { - fn default() -> Self { - Self::new() - } -} - impl ListMCPPromptsTool { pub fn new() -> Self { Self @@ -529,12 +511,6 @@ pub struct GetMCPPromptTool { max_render_chars: usize, } -impl Default for GetMCPPromptTool { - fn default() -> Self { - Self::new() - } -} - impl GetMCPPromptTool { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/agentic/tools/implementations/mermaid_interactive_tool.rs b/src/crates/core/src/agentic/tools/implementations/mermaid_interactive_tool.rs index ee284a30..c8daddcb 100644 --- a/src/crates/core/src/agentic/tools/implementations/mermaid_interactive_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/mermaid_interactive_tool.rs @@ -13,12 +13,6 @@ use serde_json::{json, Value}; /// Mermaid interactive diagram tool pub struct MermaidInteractiveTool; -impl Default for MermaidInteractiveTool { - fn default() -> Self { - Self::new() - } -} - impl MermaidInteractiveTool { pub fn new() -> Self { Self @@ -85,26 +79,29 @@ impl MermaidInteractiveTool { } // Check if sequenceDiagram has participants - if trimmed.starts_with("sequenceDiagram") - && !trimmed.contains("participant") + if trimmed.starts_with("sequenceDiagram") { + if !trimmed.contains("participant") && !trimmed.contains("->>") && !trimmed.contains("-->>") { return (false, Some("Sequence diagram (sequenceDiagram) must contain participant definitions and interaction arrows. Example: participant A\nA->>B: Message".to_string())); } + } // Check if classDiagram has class definitions - if trimmed.starts_with("classDiagram") - && !trimmed.contains("class ") && !trimmed.contains("<|--") && !trimmed.contains("..>") + if trimmed.starts_with("classDiagram") { + if !trimmed.contains("class ") && !trimmed.contains("<|--") && !trimmed.contains("..>") { return (false, Some("Class diagram (classDiagram) must contain class definitions and relationships. Example: class A\nclass B\nA <|-- B".to_string())); } + } // Check if stateDiagram has state definitions - if trimmed.starts_with("stateDiagram") - && !trimmed.contains("state ") && !trimmed.contains("[*]") && !trimmed.contains("-->") { + if trimmed.starts_with("stateDiagram") { + if !trimmed.contains("state ") && !trimmed.contains("[*]") && !trimmed.contains("-->") { return (false, Some("State diagram (stateDiagram) must contain state definitions and transitions. Example: state A\n[*] --> A".to_string())); } + } // Check for unclosed brackets let open_brackets = trimmed.matches('[').count(); diff --git a/src/crates/core/src/agentic/tools/implementations/session_control_tool.rs b/src/crates/core/src/agentic/tools/implementations/session_control_tool.rs index 13d6910c..548bfa62 100644 --- a/src/crates/core/src/agentic/tools/implementations/session_control_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/session_control_tool.rs @@ -14,12 +14,6 @@ use std::time::SystemTime; /// SessionControl tool - create, delete, or list persisted sessions pub struct SessionControlTool; -impl Default for SessionControlTool { - fn default() -> Self { - Self::new() - } -} - impl SessionControlTool { pub fn new() -> Self { Self @@ -33,7 +27,7 @@ impl SessionControlTool { let current_session_id = context.session_id.as_deref()?; let current_workspace = context.workspace_root()?; let normalized_current_workspace = - normalize_path(current_workspace.to_string_lossy().as_ref()); + normalize_path(¤t_workspace.to_string_lossy().to_string()); if normalized_current_workspace == workspace { Some(current_session_id) diff --git a/src/crates/core/src/agentic/tools/implementations/session_history_tool.rs b/src/crates/core/src/agentic/tools/implementations/session_history_tool.rs index 23183cb0..55f53bcf 100644 --- a/src/crates/core/src/agentic/tools/implementations/session_history_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/session_history_tool.rs @@ -15,12 +15,6 @@ use std::sync::Arc; /// SessionHistory tool - export a grep-friendly transcript file for a session. pub struct SessionHistoryTool; -impl Default for SessionHistoryTool { - fn default() -> Self { - Self::new() - } -} - impl SessionHistoryTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/session_message_tool.rs b/src/crates/core/src/agentic/tools/implementations/session_message_tool.rs index 9311056b..8e638719 100644 --- a/src/crates/core/src/agentic/tools/implementations/session_message_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/session_message_tool.rs @@ -17,12 +17,6 @@ use std::path::Path; /// SessionMessage tool - send a message to another session via the dialog scheduler pub struct SessionMessageTool; -impl Default for SessionMessageTool { - fn default() -> Self { - Self::new() - } -} - impl SessionMessageTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/skill_tool.rs b/src/crates/core/src/agentic/tools/implementations/skill_tool.rs index 24139356..23e8cb7f 100644 --- a/src/crates/core/src/agentic/tools/implementations/skill_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/skill_tool.rs @@ -159,7 +159,7 @@ impl Tool for SkillTool { if input .get("command") .and_then(|v| v.as_str()) - .is_none_or(|s| s.is_empty()) + .map_or(true, |s| s.is_empty()) { return ValidationResult { result: false, diff --git a/src/crates/core/src/agentic/tools/implementations/skills/registry.rs b/src/crates/core/src/agentic/tools/implementations/skills/registry.rs index bd58372a..26335353 100644 --- a/src/crates/core/src/agentic/tools/implementations/skills/registry.rs +++ b/src/crates/core/src/agentic/tools/implementations/skills/registry.rs @@ -331,8 +331,9 @@ impl SkillRegistry { remote_root: &str, ) -> Vec { let mut roots = Vec::new(); + let mut priority = 0usize; let root = remote_root.trim_end_matches('/'); - for (priority, (parent, sub, slot)) in PROJECT_SKILL_SLOTS.iter().enumerate() { + for (parent, sub, slot) in PROJECT_SKILL_SLOTS { let path = format!("{}/{}/{}", root, parent, sub); if fs.is_dir(&path).await.unwrap_or(false) { roots.push(RemoteSkillRootEntry { @@ -341,6 +342,7 @@ impl SkillRegistry { priority, }); } + priority += 1; } let mut skills = Vec::new(); diff --git a/src/crates/core/src/agentic/tools/implementations/task_tool.rs b/src/crates/core/src/agentic/tools/implementations/task_tool.rs index a6bf7079..951fe4c8 100644 --- a/src/crates/core/src/agentic/tools/implementations/task_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/task_tool.rs @@ -12,12 +12,6 @@ use std::path::Path; pub struct TaskTool; -impl Default for TaskTool { - fn default() -> Self { - Self::new() - } -} - impl TaskTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/terminal_control_tool.rs b/src/crates/core/src/agentic/tools/implementations/terminal_control_tool.rs index 28bbf456..4cf6ccff 100644 --- a/src/crates/core/src/agentic/tools/implementations/terminal_control_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/terminal_control_tool.rs @@ -10,12 +10,6 @@ use terminal_core::{CloseSessionRequest, SignalRequest, TerminalApi}; /// TerminalControl tool - kill or interrupt a terminal session pub struct TerminalControlTool; -impl Default for TerminalControlTool { - fn default() -> Self { - Self::new() - } -} - impl TerminalControlTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/fs/edit_file.rs b/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/fs/edit_file.rs index 5b5c8bc0..6978a119 100644 --- a/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/fs/edit_file.rs +++ b/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/fs/edit_file.rs @@ -1,4 +1,4 @@ -use crate::util::string::normalize_string; +use crate::util::string::normalize_string; use std::fs; /// Edit result, contains line number range information @@ -45,7 +45,7 @@ pub fn edit_file( let matches: Vec<_> = normalized_content.match_indices(&normalized_old).collect(); if matches.is_empty() { - return Err("old_string not found in file.".to_string()); + return Err(format!("old_string not found in file.")); } if matches.len() > 1 && !replace_all { @@ -75,7 +75,7 @@ pub fn edit_file( new_content = new_content.replace("\n", "\r\n"); } - fs::write(file_path, &new_content) + fs::write(&file_path, &new_content) .map_err(|e| format!("Failed to write file {}: {}", file_path, e))?; Ok(EditResult { diff --git a/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/fs/read_file.rs b/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/fs/read_file.rs index f0103333..f66a0b19 100644 --- a/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/fs/read_file.rs +++ b/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/fs/read_file.rs @@ -1,4 +1,4 @@ -use crate::util::string::truncate_string_by_chars; +use crate::util::string::truncate_string_by_chars; use std::fs::File; use std::io::BufRead; use std::io::BufReader; @@ -21,10 +21,10 @@ pub fn read_file( max_total_chars: usize, ) -> Result { if start_line == 0 { - return Err("`start_line` should start from 1".to_string()); + return Err(format!("`start_line` should start from 1",)); } if limit == 0 { - return Err("`limit` can't be 0".to_string()); + return Err(format!("`limit` can't be 0")); } if max_total_chars == 0 { return Err("`max_total_chars` can't be 0".to_string()); diff --git a/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/search/grep_search.rs b/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/search/grep_search.rs index 9ffaecfe..694ffb0d 100644 --- a/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/search/grep_search.rs +++ b/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/search/grep_search.rs @@ -1,5 +1,4 @@ -use log::{debug, info, warn}; -use std::fmt; +use log::{debug, info, warn}; use std::io; use std::path::{Component, Path, PathBuf}; use std::sync::{Arc, Mutex}; @@ -22,25 +21,21 @@ pub enum OutputMode { Count, } -impl std::str::FromStr for OutputMode { - type Err = String; - - fn from_str(s: &str) -> Result { +impl OutputMode { + pub fn from_str(s: &str) -> Self { match s { - "content" => Ok(OutputMode::Content), - "count" => Ok(OutputMode::Count), - "files_with_matches" => Ok(OutputMode::FilesWithMatches), - _ => Err(format!("Unknown output mode: {}", s)), + "content" => OutputMode::Content, + "count" => OutputMode::Count, + "files_with_matches" => OutputMode::FilesWithMatches, + _ => OutputMode::Content, // Default to Content mode } } -} -impl fmt::Display for OutputMode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + pub fn to_string(&self) -> String { match self { - OutputMode::Content => write!(f, "content"), - OutputMode::Count => write!(f, "count"), - OutputMode::FilesWithMatches => write!(f, "files_with_matches"), + OutputMode::Content => "content".to_string(), + OutputMode::Count => "count".to_string(), + OutputMode::FilesWithMatches => "files_with_matches".to_string(), } } } diff --git a/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/util/string.rs b/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/util/string.rs index 5164814a..4d3a2f8d 100644 --- a/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/util/string.rs +++ b/src/crates/core/src/agentic/tools/implementations/tool-runtime/src/util/string.rs @@ -1,4 +1,4 @@ -pub fn normalize_string(s: &str) -> String { +pub fn normalize_string(s: &str) -> String { if s.contains("\r\n") { s.replace("\r\n", "\n") } else { @@ -8,5 +8,5 @@ pub fn truncate_string_by_chars(s: &str, kept_chars: usize) -> String { let chars: Vec = s.chars().collect(); - chars[..kept_chars].iter().collect() + chars[..kept_chars].into_iter().collect() } diff --git a/src/crates/core/src/agentic/tools/implementations/web_tools.rs b/src/crates/core/src/agentic/tools/implementations/web_tools.rs index 444dad90..ce04302f 100644 --- a/src/crates/core/src/agentic/tools/implementations/web_tools.rs +++ b/src/crates/core/src/agentic/tools/implementations/web_tools.rs @@ -31,12 +31,6 @@ struct ExaContent { pub struct WebSearchTool; -impl Default for WebSearchTool { - fn default() -> Self { - Self::new() - } -} - impl WebSearchTool { pub fn new() -> Self { Self @@ -360,12 +354,6 @@ Advanced features: /// WebFetch tool pub struct WebFetchTool; -impl Default for WebFetchTool { - fn default() -> Self { - Self::new() - } -} - impl WebFetchTool { pub fn new() -> Self { Self diff --git a/src/crates/core/src/agentic/tools/pipeline/state_manager.rs b/src/crates/core/src/agentic/tools/pipeline/state_manager.rs index b809ce09..1e36d7e3 100644 --- a/src/crates/core/src/agentic/tools/pipeline/state_manager.rs +++ b/src/crates/core/src/agentic/tools/pipeline/state_manager.rs @@ -230,10 +230,8 @@ impl ToolStateManager { pub fn get_stats(&self) -> ToolStats { let tasks: Vec<_> = self.tasks.iter().map(|e| e.value().clone()).collect(); - let mut stats = ToolStats { - total: tasks.len(), - ..ToolStats::default() - }; + let mut stats = ToolStats::default(); + stats.total = tasks.len(); for task in tasks { match task.state { diff --git a/src/crates/core/src/agentic/tools/pipeline/tool_pipeline.rs b/src/crates/core/src/agentic/tools/pipeline/tool_pipeline.rs index 02e550d8..f1408357 100644 --- a/src/crates/core/src/agentic/tools/pipeline/tool_pipeline.rs +++ b/src/crates/core/src/agentic/tools/pipeline/tool_pipeline.rs @@ -96,8 +96,8 @@ fn generate_default_assistant_text(tool_name: &str, data: &serde_json::Value) -> } // If it is an empty object or empty array - if (data.is_object() && data.as_object().is_some_and(|o| o.is_empty())) - || (data.is_array() && data.as_array().is_some_and(|a| a.is_empty())) + if (data.is_object() && data.as_object().map_or(false, |o| o.is_empty())) + || (data.is_array() && data.as_array().map_or(false, |a| a.is_empty())) { return Some(format!( "Tool {} completed, returned empty result.", @@ -504,7 +504,10 @@ impl ToolPipeline { timeout_secs, tool_name ); // There is a timeout limit - timeout(Duration::from_secs(timeout_secs), rx).await.ok() + match timeout(Duration::from_secs(timeout_secs), rx).await { + Ok(result) => Some(result), + Err(_) => None, + } } None => { debug!( diff --git a/src/crates/core/src/agentic/tools/registry.rs b/src/crates/core/src/agentic/tools/registry.rs index 06a14a8c..d4799989 100644 --- a/src/crates/core/src/agentic/tools/registry.rs +++ b/src/crates/core/src/agentic/tools/registry.rs @@ -12,12 +12,6 @@ pub struct ToolRegistry { tools: IndexMap>, } -impl Default for ToolRegistry { - fn default() -> Self { - Self::new() - } -} - impl ToolRegistry { /// Create a new tool registry pub fn new() -> Self { diff --git a/src/crates/core/src/agentic/tools/user_input_manager.rs b/src/crates/core/src/agentic/tools/user_input_manager.rs index 73cc1a74..bd0e8c63 100644 --- a/src/crates/core/src/agentic/tools/user_input_manager.rs +++ b/src/crates/core/src/agentic/tools/user_input_manager.rs @@ -21,12 +21,6 @@ pub struct UserInputManager { channels: Arc>>, } -impl Default for UserInputManager { - fn default() -> Self { - Self::new() - } -} - impl UserInputManager { /// Create a new manager instance pub fn new() -> Self { diff --git a/src/crates/core/src/agentic/util/list_files.rs b/src/crates/core/src/agentic/util/list_files.rs index a20472ce..6cc17b6d 100644 --- a/src/crates/core/src/agentic/util/list_files.rs +++ b/src/crates/core/src/agentic/util/list_files.rs @@ -33,7 +33,7 @@ impl CompiledGlob { if is_dir { // For directories, try matching with and without trailing slash self.matcher.is_match(rel_path_str) - || self.matcher.is_match(format!("{}/", rel_path_str)) + || self.matcher.is_match(&format!("{}/", rel_path_str)) } else { self.matcher.is_match(rel_path_str) } @@ -105,13 +105,15 @@ pub fn list_files( let gitignore = load_gitignore(path); // Special folders that should not be expanded - let special_folders = [Path::new("/"), + let special_folders = vec![ + Path::new("/"), Path::new("/home"), Path::new("/Users"), Path::new("/System"), Path::new("/Windows"), Path::new("/Program Files"), - Path::new("/Program Files (x86)")]; + Path::new("/Program Files (x86)"), + ]; // Folders to exclude let excluded_folders = vec![ @@ -167,7 +169,9 @@ pub fn list_files( // Never exclude the root directory false } else { - excluded_folders.contains(&folder_name) + excluded_folders + .iter() + .any(|excluded| folder_name == *excluded) || (folder_name.starts_with('.') && folder_name != "." && folder_name != "..") }; diff --git a/src/crates/core/src/function_agents/git-func-agent/utils.rs b/src/crates/core/src/function_agents/git-func-agent/utils.rs index 908d0253..dd191e42 100644 --- a/src/crates/core/src/function_agents/git-func-agent/utils.rs +++ b/src/crates/core/src/function_agents/git-func-agent/utils.rs @@ -76,7 +76,10 @@ pub fn detect_change_patterns(file_changes: &[FileChange]) -> Vec let mut has_new_files = false; for change in file_changes { - if change.change_type == FileChangeType::Added { has_new_files = true } + match change.change_type { + FileChangeType::Added => has_new_files = true, + _ => {} + } if is_test_file(&change.path) { has_test_changes = true; diff --git a/src/crates/core/src/function_agents/mod.rs b/src/crates/core/src/function_agents/mod.rs index a877e2a5..3e90239c 100644 --- a/src/crates/core/src/function_agents/mod.rs +++ b/src/crates/core/src/function_agents/mod.rs @@ -1,4 +1,4 @@ -/*! +/** * Function Agents module * * Provides various function agents for automating specific tasks diff --git a/src/crates/core/src/function_agents/startchat-func-agent/work_state_analyzer.rs b/src/crates/core/src/function_agents/startchat-func-agent/work_state_analyzer.rs index ff91ada4..67f37155 100644 --- a/src/crates/core/src/function_agents/startchat-func-agent/work_state_analyzer.rs +++ b/src/crates/core/src/function_agents/startchat-func-agent/work_state_analyzer.rs @@ -30,7 +30,7 @@ impl WorkStateAnalyzer { let git_diff = if git_state .as_ref() - .is_some_and(|g| g.unstaged_files > 0 || g.staged_files > 0) + .map_or(false, |g| g.unstaged_files > 0 || g.staged_files > 0) { Self::get_git_diff(repo_path).await.unwrap_or_default() } else { @@ -242,7 +242,7 @@ impl WorkStateAnalyzer { } let result = String::from_utf8_lossy(&output.stdout); - let parts: Vec<&str> = result.split_whitespace().collect(); + let parts: Vec<&str> = result.trim().split_whitespace().collect(); if parts.len() >= 2 { let ahead = parts[0].parse().unwrap_or(0); diff --git a/src/crates/core/src/infrastructure/ai/ai_stream_handlers/src/types/anthropic.rs b/src/crates/core/src/infrastructure/ai/ai_stream_handlers/src/types/anthropic.rs index de779a96..4f101ab1 100644 --- a/src/crates/core/src/infrastructure/ai/ai_stream_handlers/src/types/anthropic.rs +++ b/src/crates/core/src/infrastructure/ai/ai_stream_handlers/src/types/anthropic.rs @@ -1,4 +1,4 @@ -use super::unified::{UnifiedResponse, UnifiedTokenUsage, UnifiedToolCall}; +use super::unified::{UnifiedResponse, UnifiedTokenUsage, UnifiedToolCall}; use serde::Deserialize; #[derive(Debug, Deserialize)] @@ -12,7 +12,6 @@ pub struct Message { } #[derive(Debug, Clone, Deserialize)] -#[derive(Default)] pub struct Usage { input_tokens: Option, output_tokens: Option, @@ -20,6 +19,16 @@ pub struct Usage { cache_creation_input_tokens: Option, } +impl Default for Usage { + fn default() -> Self { + Self { + input_tokens: None, + output_tokens: None, + cache_read_input_tokens: None, + cache_creation_input_tokens: None, + } + } +} impl Usage { pub fn update(&mut self, other: &Usage) { @@ -114,13 +123,16 @@ pub enum ContentBlock { impl From for UnifiedResponse { fn from(value: ContentBlockStart) -> Self { let mut result = UnifiedResponse::default(); - if let ContentBlock::ToolUse { id, name } = value.content_block { - let tool_call = UnifiedToolCall { - id: Some(id), - name: Some(name), - arguments: None, - }; - result.tool_call = Some(tool_call); + match value.content_block { + ContentBlock::ToolUse { id, name } => { + let tool_call = UnifiedToolCall { + id: Some(id), + name: Some(name), + arguments: None, + }; + result.tool_call = Some(tool_call); + } + _ => {} } result } @@ -135,13 +147,13 @@ pub struct ContentBlockDelta { #[serde(tag = "type")] pub enum Delta { #[serde(rename = "thinking_delta")] - Thinking { thinking: String }, + ThinkingDelta { thinking: String }, #[serde(rename = "text_delta")] - Text { text: String }, + TextDelta { text: String }, #[serde(rename = "input_json_delta")] - InputJson { partial_json: String }, + InputJsonDelta { partial_json: String }, #[serde(rename = "signature_delta")] - Signature { signature: String }, + SignatureDelta { signature: String }, #[serde(other)] Unknown, } @@ -151,13 +163,13 @@ impl TryFrom for UnifiedResponse { fn try_from(value: ContentBlockDelta) -> Result { let mut result = UnifiedResponse::default(); match value.delta { - Delta::Thinking { thinking } => { + Delta::ThinkingDelta { thinking } => { result.reasoning_content = Some(thinking); } - Delta::Text { text } => { + Delta::TextDelta { text } => { result.text = Some(text); } - Delta::InputJson { partial_json } => { + Delta::InputJsonDelta { partial_json } => { let tool_call = UnifiedToolCall { id: None, name: None, @@ -165,7 +177,7 @@ impl TryFrom for UnifiedResponse { }; result.tool_call = Some(tool_call); } - Delta::Signature { signature } => { + Delta::SignatureDelta { signature } => { result.thinking_signature = Some(signature); } Delta::Unknown => { diff --git a/src/crates/core/src/infrastructure/ai/ai_stream_handlers/src/types/unified.rs b/src/crates/core/src/infrastructure/ai/ai_stream_handlers/src/types/unified.rs index e3b36c37..309a3501 100644 --- a/src/crates/core/src/infrastructure/ai/ai_stream_handlers/src/types/unified.rs +++ b/src/crates/core/src/infrastructure/ai/ai_stream_handlers/src/types/unified.rs @@ -10,7 +10,6 @@ pub struct UnifiedToolCall { /// Unified AI response format #[derive(Debug, Clone, Serialize, Deserialize)] -#[derive(Default)] pub struct UnifiedResponse { pub text: Option, pub reasoning_content: Option, @@ -24,6 +23,19 @@ pub struct UnifiedResponse { pub provider_metadata: Option, } +impl Default for UnifiedResponse { + fn default() -> Self { + Self { + text: None, + reasoning_content: None, + thinking_signature: None, + tool_call: None, + usage: None, + finish_reason: None, + provider_metadata: None, + } + } +} /// Unified token usage statistics #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/crates/core/src/infrastructure/ai/client.rs b/src/crates/core/src/infrastructure/ai/client.rs index b24ce0ca..dd571899 100644 --- a/src/crates/core/src/infrastructure/ai/client.rs +++ b/src/crates/core/src/infrastructure/ai/client.rs @@ -101,7 +101,9 @@ impl AIClient { .filter(|s| !s.is_empty()) .collect(); - if tokens.contains(&Self::TEST_IMAGE_EXPECTED_CODE) + if tokens + .iter() + .any(|token| *token == Self::TEST_IMAGE_EXPECTED_CODE) { return true; } @@ -534,7 +536,7 @@ impl AIClient { .config .custom_headers .as_ref() - .is_some_and(|h| !h.is_empty()); + .map_or(false, |h| !h.is_empty()); let is_merge_mode = self.is_merge_headers_mode(); if has_custom_headers && !is_merge_mode { @@ -566,7 +568,7 @@ impl AIClient { .config .custom_headers .as_ref() - .is_some_and(|h| !h.is_empty()); + .map_or(false, |h| !h.is_empty()); let is_merge_mode = self.is_merge_headers_mode(); if has_custom_headers && !is_merge_mode { @@ -603,7 +605,7 @@ impl AIClient { .config .custom_headers .as_ref() - .is_some_and(|h| !h.is_empty()); + .map_or(false, |h| !h.is_empty()); let is_merge_mode = self.is_merge_headers_mode(); if has_custom_headers && !is_merge_mode { @@ -872,7 +874,7 @@ impl AIClient { if let Some(tools) = openai_tools { let tool_names = tools .iter() - .map(Self::extract_openai_tool_name) + .map(|tool| Self::extract_openai_tool_name(tool)) .collect::>(); debug!(target: "ai::openai_stream_request", "\ntools: {:?}", tool_names); if !tools.is_empty() { @@ -942,7 +944,7 @@ impl AIClient { if let Some(tools) = openai_tools { let tool_names = tools .iter() - .map(Self::extract_openai_tool_name) + .map(|tool| Self::extract_openai_tool_name(tool)) .collect::>(); debug!(target: "ai::responses_stream_request", "\ntools: {:?}", tool_names); if !tools.is_empty() { @@ -1015,7 +1017,7 @@ impl AIClient { if let Some(tools) = anthropic_tools { let tool_names = tools .iter() - .map(Self::extract_anthropic_tool_name) + .map(|tool| Self::extract_anthropic_tool_name(tool)) .collect::>(); debug!(target: "ai::anthropic_stream_request", "\ntools: {:?}", tool_names); if !tools.is_empty() { diff --git a/src/crates/core/src/infrastructure/ai/tool_call_accumulator.rs b/src/crates/core/src/infrastructure/ai/tool_call_accumulator.rs index 33eaffa5..c35b4fb5 100644 --- a/src/crates/core/src/infrastructure/ai/tool_call_accumulator.rs +++ b/src/crates/core/src/infrastructure/ai/tool_call_accumulator.rs @@ -56,7 +56,7 @@ impl PendingToolCall { .find(|(_, ch)| !ch.is_whitespace()) .map(|(idx, _)| idx)?; - if !raw_arguments[last_non_whitespace_idx..].starts_with('}') { + if raw_arguments[last_non_whitespace_idx..].chars().next() != Some('}') { return None; } diff --git a/src/crates/core/src/infrastructure/debug_log/http_server.rs b/src/crates/core/src/infrastructure/debug_log/http_server.rs index 5c360265..e894e408 100644 --- a/src/crates/core/src/infrastructure/debug_log/http_server.rs +++ b/src/crates/core/src/infrastructure/debug_log/http_server.rs @@ -31,12 +31,6 @@ pub struct IngestServerManager { actual_port: Arc>, } -impl Default for IngestServerManager { - fn default() -> Self { - Self::new() - } -} - impl IngestServerManager { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/infrastructure/debug_log/mod.rs b/src/crates/core/src/infrastructure/debug_log/mod.rs index 087e29cd..7da06c56 100644 --- a/src/crates/core/src/infrastructure/debug_log/mod.rs +++ b/src/crates/core/src/infrastructure/debug_log/mod.rs @@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use std::fs::{self, OpenOptions}; use std::io::Write; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::LazyLock; use tokio::task; use uuid::Uuid; @@ -160,7 +160,7 @@ fn build_log_line(entry: DebugLogEntry, config: &DebugLogConfig) -> Value { }) } -fn ensure_parent_exists(path: &Path) -> Result<()> { +fn ensure_parent_exists(path: &PathBuf) -> Result<()> { if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; } diff --git a/src/crates/core/src/infrastructure/filesystem/file_tree.rs b/src/crates/core/src/infrastructure/filesystem/file_tree.rs index f6004dd0..b28b54a6 100644 --- a/src/crates/core/src/infrastructure/filesystem/file_tree.rs +++ b/src/crates/core/src/infrastructure/filesystem/file_tree.rs @@ -207,7 +207,7 @@ impl FileTreeService { // For remote workspaces, return simple directory listing with empty stats if crate::service::remote_ssh::workspace_state::is_remote_path(root_path).await { let nodes = self.get_directory_contents_with_remote_hint(root_path, None).await - .map_err(BitFunError::service)?; + .map_err(|e| BitFunError::service(e))?; let stats = FileTreeStatistics { total_files: nodes.iter().filter(|n| !n.is_directory).count(), total_directories: nodes.iter().filter(|n| n.is_directory).count(), @@ -252,7 +252,7 @@ impl FileTreeService { &mut stats, ) .await - .map_err(BitFunError::service)?; + .map_err(|e| BitFunError::service(e))?; Ok((nodes, stats)) } @@ -356,9 +356,9 @@ impl FileTreeService { } let last_modified = metadata.and_then(|m| { - m.modified().ok().map(|t| { + m.modified().ok().and_then(|t| { let datetime: chrono::DateTime = t.into(); - datetime.format("%Y-%m-%d %H:%M:%S").to_string() + Some(datetime.format("%Y-%m-%d %H:%M:%S").to_string()) }) }); @@ -389,8 +389,8 @@ impl FileTreeService { .with_depth(depth) .with_enhanced_info(is_symlink, permissions, mime_type, None); - if is_directory - && (!is_symlink || self.options.follow_symlinks) { + if is_directory { + if !is_symlink || self.options.follow_symlinks { match self .build_tree_recursive(&entry_path, root_path, visited, depth + 1) .await @@ -403,6 +403,7 @@ impl FileTreeService { } } } + } nodes.push(node); } @@ -550,9 +551,9 @@ impl FileTreeService { } let last_modified = metadata.and_then(|m| { - m.modified().ok().map(|t| { + m.modified().ok().and_then(|t| { let datetime: chrono::DateTime = t.into(); - datetime.format("%Y-%m-%d %H:%M:%S").to_string() + Some(datetime.format("%Y-%m-%d %H:%M:%S").to_string()) }) }); @@ -583,8 +584,8 @@ impl FileTreeService { .with_depth(depth) .with_enhanced_info(is_symlink, permissions, mime_type, None); - if is_directory - && (!is_symlink || self.options.follow_symlinks) { + if is_directory { + if !is_symlink || self.options.follow_symlinks { match self .build_tree_recursive_with_stats( &entry_path, @@ -603,6 +604,7 @@ impl FileTreeService { } } } + } nodes.push(node); } @@ -1038,7 +1040,6 @@ impl FileTreeService { Ok(final_results) } - #[allow(clippy::too_many_arguments)] fn search_file_content_static( path: &Path, file_name: &str, diff --git a/src/crates/core/src/infrastructure/filesystem/path_manager.rs b/src/crates/core/src/infrastructure/filesystem/path_manager.rs index 94781341..6290d9e8 100644 --- a/src/crates/core/src/infrastructure/filesystem/path_manager.rs +++ b/src/crates/core/src/infrastructure/filesystem/path_manager.rs @@ -270,14 +270,14 @@ impl PathManager { let canonical_exists = canonical.exists(); let legacy_exists = legacy.exists(); - - if canonical_exists { + let chosen = if canonical_exists { canonical.clone() } else if legacy_exists { legacy.clone() } else { canonical.clone() - } + }; + chosen } /// Root for per-host, per-remote-path workspace mirrors: `~/.bitfun/remote_ssh/`. diff --git a/src/crates/core/src/miniapp/bridge_builder.rs b/src/crates/core/src/miniapp/bridge_builder.rs index 1922b632..00ecabd7 100644 --- a/src/crates/core/src/miniapp/bridge_builder.rs +++ b/src/crates/core/src/miniapp/bridge_builder.rs @@ -152,7 +152,7 @@ pub fn build_import_map(deps: &[EsmDep]) -> String { imports.insert(dep.name.clone(), serde_json::Value::String(url)); } let json = serde_json::json!({ "imports": imports }); - format!(r#""#, json) + format!(r#""#, json.to_string()) } /// Build CSP meta content from permissions (net.allow → connect-src). diff --git a/src/crates/core/src/miniapp/js_worker.rs b/src/crates/core/src/miniapp/js_worker.rs index 57df5b1c..f7cf7673 100644 --- a/src/crates/core/src/miniapp/js_worker.rs +++ b/src/crates/core/src/miniapp/js_worker.rs @@ -11,15 +11,11 @@ use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::{Child, ChildStdin, Command}; use tokio::sync::{oneshot, Mutex}; -type JsWorkerResponse = Result; -type PendingResponseSender = oneshot::Sender; -type PendingResponseMap = HashMap; - /// Single JS Worker process: stdin for requests, stderr for RPC responses, stdout for user logs. pub struct JsWorker { _child: Child, stdin: Mutex>, - pending: Arc>, + pending: Arc>>>>, last_activity: Arc, } @@ -48,7 +44,10 @@ impl JsWorker { let stderr = child.stderr.take().ok_or("No stderr")?; let _stdout = child.stdout.take(); - let pending = Arc::new(Mutex::new(PendingResponseMap::new())); + let pending = Arc::new(Mutex::new(HashMap::< + String, + oneshot::Sender>, + >::new())); let last_activity = Arc::new(AtomicI64::new( std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) diff --git a/src/crates/core/src/miniapp/js_worker_pool.rs b/src/crates/core/src/miniapp/js_worker_pool.rs index 4e854182..42d7fdc8 100644 --- a/src/crates/core/src/miniapp/js_worker_pool.rs +++ b/src/crates/core/src/miniapp/js_worker_pool.rs @@ -124,7 +124,7 @@ impl JsWorkerPool { let worker = JsWorker::spawn(&self.runtime, &self.worker_host_path, &app_dir, policy_json) .await - .map_err(BitFunError::validation)?; + .map_err(|e| BitFunError::validation(e))?; let _timeout_ms = node_perms.and_then(|n| n.timeout_ms).unwrap_or(30_000); let worker = Arc::new(Mutex::new(worker)); diff --git a/src/crates/core/src/miniapp/manager.rs b/src/crates/core/src/miniapp/manager.rs index b21a71be..442665f7 100644 --- a/src/crates/core/src/miniapp/manager.rs +++ b/src/crates/core/src/miniapp/manager.rs @@ -146,7 +146,6 @@ impl MiniAppManager { } /// Create a new MiniApp (generates id, sets created_at/updated_at, compiles). - #[allow(clippy::too_many_arguments)] pub async fn create( &self, name: String, @@ -189,7 +188,6 @@ impl MiniAppManager { } /// Update existing MiniApp (increment version, recompile, save). - #[allow(clippy::too_many_arguments)] pub async fn update( &self, app_id: &str, diff --git a/src/crates/core/src/miniapp/runtime_detect.rs b/src/crates/core/src/miniapp/runtime_detect.rs index 5b877ec3..39fce75c 100644 --- a/src/crates/core/src/miniapp/runtime_detect.rs +++ b/src/crates/core/src/miniapp/runtime_detect.rs @@ -45,7 +45,8 @@ fn get_version(executable: &std::path::Path) -> Result { let v = String::from_utf8_lossy(&out.stdout); Ok(v.trim().to_string()) } else { - Err(std::io::Error::other( + Err(std::io::Error::new( + std::io::ErrorKind::Other, "version check failed", )) } diff --git a/src/crates/core/src/service/agent_memory/agent_memory_impl.rs b/src/crates/core/src/service/agent_memory/agent_memory.rs similarity index 100% rename from src/crates/core/src/service/agent_memory/agent_memory_impl.rs rename to src/crates/core/src/service/agent_memory/agent_memory.rs diff --git a/src/crates/core/src/service/agent_memory/mod.rs b/src/crates/core/src/service/agent_memory/mod.rs index a0f94ba8..c836b9eb 100644 --- a/src/crates/core/src/service/agent_memory/mod.rs +++ b/src/crates/core/src/service/agent_memory/mod.rs @@ -1,3 +1,3 @@ -mod agent_memory_impl; +mod agent_memory; -pub(crate) use agent_memory_impl::build_workspace_agent_memory_prompt; +pub(crate) use agent_memory::build_workspace_agent_memory_prompt; diff --git a/src/crates/core/src/service/ai_memory/manager.rs b/src/crates/core/src/service/ai_memory/manager.rs index eb0c99bb..fee92d23 100644 --- a/src/crates/core/src/service/ai_memory/manager.rs +++ b/src/crates/core/src/service/ai_memory/manager.rs @@ -188,9 +188,9 @@ impl AIMemoryManager { "## {} [{}] (Importance: {}/5)\n{}\n", memory.title, type_label, memory.importance, memory.content )); - prompt.push('\n'); + prompt.push_str("\n"); } - prompt.push('\n'); + prompt.push_str("\n"); Ok(Some(prompt)) } diff --git a/src/crates/core/src/service/ai_memory/types.rs b/src/crates/core/src/service/ai_memory/types.rs index 6cd13ec6..0929da7f 100644 --- a/src/crates/core/src/service/ai_memory/types.rs +++ b/src/crates/core/src/service/ai_memory/types.rs @@ -6,7 +6,6 @@ use std::collections::HashMap; /// Memory type #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] #[serde(rename_all = "snake_case")] -#[derive(Default)] pub enum MemoryType { /// Technology preference TechPreference, @@ -19,10 +18,14 @@ pub enum MemoryType { /// Architecture decision Decision, /// Other - #[default] Other, } +impl Default for MemoryType { + fn default() -> Self { + Self::Other + } +} /// AI memory point #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/crates/core/src/service/ai_rules/service.rs b/src/crates/core/src/service/ai_rules/service.rs index 4aa8e047..19dcded7 100644 --- a/src/crates/core/src/service/ai_rules/service.rs +++ b/src/crates/core/src/service/ai_rules/service.rs @@ -636,7 +636,7 @@ The rules section has a number of possible rules/memories/context that you shoul while let Ok(Some(entry)) = entries.next_entry().await { let path = entry.path(); - if path.extension().is_some_and(|ext| ext == "mdc") { + if path.extension().map_or(false, |ext| ext == "mdc") { match self.load_rule_from_file(&path, level).await { Ok(rule) => rules.push(rule), Err(e) => { @@ -664,7 +664,7 @@ The rules section has a number of possible rules/memories/context that you shoul .ok_or_else(|| BitFunError::service("Invalid file name".to_string()))?; AIRule::from_mdc(name, level, path.to_path_buf(), &content) - .map_err(BitFunError::service) + .map_err(|e| BitFunError::service(e)) } /// Creates a rule file. @@ -733,7 +733,7 @@ The rules section has a number of possible rules/memories/context that you shoul .map_err(|e| BitFunError::service(format!("Failed to read file: {}", e)))?; let (mut frontmatter, mut body) = - parse_mdc_content(&content).map_err(BitFunError::service)?; + parse_mdc_content(&content).map_err(|e| BitFunError::service(e))?; if let Some(apply_type) = request.apply_type { match apply_type { diff --git a/src/crates/core/src/service/bootstrap/bootstrap_impl.rs b/src/crates/core/src/service/bootstrap/bootstrap.rs similarity index 100% rename from src/crates/core/src/service/bootstrap/bootstrap_impl.rs rename to src/crates/core/src/service/bootstrap/bootstrap.rs diff --git a/src/crates/core/src/service/bootstrap/mod.rs b/src/crates/core/src/service/bootstrap/mod.rs index 94bf1477..04b56c3b 100644 --- a/src/crates/core/src/service/bootstrap/mod.rs +++ b/src/crates/core/src/service/bootstrap/mod.rs @@ -1,7 +1,7 @@ -mod bootstrap_impl; +mod bootstrap; -pub use bootstrap_impl::reset_workspace_persona_files_to_default; -pub(crate) use bootstrap_impl::{ +pub use bootstrap::reset_workspace_persona_files_to_default; +pub(crate) use bootstrap::{ build_workspace_persona_prompt, ensure_workspace_persona_files_for_prompt, initialize_workspace_persona_files, is_workspace_bootstrap_pending, diff --git a/src/crates/core/src/service/config/manager.rs b/src/crates/core/src/service/config/manager.rs index 0bf49198..6d9d8297 100644 --- a/src/crates/core/src/service/config/manager.rs +++ b/src/crates/core/src/service/config/manager.rs @@ -14,9 +14,6 @@ use std::path::PathBuf; use std::sync::Arc; use tokio::fs; -type ConfigMigrationFn = fn(Value) -> BitFunResult; -type ConfigMigration = (&'static str, &'static str, ConfigMigrationFn); - /// Configuration manager. pub struct ConfigManager { config_dir: PathBuf, @@ -187,7 +184,7 @@ impl ConfigManager { /// Auto-completes missing fields in model configuration (backward compatible). /// Ensures older configurations won't panic. - fn ensure_models_config(models: &mut [AIModelConfig]) { + fn ensure_models_config(models: &mut Vec) { for model in models.iter_mut() { model.ensure_category_and_capabilities(); } @@ -232,7 +229,7 @@ impl ConfigManager { from_version: &str, mut config: Value, ) -> BitFunResult { - let migrations: Vec = + let migrations: Vec<(&str, &str, fn(Value) -> BitFunResult)> = vec![("0.0.0", "1.0.0", migrate_0_0_0_to_1_0_0)]; let mut current_version = from_version.to_string(); @@ -608,7 +605,7 @@ pub(crate) fn version_lt(v1: &str, v2: &str) -> bool { /// Parses a version string into a tuple `(major, minor, patch)`. pub(crate) fn parse_version(version: &str) -> (u32, u32, u32) { let parts: Vec<&str> = version.split('.').collect(); - let major = parts.first().and_then(|s| s.parse().ok()).unwrap_or(0); + let major = parts.get(0).and_then(|s| s.parse().ok()).unwrap_or(0); let minor = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(0); let patch = parts.get(2).and_then(|s| s.parse().ok()).unwrap_or(0); (major, minor, patch) diff --git a/src/crates/core/src/service/config/providers.rs b/src/crates/core/src/service/config/providers.rs index 4018ee0b..4a68d2ca 100644 --- a/src/crates/core/src/service/config/providers.rs +++ b/src/crates/core/src/service/config/providers.rs @@ -72,7 +72,7 @@ impl ConfigProvider for AIConfigProvider { } } if let Some(temperature) = model.temperature { - if !(0.0..=2.0).contains(&temperature) { + if temperature < 0.0 || temperature > 2.0 { warnings.push(format!( "Model '{}' temperature should be between 0 and 2", model.name @@ -543,8 +543,8 @@ impl ConfigProviderRegistry { } /// Gets a provider by name. - pub fn get_provider(&self, name: &str) -> Option<&dyn ConfigProvider> { - self.providers.get(name).map(Box::as_ref) + pub fn get_provider(&self, name: &str) -> Option<&Box> { + self.providers.get(name) } /// Returns all provider names. diff --git a/src/crates/core/src/service/config/types.rs b/src/crates/core/src/service/config/types.rs index f15a9dcc..b75dda20 100644 --- a/src/crates/core/src/service/config/types.rs +++ b/src/crates/core/src/service/config/types.rs @@ -339,10 +339,8 @@ pub enum ModelCapability { /// Model category (for UI display and filtering). #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "snake_case")] -#[derive(Default)] pub enum ModelCategory { /// General chat model. - #[default] GeneralChat, /// Multimodal model (text + image understanding). Multimodal, @@ -358,11 +356,15 @@ pub enum ModelCategory { SpeechRecognition, } +impl Default for ModelCategory { + fn default() -> Self { + Self::GeneralChat + } +} /// Default model configuration. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default)] -#[derive(Default)] pub struct DefaultModelsConfig { /// Primary model ID (for complex tasks). pub primary: Option, @@ -378,6 +380,18 @@ pub struct DefaultModelsConfig { pub speech_recognition: Option, } +impl Default for DefaultModelsConfig { + fn default() -> Self { + Self { + primary: None, + fast: None, + search: None, + image_understanding: None, + image_generation: None, + speech_recognition: None, + } + } +} /// AI configuration. #[derive(Debug, Clone, Serialize, Deserialize)] @@ -862,7 +876,6 @@ pub struct AIModelConfig { /// Proxy configuration. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(default)] -#[derive(Default)] pub struct ProxyConfig { /// Whether the proxy is enabled. pub enabled: bool, @@ -1217,6 +1230,16 @@ impl Default for AIConfig { } } +impl Default for ProxyConfig { + fn default() -> Self { + Self { + enabled: false, + url: String::new(), + username: None, + password: None, + } + } +} impl Default for AIModelConfig { fn default() -> Self { diff --git a/src/crates/core/src/service/diff/service.rs b/src/crates/core/src/service/diff/service.rs index 820ea1f7..7c6250ac 100644 --- a/src/crates/core/src/service/diff/service.rs +++ b/src/crates/core/src/service/diff/service.rs @@ -20,7 +20,6 @@ impl DiffService { } /// Creates with default configuration. - #[allow(clippy::should_implement_trait)] pub fn default() -> Self { Self::new(DiffConfig::new()) } diff --git a/src/crates/core/src/service/diff/types.rs b/src/crates/core/src/service/diff/types.rs index 3c0df60f..063ad393 100644 --- a/src/crates/core/src/service/diff/types.rs +++ b/src/crates/core/src/service/diff/types.rs @@ -65,7 +65,6 @@ pub struct DiffHunk { /// Diff result #[derive(Debug, Clone, Serialize, Deserialize)] -#[derive(Default)] pub struct DiffResult { /// Hunk list pub hunks: Vec, @@ -77,6 +76,16 @@ pub struct DiffResult { pub changes: usize, } +impl Default for DiffResult { + fn default() -> Self { + Self { + hunks: Vec::new(), + additions: 0, + deletions: 0, + changes: 0, + } + } +} /// Diff computation options #[derive(Debug, Clone, Default, Serialize, Deserialize)] diff --git a/src/crates/core/src/service/filesystem/service.rs b/src/crates/core/src/service/filesystem/service.rs index 032be276..d2ea1a0d 100644 --- a/src/crates/core/src/service/filesystem/service.rs +++ b/src/crates/core/src/service/filesystem/service.rs @@ -26,7 +26,6 @@ impl FileSystemService { } /// Creates the default service. - #[allow(clippy::should_implement_trait)] pub fn default() -> Self { Self::new(FileSystemConfig::default()) } @@ -45,7 +44,7 @@ impl FileSystemService { self.file_tree_service .build_tree_with_remote_hint(root_path, preferred_remote_connection_id) .await - .map_err(BitFunError::service) + .map_err(|e| BitFunError::service(e)) } /// Scans a directory and returns a detailed result. @@ -79,7 +78,7 @@ impl FileSystemService { self.file_tree_service .get_directory_contents_with_remote_hint(path, preferred_remote_connection_id) .await - .map_err(BitFunError::service) + .map_err(|e| BitFunError::service(e)) } /// Searches files. diff --git a/src/crates/core/src/service/filesystem/types.rs b/src/crates/core/src/service/filesystem/types.rs index 4d607ea8..d0032f76 100644 --- a/src/crates/core/src/service/filesystem/types.rs +++ b/src/crates/core/src/service/filesystem/types.rs @@ -5,12 +5,19 @@ use serde::{Deserialize, Serialize}; /// File system service configuration #[derive(Debug, Clone)] -#[derive(Default)] pub struct FileSystemConfig { pub tree_options: FileTreeOptions, pub operation_options: FileOperationOptions, } +impl Default for FileSystemConfig { + fn default() -> Self { + Self { + tree_options: FileTreeOptions::default(), + operation_options: FileOperationOptions::default(), + } + } +} /// Directory scan result #[derive(Debug, Serialize, Deserialize)] diff --git a/src/crates/core/src/service/git/git_service.rs b/src/crates/core/src/service/git/git_service.rs index e4adfde1..afd70e66 100644 --- a/src/crates/core/src/service/git/git_service.rs +++ b/src/crates/core/src/service/git/git_service.rs @@ -11,8 +11,6 @@ use tokio::time::timeout; pub struct GitService; -type CommitStats = (Option, Option, Option); - impl GitService { /// Checks whether the path is a Git repository. pub async fn is_repository>(path: P) -> Result { @@ -234,7 +232,7 @@ impl GitService { for branch in &mut branches { if !branch.remote { branch.stats = Self::calculate_branch_stats(&repo, &branch.name).ok(); - branch.is_stale = Some(Self::is_branch_stale(branch)); + branch.is_stale = Some(Self::is_branch_stale(&branch)); branch.can_merge = Self::can_merge_safely(&repo, &branch.name).ok(); branch.has_conflicts = branch.can_merge.map(|can| !can); } @@ -267,7 +265,7 @@ impl GitService { } /// Analyzes branch relationships. - fn analyze_branch_relations(branches: &mut [GitBranch]) -> Result<(), GitError> { + fn analyze_branch_relations(branches: &mut Vec) -> Result<(), GitError> { let main_branches = ["main", "master", "develop"]; let available_main_branches: Vec = branches @@ -291,7 +289,7 @@ impl GitService { if let Some(base) = &branch.base_branch { child_map .entry(base.clone()) - .or_default() + .or_insert_with(Vec::new) .push(branch.name.clone()); } } @@ -342,7 +340,11 @@ impl GitService { /// Checks whether a branch is stale. fn is_branch_stale(branch: &GitBranch) -> bool { - !matches!(&branch.last_commit_date, Some(_last_commit_date)) + if let Some(_last_commit_date) = &branch.last_commit_date { + false + } else { + true + } } /// Checks whether a branch can be merged safely. @@ -854,7 +856,7 @@ impl GitService { fn get_commit_stats( _repo: &Repository, _commit: &Commit, - ) -> Result { + ) -> Result<(Option, Option, Option), GitError> { Ok((None, None, None)) } @@ -1066,7 +1068,7 @@ impl GitService { let worktree_path_str = worktree_path.to_string_lossy().to_string(); if !worktree_dir.exists() { - std::fs::create_dir_all(&worktree_dir).map_err(GitError::IoError)?; + std::fs::create_dir_all(&worktree_dir).map_err(|e| GitError::IoError(e))?; } let args = if create_branch { diff --git a/src/crates/core/src/service/git/git_utils.rs b/src/crates/core/src/service/git/git_utils.rs index d6bcacaa..b23bc298 100644 --- a/src/crates/core/src/service/git/git_utils.rs +++ b/src/crates/core/src/service/git/git_utils.rs @@ -250,10 +250,10 @@ pub fn parse_branch_line(line: &str) -> Option<(String, bool)> { return None; } - if let Some(stripped) = trimmed.strip_prefix("* ") { - Some((stripped.to_string(), true)) - } else if let Some(stripped) = trimmed.strip_prefix(" ") { - Some((stripped.to_string(), false)) + if trimmed.starts_with("* ") { + Some((trimmed[2..].to_string(), true)) + } else if trimmed.starts_with(" ") { + Some((trimmed[2..].to_string(), false)) } else { Some((trimmed.to_string(), false)) } diff --git a/src/crates/core/src/service/git/graph.rs b/src/crates/core/src/service/git/graph.rs index 46d62517..60bf2420 100644 --- a/src/crates/core/src/service/git/graph.rs +++ b/src/crates/core/src/service/git/graph.rs @@ -190,7 +190,7 @@ pub fn build_git_graph_for_branch( let parent_hash = parent_id.to_string(); children_map .entry(parent_hash) - .or_default() + .or_insert_with(Vec::new) .push(hash.clone()); } } @@ -251,10 +251,11 @@ fn collect_refs(repo: &Repository) -> Result>, git if let Some(oid) = reference.target() { let hash = oid.to_string(); - let is_current = current_branch.as_ref().is_some_and(|cb| cb == name); + let is_current = current_branch.as_ref().map_or(false, |cb| cb == name); let is_head = head .as_ref() - .and_then(|h| h.target()) == Some(oid); + .and_then(|h| h.target()) + .map_or(false, |h| h == oid); let graph_ref = GraphRef { name: name.to_string(), @@ -265,7 +266,7 @@ fn collect_refs(repo: &Repository) -> Result>, git refs_map .entry(hash) - .or_default() + .or_insert_with(Vec::new) .push(graph_ref); } } diff --git a/src/crates/core/src/service/i18n/types.rs b/src/crates/core/src/service/i18n/types.rs index c8dfb8ba..e9849a23 100644 --- a/src/crates/core/src/service/i18n/types.rs +++ b/src/crates/core/src/service/i18n/types.rs @@ -4,15 +4,20 @@ use serde::{Deserialize, Serialize}; /// Locale identifier. /// Currently supports Chinese and English only. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub enum LocaleId { #[serde(rename = "zh-CN")] - #[default] ZhCN, #[serde(rename = "en-US")] EnUS, } +impl Default for LocaleId { + fn default() -> Self { + LocaleId::ZhCN + } +} + impl LocaleId { /// Returns the locale identifier string. pub fn as_str(&self) -> &'static str { @@ -23,7 +28,6 @@ impl LocaleId { } /// Parses a locale identifier from a string. - #[allow(clippy::should_implement_trait)] pub fn from_str(s: &str) -> Option { match s { "zh-CN" => Some(LocaleId::ZhCN), diff --git a/src/crates/core/src/service/lsp/file_sync.rs b/src/crates/core/src/service/lsp/file_sync.rs index 83d564bd..5c03168b 100644 --- a/src/crates/core/src/service/lsp/file_sync.rs +++ b/src/crates/core/src/service/lsp/file_sync.rs @@ -343,7 +343,7 @@ impl LspFileSync { if let Some(ws) = workspace { grouped .entry(ws.clone()) - .or_default() + .or_insert_with(Vec::new) .push(path); } } diff --git a/src/crates/core/src/service/lsp/global.rs b/src/crates/core/src/service/lsp/global.rs index 871454d2..d02e673f 100644 --- a/src/crates/core/src/service/lsp/global.rs +++ b/src/crates/core/src/service/lsp/global.rs @@ -12,14 +12,12 @@ use tokio::sync::RwLock; use super::file_sync::{FileSyncConfig, LspFileSync}; use super::{LspManager, WorkspaceLspManager}; -type WorkspaceManagerMap = HashMap>; -type GlobalWorkspaceManagers = Arc>; - /// Global LSP manager instance. static GLOBAL_LSP_MANAGER: OnceLock>> = OnceLock::new(); /// Global workspace manager mapping. -static WORKSPACE_MANAGERS: OnceLock = OnceLock::new(); +static WORKSPACE_MANAGERS: OnceLock>>>> = + OnceLock::new(); /// Global file sync manager. static GLOBAL_FILE_SYNC: OnceLock> = OnceLock::new(); diff --git a/src/crates/core/src/service/lsp/process.rs b/src/crates/core/src/service/lsp/process.rs index 7bd4fb57..b6651f99 100644 --- a/src/crates/core/src/service/lsp/process.rs +++ b/src/crates/core/src/service/lsp/process.rs @@ -5,7 +5,7 @@ use anyhow::{anyhow, Result}; use log::{debug, error, info, warn}; use std::collections::HashMap; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::Stdio; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; @@ -512,7 +512,7 @@ impl LspServerProcess { let response_message = super::types::JsonRpcMessage::Response(response); let mut stdin_lock = stdin.write().await; if let Err(e) = - super::protocol::write_message(&mut stdin_lock, &response_message) + super::protocol::write_message(&mut *stdin_lock, &response_message) .await { error!( @@ -532,7 +532,7 @@ impl LspServerProcess { let response_message = super::types::JsonRpcMessage::Response(response); let mut stdin_lock = stdin.write().await; if let Err(e) = - super::protocol::write_message(&mut stdin_lock, &response_message) + super::protocol::write_message(&mut *stdin_lock, &response_message) .await { error!( @@ -552,7 +552,7 @@ impl LspServerProcess { let response_message = super::types::JsonRpcMessage::Response(response); let mut stdin_lock = stdin.write().await; if let Err(e) = - super::protocol::write_message(&mut stdin_lock, &response_message) + super::protocol::write_message(&mut *stdin_lock, &response_message) .await { error!("[{}] Failed to send configuration response: {}", id, e); @@ -575,7 +575,7 @@ impl LspServerProcess { let response_message = super::types::JsonRpcMessage::Response(response); let mut stdin_lock = stdin.write().await; if let Err(e) = - super::protocol::write_message(&mut stdin_lock, &response_message) + super::protocol::write_message(&mut *stdin_lock, &response_message) .await { error!("[{}] Failed to send error response: {}", id, e); @@ -610,7 +610,7 @@ impl LspServerProcess { { let mut stdin = self.stdin.write().await; - write_message(&mut stdin, &message).await?; + write_message(&mut *stdin, &message).await?; } let response = timeout(Duration::from_secs(60), rx).await.map_err(|_| { @@ -634,7 +634,7 @@ impl LspServerProcess { let message = create_notification(method_str, params); let mut stdin = self.stdin.write().await; - write_message(&mut stdin, &message).await?; + write_message(&mut *stdin, &message).await?; Ok(()) } @@ -855,7 +855,7 @@ impl LspServerProcess { } /// Detects the runtime type. - fn detect_runtime_type(config: &ServerConfig, server_bin: &Path) -> RuntimeType { + fn detect_runtime_type(config: &ServerConfig, server_bin: &PathBuf) -> RuntimeType { if let Some(runtime) = &config.runtime { debug!("Runtime explicitly specified: {}", runtime); return match runtime.to_lowercase().as_str() { diff --git a/src/crates/core/src/service/lsp/project_detector.rs b/src/crates/core/src/service/lsp/project_detector.rs index c1976f02..d1ee7800 100644 --- a/src/crates/core/src/service/lsp/project_detector.rs +++ b/src/crates/core/src/service/lsp/project_detector.rs @@ -17,7 +17,6 @@ use tokio::fs; /// Project information. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -#[derive(Default)] pub struct ProjectInfo { /// Detected languages. pub languages: Vec, @@ -31,6 +30,17 @@ pub struct ProjectInfo { pub total_files: usize, } +impl Default for ProjectInfo { + fn default() -> Self { + Self { + languages: Vec::new(), + primary_language: None, + file_counts: HashMap::new(), + project_types: Vec::new(), + total_files: 0, + } + } +} /// Project type detector. pub struct ProjectDetector; diff --git a/src/crates/core/src/service/lsp/workspace_manager.rs b/src/crates/core/src/service/lsp/workspace_manager.rs index 26191a09..981e1e2c 100644 --- a/src/crates/core/src/service/lsp/workspace_manager.rs +++ b/src/crates/core/src/service/lsp/workspace_manager.rs @@ -476,7 +476,11 @@ impl WorkspaceLspManager { let status = { let states = self.server_states.read().await; - states.get(language).map(|state| state.status.clone()) + if let Some(state) = states.get(language) { + Some(state.status.clone()) + } else { + None + } }; if let Some(status) = status { @@ -733,7 +737,7 @@ impl WorkspaceLspManager { if let Some(state) = states.get_mut(&language) { state.status = ServerStatus::Failed; state.last_error = - Some("Server process crashed or became unresponsive".to_string()); + Some(format!("Server process crashed or became unresponsive")); } } diff --git a/src/crates/core/src/service/mcp/config/json_config.rs b/src/crates/core/src/service/mcp/config/json_config.rs index 4f6e0200..392395e9 100644 --- a/src/crates/core/src/service/mcp/config/json_config.rs +++ b/src/crates/core/src/service/mcp/config/json_config.rs @@ -89,9 +89,10 @@ impl MCPConfigService { return Err(BitFunError::validation(error_msg.to_string())); } - if config_value + if !config_value .get("mcpServers") - .and_then(|v| v.as_object()).is_none() + .and_then(|v| v.as_object()) + .is_some() { let error_msg = "'mcpServers' field must be an object"; error!("{}", error_msg); diff --git a/src/crates/core/src/service/mcp/protocol/transport_remote.rs b/src/crates/core/src/service/mcp/protocol/transport_remote.rs index 536d0cf0..00caa192 100644 --- a/src/crates/core/src/service/mcp/protocol/transport_remote.rs +++ b/src/crates/core/src/service/mcp/protocol/transport_remote.rs @@ -496,7 +496,7 @@ impl RemoteMCPTransport { let info = service.peer().peer_info().ok_or_else(|| { BitFunError::MCPError("Handshake succeeded but server info missing".to_string()) })?; - Ok(map_initialize_result(info)) + return Ok(map_initialize_result(info)); } ClientState::Connecting { transport } => { let Some(transport) = transport.take() else { @@ -813,13 +813,13 @@ fn map_prompt_message(message: rmcp::model::PromptMessage) -> MCPPromptMessage { let content = match message.content { rmcp::model::PromptMessageContent::Text { text } => { - MCPPromptMessageContent::Block(Box::new(MCPPromptMessageContentBlock::Text { text })) + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::Text { text }) } rmcp::model::PromptMessageContent::Image { image } => { - MCPPromptMessageContent::Block(Box::new(MCPPromptMessageContentBlock::Image { + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::Image { data: image.data.clone(), mime_type: image.mime_type.clone(), - })) + }) } rmcp::model::PromptMessageContent::Resource { resource } => { let mut mapped = map_resource_content(resource.resource.clone()); @@ -827,17 +827,17 @@ fn map_prompt_message(message: rmcp::model::PromptMessage) -> MCPPromptMessage { mapped.meta = map_optional_via_json(resource.meta.as_ref()); } mapped.annotations = map_annotations(resource.annotations.as_ref()); - MCPPromptMessageContent::Block(Box::new(MCPPromptMessageContentBlock::Resource { - resource: Box::new(mapped), - })) + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::Resource { + resource: mapped, + }) } rmcp::model::PromptMessageContent::ResourceLink { link } => { - MCPPromptMessageContent::Block(Box::new(MCPPromptMessageContentBlock::ResourceLink { + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::ResourceLink { uri: link.uri.clone(), name: Some(link.name.clone()), description: link.description.clone(), mime_type: link.mime_type.clone(), - })) + }) } }; @@ -874,7 +874,7 @@ fn map_content_block(content: Content) -> Option { mime_type: image.mime_type, }), rmcp::model::RawContent::Resource(resource) => Some(MCPToolResultContent::Resource { - resource: Box::new(map_resource_content(resource.resource)), + resource: map_resource_content(resource.resource), }), rmcp::model::RawContent::Audio(audio) => Some(MCPToolResultContent::Audio { data: audio.data, diff --git a/src/crates/core/src/service/mcp/protocol/types.rs b/src/crates/core/src/service/mcp/protocol/types.rs index 9ec34d12..b00351f5 100644 --- a/src/crates/core/src/service/mcp/protocol/types.rs +++ b/src/crates/core/src/service/mcp/protocol/types.rs @@ -259,7 +259,7 @@ pub enum MCPPromptMessageContent { /// Legacy: plain string content from older servers. Plain(String), /// Structured content block. - Block(Box), + Block(MCPPromptMessageContentBlock), } /// Structured content block types for prompt messages. @@ -283,7 +283,7 @@ pub enum MCPPromptMessageContentBlock { mime_type: Option, }, #[serde(rename = "resource")] - Resource { resource: Box }, + Resource { resource: MCPResourceContent }, } impl MCPPromptMessageContent { @@ -291,24 +291,33 @@ impl MCPPromptMessageContent { pub fn text_or_placeholder(&self) -> String { match self { MCPPromptMessageContent::Plain(s) => s.clone(), - MCPPromptMessageContent::Block(block) => match block.as_ref() { - MCPPromptMessageContentBlock::Text { text } => text.clone(), - MCPPromptMessageContentBlock::Image { mime_type, .. } => { - format!("[Image: {}]", mime_type) - } - MCPPromptMessageContentBlock::Audio { mime_type, .. } => { - format!("[Audio: {}]", mime_type) - } - MCPPromptMessageContentBlock::ResourceLink { uri, name, .. } => { - name.as_ref().map_or_else( - || format!("[Resource Link: {}]", uri), - |n| format!("[Resource Link: {} ({})]", n, uri), - ) - } - MCPPromptMessageContentBlock::Resource { resource } => { - format!("[Resource: {}]", resource.uri) - } - }, + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::Text { text }) => { + text.clone() + } + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::Image { + mime_type, + .. + }) => { + format!("[Image: {}]", mime_type) + } + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::Audio { + mime_type, + .. + }) => { + format!("[Audio: {}]", mime_type) + } + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::ResourceLink { + uri, + name, + .. + }) => name + .as_ref() + .map_or_else(|| format!("[Resource Link: {}]", uri), |n| { + format!("[Resource Link: {} ({})]", n, uri) + }), + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::Resource { resource }) => { + format!("[Resource: {}]", resource.uri) + } } } @@ -321,14 +330,13 @@ impl MCPPromptMessageContent { *s = s.replace(&placeholder, value); } } - MCPPromptMessageContent::Block(block) => { - if let MCPPromptMessageContentBlock::Text { text } = block.as_mut() { - for (key, value) in arguments { - let placeholder = format!("{{{{{}}}}}", key); - *text = text.replace(&placeholder, value); - } + MCPPromptMessageContent::Block(MCPPromptMessageContentBlock::Text { text }) => { + for (key, value) in arguments { + let placeholder = format!("{{{{{}}}}}", key); + *text = text.replace(&placeholder, value); } } + _ => {} } } } @@ -448,7 +456,7 @@ pub enum MCPToolResultContent { }, /// Embedded resource content. #[serde(rename = "resource")] - Resource { resource: Box }, + Resource { resource: MCPResourceContent }, } /// MCP message type (based on JSON-RPC 2.0). diff --git a/src/crates/core/src/service/project_context/service.rs b/src/crates/core/src/service/project_context/service.rs index 2c4c2f86..eaba9ff4 100644 --- a/src/crates/core/src/service/project_context/service.rs +++ b/src/crates/core/src/service/project_context/service.rs @@ -336,7 +336,7 @@ impl ProjectContextService { pub async fn cancel_generate_document(&self, doc_id: &str) -> BitFunResult<()> { super::cancellation::cancel_generation(doc_id) .await - .map_err(BitFunError::service) + .map_err(|e| BitFunError::service(e)) } /// Parses a filter string. @@ -435,7 +435,7 @@ impl ProjectContextService { workspace: &Path, filter: Option<&str>, ) -> BitFunResult { - let filter = filter.and_then(Self::parse_filter); + let filter = filter.and_then(|f| Self::parse_filter(f)); let config = self.load_config_and_cleanup(workspace).await?; let statuses = self.get_document_statuses(workspace).await?; diff --git a/src/crates/core/src/service/remote_connect/bot/command_router.rs b/src/crates/core/src/service/remote_connect/bot/command_router.rs index f090a62a..3f1676b1 100644 --- a/src/crates/core/src/service/remote_connect/bot/command_router.rs +++ b/src/crates/core/src/service/remote_connect/bot/command_router.rs @@ -1972,7 +1972,7 @@ async fn select_session( )); reply.push_str(&format!( "{}: {assistant_text}\n\n", - "AI" + if language.is_chinese() { "AI" } else { "AI" } )); reply.push_str(if language.is_chinese() { "你可以继续对话。" @@ -2147,7 +2147,6 @@ async fn submit_question_answers(tool_id: &str, answers: &[Value]) -> HandleResu } } -#[allow(clippy::too_many_arguments)] async fn handle_question_reply( state: &mut BotChatState, tool_id: String, diff --git a/src/crates/core/src/service/remote_connect/bot/feishu.rs b/src/crates/core/src/service/remote_connect/bot/feishu.rs index abca311b..9153f81a 100644 --- a/src/crates/core/src/service/remote_connect/bot/feishu.rs +++ b/src/crates/core/src/service/remote_connect/bot/feishu.rs @@ -21,12 +21,6 @@ use super::command_router::{ }; use super::{load_bot_persistence, save_bot_persistence, BotConfig, SavedBotConnection}; -type FeishuWsStream = tokio_tungstenite::WebSocketStream< - tokio_tungstenite::MaybeTlsStream, ->; -type FeishuWsWrite = futures::stream::SplitSink; -type SharedFeishuWsWrite = Arc>; - // ── Minimal protobuf codec for Feishu WebSocket binary protocol ───────── mod pb { @@ -166,7 +160,7 @@ mod pb { } fn write_varint(buf: &mut Vec, field: u32, val: u64) { - buf.extend(encode_varint((field << 3) as u64)); + buf.extend(encode_varint(((field << 3) | 0) as u64)); buf.extend(encode_varint(val)); } @@ -1141,7 +1135,16 @@ impl FeishuBot { async fn handle_data_frame_for_pairing( &self, frame: &pb::Frame, - write: &SharedFeishuWsWrite, + write: &Arc< + RwLock< + futures::stream::SplitSink< + tokio_tungstenite::WebSocketStream< + tokio_tungstenite::MaybeTlsStream, + >, + WsMessage, + >, + >, + >, ) -> Option { let msg_type = frame.get_header("type").unwrap_or(""); if msg_type != "event" { @@ -1516,8 +1519,8 @@ impl FeishuBot { } // Intercept file download callbacks before normal command routing. - if let Some(stripped) = text.strip_prefix("download_file:") { - let token = stripped.trim().to_string(); + if text.starts_with("download_file:") { + let token = text["download_file:".len()..].trim().to_string(); drop(states); self.handle_download_request(chat_id, &token).await; return; diff --git a/src/crates/core/src/service/remote_connect/bot/mod.rs b/src/crates/core/src/service/remote_connect/bot/mod.rs index afe8d037..3fbe03fa 100644 --- a/src/crates/core/src/service/remote_connect/bot/mod.rs +++ b/src/crates/core/src/service/remote_connect/bot/mod.rs @@ -385,7 +385,7 @@ pub fn extract_computer_file_paths( .find(|c: char| c.is_whitespace() || matches!(c, '<' | '>' | '(' | ')' | '"' | '\'')) .unwrap_or(rest.len()); let raw_suffix = - rest[..end].trim_end_matches(['.', ',', ';', ':', ')', ']']); + rest[..end].trim_end_matches(|c: char| matches!(c, '.' | ',' | ';' | ':' | ')' | ']')); if !raw_suffix.is_empty() { push_if_existing_file(&format!("{PREFIX}{raw_suffix}"), &mut paths, workspace_root); } @@ -437,7 +437,7 @@ pub fn extract_downloadable_file_paths( }) .unwrap_or(rest.len()); let raw_suffix = rest[..end] - .trim_end_matches(['.', ',', ';', ':', ')', ']']); + .trim_end_matches(|c: char| matches!(c, '.' | ',' | ';' | ':' | ')' | ']')); if !raw_suffix.is_empty() { let resolve_input = if prefix == "computer://" { format!("{prefix}{raw_suffix}") @@ -469,9 +469,11 @@ pub fn extract_downloadable_file_paths( && !href.starts_with("tel:") && !href.starts_with('#') && !href.starts_with("//") - && is_downloadable_by_extension(href) { + { + if is_downloadable_by_extension(href) { push_if_existing_file(href, &mut paths, workspace_root); } + } i = href_start + rel_end + 1; } else { i += 2; diff --git a/src/crates/core/src/service/remote_connect/bot/telegram.rs b/src/crates/core/src/service/remote_connect/bot/telegram.rs index 0688b651..d0c89d2e 100644 --- a/src/crates/core/src/service/remote_connect/bot/telegram.rs +++ b/src/crates/core/src/service/remote_connect/bot/telegram.rs @@ -109,7 +109,7 @@ impl TelegramBot { pub async fn send_message(&self, chat_id: i64, text: &str) -> Result<()> { let client = reqwest::Client::new(); let resp = client - .post(self.api_url("sendMessage")) + .post(&self.api_url("sendMessage")) .json(&serde_json::json!({ "chat_id": chat_id, "text": text, @@ -152,7 +152,7 @@ impl TelegramBot { let client = reqwest::Client::new(); let resp = client - .post(self.api_url("sendMessage")) + .post(&self.api_url("sendMessage")) .json(&serde_json::json!({ "chat_id": chat_id, "text": text, @@ -188,7 +188,7 @@ impl TelegramBot { let client = reqwest::Client::new(); let resp = client - .post(self.api_url("sendDocument")) + .post(&self.api_url("sendDocument")) .multipart(form) .send() .await?; @@ -280,7 +280,7 @@ impl TelegramBot { async fn answer_callback_query(&self, callback_query_id: &str) { let client = reqwest::Client::new(); let _ = client - .post(self.api_url("answerCallbackQuery")) + .post(&self.api_url("answerCallbackQuery")) .json(&serde_json::json!({ "callback_query_id": callback_query_id })) .send() .await; @@ -345,7 +345,7 @@ impl TelegramBot { ] }); let resp = client - .post(self.api_url("setMyCommands")) + .post(&self.api_url("setMyCommands")) .json(&commands) .send() .await?; @@ -443,7 +443,7 @@ impl TelegramBot { .build()?; let resp = client - .get(self.api_url("getUpdates")) + .get(&self.api_url("getUpdates")) .query(&[ ("offset", (offset + 1).to_string()), ("timeout", "30".to_string()), @@ -668,8 +668,8 @@ impl TelegramBot { } // Intercept file download callbacks before normal command routing. - if let Some(stripped) = text.strip_prefix("download_file:") { - let token = stripped.trim().to_string(); + if text.starts_with("download_file:") { + let token = text["download_file:".len()..].trim().to_string(); drop(states); self.handle_download_request(chat_id, &token).await; return; diff --git a/src/crates/core/src/service/remote_connect/bot/weixin.rs b/src/crates/core/src/service/remote_connect/bot/weixin.rs index 986c6fd3..497f534d 100644 --- a/src/crates/core/src/service/remote_connect/bot/weixin.rs +++ b/src/crates/core/src/service/remote_connect/bot/weixin.rs @@ -59,7 +59,7 @@ fn encrypt_aes_128_ecb_pkcs7(plaintext: &[u8], key: &[u8; 16]) -> Vec { let pad_len = 16 - (plaintext.len() % 16); let pad_len = if pad_len == 0 { 16 } else { pad_len }; let mut buf = plaintext.to_vec(); - buf.extend(std::iter::repeat_n(pad_len as u8, pad_len)); + buf.extend(std::iter::repeat(pad_len as u8).take(pad_len)); let mut out = Vec::with_capacity(buf.len()); for chunk in buf.chunks_exact(16) { let mut block = aes::cipher::generic_array::GenericArray::clone_from_slice(chunk); @@ -94,7 +94,7 @@ fn build_cdn_download_url(cdn_base: &str, encrypted_query_param: &str) -> String } fn decrypt_aes_128_ecb_pkcs7(ciphertext: &[u8], key: &[u8; 16]) -> Result> { - if ciphertext.is_empty() || !ciphertext.len().is_multiple_of(16) { + if ciphertext.is_empty() || ciphertext.len() % 16 != 0 { return Err(anyhow!( "invalid ciphertext length {}", ciphertext.len() @@ -824,7 +824,6 @@ impl WeixinBot { } /// `ilink/bot/getuploadurl` — returns `upload_param` for CDN POST. - #[allow(clippy::too_many_arguments)] async fn ilink_get_upload_url( &self, to_user_id: &str, @@ -1213,7 +1212,7 @@ impl WeixinBot { } fn is_weixin_media_item_type(type_id: i64) -> bool { - matches!(type_id, 2..=5) + matches!(type_id, 2 | 3 | 4 | 5) } fn body_from_item_list(items: &[Value]) -> String { @@ -1334,7 +1333,11 @@ impl WeixinBot { let mut s = header.to_string(); for (i, a) in actions.iter().enumerate() { let n = i + 1; - s.push_str(&format!("{n}. {} → {}\n", a.label, a.command)); + if language.is_chinese() { + s.push_str(&format!("{n}. {} → {}\n", a.label, a.command)); + } else { + s.push_str(&format!("{n}. {} → {}\n", a.label, a.command)); + } } s } @@ -1669,8 +1672,8 @@ impl WeixinBot { } let trimmed = text.trim(); - if let Some(stripped) = trimmed.strip_prefix("download_file:") { - let token = stripped.trim().to_string(); + if trimmed.starts_with("download_file:") { + let token = trimmed["download_file:".len()..].trim().to_string(); let workspace_root = state.current_workspace.clone(); drop(states); self.handle_download_request(&peer_id, &token, workspace_root) diff --git a/src/crates/core/src/service/remote_connect/ngrok.rs b/src/crates/core/src/service/remote_connect/ngrok.rs index 9966ac23..291a1801 100644 --- a/src/crates/core/src/service/remote_connect/ngrok.rs +++ b/src/crates/core/src/service/remote_connect/ngrok.rs @@ -41,9 +41,13 @@ fn find_ngrok() -> Option { PathBuf::from("C:\\ngrok\\ngrok.exe"), ]; - candidates - .into_iter() - .find(|path| path.exists() && path.is_file()) + for path in candidates { + if path.exists() && path.is_file() { + return Some(path); + } + } + + None } /// Check if ngrok is installed and available. diff --git a/src/crates/core/src/service/remote_connect/remote_server.rs b/src/crates/core/src/service/remote_connect/remote_server.rs index 21f8d5c0..a60fe585 100644 --- a/src/crates/core/src/service/remote_connect/remote_server.rs +++ b/src/crates/core/src/service/remote_connect/remote_server.rs @@ -433,7 +433,7 @@ pub enum RemoteResponse { #[serde(skip_serializing_if = "Option::is_none")] active_turn: Option, #[serde(skip_serializing_if = "Option::is_none")] - model_catalog: Box>, + model_catalog: Option, }, AnswerAccepted, InteractionAccepted { @@ -1198,7 +1198,7 @@ impl RemoteSessionStateTracker { if let Some(item) = state.active_items.iter_mut().rev().find(|i| { i.item_type == "tool" - && i.tool.as_ref().is_some_and(|t| { + && i.tool.as_ref().map_or(false, |t| { t.id == resolved_id || (allow_name_fallback && t.name == tool_name) }) }) { @@ -1233,7 +1233,7 @@ impl RemoteSessionStateTracker { .. } => subagent_parent_info .as_ref() - .is_some_and(|p| p.session_id == self.target_session_id), + .map_or(false, |p| p.session_id == self.target_session_id), _ => false, } } else { @@ -1333,7 +1333,7 @@ impl RemoteSessionStateTracker { } "ConfirmationNeeded" => { let params = val.get("params").cloned(); - let input_preview = params.as_ref().and_then(make_slim_params); + let input_preview = params.as_ref().and_then(|v| make_slim_params(v)); Self::upsert_active_tool( &mut s, &tool_id, @@ -1346,7 +1346,7 @@ impl RemoteSessionStateTracker { } "Started" => { let params = val.get("params").cloned(); - let input_preview = params.as_ref().and_then(make_slim_params); + let input_preview = params.as_ref().and_then(|v| make_slim_params(v)); let tool_input = if tool_name == "AskUserQuestion" || tool_name == "Task" || tool_name == "TodoWrite" @@ -1403,7 +1403,7 @@ impl RemoteSessionStateTracker { } if let Some(item) = s.active_items.iter_mut().rev().find(|i| { i.item_type == "tool" - && i.tool.as_ref().is_some_and(|t| { + && i.tool.as_ref().map_or(false, |t| { (t.id == tool_id || (allow_name_fallback && t.name == tool_name)) && t.status == "running" @@ -1430,7 +1430,7 @@ impl RemoteSessionStateTracker { } if let Some(item) = s.active_items.iter_mut().rev().find(|i| { i.item_type == "tool" - && i.tool.as_ref().is_some_and(|t| { + && i.tool.as_ref().map_or(false, |t| { (t.id == tool_id || (allow_name_fallback && t.name == tool_name)) && t.status == "running" @@ -1459,7 +1459,7 @@ impl RemoteSessionStateTracker { } if let Some(item) = s.active_items.iter_mut().rev().find(|i| { i.item_type == "tool" - && i.tool.as_ref().is_some_and(|t| { + && i.tool.as_ref().map_or(false, |t| { (t.id == tool_id || (allow_name_fallback && t.name == tool_name)) && matches!( @@ -2037,7 +2037,7 @@ impl RemoteServer { new_messages: None, total_msg_count: None, active_turn: None, - model_catalog: Box::new(None), + model_catalog: None, }; } @@ -2058,11 +2058,11 @@ impl RemoteServer { new_messages: None, total_msg_count: None, active_turn, - model_catalog: Box::new(if should_send_model_catalog { + model_catalog: if should_send_model_catalog { current_model_catalog } else { None - }), + }, }; } @@ -2120,11 +2120,11 @@ impl RemoteServer { new_messages: send_msgs, total_msg_count: send_total, active_turn, - model_catalog: Box::new(if should_send_model_catalog { + model_catalog: if should_send_model_catalog { current_model_catalog } else { None - }), + }, } } diff --git a/src/crates/core/src/service/remote_ssh/manager.rs b/src/crates/core/src/service/remote_ssh/manager.rs index 1e4b0e64..a83cf5fc 100644 --- a/src/crates/core/src/service/remote_ssh/manager.rs +++ b/src/crates/core/src/service/remote_ssh/manager.rs @@ -65,7 +65,7 @@ struct SSHHandler { /// Expected host key (if connecting to known host) expected_key: Option<(String, u16, PublicKey)>, /// Callback for new host key verification - verify_callback: Option>, + verify_callback: Option bool + Send + Sync>>, /// Known hosts storage for verification known_hosts: Option>>>, /// Host info for known hosts lookup @@ -78,8 +78,6 @@ struct SSHHandler { disconnect_reason: Arc>>, } -type HostKeyVerifyCallback = dyn Fn(String, u16, &PublicKey) -> bool + Send + Sync; - impl SSHHandler { #[allow(dead_code)] fn new() -> Self { @@ -533,7 +531,7 @@ impl SSHConnectionManager { let has_proxy_command = ssh_cfg_has(&host_settings, "ProxyCommand"); - SSHConfigLookupResult { + return SSHConfigLookupResult { found: true, config: Some(SSHConfigEntry { host: host.to_string(), @@ -543,7 +541,7 @@ impl SSHConnectionManager { identity_file, agent: if has_proxy_command { None } else { Some(true) }, }), - } + }; } #[cfg(not(feature = "ssh_config"))] diff --git a/src/crates/core/src/service/remote_ssh/remote_fs.rs b/src/crates/core/src/service/remote_ssh/remote_fs.rs index 47ded685..13c28b8f 100644 --- a/src/crates/core/src/service/remote_ssh/remote_fs.rs +++ b/src/crates/core/src/service/remote_ssh/remote_fs.rs @@ -206,7 +206,10 @@ impl RemoteFileService { .unwrap_or_else(|| path.to_string()); // Check if this is a directory - let is_dir: bool = self.exists(connection_id, path).await.unwrap_or_default(); + let is_dir = match self.exists(connection_id, path).await { + Ok(exists) => exists, + Err(_) => false, + }; // Check if it's a directory by trying to read it let is_dir = if is_dir { @@ -297,16 +300,19 @@ impl RemoteFileService { /// Remove a directory and its contents recursively via SFTP pub async fn remove_dir_all(&self, connection_id: &str, path: &str) -> anyhow::Result<()> { // First, delete all contents - if let Ok(entries) = self.read_dir(connection_id, path).await { - for entry in entries { - let entry_path = entry.path.clone(); - if entry.is_dir { - Box::pin(self.remove_dir_all(connection_id, &entry_path)).await?; - } else { - let manager = self.get_manager(connection_id).await?; - manager.sftp_remove(connection_id, &entry_path).await?; + match self.read_dir(connection_id, path).await { + Ok(entries) => { + for entry in entries { + let entry_path = entry.path.clone(); + if entry.is_dir { + Box::pin(self.remove_dir_all(connection_id, &entry_path)).await?; + } else { + let manager = self.get_manager(connection_id).await?; + manager.sftp_remove(connection_id, &entry_path).await?; + } } } + Err(_) => {} } // Then remove the directory itself diff --git a/src/crates/core/src/service/remote_ssh/remote_terminal.rs b/src/crates/core/src/service/remote_ssh/remote_terminal.rs index 8b8f6e10..5d49fa2c 100644 --- a/src/crates/core/src/service/remote_ssh/remote_terminal.rs +++ b/src/crates/core/src/service/remote_ssh/remote_terminal.rs @@ -86,7 +86,6 @@ impl RemoteTerminalManager { /// Returns a `CreateSessionResult` with a pre-subscribed output receiver. /// The owner task is spawned immediately — the output_rx is guaranteed to /// receive all data including the initial shell prompt. - #[allow(clippy::too_many_arguments)] pub async fn create_session( &self, session_id: Option, diff --git a/src/crates/core/src/service/remote_ssh/workspace_state.rs b/src/crates/core/src/service/remote_ssh/workspace_state.rs index 070212a0..e2dc62f3 100644 --- a/src/crates/core/src/service/remote_ssh/workspace_state.rs +++ b/src/crates/core/src/service/remote_ssh/workspace_state.rs @@ -338,12 +338,6 @@ pub struct RemoteWorkspaceStateManager { terminal_manager: Arc>>, } -impl Default for RemoteWorkspaceStateManager { - fn default() -> Self { - Self::new() - } -} - impl RemoteWorkspaceStateManager { pub fn new() -> Self { Self { diff --git a/src/crates/core/src/service/session/types.rs b/src/crates/core/src/service/session/types.rs index d89246ce..d76ae6c1 100644 --- a/src/crates/core/src/service/session/types.rs +++ b/src/crates/core/src/service/session/types.rs @@ -172,13 +172,16 @@ pub struct DialogTurnData { /// Persisted dialog turn kind. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] -#[derive(Default)] pub enum DialogTurnKind { - #[default] UserDialog, ManualCompaction, } +impl Default for DialogTurnKind { + fn default() -> Self { + Self::UserDialog + } +} impl DialogTurnKind { pub fn is_model_visible(self) -> bool { @@ -377,7 +380,6 @@ pub struct SessionTranscriptIndexEntry { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -#[derive(Default)] pub struct SessionTranscriptExportOptions { #[serde(default)] pub tools: bool, @@ -389,6 +391,16 @@ pub struct SessionTranscriptExportOptions { pub turns: Option>, } +impl Default for SessionTranscriptExportOptions { + fn default() -> Self { + Self { + tools: false, + tool_inputs: false, + thinking: false, + turns: None, + } + } +} #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] diff --git a/src/crates/core/src/service/snapshot/file_lock_manager.rs b/src/crates/core/src/service/snapshot/file_lock_manager.rs index c1207121..e60cff98 100644 --- a/src/crates/core/src/service/snapshot/file_lock_manager.rs +++ b/src/crates/core/src/service/snapshot/file_lock_manager.rs @@ -2,7 +2,7 @@ use crate::service::snapshot::types::{SnapshotError, SnapshotResult}; use log::{debug, info, warn}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::time::SystemTime; use tokio::sync::RwLock; @@ -203,7 +203,7 @@ impl FileLockManager { /// Adds an item to the waiting queue. async fn add_to_waiting_queue( &self, - file_path: &Path, + file_path: &PathBuf, session_id: &str, tool_name: &str, ) -> SnapshotResult<()> { @@ -216,7 +216,7 @@ impl FileLockManager { }; waiting_queue - .entry(file_path.to_path_buf()) + .entry(file_path.clone()) .or_insert_with(Vec::new) .push(queue_item); diff --git a/src/crates/core/src/service/snapshot/isolation_manager.rs b/src/crates/core/src/service/snapshot/isolation_manager.rs index 381e5741..88a61095 100644 --- a/src/crates/core/src/service/snapshot/isolation_manager.rs +++ b/src/crates/core/src/service/snapshot/isolation_manager.rs @@ -1,10 +1,11 @@ -use crate::service::snapshot::types::{SnapshotError, SnapshotResult}; +use crate::service::snapshot::types::{SnapshotError, SnapshotResult}; use log::{debug, info}; use std::fs::{self, OpenOptions}; use std::io::Write; use std::path::{Path, PathBuf}; /// Git isolation manager + pub struct IsolationManager { bitfun_dir: PathBuf, workspace_dir: PathBuf, @@ -97,12 +98,13 @@ impl IsolationManager { /// Verifies no Git operations are impacted. async fn verify_no_git_operations(&self) -> SnapshotResult<()> { let git_dir = self.workspace_dir.join(".git"); - if git_dir.exists() - && self.bitfun_dir.starts_with(&git_dir) { + if git_dir.exists() { + if self.bitfun_dir.starts_with(&git_dir) { return Err(SnapshotError::GitIsolationFailure( ".bitfun directory should not be inside .git directory".to_string(), )); } + } self.verify_isolation_integrity().await?; diff --git a/src/crates/core/src/service/snapshot/manager.rs b/src/crates/core/src/service/snapshot/manager.rs index 55dd1f21..45506467 100644 --- a/src/crates/core/src/service/snapshot/manager.rs +++ b/src/crates/core/src/service/snapshot/manager.rs @@ -366,7 +366,7 @@ impl Tool for WrappedTool { } fn user_facing_name(&self) -> String { - self.original_tool.user_facing_name().to_string() + format!("{}", self.original_tool.user_facing_name()) } async fn is_enabled(&self) -> bool { @@ -413,11 +413,11 @@ impl Tool for WrappedTool { options: &crate::agentic::tools::framework::ToolRenderOptions, ) -> String { let original_message = self.original_tool.render_tool_use_message(input, options); - original_message.to_string() + format!("{}", original_message) } fn render_tool_use_rejected_message(&self) -> String { - self.original_tool.render_tool_use_rejected_message().to_string() + format!("{}", self.original_tool.render_tool_use_rejected_message()) } fn render_tool_result_message(&self, output: &Value) -> String { diff --git a/src/crates/core/src/service/snapshot/service.rs b/src/crates/core/src/service/snapshot/service.rs index 575c1278..9c386b49 100644 --- a/src/crates/core/src/service/snapshot/service.rs +++ b/src/crates/core/src/service/snapshot/service.rs @@ -103,7 +103,6 @@ impl SnapshotService { } /// Intercept a tool call before it modifies the file system. - #[allow(clippy::too_many_arguments)] pub async fn intercept_file_modification( &self, session_id: &str, diff --git a/src/crates/core/src/service/snapshot/snapshot_core.rs b/src/crates/core/src/service/snapshot/snapshot_core.rs index 9b54f6f6..c5bcbce8 100644 --- a/src/crates/core/src/service/snapshot/snapshot_core.rs +++ b/src/crates/core/src/service/snapshot/snapshot_core.rs @@ -114,7 +114,6 @@ impl SnapshotCore { } /// Start a file operation (before snapshot), returns operation_id. - #[allow(clippy::too_many_arguments)] pub async fn start_file_operation( &mut self, session_id: &str, @@ -857,7 +856,7 @@ impl SnapshotCore { }; let path = self.session_file_path(session_id); let data = - serde_json::to_string_pretty(session).map_err(SnapshotError::Serialization)?; + serde_json::to_string_pretty(session).map_err(|e| SnapshotError::Serialization(e))?; tokio::fs::write(path, data) .await .map_err(SnapshotError::Io)?; diff --git a/src/crates/core/src/service/terminal/src/api.rs b/src/crates/core/src/service/terminal/src/api.rs index ce06f856..875692e8 100644 --- a/src/crates/core/src/service/terminal/src/api.rs +++ b/src/crates/core/src/service/terminal/src/api.rs @@ -1,4 +1,4 @@ -//! API module - Public interface for terminal operations +//! API module - Public interface for terminal operations //! //! This module provides the public API for external consumers (Tauri, WebSocket, etc.) //! It defines request/response types and the main service interface. @@ -512,7 +512,7 @@ impl TerminalApi { // Tauri-compatible commands (when used with Tauri) // ============================================================================ -// Module for Tauri command integration +/// Module for Tauri command integration // #[cfg(feature = "tauri")] // pub mod tauri_commands { // use super::*; diff --git a/src/crates/core/src/service/terminal/src/session/binding.rs b/src/crates/core/src/service/terminal/src/session/binding.rs index 26222b54..a3d2869a 100644 --- a/src/crates/core/src/service/terminal/src/session/binding.rs +++ b/src/crates/core/src/service/terminal/src/session/binding.rs @@ -99,7 +99,7 @@ impl TerminalSessionBinding { format!( "term-{}-{}", &owner_id[..8.min(owner_id.len())], - &uuid::Uuid::new_v4().to_string()[..8] + uuid::Uuid::new_v4().to_string()[..8].to_string() ) }); diff --git a/src/crates/core/src/service/terminal/src/session/manager.rs b/src/crates/core/src/service/terminal/src/session/manager.rs index a212f2e6..f0a24e88 100644 --- a/src/crates/core/src/service/terminal/src/session/manager.rs +++ b/src/crates/core/src/service/terminal/src/session/manager.rs @@ -1,4 +1,4 @@ -//! Session Manager - Manages terminal sessions lifecycle +//! Session Manager - Manages terminal sessions lifecycle use std::collections::HashMap; use std::pin::Pin; @@ -378,7 +378,6 @@ impl SessionManager { } /// Create a new terminal session with shell integration - #[allow(clippy::too_many_arguments)] pub async fn create_session( &self, session_id: Option, @@ -405,7 +404,6 @@ impl SessionManager { } /// Create a new terminal session with optional shell integration - #[allow(clippy::too_many_arguments)] pub async fn create_session_with_options( &self, session_id: Option, diff --git a/src/crates/core/src/service/terminal/src/session/mod.rs b/src/crates/core/src/service/terminal/src/session/mod.rs index 07342b87..6add098d 100644 --- a/src/crates/core/src/service/terminal/src/session/mod.rs +++ b/src/crates/core/src/service/terminal/src/session/mod.rs @@ -29,10 +29,8 @@ use crate::shell::ShellType; /// Terminal session status #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[derive(Default)] pub enum SessionStatus { /// Session is starting up - #[default] Starting, /// Session is active and running Active, @@ -46,6 +44,11 @@ pub enum SessionStatus { Terminating, } +impl Default for SessionStatus { + fn default() -> Self { + SessionStatus::Starting + } +} /// Terminal session information #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/crates/core/src/service/terminal/src/shell/integration.rs b/src/crates/core/src/service/terminal/src/shell/integration.rs index c573633b..0bf6b317 100644 --- a/src/crates/core/src/service/terminal/src/shell/integration.rs +++ b/src/crates/core/src/service/terminal/src/shell/integration.rs @@ -41,10 +41,8 @@ pub enum OscSequence { /// Command execution state #[derive(Debug, Clone, PartialEq)] -#[derive(Default)] pub enum CommandState { /// Waiting for prompt - #[default] Idle, /// Prompt is being displayed Prompt, @@ -69,6 +67,11 @@ impl CommandState { } } +impl Default for CommandState { + fn default() -> Self { + CommandState::Idle + } +} /// Event emitted by shell integration #[derive(Debug, Clone)] @@ -426,10 +429,14 @@ impl ShellIntegration { self.post_command_collecting = true; // Emit event but keep command_id for output collection - let event = self.current_command_id.as_ref().map(|cmd_id| ShellIntegrationEvent::CommandFinished { + let event = if let Some(cmd_id) = &self.current_command_id { + Some(ShellIntegrationEvent::CommandFinished { command_id: cmd_id.clone(), exit_code, - }); + }) + } else { + None + }; self.current_command = None; event diff --git a/src/crates/core/src/service/terminal/src/shell/mod.rs b/src/crates/core/src/service/terminal/src/shell/mod.rs index 6a5da7ee..8a1bc4ba 100644 --- a/src/crates/core/src/service/terminal/src/shell/mod.rs +++ b/src/crates/core/src/service/terminal/src/shell/mod.rs @@ -20,7 +20,6 @@ use serde::{Deserialize, Serialize}; /// Supported shell types #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[derive(Default)] pub enum ShellType { /// Bash shell Bash, @@ -31,7 +30,6 @@ pub enum ShellType { /// PowerShell (Windows PowerShell) PowerShell, /// PowerShell Core (cross-platform) - #[default] PowerShellCore, /// Windows CMD Cmd, @@ -133,6 +131,19 @@ impl ShellType { } } +impl Default for ShellType { + fn default() -> Self { + #[cfg(windows)] + { + // Prefer PowerShell Core over Windows PowerShell + ShellType::PowerShellCore + } + #[cfg(not(windows))] + { + ShellType::Bash + } + } +} impl std::fmt::Display for ShellType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/src/crates/core/src/service/token_usage/service.rs b/src/crates/core/src/service/token_usage/service.rs index 8206b5b8..a065e1a2 100644 --- a/src/crates/core/src/service/token_usage/service.rs +++ b/src/crates/core/src/service/token_usage/service.rs @@ -139,7 +139,6 @@ impl TokenUsageService { } /// Record a token usage event - #[allow(clippy::too_many_arguments)] pub async fn record_usage( &self, model_id: String, @@ -352,7 +351,7 @@ impl TokenUsageService { } } - current_date += Duration::days(1); + current_date = current_date + Duration::days(1); } // Filter by model_id, session_id, and subagent flag diff --git a/src/crates/core/src/service/token_usage/types.rs b/src/crates/core/src/service/token_usage/types.rs index eed1d12e..25b974ca 100644 --- a/src/crates/core/src/service/token_usage/types.rs +++ b/src/crates/core/src/service/token_usage/types.rs @@ -22,7 +22,6 @@ pub struct TokenUsageRecord { /// Aggregated token statistics for a model #[derive(Debug, Clone, Serialize, Deserialize)] -#[derive(Default)] pub struct ModelTokenStats { pub model_id: String, pub total_input: u64, @@ -40,6 +39,22 @@ pub struct ModelTokenStats { pub last_used: Option>, } +impl Default for ModelTokenStats { + fn default() -> Self { + Self { + model_id: String::new(), + total_input: 0, + total_output: 0, + total_cached: 0, + total_tokens: 0, + session_count: 0, + request_count: 0, + session_ids: HashSet::new(), + first_used: None, + last_used: None, + } + } +} /// Token statistics for a specific session #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/crates/core/src/service/workspace/context_generator.rs b/src/crates/core/src/service/workspace/context_generator.rs index 31c6b6cc..f27c3428 100644 --- a/src/crates/core/src/service/workspace/context_generator.rs +++ b/src/crates/core/src/service/workspace/context_generator.rs @@ -111,7 +111,6 @@ impl WorkspaceContextGenerator { } /// Creates a default generator. - #[allow(clippy::should_implement_trait)] pub fn default() -> Self { Self::new(None, Arc::new(FileTreeService::default())) } @@ -433,7 +432,7 @@ impl WorkspaceContextGenerator { .file_tree_service .get_directory_contents(path) .await - .map_err(BitFunError::service)?; + .map_err(|e| BitFunError::service(e))?; let mut structure = String::new(); let dir_name = path_buf @@ -552,13 +551,14 @@ impl WorkspaceContextGenerator { in_package = false; } - if in_package && line.contains('=') - && (line.starts_with("name") + if in_package && line.contains('=') { + if line.starts_with("name") || line.starts_with("description") - || line.starts_with("version")) + || line.starts_with("version") { info.push_str(&format!("{}\n", line)); } + } } if !info.is_empty() { diff --git a/src/crates/core/src/service/workspace/manager.rs b/src/crates/core/src/service/workspace/manager.rs index e7404758..ac5764fc 100644 --- a/src/crates/core/src/service/workspace/manager.rs +++ b/src/crates/core/src/service/workspace/manager.rs @@ -594,7 +594,7 @@ impl WorkspaceInfo { if stats .last_modified .as_ref() - .is_none_or(|last_modified| last_modified < &modified_dt) + .map_or(true, |last_modified| last_modified < &modified_dt) { stats.last_modified = Some(modified_dt); } @@ -1217,7 +1217,7 @@ impl WorkspaceManager { /// Removes a workspace. pub fn remove_workspace(&mut self, workspace_id: &str) -> BitFunResult<()> { - if self.workspaces.remove(workspace_id).is_some() { + if let Some(_) = self.workspaces.remove(workspace_id) { if self.current_workspace_id.as_ref() == Some(&workspace_id.to_string()) { self.current_workspace_id = None; } @@ -1325,10 +1325,9 @@ impl WorkspaceManager { /// Returns manager statistics. pub fn get_statistics(&self) -> WorkspaceManagerStatistics { - let mut stats = WorkspaceManagerStatistics { - total_workspaces: self.workspaces.len(), - ..WorkspaceManagerStatistics::default() - }; + let mut stats = WorkspaceManagerStatistics::default(); + + stats.total_workspaces = self.workspaces.len(); for workspace in self.workspaces.values() { match workspace.status { diff --git a/src/crates/core/src/service/workspace/service.rs b/src/crates/core/src/service/workspace/service.rs index 9ed6de37..f32af8a7 100644 --- a/src/crates/core/src/service/workspace/service.rs +++ b/src/crates/core/src/service/workspace/service.rs @@ -1112,8 +1112,8 @@ impl WorkspaceService { workspaces.retain(|_id, ws| { if ws.workspace_kind == WorkspaceKind::Remote { // Check if this remote workspace has the required metadata - let has_ssh_host = ws.metadata.get("sshHost").and_then(|v| v.as_str()).is_some_and(|s| !s.trim().is_empty()); - let has_connection_id = ws.metadata.get("connectionId").and_then(|v| v.as_str()).is_some_and(|s| !s.trim().is_empty()); + let has_ssh_host = ws.metadata.get("sshHost").and_then(|v| v.as_str()).map_or(false, |s| !s.trim().is_empty()); + let has_connection_id = ws.metadata.get("connectionId").and_then(|v| v.as_str()).map_or(false, |s| !s.trim().is_empty()); if !has_ssh_host || !has_connection_id { // Skip this legacy remote workspace info!("Skipping legacy remote workspace without required metadata: id={}, root_path={}", _id, ws.root_path.display()); diff --git a/src/crates/core/src/util/errors.rs b/src/crates/core/src/util/errors.rs index 95edc2e5..7db157db 100644 --- a/src/crates/core/src/util/errors.rs +++ b/src/crates/core/src/util/errors.rs @@ -138,7 +138,8 @@ impl BitFunError { } pub fn serialization>(msg: T) -> Self { - Self::Serialization(serde_json::Error::io(std::io::Error::other( + Self::Serialization(serde_json::Error::io(std::io::Error::new( + std::io::ErrorKind::Other, msg.into(), ))) } @@ -148,7 +149,7 @@ impl BitFunError { } pub fn io>(msg: T) -> Self { - Self::Io(std::io::Error::other(msg.into())) + Self::Io(std::io::Error::new(std::io::ErrorKind::Other, msg.into())) } pub fn cancelled>(msg: T) -> Self { diff --git a/src/crates/core/src/util/json_extract.rs b/src/crates/core/src/util/json_extract.rs index 13a9cc39..7c9b2f17 100644 --- a/src/crates/core/src/util/json_extract.rs +++ b/src/crates/core/src/util/json_extract.rs @@ -1,12 +1,12 @@ -//! Robust JSON extraction from AI model responses. -//! -//! AI models often wrap JSON in markdown code blocks (`` ```json ... ``` ``), -//! or include leading/trailing prose. This module provides a single public -//! helper that handles all common formats and falls back gracefully. -//! -//! When the extracted text is not valid JSON (e.g. the model emitted unescaped -//! quotes inside string values), a best-effort repair pass is attempted before -//! giving up. +/// Robust JSON extraction from AI model responses. +/// +/// AI models often wrap JSON in markdown code blocks (`` ```json ... ``` ``), +/// or include leading/trailing prose. This module provides a single public +/// helper that handles all common formats and falls back gracefully. +/// +/// When the extracted text is not valid JSON (e.g. the model emitted unescaped +/// quotes inside string values), a best-effort repair pass is attempted before +/// giving up. use log::{debug, warn}; @@ -60,11 +60,7 @@ pub fn extract_json_from_ai_response(response: &str) -> Option { // Second pass: attempt repair on each candidate. for candidate in &candidates { if let Some(repaired) = try_repair_json(candidate) { - debug!( - "JSON repair succeeded (original length={}, repaired length={})", - candidate.len(), - repaired.len() - ); + debug!("JSON repair succeeded (original length={}, repaired length={})", candidate.len(), repaired.len()); return Some(repaired); } } @@ -196,10 +192,7 @@ fn try_repair_json(input: &str) -> Option { } fn next_non_whitespace(chars: &[char], start: usize) -> Option { - chars[start..] - .iter() - .find(|c| !c.is_ascii_whitespace()) - .copied() + chars[start..].iter().find(|c| !c.is_ascii_whitespace()).copied() } /// Characters that legitimately follow a closing `"` in JSON. @@ -355,15 +348,11 @@ mod tests { #[test] fn repair_unescaped_chinese_style_quotes() { // AI writes: "headline": "用户问AI"你是什么模型"" — inner quotes are ASCII U+0022 - let input = - "```json\n{\"headline\": \"用户问AI\"你是什么模型\"\", \"detail\": \"ok\"}\n```"; + let input = "```json\n{\"headline\": \"用户问AI\"你是什么模型\"\", \"detail\": \"ok\"}\n```"; let result = extract_json_from_ai_response(input); assert!(result.is_some(), "repair should succeed"); let parsed: serde_json::Value = serde_json::from_str(&result.unwrap()).unwrap(); - assert!(parsed["headline"] - .as_str() - .unwrap() - .contains("你是什么模型")); + assert!(parsed["headline"].as_str().unwrap().contains("你是什么模型")); assert_eq!(parsed["detail"].as_str().unwrap(), "ok"); } @@ -372,10 +361,7 @@ mod tests { // "text": "他说"你好"然后又说"再见"" let input = r#"{"text": "他说"你好"然后又说"再见"", "other": "fine"}"#; let result = extract_json_from_ai_response(input); - assert!( - result.is_some(), - "repair should handle multiple rogue quotes" - ); + assert!(result.is_some(), "repair should handle multiple rogue quotes"); let parsed: serde_json::Value = serde_json::from_str(&result.unwrap()).unwrap(); assert!(parsed["text"].as_str().unwrap().contains("你好")); assert!(parsed["text"].as_str().unwrap().contains("再见")); @@ -406,10 +392,7 @@ mod tests { let result = extract_json_from_ai_response(input); assert!(result.is_some(), "should repair the fun ending JSON"); let parsed: serde_json::Value = serde_json::from_str(&result.unwrap()).unwrap(); - assert!(parsed["headline"] - .as_str() - .unwrap() - .contains("你到底是什么模型")); + assert!(parsed["headline"].as_str().unwrap().contains("你到底是什么模型")); } #[test] @@ -419,10 +402,7 @@ mod tests { let result = extract_json_from_ai_response(input); assert!(result.is_some(), "should repair interaction style JSON"); let parsed: serde_json::Value = serde_json::from_str(&result.unwrap()).unwrap(); - assert!(parsed["narrative"] - .as_str() - .unwrap() - .contains("这个项目是什么")); + assert!(parsed["narrative"].as_str().unwrap().contains("这个项目是什么")); } #[test] diff --git a/src/crates/core/src/util/process_manager.rs b/src/crates/core/src/util/process_manager.rs index 65739844..4185b703 100644 --- a/src/crates/core/src/util/process_manager.rs +++ b/src/crates/core/src/util/process_manager.rs @@ -90,7 +90,7 @@ pub fn create_command>(program: S) -> Command { { let mut cmd = cmd; cmd.creation_flags(CREATE_NO_WINDOW); - cmd + return cmd; } #[cfg(not(windows))] @@ -105,7 +105,7 @@ pub fn create_tokio_command>(program: S) -> TokioComma { let mut cmd = cmd; cmd.creation_flags(CREATE_NO_WINDOW); - cmd + return cmd; } #[cfg(not(windows))] diff --git a/src/crates/events/src/agentic.rs b/src/crates/events/src/agentic.rs index b2c61a9c..fcf70855 100644 --- a/src/crates/events/src/agentic.rs +++ b/src/crates/events/src/agentic.rs @@ -1,4 +1,4 @@ -//! Agentic Events Definition +///! Agentic Events Definition use serde::{Deserialize, Serialize}; use std::time::SystemTime; diff --git a/src/crates/events/src/types.rs b/src/crates/events/src/types.rs index 0e20f2ce..23f20576 100644 --- a/src/crates/events/src/types.rs +++ b/src/crates/events/src/types.rs @@ -5,11 +5,14 @@ use serde::{Deserialize, Serialize}; /// Event priority #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -#[derive(Default)] pub enum EventPriority { Low = 0, - #[default] Normal = 1, High = 2, } +impl Default for EventPriority { + fn default() -> Self { + Self::Normal + } +} diff --git a/src/crates/transport/src/adapters/tauri.rs b/src/crates/transport/src/adapters/tauri.rs index cff31c65..2fd16f4d 100644 --- a/src/crates/transport/src/adapters/tauri.rs +++ b/src/crates/transport/src/adapters/tauri.rs @@ -1,7 +1,7 @@ -//! Tauri transport adapter -//! -//! Uses Tauri's app.emit() system to send events to frontend -//! Maintains compatibility with current implementation +/// Tauri transport adapter +/// +/// Uses Tauri's app.emit() system to send events to frontend +/// Maintains compatibility with current implementation #[cfg(feature = "tauri-adapter")] use crate::traits::{TextChunk, ToolEventPayload, TransportAdapter}; diff --git a/src/crates/transport/src/events.rs b/src/crates/transport/src/events.rs index 3e5e28fa..1ca4a4b4 100644 --- a/src/crates/transport/src/events.rs +++ b/src/crates/transport/src/events.rs @@ -73,11 +73,14 @@ pub struct BackendEventPayload { /// Event priority #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -#[derive(Default)] pub enum EventPriority { Low = 0, - #[default] Normal = 1, High = 2, } +impl Default for EventPriority { + fn default() -> Self { + Self::Normal + } +}