diff options
| author | Michelle Tilley <michelle@michelletilley.net> | 2026-04-21 10:53:31 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-21 10:53:31 -0700 |
| commit | 33c779aa9894e1347aeaa4c73e536cf842aee684 (patch) | |
| tree | bfe0f60252518798ccf4621c7bea06021e64d2f4 /crates/atuin-ai/src/diff.rs | |
| parent | feat: AI tool rendering overhaul + edit_file tool (#3423) (diff) | |
| download | atuin-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.rs | 34 |
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(); |
