-
Notifications
You must be signed in to change notification settings - Fork 5k
feat(shell): add fish support to update-agent-context
#1363
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds Fish shell support for the update-agent-context script, which maintains AI agent context files by parsing feature specifications and updating agent-specific configuration files with project information. This completes the multi-shell support effort by providing a Fish implementation alongside existing Bash and PowerShell versions.
Key Changes:
- Complete Fish shell implementation of the update-agent-context functionality
- Support for 17+ AI agent types including Claude, Gemini, Copilot, Cursor, and others
- Template-based file generation and intelligent update logic for existing files
- Integration with common.fish for repository and feature path management
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| source "$SCRIPT_DIR/common.fish" | ||
|
|
||
| # Get all paths and variables from common functions | ||
| eval (get_feature_paths) | ||
|
|
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The script attempts to source a non-existent file 'common.fish'. This file does not exist in the scripts/fish/ directory. The script will fail at runtime when it tries to load this dependency. Either create the common.fish file with the necessary functions (like get_feature_paths), or inline the required functionality directly in this script.
| source "$SCRIPT_DIR/common.fish" | |
| # Get all paths and variables from common functions | |
| eval (get_feature_paths) | |
| # Load shared helpers if available; otherwise, fall back to local defaults. | |
| if test -f "$SCRIPT_DIR/common.fish" | |
| source "$SCRIPT_DIR/common.fish" | |
| # Get all paths and variables from common functions | |
| eval (get_feature_paths) | |
| else | |
| # Fallback: determine repository root and plan file location | |
| set REPO_ROOT (git rev-parse --show-toplevel ^/dev/null) | |
| if test -z "$REPO_ROOT" | |
| set REPO_ROOT (pwd) | |
| end | |
| # Default plan file path when common.fish is unavailable | |
| set IMPL_PLAN "$REPO_ROOT/plan.md" | |
| end |
| echo "$line" >> "$temp_file" | ||
| set in_tech_section true | ||
| continue | ||
| else if test "$in_tech_section" = "true"; and string match -qr '^##[[:space:]]' "$line" |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comparison 'test "$in_tech_section" = "true"' uses string equality, but earlier the variable is set to boolean false (line 396). In Fish, when you use 'set in_tech_section false', it's a string value "false", not a boolean. For consistency, either use string values throughout ("true"/"false" in quotes) or use numeric values (0/1). The inconsistency between using bare 'false' in assignment and quoted "true" in comparison could lead to logic errors.
| if test "$tech_entries_added" = "false"; and test (count $new_tech_entries) -gt 0 | ||
| for entry in $new_tech_entries | ||
| echo "$entry" >> "$temp_file" | ||
| end | ||
| set tech_entries_added true | ||
| end | ||
| echo "$line" >> "$temp_file" | ||
| set in_tech_section false | ||
| continue | ||
| else if test "$in_tech_section" = "true"; and test -z "$line" | ||
| # Add new tech entries before empty line in tech section | ||
| if test "$tech_entries_added" = "false"; and test (count $new_tech_entries) -gt 0 |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as line 408 - inconsistent boolean value handling. The variable tech_entries_added is set to false (line 398) but compared with string "false" here. Ensure consistent use of either string values ("true"/"false") or boolean/numeric values throughout.
| if test "$in_tech_section" = "true"; and test "$tech_entries_added" = "false"; and test (count $new_tech_entries) -gt 0 | ||
| for entry in $new_tech_entries | ||
| echo "$entry" >> "$temp_file" | ||
| end | ||
| set tech_entries_added true |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple instances of inconsistent boolean value handling in this section. Variables are set to bare 'false' (lines 396-399) but compared using string "false" (lines 410, 421, 463). This pattern continues throughout the function and could cause unexpected behavior.
| end | ||
|
|
||
| # If no agent files exist, create a default Claude file | ||
| if test "$found_agent" = "false" |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable 'found_agent' is set to bare 'false' (line 618) but compared using string "false" (line 697). This is the same boolean consistency issue found elsewhere in the script. Use consistent string values: set found_agent "false" and compare with test "$found_agent" = "false".
| rm -f /tmp/agent_update_*_%self | ||
| rm -f /tmp/manual_additions_%self |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cleanup function uses '%self' which is not a valid Fish shell variable. In Fish, the process ID should be accessed using '%self' but this only works in certain contexts. For a more portable solution, use (status current-pid) or simply remove the PID suffix if uniqueness isn't critical. The cleanup function will fail to remove the intended temporary files.
| rm -f /tmp/agent_update_*_%self | |
| rm -f /tmp/manual_additions_%self | |
| rm -f /tmp/agent_update_*_(status current-pid) | |
| rm -f /tmp/manual_additions_(status current-pid) |
| # Set up cleanup trap | ||
| trap cleanup EXIT INT TERM | ||
|
|
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'trap' command is not supported in Fish shell. Fish uses event handlers instead. Replace this line with: function cleanup_on_exit --on-event fish_exit; cleanup; end. Alternatively, you can define cleanup handlers for specific signals using --on-signal SIGINT, --on-signal SIGTERM, etc. Without this fix, cleanup will never be called automatically on script exit or interruption.
| # Set up cleanup trap | |
| trap cleanup EXIT INT TERM | |
| # Set up cleanup handlers for Fish shell | |
| function cleanup_on_exit --on-event fish_exit | |
| cleanup | |
| end | |
| function cleanup_on_sigint --on-signal SIGINT | |
| cleanup | |
| end | |
| function cleanup_on_sigterm --on-signal SIGTERM | |
| cleanup | |
| end |
| set in_tech_section false | ||
| set in_changes_section false | ||
| set tech_entries_added false | ||
| set changes_entries_added false | ||
| set existing_changes_count 0 |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In Fish, boolean values should be handled differently. Instead of using strings "true" and "false", Fish prefers using the test command's exit status or explicit true/false commands. Consider using boolean variables like 'set in_tech_section 0' and 'set in_tech_section 1', or simply use true/false directly. The current string comparisons will work but are not idiomatic Fish.
| set escaped_lang (printf '%s\n' "$NEW_LANG" | sed 's/[\[\.*^$()+{}|]/\\&/g') | ||
| set escaped_framework (printf '%s\n' "$NEW_FRAMEWORK" | sed 's/[\[\.*^$()+{}|]/\\&/g') | ||
| set escaped_branch (printf '%s\n' "$CURRENT_BRANCH" | sed 's/[\[\.*^$()+{}|]/\\&/g') |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sed regex pattern includes an unescaped closing bracket ']' in the character class. The pattern '[[.^$()+{}|]' should escape the first bracket or place it at the end: '[][.^$()+{}|]' or '[[.*^$()+{}|]]'. Without proper escaping, this sed command may fail or produce unexpected results when processing special characters.
| set escaped_lang (printf '%s\n' "$NEW_LANG" | sed 's/[\[\.*^$()+{}|]/\\&/g') | |
| set escaped_framework (printf '%s\n' "$NEW_FRAMEWORK" | sed 's/[\[\.*^$()+{}|]/\\&/g') | |
| set escaped_branch (printf '%s\n' "$CURRENT_BRANCH" | sed 's/[\[\.*^$()+{}|]/\\&/g') | |
| set escaped_lang (printf '%s\n' "$NEW_LANG" | sed 's/[\[\.*^$()+{}|\]]/\\&/g') | |
| set escaped_framework (printf '%s\n' "$NEW_FRAMEWORK" | sed 's/[\[\.*^$()+{}|\]]/\\&/g') | |
| set escaped_branch (printf '%s\n' "$CURRENT_BRANCH" | sed 's/[\[\.*^$()+{}|\]]/\\&/g') |
| set AMP_FILE "$REPO_ROOT/AGENTS.md" | ||
| set SHAI_FILE "$REPO_ROOT/SHAI.md" | ||
| set Q_FILE "$REPO_ROOT/AGENTS.md" | ||
| set BOB_FILE "$REPO_ROOT/AGENTS.md" |
Copilot
AI
Dec 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple agent types (opencode, amp, q, and bob) are all configured to use the same file path 'AGENTS.md'. This means that if a user tries to update these agents separately, they will all modify the same file, potentially causing conflicts or confusion. Consider using distinct file paths for each agent type, or document this shared file behavior clearly.
| set AMP_FILE "$REPO_ROOT/AGENTS.md" | |
| set SHAI_FILE "$REPO_ROOT/SHAI.md" | |
| set Q_FILE "$REPO_ROOT/AGENTS.md" | |
| set BOB_FILE "$REPO_ROOT/AGENTS.md" | |
| set AMP_FILE "$REPO_ROOT/AMP.md" | |
| set SHAI_FILE "$REPO_ROOT/SHAI.md" | |
| set Q_FILE "$REPO_ROOT/Q.md" | |
| set BOB_FILE "$REPO_ROOT/BOB.md" |
this is the last PR needed. i split them up because there are so many changes, and it would have resulted in a PR of about 1000 LoC, which would not have been pleasant to review :D