|
@@ -0,0 +1,719 @@
|
|
|
+#!/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
|
|
|
+# - 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|qwen|opencode|codex|windsurf
|
|
|
+# 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="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
+source "$SCRIPT_DIR/common.sh"
|
|
|
+
|
|
|
+# Get all paths and variables from common functions
|
|
|
+eval $(get_feature_paths)
|
|
|
+
|
|
|
+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"
|
|
|
+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"
|
|
|
+
|
|
|
+# 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=$?
|
|
|
+ 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"
|
|
|
+
|
|
|
+ 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
|
|
|
+
|
|
|
+ # 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
|
|
|
+
|
|
|
+ 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"
|
|
|
+ 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"
|
|
|
+ ;;
|
|
|
+ gemini)
|
|
|
+ update_agent_file "$GEMINI_FILE" "Gemini CLI"
|
|
|
+ ;;
|
|
|
+ copilot)
|
|
|
+ update_agent_file "$COPILOT_FILE" "GitHub Copilot"
|
|
|
+ ;;
|
|
|
+ cursor)
|
|
|
+ update_agent_file "$CURSOR_FILE" "Cursor IDE"
|
|
|
+ ;;
|
|
|
+ qwen)
|
|
|
+ update_agent_file "$QWEN_FILE" "Qwen Code"
|
|
|
+ ;;
|
|
|
+ opencode)
|
|
|
+ update_agent_file "$AGENTS_FILE" "opencode"
|
|
|
+ ;;
|
|
|
+ codex)
|
|
|
+ update_agent_file "$AGENTS_FILE" "Codex CLI"
|
|
|
+ ;;
|
|
|
+ windsurf)
|
|
|
+ update_agent_file "$WINDSURF_FILE" "Windsurf"
|
|
|
+ ;;
|
|
|
+ kilocode)
|
|
|
+ update_agent_file "$KILOCODE_FILE" "Kilo Code"
|
|
|
+ ;;
|
|
|
+ auggie)
|
|
|
+ update_agent_file "$AUGGIE_FILE" "Auggie CLI"
|
|
|
+ ;;
|
|
|
+ roo)
|
|
|
+ update_agent_file "$ROO_FILE" "Roo Code"
|
|
|
+ ;;
|
|
|
+ *)
|
|
|
+ log_error "Unknown agent type '$agent_type'"
|
|
|
+ log_error "Expected: claude|gemini|copilot|cursor|qwen|opencode|codex|windsurf|kilocode|auggie|roo"
|
|
|
+ exit 1
|
|
|
+ ;;
|
|
|
+ esac
|
|
|
+}
|
|
|
+
|
|
|
+update_all_existing_agents() {
|
|
|
+ local found_agent=false
|
|
|
+
|
|
|
+ # Check each possible agent file and update if it exists
|
|
|
+ if [[ -f "$CLAUDE_FILE" ]]; then
|
|
|
+ update_agent_file "$CLAUDE_FILE" "Claude Code"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -f "$GEMINI_FILE" ]]; then
|
|
|
+ update_agent_file "$GEMINI_FILE" "Gemini CLI"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -f "$COPILOT_FILE" ]]; then
|
|
|
+ update_agent_file "$COPILOT_FILE" "GitHub Copilot"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -f "$CURSOR_FILE" ]]; then
|
|
|
+ update_agent_file "$CURSOR_FILE" "Cursor IDE"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -f "$QWEN_FILE" ]]; then
|
|
|
+ update_agent_file "$QWEN_FILE" "Qwen Code"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -f "$AGENTS_FILE" ]]; then
|
|
|
+ update_agent_file "$AGENTS_FILE" "Codex/opencode"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -f "$WINDSURF_FILE" ]]; then
|
|
|
+ update_agent_file "$WINDSURF_FILE" "Windsurf"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -f "$KILOCODE_FILE" ]]; then
|
|
|
+ update_agent_file "$KILOCODE_FILE" "Kilo Code"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -f "$AUGGIE_FILE" ]]; then
|
|
|
+ update_agent_file "$AUGGIE_FILE" "Auggie CLI"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -f "$ROO_FILE" ]]; then
|
|
|
+ update_agent_file "$ROO_FILE" "Roo Code"
|
|
|
+ found_agent=true
|
|
|
+ fi
|
|
|
+
|
|
|
+ # 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"
|
|
|
+ fi
|
|
|
+}
|
|
|
+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|qwen|opencode|codex|windsurf|kilocode|auggie|roo]"
|
|
|
+}
|
|
|
+
|
|
|
+#==============================================================================
|
|
|
+# 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
|