Skip to content

Instantly share code, notes, and snippets.

@lao
Last active February 10, 2025 15:37
Show Gist options
  • Save lao/3d269feed346a1406f99e602c66d2ad5 to your computer and use it in GitHub Desktop.
Save lao/3d269feed346a1406f99e602c66d2ad5 to your computer and use it in GitHub Desktop.
# 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