diff --git a/.gitignore b/.gitignore
index d6e0d3e60c264fe0c8fd87264656ee8505c3d13a..828fb04e7d07648c6bed803ec23aa73216c8bb22 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
 .idea
 /etc/spackconfig
 /share/spack/dotkit
+/share/spack/modules
diff --git a/lib/spack/spack/cmd/module.py b/lib/spack/spack/cmd/module.py
index ead3b9abac8e026845dce5b444d0b011936a50f7..af5a68923b23189b2a0028aec6b60a7190b0a8ed 100644
--- a/lib/spack/spack/cmd/module.py
+++ b/lib/spack/spack/cmd/module.py
@@ -32,18 +32,13 @@
 from llnl.util.filesystem import mkdirp
 
 import spack.cmd
-import spack.modules
+from spack.modules import module_types
 from spack.util.string import *
 
 from spack.spec import Spec
 
 description ="Manipulate modules and dotkits."
 
-module_types = {
-    'dotkit' : spack.modules.Dotkit,
-    'tcl'    : spack.modules.TclModule
-}
-
 
 def setup_parser(subparser):
     sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='module_command')
@@ -87,13 +82,19 @@ def module_find(mtype, spec_array):
 
 
 def module_refresh():
-    shutil.rmtree(spack.dotkit_path, ignore_errors=False)
-    mkdirp(spack.dotkit_path)
+    """Regenerate all module files for installed packages known to
+       spack (some packages may no longer exist)."""
+    specs = [s for s in spack.db.installed_known_package_specs()]
+
+    for name, cls in module_types.items():
+        tty.msg("Regenerating %s module files." % name)
+        if os.path.isdir(cls.path):
+            shutil.rmtree(cls.path, ignore_errors=False)
+        mkdirp(cls.path)
+        for spec in specs:
+            tty.debug("   Writing file for %s." % spec)
+            cls(spec.package).write()
 
-    specs = spack.db.installed_package_specs()
-    for spec in specs:
-        for mt in module_types:
-            mt(spec.package).write()
 
 
 def module(parser, args):
diff --git a/lib/spack/spack/modules.py b/lib/spack/spack/modules.py
index 0f8c417793224c68b81639eeed0e3c72bf150bca..596308f801dc4fdbaf1637c3435b2c8738f007b1 100644
--- a/lib/spack/spack/modules.py
+++ b/lib/spack/spack/modules.py
@@ -56,8 +56,10 @@
 
 import spack
 
-dotkit_path  = join_path(spack.share_path, "dotkit")
-tcl_mod_path = join_path(spack.share_path, "modules")
+"""Registry of all types of modules.  Entries created by EnvModule's
+   metaclass."""
+module_types = {}
+
 
 def print_help():
     """For use by commands to tell user how to activate shell support."""
@@ -77,6 +79,15 @@ def print_help():
 
 
 class EnvModule(object):
+    name = 'env_module'
+
+    class __metaclass__(type):
+        def __init__(cls, name, bases, dict):
+            type.__init__(cls, name, bases, dict)
+            if cls.name != 'env_module':
+                module_types[cls.name] = cls
+
+
     def __init__(self, pkg=None):
         # category in the modules system
         # TODO: come up with smarter category names.
@@ -97,7 +108,7 @@ def paths(self):
         if self._paths is None:
             self._paths = {}
 
-            def add_path(self, path_name, directory):
+            def add_path(path_name, directory):
                 path = self._paths.setdefault(path_name, [])
                 path.append(directory)
 
@@ -126,14 +137,14 @@ def add_path(self, path_name, directory):
     def write(self):
         """Write out a module file for this object."""
         module_dir = os.path.dirname(self.file_name)
-        if not os.path.exists():
+        if not os.path.exists(module_dir):
             mkdirp(module_dir)
 
         # If there are no paths, no need for a dotkit.
         if not self.paths:
             return
 
-        with closing(open(self.file_name)) as f:
+        with closing(open(self.file_name, 'w')) as f:
             self._write(f)
 
 
@@ -156,10 +167,13 @@ def remove(self):
 
 
 class Dotkit(EnvModule):
+    name = 'dotkit'
+    path = join_path(spack.share_path, "dotkit")
+
     @property
     def file_name(self):
         spec = self.pkg.spec
-        return join_path(dotkit_path, spec.architecture,
+        return join_path(Dotkit.path, spec.architecture,
                          spec.format('$_$@$%@$+$#.dk'))
 
 
@@ -183,14 +197,17 @@ def _write(self, dk_file):
                 dk_file.write("dk_alter %s %s\n" % (var, directory))
 
         # Let CMake find this package.
-        dk_file.write("dk_alter CMAKE_PREFIX_PATH %s\n" % pkg.prefix)
+        dk_file.write("dk_alter CMAKE_PREFIX_PATH %s\n" % self.pkg.prefix)
 
 
 class TclModule(EnvModule):
+    name = 'tcl'
+    path = join_path(spack.share_path, "modules")
+
     @property
     def file_name(self):
         spec = self.pkg.spec
-        return join_path(tcl_mod_path, spec.architecture,
+        return join_path(TclModule.path, spec.architecture,
                          spec.format('$_$@$%@$+$#'))
 
 
@@ -214,4 +231,4 @@ def _write(self, m_file):
             for directory in dirs:
                 m_file.write("prepend-path %s \"%s\"\n" % (var, directory))
 
-        m_file.write("prepend-path CMAKE_PREFIX_PATH \"%s\"\n" % pkg.prefix)
+        m_file.write("prepend-path CMAKE_PREFIX_PATH \"%s\"\n" % self.pkg.prefix)
diff --git a/lib/spack/spack/packages.py b/lib/spack/spack/packages.py
index 00834c95d549c970a787517fe42bd8ba20ec36a6..f9840a5c057be99601734443d7752908d84f11f4 100644
--- a/lib/spack/spack/packages.py
+++ b/lib/spack/spack/packages.py
@@ -118,6 +118,16 @@ def installed_package_specs(self):
         return spack.install_layout.all_specs()
 
 
+    def installed_known_package_specs(self):
+        """Read installed package names straight from the install
+           directory layout, but return only specs for which the
+           package is known to this version of spack.
+        """
+        for spec in spack.install_layout.all_specs():
+            if self.exists(spec.name):
+                yield spec
+
+
     @memoized
     def all_package_names(self):
         """Generator function for all packages.  This looks for