Skip to main content
Configuration CI/CD Best Practices Validation

Validating Your Claude Code Configuration

How to validate .claude/ directory configuration files. Ensure Commands, Skills, Agents, and Hooks follow the correct format with automated CI checks.

January 19, 2026 6 min read By Claude World

As your Claude Code configuration grows, keeping everything properly formatted becomes critical. A missing frontmatter or invalid JSON can silently break your workflows.

This guide shows you how to validate your .claude/ configuration automatically.

The Problem

Claude Code’s .claude/ directory can contain:

.claude/
├── commands/     # Slash commands (*.md)
├── skills/       # Skills (*/SKILL.md)
├── agents/       # Custom agents (*.md)
├── hooks/        # Hook scripts (*.sh)
├── settings.json # Project settings
└── settings.local.json # Local overrides

Each file type has specific format requirements:

File TypeRequirements
CommandsFrontmatter with description
Skills# Title header required
AgentsFrontmatter with name, description, tools
HooksExecutable, bash shebang
SettingsValid JSON

One typo breaks the entire workflow.

The Solution: Automated Validation

Create a validation script that runs in CI:

#!/bin/bash
# scripts/validate-claude-config.sh

ERRORS=0

# Validate Commands
for file in .claude/commands/*.md; do
    [ -f "$file" ] || continue

    # Check frontmatter exists
    if ! head -1 "$file" | grep -q "^---$"; then
        echo "❌ $file: Missing frontmatter"
        ((ERRORS++))
    fi

    # Check description field
    if ! grep -q "^description:" "$file"; then
        echo "⚠️ $file: Missing description"
    fi
done

# Validate Agents
for file in .claude/agents/*.md; do
    [ -f "$file" ] || continue

    if ! head -1 "$file" | grep -q "^---$"; then
        echo "❌ $file: Missing frontmatter"
        ((ERRORS++))
    fi

    for field in name description tools; do
        if ! grep -q "^$field:" "$file"; then
            echo "❌ $file: Missing '$field'"
            ((ERRORS++))
        fi
    done
done

# Validate Skills
for file in .claude/skills/*/SKILL.md; do
    [ -f "$file" ] || continue

    if ! grep -q "^# " "$file"; then
        echo "❌ $file: Missing title"
        ((ERRORS++))
    fi
done

# Validate Hooks
for file in .claude/hooks/*.sh; do
    [ -f "$file" ] || continue

    if [ ! -x "$file" ]; then
        echo "❌ $file: Not executable"
        ((ERRORS++))
    fi
done

# Validate JSON settings
for json in .claude/settings.json .claude/settings.local.json; do
    [ -f "$json" ] || continue

    if ! python3 -c "import json; json.load(open('$json'))" 2>/dev/null; then
        echo "❌ $json: Invalid JSON"
        ((ERRORS++))
    fi
done

exit $ERRORS

GitHub Actions Integration

Add to your CI pipeline:

# .github/workflows/validate.yml
name: Validate Claude Config

on:
  push:
    paths:
      - '.claude/**'
  pull_request:
    paths:
      - '.claude/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Validate Claude Config
        run: ./scripts/validate-claude-config.sh

Now every PR touching .claude/ will be validated automatically.

File Format Reference

Commands (.claude/commands/*.md)

---
description: Brief description of command
---

# Command Name

Your command instructions here...

Required:

  • Frontmatter block (---)
  • description field

Agents (.claude/agents/*.md)

---
name: agent-name
description: What this agent does
tools: [Read, Write, Bash]
model: sonnet
---

# Agent System Prompt

Instructions for the agent...

Required:

  • name: Agent identifier
  • description: What it does
  • tools: Array of allowed tools

Optional:

  • model: haiku, sonnet, opus

Skills (.claude/skills/*/SKILL.md)

---
description: What this skill does
user-invocable: true
context: fork
agent: Explore
---

# Skill Name

Skill instructions...

Required:

  • # Title header

Optional:

  • description: Auto-detection trigger
  • user-invocable: Can be called with /skillname
  • context: main (default) or fork
  • agent: Which agent executes

Hooks (.claude/hooks/*.sh)

#!/bin/bash
# Your hook logic here

Required:

  • Executable permission (chmod +x)
  • Bash shebang (#!/bin/bash)

Settings (.claude/settings.json)

{
  "permissions": {
    "allow": ["Read", "Write"],
    "deny": ["Bash(*rm -rf*)"]
  },
  "hooks": {
    "PreToolUse": [...]
  }
}

Required:

  • Valid JSON syntax

Common Mistakes

1. Missing Frontmatter Delimiter

<!-- ❌ Wrong -->
description: My command

# Command

<!-- ✅ Correct -->
---
description: My command
---

# Command

2. Invalid Hook Permissions

# ❌ Script not executable
$ ./scripts/my-hook.sh
bash: permission denied

# ✅ Fix it
$ chmod +x ./scripts/my-hook.sh

3. JSON Trailing Comma

// ❌ Invalid JSON
{
  "allow": ["Read", "Write",]
}

// ✅ Valid JSON
{
  "allow": ["Read", "Write"]
}

4. Wrong Frontmatter Field Names

<!-- ❌ Wrong field name -->
---
desc: My agent
---

<!-- ✅ Correct field name -->
---
description: My agent
---

Pre-commit Hook

Run validation before every commit:

# .git/hooks/pre-commit
#!/bin/bash
./scripts/validate-claude-config.sh

Or use pre-commit framework:

# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: validate-claude-config
        name: Validate Claude Config
        entry: ./scripts/validate-claude-config.sh
        language: script
        files: ^\.claude/

Summary

CheckWhat It Validates
CommandsFrontmatter + description
AgentsFrontmatter + name + description + tools
SkillsTitle header
HooksExecutable + shebang
SettingsValid JSON

Automate validation in CI to catch errors before they break your workflow.


Get the full validation script: validate-claude-config.sh