-
Notifications
You must be signed in to change notification settings - Fork 2
214 lines (186 loc) · 9.69 KB
/
component-validation.yml
File metadata and controls
214 lines (186 loc) · 9.69 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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
name: Plugin Component Validation
on:
pull_request:
types: [opened, synchronize, ready_for_review]
paths:
- "plugins/plugin-dev/commands/**"
- "plugins/plugin-dev/skills/**"
- "plugins/plugin-dev/agents/**"
- "plugins/plugin-dev/hooks/**"
# Cancel any in-progress validation for the same PR when new commits are pushed
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
validate:
# Skip bots and draft PRs
if: |
github.actor != 'dependabot[bot]' &&
github.actor != 'claude[bot]' &&
github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
issues: write # Recommended by claude-code-action docs for full functionality
id-token: write
actions: read
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
- name: Get changed plugin component files
id: changed-files
env:
GH_TOKEN: ${{ github.token }}
run: |
# Get list of changed files in this PR, filtered to plugin components
# Note: gh pr diff includes deleted files, so we filter to only existing files
changed_files=$(gh pr diff ${{ github.event.pull_request.number }} --name-only | \
grep -E '^plugins/plugin-dev/(commands|skills|agents|hooks)/' | \
grep -v '\-SUGGESTED\.md$' | \
grep -v '\-BACKUP\.md$' | \
grep -v '\-OLD\.md$' | \
while read -r file; do [ -f "$file" ] && echo "$file"; done || true)
if [ -z "$changed_files" ]; then
echo "No plugin component files changed"
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "Changed plugin component files:"
echo "$changed_files"
echo "has_changes=true" >> "$GITHUB_OUTPUT"
# Store as newline-separated list for the prompt
{
echo "files<<EOF"
echo "$changed_files"
echo "EOF"
} >> "$GITHUB_OUTPUT"
fi
- name: Validate plugin components
if: steps.changed-files.outputs.has_changes == 'true'
uses: anthropics/claude-code-action@b47fd721da662d48c5680e154ad16a73ed74d2e0 # v1.0.93
id: validate
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
Validate ONLY the changed plugin component files listed below.
## Context
- Repository: ${{ github.repository }}
- PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}
## Changed Files to Validate
```
${{ steps.changed-files.outputs.files }}
```
**IMPORTANT**: Only validate the files listed above. Do NOT validate other files in the plugin directories.
## Instructions
1. Read and validate ONLY the changed files listed above
2. Return structured JSON with validation results
## Validation Rules
### Commands (`plugins/plugin-dev/commands/*.md`)
For each changed command file, verify:
- [ ] YAML frontmatter exists with `name`, `description`, `allowed-tools` fields
- [ ] `description` is 60 characters or fewer
- [ ] `allowed-tools` follows these rules:
- For simple commands needing only specific CLI operations: prefer Bash prefix patterns like `Bash(gh pr:*)`, `Bash(npm run:*)` (uses prefix matching with `:*` wildcard)
- Unrestricted `Bash` is acceptable for workflow/scaffolding commands that legitimately need filesystem operations (mkdir, git init, directory creation) - verify the command's stated purpose justifies broader access
- `Read` is always allowed (reading files is safe)
- `Write` is allowed ONLY if the command creates/exports files (e.g., status export)
- `AskUserQuestion` is always allowed (user interaction is safe)
- Do NOT flag tools that are appropriate for the command's purpose
- [ ] Body uses imperative form ("Do X", "Create Y") not second person ("You should X")
- [ ] No draft file patterns in filename (`*-SUGGESTED.md`, `*-BACKUP.md`, `*-OLD.md`)
### Skills (`plugins/plugin-dev/skills/*/SKILL.md`)
For each changed skill SKILL.md file, verify:
- [ ] YAML frontmatter exists with `name`, `description` fields
- [ ] `description` uses third-person with trigger phrases (starts with "This skill should be used when...")
- [ ] Body length is reasonable (core concepts, not exhaustive documentation)
### Agents (`plugins/plugin-dev/agents/*.md`)
For each changed agent file, verify:
- [ ] YAML frontmatter exists with `name`, `description`, `model`, `color`, `tools` fields
- [ ] `description` field includes `<example>` blocks (at least 2, ideally 3-4)
- [ ] Each `<example>` block has proper structure (Context line, `user:` and `assistant:` dialogue lines, and `<commentary>` XML tags)
- [ ] `tools` list is present and appropriate for the agent's purpose
### Hooks (`plugins/plugin-dev/hooks/hooks.json`)
For changed hooks file, verify:
- [ ] File is valid JSON
- [ ] Event types are valid: UserPromptSubmit, PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, PreCompact, Notification
- [ ] Matcher patterns are valid regex (test by attempting to compile)
## Output Format
Return ONLY valid JSON matching this schema:
```json
{
"commands_valid": true/false,
"commands_issues": ["issue 1", "issue 2"],
"skills_valid": true/false,
"skills_issues": ["issue 1", "issue 2"],
"agents_valid": true/false,
"agents_issues": ["issue 1", "issue 2"],
"hooks_valid": true/false,
"hooks_issues": ["issue 1", "issue 2"],
"all_valid": true/false,
"summary": "Brief overall summary"
}
```
Set `all_valid` to true only if all changed files pass validation.
Set category to valid (true) if no changed files exist for that category.
Include specific file names and line numbers in issues when possible.
claude_args: |
--model claude-opus-4-6
--allowedTools "Read,Glob,Grep"
--json-schema '{"type":"object","properties":{"commands_valid":{"type":"boolean"},"commands_issues":{"type":"array","items":{"type":"string"}},"skills_valid":{"type":"boolean"},"skills_issues":{"type":"array","items":{"type":"string"}},"agents_valid":{"type":"boolean"},"agents_issues":{"type":"array","items":{"type":"string"}},"hooks_valid":{"type":"boolean"},"hooks_issues":{"type":"array","items":{"type":"string"}},"all_valid":{"type":"boolean"},"summary":{"type":"string"}},"required":["all_valid"]}'
- name: Check validation result
if: always() && steps.changed-files.outputs.has_changes == 'true' && steps.validate.outputs.structured_output != ''
env:
VALIDATION_OUTPUT: ${{ steps.validate.outputs.structured_output }}
run: |
{
echo "## Validation Results"
echo '```json'
echo "$VALIDATION_OUTPUT"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
# Validate JSON output before processing
if ! all_valid=$(echo "$VALIDATION_OUTPUT" | jq -r '.all_valid' 2>/dev/null); then
echo "Error: Invalid JSON output from validation step"
echo "Raw output:"
echo "$VALIDATION_OUTPUT"
exit 1
fi
if [ "$all_valid" != "true" ]; then
{
echo ""
echo "### Issues Found"
# Group issues by component type for cleaner output
# Use // 0 to handle null/missing arrays gracefully
if [ "$(echo "$VALIDATION_OUTPUT" | jq -r '(.commands_issues | length) // 0')" -gt 0 ]; then
echo "**Commands:**"
echo "$VALIDATION_OUTPUT" | jq -r '.commands_issues[] | "- " + .'
fi
if [ "$(echo "$VALIDATION_OUTPUT" | jq -r '(.skills_issues | length) // 0')" -gt 0 ]; then
echo "**Skills:**"
echo "$VALIDATION_OUTPUT" | jq -r '.skills_issues[] | "- " + .'
fi
if [ "$(echo "$VALIDATION_OUTPUT" | jq -r '(.agents_issues | length) // 0')" -gt 0 ]; then
echo "**Agents:**"
echo "$VALIDATION_OUTPUT" | jq -r '.agents_issues[] | "- " + .'
fi
if [ "$(echo "$VALIDATION_OUTPUT" | jq -r '(.hooks_issues | length) // 0')" -gt 0 ]; then
echo "**Hooks:**"
echo "$VALIDATION_OUTPUT" | jq -r '.hooks_issues[] | "- " + .'
fi
} >> "$GITHUB_STEP_SUMMARY"
echo ""
echo "Component validation failed. Please fix the issues above."
exit 1
fi
{
echo ""
echo "All changed plugin components are valid."
} >> "$GITHUB_STEP_SUMMARY"
- name: Skip validation (no changes)
if: steps.changed-files.outputs.has_changes != 'true'
run: |
echo "## Validation Skipped" >> "$GITHUB_STEP_SUMMARY"
echo "No plugin component files were changed in this PR." >> "$GITHUB_STEP_SUMMARY"