# kg-tools 安装/卸载/更新脚本 (被 install.bat 调用)
# 用法: install.bat              # 安装所有工具
#       install.bat ascend-kg    # 仅安装 ascend-kg
#       install.bat -Uninstall   # 卸载所有工具
#       install.bat -Update      # 更新

param(
    [string]$Skill = "",
    [switch]$Force = $false,
    [switch]$Uninstall = $false,
    [switch]$Update = $false,
    [switch]$Claude = $false,
    [switch]$Cursor = $false,
    [switch]$Codex = $false,
    [switch]$Cline = $false,
    [switch]$OpenCode = $false,
    [switch]$Copilot = $false,
    [switch]$Trae = $false,
    [switch]$OpenClaw = $false,
    [switch]$Hermes = $false,
    [switch]$Help = $false
)

if ($Help) {
    Write-Host "用法: install.bat [工具名] [选项]"
    Write-Host ""
    Write-Host "  无参数           自动检测 agent,安装 skills\ 下所有工具"
    Write-Host "  ascend-kg        仅安装指定工具"
    Write-Host "  -Force           覆盖已有安装"
    Write-Host "  -Uninstall       卸载"
    Write-Host "  -Update          更新(git pull + 覆盖重装)"
    Write-Host "  -Claude          仅操作 Claude Code"
    Write-Host "  -Cursor          仅操作 Cursor"
    Write-Host "  -Codex           仅操作 Codex (OpenAI)"
    Write-Host "  -Cline           仅操作 Cline"
    Write-Host "  -OpenCode        仅操作 OpenCode"
    Write-Host "  -Copilot         仅操作 GitHub Copilot (.github\copilot-instructions.md)"
    Write-Host "  -Trae            仅操作 Trae (.trae\rules)"
    Write-Host "  -OpenClaw        仅操作 OpenClaw (~\.openclaw\skills)"
    Write-Host "  -Hermes          仅操作 Hermes (~\.hermes\skills + HERMES.md)"
    Write-Host ""
    Write-Host "示例:"
    Write-Host "  install.bat                      # 全部工具"
    Write-Host "  install.bat ascend-kg            # 仅 ascend-kg"
    Write-Host "  install.bat ascend-kg -Claude    # ascend-kg x Claude Code"
    Write-Host "  install.bat ascend-kg -Uninstall # 卸载 ascend-kg"
    Write-Host "  install.bat -Update              # 更新全部工具"
    return
}

if ($Update) { $Force = $true }

$ErrorActionPreference = "Stop"

$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$SkillsDir = Join-Path $ScriptDir "skills"
$HomeDir = $env:HOME
if (-not $HomeDir) { $HomeDir = $env:USERPROFILE }

# 规则文件型 agent 无原生 skill 目录,全量 skill 统一落到此便携目录
$PortableBase = Join-Path $HomeDir ".kg-tools\skills"

$skills = @()
if ($Skill) {
    $skills += $Skill
} else {
    if (Test-Path $SkillsDir) {
        Get-ChildItem $SkillsDir -Directory | ForEach-Object { $skills += $_.Name }
    }
}

if ($skills.Count -eq 0) {
    Write-Host "skills\ 下没有可安装的工具"
    return
}

$target = ""
if ($Claude) { $target = "claude" }
if ($Cursor) { $target = "cursor" }
if ($Codex) { $target = "codex" }
if ($Cline) { $target = "cline" }
if ($OpenCode) { $target = "opencode" }
if ($Copilot) { $target = "copilot" }
if ($Trae) { $target = "trae" }
if ($OpenClaw) { $target = "openclaw" }
if ($Hermes) { $target = "hermes" }

# ── 安装器 ──

# ── 复制 skill 全量目录(SKILL.md + evals/engine/references/experience/scripts + test-prompts.json)──
function Copy-SkillDir($pluginDir, $dst) {
    if (Test-Path $dst) { Remove-Item -Recurse -Force $dst }
    New-Item -ItemType Directory -Force -Path $dst | Out-Null
    Copy-Item (Join-Path $pluginDir "SKILL.md") $dst
    # scripts/ 含 scripts/check_update.sh(SKILL.md 首触自检依赖)——漏拷会致残缺安装
    foreach ($sub in @("evals", "engine", "references", "experience", "scripts")) {
        $src = Join-Path $pluginDir $sub
        if (Test-Path $src) { Copy-Item -Recurse $src $dst }
    }
    $tp = Join-Path $pluginDir "test-prompts.json"
    if (Test-Path $tp) { Copy-Item $tp $dst }
    # 源仓库定位戳(scripts/check_update.sh 凭此找源仓做更新自检)——install.sh
    # 版有此戳而 ps1 漏写,Windows 安装后自检永远定位不到源仓。正斜杠 + 无换行
    # 写入:CRLF 行尾或反斜杠路径会让 bash 侧 -d/git -C 全部判"路径不存在"。
    $repoRoot = Split-Path -Parent (Split-Path -Parent $pluginDir)
    [System.IO.File]::WriteAllText((Join-Path $dst ".repo_source"), ($repoRoot -replace '\\', '/'), (New-Object System.Text.UTF8Encoding $false))
}

function Portable-Ptr($name) {
    "> 📌 ascend-kg 完整目录已安装到 ``$PortableBase\$name``(下文 ``references/``、``experience/``、``<skill>`` 均指此目录)"
}

function Install-Claude($name, $pluginDir) {
    $skillDir = Join-Path $HomeDir ".claude\skills\$name"
    $agentsDir = Join-Path $HomeDir ".claude\agents"
    $agentSrc = Join-Path $pluginDir "agents\$name-worker.md"
    $agentDst = Join-Path $agentsDir "$name-worker.md"
    $skillMd = Join-Path $skillDir "SKILL.md"

    # skill: ~/.claude/skills/<name>/(SKILL.md + evals/ + engine/ + references/ + experience/ + test-prompts.json 全量)
    if ((Test-Path $skillMd) -and -not $Force) {
        Write-Host "  -  Claude Code skill (已存在,跳过)"
        return
    }
    Copy-SkillDir $pluginDir $skillDir

    # subagent: ~/.claude/agents/<name>-worker.md(自带铁律 system prompt,抗上下文压缩)
    New-Item -ItemType Directory -Force -Path $agentsDir | Out-Null
    if (Test-Path $agentSrc) { Copy-Item $agentSrc $agentDst }

    # CLAUDE.md 铁律块(始终加载的决策层,补"主 agent 不自动触发 skill"的洞)
    Install-ClaudeMemory $pluginDir

    Write-Host "  +  Claude Code  ->  $skillDir (skill) + $agentDst (agent) + CLAUDE.md 铁律"
}

function Install-ClaudeMemory($pluginDir) {
    $claudeMd = Join-Path $HomeDir ".claude\CLAUDE.md"
    $block = Join-Path $pluginDir "claude-memory.md"
    if (-not (Test-Path $block)) { return }
    New-Item -ItemType Directory -Force -Path (Split-Path $claudeMd) | Out-Null
    $start = '<!-- ascend-kg:START -->'
    $end = '<!-- ascend-kg:END -->'
    if (Test-Path $claudeMd) {
        $raw = Get-Content $claudeMd -Raw -ErrorAction SilentlyContinue
        if ($null -eq $raw -or $raw -eq "") { $raw = "" }
    } else { $raw = "" }
    if ($raw -ne "") {
        $pattern = "(?s)" + [regex]::Escape($start) + ".*?" + [regex]::Escape($end) + "\r?\n?"
        $raw = $raw -replace $pattern, ""
        if (-not $raw.EndsWith("`n")) { $raw += "`n" }
    }
    $raw += (Get-Content $block -Raw)
    [System.IO.File]::WriteAllText($claudeMd, $raw, (New-Object System.Text.UTF8Encoding $false))
}

function Install-Cursor($skillMd, $name, $pluginDir) {
    $dir = ".cursor\rules"
    $file = Join-Path $dir "$name.mdc"
    $header = "---`nalwaysApply: true`n---`n`n"
    if ((Test-Path $file) -and -not $Force) {
        Write-Host "  -  Cursor (已存在,跳过)"
        return
    }
    New-Item -ItemType Directory -Force -Path $dir | Out-Null
    Install-Portable $pluginDir $name
    $header + (Portable-Ptr $name) + "`n`n" + (Get-Content $skillMd -Raw) | Set-Content $file
    Write-Host "  +  Cursor  ->  $file (alwaysApply: true) + 便携目录 $PortableBase\$name"
}

function Install-Codex($skillMd, $pluginDir, $name) {
    $file = "AGENTS.md"
    if ((Test-Path $file) -and -not $Force) {
        Write-Host "  -  Codex (已存在,跳过)"
        return
    }
    Install-Portable $pluginDir $name
    (Portable-Ptr $name) + "`n`n" + (Get-Content $skillMd -Raw) | Set-Content $file
    Write-Host "  +  Codex  ->  $file + 便携目录 $PortableBase\$name"
}

function Install-HermesMemory($pluginDir) {
    $hermesMd = "HERMES.md"
    $block = Join-Path $pluginDir "hermes-memory.md"
    if (-not (Test-Path $block)) { return }
    $start = '<!-- ascend-kg:START -->'
    $end = '<!-- ascend-kg:END -->'
    if (Test-Path $hermesMd) {
        $raw = Get-Content $hermesMd -Raw -ErrorAction SilentlyContinue
        if ($null -eq $raw -or $raw -eq "") { $raw = "" }
    } else { $raw = "" }
    if ($raw -ne "") {
        $pattern = "(?s)" + [regex]::Escape($start) + ".*?" + [regex]::Escape($end) + "\r?\n?"
        $raw = $raw -replace $pattern, ""
        if (-not $raw.EndsWith("`n")) { $raw += "`n" }
    }
    $raw += (Get-Content $block -Raw)
    [System.IO.File]::WriteAllText($hermesMd, $raw, (New-Object System.Text.UTF8Encoding $false))
}

function Install-Hermes($pluginDir) {
    $skillDir = Join-Path $HomeDir ".hermes\skills\ascend-kg"
    $skillMd = Join-Path $skillDir "SKILL.md"

    # skill: ~/.hermes/skills/ascend-kg/(SKILL.md + evals/ + engine/ + references/ + experience/ + test-prompts.json 全量)
    if ((Test-Path $skillMd) -and -not $Force) {
        Write-Host "  -  Hermes skill (已存在,跳过)"
        return
    }
    Copy-SkillDir $pluginDir $skillDir

    # HERMES.md 触发指针(薄)
    Install-HermesMemory $pluginDir

    Write-Host "  +  Hermes  ->  $skillDir (skill) + HERMES.md 触发指针"
}

# ── 便携目录:规则文件型 agent 无原生 skill 目录,全量 skill 落到 $PortableBase\<name>\ ──
function Install-Portable($pluginDir, $name) {
    $dir = Join-Path $PortableBase $name
    $skillMd = Join-Path $dir "SKILL.md"
    if ((Test-Path $skillMd) -and -not $Force) { return }
    Copy-SkillDir $pluginDir $dir
}

function Install-Cline($skillMd, $pluginDir, $name) {
    $file = ".clinerules"
    if ((Test-Path $file) -and -not $Force) {
        Write-Host "  -  Cline (已存在,跳过)"
        return
    }
    Install-Portable $pluginDir $name
    (Portable-Ptr $name) + "`n`n" | Add-Content $file
    Get-Content $skillMd -Raw | Add-Content $file
    Write-Host "  +  Cline  ->  $file + 便携目录 $PortableBase\$name"
}

function Install-OpenCode($pluginDir, $name) {
    $dir = Join-Path $env:USERPROFILE ".config\opencode\skills\$name"
    $file = Join-Path $dir "SKILL.md"
    if ((Test-Path $file) -and -not $Force) {
        Write-Host "  -  OpenCode (已存在,跳过)"
        return
    }
    Copy-SkillDir $pluginDir $dir
    Write-Host "  +  OpenCode  ->  $dir (skill 全量)"
}

function Install-OpenClaw($pluginDir, $name) {
    $dir = Join-Path $HomeDir ".openclaw\skills\$name"
    $file = Join-Path $dir "SKILL.md"
    if ((Test-Path $file) -and -not $Force) {
        Write-Host "  -  OpenClaw (已存在,跳过)"
        return
    }
    Copy-SkillDir $pluginDir $dir
    Write-Host "  +  OpenClaw  ->  $dir (skill 全量)"
}

function Install-Copilot($skillMd, $pluginDir, $name) {
    $dir = ".github"
    $file = Join-Path $dir "copilot-instructions.md"
    if ((Test-Path $file) -and -not $Force) {
        Write-Host "  -  GitHub Copilot (已存在,跳过)"
        return
    }
    New-Item -ItemType Directory -Force -Path $dir | Out-Null
    Install-Portable $pluginDir $name
    (Portable-Ptr $name) + "`n`n" + (Get-Content $skillMd -Raw) | Set-Content $file
    Write-Host "  +  GitHub Copilot  ->  $file + 便携目录 $PortableBase\$name"
}

function Install-Trae($skillMd, $name, $pluginDir) {
    $dir = ".trae\rules"
    $file = Join-Path $dir "$name.md"
    $header = "---`nalwaysApply: true`n---`n`n"
    if ((Test-Path $file) -and -not $Force) {
        Write-Host "  -  Trae (已存在,跳过)"
        return
    }
    New-Item -ItemType Directory -Force -Path $dir | Out-Null
    Install-Portable $pluginDir $name
    $header + (Portable-Ptr $name) + "`n`n" + (Get-Content $skillMd -Raw) | Set-Content $file
    Write-Host "  +  Trae  ->  $file (alwaysApply: true) + 便携目录 $PortableBase\$name"
}

# ── 卸载器 ──

function Uninstall-Claude($name) {
    $skillDir = Join-Path $HomeDir ".claude\skills\$name"
    $agentDst = Join-Path $HomeDir ".claude\agents\$name-worker.md"
    $removed = $false
    if (Test-Path $skillDir) {
        Remove-Item -Recurse -Force $skillDir
        Write-Host "  -  已移除 Claude Code skill: $skillDir"
        $removed = $true
    }
    if (Test-Path $agentDst) {
        Remove-Item -Force $agentDst
        Write-Host "  -  已移除 Claude Code agent: $agentDst"
        $removed = $true
    }
    $claudeMd = Join-Path $HomeDir ".claude\CLAUDE.md"
    if ((Test-Path $claudeMd) -and ((Get-Content $claudeMd -Raw -ErrorAction SilentlyContinue) -match '<!-- ascend-kg:START -->')) {
        $raw = Get-Content $claudeMd -Raw -ErrorAction SilentlyContinue
        $start = '<!-- ascend-kg:START -->'; $end = '<!-- ascend-kg:END -->'
        $pattern = "(?s)" + [regex]::Escape($start) + ".*?" + [regex]::Escape($end) + "\r?\n?"
        $raw = $raw -replace $pattern, ""
        [System.IO.File]::WriteAllText($claudeMd, $raw, (New-Object System.Text.UTF8Encoding $false))
        Write-Host "  -  已移除 Claude CLAUDE.md 铁律块"
        $removed = $true
        $after = Get-Content $claudeMd -Raw -ErrorAction SilentlyContinue
        if ($null -ne $after -and $after -match '^\s*$') { Remove-Item -Force $claudeMd }
    }
    if (-not $removed) { Write-Host "  -  Claude Code: 未安装" }
}

function Uninstall-Cursor($name) {
    $file = ".cursor\rules\$name.mdc"
    if (Test-Path $file) {
        Remove-Item -Force $file
        Write-Host "  -  已移除 Cursor: $file"
    } else {
        Write-Host "  -  Cursor: 未安装"
    }
}

function Uninstall-Codex {
    if (Test-Path "AGENTS.md") {
        Write-Host "  !  Codex: AGENTS.md 可能含其他工具内容,请手动清理"
    } else {
        Write-Host "  -  Codex: 未安装"
    }
}

function Uninstall-Hermes {
    $skillDir = Join-Path $HomeDir ".hermes\skills\ascend-kg"
    $removed = $false
    if (Test-Path $skillDir) {
        Remove-Item -Recurse -Force $skillDir
        Write-Host "  -  已移除 Hermes skill: $skillDir"
        $removed = $true
    }
    $hermesMd = "HERMES.md"
    if ((Test-Path $hermesMd) -and ((Get-Content $hermesMd -Raw -ErrorAction SilentlyContinue) -match '<!-- ascend-kg:START -->')) {
        $raw = Get-Content $hermesMd -Raw -ErrorAction SilentlyContinue
        $start = '<!-- ascend-kg:START -->'; $end = '<!-- ascend-kg:END -->'
        $pattern = "(?s)" + [regex]::Escape($start) + ".*?" + [regex]::Escape($end) + "\r?\n?"
        $raw = $raw -replace $pattern, ""
        [System.IO.File]::WriteAllText($hermesMd, $raw, (New-Object System.Text.UTF8Encoding $false))
        Write-Host "  -  已移除 Hermes HERMES.md 触发指针块"
        $removed = $true
        $after = Get-Content $hermesMd -Raw -ErrorAction SilentlyContinue
        if ($null -ne $after -and $after -match '^\s*$') { Remove-Item -Force $hermesMd }
    }
    if (-not $removed) { Write-Host "  -  Hermes: 未安装" }
}

function Uninstall-Cline {
    if (Test-Path ".clinerules") {
        Write-Host "  !  Cline: .clinerules 可能含其他工具内容,请手动清理"
    } else {
        Write-Host "  -  Cline: 未安装"
    }
}

function Uninstall-OpenCode {
    $dir = Join-Path $env:USERPROFILE ".config\opencode\skills\ascend-kg"
    if (Test-Path $dir) {
        Remove-Item -Force -Recurse $dir
        Write-Host "  -  已移除 OpenCode: $dir"
    } else {
        Write-Host "  -  OpenCode: 未安装"
    }
}

function Uninstall-OpenClaw {
    $dir = Join-Path $HomeDir ".openclaw\skills\ascend-kg"
    if (Test-Path $dir) {
        Remove-Item -Force -Recurse $dir
        Write-Host "  -  已移除 OpenClaw: $dir"
    } else {
        Write-Host "  -  OpenClaw: 未安装"
    }
}

function Uninstall-Copilot {
    $file = ".github\copilot-instructions.md"
    if (Test-Path $file) {
        Remove-Item -Force $file
        Write-Host "  -  已移除 GitHub Copilot: $file"
    } else {
        Write-Host "  -  GitHub Copilot: 未安装"
    }
}

function Uninstall-Trae($name) {
    $file = ".trae\rules\$name.md"
    if (Test-Path $file) {
        Remove-Item -Force $file
        Write-Host "  -  已移除 Trae: $file"
    } else {
        Write-Host "  -  Trae: 未安装"
    }
}

# 便携目录为规则文件型 agent 共享,仅在「卸载全部」时移除,避免误删其他 agent 仍在用的目录
function Uninstall-Portable($name) {
    $dir = Join-Path $PortableBase $name
    if (Test-Path $dir) {
        Remove-Item -Force -Recurse $dir
        Write-Host "  -  已移除便携目录: $dir"
    }
}

function Uninstall-OneSkill($name) {
    Write-Host ""
    Write-Host "---  $name ---"
    if ($target -eq "claude") {
        Uninstall-Claude $name
    } elseif ($target -eq "cursor") {
        Uninstall-Cursor $name
    } elseif ($target -eq "codex") {
        Uninstall-Codex
    } elseif ($target -eq "cline") {
        Uninstall-Cline
    } elseif ($target -eq "opencode") {
        Uninstall-OpenCode
    } elseif ($target -eq "copilot") {
        Uninstall-Copilot
    } elseif ($target -eq "trae") {
        Uninstall-Trae $name
    } elseif ($target -eq "openclaw") {
        Uninstall-OpenClaw
    } elseif ($target -eq "hermes") {
        Uninstall-Hermes
    } else {
        Uninstall-Claude $name
        Uninstall-Cursor $name
        Uninstall-Codex
        Uninstall-Cline
        Uninstall-OpenCode
        Uninstall-Copilot
        Uninstall-Trae $name
        Uninstall-OpenClaw
        Uninstall-Hermes
        Uninstall-Portable $name
    }
}

function Install-OneSkill($name) {
    $plugin = Join-Path $SkillsDir $name
    $skillMd = Join-Path $plugin "SKILL.md"
    if (-not (Test-Path $skillMd)) {
        Write-Host "  !  $name : 找不到 $skillMd,跳过"
        return
    }
    Write-Host ""
    Write-Host "---  $name ---"
    if ($target -eq "claude") {
        Install-Claude $name $plugin
    } elseif ($target -eq "cursor") {
        Install-Cursor $skillMd $name $plugin
    } elseif ($target -eq "codex") {
        Install-Codex $skillMd $plugin $name
    } elseif ($target -eq "cline") {
        Install-Cline $skillMd $plugin $name
    } elseif ($target -eq "opencode") {
        Install-OpenCode $plugin $name
    } elseif ($target -eq "copilot") {
        Install-Copilot $skillMd $plugin $name
    } elseif ($target -eq "trae") {
        Install-Trae $skillMd $name $plugin
    } elseif ($target -eq "openclaw") {
        Install-OpenClaw $plugin $name
    } elseif ($target -eq "hermes") {
        Install-Hermes $plugin
    } else {
        if (Test-Path (Join-Path $HomeDir ".claude")) {
            Install-Claude $name $plugin
        } else {
            Write-Host "  -  Claude Code (未检测到)"
        }
        if (Test-Path ".cursor") {
            Install-Cursor $skillMd $name $plugin
        } else {
            Write-Host "  -  Cursor (未检测到)"
        }
        if ((Test-Path "AGENTS.md") -or (Test-Path ".codex")) {
            Install-Codex $skillMd $plugin $name
        } else {
            Write-Host "  -  Codex (未检测到)"
        }
        if (Test-Path ".clinerules") {
            Install-Cline $skillMd $plugin $name
        } else {
            Write-Host "  -  Cline (未检测到)"
        }
        if (Test-Path (Join-Path $env:USERPROFILE ".config\opencode")) {
            Install-OpenCode $plugin $name
        } else {
            Write-Host "  -  OpenCode (未检测到)"
        }
        if (Test-Path (Join-Path $HomeDir ".openclaw")) {
            Install-OpenClaw $plugin $name
        } else {
            Write-Host "  -  OpenClaw (未检测到)"
        }
        if (Test-Path ".trae") {
            Install-Trae $skillMd $name $plugin
        } else {
            Write-Host "  -  Trae (未检测到)"
        }
        if (Test-Path (Join-Path $HomeDir ".hermes")) {
            Install-Hermes $plugin
        } else {
            Write-Host "  -  Hermes (未检测到)"
        }
        # GitHub Copilot: .github 是通用目录,不自动检测,需 -Copilot 显式启用
    }
}

# ── 主流程 ──

if ($Update) {
    Write-Host ""
    Write-Host "正在更新 kg-tools..."
    $gitDir = Join-Path $ScriptDir ".git"
    if (Test-Path $gitDir) {
        $headBefore = git -C $ScriptDir rev-parse HEAD 2>$null
        git -C $ScriptDir pull 2>$null
        if ($LASTEXITCODE -eq 0) {
            Write-Host "已拉取最新代码"
            # 代码真的更新(HEAD 前移)时,install.ps1/复制逻辑自身可能也被更新——
            # 本进程仍是旧逻辑(「旧逻辑装新代码」竞态,同 install.sh)。PowerShell
            # 顶层无法可靠透传参数自重调,改为显式提示用户再跑一次完成升级。
            $headAfter = git -C $ScriptDir rev-parse HEAD 2>$null
            if ($headBefore -and ($headBefore -ne $headAfter)) {
                Write-Host "⚠️  代码已更新:本次可能仍用旧安装逻辑,请再执行一次 install.bat -Update 完成升级"
            }
        } else {
            Write-Host "git pull 失败,将使用本地文件重装"
        }
    } else {
        Write-Host "非 git 仓库,将使用本地文件重装"
    }
}

if ($Uninstall) {
    Write-Host "目标工具: $($skills -join ', ')"
    foreach ($s in $skills) { Uninstall-OneSkill $s }
    Write-Host ""
    Write-Host "卸载完成。Claude Code 需重启或 /reload-plugins。"
    return
}

Write-Host "目标工具: $($skills -join ', ')"

foreach ($s in $skills) { Install-OneSkill $s }

Write-Host ""
Write-Host "安装完成。"
Write-Host "- Claude Code 需重启或 /reload-plugins(让 /agents 发现 ascend-kg-worker)"
Write-Host "- Cursor / Trae / Cline / Codex / Hermes / OpenCode / OpenClaw / Copilot 下次启动自动生效"
Write-Host '- 设置 API Key: $env:ASCEND_KG_API_KEY="你的Key"'
Write-Host "  免费申请: https://ascend.wiki/register"