use super::*;
pub(super) fn record_mcp_outcome_event(
mcp_panel_state: &mut McpPanelState,
tool_name: &str,
args_val: &serde_json::Value,
command_success: bool,
) {
let mut mcp_event = crate::agent::runloop::mcp_events::McpEvent::new(
"mcp".to_string(),
tool_name.to_string(),
Some(args_val.to_string()),
);
if command_success {
mcp_event.success(None);
} else {
mcp_event.failure(Some("Command returned a non-zero exit code".to_string()));
}
mcp_panel_state.add_event(mcp_event);
}
pub(super) fn collect_modified_files(modified_files: &[String]) -> Vec<PathBuf> {
modified_files.iter().map(PathBuf::from).collect()
}
pub(super) fn collect_instruction_activity_paths(
workspace_root: &Path,
args_val: &serde_json::Value,
output: &serde_json::Value,
modified_files: &[String],
) -> Vec<PathBuf> {
let canonical_workspace = canonicalize(workspace_root).unwrap_or_else(|_| workspace_root.to_path_buf());
let mut paths = BTreeSet::new();
for modified in modified_files {
push_activity_path(workspace_root, &canonical_workspace, modified, &mut paths);
}
collect_paths_from_value(workspace_root, &canonical_workspace, Some("args"), args_val, &mut paths);
collect_paths_from_value(workspace_root, &canonical_workspace, Some("output"), output, &mut paths);
paths.into_iter().collect()
}
pub(super) fn collect_paths_from_value(
workspace_root: &Path,
canonical_workspace: &Path,
key: Option<&str>,
value: &serde_json::Value,
paths: &mut BTreeSet<PathBuf>,
) {
match value {
serde_json::Value::String(text) => {
if key.is_some_and(path_like_key) {
push_activity_path(workspace_root, canonical_workspace, text, paths);
}
}
serde_json::Value::Array(values) => {
for value in values {
collect_paths_from_value(workspace_root, canonical_workspace, key, value, paths);
}
}
serde_json::Value::Object(map) => {
for (child_key, child_value) in map {
collect_paths_from_value(
workspace_root,
canonical_workspace,
Some(child_key.as_str()),
child_value,
paths,
);
}
}
serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::Number(_) => {}
}
}
pub(super) fn path_like_key(key: &str) -> bool {
matches!(
key,
"path"
| "paths"
| "file"
| "files"
| "file_path"
| "file_paths"
| "cwd"
| "workdir"
| "directory"
| "directories"
| "root"
| "workspace"
)
}
pub(super) fn push_activity_path(
workspace_root: &Path,
canonical_workspace: &Path,
raw: &str,
paths: &mut BTreeSet<PathBuf>,
) {
let trimmed = raw.trim();
if trimmed.is_empty() || trimmed.contains("://") || trimmed.starts_with("untitled:") {
return;
}
let normalized = normalize_workspace_path(workspace_root, Path::new(trimmed));
if normalized.starts_with(canonical_workspace) || normalized.starts_with(workspace_root) {
paths.insert(normalized);
}
}