notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit da8c8e643a347ad0719590ec922180e81db40b0b
parent c83dd63f61205357624a678b767d81acacd6d74a
Author: William Casarin <jb55@jb55.com>
Date:   Wed, 28 Jan 2026 19:10:26 -0800

dave: auto-accept small edits (2 lines or less)

Reduces confirmation friction for trivial edits by automatically
approving Edit tool calls that modify 2 lines or less on both old
and new sides. Write operations always require manual approval.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Diffstat:
Mcrates/notedeck_dave/src/backend/claude.rs | 11+++++++++++
Mcrates/notedeck_dave/src/file_update.rs | 16++++++++++++++++
2 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/crates/notedeck_dave/src/backend/claude.rs b/crates/notedeck_dave/src/backend/claude.rs @@ -1,4 +1,5 @@ use crate::backend::traits::AiBackend; +use crate::file_update::FileUpdate; use crate::messages::{ CompactionInfo, DaveApiResponse, PendingPermission, PermissionRequest, PermissionResponse, SessionInfo, SubagentInfo, SubagentStatus, ToolResult, @@ -286,6 +287,16 @@ async fn session_actor( // Handle permission requests (they're blocking the SDK) Some(perm_req) = perm_rx.recv() => { + // Auto-accept small edits (2 lines or less) + const AUTO_ACCEPT_MAX_LINES: usize = 2; + if let Some(file_update) = FileUpdate::from_tool_call(&perm_req.tool_name, &perm_req.tool_input) { + if file_update.is_small_edit(AUTO_ACCEPT_MAX_LINES) { + tracing::debug!("Auto-accepting small edit ({} lines max): {}", AUTO_ACCEPT_MAX_LINES, file_update.file_path); + let _ = perm_req.response_tx.send(PermissionResult::Allow(PermissionResultAllow::default())); + continue; + } + } + // Forward permission request to UI let request_id = Uuid::new_v4(); let (ui_resp_tx, ui_resp_rx) = oneshot::channel(); diff --git a/crates/notedeck_dave/src/file_update.rs b/crates/notedeck_dave/src/file_update.rs @@ -75,6 +75,22 @@ impl FileUpdate { } } + /// Returns true if this is an Edit that changes at most `max_lines` lines + /// on both the old and new side. Never returns true for Write operations. + pub fn is_small_edit(&self, max_lines: usize) -> bool { + match &self.update_type { + FileUpdateType::Edit { + old_string, + new_string, + } => { + let old_lines = old_string.lines().count(); + let new_lines = new_string.lines().count(); + old_lines <= max_lines && new_lines <= max_lines + } + FileUpdateType::Write { .. } => false, + } + } + /// Compute the diff lines for this update pub fn compute_diff(&self) -> Vec<DiffLine> { match &self.update_type {