From ab8c887f92803c7df1dc366bfdafaebae4c8bd95 Mon Sep 17 00:00:00 2001 From: "youming.tang" Date: Thu, 9 Apr 2026 15:58:26 +0900 Subject: [PATCH 1/4] refactor(config): change default home directory from ~/forge to ~/.forge Use a hidden dotfile directory (`~/.forge`) as the default config base path, consistent with other CLI agent tools (e.g. `~/.claude`, `~/.cursor`). For backward compatibility, falls back to the legacy `~/forge` path when it exists and `~/.forge` does not. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- crates/forge_config/src/reader.rs | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d28f37e3f..64a0c80956 100644 --- a/README.md +++ b/README.md @@ -863,7 +863,7 @@ System-level environment variables (usually set automatically): ```bash # .env -FORGE_CONFIG=/custom/config/dir # Base directory for all Forge config files (default: ~/forge) +FORGE_CONFIG=/custom/config/dir # Base directory for all Forge config files (default: ~/.forge) FORGE_MAX_SEARCH_RESULT_BYTES=10240 # Maximum bytes for search results (default: 10240 - 10 KB) FORGE_HISTORY_FILE=/path/to/history # Custom path for Forge history file (default: uses system default location) FORGE_BANNER="Your custom banner text" # Custom banner text to display on startup (default: Forge ASCII art) diff --git a/crates/forge_config/src/reader.rs b/crates/forge_config/src/reader.rs index 6ce724cbda..2853756d37 100644 --- a/crates/forge_config/src/reader.rs +++ b/crates/forge_config/src/reader.rs @@ -52,12 +52,21 @@ impl ConfigReader { /// Returns the base directory for all Forge config files. /// /// If the `FORGE_CONFIG` environment variable is set, its value is used - /// directly as the base path. Otherwise defaults to `~/forge`. + /// directly as the base path. Otherwise defaults to `~/.forge`. + /// Falls back to the legacy `~/forge` path if it exists and `~/.forge` + /// does not. pub fn base_path() -> PathBuf { if let Ok(path) = std::env::var("FORGE_CONFIG") { return PathBuf::from(path); } - dirs::home_dir().unwrap_or(PathBuf::from(".")).join("forge") + let home = dirs::home_dir().unwrap_or(PathBuf::from(".")); + let new_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 !new_path.exists() && legacy_path.exists() { + return legacy_path; + } + new_path } /// Adds the provided TOML string as a config source without touching the From 28393f6fc44ea323a09550291448ac3d6ea2af26 Mon Sep 17 00:00:00 2001 From: Tushar Date: Thu, 9 Apr 2026 16:26:41 +0530 Subject: [PATCH 2/4] refactor(config): prefer ~/.forge over ~/forge fallback --- crates/forge_config/src/reader.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/forge_config/src/reader.rs b/crates/forge_config/src/reader.rs index 2853756d37..e0c5156d9c 100644 --- a/crates/forge_config/src/reader.rs +++ b/crates/forge_config/src/reader.rs @@ -59,14 +59,19 @@ impl ConfigReader { if let Ok(path) = std::env::var("FORGE_CONFIG") { return PathBuf::from(path); } + let home = dirs::home_dir().unwrap_or(PathBuf::from(".")); - let new_path = home.join(".forge"); + 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 !new_path.exists() && legacy_path.exists() { + if !path.exists() && legacy_path.exists() { + tracing::info!("Using legacy path"); return legacy_path; } - new_path + + tracing::info!("Using new path"); + path } /// Adds the provided TOML string as a config source without touching the From 56ef3bc48a1bc9dcaa016d2b624a1df18cd80bbb Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Thu, 9 Apr 2026 17:32:31 +0530 Subject: [PATCH 3/4] test(config): update base_path test to expect .forge directory --- crates/forge_config/src/reader.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/forge_config/src/reader.rs b/crates/forge_config/src/reader.rs index e0c5156d9c..f0a6534c36 100644 --- a/crates/forge_config/src/reader.rs +++ b/crates/forge_config/src/reader.rs @@ -195,8 +195,8 @@ 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 end with ".forge" + assert_eq!(actual.file_name().unwrap(), ".forge"); } #[test] From 1f1a9fe36e16074d54b6022381178eee783daeb3 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Thu, 9 Apr 2026 17:48:16 +0530 Subject: [PATCH 4/4] test(infra): update base_path test to expect .forge directory Co-Authored-By: ForgeCode --- crates/forge_infra/src/env.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/forge_infra/src/env.rs b/crates/forge_infra/src/env.rs index cb50e27558..1a89e7c965 100644 --- a/crates/forge_infra/src/env.rs +++ b/crates/forge_infra/src/env.rs @@ -213,8 +213,8 @@ 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 end with ".forge" + assert_eq!(actual.base_path.file_name().unwrap(), ".forge"); } #[test]