feat(linkedin-thought-leadership): v1.0.0 — initial open-source import
Build LinkedIn thought leadership with algorithmic understanding, strategic consistency, and AI-assisted content creation. Updated for the January 2026 360Brew algorithm change. 16 agents, 25 commands, 6 skills, 9 hooks, 24 reference docs. Personal data sanitized: voice samples generalized to template, high-engagement posts cleared, region-specific references replaced with placeholders. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7194a37129
commit
39f8b275a6
143 changed files with 32662 additions and 0 deletions
90
plugins/linkedin-thought-leadership/hooks/scripts/compile-hooks.py
Executable file
90
plugins/linkedin-thought-leadership/hooks/scripts/compile-hooks.py
Executable file
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Compile hooks.template.json + prompt .md files into hooks.json.
|
||||
|
||||
Usage:
|
||||
python3 hooks/scripts/compile-hooks.py # Generate hooks.json
|
||||
python3 hooks/scripts/compile-hooks.py --check # Verify hooks.json is up to date
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
HOOKS_DIR = Path(__file__).resolve().parent.parent
|
||||
TEMPLATE = HOOKS_DIR / "hooks.template.json"
|
||||
OUTPUT = HOOKS_DIR / "hooks.json"
|
||||
PROMPTS_DIR = HOOKS_DIR / "prompts"
|
||||
|
||||
|
||||
def load_prompt(filename: str) -> str:
|
||||
"""Load a prompt .md file and return its content as a string."""
|
||||
path = PROMPTS_DIR / filename
|
||||
if not path.exists():
|
||||
print(f"ERROR: Prompt file not found: {path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
content = path.read_text(encoding="utf-8")
|
||||
if not content.strip():
|
||||
print(f"ERROR: Prompt file is empty: {path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return content.rstrip("\n")
|
||||
|
||||
|
||||
def resolve_prompts(obj):
|
||||
"""Recursively walk JSON and replace prompt_file with inline prompt."""
|
||||
if isinstance(obj, dict):
|
||||
if "prompt_file" in obj:
|
||||
if obj.get("type") != "prompt":
|
||||
print(
|
||||
f"ERROR: prompt_file used on non-prompt hook type: {obj.get('type')}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
filename = obj.pop("prompt_file")
|
||||
obj["prompt"] = load_prompt(filename)
|
||||
return {k: resolve_prompts(v) for k, v in obj.items()}
|
||||
if isinstance(obj, list):
|
||||
return [resolve_prompts(item) for item in obj]
|
||||
return obj
|
||||
|
||||
|
||||
def compile_hooks() -> str:
|
||||
"""Read template, resolve prompts, return JSON string."""
|
||||
if not TEMPLATE.exists():
|
||||
print(f"ERROR: Template not found: {TEMPLATE}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
template = json.loads(TEMPLATE.read_text(encoding="utf-8"))
|
||||
resolved = resolve_prompts(template)
|
||||
# Strip any top-level keys except "hooks" — Claude Code requires only "hooks"
|
||||
invalid_keys = [k for k in resolved if k != "hooks"]
|
||||
for k in invalid_keys:
|
||||
print(f"WARNING: Stripping invalid top-level key '{k}' from output", file=sys.stderr)
|
||||
del resolved[k]
|
||||
return json.dumps(resolved, indent=2, ensure_ascii=False) + "\n"
|
||||
|
||||
|
||||
def main():
|
||||
check_mode = "--check" in sys.argv
|
||||
compiled = compile_hooks()
|
||||
|
||||
if check_mode:
|
||||
if not OUTPUT.exists():
|
||||
print(f"ERROR: {OUTPUT} does not exist", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
current = OUTPUT.read_text(encoding="utf-8")
|
||||
if current == compiled:
|
||||
print("OK: hooks.json is up to date")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(
|
||||
"DRIFT DETECTED: hooks.json does not match compiled output.\n"
|
||||
"Run: python3 hooks/scripts/compile-hooks.py",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
OUTPUT.write_text(compiled, encoding="utf-8")
|
||||
print(f"Compiled {OUTPUT.relative_to(HOOKS_DIR.parent)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue