fix(skills): 兼容 Codex 最小元数据校验
This commit is contained in:
parent
335a991e15
commit
5b40a90887
|
|
@ -0,0 +1,23 @@
|
|||
# Skill 元数据双模式校验
|
||||
|
||||
维护者效率 Skill 使用 Codex/Agent Skill 的最小 frontmatter,只要求 `name` 和
|
||||
`description`。仓库原有校验器则无条件要求 `version`、`metadata.requires.bins`
|
||||
和 `metadata.cliHelp`,导致符合 Codex 规范的 Skill 在仓库测试中被误报。
|
||||
|
||||
本次把校验调整为双模式:
|
||||
|
||||
- 只声明 `name` 和 `description` 时,按 Codex 最小规范校验。
|
||||
- 一旦声明 `version`、`requires.bins` 或 `cliHelp` 中任一执行元数据,就继续要求
|
||||
三项完整,并校验语义版本、`gitlink-cli` 依赖和命令帮助入口。
|
||||
- Skill 名称、目录一致性和描述长度规则保持不变。
|
||||
|
||||
这样不会为了兼容 Codex 而放过不完整的旧版执行元数据,同时确保五个维护专项 Skill
|
||||
和 `gitlink-maintenance-orchestrator` 能通过仓库校验与 Codex `quick_validate.py`。
|
||||
|
||||
验证命令:
|
||||
|
||||
```bash
|
||||
go test ./internal/skillmeta \
|
||||
-run 'TestValidateCatchesBadSkills|TestValidateAcceptsCodexMinimalFrontmatter' \
|
||||
-count=1
|
||||
```
|
||||
|
|
@ -80,17 +80,25 @@ func validateSkill(root, name string) []Problem {
|
|||
case fm.Name != name:
|
||||
add("name", fmt.Sprintf("must equal the directory name %q", name))
|
||||
}
|
||||
if !semverRe.MatchString(fm.Version) {
|
||||
add("version", "must be semantic version X.Y.Z")
|
||||
}
|
||||
if utf8.RuneCountInString(fm.Description) < minDescriptionRunes {
|
||||
add("description", fmt.Sprintf("must be at least %d characters; it is the router's only routing signal", minDescriptionRunes))
|
||||
}
|
||||
if !containsString(fm.Metadata.Requires.Bins, "gitlink-cli") {
|
||||
add("metadata.requires.bins", `must contain "gitlink-cli"`)
|
||||
}
|
||||
if strings.TrimSpace(fm.Metadata.CLIHelp) == "" {
|
||||
add("metadata.cliHelp", "must name the command group, e.g. \"gitlink-cli x --help\"")
|
||||
// Codex-compatible skills only require name and description. Once a skill
|
||||
// opts into GitLink's legacy execution metadata, validate that block as a
|
||||
// complete unit instead of accepting a partially configured declaration.
|
||||
hasExecutionMetadata := fm.Version != "" ||
|
||||
len(fm.Metadata.Requires.Bins) > 0 ||
|
||||
strings.TrimSpace(fm.Metadata.CLIHelp) != ""
|
||||
if hasExecutionMetadata {
|
||||
if !semverRe.MatchString(fm.Version) {
|
||||
add("version", "must be semantic version X.Y.Z")
|
||||
}
|
||||
if !containsString(fm.Metadata.Requires.Bins, "gitlink-cli") {
|
||||
add("metadata.requires.bins", `must contain "gitlink-cli"`)
|
||||
}
|
||||
if strings.TrimSpace(fm.Metadata.CLIHelp) == "" {
|
||||
add("metadata.cliHelp", "must name the command group, e.g. \"gitlink-cli x --help\"")
|
||||
}
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
package skillmeta
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestRepoSkillsValid treats the real skills/ registry as a regression
|
||||
// baseline: once fixed, every SKILL.md must keep passing the schema.
|
||||
|
|
@ -37,3 +41,29 @@ func TestValidateCatchesBadSkills(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAcceptsCodexMinimalFrontmatter(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
skillDir := filepath.Join(root, "gitlink-codex-minimal")
|
||||
if err := os.Mkdir(skillDir, 0o755); err != nil {
|
||||
t.Fatalf("create skill directory: %v", err)
|
||||
}
|
||||
src := []byte(`---
|
||||
name: gitlink-codex-minimal
|
||||
description: "A Codex-compatible skill with only the required routing metadata."
|
||||
---
|
||||
|
||||
# Test skill
|
||||
`)
|
||||
if err := os.WriteFile(filepath.Join(skillDir, "SKILL.md"), src, 0o644); err != nil {
|
||||
t.Fatalf("write SKILL.md: %v", err)
|
||||
}
|
||||
|
||||
problems, err := Validate(root)
|
||||
if err != nil {
|
||||
t.Fatalf("validate testdata: %v", err)
|
||||
}
|
||||
if len(problems) != 0 {
|
||||
t.Fatalf("Codex-compatible minimal frontmatter must pass validation: %v", problems)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue