#!/usr/bin/env python3
"""Refresh the kit report data + pages from CANONICAL, then rebuild the suite.
Wired into ship-kit.sh so the god/orchestration/scar pages are NEVER static after a kit change
(Wolf 2026-07-18: "they are always static"). Fast path only (no macB ssh) — the drift page uses the
standing 15-min drift watch's drift.json. Run: python3 _refresh.py"""
import os, json, re, time, subprocess, sys
from datetime import datetime

HERE = os.path.dirname(os.path.abspath(__file__)); os.chdir(HERE)
SRC = "/Users/wolf/Projects/Kit/think-like-fable"

# 1) files.json — every canonical file with its first meaningful header line
inv = []
for root, dirs, files in os.walk(SRC):
    dirs[:] = [d for d in dirs if d not in ("__pycache__", ".git")]
    for f in files:
        if f == ".DS_Store" or f.endswith(".pyc"):
            continue
        p = os.path.relpath(os.path.join(root, f), SRC)
        st = os.stat(os.path.join(root, f))
        head = ""
        try:
            with open(os.path.join(root, f), errors="ignore") as fh:
                for ln in fh:
                    s = ln.strip().lstrip("#!/").lstrip("# ").strip('" ').rstrip()
                    if s and not s.startswith(("---", "===", "import", "set -", "usr/bin", "env ", "bin/")) and len(s) > 10:
                        head = s[:180]; break
        except Exception:
            pass
        inv.append({"path": p, "dir": os.path.dirname(p) or ".", "size": st.st_size,
                    "mtime": time.strftime("%Y-%m-%d %H:%M", time.localtime(st.st_mtime)), "head": head})
json.dump(inv, open("files.json", "w"), indent=0)

# 2) canonical report metadata — commands/principles are source data, not dashboard copy.
# A skill is a command iff canonical has skills/<dir>/SKILL.md.  GOD-KIT ONLY detection is the
# exact frontmatter description prefix; similar prose (for example "GOD-KIT.") is not silently
# promoted into that class.
try:
    import yaml
    commands = []
    skills_dir = os.path.join(SRC, "skills")
    for entry in sorted(os.listdir(skills_dir)):
        skill_path = os.path.join(skills_dir, entry, "SKILL.md")
        if not os.path.isfile(skill_path):
            continue
        raw = open(skill_path).read()
        frontmatter = re.match(r"\A---\s*\n(.*?)\n---\s*(?:\n|\Z)", raw, re.S)
        if not frontmatter:
            raise ValueError(f"missing YAML frontmatter: skills/{entry}/SKILL.md")
        meta = yaml.safe_load(frontmatter.group(1)) or {}
        name = str(meta.get("name", "")).strip()
        description = " ".join(str(meta.get("description", "")).split())
        if not name or not description:
            raise ValueError(f"name/description missing: skills/{entry}/SKILL.md")
        display = re.sub(r"^GOD-KIT(?: ONLY)?\.\s*", "", description)
        gist = re.split(r"(?<=[.!?])\s+(?=[A-Z])", display, maxsplit=1)[0].strip()
        commands.append({"name": name, "description": description, "gist": gist,
                         "god_only": description.startswith("GOD-KIT ONLY"),
                         "source": f"skills/{entry}/SKILL.md"})
    principles = yaml.safe_load(open(f"{SRC}/kernel/principles.yaml")) or {}
    now = datetime.now().astimezone()
    report_data = {
        "generated_date": f"{now:%B} {now.day}, {now.year}",
        "generated_at": now.isoformat(timespec="seconds"),
        "principle_count": len(principles.get("principles", [])),
        "commands": commands,
    }
    json.dump(report_data, open("_kit-data.json", "w"), indent=2)
except Exception as e:
    sys.exit(f"REFRESH ABORTED — canonical command/principle extraction failed; reports NOT rebuilt:\n{e}")

# 3) scars.json — every lesson, mechanically extracted from kernel/lessons.yaml (zero paraphrase)
try:
    d = yaml.safe_load(open(f"{SRC}/kernel/lessons.yaml"))
    lessons = d.get("lessons", d.get("scars", []))
    raw = open(f"{SRC}/kernel/lessons.yaml").read().splitlines()
    comments = {}
    for ln in raw:
        s = ln.strip()
        if s.startswith("- id: L") and "#" in ln:
            comments[s.split()[2]] = ln.split("#", 1)[1].strip()
    out = []
    for l in lessons:
        lid = l["id"]
        out.append({"id": lid, "num": int(re.match(r"L(\d+)", lid).group(1)),
                    "name": lid.split("-", 1)[1].replace("-", " ").lower() if "-" in lid else lid,
                    "note": comments.get(lid, ""), "incident": " ".join(str(l.get("incident", "")).split()),
                    "rules": [" ".join(r.split()) for r in l.get("rules", [])], "ties": l.get("ties_to", [])})
    json.dump(out, open("scars.json", "w"), indent=0)
    scar_n = len(out)
except Exception as e:
    scar_n = f"scars.json SKIPPED ({e})"

# 4) orchestration edition file list (from the v2 zip)
z = "/Users/wolf/Projects/Kit/think-like-fable-v2.zip"
if os.path.exists(z):
    names = subprocess.run(["unzip", "-Z1", z], capture_output=True, text=True).stdout.splitlines()
    orch = sorted(n[len("think-like-fable/"):] for n in names
                  if n.startswith("think-like-fable/") and not n.endswith("/") and "__pycache__" not in n)
    open("_orch-files.txt", "w").write("\n".join(orch) + "\n")

# 5) rebuild the suite — fail LOUD, never print "rebuilt" over stale/broken reports (the silent-fail trap)
import ast
try:
    ast.parse(open("_build.py").read())          # catch a syntax error BEFORE running (f-string-heavy file)
except SyntaxError as e:
    sys.exit(f"REFRESH ABORTED — _build.py has a syntax error; reports NOT rebuilt:\n{e}")
r = subprocess.run([sys.executable, "_build.py"], capture_output=True, text=True)
if r.returncode != 0:                            # a build crash must NOT read as success
    sys.exit(f"REFRESH FAILED — _build.py crashed (exit {r.returncode}); reports are STALE:\n{r.stderr[-800:]}")
print(f"refreshed: {len(inv)} files, {scar_n} scars, {len(commands)} commands → rebuilt suite")
print("  " + (r.stdout.strip().splitlines()[-1] if r.stdout.strip() else r.stderr.strip()[-200:]))
