winx_code_agent/tools/
file_write_or_edit.rs1mod commit;
7mod matcher;
8mod parser;
9mod report;
10
11#[cfg(test)]
12mod tests;
13
14use std::sync::Arc;
15
16use tokio::sync::Mutex;
17use tracing::instrument;
18
19use crate::errors::{Result, WinxError};
20use crate::state::bash_state::BashState;
21use crate::types::{normalize_thread_id, FileWriteOrEdit};
22pub(crate) use commit::{
23 commit_edit, ensure_parent_dirs, hash_content, plan_edit, write_no_follow,
24};
25
26#[cfg(feature = "fuzzing")]
27pub(crate) fn fuzz_apply_blocks(original: &str, blocks: &str) {
28 let _ = matcher::apply_blocks_with_unescape_retry(original, blocks);
29}
30
31#[instrument(level = "info", skip(bash_state_arc, file_write_or_edit))]
32pub async fn handle_tool_call(
33 bash_state_arc: &Arc<Mutex<Option<BashState>>>,
34 file_write_or_edit: FileWriteOrEdit,
35) -> Result<String> {
36 let mut bash_state_guard = bash_state_arc.lock().await;
37 {
38 let bash_state = bash_state_guard.as_ref().ok_or(WinxError::BashStateNotInitialized)?;
39 let thread_id = normalize_thread_id(&file_write_or_edit.thread_id);
40 if thread_id != bash_state.current_thread_id {
41 return Err(WinxError::ThreadIdMismatch(thread_id));
42 }
43 }
44
45 let mut state = bash_state_guard.take().ok_or(WinxError::BashStateNotInitialized)?;
46 let recovery_state = state.clone();
47 let joined = tokio::task::spawn_blocking(move || {
48 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
49 let planned = plan_edit(
50 &state,
51 &file_write_or_edit.file_path,
52 file_write_or_edit.percentage_to_change,
53 &file_write_or_edit.text_or_search_replace_blocks,
54 true,
55 )?;
56 commit_edit(&mut state, planned)
57 }))
58 .unwrap_or_else(|_| {
59 Err(WinxError::CommandExecutionError(
60 "FileWriteOrEdit panicked on the blocking worker".to_string(),
61 ))
62 });
63 (state, result)
64 })
65 .await;
66
67 match joined {
68 Ok((state, result)) => {
69 *bash_state_guard = Some(state);
70 result
71 }
72 Err(error) => {
73 *bash_state_guard = Some(recovery_state);
74 Err(WinxError::CommandExecutionError(format!(
75 "FileWriteOrEdit blocking task failed: {error}"
76 )))
77 }
78 }
79}