fix: resolve unbound variable and duplicate file update issues

- Fix undefined FORGE_FILE variable in bash update-agent-context.sh
  - Add missing FORGE_FILE definition pointing to AGENTS.md
  - Update comment to include Forge in list of agents sharing AGENTS.md
  - Prevents crash with 'set -u' when running without explicit agent type

- Add deduplication logic to PowerShell update-agent-context.ps1
  - Implement Update-IfNew helper to track processed files by real path
  - Prevents AGENTS.md from being rewritten multiple times
  - Matches existing deduplication behavior in bash script

- Prevent duplicate YAML keys in Forge frontmatter injection
  - Check for existing 'name:' field before injection in both scripts
  - PowerShell: Parse frontmatter to detect existing name field
  - Bash: Enhanced awk script to check frontmatter state
  - Future-proofs against template changes that add name fields

All scripts now have consistent behavior and proper error handling.
This commit is contained in:
ericnoam
2026-04-02 20:34:23 +02:00
parent 99e1c3fedd
commit 90a1845c13
4 changed files with 102 additions and 30 deletions

View File

@@ -491,17 +491,49 @@ function Build-Variant {
'forge' {
$cmdDir = Join-Path $baseDir ".forge/commands"
Generate-Commands -Agent 'forge' -Extension 'md' -ArgFormat '{{parameters}}' -OutputDir $cmdDir -ScriptVariant $Script -ExtraStripKey 'handoffs'
# Inject name field into frontmatter (forge requires name + description)
$cmdFiles = Get-ChildItem -Path "$cmdDir/*.md" -File -ErrorAction SilentlyContinue
foreach ($cmdFile in $cmdFiles) {
$cmdName = [System.IO.Path]::GetFileNameWithoutExtension($cmdFile.Name)
$content = Get-Content -Path $cmdFile.FullName -Raw
# Determine whether the first frontmatter block already contains a name field
$hasNameInFrontmatter = $false
$lines = $content -split "`n"
$frontmatterStart = $null
$frontmatterEnd = $null
for ($i = 0; $i -lt $lines.Length; $i++) {
if ($lines[$i] -match '^\s*---\s*$') {
if ($null -eq $frontmatterStart) {
$frontmatterStart = $i
} elseif ($null -eq $frontmatterEnd) {
$frontmatterEnd = $i
break
}
}
}
if ($null -ne $frontmatterStart) {
if ($null -eq $frontmatterEnd) {
$frontmatterEnd = $lines.Length
}
for ($j = $frontmatterStart + 1; $j -lt $frontmatterEnd; $j++) {
if ($lines[$j] -match '^[ \t]*name\s*:') {
$hasNameInFrontmatter = $true
break
}
}
}
# Inject name field after first --- using .NET Regex.Replace with count limit
$regex = [regex]'(?m)^---$'
$content = $regex.Replace($content, "---`nname: $cmdName", 1)
if (-not $hasNameInFrontmatter) {
$regex = [regex]'(?m)^---$'
$content = $regex.Replace($content, "---`nname: $cmdName", 1)
}
Set-Content -Path $cmdFile.FullName -Value $content -NoNewline
}
}

View File

@@ -339,7 +339,15 @@ build_variant() {
[[ -f "$_cmd_file" ]] || continue
_cmd_name=$(basename "$_cmd_file" .md)
_tmp_file="${_cmd_file}.tmp"
awk -v name="$_cmd_name" 'NR==1 && /^---$/ { print; print "name: "name; next } { print }' "$_cmd_file" > "$_tmp_file"
# Only inject name if frontmatter doesn't already have one
awk -v name="$_cmd_name" '
BEGIN { in_frontmatter=0; has_name=0; first_dash_seen=0 }
NR==1 && /^---$/ { in_frontmatter=1; first_dash_seen=1; print; next }
in_frontmatter && /^---$/ { in_frontmatter=0 }
in_frontmatter && /^[ \t]*name[ \t]*:/ { has_name=1 }
first_dash_seen && !has_name && NR==2 && in_frontmatter { print "name: "name }
{ print }
' "$_cmd_file" > "$_tmp_file"
mv "$_tmp_file" "$_cmd_file"
done ;;
generic)