agent_trace/commands/
replace.rs1use crate::git_store::CommitInfo;
2use crate::observability::CliOutput;
3use crate::permissions::{check_permission, PermissionResult};
4use crate::store::Store;
5use crate::types::{Action, Actor, DocType};
6use anyhow::Result;
7use std::path::Path;
8
9pub fn run(
10 store_root: &Path,
11 find: &str,
12 replace: &str,
13 type_filter: Option<&DocType>,
14 dry_run: bool,
15 output: &dyn CliOutput,
16) -> Result<()> {
17 let store = Store::open(store_root)?;
18 let docs = store.manifest.list(type_filter);
19
20 let mut matches: Vec<(std::path::PathBuf, DocType, String)> = Vec::new();
21
22 for doc in &docs {
23 let full = store_root.join(&doc.path);
24 if !full.exists() {
25 continue;
26 }
27 let content = std::fs::read_to_string(&full)?;
28 if content.contains(find) {
29 matches.push((doc.path.clone(), doc.doc_type.clone(), content));
30 }
31 }
32
33 if matches.is_empty() {
34 output.line("No matches found.")?;
35 return Ok(());
36 }
37
38 output.line(&format!("Found {} file(s) with matches:", matches.len()))?;
39 for (path, dt, _) in &matches {
40 output.line(&format!(" [{}] {}", dt.indicator(), path.display()))?;
41 }
42
43 if dry_run {
44 output.line("(dry-run: no changes applied)")?;
45 return Ok(());
46 }
47
48 let mut committed: Vec<(std::path::PathBuf, Action, DocType)> = Vec::new();
49
50 for (path, doc_type, content) in matches {
51 let perm = check_permission(&doc_type, &Actor::User, &store.overrides, Some(&path));
53 if let PermissionResult::Denied { reason } = perm {
54 output.warn(&format!("Skipping {} (denied): {}", path.display(), reason))?;
55 continue;
56 }
57
58 let new_content = content.replace(find, replace);
59 std::fs::write(store_root.join(&path), &new_content)?;
60 committed.push((path, Action::Modify, doc_type));
61 }
62
63 if !committed.is_empty() {
64 let info = CommitInfo {
65 action: Action::Modify,
66 files: committed,
67 actor: Actor::User,
68 summary: format!("replace '{find}' with '{replace}'"),
69 agent_name: None,
70 session_id: None,
71 };
72 store.commit(&info)?;
73 output.line("Changes applied.")?;
74 }
75
76 Ok(())
77}