diff --git a/common/.config/zsh/.zshrc b/common/.config/zsh/.zshrc
index ca0a88a0..9ca61fec 100644
--- a/common/.config/zsh/.zshrc
+++ b/common/.config/zsh/.zshrc
@@ -1,3 +1,9 @@
+command -v direnv &>/dev/null && emulate zsh -c "$(direnv export zsh)"
+
+if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
+  source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
+fi
+
 for f in \
     "plugins" \
     "functions" \
@@ -7,7 +13,10 @@ for f in \
     "bindings" \
     "completions" \
     "widgets" \
-    "host"
+    "host" \
+    "prompt"
 do
     source "${ZDOTDIR}/config/${f}.zsh"
 done
+
+export GPG_TTY=$TTY
diff --git a/common/.config/zsh/config/plugins.zsh b/common/.config/zsh/config/plugins.zsh
index 748082a3..81a0675f 100644
--- a/common/.config/zsh/config/plugins.zsh
+++ b/common/.config/zsh/config/plugins.zsh
@@ -23,6 +23,9 @@ function zsnippet() {
     zinit snippet "${@}"
 }
 
+# NOTE: prompt
+zload romkatv/powerlevel10k
+
 # NOTE: virtualenv wrapper
 turbo
 zsnippet https://raw.githubusercontent.com/python-virtualenvwrapper/virtualenvwrapper/refs/heads/main/virtualenvwrapper.sh
diff --git a/common/.config/zsh/config/prompt.zsh b/common/.config/zsh/config/prompt.zsh
new file mode 100644
index 00000000..2c603686
--- /dev/null
+++ b/common/.config/zsh/config/prompt.zsh
@@ -0,0 +1,210 @@
+local -a p10k_config_opts
+[[ ! -o 'aliases'                 ]] || p10k_config_opts+=('aliases')
+[[ ! -o 'sh_glob'                 ]] || p10k_config_opts+=('sh_glob')
+[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand')
+setopt no_aliases no_sh_glob brace_expand
+
+() {
+    emulate -L zsh -o extended_glob
+
+    typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
+        context
+        dir
+        vcs
+
+        newline
+        prompt_char
+    )
+
+    typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(
+        status
+        command_execution_time
+        background_jobs
+        direnv
+        virtualenv
+        taskwarrior
+    )
+
+    typeset -g POWERLEVEL9K_MODE=nerdfont-v3
+    typeset -g POWERLEVEL9K_ICON_PADDING=none # moderate | none
+
+    typeset -g POWERLEVEL9K_BACKGROUND=
+    typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE=
+    typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' '
+    typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR=
+
+
+    typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT=true
+
+    typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=true
+
+    # NOTE: prompt_char
+    typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=2
+    typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=1
+    typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯'
+    typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮'
+    typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V'
+    typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶'
+    typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true
+    typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL=''
+
+    # NOTE: dir
+    typeset -g POWERLEVEL9K_DIR_FOREGROUND=15
+    typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3
+    typeset -g POWERLEVEL9K_DIR_CLASSES=()
+
+    typeset -g POWERLEVEL9K_DIR_CONTENT_EXPANSION='%B%F{magenta}[%F{white}%3~%F{magenta}]'
+
+    # NOTE: vcs
+    typeset -g POWERLEVEL9K_VCS_BRANCH_ICON=
+
+    function my_git_formatter() {
+        emulate -L zsh
+
+        if [[ -n $P9K_CONTENT ]]; then
+            typeset -g my_git_format=$P9K_CONTENT
+            return
+        fi
+
+        if (( $1 )); then
+            local meta='%f'
+            local clean='%F{green}'
+            local modified='%B%F{yellow}'
+            local untracked='%B%F{blue}'
+            local conflicted='%F{red}'
+        else
+            local meta='%f'
+            local clean='%f'
+            local modified='%f'
+            local untracked='%f'
+            local conflicted='%f'
+        fi
+
+        local res
+
+        if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then
+            local branch=${(V)VCS_STATUS_LOCAL_BRANCH}
+            (( $#branch > 32 )) && branch[13,-13]="…"
+            res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}${branch//\%/%%}"
+        fi
+
+        if [[ -n $VCS_STATUS_TAG && -z $VCS_STATUS_LOCAL_BRANCH ]]; then
+            local tag=${(V)VCS_STATUS_TAG}
+            (( $#tag > 32 )) && tag[13,-13]="…"
+            res+="${meta}#${clean}${tag//\%/%%}"
+        fi
+
+        [[ -z $VCS_STATUS_LOCAL_BRANCH && -z $VCS_STATUS_TAG ]] &&
+            res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}"
+
+        if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then
+            res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}"
+        fi
+
+        if [[ $VCS_STATUS_COMMIT_SUMMARY == (|*[^[:alnum:]])(wip|WIP)(|[^[:alnum:]]*) ]]; then
+            res+=" ${modified}wip"
+        fi
+
+        if (( VCS_STATUS_COMMITS_AHEAD || VCS_STATUS_COMMITS_BEHIND )); then
+            (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}"
+            (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" "
+            (( VCS_STATUS_COMMITS_AHEAD    )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}"
+        fi
+
+        (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" "
+        (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}"
+        (( VCS_STATUS_PUSH_COMMITS_AHEAD  )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}"
+        (( VCS_STATUS_STASHES             )) && res+=" ${clean}*${VCS_STATUS_STASHES}"
+        [[ -n $VCS_STATUS_ACTION          ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}"
+        (( VCS_STATUS_NUM_CONFLICTED      )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}"
+        (( VCS_STATUS_NUM_STAGED          )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}"
+        (( VCS_STATUS_NUM_UNSTAGED        )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}"
+        (( VCS_STATUS_NUM_UNTRACKED       )) && res+=" ${untracked}?${VCS_STATUS_NUM_UNTRACKED}"
+        (( VCS_STATUS_HAS_UNSTAGED == -1  )) && res+=" ${modified}─"
+
+        typeset -g my_git_format=$res
+    }
+    functions -M my_git_formatter 2>/dev/null
+
+    typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~'
+
+    typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true
+    typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}'
+    typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter(0)))+${my_git_format}}'
+    typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1
+
+    typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION=""
+
+    typeset -g POWERLEVEL9K_VCS_BACKENDS=(git)
+
+    typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=2
+    typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=2
+    typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=3
+
+    # NOTE: status
+    typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true
+
+    typeset -g POWERLEVEL9K_STATUS_OK=false
+
+    typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true
+    typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=2
+    typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION=""
+
+    typeset -g POWERLEVEL9K_STATUS_ERROR=true
+    typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=1
+    typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION=""
+
+    typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true
+    typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=1
+    typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=true
+    typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION=""
+
+    typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true
+    typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=1
+    typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION=""
+
+    # NOTE: command_execution_time
+    typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3
+    typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0
+    typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=3
+    typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s'
+    typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION="󱎫"
+
+    # NOTE: background_jobs: presence of background jobs
+    typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE_ALWAYS=true
+    typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=1
+    typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION=""
+
+    # NOTE: direnv
+    typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=3
+    typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION="▼"
+
+    # NOTE: taskwarrior
+    typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=6
+    typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT'
+    typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION=''
+
+
+    # NOTE: context
+    typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%B%F{red}%n%F{cyan}@%F{blue}%m'
+    typeset -g POWERLEVEL9K_CONTEXT_REMOTE_TEMPLATE='%B%F{blue}%n%F{cyan}@%F{red}%m'
+    typeset -g POWERLEVEL9K_CONTEXT_REMOTE_SUDO_TEMPLATE='%B%F{red}%n%F{cyan}@%F{red}%m'
+    typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%B%F{blue}%n%F{cyan}@%F{blue}%m'
+    typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION=
+
+    # NOTE: virtualenv
+    # Python virtual environment color.
+    typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=3
+    typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false
+    typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER=
+    typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION=''
+
+    # NOTE: other options
+    typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always
+    typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose
+}
+
+typeset -g POWERLEVEL9K_CONFIG_FILE="realpath $(readlink "${ZDOTDIR}/config/prompt.zsh" || echo "${ZDOTDIR}/config/prompt.zsh")"
+
+(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]}
+unset p10k_config_opts
diff --git a/common/.config/zsh/config/widgets.zsh b/common/.config/zsh/config/widgets.zsh
index 8a2eea81..a5da5dda 100644
--- a/common/.config/zsh/config/widgets.zsh
+++ b/common/.config/zsh/config/widgets.zsh
@@ -2,9 +2,16 @@ autoload -Uz add-zsh-hook
 autoload -Uz colors && colors
 autoload -Uz edit-command-line && zle -N edit-command-line
 
-function venv_prompt() {
-    [[ -n ${VIRTUAL_ENV} ]] || return
-    echo -e "%F{yellow}%f ${VIRTUAL_ENV:t:gs/%/%%}"
+# function venv_prompt() {
+#     [[ -n ${VIRTUAL_ENV} ]] || return
+#     echo -e "%F{yellow}%f ${VIRTUAL_ENV:t:gs/%/%%}"
+# }
+
+function reset_prompt() {
+    for f in chpwd "${chpwd_functions[@]}" precmd "${(@)precmd_functions:#-z4h-direnv-hook}"; do
+        [[ "${+functions[$f]}" == 0 ]] || "$f" &>/dev/null || true
+    done
+    zle reset-prompt
 }
 
 # Cursor Shape
@@ -31,7 +38,7 @@ function reset_beam() {
 }
 add-zsh-hook preexec reset_beam
 
-fancy-ctrl-z () {
+function fancy-ctrl-z () {
     if [[ $#BUFFER -eq 0 ]]; then
         BUFFER="fg"
         zle accept-line -w
@@ -42,7 +49,7 @@ fancy-ctrl-z () {
 }
 zle -N fancy-ctrl-z
 
-sudo-command-line() {
+function sudo-command-line() {
     [[ -z $BUFFER ]] && zle up-history
     if [[ $BUFFER == sudo\ * ]]; then
         LBUFFER="${LBUFFER#sudo }"
@@ -73,7 +80,7 @@ function lf-wrap() {
         fi
     fi
 
-    zle reset-prompt
+    reset_prompt
     write_title_wd
     reset_beam
 }
@@ -82,7 +89,7 @@ zle -N lf-wrap
 function lazygit-wrap() {
     [ ! -d "$(pwd)/.git" ] && [[ $(read -erk "?Not in a git repository. Create a new git repository? (y/n): ") =~ ^[Yy]$ ]] && git init
     [ -d "$(pwd)/.git" ] && lazygit -p "$(pwd)"
-    zle reset-prompt
+    reset_prompt
 }
 zle -N lazygit-wrap
 
@@ -120,4 +127,4 @@ function workon_cwd() {
 }
 add-zsh-hook chpwd workon_cwd
 
-eval "$(direnv hook zsh)"
+command -v direnv &>/dev/null && emulate zsh -c "$(direnv hook zsh)"