3030#
3131# 5. Multi-Agent Support
3232# - Handles agent-specific file paths and naming conventions
33- # - 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
33+ # - 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, Antigravity or Generic
3434# - Can update single agents or all existing agent files
3535# - Creates default Claude file if no agent files exist
3636#
3737# Usage: ./update-agent-context.sh [agent_type]
38- # 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
38+ # 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|generic
3939# Leave empty to update all existing agent files
4040
4141set -e
@@ -63,7 +63,7 @@ AGENT_TYPE="${1:-}"
6363# Agent-specific file paths
6464CLAUDE_FILE=" $REPO_ROOT /CLAUDE.md"
6565GEMINI_FILE=" $REPO_ROOT /GEMINI.md"
66- COPILOT_FILE=" $REPO_ROOT /.github/copilot-instructions.md"
66+ COPILOT_FILE=" $REPO_ROOT /.github/agents/ copilot-instructions.md"
6767CURSOR_FILE=" $REPO_ROOT /.cursor/rules/specify-rules.mdc"
6868QWEN_FILE=" $REPO_ROOT /QWEN.md"
6969AGENTS_FILE=" $REPO_ROOT /AGENTS.md"
@@ -74,7 +74,7 @@ AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md"
7474ROO_FILE=" $REPO_ROOT /.roo/rules/specify-rules.md"
7575CODEBUDDY_FILE=" $REPO_ROOT /CODEBUDDY.md"
7676QODER_FILE=" $REPO_ROOT /QODER.md"
77- # Amp, Kiro CLI, IBM Bob, Pi, and Forge all share AGENTS.md — use AGENTS_FILE to avoid
77+ # Amp, Kiro CLI, IBM Bob, and Pi all share AGENTS.md — use AGENTS_FILE to avoid
7878# updating the same file multiple times.
7979AMP_FILE=" $AGENTS_FILE "
8080SHAI_FILE=" $REPO_ROOT /SHAI.md"
@@ -86,7 +86,6 @@ VIBE_FILE="$REPO_ROOT/.vibe/agents/specify-agents.md"
8686KIMI_FILE=" $REPO_ROOT /KIMI.md"
8787TRAE_FILE=" $REPO_ROOT /.trae/rules/AGENTS.md"
8888IFLOW_FILE=" $REPO_ROOT /IFLOW.md"
89- FORGE_FILE=" $AGENTS_FILE "
9089
9190# Template file
9291TEMPLATE_FILE=" $REPO_ROOT /.specify/templates/agent-file-template.md"
@@ -117,13 +116,17 @@ log_warning() {
117116 echo " WARNING: $1 " >&2
118117}
119118
119+ # Track temporary files for cleanup on interrupt
120+ _CLEANUP_FILES=()
121+
120122# Cleanup function for temporary files
121123cleanup () {
122124 local exit_code=$?
123125 # Disarm traps to prevent re-entrant loop
124126 trap - EXIT INT TERM
125- rm -f /tmp/agent_update_* _$$
126- rm -f /tmp/manual_additions_$$
127+ for f in " ${_CLEANUP_FILES[@]+" ${_CLEANUP_FILES[@]} " } " ; do
128+ rm -f " $f " " $f .bak" " $f .tmp"
129+ done
127130 exit $exit_code
128131}
129132
@@ -284,7 +287,8 @@ get_language_conventions() {
284287create_new_agent_file () {
285288 local target_file=" $1 "
286289 local temp_file=" $2 "
287- local project_name=" $3 "
290+ local project_name
291+ project_name=$( printf ' %s\n' " $3 " | sed ' s/[\\&|]/\\&/g' )
288292 local current_date=" $4 "
289293
290294 if [[ ! -f " $TEMPLATE_FILE " ]]; then
@@ -307,18 +311,21 @@ create_new_agent_file() {
307311 # Replace template placeholders
308312 local project_structure
309313 project_structure=$( get_project_structure " $NEW_PROJECT_TYPE " )
314+ project_structure=$( printf ' %s\n' " $project_structure " | sed ' s/[\\&|]/\\&/g' )
310315
311316 local commands
312317 commands=$( get_commands_for_language " $NEW_LANG " )
313-
318+
314319 local language_conventions
315320 language_conventions=$( get_language_conventions " $NEW_LANG " )
316-
317- # Perform substitutions with error checking using safer approach
318- # Escape special characters for sed by using a different delimiter or escaping
319- local escaped_lang=$( printf ' %s\n' " $NEW_LANG " | sed ' s/[\[\.*^$()+{}|]/\\&/g' )
320- local escaped_framework=$( printf ' %s\n' " $NEW_FRAMEWORK " | sed ' s/[\[\.*^$()+{}|]/\\&/g' )
321- local escaped_branch=$( printf ' %s\n' " $CURRENT_BRANCH " | sed ' s/[\[\.*^$()+{}|]/\\&/g' )
321+
322+ # Escape special characters for sed replacement strings (right side of s|pattern|replacement|)
323+ # & and \ are replacement-side specials; | must also be escaped because it's our sed delimiter
324+ local escaped_lang=$( printf ' %s\n' " $NEW_LANG " | sed ' s/[\\&|]/\\&/g' )
325+ local escaped_framework=$( printf ' %s\n' " $NEW_FRAMEWORK " | sed ' s/[\\&|]/\\&/g' )
326+ commands=$( printf ' %s\n' " $commands " | sed ' s/[\\&|]/\\&/g' )
327+ language_conventions=$( printf ' %s\n' " $language_conventions " | sed ' s/[\\&|]/\\&/g' )
328+ local escaped_branch=$( printf ' %s\n' " $CURRENT_BRANCH " | sed ' s/[\\&|]/\\&/g' )
322329
323330 # Build technology stack and recent change strings conditionally
324331 local tech_stack
@@ -361,17 +368,17 @@ create_new_agent_file() {
361368 fi
362369 done
363370
364- # Convert \n sequences to actual newlines
365- newline=$( printf ' \n' )
366- sed -i.bak2 " s/\\\\ n/${newline} /g" " $temp_file "
371+ # Convert literal \n sequences to actual newlines (portable — works on BSD + GNU)
372+ awk ' {gsub(/\\n/,"\n")}1' " $temp_file " > " $temp_file .tmp" && mv " $temp_file .tmp" " $temp_file "
367373
368- # Clean up backup files
369- rm -f " $temp_file .bak" " $temp_file .bak2 "
374+ # Clean up backup files from sed -i.bak
375+ rm -f " $temp_file .bak"
370376
371377 # Prepend Cursor frontmatter for .mdc files so rules are auto-included
372378 if [[ " $target_file " == * .mdc ]]; then
373379 local frontmatter_file
374380 frontmatter_file=$( mktemp) || return 1
381+ _CLEANUP_FILES+=(" $frontmatter_file " )
375382 printf ' %s\n' " ---" " description: Project Development Guidelines" " globs: [\" **/*\" ]" " alwaysApply: true" " ---" " " > " $frontmatter_file "
376383 cat " $temp_file " >> " $frontmatter_file "
377384 mv " $frontmatter_file " " $temp_file "
@@ -395,6 +402,7 @@ update_existing_agent_file() {
395402 log_error " Failed to create temporary file"
396403 return 1
397404 }
405+ _CLEANUP_FILES+=(" $temp_file " )
398406
399407 # Process the file in one pass
400408 local tech_stack=$( format_technology_stack " $NEW_LANG " " $NEW_FRAMEWORK " )
@@ -519,6 +527,7 @@ update_existing_agent_file() {
519527 if ! head -1 " $temp_file " | grep -q ' ^---' ; then
520528 local frontmatter_file
521529 frontmatter_file=$( mktemp) || { rm -f " $temp_file " ; return 1; }
530+ _CLEANUP_FILES+=(" $frontmatter_file " )
522531 printf ' %s\n' " ---" " description: Project Development Guidelines" " globs: [\" **/*\" ]" " alwaysApply: true" " ---" " " > " $frontmatter_file "
523532 cat " $temp_file " >> " $frontmatter_file "
524533 mv " $frontmatter_file " " $temp_file "
@@ -571,6 +580,7 @@ update_agent_file() {
571580 log_error " Failed to create temporary file"
572581 return 1
573582 }
583+ _CLEANUP_FILES+=(" $temp_file " )
574584
575585 if create_new_agent_file " $target_file " " $temp_file " " $project_name " " $current_date " ; then
576586 if mv " $temp_file " " $target_file " ; then
@@ -691,15 +701,12 @@ update_specific_agent() {
691701 iflow)
692702 update_agent_file " $IFLOW_FILE " " iFlow CLI" || return 1
693703 ;;
694- forge)
695- update_agent_file " $AGENTS_FILE " " Forge" || return 1
696- ;;
697704 generic)
698705 log_info " Generic agent: no predefined context file. Use the agent-specific update script for your agent."
699706 ;;
700707 * )
701708 log_error " Unknown agent type '$agent_type '"
702- 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"
709+ 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|generic"
703710 exit 1
704711 ;;
705712 esac
@@ -743,7 +750,10 @@ update_all_existing_agents() {
743750 _update_if_new " $COPILOT_FILE " " GitHub Copilot" || _all_ok=false
744751 _update_if_new " $CURSOR_FILE " " Cursor IDE" || _all_ok=false
745752 _update_if_new " $QWEN_FILE " " Qwen Code" || _all_ok=false
746- _update_if_new " $AGENTS_FILE " " Codex/opencode/Amp/Kiro/Bob/Pi/Forge" || _all_ok=false
753+ _update_if_new " $AGENTS_FILE " " Codex/opencode" || _all_ok=false
754+ _update_if_new " $AMP_FILE " " Amp" || _all_ok=false
755+ _update_if_new " $KIRO_FILE " " Kiro CLI" || _all_ok=false
756+ _update_if_new " $BOB_FILE " " IBM Bob" || _all_ok=false
747757 _update_if_new " $WINDSURF_FILE " " Windsurf" || _all_ok=false
748758 _update_if_new " $JUNIE_FILE " " Junie" || _all_ok=false
749759 _update_if_new " $KILOCODE_FILE " " Kilo Code" || _all_ok=false
@@ -784,7 +794,7 @@ print_summary() {
784794 fi
785795
786796 echo
787- 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]"
797+ 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|generic]"
788798}
789799
790800# ==============================================================================
0 commit comments