playbook/scripts/sync_templates.ps1

249 lines
8.1 KiB
PowerShell

# Sync project templates to target project.
# - Copies templates/memory-bank/ -> <project-root>/memory-bank/
# - Copies templates/prompts/ -> <project-root>/docs/prompts/
# - Copies templates/AGENTS.template.md -> <project-root>/AGENTS.md
# - Copies templates/AGENT_RULES.template.md -> <project-root>/AGENT_RULES.md
# Existing targets are backed up before overwrite.
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[Alias('h', '?')]
[switch]$Help,
[Parameter(Mandatory = $false)]
[string]$ProjectRoot,
[Parameter(Mandatory = $false)]
[string]$ProjectName,
[Parameter(Mandatory = $false)]
[string]$Date,
[Parameter(Mandatory = $false)]
[switch]$NoBackup,
[Parameter(Mandatory = $false)]
[switch]$Force,
[Parameter(Mandatory = $false)]
[switch]$Full
)
$ErrorActionPreference = "Stop"
if ($Help) {
Write-Host "Usage:"
Write-Host " powershell -File scripts/sync_templates.ps1 [options]"
Write-Host " powershell -File scripts/sync_templates.ps1 -ProjectRoot C:\\path\\to\\project"
Write-Host ""
Write-Host "Options:"
Write-Host " -ProjectRoot PATH Target project root (default: git root)."
Write-Host " -ProjectName NAME Replace {{PROJECT_NAME}} placeholder."
Write-Host " -Date DATE Replace {{DATE}} placeholder (default: today)."
Write-Host " -NoBackup Skip backup of existing files."
Write-Host " -Force Overwrite without prompting."
Write-Host " -Full Append full framework section to AGENTS.md."
Write-Host " -Help Show this help."
exit 0
}
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$Src = (Resolve-Path (Join-Path $ScriptDir "..")).Path
# Defaults
if (-not $Date) {
$Date = Get-Date -Format "yyyy-MM-dd"
}
# Determine project root
if (-not $ProjectRoot) {
$ProjectRoot = (git -C $ScriptDir rev-parse --show-toplevel 2>$null)
if (-not $ProjectRoot) { $ProjectRoot = (Get-Location).Path }
}
$ProjectRoot = (Resolve-Path $ProjectRoot).Path
# Source directories
$TemplatesDir = Join-Path $Src "templates"
$MemoryBankSrc = Join-Path $TemplatesDir "memory-bank"
$PromptsSrc = Join-Path $TemplatesDir "prompts"
$AgentsSrc = Join-Path $TemplatesDir "AGENTS.template.md"
$AgentRulesSrc = Join-Path $TemplatesDir "AGENT_RULES.template.md"
# Check source exists
if (-not (Test-Path $TemplatesDir)) {
throw "Templates directory not found: $TemplatesDir"
}
# Skip if source equals destination
if ($Src -ieq $ProjectRoot) {
Write-Host "Skip: playbook root equals project root."
Write-Host "Done."
exit 0
}
$timestamp = Get-Date -Format "yyyyMMddHHmmss"
# Function: backup file/directory
function Backup-IfExists {
param([string]$Target)
if ((Test-Path $Target) -and -not $NoBackup) {
$backup = "$Target.bak.$timestamp"
Move-Item $Target $backup
Write-Host "Backed up: $(Split-Path -Leaf $Target) -> $(Split-Path -Leaf $backup)"
}
}
# Function: replace placeholders in file
function Replace-Placeholders {
param([string]$File)
if (-not (Test-Path $File)) { return }
$content = Get-Content -Raw -Path $File
if ($ProjectName) {
$content = $content.Replace("{{PROJECT_NAME}}", $ProjectName)
}
$content = $content.Replace("{{DATE}}", $Date)
Set-Content -Path $File -Value $content -Encoding UTF8 -NoNewline
}
# Function: replace placeholders in directory
function Replace-PlaceholdersDir {
param([string]$Dir)
if (-not (Test-Path $Dir)) { return }
Get-ChildItem -Path $Dir -Filter "*.md" -Recurse -File | ForEach-Object {
Replace-Placeholders -File $_.FullName
}
}
Write-Host "Syncing templates to: $ProjectRoot"
Write-Host ""
# 1. Sync memory-bank/
if (Test-Path $MemoryBankSrc) {
$MemoryBankDst = Join-Path $ProjectRoot "memory-bank"
if ((Test-Path $MemoryBankDst) -and -not $Force) {
Write-Host "memory-bank/ already exists. Use -Force to overwrite."
} else {
Backup-IfExists -Target $MemoryBankDst
New-Item -ItemType Directory -Path $MemoryBankDst -Force | Out-Null
Copy-Item -Path (Join-Path $MemoryBankSrc "*") -Destination $MemoryBankDst -Recurse -Force
# Rename .template.md to .md
Get-ChildItem -Path $MemoryBankDst -Filter "*.template.md" -File | ForEach-Object {
$newName = $_.Name -replace "\.template\.md$", ".md"
Rename-Item -Path $_.FullName -NewName $newName
}
Replace-PlaceholdersDir -Dir $MemoryBankDst
Write-Host "Synced: memory-bank/"
}
} else {
Write-Host "Skip: memory-bank/ templates not found"
}
# 2. Sync docs/prompts/
if (Test-Path $PromptsSrc) {
$PromptsDst = Join-Path $ProjectRoot "docs\prompts"
if ((Test-Path $PromptsDst) -and -not $Force) {
Write-Host "docs/prompts/ already exists. Use -Force to overwrite."
} else {
Backup-IfExists -Target $PromptsDst
$DocsDir = Join-Path $ProjectRoot "docs"
New-Item -ItemType Directory -Path $DocsDir -Force | Out-Null
New-Item -ItemType Directory -Path $PromptsDst -Force | Out-Null
Copy-Item -Path (Join-Path $PromptsSrc "*") -Destination $PromptsDst -Recurse -Force
# Rename .template.md to .md recursively
Get-ChildItem -Path $PromptsDst -Filter "*.template.md" -Recurse -File | ForEach-Object {
$newName = $_.Name -replace "\.template\.md$", ".md"
Rename-Item -Path $_.FullName -NewName $newName
}
Replace-PlaceholdersDir -Dir $PromptsDst
Write-Host "Synced: docs/prompts/"
}
} else {
Write-Host "Skip: prompts/ templates not found"
}
# 3. Sync AGENTS.md
# Choose markers based on -Full flag
if ($Full) {
$MarkerStart = "<!-- playbook:framework:start -->"
$MarkerEnd = "<!-- playbook:framework:end -->"
$SectionName = "framework"
} else {
$MarkerStart = "<!-- playbook:templates:start -->"
$MarkerEnd = "<!-- playbook:templates:end -->"
$SectionName = "templates"
}
if (Test-Path $AgentsSrc) {
$AgentsDst = Join-Path $ProjectRoot "AGENTS.md"
if (-not (Test-Path $AgentsDst)) {
# AGENTS.md doesn't exist: create from full template
Copy-Item -Path $AgentsSrc -Destination $AgentsDst -Force
Replace-Placeholders -File $AgentsDst
Write-Host "Created: AGENTS.md"
} else {
# AGENTS.md exists: update or append section
# Extract snippet from template
$templateContent = Get-Content -Raw -Path $AgentsSrc
$extractPattern = "(?s)(" + [regex]::Escape($MarkerStart) + ".*?" + [regex]::Escape($MarkerEnd) + ")"
if ($templateContent -match $extractPattern) {
$snippetContent = $Matches[1]
$content = Get-Content -Raw -Path $AgentsDst
if ($content -match [regex]::Escape($MarkerStart)) {
# Has markers: replace content between markers
$replacePattern = "(?s)" + [regex]::Escape($MarkerStart) + ".*?" + [regex]::Escape($MarkerEnd)
$newContent = $content -replace $replacePattern, $snippetContent
Set-Content -Path $AgentsDst -Value $newContent -Encoding UTF8 -NoNewline
Replace-Placeholders -File $AgentsDst
Write-Host "Updated: AGENTS.md ($SectionName section)"
} else {
# No markers: append snippet at the end
$newContent = $content.TrimEnd() + "`n`n" + $snippetContent
Set-Content -Path $AgentsDst -Value $newContent -Encoding UTF8 -NoNewline
Replace-Placeholders -File $AgentsDst
Write-Host "Appended: AGENTS.md ($SectionName section)"
}
} else {
Write-Host "Skip: markers not found in template"
}
}
} else {
Write-Host "Skip: AGENTS.template.md not found"
}
# 4. Sync AGENT_RULES.md
if (Test-Path $AgentRulesSrc) {
$AgentRulesDst = Join-Path $ProjectRoot "AGENT_RULES.md"
if ((Test-Path $AgentRulesDst) -and -not $Force) {
Write-Host "AGENT_RULES.md already exists. Use -Force to overwrite."
} else {
Backup-IfExists -Target $AgentRulesDst
Copy-Item -Path $AgentRulesSrc -Destination $AgentRulesDst -Force
Replace-Placeholders -File $AgentRulesDst
Write-Host "Synced: AGENT_RULES.md"
}
} else {
Write-Host "Skip: AGENT_RULES.template.md not found"
}
Write-Host ""
Write-Host "Done."
Write-Host ""
Write-Host "Next steps:"
Write-Host " 1. Edit memory-bank/*.md to fill in project-specific content"
Write-Host " 2. Replace remaining {{PLACEHOLDER}} values"
Write-Host " 3. Run sync_standards.ps1 to sync .agents/ rules"