mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-03 15:53:56 +00:00
feat(skillmgr): 原生技能管理器插件 + OpenClaw 兼容层职责分离
新增 internal/plugins/skillmgr(native skill 全生命周期 owner): - skill_list/info/load/unload/enable/disable/create/export/install - skill_create 两步式:先生成骨架模板,LLM 补全后传 content 覆盖写入 (plugin.ValidateSKILLContent 校验)并自动加载生效 - .skm 分发包(tar.gz):packSkill/unpackSkill 含 TarSlip 防护 (拒绝绝对路径/../逃逸、强制单根目录、校验包内 SKILL.md) - skills 目录扫描:纯 SKILL.md/skill.json 条目归本插件; sidecar(main.js/main.py)/OC plugin(openclaw.plugin.json) 留给兼容层 clawhubadapter 职责分离(OpenClaw 兼容层不再持有 native skill): - 删除 p.skills 字段与 default 分支 LoadSKILL 逻辑 - 发现纯 SKILL 条目改为发布 events.EventSkillDetected 移交事件, 由 skillmgr 订阅注册;启动时序 c<s 下全扫兜底,事件用于热新增 - claw_list/plugin_info 不再输出 SKILL 段,统一走 skill_list 方案B prompt 注入: - agentCore 新增 SkillIndexProvider 接口 + SetSkillIndexProvider - buildSystemPrompt 注入【可用技能】轻量索引(名称+版本+描述), LLM 匹配场景时主动 skill_info 拉全文按文档执行 - main.go 在插件加载后将 skillmgr 实例接线到 agent 内核小修: - extractDescription 跳过 YAML frontmatter 块(此前所有带 frontmatter 的 SKILL.md 描述都被误判为 '---') - extractField 剥离 YAML 成对引号(version: "1.0" 不再带尾引号) - plugin.ValidateSKILLContent 导出供生成侧校验
This commit is contained in:
@ -18,6 +18,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/events"
|
||||
"gitcode.com/JianFeeeee/HomeAgent/internal/plugin"
|
||||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
||||
)
|
||||
@ -46,7 +47,6 @@ type Plugin struct {
|
||||
name string
|
||||
skillsDir string
|
||||
simulatorDir string
|
||||
skills []*plugin.SKILLPlugin
|
||||
sidecars []*sidecarProcess
|
||||
manager *sidecarProcess
|
||||
mu sync.Mutex
|
||||
@ -163,13 +163,9 @@ func (p *Plugin) Start(s *sdk.PluginSDK) error {
|
||||
case hasOCManifest || hasOCPackage:
|
||||
log.Printf("[clawhubadapter] ocplugin %s handled by manager", entry.Name())
|
||||
default:
|
||||
sk, err := plugin.LoadSKILL(skillPath)
|
||||
if err != nil {
|
||||
log.Printf("[clawhubadapter] load skill %s: %v", entry.Name(), err)
|
||||
continue
|
||||
}
|
||||
p.skills = append(p.skills, sk)
|
||||
log.Printf("[clawhubadapter] loaded skill: %s v%s", sk.Name(), sk.Version())
|
||||
// 纯 SKILL 类型:不属于 OpenClaw 兼容层,移交原生 skillmgr 管理。
|
||||
// skillmgr 启动时也会自行全扫(本插件启动更早的场景由事件补齐)。
|
||||
p.publishSkillDetected(s, skillPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -592,16 +588,6 @@ func (p *Plugin) handlePluginUninstall(args map[string]interface{}) (interface{}
|
||||
}
|
||||
p.sidecars = aliveSidecars
|
||||
|
||||
// 2. Remove from SKILL list if present
|
||||
var aliveSkills []*plugin.SKILLPlugin
|
||||
for _, sk := range p.skills {
|
||||
if sk.Name() != name {
|
||||
aliveSkills = append(aliveSkills, sk)
|
||||
} else {
|
||||
logs = append(logs, fmt.Sprintf("已移除 SKILL 插件: %s v%s", sk.Name(), sk.Version()))
|
||||
}
|
||||
}
|
||||
p.skills = aliveSkills
|
||||
p.mu.Unlock()
|
||||
|
||||
// 3. Try manager for OC plugins
|
||||
@ -690,12 +676,6 @@ func (p *Plugin) handlePluginList(args map[string]interface{}) (interface{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(p.skills) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("\nSKILL 插件 (%d):", len(p.skills)))
|
||||
for _, sk := range p.skills {
|
||||
parts = append(parts, fmt.Sprintf(" %s v%s", sk.Name(), sk.Version()))
|
||||
}
|
||||
}
|
||||
caps := p.dispatcher.Capabilities()
|
||||
if len(caps) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("\nCapabilities (%d):", len(caps)))
|
||||
@ -703,7 +683,7 @@ func (p *Plugin) handlePluginList(args map[string]interface{}) (interface{}, err
|
||||
parts = append(parts, fmt.Sprintf(" %s", c))
|
||||
}
|
||||
}
|
||||
if len(result.Plugins) == 0 && len(p.sidecars) <= 1 && len(p.skills) == 0 {
|
||||
if len(result.Plugins) == 0 && len(p.sidecars) <= 1 {
|
||||
parts = append(parts, "没有已安装的插件。")
|
||||
}
|
||||
p.mu.Unlock()
|
||||
@ -738,13 +718,6 @@ func (p *Plugin) handlePluginList(args map[string]interface{}) (interface{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.skills) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("\nSKILL 插件 (%d):", len(p.skills)))
|
||||
for _, sk := range p.skills {
|
||||
parts = append(parts, fmt.Sprintf(" %s v%s", sk.Name(), sk.Version()))
|
||||
}
|
||||
}
|
||||
|
||||
caps := p.dispatcher.Capabilities()
|
||||
if len(caps) > 0 {
|
||||
parts = append(parts, fmt.Sprintf("\nCapabilities (%d):", len(caps)))
|
||||
@ -753,8 +726,8 @@ func (p *Plugin) handlePluginList(args map[string]interface{}) (interface{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(p.sidecars) == 0 && len(p.skills) == 0 {
|
||||
parts = append(parts, "没有已安装的插件。")
|
||||
if len(p.sidecars) == 0 {
|
||||
parts = append(parts, "没有已安装的插件(原生技能由 skillmgr 管理,用 skill_list 查看)。")
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
@ -811,11 +784,6 @@ func (p *Plugin) handlePluginInfo(args map[string]interface{}) (interface{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, sk := range p.skills {
|
||||
if sk.Name() == name {
|
||||
typ = "SKILL"
|
||||
}
|
||||
}
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
@ -1264,12 +1232,23 @@ func (p *Plugin) Stop() error {
|
||||
sp.Close()
|
||||
}
|
||||
p.sidecars = nil
|
||||
p.skills = nil
|
||||
p.manager = nil
|
||||
p.sdk = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// publishSkillDetected 发布 skill_detected 事件,将纯 SKILL 条目移交原生
|
||||
// skillmgr 插件注册。OpenClaw 兼容层不持有 native skill 状态。
|
||||
func (p *Plugin) publishSkillDetected(s *sdk.PluginSDK, path string) {
|
||||
s.Publish(&events.Event{
|
||||
Type: events.EventSkillDetected,
|
||||
Source: p.name,
|
||||
Payload: map[string]interface{}{"path": path},
|
||||
Timestamp: time.Now().Unix(),
|
||||
})
|
||||
log.Printf("[clawhubadapter] skill %s detected, handed off to skillmgr", filepath.Base(path))
|
||||
}
|
||||
|
||||
func errorResult(msg string) interface{} {
|
||||
return map[string]interface{}{
|
||||
"isError": true,
|
||||
@ -1338,13 +1317,8 @@ func (p *Plugin) reloadPlugin(name string) error {
|
||||
case hasOCManifest || hasOCPackage:
|
||||
return p.loadOCPlugin(p.sdk, pluginDir, name)
|
||||
default:
|
||||
sk, err := plugin.LoadSKILL(pluginDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load skill: %w", err)
|
||||
}
|
||||
p.mu.Lock()
|
||||
p.skills = append(p.skills, sk)
|
||||
p.mu.Unlock()
|
||||
// 纯 SKILL 类型:移交原生 skillmgr(与 Start 扫描行为一致)
|
||||
p.publishSkillDetected(p.sdk, pluginDir)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user