@@ -310,6 +417,12 @@
createSkillResource,
getSkillResource,
saveSkillResource,
+ createSkillReference,
+ getSkillReference,
+ saveSkillReference,
+ createSkillTemplate,
+ getSkillTemplate,
+ saveSkillTemplate,
getGlobalConstraint,
saveGlobalConstraint
} from '@/api/skills'
@@ -362,26 +475,36 @@
})
const markdownPlaceholder =
- '建议包含:技能用途、输入、输出、步骤与示例。\n\n示例:\n## 技能用途\n请描述技能的目标与限制。\n\n## 输入\n- 输入1...\n\n## 输出\n- 输出1...\n'
+ '建议结构:# Skill Title -> ## Instructions -> ## Examples -> ## Guidelines。\n' +
+ '在正文中引用 templates/...、references/...、scripts/... 可按需加载。\n\n' +
+ '示例:\n# Code Comment Expert\n## Instructions\n- 说明目标、输入、输出与步骤。\n\n## Examples\n- 输入: ...\n- 输出: ...\n\n## Guidelines\n- 约束、格式与质量标准。\n'
const quickBlocks = [
- { label: '用途', content: '\n## 技能用途\n请描述技能目标与适用场景。\n' },
- { label: '输入', content: '\n## 输入\n- 输入字段与格式说明。\n' },
- { label: '输出', content: '\n## 输出\n- 输出字段与格式说明。\n' },
- { label: '步骤', content: '\n## 关键步骤\n1. 第一步\n2. 第二步\n' },
- { label: '示例', content: '\n## 示例\n在此补充示例。\n' },
- { label: '注意事项', content: '\n## 注意事项\n- 需要注意的限制或风险。\n' }
+ { label: '标题', content: '\n# Skill Title\n' },
+ { label: '指令', content: '\n## Instructions\n- 描述该技能要做什么、如何做。\n' },
+ { label: '示例', content: '\n## Examples\n- 输入: ...\n- 输出: ...\n' },
+ { label: '指南', content: '\n## Guidelines\n- 约束、质量标准与注意事项。\n' },
+ { label: '输出格式', content: '\n## Output Format\n1. ...\n2. ...\n' },
+ { label: '引用模板', content: '\n需要结构时参考 templates/your-template.md。\n' },
+ { label: '引用参考', content: '\n如需规范/术语,参考 references/your-reference.md。\n' },
+ { label: '调用脚本', content: '\n如需自动化,执行 scripts/your-script.py "{输入}"。\n' }
]
const scripts = ref([])
const resources = ref([])
+ const references = ref([])
+ const templates = ref([])
const scriptRows = computed(() => skillsFilesToRows(scripts.value))
const resourceRows = computed(() => skillsFilesToRows(resources.value))
+ const referenceRows = computed(() => skillsFilesToRows(references.value))
+ const templateRows = computed(() => skillsFilesToRows(templates.value))
const createDialogVisible = ref(false)
const scriptDialogVisible = ref(false)
const resourceDialogVisible = ref(false)
+ const referenceDialogVisible = ref(false)
+ const templateDialogVisible = ref(false)
const newSkill = reactive({
name: '',
@@ -397,6 +520,14 @@
name: ''
})
+ const newReference = reactive({
+ name: ''
+ })
+
+ const newTemplate = reactive({
+ name: ''
+ })
+
const editorVisible = ref(false)
const editorContent = ref('')
const editorFileName = ref('')
@@ -409,6 +540,8 @@
}
if (editorType.value === 'script') return `脚本:${editorFileName.value}`
if (editorType.value === 'resource') return `资源:${editorFileName.value}`
+ if (editorType.value === 'reference') return `参考:${editorFileName.value}`
+ if (editorType.value === 'template') return `模板:${editorFileName.value}`
if (editorType.value === 'constraint') return `全局约束:${editorFileName.value}`
return `文件编辑:${editorFileName.value}`
})
@@ -464,6 +597,8 @@
form.markdown = detail?.markdown || ''
scripts.value = detail?.scripts || []
resources.value = detail?.resources || []
+ references.value = detail?.references || []
+ templates.value = detail?.templates || []
}
} catch (e) {
ElMessage.error('获取技能详情失败')
@@ -499,6 +634,8 @@
form.markdown = ''
scripts.value = []
resources.value = []
+ references.value = []
+ templates.value = []
activeTab.value = 'config'
}
@@ -600,6 +737,31 @@
form.markdown = `${form.markdown || ''}${content}`
}
+ function insertFileSnippet(kind, fileName) {
+ if (!fileName) return
+ let snippet = ''
+ switch (kind) {
+ case 'script':
+ snippet = `如需自动化处理,可执行 scripts/${fileName} "{输入}"。`
+ break
+ case 'resource':
+ snippet = `背景资料见 resources/${fileName}。`
+ break
+ case 'reference':
+ snippet = `请遵循 references/${fileName} 的规范。`
+ break
+ case 'template':
+ snippet = `输出结构参考 templates/${fileName}。`
+ break
+ default:
+ snippet = ''
+ }
+ if (!snippet) return
+ appendMarkdown(`\n${snippet}\n`)
+ ElMessage.success('已插入到 SKILL.md')
+ activeTab.value = 'config'
+ }
+
function insertFullTemplate() {
if (!form.markdown.trim()) {
form.markdown = defaultSkillTemplate()
@@ -702,6 +864,98 @@
}
}
+ function openReferenceDialog() {
+ if (!activeSkill.value) {
+ ElMessage.warning('请先选择技能')
+ return
+ }
+ newReference.name = ''
+ referenceDialogVisible.value = true
+ }
+
+ async function createReference() {
+ if (!newReference.name.trim()) {
+ ElMessage.warning('请输入参考文件名')
+ return
+ }
+ try {
+ const res = await createSkillReference({
+ tool: activeTool.value,
+ skill: activeSkill.value,
+ fileName: newReference.name.trim()
+ })
+ if (res.code === 0) {
+ referenceDialogVisible.value = false
+ await loadSkillDetail(activeSkill.value)
+ openEditor('reference', res.data.fileName, res.data.content)
+ }
+ } catch (e) {
+ ElMessage.error('创建参考失败')
+ }
+ }
+
+ async function openReferenceEditor(fileName) {
+ if (!fileName) return
+ try {
+ const res = await getSkillReference({
+ tool: activeTool.value,
+ skill: activeSkill.value,
+ fileName
+ })
+ if (res.code === 0) {
+ openEditor('reference', fileName, res.data.content)
+ }
+ } catch (e) {
+ ElMessage.error('读取参考失败')
+ }
+ }
+
+ function openTemplateDialog() {
+ if (!activeSkill.value) {
+ ElMessage.warning('请先选择技能')
+ return
+ }
+ newTemplate.name = ''
+ templateDialogVisible.value = true
+ }
+
+ async function createTemplate() {
+ if (!newTemplate.name.trim()) {
+ ElMessage.warning('请输入模板文件名')
+ return
+ }
+ try {
+ const res = await createSkillTemplate({
+ tool: activeTool.value,
+ skill: activeSkill.value,
+ fileName: newTemplate.name.trim()
+ })
+ if (res.code === 0) {
+ templateDialogVisible.value = false
+ await loadSkillDetail(activeSkill.value)
+ openEditor('template', res.data.fileName, res.data.content)
+ }
+ } catch (e) {
+ ElMessage.error('创建模板失败')
+ }
+ }
+
+ async function openTemplateEditor(fileName) {
+ if (!fileName) return
+ try {
+ const res = await getSkillTemplate({
+ tool: activeTool.value,
+ skill: activeSkill.value,
+ fileName
+ })
+ if (res.code === 0) {
+ openEditor('template', fileName, res.data.content)
+ }
+ } catch (e) {
+ ElMessage.error('读取模板失败')
+ }
+ }
+
function openEditor(type, fileName, content) {
editorType.value = type
editorFileName.value = fileName
@@ -733,6 +987,26 @@
if (res.code === 0) {
ElMessage.success('保存成功')
}
+ } else if (editorType.value === 'reference') {
+ const res = await saveSkillReference({
+ tool: activeTool.value,
+ skill: activeSkill.value,
+ fileName: editorFileName.value,
+ content: editorContent.value
+ })
+ if (res.code === 0) {
+ ElMessage.success('保存成功')
+ }
+ } else if (editorType.value === 'template') {
+ const res = await saveSkillTemplate({
+ tool: activeTool.value,
+ skill: activeSkill.value,
+ fileName: editorFileName.value,
+ content: editorContent.value
+ })
+ if (res.code === 0) {
+ ElMessage.success('保存成功')
+ }
} else if (editorType.value === 'constraint') {
let syncTools = []
if (tools.value.length > 1) {
@@ -779,11 +1053,11 @@
function defaultSkillTemplate() {
return (
- '## 技能用途\n请在这里描述技能的目标、适用场景与限制条件。\n\n' +
- '## 输入\n- 请补充输入格式与示例。\n\n' +
- '## 输出\n- 请补充输出格式与示例。\n\n' +
- '## 关键步骤\n1. 第一步\n2. 第二步\n\n' +
- '## 示例\n在此补充一到两个典型示例。\n'
+ '# Skill Title\n' +
+ '## Instructions\n- 说明目标、输入、输出与步骤。\n\n' +
+ '## Examples\n- 输入: ...\n- 输出: ...\n\n' +
+ '## Guidelines\n- 约束、格式与质量标准。\n\n' +
+ '## Output Format\n1. ...\n2. ...\n'
)
}