diff --git a/Cargo.lock b/Cargo.lock index 0bbd367a95..f77d1be63d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2165,6 +2165,7 @@ dependencies = [ "console", "convert_case 0.11.0", "derive_setters", + "dirs", "enable-ansi-support", "fake", "forge_api", diff --git a/crates/forge_main/Cargo.toml b/crates/forge_main/Cargo.toml index d3d4d472f8..8eec3efd6f 100644 --- a/crates/forge_main/Cargo.toml +++ b/crates/forge_main/Cargo.toml @@ -52,6 +52,7 @@ update-informer = { version = "1.2.0", default-features = false, features = [ "ureq", "rustls-tls", ] } +dirs.workspace = true open.workspace = true humantime.workspace = true num-format.workspace = true diff --git a/crates/forge_main/src/cli.rs b/crates/forge_main/src/cli.rs index b2c12a4e35..a60d7a5041 100644 --- a/crates/forge_main/src/cli.rs +++ b/crates/forge_main/src/cli.rs @@ -514,6 +514,12 @@ pub enum ConfigCommand { /// List configuration values. List, + + /// Print the path to the global config file. + Path, + + /// Migrate the legacy ~/forge directory to ~/.forge. + Migrate, } /// Arguments for `forge config set`. diff --git a/crates/forge_main/src/ui.rs b/crates/forge_main/src/ui.rs index 5f500d99fc..fde42605d5 100644 --- a/crates/forge_main/src/ui.rs +++ b/crates/forge_main/src/ui.rs @@ -3595,7 +3595,56 @@ impl A + Send + Sync> UI crate::cli::ConfigCommand::List => { self.on_show_config(porcelain).await?; } + crate::cli::ConfigCommand::Path => { + let path = forge_config::ConfigReader::config_path(); + self.writeln(path.display().to_string())?; + } + crate::cli::ConfigCommand::Migrate => { + self.handle_config_migrate()?; + } + } + Ok(()) + } + + /// Rename `~/forge` to `~/.forge`. + /// + /// Errors if the legacy directory does not exist, if the new directory + /// already exists, or if the rename fails. + fn handle_config_migrate(&mut self) -> Result<()> { + let home = dirs::home_dir() + .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?; + let legacy = home.join("forge"); + let new = home.join(".forge"); + + if !legacy.exists() { + anyhow::bail!( + "Legacy directory {} does not exist — nothing to migrate", + legacy.display() + ); } + + if new.exists() { + anyhow::bail!( + "Target directory {} already exists — remove it first or migrate manually", + new.display() + ); + } + + std::fs::rename(&legacy, &new).map_err(|e| { + anyhow::anyhow!( + "Failed to rename {} to {}: {}", + legacy.display(), + new.display(), + e + ) + })?; + + self.writeln_title(TitleFormat::info("Migration Completed").sub_title(format!( + "{} → {}", + legacy.display(), + new.display() + )))?; + Ok(()) } diff --git a/shell-plugin/lib/actions/config.zsh b/shell-plugin/lib/actions/config.zsh index a6d5f51479..2c98a66ccf 100644 --- a/shell-plugin/lib/actions/config.zsh +++ b/shell-plugin/lib/actions/config.zsh @@ -463,12 +463,22 @@ function _forge_action_config_edit() { return 1 fi - local config_file="${HOME}/forge/.forge.toml" + # Resolve config file path via the forge binary (honours FORGE_CONFIG, + # new ~/.forge path, and legacy ~/forge fallback automatically) + local config_file + config_file=$(forge config path 2>/dev/null) + if [[ -z "$config_file" ]]; then + _forge_log error "Failed to resolve config path from 'forge config path'" + return 1 + fi + + local config_dir + config_dir=$(dirname "$config_file") # Ensure the config directory exists - if [[ ! -d "${HOME}/forge" ]]; then - mkdir -p "${HOME}/forge" || { - _forge_log error "Failed to create ~/forge directory" + if [[ ! -d "$config_dir" ]]; then + mkdir -p "$config_dir" || { + _forge_log error "Failed to create $config_dir directory" return 1 } fi