diff --git a/crates/gyandoot-tui/src/setup.rs b/crates/gyandoot-tui/src/setup.rs index 5834c00..fef66ac 100644 --- a/crates/gyandoot-tui/src/setup.rs +++ b/crates/gyandoot-tui/src/setup.rs @@ -424,6 +424,8 @@ fn write_config_file(config: &ProviderConfig) -> anyhow::Result { struct SerializedConfig { provider: SerializedProvider, agent: SerializedAgent, + #[serde(skip_serializing_if = "Option::is_none")] + browser: Option, } #[derive(Serialize)] @@ -445,10 +447,46 @@ struct SerializedAgent { name: String, } +/// Serializable [browser] section for TOML output. +#[derive(Serialize)] +struct SerializedBrowser { + #[serde(skip_serializing_if = "Option::is_none")] + node_path: Option, +} + fn is_false(v: &bool) -> bool { !v } +/// Auto-detect the path to npx/npx.cmd. +/// +/// Tries `which npx` on Unix, `where npx.cmd` on Windows. +/// Returns `None` if not found on PATH. +fn detect_npx_path() -> Option { + let (prog, arg) = if cfg!(target_os = "windows") { + ("where", "npx.cmd") + } else { + ("which", "npx") + }; + + std::process::Command::new(prog) + .arg(arg) + .output() + .ok() + .and_then(|out| { + if out.status.success() { + let path = String::from_utf8_lossy(&out.stdout) + .lines() + .next()? + .trim() + .to_string(); + if !path.is_empty() { Some(path) } else { None } + } else { + None + } + }) +} + impl From<&ProviderConfig> for SerializedConfig { fn from(c: &ProviderConfig) -> Self { let kind_str = match c.kind { @@ -475,6 +513,9 @@ impl From<&ProviderConfig> for SerializedConfig { agent: SerializedAgent { name: "gyandoot".to_string(), }, + browser: detect_npx_path().map(|np| SerializedBrowser { + node_path: Some(np), + }), } } } diff --git a/src/main.rs b/src/main.rs index 9dce485..8ca464c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -293,6 +293,8 @@ fn load_and_convert_config(path: &std::path::Path) -> Result, + #[serde(default)] + browser: Option, } #[derive(serde::Deserialize)] @@ -316,6 +318,12 @@ fn load_and_convert_config(path: &std::path::Path) -> Result, } + #[derive(serde::Deserialize)] + struct FlatBrowser { + #[serde(default)] + node_path: Option, + } + let flat: FlatConfig = toml::from_str(&content)?; tracing::info!("Loaded config from {} (converted from flat [provider] format)", path.display()); @@ -346,7 +354,10 @@ fn load_and_convert_config(path: &std::path::Path) -> Result