From e6a51901a73087797f6a4cdae892e1193b65a0e8 Mon Sep 17 00:00:00 2001 From: Abhi <43648792+abhipatel12@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:50:22 -0500 Subject: [PATCH] feat(skills): promote Agent Skills to stable (#17693) --- .gemini/settings.json | 5 - docs/cli/commands.md | 4 +- docs/cli/index.md | 4 +- docs/cli/settings.md | 7 +- docs/cli/skills.md | 4 - docs/cli/tutorials/skills-getting-started.md | 37 +--- docs/extensions/getting-started-extensions.md | 2 - docs/get-started/configuration.md | 10 +- docs/index.md | 4 +- docs/sidebar.json | 25 ++- docs/tools/index.md | 6 +- packages/cli/src/config/config.test.ts | 36 ++++ packages/cli/src/config/config.ts | 5 +- packages/cli/src/config/settingsSchema.ts | 10 -- .../skills-backward-compatibility.test.ts | 158 ------------------ .../src/services/BuiltinCommandLoader.test.ts | 6 +- packages/core/src/config/config.ts | 2 +- schemas/settings.schema.json | 7 - 18 files changed, 79 insertions(+), 253 deletions(-) delete mode 100644 .gemini/settings.json delete mode 100644 packages/cli/src/config/skills-backward-compatibility.test.ts diff --git a/.gemini/settings.json b/.gemini/settings.json deleted file mode 100644 index aaac8204b6..0000000000 --- a/.gemini/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "experimental": { - "skills": true - } -} diff --git a/docs/cli/commands.md b/docs/cli/commands.md index 886a3d4669..f169c3d356 100644 --- a/docs/cli/commands.md +++ b/docs/cli/commands.md @@ -205,8 +205,8 @@ Slash commands provide meta-level control over the CLI itself. while others require a restart. - [**`/skills`**](./skills.md) - - **Description:** (Experimental) Manage Agent Skills, which provide on-demand - expertise and specialized workflows. + - **Description:** Manage Agent Skills, which provide on-demand expertise and + specialized workflows. - **Sub-commands:** - **`list`**: - **Description:** List all discovered skills and their current status diff --git a/docs/cli/index.md b/docs/cli/index.md index 4c5f7eac8a..437038d478 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -29,8 +29,8 @@ overview of Gemini CLI, see the [main documentation page](../index.md). in an enterprise environment. - **[Sandboxing](./sandbox.md):** Isolate tool execution in a secure, containerized environment. -- **[Agent Skills](./skills.md):** (Experimental) Extend the CLI with - specialized expertise and procedural workflows. +- **[Agent Skills](./skills.md):** Extend the CLI with specialized expertise and + procedural workflows. - **[Telemetry](./telemetry.md):** Configure observability to monitor usage and performance. - **[Token caching](./token-caching.md):** Optimize API costs by caching tokens. diff --git a/docs/cli/settings.md b/docs/cli/settings.md index 9c6af44ecc..d381bd35a9 100644 --- a/docs/cli/settings.md +++ b/docs/cli/settings.md @@ -115,13 +115,18 @@ they appear in the UI. | UI Label | Setting | Description | Default | | ----------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------- | -| Agent Skills | `experimental.skills` | Enable Agent Skills (experimental). | `false` | | Enable Codebase Investigator | `experimental.codebaseInvestigatorSettings.enabled` | Enable the Codebase Investigator agent. | `true` | | Codebase Investigator Max Num Turns | `experimental.codebaseInvestigatorSettings.maxNumTurns` | Maximum number of turns for the Codebase Investigator agent. | `10` | | Use OSC 52 Paste | `experimental.useOSC52Paste` | Use OSC 52 sequence for pasting instead of clipboardy (useful for remote sessions). | `false` | | Enable CLI Help Agent | `experimental.cliHelpAgentSettings.enabled` | Enable the CLI Help Agent. | `true` | | Plan | `experimental.plan` | Enable planning features (Plan Mode and tools). | `false` | +### Skills + +| UI Label | Setting | Description | Default | +| ------------------- | ---------------- | -------------------- | ------- | +| Enable Agent Skills | `skills.enabled` | Enable Agent Skills. | `true` | + ### HooksConfig | UI Label | Setting | Description | Default | diff --git a/docs/cli/skills.md b/docs/cli/skills.md index 448734a14d..0b001b017e 100644 --- a/docs/cli/skills.md +++ b/docs/cli/skills.md @@ -1,9 +1,5 @@ # Agent Skills -_Note: This is an experimental feature enabled via `experimental.skills`. You -can also search for "Skills" within the `/settings` interactive UI to toggle -this and manage other skill-related settings._ - Agent Skills allow you to extend Gemini CLI with specialized expertise, procedural workflows, and task-specific resources. Based on the [Agent Skills](https://agentskills.io) open standard, a "skill" is a diff --git a/docs/cli/tutorials/skills-getting-started.md b/docs/cli/tutorials/skills-getting-started.md index f559f6b1e5..236424b393 100644 --- a/docs/cli/tutorials/skills-getting-started.md +++ b/docs/cli/tutorials/skills-getting-started.md @@ -1,37 +1,10 @@ # Getting Started with Agent Skills Agent Skills allow you to extend Gemini CLI with specialized expertise. This -tutorial will guide you through creating your first skill, enabling it, and -using it in a session. +tutorial will guide you through creating your first skill and using it in a +session. -## 1. Enable Agent Skills - -Agent Skills are currently an experimental feature and must be enabled in your -settings. - -### Via the interactive UI - -1. Start a Gemini CLI session by running `gemini`. -2. Type `/settings` to open the interactive settings dialog. -3. Search for "Skills". -4. Toggle **Agent Skills** to `true`. -5. Press `Esc` to save and exit. You may need to restart the CLI for the - changes to take effect. - -### Via `settings.json` - -Alternatively, you can manually edit your global settings file at -`~/.gemini/settings.json` (create it if it doesn't exist): - -```json -{ - "experimental": { - "skills": true - } -} -``` - -## 2. Create Your First Skill +## 1. Create your first skill A skill is a directory containing a `SKILL.md` file. Let's create an **API Auditor** skill that helps you verify if local or remote endpoints are @@ -86,7 +59,7 @@ responding correctly. .catch((e) => console.error(`Result: Failed (${e.message})`)); ``` -## 3. Verify the Skill is Discovered +## 2. Verify the skill is discovered Use the `/skills` slash command (or `gemini skills list` from your terminal) to see if Gemini CLI has found your new skill. @@ -99,7 +72,7 @@ In a Gemini CLI session: You should see `api-auditor` in the list of available skills. -## 4. Use the Skill in a Chat +## 3. Use the skill in a chat Now, let's see the skill in action. Start a new session and ask a question about an endpoint. diff --git a/docs/extensions/getting-started-extensions.md b/docs/extensions/getting-started-extensions.md index 04e5987c85..b26f156f22 100644 --- a/docs/extensions/getting-started-extensions.md +++ b/docs/extensions/getting-started-extensions.md @@ -224,8 +224,6 @@ file in every session where the extension is active. ## (Optional) Step 6: Add an Agent Skill -_Note: This is an experimental feature enabled via `experimental.skills`._ - [Agent Skills](../cli/skills.md) let you bundle specialized expertise and procedural workflows. Unlike `GEMINI.md`, which provides persistent context, skills are activated only when needed, saving context tokens. diff --git a/docs/get-started/configuration.md b/docs/get-started/configuration.md index d62ee18bba..6d878f877b 100644 --- a/docs/get-started/configuration.md +++ b/docs/get-started/configuration.md @@ -850,11 +850,6 @@ their corresponding top-level category object in your `settings.json` file. - **Default:** `false` - **Requires restart:** Yes -- **`experimental.skills`** (boolean): - - **Description:** Enable Agent Skills (experimental). - - **Default:** `false` - - **Requires restart:** Yes - - **`experimental.codebaseInvestigatorSettings.enabled`** (boolean): - **Description:** Enable the Codebase Investigator agent. - **Default:** `true` @@ -899,6 +894,11 @@ their corresponding top-level category object in your `settings.json` file. #### `skills` +- **`skills.enabled`** (boolean): + - **Description:** Enable Agent Skills. + - **Default:** `true` + - **Requires restart:** Yes + - **`skills.disabled`** (array): - **Description:** List of disabled skills. - **Default:** `[]` diff --git a/docs/index.md b/docs/index.md index 217fba8391..1d123ad0cd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -56,8 +56,8 @@ This documentation is organized into the following sections: commands with `/model`. - **[Sandbox](./cli/sandbox.md):** Isolate tool execution in a secure, containerized environment. -- **[Agent Skills](./cli/skills.md):** (Experimental) Extend the CLI with - specialized expertise and procedural workflows. +- **[Agent Skills](./cli/skills.md):** Extend the CLI with specialized expertise + and procedural workflows. - **[Settings](./cli/settings.md):** Configure various aspects of the CLI's behavior and appearance with `/settings`. - **[Telemetry](./cli/telemetry.md):** Overview of telemetry in the CLI. diff --git a/docs/sidebar.json b/docs/sidebar.json index a65bade052..9409fc5742 100644 --- a/docs/sidebar.json +++ b/docs/sidebar.json @@ -204,21 +204,18 @@ { "label": "Hooks", "items": [ + { "label": "Getting started", "slug": "docs/hooks/getting-started" }, + { "label": "Hook types", "slug": "docs/hooks/hook-types" }, + { "label": "API reference", "slug": "docs/hooks/api-reference" } + ] + }, + { + "label": "Ecosystem and extensibility", + "items": [ + { "label": "Agent skills", "slug": "docs/cli/skills" }, { - "label": "Introduction", - "slug": "docs/hooks" - }, - { - "label": "Writing hooks", - "slug": "docs/hooks/writing-hooks" - }, - { - "label": "Hooks reference", - "slug": "docs/hooks/reference" - }, - { - "label": "Best practices", - "slug": "docs/hooks/best-practices" + "label": "Sub-agents (experimental)", + "slug": "docs/core/subagents" } ] }, diff --git a/docs/tools/index.md b/docs/tools/index.md index 0434046ac4..c21c3dc610 100644 --- a/docs/tools/index.md +++ b/docs/tools/index.md @@ -91,8 +91,8 @@ Additionally, these tools incorporate: - **[MCP servers](./mcp-server.md)**: MCP servers act as a bridge between the Gemini model and your local environment or other services like APIs. -- **[Agent Skills](../cli/skills.md)**: (Experimental) On-demand expertise - packages that are activated via the `activate_skill` tool to provide - specialized guidance and resources. +- **[Agent Skills](../cli/skills.md)**: On-demand expertise packages that are + activated via the `activate_skill` tool to provide specialized guidance and + resources. - **[Sandboxing](../cli/sandbox.md)**: Sandboxing isolates the model and its changes from your environment to reduce potential risk. diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts index a1ffeb0c36..af69b7d880 100644 --- a/packages/cli/src/config/config.test.ts +++ b/packages/cli/src/config/config.test.ts @@ -534,6 +534,42 @@ describe('parseArguments', () => { 'Use whoami to write a poem in file poem.md about my username in pig latin and use wc to tell me how many lines are in the poem you wrote.', ); }); + + it('should set isCommand to true for mcp command', async () => { + process.argv = ['node', 'script.js', 'mcp', 'list']; + const argv = await parseArguments(createTestMergedSettings()); + expect(argv.isCommand).toBe(true); + }); + + it('should set isCommand to true for extensions command', async () => { + process.argv = ['node', 'script.js', 'extensions', 'list']; + // Extensions command uses experimental settings + const settings = createTestMergedSettings({ + experimental: { extensionManagement: true }, + }); + const argv = await parseArguments(settings); + expect(argv.isCommand).toBe(true); + }); + + it('should set isCommand to true for skills command', async () => { + process.argv = ['node', 'script.js', 'skills', 'list']; + // Skills command enabled by default or via experimental + const settings = createTestMergedSettings({ + skills: { enabled: true }, + }); + const argv = await parseArguments(settings); + expect(argv.isCommand).toBe(true); + }); + + it('should set isCommand to true for hooks command', async () => { + process.argv = ['node', 'script.js', 'hooks', 'migrate']; + // Hooks command enabled via tools settings + const settings = createTestMergedSettings({ + tools: { enableHooks: true }, + }); + const argv = await parseArguments(settings); + expect(argv.isCommand).toBe(true); + }); }); describe('loadCliConfig', () => { diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index b049ae6d92..4d404cde69 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -289,7 +289,7 @@ export async function parseArguments( yargsInstance.command(extensionsCommand); } - if (settings.experimental?.skills || (settings.skills?.enabled ?? true)) { + if (settings.skills?.enabled ?? true) { yargsInstance.command(skillsCommand); } // Register hooks command if hooks are enabled @@ -725,8 +725,7 @@ export async function loadCliConfig( plan: settings.experimental?.plan, enableEventDrivenScheduler: settings.experimental?.enableEventDrivenScheduler, - skillsSupport: - settings.experimental?.skills || (settings.skills?.enabled ?? true), + skillsSupport: settings.skills?.enabled ?? true, disabledSkills: settings.skills?.disabled, experimentalJitContext: settings.experimental?.jitContext, noBrowser: !!process.env['NO_BROWSER'], diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index 3a953309dd..d462f6e051 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -1452,15 +1452,6 @@ const SETTINGS_SCHEMA = { description: 'Enable Just-In-Time (JIT) context loading.', showInDialog: false, }, - skills: { - type: 'boolean', - label: 'Agent Skills', - category: 'Experimental', - requiresRestart: true, - default: false, - description: 'Enable Agent Skills (experimental).', - showInDialog: true, - }, codebaseInvestigatorSettings: { type: 'object', label: 'Codebase Investigator Settings', @@ -1615,7 +1606,6 @@ const SETTINGS_SCHEMA = { default: true, description: 'Enable Agent Skills.', showInDialog: true, - ignoreInDocs: true, }, disabled: { type: 'array', diff --git a/packages/cli/src/config/skills-backward-compatibility.test.ts b/packages/cli/src/config/skills-backward-compatibility.test.ts deleted file mode 100644 index 57a2d1e4d0..0000000000 --- a/packages/cli/src/config/skills-backward-compatibility.test.ts +++ /dev/null @@ -1,158 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; -import { loadCliConfig, parseArguments } from './config.js'; -import * as trustedFolders from './trustedFolders.js'; -import { loadServerHierarchicalMemory } from '@google/gemini-cli-core'; -import { type Settings, createTestMergedSettings } from './settings.js'; - -vi.mock('./trustedFolders.js'); -vi.mock('@google/gemini-cli-core', async (importOriginal) => { - const actual = - await importOriginal(); - return { - ...actual, - loadServerHierarchicalMemory: vi.fn(), - getPty: vi.fn().mockResolvedValue({ name: 'test-pty' }), - getVersion: vi.fn().mockResolvedValue('0.0.0-test'), - }; -}); - -describe('Agent Skills Backward Compatibility', () => { - const originalArgv = process.argv; - - beforeEach(() => { - vi.resetAllMocks(); - vi.mocked(trustedFolders.isWorkspaceTrusted).mockReturnValue({ - isTrusted: true, - } as unknown as trustedFolders.TrustResult); - }); - - afterEach(() => { - process.argv = originalArgv; - }); - - describe('loadCliConfig', () => { - it('should default skillsSupport to true when no settings are present', async () => { - vi.mocked(loadServerHierarchicalMemory).mockResolvedValue({ - memoryContent: '', - fileCount: 0, - filePaths: [], - }); - - process.argv = ['node', 'gemini']; - const settings = createTestMergedSettings({}); - const config = await loadCliConfig( - settings, - 'test-session', - await parseArguments(settings), - ); - expect( - ( - config as unknown as { isSkillsSupportEnabled: () => boolean } - ).isSkillsSupportEnabled(), - ).toBe(true); - }); - - it('should prioritize skills.enabled=false from settings', async () => { - vi.mocked(loadServerHierarchicalMemory).mockResolvedValue({ - memoryContent: '', - fileCount: 0, - filePaths: [], - }); - - const settings = createTestMergedSettings({ - skills: { enabled: false }, - } as unknown as Settings); - - process.argv = ['node', 'gemini']; - const config = await loadCliConfig( - settings, - 'test-session', - await parseArguments(settings), - ); - expect( - ( - config as unknown as { isSkillsSupportEnabled: () => boolean } - ).isSkillsSupportEnabled(), - ).toBe(false); - }); - - it('should support legacy experimental.skills=true from settings', async () => { - vi.mocked(loadServerHierarchicalMemory).mockResolvedValue({ - memoryContent: '', - fileCount: 0, - filePaths: [], - }); - - const settings = createTestMergedSettings({ - experimental: { skills: true }, - } as unknown as Settings); - - process.argv = ['node', 'gemini']; - const config = await loadCliConfig( - settings, - 'test-session', - await parseArguments(settings), - ); - expect( - ( - config as unknown as { isSkillsSupportEnabled: () => boolean } - ).isSkillsSupportEnabled(), - ).toBe(true); - }); - - it('should prioritize legacy experimental.skills=true over new skills.enabled=false', async () => { - vi.mocked(loadServerHierarchicalMemory).mockResolvedValue({ - memoryContent: '', - fileCount: 0, - filePaths: [], - }); - - const settings = createTestMergedSettings({ - skills: { enabled: false }, - experimental: { skills: true }, - } as unknown as Settings); - - process.argv = ['node', 'gemini']; - const config = await loadCliConfig( - settings, - 'test-session', - await parseArguments(settings), - ); - expect( - ( - config as unknown as { isSkillsSupportEnabled: () => boolean } - ).isSkillsSupportEnabled(), - ).toBe(true); - }); - - it('should still be enabled by default if legacy experimental.skills is false (since new default is true)', async () => { - vi.mocked(loadServerHierarchicalMemory).mockResolvedValue({ - memoryContent: '', - fileCount: 0, - filePaths: [], - }); - - const settings = createTestMergedSettings({ - experimental: { skills: false }, - } as unknown as Settings); - - process.argv = ['node', 'gemini']; - const config = await loadCliConfig( - settings, - 'test-session', - await parseArguments(settings), - ); - expect( - ( - config as unknown as { isSkillsSupportEnabled: () => boolean } - ).isSkillsSupportEnabled(), - ).toBe(true); - }); - }); -}); diff --git a/packages/cli/src/services/BuiltinCommandLoader.test.ts b/packages/cli/src/services/BuiltinCommandLoader.test.ts index 44ddaeb039..2740d9ed3e 100644 --- a/packages/cli/src/services/BuiltinCommandLoader.test.ts +++ b/packages/cli/src/services/BuiltinCommandLoader.test.ts @@ -119,11 +119,12 @@ describe('BuiltinCommandLoader', () => { getEnableHooks: () => false, getEnableHooksUI: () => false, getExtensionsEnabled: vi.fn().mockReturnValue(true), - isSkillsSupportEnabled: vi.fn().mockReturnValue(false), + isSkillsSupportEnabled: vi.fn().mockReturnValue(true), isAgentsEnabled: vi.fn().mockReturnValue(false), getMcpEnabled: vi.fn().mockReturnValue(true), getSkillManager: vi.fn().mockReturnValue({ getAllSkills: vi.fn().mockReturnValue([]), + isAdminEnabled: vi.fn().mockReturnValue(true), }), } as unknown as Config; @@ -260,11 +261,12 @@ describe('BuiltinCommandLoader profile', () => { getEnableHooks: () => false, getEnableHooksUI: () => false, getExtensionsEnabled: vi.fn().mockReturnValue(true), - isSkillsSupportEnabled: vi.fn().mockReturnValue(false), + isSkillsSupportEnabled: vi.fn().mockReturnValue(true), isAgentsEnabled: vi.fn().mockReturnValue(false), getMcpEnabled: vi.fn().mockReturnValue(true), getSkillManager: vi.fn().mockReturnValue({ getAllSkills: vi.fn().mockReturnValue([]), + isAdminEnabled: vi.fn().mockReturnValue(true), }), } as unknown as Config; }); diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index 8d72082c08..3a6bc97ad3 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -634,7 +634,7 @@ export class Config { this.planEnabled = params.plan ?? false; this.enableEventDrivenScheduler = params.enableEventDrivenScheduler ?? false; - this.skillsSupport = params.skillsSupport ?? false; + this.skillsSupport = params.skillsSupport ?? true; this.disabledSkills = params.disabledSkills ?? []; this.adminSkillsEnabled = params.adminSkillsEnabled ?? true; this.modelAvailabilityService = new ModelAvailabilityService(); diff --git a/schemas/settings.schema.json b/schemas/settings.schema.json index cb8d3a2311..c8e56ffda4 100644 --- a/schemas/settings.schema.json +++ b/schemas/settings.schema.json @@ -1421,13 +1421,6 @@ "default": false, "type": "boolean" }, - "skills": { - "title": "Agent Skills", - "description": "Enable Agent Skills (experimental).", - "markdownDescription": "Enable Agent Skills (experimental).\n\n- Category: `Experimental`\n- Requires restart: `yes`\n- Default: `false`", - "default": false, - "type": "boolean" - }, "codebaseInvestigatorSettings": { "title": "Codebase Investigator Settings", "description": "Configuration for Codebase Investigator.",