#!/usr/bin/env bash

GIT_WORK_TREE="${GIT_WORK_TREE:-.}"

convert_to_absolute_path() {
  local path="$1"
  case "$path" in
    /*) echo "$path" ;;
    *) echo "$PWD/$path" ;;
  esac
}

GIT_WORK_TREE=$(convert_to_absolute_path "$GIT_WORK_TREE")
YAKS_PATH="$GIT_WORK_TREE/.yaks"

convert_done_file_to_state() {
  local done_file="$1"
  local yak_dir=$(dirname "$done_file")
  echo "done" > "$yak_dir/state"
  rm -f "$done_file"
}

migrate_done_to_state() {
  if [ ! -d "$YAKS_PATH" ]; then
    return 0
  fi

  while IFS= read -r done_file; do
    convert_done_file_to_state "$done_file"
  done < <(find "$YAKS_PATH" -type f -name "done")
}

is_git_repository() {
  git -C "$GIT_WORK_TREE" rev-parse --git-dir > /dev/null 2>&1
}

yaks_path_exists() {
  [ -d "$YAKS_PATH" ]
}

log_command() {
  is_git_repository || return 0
  yaks_path_exists || return 0

  local commit_message="$*"

  local temp_index=$(mktemp)
  trap "rm -f '$temp_index'" RETURN

  GIT_INDEX_FILE="$temp_index" GIT_WORK_TREE="$YAKS_PATH" git -C "$GIT_WORK_TREE" read-tree --empty
  GIT_INDEX_FILE="$temp_index" GIT_WORK_TREE="$YAKS_PATH" git -C "$GIT_WORK_TREE" add .
  local tree=$(GIT_INDEX_FILE="$temp_index" git -C "$GIT_WORK_TREE" write-tree)

  local parent_args=""
  if git -C "$GIT_WORK_TREE" rev-parse refs/notes/yaks >/dev/null 2>&1; then
    parent_args="-p $(git -C "$GIT_WORK_TREE" rev-parse refs/notes/yaks)"
  fi

  local new_commit=$(git -C "$GIT_WORK_TREE" commit-tree $tree $parent_args -m "$commit_message")
  git -C "$GIT_WORK_TREE" update-ref refs/notes/yaks "$new_commit"

  unset GIT_INDEX_FILE

  # Maintain invariant: refresh .yaks from refs/notes/yaks
  extract_yaks_to_working_dir
}

validate_yak_name() {
  local name="$1"
  if [[ "$name" =~ [\\:\*\?\|\<\>\"] ]]; then
    echo "Invalid yak name: contains forbidden characters (\\ : * ? | < > \")" >&2
    return 1
  fi
  return 0
}

find_all_yaks() {
  if [ ! -d "$YAKS_PATH" ]; then
    return 0
  fi
  find "$YAKS_PATH" -mindepth 1 -type d
}

is_yak_done() {
  local yak_path="$1"
  if [ -f "$yak_path/state" ]; then
    local state=$(cat "$yak_path/state")
    [ "$state" = "done" ]
  else
    return 1
  fi
}

try_exact_match() {
  local search_term="$1"
  if [ -d "$YAKS_PATH/$search_term" ]; then
    echo "$search_term"
    return 0
  fi
  return 1
}

try_fuzzy_match() {
  local search_term="$1"
  local matches=()
  while IFS= read -r yak_path; do
    local yak_name="${yak_path#$YAKS_PATH/}"
    if [[ "$yak_name" == *"$search_term"* ]]; then
      matches+=("$yak_name")
    fi
  done < <(find_all_yaks)

  if [ ${#matches[@]} -eq 0 ]; then
    return 1
  elif [ ${#matches[@]} -eq 1 ]; then
    echo "${matches[0]}"
    return 0
  else
    return 2
  fi
}

find_yak() {
  local search_term="$1"

  try_exact_match "$search_term" && return 0
  try_fuzzy_match "$search_term"
}

capture_output_and_status() {
  local temp_file=$(mktemp)
  "$@" > "$temp_file"
  local status=$?
  local output=$(cat "$temp_file")
  rm -f "$temp_file"
  echo "$output"
  return $status
}

require_yak() {
  local yak_name="$1"

  local resolved_name
  resolved_name=$(capture_output_and_status find_yak "$yak_name")
  local status=$?

  if [ $status -eq 0 ]; then
    echo "$resolved_name"
    return 0
  elif [ $status -eq 2 ]; then
    echo "Error: yak name '$yak_name' is ambiguous" >&2
    return 1
  else
    echo "Error: yak '$yak_name' not found" >&2
    return 1
  fi
}

is_macos_stat() {
  stat -f "%m" "$1" >/dev/null 2>&1
}

get_mtime() {
  local path="$1"
  if is_macos_stat "$path"; then
    stat -f "%m" "$path" 2>/dev/null
  else
    stat -c "%Y" "$path" 2>/dev/null
  fi
}

get_sort_priority() {
  local yak_path="$1"
  if [ -f "$yak_path/state" ]; then
    local state=$(cat "$yak_path/state")
    if [ "$state" = "done" ]; then
      echo "0"
      return
    fi
  fi
  echo "1"
}

sort_yaks() {
  local children="$1"
  echo "$children" | while read -r child; do
    [ -z "$child" ] && continue
    local full_path="$YAKS_PATH/$child"
    local mtime=$(get_mtime "$full_path")
    local priority=$(get_sort_priority "$full_path")
    printf "%d\t%015d\t%s\t%s\n" "$priority" "$mtime" "$child" "$child"
  done | sort -t$'\t' -k1,1n -k2,2n -k3,3 | cut -f4-
}

parse_format_option() {
  if [ "$1" = "--format" ]; then
    echo "$2"
    return 2
  else
    echo "markdown"
    return 0
  fi
}

parse_only_option() {
  if [ "$1" = "--only" ]; then
    echo "$2"
    return 2
  else
    echo ""
    return 0
  fi
}

list_yaks() {
  local format
  format=$(parse_format_option "$@")
  local shift_count=$?
  shift $shift_count

  local only
  only=$(parse_only_option "$@")
  shift_count=$?
  shift $shift_count

  if [ ! -d "$YAKS_PATH" ] || [ -z "$(ls -A "$YAKS_PATH" 2>/dev/null)" ]; then
    if [ "$format" = "plain" ] || [ "$format" = "raw" ]; then
      return
    else
      echo "You have no yaks. Are you done?"
      return
    fi
  fi

  should_display_yak() {
    local yak_name="$1"
    local yak_dir="$YAKS_PATH/$yak_name"

    if [ -z "$only" ]; then
      return 0
    fi

    local state="todo"
    if [ -f "$yak_dir/state" ]; then
      state=$(cat "$yak_dir/state")
    fi

    case "$only" in
      not-done)
        [ "$state" != "done" ]
        ;;
      done)
        [ "$state" = "done" ]
        ;;
      *)
        return 0
        ;;
    esac
  }

  display_yak_markdown() {
    local yak_name="$1"
    local depth=$(echo "$yak_name" | tr -cd '/' | wc -c | xargs)
    local indent=""
    if [ "$depth" -gt 0 ]; then
      indent=$(printf '  %.0s' $(seq 1 $depth))
    fi
    local display_name=$(basename "$yak_name")
    local yak_dir="$YAKS_PATH/$yak_name"

    local state="todo"
    if [ -f "$yak_dir/state" ]; then
      state=$(cat "$yak_dir/state")
    fi

    if [ "$state" = "done" ]; then
      echo -e "\e[90m${indent}- [x] $display_name\e[0m"
    else
      echo "${indent}- [ ] $display_name"
    fi
  }

  display_yak_plain() {
    local yak_name="$1"
    echo "$yak_name"
  }

  case "$format" in
    plain|raw)
      display_yak() { display_yak_plain "$@"; }
      ;;
    markdown|md|*)
      display_yak() { display_yak_markdown "$@"; }
      ;;
  esac

  list_dir() {
    local parent_dir="$1"

    local children=$(cd "$YAKS_PATH" && find "$parent_dir" -mindepth 1 -maxdepth 1 -type d | sed 's|^\./||')
    local sorted_children=$(sort_yaks "$children")

    while IFS= read -r child; do
      [ -z "$child" ] && continue
      if should_display_yak "$child"; then
        display_yak "$child"
      fi
      list_dir "$child"
    done <<< "$sorted_children"
  }

  list_dir "."
}

show_help() {
  cat <<EOF
Usage: yx <command> [arguments]

Commands:
  add <name>                      Add a new yak
  list, ls [--format FMT]         List all yaks
           [--only STATE]
                          --format: Output format
                                    markdown (or md): Checkbox format (default)
                                    plain (or raw): Simple list of names
                          --only: Show only yaks in a specific state
                                  not-done: Show only incomplete yaks
                                  done: Show only completed yaks
  context [--show] <name>         Edit context (uses \$EDITOR) or set from stdin
                          --show: Display yak with context
                          --edit: Edit context (default)
  done <name>                     Mark a yak as done
  done --undo <name>              Unmark a yak as done
  rm <name>                       Remove a yak by name
  move <old> <new>                Rename a yak
  mv <old> <new>                  Alias for move
  prune                           Remove all done yaks
  sync                            Push and pull yaks to/from origin via git ref
  completions [cmd]               Output yak names for shell completion
  --help                          Show this help message
EOF
}

add_yak_interactive() {
  echo "Enter yaks (empty line to finish):"
  mkdir -p "$YAKS_PATH"
  while IFS= read -r line; do
    if [ -z "$line" ]; then
      break
    fi
    validate_yak_name "$line" || exit 1
    mkdir -p "$YAKS_PATH/$line"
    echo "todo" > "$YAKS_PATH/$line/state"
    touch "$YAKS_PATH/$line/context.md"
    log_command "add $line"
  done
}

add_yak_single() {
  local yak_name="$*"
  validate_yak_name "$yak_name" || exit 1
  mkdir -p "$YAKS_PATH/$yak_name"
  echo "todo" > "$YAKS_PATH/$yak_name/state"
  touch "$YAKS_PATH/$yak_name/context.md"
  log_command "add $yak_name"
}

add_yak() {
  if [ -z "$1" ]; then
    add_yak_interactive
  else
    add_yak_single "$@"
  fi
}

has_incomplete_children() {
  local yak_name="$1"
  local yak_path="$YAKS_PATH/$yak_name"

  # Check if there are any child directories
  if ! find "$yak_path" -mindepth 1 -maxdepth 1 -type d -print -quit | grep -q .; then
    return 1  # No children found
  fi

  # Check if any children are not done
  while IFS= read -r child_path; do
    if ! is_yak_done "$child_path"; then
      return 0  # Found an incomplete child
    fi
  done < <(find "$yak_path" -mindepth 1 -maxdepth 1 -type d)

  return 1  # All children are done
}

mark_yak_done_recursively() {
  local yak_name="$1"
  local yak_path="$YAKS_PATH/$yak_name"

  # Mark this yak as done
  echo "done" > "$yak_path/state"

  # Recursively mark all children as done
  while IFS= read -r child_path; do
    local child_name="${child_path#$YAKS_PATH/}"
    mark_yak_done_recursively "$child_name"
  done < <(find "$yak_path" -mindepth 1 -maxdepth 1 -type d)
}

done_yak() {
  if [ "$1" = "--undo" ]; then
    local yak_name="${*:2}"
    local resolved_name
    resolved_name=$(require_yak "$yak_name") || exit 1
    local yak_path="$YAKS_PATH/$resolved_name"
    echo "todo" > "$yak_path/state"
    log_command "done --undo $resolved_name"
  elif [ "$1" = "--recursive" ]; then
    local yak_name="${*:2}"
    local resolved_name
    resolved_name=$(require_yak "$yak_name") || exit 1
    mark_yak_done_recursively "$resolved_name"
    log_command "done --recursive $resolved_name"
  else
    local yak_name="$*"
    local resolved_name
    resolved_name=$(require_yak "$yak_name") || exit 1

    # Check for incomplete children
    if has_incomplete_children "$resolved_name"; then
      echo "Error: cannot mark '$resolved_name' as done - it has incomplete children" >&2
      exit 1
    fi

    local yak_path="$YAKS_PATH/$resolved_name"
    echo "done" > "$yak_path/state"
    log_command "done $resolved_name"
  fi
}

remove_yak() {
  local yak_name="$*"
  local resolved_name
  resolved_name=$(require_yak "$yak_name") || exit 1
  local yak_path="$YAKS_PATH/$resolved_name"
  rm -rf "$yak_path"
  log_command "rm $resolved_name"
}

prune_yaks() {
  while IFS= read -r yak_path; do
    if is_yak_done "$yak_path"; then
      local yak_name="${yak_path#$YAKS_PATH/}"
      remove_yak "$yak_name"
    fi
  done < <(find_all_yaks)
}

ensure_parent_yaks_exist() {
  local new_name="$1"
  local parent_dir=$(dirname "$new_name")

  if [ "$parent_dir" = "." ]; then
    return 0
  fi

  local current_path=""
  IFS='/' read -ra PARTS <<< "$parent_dir"
  for part in "${PARTS[@]}"; do
    if [ -z "$current_path" ]; then
      current_path="$part"
    else
      current_path="$current_path/$part"
    fi
    local yak_path="$YAKS_PATH/$current_path"
    if [ ! -d "$yak_path" ]; then
      mkdir -p "$yak_path"
      echo "todo" > "$yak_path/state"
      touch "$yak_path/context.md"
    fi
  done
}

move_yak() {
  local old_name="$1"
  shift
  local new_name="$*"
  local resolved_old
  resolved_old=$(require_yak "$old_name") || exit 1
  local old_path="$YAKS_PATH/$resolved_old"
  local new_path="$YAKS_PATH/$new_name"
  validate_yak_name "$new_name" || exit 1

  ensure_parent_yaks_exist "$new_name"

  mv "$old_path" "$new_path"
  log_command "move $resolved_old $new_name"
}

show_yak_context() {
  local yak_name="$*"
  local resolved_name
  resolved_name=$(require_yak "$yak_name") || exit 1
  local yak_path="$YAKS_PATH/$resolved_name"
  echo "$resolved_name"
  if [ -f "$yak_path/context.md" ]; then
    echo
    cat "$yak_path/context.md"
  fi
}

edit_yak_context() {
  local yak_name="$*"
  local resolved_name
  resolved_name=$(require_yak "$yak_name") || exit 1
  local yak_path="$YAKS_PATH/$resolved_name"
  if [ -t 0 ]; then
    ${EDITOR:-vi} "$yak_path/context.md"
  else
    cat > "$yak_path/context.md"
  fi
  log_command "context $resolved_name"
}

context_yak() {
  if [ "$1" = "--show" ]; then
    shift
    show_yak_context "$@"
  else
    if [ "$1" = "--edit" ]; then
      shift
    fi
    edit_yak_context "$@"
  fi
}

detect_user_shell() {
  if [[ "$SHELL" == *"zsh"* ]]; then
    echo "zsh"
  elif [[ "$SHELL" == *"bash"* ]]; then
    echo "bash"
  else
    return 1
  fi
}

get_shell_rc_file() {
  local shell="$1"
  case "$shell" in
    zsh) echo "$HOME/.zshrc" ;;
    bash) echo "$HOME/.bashrc" ;;
  esac
}

get_completion_file() {
  local shell="$1"
  echo "completions/yx.$shell"
}

install_completions() {
  local dry_run=false
  if [ "$1" = "--dry-run" ]; then
    dry_run=true
  fi

  local shell
  if ! shell=$(detect_user_shell); then
    echo "Error: Could not detect shell (bash or zsh)" >&2
    return 1
  fi

  local rc_file=$(get_shell_rc_file "$shell")
  local completion_file=$(get_completion_file "$shell")

  # Get absolute path to completions directory
  local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
  local completion_path="$script_dir/$completion_file"

  if [ "$dry_run" = true ]; then
    echo "Would add to $rc_file:"
    echo "source $completion_path"
    return 0
  fi

  # Check if already installed
  if [ -f "$rc_file" ] && grep -q "source.*$completion_file" "$rc_file" 2>/dev/null; then
    echo "Completions already installed in $rc_file"
    return 0
  fi

  # Try to add source line to rc file
  if echo "" >> "$rc_file" 2>/dev/null && \
     echo "# yx completion" >> "$rc_file" 2>/dev/null && \
     echo "source $completion_path" >> "$rc_file" 2>/dev/null; then
    echo "Installed completions to $rc_file"
    echo "Run 'source $rc_file' or restart your shell to enable completions"
  else
    echo "Error: Failed to write to $rc_file" >&2
    echo "" >&2
    echo "Please add the following line manually to $rc_file:" >&2
    echo "" >&2
    echo "  source $completion_path" >&2
    return 1
  fi
}

completions() {
  if [ "$1" = "install" ]; then
    shift
    install_completions "$@"
    return
  fi

  local cmd="$1"
  local flag="$2"

  while IFS= read -r yak_path; do
    local yak_name="${yak_path#$YAKS_PATH/}"
    local state="todo"
    if [ -f "$yak_path/state" ]; then
      state=$(cat "$yak_path/state")
    fi

    case "$cmd" in
      done)
        if [ "$flag" = "--undo" ]; then
          if [ "$state" = "done" ]; then
            echo "$yak_name"
          fi
        else
          if [ "$state" != "done" ]; then
            echo "$yak_name"
          fi
        fi
        ;;
      *)
        echo "$yak_name"
        ;;
    esac
  done < <(find_all_yaks)
}

has_origin_remote() {
  git -C "$GIT_WORK_TREE" remote get-url origin > /dev/null 2>&1
}

check_git_setup() {
  if ! is_git_repository; then
    echo "Error: not in a git repository" >&2
    return 1
  fi

  if ! has_origin_remote; then
    echo "Error: no origin remote configured" >&2
    return 1
  fi
  return 0
}

yaks_path_has_content() {
  [ -d "$YAKS_PATH" ] && [ -n "$(ls -A "$YAKS_PATH" 2>/dev/null)" ]
}

has_uncommitted_yak_changes() {
  local local_ref="$1"

  if [ -z "$local_ref" ]; then
    yaks_path_has_content
    return
  fi

  if ! yaks_path_has_content; then
    return 1
  fi

  local check_dir=$(mktemp -d)
  trap "rm -rf '$check_dir'" RETURN
  cp -r "$YAKS_PATH"/. "$check_dir"/

  local ref_dir=$(mktemp -d)
  git -C "$GIT_WORK_TREE" archive "$local_ref" | tar -x -C "$ref_dir" 2>/dev/null || true

  if ! diff -r "$check_dir" "$ref_dir" >/dev/null 2>&1; then
    rm -rf "$ref_dir"
    return 0
  fi

  rm -rf "$ref_dir"
  return 1
}

use_remote_only() {
  local remote_ref="$1"
  git -C "$GIT_WORK_TREE" update-ref refs/notes/yaks "$remote_ref"
}

create_merge_commit() {
  local local_ref="$1"
  local remote_ref="$2"
  local merged_tree="$3"

  local merge_commit=$(git -C "$GIT_WORK_TREE" commit-tree "$merged_tree" \
    -p "$local_ref" \
    -p "$remote_ref" \
    -m "Merge yaks")
  git -C "$GIT_WORK_TREE" update-ref refs/notes/yaks "$merge_commit"
}

merge_with_git_merge_tree() {
  local local_ref="$1"
  local remote_ref="$2"

  local merged_tree=$(git -C "$GIT_WORK_TREE" merge-tree --write-tree --allow-unrelated-histories "$local_ref" "$remote_ref" 2>&1)
  local merge_status=$?

  if [ $merge_status -eq 0 ] && [ -n "$merged_tree" ]; then
    create_merge_commit "$local_ref" "$remote_ref" "$merged_tree"
  else
    echo "ERROR: git merge-tree failed unexpectedly (status=$merge_status)" >&2
    echo "This is a bug - please report with details of your yak modifications" >&2
    exit 1
  fi
}

is_ancestor() {
  local ancestor="$1"
  local descendant="$2"
  git -C "$GIT_WORK_TREE" merge-base --is-ancestor "$ancestor" "$descendant" 2>/dev/null
}

merge_local_and_remote() {
  local local_ref="$1"
  local remote_ref="$2"

  if [ -z "$local_ref" ] && [ -n "$remote_ref" ]; then
    use_remote_only "$remote_ref"
    return
  fi

  if [ -n "$local_ref" ] && [ -z "$remote_ref" ]; then
    return
  fi

  if [ -n "$local_ref" ] && [ -n "$remote_ref" ] && [ "$local_ref" != "$remote_ref" ]; then
    # Check if local is ahead of remote (fast-forward case)
    if is_ancestor "$remote_ref" "$local_ref"; then
      # Local is ahead, just use it (no merge needed)
      return
    fi

    # Check if remote is ahead of local (fast-forward case)
    if is_ancestor "$local_ref" "$remote_ref"; then
      # Remote is ahead, use it
      use_remote_only "$remote_ref"
      return
    fi

    # Neither is ahead - do a merge
    merge_with_git_merge_tree "$local_ref" "$remote_ref"
  fi
}

extract_yaks_to_working_dir() {
  rm -rf "$YAKS_PATH"
  mkdir -p "$YAKS_PATH"
  if git -C "$GIT_WORK_TREE" rev-parse refs/notes/yaks >/dev/null 2>&1; then
    git -C "$GIT_WORK_TREE" archive refs/notes/yaks | tar -x -C "$YAKS_PATH" 2>/dev/null || true
  fi
}

get_remote_ref() {
  if git -C "$GIT_WORK_TREE" rev-parse refs/remotes/origin/yaks >/dev/null 2>&1; then
    git -C "$GIT_WORK_TREE" rev-parse refs/remotes/origin/yaks
  fi
}

get_local_ref() {
  if git -C "$GIT_WORK_TREE" rev-parse refs/notes/yaks >/dev/null 2>&1; then
    git -C "$GIT_WORK_TREE" rev-parse refs/notes/yaks
  fi
}

merge_remote_into_local_yaks() {
  local remote_ref="$1"
  local temp_dir=$(mktemp -d)
  git -C "$GIT_WORK_TREE" archive "$remote_ref" | tar -x -C "$temp_dir" 2>/dev/null || true
  cp -r "$YAKS_PATH"/. "$temp_dir"/
  rm -rf "$YAKS_PATH"
  mkdir -p "$YAKS_PATH"
  cp -r "$temp_dir"/. "$YAKS_PATH"/
  rm -rf "$temp_dir"
}

sync_yaks() {
  check_git_setup || exit 1

  git -C "$GIT_WORK_TREE" fetch origin refs/notes/yaks:refs/remotes/origin/yaks 2>/dev/null || true

  local remote_ref=$(get_remote_ref)
  local local_ref=$(get_local_ref)

  # If we have local uncommitted changes AND a remote, merge files first
  if has_uncommitted_yak_changes "$local_ref" && [ -n "$remote_ref" ]; then
    merge_remote_into_local_yaks "$remote_ref"
  fi

  # Commit any uncommitted changes in .yaks (including merged state)
  if has_uncommitted_yak_changes "$local_ref"; then
    log_command "sync"
    local_ref=$(get_local_ref)
  fi

  # Merge at git ref level
  merge_local_and_remote "$local_ref" "$remote_ref"

  if git -C "$GIT_WORK_TREE" rev-parse refs/notes/yaks >/dev/null 2>&1; then
    git -C "$GIT_WORK_TREE" push origin refs/notes/yaks:refs/notes/yaks 2>/dev/null || true
  fi

  # Extract final result to .yaks (maintains invariant)
  extract_yaks_to_working_dir

  git -C "$GIT_WORK_TREE" update-ref -d refs/remotes/origin/yaks 2>/dev/null || true
}

migrate_done_to_state

# Check if git is available (except for help commands)
if [ "$1" != "--help" ] && [ -n "$1" ]; then
  if ! command -v git >/dev/null 2>&1; then
    echo "Error: git command not found" >&2
    echo "yx requires git to be installed" >&2
    exit 1
  fi
fi

# Check if we're in a git repository (except for help commands)
if [ "$1" != "--help" ] && [ -n "$1" ]; then
  if ! is_git_repository; then
    echo "Error: not in a git repository" >&2
    echo "yx must be run from within a git repository" >&2
    exit 1
  fi
fi

# Check if .yaks folder is gitignored (except for help commands)
if [ "$1" != "--help" ] && [ -n "$1" ]; then
  if ! git -C "$GIT_WORK_TREE" check-ignore -q .yaks; then
    echo "Error: .yaks folder is not gitignored" >&2
    echo "Please add .yaks to your .gitignore file" >&2
    exit 1
  fi
fi

case "$1" in
  --help|"")
    show_help
    ;;
  add)
    shift
    add_yak "$@"
    ;;
  list|ls)
    shift
    list_yaks "$@"
    ;;
  done)
    shift
    done_yak "$@"
    ;;
  rm)
    shift
    remove_yak "$@"
    ;;
  prune)
    prune_yaks
    ;;
  move|mv)
    shift
    move_yak "$@"
    ;;
  context)
    shift
    context_yak "$@"
    ;;
  completions)
    shift
    completions "$@"
    ;;
  sync)
    sync_yaks
    ;;
  *)
    show_help
    ;;
esac
