# Refresh the local mirror of the Curabis BCQuality knowledge base. # # Mirrors three layers from https://github.com/Curabis/BCQuality: # custom/ - Curabis org-specific rules (ALWAYS read in full each session) # community/ - BC community patterns (loaded on relevance via INDEX.md) # microsoft/ - platform guardrails (loaded on relevance via INDEX.md) # # The upstream file list is discovered dynamically from the GitHub tree API, so # new/removed upstream files propagate automatically - nothing is hardcoded. # After downloading, an INDEX.md is generated (one line per file, with domain + # keywords from each file's frontmatter) so an agent can scan and pull only the # files relevant to a task instead of loading all ~100 every session. # # Run periodically, then review the diff and commit: # pwsh .github/.agents/sync-bcquality-knowledge.ps1 $ErrorActionPreference = 'Stop' $repo = 'Curabis/BCQuality' $branch = 'main' $dest = Join-Path $PSScriptRoot 'bcquality-knowledge' $staging = "$dest.tmp" $rawBase = "https://raw.githubusercontent.com/$repo/$branch" $treeUrl = "https://api.github.com/repos/$repo/git/trees/$branch" + '?recursive=1' # Upstream path prefix -> local layer folder $layerMap = [ordered]@{ 'custom/knowledge/' = 'custom' 'community/knowledge/' = 'community' 'microsoft/knowledge/' = 'microsoft' } function Get-Frontmatter { param([string]$Path) $lines = Get-Content -Path $Path $fm = @{} if ($lines.Count -eq 0 -or $lines[0].Trim() -ne '---') { return $fm } for ($i = 1; $i -lt $lines.Count; $i++) { if ($lines[$i].Trim() -eq '---') { break } if ($lines[$i] -match '^\s*([\w-]+):\s*(.*)$') { $fm[$matches[1]] = $matches[2].Trim() } } return $fm } Write-Host "Fetching file tree from $repo@$branch ..." $headers = @{ 'User-Agent' = 'wareco-bcquality-sync'; 'Accept' = 'application/vnd.github+json' } $tree = (Invoke-RestMethod -Uri $treeUrl -Headers $headers).tree # Build the download worklist from the tree $files = @() foreach ($node in $tree) { if ($node.type -ne 'blob') { continue } if ($node.path -notlike '*.md') { continue } foreach ($prefix in $layerMap.Keys) { if ($node.path.StartsWith($prefix)) { $relative = $node.path.Substring($prefix.Length) # e.g. performance/avoid-commit-inside-loops.md $files += [pscustomobject]@{ Url = "$rawBase/$($node.path)" Layer = $layerMap[$prefix] Relative = $relative LocalPath = Join-Path $staging (Join-Path $layerMap[$prefix] $relative) } break } } } if ($files.Count -eq 0) { throw 'No knowledge files found in upstream tree - aborting.' } # Download into a staging folder so a mid-run failure never wipes the live copy if (Test-Path $staging) { Remove-Item -Recurse -Force $staging } New-Item -ItemType Directory -Force $staging | Out-Null Write-Host "Downloading $($files.Count) knowledge files ..." $rc = 0 foreach ($f in $files) { New-Item -ItemType Directory -Force (Split-Path $f.LocalPath) | Out-Null try { Invoke-WebRequest -Uri $f.Url -OutFile $f.LocalPath -UseBasicParsing -ErrorAction Stop Write-Host "OK $($f.Layer)/$($f.Relative)" } catch { Write-Error "FAIL $($f.Layer)/$($f.Relative)" $rc = 1 } } # Generate INDEX.md (relevance index for all layers) Write-Host "Generating INDEX.md ..." $idx = [System.Collections.Generic.List[string]]::new() $idx.Add('# BCQuality Knowledge Index') $idx.Add('') $idx.Add('') $idx.Add('') $idx.Add('Layers: `custom` is ALWAYS read in full each session. For `community` and') $idx.Add('`microsoft`, scan this index and read only the files whose domain/keywords') $idx.Add('match the task at hand.') $idx.Add('') foreach ($layer in @('custom', 'community', 'microsoft')) { $layerFiles = $files | Where-Object { $_.Layer -eq $layer } | Sort-Object Relative if (-not $layerFiles) { continue } $note = if ($layer -eq 'custom') { ' (always-on)' } else { ' (load on relevance)' } $idx.Add("## $layer$note") $idx.Add('') foreach ($f in $layerFiles) { $fm = Get-Frontmatter $f.LocalPath $domain = if ($fm.ContainsKey('domain')) { $fm['domain'] } else { '' } $keywords = if ($fm.ContainsKey('keywords')) { $fm['keywords'].Trim('[', ']') } else { '' } $rel = $f.Relative -replace '\.md$', '' $idx.Add("- ``$layer/$rel`` - domain: $domain; keywords: $keywords") } $idx.Add('') } Set-Content -Path (Join-Path $staging 'INDEX.md') -Value $idx -Encoding utf8 # Swap staging into place if (Test-Path $dest) { Remove-Item -Recurse -Force $dest } Rename-Item -Path $staging -NewName (Split-Path $dest -Leaf) Write-Host '' Write-Host "Done. $($files.Count) files across $($layerMap.Count) layers." Write-Host "Review changes with: git diff $dest" exit $rc