@@ -43,8 +43,8 @@ export function AgentSkills() {
4343
4444 < Layer title = "技能发现(Discovery)" icon = "🗂️" >
4545 < p className = "text-gray-300 mb-4" >
46- skills 启用后,< code > SkillManager</ code > 会从用户目录和项目目录扫描 < code > */SKILL.md</ code > :
47- 项目级 skill 会覆盖同名用户级 skill (同名以 YAML frontmatter 的 < code > name</ code > 为准)。
46+ skills 启用后,< code > SkillManager</ code > 会扫描 < code > */SKILL.md</ code > 并汇总为“可用技能清单”。
47+ 覆盖优先级为: < strong > Extension(最低) → User → Project(最高) </ strong > (同名以 YAML frontmatter 的 < code > name</ code > 为准)。
4848 </ p >
4949
5050 < CodeBlock
@@ -61,14 +61,26 @@ getProjectSkillsDir(): string {
6161
6262 < CodeBlock
6363 title = "发现顺序与覆盖(skillManager.ts)"
64- code = { `// packages/core/src/services/skillManager.ts
65- // 1) User skills first
66- const userSkills = await this.discoverSkillsInternal([Storage.getUserSkillsDir()]);
67- this.addSkillsWithPrecedence(userSkills);
68-
69- // 2) Project skills second (overwrites user skills with same name)
70- const projectSkills = await this.discoverSkillsInternal([storage.getProjectSkillsDir()]);
71- this.addSkillsWithPrecedence(projectSkills);` }
64+ code = { `// packages/core/src/skills/skillManager.ts
65+ // Precedence: Extensions (lowest) -> User -> Project (highest).
66+ async discoverSkills(storage: Storage, extensions: GeminiCLIExtension[] = []) {
67+ this.clearSkills();
68+
69+ // 1) Extension skills (lowest)
70+ for (const extension of extensions) {
71+ if (extension.isActive && extension.skills) {
72+ this.addSkillsWithPrecedence(extension.skills);
73+ }
74+ }
75+
76+ // 2) User skills
77+ const userSkills = await loadSkillsFromDir(Storage.getUserSkillsDir());
78+ this.addSkillsWithPrecedence(userSkills);
79+
80+ // 3) Project skills (highest, overrides)
81+ const projectSkills = await loadSkillsFromDir(storage.getProjectSkillsDir());
82+ this.addSkillsWithPrecedence(projectSkills);
83+ }` }
7284 />
7385
7486 < CodeBlock
@@ -114,6 +126,26 @@ return {
114126 </ div >
115127 </ Layer >
116128
129+ < Layer title = "Schema 收敛:把 name 变成 enum" icon = "🧷" >
130+ < p className = "text-gray-300 mb-4" >
131+ 技能列表发现完成后,CLI 会< strong > 重新注册一次</ strong > < code > ActivateSkillTool</ code > ,让参数 < code > name</ code >
132+ 从 < code > string</ code > 收敛为 < code > enum(availableSkillNames)</ code > ,从而减少模型“瞎猜技能名”的概率。
133+ </ p >
134+ < CodeBlock
135+ title = "config.ts:发现技能后重注册工具"
136+ code = { `// packages/core/src/config/config.ts (节选)
137+ if (this.skillsSupport) {
138+ await this.getSkillManager().discoverSkills(this.storage, this.getExtensions());
139+ this.getSkillManager().setDisabledSkills(this.disabledSkills);
140+
141+ // Re-register ActivateSkillTool to update its schema with discovered skill enums
142+ if (this.getSkillManager().getSkills().length > 0) {
143+ this.getToolRegistry().registerTool(new ActivateSkillTool(this, this.messageBus));
144+ }
145+ }` }
146+ />
147+ </ Layer >
148+
117149 < Layer title = "System Prompt 注入" icon = "🧱" >
118150 < p className = "text-gray-300 mb-4" >
119151 当存在可用 skills 时,System Prompt 会追加一个 < code > Available Agent Skills</ code > 段落,列出技能元信息,并要求模型:
@@ -151,8 +183,31 @@ skills.disabled: string[] # List of disabled skills (restart required)`}
151183 />
152184 </ Layer >
153185
186+ < Layer title = "扩展技能与安全披露" icon = "🛡️" >
187+ < p className = "text-gray-300 mb-4" >
188+ Extension 可以携带 < code > skills/</ code > 目录(例如 < code > skills/my-skill/SKILL.md</ code > )。在安装/更新扩展时,CLI 会在 consent
189+ 文本中明确提示:< strong > Agent skills 会把指令注入 system prompt</ strong > ,并展示每个 skill 的名称、描述与文件位置,要求用户确认后才继续安装。
190+ </ p >
191+ < CodeBlock
192+ title = "extensions/consent.ts:skills 风险提示(节选)"
193+ code = { `// packages/cli/src/config/extensions/consent.ts
194+ export const SKILLS_WARNING_MESSAGE = chalk.yellow(
195+ "Agent skills inject specialized instructions and domain-specific knowledge into the agent's system prompt..."
196+ );
197+
198+ if (skills.length > 0) {
199+ output.push("Agent Skills:");
200+ output.push(SKILLS_WARNING_MESSAGE);
201+ output.push("This extension will install the following agent skills:");
202+ for (const skill of skills) {
203+ output.push(\` * \${skill.name}: \${skill.description}\`);
204+ output.push(\` (Location: \${skill.location})\`);
205+ }
206+ }` }
207+ />
208+ </ Layer >
209+
154210 < RelatedPages pages = { relatedPages } />
155211 </ div >
156212 ) ;
157213}
158-
0 commit comments