Add ks-zl skill

This commit is contained in:
2026-05-13 18:14:30 +08:00
commit b81d919176
11 changed files with 1492 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
param(
[string]$Root = (Get-Location).Path,
[string]$OutputPath,
[switch]$Quiet
)
$ErrorActionPreference = "Stop"
if (-not (Test-Path -LiteralPath $Root)) {
throw "Root path does not exist: $Root"
}
$rootPath = (Resolve-Path -LiteralPath $Root).Path.TrimEnd([char[]]"\/")
if ([string]::IsNullOrWhiteSpace($OutputPath)) {
$OutputPath = Join-Path (Join-Path $rootPath "_indexes") "requirements-index.json"
}
function Get-RelativePath {
param([string]$Path)
$fullPath = (Resolve-Path -LiteralPath $Path).Path
if ($fullPath.StartsWith($rootPath, [System.StringComparison]::OrdinalIgnoreCase)) {
return ($fullPath.Substring($rootPath.Length).TrimStart([char[]]"\/") -replace "\\", "/")
}
return ($fullPath -replace "\\", "/")
}
function Get-FrontMatter {
param([string]$Content)
$match = [regex]::Match($Content, "(?s)\A---\r?\n(?<body>.*?)\r?\n---")
if ($match.Success) {
return $match.Groups["body"].Value
}
return ""
}
function Get-MetadataValue {
param(
[string]$FrontMatter,
[string]$Key
)
$match = [regex]::Match($FrontMatter, "(?m)^$([regex]::Escape($Key))\s*:\s*(?<value>.*)\s*$")
if ($match.Success) {
return $match.Groups["value"].Value.Trim().Trim('"').Trim("'")
}
return ""
}
function Get-MetadataList {
param(
[string]$FrontMatter,
[string]$Key
)
$lines = $FrontMatter -split "`r?`n"
$values = New-Object System.Collections.Generic.List[string]
for ($i = 0; $i -lt $lines.Count; $i++) {
if ($lines[$i] -match "^$([regex]::Escape($Key))\s*:\s*(?<inline>.*)\s*$") {
$inline = $Matches["inline"].Trim()
if ($inline.StartsWith("[") -and $inline.EndsWith("]")) {
$inline.Trim("[]").Split(",") |
ForEach-Object { $_.Trim().Trim('"').Trim("'") } |
Where-Object { -not [string]::IsNullOrWhiteSpace($_) } |
ForEach-Object { [void]$values.Add($_) }
}
for ($j = $i + 1; $j -lt $lines.Count; $j++) {
if ($lines[$j] -match "^\s*-\s*(?<value>.+?)\s*$") {
[void]$values.Add($Matches["value"].Trim().Trim('"').Trim("'"))
continue
}
if ($lines[$j] -match "^\S[^:]*\s*:") {
break
}
}
break
}
}
return @($values)
}
function Get-MarkdownSectionBody {
param(
[string]$Content,
[string]$Section
)
$match = [regex]::Match($Content, "(?ms)^##\s+$([regex]::Escape($Section))[ `t]*\r?\n(?<body>.*?)(?=^##\s+|\z)")
if ($match.Success) {
return $match.Groups["body"].Value
}
return ""
}
function ConvertTo-PlainText {
param([string]$Value)
if ([string]::IsNullOrWhiteSpace($Value)) {
return ""
}
return (($Value -replace "`r?`n", " ") -replace "\s+", " ").Trim()
}
function Get-PackageInfo {
param([string]$RelativePath)
$parts = $RelativePath -split "/"
$domain = ""
$packageType = ""
$slug = ""
if ($parts.Count -ge 4) {
$domain = $parts[0]
$packageType = $parts[1]
$slug = $parts[2]
}
return [pscustomobject]@{
domain = $domain
packageType = $packageType
slug = $slug
}
}
$excludedTopDirs = @(".git", "templates", "scripts")
$requirementFiles = Get-ChildItem -Path $rootPath -Recurse -File -Filter "requirement.md" -Force |
Where-Object {
$relativePath = Get-RelativePath $_.FullName
$topDir = ($relativePath -split "/")[0]
$excludedTopDirs -notcontains $topDir
}
$items = New-Object System.Collections.Generic.List[object]
foreach ($file in $requirementFiles) {
$relativePath = Get-RelativePath $file.FullName
$content = Get-Content -Raw -Encoding UTF8 -LiteralPath $file.FullName
if ($content -notmatch "(?m)^status:\s*requirement-draft\s*$") {
continue
}
$frontMatter = Get-FrontMatter -Content $content
$packageInfo = Get-PackageInfo -RelativePath $relativePath
$tags = @(Get-MetadataList -FrontMatter $frontMatter -Key "tags")
[void]$items.Add([pscustomobject][ordered]@{
schemaVersion = 1
type = "requirement-package"
title = Get-MetadataValue -FrontMatter $frontMatter -Key "title"
category = Get-MetadataValue -FrontMatter $frontMatter -Key "category"
tags = $tags
status = Get-MetadataValue -FrontMatter $frontMatter -Key "status"
updated = Get-MetadataValue -FrontMatter $frontMatter -Key "updated"
source = Get-MetadataValue -FrontMatter $frontMatter -Key "source"
domain = $packageInfo.domain
packageType = $packageInfo.packageType
slug = $packageInfo.slug
relativePath = $relativePath
fullPath = $file.FullName
feature = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "功能")
flow = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "流程")
dataTables = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "数据表")
dictionaries = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "字典")
businessRules = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "业务规则")
acceptance = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "验收")
portability = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "移植说明")
sourceEvidence = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "来源依据")
pending = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "待确认")
related = ConvertTo-PlainText (Get-MarkdownSectionBody -Content $content -Section "Related")
})
}
$outputDirectory = Split-Path -Parent $OutputPath
if (-not [string]::IsNullOrWhiteSpace($outputDirectory) -and -not (Test-Path -LiteralPath $outputDirectory)) {
New-Item -ItemType Directory -Path $outputDirectory -Force | Out-Null
}
$itemArray = @($items.ToArray())
$json = ConvertTo-Json -InputObject $itemArray -Depth 8
Set-Content -LiteralPath $OutputPath -Value $json -Encoding UTF8
if (-not $Quiet) {
Write-Host "Requirement index built. Indexed $($items.Count) package(s): $OutputPath"
}