161ef94b4f
Check engine pin consistency / Dockerfile / CI pin consistency (push) Successful in 8s
Sirius CI/CD Pipeline / Detect Changes (push) Successful in 23s
Validate Docker Configuration / Validate Docker Compose Configuration (push) Successful in 47s
Sirius CI/CD Pipeline / Build API (${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Build UI (${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Engine Manifest (push) Has been cancelled
Sirius CI/CD Pipeline / Merge API Manifest (push) Has been cancelled
Sirius CI/CD Pipeline / Merge UI Manifest (push) Has been cancelled
Sirius CI/CD Pipeline / Build Engine (${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Build Infra (${{ matrix.service }}, ${{ matrix.platform }}) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Infra Manifest (sirius-postgres) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Infra Manifest (sirius-rabbitmq) (push) Has been cancelled
Sirius CI/CD Pipeline / Merge Infra Manifest (sirius-valkey) (push) Has been cancelled
Sirius CI/CD Pipeline / Integration Test (push) Has been cancelled
Sirius CI/CD Pipeline / Public Stack Contract (push) Has been cancelled
Sirius CI/CD Pipeline / Dispatch Demo Deployment (sirius-demo branch) (push) Has been cancelled
Sirius CI/CD Pipeline / Dispatch Demo Canary (main branch) (push) Has been cancelled
Sirius CI/CD Pipeline / Guard Registry Namespace (push) Has been cancelled
380 lines
9.8 KiB
Bash
Executable File
380 lines
9.8 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Sirius Agent Identity Linting System
|
|
# Validates agent identity quality, consistency, and compliance
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
AGENTS_DIR="../../.cursor/agents"
|
|
SCRIPT_DIR="$(dirname "$0")"
|
|
LOG_FILE="tmp/agent-lint-$(date +%Y%m%d-%H%M%S).log"
|
|
|
|
# Create tmp directory if it doesn't exist
|
|
mkdir -p tmp
|
|
|
|
# Required YAML front matter fields
|
|
REQUIRED_FIELDS=("name" "title" "description" "role_type" "version" "last_updated" "llm_context" "context_window_target")
|
|
OPTIONAL_FIELDS=("author" "specialization" "technology_stack" "system_integration_level" "categories" "tags" "related_docs" "dependencies")
|
|
|
|
# Valid values for specific fields
|
|
VALID_ROLE_TYPES=("engineering" "design" "product" "operations" "qa" "documentation")
|
|
VALID_LLM_CONTEXTS=("high" "medium" "low")
|
|
VALID_INTEGRATION_LEVELS=("none" "low" "medium" "high")
|
|
|
|
# Length constraints
|
|
MIN_LINES=150
|
|
MAX_LINES=500
|
|
TARGET_MIN=200
|
|
TARGET_MAX=400
|
|
|
|
# Counters
|
|
TOTAL_FILES=0
|
|
PASSED_FILES=0
|
|
FAILED_FILES=0
|
|
WARNINGS=0
|
|
|
|
# Functions
|
|
log() {
|
|
echo -e "${BLUE}[$(date +'%H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
success() {
|
|
echo -e "${GREEN}✅ $1${NC}" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
warning() {
|
|
echo -e "${YELLOW}⚠️ $1${NC}" | tee -a "$LOG_FILE"
|
|
((WARNINGS++))
|
|
}
|
|
|
|
error() {
|
|
echo -e "${RED}❌ $1${NC}" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Check if file has YAML front matter
|
|
check_yaml_front_matter() {
|
|
local file="$1"
|
|
local has_yaml=false
|
|
|
|
if head -n 1 "$file" | grep -q "^---$"; then
|
|
has_yaml=true
|
|
fi
|
|
|
|
if [ "$has_yaml" = false ]; then
|
|
error "Missing YAML front matter in $file"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Extract YAML front matter
|
|
extract_yaml() {
|
|
local file="$1"
|
|
local yaml_start=0
|
|
local yaml_end=0
|
|
local line_num=0
|
|
|
|
while IFS= read -r line; do
|
|
((line_num++))
|
|
if [ "$line" = "---" ]; then
|
|
if [ $yaml_start -eq 0 ]; then
|
|
yaml_start=$line_num
|
|
else
|
|
yaml_end=$line_num
|
|
break
|
|
fi
|
|
fi
|
|
done < "$file"
|
|
|
|
if [ $yaml_start -gt 0 ] && [ $yaml_end -gt 0 ]; then
|
|
sed -n "${yaml_start},${yaml_end}p" "$file"
|
|
fi
|
|
}
|
|
|
|
# Check required fields
|
|
check_required_fields() {
|
|
local file="$1"
|
|
local yaml_content
|
|
local missing_fields=()
|
|
|
|
yaml_content=$(extract_yaml "$file")
|
|
if [ -z "$yaml_content" ]; then
|
|
return 1
|
|
fi
|
|
|
|
for field in "${REQUIRED_FIELDS[@]}"; do
|
|
if ! echo "$yaml_content" | grep -q "^${field}:"; then
|
|
missing_fields+=("$field")
|
|
fi
|
|
done
|
|
|
|
if [ ${#missing_fields[@]} -gt 0 ]; then
|
|
error "Missing required fields in $file: ${missing_fields[*]}"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Validate enumerated field values
|
|
validate_enum_fields() {
|
|
local file="$1"
|
|
local yaml_content
|
|
local errors=0
|
|
|
|
yaml_content=$(extract_yaml "$file")
|
|
if [ -z "$yaml_content" ]; then
|
|
return 1
|
|
fi
|
|
|
|
# Check role_type
|
|
local role_type=$(echo "$yaml_content" | grep "^role_type:" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -d '"')
|
|
if [ -n "$role_type" ]; then
|
|
local valid=false
|
|
for valid_type in "${VALID_ROLE_TYPES[@]}"; do
|
|
if [ "$role_type" = "$valid_type" ]; then
|
|
valid=true
|
|
break
|
|
fi
|
|
done
|
|
if [ "$valid" = false ]; then
|
|
error "Invalid role_type '$role_type' in $file. Must be one of: ${VALID_ROLE_TYPES[*]}"
|
|
((errors++))
|
|
fi
|
|
fi
|
|
|
|
# Check llm_context
|
|
local llm_context=$(echo "$yaml_content" | grep "^llm_context:" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -d '"')
|
|
if [ -n "$llm_context" ]; then
|
|
local valid=false
|
|
for valid_ctx in "${VALID_LLM_CONTEXTS[@]}"; do
|
|
if [ "$llm_context" = "$valid_ctx" ]; then
|
|
valid=true
|
|
break
|
|
fi
|
|
done
|
|
if [ "$valid" = false ]; then
|
|
error "Invalid llm_context '$llm_context' in $file. Must be one of: ${VALID_LLM_CONTEXTS[*]}"
|
|
((errors++))
|
|
fi
|
|
fi
|
|
|
|
# Check system_integration_level (optional)
|
|
local integration_level=$(echo "$yaml_content" | grep "^system_integration_level:" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -d '"')
|
|
if [ -n "$integration_level" ]; then
|
|
local valid=false
|
|
for valid_level in "${VALID_INTEGRATION_LEVELS[@]}"; do
|
|
if [ "$integration_level" = "$valid_level" ]; then
|
|
valid=true
|
|
break
|
|
fi
|
|
done
|
|
if [ "$valid" = false ]; then
|
|
error "Invalid system_integration_level '$integration_level' in $file. Must be one of: ${VALID_INTEGRATION_LEVELS[*]}"
|
|
((errors++))
|
|
fi
|
|
fi
|
|
|
|
return $errors
|
|
}
|
|
|
|
# Validate version format (semver)
|
|
validate_version() {
|
|
local file="$1"
|
|
local yaml_content
|
|
|
|
yaml_content=$(extract_yaml "$file")
|
|
if [ -z "$yaml_content" ]; then
|
|
return 1
|
|
fi
|
|
|
|
local version=$(echo "$yaml_content" | grep "^version:" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -d '"')
|
|
if [ -n "$version" ]; then
|
|
if ! echo "$version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
|
|
error "Invalid version format '$version' in $file. Must be semver (e.g., 1.0.0)"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Validate date format (ISO 8601)
|
|
validate_date() {
|
|
local file="$1"
|
|
local yaml_content
|
|
|
|
yaml_content=$(extract_yaml "$file")
|
|
if [ -z "$yaml_content" ]; then
|
|
return 1
|
|
fi
|
|
|
|
local date=$(echo "$yaml_content" | grep "^last_updated:" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -d '"')
|
|
if [ -n "$date" ]; then
|
|
if ! echo "$date" | grep -qE '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'; then
|
|
error "Invalid date format '$date' in $file. Must be YYYY-MM-DD"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Check length constraints
|
|
check_length() {
|
|
local file="$1"
|
|
local line_count=$(wc -l < "$file")
|
|
|
|
if [ $line_count -lt $MIN_LINES ]; then
|
|
error "File $file is too short ($line_count lines). Minimum: $MIN_LINES"
|
|
return 1
|
|
elif [ $line_count -gt $MAX_LINES ]; then
|
|
error "File $file is too long ($line_count lines). Maximum: $MAX_LINES"
|
|
return 1
|
|
elif [ $line_count -lt $TARGET_MIN ] || [ $line_count -gt $TARGET_MAX ]; then
|
|
warning "File $file length ($line_count lines) outside target range ($TARGET_MIN-$TARGET_MAX)"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Check context window target
|
|
check_context_target() {
|
|
local file="$1"
|
|
local yaml_content
|
|
|
|
yaml_content=$(extract_yaml "$file")
|
|
if [ -z "$yaml_content" ]; then
|
|
return 1
|
|
fi
|
|
|
|
local target=$(echo "$yaml_content" | grep "^context_window_target:" | cut -d: -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -d '"')
|
|
if [ -n "$target" ]; then
|
|
# Only check if it's a valid integer
|
|
if [[ "$target" =~ ^[0-9]+$ ]]; then
|
|
if [ "$target" -lt $MIN_LINES ] || [ "$target" -gt $MAX_LINES ]; then
|
|
warning "context_window_target $target in $file outside valid range ($MIN_LINES-$MAX_LINES)"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Validate file naming
|
|
check_filename() {
|
|
local file="$1"
|
|
local basename=$(basename "$file")
|
|
|
|
if ! echo "$basename" | grep -qE '^[a-z0-9-]+\.agent\.md$'; then
|
|
error "Invalid filename format: $basename. Must match [a-z0-9-]+.agent.md"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Main validation function
|
|
validate_file() {
|
|
local file="$1"
|
|
local file_errors=0
|
|
|
|
log "Validating $file..."
|
|
|
|
# Check filename format
|
|
if ! check_filename "$file"; then
|
|
((file_errors++))
|
|
fi
|
|
|
|
# Check YAML front matter exists
|
|
if ! check_yaml_front_matter "$file"; then
|
|
((file_errors++))
|
|
return 1 # Can't continue without YAML
|
|
fi
|
|
|
|
# Check required fields
|
|
if ! check_required_fields "$file"; then
|
|
((file_errors++))
|
|
fi
|
|
|
|
# Validate enum fields
|
|
if ! validate_enum_fields "$file"; then
|
|
((file_errors++))
|
|
fi
|
|
|
|
# Validate version format
|
|
if ! validate_version "$file"; then
|
|
((file_errors++))
|
|
fi
|
|
|
|
# Validate date format
|
|
if ! validate_date "$file"; then
|
|
((file_errors++))
|
|
fi
|
|
|
|
# Check length constraints
|
|
if ! check_length "$file"; then
|
|
((file_errors++))
|
|
fi
|
|
|
|
# Check context window target
|
|
check_context_target "$file"
|
|
|
|
if [ $file_errors -eq 0 ]; then
|
|
success "$(basename $file) passed validation"
|
|
return 0
|
|
else
|
|
error "$(basename $file) failed validation with $file_errors error(s)"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Main script
|
|
log "Starting agent identity validation..."
|
|
log "Agents directory: $AGENTS_DIR"
|
|
log "=================================================="
|
|
|
|
# Find all agent files
|
|
for file in "$AGENTS_DIR"/*.agent.md; do
|
|
if [ -f "$file" ]; then
|
|
((TOTAL_FILES++))
|
|
if validate_file "$file"; then
|
|
((PASSED_FILES++))
|
|
else
|
|
((FAILED_FILES++))
|
|
fi
|
|
echo ""
|
|
fi
|
|
done
|
|
|
|
# Summary
|
|
log "=================================================="
|
|
log "Validation Summary:"
|
|
log " Total files: $TOTAL_FILES"
|
|
log " Passed: $PASSED_FILES"
|
|
log " Failed: $FAILED_FILES"
|
|
log " Warnings: $WARNINGS"
|
|
log "=================================================="
|
|
|
|
if [ $FAILED_FILES -eq 0 ]; then
|
|
success "All agent identities passed validation!"
|
|
if [ $WARNINGS -gt 0 ]; then
|
|
warning "Validation passed with $WARNINGS warning(s)"
|
|
exit 0
|
|
fi
|
|
exit 0
|
|
else
|
|
error "Validation failed for $FAILED_FILES agent identit(y|ies)"
|
|
exit 1
|
|
fi
|
|
|