diff --git a/web/src/views/Skill.vue b/web/src/views/Skill.vue
index 29358fe..bf23ce9 100644
--- a/web/src/views/Skill.vue
+++ b/web/src/views/Skill.vue
@@ -9,11 +9,13 @@ const {
searchQuery,
filterStatus,
isEditing,
+ isEditingStep2,
isCreating,
isEditingContent,
editForm,
newSkillForm,
newSkillContent,
+ editSkillContent,
filteredSkills,
fetchSkills,
openCreate,
@@ -23,21 +25,20 @@ const {
saveNewSkill,
openEdit,
closeEdit,
- saveEdit,
+ goToEditStep2,
+ saveEditStep2,
+ closeEditStep2,
toggleStatus,
deleteSkill,
- // 导入相关 - 从 useSkills 引入
- fileInputRef,
+ // 导入相关
isImporting,
isImportingDialog,
- importFileName,
importSkillName,
importSkillDesc,
importSkillContent,
isImportStep2,
openImportDialog,
closeImportDialog,
- handleFileChange,
submitImport,
handleFolderSelect,
// 下拉菜单
@@ -215,7 +216,58 @@ onMounted(() => {
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Edit Skill Content
+
Configure skill details in SKILL.md format
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Tip: SKILL.md should contain YAML front matter with name and description, followed by the skill implementation in markdown.
+
+
+
+
+
+
@@ -319,7 +371,7 @@ onMounted(() => {
Import Skill
-
Select a file or folder to import
+
Select a folder containing SKILL.md
-
-
-
-
-
Import File
-
Select a single SKILL.md file
-
+
+
-
-
-
diff --git a/web/src/views/skill/skill.ts b/web/src/views/skill/skill.ts
index 3c40c6b..a04f766 100644
--- a/web/src/views/skill/skill.ts
+++ b/web/src/views/skill/skill.ts
@@ -40,8 +40,9 @@ export function useSkills() {
// 编辑状态
const isEditing = ref(false)
+ const isEditingContent = ref(false) // 创建弹窗的第二步
+ const isEditingStep2 = ref(false) // 编辑弹窗的第二步
const isCreating = ref(false)
- const isEditingContent = ref(false)
const editingSkill = ref
(null)
// 表单
@@ -58,6 +59,7 @@ export function useSkills() {
})
const newSkillContent = ref('')
+ const editSkillContent = ref('') // 编辑弹窗第二步的 SKILL.md 内容
// ============ 方法 ============
@@ -206,38 +208,152 @@ ${newSkillForm.value.skill_desc}
}
}
- // 打开编辑弹窗
- const openEdit = (skill: Skill) => {
+ // 打开编辑弹窗(第一步)
+ const openEdit = async (skill: Skill) => {
editingSkill.value = skill
editForm.value = {
skill_name: skill.skill_name,
skill_desc: skill.skill_desc,
skill_type: skill.skill_type,
}
+
+ // 读取现有的 SKILL.md 内容
+ console.log('Opening edit for skill:', skill)
+ try {
+ const response = await fetch(`${API_BASE}/skill/content?id=${skill.id}`)
+ console.log('Content API response:', response.status, response.statusText)
+ if (response.ok) {
+ const text = await response.text()
+ console.log('Skill content:', text.substring(0, 100))
+ editSkillContent.value = text
+ } else {
+ // API 返回 404 说明文件不存在,生成默认内容
+ console.log('File not found, using default content')
+ editSkillContent.value = `---
+name: ${skill.skill_name}
+description: ${skill.skill_desc || 'A custom skill'}
+category: ${skill.skill_type || 'user'}
+---
+
+# ${skill.skill_name}
+
+${skill.skill_desc || 'A custom skill'}
+
+## Instructions
+
+Describe what this skill does and how it works.
+
+## Tools
+
+List the tools this skill can use:
+
+-
+
+## Examples
+
+Provide some examples of how to use this skill:
+
+\`\`\`
+Example 1:
+\`\`\`
+`
+ }
+ } catch (error) {
+ console.error('Error getting skill content:', error)
+ // 网络错误也使用默认内容
+ editSkillContent.value = `---
+name: ${skill.skill_name}
+description: ${skill.skill_desc || 'A custom skill'}
+category: ${skill.skill_type || 'user'}
+---
+
+# ${skill.skill_name}
+
+${skill.skill_desc || 'A custom skill'}
+
+## Instructions
+
+Describe what this skill does and how it works.
+
+## Tools
+
+List the tools this skill can use:
+
+-
+
+## Examples
+
+Provide some examples of how to use this skill:
+
+\`\`\`
+Example 1:
+\`\`\`
+`
+ }
+
isEditing.value = true
}
// 关闭编辑弹窗
const closeEdit = () => {
isEditing.value = false
+ isEditingStep2.value = false
editingSkill.value = null
+ editSkillContent.value = ''
}
- // 保存编辑
- const saveEdit = async () => {
+ // 编辑第一步:点击 Next,跳转到编辑内容
+ const goToEditStep2 = () => {
+ if (!editForm.value.skill_name || !editForm.value.skill_desc) {
+ ElMessage.warning('Please fill in skill name and description')
+ return
+ }
+ isEditing.value = false
+ isEditingStep2.value = true
+ }
+
+ // 编辑第二步:保存
+ const saveEditStep2 = async () => {
+ if (!editingSkill.value) return
+
try {
- await updateSkill(editingSkill.value!.id, {
- skill_name: editForm.value.skill_name,
- skill_desc: editForm.value.skill_desc,
- skill_type: editForm.value.skill_type,
+ // 将内容转换为文件上传
+ const blob = new Blob([editSkillContent.value], { type: 'text/markdown' })
+ const file = new File([blob], 'SKILL.md', { type: 'text/markdown' })
+
+ const formData = new FormData()
+ formData.append('skill_name', editForm.value.skill_name)
+ formData.append('skill_desc', editForm.value.skill_desc)
+ formData.append('skill_type', editForm.value.skill_type)
+ formData.append('file', file)
+
+ const response = await fetch(`${API_BASE}/skill/${editingSkill.value.id}`, {
+ method: 'PUT',
+ body: formData,
})
- ElMessage.success('Skill updated successfully')
- isEditing.value = false
+
+ if (response.ok) {
+ ElMessage.success('Skill updated successfully')
+ await fetchSkills()
+ isEditingStep2.value = false
+ editingSkill.value = null
+ editSkillContent.value = ''
+ } else {
+ const data = await response.json()
+ ElMessage.error(data.error || 'Failed to update skill')
+ }
} catch (error) {
+ console.error('Failed to update skill:', error)
ElMessage.error('Failed to update skill')
}
}
+ // 关闭编辑第二步弹窗
+ const closeEditStep2 = () => {
+ isEditingStep2.value = false
+ isEditing.value = true
+ }
+
// 切换状态
const toggleStatus = async (skill: Skill) => {
const newStatus = skill.status === 'active' ? 'inactive' : 'active'
@@ -384,7 +500,7 @@ ${newSkillForm.value.skill_desc}
}
}
- // 处理文件夹选择
+ // 处理文件选择(单个 SKILL.md 文件)
const handleFolderSelect = async (event: Event) => {
const input = event.target as HTMLInputElement
const files = input.files
@@ -394,25 +510,13 @@ ${newSkillForm.value.skill_desc}
isImporting.value = true
try {
- const folder = files[0].webkitRelativePath?.split('/')[0] || files[0].name
+ const file = files[0]
- // 查找 SKILL.md 文件
- let skillMdFile: File | null = null
- for (let i = 0; i < files.length; i++) {
- const file = files[i]
- if (file.name === 'SKILL.md' || file.webkitRelativePath?.endsWith('/SKILL.md')) {
- skillMdFile = file
- break
- }
- }
+ // 读取文件内容
+ const content = await file.text()
+ const fileName = file.name.replace('.md', '')
- if (!skillMdFile) {
- ElMessage.error('导入失败:所选文件夹中未找到 SKILL.md 文件')
- return
- }
-
- const content = await skillMdFile.text()
- const { skillName, skillDesc } = parseSkillContent(content, folder)
+ const { skillName, skillDesc } = parseSkillContent(content, fileName)
// 使用导入弹窗显示内容
importSkillName.value = skillName
@@ -422,7 +526,7 @@ ${newSkillForm.value.skill_desc}
isImportStep2.value = true
} catch (error) {
console.error('Import failed:', error)
- ElMessage.error('导入失败,请检查文件夹格式是否正确')
+ ElMessage.error('导入失败,请检查文件格式是否正确')
} finally {
isImporting.value = false
input.value = ''
@@ -453,12 +557,14 @@ ${newSkillForm.value.skill_desc}
searchQuery,
filterStatus,
isEditing,
+ isEditingStep2,
isCreating,
isEditingContent,
editingSkill,
editForm,
newSkillForm,
newSkillContent,
+ editSkillContent,
// Computed
filteredSkills,
// Methods
@@ -470,7 +576,9 @@ ${newSkillForm.value.skill_desc}
saveNewSkill,
openEdit,
closeEdit,
- saveEdit,
+ goToEditStep2,
+ saveEditStep2,
+ closeEditStep2,
toggleStatus,
deleteSkill: handleDeleteSkill,
// 导入相关