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
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
import io
|
||||
import re
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
if sys.platform == "win32":
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
|
||||
|
||||
|
||||
def extract_existing_frontmatter(file_path: Path) -> Dict[str, any]:
|
||||
try:
|
||||
content = file_path.read_text(encoding="utf-8")
|
||||
match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)
|
||||
if match:
|
||||
fm_text = match.group(1)
|
||||
fm = {}
|
||||
for line in fm_text.split("\n"):
|
||||
if ":" in line:
|
||||
key, value = line.split(":", 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
if value.startswith("[") or value.startswith("{"):
|
||||
continue
|
||||
elif value.startswith('"') and value.endswith('"'):
|
||||
fm[key] = value.strip('"')
|
||||
elif value:
|
||||
fm[key] = value
|
||||
return fm
|
||||
except Exception as e:
|
||||
print(f"Error reading {file_path.name}: {e}")
|
||||
return {}
|
||||
|
||||
|
||||
def get_card_type_from_filename(filename: str) -> Tuple[str, str]:
|
||||
name = Path(filename).stem
|
||||
|
||||
if "AI教育" in name or "全球图景" in name or "九校" in name:
|
||||
return "concept", "概念"
|
||||
elif any(
|
||||
x in name
|
||||
for x in [
|
||||
"CMU",
|
||||
"MIT",
|
||||
"斯坦福",
|
||||
"牛津",
|
||||
"哈佛",
|
||||
"AIEOU",
|
||||
"RAISE",
|
||||
"Accelerator",
|
||||
]
|
||||
):
|
||||
return "entity", "机构"
|
||||
elif any(
|
||||
x in name for x in ["Victor", "Emma", "Ken", "Rose", "Neil", "Ryan", "Kestin"]
|
||||
):
|
||||
return "entity", "人物"
|
||||
elif "知识卡片" in name:
|
||||
return "concept", "概念"
|
||||
|
||||
return "concept", "概念"
|
||||
|
||||
|
||||
def update_frontmatter(file_path: Path) -> bool:
|
||||
try:
|
||||
content = file_path.read_text(encoding="utf-8")
|
||||
|
||||
if not content.startswith("---"):
|
||||
print(f"⚠️ No frontmatter in {file_path.name}")
|
||||
return False
|
||||
|
||||
fm_match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)
|
||||
if not fm_match:
|
||||
print(f"⚠️ Cannot parse frontmatter in {file_path.name}")
|
||||
return False
|
||||
|
||||
fm_text = fm_match.group(1)
|
||||
body = content[fm_match.end() :]
|
||||
|
||||
existing_fm = {}
|
||||
for line in fm_text.split("\n"):
|
||||
if ":" in line:
|
||||
key, value = line.split(":", 1)
|
||||
existing_fm[key.strip()] = value.strip()
|
||||
|
||||
card_type, card_category = get_card_type_from_filename(file_path.name)
|
||||
|
||||
new_fm_lines = [
|
||||
"---",
|
||||
"categories:",
|
||||
' - "[[LLM Wiki]]"',
|
||||
' - "[[知识卡片]]"',
|
||||
"tags:",
|
||||
]
|
||||
|
||||
existing_tags = existing_fm.get("tags", "")
|
||||
if existing_tags:
|
||||
if existing_tags.startswith("["):
|
||||
tag_match = re.findall(r"'([^']+)'", existing_tags)
|
||||
for tag in tag_match:
|
||||
new_fm_lines.append(f" - {tag}")
|
||||
else:
|
||||
for tag in existing_tags.split(","):
|
||||
tag = tag.strip()
|
||||
if tag:
|
||||
new_fm_lines.append(f" - {tag}")
|
||||
else:
|
||||
new_fm_lines.append(" - AI教育")
|
||||
new_fm_lines.append(" - 知识卡片")
|
||||
|
||||
new_fm_lines.extend(
|
||||
[
|
||||
f"type: {card_type}",
|
||||
f"created: {existing_fm.get('created', datetime.date.today().isoformat())}",
|
||||
]
|
||||
)
|
||||
|
||||
if "updated" in existing_fm:
|
||||
new_fm_lines.append(f"updated: {existing_fm['updated']}")
|
||||
if "source" in existing_fm:
|
||||
new_fm_lines.append(f"source: {existing_fm['source']}")
|
||||
if "title" in existing_fm:
|
||||
new_fm_lines.append(f"title: {existing_fm['title']}")
|
||||
if "review-date" in existing_fm:
|
||||
new_fm_lines.append(f"review-date: {existing_fm['review-date']}")
|
||||
if "review-status" in existing_fm:
|
||||
new_fm_lines.append(f"review-status: {existing_fm['review-status']}")
|
||||
|
||||
new_fm_lines.append("---")
|
||||
|
||||
new_content = "\n".join(new_fm_lines) + body
|
||||
file_path.write_text(new_content, encoding="utf-8")
|
||||
|
||||
print(
|
||||
f"✅ Updated: {file_path.name} (type={card_type}, category={card_category})"
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error updating {file_path.name}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("Knowledge Cards Frontmatter Batch Update Tool")
|
||||
print("=" * 60)
|
||||
print(f"Execution time: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print()
|
||||
|
||||
cards_dir = Path(__file__).parent.parent / "知识卡片"
|
||||
|
||||
if not cards_dir.exists():
|
||||
print(f"❌ Knowledge cards directory not found: {cards_dir}")
|
||||
return
|
||||
|
||||
card_files = list(cards_dir.glob("*.md"))
|
||||
print(f"Found {len(card_files)} knowledge card files")
|
||||
print()
|
||||
|
||||
success_count = 0
|
||||
fail_count = 0
|
||||
|
||||
for card_file in card_files:
|
||||
result = update_frontmatter(card_file)
|
||||
if result:
|
||||
success_count += 1
|
||||
else:
|
||||
fail_count += 1
|
||||
|
||||
print()
|
||||
print("=" * 60)
|
||||
print("Update Complete")
|
||||
print("=" * 60)
|
||||
print(f"Total files: {len(card_files)}")
|
||||
print(f"Successfully updated: {success_count}")
|
||||
print(f"Failed: {fail_count}")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user