diff --git a/crates/forge_app/src/fmt/fmt_input.rs b/crates/forge_app/src/fmt/fmt_input.rs index 1dcf23cf2c..edc463f3ef 100644 --- a/crates/forge_app/src/fmt/fmt_input.rs +++ b/crates/forge_app/src/fmt/fmt_input.rs @@ -99,7 +99,7 @@ impl FormatContent for ToolCatalog { ToolCatalog::MultiPatch(input) => { let display_path = display_path_for(&input.file_path); Some( - TitleFormat::debug("Multi-patch") + TitleFormat::debug("Replace") .sub_title(format!("{} ({} edits)", display_path, input.edits.len())) .into(), ) diff --git a/crates/forge_config/src/reader.rs b/crates/forge_config/src/reader.rs index f0a6534c36..aa77518347 100644 --- a/crates/forge_config/src/reader.rs +++ b/crates/forge_config/src/reader.rs @@ -53,8 +53,13 @@ impl ConfigReader { /// /// If the `FORGE_CONFIG` environment variable is set, its value is used /// directly as the base path. Otherwise defaults to `~/.forge`. - /// Falls back to the legacy `~/forge` path if it exists and `~/.forge` - /// does not. + /// Falls back to the legacy `~/forge` path if it exists, even if `~/.forge` + /// also exists. This prevents tools that eagerly create `~/.forge` (such as + /// the shell plugin's config-edit action) from silently switching the + /// active base path while the user's credentials and config still live + /// in `~/forge`. Once the user runs `forge config migrate` the + /// `~/forge` directory is removed, so this fallback naturally stops + /// applying. pub fn base_path() -> PathBuf { if let Ok(path) = std::env::var("FORGE_CONFIG") { return PathBuf::from(path); @@ -64,8 +69,10 @@ impl ConfigReader { let path = home.join(".forge"); let legacy_path = home.join("forge"); - // Prefer the new dotfile path, but fall back to legacy if only it exists - if !path.exists() && legacy_path.exists() { + // Prefer the legacy ~/forge path while it still exists so that an + // empty ~/.forge directory (e.g. created by `mkdir -p` in the shell + // plugin) does not cause the base path to flip before migration. + if legacy_path.exists() { tracing::info!("Using legacy path"); return legacy_path; } @@ -195,8 +202,14 @@ mod tests { #[test] fn test_base_path_falls_back_to_home_dir_when_env_var_absent() { let actual = ConfigReader::base_path(); - // Without FORGE_CONFIG set the path must end with ".forge" - assert_eq!(actual.file_name().unwrap(), ".forge"); + // Without FORGE_CONFIG set the path must be either ".forge" (new) or + // "forge" (legacy fallback when ~/forge exists on this machine). + let name = actual.file_name().unwrap(); + assert!( + name == ".forge" || name == "forge", + "Expected base_path to end with '.forge' or 'forge', got: {:?}", + name + ); } #[test] diff --git a/crates/forge_infra/src/env.rs b/crates/forge_infra/src/env.rs index 1a89e7c965..d281eabd52 100644 --- a/crates/forge_infra/src/env.rs +++ b/crates/forge_infra/src/env.rs @@ -213,8 +213,14 @@ mod tests { #[test] fn test_to_environment_falls_back_to_home_dir_when_env_var_absent() { let actual = to_environment(PathBuf::from("/any/cwd")); - // Without FORGE_CONFIG the base_path must end with ".forge" - assert_eq!(actual.base_path.file_name().unwrap(), ".forge"); + // Without FORGE_CONFIG the base_path must be either ".forge" (new default) + // or "forge" (legacy fallback when ~/forge exists on this machine). + let name = actual.base_path.file_name().unwrap(); + assert!( + name == ".forge" || name == "forge", + "Expected base_path to end with '.forge' or 'forge', got: {:?}", + name + ); } #[test] diff --git a/shell-plugin/lib/actions/config.zsh b/shell-plugin/lib/actions/config.zsh index 2c98a66ccf..5bf6d8f376 100644 --- a/shell-plugin/lib/actions/config.zsh +++ b/shell-plugin/lib/actions/config.zsh @@ -466,9 +466,9 @@ function _forge_action_config_edit() { # 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) + config_file=$($FORGE_BIN config path 2>/dev/null) if [[ -z "$config_file" ]]; then - _forge_log error "Failed to resolve config path from 'forge config path'" + _forge_log error "Failed to resolve config path from '$FORGE_BIN config path'" return 1 fi