mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-17 14:17:28 +08:00
fix skills list loading and align chat to main-agent history view
This commit is contained in:
@@ -532,11 +532,6 @@ func (s *RegistryServer) handleWebUISkills(w http.ResponseWriter, r *http.Reques
|
|||||||
|
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
entries, err := os.ReadDir(skillsDir)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
type skillItem struct {
|
type skillItem struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -548,38 +543,62 @@ func (s *RegistryServer) handleWebUISkills(w http.ResponseWriter, r *http.Reques
|
|||||||
RemoteFound bool `json:"remote_found,omitempty"`
|
RemoteFound bool `json:"remote_found,omitempty"`
|
||||||
RemoteVersion string `json:"remote_version,omitempty"`
|
RemoteVersion string `json:"remote_version,omitempty"`
|
||||||
CheckError string `json:"check_error,omitempty"`
|
CheckError string `json:"check_error,omitempty"`
|
||||||
|
Source string `json:"source,omitempty"`
|
||||||
}
|
}
|
||||||
items := make([]skillItem, 0, len(entries))
|
candDirs := []string{skillsDir, filepath.Join("/root/clawgo/workspace", "skills")}
|
||||||
|
seenDirs := map[string]struct{}{}
|
||||||
|
seenSkills := map[string]struct{}{}
|
||||||
|
items := make([]skillItem, 0)
|
||||||
checkUpdates := strings.TrimSpace(r.URL.Query().Get("check_updates")) != "0"
|
checkUpdates := strings.TrimSpace(r.URL.Query().Get("check_updates")) != "0"
|
||||||
for _, e := range entries {
|
|
||||||
if !e.IsDir() {
|
for _, dir := range candDirs {
|
||||||
|
dir = strings.TrimSpace(dir)
|
||||||
|
if dir == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name := e.Name()
|
if _, ok := seenDirs[dir]; ok {
|
||||||
enabled := !strings.HasSuffix(name, ".disabled")
|
continue
|
||||||
baseName := strings.TrimSuffix(name, ".disabled")
|
|
||||||
desc, tools, sys := readSkillMeta(filepath.Join(skillsDir, name, "SKILL.md"))
|
|
||||||
if desc == "" || len(tools) == 0 || sys == "" {
|
|
||||||
d2, t2, s2 := readSkillMeta(filepath.Join(skillsDir, baseName, "SKILL.md"))
|
|
||||||
if desc == "" { desc = d2 }
|
|
||||||
if len(tools) == 0 { tools = t2 }
|
|
||||||
if sys == "" { sys = s2 }
|
|
||||||
}
|
}
|
||||||
if tools == nil {
|
seenDirs[dir] = struct{}{}
|
||||||
tools = []string{}
|
entries, err := os.ReadDir(dir)
|
||||||
}
|
if err != nil {
|
||||||
it := skillItem{ID: baseName, Name: baseName, Description: desc, Tools: tools, SystemPrompt: sys, Enabled: enabled, UpdateChecked: checkUpdates}
|
if os.IsNotExist(err) {
|
||||||
if checkUpdates {
|
continue
|
||||||
found, version, checkErr := queryClawHubSkillVersion(r.Context(), baseName)
|
|
||||||
it.RemoteFound = found
|
|
||||||
it.RemoteVersion = version
|
|
||||||
if checkErr != nil {
|
|
||||||
it.CheckError = checkErr.Error()
|
|
||||||
}
|
}
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, e := range entries {
|
||||||
|
if !e.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := e.Name()
|
||||||
|
enabled := !strings.HasSuffix(name, ".disabled")
|
||||||
|
baseName := strings.TrimSuffix(name, ".disabled")
|
||||||
|
if _, ok := seenSkills[baseName]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seenSkills[baseName] = struct{}{}
|
||||||
|
desc, tools, sys := readSkillMeta(filepath.Join(dir, name, "SKILL.md"))
|
||||||
|
if desc == "" || len(tools) == 0 || sys == "" {
|
||||||
|
d2, t2, s2 := readSkillMeta(filepath.Join(dir, baseName, "SKILL.md"))
|
||||||
|
if desc == "" { desc = d2 }
|
||||||
|
if len(tools) == 0 { tools = t2 }
|
||||||
|
if sys == "" { sys = s2 }
|
||||||
|
}
|
||||||
|
if tools == nil { tools = []string{} }
|
||||||
|
it := skillItem{ID: baseName, Name: baseName, Description: desc, Tools: tools, SystemPrompt: sys, Enabled: enabled, UpdateChecked: checkUpdates, Source: dir}
|
||||||
|
if checkUpdates {
|
||||||
|
found, version, checkErr := queryClawHubSkillVersion(r.Context(), baseName)
|
||||||
|
it.RemoteFound = found
|
||||||
|
it.RemoteVersion = version
|
||||||
|
if checkErr != nil { it.CheckError = checkErr.Error() }
|
||||||
|
}
|
||||||
|
items = append(items, it)
|
||||||
}
|
}
|
||||||
items = append(items, it)
|
|
||||||
}
|
}
|
||||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "skills": items, "source": "clawhub"})
|
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "skills": items, "source": "clawhub"})
|
||||||
|
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
var body map[string]interface{}
|
var body map[string]interface{}
|
||||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||||
@@ -590,9 +609,7 @@ func (s *RegistryServer) handleWebUISkills(w http.ResponseWriter, r *http.Reques
|
|||||||
action = strings.ToLower(strings.TrimSpace(action))
|
action = strings.ToLower(strings.TrimSpace(action))
|
||||||
id, _ := body["id"].(string)
|
id, _ := body["id"].(string)
|
||||||
name, _ := body["name"].(string)
|
name, _ := body["name"].(string)
|
||||||
if strings.TrimSpace(name) == "" {
|
if strings.TrimSpace(name) == "" { name = id }
|
||||||
name = id
|
|
||||||
}
|
|
||||||
name = strings.TrimSpace(name)
|
name = strings.TrimSpace(name)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
http.Error(w, "name required", http.StatusBadRequest)
|
http.Error(w, "name required", http.StatusBadRequest)
|
||||||
@@ -633,9 +650,7 @@ func (s *RegistryServer) handleWebUISkills(w http.ResponseWriter, r *http.Reques
|
|||||||
var toolsList []string
|
var toolsList []string
|
||||||
if arr, ok := body["tools"].([]interface{}); ok {
|
if arr, ok := body["tools"].([]interface{}); ok {
|
||||||
for _, v := range arr {
|
for _, v := range arr {
|
||||||
if sv, ok := v.(string); ok && strings.TrimSpace(sv) != "" {
|
if sv, ok := v.(string); ok && strings.TrimSpace(sv) != "" { toolsList = append(toolsList, strings.TrimSpace(sv)) }
|
||||||
toolsList = append(toolsList, strings.TrimSpace(sv))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if action == "create" {
|
if action == "create" {
|
||||||
@@ -657,6 +672,7 @@ func (s *RegistryServer) handleWebUISkills(w http.ResponseWriter, r *http.Reques
|
|||||||
default:
|
default:
|
||||||
http.Error(w, "unsupported action", http.StatusBadRequest)
|
http.Error(w, "unsupported action", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
case http.MethodDelete:
|
case http.MethodDelete:
|
||||||
id := strings.TrimSpace(r.URL.Query().Get("id"))
|
id := strings.TrimSpace(r.URL.Query().Get("id"))
|
||||||
if id == "" {
|
if id == "" {
|
||||||
@@ -666,23 +682,34 @@ func (s *RegistryServer) handleWebUISkills(w http.ResponseWriter, r *http.Reques
|
|||||||
pathA := filepath.Join(skillsDir, id)
|
pathA := filepath.Join(skillsDir, id)
|
||||||
pathB := pathA + ".disabled"
|
pathB := pathA + ".disabled"
|
||||||
deleted := false
|
deleted := false
|
||||||
if err := os.RemoveAll(pathA); err == nil {
|
if err := os.RemoveAll(pathA); err == nil { deleted = true }
|
||||||
deleted = true
|
if err := os.RemoveAll(pathB); err == nil { deleted = true }
|
||||||
}
|
|
||||||
if err := os.RemoveAll(pathB); err == nil {
|
|
||||||
deleted = true
|
|
||||||
}
|
|
||||||
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "deleted": deleted, "id": id})
|
_ = json.NewEncoder(w).Encode(map[string]interface{}{"ok": true, "deleted": deleted, "id": id})
|
||||||
|
|
||||||
default:
|
default:
|
||||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func buildSkillMarkdown(name, desc string, tools []string, systemPrompt string) string {
|
func buildSkillMarkdown(name, desc string, tools []string, systemPrompt string) string {
|
||||||
if strings.TrimSpace(desc) == "" {
|
if strings.TrimSpace(desc) == "" {
|
||||||
desc = "No description provided."
|
desc = "No description provided."
|
||||||
}
|
}
|
||||||
t := strings.Join(tools, ", ")
|
if len(tools) == 0 {
|
||||||
|
tools = []string{""}
|
||||||
|
}
|
||||||
|
toolLines := make([]string, 0, len(tools))
|
||||||
|
for _, t := range tools {
|
||||||
|
t = strings.TrimSpace(t)
|
||||||
|
if t == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
toolLines = append(toolLines, "- "+t)
|
||||||
|
}
|
||||||
|
if len(toolLines) == 0 {
|
||||||
|
toolLines = []string{"- (none)"}
|
||||||
|
}
|
||||||
return fmt.Sprintf(`---
|
return fmt.Sprintf(`---
|
||||||
name: %s
|
name: %s
|
||||||
description: %s
|
description: %s
|
||||||
@@ -697,13 +724,13 @@ description: %s
|
|||||||
|
|
||||||
## System Prompt
|
## System Prompt
|
||||||
%s
|
%s
|
||||||
`, name, desc, name, desc, t, systemPrompt)
|
`, name, desc, name, desc, strings.Join(toolLines, "\n"), systemPrompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readSkillMeta(path string) (desc string, tools []string, systemPrompt string) {
|
func readSkillMeta(path string) (desc string, tools []string, systemPrompt string) {
|
||||||
b, err := os.ReadFile(path)
|
b, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, ""
|
return "", []string{}, ""
|
||||||
}
|
}
|
||||||
s := string(b)
|
s := string(b)
|
||||||
reDesc := regexp.MustCompile(`(?m)^description:\s*(.+)$`)
|
reDesc := regexp.MustCompile(`(?m)^description:\s*(.+)$`)
|
||||||
@@ -718,19 +745,21 @@ func readSkillMeta(path string) (desc string, tools []string, systemPrompt strin
|
|||||||
block = block[:p[0]]
|
block = block[:p[0]]
|
||||||
}
|
}
|
||||||
for _, line := range strings.Split(block, "\n") {
|
for _, line := range strings.Split(block, "\n") {
|
||||||
v := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), "-"))
|
line = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(line), "-"))
|
||||||
if v != "" {
|
if line != "" {
|
||||||
tools = append(tools, v)
|
tools = append(tools, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if tools == nil {
|
||||||
|
tools = []string{}
|
||||||
|
}
|
||||||
if loc := rePrompt.FindStringIndex(s); loc != nil {
|
if loc := rePrompt.FindStringIndex(s); loc != nil {
|
||||||
systemPrompt = strings.TrimSpace(s[loc[1]:])
|
systemPrompt = strings.TrimSpace(s[loc[1]:])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func normalizeCronJob(v interface{}) map[string]interface{} {
|
func normalizeCronJob(v interface{}) map[string]interface{} {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return map[string]interface{}{}
|
return map[string]interface{}{}
|
||||||
|
|||||||
Reference in New Issue
Block a user