#!/usr/bin/env python3
"""AG-19 bootstrap — version-control the runtime auto-memory store, backed off-machine.

The Claude Code harness keeps a machine-local, private auto-memory per project at
``~/.claude/projects/<slug>/memory/`` (the private store of AGENTS AG-05/AG-08, distinct
from AG-17's in-repo ``docs/memory/``). AG-19 requires that store to be a git repo pushed
to a **private** Codeberg remote ``<project>-memory`` so the notes survive disk loss and
stay auditable across machines.

SAFETY (the whole reason this script exists): git is scoped to the ``memory/`` directory
ITSELF — never its parent, which holds the session ``*.jsonl`` transcripts that can contain
secrets. An allowlist ``.gitignore`` (``*`` then ``!*.md``, ``!.gitignore``) guarantees only
memory-note markdown is ever tracked even if the harness later drops other files in.

Idempotent: safe to re-run after a fresh machine / clone. It (re)writes the .gitignore,
inits the repo if absent, ensures the private remote exists (creates it via ``tea`` if not),
commits any pending note changes with the AG-03 trailers, and pushes.

Usage:
  scripts/init_memory_vcs.py                       # this project (cwd → slug)
  scripts/init_memory_vcs.py --project mgf-hrb     # another sibling by name
  scripts/init_memory_vcs.py --check               # report state, make no changes
  scripts/init_memory_vcs.py --no-remote           # local git only (skip Codeberg)

Env knobs: MEMORY_VCS_LOGIN (tea login, default "codeberg"),
           MEMORY_VCS_ORG   (Codeberg owner, default "magogi-admin").
"""

from __future__ import annotations

import argparse
import os
import subprocess
import sys
from pathlib import Path

GITIGNORE = (
    "# Claude Code memory store — git-tracked per MGF standard (AG-19).\n"
    "# SAFETY: only memory note markdown is tracked. Everything else (esp. the\n"
    "# session *.jsonl transcripts in the parent dir, which can contain secrets)\n"
    "# stays out. Allowlist style: ignore all, then re-include .md + this file.\n"
    "*\n"
    "!*.md\n"
    "!.gitignore\n"
)
SIGNOFF = "Signed-off-by: Bassam Alsanie <bassam@magogi.org>"
COAUTHOR = "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"


def _slug(project_dir: Path) -> str:
    """The harness encodes a project's memory path by replacing '/' with '-'."""
    return str(project_dir.resolve()).replace("/", "-")


def _memory_dir(slug: str) -> Path:
    return Path.home() / ".claude" / "projects" / slug / "memory"


def _git(mem: Path, *args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        ["git", "-C", str(mem), *args], text=True, capture_output=True, check=check
    )


def main(argv: list[str] | None = None) -> int:
    ap = argparse.ArgumentParser(description="AG-19 memory-store version-control bootstrap.")
    ap.add_argument("--project", help="sibling name under ~/PycharmProjects (default: cwd)")
    ap.add_argument("--check", action="store_true", help="report state; make no changes")
    ap.add_argument("--no-remote", action="store_true", help="local git only; skip Codeberg")
    args = ap.parse_args(argv)

    if args.project:
        project_dir = Path.home() / "PycharmProjects" / args.project
    else:
        project_dir = Path.cwd()
    project = project_dir.name
    mem = _memory_dir(_slug(project_dir))

    if not mem.is_dir():
        print(f"✗ no memory store at {mem} — has an agent ever written memory for {project}?")
        return 1

    is_repo = (mem / ".git").is_dir()
    if args.check:
        print(f"memory store: {mem}")
        print(f"  git repo:   {'yes' if is_repo else 'NO'}")
        if is_repo:
            remote = _git(mem, "remote", "get-url", "origin", check=False)
            print(f"  remote:     {remote.stdout.strip() or '(none)'}")
            status = _git(mem, "status", "--porcelain", check=False)
            print(f"  clean:      {'yes' if not status.stdout.strip() else 'NO (uncommitted)'}")
        gi = mem / ".gitignore"
        print(f"  .gitignore: {'allowlist present' if gi.is_file() else 'MISSING'}")
        return 0

    # 1. Always (re)assert the safety allowlist before anything is staged.
    (mem / ".gitignore").write_text(GITIGNORE, encoding="utf-8")

    # 2. Init if needed.
    if not is_repo:
        _git(mem, "init", "-q")
        _git(mem, "config", "user.name", "Bassam Alsanie")
        _git(mem, "config", "user.email", "bassam@magogi.org")
        _git(mem, "branch", "-M", "main")
        print(f"✓ initialised git in {mem}")

    # 3. Ensure the private remote (best-effort create via tea).
    if not args.no_remote:
        login = os.environ.get("MEMORY_VCS_LOGIN", "codeberg")
        org = os.environ.get("MEMORY_VCS_ORG", "magogi-admin")
        repo = f"{project}-memory"
        has_origin = _git(mem, "remote", "get-url", "origin", check=False).returncode == 0
        if not has_origin:
            # Create the repo if it does not exist yet (ignore "already exists").
            subprocess.run(
                ["tea", "repos", "create", "--name", repo, "--private", "--login", login],
                text=True, capture_output=True, check=False,
            )
            url = f"ssh://git@codeberg.org/{org}/{repo}.git"
            _git(mem, "remote", "add", "origin", url)
            print(f"✓ remote origin → {url}")

    # 4. Commit any pending note changes (AG-03 trailers).
    _git(mem, "add", "-A")
    if _git(mem, "status", "--porcelain", check=False).stdout.strip():
        msg = f"chore(memory): sync {project} memory store (AG-19)\n\n{SIGNOFF}\n{COAUTHOR}\n"
        _git(mem, "commit", "-q", "-m", msg)
        print("✓ committed pending memory changes")
    else:
        print("· nothing to commit (memory store already current)")

    # 5. Push.
    if not args.no_remote and _git(mem, "remote", check=False).stdout.strip():
        push = _git(mem, "push", "-u", "origin", "main", check=False)
        if push.returncode == 0:
            print("✓ pushed to origin/main")
        else:
            print(f"✗ push failed:\n{push.stderr.strip()}")
            return 1
    return 0


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