aboutsummaryrefslogtreecommitdiff
path: root/src/detect/mod.rs
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-11 17:38:52 +0300
committerGitHub <noreply@github.com>2026-09-11 17:38:52 +0300
commitf3cbe03fdb07b860a4543c684fb81c5b346e6913 (patch)
tree01c51ed8d802be4ca09c26e15803f579f4278746 /src/detect/mod.rs
parentcd7edeb06048892ce6d6bb1a9dc37b9af494e966 (diff)
fix: detect cline launchers and idle states (#3959)
refs #2396
Diffstat (limited to 'src/detect/mod.rs')
-rw-r--r--src/detect/mod.rs86
1 files changed, 84 insertions, 2 deletions
diff --git a/src/detect/mod.rs b/src/detect/mod.rs
index c244dd89..0248d5ce 100644
--- a/src/detect/mod.rs
+++ b/src/detect/mod.rs
@@ -200,7 +200,7 @@ fn lookup_agent(name: &str) -> Option<Agent> {
"cursor" | "cursor-agent" => Some(Agent::Cursor),
"devin" | "devin-cli" | "devin cli" => Some(Agent::Devin),
"agy" | "antigravity" | "antigravity-cli" => Some(Agent::Antigravity),
- "cline" => Some(Agent::Cline),
+ "cline" | ".cline" => Some(Agent::Cline),
"omp" => Some(Agent::Omp),
"mastracode" | "mastra-code" | "mastra code" => Some(Agent::Mastracode),
"opencode" | "opencode2" | "open-code" => Some(Agent::OpenCode),
@@ -378,7 +378,10 @@ fn normalized_process_name(process: &crate::platform::ForegroundProcess) -> Stri
if let Some(wrapped_agent) =
wrapped_agent_name_from_runtime_argv(runtime, process.argv.as_deref())
{
- if identify_agent(&wrapped_agent) == Some(Agent::Qwen) {
+ if matches!(
+ identify_agent(&wrapped_agent),
+ Some(Agent::Qwen | Agent::Cline)
+ ) {
return wrapped_agent;
}
}
@@ -984,6 +987,85 @@ mod tests {
}
#[test]
+ fn identify_agent_in_job_detects_cline_native_binaries() {
+ for (name, executable) in [
+ (
+ ".cline",
+ "/home/user/.npm/lib/node_modules/cline/bin/.cline",
+ ),
+ (
+ "cline",
+ "/usr/local/lib/node_modules/@cline/cli-darwin-arm64/bin/cline",
+ ),
+ (
+ "cline.exe",
+ r"C:\Users\user\AppData\Roaming\npm\node_modules\@cline\cli-windows-x64\bin\cline.exe",
+ ),
+ ] {
+ let job = crate::platform::ForegroundJob {
+ process_group_id: 123,
+ processes: vec![foreground_process(123, name, &[executable, "--tui"])],
+ };
+
+ assert_eq!(
+ identify_agent_in_job(&job),
+ Some((Agent::Cline, name.to_string()))
+ );
+ }
+ }
+
+ #[test]
+ fn identify_agent_in_job_detects_cline_node_wrapper() {
+ for (name, argv) in [
+ (
+ "MainThread",
+ vec!["node", "/home/user/.fnm/bin/cline", "--tui"],
+ ),
+ (
+ "node",
+ vec!["node", "/usr/local/lib/node_modules/cline/bin/cline"],
+ ),
+ (
+ "node.exe",
+ vec![
+ r"C:\Program Files\nodejs\node.exe",
+ r"C:\Users\user\AppData\Roaming\npm\node_modules\cline\bin\cline",
+ ],
+ ),
+ ] {
+ let job = crate::platform::ForegroundJob {
+ process_group_id: 123,
+ processes: vec![foreground_process(123, name, &argv)],
+ };
+
+ assert_eq!(
+ identify_agent_in_job(&job),
+ Some((Agent::Cline, "cline".to_string()))
+ );
+ }
+ }
+
+ #[test]
+ fn identify_agent_in_job_rejects_unrelated_cline_mentions() {
+ for argv in [
+ vec!["node"],
+ vec!["node", "/path/to/other.js", "cline"],
+ vec!["node", "-e", "cline"],
+ vec!["node", "/path/to/cline-helper"],
+ vec!["/path/to/.cline-helper"],
+ vec!["/path/to/other", "/path/to/cline"],
+ ] {
+ let job = crate::platform::ForegroundJob {
+ process_group_id: 123,
+ processes: vec![foreground_process(123, "MainThread", &argv)],
+ };
+
+ assert_eq!(identify_agent_in_job(&job), None);
+ }
+ assert_eq!(identify_agent("MainThread"), None);
+ }
+
+ #[test]
fn identify_agent_in_job_detects_windows_cursor_install() {
let job = crate::platform::ForegroundJob {
process_group_id: 123,