mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-01 07:24:38 -07:00
Agent Skills: Implement /skills reload (#15865)
This commit is contained in:
@@ -33,6 +33,8 @@ import { RipGrepTool, canUseRipgrep } from '../tools/ripGrep.js';
|
||||
import { logRipgrepFallback } from '../telemetry/loggers.js';
|
||||
import { RipgrepFallbackEvent } from '../telemetry/types.js';
|
||||
import { ToolRegistry } from '../tools/tool-registry.js';
|
||||
import { ACTIVATE_SKILL_TOOL_NAME } from '../tools/tool-names.js';
|
||||
import type { SkillDefinition } from '../skills/skillLoader.js';
|
||||
import { DEFAULT_MODEL_CONFIGS } from './defaultModelConfigs.js';
|
||||
import {
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
@@ -57,6 +59,7 @@ vi.mock('fs', async (importOriginal) => {
|
||||
vi.mock('../tools/tool-registry', () => {
|
||||
const ToolRegistryMock = vi.fn();
|
||||
ToolRegistryMock.prototype.registerTool = vi.fn();
|
||||
ToolRegistryMock.prototype.unregisterTool = vi.fn();
|
||||
ToolRegistryMock.prototype.discoverAllTools = vi.fn();
|
||||
ToolRegistryMock.prototype.sortTools = vi.fn();
|
||||
ToolRegistryMock.prototype.getAllTools = vi.fn(() => []); // Mock methods if needed
|
||||
@@ -104,6 +107,7 @@ vi.mock('../core/client.js', () => ({
|
||||
GeminiClient: vi.fn().mockImplementation(() => ({
|
||||
initialize: vi.fn().mockResolvedValue(undefined),
|
||||
stripThoughtsFromHistory: vi.fn(),
|
||||
isInitialized: vi.fn().mockReturnValue(false),
|
||||
})),
|
||||
}));
|
||||
|
||||
@@ -1978,4 +1982,104 @@ describe('Config JIT Initialization', () => {
|
||||
expect(ContextManager).not.toHaveBeenCalled();
|
||||
expect(config.getUserMemory()).toBe('Initial Memory');
|
||||
});
|
||||
|
||||
describe('reloadSkills', () => {
|
||||
it('should refresh disabledSkills and re-register ActivateSkillTool when skills exist', async () => {
|
||||
const mockOnReload = vi.fn().mockResolvedValue({
|
||||
disabledSkills: ['skill2'],
|
||||
});
|
||||
const params: ConfigParameters = {
|
||||
sessionId: 'test-session',
|
||||
targetDir: '/tmp/test',
|
||||
debugMode: false,
|
||||
model: 'test-model',
|
||||
cwd: '/tmp/test',
|
||||
skillsSupport: true,
|
||||
onReload: mockOnReload,
|
||||
};
|
||||
|
||||
config = new Config(params);
|
||||
await config.initialize();
|
||||
|
||||
const skillManager = config.getSkillManager();
|
||||
const toolRegistry = config.getToolRegistry();
|
||||
|
||||
vi.spyOn(skillManager, 'discoverSkills').mockResolvedValue(undefined);
|
||||
vi.spyOn(skillManager, 'setDisabledSkills');
|
||||
vi.spyOn(toolRegistry, 'registerTool');
|
||||
vi.spyOn(toolRegistry, 'unregisterTool');
|
||||
|
||||
const mockSkills = [{ name: 'skill1' }];
|
||||
vi.spyOn(skillManager, 'getSkills').mockReturnValue(
|
||||
mockSkills as SkillDefinition[],
|
||||
);
|
||||
|
||||
await config.reloadSkills();
|
||||
|
||||
expect(mockOnReload).toHaveBeenCalled();
|
||||
expect(skillManager.setDisabledSkills).toHaveBeenCalledWith(['skill2']);
|
||||
expect(toolRegistry.registerTool).toHaveBeenCalled();
|
||||
expect(toolRegistry.unregisterTool).not.toHaveBeenCalledWith(
|
||||
ACTIVATE_SKILL_TOOL_NAME,
|
||||
);
|
||||
});
|
||||
|
||||
it('should unregister ActivateSkillTool when no skills exist after reload', async () => {
|
||||
const params: ConfigParameters = {
|
||||
sessionId: 'test-session',
|
||||
targetDir: '/tmp/test',
|
||||
debugMode: false,
|
||||
model: 'test-model',
|
||||
cwd: '/tmp/test',
|
||||
skillsSupport: true,
|
||||
};
|
||||
|
||||
config = new Config(params);
|
||||
await config.initialize();
|
||||
|
||||
const skillManager = config.getSkillManager();
|
||||
const toolRegistry = config.getToolRegistry();
|
||||
|
||||
vi.spyOn(skillManager, 'discoverSkills').mockResolvedValue(undefined);
|
||||
vi.spyOn(toolRegistry, 'registerTool');
|
||||
vi.spyOn(toolRegistry, 'unregisterTool');
|
||||
|
||||
vi.spyOn(skillManager, 'getSkills').mockReturnValue([]);
|
||||
|
||||
await config.reloadSkills();
|
||||
|
||||
expect(toolRegistry.unregisterTool).toHaveBeenCalledWith(
|
||||
ACTIVATE_SKILL_TOOL_NAME,
|
||||
);
|
||||
});
|
||||
|
||||
it('should clear disabledSkills when onReload returns undefined for them', async () => {
|
||||
const mockOnReload = vi.fn().mockResolvedValue({
|
||||
disabledSkills: undefined,
|
||||
});
|
||||
const params: ConfigParameters = {
|
||||
sessionId: 'test-session',
|
||||
targetDir: '/tmp/test',
|
||||
debugMode: false,
|
||||
model: 'test-model',
|
||||
cwd: '/tmp/test',
|
||||
skillsSupport: true,
|
||||
onReload: mockOnReload,
|
||||
};
|
||||
|
||||
config = new Config(params);
|
||||
// Initially set some disabled skills
|
||||
// @ts-expect-error - accessing private
|
||||
config.disabledSkills = ['skill1'];
|
||||
await config.initialize();
|
||||
|
||||
const skillManager = config.getSkillManager();
|
||||
vi.spyOn(skillManager, 'discoverSkills').mockResolvedValue(undefined);
|
||||
vi.spyOn(skillManager, 'setDisabledSkills');
|
||||
|
||||
await config.reloadSkills();
|
||||
|
||||
expect(skillManager.setDisabledSkills).toHaveBeenCalledWith([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user