diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 23cc19a..e4268a3 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -43,6 +43,9 @@ pub struct ClaudeStartOptions { #[serde(default = "default_enable_claudeai_mcp_servers")] pub enable_claudeai_mcp_servers: bool, + + #[serde(default)] + pub auto_memory_directory: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -186,6 +189,9 @@ pub struct HikariConfig { #[serde(default = "default_enable_claudeai_mcp_servers")] pub enable_claudeai_mcp_servers: bool, + + #[serde(default)] + pub auto_memory_directory: Option, } impl Default for HikariConfig { @@ -235,6 +241,7 @@ impl Default for HikariConfig { disable_cron: false, include_git_instructions: true, enable_claudeai_mcp_servers: true, + auto_memory_directory: None, } } } @@ -384,6 +391,7 @@ mod tests { assert!(!config.disable_cron); assert!(config.include_git_instructions); assert!(config.enable_claudeai_mcp_servers); + assert!(config.auto_memory_directory.is_none()); } #[test] @@ -433,6 +441,7 @@ mod tests { disable_cron: true, include_git_instructions: false, enable_claudeai_mcp_servers: false, + auto_memory_directory: Some("/custom/memory".to_string()), }; let json = serde_json::to_string(&config).unwrap(); @@ -453,6 +462,10 @@ mod tests { assert!(deserialized.disable_cron); assert!(!deserialized.include_git_instructions); assert!(!deserialized.enable_claudeai_mcp_servers); + assert_eq!( + deserialized.auto_memory_directory, + Some("/custom/memory".to_string()) + ); } #[test] diff --git a/src-tauri/src/wsl_bridge.rs b/src-tauri/src/wsl_bridge.rs index d423e23..0ea63bc 100644 --- a/src-tauri/src/wsl_bridge.rs +++ b/src-tauri/src/wsl_bridge.rs @@ -291,6 +291,13 @@ impl WslBridge { cmd.arg("--worktree"); } + // Pass auto-memory directory via settings if specified + if let Some(ref dir) = options.auto_memory_directory { + if !dir.is_empty() { + cmd.args(["--settings", &format!(r#"{{"autoMemoryDirectory":"{}"}}"#, dir)]); + } + } + cmd.current_dir(working_dir); // Set API key as environment variable if specified @@ -434,6 +441,17 @@ impl WslBridge { claude_cmd.push_str(" --worktree"); } + // Pass auto-memory directory via settings if specified + if let Some(ref dir) = options.auto_memory_directory { + if !dir.is_empty() { + let escaped_dir = dir.replace('\'', "'\\''"); + claude_cmd.push_str(&format!( + " --settings '{{\"autoMemoryDirectory\":\"{}\"}}'", + escaped_dir + )); + } + } + // Use bash -lc to load login profile (ensures PATH includes claude) cmd.args(["-e", "bash", "-lc", &claude_cmd]); @@ -3036,4 +3054,25 @@ mod tests { let result = parse_worktree_hook(line); assert!(result.is_none()); } + + /// Build the auto-memory settings JSON without executing a command (for testing) + #[cfg(test)] + fn build_auto_memory_settings_arg(dir: &str) -> String { + format!(r#"{{"autoMemoryDirectory":"{}"}}"#, dir) + } + + #[test] + fn test_e2e_auto_memory_settings_structure() { + let settings_json = build_auto_memory_settings_arg("/custom/memory/dir"); + assert_eq!( + settings_json, + r#"{"autoMemoryDirectory":"/custom/memory/dir"}"# + ); + } + + #[test] + fn test_e2e_auto_memory_settings_empty_path_skipped() { + let dir = ""; + assert!(dir.is_empty(), "Empty directory should be skipped"); + } } diff --git a/src/lib/commands/slashCommands.test.ts b/src/lib/commands/slashCommands.test.ts index c91d967..eed98ac 100644 --- a/src/lib/commands/slashCommands.test.ts +++ b/src/lib/commands/slashCommands.test.ts @@ -67,6 +67,7 @@ vi.mock("$lib/stores/config", () => ({ max_output_tokens: null, include_git_instructions: true, enable_claudeai_mcp_servers: true, + auto_memory_directory: null, }), }, })); diff --git a/src/lib/commands/slashCommands.ts b/src/lib/commands/slashCommands.ts index e5cc471..c59cb9b 100644 --- a/src/lib/commands/slashCommands.ts +++ b/src/lib/commands/slashCommands.ts @@ -68,6 +68,7 @@ async function changeDirectory(path: string): Promise { max_output_tokens: config.max_output_tokens ?? null, include_git_instructions: config.include_git_instructions ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, + auto_memory_directory: config.auto_memory_directory || null, }, }); @@ -147,6 +148,7 @@ async function startNewConversation(): Promise { max_output_tokens: config.max_output_tokens ?? null, include_git_instructions: config.include_git_instructions ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, + auto_memory_directory: config.auto_memory_directory || null, }, }); diff --git a/src/lib/components/ConfigSidebar.svelte b/src/lib/components/ConfigSidebar.svelte index b939c33..c07528d 100644 --- a/src/lib/components/ConfigSidebar.svelte +++ b/src/lib/components/ConfigSidebar.svelte @@ -61,6 +61,7 @@ disable_cron: false, include_git_instructions: true, enable_claudeai_mcp_servers: true, + auto_memory_directory: null, max_output_tokens: null, trusted_workspaces: [], background_image_path: null, @@ -602,6 +603,25 @@ being cut off mid-reply

+ + +
+ + +

+ Custom directory for auto-memory storage. Passed via + --settings autoMemoryDirectory. Leave blank to use the + default (working directory). +

+
diff --git a/src/lib/components/InputBar.svelte b/src/lib/components/InputBar.svelte index ab79a3a..221a189 100644 --- a/src/lib/components/InputBar.svelte +++ b/src/lib/components/InputBar.svelte @@ -404,6 +404,7 @@ User: ${formattedMessage}`; disable_1m_context: config.disable_1m_context ?? false, include_git_instructions: config.include_git_instructions ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, + auto_memory_directory: config.auto_memory_directory || null, }, }); diff --git a/src/lib/components/PermissionModal.svelte b/src/lib/components/PermissionModal.svelte index c40bf90..ce240d0 100644 --- a/src/lib/components/PermissionModal.svelte +++ b/src/lib/components/PermissionModal.svelte @@ -91,6 +91,7 @@ disable_1m_context: config.disable_1m_context ?? false, include_git_instructions: config.include_git_instructions ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, + auto_memory_directory: config.auto_memory_directory || null, }, }); diff --git a/src/lib/components/StatusBar.svelte b/src/lib/components/StatusBar.svelte index fe96c34..9c0d8fb 100644 --- a/src/lib/components/StatusBar.svelte +++ b/src/lib/components/StatusBar.svelte @@ -91,6 +91,7 @@ disable_cron: false, include_git_instructions: true, enable_claudeai_mcp_servers: true, + auto_memory_directory: null, }); let streamerModeActive = $state(false); @@ -173,6 +174,7 @@ max_output_tokens: currentConfig.max_output_tokens ?? null, include_git_instructions: currentConfig.include_git_instructions ?? true, enable_claudeai_mcp_servers: currentConfig.enable_claudeai_mcp_servers ?? true, + auto_memory_directory: currentConfig.auto_memory_directory || null, }, }); @@ -332,6 +334,7 @@ max_output_tokens: currentConfig.max_output_tokens ?? null, include_git_instructions: currentConfig.include_git_instructions ?? true, enable_claudeai_mcp_servers: currentConfig.enable_claudeai_mcp_servers ?? true, + auto_memory_directory: currentConfig.auto_memory_directory || null, }, }); diff --git a/src/lib/components/TaskLoopPanel.svelte b/src/lib/components/TaskLoopPanel.svelte index 5a1b0a0..c5ab836 100644 --- a/src/lib/components/TaskLoopPanel.svelte +++ b/src/lib/components/TaskLoopPanel.svelte @@ -220,6 +220,7 @@ max_output_tokens: cfg.max_output_tokens ?? null, include_git_instructions: cfg.include_git_instructions ?? true, enable_claudeai_mcp_servers: cfg.enable_claudeai_mcp_servers ?? true, + auto_memory_directory: cfg.auto_memory_directory || null, }, }); } catch (error) { diff --git a/src/lib/components/UserQuestionModal.svelte b/src/lib/components/UserQuestionModal.svelte index 833d702..96dc7f1 100644 --- a/src/lib/components/UserQuestionModal.svelte +++ b/src/lib/components/UserQuestionModal.svelte @@ -110,6 +110,7 @@ disable_1m_context: config.disable_1m_context ?? false, include_git_instructions: config.include_git_instructions ?? true, enable_claudeai_mcp_servers: config.enable_claudeai_mcp_servers ?? true, + auto_memory_directory: config.auto_memory_directory || null, }, }); diff --git a/src/lib/stores/config.test.ts b/src/lib/stores/config.test.ts index 9dad7b4..3cf9ccb 100644 --- a/src/lib/stores/config.test.ts +++ b/src/lib/stores/config.test.ts @@ -223,6 +223,7 @@ describe("config store", () => { disable_cron: false, include_git_instructions: true, enable_claudeai_mcp_servers: true, + auto_memory_directory: null, }; expect(config.model).toBe("claude-sonnet-4"); @@ -285,6 +286,7 @@ describe("config store", () => { disable_cron: false, include_git_instructions: true, enable_claudeai_mcp_servers: true, + auto_memory_directory: null, }; expect(config.model).toBeNull(); @@ -902,6 +904,7 @@ describe("config store", () => { disable_cron: false, include_git_instructions: true, enable_claudeai_mcp_servers: true, + auto_memory_directory: null, }; const mockInvokeImpl = vi.mocked(invoke); diff --git a/src/lib/stores/config.ts b/src/lib/stores/config.ts index be203eb..6d2b102 100644 --- a/src/lib/stores/config.ts +++ b/src/lib/stores/config.ts @@ -87,6 +87,8 @@ export interface HikariConfig { include_git_instructions: boolean; // Claude.ai MCP servers setting enable_claudeai_mcp_servers: boolean; + // Auto-memory directory + auto_memory_directory: string | null; } const defaultConfig: HikariConfig = { @@ -143,6 +145,7 @@ const defaultConfig: HikariConfig = { disable_cron: false, include_git_instructions: true, enable_claudeai_mcp_servers: true, + auto_memory_directory: null, }; function createConfigStore() { diff --git a/vitest.setup.ts b/vitest.setup.ts index 5090215..204b9ac 100644 --- a/vitest.setup.ts +++ b/vitest.setup.ts @@ -49,6 +49,7 @@ vi.mock("@tauri-apps/api/core", () => ({ profile_avatar_path: null, profile_bio: null, custom_theme_colors: {}, + auto_memory_directory: null, }); case "list_quick_actions": return Promise.resolve([]);