Files
gh-netresearch-claude-code-…/skills/agents/scripts/validate-structure.sh
2025-11-30 08:43:13 +08:00

179 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Validate AGENTS.md structure compliance
set -euo pipefail
PROJECT_DIR="${1:-.}"
cd "$PROJECT_DIR"
ERRORS=0
WARNINGS=0
error() {
echo "❌ ERROR: $*"
((ERRORS++))
}
warning() {
echo "⚠️ WARNING: $*"
((WARNINGS++))
}
success() {
echo "$*"
}
# Check if file has managed header
check_managed_header() {
local file="$1"
if grep -q "^<!-- Managed by agent:" "$file"; then
success "Managed header present: $file"
return 0
else
warning "Missing managed header: $file"
return 1
fi
}
# Check if root is thin (≤50 lines or has scope index)
check_root_is_thin() {
local file="$1"
local line_count=$(wc -l < "$file")
if [ "$line_count" -le 50 ]; then
success "Root is thin: $line_count lines"
return 0
elif grep -q "## Index of scoped AGENTS.md" "$file"; then
success "Root has scope index (verbose style acceptable)"
return 0
else
error "Root is bloated: $line_count lines and no scope index"
return 1
fi
}
# Check if root has precedence statement
check_precedence_statement() {
local file="$1"
if grep -qi "precedence" "$file" && grep -qi "closest.*AGENTS.md.*wins" "$file"; then
success "Precedence statement present"
return 0
else
error "Missing precedence statement in root"
return 1
fi
}
# Check if scoped file has all 9 sections
check_scoped_sections() {
local file="$1"
local required_sections=(
"## Overview"
"## Setup & environment"
"## Build & tests"
"## Code style & conventions"
"## Security & safety"
"## PR/commit checklist"
"## Good vs. bad examples"
"## When stuck"
)
local missing=()
for section in "${required_sections[@]}"; do
if ! grep -q "^$section" "$file"; then
missing+=("$section")
fi
done
if [ ${#missing[@]} -eq 0 ]; then
success "All required sections present: $file"
return 0
else
error "Missing sections in $file: ${missing[*]}"
return 1
fi
}
# Check if scope index links work
check_scope_links() {
local root_file="$1"
if ! grep -q "## Index of scoped AGENTS.md" "$root_file"; then
return 0 # No index, skip check
fi
# Extract links from scope index
local links=$(sed -n '/## Index of scoped AGENTS.md/,/^##/p' "$root_file" | grep -o '\./[^)]*AGENTS.md' || true)
if [ -z "$links" ]; then
warning "Scope index present but no links found"
return 1
fi
local broken=()
while read -r link; do
# Remove leading ./
local clean_link="${link#./}"
local full_path="$PROJECT_DIR/$clean_link"
if [ ! -f "$full_path" ]; then
broken+=("$link")
fi
done <<< "$links"
if [ ${#broken[@]} -eq 0 ]; then
success "All scope index links work"
return 0
else
error "Broken scope index links: ${broken[*]}"
return 1
fi
}
# Main validation
echo "Validating AGENTS.md structure in: $PROJECT_DIR"
echo ""
# Check root AGENTS.md
ROOT_FILE="$PROJECT_DIR/AGENTS.md"
if [ ! -f "$ROOT_FILE" ]; then
error "Root AGENTS.md not found"
else
echo "=== Root AGENTS.md ==="
check_managed_header "$ROOT_FILE"
check_root_is_thin "$ROOT_FILE"
check_precedence_statement "$ROOT_FILE"
check_scope_links "$ROOT_FILE"
echo ""
fi
# Check scoped AGENTS.md files
SCOPED_FILES=$(find "$PROJECT_DIR" -name "AGENTS.md" -not -path "$ROOT_FILE" 2>/dev/null || true)
if [ -n "$SCOPED_FILES" ]; then
echo "=== Scoped AGENTS.md Files ==="
while read -r file; do
rel_path="${file#$PROJECT_DIR/}"
echo "Checking: $rel_path"
check_managed_header "$file"
check_scoped_sections "$file"
echo ""
done <<< "$SCOPED_FILES"
fi
# Summary
echo "=== Validation Summary ==="
if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
echo "✅ All checks passed!"
exit 0
elif [ $ERRORS -eq 0 ]; then
echo "⚠️ Validation passed with $WARNINGS warning(s)"
exit 0
else
echo "❌ Validation failed with $ERRORS error(s) and $WARNINGS warning(s)"
exit 1
fi