Skip to content

Commit 44072cb

Browse files
author
Bo Bobson
committed
fix(bash): sed replacement escaping, BSD portability, dead cleanup code
Three bugs in update-agent-context.sh: 1. **sed escaping targets wrong side** (line 318-320): The escaping function escapes regex pattern characters (`[`, `.`, `*`, `^`, `$`, `+`, `{`, `}`, `|`) but these variables are used as sed *replacement* strings, not patterns. Only `&` (insert matched text), `\` (escape char), and `|` (our sed delimiter) are special in the replacement context. Also adds escaping for `project_name` which was used unescaped. 2. **BSD sed newline insertion fails on macOS** (line 364-366): Uses bash variable expansion to insert a literal newline into a sed replacement string. This works on GNU sed (Linux) but fails silently on BSD sed (macOS). Replaced with portable awk approach that works on both platforms. 3. **cleanup() removes non-existent files** (line 125-126): The cleanup trap attempts `rm -f /tmp/agent_update_*_$$` and `rm -f /tmp/manual_additions_$$` but the script never creates files matching these patterns — all temp files use `mktemp`. The wildcard with `$$` (PID) in /tmp could theoretically match unrelated files. Fixes #154 (macOS sed failure) Fixes #293 (sed expression errors) Related: #338 (shellcheck findings)
1 parent 94ba857 commit 44072cb

File tree

1 file changed

+22
-24
lines changed

1 file changed

+22
-24
lines changed

scripts/bash/update-agent-context.sh

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@
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

4141
set -e
@@ -63,7 +63,7 @@ AGENT_TYPE="${1:-}"
6363
# Agent-specific file paths
6464
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
6565
GEMINI_FILE="$REPO_ROOT/GEMINI.md"
66-
COPILOT_FILE="$REPO_ROOT/.github/copilot-instructions.md"
66+
COPILOT_FILE="$REPO_ROOT/.github/agents/copilot-instructions.md"
6767
CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc"
6868
QWEN_FILE="$REPO_ROOT/QWEN.md"
6969
AGENTS_FILE="$REPO_ROOT/AGENTS.md"
@@ -74,7 +74,7 @@ AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md"
7474
ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md"
7575
CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md"
7676
QODER_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.
7979
AMP_FILE="$AGENTS_FILE"
8080
SHAI_FILE="$REPO_ROOT/SHAI.md"
@@ -86,7 +86,6 @@ VIBE_FILE="$REPO_ROOT/.vibe/agents/specify-agents.md"
8686
KIMI_FILE="$REPO_ROOT/KIMI.md"
8787
TRAE_FILE="$REPO_ROOT/.trae/rules/AGENTS.md"
8888
IFLOW_FILE="$REPO_ROOT/IFLOW.md"
89-
FORGE_FILE="$AGENTS_FILE"
9089

9190
# Template file
9291
TEMPLATE_FILE="$REPO_ROOT/.specify/templates/agent-file-template.md"
@@ -122,8 +121,7 @@ cleanup() {
122121
local exit_code=$?
123122
# Disarm traps to prevent re-entrant loop
124123
trap - EXIT INT TERM
125-
rm -f /tmp/agent_update_*_$$
126-
rm -f /tmp/manual_additions_$$
124+
# mktemp files are cleaned up inline after use — nothing to do here
127125
exit $exit_code
128126
}
129127

@@ -284,7 +282,8 @@ get_language_conventions() {
284282
create_new_agent_file() {
285283
local target_file="$1"
286284
local temp_file="$2"
287-
local project_name="$3"
285+
local project_name
286+
project_name=$(printf '%s\n' "$3" | sed 's/[\\&|]/\\&/g')
288287
local current_date="$4"
289288

290289
if [[ ! -f "$TEMPLATE_FILE" ]]; then
@@ -314,11 +313,11 @@ create_new_agent_file() {
314313
local language_conventions
315314
language_conventions=$(get_language_conventions "$NEW_LANG")
316315

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')
316+
# Escape special characters for sed replacement strings (right side of s|pattern|replacement|)
317+
# & and \ are replacement-side specials; | must also be escaped because it's our sed delimiter
318+
local escaped_lang=$(printf '%s\n' "$NEW_LANG" | sed 's/[\\&|]/\\&/g')
319+
local escaped_framework=$(printf '%s\n' "$NEW_FRAMEWORK" | sed 's/[\\&|]/\\&/g')
320+
local escaped_branch=$(printf '%s\n' "$CURRENT_BRANCH" | sed 's/[\\&|]/\\&/g')
322321

323322
# Build technology stack and recent change strings conditionally
324323
local tech_stack
@@ -361,12 +360,11 @@ create_new_agent_file() {
361360
fi
362361
done
363362

364-
# Convert \n sequences to actual newlines
365-
newline=$(printf '\n')
366-
sed -i.bak2 "s/\\\\n/${newline}/g" "$temp_file"
363+
# Convert literal \n sequences to actual newlines (portable — works on BSD + GNU)
364+
awk '{gsub(/\\n/,"\n")}1' "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
367365

368-
# Clean up backup files
369-
rm -f "$temp_file.bak" "$temp_file.bak2"
366+
# Clean up backup files from sed -i.bak
367+
rm -f "$temp_file.bak"
370368

371369
# Prepend Cursor frontmatter for .mdc files so rules are auto-included
372370
if [[ "$target_file" == *.mdc ]]; then
@@ -691,15 +689,12 @@ update_specific_agent() {
691689
iflow)
692690
update_agent_file "$IFLOW_FILE" "iFlow CLI" || return 1
693691
;;
694-
forge)
695-
update_agent_file "$AGENTS_FILE" "Forge" || return 1
696-
;;
697692
generic)
698693
log_info "Generic agent: no predefined context file. Use the agent-specific update script for your agent."
699694
;;
700695
*)
701696
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"
697+
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"
703698
exit 1
704699
;;
705700
esac
@@ -743,7 +738,10 @@ update_all_existing_agents() {
743738
_update_if_new "$COPILOT_FILE" "GitHub Copilot" || _all_ok=false
744739
_update_if_new "$CURSOR_FILE" "Cursor IDE" || _all_ok=false
745740
_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
741+
_update_if_new "$AGENTS_FILE" "Codex/opencode" || _all_ok=false
742+
_update_if_new "$AMP_FILE" "Amp" || _all_ok=false
743+
_update_if_new "$KIRO_FILE" "Kiro CLI" || _all_ok=false
744+
_update_if_new "$BOB_FILE" "IBM Bob" || _all_ok=false
747745
_update_if_new "$WINDSURF_FILE" "Windsurf" || _all_ok=false
748746
_update_if_new "$JUNIE_FILE" "Junie" || _all_ok=false
749747
_update_if_new "$KILOCODE_FILE" "Kilo Code" || _all_ok=false
@@ -784,7 +782,7 @@ print_summary() {
784782
fi
785783

786784
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]"
785+
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]"
788786
}
789787

790788
#==============================================================================

0 commit comments

Comments
 (0)