aboutsummaryrefslogtreecommitdiff
path: root/src/detect/mod.rs
diff options
context:
space:
mode:
authorÖmer Hamid Kamışlı <hamidkamisli@icloud.com>2026-08-27 14:30:24 +0300
committerGitHub <noreply@github.com>2026-08-27 14:30:24 +0300
commit7b675f42af35508eab66ac42fe1598628597a893 (patch)
treef67d4a863c11c6967df6f4538b9d455bfc105163 /src/detect/mod.rs
parentd79fd746a96ddb5642939c9727baefce642d78e6 (diff)
feat(detect): add muse agent with generic Pick blocked detection (#2489)
* feat(detect): add muse agent with generic Pick blocked detection Add Muse agent manifests and runtime detection for idle/working/blocked states. Blocked Pick is generic on footer 'Enter to select' so any question title/message triggers, fixing missed waiting answer where previous AND-gated rule required specific hint combos. Cover process names muse, muse-bin, muse-code variants and YOLO footer. refs #herdr * fix(detect): tighten muse manifest and agent lookup - manifests: change working_esc_interrupt and blocked_approval from whole_recent to bottom_non_empty_lines(8) to use captured bottom-buffer controls instead of incidental whole-pane text - manifests: anchor idle_prompt line_regex to ^\s*⟩\s*$ so working chrome "⟩ ..." does not match idle - runtime: restrict muse lookup to explicit aliases (muse|muse-bin|muse-code|muse-cli), remove starts_with("muse") fallback that misclassified museum/muse-helper/muser; add positive/negative identification tests - keep website mirror identical; verified with agent_detection manifest check --require-website refs #2489 refs #2456 * fix(detect): address mjdouglas vendor findings for muse - manifests: widen pick_request_blocked to bottom_non_empty_lines(8) and add Enter to toggle for multi-select; add menu_request_blocked for user-opened pickers (enter confirm/esc go back/esc close/space toggle); add workspace_trust_blocked for Do you trust dialog; tighten blocked_approval to footer chrome Allow once/Always allow/Yes proceed; split idle_prompt into any gate for empty and typed draft and not-gate pick/menu anchors; change idle_status_fallback from YOLO to model \u00b7 effort \u00b7 cwd shape - runtime: replace bare muse-bin alias with muse-bin-<version> digit check to match versioned launcher binary and avoid museum/muse-binary false positives - cross-checked with vendor-side captures reported on PR #2489 against strings in shipped muse-bin binary refs #2489 * fix(detect): handle path-qualified muse-bin versioned binary lookup_agent now uses path_basename before matching so a launcher exec with absolute argv0 like /usr/local/lib/muse/muse-bin-0.1.0-R708.1 is correctly identified. is_muse_versioned_binary also checks basename so direct path-qualified names succeed without relying on the argv0_argv fallback path. Prevents missed Muse detection when screen state and sound overrides are not applied. refs #2489 * fix(detect): add enter save to muse menu detection Evidence documents /theme with enter save footer which was not matched. Add enter save to menu_request_blocked any list and to idle_prompt not-gate (highlighted row ⟩ <choice> shares glyph) and keep website mirror in sync. Update evidence comment counts from four to five. refs #2489 * docs: add Muse to supported agents table Muse detection (manifest, process identification, sound) was added earlier but docs/next agents table omitted the row. Add Muse as screen manifest / none (no integration hook, consistent with Amp/Kiro/Maki) to en/ja/zh-cn translations. * fix(detect): resize agent arrays for new upstream variant Agent::ALL and SCREEN_MANIFEST_AGENTS gained an entry from master but kept stale size annotations, breaking compilation after rebase. refs #2489 * docs: add muse detection entry to next changelog refs #2489 * docs: add muse sound override to config reference refs #2489 * fix(detect): prevent false muse blocked states refs #2489 --------- Co-authored-by: Omer Hamid Kamisli <ohkamisli@poikus.com> Co-authored-by: Ogulcan Celik <ogulcancelik@gmail.com>
Diffstat (limited to 'src/detect/mod.rs')
-rw-r--r--src/detect/mod.rs46
1 files changed, 44 insertions, 2 deletions
diff --git a/src/detect/mod.rs b/src/detect/mod.rs
index def440dc..0f956ee3 100644
--- a/src/detect/mod.rs
+++ b/src/detect/mod.rs
@@ -63,10 +63,11 @@ pub enum Agent {
Qodercli,
Qwen,
Maki,
+ Muse,
}
impl Agent {
- pub const ALL: [Self; 22] = [
+ pub const ALL: [Self; 23] = [
Self::Pi,
Self::Claude,
Self::Codex,
@@ -89,9 +90,10 @@ impl Agent {
Self::Qodercli,
Self::Qwen,
Self::Maki,
+ Self::Muse,
];
- pub const SCREEN_MANIFEST_AGENTS: [Self; 20] = [
+ pub const SCREEN_MANIFEST_AGENTS: [Self; 21] = [
Self::Pi,
Self::Claude,
Self::Codex,
@@ -112,6 +114,7 @@ impl Agent {
Self::Qodercli,
Self::Qwen,
Self::Maki,
+ Self::Muse,
];
}
@@ -139,6 +142,7 @@ pub fn agent_label(agent: Agent) -> &'static str {
Agent::Qodercli => "qodercli",
Agent::Qwen => "qwen",
Agent::Maki => "maki",
+ Agent::Muse => "muse",
}
}
@@ -172,6 +176,7 @@ pub fn interactive_agent_executable(agent: Agent) -> &'static str {
Agent::Qodercli => "qodercli",
Agent::Qwen => "qwen",
Agent::Maki => "maki",
+ Agent::Muse => "muse",
}
}
@@ -186,6 +191,7 @@ pub(crate) fn parse_canonical_agent_label(label: &str) -> Option<Agent> {
}
fn lookup_agent(name: &str) -> Option<Agent> {
+ let name = path_basename(name);
match name {
"pi" => Some(Agent::Pi),
"claude" | "claude-code" => Some(Agent::Claude),
@@ -209,10 +215,25 @@ fn lookup_agent(name: &str) -> Option<Agent> {
"qodercli" | "qoderclicn" | "qoder" | "qodercn" => Some(Agent::Qodercli),
"qwen" | "qwen-code" | "qwen code" => Some(Agent::Qwen),
"maki" => Some(Agent::Maki),
+ "muse" | "muse-code" | "muse-cli" => Some(Agent::Muse),
+ _ if is_muse_versioned_binary(name) => Some(Agent::Muse),
_ => None,
}
}
+/// Muse's install-dir launcher script resolves the active release and execs
+/// `muse-bin-<version>` (e.g. `muse-bin-0.1.0-R708.1`), so the running
+/// process never carries a bare `muse`/`muse-bin` alias. Require a digit
+/// immediately after the `muse-bin-` prefix so unrelated binaries such as
+/// `muse-binary` or a bare `muse-bin` stay unmatched.
+/// Accepts path-qualified `argv0` values by checking only the basename, since
+/// the launcher may `exec` with an absolute install-dir path.
+fn is_muse_versioned_binary(name: &str) -> bool {
+ path_basename(name)
+ .strip_prefix("muse-bin-")
+ .is_some_and(|rest| rest.starts_with(|c: char| c.is_ascii_digit()))
+}
+
/// Identify which agent is running from the process name.
/// Returns `None` for plain shells or unrecognized programs.
pub fn identify_agent(process_name: &str) -> Option<Agent> {
@@ -766,6 +787,19 @@ mod tests {
assert_eq!(identify_agent("qwen"), Some(Agent::Qwen));
assert_eq!(identify_agent("Qwen Code"), Some(Agent::Qwen));
assert_eq!(identify_agent("maki"), Some(Agent::Maki));
+ assert_eq!(identify_agent("muse"), Some(Agent::Muse));
+ assert_eq!(identify_agent("muse-code"), Some(Agent::Muse));
+ assert_eq!(identify_agent("muse-cli"), Some(Agent::Muse));
+ assert_eq!(identify_agent("muse-bin-0.1.0-R708.1"), Some(Agent::Muse));
+ assert_eq!(identify_agent("muse-bin-1.2.3"), Some(Agent::Muse));
+ assert_eq!(
+ identify_agent("/home/user/.local/bin/muse-bin-0.2.1-R1215.1"),
+ Some(Agent::Muse)
+ );
+ assert_eq!(
+ identify_agent(r"C:\Users\user\muse-bin-0.2.1-R1215.1.exe"),
+ Some(Agent::Muse)
+ );
}
#[test]
@@ -836,6 +870,7 @@ mod tests {
(Agent::Qodercli, "qodercli"),
(Agent::Qwen, "qwen"),
(Agent::Maki, "maki"),
+ (Agent::Muse, "muse"),
];
assert_eq!(expected.len(), Agent::ALL.len());
for (agent, executable) in expected {
@@ -879,6 +914,13 @@ mod tests {
assert_eq!(identify_agent("zsh"), None);
assert_eq!(identify_agent("vim"), None);
assert_eq!(identify_agent("node"), None);
+ assert_eq!(identify_agent("museum"), None);
+ assert_eq!(identify_agent("muse-helper"), None);
+ assert_eq!(identify_agent("muser"), None);
+ assert_eq!(identify_agent("musescore"), None);
+ assert_eq!(identify_agent("muse-bin"), None);
+ assert_eq!(identify_agent("muse-bin-"), None);
+ assert_eq!(identify_agent("muse-binary"), None);
}
#[test]