Skip to content

Commit fe0db04

Browse files
authored
[Feat] Summary of file write with Purpose tag in fs_write tool (#279)
1 parent 148393c commit fe0db04

File tree

4 files changed

+108
-23
lines changed

4 files changed

+108
-23
lines changed

crates/chat-cli/src/cli/chat/tools/execute/mod.rs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ use crate::cli::chat::tools::{
1414
OutputKind,
1515
};
1616
use crate::cli::chat::util::truncate_safe;
17-
use crate::cli::chat::{
18-
CONTINUATION_LINE,
19-
PURPOSE_ARROW,
20-
};
2117
use crate::os::Os;
2218

2319
// Platform-specific modules
@@ -130,18 +126,8 @@ impl ExecuteCommand {
130126
)?;
131127

132128
// Add the summary if available
133-
if let Some(summary) = &self.summary {
134-
queue!(
135-
output,
136-
style::Print(CONTINUATION_LINE),
137-
style::Print("\n"),
138-
style::Print(PURPOSE_ARROW),
139-
style::SetForegroundColor(Color::Blue),
140-
style::Print("Purpose: "),
141-
style::ResetColor,
142-
style::Print(summary),
143-
style::Print("\n"),
144-
)?;
129+
if let Some(ref summary) = self.summary {
130+
super::display_purpose(Some(summary), output)?;
145131
}
146132

147133
queue!(output, style::Print("\n"))?;

crates/chat-cli/src/cli/chat/tools/fs_write.rs

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,28 @@ pub enum FsWrite {
4848
path: String,
4949
file_text: Option<String>,
5050
new_str: Option<String>,
51+
summary: Option<String>,
5152
},
5253
#[serde(rename = "str_replace")]
5354
StrReplace {
5455
path: String,
5556
old_str: String,
5657
new_str: String,
58+
summary: Option<String>,
5759
},
5860
#[serde(rename = "insert")]
5961
Insert {
6062
path: String,
6163
insert_line: usize,
6264
new_str: String,
65+
summary: Option<String>,
6366
},
6467
#[serde(rename = "append")]
65-
Append { path: String, new_str: String },
68+
Append {
69+
path: String,
70+
new_str: String,
71+
summary: Option<String>,
72+
},
6673
}
6774

6875
impl FsWrite {
@@ -93,7 +100,9 @@ impl FsWrite {
93100
write_to_file(os, path, file_text).await?;
94101
Ok(Default::default())
95102
},
96-
FsWrite::StrReplace { path, old_str, new_str } => {
103+
FsWrite::StrReplace {
104+
path, old_str, new_str, ..
105+
} => {
97106
let path = sanitize_path_tool_arg(os, path);
98107
let file = os.fs.read_to_string(&path).await?;
99108
let matches = file.match_indices(old_str).collect::<Vec<_>>();
@@ -119,6 +128,7 @@ impl FsWrite {
119128
path,
120129
insert_line,
121130
new_str,
131+
..
122132
} => {
123133
let path = sanitize_path_tool_arg(os, path);
124134
let mut file = os.fs.read_to_string(&path).await?;
@@ -143,7 +153,7 @@ impl FsWrite {
143153
write_to_file(os, &path, file).await?;
144154
Ok(Default::default())
145155
},
146-
FsWrite::Append { path, new_str } => {
156+
FsWrite::Append { path, new_str, .. } => {
147157
let path = sanitize_path_tool_arg(os, path);
148158

149159
queue!(
@@ -182,12 +192,17 @@ impl FsWrite {
182192
};
183193
let new = stylize_output_if_able(os, &relative_path, &file_text);
184194
print_diff(output, &prev, &new, 1)?;
195+
196+
// Display summary as purpose if available after the diff
197+
super::display_purpose(self.get_summary(), output)?;
198+
185199
Ok(())
186200
},
187201
FsWrite::Insert {
188202
path,
189203
insert_line,
190204
new_str,
205+
..
191206
} => {
192207
let path = sanitize_path_tool_arg(os, path);
193208
let relative_path = format_path(cwd, &path);
@@ -206,9 +221,15 @@ impl FsWrite {
206221
let old = stylize_output_if_able(os, &relative_path, &old);
207222
let new = stylize_output_if_able(os, &relative_path, &new);
208223
print_diff(output, &old, &new, start_line)?;
224+
225+
// Display summary as purpose if available after the diff
226+
super::display_purpose(self.get_summary(), output)?;
227+
209228
Ok(())
210229
},
211-
FsWrite::StrReplace { path, old_str, new_str } => {
230+
FsWrite::StrReplace {
231+
path, old_str, new_str, ..
232+
} => {
212233
let path = sanitize_path_tool_arg(os, path);
213234
let relative_path = format_path(cwd, &path);
214235
let file = os.fs.read_to_string_sync(&path)?;
@@ -220,14 +241,21 @@ impl FsWrite {
220241
let new_str = stylize_output_if_able(os, &relative_path, new_str);
221242
print_diff(output, &old_str, &new_str, start_line)?;
222243

244+
// Display summary as purpose if available after the diff
245+
super::display_purpose(self.get_summary(), output)?;
246+
223247
Ok(())
224248
},
225-
FsWrite::Append { path, new_str } => {
249+
FsWrite::Append { path, new_str, .. } => {
226250
let path = sanitize_path_tool_arg(os, path);
227251
let relative_path = format_path(cwd, &path);
228252
let start_line = os.fs.read_to_string_sync(&path)?.lines().count() + 1;
229253
let file = stylize_output_if_able(os, &relative_path, new_str);
230254
print_diff(output, &Default::default(), &file, start_line)?;
255+
256+
// Display summary as purpose if available after the diff
257+
super::display_purpose(self.get_summary(), output)?;
258+
231259
Ok(())
232260
},
233261
}
@@ -246,7 +274,7 @@ impl FsWrite {
246274
bail!("The provided path must exist in order to replace or insert contents into it")
247275
}
248276
},
249-
FsWrite::Append { path, new_str } => {
277+
FsWrite::Append { path, new_str, .. } => {
250278
if path.is_empty() {
251279
bail!("Path must not be empty")
252280
};
@@ -299,6 +327,16 @@ impl FsWrite {
299327
_ => String::new(),
300328
}
301329
}
330+
331+
/// Returns the summary from any variant of the FsWrite enum
332+
fn get_summary(&self) -> Option<&String> {
333+
match self {
334+
FsWrite::Create { summary, .. } => summary.as_ref(),
335+
FsWrite::StrReplace { summary, .. } => summary.as_ref(),
336+
FsWrite::Insert { summary, .. } => summary.as_ref(),
337+
FsWrite::Append { summary, .. } => summary.as_ref(),
338+
}
339+
}
302340
}
303341

304342
/// Writes `content` to `path`, adding a newline if necessary.
@@ -650,6 +688,40 @@ mod tests {
650688
assert!(matches!(fw, FsWrite::Append { .. }));
651689
}
652690

691+
#[test]
692+
fn test_fs_write_deserialize_with_summary() {
693+
let path = "/my-file";
694+
let file_text = "hello world";
695+
let summary = "Added hello world content";
696+
697+
// create with summary
698+
let v = serde_json::json!({
699+
"path": path,
700+
"command": "create",
701+
"file_text": file_text,
702+
"summary": summary
703+
});
704+
let fw = serde_json::from_value::<FsWrite>(v).unwrap();
705+
assert!(matches!(fw, FsWrite::Create { .. }));
706+
if let FsWrite::Create { summary: s, .. } = &fw {
707+
assert_eq!(s.as_ref().unwrap(), summary);
708+
}
709+
710+
// str_replace with summary
711+
let v = serde_json::json!({
712+
"path": path,
713+
"command": "str_replace",
714+
"old_str": "prev string",
715+
"new_str": "new string",
716+
"summary": summary
717+
});
718+
let fw = serde_json::from_value::<FsWrite>(v).unwrap();
719+
assert!(matches!(fw, FsWrite::StrReplace { .. }));
720+
if let FsWrite::StrReplace { summary: s, .. } = &fw {
721+
assert_eq!(s.as_ref().unwrap(), summary);
722+
}
723+
}
724+
653725
#[tokio::test]
654726
async fn test_fs_write_tool_create() {
655727
let os = setup_test_directory().await;

crates/chat-cli/src/cli/chat/tools/mod.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ use std::path::{
1717
PathBuf,
1818
};
1919

20-
use crossterm::style::Stylize;
20+
use crossterm::queue;
21+
use crossterm::style::{
22+
self,
23+
Color,
24+
Stylize,
25+
};
2126
use custom_tool::CustomTool;
2227
use execute::ExecuteCommand;
2328
use eyre::Result;
@@ -415,6 +420,24 @@ fn supports_truecolor(os: &Os) -> bool {
415420
&& shell_color::get_color_support().contains(shell_color::ColorSupport::TERM24BIT)
416421
}
417422

423+
/// Helper function to display a purpose if available (for execute commands)
424+
pub fn display_purpose(purpose: Option<&String>, updates: &mut impl Write) -> Result<()> {
425+
if let Some(purpose) = purpose {
426+
queue!(
427+
updates,
428+
style::Print(super::CONTINUATION_LINE),
429+
style::Print("\n"),
430+
style::Print(super::PURPOSE_ARROW),
431+
style::SetForegroundColor(Color::Blue),
432+
style::Print("Purpose: "),
433+
style::ResetColor,
434+
style::Print(purpose),
435+
style::Print("\n"),
436+
)?;
437+
}
438+
Ok(())
439+
}
440+
418441
#[cfg(test)]
419442
mod tests {
420443
use std::path::MAIN_SEPARATOR;

crates/chat-cli/src/cli/chat/tools/tool_index.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@
111111
"path": {
112112
"description": "Absolute path to file or directory, e.g. `/repo/file.py` or `/repo`.",
113113
"type": "string"
114+
},
115+
"summary": {
116+
"description": "A brief explanation of what the file change does or why it's being made.",
117+
"type": "string"
114118
}
115119
},
116120
"required": ["command", "path"]

0 commit comments

Comments
 (0)