Files
UnityAI/hooks/archive-check.ps1
lms ee1ff7f444 [docs][refactor]: 将旧 01~06 中文教程合并整理为 modules/frameworks 文件夹结构
- 删除 01~05 旧中文教程目录
- 将 03 核心功能教程拆分为各 framework 模块的子文档
- 06 工具脚本移入 modules/frameworks/编辑器工具/UI工具/
- 非框架内容合并进 rules/20-开发规则.md 与 rules/40-工作流清单.md
- 更新所有交叉链接与文档关联表

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:36:15 +08:00

182 lines
6.2 KiB
PowerShell

# hooks/archive-check.ps1
# Check changed .md files and suggest related docs to update.
# This script only outputs suggestions; it does NOT modify any files.
#
# Mechanism:
# For every changed .md file, parse its "## 文档关联" section and extract
# Markdown links. Those links are the documents that may need syncing.
$ErrorActionPreference = "SilentlyContinue"
$scriptPath = $MyInvocation.MyCommand.Path
if (-not [System.IO.Path]::IsPathRooted($scriptPath)) {
$scriptPath = Join-Path (Get-Location) $scriptPath
}
$scriptPath = [System.IO.Path]::GetFullPath($scriptPath)
$scriptDir = Split-Path -Parent $scriptPath
$projectRoot = [System.IO.Path]::GetFullPath((Join-Path $scriptDir ".."))
Set-Location $projectRoot
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
function Resolve-RelativePath {
param(
[string]$BaseFile,
[string]$LinkPath
)
# Skip anchors, query strings, and external URLs
if ($LinkPath -match '^[a-zA-Z][a-zA-Z0-9+.-]*://') { return $null }
if ([string]::IsNullOrWhiteSpace($LinkPath)) { return $null }
# Drop fragment
$pathOnly = ($LinkPath -split '#')[0]
if ([string]::IsNullOrWhiteSpace($pathOnly)) { return $null }
$baseDir = Split-Path -Parent $BaseFile
$combined = Join-Path $baseDir $pathOnly
try {
$resolved = [System.IO.Path]::GetFullPath($combined)
} catch {
return $null
}
# Convert to relative path from project root, normalizing separators
$fullProjectRoot = [System.IO.Path]::GetFullPath($projectRoot)
if ($resolved.StartsWith($fullProjectRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
$relative = $resolved.Substring($fullProjectRoot.Length).TrimStart('\', '/')
return $relative -replace '\\', '/'
}
return $pathOnly -replace '\\', '/'
}
function Get-DocumentAssociations {
param(
[string]$MdFile
)
$fullPath = Join-Path $projectRoot $MdFile
if (-not (Test-Path $fullPath)) { return @() }
$lines = [System.IO.File]::ReadAllLines($fullPath, [System.Text.Encoding]::UTF8)
$associations = @()
$inSection = $false
$inCodeBlock = $false
foreach ($line in $lines) {
# Toggle fenced code block state (``` or ~~~)
if ($line -match '^\s*```' -or $line -match '^\s*~~~') {
$inCodeBlock = -not $inCodeBlock
continue
}
if ($inCodeBlock) { continue }
# Match exactly '## 文档关联' (not '## 文档关联机制' etc.).
if ($line -match "^##\s+文档关联\s*$") {
$inSection = $true
continue
}
if ($inSection -and $line -match '^##\s+') {
break
}
if ($inSection) {
# Extract markdown links: [text](path)
$regex = '\[(?<text>[^\]]*)\]\((?<path>[^\)]+)\)'
$matches = [regex]::Matches($line, $regex)
foreach ($match in $matches) {
$linkPath = $match.Groups['path'].Value.Trim()
$resolved = Resolve-RelativePath -BaseFile $MdFile -LinkPath $linkPath
if ($resolved) {
$associations += $resolved
}
}
}
}
return $associations | Select-Object -Unique
}
function Test-WildcardMatch {
param(
[string]$Pattern,
[string]$Path
)
$regex = "^" + ($Pattern -replace "\.", "\." -replace "\*", ".*" -replace "\?", ".") + "$"
return $Path -match $regex
}
# ---------------------------------------------------------------------------
# Collect changed .md files (tracked modifications + untracked new files)
# ---------------------------------------------------------------------------
$changedFiles = @(cmd /c "git diff --name-only 2>nul") | Where-Object { $_ }
if ($changedFiles.Count -eq 0) {
$changedFiles = @(cmd /c "git diff --name-only --cached 2>nul") | Where-Object { $_ }
}
# Also include untracked .md files so new documents are checked too
$untrackedFiles = @(cmd /c "git ls-files --others --exclude-standard 2>nul") | Where-Object { $_ }
$allChangedFiles = @($changedFiles) + @($untrackedFiles) | Where-Object { $_ } | Select-Object -Unique
if (-not $allChangedFiles) {
Write-Output "[archive-check] No documentation changes detected (git diff is empty and no untracked .md files)."
exit 0
}
$changedMdFiles = $allChangedFiles | Where-Object { $_ -like "*.md" } | Select-Object -Unique
if ($changedMdFiles.Count -eq 0) {
Write-Output "[archive-check] No .md documentation changes detected."
exit 0
}
# ---------------------------------------------------------------------------
# Build suggestions from each changed file's "## 文档关联" section
# ---------------------------------------------------------------------------
$suggestions = @{}
foreach ($changed in $changedMdFiles) {
$relatedDocs = Get-DocumentAssociations -MdFile $changed
foreach ($related in $relatedDocs) {
# Normalize related path
$relatedNormalized = $related -replace '\\', '/'
if (-not $suggestions.ContainsKey($relatedNormalized)) {
$suggestions[$relatedNormalized] = @()
}
if ($suggestions[$relatedNormalized] -notcontains $changed) {
$suggestions[$relatedNormalized] += $changed
}
}
}
# ---------------------------------------------------------------------------
# Output results
# ---------------------------------------------------------------------------
Write-Output "[archive-check] Changed documentation files:"
foreach ($changed in $changedMdFiles | Sort-Object) {
Write-Output " - $changed"
}
if ($suggestions.Count -eq 0) {
Write-Output ""
Write-Output "[archive-check] No associations found in changed files' `"## 文档关联`" sections."
exit 0
}
Write-Output ""
Write-Output "[archive-check] Related docs that may need updating:"
foreach ($related in $suggestions.Keys | Sort-Object) {
$sources = $suggestions[$related] | Sort-Object
$sourceList = $sources -join ", "
Write-Output " - $related (mentioned by: $sourceList)"
}
Write-Output ""
Write-Output "[archive-check] Please confirm whether to sync-update the above docs."
exit 0