mirror of
https://github.com/block/goose.git
synced 2026-07-09 16:09:22 +00:00
Add configurable GOOSE_DOCS_ROOT for air-gapped docs access
Resolve the goose-doc-guide docs root from the GOOSE_DOCS_ROOT config value (falling back to https://goose-docs.ai), substituting it into the builtin skill via a {{GOOSE_DOCS_ROOT}} placeholder.
This commit is contained in:
parent
af2c765c82
commit
57932e0c5c
8 changed files with 233 additions and 16 deletions
|
|
@ -124,7 +124,7 @@ Manage agent sessions: list, view, start, send messages, and interrupt agents.
|
|||
|
||||
|
||||
You have these skills at your disposal, when it is clear they can help you solve a problem or you are asked to use them:
|
||||
• goose-doc-guide - 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.
|
||||
• goose-doc-guide - Reference goose documentation to create, configure, or explain goose-specific features like recipes, extensions, sessions, and providers. You MUST read the relevant goose docs before answering. You MUST NOT rely on training data or assumptions for any goose-specific fields, values, names, syntax, or commands.
|
||||
## summarize
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1161,6 +1161,14 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_goose_docs_root(&self) -> Result<Option<String>, ConfigError> {
|
||||
match self.get_param::<String>("GOOSE_DOCS_ROOT") {
|
||||
Ok(root) => Ok(Some(root.trim().to_string()).filter(|root| !root.is_empty())),
|
||||
Err(ConfigError::NotFound(_)) => Ok(None),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_goose_thinking_effort(&self) -> Option<ThinkingEffort> {
|
||||
self.get_param::<String>("GOOSE_THINKING_EFFORT")
|
||||
.ok()
|
||||
|
|
@ -2540,6 +2548,48 @@ extensions:
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_goose_docs_root_reads_config_file() {
|
||||
let _guard = env_lock::lock_env([("GOOSE_DOCS_ROOT", None::<&str>)]);
|
||||
let config = new_test_config();
|
||||
config
|
||||
.set_param("GOOSE_DOCS_ROOT", "/tmp/goose-docs")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config.get_goose_docs_root().unwrap(),
|
||||
Some("/tmp/goose-docs".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_goose_docs_root_reads_env_value() {
|
||||
let _guard = env_lock::lock_env([("GOOSE_DOCS_ROOT", Some("/tmp/env-docs"))]);
|
||||
let config = new_test_config();
|
||||
|
||||
assert_eq!(
|
||||
config.get_goose_docs_root().unwrap(),
|
||||
Some("/tmp/env-docs".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_goose_docs_root_returns_none_when_unset() {
|
||||
let _guard = env_lock::lock_env([("GOOSE_DOCS_ROOT", None::<&str>)]);
|
||||
let config = new_test_config();
|
||||
|
||||
assert_eq!(config.get_goose_docs_root().unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_goose_docs_root_ignores_blank_value() {
|
||||
let _guard = env_lock::lock_env([("GOOSE_DOCS_ROOT", None::<&str>)]);
|
||||
let config = new_test_config();
|
||||
config.set_param("GOOSE_DOCS_ROOT", " ").unwrap();
|
||||
|
||||
assert_eq!(config.get_goose_docs_root().unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_goose_thinking_effort_reads_env() {
|
||||
let _guard = env_lock::lock_env([
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
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.
|
||||
description: Reference goose documentation to create, configure, or explain goose-specific features like recipes, extensions, sessions, and providers. You MUST read the 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**:
|
||||
|
|
@ -13,22 +13,28 @@ Do NOT use this skill for:
|
|||
- General coding tasks unrelated to goose
|
||||
- Running existing recipes (just run them directly)
|
||||
|
||||
The docs root for this session is `{{GOOSE_DOCS_ROOT}}`. It may be a local
|
||||
filesystem path or an HTTP(S) URL. When it is a local path read files with the
|
||||
shell/file tools; when it is not set or is an HTTP(S) URL fetch them from the
|
||||
canonical location which is https://goose-docs.ai. Everything below refers to
|
||||
this docs root as `<docs-root>`.
|
||||
|
||||
## Steps (COMPLETE ALL BEFORE RESPONDING)
|
||||
1. **Fetch official docs**
|
||||
- Fetch the doc map from `https://goose-docs.ai/goose-docs-map.md`
|
||||
1. **Read official docs**
|
||||
- Read the doc map from `<docs-root>/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://goose-docs.ai/docs/guides/sessions/session-management.md`
|
||||
- Read: `<docs-root>/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
|
||||
- **ONLY read paths that are explicitly listed in the doc map - do not guess or infer paths**
|
||||
- Read multiple docs in parallel and save to temp files
|
||||
- Use the temp files for subsequent searches instead of re-reading
|
||||
|
||||
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**
|
||||
- **Search the 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:**
|
||||
|
|
@ -43,14 +49,15 @@ Do NOT use this skill for:
|
|||
- [ ] 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
|
||||
- If YES and user asked for it → verify exact commands from the 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]").
|
||||
- [ ] For each item, verify it is correct according to the docs. If not found, either read 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
|
||||
- Always link to the canonical site `https://goose-docs.ai/`, even if you read the docs from a local path. Never expose local filesystem paths.
|
||||
- Remove `.md` suffix from URLs
|
||||
- Example: If you fetched `https://goose-docs.ai/docs/guides/sessions/session-management.md`, list it as `https://goose-docs.ai/docs/guides/sessions/session-management`
|
||||
- Example: If you read `docs/guides/sessions/session-management.md`, list it as `https://goose-docs.ai/docs/guides/sessions/session-management`
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub mod client;
|
|||
|
||||
pub use client::{SkillsClient, EXTENSION_NAME};
|
||||
|
||||
use crate::config::paths::Paths;
|
||||
use crate::config::{paths::Paths, Config};
|
||||
use crate::plugins::installed_plugin_skill_dirs;
|
||||
use crate::sources::parse_frontmatter;
|
||||
use agent_client_protocol::Error;
|
||||
|
|
@ -99,7 +99,27 @@ pub(crate) fn validate_skill_name(name: &str) -> Result<(), Error> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn loaded_skill_context(skill: &SourceEntry, content: &str) -> String {
|
||||
const DEFAULT_GOOSE_DOCS_ROOT: &str = "https://goose-docs.ai";
|
||||
const GOOSE_DOCS_ROOT_PLACEHOLDER: &str = "{{GOOSE_DOCS_ROOT}}";
|
||||
|
||||
/// Substitute the `{{GOOSE_DOCS_ROOT}}` placeholder in the builtin
|
||||
/// `goose-doc-guide` skill with the resolved docs root. Resolution is
|
||||
/// deterministic: the configured `GOOSE_DOCS_ROOT` if set, otherwise the
|
||||
/// canonical online docs root.
|
||||
fn resolve_docs_root_placeholder(skill: &SourceEntry, content: &str, docs_root: &str) -> String {
|
||||
if skill.name != "goose-doc-guide" || skill.source_type != SourceType::BuiltinSkill {
|
||||
return content.to_string();
|
||||
}
|
||||
|
||||
content.replace(GOOSE_DOCS_ROOT_PLACEHOLDER, docs_root)
|
||||
}
|
||||
|
||||
fn loaded_skill_context(skill: &SourceEntry, content: &str) -> Result<String> {
|
||||
let docs_root = Config::global()
|
||||
.get_goose_docs_root()?
|
||||
.unwrap_or_else(|| DEFAULT_GOOSE_DOCS_ROOT.to_string());
|
||||
let content = resolve_docs_root_placeholder(skill, content, &docs_root);
|
||||
|
||||
let title = format!("{} ({})", skill.name, skill.source_type);
|
||||
let mut output = format!(
|
||||
"# Loaded Skill: {title}\n\n{}\n\n## Content\n\n{}\n",
|
||||
|
|
@ -128,7 +148,7 @@ fn loaded_skill_context(skill: &SourceEntry, content: &str) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
output
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn loaded_skill_context_with_args(skill: &SourceEntry, args: Option<&str>) -> Result<String> {
|
||||
|
|
@ -138,7 +158,7 @@ pub fn loaded_skill_context_with_args(skill: &SourceEntry, args: Option<&str>) -
|
|||
skill.content.clone()
|
||||
};
|
||||
|
||||
Ok(loaded_skill_context(skill, &content))
|
||||
loaded_skill_context(skill, &content)
|
||||
}
|
||||
|
||||
pub fn skill_argument_hint(skill: &SourceEntry) -> Option<String> {
|
||||
|
|
@ -518,6 +538,20 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn builtin_goose_doc_guide_skill() -> SourceEntry {
|
||||
SourceEntry {
|
||||
source_type: SourceType::BuiltinSkill,
|
||||
name: "goose-doc-guide".to_string(),
|
||||
description: "Test docs skill".to_string(),
|
||||
content: "Docs root: {{GOOSE_DOCS_ROOT}}.".to_string(),
|
||||
path: "builtin://skills/goose-doc-guide".to_string(),
|
||||
global: true,
|
||||
writable: true,
|
||||
supporting_files: Vec::new(),
|
||||
properties: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loaded_skill_context_with_args_replaces_arguments_placeholder_with_raw_args() {
|
||||
let skill = skill_with_content("Review $ARGUMENTS carefully.");
|
||||
|
|
@ -553,4 +587,24 @@ mod tests {
|
|||
assert!(rendered.contains(&resolved_path));
|
||||
assert!(rendered.contains("load_skill(name: \"test-skill/scripts/my-tool.exe\")"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_docs_root_placeholder_substitutes_builtin_goose_doc_guide_root() {
|
||||
let skill = builtin_goose_doc_guide_skill();
|
||||
|
||||
let rendered =
|
||||
resolve_docs_root_placeholder(&skill, &skill.content, "/tmp/goose docs/root");
|
||||
|
||||
assert_eq!(rendered, "Docs root: /tmp/goose docs/root.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_docs_root_placeholder_ignores_non_builtin_goose_doc_guide_skills() {
|
||||
let mut skill = builtin_goose_doc_guide_skill();
|
||||
skill.source_type = SourceType::Skill;
|
||||
|
||||
let rendered = resolve_docs_root_placeholder(&skill, &skill.content, "/tmp/goose-docs");
|
||||
|
||||
assert_eq!(rendered, skill.content);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ The following settings can be configured at the root level of your config.yaml f
|
|||
| `GOOSE_CLI_DARK_THEME` | Custom syntax highlighting theme for dark mode | [bat theme name](https://github.com/sharkdp/bat#adding-new-themes) | "zenburn" | No |
|
||||
| `GOOSE_CLI_SHOW_COST` | Show estimated cost for token use in the CLI | true/false | false | No |
|
||||
| `GOOSE_ALLOWLIST` | URL for allowed extensions | Valid URL | None | No |
|
||||
| `GOOSE_DOCS_ROOT` | Documentation root used by `goose-doc-guide` (e.g. for offline/air-gapped docs) | Local path or HTTP(S) URL containing `goose-docs-map.md` and `docs/` | `https://goose-docs.ai` | No |
|
||||
| `GOOSE_RECIPE_GITHUB_REPO` | GitHub repository for recipes | Format: "org/repo" | None | No |
|
||||
| `GOOSE_AUTO_COMPACT_THRESHOLD` | Set the percentage threshold at which goose [automatically summarizes your session](/docs/guides/sessions/smart-context-management#automatic-compaction). | Float between 0.0 and 1.0 (disabled at 0.0)| 0.8 | No |
|
||||
| `SECURITY_PROMPT_ENABLED` | Enable [prompt injection detection](/docs/guides/security/prompt-injection-detection) to identify potentially harmful commands | true/false | false | No |
|
||||
|
|
@ -81,6 +82,9 @@ GOOSE_CLI_MIN_PRIORITY: 0.2
|
|||
# Recipe Configuration
|
||||
GOOSE_RECIPE_GITHUB_REPO: "aaif-goose/goose-recipes"
|
||||
|
||||
# Documentation Configuration
|
||||
GOOSE_DOCS_ROOT: "/path/to/goose-docs"
|
||||
|
||||
# Search Path Configuration
|
||||
GOOSE_SEARCH_PATHS:
|
||||
- "/usr/local/bin"
|
||||
|
|
|
|||
|
|
@ -554,6 +554,14 @@ export GOOSE_RECIPE_RETRY_TIMEOUT_SECONDS=300
|
|||
export GOOSE_RECIPE_ON_FAILURE_TIMEOUT_SECONDS=60
|
||||
```
|
||||
|
||||
## Documentation Configuration
|
||||
|
||||
This variable controls where the `goose-doc-guide` skill reads goose documentation from.
|
||||
|
||||
| Variable | Purpose | Values | Default |
|
||||
|----------|---------|---------|---------|
|
||||
| `GOOSE_DOCS_ROOT` | Documentation root for the `goose-doc-guide` skill, used for [offline/air-gapped docs](/docs/guides/offline-docs) | Local path or HTTP(S) URL containing `goose-docs-map.md` and `docs/` | `https://goose-docs.ai` |
|
||||
|
||||
## Development & Testing
|
||||
|
||||
These variables are primarily used for development, testing, and debugging goose itself.
|
||||
|
|
|
|||
91
documentation/docs/guides/offline-docs.md
Normal file
91
documentation/docs/guides/offline-docs.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
---
|
||||
title: Offline / Air-gapped Docs
|
||||
sidebar_position: 95
|
||||
sidebar_label: Offline Docs
|
||||
---
|
||||
|
||||
# Offline / Air-gapped Docs
|
||||
|
||||
The `goose-doc-guide` skill reads official goose documentation before answering
|
||||
goose-specific questions. By default it reads from `https://goose-docs.ai`. In
|
||||
an offline or air-gapped environment, point goose at a **local copy** instead by
|
||||
setting `GOOSE_DOCS_ROOT`.
|
||||
|
||||
- If `GOOSE_DOCS_ROOT` is set (in `config.yaml` or the environment), goose uses
|
||||
it as the docs root — either a local filesystem path or an HTTP(S) URL.
|
||||
- If it is not set, goose falls back to `https://goose-docs.ai`.
|
||||
|
||||
When the root is a local path, goose reads the docs with its file tools; no
|
||||
network access is required.
|
||||
|
||||
## Docs layout
|
||||
|
||||
A docs root contains a docs map and a `docs/` tree:
|
||||
|
||||
```
|
||||
<docs-root>/
|
||||
├── goose-docs-map.md
|
||||
└── docs/
|
||||
├── getting-started/...
|
||||
└── guides/...
|
||||
```
|
||||
|
||||
`goose-docs-map.md` is the index the skill searches first; every page it reads
|
||||
is referenced by a path listed there.
|
||||
|
||||
## Building a local docs root
|
||||
|
||||
Generate the tree from a goose checkout using the same version as your goose
|
||||
binary, so the docs match the runtime. For example:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
GOOSE_VERSION="${1:-v1.41.0}"
|
||||
DOCS_ROOT="${2:-/opt/goose-docs}"
|
||||
REPO="${GOOSE_REPO:-$(git rev-parse --show-toplevel)}"
|
||||
|
||||
cd "$REPO"
|
||||
git checkout --quiet "$GOOSE_VERSION"
|
||||
|
||||
cd documentation
|
||||
npm ci
|
||||
node scripts/generate-docs-map.js
|
||||
|
||||
rm -rf "$DOCS_ROOT"
|
||||
mkdir -p "$DOCS_ROOT/docs"
|
||||
cp static/goose-docs-map.md "$DOCS_ROOT/goose-docs-map.md"
|
||||
cp -r docs/getting-started docs/guides "$DOCS_ROOT/docs/"
|
||||
|
||||
CONFIG="${GOOSE_CONFIG_PATH:-$HOME/.config/goose/config.yaml}"
|
||||
mkdir -p "$(dirname "$CONFIG")"
|
||||
touch "$CONFIG"
|
||||
sed -i.bak '/^GOOSE_DOCS_ROOT:/d' "$CONFIG" && rm -f "$CONFIG.bak"
|
||||
echo "GOOSE_DOCS_ROOT: \"$DOCS_ROOT\"" >> "$CONFIG"
|
||||
```
|
||||
|
||||
## Configuring goose
|
||||
|
||||
Set `GOOSE_DOCS_ROOT` in `config.yaml`:
|
||||
|
||||
```yaml
|
||||
GOOSE_DOCS_ROOT: "/opt/goose-docs"
|
||||
```
|
||||
|
||||
Or via the environment:
|
||||
|
||||
```bash
|
||||
export GOOSE_DOCS_ROOT=/opt/goose-docs
|
||||
```
|
||||
|
||||
For a managed distribution, bake the docs tree into your image and set
|
||||
`GOOSE_DOCS_ROOT` in the shipped `config.yaml` or launcher environment.
|
||||
|
||||
## Notes
|
||||
|
||||
- Documentation links in goose's answers always render as canonical
|
||||
`https://goose-docs.ai/...` URLs, even when read locally.
|
||||
- A custom HTTP(S) mirror also works: set `GOOSE_DOCS_ROOT` to its root URL.
|
||||
- For MCP extension runtime issues offline, see
|
||||
[Airgapped/Offline Environment Issues](/docs/troubleshooting/known-issues#airgappedoffline-environment-issues).
|
||||
|
|
@ -507,6 +507,9 @@ goose Desktop uses **"shims"** (packaged versions of `npx` and `uvx`) that autom
|
|||
|
||||
4. **Require more changes**: In a corporate proxy environment or airgapped environment where the above doesn't work, it is recommended that you customize and package up goose Desktop with shims/config that will work given the network constraints you have (for example, TLS certificate limitations, [proxy configuration](/docs/guides/environment-variables#network-configuration), inability to download required content etc).
|
||||
|
||||
#### Documentation access:
|
||||
The `goose-doc-guide` skill reads goose documentation from `https://goose-docs.ai` by default, which is unavailable offline. To have goose read from a local copy of the docs instead, see [Offline / Air-gapped Docs](/docs/guides/offline-docs).
|
||||
|
||||
---
|
||||
### Need Further Help?
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue