Walk the directory tree and build a snapshot of all files. Skips directories matching any of the ignore patterns. Only includes regular files (not directories, symlinks, etc.).
(repo_root: &Path, ignore_patterns: &[String])
| 68 | /// Skips directories matching any of the ignore patterns. |
| 69 | /// Only includes regular files (not directories, symlinks, etc.). |
| 70 | fn take_snapshot(repo_root: &Path, ignore_patterns: &[String]) -> AgentResult<FileSnapshot> { |
| 71 | let mut snapshot = HashMap::new(); |
| 72 | |
| 73 | let walker = WalkDir::new(repo_root) |
| 74 | .follow_links(false) |
| 75 | .into_iter() |
| 76 | .filter_entry(|entry| { |
| 77 | // Skip ignored directories |
| 78 | if entry.file_type().is_dir() { |
| 79 | let name = entry.file_name().to_string_lossy(); |
| 80 | for pattern in ignore_patterns { |
| 81 | if name == pattern.as_str() { |
| 82 | return false; |
| 83 | } |
| 84 | } |
| 85 | // Also skip common VCS/build directories for performance |
| 86 | if matches!( |
| 87 | name.as_ref(), |
| 88 | ".git" | "node_modules" | "target" | "__pycache__" | ".DS_Store" |
| 89 | ) { |
| 90 | return false; |
| 91 | } |
| 92 | } |
| 93 | true |
| 94 | }); |
| 95 | |
| 96 | for entry in walker { |
| 97 | let entry = entry.map_err(|e| AgentError::Internal(format!("walkdir error: {}", e)))?; |
| 98 | |
| 99 | // Only track regular files |
| 100 | if !entry.file_type().is_file() { |
| 101 | continue; |
| 102 | } |
| 103 | |
| 104 | let path = entry.path(); |
| 105 | |
| 106 | // Get metadata for mtime and size |
| 107 | let metadata = match entry.metadata() { |
| 108 | Ok(m) => m, |
| 109 | Err(_) => continue, // Skip files we can't stat (permissions, etc.) |
| 110 | }; |
| 111 | |
| 112 | let mtime = metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH); |
| 113 | let size = metadata.len(); |
| 114 | |
| 115 | // Store path relative to repo root |
| 116 | if let Ok(relative) = path.strip_prefix(repo_root) { |
| 117 | snapshot.insert(relative.to_path_buf(), FileEntry { mtime, size }); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | Ok(snapshot) |
| 122 | } |
| 123 | |
| 124 | /// Diff two snapshots to produce a `TurnChanges`. |
| 125 | fn diff_snapshots(before: &FileSnapshot, after: &FileSnapshot) -> TurnChanges { |