274 lines
8.7 KiB
Python
274 lines
8.7 KiB
Python
#!/usr/bin/env python3
|
||
"""Consolidate Nexus docs: move batches to docs/archive/ and generate merged volumes."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import shutil
|
||
from datetime import date
|
||
from pathlib import Path
|
||
|
||
REPO = Path(__file__).resolve().parents[1]
|
||
DOCS = REPO / "docs"
|
||
ARCHIVE = DOCS / "archive"
|
||
TODAY = date.today().isoformat()
|
||
|
||
|
||
def ensure_dirs() -> None:
|
||
for sub in (
|
||
"audits/2026-06-04-line-walk",
|
||
"audits/2026-06-04-delta",
|
||
"audits/2026-06-04-phase2",
|
||
"audits/2026-06-04-frontend-14p",
|
||
"memory/raw",
|
||
"handoff",
|
||
"project",
|
||
"reports/2026-05-reviews",
|
||
"reports/2026-05-steps",
|
||
):
|
||
(ARCHIVE / sub).mkdir(parents=True, exist_ok=True)
|
||
|
||
|
||
def extract_summary(text: str, max_lines: int = 12) -> str:
|
||
lines: list[str] = []
|
||
for line in text.splitlines():
|
||
s = line.strip()
|
||
if not s:
|
||
continue
|
||
if s.startswith("#"):
|
||
lines.append(s)
|
||
elif any(k in s for k in ("Closure", "DoD", "P0", "P1", "FINDING", "结论", "摘要")):
|
||
lines.append(s[:200])
|
||
if len(lines) >= max_lines:
|
||
break
|
||
return "\n".join(lines) if lines else (text.splitlines()[0][:200] if text.splitlines() else "(empty)")
|
||
|
||
|
||
def extract_closure_rows(text: str) -> list[str]:
|
||
rows: list[str] = []
|
||
in_table = False
|
||
for line in text.splitlines():
|
||
if "|" in line and ("Closure" in line or "H " in line or "FINDING" in line):
|
||
in_table = True
|
||
if in_table and line.strip().startswith("|") and "---" not in line:
|
||
rows.append(line.strip())
|
||
if in_table and not line.strip() and rows:
|
||
break
|
||
return rows[:80]
|
||
|
||
|
||
def merge_glob(
|
||
pattern: str,
|
||
out_name: str,
|
||
title: str,
|
||
archive_subdir: str,
|
||
) -> None:
|
||
base = DOCS / "reports"
|
||
files = sorted(base.glob(pattern))
|
||
if not files:
|
||
print(f" skip merge (no files): {pattern}")
|
||
return
|
||
|
||
out_path = ARCHIVE / "audits" / out_name
|
||
parts = [
|
||
f"# {title}",
|
||
"",
|
||
f"> 生成日期: {TODAY} · 源文件 {len(files)} 篇 · 原文 `docs/archive/{archive_subdir}/`",
|
||
"",
|
||
"## 批次索引",
|
||
"",
|
||
]
|
||
archive_dir = ARCHIVE / archive_subdir
|
||
archive_dir.mkdir(parents=True, exist_ok=True)
|
||
|
||
all_closure: list[str] = []
|
||
for f in files:
|
||
dest = archive_dir / f.name
|
||
if f.resolve() != dest.resolve():
|
||
if dest.exists():
|
||
dest.unlink()
|
||
shutil.move(str(f), str(dest))
|
||
text = dest.read_text(encoding="utf-8", errors="replace")
|
||
parts.append(f"### `{f.name}`")
|
||
parts.append("")
|
||
parts.append(extract_summary(text))
|
||
parts.append("")
|
||
all_closure.extend(extract_closure_rows(text))
|
||
|
||
if all_closure:
|
||
parts.extend(["## Closure 摘录(合并)", ""])
|
||
seen: set[str] = set()
|
||
for row in all_closure:
|
||
if row not in seen:
|
||
seen.add(row)
|
||
parts.append(row)
|
||
parts.append("")
|
||
|
||
out_path.write_text("\n".join(parts), encoding="utf-8")
|
||
print(f" merged {len(files)} → {out_path.relative_to(REPO)}")
|
||
|
||
|
||
def merge_memory() -> None:
|
||
mem_dir = DOCS / "memory"
|
||
if not mem_dir.is_dir():
|
||
return
|
||
files = sorted(p for p in mem_dir.glob("*.md") if p.name != "MEMORY.md")
|
||
if not files:
|
||
return
|
||
raw_dir = ARCHIVE / "memory/raw"
|
||
raw_dir.mkdir(parents=True, exist_ok=True)
|
||
parts = [
|
||
"# Agent Memory 合并卷(2026-05 迁移)",
|
||
"",
|
||
f"> 生成: {TODAY} · 原 docs/memory/ {len(files)} 篇",
|
||
"",
|
||
"请以 [nexus-functional-development-guide.md](../project/nexus-functional-development-guide.md) 为准。",
|
||
"",
|
||
]
|
||
for f in files:
|
||
text = f.read_text(encoding="utf-8", errors="replace")
|
||
dest = raw_dir / f.name
|
||
if f.resolve() != dest.resolve():
|
||
shutil.move(str(f), str(dest))
|
||
parts.extend([f"## {f.stem}", "", extract_summary(text, 8), ""])
|
||
out = ARCHIVE / "memory/agentmemory-2026-05-merged.md"
|
||
out.write_text("\n".join(parts), encoding="utf-8")
|
||
(mem_dir / "MEMORY.md").write_text(
|
||
"# 项目记忆(已归档)\n\n"
|
||
"见 [agentmemory-2026-05-merged.md](../archive/memory/agentmemory-2026-05-merged.md)\n"
|
||
"与 [功能开发指南](../project/nexus-functional-development-guide.md)。\n",
|
||
encoding="utf-8",
|
||
)
|
||
print(f" memory merged → {out.relative_to(REPO)}")
|
||
|
||
|
||
def archive_handoffs() -> None:
|
||
for name in ("handoff-2026-05-31.md", "handoff-2026-06-01.md"):
|
||
src = DOCS / name
|
||
if src.is_file():
|
||
shutil.move(str(src), str(ARCHIVE / "handoff" / name))
|
||
for f in sorted((DOCS / "project").glob("AI-HANDOFF-2026-05*.md")):
|
||
shutil.move(str(f), str(ARCHIVE / "handoff" / f.name))
|
||
old = DOCS / "project" / "AI-HANDOFF-2026-06-01-files.md"
|
||
if old.is_file():
|
||
shutil.move(str(old), str(ARCHIVE / "handoff" / old.name))
|
||
print(" handoffs archived")
|
||
|
||
|
||
def archive_nexus_full_site_features() -> None:
|
||
src = DOCS / "project" / "nexus-full-site-features.md"
|
||
if not src.is_file():
|
||
return
|
||
shutil.move(str(src), str(ARCHIVE / "project" / src.name))
|
||
(DOCS / "project" / "nexus-full-site-features.md").write_text(
|
||
"# 全站功能清单(已 supersede)\n\n"
|
||
"请以 **[nexus-functional-development-guide.md](nexus-functional-development-guide.md)** 为准。\n\n"
|
||
"历史 HTML 版: [archive](../archive/project/nexus-full-site-features.md)\n",
|
||
encoding="utf-8",
|
||
)
|
||
print(" nexus-full-site-features → archive + stub")
|
||
|
||
|
||
def archive_old_line_walk_standard() -> None:
|
||
v1 = DOCS / "project" / "line-walk-audit-standard.md"
|
||
if v1.is_file():
|
||
shutil.move(str(v1), str(ARCHIVE / "project" / v1.name))
|
||
print(" line-walk v1 archived")
|
||
|
||
|
||
def archive_phase2_reviews() -> None:
|
||
dest = ARCHIVE / "reports/2026-05-reviews"
|
||
patterns = (
|
||
"review-*-2026-05-21.md",
|
||
"signoff-2026-05-21.md",
|
||
"session-log-2026-05-21.md",
|
||
"2026-05-23-full-audit-report.md",
|
||
"audit-full-vs-phase2-reconciliation.md",
|
||
"audit-phase-2*.md",
|
||
"audit-phase-3*.md",
|
||
"implementation-progress-2026-05-21.md",
|
||
)
|
||
n = 0
|
||
reports = DOCS / "reports"
|
||
for pat in patterns:
|
||
for g in reports.glob(pat):
|
||
if g.is_file():
|
||
t = dest / g.name
|
||
if t.exists():
|
||
t.unlink()
|
||
shutil.move(str(g), str(t))
|
||
n += 1
|
||
step_dir = ARCHIVE / "reports/2026-05-steps"
|
||
for f in reports.glob("step-*"):
|
||
if f.is_dir():
|
||
target = step_dir / f.name
|
||
if target.exists():
|
||
shutil.rmtree(target)
|
||
shutil.move(str(f), str(target))
|
||
n += 1
|
||
if n:
|
||
print(f" archived {n} legacy review/step items")
|
||
|
||
|
||
def write_archive_readme() -> None:
|
||
(ARCHIVE / "README.md").write_text(
|
||
f"""# Nexus 文档归档区
|
||
|
||
> 更新: {TODAY}
|
||
|
||
历史审计、旧 handoff、agentmemory。**日常请先读** [docs/README.md](../README.md)。
|
||
|
||
## 合并卷
|
||
|
||
| 文件 | 内容 |
|
||
|------|------|
|
||
| [audits/2026-06-04-line-walk-merged.md](audits/2026-06-04-line-walk-merged.md) | Line-walk batch |
|
||
| [audits/2026-06-04-delta-merged.md](audits/2026-06-04-delta-merged.md) | Delta D1–D8 |
|
||
| [audits/2026-06-04-phase2-merged.md](audits/2026-06-04-phase2-merged.md) | Phase2 P2 |
|
||
| [audits/2026-06-04-frontend-14p-merged.md](audits/2026-06-04-frontend-14p-merged.md) | 前端 14p |
|
||
| [memory/agentmemory-2026-05-merged.md](memory/agentmemory-2026-05-merged.md) | 旧 memory |
|
||
|
||
原文在对应子目录;changelog 不归档。
|
||
""",
|
||
encoding="utf-8",
|
||
)
|
||
|
||
|
||
def main() -> None:
|
||
print("=== Nexus docs consolidation ===")
|
||
ensure_dirs()
|
||
merge_glob(
|
||
"audit-bug-line-walk-2026-06-04-batch*.md",
|
||
"2026-06-04-line-walk-merged.md",
|
||
"Bug Scan Line-Walk 合并卷",
|
||
"audits/2026-06-04-line-walk",
|
||
)
|
||
merge_glob(
|
||
"audit-delta-2026-06-04-waveD*.md",
|
||
"2026-06-04-delta-merged.md",
|
||
"Delta D1–D8 合并卷",
|
||
"audits/2026-06-04-delta",
|
||
)
|
||
merge_glob(
|
||
"audit-phase2-2026-06-04-waveP2-*.md",
|
||
"2026-06-04-phase2-merged.md",
|
||
"Phase2 P2 合并卷",
|
||
"audits/2026-06-04-phase2",
|
||
)
|
||
merge_glob(
|
||
"audit-frontend-14p-2026-06-04-batch*.md",
|
||
"2026-06-04-frontend-14p-merged.md",
|
||
"前端 14p batch 合并卷",
|
||
"audits/2026-06-04-frontend-14p",
|
||
)
|
||
merge_memory()
|
||
archive_handoffs()
|
||
archive_nexus_full_site_features()
|
||
archive_old_line_walk_standard()
|
||
archive_phase2_reviews()
|
||
write_archive_readme()
|
||
print("=== done ===")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|