Phase 9 (улучшения автоматизации):
A. kb-autosync.sh переписан: pull → regenerate index → commit → push. После каждого push с Mac индекс objects-map.json и _index.md обновляются автоматически на code-server (LXC 132). B. kb-objects-map.py + kb-objects-audit.py добавлены в воскресный weekly cron на LXC 132 — health-check автогенерируется раз в неделю. C. Чистка битых wiki-ссылок (score 84 → 9): - notes/govru-diagnosis → projects/niikn/govru-quickfix-playbook (2) - claude-memory/podkop → 2026-04-17-peredelki-podkop-stability-fix - [[../snippets/clients/]] → snippets/clients/ (текстом, 2) - [[feedback_*]] (user memory) → backtick-cited (2) - [[../znamenskoye/]] → [[../znamenskoye/README]] (4) Скрипт kb-objects-audit.py улучшен: regex теперь требует [[...]] с двойной скобкой (не одной), исключает audit/ и CLAUDE.md (placeholder и autogen). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -77,32 +77,42 @@ def check_project_frontmatter(objects: list) -> list[str]:
|
||||
|
||||
|
||||
def check_broken_wikilinks(files: list[Path]) -> list[tuple[str, str, str]]:
|
||||
"""Возвращает [(source, link, reason)] для битых [[...]] ссылок."""
|
||||
"""Возвращает [(source, link, reason)] для битых [[...]] ссылок.
|
||||
|
||||
Проверка только полноценных wiki-ссылок [[...]] (двойная скобка с обеих сторон)."""
|
||||
issues = []
|
||||
all_basenames = {f.stem for f in files}
|
||||
all_relpaths = {str(f.relative_to(VAULT)).replace(".md", "") for f in files}
|
||||
pat = re.compile(r"\[\[([^\]\|#]+?)(?:\||#|\])")
|
||||
# match [[target]] / [[target|alias]] / [[target#anchor]]
|
||||
pat = re.compile(r"\[\[([^\]\|#\n]+?)(?:[\|#][^\]\n]*)?\]\]")
|
||||
for f in files:
|
||||
# пропускаем graphify-плагин output и весь audit/ (само-цитирование, autogen)
|
||||
rel = f.relative_to(VAULT)
|
||||
if "graphify-out" in rel.parts:
|
||||
continue
|
||||
if rel.parts[0] == "audit":
|
||||
continue
|
||||
if str(rel) == "CLAUDE.md":
|
||||
continue # обучающие placeholder'ы вроде [[двойные скобки]]
|
||||
try:
|
||||
text = f.read_text()
|
||||
except Exception:
|
||||
continue
|
||||
for m in pat.finditer(text):
|
||||
target = m.group(1).strip().rstrip("/")
|
||||
if not target:
|
||||
target = m.group(1).strip().rstrip("/").removesuffix(".md")
|
||||
if not target or target in (".", ".."):
|
||||
continue
|
||||
# Allow absolute by relpath, or basename match, or relative-resolved
|
||||
# Allow absolute by relpath, basename, or relative-to-source
|
||||
if target in all_relpaths:
|
||||
continue
|
||||
base = target.split("/")[-1]
|
||||
if base in all_basenames:
|
||||
continue
|
||||
# try relative to source
|
||||
src_dir = f.parent.relative_to(VAULT)
|
||||
resolved = str(src_dir / target).replace("./", "")
|
||||
if resolved in all_relpaths:
|
||||
continue
|
||||
issues.append((str(f.relative_to(VAULT)), m.group(0), "→ нет такого файла"))
|
||||
issues.append((str(rel), m.group(0), "→ нет такого файла"))
|
||||
return issues
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user