feat(tools): implement Edit and Write tools with deterministic patches (M3 complete)
This commit implements the complete M3 milestone (Edit & Write tools) including: Write tool: - Creates new files with parent directory creation - Overwrites existing files safely - Simple and straightforward implementation Edit tool: - Exact string replacement with uniqueness enforcement - Detects ambiguous matches (multiple occurrences) and fails safely - Detects no-match scenarios and fails with clear error - Automatic backup before modification - Rollback on write failure (restores from backup) - Supports multiline string replacements CLI integration: - Added `write` subcommand: `owlen write <path> <content>` - Added `edit` subcommand: `owlen edit <path> <old_string> <new_string>` - Permission checks for both Write and Edit tools - Clear error messages for permission denials Permission enforcement: - Plan mode (default): blocks Write and Edit (asks for approval) - AcceptEdits mode: allows Write and Edit - Code mode: allows all operations Testing: - 6 new tests in tools-fs for Write/Edit functionality - 5 new tests in CLI for permission enforcement with Edit/Write - Tests verify plan mode blocks, acceptEdits allows, code mode allows all - All 32 workspace tests passing Dependencies: - Added `similar` crate for future diff/patch enhancements M3 milestone complete! ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,11 @@ use std::io::{self, Write};
|
||||
|
||||
#[derive(clap::Subcommand, Debug)]
|
||||
enum Cmd {
|
||||
Read {path: String},
|
||||
Glob {pattern: String},
|
||||
Grep {root: String, pattern: String},
|
||||
Read { path: String },
|
||||
Glob { pattern: String },
|
||||
Grep { root: String, pattern: String },
|
||||
Write { path: String, content: String },
|
||||
Edit { path: String, old_string: String, new_string: String },
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
@@ -105,6 +107,42 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
Cmd::Write { path, content } => {
|
||||
// Check permission
|
||||
match perms.check(Tool::Write, None) {
|
||||
PermissionDecision::Allow => {
|
||||
tools_fs::write_file(&path, &content)?;
|
||||
println!("File written: {}", path);
|
||||
return Ok(());
|
||||
}
|
||||
PermissionDecision::Ask => {
|
||||
return Err(eyre!(
|
||||
"Permission denied: Write operation requires approval. Use --mode acceptEdits or --mode code to allow."
|
||||
));
|
||||
}
|
||||
PermissionDecision::Deny => {
|
||||
return Err(eyre!("Permission denied: Write operation is blocked."));
|
||||
}
|
||||
}
|
||||
}
|
||||
Cmd::Edit { path, old_string, new_string } => {
|
||||
// Check permission
|
||||
match perms.check(Tool::Edit, None) {
|
||||
PermissionDecision::Allow => {
|
||||
tools_fs::edit_file(&path, &old_string, &new_string)?;
|
||||
println!("File edited: {}", path);
|
||||
return Ok(());
|
||||
}
|
||||
PermissionDecision::Ask => {
|
||||
return Err(eyre!(
|
||||
"Permission denied: Edit operation requires approval. Use --mode acceptEdits or --mode code to allow."
|
||||
));
|
||||
}
|
||||
PermissionDecision::Deny => {
|
||||
return Err(eyre!("Permission denied: Edit operation is blocked."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user