feat: Allow configuring hints filename(s) (#3269)
Signed-off-by: Rami Chowdhury <rami.chowdhury@gmail.com>
This commit is contained in:
@@ -461,40 +461,57 @@ impl DeveloperRouter {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// choose_app_strategy().config_dir()
|
let hints_filenames: Vec<String> = std::env::var("CONTEXT_FILE_NAMES")
|
||||||
// - macOS/Linux: ~/.config/goose/
|
.ok()
|
||||||
// - Windows: ~\AppData\Roaming\Block\goose\config\
|
.and_then(|s| serde_json::from_str(&s).ok())
|
||||||
// keep previous behavior of expanding ~/.config in case this fails
|
.unwrap_or_else(|| vec![".goosehints".to_string()]);
|
||||||
let global_hints_path = choose_app_strategy(crate::APP_STRATEGY.clone())
|
|
||||||
.map(|strategy| strategy.in_config_dir(".goosehints"))
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
PathBuf::from(shellexpand::tilde("~/.config/goose/.goosehints").to_string())
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the directory if it doesn't exist
|
let mut global_hints_contents = Vec::with_capacity(hints_filenames.len());
|
||||||
let _ = std::fs::create_dir_all(global_hints_path.parent().unwrap());
|
let mut local_hints_contents = Vec::with_capacity(hints_filenames.len());
|
||||||
|
|
||||||
// Check for local hints in current directory
|
for hints_filename in &hints_filenames {
|
||||||
let local_hints_path = cwd.join(".goosehints");
|
// Global hints
|
||||||
|
// choose_app_strategy().config_dir()
|
||||||
|
// - macOS/Linux: ~/.config/goose/
|
||||||
|
// - Windows: ~\AppData\Roaming\Block\goose\config\
|
||||||
|
// keep previous behavior of expanding ~/.config in case this fails
|
||||||
|
let global_hints_path = choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
|
.map(|strategy| strategy.in_config_dir(hints_filename))
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
let path_str = format!("~/.config/goose/{}", hints_filename);
|
||||||
|
PathBuf::from(shellexpand::tilde(&path_str).to_string())
|
||||||
|
});
|
||||||
|
|
||||||
// Read global hints if they exist
|
if let Some(parent) = global_hints_path.parent() {
|
||||||
let mut hints = String::new();
|
let _ = std::fs::create_dir_all(parent);
|
||||||
if global_hints_path.is_file() {
|
}
|
||||||
if let Ok(global_hints) = std::fs::read_to_string(&global_hints_path) {
|
|
||||||
hints.push_str("\n### Global Hints\nThe developer extension includes some global hints that apply to all projects & directories.\n");
|
if global_hints_path.is_file() {
|
||||||
hints.push_str(&global_hints);
|
if let Ok(content) = std::fs::read_to_string(&global_hints_path) {
|
||||||
|
global_hints_contents.push(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let local_hints_path = cwd.join(hints_filename);
|
||||||
|
if local_hints_path.is_file() {
|
||||||
|
if let Ok(content) = std::fs::read_to_string(&local_hints_path) {
|
||||||
|
local_hints_contents.push(content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read local hints if they exist
|
let mut hints = String::new();
|
||||||
if local_hints_path.is_file() {
|
if !global_hints_contents.is_empty() {
|
||||||
if let Ok(local_hints) = std::fs::read_to_string(&local_hints_path) {
|
hints.push_str("\n### Global Hints\nThe developer extension includes some global hints that apply to all projects & directories.\n");
|
||||||
if !hints.is_empty() {
|
hints.push_str(&global_hints_contents.join("\n"));
|
||||||
hints.push_str("\n\n");
|
}
|
||||||
}
|
|
||||||
hints.push_str("### Project Hints\nThe developer extension includes some hints for working on the project in this directory.\n");
|
if !local_hints_contents.is_empty() {
|
||||||
hints.push_str(&local_hints);
|
if !hints.is_empty() {
|
||||||
|
hints.push_str("\n\n");
|
||||||
}
|
}
|
||||||
|
hints.push_str("### Project Hints\nThe developer extension includes some hints for working on the project in this directory.\n");
|
||||||
|
hints.push_str(&local_hints_contents.join("\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return base instructions directly when no hints are found
|
// Return base instructions directly when no hints are found
|
||||||
@@ -1749,6 +1766,39 @@ mod tests {
|
|||||||
temp_dir.close().unwrap();
|
temp_dir.close().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_goosehints_multiple_filenames() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
std::env::set_current_dir(dir.path()).unwrap();
|
||||||
|
std::env::set_var("CONTEXT_FILE_NAMES", r#"["CLAUDE.md", ".goosehints"]"#);
|
||||||
|
|
||||||
|
fs::write("CLAUDE.md", "Custom hints file content from CLAUDE.md").unwrap();
|
||||||
|
fs::write(".goosehints", "Custom hints file content from .goosehints").unwrap();
|
||||||
|
let router = DeveloperRouter::new();
|
||||||
|
let instructions = router.instructions();
|
||||||
|
|
||||||
|
assert!(instructions.contains("Custom hints file content from CLAUDE.md"));
|
||||||
|
assert!(instructions.contains("Custom hints file content from .goosehints"));
|
||||||
|
std::env::remove_var("CONTEXT_FILE_NAMES");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn test_goosehints_configurable_filename() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
std::env::set_current_dir(dir.path()).unwrap();
|
||||||
|
std::env::set_var("CONTEXT_FILE_NAMES", r#"["CLAUDE.md"]"#);
|
||||||
|
|
||||||
|
fs::write("CLAUDE.md", "Custom hints file content").unwrap();
|
||||||
|
let router = DeveloperRouter::new();
|
||||||
|
let instructions = router.instructions();
|
||||||
|
|
||||||
|
assert!(instructions.contains("Custom hints file content"));
|
||||||
|
assert!(!instructions.contains(".goosehints")); // Make sure it's not loading the default
|
||||||
|
std::env::remove_var("CONTEXT_FILE_NAMES");
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ LLMs have context windows, which are limits on how much conversation history the
|
|||||||
Turning on too many extensions can degrade performance. Enable only essential [extensions and tools](/docs/guides/managing-tools/tool-permissions) to improve tool selection accuracy, save context window space, and stay within provider tool limits.
|
Turning on too many extensions can degrade performance. Enable only essential [extensions and tools](/docs/guides/managing-tools/tool-permissions) to improve tool selection accuracy, save context window space, and stay within provider tool limits.
|
||||||
|
|
||||||
### Teach Goose your preferences
|
### Teach Goose your preferences
|
||||||
Help Goose remember how you like to work by using [`.goosehints`](/docs/guides/using-goosehints/) for permanent project preferences and the [Memory extension](/docs/mcp/memory-mcp) for things you want Goose to dynamically recall later. Both can help save valuable context window space while keeping your preferences available.
|
Help Goose remember how you like to work by using [`.goosehints` or other context files](/docs/guides/using-goosehints/) for permanent project preferences and the [Memory extension](/docs/mcp/memory-mcp) for things you want Goose to dynamically recall later. Both can help save valuable context window space while keeping your preferences available.
|
||||||
|
|
||||||
### Protect sensitive files
|
### Protect sensitive files
|
||||||
Goose is often eager to make changes. You can stop it from changing specific files by creating a [.gooseignore](/docs/guides/using-gooseignore) file. In this file, you can list all the file paths you want it to avoid.
|
Goose is often eager to make changes. You can stop it from changing specific files by creating a [.gooseignore](/docs/guides/using-gooseignore) file. In this file, you can list all the file paths you want it to avoid.
|
||||||
@@ -45,4 +45,4 @@ You can turn a successful session into a reusable "[recipe](/docs/guides/recipes
|
|||||||
You don’t need to get it right the first time. Iterating on prompts and tools is part of the workflow.
|
You don’t need to get it right the first time. Iterating on prompts and tools is part of the workflow.
|
||||||
|
|
||||||
### Keep Goose updated
|
### Keep Goose updated
|
||||||
Regularly [update](/docs/guides/updating-goose) Goose to benefit from the latest features, bug fixes, and performance improvements.
|
Regularly [update](/docs/guides/updating-goose) Goose to benefit from the latest features, bug fixes, and performance improvements.
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ Goose supports two types of hint files:
|
|||||||
You can use both global and local hints at the same time. When both exist, Goose will consider both your global preferences and project-specific requirements. If the instructions in your local hints file conflict with your global preferences, Goose will prioritize the local hints.
|
You can use both global and local hints at the same time. When both exist, Goose will consider both your global preferences and project-specific requirements. If the instructions in your local hints file conflict with your global preferences, Goose will prioritize the local hints.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
You can name your context files differently -- e.g. `AGENTS.md` -- and Goose can still pick them up. Configure the `CONTEXT_FILE_NAMES` setting!
|
||||||
|
:::
|
||||||
|
|
||||||
<Tabs groupId="interface">
|
<Tabs groupId="interface">
|
||||||
<TabItem value="ui" label="Goose Desktop" default>
|
<TabItem value="ui" label="Goose Desktop" default>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user