ktg-plugin-marketplace/plugins/linkedin-studio/scripts/test-runner.sh
Kjell Tore Guttormsen b6bb61246b refactor(linkedin)!: rename plugin linkedin-thought-leadership → linkedin-studio (v3.0.0)
BREAKING CHANGE: the marketplace slug, the agent namespace
(linkedin-studio:<agent>), and the runtime state-file path
(~/.claude/linkedin-studio.local.md) all change. Reinstall required;
existing state migrated in place (post metrics, streak, history preserved).
The /linkedin:* commands are unchanged — the command namespace is set
per-command in frontmatter and was always independent of the plugin slug.
Functionality is byte-identical to v2.4.0; this release is pure identity.

- dir + manifests: plugins/linkedin-studio + plugin.json + root marketplace.json
- agent namespace updated in commands/newsletter.md (only functional invoker)
- state path updated in 4 hook scripts + topic-rotation prompt + state template
- catch-all skill dir renamed skills/linkedin-studio (5 functional skills unchanged)
- docs + version bump to 3.0.0 across README badge, CHANGELOG, root README/CLAUDE.md
- historical records (CHANGELOG past entries, docs/ build artifacts,
  config-audit v5.0.0 snapshots) intentionally retain the old slug

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 11:32:02 +02:00

258 lines
6.9 KiB
Bash
Executable file

#!/bin/bash
# LinkedIn Studio 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 Studio 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" "network-builder" "content-repurposer" "trend-spotter"
"voice-trainer" "differentiation-checker" "post-feedback-monitor" "video-scripter"
"fact-checker" "persona-reviewer"
)
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-studio" "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