88import * as fs from 'node:fs/promises' ;
99import * as path from 'node:path' ;
1010import { homedir } from 'node:os' ;
11+ import { getSkillCreatorContent , skillCreatorMetadata } from './builtin/skill-creator.js' ;
1112import { hasSkillFile , loadSkillContent , loadSkillMetadata } from './SkillLoader.js' ;
1213import type {
1314 SkillContent ,
@@ -22,6 +23,9 @@ import type {
2223const DEFAULT_CONFIG : Required < SkillRegistryConfig > = {
2324 userSkillsDir : path . join ( homedir ( ) , '.blade' , 'skills' ) ,
2425 projectSkillsDir : '.blade/skills' ,
26+ // Claude Code 兼容路径
27+ claudeUserSkillsDir : path . join ( homedir ( ) , '.claude' , 'skills' ) ,
28+ claudeProjectSkillsDir : '.claude/skills' ,
2529 cwd : process . cwd ( ) ,
2630} ;
2731
@@ -61,6 +65,13 @@ export class SkillRegistry {
6165
6266 /**
6367 * 初始化注册表,扫描所有 skills 目录
68+ *
69+ * 优先级(后加载的覆盖先加载的):
70+ * 1. 内置 Skills(builtin)
71+ * 2. Claude Code 用户级 Skills(~/.claude/skills/)
72+ * 3. Blade 用户级 Skills(~/.blade/skills/)
73+ * 4. Claude Code 项目级 Skills(.claude/skills/)
74+ * 5. Blade 项目级 Skills(.blade/skills/)- 优先级最高
6475 */
6576 async initialize ( ) : Promise < SkillDiscoveryResult > {
6677 if ( this . initialized ) {
@@ -73,12 +84,28 @@ export class SkillRegistry {
7384 const errors : SkillDiscoveryResult [ 'errors' ] = [ ] ;
7485 const discoveredSkills : SkillMetadata [ ] = [ ] ;
7586
76- // 扫描用户级 skills(优先级 1)
87+ // 1. 加载内置 Skills(优先级最低,可被覆盖)
88+ this . loadBuiltinSkills ( ) ;
89+
90+ // 2. 扫描 Claude Code 用户级 skills(~/.claude/skills/)
91+ const claudeUserResult = await this . scanDirectory ( this . config . claudeUserSkillsDir , 'user' ) ;
92+ discoveredSkills . push ( ...claudeUserResult . skills ) ;
93+ errors . push ( ...claudeUserResult . errors ) ;
94+
95+ // 3. 扫描 Blade 用户级 skills(~/.blade/skills/)
7796 const userResult = await this . scanDirectory ( this . config . userSkillsDir , 'user' ) ;
7897 discoveredSkills . push ( ...userResult . skills ) ;
7998 errors . push ( ...userResult . errors ) ;
8099
81- // 扫描项目级 skills(优先级 2,可覆盖用户级同名 skill)
100+ // 4. 扫描 Claude Code 项目级 skills(.claude/skills/)
101+ const claudeProjectDir = path . isAbsolute ( this . config . claudeProjectSkillsDir )
102+ ? this . config . claudeProjectSkillsDir
103+ : path . join ( this . config . cwd , this . config . claudeProjectSkillsDir ) ;
104+ const claudeProjectResult = await this . scanDirectory ( claudeProjectDir , 'project' ) ;
105+ discoveredSkills . push ( ...claudeProjectResult . skills ) ;
106+ errors . push ( ...claudeProjectResult . errors ) ;
107+
108+ // 5. 扫描 Blade 项目级 skills(.blade/skills/)- 优先级最高
82109 const projectDir = path . isAbsolute ( this . config . projectSkillsDir )
83110 ? this . config . projectSkillsDir
84111 : path . join ( this . config . cwd , this . config . projectSkillsDir ) ;
@@ -99,6 +126,14 @@ export class SkillRegistry {
99126 } ;
100127 }
101128
129+ /**
130+ * 加载内置 Skills
131+ */
132+ private loadBuiltinSkills ( ) : void {
133+ // 注册 skill-creator
134+ this . skills . set ( skillCreatorMetadata . name , skillCreatorMetadata ) ;
135+ }
136+
102137 /**
103138 * 扫描指定目录下的所有 skills
104139 */
@@ -177,24 +212,71 @@ export class SkillRegistry {
177212 async loadContent ( name : string ) : Promise < SkillContent | null > {
178213 const metadata = this . skills . get ( name ) ;
179214 if ( ! metadata ) return null ;
215+
216+ // 内置 Skill 直接返回内容
217+ if ( metadata . source === 'builtin' ) {
218+ return this . loadBuiltinContent ( name ) ;
219+ }
220+
221+ // 文件系统 Skill 从文件加载
180222 return loadSkillContent ( metadata ) ;
181223 }
182224
225+ /**
226+ * 加载内置 Skill 的完整内容
227+ */
228+ private loadBuiltinContent ( name : string ) : SkillContent | null {
229+ switch ( name ) {
230+ case 'skill-creator' :
231+ return getSkillCreatorContent ( ) ;
232+ default :
233+ return null ;
234+ }
235+ }
236+
237+ /**
238+ * 获取可被 AI 自动调用的 Skills(Model-invoked)
239+ * 排除设置了 disable-model-invocation: true 的 Skills
240+ */
241+ getModelInvocableSkills ( ) : SkillMetadata [ ] {
242+ return Array . from ( this . skills . values ( ) ) . filter (
243+ ( skill ) => ! skill . disableModelInvocation
244+ ) ;
245+ }
246+
247+ /**
248+ * 获取可通过 /skill-name 命令调用的 Skills(User-invoked)
249+ * 仅包含设置了 user-invocable: true 的 Skills
250+ */
251+ getUserInvocableSkills ( ) : SkillMetadata [ ] {
252+ return Array . from ( this . skills . values ( ) ) . filter (
253+ ( skill ) => skill . userInvocable === true
254+ ) ;
255+ }
256+
183257 /**
184258 * 生成 <available_skills> 列表内容
185- * 格式:每个 skill 一行 `- name: description`
259+ * 格式:每个 skill 一行 `- name [argument-hint]: description`
260+ * 仅包含可被 AI 自动调用的 Skills
186261 */
187262 generateAvailableSkillsList ( ) : string {
188- if ( this . skills . size === 0 ) {
263+ const modelInvocableSkills = this . getModelInvocableSkills ( ) ;
264+ if ( modelInvocableSkills . length === 0 ) {
189265 return '' ;
190266 }
191267
192268 const lines : string [ ] = [ ] ;
193- for ( const skill of this . skills . values ( ) ) {
269+ for ( const skill of modelInvocableSkills ) {
194270 // 截断过长的描述,保持列表简洁
195271 const desc =
196272 skill . description . length > 100 ? `${ skill . description . substring ( 0 , 97 ) } ...` : skill . description ;
197- lines . push ( `- ${ skill . name } : ${ desc } ` ) ;
273+
274+ // 如果有 argument-hint,添加到名称后面
275+ const nameWithHint = skill . argumentHint
276+ ? `${ skill . name } ${ skill . argumentHint } `
277+ : skill . name ;
278+
279+ lines . push ( `- ${ nameWithHint } : ${ desc } ` ) ;
198280 }
199281
200282 return lines . join ( '\n' ) ;
0 commit comments