Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
56 changes: 56 additions & 0 deletions crates/goose/src/agents/builtin_skills/goose_doc_guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
name: goose-doc-guide
description: Reference goose documentation to create, configure, or explain goose-specific features like recipes, extensions, sessions, and providers. You MUST fetch relevant goose docs before answering. You MUST NOT rely on training data or assumptions for any goose-specific fields, values, names, syntax, or commands.
---

Use this skill when working with **goose-specific features**:
- Creating or editing recipes
- Configuring extensions or providers
- Explaining how goose features work
- Any goose configuration or setup task

Do NOT use this skill for:
- General coding tasks unrelated to goose
- Running existing recipes (just run them directly)

## Steps (COMPLETE ALL BEFORE RESPONDING)
1. **Fetch official docs**
- Fetch the doc map from `https://block.github.io/goose/goose-docs-map.md`
- Search the doc map for pages relevant to the user's topic and get the paths for these pages
- Use the EXACT paths from the doc map. For example:
- If doc map shows: `docs/guides/sessions/session-management.md`
- Fetch: `https://block.github.io/goose/docs/guides/sessions/session-management.md`
- Do NOT modify or guess paths.
- **ONLY fetch paths that are explicitly listed in the doc map - do not guess or infer URLs**
- Make multiple fetch calls in parallel and save to temp files
- Use the temp files for subsequent searches instead of re-fetching

2. **Create/modify content**
- For goose configuration files:
- Consult schema/field reference documentation first
- **Search the fetched docs to extract the complete schema for each element you plan to use**
- Extract example snippets to understand usage patterns
- Create your configuration based on reference specs, following example patterns
- **⚠️ STOP: Before showing the user, verify output content MUST match the schema and reference in the goose official documentation:**
- [ ] Field names match exactly as shown in docs
- [ ] Required fields/properties are present
- [ ] Value formats match examples (YAML/JSON syntax, data types, etc.)
- **If ANY verification fails, revise and repeat this step until ALL verifications pass**
- **DO NOT present unverified output to the user**

3. **MANDATORY VERIFICATION - CHECK ALL THESE ITEMS BEFORE STEP 4**
Before writing your final answer:
- [ ] You MUST NOT rely on training data or assumptions for any goose-specific fields, values, names, syntax, or commands.
- [ ] **Did you include "How to Use", CLI commands, or usage instructions?**
- If YES and user didn't ask for it → **REMOVE IT NOW**
- If YES and user asked for it → verify exact commands from fetched docs before including
- [ ] List all goose-specific items in your answer (commands, fields, syntax, values, how to use, explanations, etc.)
- [ ] For each item, verify it is correct according to the fetched docs. If not found, either fetch the relevant docs NOW and verify, or remove it (if user asked for it, state "I could not find documentation for [X]").

4. **Provide your answer and include a "Verification Completed" section**
- For EACH goose-specific item in your response, cite the specific doc file where you verified it

5. **List documentation links**
- Only include docs actually used
- Remove `.md` suffix from URLs
- Example: If you fetched `https://block.github.io/goose/docs/guides/sessions/session-management.md`, list it as `https://block.github.io/goose/docs/guides/sessions/session-management`
11 changes: 11 additions & 0 deletions crates/goose/src/agents/builtin_skills/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use include_dir::{include_dir, Dir};

static BUILTIN_SKILLS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/src/agents/builtin_skills");

pub fn get_all_builtin_skills() -> Vec<&'static str> {
BUILTIN_SKILLS_DIR
.files()
.filter(|f| f.path().extension().is_some_and(|ext| ext == "md"))

Copilot AI Jan 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mod.rs file will be included in the embedded directory by include_dir!. This means the filter on line 8 will see mod.rs (without .md extension) in addition to .md files. While this works because mod.rs is filtered out by the extension check, it's worth noting that the directory being embedded includes the Rust source file itself.

This is not a bug but could be surprising. Consider adding a comment explaining that the macro embeds the entire directory including mod.rs, which is filtered out at runtime.

Copilot uses AI. Check for mistakes.
.filter_map(|f| f.contents_utf8())
.collect()
}
1 change: 1 addition & 0 deletions crates/goose/src/agents/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod agent;
pub(crate) mod apps_extension;
mod builtin_skills;
pub(crate) mod chatrecall_extension;
pub(crate) mod code_execution_extension;
pub mod execute_commands;
Expand Down
32 changes: 31 additions & 1 deletion crates/goose/src/agents/skills_extension.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::builtin_skills;
use crate::agents::extension::PlatformExtensionContext;
use crate::agents::mcp_client::{Error, McpClientTrait};
use crate::config::paths::Paths;
Expand Down Expand Up @@ -65,17 +66,38 @@ impl SkillsClient {
instructions: Some(String::new()),
};

let mut skills = Self::load_builtin_skills();

let directories = Self::get_default_skill_directories()
.into_iter()
.filter(|d| d.exists())
.collect::<Vec<_>>();
let skills = Self::discover_skills_in_directories(&directories);
let fs_skills = Self::discover_skills_in_directories(&directories);
skills.extend(fs_skills);

Comment on lines +76 to 77

Copilot AI Jan 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filesystem skills will silently override builtin skills with the same name. When skills.extend(fs_skills) is called (line 76), any filesystem skill with a name matching a builtin skill (like "goose-doc-guide") will replace the builtin version. This could confuse users who expect the builtin skill to be available.

Consider either: (1) documenting this behavior so users understand the override mechanism, or (2) inverting the order so filesystem skills are loaded first and builtin skills supplement them, or (3) detecting conflicts and logging a warning when a filesystem skill shadows a builtin skill.

Suggested change
skills.extend(fs_skills);
// Merge filesystem-discovered skills, warning if they shadow builtin skills.
for (name, skill) in fs_skills {
if skills.contains_key(&name) {
eprintln!(
"Warning: filesystem skill \"{}\" overrides builtin skill with the same name",
name
);
}
skills.insert(name, skill);
}

Copilot uses AI. Check for mistakes.
let mut client = Self { info, skills };
client.info.instructions = Some(client.generate_instructions());
Ok(client)
}

fn load_builtin_skills() -> HashMap<String, Skill> {
let mut skills = HashMap::new();
for content in builtin_skills::get_all_builtin_skills() {
if let Ok((metadata, body)) = Self::parse_frontmatter(content) {
skills.insert(
metadata.name.clone(),
Skill {
metadata,
body,
directory: PathBuf::new(),
supporting_files: vec![],
},
);
Comment on lines +86 to +95

Copilot AI Jan 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errors during built-in skill parsing are silently ignored. If a built-in skill file has invalid frontmatter, it will fail to load without any logging or error reporting. Consider logging parse failures or returning a Result to surface issues during development.

Suggested change
if let Ok((metadata, body)) = Self::parse_frontmatter(content) {
skills.insert(
metadata.name.clone(),
Skill {
metadata,
body,
directory: PathBuf::new(),
supporting_files: vec![],
},
);
match Self::parse_frontmatter(content) {
Ok((metadata, body)) => {
skills.insert(
metadata.name.clone(),
Skill {
metadata,
body,
directory: PathBuf::new(),
supporting_files: vec![],
},
);
}
Err(e) => {
tracing::warn!(error = ?e, "Failed to parse built-in skill frontmatter; built-in skill will be skipped");
}

Copilot uses AI. Check for mistakes.
}
}
skills
}

fn get_default_skill_directories() -> Vec<PathBuf> {
let mut dirs = Vec::new();

Expand Down Expand Up @@ -832,4 +854,12 @@ Working dir goose content
.body
.contains("Working dir goose content"));
}

#[test]
fn test_builtin_skills_loaded() {
let skills = SkillsClient::load_builtin_skills();

assert!(!skills.is_empty());
assert!(skills.contains_key("goose-doc-guide"));
}
}
Loading