From 706b332ca369819f0cf2a4ec6172df2286b27808 Mon Sep 17 00:00:00 2001 From: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Date: Thu, 20 Aug 2026 11:54:51 +0000 Subject: [PATCH 1/3] fix(windows): detect cursor bundled node process refs #3032 --- docs/next/CHANGELOG.md | 1 + src/detect/mod.rs | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md index ac30bfb06c..bf237b633f 100644 --- a/docs/next/CHANGELOG.md +++ b/docs/next/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ### Fixed +- Windows PowerShell panes now keep bare `cursor-agent` launches detected after Cursor hands off to its bundled Node process. (#3032) - Claude Code panes now use visible turn, background shell, and background agent activity as working-state fallbacks when OSC titles are unavailable or disabled. (#1630, #2241) - Tab bar status commands now remove ESC-prefixed terminal control sequences instead of displaying their sequence bodies as text. (#3001) - Unix plugin pane commands now default `PWD` to their resolved working directory, so direct popup tools open at explicit `--cwd` paths while preserving caller-provided `PWD` values. (#2984) diff --git a/src/detect/mod.rs b/src/detect/mod.rs index f78567f6ac..b8755ed809 100644 --- a/src/detect/mod.rs +++ b/src/detect/mod.rs @@ -551,6 +551,21 @@ fn agent_name_from_path_token(token: &str) -> Option { } fn agent_name_from_known_package_path(path: &str) -> Option { + let mut tail = path + .rsplit(['/', '\\']) + .filter(|component| !component.is_empty()); + if let (Some(entrypoint), Some(version), Some(versions), Some(package)) = + (tail.next(), tail.next(), tail.next(), tail.next()) + { + if package.eq_ignore_ascii_case("cursor-agent") + && versions.eq_ignore_ascii_case("versions") + && !version.trim().is_empty() + && entrypoint.eq_ignore_ascii_case("index.js") + { + return Some(agent_label(Agent::Cursor).to_string()); + } + } + let components: Vec = path .split(['/', '\\']) .filter(|component| !component.is_empty()) @@ -894,6 +909,49 @@ mod tests { } } + #[test] + fn identify_agent_in_job_detects_windows_cursor_install() { + let job = crate::platform::ForegroundJob { + process_group_id: 123, + processes: vec![foreground_process( + 123, + "node.exe", + &[ + r"C:\Users\user\AppData\Local\cursor-agent\versions\2026.08.11-e8db854\node.exe", + r"C:\Users\user\AppData\Local\cursor-agent\versions\2026.08.11-e8db854\index.js", + ], + )], + }; + + assert_eq!( + identify_agent_in_job(&job), + Some((Agent::Cursor, "cursor".to_string())) + ); + } + + #[test] + fn identify_agent_in_job_ignores_non_entrypoint_cursor_script() { + for script in [ + r"C:\Users\user\AppData\Local\cursor-agent\versions\2026.08.11-e8db854\scripts\postinstall.js", + r"C:\Users\user\AppData\Local\cursor-agent\versions\2026.08.11-e8db854\index", + r"C:\Users\user\AppData\Local\cursor-agent\versions\2026.08.11-e8db854\index.exe", + ] { + let job = crate::platform::ForegroundJob { + process_group_id: 123, + processes: vec![foreground_process( + 123, + "node.exe", + &[ + r"C:\Users\user\AppData\Local\cursor-agent\versions\2026.08.11-e8db854\node.exe", + script, + ], + )], + }; + + assert_eq!(identify_agent_in_job(&job), None, "script: {script}"); + } + } + #[test] fn identify_agent_in_job_prefers_recognized_process_group_leader() { let job = crate::platform::ForegroundJob { From 648027c6dfe69111f7c8443f530d28840f65afd5 Mon Sep 17 00:00:00 2001 From: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Date: Thu, 20 Aug 2026 12:00:47 +0000 Subject: [PATCH 2/3] docs: clarify cursor detection scope --- docs/next/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md index bf237b633f..f2ff49a039 100644 --- a/docs/next/CHANGELOG.md +++ b/docs/next/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased ### Fixed -- Windows PowerShell panes now keep bare `cursor-agent` launches detected after Cursor hands off to its bundled Node process. (#3032) +- Windows panes now keep bare `cursor-agent` launches detected after Cursor hands off to its bundled Node process. (#3032) - Claude Code panes now use visible turn, background shell, and background agent activity as working-state fallbacks when OSC titles are unavailable or disabled. (#1630, #2241) - Tab bar status commands now remove ESC-prefixed terminal control sequences instead of displaying their sequence bodies as text. (#3001) - Unix plugin pane commands now default `PWD` to their resolved working directory, so direct popup tools open at explicit `--cwd` paths while preserving caller-provided `PWD` values. (#2984) From c479018233dda42033723bbd659df62d979e1b0e Mon Sep 17 00:00:00 2001 From: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Date: Thu, 20 Aug 2026 12:11:55 +0000 Subject: [PATCH 3/3] fix(windows): constrain cursor runtime detection refs #3032 --- src/detect/mod.rs | 64 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/src/detect/mod.rs b/src/detect/mod.rs index b8755ed809..def440dcf0 100644 --- a/src/detect/mod.rs +++ b/src/detect/mod.rs @@ -378,7 +378,9 @@ fn wrapped_agent_name_from_runtime_argv(runtime: &str, argv: Option<&[String]>) let runtime_name = normalized_agent_lookup_name(path_basename(runtime)); match runtime_name.as_str() { - "node" | "bun" => script_arg_agent_name(argv, &["-e", "--eval", "-p", "--print"], &[]), + "node" => cursor_agent_name_from_bundled_node_argv(argv) + .or_else(|| script_arg_agent_name(argv, &["-e", "--eval", "-p", "--print"], &[])), + "bun" => script_arg_agent_name(argv, &["-e", "--eval", "-p", "--print"], &[]), name if is_python_runtime(name) => script_arg_agent_name(argv, &["-c"], &["-m"]), "sh" | "bash" | "zsh" | "fish" => script_arg_agent_name(argv, &["-c"], &[]), "cmd" => windows_cmd_arg_agent_name(argv), @@ -388,6 +390,36 @@ fn wrapped_agent_name_from_runtime_argv(runtime: &str, argv: Option<&[String]>) } } +fn cursor_agent_name_from_bundled_node_argv(argv: &[String]) -> Option { + let (runtime_parent, runtime_name) = path_parent_and_basename(argv.first()?)?; + let (script_parent, script_name) = path_parent_and_basename(argv.get(1)?)?; + if !runtime_name.eq_ignore_ascii_case("node.exe") + || !script_name.eq_ignore_ascii_case("index.js") + || !runtime_parent.eq_ignore_ascii_case(script_parent) + { + return None; + } + + let mut tail = runtime_parent + .rsplit(['/', '\\']) + .filter(|component| !component.is_empty()); + let (Some(version), Some(versions), Some(package)) = (tail.next(), tail.next(), tail.next()) + else { + return None; + }; + (package.eq_ignore_ascii_case("cursor-agent") + && versions.eq_ignore_ascii_case("versions") + && !version.trim().is_empty()) + .then(|| agent_label(Agent::Cursor).to_string()) +} + +fn path_parent_and_basename(path: &str) -> Option<(&str, &str)> { + let split = path.rfind(['/', '\\'])?; + let parent = path[..split].trim_end_matches(['/', '\\']); + let basename = &path[split + 1..]; + (!parent.is_empty() && !basename.is_empty()).then_some((parent, basename)) +} + fn windows_cmd_arg_agent_name(argv: &[String]) -> Option { let mut args = argv.iter().skip(1); while let Some(arg) = args.next() { @@ -551,21 +583,6 @@ fn agent_name_from_path_token(token: &str) -> Option { } fn agent_name_from_known_package_path(path: &str) -> Option { - let mut tail = path - .rsplit(['/', '\\']) - .filter(|component| !component.is_empty()); - if let (Some(entrypoint), Some(version), Some(versions), Some(package)) = - (tail.next(), tail.next(), tail.next(), tail.next()) - { - if package.eq_ignore_ascii_case("cursor-agent") - && versions.eq_ignore_ascii_case("versions") - && !version.trim().is_empty() - && entrypoint.eq_ignore_ascii_case("index.js") - { - return Some(agent_label(Agent::Cursor).to_string()); - } - } - let components: Vec = path .split(['/', '\\']) .filter(|component| !component.is_empty()) @@ -930,7 +947,7 @@ mod tests { } #[test] - fn identify_agent_in_job_ignores_non_entrypoint_cursor_script() { + fn identify_agent_in_job_ignores_invalid_windows_cursor_install_paths() { for script in [ r"C:\Users\user\AppData\Local\cursor-agent\versions\2026.08.11-e8db854\scripts\postinstall.js", r"C:\Users\user\AppData\Local\cursor-agent\versions\2026.08.11-e8db854\index", @@ -950,6 +967,19 @@ mod tests { assert_eq!(identify_agent_in_job(&job), None, "script: {script}"); } + + let lookalike = crate::platform::ForegroundJob { + process_group_id: 123, + processes: vec![foreground_process( + 123, + "node.exe", + &[ + r"C:\Program Files\nodejs\node.exe", + r"C:\workspace\cursor-agent\versions\test\index.js", + ], + )], + }; + assert_eq!(identify_agent_in_job(&lookalike), None); } #[test]