From c580840307f0bb03ee3ccb8fe659282f3a1031e1 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Tue, 23 Jun 2026 15:26:51 +0300 Subject: [PATCH] fix: detect npm-wrapped pi on windows refs #754 --- src/detect/mod.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/detect/mod.rs b/src/detect/mod.rs index c8292ab2..e88d5e5d 100644 --- a/src/detect/mod.rs +++ b/src/detect/mod.rs @@ -471,9 +471,33 @@ fn agent_name_from_path_token(token: &str) -> Option { } agent_name_from_basename(path_basename(trimmed)) + .or_else(|| agent_name_from_known_package_path(trimmed)) .or_else(|| resolved_agent_name_from_path_token(trimmed)) } +fn agent_name_from_known_package_path(path: &str) -> Option { + let components: Vec = path + .split(['/', '\\']) + .filter(|component| !component.is_empty()) + .map(normalized_agent_lookup_name) + .collect(); + + for window in components.windows(5) { + if window + == [ + "node_modules", + "@earendil-works", + "pi-coding-agent", + "dist", + "cli", + ] + { + return Some(agent_label(Agent::Pi).to_string()); + } + } + None +} + fn resolved_agent_name_from_path_token(token: &str) -> Option { let path = std::path::Path::new(token); if path.components().count() < 2 { @@ -782,6 +806,43 @@ mod tests { ); } + #[test] + fn identify_agent_in_job_detects_node_wrapped_pi_package_cli() { + let job = crate::platform::ForegroundJob { + process_group_id: 123, + processes: vec![foreground_process( + 123, + "node.exe", + &[ + "node.exe", + "C:\\Users\\herdr\\AppData\\Roaming\\npm\\node_modules\\@earendil-works\\pi-coding-agent\\dist\\cli.js", + ], + )], + }; + + assert_eq!( + identify_agent_in_job(&job), + Some((Agent::Pi, "pi".to_string())) + ); + } + + #[test] + fn identify_agent_in_job_ignores_non_cli_pi_package_script() { + let job = crate::platform::ForegroundJob { + process_group_id: 123, + processes: vec![foreground_process( + 123, + "node.exe", + &[ + "node.exe", + "C:\\Users\\herdr\\AppData\\Roaming\\npm\\node_modules\\@earendil-works\\pi-coding-agent\\scripts\\build.js", + ], + )], + }; + + assert_eq!(identify_agent_in_job(&job), None); + } + #[test] fn identify_agent_in_job_detects_windows_cmd_wrapped_codex() { let job = crate::platform::ForegroundJob {