linkedin-studio/hooks/scripts/compile-hooks.py
Kjell Tore Guttormsen 4dc868ea62 feat(ultraplan-local): v1.6.0 — /ultraresearch-local deep research command
Add /ultraresearch-local for structured research combining local codebase
analysis with external knowledge via parallel agent swarms. Produces research
briefs with triangulation, confidence ratings, and source quality assessment.

New command: /ultraresearch-local with modes --quick, --local, --external, --fg.
New agents: research-orchestrator (opus), docs-researcher, community-researcher,
security-researcher, contrarian-researcher, gemini-bridge (all sonnet).
New template: research-brief-template.md.

Integration: --research flag in /ultraplan-local accepts pre-built research
briefs (up to 3), enriches the interview and exploration phases. Planning
orchestrator cross-references brief findings during synthesis.

Design principle: Context Engineering — right information to right agent at
right time. Research briefs are structured artifacts in the pipeline:
ultraresearch → brief → ultraplan --research → plan → ultraexecute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 08:58:35 +02:00

90 lines
3 KiB
Python
Executable file

#!/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()