Automatically detect when I typed "vi" but meant "cd"?

With the assumption that you call vi with the directory as the last argument:

vi() {
    if [[ -d ${!#} ]]; then
        cd "$@"
    else 
        command vi "$@"
    fi
}

Apart from @ChrisDown answer, here is another approach: bypass directories

With this approach, you can :

vi ./*

and it will start vi on all the files in the current directory even if it contains subdirs, bypassing those subdirs

vi() {
  for arg do
    [ -d "$arg" ] || set -- "$@" "$arg"
    shift
  done
  [ "$#" -gt 0 ] && command vi "$@"
}

This one just do vi, on any argument that are not directories... Hence it won't teach you to use "vi" for "cd" ;)

And it will not call vi if you just did: vi somedirectory (ie, mistyped vi instead of cd). But it will not cd there automatically then, so you still remember you have to type cd ^^

I used a "compatible" way to change the arguments lists, so that it's portable to many platforms.