- Check agents/*.md, commands/*.md, workflows/*.yaml, state/*.json - Add gtasks and other missing skills to validation list 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
221 lines
6.8 KiB
Bash
Executable File
221 lines
6.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Validate the Claude Code configuration setup
|
|
# Run this after changes to ensure everything is properly configured
|
|
|
|
set -euo pipefail
|
|
|
|
CLAUDE_DIR="${HOME}/.claude"
|
|
ERRORS=0
|
|
WARNINGS=0
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
pass() { echo -e "${GREEN}✓${NC} $1"; }
|
|
fail() { echo -e "${RED}✗${NC} $1"; ((ERRORS++)); }
|
|
warn() { echo -e "${YELLOW}!${NC} $1"; ((WARNINGS++)); }
|
|
|
|
echo "Validating Claude Code configuration..."
|
|
echo ""
|
|
|
|
# Check required directories
|
|
echo "=== Directory Structure ==="
|
|
for dir in agents commands hooks mcp skills state workflows automation; do
|
|
if [[ -d "${CLAUDE_DIR}/${dir}" ]]; then
|
|
pass "${dir}/ exists"
|
|
else
|
|
fail "${dir}/ missing"
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Check required files
|
|
echo "=== Core Files ==="
|
|
for file in CLAUDE.md README.md settings.json .gitignore; do
|
|
if [[ -f "${CLAUDE_DIR}/${file}" ]]; then
|
|
pass "${file} exists"
|
|
else
|
|
fail "${file} missing"
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Check plugin manifest
|
|
echo "=== Plugin Structure ==="
|
|
if [[ -f "${CLAUDE_DIR}/.claude-plugin/plugin.json" ]]; then
|
|
pass "plugin.json exists"
|
|
if python3 -c "import json; json.load(open('${CLAUDE_DIR}/.claude-plugin/plugin.json'))" 2>/dev/null; then
|
|
pass "plugin.json is valid JSON"
|
|
else
|
|
fail "plugin.json is invalid JSON"
|
|
fi
|
|
else
|
|
warn "plugin.json missing (optional for local use)"
|
|
fi
|
|
echo ""
|
|
|
|
# Check hooks
|
|
echo "=== Hooks ==="
|
|
if [[ -f "${CLAUDE_DIR}/hooks/hooks.json" ]]; then
|
|
pass "hooks.json exists"
|
|
if python3 -c "import json; json.load(open('${CLAUDE_DIR}/hooks/hooks.json'))" 2>/dev/null; then
|
|
pass "hooks.json is valid JSON"
|
|
else
|
|
fail "hooks.json is invalid JSON"
|
|
fi
|
|
else
|
|
warn "hooks.json missing"
|
|
fi
|
|
|
|
for script in session-start.sh pre-compact.sh; do
|
|
if [[ -x "${CLAUDE_DIR}/hooks/scripts/${script}" ]]; then
|
|
pass "hooks/scripts/${script} is executable"
|
|
elif [[ -f "${CLAUDE_DIR}/hooks/scripts/${script}" ]]; then
|
|
fail "hooks/scripts/${script} exists but not executable"
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Check skills
|
|
echo "=== Skills ==="
|
|
for skill in gmail gcal gtasks k8s-quick-status sysadmin-health usage programmer-add-project morning-report stock-lookup rag-search; do
|
|
skill_dir="${CLAUDE_DIR}/skills/${skill}"
|
|
if [[ -f "${skill_dir}/SKILL.md" ]]; then
|
|
pass "${skill}/SKILL.md exists"
|
|
|
|
# Check for scripts directory
|
|
if [[ -d "${skill_dir}/scripts" ]]; then
|
|
script_count=$(find "${skill_dir}/scripts" -type f \( -name "*.py" -o -name "*.sh" \) | wc -l)
|
|
if [[ ${script_count} -gt 0 ]]; then
|
|
pass "${skill}/scripts/ has ${script_count} script(s)"
|
|
|
|
# Check executability
|
|
while IFS= read -r script; do
|
|
[[ -f "$script" ]] || continue
|
|
if [[ -x "$script" ]]; then
|
|
pass "$(basename "$script") is executable"
|
|
else
|
|
fail "$(basename "$script") not executable"
|
|
fi
|
|
done < <(find "${skill_dir}/scripts" -type f \( -name "*.py" -o -name "*.sh" \) 2>/dev/null)
|
|
fi
|
|
fi
|
|
else
|
|
fail "${skill}/SKILL.md missing"
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Check state files
|
|
echo "=== State Files ==="
|
|
for file in component-registry.json autonomy-levels.json model-policy.json; do
|
|
state_file="${CLAUDE_DIR}/state/${file}"
|
|
if [[ -f "${state_file}" ]]; then
|
|
pass "state/${file} exists"
|
|
if python3 -c "import json; json.load(open('${state_file}'))" 2>/dev/null; then
|
|
pass "state/${file} is valid JSON"
|
|
else
|
|
fail "state/${file} is invalid JSON"
|
|
fi
|
|
else
|
|
fail "state/${file} missing"
|
|
fi
|
|
done
|
|
echo ""
|
|
|
|
# Check hybrid format enforcement
|
|
echo "=== Hybrid Format (md/json/yaml) ==="
|
|
|
|
# Agents must be .md
|
|
non_md_agents=$(find "${CLAUDE_DIR}/agents" -type f ! -name "*.md" ! -name "README*" 2>/dev/null | wc -l)
|
|
if [[ ${non_md_agents} -eq 0 ]]; then
|
|
pass "All agent files are .md"
|
|
else
|
|
fail "Found ${non_md_agents} non-.md files in agents/"
|
|
find "${CLAUDE_DIR}/agents" -type f ! -name "*.md" ! -name "README*" 2>/dev/null | while read f; do
|
|
echo " - $(basename "$f")"
|
|
done
|
|
fi
|
|
|
|
# Commands must be .md
|
|
non_md_commands=$(find "${CLAUDE_DIR}/commands" -type f ! -name "*.md" ! -name "README*" 2>/dev/null | wc -l)
|
|
if [[ ${non_md_commands} -eq 0 ]]; then
|
|
pass "All command files are .md"
|
|
else
|
|
fail "Found ${non_md_commands} non-.md files in commands/"
|
|
find "${CLAUDE_DIR}/commands" -type f ! -name "*.md" ! -name "README*" 2>/dev/null | while read f; do
|
|
echo " - $(basename "$f")"
|
|
done
|
|
fi
|
|
|
|
# Workflows must be .yaml
|
|
non_yaml_workflows=$(find "${CLAUDE_DIR}/workflows" -type f ! -name "*.yaml" ! -name "*.yml" ! -name "README*" 2>/dev/null | wc -l)
|
|
if [[ ${non_yaml_workflows} -eq 0 ]]; then
|
|
pass "All workflow files are .yaml"
|
|
else
|
|
fail "Found ${non_yaml_workflows} non-.yaml files in workflows/"
|
|
find "${CLAUDE_DIR}/workflows" -type f ! -name "*.yaml" ! -name "*.yml" ! -name "README*" 2>/dev/null | while read f; do
|
|
echo " - $(basename "$f")"
|
|
done
|
|
fi
|
|
|
|
# State must be .json (excluding subdirectories with their own patterns)
|
|
non_json_state=$(find "${CLAUDE_DIR}/state" -maxdepth 1 -type f ! -name "*.json" ! -name "README*" 2>/dev/null | wc -l)
|
|
if [[ ${non_json_state} -eq 0 ]]; then
|
|
pass "All top-level state files are .json"
|
|
else
|
|
fail "Found ${non_json_state} non-.json files in state/"
|
|
find "${CLAUDE_DIR}/state" -maxdepth 1 -type f ! -name "*.json" ! -name "README*" 2>/dev/null | while read f; do
|
|
echo " - $(basename "$f")"
|
|
done
|
|
fi
|
|
echo ""
|
|
|
|
# Check Gmail setup
|
|
echo "=== Gmail Integration ==="
|
|
if [[ -d "${CLAUDE_DIR}/mcp/gmail/venv" ]]; then
|
|
pass "Gmail venv exists"
|
|
if [[ -f "${CLAUDE_DIR}/mcp/gmail/venv/bin/python" ]]; then
|
|
pass "Gmail venv has Python"
|
|
else
|
|
fail "Gmail venv missing Python"
|
|
fi
|
|
else
|
|
warn "Gmail venv not set up"
|
|
fi
|
|
|
|
if [[ -f "${HOME}/.gmail-mcp/credentials.json" ]]; then
|
|
pass "Gmail credentials exist"
|
|
else
|
|
warn "Gmail credentials missing (~/.gmail-mcp/credentials.json)"
|
|
fi
|
|
echo ""
|
|
|
|
# Check READMEs
|
|
echo "=== Documentation ==="
|
|
readme_count=0
|
|
for dir in agents commands hooks mcp skills state workflows; do
|
|
if [[ -f "${CLAUDE_DIR}/${dir}/README.md" ]]; then
|
|
readme_count=$((readme_count + 1))
|
|
else
|
|
warn "${dir}/README.md missing"
|
|
fi
|
|
done
|
|
pass "${readme_count}/7 directory READMEs present"
|
|
echo ""
|
|
|
|
# Summary
|
|
echo "=== Summary ==="
|
|
if [[ ${ERRORS} -eq 0 && ${WARNINGS} -eq 0 ]]; then
|
|
echo -e "${GREEN}All checks passed!${NC}"
|
|
elif [[ ${ERRORS} -eq 0 ]]; then
|
|
echo -e "${YELLOW}${WARNINGS} warning(s), 0 errors${NC}"
|
|
else
|
|
echo -e "${RED}${ERRORS} error(s), ${WARNINGS} warning(s)${NC}"
|
|
fi
|
|
|
|
exit ${ERRORS}
|