#!/bin/bash # LinkedIn Thought Leadership Plugin — Structure Validator # Validates file existence, frontmatter format, and router completeness # Usage: bash scripts/test-runner.sh set -e PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PASS=0 FAIL=0 WARN=0 # Color output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color pass() { echo -e "${GREEN}✓${NC} $1"; PASS=$((PASS + 1)); } fail() { echo -e "${RED}✗${NC} $1"; FAIL=$((FAIL + 1)); } warn() { echo -e "${YELLOW}⚠${NC} $1"; WARN=$((WARN + 1)); } echo "================================================" echo "LinkedIn Thought Leadership Plugin — Structure Validator" echo "Plugin root: $PLUGIN_ROOT" echo "================================================" echo "" # --- Section 1: Core Files --- echo "--- Core Files ---" for f in ".claude-plugin/plugin.json" "CLAUDE.md" "CHANGELOG.md" "docs/DEVELOPMENT-LOG.md" "README.md" "config/REMEMBER.template.md"; do if [ -f "$PLUGIN_ROOT/$f" ]; then pass "$f exists" else fail "$f MISSING" fi done echo "" # --- Section 2: Agent Files --- echo "--- Agent Files ---" EXPECTED_AGENTS=( "engagement-coach" "content-optimizer" "strategy-advisor" "analytics-interpreter" "content-planner" "content-tracker" "performance-reporter" "network-builder" "content-repurposer" "comment-strategist" "trend-spotter" "voice-trainer" "differentiation-checker" "post-feedback-monitor" "personalization-scorer" ) for agent in "${EXPECTED_AGENTS[@]}"; do f="agents/${agent}.md" if [ -f "$PLUGIN_ROOT/$f" ]; then # Check for YAML frontmatter if head -1 "$PLUGIN_ROOT/$f" | grep -q "^---"; then # Check for required fields if grep -q "^name:" "$PLUGIN_ROOT/$f" && grep -q "^model:" "$PLUGIN_ROOT/$f" && grep -q "^color:" "$PLUGIN_ROOT/$f"; then pass "$f (frontmatter OK)" else warn "$f (missing frontmatter fields)" fi else fail "$f (no YAML frontmatter)" fi else fail "$f MISSING" fi done echo "" # --- Section 3: Command Files --- echo "--- Command Files ---" EXPECTED_COMMANDS=( "linkedin" "linkedin:setup" "linkedin:post" "linkedin:quick" "linkedin:templates" "linkedin:pipeline" "linkedin:batch" "linkedin:analyze" "linkedin:audit" "linkedin:import" "linkedin:report" "linkedin:strategy" "linkedin:authority" "linkedin:competitive" "linkedin:monetize" "linkedin:profile" "linkedin:collab" "linkedin:speaking" "linkedin:multiplatform" "linkedin:ab-test" ) for cmd in "${EXPECTED_COMMANDS[@]}"; do f="commands/${cmd}.md" if [ -f "$PLUGIN_ROOT/$f" ]; then if head -1 "$PLUGIN_ROOT/$f" | grep -q "^---"; then if grep -q "^name:" "$PLUGIN_ROOT/$f" && grep -q "^description:" "$PLUGIN_ROOT/$f"; then pass "$f (frontmatter OK)" else warn "$f (missing frontmatter fields)" fi else fail "$f (no YAML frontmatter)" fi else fail "$f MISSING" fi done echo "" # --- Section 4: Reference Files --- echo "--- Reference Files ---" EXPECTED_REFS=( "engagement-frameworks" "collaborations-guide" "algorithm-signals-reference" "linkedin-growth-playbook-2025-2026" "opportunity-generation" "linkedin-formats" "ai-content-framework" "articles-strategy-guide" "first-comment-strategy" "poll-strategy-guide" "newsletter-strategy-guide" "linkedin-visual-style" "growth-roadmaps" "thought-leadership-angles" "low-frequency-posting-strategy" "url-processing-templates" "linkedin-monetization-strategies" "troubleshooting-guide" "glossary" "ab-testing-framework" ) for ref in "${EXPECTED_REFS[@]}"; do f="references/${ref}.md" if [ -f "$PLUGIN_ROOT/$f" ]; then pass "$f exists" else fail "$f MISSING" fi done echo "" # --- Section 5: Skill Files --- echo "--- Skill Files ---" for skill in "linkedin-thought-leadership" "linkedin-content-creation" "linkedin-analytics" "linkedin-strategy" "linkedin-networking" "linkedin-voice"; do f="skills/${skill}.md" if [ -f "$PLUGIN_ROOT/$f" ]; then pass "$f exists" else fail "$f MISSING" fi done echo "" # --- Section 6: Hook Configuration --- echo "--- Hook Configuration ---" HOOKS_FILE="$PLUGIN_ROOT/hooks/hooks.json" if [ -f "$HOOKS_FILE" ]; then pass "hooks/hooks.json exists" # Validate JSON if python3 -c "import json; json.load(open('$HOOKS_FILE'))" 2>/dev/null; then pass "hooks.json is valid JSON" else fail "hooks.json is INVALID JSON" fi else fail "hooks/hooks.json MISSING" fi # Check hook prompt files for prompt in "content-quality-gate" "voice-guardian" "state-update-reminder" "post-creation-automation"; do f="hooks/prompts/${prompt}.md" if [ -f "$PLUGIN_ROOT/$f" ]; then pass "$f exists" else fail "$f MISSING" fi done echo "" # --- Section 7: Plugin.json Validation --- echo "--- Plugin.json Validation ---" PLUGIN_JSON="$PLUGIN_ROOT/.claude-plugin/plugin.json" if python3 -c " import json, sys with open('$PLUGIN_JSON') as f: data = json.load(f) required = ['name', 'version', 'auto_discover', 'description'] for field in required: if field not in data: print(f'Missing field: {field}') sys.exit(1) if data.get('auto_discover') != True: print('auto_discover is not true') sys.exit(1) print(f'Version: {data[\"version\"]}') " 2>/dev/null; then pass "plugin.json structure valid" else fail "plugin.json structure invalid" fi echo "" # --- Section 8: Router Completeness --- echo "--- Router Completeness ---" ROUTER="$PLUGIN_ROOT/commands/linkedin.md" if [ -f "$ROUTER" ]; then # Check that key commands are mentioned in router for cmd in "linkedin:setup" "linkedin:post" "linkedin:quick" "linkedin:report" "linkedin:import" "linkedin:ab-test" "linkedin:collab" "linkedin:pipeline" "linkedin:batch"; do if grep -q "$cmd" "$ROUTER"; then pass "Router references $cmd" else fail "Router MISSING reference to $cmd" fi done # Check that key agents are mentioned for agent in "engagement-coach" "content-optimizer" "network-builder" "post-feedback-monitor" "personalization-scorer"; do if grep -q "$agent" "$ROUTER"; then pass "Router references $agent" else fail "Router MISSING reference to $agent" fi done else fail "Router file MISSING" fi echo "" # --- Section 9: Analytics Structure --- echo "--- Analytics Structure ---" for d in "scripts/analytics/src" "assets/analytics"; do if [ -d "$PLUGIN_ROOT/$d" ]; then pass "$d/ directory exists" else fail "$d/ directory MISSING" fi done if [ -f "$PLUGIN_ROOT/scripts/analytics/src/cli.ts" ]; then pass "scripts/analytics/src/cli.ts exists" else fail "scripts/analytics/src/cli.ts MISSING" fi echo "" # --- Summary --- echo "================================================" echo "RESULTS" echo "================================================" echo -e "${GREEN}Passed: $PASS${NC}" echo -e "${RED}Failed: $FAIL${NC}" echo -e "${YELLOW}Warnings: $WARN${NC}" echo "" if [ $FAIL -eq 0 ]; then echo -e "${GREEN}All structural checks passed!${NC}" exit 0 else echo -e "${RED}$FAIL check(s) failed. Review above.${NC}" exit 1 fi