Files
llm_wiki/tools/scripts/detect-conflicts.py
T
hehaiguang1123 a6f05ab2d5 Phase 0-2: Schema cleanup, typed relations, event-driven automation
- Phase 0: AGENTS.md cleanup (dedup quotes, renumber sections, merge qmd)
- Phase 1: typed relations (manage-relations.py, graph-search.py, check-staleness.py, detect-conflicts.py)
- Phase 2: frontmatter validator, weekly lint, knowledge promotion, git hooks
- Fix .gitignore to track tools/ and .githooks/
- Fix git remote URL (remove plaintext token)
- New wiki pages: 504 pages, 34 raw sources
2026-07-01 08:05:43 +08:00

133 lines
4.2 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
detect-conflicts.py — 检测 wiki 页面间的矛盾
通过扫描 relations 中的 conflicts_with 关系,
检查双方是否都引用了对方,并输出矛盾报告。
用法:
python tools/scripts/detect-conflicts.py # 标准输出
python tools/scripts/detect-conflicts.py --json # JSON 格式
python tools/scripts/detect-conflicts.py --auto-callout # 自动添加 callout
"""
import argparse
import json
import re
import sys
from pathlib import Path
try:
import yaml
except ImportError:
yaml = None
WIKI = Path(r"D:\Applications\app\kepano-obsidian-main\wiki")
def read_frontmatter(fp: Path) -> dict | None:
"""读取 frontmatter,返回 dict 或 None"""
content = fp.read_text(encoding="utf-8")
m = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)
if not m or not yaml:
return None
try:
return yaml.safe_load(m.group(1)) or {}
except:
return None
def detect():
"""检测所有 conflicts_with 关系,返回冲突报告列表"""
conflicts = []
pages = {}
for fp in WIKI.glob("*.md"):
if fp.name in ("index.md", "log.md"):
continue
front = read_frontmatter(fp)
if front is None:
continue
pages[fp.stem] = front
for rel in front.get("relations", []):
if rel["type"] == "conflicts_with":
target = rel.get("target", "").strip("[]")
conf_source = rel.get("confidence", 3)
if target and target != fp.stem:
conflicts.append({
"source": fp.stem,
"target": target,
"confidence": conf_source,
"bidirectional": False
})
# 检查双向性
for c in conflicts:
target_front = pages.get(c["target"])
if target_front:
for rel in target_front.get("relations", []):
if rel["type"] == "conflicts_with" and rel.get("target", "").strip("[]") == c["source"]:
c["bidirectional"] = True
break
return conflicts
def generate_callout(source: str, target: str) -> str:
return (
f"> [!WARNING] 可能矛盾\n"
f"> 本页的声明与 [[{target}]] 存在冲突。\n"
f"> 需要人工复核并解决矛盾。\n"
)
def main():
parser = argparse.ArgumentParser(description="Detect conflicts between wiki pages")
parser.add_argument("--json", action="store_true", help="Output JSON")
parser.add_argument("--auto-callout", action="store_true", help="Auto-add callout to pages")
args = parser.parse_args()
conflicts = detect()
if args.json:
print(json.dumps(conflicts, ensure_ascii=False, indent=2))
return
bidirectional = [c for c in conflicts if c["bidirectional"]]
unidirectional = [c for c in conflicts if not c["bidirectional"]]
print(f"=== Conflict Detection ===")
print(f" Total conflict declarations: {len(conflicts)}")
print(f" Bidirectional (both confirm): {len(bidirectional)}")
print(f" Unidirectional (check needed): {len(unidirectional)}")
if bidirectional:
print(f"\n Bidirectional conflicts:")
for c in bidirectional:
print(f" [[{c['source']}]] <--conflicts_with--> [[{c['target']}]]")
if unidirectional:
print(f"\n Unidirectional conflicts (may need callout):")
for c in unidirectional:
print(f" [[{c['source']}]] --conflicts_with--> [[{c['target']}]] (unconfirmed)")
if args.auto_callout:
added = 0
for c in unidirectional:
fp = WIKI / f"{c['source']}.md"
if not fp.exists():
continue
content = fp.read_text(encoding="utf-8")
callout = generate_callout(c["source"], c["target"])
# Only add if not already present
if callout.strip() not in content:
content += f"\n\n{callout}"
fp.write_text(content, encoding="utf-8")
added += 1
print(f" Added callout to [[{c['source']}]]")
print(f" Callouts added: {added}")
if __name__ == "__main__":
main()