diff options
Diffstat (limited to 'src/detect')
| -rw-r--r-- | src/detect/manifests/cline.toml | 41 | ||||
| -rw-r--r-- | src/detect/mod.rs | 86 |
2 files changed, 123 insertions, 4 deletions
diff --git a/src/detect/manifests/cline.toml b/src/detect/manifests/cline.toml index b30f4417..2d314087 100644 --- a/src/detect/manifests/cline.toml +++ b/src/detect/manifests/cline.toml @@ -1,7 +1,7 @@ id = "cline" -version = "2026.06.10.1" +version = "2026.09.11.1" min_engine_version = 1 -updated_at = "2026-06-10T00:00:00Z" +updated_at = "2026-09-11T00:00:00Z" [[rules]] id = "tool_permission" @@ -18,6 +18,43 @@ any = [ ] [[rules]] +id = "inline_tool_permission" +state = "blocked" +priority = 300 +region = "bottom_non_empty_lines(16)" +visible_blocker = true +contains = ["Cline needs permission", "Approve tool call?", "[y] Approve", "[n] Deny"] + +[[rules]] +id = "inline_question" +state = "blocked" +priority = 300 +region = "bottom_non_empty_lines(24)" +visible_blocker = true +line_regex = ['^\s*Cline is asking a question\s*$', '^\s*>\s+\S'] +contains = ["(Tab)", "Shift+Tab"] + +[[rules]] +id = "active_turn" +state = "working" +priority = 200 +region = "bottom_non_empty_lines(20)" +visible_working = true +any = [ + { line_regex = ['^\s*[\x{2801}-\x{28FF}]\s+\S'] }, + { contains = ["Thinking... (esc to cancel)"] }, +] + +[[rules]] +id = "composer_idle" +state = "idle" +priority = 100 +region = "bottom_non_empty_lines(12)" +visible_idle = true +line_regex = ['^\s*❯(?:\s.*)?$'] +contains = ["─", "(Tab)", "Shift+Tab"] + +[[rules]] id = "default_cline_working" state = "working" priority = -10 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, |
