-
Notifications
You must be signed in to change notification settings - Fork 11
161 lines (142 loc) · 5.25 KB
/
validate-skill.yml
File metadata and controls
161 lines (142 loc) · 5.25 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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
name: Validate Skill PR
on:
pull_request:
paths:
- 'skills/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Get changed files
id: changed
uses: tj-actions/changed-files@v44
with:
files: 'skills/**/SKILL.md'
separator: '\n'
- name: Validate SKILL.md files
if: steps.changed.outputs.any_changed == 'true'
run: |
set -euo pipefail
ERRORS=0
echo "${{ steps.changed.outputs.all_changed_files }}" | while IFS= read -r file; do
[ -z "$file" ] && continue
echo "Validating: $file"
# Check file exists and is not empty
if [ ! -s "$file" ]; then
echo "::error file=$file::File is empty"
ERRORS=$((ERRORS + 1))
continue
fi
# Extract frontmatter
FRONTMATTER=$(sed -n '/^---$/,/^---$/p' "$file" | sed '1d;$d')
if [ -z "$FRONTMATTER" ]; then
echo "::error file=$file::Missing YAML frontmatter (must start and end with ---)"
ERRORS=$((ERRORS + 1))
continue
fi
# Check required fields
for field in id name description category; do
if ! echo "$FRONTMATTER" | grep -q "^${field}:"; then
echo "::error file=$file::Missing required field: $field"
ERRORS=$((ERRORS + 1))
fi
done
# Validate category
CATEGORY=$(echo "$FRONTMATTER" | grep "^category:" | sed 's/category:\s*//')
VALID_CATEGORIES=(
"Lifestyle"
"Blockchain"
"Databases"
"Research"
"Content & Media"
"Documentation"
"Testing & Security"
"DevOps"
"Data & AI"
"Business"
"Development"
"OpenAI"
"Anthropic"
"Tools"
)
VALID=0
for valid_category in "${VALID_CATEGORIES[@]}"; do
if [ "$CATEGORY" = "$valid_category" ]; then
VALID=1
break
fi
done
if [ -n "$CATEGORY" ] && [ "$VALID" -ne 1 ]; then
echo "::error file=$file::Invalid category '$CATEGORY'. Must be one of: ${VALID_CATEGORIES[*]}"
ERRORS=$((ERRORS + 1))
fi
# Check skill ID matches directory name
SKILL_ID=$(echo "$FRONTMATTER" | grep "^id:" | sed 's/id:\s*//')
DIR_NAME=$(basename "$(dirname "$file")")
if [ -n "$SKILL_ID" ] && [ "$SKILL_ID" != "$DIR_NAME" ]; then
echo "::warning file=$file::Skill id '$SKILL_ID' does not match directory name '$DIR_NAME'"
fi
# Check for prompt injection patterns
BODY=$(sed '1,/^---$/d' "$file" | sed '1,/^---$/d')
INJECTION_PATTERNS=(
"ignore previous instructions"
"ignore all previous"
"disregard your instructions"
"forget your system prompt"
"override your"
"you are now"
"new instructions:"
"system prompt override"
)
for pattern in "${INJECTION_PATTERNS[@]}"; do
if echo "$BODY" | grep -qi "$pattern"; then
echo "::error file=$file::Potential prompt injection detected: '$pattern'"
ERRORS=$((ERRORS + 1))
fi
done
# Check instruction length (rough token estimate: ~4 chars per token)
BODY_LENGTH=$(echo "$BODY" | wc -c)
MAX_CHARS=24000 # ~6000 tokens
if [ "$BODY_LENGTH" -gt "$MAX_CHARS" ]; then
echo "::error file=$file::Instructions too long ($BODY_LENGTH chars, max $MAX_CHARS)"
ERRORS=$((ERRORS + 1))
fi
echo " -> Validation passed for $file"
done
if [ "$ERRORS" -gt 0 ]; then
echo "::error::$ERRORS validation error(s) found"
exit 1
fi
echo "All validations passed!"
- name: Check for duplicate IDs
if: steps.changed.outputs.any_changed == 'true'
run: |
# Collect all skill IDs
IDS=$(find skills -name "SKILL.md" -exec grep -h "^id:" {} \; | sed 's/id:\s*//' | sort)
DUPES=$(echo "$IDS" | uniq -d)
if [ -n "$DUPES" ]; then
echo "::error::Duplicate skill IDs found: $DUPES"
exit 1
fi
echo "No duplicate IDs found."
label:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/github-script@v7
with:
script: |
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['needs-review', 'skill-contribution']
});