diff --git a/plugins/scd/README.md b/plugins/scd/README.md
new file mode 100644
index 000000000..ea7c72464
--- /dev/null
+++ b/plugins/scd/README.md
@@ -0,0 +1,123 @@
+# scd - smart change of directory
+
+Define `scd` shell function for changing to any directory with
+a few keystrokes.
+
+`scd` keeps history of the visited directories, which serves as an index of
+the known paths. The directory index is updated after every `cd` command in
+the shell and can be also filled manually by running `scd -a`. To switch to
+some directory, `scd` needs few fragments of the desired path to match with
+the index. A selection menu is displayed in case of several matches, with a
+preference given to recently visited paths. `scd` can create permanent
+directory aliases, which appear as named directories in zsh session.
+
+## INSTALLATION
+
+For oh-my-zsh, add `scd` to the `plugins` array in the ~/.zshrc file as in the
+[template file](../../templates/zshrc.zsh-template#L45).
+
+Besides zsh, `scd` can be used with *bash*, *dash* or *tcsh*
+shells and is also available as [Vim](http://www.vim.org/) plugin and
+[IPython](http://ipython.org/) extension. For installation details, see
+https://github.com/pavoljuhas/smart-change-directory.
+
+## SYNOPSIS
+
+```sh
+scd [options] [pattern1 pattern2 ...]
+```
+
+## OPTIONS
+
+
-
+-a, --add
-
+ add specified directories to the directory index.
-
+
+--unindex
-
+ remove specified directories from the index.
-
+
+-r, --recursive
-
+ apply options --add or --unindex recursively.
-
+
+--alias=ALIAS
-
+ create alias for the current or specified directory and save it to
+ ~/.scdalias.zsh.
-
+
+--unalias
-
+ remove ALIAS definition for the current or specified directory from
+ ~/.scdalias.zsh.
-
+
+--list
-
+ show matching directories and exit.
-
+
+-v, --verbose
-
+ display directory rank in the selection menu.
-
+
+-h, --help
-
+ display this options summary and exit.
+
+
+## Examples
+
+```sh
+# Index recursively some paths for the very first run
+scd -ar ~/Documents/
+
+# Change to a directory path matching "doc"
+scd doc
+
+# Change to a path matching all of "a", "b" and "c"
+scd a b c
+
+# Change to a directory path that ends with "ts"
+scd "ts(#e)"
+
+# Show selection menu and ranking of 20 most likely directories
+scd -v
+
+# Alias current directory as "xray"
+scd --alias=xray
+
+# Jump to a previously defined aliased directory
+scd xray
+```
+
+# FILES
+
+-
+~/.scdhistory
-
+ time-stamped index of visited directories.
-
+
+~/.scdalias.zsh
-
+ scd-generated definitions of directory aliases.
+
+
+# ENVIRONMENT
+
+-
+SCD_HISTFILE
-
+ path to the scd index file (by default ~/.scdhistory).
-
+
+SCD_HISTSIZE
-
+ maximum number of entries in the index (5000). Index is trimmed when it
+ exceeds SCD_HISTSIZE by more than 20%.
-
+
+SCD_MENUSIZE
-
+ maximum number of items for directory selection menu (20).
-
+
+SCD_MEANLIFE
-
+ mean lifetime in seconds for exponential decay of directory
+ likelihood (86400).
-
+
+SCD_THRESHOLD
-
+ threshold for cumulative directory likelihood. Directories with
+ lower likelihood are excluded unless they are the only match to
+ scd patterns.
+
-
+
+SCD_SCRIPT
-
+ command script file where scd writes the final
cd
+ command. This variable must be defined when scd runs in its own
+ process rather than as a shell function. It is up to the
+ scd caller to use the output in SCD_SCRIPT.
+
diff --git a/plugins/scd/scd b/plugins/scd/scd
new file mode 100755
index 000000000..9e055eadd
--- /dev/null
+++ b/plugins/scd/scd
@@ -0,0 +1,350 @@
+#!/bin/zsh -f
+
+emulate -L zsh
+if [[ $(whence -w $0) == *:' 'command ]]; then
+ emulate -R zsh
+ alias return=exit
+ local RUNNING_AS_COMMAND=1
+fi
+
+local DOC='scd -- smart change to a recently used directory
+usage: scd [options] [pattern1 pattern2 ...]
+Go to a directory path that contains all fixed string patterns. Prefer
+recently visited directories and directories with patterns in their tail
+component. Display a selection menu in case of multiple matches.
+
+Options:
+ -a, --add add specified directories to the directory index
+ --unindex remove specified directories from the index
+ -r, --recursive apply options --add or --unindex recursively
+ --alias=ALIAS create alias for the current or specified directory and
+ store it in ~/.scdalias.zsh
+ --unalias remove ALIAS definition for the current or specified
+ directory from ~/.scdalias.zsh
+ --list show matching directories and exit
+ -v, --verbose display directory rank in the selection menu
+ -h, --help display this message and exit
+'
+
+local SCD_HISTFILE=${SCD_HISTFILE:-${HOME}/.scdhistory}
+local SCD_HISTSIZE=${SCD_HISTSIZE:-5000}
+local SCD_MENUSIZE=${SCD_MENUSIZE:-20}
+local SCD_MEANLIFE=${SCD_MEANLIFE:-86400}
+local SCD_THRESHOLD=${SCD_THRESHOLD:-0.005}
+local SCD_SCRIPT=${RUNNING_AS_COMMAND:+$SCD_SCRIPT}
+local SCD_ALIAS=~/.scdalias.zsh
+
+local ICASE a d m p i tdir maxrank threshold
+local opt_help opt_add opt_unindex opt_recursive opt_verbose
+local opt_alias opt_unalias opt_list
+local -A drank dalias dkey
+local dmatching
+
+setopt extendedhistory extendedglob noautonamedirs brace_ccl
+
+# If SCD_SCRIPT is defined make sure the file exists and is empty.
+# This removes any previous old commands.
+[[ -n "$SCD_SCRIPT" ]] && [[ -s $SCD_SCRIPT || ! -f $SCD_SCRIPT ]] && (
+ umask 077
+ : >| $SCD_SCRIPT
+)
+
+# process command line options
+zmodload -i zsh/zutil
+zmodload -i zsh/datetime
+zparseopts -D -- a=opt_add -add=opt_add -unindex=opt_unindex \
+ r=opt_recursive -recursive=opt_recursive \
+ -alias:=opt_alias -unalias=opt_unalias -list=opt_list \
+ v=opt_verbose -verbose=opt_verbose h=opt_help -help=opt_help \
+ || return $?
+
+if [[ -n $opt_help ]]; then
+ print $DOC
+ return
+fi
+
+# load directory aliases if they exist
+[[ -r $SCD_ALIAS ]] && source $SCD_ALIAS
+
+# works faster than the (:a) modifier and is compatible with zsh 4.2.6
+_scd_Y19oug_abspath() {
+ set -A $1 ${(ps:\0:)"$(
+ unfunction -m "*"; shift
+ for d; do
+ cd $d && print -Nr -- $PWD && cd $OLDPWD
+ done
+ )"}
+}
+
+# define directory alias
+if [[ -n $opt_alias ]]; then
+ if [[ -n $1 && ! -d $1 ]]; then
+ print -u2 "'$1' is not a directory"
+ return 1
+ fi
+ a=${opt_alias[-1]#=}
+ _scd_Y19oug_abspath d ${1:-$PWD}
+ # alias in the current shell, update alias file if successful
+ hash -d -- $a=$d &&
+ (
+ umask 077
+ hash -dr
+ [[ -r $SCD_ALIAS ]] && source $SCD_ALIAS
+ hash -d -- $a=$d
+ hash -dL >| $SCD_ALIAS
+ )
+ return $?
+fi
+
+# undefine directory alias
+if [[ -n $opt_unalias ]]; then
+ if [[ -n $1 && ! -d $1 ]]; then
+ print -u2 "'$1' is not a directory"
+ return 1
+ fi
+ _scd_Y19oug_abspath a ${1:-$PWD}
+ a=$(print -rD ${a})
+ if [[ $a != [~][^/]## ]]; then
+ return
+ fi
+ a=${a#[~]}
+ # unalias in the current shell, update alias file if successful
+ if unhash -d -- $a 2>/dev/null && [[ -r $SCD_ALIAS ]]; then
+ (
+ umask 077
+ hash -dr
+ source $SCD_ALIAS
+ unhash -d -- $a 2>/dev/null &&
+ hash -dL >| $SCD_ALIAS
+ )
+ fi
+ return $?
+fi
+
+# Rewrite the history file if it is at least 20% oversized
+if [[ -s $SCD_HISTFILE ]] && \
+(( $(wc -l <$SCD_HISTFILE) > 1.2 * $SCD_HISTSIZE )); then
+ m=( ${(f)"$(<$SCD_HISTFILE)"} )
+ print -lr -- ${m[-$SCD_HISTSIZE,-1]} >| ${SCD_HISTFILE}
+fi
+
+# Internal functions are prefixed with "_scd_Y19oug_".
+# The "record" function adds a non-repeating directory to the history
+# and turns on history writing.
+_scd_Y19oug_record() {
+ while [[ -n $1 && $1 == ${history[$HISTCMD]} ]]; do
+ shift
+ done
+ if [[ $# != 0 ]]; then
+ ( umask 077; : >>| $SCD_HISTFILE )
+ p=": ${EPOCHSECONDS}:0;"
+ print -lr -- ${p}${^*} >> $SCD_HISTFILE
+ fi
+}
+
+if [[ -n $opt_add ]]; then
+ for a; do
+ if [[ ! -d $a ]]; then
+ print -u 2 "Directory $a does not exist"
+ return 2
+ fi
+ done
+ _scd_Y19oug_abspath m ${*:-$PWD}
+ _scd_Y19oug_record $m
+ if [[ -n $opt_recursive ]]; then
+ for d in $m; do
+ print -n "scanning ${d} ... "
+ _scd_Y19oug_record ${d}/**/*(-/N)
+ print "[done]"
+ done
+ fi
+ return
+fi
+
+# take care of removing entries from the directory index
+if [[ -n $opt_unindex ]]; then
+ if [[ ! -s $SCD_HISTFILE ]]; then
+ return
+ fi
+ # expand existing directories in the argument list
+ for i in {1..$#}; do
+ if [[ -d ${argv[i]} ]]; then
+ _scd_Y19oug_abspath d ${argv[i]}
+ argv[i]=${d}
+ fi
+ done
+ m="$(awk -v recursive=${opt_recursive} '
+ BEGIN {
+ for (i = 2; i < ARGC; ++i) {
+ argset[ARGV[i]] = 1;
+ delete ARGV[i];
+ }
+ }
+ 1 {
+ d = $0; sub(/^[^;]*;/, "", d);
+ if (d in argset) next;
+ }
+ recursive {
+ for (a in argset) {
+ if (substr(d, 1, length(a) + 1) == a"/") next;
+ }
+ }
+ { print $0 }
+ ' $SCD_HISTFILE ${*:-$PWD} )" || return $?
+ : >| ${SCD_HISTFILE}
+ [[ ${#m} == 0 ]] || print -r -- $m >> ${SCD_HISTFILE}
+ return
+fi
+
+# The "action" function is called when there is just one target directory.
+_scd_Y19oug_action() {
+ if [[ -n $opt_list ]]; then
+ for d; do
+ a=${(k)dalias[(r)${d}]}
+ print -r -- "# $a"
+ print -r -- $d
+ done
+ elif [[ $# == 1 ]]; then
+ if [[ -z $SCD_SCRIPT && -n $RUNNING_AS_COMMAND ]]; then
+ print -u2 "Warning: running as command with SCD_SCRIPT undefined."
+ fi
+ [[ -n $SCD_SCRIPT ]] && (umask 077;
+ print -r "cd ${(q)1}" >| $SCD_SCRIPT)
+ [[ -N $SCD_HISTFILE ]] && touch -a $SCD_HISTFILE
+ cd $1
+ # record the new directory unless already done in some chpwd hook
+ [[ -N $SCD_HISTFILE ]] || _scd_Y19oug_record $PWD
+ fi
+}
+
+# handle different argument scenarios ----------------------------------------
+
+## single argument that is an existing directory
+if [[ $# == 1 && -d $1 && -x $1 ]]; then
+ _scd_Y19oug_action $1
+ return $?
+## single argument that is an alias
+elif [[ $# == 1 && -d ${d::=${nameddirs[$1]}} ]]; then
+ _scd_Y19oug_action $d
+ return $?
+fi
+
+# ignore case unless there is an argument with an uppercase letter
+[[ "$*" == *[[:upper:]]* ]] || ICASE='(#i)'
+
+# calculate rank of all directories in the SCD_HISTFILE and keep it as drank
+# include a dummy entry for splitting of an empty string is buggy
+[[ -s $SCD_HISTFILE ]] && drank=( ${(f)"$(
+ print -l /dev/null -10
+ <$SCD_HISTFILE \
+ awk -v epochseconds=$EPOCHSECONDS -v meanlife=$SCD_MEANLIFE '
+ BEGIN { FS = "[:;]"; }
+ length($0) < 4096 && $2 > 0 {
+ tau = 1.0 * ($2 - epochseconds) / meanlife;
+ if (tau < -4.61) tau = -4.61;
+ prec = exp(tau);
+ sub(/^[^;]*;/, "");
+ if (NF) ptot[$0] += prec;
+ }
+ END { for (di in ptot) { print di; print ptot[di]; } }'
+ )"}
+)
+unset "drank[/dev/null]"
+
+# filter drank to the entries that match all arguments
+for a; do
+ p=${ICASE}"*${a}*"
+ drank=( ${(kv)drank[(I)${~p}]} )
+done
+
+# build a list of matching directories reverse-sorted by their probabilities
+dmatching=( ${(f)"$(
+ for d p in ${(kv)drank}; do
+ print -r -- "$p $d";
+ done | sort -grk1 | cut -d ' ' -f 2-
+ )"}
+)
+
+# if some directory paths match all patterns in order, discard all others
+p=${ICASE}"*${(j:*:)argv}*"
+m=( ${(M)dmatching:#${~p}} )
+[[ -d ${m[1]} ]] && dmatching=( $m )
+# if some directory names match last pattern, discard all others
+p=${ICASE}"*${(j:*:)argv}[^/]#"
+m=( ${(M)dmatching:#${~p}} )
+[[ -d ${m[1]} ]] && dmatching=( $m )
+# if some directory names match all patterns, discard all others
+m=( $dmatching )
+for a; do
+ p=${ICASE}"*/[^/]#${a}[^/]#"
+ m=( ${(M)m:#${~p}} )
+done
+[[ -d ${m[1]} ]] && dmatching=( $m )
+# if some directory names match all patterns in order, discard all others
+p=${ICASE}"/*${(j:[^/]#:)argv}[^/]#"
+m=( ${(M)dmatching:#${~p}} )
+[[ -d ${m[1]} ]] && dmatching=( $m )
+
+# do not match $HOME or $PWD when run without arguments
+if [[ $# == 0 ]]; then
+ dmatching=( ${dmatching:#(${HOME}|${PWD})} )
+fi
+
+# keep at most SCD_MENUSIZE of matching and valid directories
+m=( )
+for d in $dmatching; do
+ [[ ${#m} == $SCD_MENUSIZE ]] && break
+ [[ -d $d && -x $d ]] && m+=$d
+done
+dmatching=( $m )
+
+# find the maximum rank
+maxrank=0.0
+for d in $dmatching; do
+ [[ ${drank[$d]} -lt maxrank ]] || maxrank=${drank[$d]}
+done
+
+# discard all directories below the rank threshold
+threshold=$(( maxrank * SCD_THRESHOLD ))
+dmatching=( ${^dmatching}(Ne:'(( ${drank[$REPLY]} >= threshold ))':) )
+
+## process whatever directories that remained
+case ${#dmatching} in
+(0)
+ print -u2 "no matching directory"
+ return 1
+ ;;
+(1)
+ _scd_Y19oug_action $dmatching
+ return $?
+ ;;
+(*)
+ # build a list of strings to be displayed in the selection menu
+ m=( ${(f)"$(print -lD ${dmatching})"} )
+ if [[ -n $opt_verbose ]]; then
+ for i in {1..${#dmatching}}; do
+ d=${dmatching[i]}
+ m[i]=$(printf "%.3g %s" ${drank[$d]} $d)
+ done
+ fi
+ # build a map of string names to actual directory paths
+ for i in {1..${#m}}; dalias[${m[i]}]=${dmatching[i]}
+ # opt_list - output matching directories and exit
+ if [[ -n $opt_list ]]; then
+ _scd_Y19oug_action ${dmatching}
+ return
+ fi
+ # finally use the selection menu to get the answer
+ a=( {a-z} {A-Z} )
+ p=( )
+ for i in {1..${#m}}; do
+ [[ -n ${a[i]} ]] || break
+ dkey[${a[i]}]=${dalias[$m[i]]}
+ p+="${a[i]}) ${m[i]}"
+ done
+ print -c -r -- $p
+ if read -s -k 1 d && [[ -n ${dkey[$d]} ]]; then
+ _scd_Y19oug_action ${dkey[$d]}
+ fi
+ return $?
+esac
diff --git a/plugins/scd/scd.plugin.zsh b/plugins/scd/scd.plugin.zsh
new file mode 100644
index 000000000..0197c53a1
--- /dev/null
+++ b/plugins/scd/scd.plugin.zsh
@@ -0,0 +1,19 @@
+## The scd script should autoload as a shell function.
+autoload scd
+
+
+## If the scd function exists, define a change-directory-hook function
+## to record visited directories in the scd index.
+if [[ ${+functions[scd]} == 1 ]]; then
+ scd_chpwd_hook() { scd --add $PWD }
+ autoload add-zsh-hook
+ add-zsh-hook chpwd scd_chpwd_hook
+fi
+
+
+## Allow scd usage with unquoted wildcard characters such as "*" or "?".
+alias scd='noglob scd'
+
+
+## Load the directory aliases created by scd if any.
+if [[ -s ~/.scdalias.zsh ]]; then source ~/.scdalias.zsh; fi