mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 20:36:23 +08:00
* feat: add Forgecode (forge) agent support
- Add 'forgecode' to AGENT_CONFIGS in agents.py with .forge/commands
directory, markdown format, and {{parameters}} argument placeholder
- Add 'forgecode' to AGENT_CONFIG in __init__.py with .forge/ folder,
install URL, and requires_cli=True
- Add forgecode binary check in check_tool() mapping agent key
'forgecode' to the actual 'forge' CLI binary
- Add forgecode case to build_variant() in create-release-packages.sh
generating commands into .forge/commands/ with {{parameters}}
- Add forgecode to ALL_AGENTS in create-release-packages.sh
* fix: strip handoffs frontmatter and replace $ARGUMENTS for forgecode
The forgecode agent hangs when listing commands because the 'handoffs'
frontmatter field (a Claude Code-specific feature) contains 'send: true'
entries that forge tries to act on when indexing .forge/commands/ files.
Additionally, $ARGUMENTS in command bodies was never replaced with
{{parameters}}, so user input was not passed through to commands.
Python path (agents.py):
- Add strip_frontmatter_keys: [handoffs] to the forgecode AGENT_CONFIG
entry so register_commands drops the key before rendering
Bash path (create-release-packages.sh):
- Add extra_strip_key parameter to generate_commands; pass 'handoffs'
for the forgecode case in build_variant
- Use regex prefix match (~ "^"extra_key":") instead of exact
equality to handle trailing whitespace after the YAML key
- Add sed replacement of $ARGUMENTS -> $arg_format in the body
pipeline so {{parameters}} is substituted in forgecode command files
* feat: add name field injection for forgecode agent
Forgecode requires both 'name' and 'description' fields in command
frontmatter. This commit adds automatic injection of the 'name' field
during command generation for forgecode.
Changes:
- Python (agents.py): Add inject_name: True to forgecode config and
implement name injection logic in register_commands
- Bash (create-release-packages.sh): Add post-processing step to inject
name field into frontmatter after command generation
This complements the existing handoffs stripping fix (d83be82) to fully
support forgecode command requirements.
* test: update test_argument_token_format for forgecode special case
Forgecode uses {{parameters}} instead of the standard $ARGUMENTS
placeholder. Updated test to check for the correct placeholder format
for forgecode agent.
- Added special case handling for forgecode in test_argument_token_format
- Updated docstring to document forgecode's {{parameters}} format
- Test now passes for all 26 agents including forgecode
* docs: add forgecode to README documentation
Added forgecode agent to all relevant sections:
- Added to Supported AI Agents table
- Added to --ai option description
- Added to specify check command examples
- Added initialization example
- Added to CLI tools check list in detailed walkthrough
Forgecode is now fully documented alongside other supported agents.
* fix: show 'forge' binary name in user-facing messages for forgecode
Addresses Copilot PR feedback: Users should see the actual executable
name 'forge' in status and error messages, not the agent key 'forgecode'.
Changes:
- Added 'cli_binary' field to forgecode AGENT_CONFIG (set to 'forge')
- Updated check_tool() to accept optional display_key parameter
- Updated check_tool() to use cli_binary from AGENT_CONFIG when available
- Updated check() command to display cli_binary in StepTracker
- Updated init() error message to show cli_binary instead of agent key
UX improvements:
- 'specify check' now shows: '● forge (available/not found)'
- 'specify init --ai forgecode' error shows: 'forge not found'
(instead of confusing 'forgecode not found')
This makes it clear to users that they need to install the 'forge'
binary, even though they selected the 'forgecode' agent.
* refactor: rename forgecode agent key to forge
Aligns with AGENTS.md design principle: "Use the actual CLI tool
name as the key, not a shortened version" (AGENTS.md:61-83).
The actual CLI executable is 'forge', so the AGENT_CONFIG key should
be 'forge' (not 'forgecode'). This follows the same pattern as other
agents like cursor-agent and kiro-cli.
Changes:
- Renamed AGENT_CONFIG key: "forgecode" → "forge"
- Removed cli_binary field (no longer needed)
- Simplified check_tool() - removed cli_binary lookup logic
- Simplified init() and check() - removed display_key mapping
- Updated all tests: test_forge_name_field_in_frontmatter
- Updated documentation: README.md
Code simplification:
- Removed 6 lines of workaround code
- Removed 1 function parameter (display_key)
- Eliminated all special-case logic for forge
Note: No backward compatibility needed - forge is a new agent
being introduced in this PR.
* fix: ensure forge alias commands have correct name in frontmatter
When inject_name is enabled (for forge), alias command files must
have their own name field in frontmatter, not reuse the primary
command's name. This is critical for Forge's command discovery
and dispatch system.
Changes:
- For agents with inject_name, create a deepcopy of frontmatter
for each alias and set the name to the alias name
- Re-render the command content with the alias-specific frontmatter
- Ensures each alias file has the correct name field matching its
filename
This fixes command discovery issues where forge would try to invoke
aliases using the primary command's name.
* feat: add forge to PowerShell script and fix test whitespace
1. PowerShell script (create-release-packages.ps1):
- Added forge agent support for Windows users
- Enables `specify init --ai forge --offline` on Windows
- Enhanced Generate-Commands with ExtraStripKey parameter
- Added frontmatter stripping for handoffs key
- Added $ARGUMENTS replacement for {{parameters}}
- Implemented forge case with name field injection
- Complete parity with bash script
2. Test file (test_core_pack_scaffold.py):
- Removed trailing whitespace from blank lines
- Cleaner diffs and no linter warnings
Addresses Copilot PR feedback on both issues.
* fix: use .NET Regex.Replace for count-limited replacement in PowerShell
Addresses Copilot feedback: PowerShell's -replace operator does not
support a third argument for replacement count. Using it causes an
error or mis-parsing that would break forge package generation on
Windows.
Changed from:
$content -replace '(?m)^---$', "---`nname: $cmdName", 1
To:
$regex = [regex]'(?m)^---$'
$content = $regex.Replace($content, "---`nname: $cmdName", 1)
The .NET Regex.Replace() method properly supports the count parameter,
ensuring the name field is injected only after the first frontmatter
delimiter (not the closing one).
This fix is critical for Windows users running:
specify init --ai forge --offline
* Apply suggestion from @Copilot
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* feat: migrate Forge agent to Python integration system
- Create ForgeIntegration class with custom processing for {{parameters}}, handoffs stripping, and name injection
- Add update-context scripts (bash and PowerShell) for Forge
- Register Forge in integration registry
- Update AGENTS.md with Forge documentation and special processing requirements section
- Add comprehensive test suite (11 tests, all passing)
Closes migration from release packaging to Python-based scaffolding for Forge agent.
* fix: replace $ARGUMENTS with {{parameters}} in Forge templates
- Add replacement of $ARGUMENTS to {{parameters}} after template processing
- Use arg_placeholder from config (Copilot's cleaner approach)
- Remove unused 'import re' from _apply_forge_transformations()
- Enhance tests to verify $ARGUMENTS replacement works correctly
- All 11 tests pass
Fixes template processing to ensure Forge receives user-supplied parameters correctly.
* refactor: make ForgeIntegration extend MarkdownIntegration
- Change base class from IntegrationBase to MarkdownIntegration
- Eliminates ~30 lines of duplicated validation/setup boilerplate
- Aligns with the pattern used by 20+ other markdown agents (Bob, Claude, Windsurf, etc.)
- Update AGENTS.md to reflect new inheritance hierarchy
- All Forge-specific processing retained ({{parameters}}, handoffs stripping, name injection)
- All 535 integration tests pass
This addresses reviewer feedback about using the MarkdownIntegration convenience base class.
* style: remove trailing whitespace from test file
- Strip trailing spaces from blank lines in test_integration_forge.py
- Fixes W291 linting warnings
- No functional changes
* style: remove trailing whitespace from Forge integration
- Strip trailing spaces from blank lines in __init__.py
- Fixes whitespace on lines 20, 86, 90, 93, 139, 143
- Verified other files in forge/ directory have no trailing whitespace
- No functional changes, all tests pass
* test: derive expected commands from templates dynamically
- Remove hard-coded command count (9) and command set from test_directory_structure
- Use forge.list_command_templates() to derive expected commands
- Test now auto-syncs when core command templates are added/removed
- Prevents test breakage when template set changes
- All 11 tests pass
* fix: make Forge update-context scripts handle AGENTS.md directly
- Add fallback logic to update/create AGENTS.md when shared script doesn't support forge yet
- Check if shared dispatcher knows about 'forge' before delegating
- If shared script doesn't support forge, handle AGENTS.md updates directly:
- Add Forge section to existing AGENTS.md if not present
- Create new AGENTS.md with Forge section if file doesn't exist
- Both bash and PowerShell scripts implement same logic
- Prevents 'Unknown agent type' errors until shared scripts add forge support
- Future-compatible: automatically delegates when shared script supports forge
Addresses reviewer feedback about update-context scripts failing without forge support.
* feat: add Forge support to shared update-agent-context scripts
- Add forge case to bash and PowerShell update-agent-context scripts
- Add FORGE_FILE variable mapping to AGENTS.md (like opencode/codex/pi)
- Add forge to all usage/help text and ValidateSet parameters
- Include forge in update_all_existing_agents functions
Wrapper script improvements:
- Simplify Forge wrapper scripts to unconditionally delegate to shared script
- Remove complex fallback logic that created stub AGENTS.md files
- Add clear error messages if shared script is missing/not executable
- Align with pattern used by other integrations (opencode, bob, etc.)
Benefits:
- Plan command's {AGENT_SCRIPT} now works for Forge users
- No more incomplete/stub context files masking missing support
- Cleaner, more maintainable code (-39 lines in wrappers)
- Consistent architecture across all integrations
Update AGENTS.md to document that Forge integration ensures shared scripts
include forge support for context updates.
Addresses reviewer feedback about Forge support being incomplete for
workflow steps that run {AGENT_SCRIPT}.
* 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.
* fix: import timezone from datetime for rate limit header parsing
The _parse_rate_limit_headers() function uses timezone.utc on line 82
but timezone was never imported from datetime. This would raise a
NameError the first time GitHub API rate-limit headers are parsed.
Import timezone alongside datetime to fix the missing import.
* fix: correct variable scope in PowerShell deduplication and update docs
- Fix Update-IfNew in PowerShell update-agent-context.ps1
- Changed from $script: scope to Set-Variable -Scope 1
- Properly mutates parent function's local variables
- Fixes deduplication tracking for shared AGENTS.md file
- Prevents incorrect default Claude file creation
- Update create-release-packages.sh documentation
- Add missing 'forge' to AGENTS list in header comment
- Documentation now matches actual ALL_AGENTS array
Without this fix, AGENTS.md would be updated multiple times (once
for each agent sharing it: opencode, codex, amp, kiro, bob, pi, forge)
and the script would always create a default Claude file even when
agent files exist.
* fix: resolve missing scaffold_from_core_pack import in tests
The test_core_pack_scaffold.py imports scaffold_from_core_pack from
specify_cli, but that symbol does not exist in the current codebase.
This causes an ImportError when the test module is loaded.
Implement a resilient resolver that:
- Tries scaffold_from_core_pack first (expected name)
- Falls back to alternative names (scaffold_from_release_pack, etc.)
- Gracefully skips tests if no compatible entrypoint exists
This prevents import-time failures and makes the test future-proof
for when the actual scaffolding function is added or restored.
* fix: prevent duplicate path prefixes and consolidate shared file updates
PowerShell release script:
- Add deduplication pass to Rewrite-Paths function
- Prevents .specify.specify/ double prefixes in generated commands
- Matches bash script behavior with regex '(?:\.specify/){2,}' -> '.specify/'
Bash update-agent-context script:
- Consolidate AGENTS.md updates to single call
- Remove redundant calls for $AMP_FILE, $KIRO_FILE, $BOB_FILE, $FORGE_FILE
- Update label to 'Codex/opencode/Amp/Kiro/Bob/Pi/Forge' to reflect all agents
- Prevents always-deduped $FORGE_FILE call that never executed
Both fixes improve efficiency and correctness while maintaining parity
between bash and PowerShell implementations.
* refactor: remove unused rate-limit helpers and improve PowerShell scripts
- Remove unused _parse_rate_limit_headers() and _format_rate_limit_error()
from src/specify_cli/__init__.py (56 lines of dead code)
- Add GENRELEASES_DIR override support to PowerShell release script with
comprehensive safety checks (parity with bash script)
- Remove redundant shared-file update calls from PowerShell agent context
script (AMP_FILE, KIRO_FILE, BOB_FILE, FORGE_FILE all resolve to AGENTS.md)
- Update test docstring to accurately reflect Forge's {{parameters}} token
Changes align PowerShell scripts with bash equivalents and reduce maintenance
burden by removing dead code.
* fix: add missing 'forge' to PowerShell usage text and fix agent order
- Add 'forge' to usage message in Print-Summary (was missing from list)
- Reorder ValidateSet to match bash script order (vibe before qodercli)
This ensures PowerShell script documentation matches bash script and includes
all supported agents consistently.
* refactor: remove old architecture files deleted in b1832c9
Remove files that were deleted in b1832c9 (Stage 6 migration) but remained
on this branch due to merge conflicts:
- Remove .github/workflows/scripts/create-release-packages.{sh,ps1}
(replaced by inline release.yml + uv tool install)
- Remove tests/test_core_pack_scaffold.py
(scaffold system removed, tests no longer relevant)
These files existed on the feature branch because they were modified before
b1832c9 landed. The merge kept our versions, but they should be deleted to
align with the new integration-only architecture.
This PR now focuses purely on adding NEW Forge integration support, not
restoring old architecture.
* refactor: remove unused timezone import from __init__.py
Remove unused timezone import that was added in 4a57f79 for rate-limit
header parsing but became obsolete when rate-limit helper functions were
removed in 59c4212 (and also removed in upstream b1832c9).
No functional changes - purely cleanup of unused import.
* docs: clarify that handoffs is a Claude Code feature, not Forge's
Update docstrings to accurately explain that the 'handoffs' frontmatter key
is from Claude Code (for multi-agent collaboration) and is stripped because
it causes Forge to hang, not because it's a Forge-specific feature.
Changes:
- Module docstring: 'Forge-specific collaboration feature' → 'Claude Code feature that causes Forge to hang'
- Class docstring: Add '(incompatible with Forge)' clarification
- Method docstring: Add '(from Claude Code templates; incompatible with Forge)' context
This avoids implying that handoffs belongs to Forge when it actually comes
from spec-kit templates designed for Claude Code compatibility.
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
839 lines
29 KiB
Bash
839 lines
29 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# Update agent context files with information from plan.md
|
|
#
|
|
# This script maintains AI agent context files by parsing feature specifications
|
|
# and updating agent-specific configuration files with project information.
|
|
#
|
|
# MAIN FUNCTIONS:
|
|
# 1. Environment Validation
|
|
# - Verifies git repository structure and branch information
|
|
# - Checks for required plan.md files and templates
|
|
# - Validates file permissions and accessibility
|
|
#
|
|
# 2. Plan Data Extraction
|
|
# - Parses plan.md files to extract project metadata
|
|
# - Identifies language/version, frameworks, databases, and project types
|
|
# - Handles missing or incomplete specification data gracefully
|
|
#
|
|
# 3. Agent File Management
|
|
# - Creates new agent context files from templates when needed
|
|
# - Updates existing agent files with new project information
|
|
# - Preserves manual additions and custom configurations
|
|
# - Supports multiple AI agent formats and directory structures
|
|
#
|
|
# 4. Content Generation
|
|
# - Generates language-specific build/test commands
|
|
# - Creates appropriate project directory structures
|
|
# - Updates technology stacks and recent changes sections
|
|
# - Maintains consistent formatting and timestamps
|
|
#
|
|
# 5. Multi-Agent Support
|
|
# - Handles agent-specific file paths and naming conventions
|
|
# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Junie, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, Tabnine CLI, Kiro CLI, Mistral Vibe, Kimi Code, Pi Coding Agent, iFlow CLI, Forge, Antigravity or Generic
|
|
# - Can update single agents or all existing agent files
|
|
# - Creates default Claude file if no agent files exist
|
|
#
|
|
# Usage: ./update-agent-context.sh [agent_type]
|
|
# Agent types: 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
|
|
# Leave empty to update all existing agent files
|
|
|
|
set -e
|
|
|
|
# Enable strict error handling
|
|
set -u
|
|
set -o pipefail
|
|
|
|
#==============================================================================
|
|
# Configuration and Global Variables
|
|
#==============================================================================
|
|
|
|
# Get script directory and load common functions
|
|
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/common.sh"
|
|
|
|
# Get all paths and variables from common functions
|
|
_paths_output=$(get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; }
|
|
eval "$_paths_output"
|
|
unset _paths_output
|
|
|
|
NEW_PLAN="$IMPL_PLAN" # Alias for compatibility with existing code
|
|
AGENT_TYPE="${1:-}"
|
|
|
|
# Agent-specific file paths
|
|
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
|
|
GEMINI_FILE="$REPO_ROOT/GEMINI.md"
|
|
COPILOT_FILE="$REPO_ROOT/.github/copilot-instructions.md"
|
|
CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc"
|
|
QWEN_FILE="$REPO_ROOT/QWEN.md"
|
|
AGENTS_FILE="$REPO_ROOT/AGENTS.md"
|
|
WINDSURF_FILE="$REPO_ROOT/.windsurf/rules/specify-rules.md"
|
|
JUNIE_FILE="$REPO_ROOT/.junie/AGENTS.md"
|
|
KILOCODE_FILE="$REPO_ROOT/.kilocode/rules/specify-rules.md"
|
|
AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md"
|
|
ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md"
|
|
CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md"
|
|
QODER_FILE="$REPO_ROOT/QODER.md"
|
|
# Amp, Kiro CLI, IBM Bob, Pi, and Forge all share AGENTS.md — use AGENTS_FILE to avoid
|
|
# updating the same file multiple times.
|
|
AMP_FILE="$AGENTS_FILE"
|
|
SHAI_FILE="$REPO_ROOT/SHAI.md"
|
|
TABNINE_FILE="$REPO_ROOT/TABNINE.md"
|
|
KIRO_FILE="$AGENTS_FILE"
|
|
AGY_FILE="$REPO_ROOT/.agent/rules/specify-rules.md"
|
|
BOB_FILE="$AGENTS_FILE"
|
|
VIBE_FILE="$REPO_ROOT/.vibe/agents/specify-agents.md"
|
|
KIMI_FILE="$REPO_ROOT/KIMI.md"
|
|
TRAE_FILE="$REPO_ROOT/.trae/rules/AGENTS.md"
|
|
IFLOW_FILE="$REPO_ROOT/IFLOW.md"
|
|
FORGE_FILE="$AGENTS_FILE"
|
|
|
|
# Template file
|
|
TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md"
|
|
|
|
# Global variables for parsed plan data
|
|
NEW_LANG=""
|
|
NEW_FRAMEWORK=""
|
|
NEW_DB=""
|
|
NEW_PROJECT_TYPE=""
|
|
|
|
#==============================================================================
|
|
# Utility Functions
|
|
#==============================================================================
|
|
|
|
log_info() {
|
|
echo "INFO: $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo "✓ $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo "ERROR: $1" >&2
|
|
}
|
|
|
|
log_warning() {
|
|
echo "WARNING: $1" >&2
|
|
}
|
|
|
|
# Cleanup function for temporary files
|
|
cleanup() {
|
|
local exit_code=$?
|
|
# Disarm traps to prevent re-entrant loop
|
|
trap - EXIT INT TERM
|
|
rm -f /tmp/agent_update_*_$$
|
|
rm -f /tmp/manual_additions_$$
|
|
exit $exit_code
|
|
}
|
|
|
|
# Set up cleanup trap
|
|
trap cleanup EXIT INT TERM
|
|
|
|
#==============================================================================
|
|
# Validation Functions
|
|
#==============================================================================
|
|
|
|
validate_environment() {
|
|
# Check if we have a current branch/feature (git or non-git)
|
|
if [[ -z "$CURRENT_BRANCH" ]]; then
|
|
log_error "Unable to determine current feature"
|
|
if [[ "$HAS_GIT" == "true" ]]; then
|
|
log_info "Make sure you're on a feature branch"
|
|
else
|
|
log_info "Set SPECIFY_FEATURE environment variable or create a feature first"
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
# Check if plan.md exists
|
|
if [[ ! -f "$NEW_PLAN" ]]; then
|
|
log_error "No plan.md found at $NEW_PLAN"
|
|
log_info "Make sure you're working on a feature with a corresponding spec directory"
|
|
if [[ "$HAS_GIT" != "true" ]]; then
|
|
log_info "Use: export SPECIFY_FEATURE=your-feature-name or create a new feature first"
|
|
fi
|
|
exit 1
|
|
fi
|
|
|
|
# Check if template exists (needed for new files)
|
|
if [[ ! -f "$TEMPLATE_FILE" ]]; then
|
|
log_warning "Template file not found at $TEMPLATE_FILE"
|
|
log_warning "Creating new agent files will fail"
|
|
fi
|
|
}
|
|
|
|
#==============================================================================
|
|
# Plan Parsing Functions
|
|
#==============================================================================
|
|
|
|
extract_plan_field() {
|
|
local field_pattern="$1"
|
|
local plan_file="$2"
|
|
|
|
grep "^\*\*${field_pattern}\*\*: " "$plan_file" 2>/dev/null | \
|
|
head -1 | \
|
|
sed "s|^\*\*${field_pattern}\*\*: ||" | \
|
|
sed 's/^[ \t]*//;s/[ \t]*$//' | \
|
|
grep -v "NEEDS CLARIFICATION" | \
|
|
grep -v "^N/A$" || echo ""
|
|
}
|
|
|
|
parse_plan_data() {
|
|
local plan_file="$1"
|
|
|
|
if [[ ! -f "$plan_file" ]]; then
|
|
log_error "Plan file not found: $plan_file"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -r "$plan_file" ]]; then
|
|
log_error "Plan file is not readable: $plan_file"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Parsing plan data from $plan_file"
|
|
|
|
NEW_LANG=$(extract_plan_field "Language/Version" "$plan_file")
|
|
NEW_FRAMEWORK=$(extract_plan_field "Primary Dependencies" "$plan_file")
|
|
NEW_DB=$(extract_plan_field "Storage" "$plan_file")
|
|
NEW_PROJECT_TYPE=$(extract_plan_field "Project Type" "$plan_file")
|
|
|
|
# Log what we found
|
|
if [[ -n "$NEW_LANG" ]]; then
|
|
log_info "Found language: $NEW_LANG"
|
|
else
|
|
log_warning "No language information found in plan"
|
|
fi
|
|
|
|
if [[ -n "$NEW_FRAMEWORK" ]]; then
|
|
log_info "Found framework: $NEW_FRAMEWORK"
|
|
fi
|
|
|
|
if [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]]; then
|
|
log_info "Found database: $NEW_DB"
|
|
fi
|
|
|
|
if [[ -n "$NEW_PROJECT_TYPE" ]]; then
|
|
log_info "Found project type: $NEW_PROJECT_TYPE"
|
|
fi
|
|
}
|
|
|
|
format_technology_stack() {
|
|
local lang="$1"
|
|
local framework="$2"
|
|
local parts=()
|
|
|
|
# Add non-empty parts
|
|
[[ -n "$lang" && "$lang" != "NEEDS CLARIFICATION" ]] && parts+=("$lang")
|
|
[[ -n "$framework" && "$framework" != "NEEDS CLARIFICATION" && "$framework" != "N/A" ]] && parts+=("$framework")
|
|
|
|
# Join with proper formatting
|
|
if [[ ${#parts[@]} -eq 0 ]]; then
|
|
echo ""
|
|
elif [[ ${#parts[@]} -eq 1 ]]; then
|
|
echo "${parts[0]}"
|
|
else
|
|
# Join multiple parts with " + "
|
|
local result="${parts[0]}"
|
|
for ((i=1; i<${#parts[@]}; i++)); do
|
|
result="$result + ${parts[i]}"
|
|
done
|
|
echo "$result"
|
|
fi
|
|
}
|
|
|
|
#==============================================================================
|
|
# Template and Content Generation Functions
|
|
#==============================================================================
|
|
|
|
get_project_structure() {
|
|
local project_type="$1"
|
|
|
|
if [[ "$project_type" == *"web"* ]]; then
|
|
echo "backend/\\nfrontend/\\ntests/"
|
|
else
|
|
echo "src/\\ntests/"
|
|
fi
|
|
}
|
|
|
|
get_commands_for_language() {
|
|
local lang="$1"
|
|
|
|
case "$lang" in
|
|
*"Python"*)
|
|
echo "cd src && pytest && ruff check ."
|
|
;;
|
|
*"Rust"*)
|
|
echo "cargo test && cargo clippy"
|
|
;;
|
|
*"JavaScript"*|*"TypeScript"*)
|
|
echo "npm test \\&\\& npm run lint"
|
|
;;
|
|
*)
|
|
echo "# Add commands for $lang"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
get_language_conventions() {
|
|
local lang="$1"
|
|
echo "$lang: Follow standard conventions"
|
|
}
|
|
|
|
create_new_agent_file() {
|
|
local target_file="$1"
|
|
local temp_file="$2"
|
|
local project_name="$3"
|
|
local current_date="$4"
|
|
|
|
if [[ ! -f "$TEMPLATE_FILE" ]]; then
|
|
log_error "Template not found at $TEMPLATE_FILE"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -r "$TEMPLATE_FILE" ]]; then
|
|
log_error "Template file is not readable: $TEMPLATE_FILE"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Creating new agent context file from template..."
|
|
|
|
if ! cp "$TEMPLATE_FILE" "$temp_file"; then
|
|
log_error "Failed to copy template file"
|
|
return 1
|
|
fi
|
|
|
|
# Replace template placeholders
|
|
local project_structure
|
|
project_structure=$(get_project_structure "$NEW_PROJECT_TYPE")
|
|
|
|
local commands
|
|
commands=$(get_commands_for_language "$NEW_LANG")
|
|
|
|
local language_conventions
|
|
language_conventions=$(get_language_conventions "$NEW_LANG")
|
|
|
|
# Perform substitutions with error checking using safer approach
|
|
# Escape special characters for sed by using a different delimiter or escaping
|
|
local escaped_lang=$(printf '%s\n' "$NEW_LANG" | sed 's/[\[\.*^$()+{}|]/\\&/g')
|
|
local escaped_framework=$(printf '%s\n' "$NEW_FRAMEWORK" | sed 's/[\[\.*^$()+{}|]/\\&/g')
|
|
local escaped_branch=$(printf '%s\n' "$CURRENT_BRANCH" | sed 's/[\[\.*^$()+{}|]/\\&/g')
|
|
|
|
# Build technology stack and recent change strings conditionally
|
|
local tech_stack
|
|
if [[ -n "$escaped_lang" && -n "$escaped_framework" ]]; then
|
|
tech_stack="- $escaped_lang + $escaped_framework ($escaped_branch)"
|
|
elif [[ -n "$escaped_lang" ]]; then
|
|
tech_stack="- $escaped_lang ($escaped_branch)"
|
|
elif [[ -n "$escaped_framework" ]]; then
|
|
tech_stack="- $escaped_framework ($escaped_branch)"
|
|
else
|
|
tech_stack="- ($escaped_branch)"
|
|
fi
|
|
|
|
local recent_change
|
|
if [[ -n "$escaped_lang" && -n "$escaped_framework" ]]; then
|
|
recent_change="- $escaped_branch: Added $escaped_lang + $escaped_framework"
|
|
elif [[ -n "$escaped_lang" ]]; then
|
|
recent_change="- $escaped_branch: Added $escaped_lang"
|
|
elif [[ -n "$escaped_framework" ]]; then
|
|
recent_change="- $escaped_branch: Added $escaped_framework"
|
|
else
|
|
recent_change="- $escaped_branch: Added"
|
|
fi
|
|
|
|
local substitutions=(
|
|
"s|\[PROJECT NAME\]|$project_name|"
|
|
"s|\[DATE\]|$current_date|"
|
|
"s|\[EXTRACTED FROM ALL PLAN.MD FILES\]|$tech_stack|"
|
|
"s|\[ACTUAL STRUCTURE FROM PLANS\]|$project_structure|g"
|
|
"s|\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]|$commands|"
|
|
"s|\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]|$language_conventions|"
|
|
"s|\[LAST 3 FEATURES AND WHAT THEY ADDED\]|$recent_change|"
|
|
)
|
|
|
|
for substitution in "${substitutions[@]}"; do
|
|
if ! sed -i.bak -e "$substitution" "$temp_file"; then
|
|
log_error "Failed to perform substitution: $substitution"
|
|
rm -f "$temp_file" "$temp_file.bak"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
# Convert \n sequences to actual newlines
|
|
newline=$(printf '\n')
|
|
sed -i.bak2 "s/\\\\n/${newline}/g" "$temp_file"
|
|
|
|
# Clean up backup files
|
|
rm -f "$temp_file.bak" "$temp_file.bak2"
|
|
|
|
# Prepend Cursor frontmatter for .mdc files so rules are auto-included
|
|
if [[ "$target_file" == *.mdc ]]; then
|
|
local frontmatter_file
|
|
frontmatter_file=$(mktemp) || return 1
|
|
printf '%s\n' "---" "description: Project Development Guidelines" "globs: [\"**/*\"]" "alwaysApply: true" "---" "" > "$frontmatter_file"
|
|
cat "$temp_file" >> "$frontmatter_file"
|
|
mv "$frontmatter_file" "$temp_file"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
|
|
|
|
update_existing_agent_file() {
|
|
local target_file="$1"
|
|
local current_date="$2"
|
|
|
|
log_info "Updating existing agent context file..."
|
|
|
|
# Use a single temporary file for atomic update
|
|
local temp_file
|
|
temp_file=$(mktemp) || {
|
|
log_error "Failed to create temporary file"
|
|
return 1
|
|
}
|
|
|
|
# Process the file in one pass
|
|
local tech_stack=$(format_technology_stack "$NEW_LANG" "$NEW_FRAMEWORK")
|
|
local new_tech_entries=()
|
|
local new_change_entry=""
|
|
|
|
# Prepare new technology entries
|
|
if [[ -n "$tech_stack" ]] && ! grep -q "$tech_stack" "$target_file"; then
|
|
new_tech_entries+=("- $tech_stack ($CURRENT_BRANCH)")
|
|
fi
|
|
|
|
if [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]] && [[ "$NEW_DB" != "NEEDS CLARIFICATION" ]] && ! grep -q "$NEW_DB" "$target_file"; then
|
|
new_tech_entries+=("- $NEW_DB ($CURRENT_BRANCH)")
|
|
fi
|
|
|
|
# Prepare new change entry
|
|
if [[ -n "$tech_stack" ]]; then
|
|
new_change_entry="- $CURRENT_BRANCH: Added $tech_stack"
|
|
elif [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]] && [[ "$NEW_DB" != "NEEDS CLARIFICATION" ]]; then
|
|
new_change_entry="- $CURRENT_BRANCH: Added $NEW_DB"
|
|
fi
|
|
|
|
# Check if sections exist in the file
|
|
local has_active_technologies=0
|
|
local has_recent_changes=0
|
|
|
|
if grep -q "^## Active Technologies" "$target_file" 2>/dev/null; then
|
|
has_active_technologies=1
|
|
fi
|
|
|
|
if grep -q "^## Recent Changes" "$target_file" 2>/dev/null; then
|
|
has_recent_changes=1
|
|
fi
|
|
|
|
# Process file line by line
|
|
local in_tech_section=false
|
|
local in_changes_section=false
|
|
local tech_entries_added=false
|
|
local changes_entries_added=false
|
|
local existing_changes_count=0
|
|
local file_ended=false
|
|
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
# Handle Active Technologies section
|
|
if [[ "$line" == "## Active Technologies" ]]; then
|
|
echo "$line" >> "$temp_file"
|
|
in_tech_section=true
|
|
continue
|
|
elif [[ $in_tech_section == true ]] && [[ "$line" =~ ^##[[:space:]] ]]; then
|
|
# Add new tech entries before closing the section
|
|
if [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
|
|
printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
|
|
tech_entries_added=true
|
|
fi
|
|
echo "$line" >> "$temp_file"
|
|
in_tech_section=false
|
|
continue
|
|
elif [[ $in_tech_section == true ]] && [[ -z "$line" ]]; then
|
|
# Add new tech entries before empty line in tech section
|
|
if [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
|
|
printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
|
|
tech_entries_added=true
|
|
fi
|
|
echo "$line" >> "$temp_file"
|
|
continue
|
|
fi
|
|
|
|
# Handle Recent Changes section
|
|
if [[ "$line" == "## Recent Changes" ]]; then
|
|
echo "$line" >> "$temp_file"
|
|
# Add new change entry right after the heading
|
|
if [[ -n "$new_change_entry" ]]; then
|
|
echo "$new_change_entry" >> "$temp_file"
|
|
fi
|
|
in_changes_section=true
|
|
changes_entries_added=true
|
|
continue
|
|
elif [[ $in_changes_section == true ]] && [[ "$line" =~ ^##[[:space:]] ]]; then
|
|
echo "$line" >> "$temp_file"
|
|
in_changes_section=false
|
|
continue
|
|
elif [[ $in_changes_section == true ]] && [[ "$line" == "- "* ]]; then
|
|
# Keep only first 2 existing changes
|
|
if [[ $existing_changes_count -lt 2 ]]; then
|
|
echo "$line" >> "$temp_file"
|
|
((existing_changes_count++))
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
# Update timestamp
|
|
if [[ "$line" =~ (\*\*)?Last\ updated(\*\*)?:.*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] ]]; then
|
|
echo "$line" | sed "s/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/$current_date/" >> "$temp_file"
|
|
else
|
|
echo "$line" >> "$temp_file"
|
|
fi
|
|
done < "$target_file"
|
|
|
|
# Post-loop check: if we're still in the Active Technologies section and haven't added new entries
|
|
if [[ $in_tech_section == true ]] && [[ $tech_entries_added == false ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
|
|
printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
|
|
tech_entries_added=true
|
|
fi
|
|
|
|
# If sections don't exist, add them at the end of the file
|
|
if [[ $has_active_technologies -eq 0 ]] && [[ ${#new_tech_entries[@]} -gt 0 ]]; then
|
|
echo "" >> "$temp_file"
|
|
echo "## Active Technologies" >> "$temp_file"
|
|
printf '%s\n' "${new_tech_entries[@]}" >> "$temp_file"
|
|
tech_entries_added=true
|
|
fi
|
|
|
|
if [[ $has_recent_changes -eq 0 ]] && [[ -n "$new_change_entry" ]]; then
|
|
echo "" >> "$temp_file"
|
|
echo "## Recent Changes" >> "$temp_file"
|
|
echo "$new_change_entry" >> "$temp_file"
|
|
changes_entries_added=true
|
|
fi
|
|
|
|
# Ensure Cursor .mdc files have YAML frontmatter for auto-inclusion
|
|
if [[ "$target_file" == *.mdc ]]; then
|
|
if ! head -1 "$temp_file" | grep -q '^---'; then
|
|
local frontmatter_file
|
|
frontmatter_file=$(mktemp) || { rm -f "$temp_file"; return 1; }
|
|
printf '%s\n' "---" "description: Project Development Guidelines" "globs: [\"**/*\"]" "alwaysApply: true" "---" "" > "$frontmatter_file"
|
|
cat "$temp_file" >> "$frontmatter_file"
|
|
mv "$frontmatter_file" "$temp_file"
|
|
fi
|
|
fi
|
|
|
|
# Move temp file to target atomically
|
|
if ! mv "$temp_file" "$target_file"; then
|
|
log_error "Failed to update target file"
|
|
rm -f "$temp_file"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
#==============================================================================
|
|
# Main Agent File Update Function
|
|
#==============================================================================
|
|
|
|
update_agent_file() {
|
|
local target_file="$1"
|
|
local agent_name="$2"
|
|
|
|
if [[ -z "$target_file" ]] || [[ -z "$agent_name" ]]; then
|
|
log_error "update_agent_file requires target_file and agent_name parameters"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Updating $agent_name context file: $target_file"
|
|
|
|
local project_name
|
|
project_name=$(basename "$REPO_ROOT")
|
|
local current_date
|
|
current_date=$(date +%Y-%m-%d)
|
|
|
|
# Create directory if it doesn't exist
|
|
local target_dir
|
|
target_dir=$(dirname "$target_file")
|
|
if [[ ! -d "$target_dir" ]]; then
|
|
if ! mkdir -p "$target_dir"; then
|
|
log_error "Failed to create directory: $target_dir"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
if [[ ! -f "$target_file" ]]; then
|
|
# Create new file from template
|
|
local temp_file
|
|
temp_file=$(mktemp) || {
|
|
log_error "Failed to create temporary file"
|
|
return 1
|
|
}
|
|
|
|
if create_new_agent_file "$target_file" "$temp_file" "$project_name" "$current_date"; then
|
|
if mv "$temp_file" "$target_file"; then
|
|
log_success "Created new $agent_name context file"
|
|
else
|
|
log_error "Failed to move temporary file to $target_file"
|
|
rm -f "$temp_file"
|
|
return 1
|
|
fi
|
|
else
|
|
log_error "Failed to create new agent file"
|
|
rm -f "$temp_file"
|
|
return 1
|
|
fi
|
|
else
|
|
# Update existing file
|
|
if [[ ! -r "$target_file" ]]; then
|
|
log_error "Cannot read existing file: $target_file"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -w "$target_file" ]]; then
|
|
log_error "Cannot write to existing file: $target_file"
|
|
return 1
|
|
fi
|
|
|
|
if update_existing_agent_file "$target_file" "$current_date"; then
|
|
log_success "Updated existing $agent_name context file"
|
|
else
|
|
log_error "Failed to update existing agent file"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
#==============================================================================
|
|
# Agent Selection and Processing
|
|
#==============================================================================
|
|
|
|
update_specific_agent() {
|
|
local agent_type="$1"
|
|
|
|
case "$agent_type" in
|
|
claude)
|
|
update_agent_file "$CLAUDE_FILE" "Claude Code" || return 1
|
|
;;
|
|
gemini)
|
|
update_agent_file "$GEMINI_FILE" "Gemini CLI" || return 1
|
|
;;
|
|
copilot)
|
|
update_agent_file "$COPILOT_FILE" "GitHub Copilot" || return 1
|
|
;;
|
|
cursor-agent)
|
|
update_agent_file "$CURSOR_FILE" "Cursor IDE" || return 1
|
|
;;
|
|
qwen)
|
|
update_agent_file "$QWEN_FILE" "Qwen Code" || return 1
|
|
;;
|
|
opencode)
|
|
update_agent_file "$AGENTS_FILE" "opencode" || return 1
|
|
;;
|
|
codex)
|
|
update_agent_file "$AGENTS_FILE" "Codex CLI" || return 1
|
|
;;
|
|
windsurf)
|
|
update_agent_file "$WINDSURF_FILE" "Windsurf" || return 1
|
|
;;
|
|
junie)
|
|
update_agent_file "$JUNIE_FILE" "Junie" || return 1
|
|
;;
|
|
kilocode)
|
|
update_agent_file "$KILOCODE_FILE" "Kilo Code" || return 1
|
|
;;
|
|
auggie)
|
|
update_agent_file "$AUGGIE_FILE" "Auggie CLI" || return 1
|
|
;;
|
|
roo)
|
|
update_agent_file "$ROO_FILE" "Roo Code" || return 1
|
|
;;
|
|
codebuddy)
|
|
update_agent_file "$CODEBUDDY_FILE" "CodeBuddy CLI" || return 1
|
|
;;
|
|
qodercli)
|
|
update_agent_file "$QODER_FILE" "Qoder CLI" || return 1
|
|
;;
|
|
amp)
|
|
update_agent_file "$AMP_FILE" "Amp" || return 1
|
|
;;
|
|
shai)
|
|
update_agent_file "$SHAI_FILE" "SHAI" || return 1
|
|
;;
|
|
tabnine)
|
|
update_agent_file "$TABNINE_FILE" "Tabnine CLI" || return 1
|
|
;;
|
|
kiro-cli)
|
|
update_agent_file "$KIRO_FILE" "Kiro CLI" || return 1
|
|
;;
|
|
agy)
|
|
update_agent_file "$AGY_FILE" "Antigravity" || return 1
|
|
;;
|
|
bob)
|
|
update_agent_file "$BOB_FILE" "IBM Bob" || return 1
|
|
;;
|
|
vibe)
|
|
update_agent_file "$VIBE_FILE" "Mistral Vibe" || return 1
|
|
;;
|
|
kimi)
|
|
update_agent_file "$KIMI_FILE" "Kimi Code" || return 1
|
|
;;
|
|
trae)
|
|
update_agent_file "$TRAE_FILE" "Trae" || return 1
|
|
;;
|
|
pi)
|
|
update_agent_file "$AGENTS_FILE" "Pi Coding Agent" || return 1
|
|
;;
|
|
iflow)
|
|
update_agent_file "$IFLOW_FILE" "iFlow CLI" || return 1
|
|
;;
|
|
forge)
|
|
update_agent_file "$AGENTS_FILE" "Forge" || return 1
|
|
;;
|
|
generic)
|
|
log_info "Generic agent: no predefined context file. Use the agent-specific update script for your agent."
|
|
;;
|
|
*)
|
|
log_error "Unknown agent type '$agent_type'"
|
|
log_error "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"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Helper: skip non-existent files and files already updated (dedup by
|
|
# realpath so that variables pointing to the same file — e.g. AMP_FILE,
|
|
# KIRO_FILE, BOB_FILE all resolving to AGENTS_FILE — are only written once).
|
|
# Uses a linear array instead of associative array for bash 3.2 compatibility.
|
|
# Note: defined at top level because bash 3.2 does not support true
|
|
# nested/local functions. _updated_paths, _found_agent, and _all_ok are
|
|
# initialised exclusively inside update_all_existing_agents so that
|
|
# sourcing this script has no side effects on the caller's environment.
|
|
|
|
_update_if_new() {
|
|
local file="$1" name="$2"
|
|
[[ -f "$file" ]] || return 0
|
|
local real_path
|
|
real_path=$(realpath "$file" 2>/dev/null || echo "$file")
|
|
local p
|
|
if [[ ${#_updated_paths[@]} -gt 0 ]]; then
|
|
for p in "${_updated_paths[@]}"; do
|
|
[[ "$p" == "$real_path" ]] && return 0
|
|
done
|
|
fi
|
|
# Record the file as seen before attempting the update so that:
|
|
# (a) aliases pointing to the same path are not retried on failure
|
|
# (b) _found_agent reflects file existence, not update success
|
|
_updated_paths+=("$real_path")
|
|
_found_agent=true
|
|
update_agent_file "$file" "$name"
|
|
}
|
|
|
|
update_all_existing_agents() {
|
|
_found_agent=false
|
|
_updated_paths=()
|
|
local _all_ok=true
|
|
|
|
_update_if_new "$CLAUDE_FILE" "Claude Code" || _all_ok=false
|
|
_update_if_new "$GEMINI_FILE" "Gemini CLI" || _all_ok=false
|
|
_update_if_new "$COPILOT_FILE" "GitHub Copilot" || _all_ok=false
|
|
_update_if_new "$CURSOR_FILE" "Cursor IDE" || _all_ok=false
|
|
_update_if_new "$QWEN_FILE" "Qwen Code" || _all_ok=false
|
|
_update_if_new "$AGENTS_FILE" "Codex/opencode/Amp/Kiro/Bob/Pi/Forge" || _all_ok=false
|
|
_update_if_new "$WINDSURF_FILE" "Windsurf" || _all_ok=false
|
|
_update_if_new "$JUNIE_FILE" "Junie" || _all_ok=false
|
|
_update_if_new "$KILOCODE_FILE" "Kilo Code" || _all_ok=false
|
|
_update_if_new "$AUGGIE_FILE" "Auggie CLI" || _all_ok=false
|
|
_update_if_new "$ROO_FILE" "Roo Code" || _all_ok=false
|
|
_update_if_new "$CODEBUDDY_FILE" "CodeBuddy CLI" || _all_ok=false
|
|
_update_if_new "$SHAI_FILE" "SHAI" || _all_ok=false
|
|
_update_if_new "$TABNINE_FILE" "Tabnine CLI" || _all_ok=false
|
|
_update_if_new "$QODER_FILE" "Qoder CLI" || _all_ok=false
|
|
_update_if_new "$AGY_FILE" "Antigravity" || _all_ok=false
|
|
_update_if_new "$VIBE_FILE" "Mistral Vibe" || _all_ok=false
|
|
_update_if_new "$KIMI_FILE" "Kimi Code" || _all_ok=false
|
|
_update_if_new "$TRAE_FILE" "Trae" || _all_ok=false
|
|
_update_if_new "$IFLOW_FILE" "iFlow CLI" || _all_ok=false
|
|
|
|
# If no agent files exist, create a default Claude file
|
|
if [[ "$_found_agent" == false ]]; then
|
|
log_info "No existing agent files found, creating default Claude file..."
|
|
update_agent_file "$CLAUDE_FILE" "Claude Code" || return 1
|
|
fi
|
|
|
|
[[ "$_all_ok" == true ]]
|
|
}
|
|
print_summary() {
|
|
echo
|
|
log_info "Summary of changes:"
|
|
|
|
if [[ -n "$NEW_LANG" ]]; then
|
|
echo " - Added language: $NEW_LANG"
|
|
fi
|
|
|
|
if [[ -n "$NEW_FRAMEWORK" ]]; then
|
|
echo " - Added framework: $NEW_FRAMEWORK"
|
|
fi
|
|
|
|
if [[ -n "$NEW_DB" ]] && [[ "$NEW_DB" != "N/A" ]]; then
|
|
echo " - Added database: $NEW_DB"
|
|
fi
|
|
|
|
echo
|
|
log_info "Usage: $0 [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]"
|
|
}
|
|
|
|
#==============================================================================
|
|
# Main Execution
|
|
#==============================================================================
|
|
|
|
main() {
|
|
# Validate environment before proceeding
|
|
validate_environment
|
|
|
|
log_info "=== Updating agent context files for feature $CURRENT_BRANCH ==="
|
|
|
|
# Parse the plan file to extract project information
|
|
if ! parse_plan_data "$NEW_PLAN"; then
|
|
log_error "Failed to parse plan data"
|
|
exit 1
|
|
fi
|
|
|
|
# Process based on agent type argument
|
|
local success=true
|
|
|
|
if [[ -z "$AGENT_TYPE" ]]; then
|
|
# No specific agent provided - update all existing agent files
|
|
log_info "No agent specified, updating all existing agent files..."
|
|
if ! update_all_existing_agents; then
|
|
success=false
|
|
fi
|
|
else
|
|
# Specific agent provided - update only that agent
|
|
log_info "Updating specific agent: $AGENT_TYPE"
|
|
if ! update_specific_agent "$AGENT_TYPE"; then
|
|
success=false
|
|
fi
|
|
fi
|
|
|
|
# Print summary
|
|
print_summary
|
|
|
|
if [[ "$success" == true ]]; then
|
|
log_success "Agent context update completed successfully"
|
|
exit 0
|
|
else
|
|
log_error "Agent context update completed with errors"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Execute main function if script is run directly
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
main "$@"
|
|
fi
|