MCPcopy Create free account
hub / github.com/VectifyAI/OpenKB / fix_broken_links

Function fix_broken_links

openkb/lint.py:187–258  ·  view source on GitHub ↗

Rewrite or strip broken [[wikilinks]] across the wiki in place. For each Markdown page under ``wiki`` (excluding ``reports/`` and ``sources/`` and excluded files), runs :func:`strip_ghost_wikilinks` against the set of valid targets currently on disk. Targets that match fuzzily (case

(
    wiki: Path,
    *,
    restrict_to: list[Path] | None = None,
)

Source from the content-addressed store, hash-verified

185
186
187def fix_broken_links(
188 wiki: Path,
189 *,
190 restrict_to: list[Path] | None = None,
191) -> tuple[int, int]:
192 """Rewrite or strip broken [[wikilinks]] across the wiki in place.
193
194 For each Markdown page under ``wiki`` (excluding ``reports/`` and
195 ``sources/`` and excluded files), runs :func:`strip_ghost_wikilinks`
196 against the set of valid targets currently on disk. Targets that match
197 fuzzily (case, ``_`` vs ``-``, NFKC) are rewritten to canonical form;
198 targets that have no match are demoted to plain text.
199
200 Args:
201 wiki: Path to the wiki root directory.
202 restrict_to: When provided, only rewrite these files (must live
203 under ``wiki``). Paths outside the wiki and non-existent
204 paths are silently skipped. An empty list is a no-op — the
205 valid-target whitelist is still computed from the entire
206 wiki, so links like ``[[concepts/sibling]]`` resolve
207 correctly even when ``sibling.md`` is not in the scope.
208 Used by ``openkb remove`` (issue #58 / Bug 2) to clean only
209 the pages it actually touched instead of sweeping the
210 whole wiki and stripping pre-existing dangling links the
211 user may want to keep.
212
213 Returns:
214 Tuple of ``(files_changed, ghosts_stripped)``.
215 """
216 pages = _all_wiki_pages(wiki)
217 # The same fuzzy normalization _all_wiki_pages stores both the full
218 # relative path (e.g. ``concepts/attention``) and the bare stem
219 # (``attention``). Use the full-path keys so that links like
220 # ``[[concepts/foo]]`` resolve against ``concepts/`` files only.
221 known_targets: set[str] = {key for key in pages if "/" in key or key == "index"}
222 # Build the normalized index once and reuse across every file —
223 # otherwise strip_ghost_wikilinks would rebuild it per file (O(F·M)).
224 norm_index = build_norm_index(known_targets)
225
226 if restrict_to is None:
227 candidates: list[Path] = [
228 md
229 for md in wiki.rglob("*.md")
230 if md.name not in _EXCLUDED_FILES
231 and md.relative_to(wiki).parts[:1] not in (("reports",), ("sources",))
232 ]
233 else:
234 wiki_resolved = wiki.resolve()
235 candidates = []
236 for raw in restrict_to:
237 if not raw.is_file():
238 continue
239 try:
240 raw.resolve().relative_to(wiki_resolved)
241 except ValueError:
242 continue # outside wiki — skip silently
243 candidates.append(raw)
244

Calls 5

atomic_write_textFunction · 0.90
_all_wiki_pagesFunction · 0.85
build_norm_indexFunction · 0.85
_read_mdFunction · 0.85
strip_ghost_wikilinksFunction · 0.85