@@ -6,7 +6,11 @@ const path = require('path');
66const yaml = require ( 'js-yaml' ) ;
77const mime = require ( 'mime-types' ) ;
88
9- const { normalizeRelativePath } = require ( '../utils/skill-utils' ) ;
9+ const {
10+ normalizeRelativePath,
11+ extractSkillMdDescription,
12+ extractSkillMdName,
13+ } = require ( '../utils/skill-utils' ) ;
1014const {
1115 isValidSkillCategory,
1216 SKILL_CATEGORY_OPTIONS ,
@@ -69,6 +73,19 @@ class AgentsService extends Service {
6973 return normalized ;
7074 }
7175
76+ // agent.yaml 里的 ref 指向目录(skills/bugfix-workflow)或 SKILL.md 本身,
77+ // 统一归一化为包内 SKILL.md 相对路径,供 agent_files 精确匹配。
78+ resolveSkillMdPath ( refOrPath ) {
79+ const normalized = String ( refOrPath || '' ) . trim ( ) ;
80+ if ( ! normalized ) return '' ;
81+ return normalized . toLowerCase ( ) . endsWith ( '.md' ) ? normalized : `${ normalized } /SKILL.md` ;
82+ }
83+
84+ lookupSkillMd ( skillMdMap , refOrPath ) {
85+ if ( ! skillMdMap ) return '' ;
86+ return skillMdMap . get ( this . resolveSkillMdPath ( refOrPath ) ) || '' ;
87+ }
88+
7289 parseJsonArray ( value ) {
7390 if ( ! value ) return [ ] ;
7491 if ( Array . isArray ( value ) ) return value ;
@@ -929,7 +946,8 @@ class AgentsService extends Service {
929946
930947 async getAgentDetail ( name ) {
931948 await this . ensureStorageReady ( ) ;
932- const { Agent, AgentSkill, SkillsItem } = this . app . model ;
949+ const { Agent, AgentSkill, SkillsItem, AgentFile } = this . app . model ;
950+ const { Op } = this . app . Sequelize ;
933951 const row = await Agent . findOne ( {
934952 where : {
935953 name,
@@ -964,6 +982,8 @@ class AgentsService extends Service {
964982 const skillMap = new Map ( skillRows . map ( ( item ) => [ item . slug , item ] ) ) ;
965983
966984 const entrypoint = relations . find ( ( item ) => item . relation_type === 'entrypoint' ) || null ;
985+ const entrypointSkill = entrypoint ? skillMap . get ( entrypoint . skill_slug ) : null ;
986+
967987 const dependencies = relations
968988 . filter ( ( item ) => item . relation_type === 'dependency' )
969989 . map ( ( item ) => {
@@ -987,6 +1007,61 @@ class AgentsService extends Service {
9871007 path : '' ,
9881008 } ) ) ;
9891009
1010+ // 未收录的入口 Skill 和内置 Skills 不在 SkillsItem 表,其真实 name/description
1011+ // 从 agent 包内自带的 SKILL.md 解析(agent_files 已在导入时保存文件内容)。
1012+ const skillMdPaths = [
1013+ ...( entrypoint && ! entrypointSkill ? [ this . resolveSkillMdPath ( row . entrypoint_ref ) ] : [ ] ) ,
1014+ ...privateSkills . map ( ( item ) => `skills/${ item . slug } /SKILL.md` ) ,
1015+ ...dependencies
1016+ . filter ( ( item ) => ! item . collected )
1017+ . map ( ( item ) => `skills/${ item . slug } /SKILL.md` ) ,
1018+ ] . filter ( Boolean ) ;
1019+ const skillMdRows =
1020+ skillMdPaths . length > 0 && AgentFile
1021+ ? await AgentFile . findAll ( {
1022+ where : {
1023+ agent_id : row . id ,
1024+ file_path : { [ Op . in ] : skillMdPaths } ,
1025+ is_delete : 0 ,
1026+ } ,
1027+ } )
1028+ : [ ] ;
1029+ const skillMdMap = new Map ( skillMdRows . map ( ( item ) => [ item . file_path , item . content || '' ] ) ) ;
1030+
1031+ // 入口 Skill:已收录用 SkillsItem 描述;未收录回填包内 SKILL.md 的 name/description
1032+ const entrypointItem = entrypoint
1033+ ? ( ( ) => {
1034+ const skill = entrypointSkill ;
1035+ const skillMd = skill ? '' : this . lookupSkillMd ( skillMdMap , row . entrypoint_ref ) ;
1036+ return {
1037+ slug : entrypoint . skill_slug ,
1038+ name : skill ? skill . name : extractSkillMdName ( skillMd ) || entrypoint . skill_slug ,
1039+ description : skill ? skill . description || '' : extractSkillMdDescription ( skillMd ) ,
1040+ collected : Boolean ( skill ) ,
1041+ path : `/page/skills/${ entrypoint . skill_slug } ` ,
1042+ } ;
1043+ } ) ( )
1044+ : null ;
1045+
1046+ // 内置 Skills:总是从包内 SKILL.md 回填
1047+ privateSkills . forEach ( ( item ) => {
1048+ const skillMd = skillMdMap . get ( `skills/${ item . slug } /SKILL.md` ) || '' ;
1049+ const name = extractSkillMdName ( skillMd ) ;
1050+ if ( name ) item . name = name ;
1051+ const description = extractSkillMdDescription ( skillMd ) ;
1052+ if ( description ) item . description = description ;
1053+ } ) ;
1054+
1055+ // 未收录的依赖 Skills:包内有 SKILL.md 时同样回填
1056+ dependencies . forEach ( ( item ) => {
1057+ if ( item . collected ) return ;
1058+ const skillMd = skillMdMap . get ( `skills/${ item . slug } /SKILL.md` ) || '' ;
1059+ const name = extractSkillMdName ( skillMd ) ;
1060+ if ( name ) item . name = name ;
1061+ const description = extractSkillMdDescription ( skillMd ) ;
1062+ if ( description ) item . description = description ;
1063+ } ) ;
1064+
9901065 const detail = row . toJSON ( ) ;
9911066 const demoImages = this . parseJsonArray ( detail . demo_images ) . map ( ( item ) => ( {
9921067 ...item ,
@@ -1008,19 +1083,7 @@ class AgentsService extends Service {
10081083 logoUrl : this . buildAssetUrl ( detail . name , detail . logo_path ) ,
10091084 logoPath : detail . logo_path ,
10101085 demoImages,
1011- entrypoint : entrypoint
1012- ? {
1013- slug : entrypoint . skill_slug ,
1014- name : skillMap . get ( entrypoint . skill_slug )
1015- ? skillMap . get ( entrypoint . skill_slug ) . name
1016- : entrypoint . skill_slug ,
1017- description : skillMap . get ( entrypoint . skill_slug )
1018- ? skillMap . get ( entrypoint . skill_slug ) . description || ''
1019- : '' ,
1020- collected : Boolean ( skillMap . get ( entrypoint . skill_slug ) ) ,
1021- path : `/page/skills/${ entrypoint . skill_slug } ` ,
1022- }
1023- : null ,
1086+ entrypoint : entrypointItem ,
10241087 dependencies,
10251088 privateSkills,
10261089 updatedAt : detail . updated_at ? detail . updated_at . toISOString ( ) : '' ,
0 commit comments