feat(genome): Phase 1 - Extraction Script
Implementiert genome-extract.ps1: - Trait-Erkennung (Skills/Agents/Prompts/Instructions) - Verbund-Erkennung für Prompt-Router + Sub-Prompts - Git-Log-Scanning mit Zeitspanne - Mutation-Typ-Klassifizierung (content-change/member-added/member-removed) - Strukturierte Markdown-Ausgabe mit Diffs
This commit is contained in:
parent
5a26d6a85e
commit
8e75798507
1 changed files with 296 additions and 0 deletions
296
.github/genome/genome-extract.ps1
vendored
Normal file
296
.github/genome/genome-extract.ps1
vendored
Normal file
|
|
@ -0,0 +1,296 @@
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Genome Engine – Phase 1: Extraction
|
||||||
|
Extrahiert Mutations aus der Git-History für Copilot-Customization-Dateien.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Scannt git log für Änderungen im Genome-Scope (.github/skills, agents, prompts, instructions).
|
||||||
|
Gruppiert Diffs nach Trait und gibt strukturiertes Markdown aus.
|
||||||
|
|
||||||
|
.PARAMETER Since
|
||||||
|
Zeitspanne für git log (z.B. "4 days ago", "2 weeks ago"). Default: "7 days ago"
|
||||||
|
|
||||||
|
.PARAMETER RepoPath
|
||||||
|
Pfad zum Repository. Default: aktuelles Verzeichnis.
|
||||||
|
|
||||||
|
.PARAMETER OutputPath
|
||||||
|
Pfad für die Ausgabedatei. Default: .github/genome/output/raw-mutations.md
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\.github\genome\genome-extract.ps1 -Since "4 days ago"
|
||||||
|
#>
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Since = "7 days ago",
|
||||||
|
[string]$RepoPath = ".",
|
||||||
|
[string]$OutputPath = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# --- Konfiguration ---
|
||||||
|
|
||||||
|
$GenomeScopes = @(
|
||||||
|
".github/skills/"
|
||||||
|
".github/agents/"
|
||||||
|
".github/prompts/"
|
||||||
|
".github/copilot-instructions.md"
|
||||||
|
".github/kotlin-conventions.instructions.md"
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Funktionen ---
|
||||||
|
|
||||||
|
function Get-TraitKey {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Leitet den Trait-Key aus einem Dateipfad ab.
|
||||||
|
#>
|
||||||
|
param([string]$FilePath)
|
||||||
|
|
||||||
|
# Skills: skill/<ordnername>
|
||||||
|
if ($FilePath -match "^\.github/skills/([^/]+)/") {
|
||||||
|
return "skill/$($Matches[1])"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Agents: agent/<dateiname-ohne-extension>
|
||||||
|
if ($FilePath -match "^\.github/agents/(.+)\.agent\.md$") {
|
||||||
|
return "agent/$($Matches[1])"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prompts: Standalone oder Verbund
|
||||||
|
if ($FilePath -match "^\.github/prompts/(.+)\.prompt\.md$") {
|
||||||
|
$name = $Matches[1]
|
||||||
|
|
||||||
|
# Prüfe ob es ein Sub-Prompt ist (enthält Bindestrich und Router existiert)
|
||||||
|
# Verbund-Erkennung: <router>-<sub>.prompt.md → trait des Routers
|
||||||
|
# Wir suchen den längsten Präfix, der als Router existieren könnte
|
||||||
|
$parts = $name -split "-"
|
||||||
|
if ($parts.Count -gt 1) {
|
||||||
|
# Versuche progressiv kürzere Präfixe als Router-Name
|
||||||
|
for ($i = $parts.Count - 1; $i -ge 1; $i--) {
|
||||||
|
$candidate = ($parts[0..($i-1)] -join "-")
|
||||||
|
$routerPath = ".github/prompts/$candidate.prompt.md"
|
||||||
|
$fullRouterPath = Join-Path $RepoPath $routerPath
|
||||||
|
if (Test-Path $fullRouterPath) {
|
||||||
|
return "prompt/$candidate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Standalone-Prompt
|
||||||
|
return "prompt/$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Instructions
|
||||||
|
if ($FilePath -match "^\.github/(.+)\.instructions\.md$") {
|
||||||
|
return "instructions/$($Matches[1])"
|
||||||
|
}
|
||||||
|
if ($FilePath -match "^\.github/copilot-instructions\.md$") {
|
||||||
|
return "instructions/copilot-instructions"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-MutationType {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Bestimmt den Mutation-Typ aus dem Git diff-filter Status.
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[string]$Status # A, M, D, R, etc.
|
||||||
|
)
|
||||||
|
|
||||||
|
switch -Regex ($Status) {
|
||||||
|
"^A" { return "member-added" }
|
||||||
|
"^D" { return "member-removed" }
|
||||||
|
default { return "content-change" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-InGenomeScope {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Prüft ob ein Dateipfad im Genome-Scope liegt.
|
||||||
|
#>
|
||||||
|
param([string]$FilePath)
|
||||||
|
|
||||||
|
foreach ($scope in $GenomeScopes) {
|
||||||
|
if ($scope.EndsWith("/")) {
|
||||||
|
if ($FilePath.StartsWith($scope)) { return $true }
|
||||||
|
} else {
|
||||||
|
if ($FilePath -eq $scope) { return $true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Hauptlogik ---
|
||||||
|
|
||||||
|
Push-Location $RepoPath
|
||||||
|
try {
|
||||||
|
# Output-Pfad bestimmen
|
||||||
|
if (-not $OutputPath) {
|
||||||
|
$OutputPath = Join-Path $RepoPath ".github/genome/output/raw-mutations.md"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Genome Extract: Scanning commits since '$Since'..." -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Git-Log abrufen: Commits die Genome-Scope-Dateien betreffen
|
||||||
|
$logFormat = "--format=%H|%aI|%an|%s"
|
||||||
|
$commits = git log $logFormat --since="$Since" -- $GenomeScopes 2>&1
|
||||||
|
|
||||||
|
if (-not $commits -or $LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host "Keine Commits im Genome-Scope seit '$Since' gefunden." -ForegroundColor Yellow
|
||||||
|
$commits = @()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Commits parsen
|
||||||
|
$mutations = @{} # Key: trait → Value: Liste von Mutations
|
||||||
|
|
||||||
|
foreach ($line in $commits) {
|
||||||
|
if (-not $line -or $line -notmatch "\|") { continue }
|
||||||
|
|
||||||
|
$parts = $line -split "\|", 4
|
||||||
|
if ($parts.Count -lt 4) { continue }
|
||||||
|
|
||||||
|
$hash = $parts[0]
|
||||||
|
$date = $parts[1]
|
||||||
|
$author = $parts[2]
|
||||||
|
$message = $parts[3]
|
||||||
|
|
||||||
|
# Geänderte Dateien für diesen Commit abrufen
|
||||||
|
$diffFiles = git diff-tree --no-commit-id -r --name-status $hash 2>&1
|
||||||
|
|
||||||
|
foreach ($diffLine in $diffFiles) {
|
||||||
|
if (-not $diffLine -or $diffLine -notmatch "^\w") { continue }
|
||||||
|
|
||||||
|
$diffParts = $diffLine -split "\t", 3
|
||||||
|
$status = $diffParts[0]
|
||||||
|
$filePath = $diffParts[1]
|
||||||
|
|
||||||
|
# Bei Renames: Zielpfad verwenden
|
||||||
|
if ($status -match "^R" -and $diffParts.Count -ge 3) {
|
||||||
|
$filePath = $diffParts[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Normalisieren (Backslash → Forward Slash)
|
||||||
|
$filePath = $filePath -replace "\\", "/"
|
||||||
|
|
||||||
|
# Prüfe ob im Genome-Scope
|
||||||
|
if (-not (Test-InGenomeScope $filePath)) { continue }
|
||||||
|
|
||||||
|
# Trait-Key ableiten
|
||||||
|
$traitKey = Get-TraitKey $filePath
|
||||||
|
if (-not $traitKey) { continue }
|
||||||
|
|
||||||
|
# Mutation-Typ bestimmen
|
||||||
|
$mutationType = Get-MutationType $status
|
||||||
|
|
||||||
|
# Diff für diese Datei holen
|
||||||
|
$diff = git show --format="" --no-color $hash -- $filePath 2>&1
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
# Fallback: diff-tree
|
||||||
|
$diff = git diff-tree -p $hash -- $filePath 2>&1
|
||||||
|
}
|
||||||
|
$diffText = ($diff | Out-String).Trim()
|
||||||
|
|
||||||
|
# Mutation speichern
|
||||||
|
if (-not $mutations.ContainsKey($traitKey)) {
|
||||||
|
$mutations[$traitKey] = @()
|
||||||
|
}
|
||||||
|
|
||||||
|
$mutations[$traitKey] += @{
|
||||||
|
Hash = $hash.Substring(0, [Math]::Min(8, $hash.Length))
|
||||||
|
Date = $date
|
||||||
|
Author = $author
|
||||||
|
Message = $message
|
||||||
|
File = $filePath
|
||||||
|
Type = $mutationType
|
||||||
|
Diff = $diffText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Output generieren ---
|
||||||
|
|
||||||
|
$sb = [System.Text.StringBuilder]::new()
|
||||||
|
[void]$sb.AppendLine("# Raw Mutations")
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
[void]$sb.AppendLine("**Extrahiert:** $(Get-Date -Format 'yyyy-MM-dd HH:mm')")
|
||||||
|
[void]$sb.AppendLine("**Zeitraum:** seit $Since")
|
||||||
|
[void]$sb.AppendLine("**Repository:** $(Split-Path $RepoPath -Leaf)")
|
||||||
|
[void]$sb.AppendLine("**Traits mit Mutations:** $($mutations.Count)")
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
[void]$sb.AppendLine("---")
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
|
||||||
|
if ($mutations.Count -eq 0) {
|
||||||
|
[void]$sb.AppendLine("*Keine Mutations im angegebenen Zeitraum gefunden.*")
|
||||||
|
} else {
|
||||||
|
# Sortiert nach Trait-Key ausgeben
|
||||||
|
foreach ($traitKey in ($mutations.Keys | Sort-Object)) {
|
||||||
|
$traitMutations = $mutations[$traitKey]
|
||||||
|
|
||||||
|
[void]$sb.AppendLine("## Trait: ``$traitKey``")
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
[void]$sb.AppendLine("| Mutations | Dateien |")
|
||||||
|
[void]$sb.AppendLine("|-----------|---------|")
|
||||||
|
|
||||||
|
$uniqueFiles = ($traitMutations | ForEach-Object { $_.File } | Sort-Object -Unique) -join ", "
|
||||||
|
[void]$sb.AppendLine("| $($traitMutations.Count) | $uniqueFiles |")
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
|
||||||
|
# Gruppiert nach Commit (Hash)
|
||||||
|
$byCommit = $traitMutations | Group-Object -Property Hash
|
||||||
|
|
||||||
|
foreach ($commitGroup in $byCommit) {
|
||||||
|
$first = $commitGroup.Group[0]
|
||||||
|
[void]$sb.AppendLine("### [$($first.Hash)] $($first.Message)")
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
[void]$sb.AppendLine("- **Datum:** $($first.Date)")
|
||||||
|
[void]$sb.AppendLine("- **Autor:** $($first.Author)")
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
|
||||||
|
foreach ($mutation in $commitGroup.Group) {
|
||||||
|
$header = "#### " + '`' + $mutation.Type + '`' + " - " + $mutation.File
|
||||||
|
[void]$sb.AppendLine($header)
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
|
||||||
|
if ($mutation.Diff) {
|
||||||
|
# Diff auf max 80 Zeilen begrenzen
|
||||||
|
$diffLines = $mutation.Diff -split [Environment]::NewLine
|
||||||
|
if ($diffLines.Count -gt 80) {
|
||||||
|
$truncMsg = "... ($($diffLines.Count - 80) weitere Zeilen)"
|
||||||
|
$diffLines = $diffLines[0..79] + @($truncMsg)
|
||||||
|
}
|
||||||
|
[void]$sb.AppendLine('```diff')
|
||||||
|
[void]$sb.AppendLine(($diffLines -join [Environment]::NewLine))
|
||||||
|
[void]$sb.AppendLine('```')
|
||||||
|
}
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[void]$sb.AppendLine("---")
|
||||||
|
[void]$sb.AppendLine("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Datei schreiben
|
||||||
|
$outputDir = Split-Path $OutputPath -Parent
|
||||||
|
if (-not (Test-Path $outputDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
$sb.ToString() | Set-Content -Path $OutputPath -Encoding UTF8
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Extraction abgeschlossen:" -ForegroundColor Green
|
||||||
|
Write-Host " Traits: $($mutations.Count)" -ForegroundColor White
|
||||||
|
Write-Host " Mutations: $(($mutations.Values | ForEach-Object { $_.Count } | Measure-Object -Sum).Sum)" -ForegroundColor White
|
||||||
|
Write-Host " Output: $OutputPath" -ForegroundColor White
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue