Q:

Small bash script to enhance virtual environments on directory navigation

Ages ago I made a small bash script which would add a command cdp, standing for "change directory - project".
What it did was simple, it added all folders in ~/projects as autocompletion and CD'd me into them using only the project folder's name.
Over time I switched to a different company where I started working with Python, thus needing virtual environments not to mess up my workstation, which led me to adding more functionalities to the script.
I rarely use bash to do anything, rather using Python or even PHP for simple CLI tasks. I was hoping for some input regarding my first ever bash script, though, so I can keep my terminal scripts in its native language.
I think some improvements could be the way I name and call functions, and the newly added command should probably be something other than an alias.
#!/usr/bin/env bash

funcCheckVirtualEnvironment()
{
    if [ -d "venv/" ]; then
        read -p "A virtual environment was found for this project, would you like to execute the activation script? [y/n]" -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            source venv/bin/activate
            if [ -e "requirements.txt" ]; then
                read -p "PyPi requirements were found, would you like to install any uninstalled packages? [y/n]" -n 1 -r
                echo
                if [[ $REPLY =~ ^[Yy]$ ]]; then
                    pip install -r requirements.txt
                fi
            fi  
        fi
    fi      
}

funcCDP()
{

    # Add more flags in future, maybe
    case "$1" in
    "-r" )
        funcCheckVirtualEnvironment
        return 0 ;;
    esac

    if [ -d ~/projects/$1 ]; then
        cd ~/projects/$1;
        funcCheckVirtualEnvironment
    else
        echo "Directory $1 does not exist!"
    fi

}

funcCD()
{
    if [ $1 == "-"] || [ -d $1 ]; then
        command cd "$@"
        if [ -d "venv/" ]; then
            funcCheckVirtualEnvironment
        fi
    else
        echo "No such file or directory: $1"
    fi
}

_cdp()
{
        local cur=${COMP_WORDS[COMP_CWORD]}
        COMPREPLY=( $(compgen -W '$(\ls ~/projects)' -- $cur) )
}
complete -F _cdp cdp
alias cdp=funcCDP
alias cd=funcCD

A:

No shebang
It doesn't make sense to execute this as a program - it's intended to be sourced into an interactive shell.  So we shouldn't have the #! line.
Quote expansions
$1 could expand into multiple words here:
    if [ -d ~/projects/$1 ]; then
        cd ~/projects/$1;

and here:
    if [ $1 == "-"] || [ -d $1 ]; then

Send errors to stderr
    echo "Directory $1 does not exist!" >&2
    #                                   ^^^ IMPORTANT

Don't ask questions when used non-interactively
funcCheckVirtualEnvironment can misbehave when standard input is not a terminal.  We should test that case and return early:
test -r "venv/bin/activate" || return 0;
if ! test -t 0
then echo "Warning: not activating virtual environment" >&2
     return 0   # or 1 if this should be a failure
fi
read -p "Execute this project's activation script? [y/N]" -n 1 -r; echo
[[ REPLY =~ [Yy] ]] || return 0;

I've transformed some of the other if and else above into early returns, to reduce the nesting depth - I find that easier to read.  Also, I've capitalised N in the prompt, to provide the usual indication of the default.  And we know that $REPLY is a single character, so we can lose the anchors.
Let cd do its own checking instead
There's a possible race between checking for the existence of a directory and actually changing into it, so it's easier and safer to simply check whether cd succeeded:
command cd "$@" && test -d 'venv' && funcCheckVirtualEnvironment

This also allows cd to produce the error message (in the user's preferred $LANGuage).
Consider the exit status of the command
If we cd but there's no venv directory at the destination, that should be considered a success:
command cd "$@" || return $?
if test -d "venv"
then funcCheckVirtualEnvironment
else true
fi

Actually, there's no need to check for the existence of venv/, given that funcCheckVirtualEnvironment does that itself, and should succeed if it's not present:
command cd "$@" && funcCheckVirtualEnvironment

Similarly, in cdp -r, instead of return 0, a plain return is better, as that will result in the return value of the last executed command (funcCheckVirtualEnvironment in this case).
Use standard directory completion
It's bad practice to parse the output of ls.  Instead, we can ask compgen to give us directory names; we can eliminate absolute paths and subdirectories by excluding anything containing /:
_cdp()
{
        COMPREPLY=( $(cd ~/projects && compgen -X '*/*' -d $2) )
}

My version
funcCheckVirtualEnvironment()
{
    test -r 'venv/bin/activate' || return 0;
    if ! test -t 0
    then
        # Not a terminal - don't ask questions
        echo "Warning: not activating virtual environment" >&2
        return 0   # or 1 if this should be a failure
    fi

    read -p "Execute this project's activation script? [y/N]" -n 1 -r; echo
    [[ REPLY =~ [Yy] ]] || return 0;
    source venv/bin/activate || return $?
    
    test -e 'requirements.txt' || return 0;
    read -p "Install any missing PyPi packages? [y/N]" -n 1 -r; echo
    [[ REPLY =~ [Yy] ]] || return 0;
    pip install -r requirements.txt
}

funcCDP()
{
    # Add more flags in future, maybe
    case "$1" in
    "-r" )
        funcCheckVirtualEnvironment
        return
        ;;
    esac

    cd ~/projects/"$1" && funcCheckVirtualEnvironment
}

funcCD()
{
    command cd "$@" && funcCheckVirtualEnvironment
}

_cdp()
{
    COMPREPLY=( $(cd ~/projects && compgen -X '*/*' -d $2) )
}
complete -F _cdp cdp
alias cdp=funcCDP
alias cd=funcCD

