Browse Source

add: PowerShell 版 evolve.ps1,功能与 evolve.sh 完全等价

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
nonvioce 5 tháng trước cách đây
mục cha
commit
a148171244
1 tập tin đã thay đổi với 436 bổ sung và 0 xóa
  1. 436 0
      scripts/evolve.ps1

+ 436 - 0
scripts/evolve.ps1

@@ -0,0 +1,436 @@
+#!/usr/bin/env pwsh
+# ═══════════════════════════════════════════════════════════════
+# evolve.ps1 — novel-write 自优化编排器 (PowerShell)
+# 进化结果写入 evolution/ 文件夹,不修改 SKILL.md
+#
+# 用法:
+#   .\evolve.ps1              # 自动检测项目,执行进化
+#   .\evolve.ps1 -Project /path/to/novel   # 指定项目
+#   .\evolve.ps1 -Quick        # 快速模式(写后自动调用)
+#   .\evolve.ps1 -Full         # 完整进化(每卷/每10章)
+#   .\evolve.ps1 -Sync         # 将项目最优结果同步到 evolution/
+#   .\evolve.ps1 -Report       # 输出状态报告
+#   .\evolve.ps1 -Compile      # 编译:从事件日志重建进化状态
+# ═══════════════════════════════════════════════════════════════
+
+$ErrorActionPreference = "Stop"
+
+$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
+$SKILL_DIR = Split-Path -Parent $SCRIPT_DIR
+$EVOLUTION_DIR = Join-Path $SKILL_DIR "evolution"
+
+$PYTHON = if ($env:PYTHON) { $env:PYTHON } else { "python3" }
+$env:PYTHONPATH = "$SCRIPT_DIR$([IO.Path]::PathSeparator)$($env:PYTHONPATH)"
+
+# ── 日志函数 ──────────────────────────────────────────
+
+function Write-Log  { Write-Host "[evolve] $args" -ForegroundColor Blue }
+function Write-Ok   { Write-Host "[evolve] $([char]0x2713) $args" -ForegroundColor Green }
+function Write-Warn { Write-Host "[evolve] $([char]0x26A0) $args" -ForegroundColor Yellow }
+
+function Write-Banner {
+    Write-Host "┌─────────────────────────────────┐" -ForegroundColor Cyan
+    Write-Host "│  novel-write 自优化引擎          │" -ForegroundColor Cyan
+    Write-Host "│  输出: evolution/ (不修改 SKILL.md) │" -ForegroundColor Cyan
+    Write-Host "└─────────────────────────────────┘" -ForegroundColor Cyan
+}
+
+# ── Git 集成 ──────────────────────────────────────────
+
+function Test-IsGitRepo {
+    try {
+        git -C $SKILL_DIR rev-parse --git-dir 2>$null | Out-Null
+        return $true
+    } catch {
+        return $false
+    }
+}
+
+function Invoke-GitPull {
+    # 写前拉取:获取其他用户的最新进化数据
+    if (-not (Test-IsGitRepo)) { return }
+    try {
+        git -C $SKILL_DIR remote get-url origin 2>$null | Out-Null
+    } catch {
+        return
+    }
+
+    Write-Log "git pull: 获取共享进化数据..."
+    try {
+        git -C $SKILL_DIR pull --ff-only origin master 2>$null | Out-Null
+        Write-Ok "git pull 完成"
+
+        # 有新数据 → 编译所有已变更题材的事件
+        $diffOutput = git -C $SKILL_DIR diff --name-only 'HEAD@{1}..HEAD' -- evolution/genres/ 2>$null
+        if ($diffOutput) {
+            $changedGenres = $diffOutput | ForEach-Object {
+                if ($_ -match 'evolution/genres/([^/]+)') { $matches[1] }
+            } | Sort-Object -Unique
+            foreach ($gid in $changedGenres) {
+                Write-Log "  编译题材: $gid"
+                try { & $PYTHON "$SCRIPT_DIR\event_store.py" compile --genre $gid 2>$null } catch { }
+            }
+        }
+    } catch {
+        Write-Warn "git pull 失败(网络不通或无可更新),继续本地进化"
+    }
+}
+
+function Invoke-GitPushEvents {
+    # 写后推送:将本次进化事件提交并推送到远程
+    if (-not (Test-IsGitRepo)) { return }
+    try {
+        git -C $SKILL_DIR remote get-url origin 2>$null | Out-Null
+    } catch {
+        return
+    }
+
+    $hasChanges = git -C $SKILL_DIR status --porcelain -- evolution/ 2>$null
+    if (-not $hasChanges) {
+        Write-Log "git: 无变更,跳过推送"
+        return
+    }
+
+    Write-Log "git: 提交并推送进化数据..."
+
+    # 汇总变更的题材
+    $summary = ($hasChanges | ForEach-Object {
+        $_ -replace '.*evolution/', '' | ForEach-Object { ($_ -split '/')[0] }
+    } | Sort-Object -Unique) -join ','
+
+    try {
+        git -C $SKILL_DIR add evolution/ 2>$null | Out-Null
+        git -C $SKILL_DIR commit -m "evolve($($summary)): auto-commit evolution data" 2>$null | Out-Null
+
+        git -C $SKILL_DIR push origin master 2>$null | Out-Null
+        $fileCount = ($hasChanges | Measure-Object).Count
+        Write-Ok "git push 完成 ($fileCount 个文件)"
+    } catch {
+        Write-Warn "git push 失败(网络不通),下次推送会包含本次变更"
+    }
+}
+
+# ── 查找项目 ──────────────────────────────────────────
+
+function Find-Project {
+    param([string]$Target = ".")
+    try {
+        $d = (Resolve-Path $Target -ErrorAction Stop).Path
+    } catch {
+        $d = $Target
+    }
+
+    while ($d -and $d -ne (Split-Path $d -Qualifier)) {
+        if (Test-Path (Join-Path $d "项目铁律.md") -PathType Leaf) {
+            return $d
+        }
+        $parent = Split-Path $d -Parent
+        if ($parent -eq $d) { break }
+        $d = $parent
+    }
+    return $null
+}
+
+# ── 检测题材ID ────────────────────────────────────────
+
+function Get-GenreId {
+    param([string]$ProjectPath)
+    try {
+        $result = & $PYTHON -c @"
+import sys; sys.path.insert(0, '$($SCRIPT_DIR -replace '\\','/')')
+from utils import find_project_genre, map_genre_to_inkos_id
+from pathlib import Path
+print(map_genre_to_inkos_id(find_project_genre(Path('$($ProjectPath -replace '\\','/')'))))
+"@ 2>$null
+        if ($result) { return $result.Trim() }
+    } catch { }
+    return "other"
+}
+
+# ── 阶段实现 ──────────────────────────────────────────
+
+function Invoke-PhaseCollect {
+    param([string]$Project)
+    Write-Log "收集: $(Split-Path $Project -Leaf)"
+
+    $chapterDir = Join-Path $Project "正文"
+    $chapterCount = 0
+    if (Test-Path $chapterDir) {
+        $chapterCount = (Get-ChildItem -Path $chapterDir -Filter "*第*章*.md" -Recurse -ErrorAction SilentlyContinue | Measure-Object).Count
+    }
+    $hasHooks = if (Test-Path (Join-Path $Project "追踪/钩子账本.md")) { "$([char]0x2713)" } else { "$([char]0x2717)" }
+    $hasQuality = if (Test-Path (Join-Path $Project "追踪/优化日志.md")) { "$([char]0x2713)" } else { "$([char]0x2717)" }
+    $hasEvolution = if (Test-Path (Join-Path $Project "evolution")) { "$([char]0x2713)" } else { "$([char]0x2717)" }
+
+    Write-Log "  章节: $chapterCount | 钩子: $hasHooks | 质检: $hasQuality | 进化: $hasEvolution"
+
+    New-Item -ItemType Directory -Force -Path (Join-Path $Project "追踪"), (Join-Path $EVOLUTION_DIR "prompts"), (Join-Path $EVOLUTION_DIR "patterns") | Out-Null
+
+    # 检测题材,确保题材级进化目录存在
+    $genreId = Get-GenreId -ProjectPath $Project
+    Write-Log "  题材: $genreId"
+
+    # 首次使用:初始化题材进化目录
+    $genreDir = Join-Path $EVOLUTION_DIR "genres" $genreId
+    if (-not (Test-Path $genreDir)) {
+        Write-Log "  初始化题材进化目录: evolution/genres/$genreId/"
+        try {
+            & $PYTHON -c @"
+import sys; sys.path.insert(0, '$($SCRIPT_DIR -replace '\\','/')')
+from seed_extractor import SeedExtractor
+SeedExtractor().initialize_genre('$genreId')
+"@ 2>$null
+        } catch { }
+    }
+}
+
+function Invoke-PhaseAnalyze {
+    param([string]$Project)
+    Write-Log "分析..."
+
+    # 质量阈值分析(有质检数据时)
+    if (Test-Path (Join-Path $Project "追踪/优化日志.md")) {
+        try { & $PYTHON "$SCRIPT_DIR\quality_tuner.py" analyze --project "$Project" 2>$null } catch { }
+    }
+
+    # 写作模式挖掘(有章节时)
+    $chapterDir = Join-Path $Project "正文"
+    if ((Test-Path $chapterDir) -and (Get-ChildItem -Path $chapterDir -Filter "*第*章*.md" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1)) {
+        try { & $PYTHON "$SCRIPT_DIR\pattern_mine.py" mine --project "$Project" --min-score 65 2>$null } catch { }
+    }
+
+    Write-Ok "分析完成"
+}
+
+function Invoke-PhaseMutate {
+    param([string]$Project = "")
+    Write-Log "变异: 提示词进化..."
+
+    $gen = if ($env:GENERATIONS) { $env:GENERATIONS } else { "2" }
+
+    if ($Project) {
+        try { & $PYTHON "$SCRIPT_DIR\prompt_evolver.py" evolve --project "$Project" --generations $gen 2>$null } catch { }
+    } else {
+        try { & $PYTHON "$SCRIPT_DIR\prompt_evolver.py" evolve --generations $gen 2>$null } catch { }
+    }
+
+    Write-Ok "变异完成"
+}
+
+function Invoke-PhaseReport {
+    param([string]$Project = "")
+    Write-Host ""
+    Write-Host "═══ 自优化状态 ═══" -ForegroundColor Cyan
+    Write-Host ""
+
+    # 阈值
+    Write-Host "## 质量阈值 (evolution/thresholds.json)"
+    try {
+        & $PYTHON "$SCRIPT_DIR\quality_tuner.py" show 2>$null
+    } catch {
+        Write-Host "  使用默认阈值"
+    }
+
+    # 题材级
+    if ($Project) {
+        $genreId = Get-GenreId -ProjectPath $Project
+        if ($genreId) {
+            $genreDir = Join-Path $EVOLUTION_DIR "genres" $genreId
+            if (Test-Path $genreDir) {
+                Write-Host ""
+                Write-Host "## 题材级进化 (evolution/genres/$genreId/)" -ForegroundColor Cyan
+                Write-Host "  目录: $genreDir"
+                Get-ChildItem -Path $genreDir -Filter "*.json" -ErrorAction SilentlyContinue | ForEach-Object {
+                    Write-Host "    $($_.Name)"
+                }
+                Write-Host ""
+                Write-Host "  最优提示词:"
+                try { & $PYTHON "$SCRIPT_DIR\prompt_evolver.py" show --genre $genreId 2>$null } catch { }
+            }
+        }
+    }
+
+    # 提示词(skill级)
+    Write-Host ""
+    Write-Host "## 最优提示词 (evolution/prompts/current_best.json)"
+    try {
+        & $PYTHON "$SCRIPT_DIR\prompt_evolver.py" show 2>$null
+    } catch {
+        Write-Host "  使用种子提示词"
+    }
+
+    # 模式库
+    Write-Host ""
+    Write-Host "## 写作模式库 (evolution/patterns/pattern_library.json)"
+    try {
+        & $PYTHON "$SCRIPT_DIR\pattern_mine.py" show --top 5 2>$null
+    } catch {
+        Write-Host "  暂无模式数据"
+    }
+
+    # 会话状态
+    Write-Host ""
+    Write-Host "## 会话状态 (evolution/session_state.json)"
+    $sessionFile = Join-Path $EVOLUTION_DIR "session_state.json"
+    if (Test-Path $sessionFile) {
+        try {
+            & $PYTHON -c @"
+import json
+state = json.loads(open('$($sessionFile -replace '\\','/')').read())
+print(f'  反馈数: {state.get(\"total_feedback_count\", 0)}')
+recent = state.get('recent_chapters', [])
+if recent:
+    scores = [r['composite'] for r in recent[-5:]]
+    print(f'  最近5章均分: {sum(scores)/len(scores):.0f}')
+"@ 2>$null
+        } catch { }
+    } else {
+        Write-Host "  暂无"
+    }
+}
+
+function Invoke-PhaseSync {
+    param([string]$Project = "")
+    Write-Log "同步最优结果到 evolution/..."
+
+    # 提示词进化 + 同步
+    try { & $PYTHON "$SCRIPT_DIR\prompt_evolver.py" evolve --generations 1 2>$null } catch { }
+
+    # 质量阈值同步
+    if ($Project -and (Test-Path (Join-Path $Project "追踪/优化日志.md"))) {
+        try { & $PYTHON "$SCRIPT_DIR\quality_tuner.py" analyze --project "$Project" 2>$null } catch { }
+    }
+
+    # 模式库同步
+    if ($Project -and (Test-Path (Join-Path $Project "正文"))) {
+        try { & $PYTHON "$SCRIPT_DIR\pattern_mine.py" mine --project "$Project" 2>$null } catch { }
+    }
+
+    Write-Ok "同步完成 → evolution/"
+}
+
+# ── 主流程 ─────────────────────────────────────────────
+
+function Main {
+    param(
+        [string]$Project = "",
+        [string]$Mode = "Quick",
+        [switch]$Quick,
+        [switch]$Full,
+        [switch]$Sync,
+        [switch]$Report,
+        [switch]$Compile
+    )
+
+    Write-Banner
+
+    # 写前拉取:获取其他用户的最新进化数据
+    Invoke-GitPull
+
+    # 解析模式
+    if ($Quick)    { $Mode = "Quick" }
+    if ($Full)     { $Mode = "Full" }
+    if ($Sync)     { $Mode = "Sync" }
+    if ($Report)   { $Mode = "Report" }
+    if ($Compile)  { $Mode = "Compile" }
+
+    # 查找项目
+    if (-not $Project) {
+        $Project = Find-Project -Target "."
+    }
+
+    switch ($Mode) {
+        "Quick" {
+            if ($Project) {
+                Invoke-PhaseCollect -Project $Project
+                Invoke-PhaseAnalyze -Project $Project
+                Invoke-PhaseMutate -Project $Project
+            }
+            Invoke-PhaseSync -Project $Project
+        }
+
+        "Full" {
+            if (-not $Project) {
+                Write-Warn "未找到小说项目。仅执行 skill 级进化。"
+                Invoke-PhaseMutate -Project ""
+            } else {
+                Invoke-PhaseCollect -Project $Project
+                Invoke-PhaseAnalyze -Project $Project
+                Invoke-PhaseMutate -Project $Project
+                Invoke-PhaseSync -Project $Project
+            }
+            Write-Log "完整进化完成。下次建议: 每写完一卷(或每10章)执行。"
+        }
+
+        "Sync" {
+            Invoke-PhaseSync -Project $Project
+        }
+
+        "Report" {
+            Invoke-PhaseReport -Project $Project
+        }
+
+        "Compile" {
+            Write-Log "编译: 从事件日志重建进化状态..."
+            if ($Project) {
+                $genreId = Get-GenreId -ProjectPath $Project
+                if ($genreId) {
+                    & $PYTHON "$SCRIPT_DIR\event_store.py" compile --genre $genreId
+                }
+            }
+            Write-Host ""
+            Write-Host "  提示: git pull 后执行此命令即可获得最新共享状态。"
+        }
+
+        default {
+            # 默认 Quick
+            if ($Project) {
+                Invoke-PhaseCollect -Project $Project
+                Invoke-PhaseAnalyze -Project $Project
+                Invoke-PhaseMutate -Project $Project
+            }
+            Invoke-PhaseSync -Project $Project
+        }
+    }
+
+    Write-Ok "自优化完成"
+    Write-Host ""
+    Write-Host "  evolution/ 目录内容 (skill级):"
+    Get-ChildItem -Path $EVOLUTION_DIR -Filter "*.json" -ErrorAction SilentlyContinue | ForEach-Object {
+        Write-Host "    $($_.Name)"
+    }
+    Get-ChildItem -Path $EVOLUTION_DIR -Filter "*.md" -ErrorAction SilentlyContinue | ForEach-Object {
+        Write-Host "    $($_.Name)"
+    }
+
+    if ($Project) {
+        $genreId = Get-GenreId -ProjectPath $Project
+        if ($genreId) {
+            $genreDir = Join-Path $EVOLUTION_DIR "genres" $genreId
+            if (Test-Path $genreDir) {
+                Write-Host ""
+                Write-Host "  evolution/ 目录内容 (题材级: $genreId):"
+                Get-ChildItem -Path $genreDir -Recurse -Filter "*.json" -ErrorAction SilentlyContinue | ForEach-Object {
+                    $relPath = $_.FullName.Substring($genreDir.Length + 1)
+                    Write-Host "    $relPath"
+                }
+            }
+        }
+    }
+
+    # 写后推送:将本次进化事件自动提交到 git
+    Invoke-GitPushEvents
+}
+
+# ── 入口 ───────────────────────────────────────────────
+
+param(
+    [string]$Project = "",
+    [switch]$Quick,
+    [switch]$Full,
+    [switch]$Sync,
+    [switch]$Report,
+    [switch]$Compile
+)
+
+Main -Project $Project -Quick:$Quick -Full:$Full -Sync:$Sync -Report:$Report -Compile:$Compile