Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 58 additions & 7 deletions crates/goose-mcp/src/developer/rmcp_developer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,27 @@ pub struct PromptArgumentTemplate {
// Embeds the prompts directory to the build
static PROMPTS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/src/developer/prompts");

const DEFAULT_GOOSEIGNORE_CONTENT: &str = concat!(
"# This file is created automatically if no .gooseignore exists.\n",
"# Customize or uncomment the patterns below instead of deleting the file.\n",
"# Removing it will simply cause goose to recreate it on the next start.\n",
"#\n",
"# Suggested patterns you can uncomment if desired:\n",
"# **/.ssh/** # block SSH keys and configs\n",
"# **/*.key # block loose private keys\n",
"# **/*.pem # block certificates/private keys\n",
"# **/.git/** # block git metadata entirely\n",
"# **/target/** # block Rust build artifacts\n",
"# **/node_modules/** # block JS/TS dependencies\n",
"# **/*.db # block local database files\n",
"# **/*.sqlite # block SQLite databases\n",
"#\n",
"\n",
"**/.env\n",
"**/.env.*\n",
"**/secrets.*\n",
);

/// Loads prompt files from the embedded PROMPTS_DIR and returns a HashMap of prompts.
/// Ensures that each prompt name is unique.
fn load_prompt_files() -> HashMap<String, Prompt> {
Expand Down Expand Up @@ -1295,12 +1316,35 @@ impl DeveloperServer {
.map(|strategy| strategy.config_dir().join(".gooseignore"))
.ok();

let has_local_ignore = local_ignore_path.is_file();
let mut has_local_ignore = local_ignore_path.is_file();
let has_global_ignore = global_ignore_path
.as_ref()
.map(|p| p.is_file())
.unwrap_or(false);

if !has_local_ignore && !has_global_ignore {
match std::fs::write(&local_ignore_path, DEFAULT_GOOSEIGNORE_CONTENT) {
Ok(_) => {
has_local_ignore = true;
}
Err(err) => {
tracing::warn!(
"Failed to create default .gooseignore at {}: {}",
local_ignore_path.display(),
err
);

for pattern in DEFAULT_GOOSEIGNORE_CONTENT.lines() {
let trimmed = pattern.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
let _ = builder.add_line(None, trimmed);
}
}
}
}

if has_global_ignore {
let _ = builder.add(global_ignore_path.as_ref().unwrap());
}
Expand All @@ -1309,12 +1353,6 @@ impl DeveloperServer {
let _ = builder.add(&local_ignore_path);
}

if !has_local_ignore && !has_global_ignore {
let _ = builder.add_line(None, "**/.env");
let _ = builder.add_line(None, "**/.env.*");
let _ = builder.add_line(None, "**/secrets.*");
}

builder.build().expect("Failed to build ignore patterns")
}

Expand Down Expand Up @@ -2959,6 +2997,11 @@ mod tests {

let server = create_test_server();

let gooseignore_path = temp_path.join(".gooseignore");
if gooseignore_path.exists() {
fs::remove_file(&gooseignore_path).unwrap();
}

let result = server
.text_editor(Parameters(TextEditorParams {
command: "view".to_string(),
Expand Down Expand Up @@ -3236,6 +3279,14 @@ mod tests {
// Don't create any ignore files
let server = create_test_server();

let gooseignore_path = temp_dir.path().join(".gooseignore");
assert!(
gooseignore_path.exists(),
".gooseignore should be created by default"
);
let default_contents = fs::read_to_string(gooseignore_path).unwrap();
assert_eq!(default_contents, DEFAULT_GOOSEIGNORE_CONTENT);

// Default patterns should be used
assert!(
server.is_ignored(Path::new(".env")),
Expand Down