Skip to content

Commit f88cc1c

Browse files
feat: create file will not fail if file already exists (#658)
1 parent f460c23 commit f88cc1c

File tree

4 files changed

+76
-30
lines changed

4 files changed

+76
-30
lines changed

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

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use syntect::util::{
2626
LinesWithEndings,
2727
as_24_bit_terminal_escaped,
2828
};
29-
use tokio::io::AsyncWriteExt;
3029
use tracing::error;
3130

3231
use super::{
@@ -62,16 +61,20 @@ impl FsWrite {
6261
let cwd = ctx.env().current_dir()?;
6362
match self {
6463
FsWrite::Create { path, file_text } => {
64+
let relative_path = format_path(cwd, fs.chroot_path(path));
65+
let invoke_description = if fs.exists(path) {
66+
"Replacing the current file contents at"
67+
} else {
68+
"Creating a new file at"
69+
};
6570
queue!(
6671
updates,
6772
style::SetForegroundColor(Color::Green),
68-
style::Print(format!("Creating a new file at {}", format_path(cwd, path))),
73+
style::Print(format!("{} {}", invoke_description, relative_path)),
6974
style::ResetColor,
7075
style::Print("\n"),
7176
)?;
72-
let mut file = fs.create_new(path).await?;
73-
file.write_all(file_text.as_bytes()).await?;
74-
file.sync_data().await?;
77+
fs.write(&path, file_text.as_bytes()).await?;
7578
Ok(Default::default())
7679
},
7780
FsWrite::StrReplace { path, old_str, new_str } => {
@@ -132,18 +135,39 @@ impl FsWrite {
132135
let cwd = ctx.env().current_dir()?;
133136
match self {
134137
FsWrite::Create { path, file_text } => {
135-
let path = format_path(cwd, path);
136-
let file = stylize_output_if_able(&path, file_text, None, None);
138+
let relative_path = format_path(cwd, path);
137139
queue!(
138140
updates,
139141
style::Print("Path: "),
140142
style::SetForegroundColor(Color::Green),
141-
style::Print(path),
142-
style::ResetColor,
143-
style::Print("\n\nContents:\n"),
144-
style::Print(file),
143+
style::Print(&relative_path),
145144
style::ResetColor,
145+
style::Print("\n\n"),
146146
)?;
147+
if ctx.fs().exists(path) {
148+
let prev = ctx.fs().read_to_string_sync(path)?;
149+
let prev = stylize_output_if_able(&relative_path, prev.as_str(), None, Some("-"));
150+
let new = stylize_output_if_able(&relative_path, file_text, None, Some("+"));
151+
queue!(
152+
updates,
153+
style::Print("Replacing:\n"),
154+
style::Print(prev),
155+
style::ResetColor,
156+
style::Print("\n\n"),
157+
style::Print("With:\n"),
158+
style::Print(new),
159+
style::ResetColor,
160+
style::Print("\n\n")
161+
)?;
162+
} else {
163+
let file = stylize_output_if_able(&relative_path, file_text, None, None);
164+
queue!(
165+
updates,
166+
style::Print("\n\nContents:\n"),
167+
style::Print(file),
168+
style::ResetColor,
169+
)?;
170+
}
147171
Ok(())
148172
},
149173
FsWrite::Insert {
@@ -182,15 +206,11 @@ impl FsWrite {
182206
style::Print(path),
183207
style::ResetColor,
184208
style::Print("\n\n"),
185-
// style::SetAttribute(style::Attribute::Bold),
186209
style::Print("Replacing:\n"),
187-
// style::SetAttribute(style::Attribute::Reset),
188210
style::Print(old_str),
189211
style::ResetColor,
190212
style::Print("\n\n"),
191-
// style::SetAttribute(style::Attribute::Bold),
192213
style::Print("With:\n"),
193-
// style::SetAttribute(style::Attribute::Reset),
194214
style::Print(new_str),
195215
style::ResetColor
196216
)?;
@@ -433,6 +453,20 @@ mod tests {
433453
.unwrap();
434454

435455
assert_eq!(ctx.fs().read_to_string("/my-file").await.unwrap(), file_text);
456+
457+
let file_text = "Goodbye, world!\nSee you later";
458+
let v = serde_json::json!({
459+
"path": "/my-file",
460+
"command": "create",
461+
"file_text": file_text
462+
});
463+
serde_json::from_value::<FsWrite>(v)
464+
.unwrap()
465+
.invoke(&ctx, &mut stdout)
466+
.await
467+
.unwrap();
468+
469+
assert_eq!(ctx.fs().read_to_string("/my-file").await.unwrap(), file_text);
436470
}
437471

438472
#[tokio::test]

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
},
4141
{
4242
"name": "fs_write",
43-
"description": "Custom editing tool for creating and editing files\n * The `create` command cannot be used if the specified `path` already exists as a file\n * If a `command` generates a long output, it will be truncated and marked with `<response clipped>` \n Notes for using the `str_replace` command:\n * The `old_str` parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!\n * If the `old_str` parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in `old_str` to make it unique\n * The `new_str` parameter should contain the edited lines that should replace the `old_str`",
43+
"description": "Custom editing tool for creating and editing files\n * The `create` command will override the file at `path` if it already exists as a file, and otherwise create a new file\n * If a `command` generates a long output, it will be truncated and marked with `<response clipped>` \n Notes for using the `str_replace` command:\n * The `old_str` parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!\n * If the `old_str` parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in `old_str` to make it unique\n * The `new_str` parameter should contain the edited lines that should replace the `old_str`",
4444
"input_schema": {
4545
"type": "object",
4646
"properties": {

crates/q_cli/tests/chat_response_stubs/fs_write.json

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[
2+
[
3+
"I will use the `fs_write` tool. This should verify that the `create` command will not fail if the file already exists.",
4+
{
5+
"tool_use_id": "1",
6+
"name": "fs_write",
7+
"args": {
8+
"command": "create",
9+
"path": "stub_output/test_file.sh",
10+
"file_text": "#!/usr/bin/env sh\necho 'hello'\necho 'world'\n"
11+
}
12+
}
13+
],
14+
[
15+
"Now, overwriting.",
16+
{
17+
"tool_use_id": "1",
18+
"name": "fs_write",
19+
"args": {
20+
"command": "create",
21+
"path": "stub_output/test_file.sh",
22+
"file_text": "#!/usr/bin/env sh\necho 'goodbye'\necho 'world'\n"
23+
}
24+
}
25+
]
26+
]

0 commit comments

Comments
 (0)