# -*- coding: utf-8 -*- """MergeWiki.py - 把 wiki 目录下的 Markdown 快速合并成可发送给 AI 的快照 用法(在本目录下执行): python MergeWiki.py # → WikiAi0.md(若超块大小则 WikiAi0.md, WikiAi1.md, ...) python MergeWiki.py 输出前缀 # 自定义输出前缀 python MergeWiki.py 输出前缀 200 # 自定义输出前缀 + 每块大小(KB) 特点: * 递归收集本目录(脚本所在目录)下所有 *.md / *.MD(跳过 .git 与自身产出); * 按路径排序合并,结果可复现; * 每个文件前后插入 `` 边界标记; * 每块开头插入文件头,含用途与【弱时效性】声明; * 默认每块 199KB(与 Tools/sendaiandsplit.py 口径一致)。 【弱时效性声明】 wiki 会持续修订,本脚本产出的是**生成时刻的快照**。发送给 AI 前请重新生成, 分析结论请以 wiki 目录下的**实时文件**为准,不要固化。 """ import os import sys import time DEFAULT_PREFIX = "WikiAi" DEFAULT_CHUNK_KB = 199 HERE = os.path.dirname(os.path.abspath(__file__)) def CollectMarkdown(Dir: str, Prefix: str): """递归收集 *.md / *.MD(跳过 .git 与以 Prefix 开头的自身产出),返回排序后的路径列表。""" Matched = [] LowPrefix = Prefix.lower() for Root, Dirs, Files in os.walk(Dir): Dirs[:] = [d for d in Dirs if d != ".git"] for F in Files: Lower = F.lower() if not (Lower.endswith(".md")): continue if Lower.startswith(LowPrefix): continue Matched.append(os.path.join(Root, F)) Matched.sort() return Matched def Banner(Title: str, SrcDir: str, FileCount: int, Total: int, ChunkIndex: int, ChunkTotal: int, OutName: str) -> str: """生成面向 AI 分析的文件头(含【弱时效性】声明)。""" Stamp = time.strftime("%Y-%m-%d %H:%M:%S") return ( "\n" f"\n" "\n" "\n" "\n" f"\n" f"\n" f"\n" f"\n" f"\n" "\n\n" ) def MergeWiki(Dir: str, Prefix: str, ChunkKb: int) -> int: """合并 Dir 下所有 Markdown,带边界标记与【弱时效性】文件头,按 ChunkKb 分块输出。""" if not os.path.isdir(Dir): print(f"错误: 目录 {Dir} 不存在", file=sys.stderr) return 1 Matched = CollectMarkdown(Dir, Prefix) if not Matched: print(f"错误: {Dir} 下没有可合并的 Markdown", file=sys.stderr) return 1 Parts = [] Total = 0 try: for Path in Matched: Rel = os.path.relpath(Path, Dir).replace("\\", "/") with open(Path, "r", encoding="utf-8") as Fh: Content = Fh.read() Total += len(Content) Parts.append(f"\n") Parts.append(Content) if not Content.endswith("\n"): Parts.append("\n") Parts.append(f"\n\n") except OSError as Exc: print(f"错误: 读取源文件失败: {Exc}", file=sys.stderr) return 1 Body = "".join(Parts) ChunkSize = max(1, ChunkKb) * 1024 ChunkTotal = max(1, (len(Body) + ChunkSize - 1) // ChunkSize) Title = "Viper wiki 快照(AI 分析用)" for i in range(ChunkTotal): Chunk = Body[i * ChunkSize:(i + 1) * ChunkSize] OutName = f"{Prefix}{i}.md" OutPath = os.path.join(Dir, OutName) try: with open(OutPath, "w", encoding="utf-8") as Out: Out.write(Banner(Title, Dir, len(Matched), Total, i, ChunkTotal, OutName)) Out.write(Chunk) except OSError as Exc: print(f"错误: 写入 {OutName} 失败: {Exc}", file=sys.stderr) return 1 print(f"{OutName}: {len(Chunk)} 字节") print(f"wiki 合并完成: {len(Matched)} 个文件 → {ChunkTotal} 块(前缀 {Prefix},每块 {ChunkKb}KB)") print("【弱时效性】以上为生成时刻快照,请以 wiki 目录下实时文件为准。") return 0 def PrintUsage() -> None: print("用法: python MergeWiki.py [输出前缀] [块大小KB]", file=sys.stderr) print(f" 默认: 前缀 {DEFAULT_PREFIX},块大小 {DEFAULT_CHUNK_KB}KB", file=sys.stderr) print(" 合并本目录下所有 *.md/*.MD → <前缀>0.md, <前缀>1.md, ...", file=sys.stderr) print("【弱时效性】产出为生成时刻快照,分析结论请以实时文件为准。", file=sys.stderr) def main() -> int: Args = sys.argv[1:] Prefix = DEFAULT_PREFIX ChunkKb = DEFAULT_CHUNK_KB if len(Args) >= 1 and Args[0] not in ("-h", "--help"): Prefix = Args[0] if len(Args) >= 2: try: ChunkKb = int(Args[1]) except ValueError: print(f"错误: 块大小 '{Args[1]}' 不是整数", file=sys.stderr) PrintUsage() return 1 if len(Args) > 2 or (len(Args) >= 1 and Args[0] in ("-h", "--help")): PrintUsage() return 1 return MergeWiki(HERE, Prefix, ChunkKb) if __name__ == "__main__": sys.exit(main())