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.
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 Type | Requirements |
|---|---|
| Commands | Frontmatter with description |
| Skills | # Title header required |
| Agents | Frontmatter with name, description, tools |
| Hooks | Executable, bash shebang |
| Settings | Valid 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 (
---) descriptionfield
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 identifierdescription: What it doestools: 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:
# Titleheader
Optional:
description: Auto-detection triggeruser-invocable: Can be called with /skillnamecontext: main (default) or forkagent: 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
| Check | What It Validates |
|---|---|
| Commands | Frontmatter + description |
| Agents | Frontmatter + name + description + tools |
| Skills | Title header |
| Hooks | Executable + shebang |
| Settings | Valid JSON |
Automate validation in CI to catch errors before they break your workflow.
Get the full validation script: validate-claude-config.sh