Skip to main content
Glama

MCP Brain Service

by jomapps
update-agent-context.ps1•18.3 kB
#!/usr/bin/env pwsh <#! .SYNOPSIS Update agent context files with information from plan.md (PowerShell version) .DESCRIPTION Mirrors the behavior of scripts/bash/update-agent-context.sh: 1. Environment Validation 2. Plan Data Extraction 3. Agent File Management (create from template or update existing) 4. Content Generation (technology stack, recent changes, timestamp) 5. Multi-Agent Support (claude, gemini, copilot, cursor, qwen, opencode, codex, windsurf) .PARAMETER AgentType Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist). .EXAMPLE ./update-agent-context.ps1 -AgentType claude .EXAMPLE ./update-agent-context.ps1 # Updates all existing agent files .NOTES Relies on common helper functions in common.ps1 #> param( [Parameter(Position=0)] [ValidateSet('claude','gemini','copilot','cursor','qwen','opencode','codex','windsurf','kilocode','auggie','roo')] [string]$AgentType ) $ErrorActionPreference = 'Stop' # Import common helpers $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path . (Join-Path $ScriptDir 'common.ps1') # Acquire environment paths $envData = Get-FeaturePathsEnv $REPO_ROOT = $envData.REPO_ROOT $CURRENT_BRANCH = $envData.CURRENT_BRANCH $HAS_GIT = $envData.HAS_GIT $IMPL_PLAN = $envData.IMPL_PLAN $NEW_PLAN = $IMPL_PLAN # Agent file paths $CLAUDE_FILE = Join-Path $REPO_ROOT 'CLAUDE.md' $GEMINI_FILE = Join-Path $REPO_ROOT 'GEMINI.md' $COPILOT_FILE = Join-Path $REPO_ROOT '.github/copilot-instructions.md' $CURSOR_FILE = Join-Path $REPO_ROOT '.cursor/rules/specify-rules.mdc' $QWEN_FILE = Join-Path $REPO_ROOT 'QWEN.md' $AGENTS_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $WINDSURF_FILE = Join-Path $REPO_ROOT '.windsurf/rules/specify-rules.md' $KILOCODE_FILE = Join-Path $REPO_ROOT '.kilocode/rules/specify-rules.md' $AUGGIE_FILE = Join-Path $REPO_ROOT '.augment/rules/specify-rules.md' $ROO_FILE = Join-Path $REPO_ROOT '.roo/rules/specify-rules.md' $TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md' # Parsed plan data placeholders $script:NEW_LANG = '' $script:NEW_FRAMEWORK = '' $script:NEW_DB = '' $script:NEW_PROJECT_TYPE = '' function Write-Info { param( [Parameter(Mandatory=$true)] [string]$Message ) Write-Host "INFO: $Message" } function Write-Success { param( [Parameter(Mandatory=$true)] [string]$Message ) Write-Host "$([char]0x2713) $Message" } function Write-WarningMsg { param( [Parameter(Mandatory=$true)] [string]$Message ) Write-Warning $Message } function Write-Err { param( [Parameter(Mandatory=$true)] [string]$Message ) Write-Host "ERROR: $Message" -ForegroundColor Red } function Validate-Environment { if (-not $CURRENT_BRANCH) { Write-Err 'Unable to determine current feature' 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' } exit 1 } if (-not (Test-Path $NEW_PLAN)) { Write-Err "No plan.md found at $NEW_PLAN" Write-Info 'Ensure you are working on a feature with a corresponding spec directory' if (-not $HAS_GIT) { Write-Info 'Use: $env:SPECIFY_FEATURE=your-feature-name or create a new feature first' } exit 1 } if (-not (Test-Path $TEMPLATE_FILE)) { Write-Err "Template file not found at $TEMPLATE_FILE" Write-Info 'Run specify init to scaffold .specify/templates, or add agent-file-template.md there.' exit 1 } } function Extract-PlanField { param( [Parameter(Mandatory=$true)] [string]$FieldPattern, [Parameter(Mandatory=$true)] [string]$PlanFile ) if (-not (Test-Path $PlanFile)) { return '' } # Lines like **Language/Version**: Python 3.12 $regex = "^\*\*$([Regex]::Escape($FieldPattern))\*\*: (.+)$" Get-Content -LiteralPath $PlanFile | ForEach-Object { if ($_ -match $regex) { $val = $Matches[1].Trim() if ($val -notin @('NEEDS CLARIFICATION','N/A')) { return $val } } } | Select-Object -First 1 } function Parse-PlanData { param( [Parameter(Mandatory=$true)] [string]$PlanFile ) if (-not (Test-Path $PlanFile)) { Write-Err "Plan file not found: $PlanFile"; return $false } Write-Info "Parsing plan data from $PlanFile" $script:NEW_LANG = Extract-PlanField -FieldPattern 'Language/Version' -PlanFile $PlanFile $script:NEW_FRAMEWORK = Extract-PlanField -FieldPattern 'Primary Dependencies' -PlanFile $PlanFile $script:NEW_DB = Extract-PlanField -FieldPattern 'Storage' -PlanFile $PlanFile $script:NEW_PROJECT_TYPE = Extract-PlanField -FieldPattern 'Project Type' -PlanFile $PlanFile if ($NEW_LANG) { Write-Info "Found language: $NEW_LANG" } else { Write-WarningMsg 'No language information found in plan' } if ($NEW_FRAMEWORK) { Write-Info "Found framework: $NEW_FRAMEWORK" } if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Info "Found database: $NEW_DB" } if ($NEW_PROJECT_TYPE) { Write-Info "Found project type: $NEW_PROJECT_TYPE" } return $true } function Format-TechnologyStack { param( [Parameter(Mandatory=$false)] [string]$Lang, [Parameter(Mandatory=$false)] [string]$Framework ) $parts = @() if ($Lang -and $Lang -ne 'NEEDS CLARIFICATION') { $parts += $Lang } if ($Framework -and $Framework -notin @('NEEDS CLARIFICATION','N/A')) { $parts += $Framework } if (-not $parts) { return '' } return ($parts -join ' + ') } function Get-ProjectStructure { param( [Parameter(Mandatory=$false)] [string]$ProjectType ) if ($ProjectType -match 'web') { return "backend/`nfrontend/`ntests/" } else { return "src/`ntests/" } } function Get-CommandsForLanguage { param( [Parameter(Mandatory=$false)] [string]$Lang ) switch -Regex ($Lang) { 'Python' { return "cd src; pytest; ruff check ." } 'Rust' { return "cargo test; cargo clippy" } 'JavaScript|TypeScript' { return "npm test; npm run lint" } default { return "# Add commands for $Lang" } } } function Get-LanguageConventions { param( [Parameter(Mandatory=$false)] [string]$Lang ) if ($Lang) { "${Lang}: Follow standard conventions" } else { 'General: Follow standard conventions' } } function New-AgentFile { param( [Parameter(Mandatory=$true)] [string]$TargetFile, [Parameter(Mandatory=$true)] [string]$ProjectName, [Parameter(Mandatory=$true)] [datetime]$Date ) if (-not (Test-Path $TEMPLATE_FILE)) { Write-Err "Template not found at $TEMPLATE_FILE"; return $false } $temp = New-TemporaryFile Copy-Item -LiteralPath $TEMPLATE_FILE -Destination $temp -Force $projectStructure = Get-ProjectStructure -ProjectType $NEW_PROJECT_TYPE $commands = Get-CommandsForLanguage -Lang $NEW_LANG $languageConventions = Get-LanguageConventions -Lang $NEW_LANG $escaped_lang = $NEW_LANG $escaped_framework = $NEW_FRAMEWORK $escaped_branch = $CURRENT_BRANCH $content = Get-Content -LiteralPath $temp -Raw $content = $content -replace '\[PROJECT NAME\]',$ProjectName $content = $content -replace '\[DATE\]',$Date.ToString('yyyy-MM-dd') # Build the technology stack string safely $techStackForTemplate = "" if ($escaped_lang -and $escaped_framework) { $techStackForTemplate = "- $escaped_lang + $escaped_framework ($escaped_branch)" } elseif ($escaped_lang) { $techStackForTemplate = "- $escaped_lang ($escaped_branch)" } elseif ($escaped_framework) { $techStackForTemplate = "- $escaped_framework ($escaped_branch)" } $content = $content -replace '\[EXTRACTED FROM ALL PLAN.MD FILES\]',$techStackForTemplate # For project structure we manually embed (keep newlines) $escapedStructure = [Regex]::Escape($projectStructure) $content = $content -replace '\[ACTUAL STRUCTURE FROM PLANS\]',$escapedStructure # Replace escaped newlines placeholder after all replacements $content = $content -replace '\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]',$commands $content = $content -replace '\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]',$languageConventions # Build the recent changes string safely $recentChangesForTemplate = "" if ($escaped_lang -and $escaped_framework) { $recentChangesForTemplate = "- ${escaped_branch}: Added ${escaped_lang} + ${escaped_framework}" } elseif ($escaped_lang) { $recentChangesForTemplate = "- ${escaped_branch}: Added ${escaped_lang}" } elseif ($escaped_framework) { $recentChangesForTemplate = "- ${escaped_branch}: Added ${escaped_framework}" } $content = $content -replace '\[LAST 3 FEATURES AND WHAT THEY ADDED\]',$recentChangesForTemplate # Convert literal \n sequences introduced by Escape to real newlines $content = $content -replace '\\n',[Environment]::NewLine $parent = Split-Path -Parent $TargetFile if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent | Out-Null } Set-Content -LiteralPath $TargetFile -Value $content -NoNewline Remove-Item $temp -Force return $true } function Update-ExistingAgentFile { param( [Parameter(Mandatory=$true)] [string]$TargetFile, [Parameter(Mandatory=$true)] [datetime]$Date ) if (-not (Test-Path $TargetFile)) { return (New-AgentFile -TargetFile $TargetFile -ProjectName (Split-Path $REPO_ROOT -Leaf) -Date $Date) } $techStack = Format-TechnologyStack -Lang $NEW_LANG -Framework $NEW_FRAMEWORK $newTechEntries = @() if ($techStack) { $escapedTechStack = [Regex]::Escape($techStack) if (-not (Select-String -Pattern $escapedTechStack -Path $TargetFile -Quiet)) { $newTechEntries += "- $techStack ($CURRENT_BRANCH)" } } if ($NEW_DB -and $NEW_DB -notin @('N/A','NEEDS CLARIFICATION')) { $escapedDB = [Regex]::Escape($NEW_DB) if (-not (Select-String -Pattern $escapedDB -Path $TargetFile -Quiet)) { $newTechEntries += "- $NEW_DB ($CURRENT_BRANCH)" } } $newChangeEntry = '' if ($techStack) { $newChangeEntry = "- ${CURRENT_BRANCH}: Added ${techStack}" } elseif ($NEW_DB -and $NEW_DB -notin @('N/A','NEEDS CLARIFICATION')) { $newChangeEntry = "- ${CURRENT_BRANCH}: Added ${NEW_DB}" } $lines = Get-Content -LiteralPath $TargetFile $output = New-Object System.Collections.Generic.List[string] $inTech = $false; $inChanges = $false; $techAdded = $false; $changeAdded = $false; $existingChanges = 0 for ($i=0; $i -lt $lines.Count; $i++) { $line = $lines[$i] if ($line -eq '## Active Technologies') { $output.Add($line) $inTech = $true continue } if ($inTech -and $line -match '^##\s') { if (-not $techAdded -and $newTechEntries.Count -gt 0) { $newTechEntries | ForEach-Object { $output.Add($_) }; $techAdded = $true } $output.Add($line); $inTech = $false; continue } if ($inTech -and [string]::IsNullOrWhiteSpace($line)) { if (-not $techAdded -and $newTechEntries.Count -gt 0) { $newTechEntries | ForEach-Object { $output.Add($_) }; $techAdded = $true } $output.Add($line); continue } if ($line -eq '## Recent Changes') { $output.Add($line) if ($newChangeEntry) { $output.Add($newChangeEntry); $changeAdded = $true } $inChanges = $true continue } if ($inChanges -and $line -match '^##\s') { $output.Add($line); $inChanges = $false; continue } if ($inChanges -and $line -match '^- ') { if ($existingChanges -lt 2) { $output.Add($line); $existingChanges++ } continue } if ($line -match '\*\*Last updated\*\*: .*\d{4}-\d{2}-\d{2}') { $output.Add(($line -replace '\d{4}-\d{2}-\d{2}',$Date.ToString('yyyy-MM-dd'))) continue } $output.Add($line) } # Post-loop check: if we're still in the Active Technologies section and haven't added new entries if ($inTech -and -not $techAdded -and $newTechEntries.Count -gt 0) { $newTechEntries | ForEach-Object { $output.Add($_) } } Set-Content -LiteralPath $TargetFile -Value ($output -join [Environment]::NewLine) return $true } function Update-AgentFile { param( [Parameter(Mandatory=$true)] [string]$TargetFile, [Parameter(Mandatory=$true)] [string]$AgentName ) if (-not $TargetFile -or -not $AgentName) { Write-Err 'Update-AgentFile requires TargetFile and AgentName'; return $false } Write-Info "Updating $AgentName context file: $TargetFile" $projectName = Split-Path $REPO_ROOT -Leaf $date = Get-Date $dir = Split-Path -Parent $TargetFile if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null } if (-not (Test-Path $TargetFile)) { 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 } } else { try { if (Update-ExistingAgentFile -TargetFile $TargetFile -Date $date) { Write-Success "Updated existing $AgentName context file" } else { Write-Err 'Failed to update agent file'; return $false } } catch { Write-Err "Cannot access or update existing file: $TargetFile. $_" return $false } } return $true } function Update-SpecificAgent { param( [Parameter(Mandatory=$true)] [string]$Type ) switch ($Type) { 'claude' { Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code' } 'gemini' { Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI' } 'copilot' { Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot' } 'cursor' { Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE' } 'qwen' { Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code' } 'opencode' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'opencode' } 'codex' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Codex CLI' } 'windsurf' { Update-AgentFile -TargetFile $WINDSURF_FILE -AgentName 'Windsurf' } 'kilocode' { Update-AgentFile -TargetFile $KILOCODE_FILE -AgentName 'Kilo Code' } 'auggie' { Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI' } 'roo' { Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code' } default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|roo'; return $false } } } function Update-AllExistingAgents { $found = $false $ok = $true if (Test-Path $CLAUDE_FILE) { if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false }; $found = $true } if (Test-Path $GEMINI_FILE) { if (-not (Update-AgentFile -TargetFile $GEMINI_FILE -AgentName 'Gemini CLI')) { $ok = $false }; $found = $true } if (Test-Path $COPILOT_FILE) { if (-not (Update-AgentFile -TargetFile $COPILOT_FILE -AgentName 'GitHub Copilot')) { $ok = $false }; $found = $true } if (Test-Path $CURSOR_FILE) { if (-not (Update-AgentFile -TargetFile $CURSOR_FILE -AgentName 'Cursor IDE')) { $ok = $false }; $found = $true } if (Test-Path $QWEN_FILE) { if (-not (Update-AgentFile -TargetFile $QWEN_FILE -AgentName 'Qwen Code')) { $ok = $false }; $found = $true } if (Test-Path $AGENTS_FILE) { if (-not (Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Codex/opencode')) { $ok = $false }; $found = $true } if (Test-Path $WINDSURF_FILE) { if (-not (Update-AgentFile -TargetFile $WINDSURF_FILE -AgentName 'Windsurf')) { $ok = $false }; $found = $true } if (Test-Path $KILOCODE_FILE) { if (-not (Update-AgentFile -TargetFile $KILOCODE_FILE -AgentName 'Kilo Code')) { $ok = $false }; $found = $true } if (Test-Path $AUGGIE_FILE) { if (-not (Update-AgentFile -TargetFile $AUGGIE_FILE -AgentName 'Auggie CLI')) { $ok = $false }; $found = $true } if (Test-Path $ROO_FILE) { if (-not (Update-AgentFile -TargetFile $ROO_FILE -AgentName 'Roo Code')) { $ok = $false }; $found = $true } if (-not $found) { Write-Info 'No existing agent files found, creating default Claude file...' if (-not (Update-AgentFile -TargetFile $CLAUDE_FILE -AgentName 'Claude Code')) { $ok = $false } } return $ok } function Print-Summary { Write-Host '' Write-Info 'Summary of changes:' if ($NEW_LANG) { Write-Host " - Added language: $NEW_LANG" } if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" } if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" } Write-Host '' Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|roo]' } function Main { Validate-Environment Write-Info "=== Updating agent context files for feature $CURRENT_BRANCH ===" if (-not (Parse-PlanData -PlanFile $NEW_PLAN)) { Write-Err 'Failed to parse plan data'; exit 1 } $success = $true if ($AgentType) { Write-Info "Updating specific agent: $AgentType" if (-not (Update-SpecificAgent -Type $AgentType)) { $success = $false } } else { Write-Info 'No agent specified, updating all existing agent files...' if (-not (Update-AllExistingAgents)) { $success = $false } } Print-Summary if ($success) { Write-Success 'Agent context update completed successfully'; exit 0 } else { Write-Err 'Agent context update completed with errors'; exit 1 } } Main

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jomapps/mcp-brain-service'

If you have feedback or need assistance with the MCP directory API, please join our Discord server