#!/usr/bin/env python3 """Initialize a project from the skill's knowledge-wiki boilerplate.""" from __future__ import annotations import argparse import filecmp import re import shutil from datetime import date from pathlib import Path INITIALIZED_LOG_RE = re.compile( r"# Wiki Update Log\n\n" r"## \d{4}-\d{2}-\d{2}\n\n" r"\* \*\*Initialization\*\*: Wissensdatenbank nach OKF v0\.2 initialisiert\.\n?" ) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Copy the agent-managed wiki boilerplate into a project." ) parser.add_argument("project_root", type=Path, help="Target project directory") parser.add_argument( "--force", action="store_true", help="Overwrite differing boilerplate files in the target project", ) return parser.parse_args() def template_files(template_root: Path) -> list[Path]: return sorted(path for path in template_root.rglob("*") if path.is_file()) def matches_template_or_initialized_log( source: Path, target: Path, relative: Path ) -> bool: if filecmp.cmp(source, target, shallow=False): return True return ( relative == Path("wiki/log.md") and INITIALIZED_LOG_RE.fullmatch(target.read_text(encoding="utf-8")) is not None ) def main() -> int: args = parse_args() project_root = args.project_root.expanduser().resolve() template_root = Path(__file__).resolve().parent.parent / "assets" / "boilerplate" if not template_root.is_dir(): raise SystemExit(f"Boilerplate not found: {template_root}") files = template_files(template_root) conflicts = [] for source in files: relative = source.relative_to(template_root) target = project_root / relative if target.exists() and not matches_template_or_initialized_log( source, target, relative ): conflicts.append(relative) if conflicts and not args.force: rendered = "\n".join(f" - {path}" for path in conflicts) raise SystemExit( "Refusing to overwrite differing files:\n" f"{rendered}\nRun again with --force only when replacement is intended." ) created = 0 unchanged = 0 overwritten = 0 for source in files: relative = source.relative_to(template_root) target = project_root / relative target.parent.mkdir(parents=True, exist_ok=True) if target.exists() and matches_template_or_initialized_log( source, target, relative ): unchanged += 1 continue existed = target.exists() shutil.copy2(source, target) overwritten += int(existed) created += int(not existed) log_path = project_root / "wiki" / "log.md" if log_path.exists() and log_path.read_text(encoding="utf-8").strip() == "# Wiki Update Log": log_path.write_text( "# Wiki Update Log\n\n" f"## {date.today().isoformat()}\n\n" "* **Initialization**: Wissensdatenbank nach OKF v0.2 initialisiert.\n", encoding="utf-8", ) print( f"Initialized {project_root}: " f"{created} created, {overwritten} overwritten, {unchanged} unchanged." ) print( "\nKurzanleitung:\n" "- Originalquellen unter `sources/` ablegen; Entscheidungen verwaltet " "der Agent unter `sources/decisions/`.\n" "- Codex bitten, Quellen zu integrieren, das Wiki zu befragen oder " "Entscheidungen festzuhalten.\n" "- Der Skill pflegt `wiki/`, Provenienz, Index, Log und Quellenstatus." ) return 0 if __name__ == "__main__": raise SystemExit(main())