From ff6547857ef8a064636b33b6bb22e216fa68c559 Mon Sep 17 00:00:00 2001 From: Abhi <43648792+abhipatel12@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:29:12 -0500 Subject: [PATCH] revert: promote Agent Skills to stable (#17693) (#17712) --- .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/writing-extensions.md | 2 + docs/get-started/configuration.md | 10 +- docs/index.md | 4 +- docs/sidebar.json | 2 +- docs/tools/index.md | 6 +- packages/cli/src/config/config.test.ts | 2 +- 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, 241 insertions(+), 34 deletions(-) create mode 100644 .gemini/settings.json create mode 100644 packages/cli/src/config/skills-backward-compatibility.test.ts diff --git a/.gemini/settings.json b/.gemini/settings.json new file mode 100644 index 0000000000..aaac8204b6 --- /dev/null +++ b/.gemini/settings.json @@ -0,0 +1,5 @@ +{ + "experimental": { + "skills": true + } +} diff --git a/docs/cli/commands.md b/docs/cli/commands.md index ec9b2396fe..67f5afe7ae 100644 --- a/docs/cli/commands.md +++ b/docs/cli/commands.md @@ -199,8 +199,8 @@ Slash commands provide meta-level control over the CLI itself. while others require a restart. - [**`/skills`**](./skills.md) - - **Description:** Manage Agent Skills, which provide on-demand expertise and - specialized workflows. + - **Description:** (Experimental) 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 437038d478..4c5f7eac8a 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):** Extend the CLI with specialized expertise and - procedural workflows. +- **[Agent Skills](./skills.md):** (Experimental) 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 bd0b682bc8..54dcec335e 100644 --- a/docs/cli/settings.md +++ b/docs/cli/settings.md @@ -117,15 +117,10 @@ they appear in the UI. | UI Label | Setting | Description | Default | | ---------------- | ---------------------------- | ----------------------------------------------------------------------------------- | ------- | +| Agent Skills | `experimental.skills` | Enable Agent Skills (experimental). | `false` | | Use OSC 52 Paste | `experimental.useOSC52Paste` | Use OSC 52 sequence for pasting instead of clipboardy (useful for remote sessions). | `false` | | 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 0b001b017e..448734a14d 100644 --- a/docs/cli/skills.md +++ b/docs/cli/skills.md @@ -1,5 +1,9 @@ # 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 236424b393..f559f6b1e5 100644 --- a/docs/cli/tutorials/skills-getting-started.md +++ b/docs/cli/tutorials/skills-getting-started.md @@ -1,10 +1,37 @@ # 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 and using it in a -session. +tutorial will guide you through creating your first skill, enabling it, and +using it in a session. -## 1. Create your first skill +## 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 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 @@ -59,7 +86,7 @@ responding correctly. .catch((e) => console.error(`Result: Failed (${e.message})`)); ``` -## 2. Verify the skill is discovered +## 3. 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. @@ -72,7 +99,7 @@ In a Gemini CLI session: You should see `api-auditor` in the list of available skills. -## 3. Use the skill in a chat +## 4. 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/writing-extensions.md b/docs/extensions/writing-extensions.md index 0601d02231..3ed00fc577 100644 --- a/docs/extensions/writing-extensions.md +++ b/docs/extensions/writing-extensions.md @@ -225,6 +225,8 @@ 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 32a1d63186..fac2845d60 100644 --- a/docs/get-started/configuration.md +++ b/docs/get-started/configuration.md @@ -861,6 +861,11 @@ 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.useOSC52Paste`** (boolean): - **Description:** Use OSC 52 sequence for pasting instead of clipboardy (useful for remote sessions). @@ -873,11 +878,6 @@ 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 e3ffb128db..3669b961ad 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):** Extend the CLI with specialized expertise - and procedural workflows. +- **[Agent Skills](./cli/skills.md):** (Experimental) 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 204c719f33..5ed22f693f 100644 --- a/docs/sidebar.json +++ b/docs/sidebar.json @@ -77,7 +77,7 @@ { "label": "Ecosystem and extensibility", "items": [ - { "label": "Agent skills", "slug": "docs/cli/skills" }, + { "label": "Agent skills (experimental)", "slug": "docs/cli/skills" }, { "label": "Sub-agents (experimental)", "slug": "docs/core/subagents" diff --git a/docs/tools/index.md b/docs/tools/index.md index c21c3dc610..0434046ac4 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)**: On-demand expertise packages that are - activated via the `activate_skill` tool to provide specialized guidance and - resources. +- **[Agent Skills](../cli/skills.md)**: (Experimental) 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 4e56a06e6b..c8b9dcfb87 100644 --- a/packages/cli/src/config/config.test.ts +++ b/packages/cli/src/config/config.test.ts @@ -586,7 +586,7 @@ describe('parseArguments', () => { process.argv = ['node', 'script.js', 'skills', 'list']; // Skills command enabled by default or via experimental const settings = createTestMergedSettings({ - skills: { enabled: true }, + experimental: { skills: true }, }); const argv = await parseArguments(settings); expect(argv.isCommand).toBe(true); diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 8d2c9e9789..1a622f7d0c 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -304,7 +304,7 @@ export async function parseArguments( yargsInstance.command(extensionsCommand); } - if (settings.skills?.enabled ?? true) { + if (settings.experimental?.skills || (settings.skills?.enabled ?? true)) { yargsInstance.command(skillsCommand); } // Register hooks command if hooks are enabled @@ -755,7 +755,8 @@ export async function loadCliConfig( plan: settings.experimental?.plan, enableEventDrivenScheduler: settings.experimental?.enableEventDrivenScheduler, - skillsSupport: settings.skills?.enabled ?? true, + skillsSupport: + settings.experimental?.skills || (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 6b51276f2a..7ad6673768 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -1478,6 +1478,15 @@ 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, + }, useOSC52Paste: { type: 'boolean', label: 'Use OSC 52 Paste', @@ -1552,6 +1561,7 @@ 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 new file mode 100644 index 0000000000..57a2d1e4d0 --- /dev/null +++ b/packages/cli/src/config/skills-backward-compatibility.test.ts @@ -0,0 +1,158 @@ +/** + * @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 2740d9ed3e..44ddaeb039 100644 --- a/packages/cli/src/services/BuiltinCommandLoader.test.ts +++ b/packages/cli/src/services/BuiltinCommandLoader.test.ts @@ -119,12 +119,11 @@ describe('BuiltinCommandLoader', () => { getEnableHooks: () => false, getEnableHooksUI: () => false, getExtensionsEnabled: vi.fn().mockReturnValue(true), - isSkillsSupportEnabled: vi.fn().mockReturnValue(true), + isSkillsSupportEnabled: vi.fn().mockReturnValue(false), 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; @@ -261,12 +260,11 @@ describe('BuiltinCommandLoader profile', () => { getEnableHooks: () => false, getEnableHooksUI: () => false, getExtensionsEnabled: vi.fn().mockReturnValue(true), - isSkillsSupportEnabled: vi.fn().mockReturnValue(true), + isSkillsSupportEnabled: vi.fn().mockReturnValue(false), 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 d8905235c9..6302607d45 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -644,7 +644,7 @@ export class Config { this.disableLLMCorrection = params.disableLLMCorrection ?? true; this.planEnabled = params.plan ?? false; this.enableEventDrivenScheduler = params.enableEventDrivenScheduler ?? true; - this.skillsSupport = params.skillsSupport ?? true; + this.skillsSupport = params.skillsSupport ?? false; 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 8d56af9575..fdaf50e933 100644 --- a/schemas/settings.schema.json +++ b/schemas/settings.schema.json @@ -1436,6 +1436,13 @@ "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" + }, "useOSC52Paste": { "title": "Use OSC 52 Paste", "description": "Use OSC 52 sequence for pasting instead of clipboardy (useful for remote sessions).",