aboutsummaryrefslogtreecommitdiffstats
path: root/crates/atuin-ai/src/diff.rs
diff options
context:
space:
mode:
authorMichelle Tilley <michelle@michelletilley.net>2026-04-21 10:53:31 -0700
committerGitHub <noreply@github.com>2026-04-21 10:53:31 -0700
commit33c779aa9894e1347aeaa4c73e536cf842aee684 (patch)
treebfe0f60252518798ccf4621c7bea06021e64d2f4 /crates/atuin-ai/src/diff.rs
parentfeat: AI tool rendering overhaul + edit_file tool (#3423) (diff)
downloadatuin-33c779aa9894e1347aeaa4c73e536cf842aee684.zip
feat: Implement write_file tool with overwrite safety (#3432)
## Summary Implements the `write_file` client-side tool — creates new files or overwrites existing ones with an explicit `overwrite` flag for safety. - **Overwrite flag**: Writing to an existing file without `overwrite: true` returns an error directing the LLM to set the flag or use `edit_file` for targeted changes. Prevents accidental overwrites. - **Snapshots**: Existing files are backed up before overwriting (same infrastructure as `edit_file`). - **Content preview**: Completed writes show the first 10 lines in gray with line numbers, plus "+ N more lines" for longer files. - **Atomic writes**: Uses `tempfile` + fsync + rename (same as `edit_file`). - **File tracker update**: After writing, the file is registered in the tracker so subsequent `edit_file` calls work without a separate read. - **Permission**: Shares the `"Write"` rule with `edit_file` — one permission covers both tools.
Diffstat (limited to 'crates/atuin-ai/src/diff.rs')
-rw-r--r--crates/atuin-ai/src/diff.rs34
1 files changed, 34 insertions, 0 deletions
diff --git a/crates/atuin-ai/src/diff.rs b/crates/atuin-ai/src/diff.rs
index 663481c0..e704175c 100644
--- a/crates/atuin-ai/src/diff.rs
+++ b/crates/atuin-ai/src/diff.rs
@@ -101,6 +101,40 @@ impl EditPreview {
}
}
+/// Maximum lines to show in a write preview.
+const WRITE_PREVIEW_LINES: usize = 10;
+
+/// A content preview for a write_file operation.
+///
+/// Shows the first N lines of the written content plus a count of
+/// remaining lines if truncated.
+#[derive(Debug, Clone)]
+pub(crate) struct WritePreview {
+ /// First lines of content (up to WRITE_PREVIEW_LINES).
+ pub lines: Vec<String>,
+ /// Total number of lines in the written file.
+ pub total_lines: usize,
+}
+
+impl WritePreview {
+ /// Create a preview from file content.
+ pub fn from_content(content: &str) -> Self {
+ let all_lines: Vec<&str> = content.lines().collect();
+ let total_lines = all_lines.len();
+ let lines = all_lines
+ .into_iter()
+ .take(WRITE_PREVIEW_LINES)
+ .map(String::from)
+ .collect();
+ WritePreview { lines, total_lines }
+ }
+
+ /// Number of lines not shown in the preview.
+ pub fn remaining_lines(&self) -> usize {
+ self.total_lines.saturating_sub(self.lines.len())
+ }
+}
+
/// Build a single DiffHunk from a group of adjacent raw hunks.
fn build_hunk(group: &[&imara_diff::Hunk], input: &InternedInput<&str>) -> DiffHunk {
let first = group.first().unwrap();