generated from nhcarrigan/template
feat: CLI v2.1.81 features + global CLAUDE.md editor (#263)
## Summary Implements support for Claude Code CLI v2.1.81 features and adds a global CLAUDE.md editor, closing issues #237, #239, #244, #245, #246, #247, #248, and #262. ### Stream-JSON forward-compatibility (#245, #246, #247, #248) - **#248** — `output_style` field added to `System` init message; silently accepted for forward-compat - **#245** — `fast_mode_state` field added to `Result` message; logged at debug level - **#246** — `model_usage` field added to `Result` message; per-model breakdown logged at debug level - **#247** — `total_cost_usd` field added to `Result` message; authoritative cost logged at debug level ### New config options (#237, #239, #244) - **#237** — `bare_mode` config toggle: passes `--bare` to Claude Code, suppressing UI chrome for scripted headless `-p` calls - **#239** — `show_clear_context_on_plan_accept` toggle: passes `showClearContextOnPlanAccept: false` in `--settings` when disabled - **#244** — `custom_model_option` text field: sets `ANTHROPIC_CUSTOM_MODEL_OPTION` env var for custom model providers ### Global CLAUDE.md editor (#262) - New Tauri commands `get_global_claude_md` / `save_global_claude_md` read/write `~/.claude/CLAUDE.md` (creates file + directory if absent) - New "Global Instructions" section in the Config Sidebar with a textarea and Save button ### Bug fix (pre-existing) `disable_cron` and `disable_skill_shell_execution` were saved to `HikariConfig` but never passed to `start_claude` invocations — fixed in all 9 call sites. All 3 new config fields are also wired through all 9 call sites. All changes pass `check-all.sh` (ESLint → Prettier → svelte-check → Vitest → Clippy → cargo test with llvm-cov). ✨ This PR was created with help from Hikari~ 🌸 Reviewed-on: #263 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #263.
This commit is contained in:
@@ -95,6 +95,9 @@ pub enum ClaudeMessage {
|
||||
cwd: Option<String>,
|
||||
#[serde(default)]
|
||||
tools: Option<Vec<String>>,
|
||||
/// Output style hint from Claude Code (v2.1.81+). Informational only.
|
||||
#[serde(default)]
|
||||
output_style: Option<String>,
|
||||
},
|
||||
#[serde(rename = "assistant")]
|
||||
Assistant {
|
||||
@@ -119,6 +122,15 @@ pub enum ClaudeMessage {
|
||||
permission_denials: Option<Vec<PermissionDenial>>,
|
||||
#[serde(default)]
|
||||
usage: Option<UsageInfo>,
|
||||
/// Fast mode state from Claude Code v2.1.81+. Values: "default" | "enabled" | "disabled".
|
||||
#[serde(default)]
|
||||
fast_mode_state: Option<String>,
|
||||
/// Per-model usage breakdown from Claude Code v2.1.81+.
|
||||
#[serde(default)]
|
||||
model_usage: Option<serde_json::Value>,
|
||||
/// Authoritative total cost in USD reported by Claude Code v2.1.81+.
|
||||
#[serde(default)]
|
||||
total_cost_usd: Option<f64>,
|
||||
},
|
||||
#[serde(rename = "rate_limit_event")]
|
||||
RateLimitEvent {
|
||||
@@ -910,4 +922,67 @@ mod tests {
|
||||
assert!(!serialized.contains("reason"));
|
||||
assert!(!serialized.contains("conversation_id"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_init_with_output_style() {
|
||||
let json = r#"{"type":"system","subtype":"init","session_id":"sess-1","output_style":"auto"}"#;
|
||||
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
|
||||
if let ClaudeMessage::System { output_style, .. } = msg {
|
||||
assert_eq!(output_style, Some("auto".to_string()));
|
||||
} else {
|
||||
panic!("Expected System variant");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_init_without_output_style() {
|
||||
let json = r#"{"type":"system","subtype":"init","session_id":"sess-1"}"#;
|
||||
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
|
||||
if let ClaudeMessage::System { output_style, .. } = msg {
|
||||
assert!(output_style.is_none());
|
||||
} else {
|
||||
panic!("Expected System variant");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_result_message_with_fast_mode_state() {
|
||||
let json = r#"{"type":"result","subtype":"success","fast_mode_state":"enabled"}"#;
|
||||
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
|
||||
if let ClaudeMessage::Result { fast_mode_state, .. } = msg {
|
||||
assert_eq!(fast_mode_state, Some("enabled".to_string()));
|
||||
} else {
|
||||
panic!("Expected Result variant");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_result_message_with_total_cost_usd() {
|
||||
let json = r#"{"type":"result","subtype":"success","total_cost_usd":0.05}"#;
|
||||
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
|
||||
if let ClaudeMessage::Result { total_cost_usd, .. } = msg {
|
||||
assert!((total_cost_usd.unwrap() - 0.05).abs() < f64::EPSILON);
|
||||
} else {
|
||||
panic!("Expected Result variant");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_result_message_without_new_fields() {
|
||||
let json = r#"{"type":"result","subtype":"success"}"#;
|
||||
let msg: ClaudeMessage = serde_json::from_str(json).unwrap();
|
||||
if let ClaudeMessage::Result {
|
||||
fast_mode_state,
|
||||
model_usage,
|
||||
total_cost_usd,
|
||||
..
|
||||
} = msg
|
||||
{
|
||||
assert!(fast_mode_state.is_none());
|
||||
assert!(model_usage.is_none());
|
||||
assert!(total_cost_usd.is_none());
|
||||
} else {
|
||||
panic!("Expected Result variant");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user