Repository navigation
137 lines (128 loc) · 5.21 KB
/
Copy pathvalidate-plugins.yml
File metadata and controls
137 lines (128 loc) · 5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
name: Validate Plugins
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
validate-structure:
name: Validate plugin structure
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate marketplace.json
run: |
echo "Checking marketplace.json..."
python3 -c "
import json, sys
data = json.load(open('.claude-plugin/marketplace.json'))
assert 'name' in data, 'Missing name'
assert 'plugins' in data, 'Missing plugins'
for p in data['plugins']:
assert 'name' in p, f'Plugin missing name: {p}'
assert 'source' in p, f'Plugin missing source: {p}'
print(f' ✓ {p[\"name\"]}')
print(f'All {len(data[\"plugins\"])} plugins valid')
"
- name: Validate plugin.json files
run: |
echo "Checking plugin.json files..."
python3 -c "
import json, glob, sys
files = glob.glob('plugins/*/.claude-plugin/plugin.json')
assert files, 'No plugin.json files found'
for f in sorted(files):
data = json.load(open(f))
assert 'name' in data, f'{f}: missing name'
assert 'version' in data, f'{f}: missing version'
assert 'description' in data, f'{f}: missing description'
print(f' ✓ {data[\"name\"]} v{data[\"version\"]}')
print(f'All {len(files)} plugin.json files valid')
"
- name: Validate skill frontmatter
run: |
echo "Checking SKILL.md frontmatter..."
python3 -c "
import glob, re, sys
try:
import yaml
except ImportError:
import pip._vendor.tomli # fallback - just check regex
yaml = None
files = sorted(glob.glob('plugins/*/skills/*/SKILL.md'))
assert files, 'No SKILL.md files found'
errors = []
warnings = []
for f in files:
content = open(f).read()
match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
if not match:
# Some skills are reference-only (no frontmatter) — warn, don't fail
warnings.append(f'{f}: no YAML frontmatter (reference skill?)')
continue
if yaml:
fm = yaml.safe_load(match.group(1))
if not fm or 'description' not in fm:
errors.append(f'{f}: missing description in frontmatter')
continue
if len(fm['description']) < 50:
errors.append(f'{f}: description too short')
continue
print(f' ✓ {f}')
for w in warnings:
print(f' ⚠ {w}')
if errors:
for e in errors:
print(f' ✗ {e}', file=sys.stderr)
sys.exit(1)
print(f'All {len(files)} skills checked ({len(warnings)} warnings)')
"
- name: Validate agent frontmatter
run: |
echo "Checking agent frontmatter..."
python3 -c "
import glob, re, sys
try:
import yaml
except ImportError:
yaml = None
files = sorted(glob.glob('plugins/*/agents/*.md'))
assert files, 'No agent files found'
errors = []
warnings = []
required = ['name', 'description']
for f in files:
content = open(f).read()
match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
if not match:
errors.append(f'{f}: missing YAML frontmatter')
continue
if yaml:
try:
fm = yaml.safe_load(match.group(1))
except yaml.YAMLError as e:
# Some agents have inline colons in description that break YAML parsing
# Check with regex fallback: verify name: and description: lines exist
raw = match.group(1)
has_name = bool(re.search(r'^name:', raw, re.MULTILINE))
has_desc = bool(re.search(r'^description:', raw, re.MULTILINE))
if has_name and has_desc:
warnings.append(f'{f}: YAML parse warning (inline colons) — name and description present via regex')
print(f' ⚠ {f}: YAML parse issue, regex fallback OK')
continue
else:
errors.append(f'{f}: YAML parse error and missing required fields')
continue
if not fm:
errors.append(f'{f}: empty frontmatter')
continue
for field in required:
if field not in fm:
errors.append(f'{f}: missing {field}')
print(f' ✓ {f}')
if errors:
for e in errors:
print(f' ✗ {e}', file=sys.stderr)
sys.exit(1)
print(f'All {len(files)} agents checked ({len(warnings)} warnings)')
"