feat(wiki): 摄入历史性新机遇分析框架 + 静能量章节扩展 + frontmatter 验证工具增强

- 新增 wiki/历史性新机遇分析框架.md — 大镇乡谈政策分析框架(一目标一核心两支柱三周期)
- 更新 
aw/呸未然_静能量/ — 摄入 6 个章节内容(盘坐、正身、正言、正行、止念、业力)
- 新增/更新 wiki/静能量.md、wiki/佛家修行.md — 概念页扩展
- 优化 	ools/scripts/validate-frontmatter.py:
  * 支持根目录 .md 文件(如 AGENTS.md)的 wiki 专用检查
  * relations 目标存在性检查扩展到整个 vault(不限于 wiki/)
  * 添加 is_wiki_file() 判断,避免对非 wiki 文件误报
- 更新 wiki/LLM-Wiki-v2.md — 删除冗余 relation,修正 SCHEMA→AGENTS
- 更新 wiki/index.md — 页面数统计(505→506)
- 追加 wiki/log.md — ingest 记录
This commit is contained in:
2026-07-03 09:39:55 +08:00
parent 407a9213bf
commit e8c22a4794
14 changed files with 291 additions and 63 deletions
+81 -41
View File
@@ -36,7 +36,26 @@ VALID_TYPES = [
]
def validate_one(fp: Path) -> list[str]:
def build_vault_stems(vault_root: Path) -> set[str]:
"""扫描整个 vault 收集所有 .md 文件的 stem"""
stems = set()
for p in vault_root.rglob("*.md"):
rel = p.relative_to(vault_root).as_posix()
if rel.startswith(".git/") or "node_modules" in rel:
continue
stems.add(p.stem)
return stems
def is_wiki_file(fp: Path) -> bool:
"""判断文件是否在 wiki/ 目录下"""
try:
return fp.resolve().relative_to(WIKI.resolve()) is not None
except ValueError:
return False
def validate_one(fp: Path, vault_stems: set[str] | None = None) -> list[str]:
"""验证单个文件,返回错误列表"""
errors = []
content = fp.read_text(encoding="utf-8")
@@ -50,57 +69,63 @@ def validate_one(fp: Path) -> list[str]:
except yaml.YAMLError as e:
return [f"YAML parse error: {e}"]
# categories must contain [[LLM Wiki]]
cats = front.get("categories", [])
if isinstance(cats, str):
cats = [cats]
if not any(str(c).strip("[]") == "LLM Wiki" for c in cats):
errors.append("Missing [[LLM Wiki]] in categories")
# 非 wiki/ 文件跳过 wiki 专用检查
wiki_only = is_wiki_file(fp)
# tags must contain wiki
tags = front.get("tags", [])
if isinstance(tags, str):
tags = [tags]
if "wiki" not in tags:
errors.append("Missing 'wiki' in tags")
if wiki_only:
# categories must contain [[LLM Wiki]]
cats = front.get("categories", [])
if isinstance(cats, str):
cats = [cats]
if not any(str(c).strip("[]") == "LLM Wiki" for c in cats):
errors.append("Missing [[LLM Wiki]] in categories")
# type must exist and be valid
# Convention: type can be scalar (e.g. 'concept') or list (e.g. ['entity', 'People', 'Author'])
# When list, the FIRST element is the canonical type; subsequent are role/subtype annotations
ptype = front.get("type")
if not ptype:
errors.append("Missing type")
elif isinstance(ptype, str):
if ptype not in VALID_TYPES:
errors.append(f"Invalid type: '{ptype}' (valid: {', '.join(VALID_TYPES)})")
elif isinstance(ptype, list):
# tags must contain wiki
tags = front.get("tags", [])
if isinstance(tags, str):
tags = [tags]
if "wiki" not in tags:
errors.append("Missing 'wiki' in tags")
# type must exist and be valid
# type must exist and be valid
# Convention: type can be scalar (e.g. 'concept') or list (e.g. ['entity', 'People', 'Author'])
# When list, the FIRST element is the canonical type; subsequent are role/subtype annotations
ptype = front.get("type")
if not ptype:
errors.append("type list is empty")
else:
primary = ptype[0]
if primary not in VALID_TYPES:
errors.append(f"Invalid primary type: '{primary}' (valid: {', '.join(VALID_TYPES)})")
# Role/subtype annotations (2nd+ elements) are not validated against VALID_TYPES
errors.append("Missing type")
elif isinstance(ptype, str):
if ptype not in VALID_TYPES:
errors.append(f"Invalid type: '{ptype}' (valid: {', '.join(VALID_TYPES)})")
elif isinstance(ptype, list):
if not ptype:
errors.append("type list is empty")
else:
primary = ptype[0]
if primary not in VALID_TYPES:
errors.append(f"Invalid primary type: '{primary}' (valid: {', '.join(VALID_TYPES)})")
# Role/subtype annotations (2nd+ elements) are not validated against VALID_TYPES
# source must exist
if not front.get("source"):
errors.append("Missing source")
# source must exist
if not front.get("source"):
errors.append("Missing source")
# status=superseded must have superseded_by
if front.get("status") == "superseded" and not front.get("superseded_by"):
errors.append("status=superseded but missing superseded_by")
# status=superseded must have superseded_by
if front.get("status") == "superseded" and not front.get("superseded_by"):
errors.append("status=superseded but missing superseded_by")
# relations targets must exist
# relations targets must exist (check entire vault)
all_stems = vault_stems if vault_stems is not None else build_vault_stems(WIKI.parent)
for rel in front.get("relations", []):
target = str(rel.get("target", "")).strip("[]")
if target and not (WIKI / f"{target}.md").exists():
if target and target not in all_stems:
errors.append(f"Relation target [[{target}]] not found")
return errors
def get_git_changed_wiki_files() -> list[Path]:
"""获取 git 暂存区中被修改的 wiki .md 文件"""
"""获取 git 暂存区中被修改的 wiki/ 及根目录 .md 文件"""
try:
result = subprocess.run(
["git", "diff", "--cached", "--name-only", "--diff-filter=ACM"],
@@ -111,7 +136,10 @@ def get_git_changed_wiki_files() -> list[Path]:
files = []
for line in result.stdout.splitlines():
line = line.strip()
if line.startswith("wiki/") and line.endswith(".md"):
if not line.endswith(".md"):
continue
# 只校验 wiki/ 和根目录的 .md 文件
if line.startswith("wiki/") or "/" not in line:
fp = WIKI.parent / line
if fp.exists():
files.append(fp)
@@ -129,15 +157,27 @@ def main():
if not files:
sys.exit(0)
elif args.files:
files = [Path(f) if Path(f).is_absolute() else WIKI / f for f in args.files]
def resolve(f: str) -> Path:
p = Path(f)
if p.is_absolute():
return p
posix = p.as_posix()
# 根目录文件如 AGENTS.md → vault_root / AGENTS.md
if "/" not in posix and posix.endswith(".md"):
return WIKI.parent / posix
# wiki/ 前缀清理
relative = posix.removeprefix("wiki/")
return WIKI / relative
files = [resolve(f) for f in args.files]
else:
files = sorted(WIKI.glob("*.md"))
vault_stems = build_vault_stems(WIKI.parent)
all_errors = {}
for fp in files:
if fp.name in ("index.md", "log.md"):
continue
errors = validate_one(fp)
errors = validate_one(fp, vault_stems)
if errors:
all_errors[fp.stem] = errors