-
Notifications
You must be signed in to change notification settings - Fork 0
170 lines (146 loc) · 5.88 KB
/
ci.yaml
File metadata and controls
170 lines (146 loc) · 5.88 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
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_call:
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Validate skill frontmatter
run: |
status=0
for skill_dir in skills/*/; do
# Skip shared directory
if [ "$(basename "$skill_dir")" = "shared" ]; then
continue
fi
skill_file="${skill_dir}SKILL.md"
if [ ! -f "$skill_file" ]; then
echo "ERROR: Missing SKILL.md in $skill_dir"
status=1
continue
fi
# Check for YAML frontmatter delimiters
first_line=$(head -1 "$skill_file")
if [ "$first_line" != "---" ]; then
echo "ERROR: $skill_file missing YAML frontmatter (no opening ---)"
status=1
continue
fi
# Extract frontmatter (between first and second ---)
frontmatter=$(sed -n '2,/^---$/p' "$skill_file" | sed '$d')
# Check for name field (must be lowercase with hyphens)
if ! echo "$frontmatter" | grep -qE '^name: [a-z0-9-]+$'; then
echo "ERROR: $skill_file 'name' missing or not lowercase-with-hyphens"
status=1
fi
# Check for description field (must start with "Use when")
if ! echo "$frontmatter" | grep -qE '^description: "?Use when'; then
echo "ERROR: $skill_file 'description' missing or does not start with 'Use when'"
status=1
fi
echo "OK: $skill_file"
done
exit $status
- name: Validate agent frontmatter
run: |
status=0
for agent_file in .github/agents/*.agent.md; do
[ -f "$agent_file" ] || continue
first_line=$(head -1 "$agent_file")
if [ "$first_line" != "---" ]; then
echo "ERROR: $agent_file missing YAML frontmatter"
status=1
continue
fi
frontmatter=$(sed -n '2,/^---$/p' "$agent_file" | sed '$d')
if ! echo "$frontmatter" | grep -qE '^description:'; then
echo "ERROR: $agent_file missing 'description' in frontmatter"
status=1
fi
if ! echo "$frontmatter" | grep -qE '^handoffs:'; then
echo "ERROR: $agent_file missing 'handoffs' in frontmatter"
status=1
fi
# Detect truncation artefacts in description: unbalanced parens,
# brackets, or backticks render as broken Markdown in Copilot UIs
# (see issue #12 — pharaoh.write-plan and pharaoh.toctree-emit).
description=$(echo "$frontmatter" | sed -n 's/^description:[[:space:]]*//p')
opens=$(echo -n "$description" | tr -cd '(' | wc -c)
closes=$(echo -n "$description" | tr -cd ')' | wc -c)
if [ "$opens" -ne "$closes" ]; then
echo "ERROR: $agent_file 'description' has unbalanced parentheses ($opens '(' vs $closes ')')"
status=1
fi
obrack=$(echo -n "$description" | tr -cd '[' | wc -c)
cbrack=$(echo -n "$description" | tr -cd ']' | wc -c)
if [ "$obrack" -ne "$cbrack" ]; then
echo "ERROR: $agent_file 'description' has unbalanced brackets ($obrack '[' vs $cbrack ']')"
status=1
fi
ticks=$(echo -n "$description" | tr -cd '`' | wc -c)
if [ $((ticks % 2)) -ne 0 ]; then
echo "ERROR: $agent_file 'description' has unbalanced backticks ($ticks total)"
status=1
fi
echo "OK: $agent_file"
done
exit $status
- name: Cross-reference skills and agents
run: |
status=0
# Check every pharaoh skill has a matching agent
for skill_dir in skills/pharaoh-*/; do
skill_name=$(basename "$skill_dir")
# Convert pharaoh-change -> pharaoh.change
agent_name=$(echo "$skill_name" | sed 's/-/./')
agent_file=".github/agents/${agent_name}.agent.md"
if [ ! -f "$agent_file" ]; then
echo "ERROR: Skill $skill_name has no matching agent at $agent_file"
status=1
else
echo "OK: $skill_name <-> $agent_file"
fi
done
# Check every pharaoh agent has a matching skill
for agent_file in .github/agents/pharaoh.*.agent.md; do
[ -f "$agent_file" ] || continue
agent_basename=$(basename "$agent_file" .agent.md)
# Convert pharaoh.change -> pharaoh-change
skill_name=$(echo "$agent_basename" | sed 's/\./-/')
skill_dir="skills/${skill_name}/"
if [ ! -d "$skill_dir" ]; then
echo "ERROR: Agent $agent_basename has no matching skill at $skill_dir"
status=1
fi
done
exit $status
- name: Check internal links
shell: bash
run: |
status=0
shopt -s globstar nullglob
for md_file in *.md docs/**/*.md; do
[ -f "$md_file" ] || continue
dir=$(dirname "$md_file")
links=$(grep -oP '\[.*?\]\(\K[^)]+' "$md_file" | grep -v '^http' | grep -v '^mailto:' | grep -v '^#' | grep -v '^?' || true)
for link in $links; do
path="${link%%#*}"
[ -z "$path" ] && continue
target="${dir}/${path}"
if [ ! -e "$target" ]; then
echo "ERROR: $md_file links to '$link' but $target does not exist"
echo "FAILED" >> /tmp/link_check_failed
fi
done
done
if [ -f /tmp/link_check_failed ]; then
exit 1
fi