feat: CLI v2.1.81 features + global CLAUDE.md editor (#263)
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m9s
CI / Lint & Test (push) Successful in 18m55s
CI / Build Linux (push) Successful in 22m9s
CI / Build Windows (cross-compile) (push) Successful in 31m38s

## 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:
2026-04-13 13:32:03 -07:00
committed by Naomi Carrigan
parent 5663b1c09a
commit b88f25a61b
15 changed files with 432 additions and 2 deletions
+71 -2
View File
@@ -303,6 +303,11 @@ impl WslBridge {
cmd.arg("--worktree");
}
// Add bare flag if requested (v2.1.81+)
if options.bare_mode {
cmd.arg("--bare");
}
// Pass combined settings via --settings flag if any settings are specified
{
let has_memory_dir = options
@@ -315,8 +320,13 @@ impl WslBridge {
.as_ref()
.map(|m| !m.is_empty())
.unwrap_or(false);
let suppress_clear_context = !options.show_clear_context_on_plan_accept;
if has_memory_dir || has_overrides || options.disable_skill_shell_execution {
if has_memory_dir
|| has_overrides
|| options.disable_skill_shell_execution
|| suppress_clear_context
{
let mut settings = serde_json::Map::new();
if let Some(ref dir) = options.auto_memory_directory {
if !dir.is_empty() {
@@ -339,6 +349,12 @@ impl WslBridge {
serde_json::Value::Bool(true),
);
}
if suppress_clear_context {
settings.insert(
"showClearContextOnPlanAccept".to_string(),
serde_json::Value::Bool(false),
);
}
if let Ok(settings_json) = serde_json::to_string(&settings) {
cmd.args(["--settings", &settings_json]);
}
@@ -379,6 +395,13 @@ impl WslBridge {
cmd.env("ENABLE_CLAUDEAI_MCP_SERVERS", "false");
}
// Set custom model option if specified (v2.1.81+)
if let Some(ref custom_opt) = options.custom_model_option {
if !custom_opt.is_empty() {
cmd.env("ANTHROPIC_CUSTOM_MODEL_OPTION", custom_opt);
}
}
cmd
} else {
// Running on Windows - use wsl with bash login shell to ensure PATH is loaded
@@ -446,6 +469,14 @@ impl WslBridge {
claude_cmd.push_str("ENABLE_CLAUDEAI_MCP_SERVERS=false ");
}
// Set custom model option if specified (v2.1.81+)
if let Some(ref custom_opt) = options.custom_model_option {
if !custom_opt.is_empty() {
let escaped = custom_opt.replace('\'', "'\\''");
claude_cmd.push_str(&format!("ANTHROPIC_CUSTOM_MODEL_OPTION='{}' ", escaped));
}
}
claude_cmd.push_str(
"claude --output-format stream-json --input-format stream-json --verbose",
);
@@ -496,6 +527,11 @@ impl WslBridge {
claude_cmd.push_str(" --worktree");
}
// Add bare flag if requested (v2.1.81+)
if options.bare_mode {
claude_cmd.push_str(" --bare");
}
// Pass combined settings via --settings flag if any settings are specified
{
let has_memory_dir = options
@@ -508,8 +544,13 @@ impl WslBridge {
.as_ref()
.map(|m| !m.is_empty())
.unwrap_or(false);
let suppress_clear_context = !options.show_clear_context_on_plan_accept;
if has_memory_dir || has_overrides || options.disable_skill_shell_execution {
if has_memory_dir
|| has_overrides
|| options.disable_skill_shell_execution
|| suppress_clear_context
{
let mut settings = serde_json::Map::new();
if let Some(ref dir) = options.auto_memory_directory {
if !dir.is_empty() {
@@ -532,6 +573,12 @@ impl WslBridge {
serde_json::Value::Bool(true),
);
}
if suppress_clear_context {
settings.insert(
"showClearContextOnPlanAccept".to_string(),
serde_json::Value::Bool(false),
);
}
if let Ok(settings_json) = serde_json::to_string(&settings) {
let escaped = settings_json.replace('\'', "'\\''");
claude_cmd.push_str(&format!(" --settings '{}'", escaped));
@@ -2122,6 +2169,9 @@ fn process_json_line(
usage,
duration_ms,
num_turns,
fast_mode_state,
model_usage,
total_cost_usd,
} => {
tracing::info!(
"Received Result message: subtype={}, has_denials={}, denial_count={:?}",
@@ -2206,6 +2256,25 @@ fn process_json_line(
});
}
// Log fast mode state if present (v2.1.81+)
if let Some(ref state) = fast_mode_state {
tracing::debug!("Fast mode state: {}", state);
}
// Log per-model usage if available (v2.1.81+)
if let Some(ref model_usage_val) = model_usage {
if let Some(map) = model_usage_val.as_object() {
for (model_name, _usage_val) in map {
tracing::debug!("Per-model usage logged for: {}", model_name);
}
}
}
// Log authoritative cost from Claude Code if available (v2.1.81+)
if let Some(auth_cost) = total_cost_usd {
tracing::debug!("Authoritative total cost from Claude Code: ${:.6}", auth_cost);
}
// Clear tracking fields since request completed successfully
{
let mut stats_guard = stats.write();