"""위키 생성 CLI (계획서 v4 §5.7, §11.6). python -m wiki_out.run [--program X] [--all] [--limit N] [--dry-run] - 요약(Stage 3) 이 없어도 파서 구조 사실만으로 문서를 만든다(조각이 생기면 재실행 시 채워짐). - programs/packages/concepts 는 사람 교정 보존 병합(merge.py). 로직 조각은 프로그램 문서 안의 섹션. """ from __future__ import annotations import argparse import json import re from config.glossary import load_glossary from config.settings import settings from index.db import connect from .entities import write_entity_wiki from .merge import merge_write from .okf_writer import PIPELINE, _fm, _lst, _now, _q, write_manifest, write_program_wiki def write_concepts() -> int: """도메인 용어 사전을 concepts/ 문서로 출력. 사전 로딩은 config.glossary 로 옮겼다 — 질의 확장(query/expand.py)이 같은 사전을 써야 하고, 파싱 규칙이 두 곳에 있으면 갈라진다. """ glossary = load_glossary() count = 0 for concept, related in glossary.items(): fm = [ "type: concept", f"title: {_q(concept)}", f"description: {_q(concept + ' — 도메인 개념 (질의 확장용)')}", f"tags: {_lst(related[:10])}", f"generated: {{ by: {_q(PIPELINE)}, at: {_q(_now())} }}", ] body = ["## 관련 용어", "", "이 목록은 검색 질의 확장에도 쓰인다 — `/search/logic?q=` 에 아래 어느 말로 물어도 걸린다.", ""] + [f"- {t}" for t in related] + [""] rel = f"concepts/{re.sub(r'[^0-9A-Za-z가-힣_.-]', '_', concept)}.md" merge_write(settings.wiki_dir / rel, _fm(fm) + "\n" + "\n".join(body), code_hash=None, wiki_dir=settings.wiki_dir, doc_rel=rel) count += 1 return count def main() -> None: ap = argparse.ArgumentParser(description="OKF 위키 생성") ap.add_argument("--program", default=None, help="프로그램 하나만") ap.add_argument("--all", action="store_true", help="소스 있는 전체 프로그램") ap.add_argument("--limit", type=int, default=None) ap.add_argument("--no-entities", action="store_true", help="tables/ functions/ tcodes/ 엔티티 페이지를 만들지 않는다") ap.add_argument("--dry-run", action="store_true") args = ap.parse_args() con = connect() try: if args.program: names = [args.program.upper()] elif args.all: rows = con.execute( "SELECT name FROM program WHERE has_source=1 ORDER BY name").fetchall() names = [r["name"] for r in rows] else: ap.error("--program 또는 --all 필요") if args.limit: names = names[: args.limit] if args.dry_run: print(json.dumps({"would_process": len(names)}, ensure_ascii=False)) return totals = {"programs": 0, "chunks": 0, "packages": 0, "failed": 0} for name in names: try: s = write_program_wiki(name, con=con) totals["programs"] += s["programs"] totals["chunks"] += s["chunks"] totals["packages"] += s["packages"] except Exception as e: # noqa: BLE001 totals["failed"] += 1 print(f"[FAIL] {name}: {type(e).__name__}: {e}") totals["concepts"] = write_concepts() if not args.no_entities: # tables/ functions/ tcodes/ — 파서 사실만으로 만들어진다 (수정사항 8번) totals.update(write_entity_wiki(con, settings.wiki_dir)) write_manifest(settings.wiki_dir, con) print(json.dumps(totals, ensure_ascii=False)) finally: con.close() if __name__ == "__main__": main()