mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-02-01 22:48:03 +00:00
feat(agent): enable agent skills by default (#16736)
This commit is contained in:
@@ -285,16 +285,15 @@ export async function parseArguments(
|
||||
return true;
|
||||
});
|
||||
|
||||
if (settings.experimental.extensionManagement) {
|
||||
if (settings.experimental?.extensionManagement) {
|
||||
yargsInstance.command(extensionsCommand);
|
||||
}
|
||||
|
||||
if (settings.experimental.skills) {
|
||||
if (settings.experimental?.skills || (settings.skills?.enabled ?? true)) {
|
||||
yargsInstance.command(skillsCommand);
|
||||
}
|
||||
|
||||
// Register hooks command if hooks are enabled
|
||||
if (settings.tools.enableHooks) {
|
||||
if (settings.tools?.enableHooks) {
|
||||
yargsInstance.command(hooksCommand);
|
||||
}
|
||||
|
||||
@@ -722,7 +721,8 @@ export async function loadCliConfig(
|
||||
enableExtensionReloading: settings.experimental?.extensionReloading,
|
||||
enableAgents: settings.experimental?.enableAgents,
|
||||
plan: settings.experimental?.plan,
|
||||
skillsSupport: settings.experimental?.skills,
|
||||
skillsSupport:
|
||||
settings.experimental?.skills || (settings.skills?.enabled ?? true),
|
||||
disabledSkills: settings.skills?.disabled,
|
||||
experimentalJitContext: settings.experimental?.jitContext,
|
||||
noBrowser: !!process.env['NO_BROWSER'],
|
||||
|
||||
@@ -357,6 +357,17 @@ describe('SettingsSchema', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should have skills setting enabled by default', () => {
|
||||
const setting = getSettingsSchema().skills.properties.enabled;
|
||||
expect(setting).toBeDefined();
|
||||
expect(setting.type).toBe('boolean');
|
||||
expect(setting.category).toBe('Advanced');
|
||||
expect(setting.default).toBe(true);
|
||||
expect(setting.requiresRestart).toBe(true);
|
||||
expect(setting.showInDialog).toBe(true);
|
||||
expect(setting.description).toBe('Enable Agent Skills.');
|
||||
});
|
||||
|
||||
it('should have plan setting in schema', () => {
|
||||
const setting = getSettingsSchema().experimental.properties.plan;
|
||||
expect(setting).toBeDefined();
|
||||
|
||||
@@ -109,6 +109,7 @@ export interface SettingDefinition {
|
||||
key?: string;
|
||||
properties?: SettingsSchema;
|
||||
showInDialog?: boolean;
|
||||
ignoreInDocs?: boolean;
|
||||
mergeStrategy?: MergeStrategy;
|
||||
/** Enum type options */
|
||||
options?: readonly SettingEnumOption[];
|
||||
@@ -1597,6 +1598,16 @@ const SETTINGS_SCHEMA = {
|
||||
description: 'Settings for agent skills.',
|
||||
showInDialog: false,
|
||||
properties: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
label: 'Enable Agent Skills',
|
||||
category: 'Advanced',
|
||||
requiresRestart: true,
|
||||
default: true,
|
||||
description: 'Enable Agent Skills.',
|
||||
showInDialog: true,
|
||||
ignoreInDocs: true,
|
||||
},
|
||||
disabled: {
|
||||
type: 'array',
|
||||
label: 'Disabled Skills',
|
||||
|
||||
158
packages/cli/src/config/skills-backward-compatibility.test.ts
Normal file
158
packages/cli/src/config/skills-backward-compatibility.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10,7 +10,12 @@ import { MessageType, type HistoryItemSkillsList } from '../types.js';
|
||||
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
|
||||
import type { CommandContext } from './types.js';
|
||||
import type { Config, SkillDefinition } from '@google/gemini-cli-core';
|
||||
import { SettingScope, type LoadedSettings } from '../../config/settings.js';
|
||||
import {
|
||||
SettingScope,
|
||||
type LoadedSettings,
|
||||
createTestMergedSettings,
|
||||
type MergedSettings,
|
||||
} from '../../config/settings.js';
|
||||
|
||||
vi.mock('../../config/settings.js', async (importOriginal) => {
|
||||
const actual =
|
||||
@@ -55,7 +60,7 @@ describe('skillsCommand', () => {
|
||||
}),
|
||||
} as unknown as Config,
|
||||
settings: {
|
||||
merged: { skills: { disabled: [] } },
|
||||
merged: createTestMergedSettings({ skills: { disabled: [] } }),
|
||||
workspace: { path: '/workspace' },
|
||||
setValue: vi.fn(),
|
||||
} as unknown as LoadedSettings,
|
||||
@@ -181,7 +186,11 @@ describe('skillsCommand', () => {
|
||||
|
||||
describe('disable/enable', () => {
|
||||
beforeEach(() => {
|
||||
context.services.settings.merged.skills = { disabled: [] };
|
||||
(
|
||||
context.services.settings as unknown as { merged: MergedSettings }
|
||||
).merged = createTestMergedSettings({
|
||||
skills: { enabled: true, disabled: [] },
|
||||
});
|
||||
(
|
||||
context.services.settings as unknown as { workspace: { path: string } }
|
||||
).workspace = {
|
||||
@@ -234,7 +243,14 @@ describe('skillsCommand', () => {
|
||||
const enableCmd = skillsCommand.subCommands!.find(
|
||||
(s) => s.name === 'enable',
|
||||
)!;
|
||||
context.services.settings.merged.skills = { disabled: ['skill1'] };
|
||||
(
|
||||
context.services.settings as unknown as { merged: MergedSettings }
|
||||
).merged = createTestMergedSettings({
|
||||
skills: {
|
||||
enabled: true,
|
||||
disabled: ['skill1'],
|
||||
},
|
||||
});
|
||||
(
|
||||
context.services.settings as unknown as {
|
||||
workspace: { settings: { skills: { disabled: string[] } } };
|
||||
|
||||
@@ -1537,6 +1537,13 @@
|
||||
"default": {},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"title": "Enable Agent Skills",
|
||||
"description": "Enable Agent Skills.",
|
||||
"markdownDescription": "Enable Agent Skills.\n\n- Category: `Advanced`\n- Requires restart: `yes`\n- Default: `true`",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"disabled": {
|
||||
"title": "Disabled Skills",
|
||||
"description": "List of disabled skills.",
|
||||
|
||||
@@ -133,6 +133,10 @@ function collectEntries(
|
||||
definition.properties &&
|
||||
Object.keys(definition.properties).length > 0;
|
||||
|
||||
if (definition.ignoreInDocs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hasChildren && (options.includeAll || definition.showInDialog)) {
|
||||
if (!sections.has(sectionKey)) {
|
||||
sections.set(sectionKey, []);
|
||||
|
||||
Reference in New Issue
Block a user