Initial commit
This commit is contained in:
254
commands/documentation-validation/.scripts/changelog-validator.sh
Executable file
254
commands/documentation-validation/.scripts/changelog-validator.sh
Executable file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ============================================================================
|
||||
# CHANGELOG Validator
|
||||
# ============================================================================
|
||||
# Purpose: Validate CHANGELOG.md format compliance (Keep a Changelog)
|
||||
# Version: 1.0.0
|
||||
# Usage: ./changelog-validator.sh <changelog-path> [--strict] [--json]
|
||||
# Returns: 0=success, 1=error, JSON output to stdout if --json
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Default values
|
||||
STRICT_MODE=false
|
||||
JSON_OUTPUT=false
|
||||
REQUIRE_UNRELEASED=true
|
||||
|
||||
# Valid change categories per Keep a Changelog
|
||||
VALID_CATEGORIES=("Added" "Changed" "Deprecated" "Removed" "Fixed" "Security")
|
||||
|
||||
# Parse arguments
|
||||
CHANGELOG_PATH="${1:-CHANGELOG.md}"
|
||||
shift || true
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--strict)
|
||||
STRICT_MODE=true
|
||||
shift
|
||||
;;
|
||||
--json)
|
||||
JSON_OUTPUT=true
|
||||
shift
|
||||
;;
|
||||
--no-unreleased)
|
||||
REQUIRE_UNRELEASED=false
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Initialize results
|
||||
declare -a issues=()
|
||||
declare -a version_entries=()
|
||||
declare -a categories_used=()
|
||||
has_title=false
|
||||
has_unreleased=false
|
||||
compliance_score=100
|
||||
|
||||
# Check if file exists
|
||||
if [[ ! -f "$CHANGELOG_PATH" ]]; then
|
||||
if $JSON_OUTPUT; then
|
||||
cat <<EOF
|
||||
{
|
||||
"error": "CHANGELOG not found",
|
||||
"path": "$CHANGELOG_PATH",
|
||||
"present": false,
|
||||
"score": 0,
|
||||
"status": "warning",
|
||||
"issues": ["CHANGELOG.md not found"]
|
||||
}
|
||||
EOF
|
||||
else
|
||||
echo "⚠️ WARNING: CHANGELOG not found at $CHANGELOG_PATH"
|
||||
echo "CHANGELOG is recommended but not required for initial submission."
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Read content
|
||||
content=$(<"$CHANGELOG_PATH")
|
||||
|
||||
# Check for title
|
||||
if echo "$content" | grep -qiE "^#\s*(changelog|change.?log)"; then
|
||||
has_title=true
|
||||
else
|
||||
issues+=("Missing title 'Changelog' or 'Change Log'")
|
||||
((compliance_score-=10)) || true
|
||||
fi
|
||||
|
||||
# Check for Unreleased section
|
||||
if echo "$content" | grep -qE "^##\s*\[Unreleased\]"; then
|
||||
has_unreleased=true
|
||||
else
|
||||
if $REQUIRE_UNRELEASED; then
|
||||
issues+=("Missing [Unreleased] section")
|
||||
((compliance_score-=15)) || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Extract version headers
|
||||
while IFS= read -r line; do
|
||||
if [[ $line =~ ^##[[:space:]]*\[([0-9]+\.[0-9]+\.[0-9]+)\][[:space:]]*-[[:space:]]*([0-9]{4}-[0-9]{2}-[0-9]{2}) ]]; then
|
||||
version="${BASH_REMATCH[1]}"
|
||||
date="${BASH_REMATCH[2]}"
|
||||
version_entries+=("$version|$date")
|
||||
elif [[ $line =~ ^##[[:space:]]*\[?([0-9]+\.[0-9]+\.[0-9]+)\]? ]] && [[ ! $line =~ \[Unreleased\] ]]; then
|
||||
# Invalid format detected
|
||||
issues+=("Invalid version header format: '$line' (should be '## [X.Y.Z] - YYYY-MM-DD')")
|
||||
((compliance_score-=10)) || true
|
||||
fi
|
||||
done <<< "$content"
|
||||
|
||||
# Check for valid change categories
|
||||
for category in "${VALID_CATEGORIES[@]}"; do
|
||||
if echo "$content" | grep -qE "^###[[:space:]]*$category"; then
|
||||
categories_used+=("$category")
|
||||
fi
|
||||
done
|
||||
|
||||
# Detect invalid categories
|
||||
while IFS= read -r line; do
|
||||
if [[ $line =~ ^###[[:space:]]+(.*) ]]; then
|
||||
cat_name="${BASH_REMATCH[1]}"
|
||||
is_valid=false
|
||||
for valid_cat in "${VALID_CATEGORIES[@]}"; do
|
||||
if [[ "$cat_name" == "$valid_cat" ]]; then
|
||||
is_valid=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if ! $is_valid; then
|
||||
issues+=("Non-standard category: '### $cat_name' (should be one of: ${VALID_CATEGORIES[*]})")
|
||||
((compliance_score-=5)) || true
|
||||
fi
|
||||
fi
|
||||
done <<< "$content"
|
||||
|
||||
# Check date formats in version headers
|
||||
for entry in "${version_entries[@]}"; do
|
||||
date_part="${entry#*|}"
|
||||
if [[ ! $date_part =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||
issues+=("Invalid date format in version entry: $date_part (should be YYYY-MM-DD)")
|
||||
((compliance_score-=5)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Ensure score doesn't go negative
|
||||
if ((compliance_score < 0)); then
|
||||
compliance_score=0
|
||||
fi
|
||||
|
||||
# Determine status
|
||||
status="pass"
|
||||
if ((compliance_score < 60)); then
|
||||
status="fail"
|
||||
elif ((compliance_score < 80)); then
|
||||
status="warning"
|
||||
fi
|
||||
|
||||
# Output results
|
||||
if $JSON_OUTPUT; then
|
||||
# Build JSON output
|
||||
cat <<EOF
|
||||
{
|
||||
"present": true,
|
||||
"path": "$CHANGELOG_PATH",
|
||||
"has_title": $has_title,
|
||||
"has_unreleased": $has_unreleased,
|
||||
"version_count": ${#version_entries[@]},
|
||||
"version_entries": [
|
||||
$(IFS=,; for entry in "${version_entries[@]}"; do
|
||||
version="${entry%|*}"
|
||||
date="${entry#*|}"
|
||||
echo " {\"version\": \"$version\", \"date\": \"$date\"}"
|
||||
done | paste -sd, -)
|
||||
],
|
||||
"categories_used": [
|
||||
$(IFS=,; for cat in "${categories_used[@]}"; do
|
||||
echo " \"$cat\""
|
||||
done | paste -sd, -)
|
||||
],
|
||||
"compliance_score": $compliance_score,
|
||||
"status": "$status",
|
||||
"issues": [
|
||||
$(IFS=,; for issue in "${issues[@]}"; do
|
||||
# Escape quotes in issue text
|
||||
escaped_issue="${issue//\"/\\\"}"
|
||||
echo " \"$escaped_issue\""
|
||||
done | paste -sd, -)
|
||||
]
|
||||
}
|
||||
EOF
|
||||
else
|
||||
# Human-readable output
|
||||
echo ""
|
||||
echo "CHANGELOG Validation Results"
|
||||
echo "========================================"
|
||||
echo "File: $CHANGELOG_PATH"
|
||||
echo "Compliance Score: $compliance_score/100"
|
||||
echo ""
|
||||
|
||||
if $has_title; then
|
||||
echo "✓ Title present"
|
||||
else
|
||||
echo "✗ Title missing"
|
||||
fi
|
||||
|
||||
if $has_unreleased; then
|
||||
echo "✓ [Unreleased] section present"
|
||||
else
|
||||
if $REQUIRE_UNRELEASED; then
|
||||
echo "✗ [Unreleased] section missing"
|
||||
else
|
||||
echo "⚠ [Unreleased] section missing (not required)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Version Entries: ${#version_entries[@]}"
|
||||
for entry in "${version_entries[@]}"; do
|
||||
version="${entry%|*}"
|
||||
date="${entry#*|}"
|
||||
echo " • [$version] - $date"
|
||||
done
|
||||
|
||||
if [[ ${#categories_used[@]} -gt 0 ]]; then
|
||||
echo ""
|
||||
echo "Change Categories Used:"
|
||||
for cat in "${categories_used[@]}"; do
|
||||
echo " • $cat"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ ${#issues[@]} -gt 0 ]]; then
|
||||
echo ""
|
||||
echo "Issues Found: ${#issues[@]}"
|
||||
for issue in "${issues[@]}"; do
|
||||
echo " ✗ $issue"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [[ "$status" == "pass" ]]; then
|
||||
echo "Overall: ✓ PASS"
|
||||
elif [[ "$status" == "warning" ]]; then
|
||||
echo "Overall: ⚠ WARNINGS"
|
||||
else
|
||||
echo "Overall: ✗ FAIL"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Exit with appropriate code
|
||||
if [[ "$status" == "fail" ]]; then
|
||||
exit 1
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
325
commands/documentation-validation/.scripts/example-validator.sh
Executable file
325
commands/documentation-validation/.scripts/example-validator.sh
Executable file
@@ -0,0 +1,325 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ============================================================================
|
||||
# Example Validator
|
||||
# ============================================================================
|
||||
# Purpose: Validate example quality and detect placeholder patterns
|
||||
# Version: 1.0.0
|
||||
# Usage: ./example-validator.sh <path> [--no-placeholders] [--recursive] [--json]
|
||||
# Returns: 0=success, 1=warning, JSON output to stdout if --json
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Default values
|
||||
NO_PLACEHOLDERS=true
|
||||
RECURSIVE=true
|
||||
JSON_OUTPUT=false
|
||||
EXTENSIONS="md,txt,json,sh,py,js,ts,yaml,yml"
|
||||
|
||||
# Parse arguments
|
||||
TARGET_PATH="${1:-.}"
|
||||
shift || true
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--no-placeholders)
|
||||
NO_PLACEHOLDERS=true
|
||||
shift
|
||||
;;
|
||||
--allow-placeholders)
|
||||
NO_PLACEHOLDERS=false
|
||||
shift
|
||||
;;
|
||||
--recursive)
|
||||
RECURSIVE=true
|
||||
shift
|
||||
;;
|
||||
--non-recursive)
|
||||
RECURSIVE=false
|
||||
shift
|
||||
;;
|
||||
--json)
|
||||
JSON_OUTPUT=true
|
||||
shift
|
||||
;;
|
||||
--extensions)
|
||||
EXTENSIONS="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Initialize counters
|
||||
files_checked=0
|
||||
example_count=0
|
||||
placeholder_count=0
|
||||
todo_count=0
|
||||
declare -a issues=()
|
||||
declare -a files_with_issues=()
|
||||
|
||||
# Build find command based on recursiveness
|
||||
if $RECURSIVE; then
|
||||
FIND_DEPTH=""
|
||||
else
|
||||
FIND_DEPTH="-maxdepth 1"
|
||||
fi
|
||||
|
||||
# Build extension pattern
|
||||
ext_pattern=""
|
||||
IFS=',' read -ra EXT_ARRAY <<< "$EXTENSIONS"
|
||||
for ext in "${EXT_ARRAY[@]}"; do
|
||||
if [[ -z "$ext_pattern" ]]; then
|
||||
ext_pattern="-name '*.${ext}'"
|
||||
else
|
||||
ext_pattern="$ext_pattern -o -name '*.${ext}'"
|
||||
fi
|
||||
done
|
||||
|
||||
# Find files to check
|
||||
mapfile -t files < <(eval "find '$TARGET_PATH' $FIND_DEPTH -type f \( $ext_pattern \) 2>/dev/null" || true)
|
||||
|
||||
# Placeholder patterns to detect
|
||||
declare -a PLACEHOLDER_PATTERNS=(
|
||||
'TODO[:\)]'
|
||||
'FIXME[:\)]'
|
||||
'XXX[:\)]'
|
||||
'HACK[:\)]'
|
||||
'placeholder'
|
||||
'PLACEHOLDER'
|
||||
'your-.*-here'
|
||||
'<your-'
|
||||
'INSERT.?HERE'
|
||||
'YOUR_[A-Z_]+'
|
||||
)
|
||||
|
||||
# Generic dummy value patterns
|
||||
declare -a GENERIC_PATTERNS=(
|
||||
'\bfoo\b'
|
||||
'\bbar\b'
|
||||
'\bbaz\b'
|
||||
'\bdummy\b'
|
||||
)
|
||||
|
||||
# Acceptable patterns (don't count these)
|
||||
declare -a ACCEPTABLE_PATTERNS=(
|
||||
'\{\{[^}]+\}\}' # {{variable}} template syntax
|
||||
'\$\{[^}]+\}' # ${variable} template syntax
|
||||
'\$[A-Z_]+' # $VARIABLE environment variables
|
||||
)
|
||||
|
||||
# Function to check if line contains acceptable pattern
|
||||
is_acceptable_pattern() {
|
||||
local line="$1"
|
||||
|
||||
for pattern in "${ACCEPTABLE_PATTERNS[@]}"; do
|
||||
if echo "$line" | grep -qE "$pattern"; then
|
||||
return 0 # Is acceptable
|
||||
fi
|
||||
done
|
||||
|
||||
return 1 # Not acceptable
|
||||
}
|
||||
|
||||
# Function to count code examples in markdown
|
||||
count_code_examples() {
|
||||
local file="$1"
|
||||
|
||||
if [[ ! "$file" =~ \.md$ ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Count code blocks (```)
|
||||
local count
|
||||
count=$(grep -c '```' "$file" 2>/dev/null || echo "0")
|
||||
|
||||
# Ensure count is numeric and divide by 2 since each code block has opening and closing
|
||||
if [[ "$count" =~ ^[0-9]+$ ]]; then
|
||||
count=$((count / 2))
|
||||
else
|
||||
count=0
|
||||
fi
|
||||
|
||||
echo "$count"
|
||||
}
|
||||
|
||||
# Check each file
|
||||
for file in "${files[@]}"; do
|
||||
((files_checked++)) || true
|
||||
|
||||
# Count examples in markdown files
|
||||
if [[ "$file" =~ \.md$ ]]; then
|
||||
file_examples=$(count_code_examples "$file")
|
||||
((example_count += file_examples)) || true
|
||||
fi
|
||||
|
||||
file_issues=0
|
||||
|
||||
# Check for placeholder patterns
|
||||
for pattern in "${PLACEHOLDER_PATTERNS[@]}"; do
|
||||
while IFS=: read -r line_num line_content; do
|
||||
# Skip if it's an acceptable pattern
|
||||
if is_acceptable_pattern "$line_content"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
((placeholder_count++)) || true
|
||||
((file_issues++)) || true
|
||||
|
||||
issue="$file:$line_num: Placeholder pattern detected"
|
||||
issues+=("$issue")
|
||||
|
||||
# Track TODO/FIXME separately
|
||||
if echo "$pattern" | grep -qE 'TODO|FIXME|XXX'; then
|
||||
((todo_count++)) || true
|
||||
fi
|
||||
done < <(grep -inE "$pattern" "$file" 2>/dev/null || true)
|
||||
done
|
||||
|
||||
# Check for generic dummy values (only in non-test files)
|
||||
if [[ ! "$file" =~ test ]] && [[ ! "$file" =~ example ]] && [[ ! "$file" =~ spec ]]; then
|
||||
for pattern in "${GENERIC_PATTERNS[@]}"; do
|
||||
while IFS=: read -r line_num line_content; do
|
||||
# Skip code comments explaining these terms
|
||||
if echo "$line_content" | grep -qE '(#|//|/\*).*'"$pattern"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Skip if in an acceptable context
|
||||
if is_acceptable_pattern "$line_content"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
((file_issues++)) || true
|
||||
issue="$file:$line_num: Generic placeholder value detected"
|
||||
issues+=("$issue")
|
||||
done < <(grep -inE "$pattern" "$file" 2>/dev/null || true)
|
||||
done
|
||||
fi
|
||||
|
||||
# Track files with issues
|
||||
if ((file_issues > 0)); then
|
||||
files_with_issues+=("$file:$file_issues")
|
||||
fi
|
||||
done
|
||||
|
||||
# Calculate quality score
|
||||
quality_score=100
|
||||
((quality_score -= placeholder_count * 10)) || true
|
||||
((quality_score -= todo_count * 5)) || true
|
||||
|
||||
if ((example_count < 2)); then
|
||||
((quality_score -= 20)) || true
|
||||
fi
|
||||
|
||||
# Ensure score doesn't go negative
|
||||
if ((quality_score < 0)); then
|
||||
quality_score=0
|
||||
fi
|
||||
|
||||
# Determine status
|
||||
status="pass"
|
||||
if ((quality_score < 60)); then
|
||||
status="fail"
|
||||
elif ((quality_score < 80)); then
|
||||
status="warning"
|
||||
fi
|
||||
|
||||
# Output results
|
||||
if $JSON_OUTPUT; then
|
||||
# Build JSON output
|
||||
cat <<EOF
|
||||
{
|
||||
"files_checked": $files_checked,
|
||||
"example_count": $example_count,
|
||||
"placeholder_count": $placeholder_count,
|
||||
"todo_count": $todo_count,
|
||||
"files_with_issues": ${#files_with_issues[@]},
|
||||
"quality_score": $quality_score,
|
||||
"status": "$status",
|
||||
"issues": [
|
||||
$(IFS=; for issue in "${issues[@]:0:20}"; do # Limit to first 20 issues
|
||||
# Escape quotes in issue text
|
||||
escaped_issue="${issue//\"/\\\"}"
|
||||
echo " \"$escaped_issue\","
|
||||
done | sed '$ s/,$//')
|
||||
],
|
||||
"files_with_issues_list": [
|
||||
$(IFS=; for file_info in "${files_with_issues[@]:0:10}"; do # Limit to first 10 files
|
||||
file_path="${file_info%:*}"
|
||||
file_count="${file_info#*:}"
|
||||
echo " {\"file\": \"$file_path\", \"issue_count\": $file_count},"
|
||||
done | sed '$ s/,$//')
|
||||
]
|
||||
}
|
||||
EOF
|
||||
else
|
||||
# Human-readable output
|
||||
echo ""
|
||||
echo "Example Quality Validation"
|
||||
echo "========================================"
|
||||
echo "Files Checked: $files_checked"
|
||||
echo "Code Examples Found: $example_count"
|
||||
echo "Quality Score: $quality_score/100"
|
||||
echo ""
|
||||
|
||||
if ((placeholder_count > 0)) || ((todo_count > 0)); then
|
||||
echo "Issues Detected:"
|
||||
echo " • Placeholder patterns: $placeholder_count"
|
||||
echo " • TODO/FIXME markers: $todo_count"
|
||||
echo " • Files with issues: ${#files_with_issues[@]}"
|
||||
echo ""
|
||||
|
||||
if ((${#files_with_issues[@]} > 0)); then
|
||||
echo "Files with issues:"
|
||||
for file_info in "${files_with_issues[@]:0:5}"; do # Show first 5
|
||||
file_path="${file_info%:*}"
|
||||
file_count="${file_info#*:}"
|
||||
echo " • $file_path ($file_count issues)"
|
||||
done
|
||||
|
||||
if ((${#files_with_issues[@]} > 5)); then
|
||||
echo " ... and $((${#files_with_issues[@]} - 5)) more files"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Sample Issues:"
|
||||
for issue in "${issues[@]:0:5}"; do # Show first 5
|
||||
echo " • $issue"
|
||||
done
|
||||
|
||||
if ((${#issues[@]} > 5)); then
|
||||
echo " ... and $((${#issues[@]} - 5)) more issues"
|
||||
fi
|
||||
else
|
||||
echo "✓ No placeholder patterns detected"
|
||||
fi
|
||||
|
||||
if ((example_count < 2)); then
|
||||
echo ""
|
||||
echo "⚠ Recommendation: Add more code examples (found: $example_count, recommended: 3+)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [[ "$status" == "pass" ]]; then
|
||||
echo "Overall: ✓ PASS"
|
||||
elif [[ "$status" == "warning" ]]; then
|
||||
echo "Overall: ⚠ WARNINGS"
|
||||
else
|
||||
echo "Overall: ✗ FAIL"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Exit with appropriate code
|
||||
if [[ "$status" == "fail" ]]; then
|
||||
exit 1
|
||||
elif [[ "$status" == "warning" ]]; then
|
||||
exit 0 # Warning is not a failure
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
344
commands/documentation-validation/.scripts/license-detector.py
Executable file
344
commands/documentation-validation/.scripts/license-detector.py
Executable file
@@ -0,0 +1,344 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# ============================================================================
|
||||
# License Detector
|
||||
# ============================================================================
|
||||
# Purpose: Detect and validate LICENSE file content
|
||||
# Version: 1.0.0
|
||||
# Usage: ./license-detector.py <path> [--expected LICENSE] [--json]
|
||||
# Returns: 0=success, 1=error, JSON output to stdout
|
||||
# ============================================================================
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional, Tuple
|
||||
|
||||
# OSI-approved license patterns
|
||||
LICENSE_PATTERNS = {
|
||||
"MIT": {
|
||||
"pattern": r"Permission is hereby granted, free of charge",
|
||||
"confidence": 95,
|
||||
"osi_approved": True,
|
||||
"full_name": "MIT License"
|
||||
},
|
||||
"Apache-2.0": {
|
||||
"pattern": r"Licensed under the Apache License, Version 2\.0",
|
||||
"confidence": 95,
|
||||
"osi_approved": True,
|
||||
"full_name": "Apache License 2.0"
|
||||
},
|
||||
"GPL-3.0": {
|
||||
"pattern": r"GNU GENERAL PUBLIC LICENSE.*Version 3",
|
||||
"confidence": 95,
|
||||
"osi_approved": True,
|
||||
"full_name": "GNU General Public License v3.0"
|
||||
},
|
||||
"GPL-2.0": {
|
||||
"pattern": r"GNU GENERAL PUBLIC LICENSE.*Version 2",
|
||||
"confidence": 95,
|
||||
"osi_approved": True,
|
||||
"full_name": "GNU General Public License v2.0"
|
||||
},
|
||||
"BSD-3-Clause": {
|
||||
"pattern": r"Redistribution and use in source and binary forms.*3\.",
|
||||
"confidence": 85,
|
||||
"osi_approved": True,
|
||||
"full_name": "BSD 3-Clause License"
|
||||
},
|
||||
"BSD-2-Clause": {
|
||||
"pattern": r"Redistribution and use in source and binary forms",
|
||||
"confidence": 80,
|
||||
"osi_approved": True,
|
||||
"full_name": "BSD 2-Clause License"
|
||||
},
|
||||
"ISC": {
|
||||
"pattern": r"Permission to use, copy, modify, and/or distribute",
|
||||
"confidence": 90,
|
||||
"osi_approved": True,
|
||||
"full_name": "ISC License"
|
||||
},
|
||||
"MPL-2.0": {
|
||||
"pattern": r"Mozilla Public License Version 2\.0",
|
||||
"confidence": 95,
|
||||
"osi_approved": True,
|
||||
"full_name": "Mozilla Public License 2.0"
|
||||
}
|
||||
}
|
||||
|
||||
# License name variations/aliases
|
||||
LICENSE_ALIASES = {
|
||||
"MIT License": "MIT",
|
||||
"MIT license": "MIT",
|
||||
"Apache License 2.0": "Apache-2.0",
|
||||
"Apache 2.0": "Apache-2.0",
|
||||
"Apache-2": "Apache-2.0",
|
||||
"GNU GPL v3": "GPL-3.0",
|
||||
"GPLv3": "GPL-3.0",
|
||||
"GNU GPL v2": "GPL-2.0",
|
||||
"GPLv2": "GPL-2.0",
|
||||
"BSD 3-Clause": "BSD-3-Clause",
|
||||
"BSD 2-Clause": "BSD-2-Clause",
|
||||
}
|
||||
|
||||
def find_license_file(path: str) -> Optional[str]:
|
||||
"""Find LICENSE file in path."""
|
||||
path_obj = Path(path)
|
||||
|
||||
# Check if path is directly to LICENSE
|
||||
if path_obj.is_file() and 'license' in path_obj.name.lower():
|
||||
return str(path_obj)
|
||||
|
||||
# Search for LICENSE in directory
|
||||
if path_obj.is_dir():
|
||||
for filename in ['LICENSE', 'LICENSE.txt', 'LICENSE.md', 'COPYING', 'COPYING.txt', 'LICENCE']:
|
||||
license_path = path_obj / filename
|
||||
if license_path.exists():
|
||||
return str(license_path)
|
||||
|
||||
return None
|
||||
|
||||
def read_plugin_manifest(path: str) -> Optional[str]:
|
||||
"""Read license from plugin.json."""
|
||||
path_obj = Path(path)
|
||||
|
||||
if path_obj.is_file():
|
||||
path_obj = path_obj.parent
|
||||
|
||||
manifest_path = path_obj / '.claude-plugin' / 'plugin.json'
|
||||
|
||||
if not manifest_path.exists():
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(manifest_path, 'r', encoding='utf-8') as f:
|
||||
manifest = json.load(f)
|
||||
return manifest.get('license')
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def detect_license(content: str) -> Tuple[Optional[str], int, bool]:
|
||||
"""
|
||||
Detect license type from content.
|
||||
Returns: (license_type, confidence, is_complete)
|
||||
"""
|
||||
content_normalized = ' '.join(content.split()) # Normalize whitespace
|
||||
|
||||
best_match = None
|
||||
best_confidence = 0
|
||||
|
||||
# Check for license text patterns
|
||||
for license_id, license_info in LICENSE_PATTERNS.items():
|
||||
pattern = license_info["pattern"]
|
||||
if re.search(pattern, content, re.IGNORECASE | re.DOTALL):
|
||||
confidence = license_info["confidence"]
|
||||
if confidence > best_confidence:
|
||||
best_match = license_id
|
||||
best_confidence = confidence
|
||||
|
||||
# Check if it's just a name without full text
|
||||
is_complete = True
|
||||
if best_match and len(content.strip()) < 200: # Very short content
|
||||
is_complete = False
|
||||
|
||||
# If no pattern match, check for just license names
|
||||
if not best_match:
|
||||
for alias, license_id in LICENSE_ALIASES.items():
|
||||
if re.search(r'\b' + re.escape(alias) + r'\b', content, re.IGNORECASE):
|
||||
best_match = license_id
|
||||
best_confidence = 50 # Lower confidence for name-only
|
||||
is_complete = False
|
||||
break
|
||||
|
||||
return best_match, best_confidence, is_complete
|
||||
|
||||
def normalize_license_name(license_name: str) -> str:
|
||||
"""Normalize license name for comparison."""
|
||||
if not license_name:
|
||||
return ""
|
||||
|
||||
# Check if it's already a standard ID
|
||||
if license_name in LICENSE_PATTERNS:
|
||||
return license_name
|
||||
|
||||
# Check aliases
|
||||
if license_name in LICENSE_ALIASES:
|
||||
return LICENSE_ALIASES[license_name]
|
||||
|
||||
# Normalize common variations
|
||||
normalized = license_name.strip()
|
||||
normalized = re.sub(r'\s+', ' ', normalized)
|
||||
|
||||
# Try fuzzy matching
|
||||
for alias, license_id in LICENSE_ALIASES.items():
|
||||
if normalized.lower() == alias.lower():
|
||||
return license_id
|
||||
|
||||
return license_name
|
||||
|
||||
def licenses_match(detected: str, expected: str) -> Tuple[bool, str]:
|
||||
"""
|
||||
Check if detected license matches expected.
|
||||
Returns: (matches, match_type)
|
||||
"""
|
||||
detected_norm = normalize_license_name(detected)
|
||||
expected_norm = normalize_license_name(expected)
|
||||
|
||||
if detected_norm == expected_norm:
|
||||
return True, "exact"
|
||||
|
||||
# Check if they're aliases of the same license
|
||||
if detected_norm in LICENSE_PATTERNS and expected_norm in LICENSE_PATTERNS:
|
||||
if LICENSE_PATTERNS[detected_norm]["full_name"] == LICENSE_PATTERNS[expected_norm]["full_name"]:
|
||||
return True, "alias"
|
||||
|
||||
# Fuzzy match
|
||||
if detected_norm.lower().replace('-', '').replace(' ', '') == expected_norm.lower().replace('-', '').replace(' ', ''):
|
||||
return True, "fuzzy"
|
||||
|
||||
return False, "mismatch"
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Detect and validate LICENSE file')
|
||||
parser.add_argument('path', help='Path to LICENSE file or directory containing it')
|
||||
parser.add_argument('--expected', help='Expected license type (from plugin.json)', default=None)
|
||||
parser.add_argument('--strict', action='store_true', help='Strict validation (requires full text)')
|
||||
parser.add_argument('--json', action='store_true', help='Output JSON format')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Find LICENSE file
|
||||
license_path = find_license_file(args.path)
|
||||
|
||||
if not license_path:
|
||||
result = {
|
||||
"error": "LICENSE file not found",
|
||||
"path": args.path,
|
||||
"present": False,
|
||||
"score": 0,
|
||||
"status": "fail",
|
||||
"issues": ["LICENSE file not found in specified path"]
|
||||
}
|
||||
if args.json:
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
print("❌ CRITICAL: LICENSE file not found")
|
||||
print(f"Path: {args.path}")
|
||||
print("LICENSE file is required for plugin submission.")
|
||||
return 1
|
||||
|
||||
# Read LICENSE content
|
||||
try:
|
||||
with open(license_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
except Exception as e:
|
||||
result = {
|
||||
"error": f"Failed to read LICENSE: {str(e)}",
|
||||
"path": license_path,
|
||||
"present": True,
|
||||
"score": 0,
|
||||
"status": "fail"
|
||||
}
|
||||
if args.json:
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
print(f"❌ ERROR: Failed to read LICENSE: {e}")
|
||||
return 1
|
||||
|
||||
# Detect license
|
||||
detected_license, confidence, is_complete = detect_license(content)
|
||||
|
||||
# Read expected license from plugin.json if not provided
|
||||
if not args.expected:
|
||||
args.expected = read_plugin_manifest(args.path)
|
||||
|
||||
# Check consistency
|
||||
matches_manifest = True
|
||||
match_type = None
|
||||
if args.expected:
|
||||
matches_manifest, match_type = licenses_match(detected_license or "", args.expected)
|
||||
|
||||
# Determine if OSI approved
|
||||
is_osi_approved = False
|
||||
if detected_license and detected_license in LICENSE_PATTERNS:
|
||||
is_osi_approved = LICENSE_PATTERNS[detected_license]["osi_approved"]
|
||||
|
||||
# Build issues list
|
||||
issues = []
|
||||
score = 100
|
||||
|
||||
if not detected_license:
|
||||
issues.append("Unable to identify license type")
|
||||
score -= 50
|
||||
elif not is_complete:
|
||||
issues.append("LICENSE contains only license name, not full text")
|
||||
score -= 20 if args.strict else 10
|
||||
|
||||
if not is_osi_approved and detected_license:
|
||||
issues.append("License is not OSI-approved")
|
||||
score -= 30
|
||||
|
||||
if args.expected and not matches_manifest:
|
||||
issues.append(f"LICENSE ({detected_license or 'unknown'}) does not match plugin.json ({args.expected})")
|
||||
score -= 20
|
||||
|
||||
score = max(0, score)
|
||||
|
||||
# Determine status
|
||||
if score >= 80:
|
||||
status = "pass"
|
||||
elif score >= 60:
|
||||
status = "warning"
|
||||
else:
|
||||
status = "fail"
|
||||
|
||||
# Build result
|
||||
result = {
|
||||
"present": True,
|
||||
"path": license_path,
|
||||
"detected_license": detected_license,
|
||||
"confidence": confidence,
|
||||
"is_complete": is_complete,
|
||||
"is_osi_approved": is_osi_approved,
|
||||
"manifest_license": args.expected,
|
||||
"matches_manifest": matches_manifest,
|
||||
"match_type": match_type,
|
||||
"score": score,
|
||||
"status": status,
|
||||
"issues": issues
|
||||
}
|
||||
|
||||
# Output
|
||||
if args.json:
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
# Human-readable output
|
||||
print(f"\nLICENSE Validation Results")
|
||||
print("=" * 50)
|
||||
print(f"File: {license_path}")
|
||||
print(f"Detected: {detected_license or 'Unknown'} (confidence: {confidence}%)")
|
||||
print(f"Score: {score}/100")
|
||||
print(f"\nOSI Approved: {'✓ Yes' if is_osi_approved else '✗ No'}")
|
||||
print(f"Complete Text: {'✓ Yes' if is_complete else '⚠ No (name only)'}")
|
||||
|
||||
if args.expected:
|
||||
print(f"\nConsistency Check:")
|
||||
print(f" plugin.json: {args.expected}")
|
||||
print(f" LICENSE file: {detected_license or 'Unknown'}")
|
||||
print(f" Match: {'✓ Yes' if matches_manifest else '✗ No'}")
|
||||
|
||||
if issues:
|
||||
print(f"\nIssues Found: {len(issues)}")
|
||||
for issue in issues:
|
||||
print(f" • {issue}")
|
||||
|
||||
print(f"\nOverall: {'✓ PASS' if status == 'pass' else '⚠ WARNING' if status == 'warning' else '✗ FAIL'}")
|
||||
print()
|
||||
|
||||
return 0 if status != "fail" else 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
311
commands/documentation-validation/.scripts/readme-checker.py
Executable file
311
commands/documentation-validation/.scripts/readme-checker.py
Executable file
@@ -0,0 +1,311 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# ============================================================================
|
||||
# README Checker
|
||||
# ============================================================================
|
||||
# Purpose: Validate README.md completeness and quality
|
||||
# Version: 1.0.0
|
||||
# Usage: ./readme-checker.py <readme-path> [options]
|
||||
# Returns: 0=success, 1=error, JSON output to stdout
|
||||
# ============================================================================
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
# Required sections (case-insensitive patterns)
|
||||
REQUIRED_SECTIONS = {
|
||||
"overview": r"(?i)^#{1,3}\s*(overview|description|about)",
|
||||
"installation": r"(?i)^#{1,3}\s*installation",
|
||||
"usage": r"(?i)^#{1,3}\s*usage",
|
||||
"examples": r"(?i)^#{1,3}\s*(examples?|demonstrations?)",
|
||||
"license": r"(?i)^#{1,3}\s*licen[cs]e"
|
||||
}
|
||||
|
||||
# Optional but recommended sections
|
||||
RECOMMENDED_SECTIONS = {
|
||||
"configuration": r"(?i)^#{1,3}\s*(configuration|setup|config)",
|
||||
"troubleshooting": r"(?i)^#{1,3}\s*(troubleshooting|faq|common.?issues)",
|
||||
"contributing": r"(?i)^#{1,3}\s*contribut",
|
||||
"changelog": r"(?i)^#{1,3}\s*(changelog|version.?history|releases)"
|
||||
}
|
||||
|
||||
def find_readme(path: str) -> str:
|
||||
"""Find README file in path."""
|
||||
path_obj = Path(path)
|
||||
|
||||
# Check if path is directly to README
|
||||
if path_obj.is_file() and path_obj.name.lower().startswith('readme'):
|
||||
return str(path_obj)
|
||||
|
||||
# Search for README in directory
|
||||
if path_obj.is_dir():
|
||||
for filename in ['README.md', 'readme.md', 'README.txt', 'README']:
|
||||
readme_path = path_obj / filename
|
||||
if readme_path.exists():
|
||||
return str(readme_path)
|
||||
|
||||
return None
|
||||
|
||||
def analyze_sections(content: str) -> Tuple[List[str], List[str]]:
|
||||
"""Analyze README sections."""
|
||||
lines = content.split('\n')
|
||||
found_sections = []
|
||||
missing_sections = []
|
||||
|
||||
# Check required sections
|
||||
for section_name, pattern in REQUIRED_SECTIONS.items():
|
||||
found = False
|
||||
for line in lines:
|
||||
if re.match(pattern, line.strip()):
|
||||
found = True
|
||||
found_sections.append(section_name)
|
||||
break
|
||||
|
||||
if not found:
|
||||
missing_sections.append(section_name)
|
||||
|
||||
return found_sections, missing_sections
|
||||
|
||||
def count_examples(content: str) -> int:
|
||||
"""Count code examples in README."""
|
||||
# Count code blocks (```...```)
|
||||
code_blocks = re.findall(r'```[\s\S]*?```', content)
|
||||
return len(code_blocks)
|
||||
|
||||
def check_quality_issues(content: str) -> List[str]:
|
||||
"""Check for quality issues."""
|
||||
issues = []
|
||||
|
||||
# Check for excessive placeholder text
|
||||
placeholder_patterns = [
|
||||
r'TODO',
|
||||
r'FIXME',
|
||||
r'XXX',
|
||||
r'placeholder',
|
||||
r'your-.*-here',
|
||||
r'<your-'
|
||||
]
|
||||
|
||||
for pattern in placeholder_patterns:
|
||||
matches = re.findall(pattern, content, re.IGNORECASE)
|
||||
if len(matches) > 5: # More than 5 is excessive
|
||||
issues.append(f"Excessive placeholder patterns: {len(matches)} instances of '{pattern}'")
|
||||
|
||||
# Check for very short sections
|
||||
lines = content.split('\n')
|
||||
current_section = None
|
||||
section_lengths = {}
|
||||
|
||||
for line in lines:
|
||||
if re.match(r'^#{1,3}\s+', line):
|
||||
current_section = line.strip()
|
||||
section_lengths[current_section] = 0
|
||||
elif current_section and line.strip():
|
||||
section_lengths[current_section] += len(line)
|
||||
|
||||
for section, length in section_lengths.items():
|
||||
if length < 100 and any(keyword in section.lower() for keyword in ['installation', 'usage', 'example']):
|
||||
issues.append(f"Section '{section}' is very short ({length} chars), consider expanding")
|
||||
|
||||
return issues
|
||||
|
||||
def calculate_score(found_sections: List[str], missing_sections: List[str],
|
||||
length: int, example_count: int, quality_issues: List[str]) -> int:
|
||||
"""Calculate README quality score (0-100)."""
|
||||
score = 100
|
||||
|
||||
# Deduct for missing required sections (15 points each)
|
||||
score -= len(missing_sections) * 15
|
||||
|
||||
# Deduct if too short
|
||||
if length < 200:
|
||||
score -= 30 # Critical
|
||||
elif length < 500:
|
||||
score -= 10 # Warning
|
||||
|
||||
# Deduct if no examples
|
||||
if example_count == 0:
|
||||
score -= 15
|
||||
elif example_count < 2:
|
||||
score -= 5
|
||||
|
||||
# Deduct for quality issues (5 points each, max 20)
|
||||
score -= min(len(quality_issues) * 5, 20)
|
||||
|
||||
return max(0, score)
|
||||
|
||||
def generate_recommendations(found_sections: List[str], missing_sections: List[str],
|
||||
length: int, example_count: int, quality_issues: List[str]) -> List[Dict]:
|
||||
"""Generate actionable recommendations."""
|
||||
recommendations = []
|
||||
|
||||
# Missing sections
|
||||
for section in missing_sections:
|
||||
impact = 15
|
||||
recommendations.append({
|
||||
"priority": "critical" if section in ["overview", "installation", "usage"] else "important",
|
||||
"action": f"Add {section.title()} section",
|
||||
"impact": impact,
|
||||
"effort": "medium" if section == "examples" else "low",
|
||||
"description": f"Include a comprehensive {section} section with clear explanations"
|
||||
})
|
||||
|
||||
# Length issues
|
||||
if length < 500:
|
||||
gap = 500 - length
|
||||
recommendations.append({
|
||||
"priority": "important" if length >= 200 else "critical",
|
||||
"action": f"Expand README by {gap} characters",
|
||||
"impact": 10 if length >= 200 else 30,
|
||||
"effort": "medium",
|
||||
"description": "Add more detail to existing sections or include additional sections"
|
||||
})
|
||||
|
||||
# Example issues
|
||||
if example_count < 3:
|
||||
needed = 3 - example_count
|
||||
recommendations.append({
|
||||
"priority": "important",
|
||||
"action": f"Add {needed} more code example{'s' if needed > 1 else ''}",
|
||||
"impact": 15 if example_count == 0 else 5,
|
||||
"effort": "medium",
|
||||
"description": "Include concrete, copy-pasteable usage examples"
|
||||
})
|
||||
|
||||
# Quality issues
|
||||
for issue in quality_issues:
|
||||
recommendations.append({
|
||||
"priority": "recommended",
|
||||
"action": "Address quality issue",
|
||||
"impact": 5,
|
||||
"effort": "low",
|
||||
"description": issue
|
||||
})
|
||||
|
||||
return sorted(recommendations, key=lambda x: (
|
||||
{"critical": 0, "important": 1, "recommended": 2}[x["priority"]],
|
||||
-x["impact"]
|
||||
))
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Validate README.md quality')
|
||||
parser.add_argument('path', help='Path to README.md or directory containing it')
|
||||
parser.add_argument('--sections', help='Comma-separated required sections', default=None)
|
||||
parser.add_argument('--min-length', type=int, default=500, help='Minimum character count')
|
||||
parser.add_argument('--strict', action='store_true', help='Enable strict validation')
|
||||
parser.add_argument('--json', action='store_true', help='Output JSON format')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Find README file
|
||||
readme_path = find_readme(args.path)
|
||||
|
||||
if not readme_path:
|
||||
result = {
|
||||
"error": "README.md not found",
|
||||
"path": args.path,
|
||||
"present": False,
|
||||
"score": 0,
|
||||
"issues": ["README.md file not found in specified path"]
|
||||
}
|
||||
print(json.dumps(result, indent=2))
|
||||
return 1
|
||||
|
||||
# Read README content
|
||||
try:
|
||||
with open(readme_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
except Exception as e:
|
||||
result = {
|
||||
"error": f"Failed to read README: {str(e)}",
|
||||
"path": readme_path,
|
||||
"present": True,
|
||||
"score": 0
|
||||
}
|
||||
print(json.dumps(result, indent=2))
|
||||
return 1
|
||||
|
||||
# Analyze README
|
||||
length = len(content)
|
||||
found_sections, missing_sections = analyze_sections(content)
|
||||
example_count = count_examples(content)
|
||||
quality_issues = check_quality_issues(content)
|
||||
|
||||
# Calculate score
|
||||
score = calculate_score(found_sections, missing_sections, length, example_count, quality_issues)
|
||||
|
||||
# Generate recommendations
|
||||
recommendations = generate_recommendations(found_sections, missing_sections, length, example_count, quality_issues)
|
||||
|
||||
# Build result
|
||||
result = {
|
||||
"present": True,
|
||||
"path": readme_path,
|
||||
"length": length,
|
||||
"min_length": args.min_length,
|
||||
"meets_min_length": length >= args.min_length,
|
||||
"sections": {
|
||||
"found": found_sections,
|
||||
"missing": missing_sections,
|
||||
"required_count": len(REQUIRED_SECTIONS),
|
||||
"found_count": len(found_sections)
|
||||
},
|
||||
"examples": {
|
||||
"count": example_count,
|
||||
"sufficient": example_count >= 2
|
||||
},
|
||||
"quality_issues": quality_issues,
|
||||
"score": score,
|
||||
"rating": (
|
||||
"excellent" if score >= 90 else
|
||||
"good" if score >= 75 else
|
||||
"fair" if score >= 60 else
|
||||
"needs_improvement" if score >= 40 else
|
||||
"poor"
|
||||
),
|
||||
"recommendations": recommendations[:10], # Top 10
|
||||
"status": "pass" if score >= 60 and not missing_sections else "warning" if score >= 40 else "fail"
|
||||
}
|
||||
|
||||
# Output
|
||||
if args.json:
|
||||
print(json.dumps(result, indent=2))
|
||||
else:
|
||||
# Human-readable output
|
||||
print(f"\nREADME Validation Results")
|
||||
print("=" * 50)
|
||||
print(f"File: {readme_path}")
|
||||
print(f"Length: {length} characters (min: {args.min_length})")
|
||||
print(f"Score: {score}/100 ({result['rating'].title()})")
|
||||
print(f"\nSections Found: {len(found_sections)}/{len(REQUIRED_SECTIONS)}")
|
||||
for section in found_sections:
|
||||
print(f" ✓ {section.title()}")
|
||||
|
||||
if missing_sections:
|
||||
print(f"\nMissing Sections: {len(missing_sections)}")
|
||||
for section in missing_sections:
|
||||
print(f" ✗ {section.title()}")
|
||||
|
||||
print(f"\nCode Examples: {example_count}")
|
||||
|
||||
if quality_issues:
|
||||
print(f"\nQuality Issues: {len(quality_issues)}")
|
||||
for issue in quality_issues[:5]: # Top 5
|
||||
print(f" • {issue}")
|
||||
|
||||
if recommendations:
|
||||
print(f"\nTop Recommendations:")
|
||||
for i, rec in enumerate(recommendations[:5], 1):
|
||||
print(f" {i}. [{rec['priority'].upper()}] {rec['action']} (+{rec['impact']} pts)")
|
||||
|
||||
print()
|
||||
|
||||
return 0 if score >= 60 else 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
308
commands/documentation-validation/check-license.md
Normal file
308
commands/documentation-validation/check-license.md
Normal file
@@ -0,0 +1,308 @@
|
||||
## Operation: Check LICENSE File
|
||||
|
||||
Validate LICENSE file presence, format, and consistency with plugin metadata.
|
||||
|
||||
### Parameters from $ARGUMENTS
|
||||
|
||||
- **path**: Target plugin/marketplace path (required)
|
||||
- **expected**: Expected license type (optional, reads from plugin.json if not provided)
|
||||
- **strict**: Enable strict validation mode (optional, default: false)
|
||||
- **check-consistency**: Verify consistency with plugin.json (optional, default: true)
|
||||
|
||||
### LICENSE Requirements
|
||||
|
||||
**File Presence**:
|
||||
- LICENSE or LICENSE.txt in plugin root
|
||||
- Also accept: LICENSE.md, COPYING, COPYING.txt
|
||||
|
||||
**OSI-Approved Licenses** (recommended):
|
||||
- MIT License
|
||||
- Apache License 2.0
|
||||
- GNU General Public License (GPL) v2/v3
|
||||
- BSD 2-Clause or 3-Clause License
|
||||
- Mozilla Public License 2.0
|
||||
- ISC License
|
||||
- Creative Commons (for documentation)
|
||||
|
||||
**Validation Checks**:
|
||||
1. **File exists**: LICENSE file present in root
|
||||
2. **Valid content**: Contains recognized license text
|
||||
3. **Complete**: Full license text, not just license name
|
||||
4. **Consistency**: Matches license field in plugin.json
|
||||
5. **OSI-approved**: Recognized open-source license
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Locate LICENSE File**
|
||||
```
|
||||
Check for files in plugin root (case-insensitive):
|
||||
- LICENSE
|
||||
- LICENSE.txt
|
||||
- LICENSE.md
|
||||
- COPYING
|
||||
- COPYING.txt
|
||||
- LICENCE (UK spelling)
|
||||
|
||||
If multiple found, prefer LICENSE over others
|
||||
```
|
||||
|
||||
2. **Read Plugin Metadata**
|
||||
```
|
||||
Read plugin.json
|
||||
Extract license field value
|
||||
Store expected license type for comparison
|
||||
```
|
||||
|
||||
3. **Execute License Detector**
|
||||
```bash
|
||||
Execute .scripts/license-detector.py with parameters:
|
||||
- License file path
|
||||
- Expected license type (from plugin.json)
|
||||
- Strict mode flag
|
||||
|
||||
Script returns:
|
||||
- detected_license: Identified license type
|
||||
- confidence: 0-100 (match confidence)
|
||||
- is_osi_approved: Boolean
|
||||
- is_complete: Boolean (full text vs just name)
|
||||
- matches_manifest: Boolean
|
||||
- issues: Array of problems
|
||||
```
|
||||
|
||||
4. **Validate License Content**
|
||||
```
|
||||
Check for license text patterns:
|
||||
- MIT: "Permission is hereby granted, free of charge..."
|
||||
- Apache 2.0: "Licensed under the Apache License, Version 2.0"
|
||||
- GPL-3.0: "GNU GENERAL PUBLIC LICENSE Version 3"
|
||||
- BSD-2-Clause: "Redistribution and use in source and binary forms"
|
||||
|
||||
Detect incomplete licenses:
|
||||
- Just "MIT" or "MIT License" (missing full text)
|
||||
- Just "Apache 2.0" (missing full text)
|
||||
- Links to license without including text
|
||||
```
|
||||
|
||||
5. **Check Consistency**
|
||||
```
|
||||
Compare detected license with plugin.json:
|
||||
- Exact match: ✅ PASS
|
||||
- Close match (e.g., "MIT" vs "MIT License"): ⚠️ WARNING
|
||||
- Mismatch: ❌ ERROR
|
||||
- Not specified in plugin.json: ⚠️ WARNING
|
||||
|
||||
Normalize license names for comparison:
|
||||
- "MIT License" == "MIT"
|
||||
- "Apache-2.0" == "Apache License 2.0"
|
||||
- "GPL-3.0" == "GNU GPL v3"
|
||||
```
|
||||
|
||||
6. **Verify OSI Approval**
|
||||
```
|
||||
Check against OSI-approved license list:
|
||||
- MIT: ✅ Approved
|
||||
- Apache-2.0: ✅ Approved
|
||||
- GPL-2.0, GPL-3.0: ✅ Approved
|
||||
- BSD-2-Clause, BSD-3-Clause: ✅ Approved
|
||||
- Proprietary: ❌ Not approved
|
||||
- Custom/Unknown: ⚠️ Review required
|
||||
```
|
||||
|
||||
7. **Format Output**
|
||||
```
|
||||
Display:
|
||||
- ✅/❌ File presence
|
||||
- Detected license type
|
||||
- OSI approval status
|
||||
- Consistency with plugin.json
|
||||
- Completeness (full text vs name only)
|
||||
- Issues and recommendations
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Check LICENSE with defaults (reads expected from plugin.json)
|
||||
/documentation-validation license path:.
|
||||
|
||||
# Check with explicit expected license
|
||||
/documentation-validation license path:. expected:MIT
|
||||
|
||||
# Strict validation (requires full license text)
|
||||
/documentation-validation license path:. strict:true
|
||||
|
||||
# Skip consistency check (only validate file)
|
||||
/documentation-validation license path:. check-consistency:false
|
||||
|
||||
# Check specific plugin
|
||||
/documentation-validation license path:/path/to/plugin expected:Apache-2.0
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Error: LICENSE file not found**
|
||||
```
|
||||
❌ CRITICAL: LICENSE file not found in <path>
|
||||
|
||||
Remediation:
|
||||
1. Create LICENSE file in plugin root directory
|
||||
2. Include full license text (not just the name)
|
||||
3. Use an OSI-approved open-source license (MIT recommended)
|
||||
4. Ensure license field in plugin.json matches LICENSE file
|
||||
|
||||
Recommended licenses for plugins:
|
||||
- MIT: Simple, permissive (most common)
|
||||
- Apache 2.0: Permissive with patent grant
|
||||
- GPL-3.0: Copyleft (requires derivatives to use same license)
|
||||
- BSD-3-Clause: Permissive, similar to MIT
|
||||
|
||||
Full license texts available at: https://choosealicense.com/
|
||||
|
||||
This is a BLOCKING issue - plugin cannot be submitted without a LICENSE.
|
||||
```
|
||||
|
||||
**Error: Incomplete license text**
|
||||
```
|
||||
⚠️ WARNING: LICENSE file contains only license name, not full text
|
||||
|
||||
Current content: "MIT License"
|
||||
Required: Full MIT License text
|
||||
|
||||
The LICENSE file should contain the complete license text, not just the name.
|
||||
|
||||
For MIT License, include:
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction...
|
||||
[full license text]
|
||||
|
||||
Get full text: https://opensource.org/licenses/MIT
|
||||
```
|
||||
|
||||
**Error: License mismatch with plugin.json**
|
||||
```
|
||||
❌ ERROR: LICENSE file does not match plugin.json declaration
|
||||
|
||||
plugin.json declares: "Apache-2.0"
|
||||
LICENSE file contains: "MIT License"
|
||||
|
||||
Remediation:
|
||||
1. Update plugin.json to declare "MIT" license, OR
|
||||
2. Replace LICENSE file with Apache 2.0 license text
|
||||
|
||||
Consistency is required - both files must specify the same license.
|
||||
```
|
||||
|
||||
**Error: Non-OSI-approved license**
|
||||
```
|
||||
❌ ERROR: License is not OSI-approved
|
||||
|
||||
Detected license: "Proprietary" or "Custom License"
|
||||
|
||||
OpenPlugins marketplace requires OSI-approved open-source licenses.
|
||||
|
||||
Recommended licenses:
|
||||
- MIT License (most permissive)
|
||||
- Apache License 2.0
|
||||
- GNU GPL v3
|
||||
- BSD 3-Clause
|
||||
|
||||
Choose a license: https://choosealicense.com/
|
||||
OSI-approved list: https://opensource.org/licenses
|
||||
|
||||
This is a BLOCKING issue - plugin cannot be submitted with proprietary license.
|
||||
```
|
||||
|
||||
**Error: Unrecognized license**
|
||||
```
|
||||
⚠️ WARNING: Unable to identify license type
|
||||
|
||||
The LICENSE file content does not match known license patterns.
|
||||
|
||||
Possible issues:
|
||||
- Custom or modified license (not allowed)
|
||||
- Corrupted or incomplete license text
|
||||
- Non-standard format
|
||||
|
||||
Remediation:
|
||||
1. Use standard, unmodified license text from official source
|
||||
2. Choose from OSI-approved licenses
|
||||
3. Do not modify standard license text (except copyright holder)
|
||||
4. Get standard text from https://choosealicense.com/
|
||||
|
||||
If using a valid OSI license, ensure text matches standard format exactly.
|
||||
```
|
||||
|
||||
### Output Format
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
LICENSE VALIDATION RESULTS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
File: ✅ LICENSE found
|
||||
|
||||
License Type: <detected-license>
|
||||
Confidence: <0-100>% ✅
|
||||
|
||||
OSI Approved: ✅ Yes
|
||||
Complete Text: ✅ Yes (full license included)
|
||||
|
||||
Consistency Check:
|
||||
plugin.json declares: "<license>"
|
||||
LICENSE file contains: "<detected-license>"
|
||||
Match: ✅ Consistent
|
||||
|
||||
Validation: ✅ PASS
|
||||
|
||||
Recommendations:
|
||||
- License is valid and properly formatted
|
||||
- Meets OpenPlugins requirements
|
||||
- Ready for submission
|
||||
|
||||
Overall: <PASS|WARNINGS|FAIL>
|
||||
```
|
||||
|
||||
### Integration
|
||||
|
||||
This operation is invoked by:
|
||||
- `/documentation-validation license path:.` (direct)
|
||||
- `/documentation-validation full-docs path:.` (as part of complete validation)
|
||||
- `/validation-orchestrator comprehensive path:.` (via orchestrator)
|
||||
|
||||
Results contribute to documentation quality score:
|
||||
- Present, valid, consistent: +5 points
|
||||
- Present but issues: 0 points (with warnings)
|
||||
- Missing: BLOCKING issue (-20 points)
|
||||
|
||||
### Common License Patterns
|
||||
|
||||
**MIT License Detection**:
|
||||
```
|
||||
Pattern: "Permission is hereby granted, free of charge"
|
||||
Confidence: 95%+
|
||||
```
|
||||
|
||||
**Apache 2.0 Detection**:
|
||||
```
|
||||
Pattern: "Licensed under the Apache License, Version 2.0"
|
||||
Confidence: 95%+
|
||||
```
|
||||
|
||||
**GPL-3.0 Detection**:
|
||||
```
|
||||
Pattern: "GNU GENERAL PUBLIC LICENSE" + "Version 3"
|
||||
Confidence: 95%+
|
||||
```
|
||||
|
||||
**BSD Detection**:
|
||||
```
|
||||
Pattern: "Redistribution and use in source and binary forms"
|
||||
Confidence: 90%+
|
||||
```
|
||||
|
||||
**Request**: $ARGUMENTS
|
||||
193
commands/documentation-validation/check-readme.md
Normal file
193
commands/documentation-validation/check-readme.md
Normal file
@@ -0,0 +1,193 @@
|
||||
## Operation: Check README Completeness
|
||||
|
||||
Validate README.md completeness, structure, and quality against OpenPlugins standards.
|
||||
|
||||
### Parameters from $ARGUMENTS
|
||||
|
||||
- **path**: Target plugin/marketplace path (required)
|
||||
- **sections**: Comma-separated required sections (optional, defaults to standard set)
|
||||
- **min-length**: Minimum character count (optional, default: 500)
|
||||
- **strict**: Enable strict validation mode (optional, default: false)
|
||||
|
||||
### README Requirements
|
||||
|
||||
**Required Sections** (case-insensitive matching):
|
||||
1. **Overview/Description**: Plugin purpose and functionality
|
||||
2. **Installation**: How to install and configure
|
||||
3. **Usage**: How to use the plugin with examples
|
||||
4. **Examples**: At least 2-3 concrete usage examples
|
||||
5. **License**: License information or reference
|
||||
|
||||
**Quality Criteria**:
|
||||
- Minimum 500 characters (configurable)
|
||||
- No excessive placeholder text
|
||||
- Proper markdown formatting
|
||||
- Working links (if present)
|
||||
- Code blocks properly formatted
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Locate README File**
|
||||
```
|
||||
Check for README.md in plugin root
|
||||
If not found, check for README.txt or readme.md
|
||||
If still not found, report critical error
|
||||
```
|
||||
|
||||
2. **Execute README Checker Script**
|
||||
```bash
|
||||
Execute .scripts/readme-checker.py with parameters:
|
||||
- File path to README.md
|
||||
- Required sections list
|
||||
- Minimum length threshold
|
||||
- Strict mode flag
|
||||
|
||||
Script returns JSON with:
|
||||
- sections_found: Array of detected sections
|
||||
- sections_missing: Array of missing sections
|
||||
- length: Character count
|
||||
- quality_score: 0-100
|
||||
- issues: Array of specific problems
|
||||
```
|
||||
|
||||
3. **Analyze Results**
|
||||
```
|
||||
CRITICAL (blocking):
|
||||
- README.md file missing
|
||||
- Length < 200 characters
|
||||
- Missing 3+ required sections
|
||||
|
||||
WARNING (should fix):
|
||||
- Length < 500 characters
|
||||
- Missing 1-2 required sections
|
||||
- Missing examples section
|
||||
|
||||
RECOMMENDATION (nice to have):
|
||||
- Add troubleshooting section
|
||||
- Expand examples
|
||||
- Add badges or visual elements
|
||||
```
|
||||
|
||||
4. **Calculate Section Score**
|
||||
```
|
||||
score = 100
|
||||
score -= (missing_required_sections × 15)
|
||||
score -= (length < 500) ? 10 : 0
|
||||
score -= (no_examples) ? 15 : 0
|
||||
score = max(0, score)
|
||||
```
|
||||
|
||||
5. **Format Output**
|
||||
```
|
||||
Display:
|
||||
- ✅/❌ File presence
|
||||
- ✅/⚠️/❌ Each required section
|
||||
- Length statistics
|
||||
- Quality score
|
||||
- Specific improvement recommendations
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Check README with defaults
|
||||
/documentation-validation readme path:.
|
||||
|
||||
# Check with custom sections
|
||||
/documentation-validation readme path:./my-plugin sections:"overview,installation,usage,examples,contributing,license"
|
||||
|
||||
# Strict validation with higher standards
|
||||
/documentation-validation readme path:. min-length:1000 strict:true
|
||||
|
||||
# Check specific plugin
|
||||
/documentation-validation readme path:/path/to/plugin sections:"overview,usage,license"
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Error: README.md not found**
|
||||
```
|
||||
❌ CRITICAL: README.md file not found in <path>
|
||||
|
||||
Remediation:
|
||||
1. Create README.md in plugin root directory
|
||||
2. Include required sections: Overview, Installation, Usage, Examples, License
|
||||
3. Ensure minimum 500 characters of meaningful content
|
||||
4. See https://github.com/dhofheinz/open-plugins/blob/main/README.md for example
|
||||
|
||||
This is a BLOCKING issue - plugin cannot be submitted without README.
|
||||
```
|
||||
|
||||
**Error: README too short**
|
||||
```
|
||||
⚠️ WARNING: README.md is only <X> characters (minimum: 500)
|
||||
|
||||
Current length: <X> characters
|
||||
Required: 500 characters minimum
|
||||
Gap: <500-X> characters
|
||||
|
||||
Remediation:
|
||||
- Expand installation instructions with examples
|
||||
- Add 2-3 usage examples with code blocks
|
||||
- Include configuration options
|
||||
- Add troubleshooting section
|
||||
```
|
||||
|
||||
**Error: Missing required sections**
|
||||
```
|
||||
❌ ERROR: Missing <N> required sections
|
||||
|
||||
Missing sections:
|
||||
- Installation: How to install the plugin
|
||||
- Examples: At least 2 concrete usage examples
|
||||
- License: License information or reference to LICENSE file
|
||||
|
||||
Remediation:
|
||||
Add each missing section with meaningful content.
|
||||
See CONTRIBUTING.md for section requirements.
|
||||
```
|
||||
|
||||
### Output Format
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
README VALIDATION RESULTS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
File: ✅ README.md found
|
||||
|
||||
Required Sections:
|
||||
✅ Overview/Description
|
||||
✅ Installation
|
||||
✅ Usage
|
||||
⚠️ Examples (found 1, recommended: 3+)
|
||||
✅ License
|
||||
|
||||
Length: <X> characters (minimum: 500) ✅
|
||||
|
||||
Quality Score: <0-100>/100
|
||||
|
||||
Issues Found: <N>
|
||||
|
||||
Critical (blocking): <count>
|
||||
Warnings (should fix): <count>
|
||||
Recommendations: <count>
|
||||
|
||||
Top Recommendations:
|
||||
1. Add 2 more usage examples with code blocks [+15 pts]
|
||||
2. Expand installation section with configuration options [+5 pts]
|
||||
3. Include troubleshooting section [+5 pts]
|
||||
|
||||
Overall: <PASS|WARNINGS|FAIL>
|
||||
```
|
||||
|
||||
### Integration
|
||||
|
||||
This operation is invoked by:
|
||||
- `/documentation-validation readme path:.` (direct)
|
||||
- `/documentation-validation full-docs path:.` (as part of complete validation)
|
||||
- `/validation-orchestrator comprehensive path:.` (via orchestrator)
|
||||
|
||||
Results feed into quality-analysis scoring system.
|
||||
|
||||
**Request**: $ARGUMENTS
|
||||
533
commands/documentation-validation/full-documentation.md
Normal file
533
commands/documentation-validation/full-documentation.md
Normal file
@@ -0,0 +1,533 @@
|
||||
## Operation: Full Documentation Validation
|
||||
|
||||
Execute comprehensive documentation validation workflow covering all documentation aspects.
|
||||
|
||||
### Parameters from $ARGUMENTS
|
||||
|
||||
- **path**: Target plugin/marketplace path (required)
|
||||
- **detailed**: Include detailed sub-reports (optional, default: true)
|
||||
- **fix-suggestions**: Generate actionable improvement suggestions (optional, default: true)
|
||||
- **format**: Output format (text|json|markdown) (optional, default: text)
|
||||
|
||||
### Full Documentation Workflow
|
||||
|
||||
This operation orchestrates all documentation validation sub-operations to provide
|
||||
a complete documentation quality assessment.
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Initialize Validation Context**
|
||||
```
|
||||
Create validation context:
|
||||
- Target path
|
||||
- Timestamp
|
||||
- Validation mode: comprehensive
|
||||
- Results storage structure
|
||||
|
||||
Prepare for aggregating results from:
|
||||
- README validation
|
||||
- CHANGELOG validation
|
||||
- LICENSE validation
|
||||
- Examples validation
|
||||
```
|
||||
|
||||
2. **Execute README Validation**
|
||||
```
|
||||
Invoke: check-readme.md operation
|
||||
Parameters:
|
||||
- path: <target-path>
|
||||
- sections: default required sections
|
||||
- min-length: 500
|
||||
|
||||
Capture results:
|
||||
- README present: Boolean
|
||||
- Sections found: Array
|
||||
- Sections missing: Array
|
||||
- Length: Integer
|
||||
- Score: 0-100
|
||||
- Issues: Array
|
||||
```
|
||||
|
||||
3. **Execute CHANGELOG Validation**
|
||||
```
|
||||
Invoke: validate-changelog.md operation
|
||||
Parameters:
|
||||
- file: CHANGELOG.md
|
||||
- format: keepachangelog
|
||||
- require-unreleased: true
|
||||
|
||||
Capture results:
|
||||
- CHANGELOG present: Boolean
|
||||
- Format compliance: 0-100%
|
||||
- Version entries: Array
|
||||
- Issues: Array
|
||||
- Score: 0-100
|
||||
```
|
||||
|
||||
4. **Execute LICENSE Validation**
|
||||
```
|
||||
Invoke: check-license.md operation
|
||||
Parameters:
|
||||
- path: <target-path>
|
||||
- check-consistency: true
|
||||
|
||||
Capture results:
|
||||
- LICENSE present: Boolean
|
||||
- License type: String
|
||||
- OSI approved: Boolean
|
||||
- Consistent with manifest: Boolean
|
||||
- Issues: Array
|
||||
- Score: 0-100
|
||||
```
|
||||
|
||||
5. **Execute Examples Validation**
|
||||
```
|
||||
Invoke: validate-examples.md operation
|
||||
Parameters:
|
||||
- path: <target-path>
|
||||
- no-placeholders: true
|
||||
- recursive: true
|
||||
|
||||
Capture results:
|
||||
- Files checked: Integer
|
||||
- Examples found: Integer
|
||||
- Placeholders detected: Integer
|
||||
- Quality score: 0-100
|
||||
- Issues: Array
|
||||
```
|
||||
|
||||
6. **Aggregate Results**
|
||||
```
|
||||
Calculate overall documentation score:
|
||||
|
||||
weights = {
|
||||
readme: 40%, # Most important
|
||||
examples: 30%, # Critical for usability
|
||||
license: 20%, # Required for submission
|
||||
changelog: 10% # Recommended but not critical
|
||||
}
|
||||
|
||||
overall_score = (
|
||||
readme_score × 0.40 +
|
||||
examples_score × 0.30 +
|
||||
license_score × 0.20 +
|
||||
changelog_score × 0.10
|
||||
)
|
||||
|
||||
Round to integer: 0-100
|
||||
```
|
||||
|
||||
7. **Categorize Issues by Priority**
|
||||
```
|
||||
CRITICAL (P0 - Blocking):
|
||||
- README.md missing
|
||||
- LICENSE file missing
|
||||
- README < 200 characters
|
||||
- Non-OSI-approved license
|
||||
- License mismatch with manifest
|
||||
|
||||
IMPORTANT (P1 - Should Fix):
|
||||
- README missing 2+ required sections
|
||||
- README < 500 characters
|
||||
- No examples in README
|
||||
- 5+ placeholder patterns
|
||||
- CHANGELOG has format errors
|
||||
|
||||
RECOMMENDED (P2 - Nice to Have):
|
||||
- CHANGELOG missing
|
||||
- README missing optional sections
|
||||
- < 3 examples
|
||||
- Minor placeholder patterns
|
||||
```
|
||||
|
||||
8. **Generate Improvement Roadmap**
|
||||
```
|
||||
Create prioritized action plan:
|
||||
|
||||
For each issue:
|
||||
- Identify impact on overall score
|
||||
- Estimate effort (Low/Medium/High)
|
||||
- Calculate score improvement
|
||||
- Generate specific remediation steps
|
||||
|
||||
Sort by: Priority → Score Impact → Effort
|
||||
|
||||
Example:
|
||||
1. [P0] Add LICENSE file → +20 pts → 15 min
|
||||
2. [P1] Expand README to 500+ chars → +10 pts → 30 min
|
||||
3. [P1] Add 2 usage examples → +15 pts → 20 min
|
||||
4. [P2] Create CHANGELOG.md → +10 pts → 15 min
|
||||
```
|
||||
|
||||
9. **Determine Publication Readiness**
|
||||
```
|
||||
Publication readiness determination:
|
||||
|
||||
READY (90-100):
|
||||
- All critical requirements met
|
||||
- High-quality documentation
|
||||
- No blocking issues
|
||||
- Immediate submission recommended
|
||||
|
||||
READY WITH MINOR IMPROVEMENTS (75-89):
|
||||
- Critical requirements met
|
||||
- Some recommended improvements
|
||||
- Can submit, but improvements increase quality
|
||||
- Suggested: Address P1 issues before submission
|
||||
|
||||
NEEDS WORK (60-74):
|
||||
- Critical requirements met
|
||||
- Several important issues
|
||||
- Should address P1 issues before submission
|
||||
- Documentation needs expansion
|
||||
|
||||
NOT READY (<60):
|
||||
- Critical issues present
|
||||
- Insufficient documentation quality
|
||||
- Must address P0 and P1 issues
|
||||
- Submission will be rejected
|
||||
```
|
||||
|
||||
10. **Format Output**
|
||||
```
|
||||
Based on format parameter:
|
||||
- text: Human-readable report
|
||||
- json: Structured JSON for automation
|
||||
- markdown: Formatted markdown report
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Full documentation validation with defaults
|
||||
/documentation-validation full-docs path:.
|
||||
|
||||
# With detailed sub-reports
|
||||
/documentation-validation full-docs path:. detailed:true
|
||||
|
||||
# JSON output for automation
|
||||
/documentation-validation full-docs path:. format:json
|
||||
|
||||
# Without fix suggestions (faster)
|
||||
/documentation-validation full-docs path:. fix-suggestions:false
|
||||
|
||||
# Validate specific plugin
|
||||
/documentation-validation full-docs path:/path/to/plugin
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Error: Multiple critical issues**
|
||||
```
|
||||
❌ CRITICAL: Multiple blocking documentation issues
|
||||
|
||||
Documentation Score: <score>/100 ⚠️
|
||||
|
||||
BLOCKING ISSUES (<count>):
|
||||
1. README.md not found
|
||||
→ Create README.md with required sections
|
||||
→ Minimum 500 characters
|
||||
→ Include Overview, Installation, Usage, Examples, License
|
||||
|
||||
2. LICENSE file not found
|
||||
→ Create LICENSE file with OSI-approved license
|
||||
→ MIT License recommended
|
||||
→ Must match plugin.json license field
|
||||
|
||||
3. License mismatch
|
||||
→ plugin.json declares "Apache-2.0"
|
||||
→ LICENSE file contains "MIT"
|
||||
→ Update one to match the other
|
||||
|
||||
IMPORTANT ISSUES (<count>):
|
||||
- README missing Examples section
|
||||
- No code examples found
|
||||
- CHANGELOG.md recommended
|
||||
|
||||
YOUR NEXT STEPS:
|
||||
1. Add LICENSE file (CRITICAL - 15 minutes)
|
||||
2. Create comprehensive README.md (CRITICAL - 30 minutes)
|
||||
3. Add 3 usage examples (IMPORTANT - 20 minutes)
|
||||
|
||||
After addressing critical issues, revalidate with:
|
||||
/documentation-validation full-docs path:.
|
||||
```
|
||||
|
||||
**Error: Documentation too sparse**
|
||||
```
|
||||
⚠️ WARNING: Documentation exists but is too sparse
|
||||
|
||||
Documentation Score: 65/100 ⚠️
|
||||
|
||||
Your documentation meets minimum requirements but needs expansion
|
||||
for professional quality.
|
||||
|
||||
AREAS NEEDING IMPROVEMENT:
|
||||
1. README is only 342 characters (minimum: 500)
|
||||
→ Expand installation instructions
|
||||
→ Add more detailed usage examples
|
||||
→ Include troubleshooting section
|
||||
|
||||
2. Only 1 example found (recommended: 3+)
|
||||
→ Add basic usage example
|
||||
→ Add advanced example
|
||||
→ Add configuration example
|
||||
|
||||
3. CHANGELOG missing
|
||||
→ Create CHANGELOG.md
|
||||
→ Use Keep a Changelog format
|
||||
→ Document version 1.0.0 features
|
||||
|
||||
IMPACT:
|
||||
Current: 65/100 (Fair)
|
||||
After improvements: ~85/100 (Good)
|
||||
|
||||
Time investment: ~45 minutes
|
||||
Quality improvement: +20 points
|
||||
```
|
||||
|
||||
### Output Format
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
COMPREHENSIVE DOCUMENTATION VALIDATION
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Target: <path>
|
||||
Type: <marketplace|plugin>
|
||||
Timestamp: <YYYY-MM-DD HH:MM:SS>
|
||||
|
||||
OVERALL DOCUMENTATION SCORE: <0-100>/100 <⭐⭐⭐⭐⭐>
|
||||
Rating: <Excellent|Good|Fair|Needs Improvement|Poor>
|
||||
Publication Ready: <Yes|Yes with improvements|Needs work|No>
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
COMPONENT SCORES
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
README (Weight: 40%)
|
||||
Score: <0-100>/100 ✅
|
||||
Status: ✅ Complete and comprehensive
|
||||
Sections: <N>/5 required sections found
|
||||
Length: <N> characters (minimum: 500) ✅
|
||||
Issues: None
|
||||
|
||||
EXAMPLES (Weight: 30%)
|
||||
Score: <0-100>/100 ⚠️
|
||||
Status: ⚠️ Could be improved
|
||||
Examples found: <N> (recommended: 3+)
|
||||
Placeholders: <N> detected
|
||||
Issues: <N> placeholder patterns found
|
||||
|
||||
LICENSE (Weight: 20%)
|
||||
Score: <0-100>/100 ✅
|
||||
Status: ✅ Valid and consistent
|
||||
Type: MIT License
|
||||
OSI Approved: ✅ Yes
|
||||
Consistency: ✅ Matches plugin.json
|
||||
Issues: None
|
||||
|
||||
CHANGELOG (Weight: 10%)
|
||||
Score: <0-100>/100 ⚠️
|
||||
Status: ⚠️ Missing (recommended but not required)
|
||||
Format: N/A
|
||||
Versions: 0
|
||||
Issues: CHANGELOG.md not found
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
ISSUES SUMMARY
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Critical (P0 - Blocking): <count>
|
||||
Important (P1 - Should Fix): <count>
|
||||
Recommended (P2 - Nice to Have): <count>
|
||||
|
||||
CRITICAL ISSUES:
|
||||
[None - Ready for submission] ✅
|
||||
|
||||
IMPORTANT ISSUES:
|
||||
⚠️ 1. Add 2 more usage examples to README
|
||||
Impact: +15 points
|
||||
Effort: Low (20 minutes)
|
||||
|
||||
⚠️ 2. Replace 3 placeholder patterns in examples
|
||||
Impact: +10 points
|
||||
Effort: Low (10 minutes)
|
||||
|
||||
RECOMMENDATIONS:
|
||||
💡 1. Create CHANGELOG.md for version tracking
|
||||
Impact: +10 points
|
||||
Effort: Low (15 minutes)
|
||||
|
||||
💡 2. Add troubleshooting section to README
|
||||
Impact: +5 points
|
||||
Effort: Low (15 minutes)
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
IMPROVEMENT ROADMAP
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Current Score: <score>/100
|
||||
Target Score: 90/100 (Excellent - Publication Ready)
|
||||
Gap: <gap> points
|
||||
|
||||
RECOMMENDED ACTIONS (to reach 90+):
|
||||
|
||||
1. [+15 pts] Add usage examples
|
||||
Priority: High
|
||||
Effort: 20 minutes
|
||||
Description:
|
||||
- Add 2 more concrete usage examples to README
|
||||
- Include basic, intermediate, and advanced scenarios
|
||||
- Use real plugin commands and parameters
|
||||
|
||||
2. [+10 pts] Clean up placeholder patterns
|
||||
Priority: Medium
|
||||
Effort: 10 minutes
|
||||
Description:
|
||||
- Replace "YOUR_VALUE" patterns with concrete examples
|
||||
- Complete or remove TODO markers
|
||||
- Use template syntax (${VAR}) for user-provided values
|
||||
|
||||
3. [+10 pts] Create CHANGELOG.md
|
||||
Priority: Medium
|
||||
Effort: 15 minutes
|
||||
Description:
|
||||
- Use Keep a Changelog format
|
||||
- Document version 1.0.0 initial release
|
||||
- Add [Unreleased] section for future changes
|
||||
|
||||
AFTER IMPROVEMENTS:
|
||||
Projected Score: ~90/100 ⭐⭐⭐⭐⭐
|
||||
Time Investment: ~45 minutes
|
||||
Status: Excellent - Ready for submission
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
PUBLICATION READINESS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Status: ✅ READY WITH MINOR IMPROVEMENTS
|
||||
|
||||
Your plugin documentation meets all critical requirements and is ready
|
||||
for submission to OpenPlugins marketplace. The recommended improvements
|
||||
above will increase quality score and provide better user experience.
|
||||
|
||||
✅ Strengths:
|
||||
- Comprehensive README with all required sections
|
||||
- Valid OSI-approved license (MIT)
|
||||
- License consistent with plugin.json
|
||||
- Good documentation structure
|
||||
|
||||
⚠️ Improvement Opportunities:
|
||||
- Add more usage examples for better user onboarding
|
||||
- Create CHANGELOG for version tracking
|
||||
- Clean up minor placeholder patterns
|
||||
|
||||
NEXT STEPS:
|
||||
1. (Optional) Address recommended improvements (~45 min)
|
||||
2. Run validation again to verify improvements
|
||||
3. Submit to OpenPlugins marketplace
|
||||
|
||||
Command to revalidate:
|
||||
/documentation-validation full-docs path:.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
```
|
||||
|
||||
### Integration
|
||||
|
||||
This operation is the primary entry point for complete documentation validation:
|
||||
|
||||
**Invoked by**:
|
||||
- `/documentation-validation full-docs path:.` (direct invocation)
|
||||
- `/validation-orchestrator comprehensive path:.` (as part of full plugin validation)
|
||||
- marketplace-validator agent (automatic documentation assessment)
|
||||
|
||||
**Invokes sub-operations**:
|
||||
- `/documentation-validation readme path:.`
|
||||
- `/documentation-validation changelog file:CHANGELOG.md`
|
||||
- `/documentation-validation license path:.`
|
||||
- `/documentation-validation examples path:.`
|
||||
|
||||
**Feeds results to**:
|
||||
- `/quality-analysis full-analysis` (for overall quality scoring)
|
||||
- `/quality-analysis generate-report` (for report generation)
|
||||
|
||||
### JSON Output Format
|
||||
|
||||
When `format:json` is specified:
|
||||
|
||||
```json
|
||||
{
|
||||
"validation_type": "full-documentation",
|
||||
"target_path": "/path/to/plugin",
|
||||
"timestamp": "2025-01-15T10:30:00Z",
|
||||
"overall_score": 85,
|
||||
"rating": "Good",
|
||||
"publication_ready": "yes_with_improvements",
|
||||
"components": {
|
||||
"readme": {
|
||||
"score": 90,
|
||||
"status": "pass",
|
||||
"present": true,
|
||||
"sections_found": 5,
|
||||
"sections_missing": 0,
|
||||
"length": 1234,
|
||||
"issues": []
|
||||
},
|
||||
"changelog": {
|
||||
"score": 70,
|
||||
"status": "warning",
|
||||
"present": true,
|
||||
"compliance": 70,
|
||||
"issues": ["Invalid version header format"]
|
||||
},
|
||||
"license": {
|
||||
"score": 100,
|
||||
"status": "pass",
|
||||
"present": true,
|
||||
"type": "MIT",
|
||||
"osi_approved": true,
|
||||
"consistent": true,
|
||||
"issues": []
|
||||
},
|
||||
"examples": {
|
||||
"score": 75,
|
||||
"status": "warning",
|
||||
"examples_found": 2,
|
||||
"placeholders_detected": 3,
|
||||
"issues": ["Placeholder patterns detected"]
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"critical": [],
|
||||
"important": [
|
||||
{
|
||||
"component": "examples",
|
||||
"message": "Add 2 more usage examples",
|
||||
"impact": 15,
|
||||
"effort": "low"
|
||||
}
|
||||
],
|
||||
"recommended": [
|
||||
{
|
||||
"component": "readme",
|
||||
"message": "Add troubleshooting section",
|
||||
"impact": 5,
|
||||
"effort": "low"
|
||||
}
|
||||
]
|
||||
},
|
||||
"improvement_roadmap": [
|
||||
{
|
||||
"action": "Add usage examples",
|
||||
"points": 15,
|
||||
"priority": "high",
|
||||
"effort": "20 minutes"
|
||||
}
|
||||
],
|
||||
"projected_score_after_improvements": 95
|
||||
}
|
||||
```
|
||||
|
||||
**Request**: $ARGUMENTS
|
||||
99
commands/documentation-validation/skill.md
Normal file
99
commands/documentation-validation/skill.md
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
description: Validate documentation completeness, format, and quality for plugins and marketplaces
|
||||
---
|
||||
|
||||
You are the Documentation Validation coordinator, ensuring comprehensive and high-quality documentation.
|
||||
|
||||
## Your Mission
|
||||
|
||||
Parse `$ARGUMENTS` to determine the requested documentation validation operation and route to the appropriate sub-command.
|
||||
|
||||
## Available Operations
|
||||
|
||||
Parse the first word of `$ARGUMENTS` to determine which operation to execute:
|
||||
|
||||
- **readme** → Read `.claude/commands/documentation-validation/check-readme.md`
|
||||
- **changelog** → Read `.claude/commands/documentation-validation/validate-changelog.md`
|
||||
- **license** → Read `.claude/commands/documentation-validation/check-license.md`
|
||||
- **examples** → Read `.claude/commands/documentation-validation/validate-examples.md`
|
||||
- **full-docs** → Read `.claude/commands/documentation-validation/full-documentation.md`
|
||||
|
||||
## Argument Format
|
||||
|
||||
```
|
||||
/documentation-validation <operation> [parameters]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Check README completeness
|
||||
/documentation-validation readme path:. sections:"overview,installation,usage,examples"
|
||||
|
||||
# Validate CHANGELOG format
|
||||
/documentation-validation changelog file:CHANGELOG.md format:keepachangelog
|
||||
|
||||
# Check LICENSE file
|
||||
/documentation-validation license path:. expected:MIT
|
||||
|
||||
# Validate example quality
|
||||
/documentation-validation examples path:. no-placeholders:true
|
||||
|
||||
# Run complete documentation validation
|
||||
/documentation-validation full-docs path:.
|
||||
```
|
||||
|
||||
## Documentation Standards
|
||||
|
||||
**README.md Requirements**:
|
||||
- Overview/Description section
|
||||
- Installation instructions
|
||||
- Usage examples (minimum 2)
|
||||
- Configuration options (if applicable)
|
||||
- License information
|
||||
- Length: Minimum 500 characters
|
||||
|
||||
**CHANGELOG.md Requirements**:
|
||||
- Keep a Changelog format
|
||||
- Version headers ([X.Y.Z] - YYYY-MM-DD)
|
||||
- Change categories: Added, Changed, Deprecated, Removed, Fixed, Security
|
||||
- Unreleased section for upcoming changes
|
||||
|
||||
**LICENSE Requirements**:
|
||||
- LICENSE or LICENSE.txt file present
|
||||
- Valid OSI-approved license
|
||||
- License matches plugin.json declaration
|
||||
|
||||
**Examples Requirements**:
|
||||
- No placeholder text (TODO, FIXME, XXX, placeholder)
|
||||
- Complete, runnable examples
|
||||
- Real values, not dummy data
|
||||
- Proper formatting and syntax
|
||||
|
||||
## Quality Scoring
|
||||
|
||||
Documentation contributes to overall quality score:
|
||||
- Complete README: +15 points
|
||||
- CHANGELOG present: +10 points
|
||||
- LICENSE valid: +5 points
|
||||
- Quality examples: +10 points
|
||||
|
||||
## Error Handling
|
||||
|
||||
If the operation is not recognized:
|
||||
1. List all available documentation operations
|
||||
2. Show documentation standards
|
||||
3. Provide improvement suggestions
|
||||
|
||||
## Base Directory
|
||||
|
||||
Base directory for this skill: `.claude/commands/documentation-validation/`
|
||||
|
||||
## Your Task
|
||||
|
||||
1. Parse `$ARGUMENTS` to extract operation and parameters
|
||||
2. Read the corresponding operation file
|
||||
3. Execute documentation validation checks
|
||||
4. Return detailed findings with specific improvement guidance
|
||||
|
||||
**Current Request**: $ARGUMENTS
|
||||
286
commands/documentation-validation/validate-changelog.md
Normal file
286
commands/documentation-validation/validate-changelog.md
Normal file
@@ -0,0 +1,286 @@
|
||||
## Operation: Validate CHANGELOG Format
|
||||
|
||||
Validate CHANGELOG.md format compliance with "Keep a Changelog" standard.
|
||||
|
||||
### Parameters from $ARGUMENTS
|
||||
|
||||
- **file**: Path to CHANGELOG file (optional, default: CHANGELOG.md)
|
||||
- **format**: Expected format (optional, default: keepachangelog)
|
||||
- **strict**: Enable strict validation (optional, default: false)
|
||||
- **require-unreleased**: Require [Unreleased] section (optional, default: true)
|
||||
|
||||
### CHANGELOG Requirements
|
||||
|
||||
**Keep a Changelog Format** (https://keepachangelog.com/):
|
||||
|
||||
```markdown
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- New features not yet released
|
||||
|
||||
## [1.0.0] - 2025-01-15
|
||||
### Added
|
||||
- Initial release feature
|
||||
### Changed
|
||||
- Modified behavior
|
||||
### Fixed
|
||||
- Bug fixes
|
||||
```
|
||||
|
||||
**Required Elements**:
|
||||
1. **Title**: "Changelog" or "Change Log"
|
||||
2. **Version Headers**: `## [X.Y.Z] - YYYY-MM-DD` format
|
||||
3. **Change Categories**: Added, Changed, Deprecated, Removed, Fixed, Security
|
||||
4. **Unreleased Section**: `## [Unreleased]` for upcoming changes
|
||||
5. **Chronological Order**: Newest versions first
|
||||
|
||||
**Valid Change Categories**:
|
||||
- **Added**: New features
|
||||
- **Changed**: Changes in existing functionality
|
||||
- **Deprecated**: Soon-to-be removed features
|
||||
- **Removed**: Removed features
|
||||
- **Fixed**: Bug fixes
|
||||
- **Security**: Security vulnerability fixes
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Locate CHANGELOG File**
|
||||
```
|
||||
Check for CHANGELOG.md in plugin root
|
||||
Also check: CHANGELOG, CHANGELOG.txt, changelog.md, HISTORY.md
|
||||
If not found, report as missing (WARNING, not CRITICAL)
|
||||
```
|
||||
|
||||
2. **Execute CHANGELOG Validator**
|
||||
```bash
|
||||
Execute .scripts/changelog-validator.sh with parameters:
|
||||
- File path to CHANGELOG
|
||||
- Expected format (keepachangelog)
|
||||
- Strict mode flag
|
||||
- Require unreleased flag
|
||||
|
||||
Script returns:
|
||||
- has_title: Boolean
|
||||
- has_unreleased: Boolean
|
||||
- version_headers: Array of version entries
|
||||
- categories_used: Array of change categories
|
||||
- issues: Array of format violations
|
||||
- compliance_score: 0-100
|
||||
```
|
||||
|
||||
3. **Validate Version Headers**
|
||||
```
|
||||
For each version header:
|
||||
- Check format: ## [X.Y.Z] - YYYY-MM-DD
|
||||
- Validate semantic version (X.Y.Z)
|
||||
- Validate date format (YYYY-MM-DD)
|
||||
- Check chronological order (newest first)
|
||||
|
||||
Common violations:
|
||||
- Missing brackets: ## 1.0.0 - 2025-01-15 (should be [1.0.0])
|
||||
- Wrong date format: ## [1.0.0] - 01/15/2025
|
||||
- Invalid semver: ## [1.0] - 2025-01-15
|
||||
```
|
||||
|
||||
4. **Validate Change Categories**
|
||||
```
|
||||
For each version section:
|
||||
- Check for valid category headers (### Added, ### Fixed, etc.)
|
||||
- Warn if no categories used
|
||||
- Recommend appropriate categories
|
||||
|
||||
Invalid category examples:
|
||||
- "### New Features" (should be "### Added")
|
||||
- "### Bugs" (should be "### Fixed")
|
||||
- "### Updates" (should be "### Changed")
|
||||
```
|
||||
|
||||
5. **Calculate Compliance Score**
|
||||
```
|
||||
score = 100
|
||||
score -= (!has_title) ? 10 : 0
|
||||
score -= (!has_unreleased) ? 15 : 0
|
||||
score -= (invalid_version_headers × 10)
|
||||
score -= (invalid_categories × 5)
|
||||
score -= (wrong_date_format × 5)
|
||||
score = max(0, score)
|
||||
```
|
||||
|
||||
6. **Format Output**
|
||||
```
|
||||
Display:
|
||||
- ✅/⚠️/❌ File presence
|
||||
- ✅/❌ Format compliance
|
||||
- ✅/⚠️ Version headers
|
||||
- ✅/⚠️ Change categories
|
||||
- Compliance score
|
||||
- Specific violations
|
||||
- Improvement recommendations
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Validate default CHANGELOG.md
|
||||
/documentation-validation changelog file:CHANGELOG.md
|
||||
|
||||
# Validate with custom path
|
||||
/documentation-validation changelog file:./HISTORY.md
|
||||
|
||||
# Strict validation (all elements required)
|
||||
/documentation-validation changelog file:CHANGELOG.md strict:true
|
||||
|
||||
# Don't require Unreleased section
|
||||
/documentation-validation changelog file:CHANGELOG.md require-unreleased:false
|
||||
|
||||
# Part of full documentation check
|
||||
/documentation-validation full-docs path:.
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Error: CHANGELOG not found**
|
||||
```
|
||||
⚠️ WARNING: CHANGELOG.md not found in <path>
|
||||
|
||||
Remediation:
|
||||
1. Create CHANGELOG.md in plugin root directory
|
||||
2. Use "Keep a Changelog" format (https://keepachangelog.com/)
|
||||
3. Include [Unreleased] section for upcoming changes
|
||||
4. Document version history with proper headers
|
||||
|
||||
Example:
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Features in development
|
||||
|
||||
## [1.0.0] - 2025-01-15
|
||||
### Added
|
||||
- Initial release
|
||||
|
||||
Note: CHANGELOG is recommended but not required for initial submission.
|
||||
It becomes important for version updates.
|
||||
```
|
||||
|
||||
**Error: Invalid version header format**
|
||||
```
|
||||
❌ ERROR: Invalid version header format detected
|
||||
|
||||
Invalid headers found:
|
||||
- Line 10: "## 1.0.0 - 2025-01-15" (missing brackets)
|
||||
- Line 25: "## [1.0] - 01/15/2025" (invalid semver and date format)
|
||||
|
||||
Correct format:
|
||||
## [X.Y.Z] - YYYY-MM-DD
|
||||
|
||||
Examples:
|
||||
- ## [1.0.0] - 2025-01-15
|
||||
- ## [2.1.3] - 2024-12-20
|
||||
- ## [0.1.0] - 2024-11-05
|
||||
|
||||
Remediation:
|
||||
1. Add brackets around version numbers: [1.0.0]
|
||||
2. Use semantic versioning: MAJOR.MINOR.PATCH
|
||||
3. Use ISO date format: YYYY-MM-DD
|
||||
```
|
||||
|
||||
**Error: Missing Unreleased section**
|
||||
```
|
||||
⚠️ WARNING: Missing [Unreleased] section
|
||||
|
||||
The Keep a Changelog format recommends an [Unreleased] section for tracking
|
||||
upcoming changes before they're officially released.
|
||||
|
||||
Add to top of CHANGELOG (after title):
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Features in development
|
||||
### Changed
|
||||
- Planned changes
|
||||
```
|
||||
|
||||
**Error: Invalid change categories**
|
||||
```
|
||||
⚠️ WARNING: Non-standard change categories detected
|
||||
|
||||
Invalid categories found:
|
||||
- "### New Features" (should be "### Added")
|
||||
- "### Bug Fixes" (should be "### Fixed")
|
||||
- "### Updates" (should be "### Changed")
|
||||
|
||||
Valid categories:
|
||||
- Added: New features
|
||||
- Changed: Changes in existing functionality
|
||||
- Deprecated: Soon-to-be removed features
|
||||
- Removed: Removed features
|
||||
- Fixed: Bug fixes
|
||||
- Security: Security vulnerability fixes
|
||||
|
||||
Remediation:
|
||||
Replace non-standard categories with Keep a Changelog categories.
|
||||
```
|
||||
|
||||
### Output Format
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
CHANGELOG VALIDATION RESULTS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
File: ✅ CHANGELOG.md found
|
||||
|
||||
Format: Keep a Changelog
|
||||
Compliance: <0-100>% ✅/⚠️/❌
|
||||
|
||||
Structure:
|
||||
✅ Title present
|
||||
✅ [Unreleased] section present
|
||||
✅ Version headers formatted correctly
|
||||
✅ Change categories valid
|
||||
|
||||
Version Entries: <count>
|
||||
- [1.0.0] - 2025-01-15 ✅
|
||||
- [0.2.0] - 2024-12-20 ✅
|
||||
- [0.1.0] - 2024-11-05 ✅
|
||||
|
||||
Change Categories Used:
|
||||
✅ Added (3 versions)
|
||||
✅ Changed (2 versions)
|
||||
✅ Fixed (3 versions)
|
||||
|
||||
Issues Found: <N>
|
||||
|
||||
Violations:
|
||||
<List specific issues if any>
|
||||
|
||||
Recommendations:
|
||||
1. Add Security category for vulnerability fixes
|
||||
2. Expand [Unreleased] section with upcoming features
|
||||
3. Add links to version comparison (optional)
|
||||
|
||||
Overall: <PASS|WARNINGS|FAIL>
|
||||
```
|
||||
|
||||
### Integration
|
||||
|
||||
This operation is invoked by:
|
||||
- `/documentation-validation changelog file:CHANGELOG.md` (direct)
|
||||
- `/documentation-validation full-docs path:.` (as part of complete validation)
|
||||
- `/validation-orchestrator comprehensive path:.` (via orchestrator)
|
||||
|
||||
Results contribute to documentation quality score:
|
||||
- Present and compliant: +10 points
|
||||
- Present but non-compliant: +5 points
|
||||
- Missing: 0 points (warning but not blocking)
|
||||
|
||||
**Request**: $ARGUMENTS
|
||||
335
commands/documentation-validation/validate-examples.md
Normal file
335
commands/documentation-validation/validate-examples.md
Normal file
@@ -0,0 +1,335 @@
|
||||
## Operation: Validate Example Quality
|
||||
|
||||
Validate example code quality, detecting placeholders and ensuring examples are complete and runnable.
|
||||
|
||||
### Parameters from $ARGUMENTS
|
||||
|
||||
- **path**: Target plugin/marketplace path (required)
|
||||
- **no-placeholders**: Strict placeholder enforcement (optional, default: true)
|
||||
- **recursive**: Check all markdown and code files recursively (optional, default: true)
|
||||
- **extensions**: File extensions to check (optional, default: "md,txt,json,sh,py,js")
|
||||
|
||||
### Example Quality Requirements
|
||||
|
||||
**Complete Examples**:
|
||||
- Concrete, runnable code or commands
|
||||
- Real values, not placeholder text
|
||||
- Proper syntax and formatting
|
||||
- Context and explanations
|
||||
- Expected output or results
|
||||
|
||||
**No Placeholder Patterns**:
|
||||
- **TODO**: `TODO`, `@TODO`, `// TODO:`
|
||||
- **FIXME**: `FIXME`, `@FIXME`, `// FIXME:`
|
||||
- **XXX**: `XXX`, `@XXX`, `// XXX:`
|
||||
- **Placeholders**: `placeholder`, `PLACEHOLDER`, `your-value-here`, `<your-value>`, `[YOUR-VALUE]`
|
||||
- **Generic**: `example`, `sample`, `test`, `dummy`, `foo`, `bar`, `baz`
|
||||
- **User substitution**: `<username>`, `<your-email>`, `your-api-key`, `INSERT-HERE`
|
||||
|
||||
**Acceptable Patterns** (not placeholders):
|
||||
- Template variables: `{{variable}}`, `${variable}`, `$VARIABLE`
|
||||
- Documentation examples: `<name>`, `[optional]` in usage syntax
|
||||
- Actual values: Real plugin names, real commands, concrete examples
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Identify Files to Validate**
|
||||
```
|
||||
Scan plugin directory for documentation files:
|
||||
- README.md (primary source)
|
||||
- CONTRIBUTING.md
|
||||
- docs/**/*.md
|
||||
- examples/**/*
|
||||
- *.sh, *.py, *.js (example scripts)
|
||||
|
||||
If recursive:false, only check README.md
|
||||
```
|
||||
|
||||
2. **Execute Example Validator**
|
||||
```bash
|
||||
Execute .scripts/example-validator.sh with parameters:
|
||||
- Path to plugin directory
|
||||
- No-placeholders flag
|
||||
- Recursive flag
|
||||
- File extensions to check
|
||||
|
||||
Script returns:
|
||||
- files_checked: Count of files analyzed
|
||||
- placeholders_found: Array of placeholder instances
|
||||
- files_with_issues: Array of files containing placeholders
|
||||
- example_count: Number of code examples found
|
||||
- quality_score: 0-100
|
||||
```
|
||||
|
||||
3. **Detect Placeholder Patterns**
|
||||
```bash
|
||||
Search for patterns (case-insensitive):
|
||||
|
||||
# TODO/FIXME/XXX markers
|
||||
grep -iE '(TODO|FIXME|XXX|HACK)[:)]' <files>
|
||||
|
||||
# Placeholder text
|
||||
grep -iE '(placeholder|your-.*-here|<your-|INSERT.?HERE)' <files>
|
||||
|
||||
# Generic dummy values
|
||||
grep -iE '\b(foo|bar|baz|dummy|sample|test)\b' <files>
|
||||
|
||||
# User substitution patterns
|
||||
grep -iE '(<username>|<email>|<api-key>|YOUR_[A-Z_]+)' <files>
|
||||
|
||||
# Exclude:
|
||||
- Comments explaining placeholders
|
||||
- Documentation of template syntax
|
||||
- Proper template variables ({{x}}, ${x})
|
||||
```
|
||||
|
||||
4. **Analyze Code Blocks**
|
||||
```
|
||||
For each code block in markdown:
|
||||
- Extract language and content
|
||||
- Check for placeholder patterns
|
||||
- Verify syntax highlighting specified
|
||||
- Ensure examples are complete
|
||||
|
||||
Example extraction:
|
||||
```bash
|
||||
/plugin install my-plugin@marketplace ✅ Concrete
|
||||
/plugin install <plugin-name> ⚠️ Documentation (acceptable)
|
||||
/plugin install YOUR_PLUGIN ❌ Placeholder
|
||||
```
|
||||
```
|
||||
|
||||
5. **Count and Categorize Examples**
|
||||
```
|
||||
Count examples by type:
|
||||
- Command examples: /plugin install ...
|
||||
- Configuration examples: JSON snippets
|
||||
- Code examples: Script samples
|
||||
- Usage examples: Real-world scenarios
|
||||
|
||||
Quality criteria:
|
||||
- At least 2-3 concrete examples
|
||||
- Examples cover primary use cases
|
||||
- Examples are copy-pasteable
|
||||
```
|
||||
|
||||
6. **Calculate Quality Score**
|
||||
```
|
||||
score = 100
|
||||
score -= (placeholder_instances × 10) # -10 per placeholder
|
||||
score -= (todo_markers × 5) # -5 per TODO/FIXME
|
||||
score -= (example_count < 2) ? 20 : 0 # -20 if < 2 examples
|
||||
score -= (incomplete_examples × 15) # -15 per incomplete example
|
||||
score = max(0, score)
|
||||
```
|
||||
|
||||
7. **Format Output**
|
||||
```
|
||||
Display:
|
||||
- Files checked count
|
||||
- Examples found count
|
||||
- Placeholders detected
|
||||
- Quality score
|
||||
- Specific issues with file/line references
|
||||
- Improvement recommendations
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Validate examples with strict placeholder checking (default)
|
||||
/documentation-validation examples path:.
|
||||
|
||||
# Check only README.md (non-recursive)
|
||||
/documentation-validation examples path:. recursive:false
|
||||
|
||||
# Allow placeholders (lenient mode)
|
||||
/documentation-validation examples path:. no-placeholders:false
|
||||
|
||||
# Check specific file extensions
|
||||
/documentation-validation examples path:. extensions:"md,sh,py"
|
||||
|
||||
# Strict validation of examples directory
|
||||
/documentation-validation examples path:./examples no-placeholders:true recursive:true
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Error: Placeholders detected**
|
||||
```
|
||||
⚠️ WARNING: Placeholder patterns detected in examples
|
||||
|
||||
Placeholders found: <N> instances across <M> files
|
||||
|
||||
README.md:
|
||||
- Line 45: /plugin install YOUR_PLUGIN_NAME
|
||||
^ Should be concrete plugin name
|
||||
- Line 67: API_KEY=your-api-key-here
|
||||
^ Should be removed or use template syntax
|
||||
|
||||
examples/usage.sh:
|
||||
- Line 12: # TODO: Add authentication example
|
||||
^ Complete example or remove TODO
|
||||
|
||||
Remediation:
|
||||
1. Replace "YOUR_PLUGIN_NAME" with actual plugin name
|
||||
2. Use template syntax for user-provided values: ${API_KEY}
|
||||
3. Remove TODO markers - complete examples or remove them
|
||||
4. Provide concrete, copy-pasteable examples
|
||||
|
||||
Acceptable patterns:
|
||||
- Template variables: ${VARIABLE}, {{variable}}
|
||||
- Documentation syntax: <name> in usage descriptions
|
||||
- Generic placeholders in template explanations
|
||||
```
|
||||
|
||||
**Error: Too few examples**
|
||||
```
|
||||
⚠️ WARNING: Insufficient examples in documentation
|
||||
|
||||
Examples found: <N> (minimum recommended: 3)
|
||||
|
||||
README.md contains <N> code examples:
|
||||
- Installation example ✅
|
||||
- Basic usage ❌ Missing
|
||||
- Advanced usage ❌ Missing
|
||||
|
||||
Remediation:
|
||||
Add at least 2-3 concrete usage examples showing:
|
||||
1. Basic usage (most common scenario)
|
||||
2. Common configuration options
|
||||
3. Advanced or specialized use case
|
||||
|
||||
Example structure:
|
||||
```bash
|
||||
# Basic usage
|
||||
/my-plugin action param:value
|
||||
|
||||
# With options
|
||||
/my-plugin action param:value option:true
|
||||
|
||||
# Advanced example
|
||||
/my-plugin complex-action config:custom nested:value
|
||||
```
|
||||
|
||||
Good examples are copy-pasteable and use real values.
|
||||
```
|
||||
|
||||
**Error: Incomplete examples**
|
||||
```
|
||||
⚠️ WARNING: Incomplete or broken examples detected
|
||||
|
||||
Incomplete examples: <N>
|
||||
|
||||
README.md:
|
||||
- Line 34: Code block with syntax error
|
||||
- Line 56: Example missing expected output
|
||||
- Line 78: Example truncated with "..."
|
||||
|
||||
Remediation:
|
||||
1. Ensure all code examples are syntactically valid
|
||||
2. Show expected output or results after examples
|
||||
3. Complete truncated examples (no "..." placeholders)
|
||||
4. Test examples before including in documentation
|
||||
|
||||
Example format:
|
||||
```bash
|
||||
# Command with description
|
||||
/plugin install example-plugin@marketplace
|
||||
|
||||
# Expected output:
|
||||
# ✓ Installing example-plugin@marketplace
|
||||
# ✓ Plugin installed successfully
|
||||
```
|
||||
```
|
||||
|
||||
**Error: Generic dummy values**
|
||||
```
|
||||
⚠️ WARNING: Generic placeholder values detected
|
||||
|
||||
Generic values found:
|
||||
- README.md:45 - "foo", "bar" used as example values
|
||||
- examples/config.json:12 - "sample" as placeholder
|
||||
|
||||
While "foo/bar" are common in documentation, concrete examples
|
||||
are more helpful for users.
|
||||
|
||||
Remediation:
|
||||
Replace generic values with realistic examples:
|
||||
- Instead of "foo", use actual plugin name
|
||||
- Instead of "bar", use real parameter value
|
||||
- Instead of "sample", use concrete example
|
||||
|
||||
Good: /my-plugin process file:README.md
|
||||
Bad: /my-plugin process file:foo.txt
|
||||
```
|
||||
|
||||
### Output Format
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
EXAMPLE QUALITY VALIDATION
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Files Checked: <N>
|
||||
Code Examples Found: <N>
|
||||
|
||||
Example Count by Type:
|
||||
- Command examples: <N> ✅
|
||||
- Configuration examples: <N> ✅
|
||||
- Usage examples: <N> ⚠️ (recommend 3+)
|
||||
|
||||
Placeholder Detection:
|
||||
TODO/FIXME markers: <N> ❌
|
||||
Placeholder patterns: <N> ❌
|
||||
Generic values (foo/bar): <N> ⚠️
|
||||
|
||||
Quality Score: <0-100>/100
|
||||
|
||||
Issues by File:
|
||||
README.md: <N> issues
|
||||
├─ Line 45: YOUR_PLUGIN_NAME (placeholder)
|
||||
├─ Line 67: TODO marker
|
||||
└─ Line 89: Generic "foo" value
|
||||
|
||||
examples/usage.sh: <N> issues
|
||||
└─ Line 12: Incomplete example
|
||||
|
||||
Recommendations:
|
||||
1. Replace <N> placeholder patterns with concrete values [+10 pts]
|
||||
2. Complete or remove <N> TODO markers [+5 pts]
|
||||
3. Add <N> more usage examples [+15 pts]
|
||||
|
||||
Overall: <PASS|WARNINGS|FAIL>
|
||||
```
|
||||
|
||||
### Integration
|
||||
|
||||
This operation is invoked by:
|
||||
- `/documentation-validation examples path:.` (direct)
|
||||
- `/documentation-validation full-docs path:.` (as part of complete validation)
|
||||
- `/validation-orchestrator comprehensive path:.` (via orchestrator)
|
||||
|
||||
Results contribute to documentation quality score:
|
||||
- High-quality examples (90+): +10 points
|
||||
- Some issues (60-89): +5 points
|
||||
- Poor quality (<60): 0 points
|
||||
- Missing examples: -10 points
|
||||
|
||||
### Special Cases
|
||||
|
||||
**Template Documentation**:
|
||||
If the plugin provides templates or scaffolding, some placeholders
|
||||
are acceptable when properly documented as template variables.
|
||||
|
||||
Example:
|
||||
```markdown
|
||||
The generated code includes template variables:
|
||||
- {{PROJECT_NAME}} - Will be replaced with actual project name
|
||||
- {{AUTHOR}} - Will be replaced with author information
|
||||
```
|
||||
|
||||
This is acceptable because the placeholders are documented as
|
||||
intentional template syntax.
|
||||
|
||||
**Request**: $ARGUMENTS
|
||||
Reference in New Issue
Block a user