diff --git a/.claude/skills/skill-linter/.skillfish.json b/.claude/skills/skill-linter/.skillfish.json new file mode 100644 index 00000000..7bfea597 --- /dev/null +++ b/.claude/skills/skill-linter/.skillfish.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "name": "skill-linter", + "owner": "majesticlabs-dev", + "repo": "majestic-marketplace", + "path": ".claude/skills/skill-linter", + "branch": "master", + "sha": "2ded694ad1e9b7103d6aa6d2d2243d2dc79f23b4", + "source": "manual" +} \ No newline at end of file diff --git a/.claude/skills/skill-linter/SKILL.md b/.claude/skills/skill-linter/SKILL.md new file mode 100644 index 00000000..ac0c59ba --- /dev/null +++ b/.claude/skills/skill-linter/SKILL.md @@ -0,0 +1,227 @@ +--- +name: skill-linter +description: Validate skills against agentskills.io specification. Use when adding new skills to the marketplace, reviewing skill PRs, checking skill compliance, or running quality gates on skills. Validates frontmatter fields (name, description, compatibility, metadata, allowed-tools), directory naming, line limits, and structure. +allowed-tools: Bash Read Glob Grep +--- + +# Skill Linter + +## When to Use + +- Adding new skills to the marketplace +- Reviewing skill PRs +- Running quality gates before merge +- Checking existing skills for compliance + +## Validation Rules + +### Required Frontmatter + +| Field | Constraints | +|-------|-------------| +| `name` | 1-64 chars, lowercase alphanumeric + hyphens, no leading/trailing/consecutive hyphens, must match parent directory name | +| `description` | 1-1024 chars, non-empty, should include keywords for discoverability | + +### Optional Frontmatter + +| Field | Constraints | +|-------|-------------| +| `compatibility` | 1-500 chars, environment requirements | +| `metadata` | Key-value pairs (string values only) | +| `allowed-tools` | Space-delimited tool list (FAIL on commas or array syntax) | + +### Structure Requirements + +| Rule | Requirement | +|------|-------------| +| Directory name | Must match `name` field exactly | +| SKILL.md | Required, must exist | +| Line limit | Max 500 lines in SKILL.md | +| Subdirectories | Only `scripts/`, `references/`, `assets/` allowed | + +### Content Quality Rules + +| Rule | Requirement | +|------|-------------| +| No ASCII art | Box-drawing characters (─│┌┐└┘├┤┬┴┼), arrows (↑↓←→↔), and decorative diagrams waste tokens. LLMs tokenize character-by-character, not visually. Use plain lists or tables instead. | +| No decorative quotes | Inspirational quotes or attributions ("As X said...") have no functional value for LLM execution. | +| No persona statements | "You are an expert..." wastes tokens. Use **Audience:** / **Goal:** framing instead. | +| Functional content only | Every line should improve LLM behavior. Ask: "Does this help Claude execute better?" | + +### Audience/Goal Framing (Required) + +Replace persona roleplay with audience-focused framing: + +**❌ Bad (persona):** +``` +You are an expert software engineer with deep expertise in testing. +Your role is to analyze code and generate thorough test coverage. +``` + +**✅ Good (audience/goal):** +``` +**Audience:** Developers needing test coverage for new or changed code. + +**Goal:** Generate comprehensive tests based on specified test type and framework. +``` + +**Rationale:** "Explain X for audience Y" yields better-tailored outputs than "Act as persona Z". + +**ASCII Art Detection Pattern:** +```regex +[─│┌┐└┘├┤┬┴┼╭╮╯╰═║╔╗╚╝╠╣╦╩╬↑↓←→↔⇒⇐⇔▲▼◄►]{3,} +``` + +Files matching this pattern should be flagged for review. + +### Description Routing Quality (WARN) + +Skill descriptions are routing logic, not documentation. They answer: +- **When** to use this skill (trigger conditions) +- **When NOT** to use (negative routing) +- **What outputs** to expect (success criteria) + +| Quality Level | Example | +|---------------|---------| +| Good | "Use when implementing Stimulus controllers. Not for React components. Outputs controller with targets and actions." | +| Adequate | "Use when working with Stimulus controllers in Rails applications." | +| Poor | "Stimulus controller development skill." | +| Anti-pattern | "Comprehensive, powerful toolkit for building cutting-edge Stimulus controllers." | + +Templates inside skills are encouraged — loaded only on trigger, essentially free tokens. + +### Skill Disambiguation (INFO) + +When multiple skills cover similar domains, include negative routing: + +**Example (minitest vs rspec):** + +| Skill | Description Routing | +|-------|-------------------| +| `minitest-coder` | "Use when writing Minitest tests. Not for RSpec — use rspec-coder instead." | +| `rspec-coder` | "Use when writing RSpec tests. Not for Minitest — use minitest-coder instead." | + +"Don't use when..." is as important as "Use when..." for routing accuracy. + +### Name Pattern + +```regex +^[a-z][a-z0-9]*(-[a-z0-9]+)*$ +``` + +**Valid:** `my-skill`, `skill1`, `api-v2-handler` +**Invalid:** `-skill`, `skill-`, `my--skill`, `MySkill`, `my_skill` + +### Command/Agent Invocation Patterns + +Skills should primarily provide knowledge, not orchestration. If invocations are needed: + +| Pattern | Status | Use Instead | +|---------|--------|-------------| +| `Skill("command", args: "...")` | ❌ Deprecated | `/command args` | +| `SlashCommand("command", ...)` | ❌ Deprecated | `/command args` | +| `Task(subagent_type="agent", ...)` | ✅ Correct | (no change) | + +**✅ Preferred command invocation:** +``` +/majestic:config tech_stack generic +/majestic-engineer:tdd-workflow +/majestic-ralph:start "task" --max-iterations 50 +``` + +**❌ Deprecated patterns:** +``` +Skill("config-reader", args: "tech_stack generic") +SlashCommand("majestic:build-task", args: "...") +``` + +**Note:** Agent invocation via `Task()` is correct - there is no `@agent` syntax. + +## Usage + +### Validate Single Skill + +```bash +./scripts/validate-skill.sh path/to/skill-name +``` + +### Validate All Marketplace Skills + +```bash +for skill in plugins/*/skills/*/; do + ./scripts/validate-skill.sh "$skill" +done +``` + +### CI Integration + +Add to pre-commit hook or CI pipeline: + +```yaml +- name: Lint Skills + run: | + for skill in plugins/*/skills/*/; do + .claude/skills/skill-linter/scripts/validate-skill.sh "$skill" || exit 1 + done +``` + +## Validation Script + +The linter script at `scripts/validate-skill.sh` performs these checks: + +1. **Directory exists** with SKILL.md file +2. **Frontmatter present** with YAML delimiters +3. **Name field valid** - pattern, length, matches directory +4. **Description field valid** - present, length constraints +5. **Optional fields valid** - if present, match constraints +6. **Line count** - under 500 lines +7. **Subdirectory names** - only allowed directories +8. **No ASCII art** - box-drawing characters outside fenced code blocks (WARN) +9. **No persona statements** - "You are a/an/the" outside fenced code blocks (FAIL) +10. **Description routing quality** - routing keywords present (WARN) +11. **Marketing copy detection** - buzzwords in description (WARN) + +## Error Codes + +| Code | Meaning | +|------|---------| +| 0 | All validations passed | +| 1 | Missing SKILL.md | +| 2 | Invalid frontmatter | +| 3 | Name validation failed | +| 4 | Description validation failed | +| 5 | Optional field validation failed | +| 6 | Line limit exceeded | +| 7 | Invalid subdirectory | +| 8 | ASCII art detected (warning) | +| 9 | Persona statement detected | +| 10 | Description routing quality (warning) | +| 11 | Marketing copy detected (warning) | + +## Example Output + +``` +Validating: plugins/majestic-tools/skills/brainstorming + +[PASS] SKILL.md exists +[PASS] Frontmatter present +[PASS] Name 'brainstorming' valid (12 chars) +[PASS] Name matches directory +[PASS] Description valid (156 chars) +[PASS] Line count: 87/500 +[PASS] Subdirectories valid +[PASS] No ASCII art outside code blocks +[PASS] No persona statements +[PASS] Description has routing keywords +[PASS] No marketing copy in description + +Result: ALL CHECKS PASSED +``` + +## Spec Reference + +Based on [agentskills.io/specification](https://agentskills.io/specification): + +- **Progressive disclosure** - Metadata ~100 tokens at startup, full content <5000 tokens when activated +- **Reference files** - Keep one level deep from SKILL.md +- **Token budget** - Main SKILL.md recommended under 500 lines diff --git a/.claude/skills/skill-linter/scripts/validate-skill.sh b/.claude/skills/skill-linter/scripts/validate-skill.sh new file mode 100755 index 00000000..4822eb11 --- /dev/null +++ b/.claude/skills/skill-linter/scripts/validate-skill.sh @@ -0,0 +1,261 @@ +#!/bin/bash +# Skill Linter - Validates skills against agentskills.io specification +# Usage: ./validate-skill.sh path/to/skill-directory + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' # No Color + +# Counters +PASS=0 +FAIL=0 +WARN=0 + +pass() { + echo -e "${GREEN}[PASS]${NC} $1" + PASS=$((PASS + 1)) +} + +fail() { + echo -e "${RED}[FAIL]${NC} $1" + FAIL=$((FAIL + 1)) +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" + WARN=$((WARN + 1)) +} + +# Validate arguments +if [ $# -lt 1 ]; then + echo "Usage: $0 " + echo "Example: $0 plugins/majestic-tools/skills/brainstorming" + exit 1 +fi + +SKILL_DIR="$1" +SKILL_DIR="${SKILL_DIR%/}" # Remove trailing slash if present + +echo "" +echo "Validating: $SKILL_DIR" +echo "-------------------------------------------" + +# 1. Check directory exists +if [ ! -d "$SKILL_DIR" ]; then + fail "Directory does not exist: $SKILL_DIR" + exit 1 +fi + +# 2. Check SKILL.md exists +SKILL_FILE="$SKILL_DIR/SKILL.md" +if [ ! -f "$SKILL_FILE" ]; then + fail "SKILL.md not found" + exit 1 +fi +pass "SKILL.md exists" + +# 3. Extract frontmatter +CONTENT=$(cat "$SKILL_FILE") + +# Check for frontmatter delimiters +if ! echo "$CONTENT" | head -1 | grep -q '^---$'; then + fail "Missing frontmatter opening delimiter (---)" + exit 2 +fi + +# Find closing delimiter (skip first line) +CLOSING_LINE=$(echo "$CONTENT" | tail -n +2 | grep -n '^---$' | head -1 | cut -d: -f1) +if [ -z "$CLOSING_LINE" ]; then + fail "Missing frontmatter closing delimiter (---)" + exit 2 +fi +pass "Frontmatter delimiters present" + +# Extract frontmatter content (between delimiters) - portable approach +# CLOSING_LINE is position of --- in tail-n+2 output, so frontmatter is lines 2 to CLOSING_LINE in original +FRONTMATTER=$(echo "$CONTENT" | sed -n "2,${CLOSING_LINE}p") + +# 4. Validate name field +NAME=$(echo "$FRONTMATTER" | grep '^name:' | sed 's/^name:[[:space:]]*//' | tr -d '"' | tr -d "'") + +if [ -z "$NAME" ]; then + fail "Missing required field: name" + exit 3 +fi + +# Check name length (1-64) +NAME_LEN=${#NAME} +if [ "$NAME_LEN" -lt 1 ] || [ "$NAME_LEN" -gt 64 ]; then + fail "Name length must be 1-64 chars (got $NAME_LEN)" + exit 3 +fi + +# Check name pattern: lowercase alphanumeric with hyphens +# Must not start/end with hyphen, no consecutive hyphens +if ! echo "$NAME" | grep -qE '^[a-z][a-z0-9]*(-[a-z0-9]+)*$'; then + # More specific error messages + if echo "$NAME" | grep -qE '^-'; then + fail "Name cannot start with hyphen: $NAME" + elif echo "$NAME" | grep -qE '-$'; then + fail "Name cannot end with hyphen: $NAME" + elif echo "$NAME" | grep -qE '--'; then + fail "Name cannot contain consecutive hyphens: $NAME" + elif echo "$NAME" | grep -qE '[A-Z]'; then + fail "Name must be lowercase: $NAME" + elif echo "$NAME" | grep -qE '[_]'; then + fail "Name cannot contain underscores (use hyphens): $NAME" + else + fail "Name must match pattern ^[a-z][a-z0-9]*(-[a-z0-9]+)*$: $NAME" + fi + exit 3 +fi +pass "Name '$NAME' valid ($NAME_LEN chars)" + +# 5. Check name matches directory +DIR_NAME=$(basename "$SKILL_DIR") +if [ "$NAME" != "$DIR_NAME" ]; then + fail "Name '$NAME' does not match directory name '$DIR_NAME'" + exit 3 +fi +pass "Name matches directory" + +# 6. Validate description field +# Handle multi-line descriptions +DESCRIPTION=$(echo "$FRONTMATTER" | awk '/^description:/{flag=1; sub(/^description:[[:space:]]*/, ""); print; next} flag && /^[a-z_-]+:/{exit} flag{print}' | tr '\n' ' ' | sed 's/[[:space:]]*$//') + +if [ -z "$DESCRIPTION" ]; then + fail "Missing required field: description" + exit 4 +fi + +# Check description length (1-1024) +DESC_LEN=${#DESCRIPTION} +if [ "$DESC_LEN" -lt 1 ]; then + fail "Description cannot be empty" + exit 4 +fi +if [ "$DESC_LEN" -gt 1024 ]; then + fail "Description exceeds 1024 chars (got $DESC_LEN)" + exit 4 +fi +pass "Description valid ($DESC_LEN chars)" + +# 7. Validate optional fields if present + +# Check compatibility (if present, max 500 chars) +COMPAT=$(echo "$FRONTMATTER" | grep '^compatibility:' | sed 's/^compatibility:[[:space:]]*//' || true) +if [ -n "$COMPAT" ]; then + COMPAT_LEN=${#COMPAT} + if [ "$COMPAT_LEN" -gt 500 ]; then + fail "Compatibility exceeds 500 chars (got $COMPAT_LEN)" + exit 5 + fi + pass "Compatibility valid ($COMPAT_LEN chars)" +fi + +# Check allowed-tools (if present) +TOOLS=$(echo "$FRONTMATTER" | grep '^allowed-tools:' | sed 's/^allowed-tools:[[:space:]]*//' || true) +if [ -n "$TOOLS" ]; then + # FAIL if commas found (must be space-delimited) + if echo "$TOOLS" | grep -qE ','; then + fail "allowed-tools must be space-delimited, not comma-separated: $TOOLS" + # FAIL if bracket array syntax found + elif echo "$TOOLS" | grep -qE '[\[\]]'; then + fail "allowed-tools must be space-delimited, not array syntax: $TOOLS" + else + pass "Allowed-tools field valid" + fi +fi +# FAIL if YAML multi-line array detected (allowed-tools:\n - item) +if echo "$FRONTMATTER" | grep -q '^allowed-tools:$'; then + NEXT_LINE=$(echo "$FRONTMATTER" | grep -A1 '^allowed-tools:$' | tail -1) + if echo "$NEXT_LINE" | grep -qE '^[[:space:]]*-[[:space:]]'; then + fail "allowed-tools must be space-delimited, not YAML array" + fi +fi + +# 8. Check line count (max 500) +LINE_COUNT=$(wc -l < "$SKILL_FILE" | tr -d ' ') +if [ "$LINE_COUNT" -gt 500 ]; then + fail "Line count exceeds 500 (got $LINE_COUNT)" + exit 6 +fi +pass "Line count: $LINE_COUNT/500" + +# 9. Check subdirectories (only scripts/, references/, assets/ allowed) +ALLOWED_DIRS="scripts references assets" +INVALID_DIRS="" +HAS_RESOURCES=false + +for subdir in "$SKILL_DIR"/*/; do + if [ -d "$subdir" ]; then + subdir_name=$(basename "$subdir") + if [ "$subdir_name" = "resources" ]; then + HAS_RESOURCES=true + elif ! echo "$ALLOWED_DIRS" | grep -qw "$subdir_name"; then + INVALID_DIRS="$INVALID_DIRS $subdir_name" + fi + fi +done + +if [ -n "$INVALID_DIRS" ]; then + warn "Non-standard subdirectories found:$INVALID_DIRS (allowed: scripts, references, assets)" +else + pass "Subdirectories valid" +fi + +if [ "$HAS_RESOURCES" = true ]; then + warn "resources/ is deprecated — use references/, assets/, scripts/ instead" +fi + +# 10. Content analysis - strip fenced code blocks for checks that need prose-only content +CONTENT_NO_FENCES=$(awk '/^```/{skip=!skip; next} !skip{print}' "$SKILL_FILE") + +# 10a. Check for ASCII art outside fenced code blocks (WARN) +if echo "$CONTENT_NO_FENCES" | grep -qE '[─│┌┐└┘├┤┬┴┼╭╮╯╰═║╔╗╚╝╠╣╦╩╬↑↓←→↔⇒⇐⇔▲▼◄►]{3,}'; then + warn "ASCII art detected outside code blocks - use plain lists or tables" +else + pass "No ASCII art outside code blocks" +fi + +# 10b. Check for persona statements outside fenced code blocks (FAIL) +if echo "$CONTENT_NO_FENCES" | grep -qiE '^[[:space:]]*You are (a|an|the) '; then + fail "Persona statement detected ('You are a/an/the...') - use Audience/Goal framing" +else + pass "No persona statements" +fi + +# 11. Check description routing quality (WARN) +if ! echo "$DESCRIPTION" | grep -qiE "(use when|don't use|not for|triggers on)"; then + warn "Description lacks routing keywords (use when, don't use, not for, triggers on)" +else + pass "Description has routing keywords" +fi + +# 12. Check for marketing copy in description (WARN) +if echo "$DESCRIPTION" | grep -qiE "(comprehensive|powerful|robust|cutting-edge|world-class|state-of-the-art|best-in-class|game-changing)"; then + warn "Description contains marketing buzzwords - use precise, functional language" +else + pass "No marketing copy in description" +fi + +# Summary +echo "-------------------------------------------" +echo -e "Result: ${GREEN}$PASS passed${NC}, ${RED}$FAIL failed${NC}, ${YELLOW}$WARN warnings${NC}" + +if [ "$FAIL" -gt 0 ]; then + echo -e "${RED}VALIDATION FAILED${NC}" + exit 1 +fi + +if [ "$WARN" -gt 0 ]; then + echo -e "${YELLOW}PASSED WITH WARNINGS${NC}" + exit 0 +fi + +echo -e "${GREEN}ALL CHECKS PASSED${NC}" +exit 0 diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 00000000..8211a3d8 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,365 @@ +# GitHub Actions Workflows + +This directory contains CI/CD workflows for the agentic collections repository. + +## Available Workflows + +### 1. `skill-spec-report.yml` - Skill Specification Linter Report + +**Purpose**: Validates skills against agentskills.io specification using the skill-linter and generates a comprehensive compliance report. + +**Triggers**: +- **Pull requests** → Validates ALL skills in affected packs (ensures pack consistency) +- **Pushes to main** → Validates ALL skills across all packs (ensures repo health) +- **Manual dispatch** → Choose between all skills or pack-wide validation +- **Excludes**: Draft pull requests + +**Validation Strategy** (Pack-Wide Validation): +- ⚡ **PRs**: Validates ALL skills in affected packs (pack-wide consistency) + - Example: Change `rh-virt/skills/vm-create/SKILL.md` → validates ALL `rh-virt/skills/*` +- 🔍 **Push to main**: Full validation of all 37 skills across all packs +- 🎛️ **Manual**: Choose validation scope via workflow dispatch + +**What it validates**: + +**agentskills.io Specification Compliance:** +- ✅ Directory structure (skill-name/SKILL.md) +- ✅ YAML frontmatter delimiters and completeness +- ✅ Name field (1-64 chars, lowercase, pattern matching, directory alignment) +- ✅ Description field (1-1024 chars, routing keywords, no marketing copy) +- ✅ Optional fields (compatibility, allowed-tools format) +- ✅ Line count (max 500 lines in SKILL.md) +- ✅ Subdirectory validation (only scripts/, references/, assets/) +- ✅ Content quality (no ASCII art, no persona statements) + +**Behavior**: +- **Errors detected** → ❌ Workflow fails, blocks PR merge +- **Warnings only** → ⚠️ Workflow passes, allows merge with warnings +- **All pass** → ✅ Workflow passes + +**Report Format**: +- Real-time progress (✅/⚠️/❌) for each skill +- **Detailed error output** shown ONLY for failed skills +- **Summary table** at the end with counts (Total/Passed/Warnings/Failed) + +**How to run locally**: +```bash +# Validate ALL skills +./scripts/run-skill-linter.sh + +# Validate only changed skills (detects git changes) +CHANGED=$(./scripts/detect-changed-skills.sh) +if [ -n "$CHANGED" ]; then + ./scripts/run-skill-linter.sh $CHANGED +fi + +# Validate specific skills +./scripts/run-skill-linter.sh rh-virt/skills/vm-create rh-virt/skills/vm-delete + +# Validate single skill (detailed output) +./.claude/skills/skill-linter/scripts/validate-skill.sh rh-virt/skills/vm-create/ +``` + +**Manual workflow dispatch**: +1. Go to Actions → Skill Specification Report +2. Click "Run workflow" +3. Choose: + - **Validate all skills: true** → Full scan (37 skills) + - **Validate all skills: false** → Changed skills only + +**Expected output**: +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Skill Specification Linter Report + agentskills.io Specification Compliance +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Found 37 skill(s) to validate + +✅ rh-sre/cve-impact +✅ rh-sre/fleet-inventory +⚠️ rh-developer/helm-deploy - PASSED WITH WARNINGS +❌ rh-virt/vm-create - FAILED + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +DETAILED ERROR REPORT +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +FAILED: rh-virt/vm-create +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +[FAIL] Missing frontmatter opening delimiter (---) +... + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +VALIDATION SUMMARY +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Metric Count +──────────────────────────────────────────────────────────────── +Total Skills: 37 +✅ Passed: 30 +⚠️ Passed with Warnings: 6 +❌ Failed: 1 + +❌ VALIDATION FAILED - ERRORS DETECTED +Skills with errors must be fixed before merge +``` + +**When validation fails**: + +The workflow will: +1. Show detailed error output for each failed skill +2. Display summary table with failure counts +3. Block PR merge (exit code 1) +4. Provide guidance on fixing errors locally + +**When validation passes with warnings**: + +The workflow will: +1. Show which skills have warnings +2. Display summary table +3. Allow PR merge (exit code 0) +4. Warn that warnings should be reviewed + +**Common validation errors**: +- Missing frontmatter delimiters (---) +- Name doesn't match directory name +- Description exceeds 1024 characters or lacks routing keywords +- Line count exceeds 500 lines +- Invalid `allowed-tools` format (must be space-delimited) +- ASCII art or persona statements in content +- Marketing buzzwords in description + +**Related files**: +- `scripts/run-skill-linter.sh` - Comprehensive linter reporter script (accepts optional skill dirs) +- `scripts/detect-changed-skills.sh` - Detects changed skills in PRs and commits +- `.claude/skills/skill-linter/scripts/validate-skill.sh` - Core validation script +- `.claude/skills/skill-linter/SKILL.md` - Linter documentation + +**Performance**: +- **PR validation (single pack)**: ~10-40 seconds (e.g., all 9 rh-virt skills) +- **PR validation (multiple packs)**: ~20-60 seconds (varies by pack count) +- **Full validation (all packs)**: ~60-90 seconds (all 37 skills) +- **Pack-wide**: 30-60% faster than full validation (depends on pack size) + +**Scope**: This workflow validates **ONLY** agentskills.io specification compliance. Repository-specific design principles (model, color, sections, etc.) are validated by other workflows. + +### 2. `compliance-check.yml` - Agentic Collections Structure Validation + +**Purpose**: Validates the entire agentic collections repository structure and runs skill design compliance checks on changed skills only. + +**Triggers**: +- **Every pull request** +- Pushes to `main` branch + +**What it validates**: + +**Repository structure validation (`make validate`):** +- ✅ Collection directory structure and naming conventions +- ✅ Required files presence (README.md, .mcp.json, etc.) +- ✅ Plugin metadata completeness +- ✅ MCP server configurations + +**Changed skills validation (`./scripts/ci-validate-changed-skills.sh`):** +- ✅ Detects which skills were modified in the PR/push +- ✅ Validates only changed skills against SKILL_DESIGN_PRINCIPLES.md +- ✅ Runs design compliance checks specific to modified skills + +**How to run locally**: +```bash +# Validate entire repository structure +make validate + +# Validate changed skills (simulates CI environment) +./scripts/ci-validate-changed-skills.sh + +# Or validate all skills +make validate-skill-design +``` + +**Expected output**: +``` +Validating repository structure... +✓ Collection structure valid +✓ Plugin metadata valid +✓ MCP configurations valid + +Validating changed skills... +Found 2 changed skill(s): vm-create, vm-delete +✓ vm-create passed design compliance +✓ vm-delete passed design compliance +``` + +**When validation fails**: + +The workflow will fail and provide: +1. Specific structural errors in the repository +2. Design compliance violations for changed skills +3. Reference to SKILL_DESIGN_PRINCIPLES.md + +**Common validation errors**: +- Missing required collection files (README.md, .mcp.json) +- Invalid MCP server configuration syntax +- Skills not following design principles (see SKILL_DESIGN_PRINCIPLES.md) +- Missing documentation in collections + +**Related files**: +- `Makefile` - Build and validation targets +- `scripts/ci-validate-changed-skills.sh` - Changed skills detector and validator +- `scripts/validate_skill_design.py` - Design compliance validation script +- `SKILL_DESIGN_PRINCIPLES.md` - Design principles checklist + +### 3. `deploy-pages.yml` - GitHub Pages Documentation Deployment + +**Purpose**: Generates and deploys HTML documentation for all agentic collections to GitHub Pages. + +**Triggers**: +- **Manual dispatch** (workflow_dispatch) +- Pushes to `main` branch affecting documentation paths: + - `rh-sre/**` + - `rh-developer/**` + - `ocp-admin/**` + - `rh-support-engineer/**` + - `rh-virt/**` + - `scripts/**` + - `docs/**` + - `.github/workflows/deploy-pages.yml` + +**What it does**: + +**Documentation generation (`make generate`):** +- ✅ Generates HTML documentation from Markdown files +- ✅ Creates collection indexes and navigation +- ✅ Builds skill reference pages +- ✅ Generates searchable documentation site + +**Deployment:** +- ✅ Configures GitHub Pages environment +- ✅ Uploads documentation artifacts +- ✅ Deploys to GitHub Pages with proper permissions + +**How to run locally**: +```bash +# Generate documentation locally +make generate + +# Preview generated docs +cd docs && python3 -m http.server 8000 +# Open http://localhost:8000 in your browser +``` + +**Expected output**: +``` +Generating documentation... +✓ Processing rh-sre collection +✓ Processing rh-developer collection +✓ Processing rh-virt collection +✓ Building site navigation +✓ Documentation generated in docs/ + +Deploying to GitHub Pages... +✓ Artifact uploaded +✓ Deployed successfully +``` + +**When deployment fails**: + +The workflow will fail if: +1. Documentation generation fails (invalid Markdown, missing files) +2. GitHub Pages permissions not configured +3. Artifact upload fails +4. Deployment step fails + +**Common deployment errors**: +- Missing Python dependencies (resolved by `make install`) +- Invalid frontmatter in Markdown files +- GitHub Pages not enabled in repository settings +- Insufficient workflow permissions + +**Related files**: +- `Makefile` - Documentation generation targets +- `scripts/generate-docs.py` - Documentation generator (if exists) +- `docs/` - Generated documentation output directory + +**Concurrency settings**: +- Only one deployment runs at a time (group: "pages") +- New deployments cancel in-progress ones + +## Adding New Workflows + +When adding new workflows: + +1. **Name the file descriptively**: `action-description.yml` +2. **Add documentation** in this README +3. **Define clear triggers** (PR, push, manual, schedule) +4. **Use semantic job names** that describe what they validate/test +5. **Provide clear error messages** when workflows fail +6. **Keep workflows focused** - one responsibility per workflow + +## Best Practices + +### Workflow Design +- ✅ Use specific path filters to avoid unnecessary runs +- ✅ Checkout with full history (`fetch-depth: 0`) when needed for diffs +- ✅ Use established GitHub Actions from trusted sources +- ✅ Provide summary outputs for quick review + +### Error Reporting +- ✅ Clear failure messages with actionable steps +- ✅ Reference documentation for resolution +- ✅ Group related errors together + +### Performance +- ✅ Run only on relevant file changes +- ✅ Use caching when applicable +- ✅ Parallelize independent validation steps + +## Troubleshooting + +### Workflow not triggering + +Check: +1. File paths match the `paths:` filter +2. Branch protection rules aren't blocking the workflow +3. GitHub Actions are enabled in repository settings + +### Validation script fails locally but passes in CI (or vice versa) + +This can happen due to: +1. Different file line endings (CRLF vs LF) +2. Different bash versions +3. Missing script permissions (`chmod +x`) + +**Fix**: +```bash +# Ensure script is executable +chmod +x scripts/validate-skills.sh + +# Check line endings +file scripts/validate-skills.sh + +# Convert to LF if needed +dos2unix scripts/validate-skills.sh +``` + +### False positives in validation + +If the validator reports errors for valid skills: +1. Review the validation logic in `scripts/validate-skills.sh` +2. Check if your skill follows SKILL_DESIGN_PRINCIPLES.md requirements exactly +3. Verify agentskills.io specification compliance +4. Open an issue if the validator has a bug + +## Maintenance + +This README should be updated when: +- New workflows are added +- Validation logic changes +- New validation levels are introduced +- Troubleshooting patterns emerge + +**Last Updated**: 2026-02-27 +**Workflows Count**: 3 (skill-spec-report.yml, compliance-check.yml, deploy-pages.yml) + +**Note**: `validate-skills.yml` was removed 2026-02-27. Repository-specific design principle validation (Tier 2) will be handled by a separate workflow. diff --git a/.github/workflows/skill-spec-report.yml b/.github/workflows/skill-spec-report.yml new file mode 100644 index 00000000..622e3ada --- /dev/null +++ b/.github/workflows/skill-spec-report.yml @@ -0,0 +1,105 @@ +name: Skill Specification Report + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + push: + branches: [main] + workflow_dispatch: + inputs: + validate_all: + description: 'Validate all skills (true) or changed only (false)' + required: false + default: 'true' + type: choice + options: + - 'true' + - 'false' + +jobs: + skill-linter: + # Skip draft PRs + if: github.event.pull_request.draft == false || github.event_name == 'push' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Full history for diff comparison + + - name: Make scripts executable + run: | + chmod +x .claude/skills/skill-linter/scripts/validate-skill.sh + chmod +x scripts/run-skill-linter.sh + chmod +x scripts/detect-changed-skills.sh + + - name: Detect changed skills (PRs only) + if: github.event_name == 'pull_request' + id: detect + env: + GITHUB_EVENT_NAME: ${{ github.event_name }} + GITHUB_BASE_REF: ${{ github.base_ref }} + run: | + CHANGED_SKILLS=$(./scripts/detect-changed-skills.sh || true) + if [ -z "$CHANGED_SKILLS" ]; then + echo "changed=false" >> $GITHUB_OUTPUT + echo "skills=" >> $GITHUB_OUTPUT + else + echo "changed=true" >> $GITHUB_OUTPUT + # Convert to space-separated for script args + SKILLS_ARGS=$(echo "$CHANGED_SKILLS" | tr '\n' ' ') + echo "skills=$SKILLS_ARGS" >> $GITHUB_OUTPUT + echo "Changed skills detected:" + echo "$CHANGED_SKILLS" + fi + + - name: Run Skill Specification Linter + id: linter + run: | + # Determine validation mode + if [ "${{ github.event_name }}" = "pull_request" ]; then + if [ "${{ steps.detect.outputs.changed }}" = "true" ]; then + echo "Running linter on changed skills only..." + ./scripts/run-skill-linter.sh ${{ steps.detect.outputs.skills }} + else + echo "ℹ️ No skills changed in this PR - skipping validation" + echo "0" > /tmp/skill-linter-exit-code + exit 0 + fi + elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + if [ "${{ github.event.inputs.validate_all }}" = "true" ]; then + echo "Running linter on ALL skills (manual dispatch)..." + ./scripts/run-skill-linter.sh + else + echo "Running linter on changed skills only (manual dispatch)..." + CHANGED_SKILLS=$(./scripts/detect-changed-skills.sh || true) + if [ -n "$CHANGED_SKILLS" ]; then + ./scripts/run-skill-linter.sh $(echo "$CHANGED_SKILLS" | tr '\n' ' ') + else + echo "ℹ️ No skills changed - skipping validation" + echo "0" > /tmp/skill-linter-exit-code + fi + fi + else + # Push to main: validate ALL skills + echo "Running linter on ALL skills (push to main)..." + ./scripts/run-skill-linter.sh + fi + continue-on-error: true + + - name: Check results + run: | + if [ -f /tmp/skill-linter-exit-code ]; then + EXIT_CODE=$(cat /tmp/skill-linter-exit-code) + if [ "$EXIT_CODE" -ne 0 ]; then + echo "❌ Skill linter detected errors - blocking PR merge" + exit 1 + else + echo "✅ All skills passed or have warnings only" + exit 0 + fi + else + echo "❌ Linter did not complete" + exit 1 + fi diff --git a/CLAUDE.md b/CLAUDE.md index b6056eb9..5699ac47 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,9 +57,26 @@ Each pack follows this structure: **Key Pattern**: Agents orchestrate skills; skills encapsulate tools. Never call MCP tools directly - always go through skills. -## Design Principles for Skills and Agents +## Skill and Agent Requirements -See [SKILL_DESIGN_PRINCIPLES.md](SKILL_DESIGN_PRINCIPLES.md) for the complete design principles, templates, and rationale. Validate compliance with `make validate-skill-design`. +**CRITICAL:** EVERY SKILL and AGENT must comply with: +- **Tier 1:** agentskills.io specification (AUTOMATED via linter) +- **Tier 2:** Repository design principles (MANUAL review) + +**Before committing any skill:** + +1. **Run automated validation (Tier 1):** + ```bash + ./scripts/run-skill-linter.sh skills/skill-name/ + ``` + +2. **Manual review (Tier 2):** + - Review [SKILL_DESIGN_PRINCIPLES.md](SKILL_DESIGN_PRINCIPLES.md) for complete requirements + - Use appropriate template (general or collection-specific) + - Verify all design principles are followed + +**Documentation:** +- [SKILL_DESIGN_PRINCIPLES.md](SKILL_DESIGN_PRINCIPLES.md) - Complete design principles, templates, and rationale ### MCP Server Integration @@ -152,10 +169,18 @@ last_updated: YYYY-MM-DD ### Adding a Skill 1. Create `skills//SKILL.md` -2. Define YAML frontmatter with root-level fields (name, description, model, color) and optional `metadata` for custom fields (author, priority, etc.). See [SKILL_DESIGN_PRINCIPLES.md](SKILL_DESIGN_PRINCIPLES.md) for the 2026 Agentic Skills structure. -3. Document workflow with MCP tool references -4. Include concrete examples +2. Define YAML frontmatter with mandatory fields: + - `name`, `description` (agentskills.io spec) + - `model` (inherit|sonnet|haiku), `color` (cyan|green|blue|yellow|red) - Repository requirement + - Optional: `metadata` for custom fields (author, priority, version) +3. Follow [SKILL_DESIGN_PRINCIPLES.md](SKILL_DESIGN_PRINCIPLES.md) for: + - Section structure and ordering + - Prerequisites with verification + - Workflow with precise parameters + - Dependencies declaration +4. Include concrete examples and complete error handling 5. Test with `Skill` tool invocation +6. Validate with `./scripts/run-skill-linter.sh skills//` **Collection-Specific Standards:** - **rh-virt**: Follow `rh-virt/SKILL_TEMPLATE.md` for enhanced quality standards including mandatory Common Issues and Example Usage sections @@ -163,10 +188,11 @@ last_updated: YYYY-MM-DD ### Adding an Agent 1. Create `agents/.md` -2. Define YAML frontmatter (name, description, model, tools, color) -3. Document workflow orchestrating skills -4. Provide clear examples of when to use agent vs skills -5. Test with `Task` tool invocation +2. Follow skill requirements in [SKILL_DESIGN_PRINCIPLES.md](SKILL_DESIGN_PRINCIPLES.md) (agents use same structure) +3. Define YAML frontmatter (name, description, model, tools) +4. Document workflow that orchestrates multiple skills +5. Provide clear examples of when to use agent vs individual skills +6. Test with `Task` tool invocation ### Adding Documentation (rh-sre pattern) @@ -220,24 +246,19 @@ When creating new collections, follow the pattern that best matches your needs: ### Core Architecture 1. **Skills encapsulate tools** - Never call MCP tools directly; always invoke skills 2. **Agents orchestrate skills** - Complex workflows delegate to specialized skills -3. **Skill precedence** - Skills > Tools in all cases (Design Principle #3) +3. **agentskills.io compliance** - All skills follow the official specification +4. **Progressive disclosure** - Load docs incrementally based on task needs ### Security & Configuration -4. **Environment variables for secrets** - Never hardcode credentials -5. **Never expose credential values** - Check env vars are set, but NEVER print their values in output -6. **Verify prerequisites** - Check MCP server availability before execution (Design Principle #7) -7. **Human-in-the-loop for critical ops** - Require explicit confirmation (Design Principle #5) +5. **Environment variables for secrets** - Never hardcode credentials +6. **Never expose credential values** - Check env vars are set, but NEVER print their values in output +7. **MCP server integration** - Use `.mcp.json` with environment variable references -### Documentation & Transparency +### Documentation & Quality 8. **Official sources only** - Document all sources in SOURCES.md -9. **Declare document consultation** - Explicitly state "I consulted [file]" (Design Principle #1) -10. **Progressive disclosure** - Load docs incrementally based on task needs - -### Quality & Usability -11. **Precise parameters** - Specify exact tool parameters for first-attempt success (Design Principle #2) -12. **Declare dependencies** - List all skills, tools, docs, and MCP servers (Design Principle #4) -13. **Production-ready examples** - No toy code, include error handling -14. **Persona-focused design** - Each collection serves specific user roles -15. **Concise skill descriptions** - Keep YAML frontmatter under 500 tokens (Design Principle #3) +9. **Production-ready examples** - No toy code, include error handling +10. **Persona-focused design** - Each collection serves specific user roles -**See**: [SKILL_DESIGN_PRINCIPLES.md](SKILL_DESIGN_PRINCIPLES.md) for detailed requirements and templates. +**Validation:** +- Design principles and requirements: [SKILL_DESIGN_PRINCIPLES.md](./SKILL_DESIGN_PRINCIPLES.md) +- Automated linter (Tier 1): `./scripts/run-skill-linter.sh` diff --git a/SKILL_DESIGN_PRINCIPLES.md b/SKILL_DESIGN_PRINCIPLES.md index eeabd683..4adc11f7 100644 --- a/SKILL_DESIGN_PRINCIPLES.md +++ b/SKILL_DESIGN_PRINCIPLES.md @@ -1,525 +1,485 @@ # Design Principles for Skills and Agents -This document defines the design principles for creating skills and agents in agentic collections. It is referenced from [CLAUDE.md](CLAUDE.md). Validate compliance with `make validate-skill-design` (see [README.md](README.md#skill-design-validation)). +Repository-specific design principles for creating skills and agents in agentic collections. Referenced from [CLAUDE.md](CLAUDE.md). -## 1. Document Consultation Transparency +**Scope**: Tier 2 requirements - repository enhancements beyond base agentskills.io specification (Tier 1 validated by linter). -When a skill or agent consults documentation (from `docs/` or skill/agent files), it **MUST**: -1. **Actually read the file** using the Read tool to load it into context -2. **Then declare** the consultation to the user +--- -**CRITICAL**: Document consultation means READING the file, not just claiming to have read it. +## Core Design Principles -**Required Implementation**: -```markdown -**Document Consultation** (REQUIRED): -1. **Action**: Read [filename.md](path/to/filename.md) using the Read tool to understand [specific topic] -2. **Output to user**: "I consulted [filename.md](path/to/filename.md) to understand [specific topic]." -``` +### 1. Document Consultation Transparency -**❌ WRONG - Transparency Theater** (just claims, no actual reading): +When consulting documentation, **MUST** actually read the file using Read tool, then declare consultation. + +**Required Pattern:** ```markdown -**Document Consultation** (output to user): -``` -I consulted [filename.md](path/to/filename.md) to understand [topic]. -``` +**Document Consultation** (REQUIRED - Execute FIRST): +1. **Action**: Read [file.md](path/to/file.md) using Read tool to understand [topic] +2. **Output to user**: "I consulted [file.md](path/to/file.md) to understand [topic]." ``` -**✅ CORRECT - Actual Consultation** (reads first, then declares): +**❌ WRONG - Transparency Theater:** ```markdown -**Document Consultation** (REQUIRED): -1. **Action**: Read [cvss-scoring.md](../../docs/references/cvss-scoring.md) using the Read tool to understand CVSS severity mapping -2. **Output to user**: "I consulted [cvss-scoring.md](../../docs/references/cvss-scoring.md) to understand CVSS severity mapping." +I consulted file.md to understand [topic]. # Claim without actually reading ``` -**Examples in execution**: -- Read `docs/references/cvss-scoring.md` → "I consulted [cvss-scoring.md](rh-sre/docs/references/cvss-scoring.md) to verify the CVSS severity mapping." -- Read `skills/playbook-generator/SKILL.md` → "I consulted [playbook-generator/SKILL.md](rh-sre/skills/playbook-generator/SKILL.md) to understand playbook generation parameters." +**Rationale**: Ensures AI enriches context with domain knowledge; users understand knowledge sources; auditable via Read tool logs. -**Rationale**: -- **Substance**: Ensures AI actually enriches its context with domain knowledge -- **Transparency**: Users understand the AI's knowledge sources -- **Auditability**: The execution-summary skill can track actual Read tool calls +--- -## 2. Precise Parameter Specification +### 2. Precise Parameter Specification -Skills MUST specify **exact parameters** when instructing agents to use tools, ensuring first-attempt success. +Specify **exact parameters** with formats and examples for first-attempt success. -**CRITICAL**: Document consultation must be specified BEFORE tool parameters to ensure it happens first. +**Required Workflow Step Format:** +```markdown +### Step N: [Action Name] -**❌ Bad Example - Vague parameters**: -``` -Use get_cve tool with the CVE ID -``` +**Document Consultation** (if applicable): [See Principle #1] + +**MCP Tool**: `tool_name` or `category__tool_name` (from server-name) + +**Parameters**: +- `param`: "value" (type, constraints, format details) + +**Expected Output**: [Description] -**❌ Bad Example - Wrong parameters**: +**Error Handling**: +- If [condition]: [resolution] ``` -**MCP Tool**: get_cves +**✅ Good Example:** +```markdown **Parameters**: -- severity: ["Critical", "Important"] -- sort_by: "cvss_score" +- `impact`: "7,6" (comma-separated: 7=Important, 6=Moderate, 5=Low) +- `sort`: "-cvss_score" (use - for descending) +- `limit`: 20 (integer, range: 1-100) ``` -(Actual tool uses `impact: "7,6"` and `sort: "-cvss_score"`) -**✅ Good Example - Correct structure with document consultation first**: +**❌ Bad Example:** +```markdown +**Parameters**: +- Use the CVE ID +- Set severity to high ``` -**CRITICAL**: Document consultation MUST happen BEFORE tool invocation. -**Document Consultation** (REQUIRED - Execute FIRST): -1. **Action**: Read [vulnerability-logic.md](../../docs/insights/vulnerability-logic.md) using the Read tool -2. **Output to user**: "I consulted [vulnerability-logic.md]..." +**Rationale**: Exact names/formats prevent errors; first-attempt success reduces wasted cycles. -**MCP Tool**: `get_cves` or `vulnerability__get_cves` (from lightspeed-mcp) +--- -**Parameters**: -- impact: "7,6" (string with comma-separated impact levels: 7=Important, 6=Moderate, 5=Low) -- sort: "-cvss_score" (use - prefix for descending; valid fields: "cvss_score", "public_date") -- limit: 20 (maximum number of CVEs to return) -``` +### 3. Skill Precedence and Conciseness + +**A. Skills Over Tools** -**Rationale**: -- **Ordering**: Document consultation before parameters ensures it's executed first -- **Precision**: Exact parameter names and formats prevent tool errors -- **Examples**: Value examples (e.g., "7,6") show correct format -- **Determinism**: First-attempt success reduces wasted cycles +Delegate to skills, not raw MCP tools. -## 3. Skill Precedence and Conciseness +**✅ CORRECT:** `invoke the vm-inventory skill` +**❌ WRONG:** `use the resources_list tool` -**Precedence Rule**: Skills > Tools (always invoke skills, not raw MCP tools) +**B. Concise Descriptions** -**Conciseness Requirement**: Skill descriptions (loaded at agent start time) must be: -- **Under 500 tokens** for the YAML frontmatter description field -- **Focus on "when to use"** with 3-5 concrete examples -- **Defer implementation details** to the skill body (not frontmatter) +- Under 500 tokens in frontmatter `description` +- 3-5 "Use when" examples +- "NOT for" anti-patterns with alternatives +- Defer implementation to skill body -**Example**: +**✅ Complete Example:** ```yaml ---- -name: cve-impact description: | - Analyze CVE impact across the fleet without immediate remediation. + Analyze CVE impact without remediation. Use when: - - "What are the most critical vulnerabilities?" - - "Show CVEs affecting my systems" - - "List high-severity CVEs" + - "What are critical vulnerabilities?" + - "Show CVEs affecting systems" + - User mentions "CVEs", "vulnerabilities" - NOT for remediation actions (use remediator agent instead). - model: inherit # Root: Runtime configuration required before skill execution - color: blue # Root: UX - IDE sidebar/terminal theme ---- + NOT for remediation (use remediator agent instead). ``` -**Rationale**: Minimizes token usage at agent initialization while maintaining clarity. +**Rationale**: Minimizes token usage at initialization; prevents skill misuse. -### Root-Level Frontmatter (2026 Agentic Skills Standard) - -Primary UI and runtime configuration fields belong at the **root level** of YAML frontmatter so the IDE or Agent Host can parse them without traversing nested blocks. - -| Field Type | Location | Examples | -|------------|----------|----------| -| Runtime | Root | `model`, `allowed-tools` | -| UX/UI | Root | `color`, `icon`, `version` | -| Custom | `metadata` | `author`, `priority`, `compliance` | - -**Standard Color Values** (Cursor, Claude Code): - -| Color | Use Case | -|-------|----------| -| blue, cyan | Analysis, read-only | -| green | Success, deployment | -| yellow | Caution, validation | -| red | Critical, security, remediation | -| magenta | Creative, generation | +--- -## 4. Dependencies Declaration +### 4. Dependencies Declaration -Every skill MUST include a **Dependencies** section listing: -- **Skills**: Other skills this skill may invoke -- **MCP Tools**: Specific tools from MCP servers -- **MCP Servers**: Required MCP server names -- **Documentation**: Reference docs for context +Every skill MUST include complete Dependencies section. -**Required Format**: +**Required Structure:** ```markdown ## Dependencies ### Required MCP Servers -- `lightspeed-mcp` - Red Hat Lightspeed platform access +- `server-name` - Description ([setup guide](link)) ### Required MCP Tools -- `vulnerability__get_cves` (from lightspeed-mcp) - List CVEs -- `vulnerability__get_cve` (from lightspeed-mcp) - Get CVE details +- `tool_name` (from server-name) - What it does + - Parameters: param1, param2 ### Related Skills -- `cve-validation` - Validate CVEs before impact analysis -- `fleet-inventory` - Identify affected systems +- `skill-name` - When to use instead ### Reference Documentation -- [cvss-scoring.md](docs/references/cvss-scoring.md) - CVSS severity mappings -- [insights-api.md](docs/insights/insights-api.md) - API usage patterns +**Internal:** [doc.md](path) - Purpose +**Official:** [Title - Product](https://docs.redhat.com/...) ``` -**Rationale**: Makes dependencies explicit for debugging and ensures proper error handling. +**Rationale**: Makes dependencies explicit for debugging and troubleshooting. -## 5. Human-in-the-Loop Requirements +--- -Skills performing **critical operations** MUST include this section: +### 5. Human-in-the-Loop Requirements -**Required Section**: -```markdown -## Critical: Human-in-the-Loop Requirements +Skills performing critical operations MUST require explicit confirmation. -This skill requires explicit user confirmation at the following steps: +**When Required:** Create, delete, modify, restore, execute commands, affect multiple systems +**NOT Required:** Read-only operations (list, view, get) -1. **Before Tool Invocation** [if applicable] - - Ask: "Should I proceed with [specific action]?" - - Wait for user confirmation - -2. **Before Destructive Actions** [if applicable] - - Display preview of changes - - Ask: "Review the changes above. Should I execute this?" - - Wait for explicit "yes" or "proceed" +**Required Section:** +```markdown +## Critical: Human-in-the-Loop Requirements -3. **After Each Major Step** [if applicable] - - Report results - - Ask: "Continue to next step?" +1. **Before [Action]** + - Display preview: [what will happen] + - Ask: "Should I [action]?" + - Wait for confirmation (yes/no) -**Never assume approval** - always wait for explicit user confirmation. +**Never assume approval** - always wait for explicit confirmation. ``` -**When to Use**: -- Playbook execution (ansible-mcp-server) -- System modifications (package updates, config changes) -- Multi-system operations (batch remediation) -- Data deletion or irreversible actions +**For Destructive Operations - Add Typed Confirmation:** +```markdown +2. **Typed Confirmation** + - Ask: "Type exact resource name to confirm: " + - Verify exact match, cancel if mismatch + - Ask: "Type 'DELETE' to proceed" + - Only proceed on exact match +``` -**Rationale**: Prevents unintended automation; maintains user control over critical operations. +**Rationale**: Prevents unintended automation; maintains user control; reduces accidental data loss. -## 6. Mandatory Skill Sections +--- -Every skill MUST include these sections in order: +### 6. Mandatory Skill Sections -### Template Structure: +**Required Section Order:** +1. YAML frontmatter +2. `# [Skill Name]` heading + overview (1-2 sentences) +3. `## Critical: Human-in-the-Loop Requirements` (if applicable) +4. `## Prerequisites` +5. `## When to Use This Skill` +6. `## Workflow` +7. `## Dependencies` +8. `## Example Usage` (recommended) -```markdown +**A. Mandatory Frontmatter Fields:** +```yaml --- -name: skill-name -description: | - [Concise when-to-use with 3-5 examples] -model: inherit|sonnet|haiku -color: red|blue|green|yellow|cyan|magenta -version: 1.0.0 -metadata: - author: "team-name" - priority: "high" +name: skill-name # MANDATORY - kebab-case, matches directory +description: | # MANDATORY - <500 tokens, includes use cases + [With "Use when" and "NOT for"] +model: inherit # MANDATORY - inherit | sonnet | haiku +color: green # MANDATORY - cyan|green|blue|yellow|red --- +``` -# [Skill Name] - -## Prerequisites - -**Required MCP Servers**: `server-name` ([setup guide](link)) -**Required MCP Tools**: `tool_name` (from server-name) -**Environment Variables**: `VAR_NAME` (if applicable) - -**Verification**: -Before executing, verify MCP server availability: -1. Check `server-name` is configured in `.mcp.json` -2. Verify environment variables are set -3. If missing: Report to user with setup instructions - -**Human Notification on Failure**: -If prerequisites are not met: -- ❌ "Cannot proceed: MCP server `server-name` is not available" -- 📋 "Setup required: [link to setup guide]" -- ❓ "How would you like to proceed? (setup now / skip / abort)" -- ⏸️ Wait for user decision +**Model Values:** +- `inherit` - Use parent context (recommended) +- `sonnet` - Complex reasoning +- `haiku` - Simple, fast operations + +**Color Values (Risk-Based):** +- `cyan` - Read-only (list, view, get) +- `green` - Additive (create, clone) +- `blue` - Reversible (start, stop, restart) +- `yellow` - Destructive but recoverable (snapshot-delete) +- `red` - Irreversible (delete, restore) + +**B. Prerequisites Section Must Include:** +- Required MCP Servers with setup links +- Required MCP Tools with descriptions +- Environment Variables (if any) +- Verification Steps +- Human Notification Protocol +- Security warning + +See Principle #7 for details. + +**C. When to Use Section Must Include:** +- 3+ specific scenarios +- "Do NOT use when" with alternatives + +**D. Workflow Section Steps Must Include:** +- `### Step N: [Action]` heading +- Document consultation (if applicable) +- MCP Tool with server +- Parameters with exact format +- Expected output +- Error handling (2+ conditions) -## When to Use This Skill +--- -Use this skill when: -- [Specific scenario 1] -- [Specific scenario 2] -- [Specific scenario 3] +### 7. MCP Server Availability Verification -Do NOT use when: -- [Anti-pattern 1] → Use [alternative] instead -- [Anti-pattern 2] → Use [alternative] instead +Prerequisites MUST include verification and human notification protocol. -## Workflow +**CRITICAL SECURITY - NEVER expose credential values:** -### Step 1: [Action Name] +**❌ WRONG:** +```bash +echo $API_SECRET # Exposes value +echo "SECRET=$API_SECRET" # Exposes value +``` -**CRITICAL**: Document consultation MUST happen BEFORE tool invocation. +**✅ CORRECT:** +```bash +# Report boolean only +test -n "$API_SECRET" && echo "✓ API_SECRET is set" || echo "✗ Not set" +``` -**Document Consultation** (REQUIRED - Execute FIRST): -1. **Action**: Read [doc.md](../../docs/category/doc.md) using the Read tool to understand [specific topic] -2. **Output to user**: "I consulted [doc.md](../../docs/category/doc.md) to understand [specific topic]." +**Required Prerequisites Pattern:** +```markdown +## Prerequisites -**MCP Tool**: `tool_name` or `toolset__tool_name` (from server-name) +**Required MCP Servers:** `server-name` ([setup](link)) -**Parameters**: -- `param1`: [exact specification with example - see Design Principle #2] - - Example: `"CVE-2024-1234"` -- `param2`: [exact specification with example] - - Example: `true` (description of what this does) +**Required MCP Tools:** `tool_name` - Description -**Expected Output**: [describe what the tool returns] +**Environment Variables:** `VAR` - What it controls -**Error Handling**: -- If [error condition]: [how to handle] +**Verification Steps:** +1. Check `server-name` in `.mcp.json` +2. Verify `VAR` is set (without exposing value) +3. If missing → Human Notification Protocol -### Step 2: [Next Action] +**Human Notification Protocol:** -[Continue pattern...] +When prerequisites fail: +1. **Stop immediately** - No tool calls +2. **Report error:** + ``` + ❌ Cannot execute skill: MCP server `name` unavailable + 📋 Setup: [instructions + link] + ``` +3. **Request decision:** "How to proceed? (setup/skip/abort)" +4. **Wait for user input** -## Dependencies +**Security:** Never display credential values. +``` -[As specified in principle #4] +**Rationale**: Graceful degradation; clear guidance; prevents credential exposure. -## Critical: Human-in-the-Loop Requirements +--- -[As specified in principle #5, if applicable] +## Additional Quality Standards -## Example Usage +### 8. Single Responsibility -[Concrete example with user query and skill response] -``` +One clear purpose per skill. -**Rationale**: Standardizes skill structure for consistency and completeness. +**✅ Good:** `vm-create`, `vm-delete`, `vm-inventory` (separate skills) +**❌ Bad:** `vm-manager` (creates, deletes, lists - too broad) -## 7. MCP Server Availability Verification +--- -The **Prerequisites** section MUST include verification logic: +### 9. Naming Conventions -**CRITICAL SECURITY CONSTRAINT**: -- **NEVER print environment variable values in user-visible output** -- When checking if env vars are set, only report presence/absence -- Do NOT use `echo $VAR_NAME` or display actual credential values -- Protect sensitive data like API keys, tokens, secrets, passwords +- kebab-case only +- Folder matches `name` field exactly +- File named `SKILL.md` (uppercase) +- Name: 1-64 chars, `a-z0-9-`, no consecutive `--`, no leading/trailing `-` -**❌ WRONG - Exposes credentials**: -```bash -echo $LIGHTSPEED_CLIENT_SECRET # Shows actual secret value -``` +**✅ Correct:** `skills/vm-create/SKILL.md` +**❌ Wrong:** `skills/VM-Create/skill.md`, `skills/vm_create/SKILL.md` -**✅ CORRECT - Check without exposing**: -```bash -# Check if set (exit code only, no output) -test -n "$LIGHTSPEED_CLIENT_SECRET" - -# Or check and report boolean result -if [ -n "$LIGHTSPEED_CLIENT_SECRET" ]; then - echo "✓ LIGHTSPEED_CLIENT_SECRET is set" -else - echo "✗ LIGHTSPEED_CLIENT_SECRET is not set" -fi -``` +--- -**In User-Visible Messages**: -``` -✓ Environment variable LIGHTSPEED_CLIENT_ID is set -✓ Environment variable LIGHTSPEED_CLIENT_SECRET is set -``` +### 10. Content Quality -**NEVER show**: -``` -LIGHTSPEED_CLIENT_SECRET=sk-abc123-xyz789-... ❌ SECURITY VIOLATION -``` +**Required:** +- No hardcoded values (use ``, ``) +- No broken links +- Production-ready examples +- Complete error handling -**Rationale**: Prevents accidental credential exposure in conversation history, logs, or screenshots. +**✅ Good:** `namespace: ""` +**❌ Bad:** `namespace: "production"` --- -**Required Pattern**: -```markdown -## Prerequisites +## Root-Level Frontmatter (2026 Standard) -**Required MCP Servers**: `lightspeed-mcp` ([setup guide](https://console.redhat.com/)) -**Required MCP Tools**: -- `vulnerability__get_cves` -- `vulnerability__get_cve` - -**Verification Steps**: -1. **Check MCP Server Configuration** - - Verify `lightspeed-mcp` exists in `.mcp.json` - - If missing → Proceed to Human Notification - -2. **Check Environment Variables** - - Verify `LIGHTSPEED_CLIENT_ID` is set - - Verify `LIGHTSPEED_CLIENT_SECRET` is set - - If missing → Proceed to Human Notification - -3. **Test MCP Server Connection** (optional, for critical skills) - - Attempt simple tool call (e.g., `get_mcp_version`) - - If fails → Proceed to Human Notification - -**Human Notification Protocol**: - -When prerequisites fail, the skill MUST: - -1. **Stop Execution Immediately** - Do not attempt tool calls -2. **Report Clear Error**: - ``` - ❌ Cannot execute [skill-name]: MCP server `lightspeed-mcp` is not available - - 📋 Setup Instructions: - 1. Add lightspeed-mcp to `.mcp.json` (see: [setup guide]) - 2. Set environment variables: - export LIGHTSPEED_CLIENT_ID="your-id" - export LIGHTSPEED_CLIENT_SECRET="your-secret" - 3. Restart Claude Code to reload MCP servers - - 🔗 Documentation: [link to MCP server docs] - ``` - -3. **Request User Decision**: - ``` - ❓ How would you like to proceed? - - Options: - - "setup" - I'll help you configure the MCP server now - - "skip" - Skip this skill and continue with alternative approach - - "abort" - Stop the workflow entirely - - Please respond with your choice. - ``` - -4. **Wait for Explicit User Input** - Do not proceed automatically - -**Error Message Templates**: - -- Missing MCP Server: - ``` - ❌ MCP server `{server_name}` not configured in .mcp.json - 📋 Add server configuration: [setup guide link] - ``` - -- Missing Environment Variable: - ``` - ❌ Environment variable `{VAR_NAME}` not set - 📋 Set variable: export {VAR_NAME}="your-value" - - ⚠️ SECURITY: Never expose actual values in output or logs - ``` - -- Connection Failure: - ``` - ❌ Cannot connect to `{server_name}` MCP server - 📋 Possible causes: - - Container not running (run: podman ps) - - Network issues (check: podman logs) - - Invalid credentials (verify env vars) - ``` -``` +UI/runtime fields at root; custom fields in `metadata`. -**Rationale**: Provides graceful degradation and clear user guidance when dependencies are missing. +| Field Type | Location | Examples | +|------------|----------|----------| +| Runtime | Root | `model`, `allowed-tools` | +| UX/UI | Root | `color`, `version` | +| Custom | `metadata` | `author`, `priority` | -## Skill File Format +--- -Skills MUST follow the structure defined in **Design Principle #6** above. Here's a minimal template: +## Skill Template ```yaml --- name: skill-name description: | - [Concise when-to-use with 3-5 examples - under 500 tokens] -model: inherit|sonnet|haiku -color: red|blue|green|yellow|cyan|magenta -version: 1.0.0 +[Description] + +Use when: +- "Example query 1" +- "Example query 2" +- User mentions "keyword" + +NOT for [use case] (use [skill] instead). +model: inherit +color: green metadata: - author: "team-name" - priority: "high" +author: "team" +version: "1.0" --- -# [Skill Name] +# /skill-name Skill + +[Overview - 1-2 sentences] + +## Critical: Human-in-the-Loop Requirements +[See Principle #5 - if applicable] ## Prerequisites -[As defined in Design Principle #7 - with verification and human notification] + +**Required MCP Servers:** `server` ([setup](link)) +**Required MCP Tools:** `tool` (from server) - Description +**Environment Variables:** `VAR` - Description + +**Verification Steps:** +[See Principle #7] + +**Human Notification Protocol:** +[See Principle #7] + +**Security:** Never display credential values. ## When to Use This Skill -[Clear use cases and anti-patterns] + +Use when: +- [Scenario 1] +- [Scenario 2] + +Do NOT use when: +- [Anti-pattern] → Use `skill` instead ## Workflow + ### Step 1: [Action] -**CRITICAL**: Document consultation MUST happen BEFORE tool invocation. +**Document Consultation** (if needed): +1. **Action**: Read [doc.md](path) using Read tool +2. **Output**: "I consulted [doc.md](path)..." -**Document Consultation** (REQUIRED - Execute FIRST): -1. **Action**: Read [doc.md](path/to/doc.md) using the Read tool to understand [topic] -2. **Output to user**: "I consulted [doc.md](path/to/doc.md) to understand [topic]." +**MCP Tool:** `tool_name` (from server) -**MCP Tool**: `tool_name` or `toolset__tool_name` (from server-name) +**Parameters:** +- `param`: "value" (format details) -**Parameters**: -- param1: "value" (exact format with example - Design Principle #2) -- param2: true (description of what this does) +**Expected Output:** +```json +{"status": "success"} +``` -[Implementation details] +**Error Handling:** +- If [condition]: [resolution] ## Dependencies -[As defined in Design Principle #4] -## Critical: Human-in-the-Loop Requirements -[If applicable - Design Principle #5] -``` +### Required MCP Servers +- `server` - Description ([setup](link)) + +### Required MCP Tools +- `tool` (from server) - What it does -**Important**: See this document for complete requirements and rationale. +### Related Skills +- `skill` - When to use -## Agent File Format +### Reference Documentation +**Internal:** [doc.md](path) +**Official:** [Title](link) -Agents MUST follow similar principles as skills, with focus on skill orchestration: +## Example Usage +[User query + skill response] +``` + +--- + +## Agent Template ```yaml --- name: agent-name description: | - When to use this agent vs skills - [Concise with 3-5 examples - under 500 tokens] +Multi-step workflow orchestrating skills. + +Use when: +- [Complex workflow] + +NOT for single ops (use skills). model: inherit color: red -version: 1.0.0 metadata: - author: "team-name" +author: "team" tools: ["All"] --- # [Agent Name] +[Overview] + ## Prerequisites -[MCP servers and skills this agent depends on - Design Principle #7] +[MCP servers and skills - see Principle #7] ## When to Use This Agent -[Multi-step workflows requiring orchestration] +[Multi-step workflows vs individual skills] ## Workflow -### 1. Step Name -**Invoke the skill-name skill**: +### Step 1: [Action] + +**Invoke skill:** ``` Skill: skill-name -Args: [Precise parameters - Design Principle #2] +Args: [precise parameters] ``` -**Document Consultation** (if needed): -I consulted [filename.md](path/to/filename.md) to understand [topic]. -[Design Principle #1] - **Human Confirmation** (if critical): -Ask: "Should I proceed with [action]?" -Wait for confirmation. -[Design Principle #5] - -### 2. Next Step -[Continue orchestration pattern...] +Ask: "Proceed?" Wait for confirmation. ## Dependencies -[Skills, tools, docs this agent uses - Design Principle #4] +[Skills, tools, docs - see Principle #4] ## Critical: Human-in-the-Loop Requirements -[For agents performing critical operations - Design Principle #5] +[If applicable - see Principle #5] ``` -**Important**: Agents inherit the same design principles as skills. See this document for complete requirements. +--- + +## Summary + +**Core Principles:** +1. **Document Consultation Transparency** - Read files, then declare +2. **Precise Parameter Specification** - Exact formats with examples +3. **Skill Precedence and Conciseness** - Skills over tools; <500 tokens +4. **Dependencies Declaration** - Explicit dependencies +5. **Human-in-the-Loop** - Confirmations for critical ops +6. **Mandatory Sections** - Standard structure +7. **MCP Verification** - Prerequisites with security +8. **Single Responsibility** - One purpose per skill +9. **Naming Conventions** - kebab-case +10. **Content Quality** - Production-ready examples + +--- + +**Last Updated**: 2026-03-02 +**Version**: 5.0 +**Applies To**: All agentic collections +**Specification Compliance**: agentskills.io v1.0 diff --git a/scripts/ci-validate-changed-skills.sh b/scripts/ci-validate-changed-skills.sh index 2eb07278..d41eaa17 100755 --- a/scripts/ci-validate-changed-skills.sh +++ b/scripts/ci-validate-changed-skills.sh @@ -27,7 +27,7 @@ else DIFF_CMD="git diff --name-only HEAD" fi -CHANGED=$($DIFF_CMD 2>/dev/null | grep -E '^([^/]+/skills/[^/]+/SKILL\.md|\.claude/skills/[^/]+/SKILL\.md)$' || true) +CHANGED=$($DIFF_CMD 2>/dev/null | grep -E '^[^/]+/skills/[^/]+/SKILL\.md$' | grep -v '^\.claude/' || true) if [ -z "$CHANGED" ]; then echo "No skills changed, skipping skill design validation" diff --git a/scripts/detect-changed-skills.sh b/scripts/detect-changed-skills.sh new file mode 100755 index 00000000..43be2074 --- /dev/null +++ b/scripts/detect-changed-skills.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Detect changed skills in CI (PRs and pushes to main) +# Outputs ALL skills in affected packs (not just changed skills) +# Exits 0 if no skills changed +# +# Strategy: If ANY skill changes in a pack (e.g., rh-virt), +# validate ALL skills in that pack for consistency + +set -e + +if [ -n "$VALIDATE_INCLUDE_UNCOMMITTED" ]; then + # Local dev: include staged + unstaged changes vs HEAD + DIFF_CMD="git diff --name-only HEAD" +elif [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + # Three-dot diff: merge-base(base, HEAD)..HEAD = changes in the PR + BASE_REF="${GITHUB_BASE_REF:-main}" + git fetch origin "$BASE_REF" 2>/dev/null || true + DIFF_CMD="git diff --name-only origin/$BASE_REF...HEAD" +elif [ "$GITHUB_EVENT_NAME" = "push" ]; then + # Push event: diff between before and after + BEFORE="${GITHUB_EVENT_BEFORE:-}" + if [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then + # No base commit for diff, exit with no changes + exit 0 + fi + DIFF_CMD="git diff --name-only $BEFORE HEAD" +else + # Default: local dev, include uncommitted changes + DIFF_CMD="git diff --name-only HEAD" +fi + +# Find changed SKILL.md files +# Exclude .claude/ directory (internal tooling, not subject to same validation) +CHANGED_FILES=$($DIFF_CMD 2>/dev/null | grep -E '^[^/]+/skills/[^/]+/SKILL\.md$' | grep -v '^\.claude/' || true) + +if [ -z "$CHANGED_FILES" ]; then + exit 0 +fi + +# Extract unique pack names (rh-virt, rh-sre, etc.) +AFFECTED_PACKS=$(echo "$CHANGED_FILES" | cut -d'/' -f1 | sort -u) + +# For each affected pack, find ALL skills in that pack +for pack in $AFFECTED_PACKS; do + find "$pack/skills" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort +done diff --git a/scripts/run-skill-linter.sh b/scripts/run-skill-linter.sh new file mode 100755 index 00000000..a9742c37 --- /dev/null +++ b/scripts/run-skill-linter.sh @@ -0,0 +1,184 @@ +#!/bin/bash +# Skill Specification Linter Reporter +# Runs agentskills.io spec linter on skills and generates detailed error report + summary table +# Usage: ./run-skill-linter.sh [skill-dir1] [skill-dir2] ... +# No args: validates ALL skills +# With args: validates only specified skill directories + +set -o pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +# Counters +TOTAL_SKILLS=0 +PASSED_SKILLS=0 +WARNED_SKILLS=0 +FAILED_SKILLS=0 +HAS_ERRORS=false + +# Storage for failed skills details +FAILED_DETAILS_FILE=$(mktemp) +SUMMARY_FILE=$(mktemp) + +# Header +echo "" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo -e "${BOLD} Skill Specification Linter Report${NC}" +echo -e "${BOLD} agentskills.io Specification Compliance${NC}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo "" + +# Determine which skills to validate +if [ $# -eq 0 ]; then + # No arguments: validate ALL collection skills (exclude .claude/ internal tooling) + SKILL_PATHS=$(find . -name "SKILL.md" -type f | grep -E "(rh-sre|rh-developer|rh-virt|ocp-admin|rh-support-engineer)/skills/" | sort) + VALIDATION_MODE="all" +else + # Arguments provided: validate only specified skill directories + SKILL_PATHS="" + for skill_dir in "$@"; do + # Remove trailing slash if present + skill_dir="${skill_dir%/}" + # Add SKILL.md path + if [ -f "$skill_dir/SKILL.md" ]; then + SKILL_PATHS="$SKILL_PATHS$skill_dir/SKILL.md"$'\n' + else + echo -e "${YELLOW}⚠️ Warning: $skill_dir/SKILL.md not found, skipping${NC}" + fi + done + SKILL_PATHS=$(echo "$SKILL_PATHS" | grep -v '^$' | sort) + VALIDATION_MODE="changed" +fi + +if [ -z "$SKILL_PATHS" ]; then + echo -e "${BLUE}ℹ️ No skills to validate${NC}" + exit 0 +fi + +# Count total skills +TOTAL_SKILLS=$(echo "$SKILL_PATHS" | wc -l | tr -d ' ') + +if [ "$VALIDATION_MODE" = "all" ]; then + echo -e "${BLUE}Validating ALL skills: ${TOTAL_SKILLS} skill(s)${NC}" +else + echo -e "${BLUE}Validating skills in affected packs: ${TOTAL_SKILLS} skill(s)${NC}" +fi +echo "" + +# Validate each skill +while IFS= read -r skill_file; do + skill_dir=$(dirname "$skill_file") + skill_name=$(basename "$skill_dir") + collection=$(echo "$skill_dir" | cut -d'/' -f2) + + # Run linter and capture output + LINTER_OUTPUT=$(mktemp) + ./.claude/skills/skill-linter/scripts/validate-skill.sh "$skill_dir" > "$LINTER_OUTPUT" 2>&1 + EXIT_CODE=$? + + # Parse output for pass/warn/fail + if [ $EXIT_CODE -eq 0 ]; then + if grep -q "\[WARN\]" "$LINTER_OUTPUT"; then + # Passed with warnings + WARNED_SKILLS=$((WARNED_SKILLS + 1)) + echo -e "⚠️ ${YELLOW}${collection}/${skill_name}${NC} - PASSED WITH WARNINGS" + else + # Clean pass + PASSED_SKILLS=$((PASSED_SKILLS + 1)) + echo -e "✅ ${GREEN}${collection}/${skill_name}${NC}" + fi + else + # Failed + FAILED_SKILLS=$((FAILED_SKILLS + 1)) + HAS_ERRORS=true + echo -e "❌ ${RED}${collection}/${skill_name}${NC} - FAILED" + + # Store detailed output for failed skills + echo "" >> "$FAILED_DETAILS_FILE" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" >> "$FAILED_DETAILS_FILE" + echo -e "${RED}${BOLD}FAILED: ${collection}/${skill_name}${NC}" >> "$FAILED_DETAILS_FILE" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" >> "$FAILED_DETAILS_FILE" + cat "$LINTER_OUTPUT" >> "$FAILED_DETAILS_FILE" + echo "" >> "$FAILED_DETAILS_FILE" + fi + + # Store summary entry + if [ $EXIT_CODE -eq 0 ]; then + if grep -q "\[WARN\]" "$LINTER_OUTPUT"; then + echo "${collection}/${skill_name}|⚠️ WARNINGS" >> "$SUMMARY_FILE" + else + echo "${collection}/${skill_name}|✅ PASSED" >> "$SUMMARY_FILE" + fi + else + echo "${collection}/${skill_name}|❌ FAILED" >> "$SUMMARY_FILE" + fi + + rm -f "$LINTER_OUTPUT" +done <<< "$SKILL_PATHS" + +echo "" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + +# Show detailed errors if any +if [ "$FAILED_SKILLS" -gt 0 ]; then + echo "" + echo -e "${RED}${BOLD}DETAILED ERROR REPORT${NC}" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + cat "$FAILED_DETAILS_FILE" + echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "" +fi + +# Summary Table +echo "" +echo -e "${BOLD}VALIDATION SUMMARY${NC}" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo "" +printf "${BOLD}%-42s%s${NC}\n" "Metric" "Count" +echo "────────────────────────────────────────────────────────────────" +printf "%-42s${BLUE}%s${NC}\n" "Total Skills:" "$TOTAL_SKILLS" +printf "✅ %-39s${GREEN}%s${NC}\n" "Passed:" "$PASSED_SKILLS" +printf "⚠️ %-40s${YELLOW}%s${NC}\n" "Passed with Warnings:" "$WARNED_SKILLS" +printf "❌ %-39s${RED}%s${NC}\n" "Failed:" "$FAILED_SKILLS" +echo "" + +# Status indicator +if [ "$HAS_ERRORS" = true ]; then + echo -e "${RED}${BOLD}❌ VALIDATION FAILED - ERRORS DETECTED${NC}" + echo -e "${RED}Skills with errors must be fixed before merge${NC}" + EXIT_STATUS=1 +elif [ "$WARNED_SKILLS" -gt 0 ]; then + echo -e "${YELLOW}${BOLD}⚠️ PASSED WITH WARNINGS${NC}" + echo -e "${YELLOW}Review warnings above - PR can be merged${NC}" + EXIT_STATUS=0 +else + echo -e "${GREEN}${BOLD}✅ ALL SKILLS PASSED${NC}" + EXIT_STATUS=0 +fi + +echo "" +echo -e "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +echo "" + +# Additional guidance on errors +if [ "$FAILED_SKILLS" -gt 0 ]; then + echo -e "${BOLD}How to fix:${NC}" + echo "1. Review the detailed error report above" + echo "2. Run locally: ./.claude/skills/skill-linter/scripts/validate-skill.sh " + echo "3. See agentskills.io specification: https://agentskills.io/specification" + echo "" +fi + +# Cleanup +rm -f "$FAILED_DETAILS_FILE" "$SUMMARY_FILE" + +# Save exit code for workflow +echo "$EXIT_STATUS" > /tmp/skill-linter-exit-code + +exit $EXIT_STATUS diff --git a/scripts/validate-skills.sh b/scripts/validate-skills.sh new file mode 100755 index 00000000..a70b862a --- /dev/null +++ b/scripts/validate-skills.sh @@ -0,0 +1,696 @@ +#!/bin/bash +# +# validate-skills.sh +# Universal skill validation for all agentic collections +# +# Validates skills against: +# - Tier 1: agentskills.io specification (via run-skill-linter.sh) +# - Tier 2: Repository design principles (SKILL_DESIGN_PRINCIPLES.md) +# +# Usage: +# ./scripts/validate-skills.sh [path] # Validate specific skill or collection +# ./scripts/validate-skills.sh # Validate all collections +# ./scripts/validate-skills.sh --strict # Exit on first error +# + +set -o pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Counters +TOTAL_SKILLS=0 +PASSED_SKILLS=0 +FAILED_SKILLS=0 +TOTAL_ERRORS=0 + +# Flags +STRICT_MODE=false +VERBOSE=false +TARGET_PATHS=() + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --strict) + STRICT_MODE=true + shift + ;; + --verbose|-v) + VERBOSE=true + shift + ;; + --help|-h) + echo "Usage: $0 [OPTIONS] [PATH...]" + echo "" + echo "Options:" + echo " --strict Exit on first error" + echo " --verbose,-v Show detailed validation steps" + echo " --help,-h Show this help message" + echo "" + echo "Examples:" + echo " $0 # Validate all skills in current directory" + echo " $0 path/to/collection/ # Validate all skills in collection" + echo " $0 path/to/skill-dir/ # Validate single skill" + echo " $0 path/to/dir/* # Validate multiple paths using glob" + echo " $0 skill1/ skill2/ skill3/ # Validate specific skills" + echo " $0 --strict path/ # Exit on first error" + exit 0 + ;; + *) + TARGET_PATHS+=("$1") + shift + ;; + esac +done + +# Validation functions + +log_info() { + if [[ "$VERBOSE" == true ]]; then + echo -e "${BLUE}ℹ${NC} $1" + fi +} + +log_pass() { + echo -e "${GREEN}✓${NC} $1" +} + +log_error() { + echo -e "${RED}✗${NC} $1" + ((TOTAL_ERRORS++)) +} + +log_warn() { + echo -e "${YELLOW}⚠${NC} $1" +} + +# Section 0: agentskills.io Specification Compliance +validate_agentskills_spec() { + local skill_dir="$1" + local skill_name=$(basename "$skill_dir") + local errors=0 + + log_info "Validating agentskills.io specification for $skill_name" + + # Check SKILL.md exists + if [[ ! -f "$skill_dir/SKILL.md" ]]; then + log_error "$skill_name: Missing SKILL.md file" + ((errors++)) + fi + + # Validate name format (1-64 chars, lowercase, numbers, hyphens only) + if ! echo "$skill_name" | grep -Eq '^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$'; then + log_error "$skill_name: Invalid name format (must be 1-64 chars, lowercase, no consecutive hyphens)" + ((errors++)) + fi + + # Check for consecutive hyphens + if echo "$skill_name" | grep -q '\-\-'; then + log_error "$skill_name: Name contains consecutive hyphens (--)" + ((errors++)) + fi + + # Check name matches directory + if [[ -f "$skill_dir/SKILL.md" ]]; then + local frontmatter_name=$(awk '/^---$/{if(++n==2) exit} n==1' "$skill_dir/SKILL.md" | grep '^name:' | sed 's/name: *//' | tr -d '"' | tr -d "'") + if [[ -n "$frontmatter_name" && "$frontmatter_name" != "$skill_name" ]]; then + log_error "$skill_name: Name in frontmatter ($frontmatter_name) doesn't match directory name" + ((errors++)) + fi + fi + + return $errors +} + +# Section 1: YAML Frontmatter +validate_yaml_frontmatter() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating YAML frontmatter for $skill_name" + + # Extract frontmatter (between first two --- markers) + if ! grep -q '^---$' "$skill_file"; then + log_error "$skill_name: Missing YAML frontmatter delimiters (---)" + ((errors++)) + return $errors + fi + + local frontmatter=$(awk '/^---$/{if(++n==2) exit} n==1' "$skill_file") + + # Check required fields + if ! echo "$frontmatter" | grep -q '^name:'; then + log_error "$skill_name: Missing 'name' field in frontmatter" + ((errors++)) + fi + + if ! echo "$frontmatter" | grep -q '^description:'; then + log_error "$skill_name: Missing 'description' field in frontmatter" + ((errors++)) + fi + + # Check model field exists + if ! echo "$frontmatter" | grep -q '^model:'; then + log_error "$skill_name: Missing 'model' field in frontmatter (MANDATORY)" + ((errors++)) + else + # Validate model value is one of: inherit, sonnet, haiku + local model_value=$(echo "$frontmatter" | grep '^model:' | sed 's/model: *//' | tr -d '"' | tr -d "'" | xargs) + if [[ "$model_value" != "inherit" && "$model_value" != "sonnet" && "$model_value" != "haiku" ]]; then + log_error "$skill_name: Invalid model value '$model_value' (must be: inherit, sonnet, or haiku)" + ((errors++)) + fi + fi + + # Check color field exists (MANDATORY) + if ! echo "$frontmatter" | grep -q '^color:'; then + log_error "$skill_name: Missing 'color' field in frontmatter (MANDATORY)" + ((errors++)) + else + # Validate color value is one of: cyan, green, blue, yellow, red + local color_value=$(echo "$frontmatter" | grep '^color:' | sed 's/color: *//' | tr -d '"' | tr -d "'" | xargs) + if [[ "$color_value" != "cyan" && "$color_value" != "green" && "$color_value" != "blue" && "$color_value" != "yellow" && "$color_value" != "red" ]]; then + log_error "$skill_name: Invalid color value '$color_value' (must be: cyan, green, blue, yellow, or red)" + ((errors++)) + fi + fi + + # Check description has "Use when" examples (check in full frontmatter) + if ! echo "$frontmatter" | grep -qi "use when\|trigger\|request"; then + log_warn "$skill_name: Description should include 'Use when' examples" + fi + + # Check description has "NOT for" anti-pattern + if ! echo "$frontmatter" | grep -qi "NOT for\|Do NOT\|not for"; then + log_warn "$skill_name: Description should include 'NOT for' anti-pattern" + fi + + # Check description length (approximate - under 1024 chars per agentskills.io) + # Extract just the description value + local description=$(echo "$frontmatter" | awk '/^description:/,/^[a-z_]+:/ {if (!/^[a-z_]+:/ || /^description:/) print}' | sed '1d') + local desc_length=$(echo "$description" | wc -c) + if [[ $desc_length -gt 1024 ]]; then + log_error "$skill_name: Description exceeds 1024 characters ($desc_length chars)" + ((errors++)) + fi + + return $errors +} + +# Section 1.5: SKILL.md Header Format (Section 12 in checklist) +validate_skill_header() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating SKILL.md header format for $skill_name" + + # Extract the first heading after frontmatter + local first_heading=$(awk '/^---$/{if(++n==2) {getline; while(getline) {if(/^# /) {print; exit}}}}' "$skill_file") + + if [[ -z "$first_heading" ]]; then + log_error "$skill_name: Missing level 1 heading (# ) after frontmatter" + ((errors++)) + return $errors + fi + + # Validate heading format: # / Skill or # [Skill Name] + # Extract heading text (remove leading "# ") + local heading_text=$(echo "$first_heading" | sed 's/^# *//') + + # Check if it follows one of the standard formats + local valid_format=false + + # Format 1: / Skill + if echo "$heading_text" | grep -Eq "^/$skill_name Skill$"; then + valid_format=true + fi + + # Format 2: [Title Case] Skill (flexible matching) + if echo "$heading_text" | grep -Eq " Skill$"; then + valid_format=true + fi + + if [[ "$valid_format" == false ]]; then + log_warn "$skill_name: Heading '$heading_text' should follow format: '/$skill_name Skill' or '[Skill Name]'" + fi + + # Check for overview paragraph after heading + # Get first non-empty line after the heading + local overview=$(awk '/^---$/{if(++n==2) found=1} found && /^# / {getline; while(getline) {if(/^[^#]/ && NF>0) {print; exit}}}' "$skill_file") + + if [[ -z "$overview" ]]; then + log_warn "$skill_name: Missing overview paragraph (1-2 sentences) immediately after heading" + fi + + return $errors +} + +# Section 2: Mandatory Section Order +validate_section_order() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating section order for $skill_name" + + # Required sections in order (after frontmatter) + local required_sections=( + "^# " + "^## Prerequisites" + "^## When to Use This Skill" + "^## Workflow" + "^## Dependencies" + ) + + local prev_line=0 + for section in "${required_sections[@]}"; do + local line_num=$(grep -n "$section" "$skill_file" | head -1 | cut -d: -f1) + + if [[ -z "$line_num" ]]; then + log_error "$skill_name: Missing required section matching '$section'" + ((errors++)) + continue + fi + + if [[ $line_num -le $prev_line ]]; then + log_error "$skill_name: Section '$section' out of order (line $line_num, expected after line $prev_line)" + ((errors++)) + fi + + prev_line=$line_num + done + + return $errors +} + +# Section 3: Prerequisites Section +validate_prerequisites() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating Prerequisites section for $skill_name" + + # Check Prerequisites section exists + if ! grep -q "^## Prerequisites" "$skill_file"; then + log_error "$skill_name: Missing Prerequisites section" + ((errors++)) + return $errors + fi + + # Extract Prerequisites section + local prereqs=$(awk '/^## Prerequisites$/,/^## [^P]/' "$skill_file") + + # Check for required subsections (relaxed checking) + if ! echo "$prereqs" | grep -qi "required.*mcp.*server\|mcp.*server"; then + log_warn "$skill_name: Prerequisites should list Required MCP Servers" + fi + + if ! echo "$prereqs" | grep -qi "verification\|verify"; then + log_warn "$skill_name: Prerequisites should include verification steps" + fi + + if ! echo "$prereqs" | grep -qi "human notification\|error.*protocol"; then + log_warn "$skill_name: Prerequisites should include human notification protocol" + fi + + # Check for security warning about credentials + if ! echo "$prereqs" | grep -qi "never.*display\|never.*expose\|credential.*value"; then + log_warn "$skill_name: Prerequisites should warn against exposing credentials" + fi + + return $errors +} + +# Section 4: When to Use This Skill +validate_when_to_use() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating 'When to Use' section for $skill_name" + + if ! grep -q "^## When to Use This Skill" "$skill_file"; then + log_error "$skill_name: Missing 'When to Use This Skill' section" + ((errors++)) + return $errors + fi + + local when_section=$(awk '/^## When to Use This Skill$/,/^## [^W]/' "$skill_file") + + # Check for "Use when" scenarios + if ! echo "$when_section" | grep -qi "use.*when\|trigger.*when\|invoke.*when"; then + log_warn "$skill_name: 'When to Use' section should list specific scenarios" + fi + + # Check for "Do NOT use" anti-patterns + if ! echo "$when_section" | grep -qi "do not\|NOT for\|not when"; then + log_error "$skill_name: 'When to Use' section must include 'Do NOT use' anti-patterns" + ((errors++)) + fi + + # Check that anti-patterns mention alternative skills + if echo "$when_section" | grep -qi "do not\|NOT for" && \ + ! echo "$when_section" | grep -qi "use.*skill\|instead\|alternative"; then + log_warn "$skill_name: Anti-patterns should reference alternative skills by name" + fi + + return $errors +} + +# Section 5: Workflow Section +validate_workflow() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating Workflow section for $skill_name" + + if ! grep -q "^## Workflow" "$skill_file"; then + log_error "$skill_name: Missing Workflow section" + ((errors++)) + return $errors + fi + + local workflow=$(awk '/^## Workflow$/,/^## [^W]/' "$skill_file") + + # Check for workflow steps + if ! echo "$workflow" | grep -q "^### Step\|^### [0-9]"; then + log_warn "$skill_name: Workflow should have numbered steps (### Step N: or ### 1.)" + fi + + # Check for MCP Tool references + if ! echo "$workflow" | grep -qi "\*\*MCP Tool\*\*\|mcp.*tool"; then + log_warn "$skill_name: Workflow should specify MCP Tools used" + fi + + # Check for Parameters specification + if echo "$workflow" | grep -qi "\*\*MCP Tool\*\*" && \ + ! echo "$workflow" | grep -qi "\*\*Parameters\*\*"; then + log_warn "$skill_name: Workflow steps with MCP Tools should specify parameters" + fi + + # Check for Error Handling + if ! echo "$workflow" | grep -qi "\*\*Error.*Handling\*\*\|error.*condition"; then + log_warn "$skill_name: Workflow steps should include error handling" + fi + + return $errors +} + +# Section 6: Document Consultation Transparency +validate_document_consultation() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating document consultation transparency for $skill_name" + + # If skill mentions consulting documents, verify it follows the pattern + if grep -qi "consult.*document\|read.*doc" "$skill_file"; then + if ! grep -qi "Read tool\|Read.*to understand" "$skill_file"; then + log_warn "$skill_name: Document consultation should use Read tool first (CLAUDE.md Principle #1)" + fi + + if ! grep -qi "I consulted\|Output to user" "$skill_file"; then + log_warn "$skill_name: Document consultation should declare consultation to user" + fi + fi + + return $errors +} + +# Section 7: Dependencies Section +validate_dependencies() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating Dependencies section for $skill_name" + + if ! grep -q "^## Dependencies" "$skill_file"; then + log_error "$skill_name: Missing Dependencies section" + ((errors++)) + return $errors + fi + + local deps=$(awk '/^## Dependencies$/,/^## [^D]/' "$skill_file") + + # Check for required subsections + if ! echo "$deps" | grep -qi "required.*mcp.*server"; then + log_warn "$skill_name: Dependencies should list Required MCP Servers" + fi + + if ! echo "$deps" | grep -qi "required.*mcp.*tool\|mcp.*tool"; then + log_warn "$skill_name: Dependencies should list Required MCP Tools" + fi + + return $errors +} + +# Section 8: Human-in-the-Loop Requirements +validate_human_in_loop() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating Human-in-the-Loop requirements for $skill_name" + + # Check if skill modifies state (create, delete, update, restore, execute) + local is_mutating=false + if echo "$skill_name" | grep -Eq "create|delete|update|restore|execute|modify|run|deploy|clone"; then + is_mutating=true + fi + + # If mutating, should have Human-in-the-Loop section + if [[ "$is_mutating" == true ]]; then + if ! grep -q "^## Critical: Human-in-the-Loop Requirements" "$skill_file"; then + log_warn "$skill_name: Modifying skill should have 'Human-in-the-Loop Requirements' section" + else + local hitl=$(awk '/^## Critical: Human-in-the-Loop Requirements$/,/^## [^C]/' "$skill_file") + + # Check for confirmation requirements + if ! echo "$hitl" | grep -qi "confirmation\|confirm\|approval\|never assume"; then + log_warn "$skill_name: Human-in-the-Loop section should specify confirmation requirements" + fi + fi + fi + + return $errors +} + +# Section 9: Security Requirements +validate_security() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating security requirements for $skill_name" + + # Check for credential exposure in examples + if grep -q 'echo \$.*SECRET\|echo \$.*PASSWORD\|echo \$.*TOKEN\|echo \$.*KEY' "$skill_file"; then + log_error "$skill_name: SECURITY VIOLATION - Exposing credential values with 'echo \$VAR'" + ((errors++)) + fi + + # Check for hardcoded credentials (common patterns) + if grep -Eq 'password.*=.*["'"'"'][^$]|secret.*=.*["'"'"'][^$]|token.*=.*["'"'"'][^$]' "$skill_file"; then + log_warn "$skill_name: Possible hardcoded credentials found (review manually)" + fi + + # Check for proper credential checking pattern + if grep -q 'KUBECONFIG\|CLIENT_SECRET\|API_KEY\|TOKEN' "$skill_file"; then + if grep -q 'test -n.*\$\|if \[ -n.*\$' "$skill_file"; then + log_info "$skill_name: Uses proper credential checking (test -n)" + fi + fi + + return $errors +} + +# Section 10: Content Quality +validate_content_quality() { + local skill_file="$1" + local skill_name="$2" + local errors=0 + + log_info "Validating content quality for $skill_name" + + # Check for broken markdown links (basic check) + local broken_links=$(grep -o '\[.*\](.*\.md)' "$skill_file" | grep -o '(.*\.md)' | tr -d '()' || true) + if [[ -n "$broken_links" ]]; then + while IFS= read -r link; do + # Convert relative path to absolute + local skill_dir=$(dirname "$skill_file") + local abs_link="$skill_dir/$link" + + # Resolve relative paths (../) + abs_link=$(cd "$skill_dir" && realpath -m "$link" 2>/dev/null || echo "$abs_link") + + if [[ ! -f "$abs_link" ]]; then + log_warn "$skill_name: Possibly broken link: $link" + fi + done <<< "$broken_links" + fi + + # Check file size (recommend under 5000 tokens ≈ 20KB for text) + local file_size=$(wc -c < "$skill_file") + if [[ $file_size -gt 20480 ]]; then + log_warn "$skill_name: SKILL.md is large ($file_size bytes). Consider moving content to references/" + fi + + return $errors +} + +# Main validation function +validate_skill() { + local skill_dir="$1" + local skill_name=$(basename "$skill_dir") + local skill_file="$skill_dir/SKILL.md" + + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo -e "${BLUE}Validating:${NC} $skill_name" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + ((TOTAL_SKILLS++)) + + local skill_errors=0 + + # Run all validations + validate_agentskills_spec "$skill_dir" || ((skill_errors+=$?)) + + if [[ -f "$skill_file" ]]; then + validate_yaml_frontmatter "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_skill_header "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_section_order "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_prerequisites "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_when_to_use "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_workflow "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_document_consultation "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_dependencies "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_human_in_loop "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_security "$skill_file" "$skill_name" || ((skill_errors+=$?)) + validate_content_quality "$skill_file" "$skill_name" || ((skill_errors+=$?)) + fi + + # Report skill result + echo "" + if [[ $skill_errors -eq 0 ]]; then + log_pass "Skill '$skill_name' passed validation" + ((PASSED_SKILLS++)) + else + log_error "Skill '$skill_name' failed with $skill_errors error(s)" + ((FAILED_SKILLS++)) + + if [[ "$STRICT_MODE" == true ]]; then + echo "" + echo -e "${RED}Strict mode enabled. Exiting on first failure.${NC}" + exit 1 + fi + fi + + return $skill_errors +} + +# Find and validate skills +find_and_validate_skills() { + local search_path="${1:-.}" + + # Check if path exists + if [[ ! -e "$search_path" ]]; then + echo -e "${YELLOW}Path not found: '$search_path'${NC}" + return 0 + fi + + # If the path directly contains SKILL.md, validate it as a single skill + if [[ -f "$search_path/SKILL.md" ]]; then + validate_skill "$search_path" + return 0 + fi + + # If the path IS a SKILL.md file, validate its parent directory + if [[ -f "$search_path" && $(basename "$search_path") == "SKILL.md" ]]; then + local skill_dir=$(dirname "$search_path") + validate_skill "$skill_dir" + return 0 + fi + + # Otherwise, search recursively for SKILL.md files + local skill_files=() + while IFS= read -r -d '' file; do + skill_files+=("$file") + done < <(find "$search_path" -type f -name "SKILL.md" -print0 2>/dev/null) + + if [[ ${#skill_files[@]} -eq 0 ]]; then + echo -e "${YELLOW}No skills found in '$search_path'${NC}" + return 0 + fi + + # Validate each skill + for skill_file in "${skill_files[@]}"; do + local skill_dir=$(dirname "$skill_file") + validate_skill "$skill_dir" + done +} + +# Main execution +main() { + local repo_root + repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) + cd "$repo_root" || exit 1 + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo -e "${BLUE}Universal Skill Validator${NC}" + echo "Validates skills against SKILL_DESIGN_PRINCIPLES.md" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + # Determine target paths + if [[ ${#TARGET_PATHS[@]} -eq 0 ]]; then + echo -e "Target: ${BLUE}Current directory (.)${NC}" + TARGET_PATHS=(".") + else + echo -e "Target: ${BLUE}${#TARGET_PATHS[@]} path(s) specified${NC}" + fi + + if [[ "$STRICT_MODE" == true ]]; then + echo -e "Mode: ${RED}Strict (exit on first error)${NC}" + fi + + echo "" + + # Run validation on all target paths + for target_path in "${TARGET_PATHS[@]}"; do + find_and_validate_skills "$target_path" + done + + # Print summary + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo -e "${BLUE}Validation Summary${NC}" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo -e "Total Skills: $TOTAL_SKILLS" + echo -e "${GREEN}Passed:${NC} $PASSED_SKILLS" + echo -e "${RED}Failed:${NC} $FAILED_SKILLS" + echo -e "${RED}Total Errors:${NC} $TOTAL_ERRORS" + echo "" + + # Exit code + if [[ $FAILED_SKILLS -eq 0 ]]; then + echo -e "${GREEN}✓ All skills passed validation${NC}" + exit 0 + else + echo -e "${RED}✗ Some skills failed validation${NC}" + exit 1 + fi +} + +# Run main +main "$@"