feat: detect kilo code cli

refs #270
This commit is contained in:
Ogulcan Celik 2026-06-01 03:50:45 +03:00
parent 2fcf9b678d
commit 1d56cd0e18
10 changed files with 71 additions and 3 deletions

View File

@ -4,6 +4,7 @@
### Added
- Added `ui.right_click_passthrough_modifier` so a configured modifier such as `ctrl` can forward right-click hold and drag gestures to mouse-reporting pane apps while normal right-click still opens Herdr's pane menu. (#148)
- Added Kilo Code CLI automatic detection for idle, working, and blocked terminal states. (#270)
## [0.6.6] - 2026-05-31

View File

@ -154,6 +154,7 @@ automatic detection works out of the box. process name matching plus terminal ou
| [opencode](https://github.com/anomalyco/opencode) | ✓ | ✓ | ✓ |
| [grok cli](https://x.ai/grok) | ✓ | ✓ | ✓ |
| [hermes agent](https://github.com/NousResearch/hermes-agent) | ✓ | ✓ | ✓ |
| [kilo code cli](https://kilo.ai/) | ✓ | ✓ | ✓ |
| cursor agent | ✓ | ✓ | ✓ |
| antigravity cli | ✓ | ✓ | ✓ |
| kimi code cli | ✓ | ✓ | ✓ |

View File

@ -19,6 +19,7 @@ Automatic detection works out of the box for common coding agents.
| OpenCode | yes | yes | yes |
| Grok CLI | yes | yes | yes |
| Hermes Agent | yes | yes | yes |
| Kilo Code CLI | yes | yes | yes |
| Cursor Agent | yes | yes | yes |
| Antigravity CLI | yes | yes | yes |
| Kimi Code CLI | yes | yes | yes |

View File

@ -401,7 +401,7 @@ cjk_ime_cursor_shape = "steady_block"
When enabled, the cursor stays visible at the focused pane's reported position. If the pane reports no cursor position, the anchor falls back to the pane's top-left so a stable IME hint is always available.
`cjk_ime_agents` is an optional allow-list. When empty, the reveal applies to any focused pane. When non-empty, the reveal only applies if the focused pane's detected agent matches one of the listed names — useful to enable the reveal only for AI-agent TUIs that paint their own cursor while leaving plain shells untouched. Accepted names: `pi`, `claude`, `codex`, `gemini`, `cursor`, `agy`, `cline`, `opencode`, `copilot`, `kimi`, `kiro`, `droid`, `amp`, `grok`, `hermes`, `qodercli`, and `qoder`. Unknown names are ignored; if the list contains no valid names, the reveal does not apply.
`cjk_ime_agents` is an optional allow-list. When empty, the reveal applies to any focused pane. When non-empty, the reveal only applies if the focused pane's detected agent matches one of the listed names — useful to enable the reveal only for AI-agent TUIs that paint their own cursor while leaving plain shells untouched. Accepted names: `pi`, `claude`, `codex`, `gemini`, `cursor`, `agy`, `cline`, `opencode`, `copilot`, `kimi`, `kiro`, `droid`, `amp`, `grok`, `hermes`, `kilo`, `qodercli`, and `qoder`. Unknown names are ignored; if the list contains no valid names, the reveal does not apply.
`cjk_ime_cursor_shape` controls the DECSCUSR shape rendered for the IME anchor. Accepted values: `block`, `steady_block` (default), `underline`, `steady_underline`, `bar`, `steady_bar`.

View File

@ -414,7 +414,8 @@ pub struct ExperimentalConfig {
/// list means apply to any focused pane. Unknown agent names are ignored;
/// if the list contains no valid names, the reveal does not apply.
/// Accepted names: pi, claude, codex, gemini, cursor, cline, opencode,
/// copilot, kimi, kiro, droid, amp, grok, hermes. Default: empty.
/// copilot, kimi, kiro, droid, amp, grok, hermes, kilo, qodercli, qoder.
/// Default: empty.
pub cjk_ime_agents: Vec<String>,
/// Cursor shape rendered for the IME anchor when
/// `reveal_hidden_cursor_for_cjk_ime` is enabled. Default: "steady_block".

View File

@ -40,6 +40,7 @@ pub struct AgentSoundOverrides {
pub amp: AgentSoundSetting,
pub grok: AgentSoundSetting,
pub hermes: AgentSoundSetting,
pub kilo: AgentSoundSetting,
pub qodercli: AgentSoundSetting,
}
@ -131,6 +132,7 @@ impl AgentSoundOverrides {
Some(Agent::Amp) => self.amp,
Some(Agent::Grok) => self.grok,
Some(Agent::Hermes) => self.hermes,
Some(Agent::Kilo) => self.kilo,
Some(Agent::Qodercli) => self.qodercli,
None => AgentSoundSetting::Default,
}
@ -167,6 +169,7 @@ impl Default for AgentSoundOverrides {
amp: AgentSoundSetting::Default,
grok: AgentSoundSetting::Default,
hermes: AgentSoundSetting::Default,
kilo: AgentSoundSetting::Default,
qodercli: AgentSoundSetting::Default,
}
}

View File

@ -0,0 +1,9 @@
use super::super::AgentState;
pub(super) fn detect(content: &str) -> AgentState {
if content.to_lowercase().contains("esc interrupt") {
return AgentState::Working;
}
super::opencode::detect(content)
}

View File

@ -9,6 +9,7 @@ pub(super) mod gemini;
pub(super) mod github_copilot;
pub(super) mod grok;
pub(super) mod hermes;
pub(super) mod kilo;
pub(super) mod kimi;
pub(super) mod kiro;
pub(super) mod opencode;
@ -34,6 +35,7 @@ pub(super) fn detect(agent: Agent, screen_content: &str) -> AgentDetection {
Agent::Amp => amp::detect(screen_content),
Agent::Grok => grok::detect(screen_content),
Agent::Hermes => hermes::detect(screen_content),
Agent::Kilo => kilo::detect(screen_content),
Agent::Qodercli => qodercli::detect(screen_content),
};

View File

@ -54,6 +54,7 @@ pub enum Agent {
Amp,
Grok,
Hermes,
Kilo,
Qodercli,
}
@ -74,6 +75,7 @@ pub fn agent_label(agent: Agent) -> &'static str {
Agent::Amp => "amp",
Agent::Grok => "grok",
Agent::Hermes => "hermes",
Agent::Kilo => "kilo",
Agent::Qodercli => "qodercli",
}
}
@ -96,6 +98,7 @@ pub fn parse_agent_label(agent: &str) -> Option<Agent> {
"amp" | "amp-local" => Some(Agent::Amp),
"grok" | "grok-build" => Some(Agent::Grok),
"hermes" | "hermes-agent" => Some(Agent::Hermes),
"kilo" | "kilo-code" | "kilo code" => Some(Agent::Kilo),
"qodercli" | "qoderclicn" | "qoder" | "qodercn" => Some(Agent::Qodercli),
_ => None,
}
@ -122,6 +125,7 @@ pub fn identify_agent(process_name: &str) -> Option<Agent> {
"amp" | "amp-local" => Some(Agent::Amp),
"grok" | "grok-build" => Some(Agent::Grok),
"hermes" | "hermes-agent" => Some(Agent::Hermes),
"kilo" | "kilo-code" | "kilo code" => Some(Agent::Kilo),
"qodercli" | "qoderclicn" | "qoder" | "qodercn" => Some(Agent::Qodercli),
_ => None,
}
@ -255,6 +259,11 @@ fn detect_qodercli(content: &str) -> AgentState {
detect_state(Some(Agent::Qodercli), content)
}
#[cfg(test)]
fn detect_kilo(content: &str) -> AgentState {
detect_state(Some(Agent::Kilo), content)
}
// ---------------------------------------------------------------------------
// Shared helpers
// ---------------------------------------------------------------------------
@ -567,6 +576,11 @@ mod tests {
AgentState::Working,
),
(Agent::Hermes, "msg=interrupt", AgentState::Working),
(
Agent::Kilo,
"Ask · DeepSeek V4 Pro\nesc interrupt",
AgentState::Working,
),
(Agent::Qodercli, "\u{280B} Thinking...", AgentState::Working),
];
@ -604,6 +618,8 @@ mod tests {
assert_eq!(identify_agent("grok-build"), Some(Agent::Grok));
assert_eq!(identify_agent("hermes"), Some(Agent::Hermes));
assert_eq!(identify_agent("hermes-agent"), Some(Agent::Hermes));
assert_eq!(identify_agent("kilo"), Some(Agent::Kilo));
assert_eq!(identify_agent("kilo-code"), Some(Agent::Kilo));
}
#[test]
@ -623,6 +639,7 @@ mod tests {
assert_eq!(parse_agent_label("kiro-cli"), Some(Agent::Kiro));
assert_eq!(parse_agent_label("grok-build"), Some(Agent::Grok));
assert_eq!(parse_agent_label("hermes-agent"), Some(Agent::Hermes));
assert_eq!(parse_agent_label("kilo-code"), Some(Agent::Kilo));
}
#[test]
@ -634,6 +651,7 @@ mod tests {
assert_eq!(agent_label(Agent::Kiro), "kiro");
assert_eq!(agent_label(Agent::Grok), "grok");
assert_eq!(agent_label(Agent::Hermes), "hermes");
assert_eq!(agent_label(Agent::Kilo), "kilo");
}
#[test]
@ -1581,6 +1599,38 @@ mod tests {
assert_eq!(detect_opencode("> "), AgentState::Idle);
}
// ---- Kilo ----
#[test]
fn kilo_waiting_question_prompt() {
assert_eq!(
detect_kilo(
"Write the following joke to ~/joke.md?\n\
1. Yes, write it\n\
2. No, thanks\n\
3. Type your own answer\n\
select enter submit esc dismiss",
),
AgentState::Blocked
);
}
#[test]
fn kilo_working() {
assert_eq!(
detect_kilo("Ask · DeepSeek V4 Pro\nesc interrupt\n12.8K (1%) · $0.01"),
AgentState::Working
);
}
#[test]
fn kilo_idle() {
assert_eq!(
detect_kilo("Ask anything... \"Fix broken tests\"\nCode · DeepSeek V4 Pro"),
AgentState::Idle
);
}
// ---- GitHub Copilot ----
#[test]

View File

@ -265,7 +265,7 @@ pane_history = false
# matches one of these names. Empty means apply to any focused pane.
# If the list contains no valid names, the reveal does not apply.
# Accepted: pi, claude, codex, gemini, cursor, cline, opencode, copilot,
# kimi, kiro, droid, amp, grok, hermes, qodercli, qoder.
# kimi, kiro, droid, amp, grok, hermes, kilo, qodercli, qoder.
# cjk_ime_agents = []
# Cursor shape rendered when reveal_hidden_cursor_for_cjk_ime is true.
# Values: block, steady_block (default), underline, steady_underline, bar, steady_bar.