From df4cf87a20070057a3df037d4e5ff34299eb9047 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Sat, 7 Nov 2026 00:24:02 +0100 Subject: [PATCH] refactor(config): make the settings contract frontend-agnostic rhema-config held both vocabularies: the stored settings and the terminal UI's theme, palette, and onboarding state, under one Config type with a global directory. A second frontend had to take the UI settings to get the profiles, and every path helper resolved $XDG_CONFIG_HOME/rhema no matter who called. The crate now ships only the framework's contract. Settings is the stored section, flattenable as one serde field. Home carries the directory conventions as a value, so a frontend keeps its settings, skills, subagents, and SYSTEM.md under its own base while .agents/skills stays machine-global. Store is the seam a foreign frontend implements over its own file and format, YamlStore is the reference impl, and Platform collects home, store, and keychain namespace at one construction site. KeyringSecretStore::named gives each frontend its own OS keychain service, so two frontends never share credentials by accident. examples/godcode.rs builds the whole shape runnable: its own directory, its own JSON document, its own namespace. rhema-code's store::Document flattens the section beside its UI fields, the layout on disk unchanged. serde's flatten silently drops unknown keys, so the load path re-checks every leftover key against the section: a typo'd profiles: stays loud inside the bigger document. CodingAgentConfig gains home: Option, so a frontend redirects skill discovery without calling the framework. claw and acp name their keychain namespaces, and the mcp, coding-agent, and subagents crates read conventions from the Home they are handed. --- crates/rhema-claw/src/config.rs | 2 +- crates/rhema-claw/src/main.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/rhema-claw/src/config.rs b/crates/rhema-claw/src/config.rs index 3e73bcc..209f26e 100644 --- a/crates/rhema-claw/src/config.rs +++ b/crates/rhema-claw/src/config.rs @@ -210,7 +210,7 @@ impl ClawConfig { /// The default config path: `/rhema/claw.yaml`, the same /// base the stored rhema config lives under. pub fn default_path() -> Result { - Ok(rhema_config::Config::dir()?.join("claw.yaml")) + Ok(rhema_config::Home::discover()?.config.join("claw.yaml")) } /// The state dir: `$XDG_STATE_HOME/rhema/claw`, else diff --git a/crates/rhema-claw/src/main.rs b/crates/rhema-claw/src/main.rs index aabb6ec..1f21bcf 100644 --- a/crates/rhema-claw/src/main.rs +++ b/crates/rhema-claw/src/main.rs @@ -169,11 +169,11 @@ fn mock_backends() -> Arc { /// the keyless llama-cpp backend is always constructible — and a /// dispatch against an unreachable provider fails loudly per turn. fn resolve_backends(config: &ClawConfig) -> Result, ClawError> { - let stored = match rhema_config::Config::load() { + let stored = match rhema_config::Settings::load() { Ok(stored) => stored, Err(err) => { eprintln!("warning: ignoring unreadable rhema config: {err}"); - rhema_config::Config::default() + rhema_config::Settings::default() } }; let profile = stored.effective(None); @@ -205,7 +205,7 @@ fn resolve_backends(config: &ClawConfig) -> Result