Last active
February 10, 2025 15:37
-
-
Save lao/3d269feed346a1406f99e602c66d2ad5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Git AI Shell Functions and Utilities | |
# Author: Lucas Oliveira | |
# Last Modified: 2024-03-16 | |
############################################################################### | |
# CONFIGURATION | |
############################################################################### | |
# Update the default Claude model to use the latest version | |
: ${CLAUDE_MODEL:="claude-3-5-sonnet-latest"} # Use existing env var or set default | |
# OpenAI GPT model configuration | |
SGPT_MODEL="o1" # Default model for sgpt commands | |
# Prompts for various functions | |
GIT_CHECK_FFC_PROMPT="Analyze the following git changes. The input will be structured as follows: | |
1. First, you'll see the complete diff showing all changes | |
2. Then, for each modified file, you'll see '---FILE_CONTENT---' followed by the current content of that file | |
Consider both the changes and the full context when analyzing for: | |
1. Logical errors or potential bugs | |
2. Security vulnerabilities | |
3. Performance issues | |
4. Code style inconsistencies | |
5. Best practice violations | |
6. Consistency with the existing codebase | |
Provide a concise summary of any problems found, or respond with 'No issues detected.' if no problems were found. | |
Format your response in markdown, using appropriate headings, lists, and code blocks." | |
GIT_COMMIT_FFC_PROMPT="Given both the git diff of staged changes and the full content of modified files, generate a comprehensive git commit message following these guidelines: | |
1. Follow the Conventional Commits specification (feat:, fix:, chore:, etc.) | |
2. The first line should be a concise summary (max 72 chars) | |
3. Add a blank line followed by a short description of the changes (max 100 chars) if the changes are not obvious | |
Consider both the specific changes and the broader context of the modified files." | |
GIT_PR_DESCRIPTION_PROMPT="Generate a concise pull request description that includes: | |
1. A clear summary of the changes | |
2. The main purpose/goal of the changes | |
3. Any important technical details or decisions | |
4. Any breaking changes or dependencies | |
Format in markdown. Be concise but thorough. Exclude git hashes and branch names. | |
The out put should be a markdown following the following format: | |
{TEMPLATE} | |
" | |
GIT_PR_DESCRIPTION_PROMPT_TEMPLATE=" | |
## The Problem | |
> 2. The main purpose/goal of the changes | |
## The Fix | |
> 1. A clear summary of the changes | |
> 3. Any important technical details or decisions | |
> 4.Any breaking changes or dependencies | |
## Test performed | |
> e.g. instructions that someone else can use to reproduce | |
### Link to FE PR if applicable | |
" | |
############################################################################### | |
# GIT UTILITIES | |
############################################################################### | |
# @function ensure_gum_installed | |
# @description Checks if gum is installed, if not attempts to install it | |
# @example ensure_gum_installed | |
ensure_gum_installed() { | |
if ! command -v gum &> /dev/null; then | |
echo "gum is not installed. Attempting to install..." | |
if command -v brew &> /dev/null; then | |
brew install gum | |
else | |
echo "Homebrew is not installed. Please install gum manually:" | |
echo "Visit: https://github.com/charmbracelet/gum#installation" | |
return 1 | |
fi | |
fi | |
return 0 | |
} | |
# @function ensure_glow_installed | |
# @description Checks if glow is installed, if not attempts to install it | |
# @example ensure_glow_installed | |
ensure_glow_installed() { | |
if ! command -v glow &> /dev/null; then | |
echo "glow is not installed. Attempting to install..." | |
if command -v brew &> /dev/null; then | |
brew install glow | |
else | |
echo "Homebrew is not installed. Please install glow manually:" | |
echo "Visit: https://github.com/charmbracelet/glow#installation" | |
return 1 | |
fi | |
fi | |
return 0 | |
} | |
# @function ensure_aichat_installed | |
# @description Checks if aichat is installed, if not attempts to install it | |
# @example ensure_aichat_installed | |
ensure_aichat_installed() { | |
# Ensure aichat is installed | |
if ! command -v aichat &> /dev/null; then | |
echo "aichat is not installed. Attempting to install..." | |
if command -v brew &> /dev/null; then | |
brew install aichat | |
else | |
echo "Please install aichat manually:" | |
echo "Visit: https://github.com/sigoden/aichat#install" | |
return 1 | |
fi | |
fi | |
return 0 | |
} | |
# @function gitdiffbranch | |
# @description Generates a diff file between the current branch and a target branch | |
# @param $1 - Target branch name (defaults to master) | |
# @example gitdiffbranch develop | |
gitdiffbranch() { | |
local current_branch=$(git rev-parse --abbrev-ref HEAD) | |
local compare_branch=${1:-master} | |
local output_file="diff_${current_branch}_${compare_branch}.txt" | |
git diff "$compare_branch".."$current_branch" > "$output_file" | |
echo "Diff between '$current_branch' and '$compare_branch' has been saved to '$output_file'" | |
} | |
alias gdiffbranch="gitdiffbranch" | |
# @function gitcheck_full_file_content | |
# @description Analyzes git changes with full file context using AI for one or more models in parallel | |
# @param $1 - Optional branch name or git diff argument | |
# Additional optional parameter: --models=model1,model2,... | |
# @example gitcheck_full_file_content develop --models=gpt-4,o1,claude | |
# @example gitcheck_full_file_content develop --models=gpt-4,claude:claude-3-5-sonnet-20240620 | |
gitcheck_full_file_content() { | |
# Check for required dependencies | |
ensure_gum_installed || return 1 | |
ensure_glow_installed || return 1 | |
ensure_aichat_installed || return 1 | |
local diff_command | |
local changed_files | |
local view_mode="column" # Default view mode | |
# Process possible options: branch parameter, models flag, and view mode | |
local branch_parameter="" | |
local models_string="" | |
while (( "$#" )); do | |
case "$1" in | |
--models=*) | |
models_string="${1#--models=}" | |
;; | |
--view=*) | |
view_mode="${1#--view=}" | |
;; | |
*) | |
if [ -z "$branch_parameter" ]; then | |
branch_parameter="$1" | |
else | |
if [ -z "$models_string" ]; then | |
models_string="$1" | |
else | |
models_string="$models_string,$1" | |
fi | |
fi | |
;; | |
esac | |
shift | |
done | |
# Define the diff command and changed_files based on branch_parameter (if valid) or staged diff. | |
if [ -z "$branch_parameter" ]; then | |
diff_command="git diff --staged" | |
changed_files=$(git diff --staged --name-only) | |
elif git rev-parse --verify "$branch_parameter" >/dev/null 2>&1; then | |
diff_command="git diff $branch_parameter" | |
changed_files=$(git diff "$branch_parameter" --name-only) | |
else | |
diff_command="git diff $branch_parameter" | |
changed_files=$(git diff "$branch_parameter" --name-only) | |
fi | |
if [ -z "$changed_files" ]; then | |
echo "# Git Check Results\n\nβ No changes detected." | gum format | |
return 0 | |
fi | |
local full_content="" | |
full_content+="# Complete Diff:\n\n" | |
full_content+=$(eval "$diff_command") | |
full_content+="\n\n" | |
while IFS= read -r file; do | |
if [ -f "$file" ]; then | |
full_content+="---FILE_CONTENT--- $file\n\n" | |
full_content+=$(cat "$file") | |
full_content+="\n\n" | |
fi | |
done <<< "$changed_files" | |
# If no models are provided, use the default SGPT_MODEL value. | |
if [ -z "$models_string" ]; then | |
models_string="$SGPT_MODEL" | |
fi | |
# Create a temporary directory for model outputs | |
local tmp_dir=$(mktemp -d) | |
trap 'rm -rf "$tmp_dir"' EXIT | |
# Temporarily disable job control messages | |
setopt LOCAL_OPTIONS NO_NOTIFY NO_MONITOR | |
# Split models into array and launch parallel processes | |
local models_array=(${(s:,:)models_string}) | |
local pids=() | |
# Function to process a single model | |
process_model() { | |
local model="$1" | |
local output_file="$2" | |
local full_content="$3" | |
local output | |
# Handle different model types | |
if [[ "$model" == claude:* ]]; then | |
output=$(echo "$full_content" | aichat -m "$model" "$GIT_CHECK_FFC_PROMPT" 2>&1) | |
elif [[ "$model" == "claude" ]]; then | |
output=$(echo "$full_content" | aichat -m "claude:claude-3-5-sonnet-latest" "$GIT_CHECK_FFC_PROMPT" 2>&1) | |
elif [[ "$model" == "claude-fast" ]]; then | |
output=$(echo "$full_content" | aichat -m "claude:claude-3-5-haiku-latest" "$GIT_CHECK_FFC_PROMPT" 2>&1) | |
else | |
output=$(echo "$full_content" | sgpt stdin -m "$model" "$GIT_CHECK_FFC_PROMPT" 2>&1) | |
fi | |
if [ $? -ne 0 ] || [ -z "$output" ]; then | |
output="Error: No output from AI for model '$model'." | |
fi | |
echo "$output" > "$output_file" | |
} | |
# Launch parallel processes for each model | |
for model in "${models_array[@]}"; do | |
model="${model//[[:space:]]/}" | |
if [ -z "$model" ]; then | |
continue | |
fi | |
local output_file="$tmp_dir/$model" | |
process_model "$model" "$output_file" "$full_content" &>/dev/null & | |
pids+=($!) | |
done | |
# Show spinner while waiting for processes (in a subshell to avoid job messages) | |
echo "π€ Analyzing with ${#models_array[@]} models..." | |
local spinner_pid | |
( | |
while true; do | |
for s in β β β Ή β Έ β Ό β ΄ β ¦ β § β β ; do | |
echo -ne "\r$s " | |
sleep 0.1 | |
done | |
done | |
) &>/dev/null & | |
spinner_pid=$! | |
# Wait for all processes to complete | |
for pid in "${pids[@]}"; do | |
wait $pid &>/dev/null | |
done | |
# Kill spinner (suppress error message if already terminated) | |
kill $spinner_pid &>/dev/null || true | |
echo -ne "\rβ Analysis complete\n" | |
# Process results | |
local all_no_issues=true | |
local styled_outputs=() | |
local formatted_output | |
local styled_output | |
for model in "${models_array[@]}"; do | |
model="${model//[[:space:]]/}" | |
if [ -z "$model" ]; then | |
continue | |
fi | |
local output_file="$tmp_dir/$model" | |
local output="$(cat "$output_file")" | |
if [[ "${output:l}" != *"no"*"issues"*"detected"* && "${output:l}" != *"no"*"issues"*"were detected"* ]]; then | |
all_no_issues=false | |
fi | |
# Format output as before | |
if [ "$view_mode" = "column" ]; then | |
if [ ${#models_array[@]} -eq 1 ]; then | |
formatted_output=$(echo "Model: $model\n\n$output" | glow -) | |
styled_output="$formatted_output\n$(printf '%*s' "$(tput cols)" '' | tr ' ' '-')" | |
else | |
formatted_output=$(echo "$output" | gum format --type markdown | gum format --type code) | |
styled_output=$(echo "Model: $model\n\n$formatted_output" | gum style \ | |
--width 70 \ | |
--border double \ | |
--border-foreground 212 \ | |
--padding "1 2") | |
fi | |
else | |
formatted_output=$(echo "Model: $model\n\n$output" | glow -) | |
styled_output="$formatted_output\n$(printf '%*s' "$(tput cols)" '' | tr ' ' '-')" | |
fi | |
styled_outputs+=("$styled_output") | |
done | |
# Display outputs based on view mode | |
if [ "$view_mode" = "column" ] && [ ${#styled_outputs[@]} -gt 1 ]; then | |
echo "$(gum join --horizontal ${styled_outputs[@]} | gum style --padding "2 0")" | |
else | |
echo "${styled_outputs[@]}" | glow - | |
fi | |
# Commit prompt handling. | |
if $all_no_issues; then | |
echo -e "\nβ All models reported no issues." | gum style --foreground 2 | |
if gum confirm "Would you like to proceed with committing these changes?"; then | |
gitcommit_full_file_content | |
else | |
echo "Commit skipped. Your changes remain staged." | gum style --foreground 3 | |
return 0 | |
fi | |
else | |
echo -e "\nOne or more models reported potential issues." | gum style --foreground 1 | |
local choice=$(gum choose "Proceed to commit" "Exit to fix issues") | |
case $choice in | |
"Proceed to commit") | |
echo "Proceeding to commit..." | gum style --foreground 4 | |
gitcommit_full_file_content | |
;; | |
"Exit to fix issues") | |
echo "Exiting. Please fix the issues and run the check again." | gum style --foreground 3 | |
return 1 | |
;; | |
esac | |
fi | |
} | |
alias gcheckffc="gitcheck_full_file_content" | |
# @function gitcommit_full_file_content | |
# @description Generates a detailed commit message with full file context using AI | |
# @param --edit|-e - Optional flag to open message in editor before committing | |
# @example gitcommit_full_file_content --edit | |
gitcommit_full_file_content() { | |
local edit_mode=false | |
local OPTIND | |
while getopts ":e-:" opt; do | |
case $opt in | |
e) | |
edit_mode=true | |
;; | |
-) | |
case "${OPTARG}" in | |
edit) | |
edit_mode=true | |
;; | |
*) | |
echo "Invalid option: --${OPTARG}" >&2 | |
return 1 | |
;; | |
esac | |
;; | |
\?) | |
echo "Invalid option: -$OPTARG" >&2 | |
return 1 | |
;; | |
esac | |
done | |
local changed_files=$(git diff --staged --name-only) | |
if [ -z "$changed_files" ]; then | |
echo "β No staged changes detected." >&2 | |
return 1 | |
fi | |
local full_content="" | |
full_content+="# Complete Diff:\n\n" | |
full_content+=$(git diff --staged) | |
full_content+="\n\n" | |
while IFS= read -r file; do | |
if [ -f "$file" ]; then | |
full_content+="---FILE_CONTENT--- $file\n\n" | |
full_content+=$(cat "$file") | |
full_content+="\n\n" | |
fi | |
done <<< "$changed_files" | |
local commit_message | |
commit_message=$(echo "$full_content" | sgpt stdin -m "$SGPT_MODEL" "$GIT_COMMIT_FFC_PROMPT") | |
if [ $? -ne 0 ]; then | |
echo "β Failed to generate commit message" >&2 | |
return 1 | |
fi | |
if [ "$edit_mode" = true ]; then | |
local temp_file=$(mktemp) | |
echo "$commit_message" > "$temp_file" | |
${EDITOR:-vim} "$temp_file" | |
commit_message=$(cat "$temp_file") | |
rm "$temp_file" | |
else | |
echo "Generated commit message:" | |
echo "$commit_message" | glow - | |
local choice=$(gum choose \ | |
"Use this message" \ | |
"Edit the message" \ | |
"Cancel") | |
case $choice in | |
"Use this message") | |
;; | |
"Edit the message") | |
local temp_file=$(mktemp) | |
echo "$commit_message" > "$temp_file" | |
${EDITOR:-vim} "$temp_file" | |
commit_message=$(cat "$temp_file") | |
rm "$temp_file" | |
;; | |
"Cancel") | |
echo "Commit cancelled" | |
return 0 | |
;; | |
*) | |
echo "Invalid choice" | |
return 1 | |
;; | |
esac | |
fi | |
# Create a clean version of the commit message by stripping markdown | |
local stripped_message | |
# Function to strip markdown formatting | |
strip_markdown() { | |
local text="$1" | |
# Remove code blocks (both ``` and indented) | |
text=$(echo "$text" | awk ' | |
/^```/ { in_block = !in_block; next } | |
!in_block { print } | |
' | sed -E '/^ /d') | |
# Remove headers (#) | |
text=$(echo "$text" | sed -E 's/^#+ +//') | |
# Remove bold/italic (**/* and __/_) | |
text=$(echo "$text" | sed -E ' | |
s/\*\*([^\*]+)\*\*/\1/g | |
s/\*([^\*]+)\*/\1/g | |
s/__([^_]+)__/\1/g | |
s/_([^_]+)_/\1/g | |
') | |
# Remove bullet points and numbered lists | |
text=$(echo "$text" | sed -E ' | |
s/^- +// | |
s/^\* +// | |
s/^[0-9]+\. +// | |
') | |
# Remove links [text](url) -> text | |
text=$(echo "$text" | sed -E 's/\[([^\]]+)\]\([^\)]+\)/\1/g') | |
# Remove blockquotes | |
text=$(echo "$text" | sed -E 's/^> +//') | |
# Collapse multiple empty lines and remove starting/ending empty lines | |
text=$(echo "$text" | awk ' | |
NF { blank = 0; print } | |
!NF { blank++; if (blank == 1) print } | |
' | sed -E '/^$/N;/^\n$/D') | |
echo "$text" | |
} | |
# Strip markdown and preserve important newlines | |
stripped_message=$(strip_markdown "$commit_message") | |
# Show preview of stripped message | |
echo -e "\nCommit message after markdown stripping:" | |
echo -e "$stripped_message" | |
echo -e "\nProceeding with commit..." | |
git commit -m "$stripped_message" | |
} | |
# @function generate_pr_description | |
# @description Generates a concise PR description using AI based on branch changes | |
# @param --edit|-e - Optional flag to open description in editor before finalizing | |
# @param --base|-b - Optional base branch name (defaults to main/master) | |
# @example generate_pr_description --edit --base develop | |
generate_pr_description() { | |
local edit_mode=false | |
local base_branch="" | |
local OPTIND | |
while getopts ":eb:-:" opt; do | |
case $opt in | |
e) | |
edit_mode=true | |
;; | |
b) | |
base_branch="$OPTARG" | |
;; | |
-) | |
case "${OPTARG}" in | |
edit) | |
edit_mode=true | |
;; | |
base=*) | |
base_branch="${OPTARG#*=}" | |
;; | |
*) | |
echo "Invalid option: --${OPTARG}" >&2 | |
return 1 | |
;; | |
esac | |
;; | |
\?) | |
echo "Invalid option: -$OPTARG" >&2 | |
return 1 | |
;; | |
esac | |
done | |
# If no base branch specified, try to determine default | |
if [ -z "$base_branch" ]; then | |
if git show-ref --verify --quiet refs/heads/main; then | |
base_branch="main" | |
elif git show-ref --verify --quiet refs/heads/master; then | |
base_branch="master" | |
else | |
echo "β Could not determine base branch. Please specify with --base" >&2 | |
return 1 | |
fi | |
fi | |
# Get current branch name | |
local current_branch=$(git symbolic-ref --short HEAD) | |
if [ -z "$current_branch" ]; then | |
echo "β Not on a branch" >&2 | |
return 1 | |
fi | |
# Prepare context for AI | |
local context="" | |
# Add branch name context | |
context+="# Branch Name:\n$current_branch\n\n" | |
# Add commit summaries | |
context+="# Commit History:\n" | |
context+=$(git log --no-merges --pretty=format:"%h %s" $base_branch..$current_branch) | |
context+="\n\n" | |
# Add diff summary | |
context+="# Changes Summary:\n" | |
context+=$(git diff --stat $base_branch..$current_branch) | |
context+="\n\n" | |
# Add detailed diff | |
context+="# Detailed Changes:\n" | |
context+=$(git diff $base_branch..$current_branch) | |
# Try to find the PR template in the repository | |
local template="" | |
local template_paths=( | |
".github/pull_request_template.md" | |
".github/PULL_REQUEST_TEMPLATE.md" | |
"docs/pull_request_template.md" | |
".gitlab/merge_request_templates/default.md" | |
) | |
for template_path in "${template_paths[@]}"; do | |
if [ -f "$template_path" ]; then | |
template=$(cat "$template_path") | |
echo "ποΈ Found template: $template_path" | |
break | |
fi | |
done | |
# If no template found, use the default template | |
if [ -z "$template" ]; then | |
template="$GIT_PR_DESCRIPTION_PROMPT_TEMPLATE" | |
fi | |
# Replace {TEMPLATE} in the prompt with the actual template | |
local prompt="$GIT_PR_DESCRIPTION_PROMPT" | |
prompt="${prompt/\{TEMPLATE\}/$template}" | |
# Generate description using AI with the updated prompt | |
local description | |
description=$(echo "$context" | sgpt stdin -m "$SGPT_MODEL" "$prompt") | |
if [ $? -ne 0 ]; then | |
echo "β Failed to generate PR description" >&2 | |
return 1 | |
fi | |
if [ "$edit_mode" = true ]; then | |
local temp_file=$(mktemp) | |
echo "$description" > "$temp_file" | |
${EDITOR:-vim} "$temp_file" | |
description=$(cat "$temp_file") | |
rm "$temp_file" | |
else | |
echo "Generated PR description:" | |
echo "$description" | glow - | |
echo -e "\nWould you like to:" | |
echo "1) Use this description" | |
echo "2) Edit the description" | |
echo "3) Cancel" | |
read -r choice | |
case $choice in | |
1) | |
;; | |
2) | |
local temp_file=$(mktemp) | |
echo "$description" > "$temp_file" | |
${EDITOR:-vim} "$temp_file" | |
description=$(cat "$temp_file") | |
rm "$temp_file" | |
;; | |
3) | |
echo "Operation cancelled" | |
return 0 | |
;; | |
*) | |
echo "Invalid choice" | |
return 1 | |
;; | |
esac | |
fi | |
# Copy the description to clipboard if possible | |
if command -v pbcopy >/dev/null 2>&1; then | |
echo "$description" | pbcopy | |
echo "β PR description copied to clipboard" | |
elif command -v xclip >/dev/null 2>&1; then | |
echo "$description" | xclip -selection clipboard | |
echo "β PR description copied to clipboard" | |
else | |
echo "β PR description generated" | |
fi | |
# Create clean version for clipboard | |
# Preserve the template structure including quote markers | |
clean_description=$(echo "$description" | sed -E ' | |
/^[[:space:]]*$/d | |
') | |
# Copy the clean version to clipboard | |
if command -v pbcopy >/dev/null 2>&1; then | |
echo "$clean_description" | pbcopy | |
echo "β PR description copied to clipboard" | |
elif command -v xclip >/dev/null 2>&1; then | |
echo "$clean_description" | xclip -selection clipboard | |
echo "β PR description copied to clipboard" | |
else | |
echo "β PR description generated" | |
fi | |
# Print final description | |
echo -e "\nFinal PR Description:" | |
echo "$clean_description" | glow - | |
# Provide a helpful hint about the FE PR link | |
echo -e "\nNote: Don't forget to manually add the frontend PR link if applicable!" | |
} | |
# @function ask_question_on_branch_changes | |
# @description Ask a question on branch changes using AI | |
# @param -q - The question to ask | |
# @param -m - The models to use for the question | |
# @example ask_question_on_branch_changes -q "What are the changes in this branch?" | |
# @example ask_question_on_branch_changes -q "What are the changes in this branch?" -m "gpt-4o" | |
# @example ask_question_on_branch_changes -q "What are the changes in this branch?" -m "gpt-4o,o1" | |
ask_question_on_branch_changes() { | |
local question="" | |
local models_string="" | |
local base_branch="" | |
local OPTIND | |
while getopts ":q:m:b:-:" opt; do | |
case $opt in | |
q) | |
question="$OPTARG" | |
;; | |
m) | |
models_string="$OPTARG" | |
;; | |
b) | |
base_branch="$OPTARG" | |
;; | |
-) | |
case "${OPTARG}" in | |
question=*) | |
question="${OPTARG#*=}" | |
;; | |
models=*) | |
models_string="${OPTARG#*=}" | |
;; | |
base=*) | |
base_branch="${OPTARG#*=}" | |
;; | |
*) | |
echo "Invalid option: --${OPTARG}" >&2 | |
return 1 | |
;; | |
esac | |
;; | |
\?) | |
echo "Invalid option: -$OPTARG" >&2 | |
return 1 | |
;; | |
esac | |
done | |
# Check if question is provided | |
if [ -z "$question" ]; then | |
echo "β No question provided. Use -q or --question to specify a question." >&2 | |
return 1 | |
fi | |
# If no base branch specified, try to determine default | |
if [ -z "$base_branch" ]; then | |
if git show-ref --verify --quiet refs/heads/main; then | |
base_branch="main" | |
elif git show-ref --verify --quiet refs/heads/master; then | |
base_branch="master" | |
else | |
echo "β Could not determine base branch. Please specify with --base" >&2 | |
return 1 | |
fi | |
fi | |
# Get current branch name | |
local current_branch=$(git symbolic-ref --short HEAD) | |
if [ -z "$current_branch" ]; then | |
echo "β Not on a branch" >&2 | |
return 1 | |
fi | |
# If no models are provided, use the default SGPT_MODEL value | |
if [ -z "$models_string" ]; then | |
models_string="$SGPT_MODEL" | |
fi | |
# Split the models_string into an array using zsh splitting syntax | |
local models_array | |
models_array=(${(s:,:)models_string}) | |
# Prepare context for AI | |
local context="" | |
# Add branch name context | |
context+="# Branch Name:\n$current_branch\n\n" | |
# Add commit summaries | |
context+="# Commit History:\n" | |
context+=$(git log --no-merges --pretty=format:"%h %s" $base_branch..$current_branch) | |
context+="\n\n" | |
# Add diff summary | |
context+="# Changes Summary:\n" | |
context+=$(git diff --stat $base_branch..$current_branch) | |
context+="\n\n" | |
# Add detailed diff | |
context+="# Detailed Changes:\n" | |
context+=$(git diff $base_branch..$current_branch) | |
context+="\n\n" | |
# Add the user's question | |
context+="# Question:\n$question\n" | |
# Create prompt for the AI | |
local prompt="Based on the provided git branch information and changes, please answer the following question: | |
$question | |
Please provide a clear and concise answer based on the branch context. Format the response in markdown when appropriate." | |
# Declare associative array for results | |
declare -A results | |
local output | |
# Run analysis with each model | |
for model in "${models_array[@]}"; do | |
# Trim whitespace and skip empty entries | |
model="${model//[[:space:]]/}" | |
if [ -z "$model" ]; then | |
continue | |
fi | |
echo "π€ Analyzing with model: $model..." | |
# Capture both stdout and stderr from sgpt | |
output=$(echo "$context" | sgpt stdin -m "$model" "$prompt" 2>&1) | |
# If sgpt returns a non-zero exit code or empty output, set a fallback message | |
if [ $? -ne 0 ] || [ -z "$output" ]; then | |
output="Error: Failed to get response from model '$model'." | |
fi | |
results["$model"]="$output" | |
done | |
# Display the results per model | |
echo -e "\nπ Results:\n" | |
for model in "${models_array[@]}"; do | |
if [ ${#models_array[@]} -gt 1 ]; then | |
echo "===== Response from model: $model =====" | |
fi | |
echo "" | |
echo "${results["$model"]}" | glow - | |
echo "" | |
done | |
} | |
# Add alias for the new function | |
alias gask="ask_question_on_branch_changes" | |
# Define environment variables (can be customized in shell config) | |
: ${SGPT_MODEL:="gpt-4"} # Default AI model | |
: ${PR_DESCRIPTION_PROMPT:=""} # Custom prompt (uses default if empty) | |
# # Define environment variables (can be customized in shell config) | |
# : ${SGPT_MODEL:="gpt-4"} # Default AI model | |
# : ${PR_DESCRIPTION_PROMPT:=""} # Custom prompt (uses default if empty) | |
############################################################################### | |
# ALIASES | |
############################################################################### | |
# Git utilities aliases | |
alias gdiffbranch="gitdiffbranch" | |
alias gcheckffc="gitcheck_full_file_content" | |
alias gcommitffc="gitcommit_full_file_content" | |
alias gcommit="gitcommit_full_file_content" | |
alias gengitprdescription="generate_pr_description" | |
alias ggdescription="generate_pr_description" | |
############################################################################### | |
# GIT WORKFLOW MENU | |
############################################################################### | |
# @function git_workflow_menu | |
# @description Provides an interactive menu for common Git workflow actions | |
# @example git_workflow_menu | |
git_workflow_menu() { | |
# Ensure required dependencies are installed | |
ensure_gum_installed || return 1 | |
ensure_glow_installed || return 1 | |
# Define the menu options | |
local choice=$(gum choose \ | |
"Check full file changes" \ | |
"Generate PR description" \ | |
"Generate commit message" \ | |
"Exit") | |
case "$choice" in | |
"Check full file changes") | |
echo "Running full file check..." | |
gitcheck_full_file_content | |
;; | |
"Generate PR description") | |
echo "Generating PR description..." | |
generate_pr_description | |
;; | |
"Generate commit message") | |
echo "Generating commit message..." | |
gitcommit_full_file_content | |
;; | |
"Exit") | |
echo "Exiting..." | |
return 0 | |
;; | |
*) | |
echo "Invalid choice" | |
return 1 | |
;; | |
esac | |
} | |
# Add alias for the workflow menu | |
alias gwf="git_workflow_menu" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment