Skip to content

Update dotnet-init documentation for new project templates #34

Update dotnet-init documentation for new project templates

Update dotnet-init documentation for new project templates #34

Workflow file for this run

name: Validate
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
DOTNET_VERSION: '10.0.x'
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
validate-skills:
name: Validate Skills
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check SKILL.md frontmatter
run: |
errors=0
for skill in skills/*/SKILL.md; do
# Check YAML frontmatter exists
if ! head -1 "$skill" | grep -q "^---$"; then
echo "ERROR: $skill missing YAML frontmatter"
errors=$((errors + 1))
fi
# Check required fields
if ! grep -q "^name:" "$skill"; then
echo "ERROR: $skill missing 'name' in frontmatter"
errors=$((errors + 1))
fi
if ! grep -q "^description:" "$skill"; then
echo "ERROR: $skill missing 'description' in frontmatter"
errors=$((errors + 1))
fi
# Check line count (max 400)
lines=$(wc -l < "$skill")
if [ "$lines" -gt 400 ]; then
echo "ERROR: $skill has $lines lines (max 400)"
errors=$((errors + 1))
fi
# Check required sections
for section in "Core Principles" "Patterns" "Anti-patterns" "Decision Guide"; do
if ! grep -q "## $section" "$skill"; then
echo "WARNING: $skill missing '## $section' section"
fi
done
done
if [ $errors -gt 0 ]; then
echo "$errors error(s) found"
exit 1
fi
echo "All skills validated successfully"
validate-commands:
name: Validate Commands
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check command frontmatter and structure
run: |
errors=0
if [ ! -d "commands" ] || [ -z "$(ls commands/*.md 2>/dev/null)" ]; then
echo "WARNING: No command files found in commands/"
exit 0
fi
for cmd in commands/*.md; do
# Check YAML frontmatter exists
if ! head -1 "$cmd" | grep -q "^---$"; then
echo "ERROR: $cmd missing YAML frontmatter"
errors=$((errors + 1))
fi
# Check description field
if ! grep -q "^description:" "$cmd"; then
echo "ERROR: $cmd missing 'description' in frontmatter"
errors=$((errors + 1))
fi
# Check line count (max 200)
lines=$(wc -l < "$cmd")
if [ "$lines" -gt 200 ]; then
echo "ERROR: $cmd has $lines lines (max 200)"
errors=$((errors + 1))
fi
# Check required sections
for section in "What" "When" "How"; do
if ! grep -q "## $section" "$cmd"; then
echo "WARNING: $cmd missing '## $section' section"
fi
done
done
if [ $errors -gt 0 ]; then
echo "$errors error(s) found"
exit 1
fi
count=$(ls commands/*.md 2>/dev/null | wc -l)
echo "All $count commands validated successfully"
validate-rules:
name: Validate Rules
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check rule frontmatter and size
run: |
errors=0
if [ ! -d ".claude/rules" ] && [ ! -d "rules" ]; then
echo "WARNING: No rules directory found"
exit 0
fi
for rule in $(find .claude/rules rules -name "*.md" -type f 2>/dev/null); do
# Check YAML frontmatter exists
if ! head -1 "$rule" | grep -q "^---$"; then
echo "ERROR: $rule missing YAML frontmatter"
errors=$((errors + 1))
fi
# Check alwaysApply: true
if ! grep -q "^alwaysApply: true" "$rule"; then
echo "ERROR: $rule missing 'alwaysApply: true' in frontmatter"
errors=$((errors + 1))
fi
# Check line count (max 100)
lines=$(wc -l < "$rule")
if [ "$lines" -gt 100 ]; then
echo "ERROR: $rule has $lines lines (max 100)"
errors=$((errors + 1))
fi
done
if [ $errors -gt 0 ]; then
echo "$errors error(s) found"
exit 1
fi
count=$(find rules -name "*.md" -type f | wc -l)
echo "All $count rules validated successfully"
validate-agents:
name: Validate Agents
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check agent structure
run: |
errors=0
for agent in agents/*.md; do
# Check required sections
for section in "Role Definition" "Skill Dependencies" "MCP Tool Usage" "Boundaries"; do
if ! grep -q "## $section" "$agent"; then
echo "ERROR: $agent missing '## $section' section"
errors=$((errors + 1))
fi
done
# Check that referenced skills exist
while IFS= read -r skill; do
if [ ! -f "skills/$skill/SKILL.md" ]; then
echo "WARNING: $agent references skill '$skill' but skills/$skill/SKILL.md not found"
fi
done < <(grep -oP '`(\w[\w-]+)`' "$agent" | tr -d '`' | sort -u | while read s; do
if [ -d "skills/$s" ]; then echo "$s"; fi
done)
done
if [ $errors -gt 0 ]; then
echo "$errors error(s) found"
exit 1
fi
count=$(ls agents/*.md 2>/dev/null | wc -l)
echo "All $count agents validated successfully"
validate-cross-references:
name: Validate Cross-References
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check skill references resolve
run: |
errors=0
# Check commands reference existing skills
for cmd in commands/*.md; do
while IFS= read -r skill; do
if [ ! -f "skills/$skill/SKILL.md" ]; then
echo "WARNING: $cmd references skill '$skill' — not found"
fi
done < <(grep -oP 'skills/[\w-]+' "$cmd" 2>/dev/null | sed 's|skills/||' | sort -u)
done
# Check AGENTS.md references existing skills and agents
while IFS= read -r agent_file; do
if [ ! -f "$agent_file" ]; then
echo "ERROR: AGENTS.md references '$agent_file' but it does not exist"
errors=$((errors + 1))
fi
done < <(grep -oP 'agents/[\w-]+\.md' AGENTS.md 2>/dev/null | sort -u)
if [ $errors -gt 0 ]; then
echo "$errors error(s) found"
exit 1
fi
echo "Cross-references validated"
validate-hooks:
name: Validate Hooks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check hook scripts exist and are valid
run: |
errors=0
# Extract script paths from hooks.json
scripts=$(python3 << 'PYEOF'
import json, re
d = json.load(open('hooks/hooks.json'))
for event, entries in d.get('hooks', {}).items():
for entry in entries:
for hook in entry.get('hooks', []):
cmd = hook.get('command', '')
clean = cmd.replace('${CLAUDE_PLUGIN_ROOT}/', '')
for match in re.findall(r'[\w][\w/.-]*\.sh', clean):
print(match)
PYEOF
)
for script in $scripts; do
if [ ! -f "$script" ]; then
echo "ERROR: hooks.json references '$script' but it does not exist"
errors=$((errors + 1))
else
echo "OK: $script"
# Basic syntax check
if command -v bash > /dev/null; then
if ! bash -n "$script" 2>/dev/null; then
echo "ERROR: $script has syntax errors"
errors=$((errors + 1))
fi
fi
fi
done
if [ $errors -gt 0 ]; then
echo "$errors error(s) found"
exit 1
fi
echo "All hook scripts validated"
validate-plugin:
name: Validate Plugin Manifests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate JSON syntax
run: |
errors=0
for f in .claude-plugin/plugin.json .claude-plugin/marketplace.json .mcp.json hooks/hooks.json; do
if ! python3 -m json.tool "$f" > /dev/null 2>&1; then
echo "ERROR: $f is not valid JSON"
errors=$((errors + 1))
else
echo "OK: $f"
fi
done
if [ $errors -gt 0 ]; then
echo "$errors JSON error(s) found"
exit 1
fi
- name: Validate plugin.json required keys
run: |
errors=0
for key in name description; do
if ! python3 -c "import json,sys; d=json.load(open('.claude-plugin/plugin.json')); assert '$key' in d" 2>/dev/null; then
echo "ERROR: plugin.json missing required key '$key'"
errors=$((errors + 1))
fi
done
if [ $errors -gt 0 ]; then
exit 1
fi
echo "plugin.json keys validated"
- name: Validate component directories exist
run: |
errors=0
for path in skills/ agents/ commands/ .claude/rules/ hooks/hooks.json .mcp.json; do
if [ ! -e "$path" ]; then
echo "ERROR: Expected '$path' but it does not exist"
errors=$((errors + 1))
else
echo "OK: $path"
fi
done
if [ $errors -gt 0 ]; then
exit 1
fi
echo "All component directories exist"
- name: Validate .mcp.json has server entries
run: |
count=$(python3 -c "import json; d=json.load(open('.mcp.json')); print(len(d.get('mcpServers',{})))")
if [ "$count" -eq 0 ]; then
echo "ERROR: .mcp.json has no MCP server entries"
exit 1
fi
echo ".mcp.json has $count server(s)"
- name: Validate hooks.json structure
run: |
python3 -c "
import json, sys
d = json.load(open('hooks/hooks.json'))
hooks = d.get('hooks', {})
if not hooks:
print('ERROR: hooks.json has no hook events')
sys.exit(1)
for event, entries in hooks.items():
for entry in entries:
if 'matcher' not in entry:
print(f'ERROR: {event} entry missing matcher')
sys.exit(1)
if 'hooks' not in entry or not entry['hooks']:
print(f'ERROR: {event} matcher \"{entry[\"matcher\"]}\" has no hooks')
sys.exit(1)
print('hooks.json structure validated')
"
build-mcp:
name: Build MCP Server
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore
run: dotnet restore mcp/CWM.RoslynNavigator/CWM.RoslynNavigator.slnx
- name: Build
run: dotnet build mcp/CWM.RoslynNavigator/CWM.RoslynNavigator.slnx --no-restore -c Release
test-mcp:
name: Test MCP Server
runs-on: ubuntu-latest
needs: build-mcp
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore
run: dotnet restore mcp/CWM.RoslynNavigator/tests/CWM.RoslynNavigator.Tests.csproj
- name: Test
run: dotnet test mcp/CWM.RoslynNavigator/tests/CWM.RoslynNavigator.Tests.csproj --no-restore -c Release --logger trx --results-directory TestResults
- name: Publish test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: TestResults/*.trx
format-check:
name: Format Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore
run: dotnet restore mcp/CWM.RoslynNavigator/CWM.RoslynNavigator.slnx
- name: Format check
run: dotnet format mcp/CWM.RoslynNavigator/CWM.RoslynNavigator.slnx --verify-no-changes --no-restore