feat: Claude Code CLI v2.1.105–v2.1.131 support #274

Merged
naomi merged 7 commits from feat/lotsa into main 2026-05-06 16:16:06 -07:00
2 changed files with 68 additions and 0 deletions
Showing only changes of commit cac521d28e - Show all commits
+42
View File
@@ -98,6 +98,10 @@ pub enum ClaudeMessage {
/// Output style hint from Claude Code (v2.1.81+). Informational only. /// Output style hint from Claude Code (v2.1.81+). Informational only.
#[serde(default)] #[serde(default)]
output_style: Option<String>, output_style: Option<String>,
/// Plugin errors from Claude Code (v2.1.111+). Populated when plugins are demoted
/// due to unsatisfied dependencies.
#[serde(default)]
plugin_errors: Option<serde_json::Value>,
}, },
#[serde(rename = "assistant")] #[serde(rename = "assistant")]
Assistant { Assistant {
@@ -977,6 +981,44 @@ mod tests {
} }
} }
#[test]
fn test_system_init_with_plugin_errors() {
let json = r#"{"type":"system","subtype":"init","session_id":"sess-1","plugin_errors":["Plugin 'foo' requires 'bar' which is not installed","Plugin 'baz' failed to load"]}"#;
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
if let ClaudeMessage::System { plugin_errors, .. } = msg {
let errors = plugin_errors.expect("plugin_errors should be present");
let arr = errors.as_array().expect("plugin_errors should be an array");
assert_eq!(arr.len(), 2);
assert_eq!(arr[0].as_str(), Some("Plugin 'foo' requires 'bar' which is not installed"));
} else {
panic!("Expected System variant");
}
}
#[test]
fn test_system_init_without_plugin_errors() {
let json = r#"{"type":"system","subtype":"init","session_id":"sess-1"}"#;
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
if let ClaudeMessage::System { plugin_errors, .. } = msg {
assert!(plugin_errors.is_none());
} else {
panic!("Expected System variant");
}
}
#[test]
fn test_system_init_with_empty_plugin_errors() {
let json = r#"{"type":"system","subtype":"init","session_id":"sess-1","plugin_errors":[]}"#;
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
if let ClaudeMessage::System { plugin_errors, .. } = msg {
let errors = plugin_errors.expect("plugin_errors should be present");
let arr = errors.as_array().expect("plugin_errors should be an array");
assert!(arr.is_empty());
} else {
panic!("Expected System variant");
}
}
#[test] #[test]
fn test_result_message_with_fast_mode_state() { fn test_result_message_with_fast_mode_state() {
let json = r#"{"type":"result","subtype":"success","fast_mode_state":"enabled"}"#; let json = r#"{"type":"result","subtype":"success","fast_mode_state":"enabled"}"#;
+26
View File
@@ -1850,6 +1850,7 @@ fn process_json_line(
subtype, subtype,
session_id, session_id,
cwd, cwd,
plugin_errors,
.. ..
} => { } => {
if subtype == "init" { if subtype == "init" {
@@ -1874,6 +1875,31 @@ fn process_json_line(
}, },
); );
} }
// Warn about any plugins that failed to load (v2.1.111+)
if let Some(errors) = plugin_errors {
if let Some(arr) = errors.as_array() {
for error in arr {
let msg = if let Some(s) = error.as_str() {
s.to_string()
} else {
error.to_string()
};
let _ = app.emit(
"claude:output",
OutputEvent {
line_type: "error".to_string(),
content: format!("Plugin error: {}", msg),
tool_name: None,
conversation_id: conversation_id.clone(),
cost: None,
parent_tool_use_id: None,
},
);
}
}
}
emit_state_change(app, CharacterState::Idle, None, conversation_id.clone()); emit_state_change(app, CharacterState::Idle, None, conversation_id.clone());
} }
} }