✨ feat(templates): add sync templates scaffolding
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
rem Sync project templates to target project.
|
||||
rem - Copies templates/memory-bank/ -> <project-root>/memory-bank/
|
||||
rem - Copies templates/prompts/ -> <project-root>/docs/prompts/
|
||||
rem - Copies templates/AGENTS.template.md -> <project-root>/AGENTS.md
|
||||
rem - Copies templates/AGENT_RULES.template.md -> <project-root>/AGENT_RULES.md
|
||||
rem Existing targets are NOT overwritten (skip if exists).
|
||||
rem
|
||||
rem Usage:
|
||||
rem sync_templates.bat # sync to current git root
|
||||
rem sync_templates.bat <project-root> # sync to specified project
|
||||
rem sync_templates.bat --force # overwrite existing files
|
||||
rem sync_templates.bat --full # append full framework to existing AGENTS.md
|
||||
|
||||
set "SCRIPT_DIR=%~dp0"
|
||||
for %%I in ("%SCRIPT_DIR%..") do set "SRC=%%~fI"
|
||||
|
||||
set "FORCE=0"
|
||||
set "FULL=0"
|
||||
set "PROJECT_ROOT="
|
||||
|
||||
:parse_args
|
||||
if "%~1"=="" goto args_done
|
||||
if /I "%~1"=="--force" (
|
||||
set "FORCE=1"
|
||||
shift
|
||||
goto parse_args
|
||||
)
|
||||
if /I "%~1"=="--full" (
|
||||
set "FULL=1"
|
||||
shift
|
||||
goto parse_args
|
||||
)
|
||||
if /I "%~1"=="-h" goto show_help
|
||||
if /I "%~1"=="--help" goto show_help
|
||||
set "PROJECT_ROOT=%~1"
|
||||
shift
|
||||
goto parse_args
|
||||
|
||||
:show_help
|
||||
echo Usage:
|
||||
echo sync_templates.bat [options] [project-root]
|
||||
echo.
|
||||
echo Options:
|
||||
echo --force Overwrite existing files
|
||||
echo --full Append full framework (规则优先级 + 新会话开始时) to existing AGENTS.md
|
||||
echo -h, --help Show this help
|
||||
exit /b 0
|
||||
|
||||
:args_done
|
||||
|
||||
rem Determine project root
|
||||
if "%PROJECT_ROOT%"=="" (
|
||||
for /f "delims=" %%R in ('git -C "%SCRIPT_DIR%" rev-parse --show-toplevel 2^>nul') do set "PROJECT_ROOT=%%R"
|
||||
)
|
||||
if "%PROJECT_ROOT%"=="" set "PROJECT_ROOT=%cd%"
|
||||
for %%I in ("%PROJECT_ROOT%") do set "PROJECT_ROOT=%%~fI"
|
||||
|
||||
rem Source directories
|
||||
set "TEMPLATES_DIR=%SRC%\templates"
|
||||
set "MEMORY_BANK_SRC=%TEMPLATES_DIR%\memory-bank"
|
||||
set "PROMPTS_SRC=%TEMPLATES_DIR%\prompts"
|
||||
set "AGENTS_SRC=%TEMPLATES_DIR%\AGENTS.template.md"
|
||||
set "AGENT_RULES_SRC=%TEMPLATES_DIR%\AGENT_RULES.template.md"
|
||||
|
||||
rem Check source exists
|
||||
if not exist "%TEMPLATES_DIR%" (
|
||||
echo ERROR: Templates directory not found: %TEMPLATES_DIR%
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
rem Skip if source equals destination
|
||||
if /I "%SRC%"=="%PROJECT_ROOT%" (
|
||||
echo Skip: playbook root equals project root.
|
||||
echo Done.
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
for /f "usebackq delims=" %%D in (`powershell -NoProfile -Command "Get-Date -Format 'yyyy-MM-dd'"`) do set "SYNC_DATE=%%D"
|
||||
if "%SYNC_DATE%"=="" set "SYNC_DATE=%date%"
|
||||
|
||||
echo Syncing templates to: %PROJECT_ROOT%
|
||||
echo.
|
||||
|
||||
rem 1. Sync memory-bank/
|
||||
set "MEMORY_BANK_DST=%PROJECT_ROOT%\memory-bank"
|
||||
if exist "%MEMORY_BANK_SRC%" (
|
||||
if exist "%MEMORY_BANK_DST%" (
|
||||
if "%FORCE%"=="0" (
|
||||
echo memory-bank/ already exists. Skip. Use --force to overwrite.
|
||||
goto sync_prompts
|
||||
)
|
||||
)
|
||||
if not exist "%MEMORY_BANK_DST%" mkdir "%MEMORY_BANK_DST%"
|
||||
xcopy "%MEMORY_BANK_SRC%\*" "%MEMORY_BANK_DST%\" /e /i /y >nul 2>nul
|
||||
|
||||
rem Rename .template.md to .md
|
||||
for %%F in ("%MEMORY_BANK_DST%\*.template.md") do (
|
||||
set "OLDNAME=%%~nxF"
|
||||
set "NEWNAME=!OLDNAME:.template.md=.md!"
|
||||
ren "%%F" "!NEWNAME!"
|
||||
)
|
||||
|
||||
rem Replace {{DATE}} placeholder
|
||||
for %%F in ("%MEMORY_BANK_DST%\*.md") do (
|
||||
powershell -NoProfile -Command "$f='%%~fF'; $c=Get-Content -Raw $f; $c=$c.Replace('{{DATE}}','%SYNC_DATE%'); Set-Content -Path $f -Value $c -Encoding UTF8 -NoNewline"
|
||||
)
|
||||
echo Synced: memory-bank/
|
||||
) else (
|
||||
echo Skip: memory-bank/ templates not found
|
||||
)
|
||||
|
||||
:sync_prompts
|
||||
rem 2. Sync docs/prompts/
|
||||
set "PROMPTS_DST=%PROJECT_ROOT%\docs\prompts"
|
||||
if exist "%PROMPTS_SRC%" (
|
||||
if exist "%PROMPTS_DST%" (
|
||||
if "%FORCE%"=="0" (
|
||||
echo docs/prompts/ already exists. Skip. Use --force to overwrite.
|
||||
goto sync_agents
|
||||
)
|
||||
)
|
||||
if not exist "%PROJECT_ROOT%\docs" mkdir "%PROJECT_ROOT%\docs"
|
||||
if not exist "%PROMPTS_DST%" mkdir "%PROMPTS_DST%"
|
||||
xcopy "%PROMPTS_SRC%\*" "%PROMPTS_DST%\" /e /i /y >nul 2>nul
|
||||
|
||||
rem Rename .template.md to .md recursively
|
||||
for /r "%PROMPTS_DST%" %%F in (*.template.md) do (
|
||||
set "OLDNAME=%%~nxF"
|
||||
set "NEWNAME=!OLDNAME:.template.md=.md!"
|
||||
ren "%%F" "!NEWNAME!"
|
||||
)
|
||||
|
||||
rem Replace {{DATE}} placeholder
|
||||
for /r "%PROMPTS_DST%" %%F in (*.md) do (
|
||||
powershell -NoProfile -Command "$f='%%~fF'; $c=Get-Content -Raw $f; $c=$c.Replace('{{DATE}}','%SYNC_DATE%'); Set-Content -Path $f -Value $c -Encoding UTF8 -NoNewline"
|
||||
)
|
||||
echo Synced: docs/prompts/
|
||||
) else (
|
||||
echo Skip: prompts/ templates not found
|
||||
)
|
||||
|
||||
:sync_agents
|
||||
rem 3. Sync AGENTS.md
|
||||
set "AGENTS_DST=%PROJECT_ROOT%\AGENTS.md"
|
||||
|
||||
rem Choose markers based on --full flag
|
||||
if "%FULL%"=="1" (
|
||||
set "MARKER_START=<!-- playbook:framework:start -->"
|
||||
set "MARKER_END=<!-- playbook:framework:end -->"
|
||||
set "SECTION_NAME=framework"
|
||||
) else (
|
||||
set "MARKER_START=<!-- playbook:templates:start -->"
|
||||
set "MARKER_END=<!-- playbook:templates:end -->"
|
||||
set "SECTION_NAME=templates"
|
||||
)
|
||||
|
||||
if exist "%AGENTS_SRC%" (
|
||||
if not exist "%AGENTS_DST%" (
|
||||
rem AGENTS.md doesn't exist: create from full template
|
||||
copy /y "%AGENTS_SRC%" "%AGENTS_DST%" >nul
|
||||
powershell -NoProfile -Command "$f='%AGENTS_DST%'; $c=Get-Content -Raw $f; $c=$c.Replace('{{DATE}}','%SYNC_DATE%'); Set-Content -Path $f -Value $c -Encoding UTF8 -NoNewline"
|
||||
echo Created: AGENTS.md
|
||||
) else (
|
||||
rem AGENTS.md exists: update or append section (extract from template)
|
||||
powershell -NoProfile -Command ^
|
||||
"$src='%AGENTS_SRC%'; $dst='%AGENTS_DST%'; $date='%SYNC_DATE%'; " ^
|
||||
"$markerStart='!MARKER_START!'; $markerEnd='!MARKER_END!'; $sectionName='!SECTION_NAME!'; " ^
|
||||
"$templateContent = Get-Content -Raw $src; " ^
|
||||
"$extractPattern = '(?s)(' + [regex]::Escape($markerStart) + '.*?' + [regex]::Escape($markerEnd) + ')'; " ^
|
||||
"if ($templateContent -match $extractPattern) { " ^
|
||||
" $snippetContent = $Matches[1]; " ^
|
||||
" $content = Get-Content -Raw $dst; " ^
|
||||
" if ($content -match [regex]::Escape($markerStart)) { " ^
|
||||
" $replacePattern = '(?s)' + [regex]::Escape($markerStart) + '.*?' + [regex]::Escape($markerEnd); " ^
|
||||
" $newContent = $content -replace $replacePattern, $snippetContent; " ^
|
||||
" $newContent = $newContent.Replace('{{DATE}}', $date); " ^
|
||||
" Set-Content -Path $dst -Value $newContent -Encoding UTF8 -NoNewline; " ^
|
||||
" Write-Host \"Updated: AGENTS.md ($sectionName section)\"; " ^
|
||||
" } else { " ^
|
||||
" $newContent = $content.TrimEnd() + \"`n`n\" + $snippetContent; " ^
|
||||
" $newContent = $newContent.Replace('{{DATE}}', $date); " ^
|
||||
" Set-Content -Path $dst -Value $newContent -Encoding UTF8 -NoNewline; " ^
|
||||
" Write-Host \"Appended: AGENTS.md ($sectionName section)\"; " ^
|
||||
" } " ^
|
||||
"} else { " ^
|
||||
" Write-Host 'Skip: markers not found in template'; " ^
|
||||
"}"
|
||||
)
|
||||
) else (
|
||||
echo Skip: AGENTS.template.md not found
|
||||
)
|
||||
|
||||
:sync_agent_rules
|
||||
rem 4. Sync AGENT_RULES.md
|
||||
set "AGENT_RULES_DST=%PROJECT_ROOT%\AGENT_RULES.md"
|
||||
if exist "%AGENT_RULES_SRC%" (
|
||||
if exist "%AGENT_RULES_DST%" (
|
||||
if "%FORCE%"=="0" (
|
||||
echo AGENT_RULES.md already exists. Skip. Use --force to overwrite.
|
||||
goto sync_todo
|
||||
)
|
||||
)
|
||||
copy /y "%AGENT_RULES_SRC%" "%AGENT_RULES_DST%" >nul
|
||||
powershell -NoProfile -Command "$f='%AGENT_RULES_DST%'; $c=Get-Content -Raw $f; $c=$c.Replace('{{DATE}}','%SYNC_DATE%'); Set-Content -Path $f -Value $c -Encoding UTF8 -NoNewline"
|
||||
echo Synced: AGENT_RULES.md
|
||||
) else (
|
||||
echo Skip: AGENT_RULES.template.md not found
|
||||
)
|
||||
|
||||
:sync_todo
|
||||
rem 5. Create TODO.md if not exist
|
||||
set "TODO_DST=%PROJECT_ROOT%\TODO.md"
|
||||
if not exist "%TODO_DST%" (
|
||||
> "%TODO_DST%" echo # TODO
|
||||
>> "%TODO_DST%" echo.
|
||||
>> "%TODO_DST%" echo ## Plan 1: [计划名称]
|
||||
>> "%TODO_DST%" echo.
|
||||
>> "%TODO_DST%" echo - [ ] 任务 1
|
||||
>> "%TODO_DST%" echo - [ ] 任务 2
|
||||
>> "%TODO_DST%" echo.
|
||||
>> "%TODO_DST%" echo ---
|
||||
>> "%TODO_DST%" echo.
|
||||
>> "%TODO_DST%" echo **最后更新**:%SYNC_DATE%
|
||||
echo Created: TODO.md
|
||||
)
|
||||
|
||||
rem 6. Create CONFIRM.md if not exist
|
||||
set "CONFIRM_DST=%PROJECT_ROOT%\CONFIRM.md"
|
||||
if not exist "%CONFIRM_DST%" (
|
||||
> "%CONFIRM_DST%" echo # 待确认事项
|
||||
>> "%CONFIRM_DST%" echo.
|
||||
>> "%CONFIRM_DST%" echo ## 待确认
|
||||
>> "%CONFIRM_DST%" echo.
|
||||
>> "%CONFIRM_DST%" echo ^<!-- 记录需要确认的事项 --^>
|
||||
>> "%CONFIRM_DST%" echo.
|
||||
>> "%CONFIRM_DST%" echo ## 已确认
|
||||
>> "%CONFIRM_DST%" echo.
|
||||
>> "%CONFIRM_DST%" echo ^<!-- 已确认的事项移到这里 --^>
|
||||
>> "%CONFIRM_DST%" echo.
|
||||
>> "%CONFIRM_DST%" echo ---
|
||||
>> "%CONFIRM_DST%" echo.
|
||||
>> "%CONFIRM_DST%" echo **最后更新**:%SYNC_DATE%
|
||||
echo Created: CONFIRM.md
|
||||
)
|
||||
|
||||
echo.
|
||||
echo Done.
|
||||
echo.
|
||||
echo Next steps:
|
||||
echo 1. Edit memory-bank\*.md to fill in project-specific content
|
||||
echo 2. Replace remaining {{PLACEHOLDER}} values
|
||||
echo 3. Run sync_standards.bat to sync .agents\ rules
|
||||
|
||||
endlocal
|
||||
@@ -0,0 +1,266 @@
|
||||
# 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, Position = 0)]
|
||||
[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"
|
||||
$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"
|
||||
}
|
||||
|
||||
# 5. Create TODO.md and CONFIRM.md if not exist
|
||||
$TodoPath = Join-Path $ProjectRoot "TODO.md"
|
||||
if (-not (Test-Path $TodoPath)) {
|
||||
@"
|
||||
# TODO
|
||||
|
||||
## Plan 1: [计划名称]
|
||||
|
||||
- [ ] 任务 1
|
||||
- [ ] 任务 2
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:$Date
|
||||
"@ | Set-Content -Path $TodoPath -Encoding UTF8
|
||||
Write-Host "Created: TODO.md"
|
||||
}
|
||||
|
||||
$ConfirmPath = Join-Path $ProjectRoot "CONFIRM.md"
|
||||
if (-not (Test-Path $ConfirmPath)) {
|
||||
@"
|
||||
# 待确认事项
|
||||
|
||||
## 待确认
|
||||
|
||||
<!-- 记录需要确认的事项 -->
|
||||
|
||||
## 已确认
|
||||
|
||||
<!-- 已确认的事项移到这里 -->
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:$Date
|
||||
"@ | Set-Content -Path $ConfirmPath -Encoding UTF8
|
||||
Write-Host "Created: CONFIRM.md"
|
||||
}
|
||||
|
||||
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"
|
||||
@@ -0,0 +1,349 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
# 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.
|
||||
#
|
||||
# Usage:
|
||||
# sh scripts/sync_templates.sh # sync to current git root
|
||||
# sh scripts/sync_templates.sh <project-root> # sync to specified project
|
||||
# sh scripts/sync_templates.sh --project-name "MyProject" --date "2026-01-20"
|
||||
#
|
||||
# Options:
|
||||
# --project-name NAME Replace {{PROJECT_NAME}} placeholder
|
||||
# --date DATE Replace {{DATE}} placeholder (default: today)
|
||||
# --no-backup Skip backup of existing files
|
||||
# --force Overwrite without prompting
|
||||
|
||||
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)"
|
||||
SRC="$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd -P)"
|
||||
|
||||
# Defaults
|
||||
PROJECT_NAME=""
|
||||
SYNC_DATE="$(date +%Y-%m-%d 2>/dev/null || echo "{{DATE}}")"
|
||||
NO_BACKUP=0
|
||||
FORCE=0
|
||||
FULL=0
|
||||
PROJECT_ROOT=""
|
||||
|
||||
# Parse arguments
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--project-name)
|
||||
PROJECT_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--date)
|
||||
SYNC_DATE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--no-backup)
|
||||
NO_BACKUP=1
|
||||
shift
|
||||
;;
|
||||
--force)
|
||||
FORCE=1
|
||||
shift
|
||||
;;
|
||||
--full)
|
||||
FULL=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
sh scripts/sync_templates.sh [options] [project-root]
|
||||
|
||||
Options:
|
||||
--project-name NAME Replace {{PROJECT_NAME}} placeholder
|
||||
--date DATE Replace {{DATE}} placeholder (default: today)
|
||||
--no-backup Skip backup of existing files
|
||||
--force Overwrite without prompting
|
||||
--full Append full framework (规则优先级 + 新会话开始时) to existing AGENTS.md
|
||||
-h, --help Show this help
|
||||
|
||||
Examples:
|
||||
sh scripts/sync_templates.sh
|
||||
sh scripts/sync_templates.sh /path/to/project
|
||||
sh scripts/sync_templates.sh --full /path/to/project
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
echo "ERROR: Unknown option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
PROJECT_ROOT="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Determine project root
|
||||
if [ -z "$PROJECT_ROOT" ]; then
|
||||
PROJECT_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || pwd)"
|
||||
fi
|
||||
PROJECT_ROOT="$(CDPATH= cd -- "$PROJECT_ROOT" && pwd -P)"
|
||||
|
||||
# Source directories
|
||||
TEMPLATES_DIR="$SRC/templates"
|
||||
MEMORY_BANK_SRC="$TEMPLATES_DIR/memory-bank"
|
||||
PROMPTS_SRC="$TEMPLATES_DIR/prompts"
|
||||
AGENTS_SRC="$TEMPLATES_DIR/AGENTS.template.md"
|
||||
AGENT_RULES_SRC="$TEMPLATES_DIR/AGENT_RULES.template.md"
|
||||
|
||||
# Check source exists
|
||||
if [ ! -d "$TEMPLATES_DIR" ]; then
|
||||
echo "ERROR: Templates directory not found: $TEMPLATES_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Skip if source equals destination (running from playbook repo itself)
|
||||
if [ "$SRC" = "$PROJECT_ROOT" ]; then
|
||||
echo "Skip: playbook root equals project root."
|
||||
echo "Done."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
timestamp="$(date +%Y%m%d%H%M%S 2>/dev/null || echo bak)"
|
||||
|
||||
# Function: backup file/directory
|
||||
backup_if_exists() {
|
||||
target="$1"
|
||||
if [ -e "$target" ] && [ "$NO_BACKUP" -eq 0 ]; then
|
||||
backup="${target}.bak.$timestamp"
|
||||
mv "$target" "$backup"
|
||||
echo "Backed up: $(basename "$target") -> $(basename "$backup")"
|
||||
fi
|
||||
}
|
||||
|
||||
escape_sed_replacement() {
|
||||
printf '%s' "$1" | sed 's/[&/|\\]/\\&/g'
|
||||
}
|
||||
|
||||
# Function: replace placeholders in file
|
||||
replace_placeholders() {
|
||||
file="$1"
|
||||
[ -f "$file" ] || return 0
|
||||
|
||||
tmp="$(mktemp 2>/dev/null || echo "$file.tmp.$timestamp")"
|
||||
date_repl="$(escape_sed_replacement "$SYNC_DATE")"
|
||||
if [ -n "$PROJECT_NAME" ]; then
|
||||
project_repl="$(escape_sed_replacement "$PROJECT_NAME")"
|
||||
sed -e "s/{{PROJECT_NAME}}/$project_repl/g" -e "s/{{DATE}}/$date_repl/g" "$file" > "$tmp"
|
||||
else
|
||||
sed -e "s/{{DATE}}/$date_repl/g" "$file" > "$tmp"
|
||||
fi
|
||||
mv "$tmp" "$file"
|
||||
}
|
||||
|
||||
# Function: replace placeholders in directory
|
||||
replace_placeholders_dir() {
|
||||
dir="$1"
|
||||
[ -d "$dir" ] || return 0
|
||||
|
||||
find "$dir" -type f -name '*.md' -print | while IFS= read -r file; do
|
||||
replace_placeholders "$file"
|
||||
done
|
||||
}
|
||||
|
||||
echo "Syncing templates to: $PROJECT_ROOT"
|
||||
echo ""
|
||||
|
||||
# 1. Sync memory-bank/
|
||||
if [ -d "$MEMORY_BANK_SRC" ]; then
|
||||
MEMORY_BANK_DST="$PROJECT_ROOT/memory-bank"
|
||||
|
||||
if [ -e "$MEMORY_BANK_DST" ] && [ "$FORCE" -eq 0 ]; then
|
||||
echo "memory-bank/ already exists. Use --force to overwrite."
|
||||
else
|
||||
backup_if_exists "$MEMORY_BANK_DST"
|
||||
mkdir -p "$MEMORY_BANK_DST"
|
||||
cp -R "$MEMORY_BANK_SRC"/* "$MEMORY_BANK_DST/" 2>/dev/null || true
|
||||
|
||||
# Rename .template.md to .md
|
||||
for f in "$MEMORY_BANK_DST"/*.template.md; do
|
||||
[ -f "$f" ] || continue
|
||||
newname="$(echo "$f" | sed 's/\.template\.md$/.md/')"
|
||||
mv "$f" "$newname"
|
||||
done
|
||||
|
||||
replace_placeholders_dir "$MEMORY_BANK_DST"
|
||||
echo "Synced: memory-bank/"
|
||||
fi
|
||||
else
|
||||
echo "Skip: memory-bank/ templates not found"
|
||||
fi
|
||||
|
||||
# 2. Sync docs/prompts/
|
||||
if [ -d "$PROMPTS_SRC" ]; then
|
||||
PROMPTS_DST="$PROJECT_ROOT/docs/prompts"
|
||||
|
||||
if [ -e "$PROMPTS_DST" ] && [ "$FORCE" -eq 0 ]; then
|
||||
echo "docs/prompts/ already exists. Use --force to overwrite."
|
||||
else
|
||||
backup_if_exists "$PROMPTS_DST"
|
||||
mkdir -p "$PROJECT_ROOT/docs"
|
||||
mkdir -p "$PROMPTS_DST"
|
||||
cp -R "$PROMPTS_SRC"/* "$PROMPTS_DST/" 2>/dev/null || true
|
||||
|
||||
# Rename .template.md to .md (recursive)
|
||||
find "$PROMPTS_DST" -type f -name '*.template.md' -print | while IFS= read -r f; do
|
||||
newname="$(echo "$f" | sed 's/\.template\.md$/.md/')"
|
||||
mv "$f" "$newname"
|
||||
done
|
||||
|
||||
replace_placeholders_dir "$PROMPTS_DST"
|
||||
echo "Synced: docs/prompts/"
|
||||
fi
|
||||
else
|
||||
echo "Skip: prompts/ templates not found"
|
||||
fi
|
||||
|
||||
# 3. Sync AGENTS.md
|
||||
# Choose markers based on --full flag
|
||||
if [ "$FULL" -eq 1 ]; then
|
||||
MARKER_START="<!-- playbook:framework:start -->"
|
||||
MARKER_END="<!-- playbook:framework:end -->"
|
||||
SECTION_NAME="framework"
|
||||
else
|
||||
MARKER_START="<!-- playbook:templates:start -->"
|
||||
MARKER_END="<!-- playbook:templates:end -->"
|
||||
SECTION_NAME="templates"
|
||||
fi
|
||||
|
||||
if [ -f "$AGENTS_SRC" ]; then
|
||||
AGENTS_DST="$PROJECT_ROOT/AGENTS.md"
|
||||
|
||||
if [ ! -e "$AGENTS_DST" ]; then
|
||||
# AGENTS.md doesn't exist: create from full template
|
||||
cp "$AGENTS_SRC" "$AGENTS_DST"
|
||||
replace_placeholders "$AGENTS_DST"
|
||||
echo "Created: AGENTS.md"
|
||||
else
|
||||
# AGENTS.md exists: update or append section
|
||||
# Extract snippet from template
|
||||
snippet_content="$(awk -v start="$MARKER_START" -v end="$MARKER_END" '
|
||||
$0 ~ start { found=1 }
|
||||
found { print }
|
||||
$0 ~ end { found=0 }
|
||||
' "$AGENTS_SRC")"
|
||||
|
||||
if [ -z "$snippet_content" ]; then
|
||||
echo "Skip: markers not found in template"
|
||||
elif grep -q "$MARKER_START" "$AGENTS_DST"; then
|
||||
# Has markers: replace content between markers in place
|
||||
snippet_tmp="$(mktemp 2>/dev/null || echo "$AGENTS_DST.snippet.$timestamp")"
|
||||
printf "%s\n" "$snippet_content" > "$snippet_tmp"
|
||||
tmp="$(mktemp 2>/dev/null || echo "$AGENTS_DST.tmp.$timestamp")"
|
||||
awk -v start="$MARKER_START" -v end="$MARKER_END" -v snippet="$snippet_tmp" '
|
||||
BEGIN {
|
||||
while ((getline line < snippet) > 0) { block[++n] = line }
|
||||
close(snippet)
|
||||
inblock = 0
|
||||
replaced = 0
|
||||
}
|
||||
{
|
||||
if (!replaced && $0 ~ start) {
|
||||
for (i=1; i<=n; i++) print block[i]
|
||||
inblock = 1
|
||||
replaced = 1
|
||||
next
|
||||
}
|
||||
if (inblock) {
|
||||
if ($0 ~ end) { inblock = 0 }
|
||||
next
|
||||
}
|
||||
print
|
||||
}
|
||||
' "$AGENTS_DST" > "$tmp"
|
||||
mv "$tmp" "$AGENTS_DST"
|
||||
rm -f "$snippet_tmp"
|
||||
replace_placeholders "$AGENTS_DST"
|
||||
echo "Updated: AGENTS.md ($SECTION_NAME section)"
|
||||
else
|
||||
# No markers: append snippet at the end
|
||||
echo "" >> "$AGENTS_DST"
|
||||
echo "$snippet_content" >> "$AGENTS_DST"
|
||||
replace_placeholders "$AGENTS_DST"
|
||||
echo "Appended: AGENTS.md ($SECTION_NAME section)"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Skip: AGENTS.template.md not found"
|
||||
fi
|
||||
|
||||
# 4. Sync AGENT_RULES.md
|
||||
if [ -f "$AGENT_RULES_SRC" ]; then
|
||||
AGENT_RULES_DST="$PROJECT_ROOT/AGENT_RULES.md"
|
||||
|
||||
if [ -e "$AGENT_RULES_DST" ] && [ "$FORCE" -eq 0 ]; then
|
||||
echo "AGENT_RULES.md already exists. Use --force to overwrite."
|
||||
else
|
||||
backup_if_exists "$AGENT_RULES_DST"
|
||||
cp "$AGENT_RULES_SRC" "$AGENT_RULES_DST"
|
||||
replace_placeholders "$AGENT_RULES_DST"
|
||||
echo "Synced: AGENT_RULES.md"
|
||||
fi
|
||||
else
|
||||
echo "Skip: AGENT_RULES.template.md not found"
|
||||
fi
|
||||
|
||||
# 5. Create TODO.md and CONFIRM.md if not exist
|
||||
if [ ! -f "$PROJECT_ROOT/TODO.md" ]; then
|
||||
cat > "$PROJECT_ROOT/TODO.md" <<'EOF'
|
||||
# TODO
|
||||
|
||||
## Plan 1: [计划名称]
|
||||
|
||||
- [ ] 任务 1
|
||||
- [ ] 任务 2
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:{{DATE}}
|
||||
EOF
|
||||
date_repl="$(escape_sed_replacement "$SYNC_DATE")"
|
||||
if ! sed -i "s/{{DATE}}/$date_repl/g" "$PROJECT_ROOT/TODO.md" 2>/dev/null; then
|
||||
sed "s/{{DATE}}/$date_repl/g" "$PROJECT_ROOT/TODO.md" > "$PROJECT_ROOT/TODO.md.tmp"
|
||||
mv "$PROJECT_ROOT/TODO.md.tmp" "$PROJECT_ROOT/TODO.md"
|
||||
fi
|
||||
echo "Created: TODO.md"
|
||||
fi
|
||||
|
||||
if [ ! -f "$PROJECT_ROOT/CONFIRM.md" ]; then
|
||||
cat > "$PROJECT_ROOT/CONFIRM.md" <<'EOF'
|
||||
# 待确认事项
|
||||
|
||||
## 待确认
|
||||
|
||||
<!-- 记录需要确认的事项 -->
|
||||
|
||||
## 已确认
|
||||
|
||||
<!-- 已确认的事项移到这里 -->
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:{{DATE}}
|
||||
EOF
|
||||
date_repl="$(escape_sed_replacement "$SYNC_DATE")"
|
||||
if ! sed -i "s/{{DATE}}/$date_repl/g" "$PROJECT_ROOT/CONFIRM.md" 2>/dev/null; then
|
||||
sed "s/{{DATE}}/$date_repl/g" "$PROJECT_ROOT/CONFIRM.md" > "$PROJECT_ROOT/CONFIRM.md.tmp"
|
||||
mv "$PROJECT_ROOT/CONFIRM.md.tmp" "$PROJECT_ROOT/CONFIRM.md"
|
||||
fi
|
||||
echo "Created: CONFIRM.md"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done."
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Edit memory-bank/*.md to fill in project-specific content"
|
||||
echo " 2. Replace remaining {{PLACEHOLDER}} values"
|
||||
echo " 3. Run sync_standards.sh to sync .agents/ rules"
|
||||
Reference in New Issue
Block a user