From 8185d3a70668106b56ebc105b7ddb28192f24ff0 Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Mon, 17 Nov 2025 09:32:24 -0500 Subject: [PATCH 01/14] Match lower case (#5763) Co-authored-by: Douwe Osinga --- crates/goose-mcp/src/mcp_server_runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/goose-mcp/src/mcp_server_runner.rs b/crates/goose-mcp/src/mcp_server_runner.rs index d1f26327ef..4bc9e94c53 100644 --- a/crates/goose-mcp/src/mcp_server_runner.rs +++ b/crates/goose-mcp/src/mcp_server_runner.rs @@ -17,7 +17,7 @@ pub async fn run_mcp_server(name: &str) -> Result<()> { tracing::info!("Starting MCP server"); - match name { + match name.to_lowercase().as_str() { "autovisualiser" => serve_and_wait(AutoVisualiserRouter::new()).await, "computercontroller" => serve_and_wait(ComputerControllerServer::new()).await, "developer" => serve_and_wait(DeveloperServer::new()).await, From 0782874a92388e6add8ddefbb4d4240bef892eca Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Mon, 17 Nov 2025 09:36:11 -0500 Subject: [PATCH 02/14] More time for goosed (#5746) --- ui/desktop/src/goosed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/desktop/src/goosed.ts b/ui/desktop/src/goosed.ts index e6459cc68a..5481fc8d5c 100644 --- a/ui/desktop/src/goosed.ts +++ b/ui/desktop/src/goosed.ts @@ -27,7 +27,7 @@ export const findAvailablePort = (): Promise => { // Check if goosed server is ready by polling the status endpoint export const checkServerStatus = async (client: Client, errorLog: string[]): Promise => { const interval = 100; // ms - const maxAttempts = 30; // 3s + const maxAttempts = 100; // 10s const fatal = (line: string) => { const trimmed = line.trim().toLowerCase(); From 8082c692286d9dc658001414a3cb63c24f4cac2d Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Mon, 17 Nov 2025 10:49:34 -0500 Subject: [PATCH 03/14] Simplify finding `goosed` (#5739) --- .../settings/extensions/utils.test.ts | 10 -- ui/desktop/src/goosed.ts | 44 ++++++++- ui/desktop/src/main.ts | 7 +- ui/desktop/src/preload.ts | 2 - ui/desktop/src/utils/pathUtils.ts | 92 ------------------- 5 files changed, 43 insertions(+), 112 deletions(-) diff --git a/ui/desktop/src/components/settings/extensions/utils.test.ts b/ui/desktop/src/components/settings/extensions/utils.test.ts index 98c90963d4..fc5b5e4c44 100644 --- a/ui/desktop/src/components/settings/extensions/utils.test.ts +++ b/ui/desktop/src/components/settings/extensions/utils.test.ts @@ -12,16 +12,6 @@ import { } from './utils'; import type { FixedExtensionEntry } from '../../ConfigContext'; -// Mock window.electron -const mockElectron = { - getBinaryPath: vi.fn(), -}; - -Object.defineProperty(window, 'electron', { - value: mockElectron, - writable: true, -}); - describe('Extension Utils', () => { beforeEach(() => { vi.clearAllMocks(); diff --git a/ui/desktop/src/goosed.ts b/ui/desktop/src/goosed.ts index 5481fc8d5c..eda35226c5 100644 --- a/ui/desktop/src/goosed.ts +++ b/ui/desktop/src/goosed.ts @@ -1,8 +1,9 @@ +import Electron from 'electron'; +import fs from 'node:fs'; import { spawn, ChildProcess } from 'child_process'; import { createServer } from 'net'; import os from 'node:os'; import path from 'node:path'; -import { getBinaryPath } from './utils/pathUtils'; import log from './utils/logger'; import { App } from 'electron'; import { Buffer } from 'node:buffer'; @@ -94,7 +95,7 @@ export const startGoosed = async ( return connectToExternalBackend(dir, 3000); } - let goosedPath = getBinaryPath(app, 'goosed'); + let goosedPath = getGoosedBinaryPath(app); const resolvedGoosedPath = path.resolve(goosedPath); @@ -215,3 +216,42 @@ export const startGoosed = async ( log.info(`Goosed server successfully started on port ${port}`); return [port, dir, goosedProcess, stderrLines]; }; + +const getGoosedBinaryPath = (app: Electron.App): string => { + let executableName = process.platform === 'win32' ? 'goosed.exe' : 'goosed'; + + let possiblePaths: string[]; + if (!app.isPackaged) { + possiblePaths = [ + path.join(process.cwd(), 'src', 'bin', executableName), + path.join(process.cwd(), 'bin', executableName), + path.join(process.cwd(), '..', '..', 'target', 'debug', executableName), + path.join(process.cwd(), '..', '..', 'target', 'release', executableName), + ]; + } else { + possiblePaths = [path.join(process.resourcesPath, 'bin', executableName)]; + } + + for (const binPath of possiblePaths) { + try { + const resolvedPath = path.resolve(binPath); + + if (fs.existsSync(resolvedPath)) { + const stats = fs.statSync(resolvedPath); + if (stats.isFile()) { + return resolvedPath; + } else { + log.error(`Path exists but is not a regular file: ${resolvedPath}`); + } + } + } catch (error) { + log.error(`Error checking path ${binPath}:`, error); + } + } + + throw new Error( + `Could not find ${executableName} binary in any of the expected locations: ${possiblePaths.join( + ', ' + )}` + ); +}; diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index 001e034ef7..24dfe2f50f 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -25,7 +25,7 @@ import os from 'node:os'; import { spawn } from 'child_process'; import 'dotenv/config'; import { checkServerStatus, startGoosed } from './goosed'; -import { expandTilde, getBinaryPath } from './utils/pathUtils'; +import { expandTilde } from './utils/pathUtils'; import log from './utils/logger'; import { ensureWinShims } from './utils/winShims'; import { addRecentDir, loadRecentDirs } from './utils/recentDirs'; @@ -1599,11 +1599,6 @@ ipcMain.handle('check-ollama', async () => { } }); -// Handle binary path requests -ipcMain.handle('get-binary-path', (_event, binaryName) => { - return getBinaryPath(app, binaryName); -}); - ipcMain.handle('read-file', async (_event, filePath) => { try { const expandedPath = expandTilde(filePath); diff --git a/ui/desktop/src/preload.ts b/ui/desktop/src/preload.ts index 070e8a0afe..20a0d95b74 100644 --- a/ui/desktop/src/preload.ts +++ b/ui/desktop/src/preload.ts @@ -66,7 +66,6 @@ type ElectronAPI = { selectFileOrDirectory: (defaultPath?: string) => Promise; startPowerSaveBlocker: () => Promise; stopPowerSaveBlocker: () => Promise; - getBinaryPath: (binaryName: string) => Promise; readFile: (directory: string) => Promise; writeFile: (directory: string, content: string) => Promise; ensureDirectory: (dirPath: string) => Promise; @@ -169,7 +168,6 @@ const electronAPI: ElectronAPI = { ipcRenderer.invoke('select-file-or-directory', defaultPath), startPowerSaveBlocker: () => ipcRenderer.invoke('start-power-save-blocker'), stopPowerSaveBlocker: () => ipcRenderer.invoke('stop-power-save-blocker'), - getBinaryPath: (binaryName: string) => ipcRenderer.invoke('get-binary-path', binaryName), readFile: (filePath: string) => ipcRenderer.invoke('read-file', filePath), writeFile: (filePath: string, content: string) => ipcRenderer.invoke('write-file', filePath, content), diff --git a/ui/desktop/src/utils/pathUtils.ts b/ui/desktop/src/utils/pathUtils.ts index f2865227a8..51af4abe4e 100644 --- a/ui/desktop/src/utils/pathUtils.ts +++ b/ui/desktop/src/utils/pathUtils.ts @@ -1,97 +1,5 @@ import path from 'node:path'; -import fs from 'node:fs'; import os from 'node:os'; -import Electron from 'electron'; -import log from './logger'; - -export const getBinaryPath = (app: Electron.App, binaryName: string): string => { - // On Windows, rely on PATH we just patched in ensureWinShims for command-line tools - // but use explicit resources/bin path for goosed.exe - if (process.platform === 'win32') { - // For goosed.exe, always use the explicit resources/bin path - if (binaryName === 'goosed') { - return path.join(process.resourcesPath, 'bin', 'goosed.exe'); - } - - // Map binary names to their Windows equivalents - const windowsBinaryMap: Record = { - npx: 'npx.cmd', - uvx: 'uvx.exe', - }; - - // For other binaries, use Windows-specific extensions if available - const windowsBinary = windowsBinaryMap[binaryName] || binaryName; - return windowsBinary; - } - - // For non-Windows platforms, use the original logic - const possiblePaths: string[] = []; - addPaths(false, possiblePaths, binaryName, app); - - for (const binPath of possiblePaths) { - try { - // Security: Resolve the path and validate it's within expected directories - const resolvedPath = path.resolve(binPath); - - // Ensure the resolved path doesn't contain suspicious sequences - if ( - resolvedPath.includes('..') || - resolvedPath.includes(';') || - resolvedPath.includes('|') || - resolvedPath.includes('&') - ) { - log.error(`Suspicious path detected, skipping: ${resolvedPath}`); - continue; - } - - if (fs.existsSync(resolvedPath)) { - // Additional security check: ensure it's a regular file - const stats = fs.statSync(resolvedPath); - if (stats.isFile()) { - return resolvedPath; - } else { - log.error(`Path exists but is not a regular file: ${resolvedPath}`); - } - } - } catch (error) { - log.error(`Error checking path ${binPath}:`, error); - } - } - - throw new Error( - `Could not find ${binaryName} binary in any of the expected locations: ${possiblePaths.join( - ', ' - )}` - ); -}; - -const addPaths = ( - isWindows: boolean, - possiblePaths: string[], - executableName: string, - app: Electron.App -): void => { - if (!app.isPackaged) { - possiblePaths.push( - path.join(process.cwd(), 'src', 'bin', executableName), - path.join(process.cwd(), 'bin', executableName), - path.join(process.cwd(), '..', '..', 'target', 'release', executableName) - ); - } else { - possiblePaths.push( - path.join(process.resourcesPath, 'bin', executableName), - path.join(app.getAppPath(), 'resources', 'bin', executableName) - ); - - if (isWindows) { - possiblePaths.push( - path.join(process.resourcesPath, executableName), - path.join(app.getAppPath(), 'resources', executableName), - path.join(app.getPath('exe'), '..', 'bin', executableName) - ); - } - } -}; /** * Expands tilde (~) to the user's home directory From 655d5d5b1499bd664b39aa53c813309eb3492062 Mon Sep 17 00:00:00 2001 From: Rizel Scarlett Date: Mon, 17 Nov 2025 11:32:30 -0500 Subject: [PATCH 04/14] blog post: How to Successfully Migrate Your App with an AI Agent (#5762) --- .../index.md | 144 ++++++++++++++++++ .../migrate-app-ai-agent.png | Bin 0 -> 71020 bytes 2 files changed, 144 insertions(+) create mode 100644 documentation/blog/2025-11-17-migrate-app-with-ai-agent/index.md create mode 100644 documentation/blog/2025-11-17-migrate-app-with-ai-agent/migrate-app-ai-agent.png diff --git a/documentation/blog/2025-11-17-migrate-app-with-ai-agent/index.md b/documentation/blog/2025-11-17-migrate-app-with-ai-agent/index.md new file mode 100644 index 0000000000..22c379e264 --- /dev/null +++ b/documentation/blog/2025-11-17-migrate-app-with-ai-agent/index.md @@ -0,0 +1,144 @@ +--- +title: How to Successfully Migrate Your App with an AI Agent +description: A step-by-step prompt strategy for AI-assisted code migration with real examples from a Next.js refactor +authors: + - rizel +--- + +![How to Successfully Migrate Your App with an AI Agent](migrate-app-ai-agent.png) + +"Migrate my app from x language to y language." You hit enter, watch your AI agent spin its wheels, and eventually every success story you've heard feels like a carefully orchestrated lie. + +Most failures have less to do with the agent's capability and more to do with poor prompt and context strategy. Think about it: if someone dropped you into a complex, unfamiliar codebase and said "migrate this," you'd be lost without a plan. You'd need to explore the code, ask questions about its structure, and break the work into manageable steps. + +Your AI agent needs the same approach: guided exploration, strategic questions, and decomposed tasks. + + + +I recently put this approach into practice with [goose](/), migrating a legacy LLM credit provisioning system split across two repositories (React/Vite frontend and Node/Express backend) into a unified Next.js framework. + +## Why I Needed to Refactor + +I originally built the app to distribute LLM API credits at a Boston meetup. It was a quick prototype that experienced unexpected adoption, exposing fundamental architectural problems. (And I have shiny toy syndrome, so I struggled to return to the app to improve it). I wanted to make the following improvements: + +- Email-based provisioning +- Dynamic credit allocation per event +- Analytics infrastructure +- Admin panel + +I hopped on a livestream to tackle this huge refactor but seconds in I realized I realistically could not do this all in one hour. I focused on consolidating the fragmented codebase first. Two repositories (React/Vite frontend, Express backend) needed to become one monolithic Next.js application. But simply telling goose "Convert to Next.js" wouldn't work without proper context building. + +## My Prompt Strategy + +### Building a Shared Mental Model + +Before I instructed goose to write any code, I prioritized helping it understand the codebase systematically with the following prompt: + +> Can you get a lay of the land for the two projects found here and how they communicate? + +goose employed the [analyze tool](/docs/mcp/developer-mcp#developer-extension-tools) to generate a high-level architectural flow. (The analyze tool is part of the [Developer extension](/docs/mcp/developer-mcp), an [MCP server](https://modelcontextprotocol.io/docs/learn/server-concepts) that's built into goose). + +``` +User Browser + ↓ +[goose-access-gateway] (React SPA) + ↓ (HTTPS REST API) +[goose-hacknight-backend] (Express API) + ↓ (HTTPS REST API) +[OpenRouter API] (Third-party service) +``` + +It also shared all the various endpoints and how to run the repos. This mapping served dual purposes: establishing the agent's contextual foundation and refreshing my own mental model of an eight-month-old implementation. + +### Defining the Scope + +With the landscape mapped, I needed to prevent scope creep. I deliberately focused the agent's attention on the frontend to avoid chaotic, uncontrolled changes across the entire codebase. + +> Tell me the commands to run the frontend project. + +Yes, I could have found the commands in the package.json, but asking goose to do it served a purpose: it grounded goose in the actual project setup and prevented it from hallucinating commands or ports. + +:::tip Pro tip +I always have goose tell me what commands to run (like npm run dev) rather than executing them itself. Long-running or blocking commands can halt goose's process. +::: + +### Verification Driven Development + +One major pitfall of AI-assisted coding is that agents cannot validate their code beyond syntactic correctness. + +To address this, I enabled the [Chrome Dev Tools extension](/docs/mcp/chrome-devtools-mcp), granting the agent browser-level inspection capabilities: DOM manipulation verification, CSS property validation, and performance profiling. This extension gave goose "eyes", which meant I could give it my most ambitious prompt yet: + +> I have the frontend running right now on localhost:8080. I want to take this UI design and start from scratch a bit. I need all new logic especially for the backend. Can we create a new directory and create a Next.js project and for now let's just do the frontend, but don't add any of the API calls or anything. We just want to retain the design of the frontend page. Please recreate that. Use the Chrome Dev Tools extension to see how the UI looks so you can copy it and use the to do extension to help you plan. If there are interactive commands or you can run an install or something like that just tell me to do it...and give me the details of what I need to run. + +This was a huge prompt, so let's break down what each part accomplished: + +- **Isolation:** create a new directory +- **Scope:** just do the frontend, but don't add any of the API calls +- **Verification:** Use the Chrome Dev Tools extension to see how the UI looks +- **Planning:** use the to do extension to help you plan +- **Interaction:** just tell me to do it...and give me the details of what I need to run + +:::note +In retrospect, the instruction regarding blocking commands should have been codified in [persistent context files](/docs/guides/using-goosehints) ([AGENTS.md](https://agents.md/) or [goosehints](/docs/guides/using-goosehints)) rather than inline prompts. +::: + +But, I was so happy that goose generated a pixel perfect recreation of the app. + +### Task Decomposition + +The agent's successful, perfect recreation of the UI was largely due to the [Todo extension](/docs/mcp/todo-mcp), an MCP server that's built into goose. I find that this extension helps prevent scope drift, where agents autonomously expand into adjacent functionality after completing an objective. + +The to do list included items like: + +- Copy logo assets from old project +- Create glass-morphism card component +- Add logo with fade in animation +- Verify theme toggle works + +When I ran the app locally, I did encounter a Tailwind CSS v4/v3 syntax error, but goose used the Chrome Dev Tools extension and the Todo extension to quickly fix it. + +### Automated Version Control + +Because my UI was pixel-perfect, I felt confident enough to introduce some backend logic, but I knew introducing this level of complexity would require granular version control. When an agent makes a dozen changes, it's easy to end up with unwanted code buried in the history. Manually tracking and reverting these changes is tedious. + +To solve this problem, I instituted an automated commit policy by adding a persistent directive to my .goosehints file: + +> Every time you make a change, make a commit using the GitHub CLI or the GitHub MCP Server. + +### Pattern Replication + +The final step was to add the backend logic for emailing API keys. Instead of asking goose to invent this from scratch, I had it learn from a known-working system: a separate app with similar provisioning logic. + +I gave goose the following prompt: + +> There's a recipe cookbook. To submit people have to open up a PR and then it sends them an email with an API key. Are you able to find the logic where it sends the API key? + +Once it analyzed that code, I gave the final instruction: + +> Use what you learned from the recipe project logic to make this happen in goose-credits... send the API key to their email using the SendGrid API. + +This "copy-and-adapt" strategy was incredibly effective. goose successfully implemented the necessary API routes and clearly identified the environment variables I needed to supply. I manually added those variables. I didn't give them to goose for security purposes. + +## The Lesson + +I shared my messy, tedious conversation with goose (using Claude Sonnet 4.5) so that readers can confidently ditch one-shot prompts for complex tasks and work incrementally with agents. Just like coding, collaborating with an agent requires patience, but it doesn't have to feel stressful. + +I hope this clarifies how to converse with an agent and accomplish complex tasks like migrations. If you want to see this in action, you're in luck; below is a VOD livestream of me navigating the project in real-time. + + + +*Ready to try AI-assisted migration with goose? Get started with our [quickstart guide](/docs/quickstart) and share your experience in our [Discord community](http://discord.gg/goose-oss).* + + + + + + + + + + + + + + diff --git a/documentation/blog/2025-11-17-migrate-app-with-ai-agent/migrate-app-ai-agent.png b/documentation/blog/2025-11-17-migrate-app-with-ai-agent/migrate-app-ai-agent.png new file mode 100644 index 0000000000000000000000000000000000000000..7aaa29f831c3e6bbc7eb3d9cc625a5d4c0a27dd5 GIT binary patch literal 71020 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%0A>w3~fQ;ynWF<1t_LCLV2o!?t^PH{ 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 Date: Mon, 17 Nov 2025 13:05:40 -0500 Subject: [PATCH 05/14] fix: Implement a CredentialStore for auth (#5741) --- Cargo.lock | 52 +++++++-- Cargo.toml | 2 +- crates/goose-bench/Cargo.toml | 2 +- crates/goose-cli/Cargo.toml | 2 +- crates/goose-mcp/Cargo.toml | 2 +- crates/goose-server/Cargo.toml | 2 +- crates/goose/Cargo.toml | 2 +- crates/goose/src/agents/extension_manager.rs | 1 + crates/goose/src/oauth/mod.rs | 39 ++++--- crates/goose/src/oauth/persist.rs | 109 ++++++++----------- ui/desktop/openapi.json | 4 + ui/desktop/src/api/types.gen.ts | 3 + 12 files changed, 129 insertions(+), 91 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a5a2d38b4b..58f8da8595 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -281,9 +281,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.87" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -2661,7 +2661,7 @@ dependencies = [ "rand 0.8.5", "regex", "reqwest 0.12.12", - "rmcp", + "rmcp 0.9.0", "schemars", "serde", "serde_json", @@ -2711,7 +2711,7 @@ dependencies = [ "once_cell", "paste", "regex", - "rmcp", + "rmcp 0.9.0", "serde", "serde_json", "tokio", @@ -2750,7 +2750,7 @@ dependencies = [ "open", "rand 0.8.5", "regex", - "rmcp", + "rmcp 0.9.0", "rustyline", "serde", "serde_json", @@ -2805,7 +2805,7 @@ dependencies = [ "rayon", "regex", "reqwest 0.11.27", - "rmcp", + "rmcp 0.8.5", "schemars", "serde", "serde_json", @@ -2856,7 +2856,7 @@ dependencies = [ "goose-mcp", "http 1.2.0", "reqwest 0.12.12", - "rmcp", + "rmcp 0.9.0", "schemars", "serde", "serde_json", @@ -5574,6 +5574,29 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5947688160b56fb6c827e3c20a72c90392a1d7e9dec74749197aa1780ac42ca" dependencies = [ + "base64 0.22.1", + "chrono", + "futures", + "paste", + "pin-project-lite", + "rmcp-macros 0.8.5", + "schemars", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "rmcp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acc36ea743d4bbc97e9f3c33bf0b97765a5cf338de3d9c3d2f321a6e38095615" +dependencies = [ + "async-trait", "base64 0.22.1", "chrono", "futures", @@ -5583,7 +5606,7 @@ dependencies = [ "pin-project-lite", "process-wrap", "reqwest 0.12.12", - "rmcp-macros", + "rmcp-macros 0.9.0", "schemars", "serde", "serde_json", @@ -5609,6 +5632,19 @@ dependencies = [ "syn 2.0.99", ] +[[package]] +name = "rmcp-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "263caba1c96f2941efca0fdcd97b03f42bcde52d2347d05e5d77c93ab18c5b58" +dependencies = [ + "darling 0.21.0", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.99", +] + [[package]] name = "ron" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index dc2bb1febc..4b6b888646 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ uninlined_format_args = "allow" string_slice = "warn" [workspace.dependencies] -rmcp = { version = "0.8.5", features = ["schemars", "auth"] } +rmcp = { version = "0.9.0", features = ["schemars", "auth"] } # Patch for Windows cross-compilation issue with crunchy [patch.crates-io] diff --git a/crates/goose-bench/Cargo.toml b/crates/goose-bench/Cargo.toml index d91d09df32..cabba6121a 100644 --- a/crates/goose-bench/Cargo.toml +++ b/crates/goose-bench/Cargo.toml @@ -16,7 +16,7 @@ paste = "1.0" ctor = "0.2.7" goose = { path = "../goose" } rmcp = { workspace = true } -async-trait = "0.1.86" +async-trait = "0.1.89" chrono = { version = "0.4", features = ["serde"] } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } diff --git a/crates/goose-cli/Cargo.toml b/crates/goose-cli/Cargo.toml index 7fc20c75a5..7404a2f84a 100644 --- a/crates/goose-cli/Cargo.toml +++ b/crates/goose-cli/Cargo.toml @@ -43,7 +43,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json", tracing-appender = "0.2" once_cell = "1.20.2" shlex = "1.3.0" -async-trait = "0.1.86" +async-trait = "0.1.89" base64 = "0.22.1" regex = "1.11.1" nix = { version = "0.30.1", features = ["process", "signal"] } diff --git a/crates/goose-mcp/Cargo.toml b/crates/goose-mcp/Cargo.toml index 34c0531c5e..15ce91bc61 100644 --- a/crates/goose-mcp/Cargo.toml +++ b/crates/goose-mcp/Cargo.toml @@ -33,7 +33,7 @@ reqwest = { version = "0.11", features = [ "json", "rustls-tls-native-roots", ], default-features = false } -async-trait = "0.1" +async-trait = "0.1.89" chrono = { version = "0.4.38", features = ["serde"] } etcetera = "0.8.0" tempfile = "3.8" diff --git a/crates/goose-server/Cargo.toml b/crates/goose-server/Cargo.toml index 59d4f20418..98c43f542b 100644 --- a/crates/goose-server/Cargo.toml +++ b/crates/goose-server/Cargo.toml @@ -50,5 +50,5 @@ path = "src/bin/generate_schema.rs" [dev-dependencies] tower = "0.5" -async-trait = "0.1" +async-trait = "0.1.89" tempfile = "3.15.0" diff --git a/crates/goose/Cargo.toml b/crates/goose/Cargo.toml index efc80530e8..b56c1ee617 100644 --- a/crates/goose/Cargo.toml +++ b/crates/goose/Cargo.toml @@ -49,7 +49,7 @@ serde_urlencoded = "0.7" jsonschema = "0.30.0" uuid = { version = "1.0", features = ["v4"] } regex = "1.11.1" -async-trait = "0.1" +async-trait = "0.1.89" async-stream = "0.3" minijinja = { version = "2.10.2", features = ["loader"] } include_dir = "0.7.4" diff --git a/crates/goose/src/agents/extension_manager.rs b/crates/goose/src/agents/extension_manager.rs index 0401746ec7..dd8f356ded 100644 --- a/crates/goose/src/agents/extension_manager.rs +++ b/crates/goose/src/agents/extension_manager.rs @@ -674,6 +674,7 @@ impl ExtensionManager { output_schema: tool.output_schema, icons: None, title: None, + meta: None, }); } } diff --git a/crates/goose/src/oauth/mod.rs b/crates/goose/src/oauth/mod.rs index d57fce564a..1eccaa7223 100644 --- a/crates/goose/src/oauth/mod.rs +++ b/crates/goose/src/oauth/mod.rs @@ -1,9 +1,11 @@ +mod persist; + use axum::extract::{Query, State}; use axum::response::Html; use axum::routing::get; use axum::Router; use minijinja::render; -use rmcp::transport::auth::OAuthState; +use rmcp::transport::auth::{CredentialStore, OAuthState, StoredCredentials}; use rmcp::transport::AuthorizationManager; use serde::Deserialize; use std::net::SocketAddr; @@ -11,9 +13,7 @@ use std::sync::Arc; use tokio::sync::{oneshot, Mutex}; use tracing::warn; -use crate::oauth::persist::{clear_credentials, load_cached_state, save_credentials}; - -mod persist; +use crate::oauth::persist::GooseCredentialStore; const CALLBACK_TEMPLATE: &str = include_str!("oauth_callback.html"); @@ -32,18 +32,21 @@ pub async fn oauth_flow( mcp_server_url: &String, name: &String, ) -> Result { - if let Ok(oauth_state) = load_cached_state(mcp_server_url, name).await { - if let Some(authorization_manager) = oauth_state.into_authorization_manager() { - if authorization_manager.refresh_token().await.is_ok() { - return Ok(authorization_manager); - } + let credential_store = GooseCredentialStore::new(name.clone()); + let mut auth_manager = AuthorizationManager::new(mcp_server_url).await?; + auth_manager.set_credential_store(credential_store.clone()); + + if auth_manager.initialize_from_store().await? { + if auth_manager.refresh_token().await.is_ok() { + return Ok(auth_manager); } - if let Err(e) = clear_credentials(name) { + if let Err(e) = credential_store.clear().await { warn!("error clearing bad credentials: {}", e); } } + // No existing credentials or they were invalid - need to do the full oauth flow let (code_sender, code_receiver) = oneshot::channel::(); let app_state = AppState { code_receiver: Arc::new(Mutex::new(Some(code_sender))), @@ -74,6 +77,7 @@ pub async fn oauth_flow( }); let mut oauth_state = OAuthState::new(mcp_server_url, None).await?; + let redirect_uri = format!("http://localhost:{}/oauth_callback", used_addr.port()); oauth_state .start_authorization(&[], redirect_uri.as_str(), Some("goose")) @@ -91,13 +95,20 @@ pub async fn oauth_flow( } = code_receiver.await?; oauth_state.handle_callback(&auth_code, &csrf_token).await?; - if let Err(e) = save_credentials(name, &oauth_state).await { - warn!("Failed to save credentials: {}", e); - } + let (client_id, token_response) = oauth_state.get_credentials().await?; - let auth_manager = oauth_state + let mut auth_manager = oauth_state .into_authorization_manager() .ok_or_else(|| anyhow::anyhow!("Failed to get authorization manager"))?; + credential_store + .save(StoredCredentials { + client_id, + token_response, + }) + .await?; + + auth_manager.set_credential_store(credential_store); + Ok(auth_manager) } diff --git a/crates/goose/src/oauth/persist.rs b/crates/goose/src/oauth/persist.rs index 1600b84248..b0c4155ecd 100644 --- a/crates/goose/src/oauth/persist.rs +++ b/crates/goose/src/oauth/persist.rs @@ -1,71 +1,54 @@ -use oauth2::{basic::BasicTokenType, EmptyExtraTokenFields, StandardTokenResponse}; -use reqwest::IntoUrl; -use rmcp::transport::{auth::OAuthState, AuthError}; -use serde::{Deserialize, Serialize}; +use rmcp::transport::auth::{AuthError, CredentialStore, StoredCredentials}; use crate::config::Config; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SerializableCredentials { - pub client_id: String, - pub token_response: Option>, +/// Goose-specific credential store that uses the Config system +/// +/// This implementation stores OAuth credentials in the goose configuration +/// system, which handles secure storage (e.g., keychain integration). + +#[derive(Clone)] +pub struct GooseCredentialStore { + name: String, } -fn secret_key(name: &str) -> String { - format!("oauth_creds_{name}") -} +impl GooseCredentialStore { + pub fn new(name: String) -> Self { + Self { name } + } -pub async fn save_credentials( - name: &str, - oauth_state: &OAuthState, -) -> Result<(), Box> { - let config = Config::global(); - let (client_id, token_response) = oauth_state.get_credentials().await?; - - let credentials = SerializableCredentials { - client_id, - token_response, - }; - - let key = secret_key(name); - config.set_secret(&key, &credentials)?; - - Ok(()) -} - -async fn load_credentials( - name: &str, -) -> Result> { - let config = Config::global(); - let key = secret_key(name); - let credentials: SerializableCredentials = config.get_secret(&key)?; - - Ok(credentials) -} - -pub fn clear_credentials(name: &str) -> Result<(), Box> { - let config = Config::global(); - - Ok(config.delete_secret(&secret_key(name))?) -} - -pub async fn load_cached_state( - base_url: U, - name: &str, -) -> Result { - let credentials = load_credentials(name) - .await - .map_err(|e| AuthError::InternalError(format!("Failed to load credentials: {}", e)))?; - - if let Some(token_response) = credentials.token_response { - let mut oauth_state = OAuthState::new(base_url, None).await?; - oauth_state - .set_credentials(&credentials.client_id, token_response) - .await?; - Ok(oauth_state) - } else { - Err(AuthError::InternalError( - "No token response in cached credentials".to_string(), - )) + fn secret_key(&self) -> String { + format!("oauth_creds_{}", self.name) + } +} + +#[async_trait::async_trait] +impl CredentialStore for GooseCredentialStore { + async fn load(&self) -> Result, AuthError> { + let config = Config::global(); + let key = self.secret_key(); + + match config.get_secret::(&key) { + Ok(credentials) => Ok(Some(credentials)), + Err(_) => Ok(None), // No credentials found + } + } + + async fn save(&self, credentials: StoredCredentials) -> Result<(), AuthError> { + let config = Config::global(); + let key = self.secret_key(); + + config + .set_secret(&key, &credentials) + .map_err(|e| AuthError::InternalError(format!("Failed to save credentials: {}", e))) + } + + async fn clear(&self) -> Result<(), AuthError> { + let config = Config::global(); + let key = self.secret_key(); + + config + .delete_secret(&key) + .map_err(|e| AuthError::InternalError(format!("Failed to clear credentials: {}", e))) } } diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index e7e4f8a4f6..8b87f06bad 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -4643,6 +4643,10 @@ "inputSchema" ], "properties": { + "_meta": { + "type": "object", + "additionalProperties": true + }, "annotations": { "anyOf": [ { diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index 75de116e56..c1e9b62014 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -810,6 +810,9 @@ export type TokenState = { }; export type Tool = { + _meta?: { + [key: string]: unknown; + }; annotations?: ToolAnnotations | { [key: string]: unknown; }; From 3b4904ed0437bcdb78dd36db01d525924813a61e Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Mon, 17 Nov 2025 13:11:58 -0500 Subject: [PATCH 06/14] fix windows cli build (#5768) --- .github/workflows/build-cli.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 265d931490..22f2e0aead 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -200,6 +200,8 @@ jobs: ls -la target/x86_64-pc-windows-gnu/release/ " + sudo chown -R $USER:$USER target/ + # Verify build succeeded if [ ! -f "./target/x86_64-pc-windows-gnu/release/goose.exe" ]; then echo "❌ Windows CLI binary not found." From a9a326ad6845f5322074dbccf3582dd37be2fb5b Mon Sep 17 00:00:00 2001 From: Timothy <1695613+timothyis@users.noreply.github.com> Date: Mon, 17 Nov 2025 19:06:58 +0000 Subject: [PATCH 07/14] Remove empty settings card for Scheduling Engine (#5771) Signed-off-by: Timothy <1695613+timothyis@users.noreply.github.com> --- .../src/components/settings/chat/ChatSettingsSection.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ui/desktop/src/components/settings/chat/ChatSettingsSection.tsx b/ui/desktop/src/components/settings/chat/ChatSettingsSection.tsx index fbdc4e5edc..57d02b656f 100644 --- a/ui/desktop/src/components/settings/chat/ChatSettingsSection.tsx +++ b/ui/desktop/src/components/settings/chat/ChatSettingsSection.tsx @@ -40,14 +40,6 @@ export default function ChatSettingsSection() { - - - Scheduling Engine - - Choose which scheduling backend to use for scheduled recipes and tasks - - - Tool Selection Strategy (preview) From 63e9a37b13f2b6da011006ca01fac5b6dfdd4cd7 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Mon, 17 Nov 2025 11:10:47 -0800 Subject: [PATCH 08/14] Handle spaces in extension names also (#5770) --- crates/goose-mcp/src/mcp_server_runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/goose-mcp/src/mcp_server_runner.rs b/crates/goose-mcp/src/mcp_server_runner.rs index 4bc9e94c53..2f15fe53af 100644 --- a/crates/goose-mcp/src/mcp_server_runner.rs +++ b/crates/goose-mcp/src/mcp_server_runner.rs @@ -17,7 +17,7 @@ pub async fn run_mcp_server(name: &str) -> Result<()> { tracing::info!("Starting MCP server"); - match name.to_lowercase().as_str() { + match name.to_lowercase().replace(' ', "").as_str() { "autovisualiser" => serve_and_wait(AutoVisualiserRouter::new()).await, "computercontroller" => serve_and_wait(ComputerControllerServer::new()).await, "developer" => serve_and_wait(DeveloperServer::new()).await, From a885f5c3b9f32fd623832deba045eda364ea660a Mon Sep 17 00:00:00 2001 From: vlascik Date: Mon, 17 Nov 2025 20:12:56 +0100 Subject: [PATCH 09/14] fix: get win node path from registry (#5731) Signed-off-by: V. Lascik --- Cargo.lock | 13 +++++++- crates/goose-server/Cargo.toml | 4 +++ crates/goose-server/src/routes/agent.rs | 40 ++++++++++++++++++++++--- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58f8da8595..6f7976f868 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2874,6 +2874,7 @@ dependencies = [ "tracing-subscriber", "utoipa", "uuid", + "winreg 0.55.0", ] [[package]] @@ -5489,7 +5490,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.50.0", ] [[package]] @@ -8484,6 +8485,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + [[package]] name = "winsafe" version = "0.0.19" diff --git a/crates/goose-server/Cargo.toml b/crates/goose-server/Cargo.toml index 98c43f542b..a616de6489 100644 --- a/crates/goose-server/Cargo.toml +++ b/crates/goose-server/Cargo.toml @@ -39,6 +39,10 @@ reqwest = { version = "0.12.9", features = ["json", "rustls-tls", "blocking", "m tokio-util = "0.7.15" uuid = { version = "1.11", features = ["v4"] } serde_path_to_error = "0.1.20" +winreg = { version = "0.55.0", optional = true } + +[target.'cfg(windows)'.dependencies] +winreg = { version = "0.55.0" } [[bin]] name = "goosed" diff --git a/crates/goose-server/src/routes/agent.rs b/crates/goose-server/src/routes/agent.rs index 90c6db9a51..3d1be23ae2 100644 --- a/crates/goose-server/src/routes/agent.rs +++ b/crates/goose-server/src/routes/agent.rs @@ -482,13 +482,45 @@ async fn agent_add_extension( State(state): State>, Json(request): Json, ) -> Result { - if cfg!(target_os = "windows") { + #[cfg(windows)] + { + use winreg::enums::{HKEY_LOCAL_MACHINE, KEY_READ}; + use winreg::RegKey; + if let ExtensionConfig::Stdio { cmd, .. } = &request.config { if cmd.ends_with("npx.cmd") || cmd.ends_with("npx") { - let node_exists = std::path::Path::new(r"C:\Program Files\nodejs\node.exe") + let mut node_exists = std::path::Path::new(r"C:\Program Files\nodejs\node.exe") .exists() || std::path::Path::new(r"C:\Program Files (x86)\nodejs\node.exe").exists(); + // Also check Windows registry: HKEY_LOCAL_MACHINE\\SOFTWARE\\Node.js InstallPath + if !node_exists { + // Try 64-bit view first, then 32-bit. Use open_subkey_with_flags to avoid WOW64 redirection issues. + let install_path_from_reg: Option = (|| { + let hk_local_machine = RegKey::predef(HKEY_LOCAL_MACHINE); + // Common keys to try + let keys = vec!["SOFTWARE\\Node.js", "SOFTWARE\\WOW6432Node\\Node.js"]; + for k in keys.iter() { + if let Ok(subkey) = hk_local_machine.open_subkey_with_flags(k, KEY_READ) + { + if let Ok(val) = subkey.get_value::("InstallPath") { + if !val.trim().is_empty() { + return Some(val); + } + } + } + } + None + })(); + + if let Some(path_str) = install_path_from_reg { + let node_path = std::path::Path::new(&path_str).join("node.exe"); + if node_path.exists() { + node_exists = true; + } + } + } + if !node_exists { let cmd_path = std::path::Path::new(&cmd); let script_dir = cmd_path @@ -497,7 +529,7 @@ async fn agent_add_extension( let install_script = script_dir.join("install-node.cmd"); if install_script.exists() { - eprintln!("Installing Node.js..."); + eprintln!("Node.js not found on the system, installing Node.js..."); let output = std::process::Command::new(&install_script) .arg("https://nodejs.org/dist/v23.10.0/node-v23.10.0-x64.msi") .output() @@ -513,7 +545,7 @@ async fn agent_add_extension( } } else { return Err(ErrorResponse::internal(format!( - "Node.js not detected and no installer script not found at: {}", + "Node.js not found on the system, and no installer script found at: {}", install_script.display() ))); } From 56e3be26daf39b860f16220a3a46f56849e8b2a5 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Mon, 17 Nov 2025 11:16:24 -0800 Subject: [PATCH 10/14] Don't show update tray icon if GOOSE_VERSION is set (#5750) --- ui/desktop/src/utils/autoUpdater.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/desktop/src/utils/autoUpdater.ts b/ui/desktop/src/utils/autoUpdater.ts index 9495c9d190..ee7e55d053 100644 --- a/ui/desktop/src/utils/autoUpdater.ts +++ b/ui/desktop/src/utils/autoUpdater.ts @@ -469,6 +469,10 @@ function sendStatusToWindow(event: string, data?: unknown) { function updateTrayIcon(hasUpdate: boolean) { if (!trayRef) return; + if (process.env.GOOSE_VERSION) { + hasUpdate = false; + } + const isDev = !app.isPackaged; let iconPath: string; From 169135948b2fb1a59ec5d7b23b8603d0adbabe28 Mon Sep 17 00:00:00 2001 From: BestCodes <106822363+The-Best-Codes@users.noreply.github.com> Date: Mon, 17 Nov 2025 14:04:36 -0600 Subject: [PATCH 11/14] fix: add one-time cleanup for linux hermit locking issues (#5742) Signed-off-by: The-Best-Codes --- ui/desktop/src/bin/node-setup-common.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ui/desktop/src/bin/node-setup-common.sh b/ui/desktop/src/bin/node-setup-common.sh index 16eee68547..2bf036b1b2 100755 --- a/ui/desktop/src/bin/node-setup-common.sh +++ b/ui/desktop/src/bin/node-setup-common.sh @@ -23,6 +23,18 @@ trap 'log "An error occurred. Exiting with status $?."' ERR log "Starting node setup (common)." +# One-time cleanup for existing Linux users to fix locking issues +CLEANUP_MARKER="~/.config/goose/.mcp-hermit-cleanup-v1" +if [[ "$(uname -s)" == "Linux" ]] && [ ! -f "$CLEANUP_MARKER" ]; then + log "Performing one-time cleanup of old mcp-hermit directory to fix locking issues." + if [ -d ~/.config/goose/mcp-hermit ]; then + rm -rf ~/.config/goose/mcp-hermit + log "Removed old mcp-hermit directory." + fi + touch "$CLEANUP_MARKER" + log "Cleanup completed. Marker file created." +fi + # Ensure ~/.config/goose/mcp-hermit/bin exists log "Creating directory ~/.config/goose/mcp-hermit/bin if it does not exist." mkdir -p ~/.config/goose/mcp-hermit/bin From 4494ba773f46951468c7530f080d8e6e7ae7051e Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Mon, 17 Nov 2025 13:14:46 -0800 Subject: [PATCH 12/14] Bump auto scroll threshold (#5738) --- ui/desktop/src/components/ui/scroll-area.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/desktop/src/components/ui/scroll-area.tsx b/ui/desktop/src/components/ui/scroll-area.tsx index 43ce277339..5fd46161fb 100644 --- a/ui/desktop/src/components/ui/scroll-area.tsx +++ b/ui/desktop/src/components/ui/scroll-area.tsx @@ -46,7 +46,7 @@ const ScrollArea = React.forwardRef( const isActivelyScrollingRef = React.useRef(false); const scrollTimeoutRef = React.useRef(null); - const BOTTOM_SCROLL_THRESHOLD = 100; + const BOTTOM_SCROLL_THRESHOLD = 200; const isAtBottom = React.useCallback(() => { if (!viewportRef.current) return false; From 5b2871048f1fbb698dd30b23e5c1552866b69424 Mon Sep 17 00:00:00 2001 From: dianed-square <73617011+dianed-square@users.noreply.github.com> Date: Mon, 17 Nov 2025 20:51:12 -0800 Subject: [PATCH 13/14] docs: quick launcher (#5779) --- .../guides/sessions/session-management.md | 18 +++++++-- documentation/docs/guides/tips.md | 39 ++++++++++--------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/documentation/docs/guides/sessions/session-management.md b/documentation/docs/guides/sessions/session-management.md index a7d38fcc38..3a8199eafd 100644 --- a/documentation/docs/guides/sessions/session-management.md +++ b/documentation/docs/guides/sessions/session-management.md @@ -49,15 +49,25 @@ In your first session, goose prompts you to [set up an LLM (Large Language Model On macOS, you can drag and drop a folder onto the goose icon in the dock to open a new session in that directory. ::: - You can also use keyboard shortcuts to start a new session or bring focus to open goose windows. + You can also use keyboard shortcuts to start a new session or manage goose windows. | Action | macOS | Windows/Linux | |--------|-------|---------------| - | New Session in Current Directory | `Cmd+N` | `Ctrl+N` | - | New Session in Different Directory | `Cmd+O` | `Ctrl+O` | - | Focus goose Window | `Cmd+Option+Shift+G` | `Ctrl+Alt+Shift+G` | + | New Session with [Quick Launcher](#quick-launcher) | `Cmd+Option+Shift+G` | `Ctrl+Alt+Shift+G` | + | New Session in Current Directory | `Cmd+N` | `Ctrl+N` | + | New Session in Current Directory (Same Window) | `Cmd+T` | `Ctrl+T` | + | New Session in Different Directory | `Cmd+O` | `Ctrl+O` | | Toggle Sidebar | `Cmd+B` | `Ctrl+B` | + | Open Settings | `Cmd+,` | `Ctrl+,` | | Keep goose Window Always on Top | `Cmd+Shift+T` | `Ctrl+Shift+T` | + +
+ #### Quick Launcher + Start a new session by typing your prompt into a popup: + 1. Press `Cmd+Option+Shift+G` (macOS) or `Ctrl+Alt+Shift+G` (Windows/Linux) to open the popup + 2. Type your prompt and press `Enter` + + The session opens to your most recently opened directory in a new goose window. diff --git a/documentation/docs/guides/tips.md b/documentation/docs/guides/tips.md index 7b1b29c0a5..d2f9187288 100644 --- a/documentation/docs/guides/tips.md +++ b/documentation/docs/guides/tips.md @@ -1,42 +1,45 @@ --- -title: Quick Goose Tips +title: Quick goose Tips sidebar_position: 30 sidebar_label: Quick Tips -description: Best practices for working with Goose +description: Best practices for working with goose --- -### Goose works on your behalf -Goose is an AI agent, which means you can prompt Goose to perform tasks for you like opening applications, running shell commands, automating workflows, writing code, browsing the web, and more. +### goose works on your behalf +goose is an AI agent, which means you can prompt goose to perform tasks for you like opening applications, running shell commands, automating workflows, writing code, browsing the web, and more. -### Prompt Goose using natural language -You don't need fancy language or special syntax to prompt Goose. Talk with Goose like you would talk to a friend. You can even use slang or say please and thank you; Goose will understand. +### Prompt goose using natural language +You don't need fancy language or special syntax to prompt goose. Talk with goose like you would talk to a friend. You can even use slang or say please and thank you; goose will understand. -### Extend Goose's capabilities to any application -Goose's capabilities are extensible. As an [MCP](https://modelcontextprotocol.io/) client, Goose can connect to your apps and services through [extensions](/extensions), allowing it to work across your entire workflow. +### Extend goose's capabilities to any application +goose's capabilities are extensible. As an [MCP](https://modelcontextprotocol.io/) client, goose can connect to your apps and services through [extensions](/extensions), allowing it to work across your entire workflow. -### Choose how much control Goose has -You can customize how much [supervision](/docs/guides/goose-permissions) Goose needs. Choose between full autonomy, requiring approval before actions, or simply chatting without any actions. +### Choose how much control goose has +You can customize how much [supervision](/docs/guides/goose-permissions) goose needs. Choose between full autonomy, requiring approval before actions, or simply chatting without any actions. ### Choose the right LLM -Your experience with Goose is shaped by your [choice of LLM](/blog/2025/03/31/goose-benchmark), as it handles all the planning while Goose manages the execution. When choosing an LLM, consider its tool support, specific capabilities, and associated costs. +Your experience with goose is shaped by your [choice of LLM](/blog/2025/03/31/goose-benchmark), as it handles all the planning while goose manages the execution. When choosing an LLM, consider its tool support, specific capabilities, and associated costs. ### Keep sessions short LLMs have context windows, which are limits on how much conversation history they can retain. Once exceeded, they may forget earlier parts of the conversation. Monitor your token usage and [start new sessions](/docs/guides/sessions/session-management) as needed. +### Use Quick Launcher for faster session starts +Press `Cmd+Option+Shift+G` (macOS) or `Ctrl+Alt+Shift+G` (Windows/Linux) and send a prompt to start a new session instantly. + ### Turn off unnecessary extensions or tool 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 -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. +### Teach goose your preferences +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 -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. ### Version Control Commit your code changes early and often. This allows you to rollback any unexpected changes. -### Control which extensions Goose can use -Administrators can use an [allowlist](/docs/guides/allowlist) to restrict Goose to approved extensions only. This helps prevent risky installs from unknown MCP servers. +### Control which extensions goose can use +Administrators can use an [allowlist](/docs/guides/allowlist) to restrict goose to approved extensions only. This helps prevent risky installs from unknown MCP servers. ### Set up starter templates You can turn a successful session into a reusable "[recipe](/docs/guides/recipes/session-recipes)" to share with others or use again later—no need to start from scratch. @@ -44,8 +47,8 @@ You can turn a successful session into a reusable "[recipe](/docs/guides/recipes ### Embrace an experimental mindset You don’t need to get it right the first time. Iterating on prompts and tools is part of the workflow. -### Keep Goose updated -Regularly [update](/docs/guides/updating-goose) Goose to benefit from the latest features, bug fixes, and performance improvements. +### Keep goose updated +Regularly [update](/docs/guides/updating-goose) goose to benefit from the latest features, bug fixes, and performance improvements. ### Pair Two Models to Save Money Use [lead/worker model](/docs/tutorials/lead-worker/) to have goose use a "lead" model for early planning before handing the task to a lower-cost "worker" model for execution. From 9ac8c22f2e6165fded989b34e35efd0dff285603 Mon Sep 17 00:00:00 2001 From: Manik Surtani Date: Tue, 18 Nov 2025 17:54:26 +1100 Subject: [PATCH 14/14] chore: incorporate LF feedback (#5787) --- GOVERNANCE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index e7ad1aec5c..a9e5770999 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -122,6 +122,7 @@ If a Core Maintainer leaves for any reason: * The remaining Core Maintainers should appoint a replacement within 30 days. * If Core Maintainer count falls below three, appointment of new Core Maintainers becomes a priority and appointment must happen within 15 days. No major decisions will be made until a new Core Maintainer is appointed. +* If there is no qualified developer available who is willing to serve as a Core Maintainer, the remaining Core Maintainer(s) shall instead create and adopt a plan for recruiting and mentoring a new Core Maintainer. * Until a replacement is appointed, remaining Core Maintainers continue governance responsibilities ## Communication