feat: 新增获取skill内容的API路由
- 新增 GET /skill/content 获取skill的SKILL.md内容 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -491,6 +491,7 @@ func main() {
|
|||||||
skillGroup.GET("/list", skillHandler.List)
|
skillGroup.GET("/list", skillHandler.List)
|
||||||
skillGroup.GET("/sync", skillHandler.Sync)
|
skillGroup.GET("/sync", skillHandler.Sync)
|
||||||
skillGroup.GET("/:id", skillHandler.GetByID)
|
skillGroup.GET("/:id", skillHandler.GetByID)
|
||||||
|
skillGroup.GET("/content", skillHandler.GetSkillContent)
|
||||||
skillGroup.POST("/add", skillHandler.Create)
|
skillGroup.POST("/add", skillHandler.Create)
|
||||||
skillGroup.PUT("/:id", skillHandler.Update)
|
skillGroup.PUT("/:id", skillHandler.Update)
|
||||||
skillGroup.DELETE("/:id", skillHandler.Delete)
|
skillGroup.DELETE("/:id", skillHandler.Delete)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -234,6 +235,19 @@ func (h *SkillHandler) getProjectRoot() string {
|
|||||||
// @Param skill body model.Skill true "技能信息"
|
// @Param skill body model.Skill true "技能信息"
|
||||||
// @Success 200 {object} map[string]interface{} "{"message": "skill updated"}"
|
// @Success 200 {object} map[string]interface{} "{"message": "skill updated"}"
|
||||||
// @Router /skill/{id} [put]
|
// @Router /skill/{id} [put]
|
||||||
|
// Update 更新技能
|
||||||
|
// @Summary 更新技能
|
||||||
|
// @Description 更新技能信息,支持文件上传
|
||||||
|
// @Tags 技能管理
|
||||||
|
// @Accept multipart/form-data
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "技能ID"
|
||||||
|
// @Param skill_name formData string false "技能名称"
|
||||||
|
// @Param skill_desc formData string false "技能描述"
|
||||||
|
// @Param skill_type formData string false "技能类型"
|
||||||
|
// @Param file formData file false "技能文件(SKILL.md)"
|
||||||
|
// @Success 200 {object} map[string]interface{} "{"message": "skill updated"}"
|
||||||
|
// @Router /skill/{id} [put]
|
||||||
func (h *SkillHandler) Update(c *gin.Context) {
|
func (h *SkillHandler) Update(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if id == "" {
|
if id == "" {
|
||||||
@@ -241,19 +255,142 @@ func (h *SkillHandler) Update(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var skill model.Skill
|
// 获取现有技能信息
|
||||||
if err := c.ShouldBindJSON(&skill); err != nil {
|
existingSkill, err := h.skillService.GetSkillByID(id)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
if err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "skill not found"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
skill.ID = id
|
skillName := c.PostForm("skill_name")
|
||||||
if err := h.skillService.UpdateSkill(&skill); err != nil {
|
skillDesc := c.PostForm("skill_desc")
|
||||||
|
skillType := c.PostForm("skill_type")
|
||||||
|
|
||||||
|
// 如果没有传则使用现有值
|
||||||
|
if skillName == "" {
|
||||||
|
skillName = existingSkill.SkillName
|
||||||
|
}
|
||||||
|
if skillDesc == "" {
|
||||||
|
skillDesc = existingSkill.SkillDesc
|
||||||
|
}
|
||||||
|
if skillType == "" {
|
||||||
|
skillType = existingSkill.SkillType
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取项目根目录
|
||||||
|
projectRoot := h.getProjectRoot()
|
||||||
|
if projectRoot == "" {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "cannot find project root"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果技能名称改变了,需要重命名文件夹
|
||||||
|
oldPath := existingSkill.Path
|
||||||
|
if skillName != existingSkill.SkillName && oldPath != "" {
|
||||||
|
// 旧目录
|
||||||
|
oldDir := filepath.Dir(oldPath)
|
||||||
|
// 新目录
|
||||||
|
skillDir := "user"
|
||||||
|
if skillType == "system" {
|
||||||
|
skillDir = "system"
|
||||||
|
}
|
||||||
|
newDir := filepath.Join(projectRoot, "core", "agents", "skills", skillDir, skillName)
|
||||||
|
|
||||||
|
// 重命名目录
|
||||||
|
if err := os.Rename(oldDir, newDir); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to rename skill directory: " + err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新路径
|
||||||
|
existingSkill.Path = filepath.Join(newDir, "SKILL.md")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理文件上传
|
||||||
|
file, err := c.FormFile("file")
|
||||||
|
if err == nil {
|
||||||
|
// 保存上传的文件为 SKILL.md
|
||||||
|
skillFilePath := filepath.Join(filepath.Dir(existingSkill.Path), "SKILL.md")
|
||||||
|
if err := c.SaveUploadedFile(file, skillFilePath); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save skill file: " + err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新技能信息
|
||||||
|
existingSkill.SkillName = skillName
|
||||||
|
existingSkill.SkillDesc = skillDesc
|
||||||
|
existingSkill.SkillType = skillType
|
||||||
|
|
||||||
|
if err := h.skillService.UpdateSkill(existingSkill); err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "skill updated"})
|
c.JSON(http.StatusOK, gin.H{"message": "skill updated", "skill": existingSkill})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSkillContent 获取技能文件内容
|
||||||
|
// @Summary 获取技能文件内容
|
||||||
|
// @Description 获取指定技能对应的 SKILL.md 文件内容
|
||||||
|
// @Tags 技能管理
|
||||||
|
// @Accept json
|
||||||
|
// @Produce text/plain
|
||||||
|
// @Param id path string true "技能ID"
|
||||||
|
// @Success 200 {string} string "文件内容"
|
||||||
|
// @Router /skill/content [get]
|
||||||
|
func (h *SkillHandler) GetSkillContent(c *gin.Context) {
|
||||||
|
id := c.Query("id")
|
||||||
|
if id == "" {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "skill id is required"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取技能信息
|
||||||
|
skill, err := h.skillService.GetSkillByID(id)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "skill not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定文件路径
|
||||||
|
filePath := skill.Path
|
||||||
|
if filePath == "" {
|
||||||
|
// 如果 path 为空,根据 skill_name 和 skill_type 构造默认路径
|
||||||
|
projectRoot := h.getProjectRoot()
|
||||||
|
if projectRoot != "" {
|
||||||
|
skillDir := "user"
|
||||||
|
if skill.SkillType == "system" {
|
||||||
|
skillDir = "system"
|
||||||
|
}
|
||||||
|
filePath = filepath.Join(projectRoot, "core", "agents", "skills", skillDir, skill.SkillName, "SKILL.md")
|
||||||
|
fmt.Printf("GetSkillContent: path is empty, constructed path: %s\n", filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果仍然没有路径,返回错误
|
||||||
|
if filePath == "" {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "skill path is empty"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||||
|
fmt.Printf("GetSkillContent error: file not found, path=%s\n", filePath)
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "skill file not found: " + filePath})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("GetSkillContent error: read file failed, path=%s, err=%s\n", filePath, err.Error())
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to read file"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("GetSkillContent success: id=%s, path=%s, size=%d\n", id, filePath, len(content))
|
||||||
|
c.Data(http.StatusOK, "text/plain; charset=utf-8", content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete 删除技能
|
// Delete 删除技能
|
||||||
|
|||||||
Reference in New Issue
Block a user