update-agent-context.ps1 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. #!/usr/bin/env pwsh
  2. <#!
  3. .SYNOPSIS
  4. Update agent context files with information from plan.md (PowerShell version)
  5. .DESCRIPTION
  6. Mirrors the behavior of scripts/bash/update-agent-context.sh:
  7. 1. Environment Validation
  8. 2. Plan Data Extraction
  9. 3. Agent File Management (create from template or update existing)
  10. 4. Content Generation (technology stack, recent changes, timestamp)
  11. 5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, junie, kilocode, auggie, roo, codebuddy, amp, shai, tabnine, kiro-cli, agy, bob, vibe, qodercli, kimi, trae, pi, iflow, forge, generic)
  12. .PARAMETER AgentType
  13. Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist).
  14. .EXAMPLE
  15. ./update-agent-context.ps1 -AgentType claude
  16. .EXAMPLE
  17. ./update-agent-context.ps1 # Updates all existing agent files
  18. .NOTES
  19. Relies on common helper functions in common.ps1
  20. #>
  21. param(
  22. [Parameter(Position=0)]
  23. [ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','junie','kilocode','auggie','roo','codebuddy','amp','shai','tabnine','kiro-cli','agy','bob','vibe','qodercli','kimi','trae','pi','iflow','forge','generic')]
  24. [string]$AgentType
  25. )
  26. $ErrorActionPreference = 'Stop'
  27. # Import common helpers
  28. $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
  29. . (Join-Path $ScriptDir 'common.ps1')
  30. # Acquire environment paths
  31. $envData = Get-FeaturePathsEnv
  32. $REPO_ROOT = $envData.REPO_ROOT
  33. $CURRENT_BRANCH = $envData.CURRENT_BRANCH
  34. $HAS_GIT = $envData.HAS_GIT
  35. $IMPL_PLAN = $envData.IMPL_PLAN
  36. $NEW_PLAN = $IMPL_PLAN
  37. # Agent file paths
  38. $CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md'
  39. $GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md'
  40. $COPILOT_FILE = Join-Path $REPO_ROOT '.github/copilot-instructions.md'
  41. $CURSOR_FILE = Join-Path $REPO_ROOT '.cursor/rules/specify-rules.mdc'
  42. $QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md'
  43. $AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
  44. $WINDSURF_FILE = Join-Path $REPO_ROOT '.windsurf/rules/specify-rules.md'
  45. $JUNIE_FILE = Join-Path $REPO_ROOT '.junie/AGENTS.md'
  46. $KILOCODE_FILE = Join-Path $REPO_ROOT '.kilocode/rules/specify-rules.md'
  47. $AUGGIE_FILE = Join-Path $REPO_ROOT '.augment/rules/specify-rules.md'
  48. $ROO_FILE = Join-Path $REPO_ROOT '.roo/rules/specify-rules.md'
  49. $CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md'
  50. $QODER_FILE = Join-Path $REPO_ROOT 'QODER.md'
  51. $AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
  52. $SHAI_FILE = Join-Path $REPO_ROOT 'SHAI.md'
  53. $TABNINE_FILE = Join-Path $REPO_ROOT 'TABNINE.md'
  54. $KIRO_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
  55. $AGY_FILE = Join-Path $REPO_ROOT '.agent/rules/specify-rules.md'
  56. $BOB_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
  57. $VIBE_FILE = Join-Path $REPO_ROOT '.vibe/agents/specify-agents.md'
  58. $KIMI_FILE = Join-Path $REPO_ROOT 'KIMI.md'
  59. $TRAE_FILE = Join-Path $REPO_ROOT '.trae/rules/AGENTS.md'
  60. $IFLOW_FILE = Join-Path $REPO_ROOT 'IFLOW.md'
  61. $FORGE_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
  62. $TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md'
  63. # Parsed plan data placeholders
  64. $script:NEW_LANG = ''
  65. $script:NEW_FRAMEWORK = ''
  66. $script:NEW_DB = ''
  67. $script:NEW_PROJECT_TYPE = ''
  68. function Write-Info {
  69. param(
  70. [Parameter(Mandatory=$true)]
  71. [string]$Message
  72. )
  73. Write-Host "INFO: $Message"
  74. }
  75. function Write-Success {
  76. param(
  77. [Parameter(Mandatory=$true)]
  78. [string]$Message
  79. )
  80. Write-Host "$([char]0x2713) $Message"
  81. }
  82. function Write-WarningMsg {
  83. param(
  84. [Parameter(Mandatory=$true)]
  85. [string]$Message
  86. )
  87. Write-Warning $Message
  88. }
  89. function Write-Err {
  90. param(
  91. [Parameter(Mandatory=$true)]
  92. [string]$Message
  93. )
  94. Write-Host "ERROR: $Message" -ForegroundColor Red
  95. }
  96. function Validate-Environment {
  97. if (-not $CURRENT_BRANCH) {
  98. Write-Err 'Unable to determine current feature'
  99. if ($HAS_GIT) { Write-Info "Make sure you're on a feature branch" } else { Write-Info 'Set SPECIFY_FEATURE environment variable or create a feature first' }
  100. exit 1
  101. }
  102. if (-not (Test-Path $NEW_PLAN)) {
  103. Write-Err "No plan.md found at $NEW_PLAN"
  104. Write-Info 'Ensure you are working on a feature with a corresponding spec directory'
  105. if (-not $HAS_GIT) { Write-Info 'Use: $env:SPECIFY_FEATURE=your-feature-name or create a new feature first' }
  106. exit 1
  107. }
  108. if (-not (Test-Path $TEMPLATE_FILE)) {
  109. Write-Err "Template file not found at $TEMPLATE_FILE"
  110. Write-Info 'Run specify init to scaffold .specify/templates, or add agent-file-template.md there.'
  111. exit 1
  112. }
  113. }
  114. function Extract-PlanField {
  115. param(
  116. [Parameter(Mandatory=$true)]
  117. [string]$FieldPattern,
  118. [Parameter(Mandatory=$true)]
  119. [string]$PlanFile
  120. )
  121. if (-not (Test-Path $PlanFile)) { return '' }
  122. # Lines like **Language/Version**: Python 3.12
  123. $regex = "^\*\*$([Regex]::Escape($FieldPattern))\*\*: (.+)$"
  124. Get-Content -LiteralPath $PlanFile -Encoding utf8 | ForEach-Object {
  125. if ($_ -match $regex) {
  126. $val = $Matches[1].Trim()
  127. if ($val -notin @('NEEDS CLARIFICATION','N/A')) { return $val }
  128. }
  129. } | Select-Object -First 1
  130. }
  131. function Parse-PlanData {
  132. param(
  133. [Parameter(Mandatory=$true)]
  134. [string]$PlanFile
  135. )
  136. if (-not (Test-Path $PlanFile)) { Write-Err "Plan file not found: $PlanFile"; return $false }
  137. Write-Info "Parsing plan data from $PlanFile"
  138. $script:NEW_LANG = Extract-PlanField -FieldPattern 'Language/Version' -PlanFile $PlanFile
  139. $script:NEW_FRAMEWORK = Extract-PlanField -FieldPattern 'Primary Dependencies' -PlanFile $PlanFile
  140. $script:NEW_DB = Extract-PlanField -FieldPattern 'Storage' -PlanFile $PlanFile
  141. $script:NEW_PROJECT_TYPE = Extract-PlanField -FieldPattern 'Project Type' -PlanFile $PlanFile
  142. if ($NEW_LANG) { Write-Info "Found language: $NEW_LANG" } else { Write-WarningMsg 'No language information found in plan' }
  143. if ($NEW_FRAMEWORK) { Write-Info "Found framework: $NEW_FRAMEWORK" }
  144. if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Info "Found database: $NEW_DB" }
  145. if ($NEW_PROJECT_TYPE) { Write-Info "Found project type: $NEW_PROJECT_TYPE" }
  146. return $true
  147. }
  148. function Format-TechnologyStack {
  149. param(
  150. [Parameter(Mandatory=$false)]
  151. [string]$Lang,
  152. [Parameter(Mandatory=$false)]
  153. [string]$Framework
  154. )
  155. $parts = @()
  156. if ($Lang -and $Lang -ne 'NEEDS CLARIFICATION') { $parts += $Lang }
  157. if ($Framework -and $Framework -notin @('NEEDS CLARIFICATION','N/A')) { $parts += $Framework }
  158. if (-not $parts) { return '' }
  159. return ($parts -join ' + ')
  160. }
  161. function Get-ProjectStructure {
  162. param(
  163. [Parameter(Mandatory=$false)]
  164. [string]$ProjectType
  165. )
  166. if ($ProjectType -match 'web') { return "backend/`nfrontend/`ntests/" } else { return "src/`ntests/" }
  167. }
  168. function Get-CommandsForLanguage {
  169. param(
  170. [Parameter(Mandatory=$false)]
  171. [string]$Lang
  172. )
  173. switch -Regex ($Lang) {
  174. 'Python' { return "cd src; pytest; ruff check ." }
  175. 'Rust' { return "cargo test; cargo clippy" }
  176. 'JavaScript|TypeScript' { return "npm test; npm run lint" }
  177. default { return "# Add commands for $Lang" }
  178. }
  179. }
  180. function Get-LanguageConventions {
  181. param(
  182. [Parameter(Mandatory=$false)]
  183. [string]$Lang
  184. )
  185. if ($Lang) { "${Lang}: Follow standard conventions" } else { 'General: Follow standard conventions' }
  186. }
  187. function New-AgentFile {
  188. param(
  189. [Parameter(Mandatory=$true)]
  190. [string]$TargetFile,
  191. [Parameter(Mandatory=$true)]
  192. [string]$ProjectName,
  193. [Parameter(Mandatory=$true)]
  194. [datetime]$Date
  195. )
  196. if (-not (Test-Path $TEMPLATE_FILE)) { Write-Err "Template not found at $TEMPLATE_FILE"; return $false }
  197. $temp = New-TemporaryFile
  198. Copy-Item -LiteralPath $TEMPLATE_FILE -Destination $temp -Force
  199. $projectStructure = Get-ProjectStructure -ProjectType $NEW_PROJECT_TYPE
  200. $commands = Get-CommandsForLanguage -Lang $NEW_LANG
  201. $languageConventions = Get-LanguageConventions -Lang $NEW_LANG
  202. $escaped_lang = $NEW_LANG
  203. $escaped_framework = $NEW_FRAMEWORK
  204. $escaped_branch = $CURRENT_BRANCH
  205. $content = Get-Content -LiteralPath $temp -Raw -Encoding utf8
  206. $content = $content -replace '\[PROJECT NAME\]',$ProjectName
  207. $content = $content -replace '\[DATE\]',$Date.ToString('yyyy-MM-dd')
  208. # Build the technology stack string safely
  209. $techStackForTemplate = ""
  210. if ($escaped_lang -and $escaped_framework) {
  211. $techStackForTemplate = "- $escaped_lang + $escaped_framework ($escaped_branch)"
  212. } elseif ($escaped_lang) {
  213. $techStackForTemplate = "- $escaped_lang ($escaped_branch)"
  214. } elseif ($escaped_framework) {
  215. $techStackForTemplate = "- $escaped_framework ($escaped_branch)"
  216. }
  217. $content = $content -replace '\[EXTRACTED FROM ALL PLAN.MD FILES\]',$techStackForTemplate
  218. # For project structure we manually embed (keep newlines)
  219. $escapedStructure = [Regex]::Escape($projectStructure)
  220. $content = $content -replace '\[ACTUAL STRUCTURE FROM PLANS\]',$escapedStructure
  221. # Replace escaped newlines placeholder after all replacements
  222. $content = $content -replace '\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]',$commands
  223. $content = $content -replace '\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]',$languageConventions
  224. # Build the recent changes string safely
  225. $recentChangesForTemplate = ""
  226. if ($escaped_lang -and $escaped_framework) {
  227. $recentChangesForTemplate = "- ${escaped_branch}: Added ${escaped_lang} + ${escaped_framework}"
  228. } elseif ($escaped_lang) {
  229. $recentChangesForTemplate = "- ${escaped_branch}: Added ${escaped_lang}"
  230. } elseif ($escaped_framework) {
  231. $recentChangesForTemplate = "- ${escaped_branch}: Added ${escaped_framework}"
  232. }
  233. $content = $content -replace '\[LAST 3 FEATURES AND WHAT THEY ADDED\]',$recentChangesForTemplate
  234. # Convert literal \n sequences introduced by Escape to real newlines
  235. $content = $content -replace '\\n',[Environment]::NewLine
  236. # Prepend Cursor frontmatter for .mdc files so rules are auto-included
  237. if ($TargetFile -match '\.mdc$') {
  238. $frontmatter = @('---','description: Project Development Guidelines','globs: ["**/*"]','alwaysApply: true','---','') -join [Environment]::NewLine
  239. $content = $frontmatter + $content
  240. }
  241. $parent = Split-Path -Parent $TargetFile
  242. if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent | Out-Null }
  243. Set-Content -LiteralPath $TargetFile -Value $content -NoNewline -Encoding utf8
  244. Remove-Item $temp -Force
  245. return $true
  246. }
  247. function Update-ExistingAgentFile {
  248. param(
  249. [Parameter(Mandatory=$true)]
  250. [string]$TargetFile,
  251. [Parameter(Mandatory=$true)]
  252. [datetime]$Date
  253. )
  254. if (-not (Test-Path $TargetFile)) { return (New-AgentFile -TargetFile $TargetFile -ProjectName (Split-Path $REPO_ROOT -Leaf) -Date $Date) }
  255. $techStack = Format-TechnologyStack -Lang $NEW_LANG -Framework $NEW_FRAMEWORK
  256. $newTechEntries = @()
  257. if ($techStack) {
  258. $escapedTechStack = [Regex]::Escape($techStack)
  259. if (-not (Select-String -Pattern $escapedTechStack -Path $TargetFile -Quiet)) {
  260. $newTechEntries += "- $techStack ($CURRENT_BRANCH)"
  261. }
  262. }
  263. if ($NEW_DB -and $NEW_DB -notin @('N/A','NEEDS CLARIFICATION')) {
  264. $escapedDB = [Regex]::Escape($NEW_DB)
  265. if (-not (Select-String -Pattern $escapedDB -Path $TargetFile -Quiet)) {
  266. $newTechEntries += "- $NEW_DB ($CURRENT_BRANCH)"
  267. }
  268. }
  269. $newChangeEntry = ''
  270. if ($techStack) { $newChangeEntry = "- ${CURRENT_BRANCH}: Added ${techStack}" }
  271. elseif ($NEW_DB -and $NEW_DB -notin @('N/A','NEEDS CLARIFICATION')) { $newChangeEntry = "- ${CURRENT_BRANCH}: Added ${NEW_DB}" }
  272. $lines = Get-Content -LiteralPath $TargetFile -Encoding utf8
  273. $output = New-Object System.Collections.Generic.List[string]
  274. $inTech = $false; $inChanges = $false; $techAdded = $false; $changeAdded = $false; $existingChanges = 0
  275. for ($i=0; $i -lt $lines.Count; $i++) {
  276. $line = $lines[$i]
  277. if ($line -eq '## Active Technologies') {
  278. $output.Add($line)
  279. $inTech = $true
  280. continue
  281. }
  282. if ($inTech -and $line -match '^##\s') {
  283. if (-not $techAdded -and $newTechEntries.Count -gt 0) { $newTechEntries | ForEach-Object { $output.Add($_) }; $techAdded = $true }
  284. $output.Add($line); $inTech = $false; continue
  285. }
  286. if ($inTech -and [string]::IsNullOrWhiteSpace($line)) {
  287. if (-not $techAdded -and $newTechEntries.Count -gt 0) { $newTechEntries | ForEach-Object { $output.Add($_) }; $techAdded = $true }
  288. $output.Add($line); continue
  289. }
  290. if ($line -eq '## Recent Changes') {
  291. $output.Add($line)
  292. if ($newChangeEntry) { $output.Add($newChangeEntry); $changeAdded = $true }
  293. $inChanges = $true
  294. continue
  295. }
  296. if ($inChanges -and $line -match '^##\s') { $output.Add($line); $inChanges = $false; continue }
  297. if ($inChanges -and $line -match '^- ') {
  298. if ($existingChanges -lt 2) { $output.Add($line); $existingChanges++ }
  299. continue
  300. }
  301. if ($line -match '(\*\*)?Last updated(\*\*)?: .*\d{4}-\d{2}-\d{2}') {
  302. $output.Add(($line -replace '\d{4}-\d{2}-\d{2}',$Date.ToString('yyyy-MM-dd')))
  303. continue
  304. }
  305. $output.Add($line)
  306. }
  307. # Post-loop check: if we're still in the Active Technologies section and haven't added new entries
  308. if ($inTech -and -not $techAdded -and $newTechEntries.Count -gt 0) {
  309. $newTechEntries | ForEach-Object { $output.Add($_) }
  310. }
  311. # Ensure Cursor .mdc files have YAML frontmatter for auto-inclusion
  312. if ($TargetFile -match '\.mdc$' -and $output.Count -gt 0 -and $output[0] -ne '---') {
  313. $frontmatter = @('---','description: Project Development Guidelines','globs: ["**/*"]','alwaysApply: true','---','')
  314. $output.InsertRange(0, $frontmatter)
  315. }
  316. Set-Content -LiteralPath $TargetFile -Value ($output -join [Environment]::NewLine) -Encoding utf8
  317. return $true
  318. }
  319. function Update-AgentFile {
  320. param(
  321. [Parameter(Mandatory=$true)]
  322. [string]$TargetFile,
  323. [Parameter(Mandatory=$true)]
  324. [string]$AgentName
  325. )
  326. if (-not $TargetFile -or -not $AgentName) { Write-Err 'Update-AgentFile requires TargetFile and AgentName'; return $false }
  327. Write-Info "Updating $AgentName context file: $TargetFile"
  328. $projectName = Split-Path $REPO_ROOT -Leaf
  329. $date = Get-Date
  330. $dir = Split-Path -Parent $TargetFile
  331. if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null }
  332. if (-not (Test-Path $TargetFile)) {
  333. if (New-AgentFile -TargetFile $TargetFile -ProjectName $projectName -Date $date) { Write-Success "Created new $AgentName context file" } else { Write-Err 'Failed to create new agent file'; return $false }
  334. } else {
  335. try {
  336. if (Update-ExistingAgentFile -TargetFile $TargetFile -Date $date) { Write-Success "Updated existing $AgentName context file" } else { Write-Err 'Failed to update agent file'; return $false }
  337. } catch {
  338. Write-Err "Cannot access or update existing file: $TargetFile. $_"
  339. return $false
  340. }
  341. }
  342. return $true
  343. }
  344. function Update-SpecificAgent {
  345. param(
  346. [Parameter(Mandatory=$true)]
  347. [string]$Type
  348. )
  349. switch ($Type) {
  350. 'claude' { Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code' }
  351. 'gemini' { Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI' }
  352. 'copilot' { Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot' }
  353. 'cursor-agent' { Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE' }
  354. 'qwen' { Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code' }
  355. 'opencode' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'opencode' }
  356. 'codex' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Codex CLI' }
  357. 'windsurf' { Update-AgentFile -TargetFile $WINDSURF_FILE -AgentName 'Windsurf' }
  358. 'junie' { Update-AgentFile -TargetFile $JUNIE_FILE -AgentName 'Junie' }
  359. 'kilocode' { Update-AgentFile -TargetFile $KILOCODE_FILE -AgentName 'Kilo Code' }
  360. 'auggie' { Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI' }
  361. 'roo' { Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code' }
  362. 'codebuddy' { Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI' }
  363. 'qodercli' { Update-AgentFile -TargetFile $QODER_FILE -AgentName 'Qoder CLI' }
  364. 'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' }
  365. 'shai' { Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI' }
  366. 'tabnine' { Update-AgentFile -TargetFile $TABNINE_FILE -AgentName 'Tabnine CLI' }
  367. 'kiro-cli' { Update-AgentFile -TargetFile $KIRO_FILE -AgentName 'Kiro CLI' }
  368. 'agy' { Update-AgentFile -TargetFile $AGY_FILE -AgentName 'Antigravity' }
  369. 'bob' { Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob' }
  370. 'vibe' { Update-AgentFile -TargetFile $VIBE_FILE -AgentName 'Mistral Vibe' }
  371. 'kimi' { Update-AgentFile -TargetFile $KIMI_FILE -AgentName 'Kimi Code' }
  372. 'trae' { Update-AgentFile -TargetFile $TRAE_FILE -AgentName 'Trae' }
  373. 'pi' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Pi Coding Agent' }
  374. 'iflow' { Update-AgentFile -TargetFile $IFLOW_FILE -AgentName 'iFlow CLI' }
  375. 'forge' { Update-AgentFile -TargetFile $FORGE_FILE -AgentName 'Forge' }
  376. 'generic' { Write-Info 'Generic agent: no predefined context file. Use the agent-specific update script for your agent.' }
  377. default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|generic'; return $false }
  378. }
  379. }
  380. function Update-AllExistingAgents {
  381. $found = $false
  382. $ok = $true
  383. $updatedPaths = @()
  384. # Helper function to update only if file exists and hasn't been updated yet
  385. function Update-IfNew {
  386. param(
  387. [Parameter(Mandatory=$true)]
  388. [string]$FilePath,
  389. [Parameter(Mandatory=$true)]
  390. [string]$AgentName
  391. )
  392. if (-not (Test-Path $FilePath)) { return $true }
  393. # Get the real path to detect duplicates (e.g., AMP_FILE, KIRO_FILE, BOB_FILE all point to AGENTS.md)
  394. $realPath = (Get-Item -LiteralPath $FilePath).FullName
  395. # Check if we've already updated this file
  396. if ($updatedPaths -contains $realPath) {
  397. return $true
  398. }
  399. # Record the file as seen before attempting the update
  400. # Use parent scope (1) to modify Update-AllExistingAgents' local variables
  401. Set-Variable -Name updatedPaths -Value ($updatedPaths + $realPath) -Scope 1
  402. Set-Variable -Name found -Value $true -Scope 1
  403. # Perform the update
  404. return (Update-AgentFile -TargetFile $FilePath -AgentName $AgentName)
  405. }
  406. if (-not (Update-IfNew -FilePath $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }
  407. if (-not (Update-IfNew -FilePath $GEMINI_FILE -AgentName 'Gemini CLI')) { $ok = $false }
  408. if (-not (Update-IfNew -FilePath $COPILOT_FILE -AgentName 'GitHub Copilot')) { $ok = $false }
  409. if (-not (Update-IfNew -FilePath $CURSOR_FILE -AgentName 'Cursor IDE')) { $ok = $false }
  410. if (-not (Update-IfNew -FilePath $QWEN_FILE -AgentName 'Qwen Code')) { $ok = $false }
  411. if (-not (Update-IfNew -FilePath $AGENTS_FILE -AgentName 'Codex/opencode/Amp/Kiro/Bob/Pi/Forge')) { $ok = $false }
  412. if (-not (Update-IfNew -FilePath $WINDSURF_FILE -AgentName 'Windsurf')) { $ok = $false }
  413. if (-not (Update-IfNew -FilePath $JUNIE_FILE -AgentName 'Junie')) { $ok = $false }
  414. if (-not (Update-IfNew -FilePath $KILOCODE_FILE -AgentName 'Kilo Code')) { $ok = $false }
  415. if (-not (Update-IfNew -FilePath $AUGGIE_FILE -AgentName 'Auggie CLI')) { $ok = $false }
  416. if (-not (Update-IfNew -FilePath $ROO_FILE -AgentName 'Roo Code')) { $ok = $false }
  417. if (-not (Update-IfNew -FilePath $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI')) { $ok = $false }
  418. if (-not (Update-IfNew -FilePath $QODER_FILE -AgentName 'Qoder CLI')) { $ok = $false }
  419. if (-not (Update-IfNew -FilePath $SHAI_FILE -AgentName 'SHAI')) { $ok = $false }
  420. if (-not (Update-IfNew -FilePath $TABNINE_FILE -AgentName 'Tabnine CLI')) { $ok = $false }
  421. if (-not (Update-IfNew -FilePath $AGY_FILE -AgentName 'Antigravity')) { $ok = $false }
  422. if (-not (Update-IfNew -FilePath $VIBE_FILE -AgentName 'Mistral Vibe')) { $ok = $false }
  423. if (-not (Update-IfNew -FilePath $KIMI_FILE -AgentName 'Kimi Code')) { $ok = $false }
  424. if (-not (Update-IfNew -FilePath $TRAE_FILE -AgentName 'Trae')) { $ok = $false }
  425. if (-not (Update-IfNew -FilePath $IFLOW_FILE -AgentName 'iFlow CLI')) { $ok = $false }
  426. if (-not $found) {
  427. Write-Info 'No existing agent files found, creating default Claude file...'
  428. if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }
  429. }
  430. return $ok
  431. }
  432. function Print-Summary {
  433. Write-Host ''
  434. Write-Info 'Summary of changes:'
  435. if ($NEW_LANG) { Write-Host " - Added language: $NEW_LANG" }
  436. if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" }
  437. if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" }
  438. Write-Host ''
  439. Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|generic]'
  440. }
  441. function Main {
  442. Validate-Environment
  443. Write-Info "=== Updating agent context files for feature $CURRENT_BRANCH ==="
  444. if (-not (Parse-PlanData -PlanFile $NEW_PLAN)) { Write-Err 'Failed to parse plan data'; exit 1 }
  445. $success = $true
  446. if ($AgentType) {
  447. Write-Info "Updating specific agent: $AgentType"
  448. if (-not (Update-SpecificAgent -Type $AgentType)) { $success = $false }
  449. }
  450. else {
  451. Write-Info 'No agent specified, updating all existing agent files...'
  452. if (-not (Update-AllExistingAgents)) { $success = $false }
  453. }
  454. Print-Summary
  455. if ($success) { Write-Success 'Agent context update completed successfully'; exit 0 } else { Write-Err 'Agent context update completed with errors'; exit 1 }
  456. }
  457. Main