aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-01 03:50:45 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-01 04:16:36 +0300
commit1d56cd0e18fc69a85a0889ca6f9a9f3069a4be3a (patch)
treef18bc80137a6dcfbccc7aa3666b94649197dbae5
parent2fcf9b678d0f154b2d41aa5fafa25aaa73ec7f17 (diff)
feat: detect kilo code cli
refs #270
-rw-r--r--docs/next/CHANGELOG.md1
-rw-r--r--docs/next/README.md1
-rw-r--r--docs/next/website/src/content/docs/agents.mdx1
-rw-r--r--docs/next/website/src/content/docs/configuration.mdx2
-rw-r--r--src/config/model.rs3
-rw-r--r--src/config/sound.rs3
-rw-r--r--src/detect/agents/kilo.rs9
-rw-r--r--src/detect/agents/mod.rs2
-rw-r--r--src/detect/mod.rs50
-rw-r--r--src/main.rs2
10 files changed, 71 insertions, 3 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md
index 13612ccf..724604b6 100644
--- a/docs/next/CHANGELOG.md
+++ b/docs/next/CHANGELOG.md
@@ -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
diff --git a/docs/next/README.md b/docs/next/README.md
index 537377a8..34010388 100644
--- a/docs/next/README.md
+++ b/docs/next/README.md
@@ -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 | ✓ | ✓ | ✓ |
diff --git a/docs/next/website/src/content/docs/agents.mdx b/docs/next/website/src/content/docs/agents.mdx
index 57a0848f..bc540198 100644
--- a/docs/next/website/src/content/docs/agents.mdx
+++ b/docs/next/website/src/content/docs/agents.mdx
@@ -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 |
diff --git a/docs/next/website/src/content/docs/configuration.mdx b/docs/next/website/src/content/docs/configuration.mdx
index e1a5b8fa..b060b9c5 100644
--- a/docs/next/website/src/content/docs/configuration.mdx
+++ b/docs/next/website/src/content/docs/configuration.mdx
@@ -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`.
diff --git a/src/config/model.rs b/src/config/model.rs
index 0776e09b..c312acfa 100644
--- a/src/config/model.rs
+++ b/src/config/model.rs
@@ -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".
diff --git a/src/config/sound.rs b/src/config/sound.rs
index e4f15108..50b2355b 100644
--- a/src/config/sound.rs
+++ b/src/config/sound.rs
@@ -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,
}
}
diff --git a/src/detect/agents/kilo.rs b/src/detect/agents/kilo.rs
new file mode 100644
index 00000000..1ab3651a
--- /dev/null
+++ b/src/detect/agents/kilo.rs
@@ -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)
+}
diff --git a/src/detect/agents/mod.rs b/src/detect/agents/mod.rs
index 87b4043f..4597c122 100644
--- a/src/detect/agents/mod.rs
+++ b/src/detect/agents/mod.rs
@@ -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),
};
diff --git a/src/detect/mod.rs b/src/detect/mod.rs
index cf600f51..b9806b19 100644
--- a/src/detect/mod.rs
+++ b/src/detect/mod.rs
@@ -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]
diff --git a/src/main.rs b/src/main.rs
index b317c2da..b7b11b16 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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.