Merge pull request #134 from erichs/plugin_metadata

Plugin management system (big thanks to @erichs)
diff --git a/bash_it.sh b/bash_it.sh
index cd97ab3..693c024 100755
--- a/bash_it.sh
+++ b/bash_it.sh
@@ -82,11 +82,13 @@
   echo
   echo "Here is a list of commands you can use to get help screens for specific pieces of Bash it:"
   echo
-  echo "  rails-help                  This will list out all the aliases you can use with rails."
-  echo "  git-help                    This will list out all the aliases you can use with git."
-  echo "  todo-help                   This will list out all the aliases you can use with todo.txt-cli"
-  echo "  brew-help                   This will list out all the aliases you can use with Homebrew"
-  echo "  aliases-help                Generic list of aliases."
-  echo "  plugins-help                This will list out all the plugins and functions you can use with bash-it"
+  echo "  rails-help                  list out all aliases you can use with rails."
+  echo "  git-help                    list out all aliases you can use with git."
+  echo "  todo-help                   list out all aliases you can use with todo.txt-cli"
+  echo "  brew-help                   list out all aliases you can use with Homebrew"
+  echo "  aliases-help                generic list of aliases."
+  echo "  plugins-help                list out all functions you have installed with bash-it"
+  echo "  bash-it-plugins             summarize bash-it plugins, and their installation status"
+  echo "  reference <function name>   detailed help for a specific function"
   echo
 }
diff --git a/lib/composure.sh b/lib/composure.sh
index 1bbf400..beaf13a 100644
--- a/lib/composure.sh
+++ b/lib/composure.sh
@@ -1,191 +1,27 @@
 # composure - by erichs
-# light-hearted shell functions for intuitive shell programming
+# light-hearted functions for intuitive shell programming
 
 # install: source this script in your ~/.profile or ~/.${SHELL}rc script
 
 # latest source available at http://git.io/composure
 # known to work on bash, zsh, and ksh93
 
-# define default metadata keywords:
-about ()   { :; }
-group ()   { :; }
-param ()   { :; }
-author ()  { :; }
-example () { :; }
+# 'plumbing' functions
 
-cite ()
+composure_keywords ()
 {
-    about creates a new meta keyword for use in your functions
-    param 1: keyword
-    example $ cite url
-    example $ url http://somewhere.com
-    group composure
-
-    # this is the storage half of the 'metadata' system:
-    # we create dynamic metadata keywords with function wrappers around
-    # the NOP command, ':'
-
-    # anything following a keyword will get parsed as a positional
-    # parameter, but stay resident in the ENV. As opposed to shell
-    # comments, '#', which do not get parsed, thus are not available
-    # at runtime.
-
-    # a BIG caveat--your metadata must be roughly parsable: do not use
-    # contractions, and consider single or double quoting if it contains
-    # non-alphanumeric characters
-
-    typeset keyword
-    for keyword in $*; do
-        eval "function $keyword { :; }"
-    done
-}
-
-draft ()
-{
-    about wraps last command into a new function
-    param 1: name to give function
-    example $ ls
-    example $ draft list
-    example $ list
-    group composure
-
-    typeset func=$1
-    eval 'function ' $func ' { ' $(fc -ln -1) '; }'
-    typeset file=$(mktemp /tmp/draft.XXXX)
-    typeset -f $func > $file
-    transcribe $func $file draft
-    rm $file 2>/dev/null
-}
-
-glossary ()
-{
-    about displays help summary for all functions, or summary for a group of functions
-    param 1: optional, group name
-    example $ glossary
-    example $ glossary misc
-    group composure
-
-    typeset targetgroup=${1:-}
-
-    for func in $(listfunctions); do
-        typeset about="$(metafor $func about)"
-        if [ -n "$targetgroup" ]; then
-            typeset group="$(metafor $func group)"
-            if [ "$group" != "$targetgroup" ]; then
-                continue  # skip non-matching groups, if specified
-            fi
-        fi
-        letterpress "$about" $func
-    done
+    echo "about author example group param version"
 }
 
 letterpress ()
 {
-    typeset metadata=$1 leftcol=${2:- } rightcol
+    typeset rightcol="$1" leftcol="${2:- }"
 
-    if [ -z "$metadata" ]; then
+    if [ -z "$rightcol" ]; then
         return
     fi
 
-    OLD=$IFS; IFS=$'\n'
-    for rightcol in $metadata; do
-        printf "%-20s%s\n" $leftcol $rightcol
-    done
-    IFS=$OLD
-}
-
-listfunctions ()
-{
-    # unfortunately, there does not seem to be a easy, portable way to list just the
-    # names of the defined shell functions...
-
-    # here's a hack I modified from a StackOverflow post:
-    # we loop over the ps listing for the current process ($$), and print the last column (CMD)
-    # stripping any leading hyphens bash sometimes throws in there
-
-    typeset x ans
-    typeset this=$(for x in $(ps -p $$); do ans=$x; done; printf "%s\n" $ans | sed 's/^-*//')
-    case "$this" in
-        bash)
-            typeset -F | awk '{print $3}'
-            ;;
-        *)
-            # trim everything following '()' in ksh
-            typeset +f | sed 's/().*$//'
-            ;;
-    esac
-}
-
-metafor ()
-{
-    about prints function metadata associated with keyword
-    param 1: function name
-    param 2: meta keyword
-    example $ metafor glossary example
-    group composure
-    typeset func=$1 keyword=$2
-
-    # this sed-fu is the retrieval half of the 'metadata' system:
-    # first 'cat' the function definition,
-    # then 'grep' for the metadata keyword, and
-    # then parse and massage the matching line
-    typeset -f $func | sed -n "s/;$//;s/^[ 	]*$keyword \([^([].*\)*$/\1/p"
-}
-
-reference ()
-{
-    about displays apidoc help for a specific function
-    param 1: function name
-    example $ reference revise
-    group composure
-
-    typeset func=$1
-
-    typeset about="$(metafor $func about)"
-    letterpress "$about" $func
-
-    typeset params="$(metafor $func param)"
-    if [ -n "$params" ]; then
-        printf "parameters:\n"
-        letterpress "$params"
-    fi
-
-    typeset examples="$(metafor $func example)"
-    if [ -n "$examples" ]; then
-        printf "examples:\n"
-        letterpress "$examples"
-    fi
-}
-
-revise ()
-{
-    about loads function into editor for revision
-    param 1: name of function
-    example $ revise myfunction
-    group composure
-
-    typeset func=$1
-    typeset temp=$(mktemp /tmp/revise.XXXX)
-
-    # populate tempfile...
-    if [ -f ~/.composure/$func.inc ]; then
-        # ...with contents of latest git revision...
-        cat ~/.composure/$func.inc >> $temp
-    else
-        # ...or from ENV if not previously versioned
-        typeset -f $func >> $temp
-    fi
-
-    if [ -z "$EDITOR" ]
-    then
-      typeset EDITOR=vi
-    fi
-
-    $EDITOR $temp
-    source $temp
-
-    transcribe $func $temp revise
-    rm $temp
+    printf "%-20s%s\n" "$leftcol" "$rightcol"
 }
 
 transcribe ()
@@ -246,6 +82,338 @@
     fi
 }
 
+transcribe ()
+{
+    typeset func=$1
+    typeset file=$2
+    typeset operation="$3"
+
+    if git --version >/dev/null 2>&1; then
+        if [ -d ~/.composure ]; then
+            (
+                cd ~/.composure
+                if git rev-parse 2>/dev/null; then
+                    if [ ! -f $file ]; then
+                        printf "%s\n" "Oops! Couldn't find $file to version it for you..."
+                        return
+                    fi
+                    cp $file ~/.composure/$func.inc
+                    git add --all .
+                    git commit -m "$operation $func"
+                fi
+            )
+        else
+            if [ "$USE_COMPOSURE_REPO" = "0" ]; then
+                return  # if you say so...
+            fi
+            printf "%s\n" "I see you don't have a ~/.composure repo..."
+            typeset input
+            typeset valid=0
+            while [ $valid != 1 ]; do
+                printf "\n%s" 'would you like to create one? y/n: '
+                read input
+                case $input in
+                    y|yes|Y|Yes|YES)
+                        (
+                            echo 'creating git repository for your functions...'
+                            mkdir ~/.composure
+                            cd ~/.composure
+                            git init
+                            echo "composure stores your function definitions here" > README.txt
+                            git add README.txt
+                            git commit -m 'initial commit'
+                        )
+                        # if at first you don't succeed...
+                        transcribe "$func" "$file" "$operation"
+                        valid=1
+                        ;;
+                    n|no|N|No|NO)
+                        printf "%s\n" "ok. add 'export USE_COMPOSURE_REPO=0' to your startup script to disable this message."
+                        valid=1
+                    ;;
+                    *)
+                        printf "%s\n" "sorry, didn't get that..."
+                    ;;
+                esac
+            done
+       fi
+    fi
+}
+
+typeset_functions ()
+{
+    # unfortunately, there does not seem to be a easy, portable way to list just the
+    # names of the defined shell functions...
+
+    # first, determine our shell:
+    typeset shell
+    if [ -n "$SHELL" ]; then
+        shell=$(basename $SHELL)  # we assume this is set correctly!
+    else
+        # we'll have to try harder
+        # here's a hack I modified from a StackOverflow post:
+        # we loop over the ps listing for the current process ($$), and print the last column (CMD)
+        # stripping any leading hyphens bash sometimes throws in there
+        typeset x ans
+        typeset this=$(for x in $(ps -p $$); do ans=$x; done; printf "%s\n" $ans | sed 's/^-*//')
+        typeset shell=$(basename $this)  # e.g. /bin/bash => bash
+    fi
+    case "$shell" in
+        bash)
+            typeset -F | awk '{print $3}'
+            ;;
+        *)
+            # trim everything following '()' in ksh
+            typeset +f | sed 's/().*$//'
+            ;;
+    esac
+}
+
+
+# bootstrap metadata keywords for porcelain functions
+for f in $(composure_keywords)
+do
+    eval "$f() { :; }"
+done
+unset f
+
+
+# 'porcelain' functions
+
+cite ()
+{
+    about creates one or more meta keywords for use in your functions
+    param one or more keywords
+    example '$ cite url username'
+    example '$ url http://somewhere.com'
+    example '$ username alice'
+    group composure
+
+    # this is the storage half of the 'metadata' system:
+    # we create dynamic metadata keywords with function wrappers around
+    # the NOP command, ':'
+
+    # anything following a keyword will get parsed as a positional
+    # parameter, but stay resident in the ENV. As opposed to shell
+    # comments, '#', which do not get parsed and are not available
+    # at runtime.
+
+    # a BIG caveat--your metadata must be roughly parsable: do not use
+    # contractions, and consider single or double quoting if it contains
+    # non-alphanumeric characters
+
+    if [ -z "$1" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference cite
+        return
+    fi
+
+    typeset keyword
+    for keyword in $*; do
+        eval "$keyword() { :; }"
+    done
+}
+
+draft ()
+{
+    about wraps command from history into a new function, default is last command
+    param 1: name to give function
+    param 2: optional history line number
+    example '$ ls'
+    example '$ draft list'
+    example '$ draft newfunc 1120  # wraps command at history line 1120 in newfunc()'
+    group composure
+
+    typeset func=$1
+    typeset num=$2
+    typeset cmd
+
+    if [ -z "$func" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference draft
+        return
+    fi
+
+    # aliases bind tighter than function names, disallow them
+    if [ -n "$(type -a $func 2>/dev/null | grep 'is.*alias')" ]; then
+        printf '%s\n' "sorry, $(type -a $func). please choose another name."
+        return
+    fi
+
+    if [ -z "$num" ]; then
+        # parse last command from fc output
+        # some versions of 'fix command, fc' need corrective lenses...
+        typeset myopic=$(fc -ln -1 | grep draft)
+        typeset lines=1
+        if [ -n "$myopic" ]; then
+            lines=2
+        fi
+        cmd=$(fc -ln -$lines | head -1 | sed 's/^[[:blank:]]*//')
+    else
+        # parse command from history line number
+        cmd=$(eval "history | grep '^[[:blank:]]*$num' | head -1" | sed 's/^[[:blank:][:digit:]]*//')
+    fi
+    eval "$func() { $cmd; }"
+    typeset file=$(mktemp /tmp/draft.XXXX)
+    typeset -f $func > $file
+    transcribe $func $file draft
+    rm $file 2>/dev/null
+}
+
+glossary ()
+{
+    about displays help summary for all functions, or summary for a group of functions
+    param 1: optional, group name
+    example '$ glossary'
+    example '$ glossary misc'
+    group composure
+
+    typeset targetgroup=${1:-}
+
+    for func in $(typeset_functions); do
+        if [ -n "$targetgroup" ]; then
+            typeset group="$(typeset -f $func | metafor group)"
+            if [ "$group" != "$targetgroup" ]; then
+                continue  # skip non-matching groups, if specified
+            fi
+        fi
+        typeset about="$(typeset -f $func | metafor about)"
+        letterpress "$about" $func
+    done
+}
+
+metafor ()
+{
+    about prints function metadata associated with keyword
+    param 1: meta keyword
+    example '$ typeset -f glossary | metafor example'
+    group composure
+
+    typeset keyword=$1
+
+    if [ -z "$keyword" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference metafor
+        return
+    fi
+
+    # this sed-fu is the retrieval half of the 'metadata' system:
+    # 'grep' for the metadata keyword, and then parse/filter the matching line
+
+    # grep keyword # strip trailing '|"|; # ignore thru keyword and leading '|"
+    sed -n "/$keyword / s/['\";]*$//;s/^[ 	]*$keyword ['\"]*\([^([].*\)*$/\1/p"
+}
+
+reference ()
+{
+    about displays apidoc help for a specific function
+    param 1: function name
+    example '$ reference revise'
+    group composure
+
+    typeset func=$1
+    if [ -z "$func" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference reference
+        return
+    fi
+
+    typeset line
+
+    typeset about="$(typeset -f $func | metafor about)"
+    letterpress "$about" $func
+
+    typeset author="$(typeset -f $func | metafor author)"
+    if [ -n "$author" ]; then
+        letterpress "$author" 'author:'
+    fi
+
+    typeset version="$(typeset -f $func | metafor version)"
+    if [ -n "$version" ]; then
+        letterpress "$version" 'version:'
+    fi
+
+    if [ -n "$(typeset -f $func | metafor param)" ]; then
+        printf "parameters:\n"
+        typeset -f $func | metafor param | while read line
+        do
+            letterpress "$line"
+        done
+    fi
+
+    if [ -n "$(typeset -f $func | metafor example)" ]; then
+        printf "examples:\n"
+        typeset -f $func | metafor example | while read line
+        do
+            letterpress "$line"
+        done
+    fi
+}
+
+revise ()
+{
+    about loads function into editor for revision
+    param 1: name of function
+    example '$ revise myfunction'
+    group composure
+
+    typeset func=$1
+    typeset temp=$(mktemp /tmp/revise.XXXX)
+
+    if [ -z "$func" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference revise
+        return
+    fi
+
+    # populate tempfile...
+    if [ -f ~/.composure/$func.inc ]; then
+        # ...with contents of latest git revision...
+        cat ~/.composure/$func.inc >> $temp
+    else
+        # ...or from ENV if not previously versioned
+        typeset -f $func >> $temp
+    fi
+
+    if [ -z "$EDITOR" ]
+    then
+      typeset EDITOR=vi
+    fi
+
+    $EDITOR $temp
+    . $temp  # source edited file
+
+    transcribe $func $temp revise
+    rm $temp
+}
+
+write ()
+{
+    about writes one or more composed function definitions to stdout
+    param one or more function names
+    example '$ write finddown foo'
+    example '$ write finddown'
+    group composure
+
+    if [ -z "$1" ]; then
+        printf '%s\n' 'missing parameter(s)'
+        reference write
+        return
+    fi
+
+# bootstrap metadata
+cat <<END
+for f in $(composure_keywords)
+do
+    eval "\$f() { :; }"
+done
+unset f
+END
+
+    # include cite() to enable custom keywords
+    typeset -f cite $*
+}
+
 : <<EOF
 License: The MIT License
 
diff --git a/lib/helpers.bash b/lib/helpers.bash
index ec6587e..d0c16b8 100644
--- a/lib/helpers.bash
+++ b/lib/helpers.bash
@@ -28,3 +28,147 @@
 function reload_plugins() {
   _load_bash_it_files "plugins"
 }
+
+bash-it-plugins ()
+{
+    about 'summarizes available bash_it plugins'
+    group 'lib'
+
+    typeset f
+    typeset enabled
+    printf "%-20s%-10s%s\n" 'Plugin' 'Enabled?' 'Description'
+    for f in $BASH_IT/plugins/available/*.bash
+    do
+        if [ -e $BASH_IT/plugins/enabled/$(basename $f) ]; then
+            enabled='x'
+        else
+            enabled=' '
+        fi
+        printf "%-20s%-10s%s\n" "$(basename $f | cut -d'.' -f1)" "  [$enabled]" "$(cat $f | metafor about-plugin)"
+    done
+    printf '\n%s\n' 'to enable a plugin, do:'
+    printf '%s\n' '$ enable-plugin  <plugin name> -or- $ enable-plugin all'
+    printf '\n%s\n' 'to disable a plugin, do:'
+    printf '%s\n' '$ disable-plugin <plugin name> -or- $ disable-plugin all'
+}
+
+disable-plugin ()
+{
+    about 'disables bash_it plugin'
+    param '1: plugin name'
+    example '$ disable-plugin rvm'
+    group 'lib'
+
+    if [ -z "$1" ]; then
+        reference disable-plugin
+        return
+    fi
+
+    if [ "$1" = "all" ]; then
+        typeset f plugin
+        for f in $BASH_IT/plugins/available/*.bash
+        do
+            plugin=$(basename $f)
+            if [ -e $BASH_IT/plugins/enabled/$plugin ]; then
+                rm $BASH_IT/plugins/enabled/$(basename $plugin)
+            fi
+        done
+    else
+        typeset plugin=$(ls $BASH_IT/plugins/enabled/$1.*bash 2>/dev/null | head -1)
+        if [ ! -h $plugin ]; then
+            printf '%s\n' 'sorry, that does not appear to be an enabled plugin.'
+            return
+        fi
+        rm $BASH_IT/plugins/enabled/$(basename $plugin)
+    fi
+
+    printf '%s\n' "$1 disabled."
+}
+
+enable-plugin ()
+{
+    about 'enables bash_it plugin'
+    param '1: plugin name'
+    example '$ enable-plugin rvm'
+    group 'lib'
+
+    if [ -z "$1" ]; then
+        reference enable-plugin
+        return
+    fi
+
+    if [ "$1" = "all" ]; then
+        typeset f plugin
+        for f in $BASH_IT/plugins/available/*.bash
+        do
+            plugin=$(basename $f)
+            if [ ! -h $BASH_IT/plugins/enabled/$plugin ]; then
+                ln -s $BASH_IT/plugins/available/$plugin $BASH_IT/plugins/enabled/$plugin
+            fi
+        done
+    else
+        typeset plugin=$(ls $BASH_IT/plugins/available/$1.*bash 2>/dev/null | head -1)
+        if [ -z "$plugin" ]; then
+            printf '%s\n' 'sorry, that does not appear to be an available plugin.'
+            return
+        fi
+
+        plugin=$(basename $plugin)
+        if [ -e $BASH_IT/plugins/enabled/$plugin ]; then
+            printf '%s\n' "$1 is already enabled."
+            return
+        fi
+
+        ln -s $BASH_IT/plugins/available/$plugin $BASH_IT/plugins/enabled/$plugin
+    fi
+
+    printf '%s\n' "$1 enabled."
+}
+
+plugins-help ()
+{
+    about 'summarize all functions defined by enabled bash-it plugins'
+    group 'lib'
+
+    # display a brief progress message...
+    printf '%s' 'please wait, building help...'
+    typeset grouplist=$(mktemp /tmp/grouplist.XXXX)
+    typeset func
+    for func in $(typeset_functions)
+    do
+        typeset group="$(typeset -f $func | metafor group)"
+        if [ -z "$group" ]; then
+            group='misc'
+        fi
+        typeset about="$(typeset -f $func | metafor about)"
+        letterpress "$about" $func >> $grouplist.$group
+        echo $grouplist.$group >> $grouplist
+    done
+    # clear progress message
+    printf '\r%s\n' '                              '
+    typeset group
+    typeset gfile
+    for gfile in $(cat $grouplist | sort | uniq)
+    do
+        printf '%s\n' "${gfile##*.}:"
+        cat $gfile
+        printf '\n'
+        rm $gfile 2> /dev/null
+    done | less
+    rm $grouplist 2> /dev/null
+}
+
+all_groups ()
+{
+    about 'displays all unique metadata groups'
+    group 'lib'
+
+    typeset func
+    typeset file=$(mktemp /tmp/composure.XXXX)
+    for func in $(typeset_functions)
+    do
+        typeset -f $func | metafor group >> $file
+    done
+    cat $file | sort | uniq
+    rm $file
+}
diff --git a/plugins/available/_xterm.plugins.bash b/plugins/available/_xterm.plugin.bash
similarity index 85%
rename from plugins/available/_xterm.plugins.bash
rename to plugins/available/_xterm.plugin.bash
index 9c8c668..b3810e7 100644
--- a/plugins/available/_xterm.plugins.bash
+++ b/plugins/available/_xterm.plugin.bash
@@ -6,6 +6,9 @@
 #
 # [issue 108]: https://github.com/revans/bash-it/issues/108
 
+cite about-plugin
+about-plugin 'automatically set your xterm title with host and location info'
+
 set_xterm_title () {
     local title="$1"
     echo -ne "\e]0;$title\007"
diff --git a/plugins/available/base.plugin.bash b/plugins/available/base.plugin.bash
old mode 100755
new mode 100644
index 1576ada..98d1fb4
--- a/plugins/available/base.plugin.bash
+++ b/plugins/available/base.plugin.bash
@@ -1,24 +1,26 @@
-#!/usr/bin/env bash
-
-# For generic functions.
+cite about-plugin
+about-plugin 'miscellaneous tools'
 
 ips ()
 {
-    about display all ip addresses for this host
+    about 'display all ip addresses for this host'
+    group 'base'
     ifconfig | grep "inet " | awk '{ print $2 }'
 }
 
 down4me ()
 {
-    about checks whether a website is down for you, or everybody
-    param 1: website url
+    about 'checks whether a website is down for you, or everybody'
+    param '1: website url'
     example '$ down4me http://www.google.com'
+    group 'base'
     curl -s "http://www.downforeveryoneorjustme.com/$1" | sed '/just you/!d;s/<[^>]*>//g'
 }
 
 myip ()
 {
-    about displays your ip address, as seen by the Internet
+    about 'displays your ip address, as seen by the Internet'
+    group 'base'
     res=$(curl -s checkip.dyndns.org | grep -Eo '[0-9\.]+')
     echo -e "Your public IP is: ${echo_bold_green} $res ${echo_normal}"
 }
@@ -26,9 +28,10 @@
 
 pickfrom ()
 {
-    about picks random line from file
-    param 1: filename
+    about 'picks random line from file'
+    param '1: filename'
     example '$ pickfrom /usr/share/dict/words'
+    group 'base'
     local file=$1
     [ -z "$file" ] && reference $FUNCNAME && return
     length=$(cat $file | wc -l)
@@ -38,11 +41,12 @@
 
 pass ()
 {
-    about generates random password from dictionary words
-    param optional integer length
-    param if unset, defaults to 4
+    about 'generates random password from dictionary words'
+    param 'optional integer length'
+    param 'if unset, defaults to 4'
     example '$ pass'
     example '$ pass 6'
+    group 'base'
     local i pass length=${1:-4}
     pass=$(echo $(for i in $(eval echo "{1..$length}"); do pickfrom /usr/share/dict/words; done))
     echo "With spaces (easier to memorize): $pass"
@@ -51,9 +55,10 @@
 
 pmdown ()
 {
-    about preview markdown file in a browser
-    param 1: markdown file
+    about 'preview markdown file in a browser'
+    param '1: markdown file'
     example '$ pmdown README.md'
+    group 'base'
     if command -v markdown &>/dev/null
     then
       markdown $1 | browser
@@ -64,54 +69,62 @@
 
 mkcd ()
 {
-    about make a directory and cd into it
-    param path to create
+    about 'make a directory and cd into it'
+    param 'path to create'
     example '$ mkcd foo'
     example '$ mkcd /tmp/img/photos/large'
+    group 'base'
     mkdir -p "$*"
     cd "$*"
 }
 
 lsgrep ()
 {
-    about search through directory contents with grep
+    about 'search through directory contents with grep'
+    group 'base'
     ls | grep "$*"
 }
 
 
 pman ()
 {
-    about view man documentation in Preview
-    param 1: man page to view
+    about 'view man documentation in Preview'
+    param '1: man page to view'
     example '$ pman bash'
+    group 'base'
     man -t "${1}" | open -f -a $PREVIEW
 }
 
 
 pcurl ()
 {
-    about download file and Preview it
-    param 1: download URL
+    about 'download file and Preview it'
+    param '1: download URL'
     example '$ pcurl http://www.irs.gov/pub/irs-pdf/fw4.pdf'
+    group 'base'
     curl "${1}" | open -f -a $PREVIEW
 }
 
 pri ()
 {
-    about display information about Ruby classes, modules, or methods, in Preview
-    param 1: Ruby method, module, or class
+    about 'display information about Ruby classes, modules, or methods, in Preview'
+    param '1: Ruby method, module, or class'
     example '$ pri Array'
+    group 'base'
     ri -T "${1}" | open -f -a $PREVIEW
 }
 
 quiet ()
 {
+    about 'what *does* this do?'
+    group 'base'
 	$* &> /dev/null &
 }
 
 banish-cookies ()
 {
-    about redirect .adobe and .macromedia files to /dev/null
+    about 'redirect .adobe and .macromedia files to /dev/null'
+    group 'base'
 	rm -r ~/.macromedia ~/.adobe
 	ln -s /dev/null ~/.adobe
 	ln -s /dev/null ~/.macromedia
@@ -119,8 +132,9 @@
 
 usage ()
 {
-    about disk usage per directory, in Mac OS X and Linux
-    param 1: directory name
+    about 'disk usage per directory, in Mac OS X and Linux'
+    param '1: directory name'
+    group 'base'
     if [ $(uname) = "Darwin" ]; then
         if [ -n $1 ]; then
             du -hd $1
@@ -139,9 +153,10 @@
 
 t ()
 {
-    about one thing todo
-    param if not set, display todo item
-    param 1: todo text
+    about 'one thing todo'
+    param 'if not set, display todo item'
+    param '1: todo text'
+    group 'base'
 	if [[ "$*" == "" ]] ; then
 	    cat ~/.t
 	else
@@ -151,35 +166,19 @@
 
 command_exists ()
 {
-    about checks for existence of a command
-    param 1: command to check
+    about 'checks for existence of a command'
+    param '1: command to check'
     example '$ command_exists ls && echo exists'
+    group 'base'
     type "$1" &> /dev/null ;
 }
 
-plugins-help ()
-{
-    about list all plugins and functions defined by bash-it
-    echo "bash-it Plugins Help-Message"
-    echo
-
-    set | grep "()" \
-    | sed -e "/^_/d" | grep -v "BASH_ARGC=()" \
-    | sed -e "/^\s/d" | grep -v "BASH_LINENO=()" \
-    | grep -v "BASH_ARGV=()" \
-    | grep -v "BASH_SOURCE=()" \
-    | grep -v "DIRSTACK=()" \
-    | grep -v "GROUPS=()" \
-    | grep -v "BASH_CMDS=()" \
-    | grep -v "BASH_ALIASES=()" \
-    | grep -v "COMPREPLY=()" | sed -e "s/()//"
-}
-
 # useful for administrators and configs
 buf ()
 {
-    about back up file with timestamp
-    param filename
+    about 'back up file with timestamp'
+    param 'filename'
+    group 'base'
     local filename=$1
     local filetime=$(date +%Y%m%d_%H%M%S)
     cp ${filename} ${filename}_${filetime}
diff --git a/plugins/available/battery.plugin.bash b/plugins/available/battery.plugin.bash
index 4b9d4b9..002d42f 100644
--- a/plugins/available/battery.plugin.bash
+++ b/plugins/available/battery.plugin.bash
@@ -1,11 +1,15 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'display info about your battery charge level'
 
 battery_percentage(){
+  about 'displays battery charge as a percentage of full (100%)'
+  group 'battery'
+
   if command_exists acpi;
   then
     local ACPI_OUTPUT=$(acpi -b)
     case $ACPI_OUTPUT in
-      *" Unknown"*) 
+      *" Unknown"*)
         local PERC_OUTPUT=$(echo $ACPI_OUTPUT | head -c 22 | tail -c 2)
         case $PERC_OUTPUT in
           *%)
@@ -16,7 +20,7 @@
             ;;
         esac
         ;;
-      *" Discharging"*) 
+      *" Discharging"*)
         local PERC_OUTPUT=$(echo $ACPI_OUTPUT | head -c 26 | tail -c 2)
         case $PERC_OUTPUT in
           *%)
@@ -27,7 +31,7 @@
             ;;
         esac
         ;;
-      *" Charging"*) 
+      *" Charging"*)
         local PERC_OUTPUT=$(echo $ACPI_OUTPUT | head -c 23 | tail -c 2)
         case $PERC_OUTPUT in
           *%)
@@ -38,7 +42,7 @@
             ;;
         esac
         ;;
-      *" Full"*) 
+      *" Full"*)
         echo '99'
         ;;
       *)
@@ -52,7 +56,7 @@
     #local IOREG_OUTPUT_10_5=$(ioreg -l | grep -i capacity | grep -v Legacy| tr '\n' ' | ' | awk '{printf("%.2f%%", $14/$7 * 100)}')
     local IOREG_OUTPUT=$(ioreg -n AppleSmartBattery -r | awk '$1~/Capacity/{c[$1]=$3} END{OFMT="%.2f%%"; max=c["\"MaxCapacity\""]; print (max>0? 100*c["\"CurrentCapacity\""]/max: "?")}')
     case $IOREG_OUTPUT in
-      100*) 
+      100*)
         echo '99'
         ;;
       *)
@@ -65,6 +69,9 @@
 }
 
 battery_charge(){
+  about 'graphical display of your battery charge'
+  group 'battery'
+
   # Full char
   local F_C='▸'
   # Depleted char
diff --git a/plugins/available/browser.plugin.bash b/plugins/available/browser.plugin.bash
index fdfba4f..f7d820a 100644
--- a/plugins/available/browser.plugin.bash
+++ b/plugins/available/browser.plugin.bash
@@ -1,42 +1,34 @@
 # based on https://gist.github.com/318247
 
-# Usage: browser
-# pipe html to a browser
-# e.g.
-# $ echo "<h1>hi mom!</h1>" | browser
-# $ ron -5 man/rip.5.ron | browser
+cite about-plugin
+about-plugin 'render commandline output in your browser'
 
 function browser() {
+    about 'pipe html to a browser'
+    example '$ echo "<h1>hi mom!</h1>" | browser'
+    example '$ ron -5 man/rip.5.ron | browser'
+    group 'browser'
+
     if [ -t 0 ]; then
         if [ -n "$1" ]; then
             open $1
         else
-            cat <<usage
-Usage: browser
-pipe html to a browser
-
-$ echo '<h1>hi mom!</h1>' | browser
-$ ron -5 man/rip.5.ron | browser
-usage
-
-    fi
+            reference browser
+        fi
 
     else
         f="/tmp/browser.$RANDOM.html"
         cat /dev/stdin > $f
-        open $f 
+        open $f
     fi
 }
 
 
-# pipe hot spicy interwebs into textmate and cleanup!
-#
-# Usage: wmate
-# wget into a pipe into TextMate and force Tidy (you can undo in textmate)
-# e.g.
-# $ wmate google.com
-
 function wmate() {
+    about 'pipe hot spicy interwebs into textmate and cleanup!'
+    example '$ wmate google.com'
+    group 'browser'
+
     if [ -t 0 ]; then
         if [ -n "$1" ]; then
             wget -qO- $1 | /usr/bin/mate
@@ -64,34 +56,21 @@
 EOT`
 
         else
-            cat <<usage
-Usage: wmate google.com
-wget into a pipe into TextMate and force Tidy (you can undo in textmate)
-
-$ wmate google.com
-usage
-
+            reference wmate
       fi
     fi
 }
 
-#
-# Usage: raw google.com
-# wget into a temp file and pump it into your browser
-#
-# e.g.
-# $ raw google.com
 function raw() {
+    about 'write wget into a temp file and pump it into your browser'
+    example '$ raw google.com'
+    group 'browser'
+
     if [ -t 0 ]; then
         if [ -n "$1" ]; then
             wget -qO- $1 | browser
         else
-            cat <<usage
-Usage: raw google.com
-wget into a temp file and pump it into your browser
-
-$ raw google.com
-usage
-      fi
+            reference raw
+        fi
     fi
 }
diff --git a/plugins/available/dirs.plugins.bash b/plugins/available/dirs.plugin.bash
old mode 100755
new mode 100644
similarity index 85%
rename from plugins/available/dirs.plugins.bash
rename to plugins/available/dirs.plugin.bash
index e03ccf5..bb1fb02
--- a/plugins/available/dirs.plugins.bash
+++ b/plugins/available/dirs.plugin.bash
@@ -1,5 +1,3 @@
-#!/usr/bin/env bash
-
 # Directory stack navigation:
 #
 # Add to stack with: pu /path/to/directory
@@ -7,6 +5,9 @@
 # Show stack with: d
 # Jump to location by number.
 
+cite about-plugin
+about-plugin 'directory stack navigation'
+
 # Show directory stack
 alias d="dirs -v -l"
 
@@ -31,6 +32,9 @@
 alias po="popd"
 
 function dirs-help() {
+  about 'directory navigation alias usage'
+  group 'dirs'
+
   echo "Directory Navigation Alias Usage"
   echo
   echo "Use the power of directory stacking to move"
@@ -64,10 +68,18 @@
 alias L='cat ~/.dirs'
 
 G () {				# goes to distination dir otherwise , stay in the dir
+    about 'goes to destination dir'
+    param '1: directory'
+    example '$ G ..'
+    group 'dirs'
+
     cd ${1:-$(pwd)} ;
 }
 
 S () {				# SAVE a BOOKMARK
+    about 'save a bookmark'
+    group 'dirs'
+
     sed "/$@/d" ~/.dirs > ~/.dirs1;
     \mv ~/.dirs1 ~/.dirs;
     echo "$@"=\"`pwd`\" >> ~/.dirs;
@@ -75,6 +87,9 @@
 }
 
 R () {				# remove a BOOKMARK
+    about 'remove a bookmark'
+    group 'dirs'
+
     sed "/$@/d" ~/.dirs > ~/.dirs1;
     \mv ~/.dirs1 ~/.dirs;
 }
diff --git a/plugins/available/extract.plugin.bash b/plugins/available/extract.plugin.bash
index 1c3e9b1..bb52045 100644
--- a/plugins/available/extract.plugin.bash
+++ b/plugins/available/extract.plugin.bash
@@ -1,3 +1,5 @@
+cite about-plugin
+about-plugin 'one command to extract them all...'
 extract () {
   if [ $# -ne 1 ]
   then
diff --git a/plugins/available/git.plugins.bash b/plugins/available/git.plugin.bash
similarity index 83%
rename from plugins/available/git.plugins.bash
rename to plugins/available/git.plugin.bash
index 41b031f..0b77111 100644
--- a/plugins/available/git.plugins.bash
+++ b/plugins/available/git.plugin.bash
@@ -1,26 +1,42 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'git helper functions'
 
 function git_remote {
+  about 'adds remote $GIT_HOSTING:$1 to current repo'
+  group 'git'
+
   echo "Running: git remote add origin ${GIT_HOSTING}:$1.git"
   git remote add origin $GIT_HOSTING:$1.git
 }
 
 function git_first_push {
+  about 'push into origin refs/heads/master'
+  group 'git'
+
   echo "Running: git push origin master:refs/heads/master"
   git push origin master:refs/heads/master
 }
 
 function git_remove_missing_files() {
+  about "git rm's missing files"
+  group 'git'
+
   git ls-files -d -z | xargs -0 git update-index --remove
 }
 
 # Adds files to git's exclude file (same as .gitignore)
 function local-ignore() {
+  about 'adds file or path to git exclude file'
+  param '1: file or path fragment to ignore'
+  group 'git'
   echo "$1" >> .git/info/exclude
 }
 
 # get a quick overview for your git repo
 function git_info() {
+    about 'overview for your git repo'
+    group 'git'
+
     if [ -n "$(git symbolic-ref HEAD 2> /dev/null)" ]; then
         # print informations
         echo "git repo overview"
@@ -29,7 +45,7 @@
 
         # print all remotes and thier details
         for remote in $(git remote show); do
-            echo $remote:  
+            echo $remote:
             git remote show $remote
             echo
         done
@@ -43,10 +59,10 @@
         fi
 
         # print at least 5 last log entries
-        echo 
+        echo
         echo "log:"
         git log -5 --oneline
-        echo 
+        echo
 
     else
         echo "you're currently not in a git repository"
@@ -55,6 +71,9 @@
 }
 
 function git_stats {
+    about 'display stats per author'
+    group 'git'
+
 # awesome work from https://github.com/esc/git-stats
 # including some modifications
 
diff --git a/plugins/available/hg.plugin.bash b/plugins/available/hg.plugin.bash
new file mode 100644
index 0000000..020b920
--- /dev/null
+++ b/plugins/available/hg.plugin.bash
@@ -0,0 +1,25 @@
+cite about-plugin
+about-plugin 'hg helper functions'
+
+hg_dirty() {
+    about 'displays dirty status of hg repository'
+    group 'hg'
+
+    hg status --no-color 2> /dev/null \
+    | awk '$1 == "?" { print "?" } $1 != "?" { print "!" }' \
+    | sort | uniq | head -c1
+}
+
+hg_in_repo() {
+    about 'determine if pwd is an hg repo'
+    group 'hg'
+
+    [[ `hg branch 2> /dev/null` ]] && echo 'on '
+}
+
+hg_branch() {
+    about 'display current hg branch'
+    group 'hg'
+
+    hg branch 2> /dev/null
+}
diff --git a/plugins/available/hg.plugins.bash b/plugins/available/hg.plugins.bash
deleted file mode 100644
index 33c81d3..0000000
--- a/plugins/available/hg.plugins.bash
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env bash
-hg_dirty() {
-    hg status --no-color 2> /dev/null \
-    | awk '$1 == "?" { print "?" } $1 != "?" { print "!" }' \
-    | sort | uniq | head -c1
-}
-
-hg_in_repo() {
-    [[ `hg branch 2> /dev/null` ]] && echo 'on '
-}
-
-hg_branch() {
-    hg branch 2> /dev/null
-}
diff --git a/plugins/available/javascript.plugins.bash b/plugins/available/javascript.plugin.bash
similarity index 67%
rename from plugins/available/javascript.plugins.bash
rename to plugins/available/javascript.plugin.bash
index 76a7e13..e8037b9 100644
--- a/plugins/available/javascript.plugins.bash
+++ b/plugins/available/javascript.plugin.bash
@@ -1,16 +1,22 @@
-#!/usr/bin/env bash
-#
-# The install directory is hard-coded. TOOD: allow the directory to be specified on the command line.
-#
+# The install directory is hard-coded. TODO: allow the directory to be specified on the command line.
+
+cite about-plugin
+about-plugin 'download jquery files into current project'
 
 [[ -z "$JQUERY_VERSION_NUMBER" ]] && JQUERY_VERSION_NUMBER="1.6.1"
 [[ -z "$JQUERY_UI_VERSION_NUMBER" ]] && JQUERY_UI_VERSION_NUMBER="1.8.13"
 
 function rails_jquery {
+  about 'download rails.js into public/javascripts'
+  group 'javascript'
+
   curl -o public/javascripts/rails.js http://github.com/rails/jquery-ujs/raw/master/src/rails.js
 }
 
 function jquery_install {
+  about 'download jquery.js into public/javascripts'
+  group 'javascript'
+
   if [ -z "$1" ]
   then
       version=$JQUERY_VERSION_NUMBER
@@ -21,6 +27,9 @@
 }
 
 function jquery_ui_install {
+  about 'download jquery_us.js into public/javascripts'
+  group 'javascript'
+
   if [ -z "$1" ]
   then
       version=$JQUERY_UI_VERSION_NUMBER
diff --git a/plugins/available/jekyll.plugins.bash b/plugins/available/jekyll.plugin.bash
similarity index 92%
rename from plugins/available/jekyll.plugins.bash
rename to plugins/available/jekyll.plugin.bash
index 74e3cb1..6254a87 100644
--- a/plugins/available/jekyll.plugins.bash
+++ b/plugins/available/jekyll.plugin.bash
@@ -1,6 +1,11 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'manage your jekyll site'
 
 editpost() {
+  about 'edit a post'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
@@ -35,11 +40,11 @@
     DATE=`echo $POST | grep -oE "[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}"`
     TITLE=`cat $POST | grep -oE "title: (.+)"`
     TITLE=`echo $TITLE | sed 's/title: //'`
-    echo "$COUNTER) 	$DATE	$TITLE" >> "$TMPFILE"	
+    echo "$COUNTER) 	$DATE	$TITLE" >> "$TMPFILE"
     POSTS[$COUNTER]=$POST
     COUNTER=`expr $COUNTER + 1`
   done
-  less $TMPFILE	
+  less $TMPFILE
   read -p "Number of post to edit: " POST_TO_EDIT
   if [ -z "$JEKYLL_EDITOR" ]
   then
@@ -50,6 +55,10 @@
 }
 
 newpost() {
+  about 'create a new post'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
@@ -93,7 +102,7 @@
   then
     select OPTION in $OPTIONS
     do
-      if [[ $OPTION = "Text" ]] 
+      if [[ $OPTION = "Text" ]]
       then
         POST_TYPE="Text"
         break
@@ -257,6 +266,10 @@
 }
 
 function testsite() {
+  about 'launches local jekyll server'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
@@ -285,6 +298,10 @@
 }
 
 function buildsite() {
+  about 'builds site'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
@@ -314,6 +331,10 @@
 }
 
 function deploysite() {
+  about 'rsyncs site to remote host'
+  param '1: site directory'
+  group 'jekyll'
+
   unset SITE
   if [ -z "$1" ]
   then
diff --git a/plugins/available/latex.plugin.bash b/plugins/available/latex.plugin.bash
index c2ce9dd..eefec59 100644
--- a/plugins/available/latex.plugin.bash
+++ b/plugins/available/latex.plugin.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'use mactex'
 
 # add mactex to the path if its present
 MACTEX_PATH=/usr/local/texlive/2009/bin/universal-darwin
diff --git a/plugins/available/nginx.plugins.bash b/plugins/available/nginx.plugin.bash
similarity index 81%
rename from plugins/available/nginx.plugins.bash
rename to plugins/available/nginx.plugin.bash
index 6dd86dd..660b503 100644
--- a/plugins/available/nginx.plugins.bash
+++ b/plugins/available/nginx.plugin.bash
@@ -1,6 +1,10 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'manage your nginx service'
 
 function nginx_reload() {
+  about 'reload your nginx config'
+  group 'nginx'
+
   FILE="${NGINX_PATH}/logs/nginx.pid"
   if [ -e $FILE ]; then
     echo "Reloading NGINX..."
@@ -13,6 +17,9 @@
 }
 
 function nginx_stop() {
+  about 'stop nginx'
+  group 'nginx'
+
   FILE="${NGINX_PATH}/logs/nginx.pid"
   if [ -e $FILE ]; then
     echo "Stopping NGINX..."
@@ -25,6 +32,9 @@
 }
 
 function nginx_start() {
+  about 'start nginx'
+  group 'nginx'
+
   FILE="${NGINX_PATH}/sbin/nginx"
   if [ -e $FILE ]; then
     echo "Starting NGINX..."
@@ -35,6 +45,9 @@
 }
 
 function nginx_restart() {
+  about 'restart nginx'
+  group 'nginx'
+
   FILE="${NGINX_PATH}/logs/nginx.pid"
   if [ -e $FILE ]; then
     echo "Stopping NGINX..."
diff --git a/plugins/available/nvm.plugin.bash b/plugins/available/nvm.plugin.bash
index 3f489dd..2e607d6 100644
--- a/plugins/available/nvm.plugin.bash
+++ b/plugins/available/nvm.plugin.bash
@@ -5,6 +5,9 @@
 # Implemented by Tim Caswell <tim@creationix.com>
 # with much bash help from Matthew Ranney
 
+cite about-plugin
+about-plugin 'node version manager, as a bash function'
+
 export NVM_DIR=$HOME/.nvm
 
 if [ ! -d "$NVM_DIR" ]; then
@@ -75,6 +78,10 @@
 
 nvm()
 {
+  about 'Node Version Manager'
+  param '1: command, see nvm help'
+  group 'nvm'
+
   if [ $# -lt 1 ]; then
     nvm help
     return
diff --git a/plugins/available/osx.plugin.bash b/plugins/available/osx.plugin.bash
index 7d3d077..fde9c83 100644
--- a/plugins/available/osx.plugin.bash
+++ b/plugins/available/osx.plugin.bash
@@ -1,6 +1,10 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'osx-specific functions'
 
 function tab() {
+  about 'opens a new terminal tab'
+  group 'osx'
+
   osascript 2>/dev/null <<EOF
     tell application "System Events"
       tell process "Terminal" to keystroke "t" using command down
@@ -15,6 +19,10 @@
 # this one switches your os x dock between 2d and 3d
 # thanks to savier.zwetschge.org
 function dock-switch() {
+    about 'switch dock between 2d and 3d'
+    param '1: "2d" or "3d"'
+    example '$ dock-switch 2d'
+    group 'osx'
 
     if [ $(uname) = "Darwin" ]; then
 
@@ -39,6 +47,10 @@
 # Download a file and open it in Preview
 
 function prevcurl() {
+  about 'download a file and open it in Preview'
+  param '1: url'
+  group 'osx'
+
   if [ ! $(uname) = "Darwin" ]
   then
     echo "This function only works with Mac OS X"
diff --git a/plugins/available/python.plugin.bash b/plugins/available/python.plugin.bash
index 6c87f8a..c705d5b 100644
--- a/plugins/available/python.plugin.bash
+++ b/plugins/available/python.plugin.bash
@@ -1,4 +1,5 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'alias "http" to SimpleHTTPServer'
 
 if [ $(uname) = "Linux" ]
 then
diff --git a/plugins/available/rbenv.plugin.bash b/plugins/available/rbenv.plugin.bash
index c45d714..70fe62c 100644
--- a/plugins/available/rbenv.plugin.bash
+++ b/plugins/available/rbenv.plugin.bash
@@ -1,8 +1,10 @@
-#!/usr/bin/env bash
-
 # Load rbebv, if you are using it
+
+cite about-plugin
+about-plugin 'load rbenv, if you are using it'
+
 export PATH="$HOME/.rbenv/bin:$PATH"
 [[ `which rbenv` ]] && eval "$(rbenv init -)"
 
 # Load the auto-completion script if rbenv was loaded.
-[[ -e ~/.rbenv/completions/rbenv.bash ]] && source ~/.rbenv/completions/rbenv.bash
\ No newline at end of file
+[[ -e ~/.rbenv/completions/rbenv.bash ]] && source ~/.rbenv/completions/rbenv.bash
diff --git a/plugins/available/ruby.plugin.bash b/plugins/available/ruby.plugin.bash
index 9ae61a4..f36bb15 100644
--- a/plugins/available/ruby.plugin.bash
+++ b/plugins/available/ruby.plugin.bash
@@ -1,4 +1,10 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'adds "remove_gem" function'
+
 function remove_gem {
+  about 'removes installed gem'
+  param '1: installed gem name'
+  group 'ruby'
+
   gem list | grep $1 | awk '{ print $1; }' | xargs sudo gem uninstall
 }
diff --git a/plugins/available/rvm.plugin.bash b/plugins/available/rvm.plugin.bash
index e0d18ba..6acad07 100644
--- a/plugins/available/rvm.plugin.bash
+++ b/plugins/available/rvm.plugin.bash
@@ -1,6 +1,8 @@
-#!/usr/bin/env bash
-
 # Load RVM, if you are using it
+
+cite about-plugin
+about-plugin 'load rvm, if you are using it'
+
 [[ -s $HOME/.rvm/scripts/rvm ]] && source $HOME/.rvm/scripts/rvm
 
 # Check to make sure that RVM is actually loaded before adding
diff --git a/plugins/available/ssh.plugin.bash b/plugins/available/ssh.plugin.bash
new file mode 100644
index 0000000..4e17206
--- /dev/null
+++ b/plugins/available/ssh.plugin.bash
@@ -0,0 +1,19 @@
+cite about-plugin
+about-plugin 'ssh helper functions'
+
+function add_ssh() {
+  about 'add entry to ssh config'
+  param '1: host'
+  param '2: hostname'
+  param '3: user'
+  group 'ssh'
+
+  echo -en "\n\nHost $1\n  HostName $2\n  User $3\n  ServerAliveInterval 30\n  ServerAliveCountMax 120" >> ~/.ssh/config
+}
+
+function sshlist() {
+  about 'list hosts defined in ssh config'
+  group 'ssh'
+
+  awk '$1 ~ /Host$/ { print $2 }' ~/.ssh/config
+}
diff --git a/plugins/available/ssh.plugins.bash b/plugins/available/ssh.plugins.bash
deleted file mode 100644
index 39718fb..0000000
--- a/plugins/available/ssh.plugins.bash
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-
-function add_ssh() {
-  echo -en "\n\nHost $1\n  HostName $2\n  User $3\n  ServerAliveInterval 30\n  ServerAliveCountMax 120" >> ~/.ssh/config
-}
-
-function sshlist() {
-  awk '$1 ~ /Host$/ { print $2 }' ~/.ssh/config
-}
diff --git a/plugins/available/subversion.plugin.bash b/plugins/available/subversion.plugin.bash
index a7fe941..bdb3edf 100644
--- a/plugins/available/subversion.plugin.bash
+++ b/plugins/available/subversion.plugin.bash
@@ -1,8 +1,21 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'svn helper functions'
+
 rm_svn(){
+  about 'remove ".svn" files from directory'
+  param '1: directory to search for files'
+  group 'svn'
+
+  if [ -z "$1" ]; then
+      reference rm_svn
+      return
+  fi
   find $1 -name .svn -print0 | xargs -0 rm -rf
 }
 
 svn_add(){
-	svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add
+    about 'add to svn repo'
+    group 'svn'
+
+    svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add
 }
diff --git a/plugins/available/tmux.plugin.bash b/plugins/available/tmux.plugin.bash
index ff35336..f7ee337 100644
--- a/plugins/available/tmux.plugin.bash
+++ b/plugins/available/tmux.plugin.bash
@@ -1,2 +1,6 @@
 # make sure that tmux is launched in 256 color mode
+
+cite about-plugin
+about-plugin 'make sure that tmux is launched in 256 color mode'
+
 alias tmux="TERM=xterm-256color tmux"
diff --git a/plugins/available/tmuxinator.plugin.bash b/plugins/available/tmuxinator.plugin.bash
index cf5500d..a011435 100644
--- a/plugins/available/tmuxinator.plugin.bash
+++ b/plugins/available/tmuxinator.plugin.bash
@@ -1,3 +1,4 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'sources tmuxinator script if available'
 
 [[ -s $HOME/.tmuxinator/scripts/tmuxinator ]] && . $HOME/.tmuxinator/scripts/tmuxinator
diff --git a/plugins/available/vagrant.plugins.bash b/plugins/available/vagrant.plugin.bash
similarity index 94%
rename from plugins/available/vagrant.plugins.bash
rename to plugins/available/vagrant.plugin.bash
index 3131744..aa223e7 100644
--- a/plugins/available/vagrant.plugins.bash
+++ b/plugins/available/vagrant.plugin.bash
@@ -1,4 +1,6 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'set up vagrant autocompletion'
+
 _vagrant()
 {
     cur="${COMP_WORDS[COMP_CWORD]}"
diff --git a/plugins/available/virtualenv.plugin.bash b/plugins/available/virtualenv.plugin.bash
index ba27c76..302e19d 100644
--- a/plugins/available/virtualenv.plugin.bash
+++ b/plugins/available/virtualenv.plugin.bash
@@ -1,19 +1,30 @@
-#!/usr/bin/env bash
-
 # make sure virtualenvwrapper is enabled if available
+
+cite about-plugin
+about-plugin 'virtualenvwrapper helper functions'
+
 [[ `which virtualenvwrapper.sh` ]] && . virtualenvwrapper.sh
 
-# create a new virtualenv for this directory
+
 function mkvenv {
+  about 'create a new virtualenv for this directory'
+  group 'virtualenv'
+
   cwd=`basename \`pwd\``
   mkvirtualenv --no-site-packages --distribute $cwd
 }
 
-# create a new virtualenv for the branch you're currently in
+
 function mkvbranch {
+  about 'create a new virtualenv for the current branch'
+  group 'virtualenv'
+
   mkvirtualenv --no-site-packages --distribute "$(basename `pwd`)@$(git_prompt_info)"
 }
 
 function wovbranch {
+  about 'sets workon branch'
+  group 'virtualenv'
+
   workon "$(basename `pwd`)@$(git_prompt_info)"
 }
diff --git a/plugins/available/z.bash b/plugins/available/z.plugin.bash
similarity index 91%
rename from plugins/available/z.bash
rename to plugins/available/z.plugin.bash
index ef3e3d9..6653575 100644
--- a/plugins/available/z.bash
+++ b/plugins/available/z.plugin.bash
@@ -1,7 +1,7 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'maintains a jump-list of the directories you actually use'
+about-plugin '                              z is DEPRECATED, use fasd instead'
 
-# maintains a jump-list of the directories you actually use
-#
 # INSTALL:
 #   * put something like this in your .bashrc:
 #     . /path/to/z.sh
@@ -15,6 +15,11 @@
 #   * z -t foo  # goes to most recently accessed dir matching foo
 #   * z -l foo  # list all dirs matching foo (by frecency)
 
+if [ -e $BASH_IT/plugins/enabled/fasd.plugin.bash ]; then
+    printf '%s\n' 'sorry, the z plugin is incompatible with the fasd plugin. you may use either, but not both.'
+    return
+fi
+
 z() {
  local datafile="$HOME/.z"
  if [ "$1" = "--add" ]; then
diff --git a/plugins/available/z_autoenv.plugins.bash b/plugins/available/z_autoenv.plugin.bash
similarity index 87%
rename from plugins/available/z_autoenv.plugins.bash
rename to plugins/available/z_autoenv.plugin.bash
index 04efa85..dd1aec1 100644
--- a/plugins/available/z_autoenv.plugins.bash
+++ b/plugins/available/z_autoenv.plugin.bash
@@ -1,4 +1,6 @@
-#!/usr/bin/env bash
+cite about-plugin
+about-plugin 'source into environment when cding to directories'
+
 if [[ -n "${ZSH_VERSION}" ]]
 then __array_offset=0
 else __array_offset=1