v0.1.0 cf60d0e

update-agent-context.sh RawHistoryFolder

1#!/usr/bin/env bash
2# Incrementally update agent context files based on new feature plan
3# Supports: CLAUDE.md, GEMINI.md, and .github/copilot-instructions.md
4# O(1) operation - only reads current context file and new plan.md
5
6set -e
7
8REPO_ROOT=$(git rev-parse --show-toplevel)
9CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
10FEATURE_DIR="$REPO_ROOT/specs/$CURRENT_BRANCH"
11NEW_PLAN="$FEATURE_DIR/plan.md"
12
13# Determine which agent context files to update
14CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
15GEMINI_FILE="$REPO_ROOT/GEMINI.md"
16COPILOT_FILE="$REPO_ROOT/.github/copilot-instructions.md"
17
18# Allow override via argument
19AGENT_TYPE="$1"
20
21if [ ! -f "$NEW_PLAN" ]; then
22    echo "ERROR: No plan.md found at $NEW_PLAN"
23    exit 1
24fi
25
26echo "=== Updating agent context files for feature $CURRENT_BRANCH ==="
27
28# Extract tech from new plan
29NEW_LANG=$(grep "^**Language/Version**: " "$NEW_PLAN" 2>/dev/null | head -1 | sed 's/^**Language\/Version**: //' | grep -v "NEEDS CLARIFICATION" || echo "")
30NEW_FRAMEWORK=$(grep "^**Primary Dependencies**: " "$NEW_PLAN" 2>/dev/null | head -1 | sed 's/^**Primary Dependencies**: //' | grep -v "NEEDS CLARIFICATION" || echo "")
31NEW_TESTING=$(grep "^**Testing**: " "$NEW_PLAN" 2>/dev/null | head -1 | sed 's/^**Testing**: //' | grep -v "NEEDS CLARIFICATION" || echo "")
32NEW_DB=$(grep "^**Storage**: " "$NEW_PLAN" 2>/dev/null | head -1 | sed 's/^**Storage**: //' | grep -v "N/A" | grep -v "NEEDS CLARIFICATION" || echo "")
33NEW_PROJECT_TYPE=$(grep "^**Project Type**: " "$NEW_PLAN" 2>/dev/null | head -1 | sed 's/^**Project Type**: //' || echo "")
34
35# Function to update a single agent context file
36update_agent_file() {
37    local target_file="$1"
38    local agent_name="$2"
39    
40    echo "Updating $agent_name context file: $target_file"
41    
42    # Create temp file for new context
43    local temp_file=$(mktemp)
44    
45    # If file doesn't exist, create from template
46    if [ ! -f "$target_file" ]; then
47        echo "Creating new $agent_name context file..."
48        
49        # Check if this is the SDD repo itself
50        if [ -f "$REPO_ROOT/templates/agent-file-template.md" ]; then
51            cp "$REPO_ROOT/templates/agent-file-template.md" "$temp_file"
52        else
53            echo "ERROR: Template not found at $REPO_ROOT/templates/agent-file-template.md"
54            return 1
55        fi
56        
57        # Replace placeholders
58        sed -i.bak "s/\[PROJECT NAME\]/$(basename $REPO_ROOT)/" "$temp_file"
59        sed -i.bak "s/\[DATE\]/$(date +%Y-%m-%d)/" "$temp_file"
60        sed -i.bak "s/\[EXTRACTED FROM ALL PLAN.MD FILES\]/- $NEW_LANG + $NEW_FRAMEWORK ($CURRENT_BRANCH)/" "$temp_file"
61        
62        # Add project structure based on type
63        if [[ "$NEW_PROJECT_TYPE" == *"web"* ]]; then
64            sed -i.bak "s|\[ACTUAL STRUCTURE FROM PLANS\]|backend/\nfrontend/\ntests/|" "$temp_file"
65        else
66            sed -i.bak "s|\[ACTUAL STRUCTURE FROM PLANS\]|src/\ntests/|" "$temp_file"
67        fi
68        
69        # Add minimal commands
70        if [[ "$NEW_LANG" == *"Python"* ]]; then
71            COMMANDS="cd src && pytest && ruff check ."
72        elif [[ "$NEW_LANG" == *"Rust"* ]]; then
73            COMMANDS="cargo test && cargo clippy"
74        elif [[ "$NEW_LANG" == *"JavaScript"* ]] || [[ "$NEW_LANG" == *"TypeScript"* ]]; then
75            COMMANDS="npm test && npm run lint"
76        else
77            COMMANDS="# Add commands for $NEW_LANG"
78        fi
79        sed -i.bak "s|\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]|$COMMANDS|" "$temp_file"
80        
81        # Add code style
82        sed -i.bak "s|\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]|$NEW_LANG: Follow standard conventions|" "$temp_file"
83        
84        # Add recent changes
85        sed -i.bak "s|\[LAST 3 FEATURES AND WHAT THEY ADDED\]|- $CURRENT_BRANCH: Added $NEW_LANG + $NEW_FRAMEWORK|" "$temp_file"
86        
87        rm "$temp_file.bak"
88    else
89        echo "Updating existing $agent_name context file..."
90        
91        # Extract manual additions
92        local manual_start=$(grep -n "<!-- MANUAL ADDITIONS START -->" "$target_file" | cut -d: -f1)
93        local manual_end=$(grep -n "<!-- MANUAL ADDITIONS END -->" "$target_file" | cut -d: -f1)
94        
95        if [ ! -z "$manual_start" ] && [ ! -z "$manual_end" ]; then
96            sed -n "${manual_start},${manual_end}p" "$target_file" > /tmp/manual_additions.txt
97        fi
98        
99        # Parse existing file and create updated version
100        python3 - << EOF
101import re
102import sys
103from datetime import datetime
104
105# Read existing file
106with open("$target_file", 'r') as f:
107    content = f.read()
108
109# Check if new tech already exists
110tech_section = re.search(r'## Active Technologies\n(.*?)\n\n', content, re.DOTALL)
111if tech_section:
112    existing_tech = tech_section.group(1)
113    
114    # Add new tech if not already present
115    new_additions = []
116    if "$NEW_LANG" and "$NEW_LANG" not in existing_tech:
117        new_additions.append(f"- $NEW_LANG + $NEW_FRAMEWORK ($CURRENT_BRANCH)")
118    if "$NEW_DB" and "$NEW_DB" not in existing_tech and "$NEW_DB" != "N/A":
119        new_additions.append(f"- $NEW_DB ($CURRENT_BRANCH)")
120    
121    if new_additions:
122        updated_tech = existing_tech + "\n" + "\n".join(new_additions)
123        content = content.replace(tech_section.group(0), f"## Active Technologies\n{updated_tech}\n\n")
124
125# Update project structure if needed
126if "$NEW_PROJECT_TYPE" == "web" and "frontend/" not in content:
127    struct_section = re.search(r'## Project Structure\n\`\`\`\n(.*?)\n\`\`\`', content, re.DOTALL)
128    if struct_section:
129        updated_struct = struct_section.group(1) + "\nfrontend/src/      # Web UI"
130        content = re.sub(r'(## Project Structure\n\`\`\`\n).*?(\n\`\`\`)', 
131                        f'\\1{updated_struct}\\2', content, flags=re.DOTALL)
132
133# Add new commands if language is new
134if "$NEW_LANG" and f"# {NEW_LANG}" not in content:
135    commands_section = re.search(r'## Commands\n\`\`\`bash\n(.*?)\n\`\`\`', content, re.DOTALL)
136    if not commands_section:
137        commands_section = re.search(r'## Commands\n(.*?)\n\n', content, re.DOTALL)
138    
139    if commands_section:
140        new_commands = commands_section.group(1)
141        if "Python" in "$NEW_LANG":
142            new_commands += "\ncd src && pytest && ruff check ."
143        elif "Rust" in "$NEW_LANG":
144            new_commands += "\ncargo test && cargo clippy"
145        elif "JavaScript" in "$NEW_LANG" or "TypeScript" in "$NEW_LANG":
146            new_commands += "\nnpm test && npm run lint"
147        
148        if "```bash" in content:
149            content = re.sub(r'(## Commands\n\`\`\`bash\n).*?(\n\`\`\`)', 
150                            f'\\1{new_commands}\\2', content, flags=re.DOTALL)
151        else:
152            content = re.sub(r'(## Commands\n).*?(\n\n)', 
153                            f'\\1{new_commands}\\2', content, flags=re.DOTALL)
154
155# Update recent changes (keep only last 3)
156changes_section = re.search(r'## Recent Changes\n(.*?)(\n\n|$)', content, re.DOTALL)
157if changes_section:
158    changes = changes_section.group(1).strip().split('\n')
159    changes.insert(0, f"- $CURRENT_BRANCH: Added $NEW_LANG + $NEW_FRAMEWORK")
160    # Keep only last 3
161    changes = changes[:3]
162    content = re.sub(r'(## Recent Changes\n).*?(\n\n|$)', 
163                    f'\\1{chr(10).join(changes)}\\2', content, flags=re.DOTALL)
164
165# Update date
166content = re.sub(r'Last updated: \d{4}-\d{2}-\d{2}', 
167                f'Last updated: {datetime.now().strftime("%Y-%m-%d")}', content)
168
169# Write to temp file
170with open("$temp_file", 'w') as f:
171    f.write(content)
172EOF
173
174        # Restore manual additions if they exist
175        if [ -f /tmp/manual_additions.txt ]; then
176            # Remove old manual section from temp file
177            sed -i.bak '/<!-- MANUAL ADDITIONS START -->/,/<!-- MANUAL ADDITIONS END -->/d' "$temp_file"
178            # Append manual additions
179            cat /tmp/manual_additions.txt >> "$temp_file"
180            rm /tmp/manual_additions.txt "$temp_file.bak"
181        fi
182    fi
183    
184    # Move temp file to final location
185    mv "$temp_file" "$target_file"
186    echo "✅ $agent_name context file updated successfully"
187}
188
189# Update files based on argument or detect existing files
190case "$AGENT_TYPE" in
191    "claude")
192        update_agent_file "$CLAUDE_FILE" "Claude Code"
193        ;;
194    "gemini") 
195        update_agent_file "$GEMINI_FILE" "Gemini CLI"
196        ;;
197    "copilot")
198        update_agent_file "$COPILOT_FILE" "GitHub Copilot"
199        ;;
200    "")
201        # Update all existing files
202        [ -f "$CLAUDE_FILE" ] && update_agent_file "$CLAUDE_FILE" "Claude Code"
203        [ -f "$GEMINI_FILE" ] && update_agent_file "$GEMINI_FILE" "Gemini CLI" 
204        [ -f "$COPILOT_FILE" ] && update_agent_file "$COPILOT_FILE" "GitHub Copilot"
205        
206        # If no files exist, create based on current directory or ask user
207        if [ ! -f "$CLAUDE_FILE" ] && [ ! -f "$GEMINI_FILE" ] && [ ! -f "$COPILOT_FILE" ]; then
208            echo "No agent context files found. Creating Claude Code context file by default."
209            update_agent_file "$CLAUDE_FILE" "Claude Code"
210        fi
211        ;;
212    *)
213        echo "ERROR: Unknown agent type '$AGENT_TYPE'. Use: claude, gemini, copilot, or leave empty for all."
214        exit 1
215        ;;
216esac
217echo ""
218echo "Summary of changes:"
219if [ ! -z "$NEW_LANG" ]; then
220    echo "- Added language: $NEW_LANG"
221fi
222if [ ! -z "$NEW_FRAMEWORK" ]; then
223    echo "- Added framework: $NEW_FRAMEWORK"
224fi
225if [ ! -z "$NEW_DB" ] && [ "$NEW_DB" != "N/A" ]; then
226    echo "- Added database: $NEW_DB"
227fi
228
229echo ""
230echo "Usage: $0 [claude|gemini|copilot]"
231echo "  - No argument: Update all existing agent context files"
232echo "  - claude: Update only CLAUDE.md"
233echo "  - gemini: Update only GEMINI.md" 
234echo "  - copilot: Update only .github/copilot-instructions.md"

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close