diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-22 14:39:00 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-22 14:39:00 +0300 |
| commit | 760529ce3d8a160c1d6ebafe6b53e9c9fe7574ca (patch) | |
| tree | 7fd41983c9ea5c219e2fab07c9b33532c025f312 /src | |
| parent | 716926f0c68e4e06fc308868cb919fec0bb68c53 (diff) | |
fix: detect kimi code cli states
refs #215
Diffstat (limited to 'src')
| -rw-r--r-- | src/detect.rs | 112 |
1 files changed, 83 insertions, 29 deletions
diff --git a/src/detect.rs b/src/detect.rs index dd7d21c8..31d40b39 100644 --- a/src/detect.rs +++ b/src/detect.rs @@ -68,7 +68,7 @@ pub fn parse_agent_label(agent: &str) -> Option<Agent> { "cline" => Some(Agent::Cline), "opencode" | "open-code" => Some(Agent::OpenCode), "copilot" | "github-copilot" | "ghcs" => Some(Agent::GithubCopilot), - "kimi" => Some(Agent::Kimi), + "kimi" | "kimi-code" | "kimi code" => Some(Agent::Kimi), "kiro" | "kiro-cli" => Some(Agent::Kiro), "droid" => Some(Agent::Droid), "amp" | "amp-local" => Some(Agent::Amp), @@ -93,7 +93,7 @@ pub fn identify_agent(process_name: &str) -> Option<Agent> { "cline" => Some(Agent::Cline), "opencode" | "open-code" => Some(Agent::OpenCode), "copilot" | "github-copilot" | "ghcs" => Some(Agent::GithubCopilot), - "kimi" => Some(Agent::Kimi), + "kimi" | "kimi-code" | "kimi code" => Some(Agent::Kimi), "kiro" | "kiro-cli" => Some(Agent::Kiro), "droid" => Some(Agent::Droid), "amp" | "amp-local" => Some(Agent::Amp), @@ -357,33 +357,52 @@ fn detect_github_copilot(content: &str) -> AgentState { } fn detect_kimi(content: &str) -> AgentState { - let lower = content.to_lowercase(); - - // Blocked - if lower.contains("allow?") - || lower.contains("confirm?") - || lower.contains("approve?") - || lower.contains("proceed?") - || lower.contains("[y/n]") - || lower.contains("(y/n)") - { + if has_kimi_blocked_prompt(content) { return AgentState::Blocked; } - // Working - if lower.contains("thinking") - || lower.contains("processing") - || lower.contains("generating") - || lower.contains("waiting for response") - || lower.contains("ctrl+c to cancel") - || lower.contains("ctrl-c to cancel") - { + if has_kimi_working_status(content) { return AgentState::Working; } AgentState::Idle } +fn has_kimi_blocked_prompt(content: &str) -> bool { + let lower = content.to_lowercase(); + lower.contains("requesting approval") + && (lower.contains("approve once") || lower.contains("approve for this session")) + && lower.contains("reject") + && (lower.contains("1/2/3/4 choose") || lower.contains("โต confirm")) +} + +fn has_kimi_working_status(content: &str) -> bool { + content.lines().any(|line| { + let trimmed = line.trim(); + if matches!( + trimmed, + "๐" | "๐" | "๐" | "๐" | "๐" | "๐" | "๐" | "๐" + ) { + return true; + } + + let mut chars = trimmed.chars(); + let Some(first) = chars.next() else { + return false; + }; + if !('\u{2800}'..='\u{28FF}').contains(&first) { + return false; + } + + let rest = chars + .as_str() + .trim_start_matches(|c| ('\u{2800}'..='\u{28FF}').contains(&c)) + .trim_start() + .to_lowercase(); + rest.starts_with("thinking...") || rest.starts_with("using ") + }) +} + /// Kiro CLI detection. /// /// Kiro exposes reliable working and idle terminal markers. Confirmation @@ -1004,6 +1023,7 @@ mod tests { assert_eq!(identify_agent("cline"), Some(Agent::Cline)); assert_eq!(identify_agent("opencode"), Some(Agent::OpenCode)); assert_eq!(identify_agent("kimi"), Some(Agent::Kimi)); + assert_eq!(identify_agent("Kimi Code"), Some(Agent::Kimi)); assert_eq!(identify_agent("kiro"), Some(Agent::Kiro)); assert_eq!(identify_agent("kiro-cli"), Some(Agent::Kiro)); assert_eq!(identify_agent("copilot"), Some(Agent::GithubCopilot)); @@ -1022,6 +1042,7 @@ mod tests { assert_eq!(parse_agent_label("agy"), Some(Agent::Antigravity)); assert_eq!(parse_agent_label("antigravity"), Some(Agent::Antigravity)); assert_eq!(parse_agent_label("copilot"), Some(Agent::GithubCopilot)); + assert_eq!(parse_agent_label("kimi-code"), Some(Agent::Kimi)); assert_eq!( parse_agent_label("github-copilot"), Some(Agent::GithubCopilot) @@ -1687,28 +1708,61 @@ mod tests { // ---- Kimi ---- #[test] - fn kimi_waiting_approve() { - assert_eq!(detect_kimi("approve?"), AgentState::Blocked); + fn kimi_blocked_approval_prompt_wins_over_spinner() { + let screen = "โ Using Shell (git log --oneline -10)\nโญโ approval โโฎ\nShell is requesting approval to run command:\ngit log --oneline -10\nโ [1] Approve once\n[2] Approve for this session\n[3] Reject\n[4] Reject, tell the model what to do instead\nโฒ/โผ select 1/2/3/4 choose โต confirm"; + assert_eq!(detect_kimi(screen), AgentState::Blocked); + } + + #[test] + fn kimi_approval_words_without_prompt_stay_idle() { + assert_eq!(detect_kimi("approve?"), AgentState::Idle); + assert_eq!(detect_kimi("continue? [y/n]"), AgentState::Idle); + } + + #[test] + fn kimi_working_braille_thinking() { + assert_eq!( + detect_kimi("โ ฆ Thinking... <1s ยท 19 tokens"), + AgentState::Working + ); + } + + #[test] + fn kimi_working_braille_using_tool() { + assert_eq!( + detect_kimi("โ น Using Shell (git log -20 --name-status)"), + AgentState::Working + ); } #[test] - fn kimi_waiting_yn() { - assert_eq!(detect_kimi("continue? [y/n]"), AgentState::Blocked); + fn kimi_working_moon_spinner() { + assert_eq!(detect_kimi("๐"), AgentState::Working); + assert_eq!(detect_kimi("๐"), AgentState::Working); + assert_eq!(detect_kimi("๐"), AgentState::Working); } #[test] - fn kimi_working_thinking() { - assert_eq!(detect_kimi("thinking"), AgentState::Working); + fn kimi_working_moon_spinner_above_input_box() { + let screen = "โจ yo\n\n๐\n\nโโ input โโโโโโโโโโโโโโโโโโโโโโโโโ"; + assert_eq!(detect_kimi(screen), AgentState::Working); } #[test] - fn kimi_working_generating() { - assert_eq!(detect_kimi("generating code"), AgentState::Working); + fn kimi_old_transcript_words_stay_idle() { + assert_eq!(detect_kimi("thinking"), AgentState::Idle); + assert_eq!(detect_kimi("generating code"), AgentState::Idle); + assert_eq!( + detect_kimi("Used Shell (git log --oneline -10)"), + AgentState::Idle + ); + assert_eq!(detect_kimi("some ๐ in prose"), AgentState::Idle); } #[test] fn kimi_idle() { - assert_eq!(detect_kimi("> "), AgentState::Idle); + let screen = "Welcome to Kimi Code CLI!\nโโ input โ\nโโโโโโโโโโโโโโโโ\nagent (Kimi-k2.6 โ) ~/Projects/herdr"; + assert_eq!(detect_kimi(screen), AgentState::Idle); } // ---- Kiro ---- |
