private/Write-WayforgeKimiAdapter.ps1

function Write-WayforgeKimiAdapter {
    <#
    .SYNOPSIS
        Generates a Kimi Code config snippet (.workflow/harness/kimi.config.toml)
        for the user to add to their global ~/.kimi-code/config.toml.
    .DESCRIPTION
        Kimi reads hooks and permission rules ONLY from the global
        ~/.kimi-code/config.toml — there is no project-scoped hooks file. Rather
        than mutate the user's global environment, this emits the exact TOML to
        add (PreToolUse hook + forbid-derived permission.rules) and warns.
    #>

    param([string] $ProjectRoot, [string] $WorkflowName)

    $set    = Get-WayforgeGateSet -Root $ProjectRoot -WorkflowName $WorkflowName
    $stages = Get-WayforgeStages -Gates $set.Gates

    # The PreToolUse hook is the source of truth: it evaluates the complete
    # Wayforge model (on/when/severity/all forbid dimensions). Static permission
    # rules are emitted only as fail-closed defense-in-depth, and only for forbids
    # Kimi can represent EXACTLY: unconditional (when: always), blocking, pre-tool
    # path guardrails. Anything conditional or non-pre-tool stays hook-only, so we
    # never broaden a gate's semantics into an always-active project denial.
    $rules = [System.Collections.Generic.List[string]]::new()
    foreach ($gate in $set.Gates) {
        $on       = @(Get-WayforgeField $gate 'on')
        $when     = Get-WayforgeField $gate 'when' 'always'
        $severity = Get-WayforgeField $gate 'severity' 'block'
        if (('pre-tool' -notin $on) -or ($when -ne 'always') -or ($severity -ne 'block')) { continue }

        $forbid = Get-WayforgeField (Get-WayforgeField $gate 'check') 'forbid'
        if ($forbid) {
            $paths = @(Get-WayforgeField $forbid 'path' | Where-Object { $_ })
            $tools = @(Get-WayforgeField $forbid 'tool' | Where-Object { $_ })
            if (-not $tools) { $tools = @('edit', 'write') }
            foreach ($p in $paths) {
                foreach ($t in $tools) {
                    $cap = switch ($t) {
                        'edit'  { 'Edit' }  'write' { 'Write' }  'bash' { 'Bash' }
                        default { (Get-Culture).TextInfo.ToTitleCase($t) }
                    }
                    $rules.Add("$cap($p)") | Out-Null
                }
            }
        }
    }

    $sb = [System.Text.StringBuilder]::new()
    [void]$sb.AppendLine('# Kimi Code enforcement, generated by PSWayforge.')
    [void]$sb.AppendLine('# Kimi reads hooks ONLY from the GLOBAL ~/.kimi-code/config.toml')
    [void]$sb.AppendLine('# (Windows: %USERPROFILE%\.kimi-code\config.toml). Add the entries below to')
    [void]$sb.AppendLine('# that file. The command path is relative to the project Kimi runs in.')
    [void]$sb.AppendLine()
    if ($stages['pre-tool']) {
        [void]$sb.AppendLine('[[hooks]]')
        [void]$sb.AppendLine('event = "PreToolUse"')
        [void]$sb.AppendLine('matcher = "Bash|Edit|Write"')
        [void]$sb.AppendLine('command = "pwsh -NoProfile -File .workflow/hooks/gate.ps1 -Stage pre-tool -AsHook kimi"')
        [void]$sb.AppendLine('timeout = 60')
        [void]$sb.AppendLine()
    }
    foreach ($rule in $rules) {
        [void]$sb.AppendLine('[[permission.rules]]')
        [void]$sb.AppendLine('decision = "deny"')
        [void]$sb.AppendLine("pattern = ""$rule""")
        [void]$sb.AppendLine('scope = "project"')
        [void]$sb.AppendLine()
    }

    $dir = Join-Path $ProjectRoot '.workflow/harness'
    if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
    $path = Join-Path $dir 'kimi.config.toml'
    Set-Content -Path $path -Value $sb.ToString() -Encoding utf8NoBOM

    Write-Warning "Kimi hooks are global-only. Add the entries in '$path' to your ~/.kimi-code/config.toml to enable Kimi enforcement."
    return $path
}