#!/usr/bin/env python3
"""Generate host-native command skills from commands/*.md (REQ-108).

Claude Code loads the native slash commands in commands/ and .claude/commands/.
Codex and opencode load SKILLS, not Claude command directories, so every
administration command ships a generated `source-command-<name>` skill giving
those hosts a supported, discoverable equivalent. The command file is the only
source of truth: this generator derives each SKILL.md from it and mirrors the
result into every hub skill directory. Never edit the generated skills by hand.

Usage:
  python scripts/generate_command_skills.py          # write all copies
  python scripts/generate_command_skills.py --check  # exit 1 if anything drifted
"""
from __future__ import annotations

import argparse
import re
import sys
from pathlib import Path

MIRROR_DIRS = (
    "skills",
    ".claude/skills",
    ".agents/skills",
    ".opencode/skill",
    "templates/repo-skeleton/.claude/skills",
    "templates/repo-skeleton/.agents/skills",
    "templates/repo-skeleton/.opencode/skill",
)

_FRONTMATTER = re.compile(r"\A---\r?\n(.*?)\r?\n---\r?\n", re.S)


def command_names(root):
    """The administration command names (prd-*), sorted."""
    return sorted(p.stem for p in (Path(root) / "commands").glob("prd-*.md"))


def _parse_command(root, name):
    text = (Path(root) / "commands" / f"{name}.md").read_text(encoding="utf-8-sig")
    match = _FRONTMATTER.match(text)
    if not match:
        raise ValueError(f"commands/{name}.md has no frontmatter")
    fields = {}
    for line in match.group(1).splitlines():
        if ":" in line:
            key, _, value = line.partition(":")
            fields[key.strip()] = value.strip().strip('"')
    return fields, text[match.end():].lstrip("\n")


def build_skill_markdown(root, name):
    """The exact SKILL.md content for one command — deterministic, so the
    parity test can recompute and compare."""
    fields, body = _parse_command(root, name)
    description = fields.get("description", "").rstrip(".")
    argument_hint = fields.get("argument-hint", "")
    lines = [
        "---",
        f"name: source-command-{name}",
        f"description: {description}. Host-native /{name} equivalent for hosts "
        "that load skills instead of Claude command files; trigger on "
        f"\"/{name}\", \"{name}\", or a request to run the {name} operation.",
        "---",
        "",
        f"# /{name} (host-native command skill)",
        "",
        f"Generated from `commands/{name}.md` by `scripts/generate_command_skills.py`"
        " — do not edit by hand; edit the command file and regenerate.",
        "",
        "Treat the user's invocation as this command. Where the instructions use"
        " `$ARGUMENTS`, substitute the arguments the user supplied"
        + (f" (expected: {argument_hint})." if argument_hint else "."),
        "",
        body.rstrip(),
        "",
        "## Staleness",
        "",
        "This adapter owns no records of its own; apply the shared policy in"
        " `.prd_plugin/method/staleness-rules.md` to any records the underlying"
        " operation reads or writes.",
        "",
    ]
    return "\n".join(lines)


def generate(root, check=False):
    root = Path(root)
    drifted = []
    for name in command_names(root):
        content = build_skill_markdown(root, name)
        for mirror in MIRROR_DIRS:
            target = root / mirror / f"source-command-{name}" / "SKILL.md"
            current = target.read_text(encoding="utf-8") if target.is_file() else None
            if current == content:
                continue
            drifted.append(str(target.relative_to(root)))
            if not check:
                target.parent.mkdir(parents=True, exist_ok=True)
                target.write_text(content, encoding="utf-8", newline="\n")
    return drifted


def main(argv=None):
    parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    parser.add_argument("--repo-root", default=".")
    parser.add_argument("--check", action="store_true",
                        help="Report drifted/missing copies without writing; exit 1 if any.")
    args = parser.parse_args(argv)
    drifted = generate(args.repo_root, check=args.check)
    if args.check and drifted:
        print("command-skill drift (regenerate with scripts/generate_command_skills.py):")
        for path in drifted:
            print(f"  {path}")
        return 1
    for path in drifted:
        print(f"wrote {path}")
    if not drifted:
        print("all command skills up to date")
    return 0


if __name__ == "__main__":
    sys.exit(main())
