# PowerShell Lint Script for LLM Knowledge Base # This script scans the knowledge/ directory for broken WikiLinks, orphan pages, and unlinked concepts/entities. $knowledgeDir = Resolve-Path "knowledge" $files = Get-ChildItem -Path $knowledgeDir -Recurse -Filter *.md # Maps to keep track of files and links $existingFiles = @{} # file name/path -> absolute path $fileIncomingLinks = @{} # relative path -> list of incoming files # Exclude list for orphan check (master files) $excludeOrphans = @( "knowledge\index.md", "knowledge\log.md", "knowledge\feynman_problems.md" ) # Initialize files foreach ($file in $files) { # Normalize relative path (e.g. "knowledge/concepts/accidental-dba.md") $relPath = (Resolve-Path $file.FullName -Relative) -replace "^\.\\", "" -replace "\\", "/" $existingFiles[$relPath] = $file.FullName # Also index by base name (without extension) for simple links, e.g. [[log]] -> "knowledge/log.md" $baseName = [System.IO.Path]::GetFileNameWithoutExtension($file.Name) $existingFiles[$baseName] = $file.FullName $existingFiles[$relPath -replace "\.md$", ""] = $file.FullName # Initialize incoming links list $fileIncomingLinks[$relPath] = [System.Collections.Generic.List[string]]::new() } $brokenLinks = [System.Collections.Generic.List[PSCustomObject]]::new() # Scan each file for links foreach ($file in $files) { $relPath = (Resolve-Path $file.FullName -Relative) -replace "^\.\\", "" -replace "\\", "/" $content = Get-Content -Path $file.FullName -Raw # Match [[WikiLinks]] (can include aliases, e.g. [[link|alias]] or [[link]]) # Regex matches anything inside [[ and ]] except newlines or brackets $matches = [regex]::Matches($content, "\[\[([^\]|]+)(?:\|[^\]]*)?\]\]") foreach ($m in $matches) { $linkTarget = $m.Groups[1].Value.Trim() # Normalize target path # Try resolving directly, with .md, or prefixing with "knowledge/" $resolvedPath = $null $candidates = @( $linkTarget, "$linkTarget.md", "knowledge/$linkTarget", "knowledge/$linkTarget.md" ) foreach ($c in $candidates) { $normalizedC = $c -replace "\\", "/" if ($existingFiles.ContainsKey($normalizedC)) { $resolvedPath = $existingFiles[$normalizedC] break } } if ($resolvedPath) { # Link is valid! Log incoming link $resolvedRelPath = (Resolve-Path $resolvedPath -Relative) -replace "^\.\\", "" -replace "\\", "/" if ($resolvedRelPath -ne $relPath) { # ignore self-links $fileIncomingLinks[$resolvedRelPath].Add($relPath) } } else { # Link is broken! $brokenLinks.Add([PSCustomObject]@{ SourceFile = $relPath LinkTarget = $linkTarget MatchText = $m.Value }) } } } # Find Orphans $orphans = [System.Collections.Generic.List[string]]::new() foreach ($key in $fileIncomingLinks.Keys) { # Check if file is in exclude orphans list $isExcluded = $false foreach ($ex in $excludeOrphans) { $normalizedEx = $ex -replace "\\", "/" if ($key -eq $normalizedEx -or $key -like "knowledge/indices/*") { $isExcluded = $true break } } # An orphan is a file that is not master index/indices and has 0 incoming links (except from index files themselves) if (-not $isExcluded) { $incoming = $fileIncomingLinks[$key] # Filter out incoming links that are indices or master index $realIncomingCount = 0 foreach ($inc in $incoming) { if ($inc -ne "knowledge/index.md" -and $inc -notlike "knowledge/indices/*") { $realIncomingCount++ } } if ($realIncomingCount -eq 0) { $orphans.Add($key) } } } # Print Beautiful Report Write-Host "==========================================" -ForegroundColor Green Write-Host " KNOWLEDGE BASE LINT REPORT" -ForegroundColor Green Write-Host "==========================================" -ForegroundColor Green Write-Host "Total Files Scanned: $($files.Count)" -ForegroundColor Cyan Write-Host "" if ($brokenLinks.Count -eq 0) { Write-Host "[OK] No broken WikiLinks found!" -ForegroundColor Green } else { Write-Host "[WARNING] Found $($brokenLinks.Count) broken WikiLink(s):" -ForegroundColor Yellow foreach ($bl in $brokenLinks) { Write-Host " - In '$($bl.SourceFile)': Link '$($bl.LinkTarget)' is broken (Match: '$($bl.MatchText)')" -ForegroundColor Red } } Write-Host "" if ($orphans.Count -eq 0) { Write-Host "[OK] No orphan pages found!" -ForegroundColor Green } else { Write-Host "[INFO] Found $($orphans.Count) orphan page(s) (no incoming links from other content pages):" -ForegroundColor Yellow foreach ($o in $orphans) { Write-Host " - $o" -ForegroundColor DarkYellow } } Write-Host "" Write-Host "Lint completed successfully." -ForegroundColor Green