diff --git a/.conduit.yaml b/.conduit.yaml
index acb7792..491664f 100644
--- a/.conduit.yaml
+++ b/.conduit.yaml
@@ -16,6 +16,8 @@
     - {src: zshrc, dst: ~/.zshrc}
     - src: prompt_fresh_setup
       dst: ~/.local/share/zsh/site-functions/prompt_fresh_setup
+    - src: build/_build-dir
+      dst: ~/.local/share/zsh/site-functions/_build-dir
     - src: sandbox/_sandbox
       dst: ~/.local/share/zsh/site-functions/_sandbox
     - src: layout/_layout
@@ -26,6 +28,4 @@
     - https://github.com/zsh-users/zsh-autosuggestions.git
     - https://github.com/zsh-users/zsh-history-substring-search.git
     - https://github.com/zdharma/fast-syntax-highlighting.git
-- pip:
-    - pick
 - message: zsh will be the default prompt after next login
diff --git a/build/_build-dir b/build/_build-dir
new file mode 100644
index 0000000..fb7aaf9
--- /dev/null
+++ b/build/_build-dir
@@ -0,0 +1,6 @@
+#compdef build-dir
+
+_arguments \
+  '(-h --help)'{-h,--help}'[]' \
+  '--build[invoke a build after selection]' \
+  '1:directory:_files'
diff --git a/build/build.plugin.zsh b/build/build.plugin.zsh
index 5a1510b..6bdc4c9 100644
--- a/build/build.plugin.zsh
+++ b/build/build.plugin.zsh
@@ -17,46 +17,140 @@ elif [ `uname` = Darwin ]; then
     alias debug='lldb --'
 fi
 
-# Store the path to `build-dir.py`, using `${0:a:h}` does not work in a
-# function because it will result in `pwd` not this scripts directory.
-_build_dir_py=${0:a:h}/build-dir.py
-
 # Interactively choose a `~build` directory for `build` to build.
-# TODO: Support arguments:
-# * [ ] [-h,--help] - show this help message and exit
-# * [x] [--build] - execute the build commend
-# * [ ] <dir> - start in another directory, not `pwd`
 build-dir() {
-  # Get a unique filename to store the chosen build directory in.
-  [ `uname` = Darwin ] && local file=`mktemp` || local file=`tempfile`
-  # Prompt user to choose a build directory.
-  python $_build_dir_py $file;
-  local error=$?
-  # If the file exists, read the build directory from it, then delete it.
-  [ -f $file ] && local build_dir=$PWD/`cat $file`; rm $file
-  # If choosing a build directory failed, return that error.
-  [[ "$error" = "0" ]] || return $error
-  # If `build.ninja` exists in alias `ninja`, return.
-  [ -f $build_dir/build.ninja ] && \
-    local build="ninja -C $build_dir"
-  # If `Makefile` exists in alias `make`, return.
-  if [ -f $build_dir/Makefile ]; then
-    [ `uname` = Darwin ] && \
-      local cpu_count=`sysctl -n hw.ncpu` ||
-      local cpu_count=`grep -c '^processor' /proc/cpuinfo`
-    local build="make -j $cpu_count -C $build_dir"
+  local usage='usage: build-dir [-h] [--build] [<directory>]'
+  local -a help do_build
+  zparseopts -D h=help -help=help -build=do_build
+  if [[ -n $help ]]; then
+    cat << EOF
+$usage
+
+Find and select the current build directory interactively.
+
+positional arguments:
+  <directory> the build directory to select
+
+optional arguments:
+  -h, --help  show this help message and exit
+  --build     invoke a build after selection
+EOF
+    exit
   fi
-  # If the build variable is not defined the command could not be determined.
-  if [ -z $build ]; then
-    # TODO: Prompt user to choose a build command?
-    echo "could not determine build command"
-    return 1
+  error() { echo "\e[31merror:\e[0m $1"; exit 1 }
+  local build_dir
+  if [[ ${#*} -gt 1 ]]; then
+    echo $usage
+    error "unexpected position arguments: ${*[2,${#*}]}"
+  elif [[ ${#*} -eq 1 ]]; then
+    build_dir=${*[1]}
+    [[ ! -d $build_dir ]] && \
+      error "directory not found: $build_dir"
+  fi
+
+  # If <directory> was not set begin selection
+  if [[ -z $build_dir ]]; then
+    # Find build directories
+    local -a build_dirs
+    for entry in `ls -A`; do
+      [ -d $entry ] && [[ $entry =~ build* ]] && \
+        build_dirs+=$entry
+    done
+
+    # Interactively select a build directory if more than 1 found
+    integer index=0
+    if [[ ${#build_dirs} -eq 0 ]]; then
+      error "no build directories found"
+    elif [[ ${#build_dirs} -gt 1 ]]; then
+      zmodload zsh/curses && {
+        # Get the size of the terminal
+        local size=`stty size`
+        integer height=${size% *}
+        integer width=${size#* }
+
+        # Create the window and hide the cursor
+        zcurses init
+        zcurses addwin build-dir $height $width 0 0
+
+        # Hide the cursor for zcurses, trap SIGINT to ensure cleanup in
+        # always-list occurs below
+        tput civis; trap 'exit 130' INT
+
+        # Enter display loop
+        local key keypad
+        while (( 1 )); do
+          zcurses clear build-dir
+
+          # Add the prompt text
+          zcurses move build-dir 1 1
+          zcurses string build-dir 'Select a build directory:'
+
+          # Add the selections text
+          for (( i = 0; i < ${#build_dirs}; i++ )); do
+            integer line=$i+3
+            zcurses move build-dir $line 1
+            [[ $index -eq $i ]] &&
+              zcurses string build-dir "* " ||
+              zcurses string build-dir "  "
+            zcurses string build-dir ${build_dirs[$i+1]}
+          done
+
+          # Display the text the and wait for input
+          zcurses refresh build-dir
+          zcurses input build-dir key keypad
+
+          # Handle user input
+          case $key in
+            (UP|k|$'\C-P')
+              [[ $index -gt 0 ]] && index=$index-1 ;;
+            (DOWN|j|$'\C-N')
+              [[ $index -lt ${#build_dirs}-1 ]] && index=$index+1 ;;
+            (ENTER|$'\n')
+              break ;;
+          esac
+        done
+      } always {
+        # Restore the cursor and cleanup zcurses
+        tput cvvis; tput cnorm
+        zcurses delwin build-dir
+        zcurses end
+      }
+    fi
+  fi
+
+  # On success setup the build directory for use
+  if [[ $? -eq 0 ]]; then
+    # Set the build directory from selection if empty
+    [[ -z $build_dir ]] && \
+      build_dir=${build_dirs[$index+1]}
+
+    # If `build.ninja` exists in alias `ninja`, return.
+    local build
+    [ -f $build_dir/build.ninja ] && \
+      build="ninja -C $build_dir"
+
+    # If `Makefile` exists in alias `make`, return.
+    if [ -f $build_dir/Makefile ]; then
+      [ `uname` = Darwin ] && \
+        local cpu_count=`sysctl -n hw.ncpu` ||
+        local cpu_count=`grep -c '^processor' /proc/cpuinfo`
+      build="make -j $cpu_count -C $build_dir"
+    fi
+
+    # If the build variable is not defined the command could not be determined
+    if [ -z $build ]; then
+      echo "\e[33mwarning:\e[0m build command detection failed: $build_dir"
+      # Prompt user to enter a build command
+      vared -p 'enter comand: ' build
+    fi
+
+    # Redefine the `build` alias and update the `~build` hash directory
+    alias build="$build"
+    hash -d build=$build_dir
+
+    # If `--build` is specified then evaluate the command.
+    [[ -n $do_build ]] && eval build
   fi
-  # Redefine the `build` alias and update the `~build` hash directory.
-  alias build="$build"
-  hash -d build=$build_dir
-  # If `--build` is specified then evaluate the command.
-  [[ "$1" = "--build" ]] && eval $build || true
 }
 
 # Build then run a target residing in `~build/bin`.