Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/next/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

### Fixed
- 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)
Expand Down
58 changes: 58 additions & 0 deletions src/detect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,21 @@ fn agent_name_from_path_token(token: &str) -> Option<String> {
}

fn agent_name_from_known_package_path(path: &str) -> Option<String> {
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")
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated
{
return Some(agent_label(Agent::Cursor).to_string());
}
}

let components: Vec<String> = path
.split(['/', '\\'])
.filter(|component| !component.is_empty())
Expand Down Expand Up @@ -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 {
Expand Down
Loading