public/Update-WayforgeAgentsFile.ps1

function Update-WayforgeAgentsFile {
    <#
    .SYNOPSIS
        Renders each workflow's `steps` into a managed block in AGENTS.md.
 
    .DESCRIPTION
        Projects the narrative process (the `steps` in .workflow/definitions/*.yaml)
        into AGENTS.md, between <!-- wayforge:workflow:start --> and
        <!-- wayforge:workflow:end --> markers. It is non-destructive: content
        outside the markers is preserved, and the block is refreshed idempotently
        on every run. AGENTS.md is created if it does not exist.
 
    .PARAMETER ProjectPath
        A path inside the project. Defaults to the current directory.
 
    .PARAMETER WorkflowName
        A single workflow to render. Defaults to every definition that has steps.
 
    .EXAMPLE
        Update-WayforgeAgentsFile
 
        Refreshes the workflow section of AGENTS.md from the current definitions.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([string])]
    param(
        [string] $ProjectPath = (Get-Location).Path,
        [string] $WorkflowName
    )

    $root = Resolve-WayforgeGitRoot -Path $ProjectPath

    $definitionsDir = Join-Path $root '.workflow/definitions'
    $names = if ($WorkflowName) {
        , $WorkflowName
    }
    elseif (Test-Path $definitionsDir) {
        Get-ChildItem -Path $definitionsDir -Filter '*.yaml' | ForEach-Object { $_.BaseName }
    }
    else {
        @()
    }

    $sections = [System.Collections.Generic.List[string]]::new()
    foreach ($name in $names) {
        $wf    = Get-WayforgeWorkflow -Name $name -ProjectPath $root
        $steps = @(Get-WayforgeField $wf 'steps' | Where-Object { $_ })   # @() filters an absent 'steps' (avoids @($null) count 1)
        if ($steps.Count -eq 0) { continue }

        $lines = [System.Collections.Generic.List[string]]::new()
        $lines.Add("### Workflow: $name") | Out-Null
        $desc = Get-WayforgeField $wf 'description'
        if ($desc) { $lines.Add('') | Out-Null; $lines.Add([string]$desc) | Out-Null }
        $lines.Add('') | Out-Null

        $index = 1
        foreach ($step in $steps) {
            $id       = Get-WayforgeField $step 'id' "step$index"
            $stepName = Get-WayforgeField $step 'name' $id
            $line     = '{0}. **{1}** (`{2}`)' -f $index, $stepName, $id     # single-quoted: backticks literal
            $stepDesc = Get-WayforgeField $step 'description'
            if ($stepDesc) { $line += " — $stepDesc" }
            $lines.Add($line) | Out-Null
            $index++
        }
        $sections.Add(($lines -join "`n")) | Out-Null
    }

    if ($sections.Count -eq 0) { return $null }

    $startMarker = '<!-- wayforge:workflow:start -->'
    $endMarker   = '<!-- wayforge:workflow:end -->'
    $body = @(
        $startMarker
        '## Workflow'
        ''
        'The steps below are enforced by PSWayforge gates. This section is generated from `.workflow/definitions/`; edits between the markers are overwritten.'
        ''
        ($sections -join "`n`n")
        $endMarker
    ) -join "`n"

    $agentsPath = Join-Path $root 'AGENTS.md'
    $content = if (Test-Path $agentsPath -PathType Leaf) { Get-Content $agentsPath -Raw } else { '' }

    $startIdx = $content.IndexOf($startMarker)
    $endIdx   = $content.IndexOf($endMarker)
    $new = if ($startIdx -ge 0 -and $endIdx -gt $startIdx) {
        $content.Substring(0, $startIdx) + $body + $content.Substring($endIdx + $endMarker.Length)
    }
    elseif ([string]::IsNullOrWhiteSpace($content)) {
        "# Agent workspace`n`n$body`n"
    }
    else {
        $content.TrimEnd() + "`n`n" + $body + "`n"
    }

    if ($PSCmdlet.ShouldProcess($agentsPath, 'Render workflow section into AGENTS.md')) {
        Set-Content -Path $agentsPath -Value $new -Encoding utf8NoBOM
    }
    return $agentsPath
}