notedeck

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

commit 66b0899c939c475d09d85c51651a3e4e666d2e78
parent e9f56cf554c7434d4582d941033ce1ea58d5fe75
Author: William Casarin <jb55@jb55.com>
Date:   Thu,  5 Feb 2026 09:49:28 -0800

feat(dave): expand auto-accept rules for read-only bash commands

Add common read-only commands (grep, rg, find, ls, cat, head, tail, etc.)
and git read-only commands (status, log, diff, show, branch) to auto-accept.
This reduces friction for exploratory operations while keeping write
operations requiring explicit approval.

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

Diffstat:
Mcrates/notedeck_dave/src/auto_accept.rs | 110+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 110 insertions(+), 0 deletions(-)

diff --git a/crates/notedeck_dave/src/auto_accept.rs b/crates/notedeck_dave/src/auto_accept.rs @@ -58,11 +58,58 @@ impl Default for AutoAcceptRules { AutoAcceptRule::SmallEdit { max_lines: 2 }, AutoAcceptRule::BashCommand { prefixes: vec![ + // Cargo commands "cargo build".into(), "cargo check".into(), "cargo test".into(), "cargo fmt".into(), "cargo clippy".into(), + "cargo run".into(), + "cargo doc".into(), + // Read-only bash commands + "grep ".into(), + "grep\t".into(), + "rg ".into(), + "rg\t".into(), + "find ".into(), + "find\t".into(), + "ls ".into(), + "ls\t".into(), + "ls\n".into(), + "cat ".into(), + "cat\t".into(), + "head ".into(), + "head\t".into(), + "tail ".into(), + "tail\t".into(), + "wc ".into(), + "wc\t".into(), + "file ".into(), + "file\t".into(), + "stat ".into(), + "stat\t".into(), + "which ".into(), + "which\t".into(), + "type ".into(), + "type\t".into(), + "pwd".into(), + "tree ".into(), + "tree\t".into(), + "tree\n".into(), + "du ".into(), + "du\t".into(), + "df ".into(), + "df\t".into(), + // Git read-only commands + "git status".into(), + "git log".into(), + "git diff".into(), + "git show".into(), + "git branch".into(), + "git remote".into(), + "git rev-parse".into(), + "git ls-files".into(), + "git describe".into(), ], }, AutoAcceptRule::ReadOnlyTool { @@ -206,4 +253,67 @@ mod tests { let input = json!({ "command": " cargo build" }); assert!(rules.should_auto_accept("Bash", &input)); } + + #[test] + fn test_grep_bash_auto_accept() { + let rules = default_rules(); + let input = json!({ "command": "grep -rn \"pattern\" /path" }); + assert!(rules.should_auto_accept("Bash", &input)); + } + + #[test] + fn test_rg_bash_auto_accept() { + let rules = default_rules(); + let input = json!({ "command": "rg \"pattern\" /path" }); + assert!(rules.should_auto_accept("Bash", &input)); + } + + #[test] + fn test_find_bash_auto_accept() { + let rules = default_rules(); + let input = json!({ "command": "find . -name \"*.rs\"" }); + assert!(rules.should_auto_accept("Bash", &input)); + } + + #[test] + fn test_git_status_auto_accept() { + let rules = default_rules(); + let input = json!({ "command": "git status" }); + assert!(rules.should_auto_accept("Bash", &input)); + } + + #[test] + fn test_git_log_auto_accept() { + let rules = default_rules(); + let input = json!({ "command": "git log --oneline -10" }); + assert!(rules.should_auto_accept("Bash", &input)); + } + + #[test] + fn test_git_push_not_auto_accept() { + let rules = default_rules(); + let input = json!({ "command": "git push origin main" }); + assert!(!rules.should_auto_accept("Bash", &input)); + } + + #[test] + fn test_git_commit_not_auto_accept() { + let rules = default_rules(); + let input = json!({ "command": "git commit -m \"test\"" }); + assert!(!rules.should_auto_accept("Bash", &input)); + } + + #[test] + fn test_ls_auto_accept() { + let rules = default_rules(); + let input = json!({ "command": "ls -la /tmp" }); + assert!(rules.should_auto_accept("Bash", &input)); + } + + #[test] + fn test_cat_auto_accept() { + let rules = default_rules(); + let input = json!({ "command": "cat /path/to/file.txt" }); + assert!(rules.should_auto_accept("Bash", &input)); + } }