revert: promote Agent Skills to stable (#17693) (#17712)

This commit is contained in:
Abhi
2026-01-27 18:29:12 -05:00
committed by GitHub
parent b51323b40c
commit ff6547857e
18 changed files with 241 additions and 34 deletions

5
.gemini/settings.json Normal file
View File

@@ -0,0 +1,5 @@
{
"experimental": {
"skills": true
}
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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 |

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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:** `[]`

View File

@@ -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.

View File

@@ -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"

View File

@@ -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.

View File

@@ -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);

View File

@@ -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'],

View File

@@ -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',

View File

@@ -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<typeof import('@google/gemini-cli-core')>();
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);
});
});
});

View File

@@ -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;
});

View File

@@ -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();

View File

@@ -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).",