@pennyfarthing/core 7.6.0 → 7.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +109 -201
  2. package/package.json +1 -1
  3. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  4. package/packages/core/dist/cli/commands/doctor.js +91 -0
  5. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  6. package/packages/core/dist/cli/commands/init.js +31 -0
  7. package/packages/core/dist/cli/commands/init.js.map +1 -1
  8. package/packages/core/dist/cli/commands/update.js +31 -0
  9. package/packages/core/dist/cli/commands/update.js.map +1 -1
  10. package/pennyfarthing-dist/agents/architect.md +48 -53
  11. package/pennyfarthing-dist/agents/dev.md +74 -164
  12. package/pennyfarthing-dist/agents/devops.md +44 -39
  13. package/pennyfarthing-dist/agents/handoff.md +46 -23
  14. package/pennyfarthing-dist/agents/orchestrator.md +84 -255
  15. package/pennyfarthing-dist/agents/pm.md +40 -50
  16. package/pennyfarthing-dist/agents/reviewer-preflight.md +58 -26
  17. package/pennyfarthing-dist/agents/reviewer.md +107 -298
  18. package/pennyfarthing-dist/agents/sm-file-summary.md +51 -30
  19. package/pennyfarthing-dist/agents/sm-finish.md +59 -38
  20. package/pennyfarthing-dist/agents/sm-handoff.md +40 -33
  21. package/pennyfarthing-dist/agents/sm-setup.md +89 -47
  22. package/pennyfarthing-dist/agents/sm.md +171 -558
  23. package/pennyfarthing-dist/agents/tea.md +77 -146
  24. package/pennyfarthing-dist/agents/tech-writer.md +43 -24
  25. package/pennyfarthing-dist/agents/testing-runner.md +73 -30
  26. package/pennyfarthing-dist/agents/ux-designer.md +39 -25
  27. package/pennyfarthing-dist/agents/workflow-status-check.md +34 -16
  28. package/pennyfarthing-dist/commands/benchmark.md +19 -1
  29. package/pennyfarthing-dist/commands/continue-session.md +1 -1
  30. package/pennyfarthing-dist/commands/solo.md +5 -0
  31. package/pennyfarthing-dist/commands/theme-maker.md +5 -5
  32. package/pennyfarthing-dist/commands/work.md +1 -1
  33. package/pennyfarthing-dist/guides/XML-TAGS.md +179 -0
  34. package/pennyfarthing-dist/guides/agent-behavior.md +37 -2
  35. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +432 -0
  36. package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +27 -7
  37. package/pennyfarthing-dist/guides/scale-levels.md +114 -0
  38. package/pennyfarthing-dist/personas/themes/gilligans-island.yaml +2 -2
  39. package/pennyfarthing-dist/personas/themes/star-trek-tos.yaml +1 -1
  40. package/pennyfarthing-dist/scripts/core/agent-session.sh +13 -7
  41. package/pennyfarthing-dist/scripts/core/check-context.sh +25 -8
  42. package/pennyfarthing-dist/scripts/core/prime.sh +57 -32
  43. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +45 -4
  44. package/pennyfarthing-dist/scripts/git/git-status-all.sh +32 -7
  45. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +30 -11
  46. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +80 -23
  47. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.mjs +393 -0
  48. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +20 -0
  49. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +402 -0
  50. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +7 -0
  51. package/pennyfarthing-dist/scripts/hooks/tests/question-reflector.test.mjs +545 -0
  52. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +94 -0
  53. package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +10 -152
  54. package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +14 -4
  55. package/pennyfarthing-dist/scripts/jira/jira-sync.sh +12 -4
  56. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +11 -99
  57. package/pennyfarthing-dist/scripts/lib/common.sh +55 -0
  58. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +97 -0
  59. package/pennyfarthing-dist/scripts/misc/deploy.sh +13 -1
  60. package/pennyfarthing-dist/scripts/misc/statusline.sh +27 -22
  61. package/pennyfarthing-dist/scripts/story/create-story.sh +14 -154
  62. package/pennyfarthing-dist/scripts/story/size-story.sh +12 -192
  63. package/pennyfarthing-dist/scripts/story/story-template.sh +12 -156
  64. package/pennyfarthing-dist/scripts/test/ground-truth-judge.py +24 -93
  65. package/pennyfarthing-dist/scripts/test/swebench-judge.py +33 -59
  66. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +575 -0
  67. package/pennyfarthing-dist/scripts/workflow/check.py +502 -0
  68. package/pennyfarthing-dist/skills/skill-registry.yaml +52 -16
  69. package/pennyfarthing-dist/skills/sprint/skill.md +1 -1
  70. package/pennyfarthing-dist/templates/settings.local.json.template +11 -0
@@ -0,0 +1,575 @@
1
+ #!/usr/bin/env zsh
2
+ # validate-agent-schema.sh
3
+ # Validates agent files against schema and best practices
4
+ #
5
+ # Schema Rules:
6
+ # - Primary agents: Must have required XML tags, everything in tags
7
+ # - Subagents: Must have YAML frontmatter (validated separately)
8
+ #
9
+ # Best Practice Rules:
10
+ # - First <critical> within line 30
11
+ # - <on-activation> within line 100
12
+ # - File size under 300 lines
13
+ # - All XML tags properly closed
14
+ # - Checklists use proper markdown format
15
+ #
16
+ # Exit codes:
17
+ # 0 = All agents valid
18
+ # 1 = One or more agents invalid
19
+
20
+ set -euo pipefail
21
+
22
+ # Find project root
23
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
24
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
25
+ AGENTS_DIR="$PROJECT_ROOT/pennyfarthing-dist/agents"
26
+
27
+ # Colors
28
+ RED='\033[0;31m'
29
+ GREEN='\033[0;32m'
30
+ YELLOW='\033[0;33m'
31
+ BLUE='\033[0;34m'
32
+ NC='\033[0m'
33
+
34
+ # Primary agents (not subagents)
35
+ PRIMARY_AGENTS=(
36
+ "sm.md"
37
+ "tea.md"
38
+ "dev.md"
39
+ "reviewer.md"
40
+ "orchestrator.md"
41
+ "architect.md"
42
+ "pm.md"
43
+ "devops.md"
44
+ "tech-writer.md"
45
+ "ux-designer.md"
46
+ )
47
+
48
+ # Subagents (have YAML frontmatter, different schema)
49
+ SUBAGENTS=(
50
+ "handoff.md"
51
+ "sm-setup.md"
52
+ "sm-finish.md"
53
+ "sm-file-summary.md"
54
+ "sm-handoff.md"
55
+ "workflow-status-check.md"
56
+ "testing-runner.md"
57
+ "reviewer-preflight.md"
58
+ )
59
+
60
+ # Required tags for primary agents
61
+ # Note: persona is emitted by agent-session.sh, not in agent files
62
+ REQUIRED_TAGS=(
63
+ "role"
64
+ "helpers"
65
+ "exit"
66
+ )
67
+
68
+ # Optional but recommended tags
69
+ RECOMMENDED_TAGS=(
70
+ "critical"
71
+ "on-activation"
72
+ "skills"
73
+ )
74
+
75
+ # Tags that require checklists
76
+ CHECKLIST_TAGS=(
77
+ "gate"
78
+ "handoff-gate"
79
+ "self-review"
80
+ "review-checklist"
81
+ )
82
+
83
+ # Mindset tags - each primary agent should have one
84
+ # Format: agent_file:expected_tag
85
+ declare -A MINDSET_TAGS=(
86
+ ["sm.md"]="coordination-discipline"
87
+ ["tea.md"]="test-paranoia"
88
+ ["dev.md"]="minimalist-discipline"
89
+ ["reviewer.md"]="adversarial-mindset"
90
+ ["orchestrator.md"]="systems-thinking"
91
+ ["architect.md"]="pragmatic-restraint"
92
+ ["pm.md"]="ruthless-prioritization"
93
+ ["devops.md"]="automation-discipline"
94
+ ["tech-writer.md"]="clarity-obsession"
95
+ ["ux-designer.md"]="consistency-guardian"
96
+ )
97
+
98
+ # Best practice thresholds
99
+ MAX_LINES=300
100
+ FIRST_CRITICAL_MAX_LINE=30
101
+ ON_ACTIVATION_MAX_LINE=100
102
+
103
+ # Counters
104
+ valid_count=0
105
+ warning_count=0
106
+ invalid_count=0
107
+
108
+ # Verbose mode
109
+ VERBOSE=false
110
+ FIX_MODE=false
111
+
112
+ # Parse arguments
113
+ while [[ $# -gt 0 ]]; do
114
+ case $1 in
115
+ -v|--verbose) VERBOSE=true; shift ;;
116
+ --fix) FIX_MODE=true; shift ;;
117
+ -h|--help)
118
+ echo "Usage: validate-agent-schema.sh [-v|--verbose] [--fix]"
119
+ echo " -v, --verbose Show detailed validation info"
120
+ echo " --fix Attempt to fix simple issues (not implemented)"
121
+ exit 0
122
+ ;;
123
+ *) shift ;;
124
+ esac
125
+ done
126
+
127
+ # =============================================================================
128
+ # Validation Functions
129
+ # =============================================================================
130
+
131
+ check_xml_tags_balanced() {
132
+ local file="$1"
133
+ local errors=()
134
+
135
+ # Find all opening tags
136
+ local open_tags=($(grep -oE '<[a-z][-a-z]*>' "$file" 2>/dev/null | sed 's/[<>]//g' | sort -u))
137
+
138
+ for tag in "${open_tags[@]}"; do
139
+ local open_count=$(grep -c "<${tag}>" "$file" 2>/dev/null || echo 0)
140
+ local close_count=$(grep -c "</${tag}>" "$file" 2>/dev/null || echo 0)
141
+
142
+ if [[ $open_count -ne $close_count ]]; then
143
+ errors+=("Tag <$tag> has $open_count opens but $close_count closes")
144
+ fi
145
+ done
146
+
147
+ if [[ ${#errors[@]} -gt 0 ]]; then
148
+ for error in "${errors[@]}"; do
149
+ echo " ${YELLOW}→${NC} $error"
150
+ done
151
+ return 1
152
+ fi
153
+ return 0
154
+ }
155
+
156
+ check_required_tags() {
157
+ local file="$1"
158
+ local missing=()
159
+
160
+ for tag in "${REQUIRED_TAGS[@]}"; do
161
+ if ! grep -q "<${tag}>" "$file" 2>/dev/null; then
162
+ missing+=("$tag")
163
+ fi
164
+ done
165
+
166
+ if [[ ${#missing[@]} -gt 0 ]]; then
167
+ echo " ${RED}→${NC} Missing required tags: ${missing[*]}"
168
+ return 1
169
+ fi
170
+ return 0
171
+ }
172
+
173
+ check_recommended_tags() {
174
+ local file="$1"
175
+ local missing=()
176
+
177
+ for tag in "${RECOMMENDED_TAGS[@]}"; do
178
+ if ! grep -q "<${tag}>" "$file" 2>/dev/null; then
179
+ missing+=("$tag")
180
+ fi
181
+ done
182
+
183
+ if [[ ${#missing[@]} -gt 0 ]]; then
184
+ echo " ${YELLOW}→${NC} Missing recommended tags: ${missing[*]}"
185
+ return 1 # Warning, not error
186
+ fi
187
+ return 0
188
+ }
189
+
190
+ check_checklist_format() {
191
+ local file="$1"
192
+ local errors=()
193
+
194
+ for tag in "${CHECKLIST_TAGS[@]}"; do
195
+ if grep -q "<${tag}>" "$file" 2>/dev/null; then
196
+ # Extract content between tags
197
+ local content=$(sed -n "/<${tag}>/,/<\/${tag}>/p" "$file" 2>/dev/null)
198
+
199
+ # Check for checklist items
200
+ if echo "$content" | grep -qE '^\s*-\s*\['; then
201
+ # Validate checklist format: - [ ] or - [x]
202
+ local bad_format=$(echo "$content" | grep -E '^\s*-\s*\[' | grep -vE '^\s*-\s*\[\s*[x ]?\s*\]' || true)
203
+ if [[ -n "$bad_format" ]]; then
204
+ errors+=("Tag <$tag> has malformed checklist items")
205
+ fi
206
+ fi
207
+ fi
208
+ done
209
+
210
+ if [[ ${#errors[@]} -gt 0 ]]; then
211
+ for error in "${errors[@]}"; do
212
+ echo " ${YELLOW}→${NC} $error"
213
+ done
214
+ return 1
215
+ fi
216
+ return 0
217
+ }
218
+
219
+ check_best_practices() {
220
+ local file="$1"
221
+ local filename=$(basename "$file")
222
+ local warnings=()
223
+ local errors=()
224
+
225
+ # Check file length
226
+ local line_count=$(wc -l < "$file" | tr -d ' ')
227
+ if [[ $line_count -gt $MAX_LINES ]]; then
228
+ errors+=("File has $line_count lines (max: $MAX_LINES)")
229
+ fi
230
+
231
+ # Check first <critical> position
232
+ local first_critical=$(grep -n '<critical>' "$file" 2>/dev/null | head -1 | cut -d: -f1)
233
+ if [[ -n "$first_critical" && $first_critical -gt $FIRST_CRITICAL_MAX_LINE ]]; then
234
+ warnings+=("First <critical> at line $first_critical (target: ≤$FIRST_CRITICAL_MAX_LINE)")
235
+ fi
236
+
237
+ # Check <on-activation> position
238
+ local on_activation=$(grep -n '<on-activation>' "$file" 2>/dev/null | head -1 | cut -d: -f1)
239
+ if [[ -n "$on_activation" && $on_activation -gt $ON_ACTIVATION_MAX_LINE ]]; then
240
+ warnings+=("<on-activation> at line $on_activation (target: ≤$ON_ACTIVATION_MAX_LINE)")
241
+ fi
242
+
243
+ # Check for content outside XML tags (after header)
244
+ # Allow: # Header, blank lines, ## sections inside flows
245
+ # This is a soft check - some content outside tags is OK
246
+
247
+ # Report
248
+ if [[ ${#errors[@]} -gt 0 ]]; then
249
+ for error in "${errors[@]}"; do
250
+ echo " ${RED}→${NC} $error"
251
+ done
252
+ fi
253
+
254
+ if [[ ${#warnings[@]} -gt 0 ]]; then
255
+ for warning in "${warnings[@]}"; do
256
+ echo " ${YELLOW}→${NC} $warning"
257
+ done
258
+ fi
259
+
260
+ [[ ${#errors[@]} -eq 0 ]]
261
+ }
262
+
263
+ check_header_format() {
264
+ local file="$1"
265
+ local filename=$(basename "$file" .md)
266
+
267
+ # Check for proper header: # AgentName Agent - Title
268
+ local header=$(head -1 "$file")
269
+ if ! echo "$header" | grep -qE '^# .+ Agent'; then
270
+ echo " ${YELLOW}→${NC} Header should be '# Name Agent - Description'"
271
+ return 1
272
+ fi
273
+ return 0
274
+ }
275
+
276
+ check_no_orphan_content() {
277
+ local file="$1"
278
+ local errors=()
279
+
280
+ # Content after last closing tag is an error (except whitespace)
281
+ local last_close_line=$(grep -n '</[a-z][-a-z]*>' "$file" 2>/dev/null | tail -1 | cut -d: -f1)
282
+ local total_lines=$(wc -l < "$file" | tr -d ' ')
283
+
284
+ if [[ -n "$last_close_line" ]]; then
285
+ # Check if there's non-whitespace content after last tag
286
+ local remaining=$(tail -n +$((last_close_line + 1)) "$file" | grep -v '^[[:space:]]*$' || true)
287
+ if [[ -n "$remaining" ]]; then
288
+ errors+=("Content found after last closing tag (line $last_close_line)")
289
+ fi
290
+ fi
291
+
292
+ if [[ ${#errors[@]} -gt 0 ]]; then
293
+ for error in "${errors[@]}"; do
294
+ echo " ${YELLOW}→${NC} $error"
295
+ done
296
+ return 1
297
+ fi
298
+ return 0
299
+ }
300
+
301
+ check_mindset_tag() {
302
+ local file="$1"
303
+ local filename=$(basename "$file")
304
+
305
+ # Get expected mindset tag for this agent
306
+ local expected_tag="${MINDSET_TAGS[$filename]:-}"
307
+
308
+ if [[ -z "$expected_tag" ]]; then
309
+ # No mindset tag expected for this agent
310
+ return 0
311
+ fi
312
+
313
+ if ! grep -q "<${expected_tag}>" "$file" 2>/dev/null; then
314
+ echo " ${RED}→${NC} Missing mindset tag: <$expected_tag>"
315
+ return 1
316
+ fi
317
+
318
+ # Verify it's closed
319
+ if ! grep -q "</${expected_tag}>" "$file" 2>/dev/null; then
320
+ echo " ${RED}→${NC} Unclosed mindset tag: <$expected_tag>"
321
+ return 1
322
+ fi
323
+
324
+ return 0
325
+ }
326
+
327
+ check_parameters_section() {
328
+ local file="$1"
329
+
330
+ # If file has <helpers> tag, it should also have <parameters>
331
+ if grep -q "<helpers>" "$file" 2>/dev/null; then
332
+ if ! grep -q "<parameters>" "$file" 2>/dev/null; then
333
+ echo " ${YELLOW}→${NC} Has <helpers> but missing <parameters> section"
334
+ return 1
335
+ fi
336
+ fi
337
+ return 0
338
+ }
339
+
340
+ check_arguments_section() {
341
+ local file="$1"
342
+
343
+ # Subagents should have <arguments> section
344
+ if ! grep -q "<arguments>" "$file" 2>/dev/null; then
345
+ echo " ${YELLOW}→${NC} Missing <arguments> section"
346
+ return 1
347
+ fi
348
+
349
+ # Verify it's closed
350
+ if ! grep -q "</arguments>" "$file" 2>/dev/null; then
351
+ echo " ${RED}→${NC} Unclosed <arguments> tag"
352
+ return 1
353
+ fi
354
+
355
+ return 0
356
+ }
357
+
358
+ check_all_content_in_tags() {
359
+ local file="$1"
360
+ local orphan_lines=()
361
+ local in_tag=0
362
+ local line_num=0
363
+
364
+ while IFS= read -r line; do
365
+ ((line_num++))
366
+
367
+ # Skip header line (# Agent Name)
368
+ [[ $line_num -eq 1 && $line =~ ^#\ ]] && continue
369
+
370
+ # Skip blank lines
371
+ [[ -z "${line// /}" ]] && continue
372
+
373
+ # Count opening and closing tags on this line
374
+ local opens=$(echo "$line" | grep -oE '<[a-z][-a-z]*>' | wc -l | tr -d ' ')
375
+ local closes=$(echo "$line" | grep -oE '</[a-z][-a-z]*>' | wc -l | tr -d ' ')
376
+
377
+ # If at depth 0 and line doesn't contain a tag, it's orphaned
378
+ if [[ $in_tag -eq 0 && $opens -eq 0 ]]; then
379
+ orphan_lines+=("$line_num")
380
+ fi
381
+
382
+ in_tag=$((in_tag + opens - closes))
383
+ done < "$file"
384
+
385
+ if [[ ${#orphan_lines[@]} -gt 0 ]]; then
386
+ local first_few="${orphan_lines[*]:0:5}"
387
+ echo " ${RED}→${NC} Content outside XML tags at lines: $first_few..."
388
+ return 1
389
+ fi
390
+ return 0
391
+ }
392
+
393
+ # =============================================================================
394
+ # Main Validation
395
+ # =============================================================================
396
+
397
+ validate_primary_agent() {
398
+ local file="$1"
399
+ local filename=$(basename "$file")
400
+ local has_error=false
401
+ local has_warning=false
402
+
403
+ if [[ "$VERBOSE" == "true" ]]; then
404
+ echo ""
405
+ echo -e "${BLUE}Validating:${NC} $filename"
406
+ fi
407
+
408
+ # Required checks (errors)
409
+ if ! check_xml_tags_balanced "$file"; then
410
+ has_error=true
411
+ fi
412
+
413
+ if ! check_required_tags "$file"; then
414
+ has_error=true
415
+ fi
416
+
417
+ # Best practice checks (can be warnings or errors)
418
+ if ! check_best_practices "$file"; then
419
+ has_error=true
420
+ fi
421
+
422
+ # All content must be within XML tags (required)
423
+ if ! check_all_content_in_tags "$file"; then
424
+ has_error=true
425
+ fi
426
+
427
+ # Mindset tag check (required)
428
+ if ! check_mindset_tag "$file"; then
429
+ has_error=true
430
+ fi
431
+
432
+ # Parameters section check (warning if helpers present)
433
+ if ! check_parameters_section "$file"; then
434
+ has_warning=true
435
+ fi
436
+
437
+ # Warning checks
438
+ if ! check_recommended_tags "$file"; then
439
+ has_warning=true
440
+ fi
441
+
442
+ if ! check_checklist_format "$file"; then
443
+ has_warning=true
444
+ fi
445
+
446
+ if ! check_header_format "$file"; then
447
+ has_warning=true
448
+ fi
449
+
450
+ # Report result
451
+ if [[ "$has_error" == "true" ]]; then
452
+ echo -e "${RED}✗ INVALID:${NC} $filename"
453
+ ((invalid_count++))
454
+ return 1
455
+ elif [[ "$has_warning" == "true" ]]; then
456
+ echo -e "${YELLOW}⚠ WARNING:${NC} $filename"
457
+ ((warning_count++))
458
+ return 0
459
+ else
460
+ echo -e "${GREEN}✓ VALID:${NC} $filename"
461
+ ((valid_count++))
462
+ return 0
463
+ fi
464
+ }
465
+
466
+ validate_subagent() {
467
+ local file="$1"
468
+ local filename=$(basename "$file")
469
+
470
+ # Check for YAML frontmatter
471
+ local first_line=$(head -1 "$file")
472
+ if [[ "$first_line" != "---" ]]; then
473
+ echo -e "${RED}✗ INVALID:${NC} $filename (missing YAML frontmatter)"
474
+ ((invalid_count++))
475
+ return 1
476
+ fi
477
+
478
+ # Check for closing ---
479
+ local frontmatter_end=$(awk '/^---$/{count++; if(count==2) print NR}' "$file")
480
+ if [[ -z "$frontmatter_end" ]]; then
481
+ echo -e "${RED}✗ INVALID:${NC} $filename (unclosed YAML frontmatter)"
482
+ ((invalid_count++))
483
+ return 1
484
+ fi
485
+
486
+ # Check required frontmatter fields
487
+ local frontmatter=$(sed -n "2,$((frontmatter_end - 1))p" "$file")
488
+ local missing=()
489
+
490
+ for field in name description tools model; do
491
+ if ! echo "$frontmatter" | grep -q "^${field}:"; then
492
+ missing+=("$field")
493
+ fi
494
+ done
495
+
496
+ if [[ ${#missing[@]} -gt 0 ]]; then
497
+ echo -e "${RED}✗ INVALID:${NC} $filename (missing: ${missing[*]})"
498
+ ((invalid_count++))
499
+ return 1
500
+ fi
501
+
502
+ # Check XML tags are balanced
503
+ if ! check_xml_tags_balanced "$file"; then
504
+ echo -e "${RED}✗ INVALID:${NC} $filename"
505
+ ((invalid_count++))
506
+ return 1
507
+ fi
508
+
509
+ # Check for <arguments> section
510
+ if ! check_arguments_section "$file"; then
511
+ echo -e "${YELLOW}⚠ WARNING:${NC} $filename (subagent)"
512
+ ((warning_count++))
513
+ return 0
514
+ fi
515
+
516
+ echo -e "${GREEN}✓ VALID:${NC} $filename (subagent)"
517
+ ((valid_count++))
518
+ return 0
519
+ }
520
+
521
+ # =============================================================================
522
+ # Run Validation
523
+ # =============================================================================
524
+
525
+ echo "Agent Schema Validator"
526
+ echo "======================"
527
+ echo "Directory: $AGENTS_DIR"
528
+ echo ""
529
+
530
+ echo -e "${BLUE}Primary Agents:${NC}"
531
+ for agent in "${PRIMARY_AGENTS[@]}"; do
532
+ if [[ -f "$AGENTS_DIR/$agent" ]]; then
533
+ validate_primary_agent "$AGENTS_DIR/$agent" || true
534
+ else
535
+ echo -e "${YELLOW}⚠ MISSING:${NC} $agent"
536
+ ((warning_count++))
537
+ fi
538
+ done
539
+
540
+ echo ""
541
+ echo -e "${BLUE}Subagents:${NC}"
542
+ for subagent in "${SUBAGENTS[@]}"; do
543
+ if [[ -f "$AGENTS_DIR/$subagent" ]]; then
544
+ validate_subagent "$AGENTS_DIR/$subagent" || true
545
+ else
546
+ echo -e "${YELLOW}⚠ MISSING:${NC} $subagent"
547
+ ((warning_count++))
548
+ fi
549
+ done
550
+
551
+ # =============================================================================
552
+ # Summary
553
+ # =============================================================================
554
+
555
+ echo ""
556
+ echo "====================================="
557
+ echo "Validation Summary"
558
+ echo "====================================="
559
+ total=$((${#PRIMARY_AGENTS[@]} + ${#SUBAGENTS[@]}))
560
+ echo -e "Valid: ${GREEN}$valid_count${NC} / $total"
561
+ echo -e "Warnings: ${YELLOW}$warning_count${NC}"
562
+ echo -e "Invalid: ${RED}$invalid_count${NC}"
563
+ echo ""
564
+
565
+ if [[ $invalid_count -eq 0 ]]; then
566
+ if [[ $warning_count -eq 0 ]]; then
567
+ echo -e "${GREEN}All agents pass schema validation!${NC}"
568
+ else
569
+ echo -e "${YELLOW}All agents valid with warnings.${NC}"
570
+ fi
571
+ exit 0
572
+ else
573
+ echo -e "${RED}$invalid_count agent(s) failed validation.${NC}"
574
+ exit 1
575
+ fi