Download packages/core/src/skills/skillManager.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 5.76 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/skills/skillManager.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/core/src/skills/skillManager.ts
-
curl -L -o skillManager.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/core/src/skills/skillManager.ts
5.76 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import * as path from 'node:path'; | |
| import { fileURLToPath } from 'node:url'; | |
| import { Storage } from '../config/storage.js'; | |
| import { type SkillDefinition, loadSkillsFromDir } from './skillLoader.js'; | |
| import type { GeminiCLIExtension } from '../config/config.js'; | |
| import { debugLogger } from '../utils/debugLogger.js'; | |
| import { coreEvents } from '../utils/events.js'; | |
| export { type SkillDefinition }; | |
| export class SkillManager { | |
| private skills: SkillDefinition[] = []; | |
| private activeSkillNames: Set<string> = new Set(); | |
| private adminSkillsEnabled = true; | |
| /** | |
| * Clears all discovered skills. | |
| */ | |
| clearSkills(): void { | |
| this.skills = []; | |
| } | |
| /** | |
| * Resets session-scoped state (active skill names). | |
| */ | |
| reset(): void { | |
| this.activeSkillNames.clear(); | |
| } | |
| /** | |
| * Sets administrative settings for skills. | |
| */ | |
| setAdminSettings(enabled: boolean): void { | |
| this.adminSkillsEnabled = enabled; | |
| } | |
| /** | |
| * Returns true if skills are enabled by the admin. | |
| */ | |
| isAdminEnabled(): boolean { | |
| return this.adminSkillsEnabled; | |
| } | |
| /** | |
| * Discovers skills from standard user and workspace locations, as well as extensions. | |
| * Precedence: Extensions (lowest) -> User -> Workspace (highest). | |
| */ | |
| async discoverSkills( | |
| storage: Storage, | |
| extensions: GeminiCLIExtension[] = [], | |
| isTrusted: boolean = false, | |
| ): Promise<void> { | |
| this.clearSkills(); | |
| // 1. Built-in skills (lowest precedence) | |
| await this.discoverBuiltinSkills(); | |
| // 2. Extension skills | |
| for (const extension of extensions) { | |
| if (extension.isActive && extension.skills) { | |
| this.addSkillsWithPrecedence(extension.skills); | |
| } | |
| } | |
| // 3. User skills | |
| const userSkills = await loadSkillsFromDir(Storage.getUserSkillsDir()); | |
| this.addSkillsWithPrecedence(userSkills); | |
| // 3.1 User agent skills alias (.agents/skills) | |
| const userAgentSkills = await loadSkillsFromDir( | |
| Storage.getUserAgentSkillsDir(), | |
| ); | |
| this.addSkillsWithPrecedence(userAgentSkills); | |
| // 4. Workspace skills (highest precedence) | |
| if (!isTrusted) { | |
| debugLogger.debug( | |
| 'Workspace skills disabled because folder is not trusted.', | |
| ); | |
| return; | |
| } | |
| const projectSkills = await loadSkillsFromDir( | |
| storage.getProjectSkillsDir(), | |
| ); | |
| this.addSkillsWithPrecedence(projectSkills); | |
| // 4.1 Workspace agent skills alias (.agents/skills) | |
| const projectAgentSkills = await loadSkillsFromDir( | |
| storage.getProjectAgentSkillsDir(), | |
| ); | |
| this.addSkillsWithPrecedence(projectAgentSkills); | |
| } | |
| /** | |
| * Discovers built-in skills. | |
| */ | |
| private async discoverBuiltinSkills(): Promise<void> { | |
| const __dirname = path.dirname(fileURLToPath(import.meta.url)); | |
| const builtinDir = path.join(__dirname, 'builtin'); | |
| const builtinSkills = await loadSkillsFromDir(builtinDir); | |
| for (const skill of builtinSkills) { | |
| skill.isBuiltin = true; | |
| } | |
| this.addSkillsWithPrecedence(builtinSkills); | |
| } | |
| /** | |
| * Adds skills to the manager programmatically. | |
| */ | |
| addSkills(skills: SkillDefinition[]): void { | |
| this.addSkillsWithPrecedence(skills); | |
| } | |
| private addSkillsWithPrecedence(newSkills: SkillDefinition[]): void { | |
| const skillMap = new Map<string, SkillDefinition>( | |
| this.skills.map((s) => [s.name, s]), | |
| ); | |
| for (const newSkill of newSkills) { | |
| const existingSkill = skillMap.get(newSkill.name); | |
| if (existingSkill && existingSkill.location !== newSkill.location) { | |
| if (existingSkill.isBuiltin) { | |
| debugLogger.warn( | |
| `Skill "${newSkill.name}" from "${newSkill.location}" is overriding the built-in skill.`, | |
| ); | |
| } else { | |
| coreEvents.emitFeedback( | |
| 'warning', | |
| `Skill conflict detected: "${newSkill.name}" from "${newSkill.location}" is overriding the same skill from "${existingSkill.location}".`, | |
| ); | |
| } | |
| } | |
| skillMap.set(newSkill.name, newSkill); | |
| } | |
| this.skills = Array.from(skillMap.values()); | |
| } | |
| /** | |
| * Returns the list of enabled discovered skills. | |
| */ | |
| getSkills(): SkillDefinition[] { | |
| return this.skills.filter((s) => !s.disabled); | |
| } | |
| /** | |
| * Returns the list of enabled discovered skills that should be displayed in the UI. | |
| * This excludes built-in skills. | |
| */ | |
| getDisplayableSkills(): SkillDefinition[] { | |
| return this.skills.filter((s) => !s.disabled && !s.isBuiltin); | |
| } | |
| /** | |
| * Returns all discovered skills, including disabled ones. | |
| */ | |
| getAllSkills(): SkillDefinition[] { | |
| return this.skills; | |
| } | |
| /** | |
| * Filters discovered skills by name. | |
| */ | |
| filterSkills(predicate: (skill: SkillDefinition) => boolean): void { | |
| this.skills = this.skills.filter(predicate); | |
| } | |
| /** | |
| * Sets the list of disabled skill names. | |
| */ | |
| setDisabledSkills(disabledNames: string[]): void { | |
| const lowercaseDisabledNames = disabledNames.map((n) => n.toLowerCase()); | |
| for (const skill of this.skills) { | |
| skill.disabled = lowercaseDisabledNames.includes( | |
| skill.name.toLowerCase(), | |
| ); | |
| } | |
| } | |
| /** | |
| * Reads the full content (metadata + body) of a skill by name. | |
| */ | |
| getSkill(name: string): SkillDefinition | null { | |
| const lowercaseName = name.toLowerCase(); | |
| return ( | |
| this.skills.find((s) => s.name.toLowerCase() === lowercaseName) ?? null | |
| ); | |
| } | |
| /** | |
| * Activates a skill by name. | |
| */ | |
| activateSkill(name: string): void { | |
| this.activeSkillNames.add(name); | |
| } | |
| /** | |
| * Checks if a skill is active. | |
| */ | |
| isSkillActive(name: string): boolean { | |
| return this.activeSkillNames.has(name); | |
| } | |
| } | |