feat(permissions): implement permission system with plan mode enforcement (M1 complete)
This commit implements the complete M1 milestone (Config & Permissions) including: - New permissions crate with Tool, Action, Mode, and PermissionManager - Three permission modes: Plan (read-only default), AcceptEdits, Code - Pattern matching for permission rules (exact match and prefix with *) - Integration with config-agent for mode-based permission management - CLI integration with --mode flag to override configured mode - Permission checks for Read, Glob, and Grep operations - Comprehensive test suite (10 tests in permissions, 4 in config, 4 in CLI) Also fixes: - Fixed failing test in tools-fs (glob pattern issue) - Improved glob_list() root extraction to handle patterns like "/*.txt" All 21 workspace tests passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,14 +12,18 @@ pub fn glob_list(pattern: &str) -> Result<Vec<String>> {
|
||||
let glob = Glob::new(pattern)?.compile_matcher();
|
||||
|
||||
// Extract the literal prefix to determine the root directory
|
||||
let root = pattern
|
||||
.split("**")
|
||||
.next()
|
||||
.and_then(|s| {
|
||||
let trimmed = s.trim_end_matches('/');
|
||||
if trimmed.is_empty() { None } else { Some(trimmed) }
|
||||
})
|
||||
.unwrap_or(".");
|
||||
// Find the position of the first glob metacharacter
|
||||
let first_glob = pattern
|
||||
.find(|c| matches!(c, '*' | '?' | '[' | '{'))
|
||||
.unwrap_or(pattern.len());
|
||||
|
||||
// Find the last directory separator before the first glob metacharacter
|
||||
let root = if first_glob > 0 {
|
||||
let prefix = &pattern[..first_glob];
|
||||
prefix.rfind('/').map(|pos| &prefix[..pos]).unwrap_or(".")
|
||||
} else {
|
||||
"."
|
||||
};
|
||||
|
||||
let mut out = Vec::new();
|
||||
for result in WalkBuilder::new(root)
|
||||
|
||||
@@ -11,7 +11,8 @@ fn read_and_glob_respect_gitignore() {
|
||||
fs::write(root.join("secret/secret.txt"), "token=123").unwrap();
|
||||
fs::write(root.join(".gitignore"), "secret/\n").unwrap();
|
||||
|
||||
let files = glob_list(root.to_str().unwrap()).unwrap();
|
||||
let pattern = format!("{}/**/*", root.display());
|
||||
let files = glob_list(&pattern).unwrap();
|
||||
assert!(files.iter().any(|p| p.ends_with("a.txt")));
|
||||
assert!(!files.iter().any(|p| p.contains("secret.txt")));
|
||||
assert_eq!(read_file(root.join("a.txt").to_str().unwrap()).unwrap(), "hello");
|
||||
|
||||
Reference in New Issue
Block a user