diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.markdown
@@ -0,0 +1,7 @@
+# Change log
+
+Autoexporter uses [Semantic Versioning][].
+The change log is available through the [releases on GitHub][].
+
+[Semantic Versioning]: http://semver.org/spec/v2.0.0.html
+[releases on GitHub]: https://github.com/tfausak/autoexporter/releases
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Change log
-
-Autoexporter uses [Semantic Versioning][].
-The change log is available through the [releases on GitHub][].
-
-[Semantic Versioning]: http://semver.org/spec/v2.0.0.html
-[releases on GitHub]: https://github.com/tfausak/autoexporter/releases
diff --git a/LICENSE.markdown b/LICENSE.markdown
new file mode 100644
--- /dev/null
+++ b/LICENSE.markdown
@@ -0,0 +1,23 @@
+[The MIT License (MIT)][]
+
+Copyright (c) 2016 Taylor Fausak
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+[The MIT License (MIT)]: https://opensource.org/licenses/MIT
diff --git a/LICENSE.md b/LICENSE.md
deleted file mode 100644
--- a/LICENSE.md
+++ /dev/null
@@ -1,23 +0,0 @@
-[The MIT License (MIT)][]
-
-Copyright (c) 2016 Taylor Fausak
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-[The MIT License (MIT)]: https://opensource.org/licenses/MIT
diff --git a/README.markdown b/README.markdown
new file mode 100644
--- /dev/null
+++ b/README.markdown
@@ -0,0 +1,66 @@
+# [Autoexporter][]
+
+[![Version badge][]][version]
+[![Build badge][]][build]
+
+Autoexporter automatically re-exports Haskell modules.
+
+Let's say you have a module `M` that just exports some other modules. It might
+look like this:
+
+``` haskell
+module M
+  ( module M.A
+  , module M.B
+  ) where
+
+import M.A
+import M.B
+```
+
+This code is error-prone. If you add a new module, say `M.C`, you have to
+remember to come back to this file and re-export it. And this code is tedious
+to write. You have to list each module twice. You can do a little better, but
+not much.
+
+``` haskell
+module M (module X) where
+import M.A as X
+import M.B as X
+```
+
+Now you don't have to write every module twice, but you still have to remember
+to re-export everything. And the generated documentation for this module
+doesn't include anything about the exported modules.
+
+Autoexporter handles this for you. Instead of either of the above approaches,
+simply drop this into the `M` module:
+
+``` haskell
+{-# OPTIONS_GHC -F -pgmF autoexporter #-}
+```
+
+That will generate code that looks like this:
+
+``` haskell
+module M (
+  module M.A,
+  module M.B,
+) where
+import M.A
+import M.B
+```
+
+Autoexporter will generally behave as you'd expect, but there are a couple
+things to look out for:
+
+- Only immediate children will be re-exported. If you use this in some module
+  `M`, it won't pull in `M.A.B`.
+
+- You cannot selectively include or exclude any modules.
+
+[Autoexporter]: https://github.com/tfausak/autoexporter
+[Version badge]: https://www.stackage.org/package/autoexporter/badge/nightly?label=version
+[version]: https://www.stackage.org/nightly/package/autoexporter
+[Build badge]: https://travis-ci.org/tfausak/autoexporter.svg?branch=master
+[build]: https://travis-ci.org/tfausak/autoexporter
diff --git a/README.md b/README.md
deleted file mode 100644
--- a/README.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# [Autoexporter][]
-
-Autoexporter automatically re-exports Haskell modules.
-
-[![Version badge][]][version]
-[![Build badge][]][build]
-
--   [Install](#install)
--   [Use](#use)
-
-## Install
-
-1.  Install [Stack][].
-
-2.  `stack install autoexporter`
-
-## Use
-
-Let's say you have a module `M` that just exports some other modules. It might
-look like this:
-
-``` haskell
-module M
-    ( module M.A
-    , module M.B
-    ) where
-
-import M.A
-import M.B
-```
-
-This code is error-prone. If you add a new module, say `M.C`, you have to
-remember to come back to this file and re-export it. And this code is tedious
-to write. You have to list each module twice. You can do a little better, but
-not much.
-
-``` haskell
-module M (module X) where
-import M.A as X
-import M.B as X
-```
-
-Now you don't have to write every module twice, but you still have to remember
-to re-export everything. And the generated documentation for this module
-doesn't include anything about the exported modules.
-
-Autoexporter handles this for you. Instead of either of the above approaches,
-simply drop this into the `M` module:
-
-``` haskell
-{-# OPTIONS_GHC -F -pgmF autoexporter #-}
-```
-
-That will generate code like the first example. A couple caveats:
-
--   Only immediate children will be re-exported. If you use this in some module
-    `M`, it won't pull in `M.A.B`.
-
--   You cannot selectively leave out any modules. You also cannot selectively
-    exclude any imports from any of the modules.
-
-    -   This could be allowed via `-optF`. I will happily accept patches for
-        this as well.
-
-[Autoexporter]: https://github.com/tfausak/autoexporter
-[Version badge]: https://www.stackage.org/package/autoexporter/badge/nightly?label=version
-[version]: https://www.stackage.org/package/autoexporter
-[Build badge]: https://travis-ci.org/tfausak/autoexporter.svg?branch=master
-[build]: https://travis-ci.org/tfausak/autoexporter
-[Stack]: http://haskellstack.org
diff --git a/autoexporter.cabal b/autoexporter.cabal
--- a/autoexporter.cabal
+++ b/autoexporter.cabal
@@ -3,7 +3,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           autoexporter
-version:        0.2.3
+version:        1.0.0
 synopsis:       Automatically re-export modules.
 description:    Autoexporter automatically re-exports modules.
 category:       Utility
@@ -11,14 +11,14 @@
 bug-reports:    https://github.com/tfausak/autoexporter/issues
 maintainer:     Taylor Fausak
 license:        MIT
+license-file:   LICENSE.markdown
 build-type:     Simple
 cabal-version:  >= 1.10
 
 extra-source-files:
-    CHANGELOG.md
-    LICENSE.md
+    CHANGELOG.markdown
     package.yaml
-    README.md
+    README.markdown
     stack.yaml
 
 source-repository head
@@ -27,7 +27,7 @@
 
 library
   hs-source-dirs:
-      library
+      source/library
   ghc-options: -Wall
   build-depends:
       base >=4.7 && <4.10
@@ -41,7 +41,7 @@
 executable autoexporter
   main-is: Main.hs
   hs-source-dirs:
-      executable
+      source/executable
   ghc-options: -Wall
   build-depends:
       base
diff --git a/executable/Main.hs b/executable/Main.hs
deleted file mode 100644
--- a/executable/Main.hs
+++ /dev/null
@@ -1,3 +0,0 @@
-module Main (module Autoexporter) where
-
-import Autoexporter (main)
diff --git a/library/Autoexporter.hs b/library/Autoexporter.hs
deleted file mode 100644
--- a/library/Autoexporter.hs
+++ /dev/null
@@ -1,41 +0,0 @@
-module Autoexporter (main) where
-
-import qualified Data.List as List
-import qualified Data.Maybe as Maybe
-import qualified Distribution.ModuleName as ModuleName
-import qualified Distribution.Text as Text
-import qualified System.Directory as Directory
-import qualified System.Environment as Environment
-import qualified System.FilePath as FilePath
-
-main :: IO ()
-main = do
-    args <- Environment.getArgs
-    case args of
-        [name, inputFile, outputFile] -> do
-            let moduleName = makeModuleName name
-
-            let directory = FilePath.dropExtension inputFile
-            files <- Directory.getDirectoryContents directory
-            let haskellFiles = filter (\ f -> List.isSuffixOf ".hs" f || List.isSuffixOf ".lhs" f) files
-            let paths = map (directory FilePath.</>) haskellFiles
-            let modules = List.sort (map makeModuleName paths)
-            let exports = init (unlines (map (\ x -> "    module " ++ x ++ ",") modules))
-            let imports = init (unlines (map ("import " ++) modules))
-
-            let output = unlines
-                    [ unwords ["module", moduleName, "("]
-                    , exports
-                    , ") where"
-                    , ""
-                    , imports
-                    ]
-            writeFile outputFile output
-        _ -> error ("unexpected arguments: " ++ show args)
-
-makeModuleName :: FilePath -> String
-makeModuleName name =
-    let path = FilePath.dropExtension name
-        parts = FilePath.splitDirectories path
-        rest = reverse (takeWhile (\ x -> Maybe.isJust (Text.simpleParse x :: Maybe ModuleName.ModuleName)) (reverse parts))
-    in  List.intercalate "." rest
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -6,12 +6,11 @@
     - base
     - autoexporter
     main: Main.hs
-    source-dirs: executable
+    source-dirs: source/executable
 extra-source-files:
-- CHANGELOG.md
-- LICENSE.md
+- CHANGELOG.markdown
 - package.yaml
-- README.md
+- README.markdown
 - stack.yaml
 ghc-options:
 - -Wall
@@ -22,9 +21,10 @@
   - Cabal >=1.22 && <1.25
   - directory >=1.2 && <1.4
   - filepath >=1.3 && <1.5
-  source-dirs: library
+  source-dirs: source/library
 license: MIT
+license-file: LICENSE.markdown
 maintainer: Taylor Fausak
 name: autoexporter
 synopsis: Automatically re-export modules.
-version: '0.2.3'
+version: '1.0.0'
diff --git a/source/executable/Main.hs b/source/executable/Main.hs
new file mode 100644
--- /dev/null
+++ b/source/executable/Main.hs
@@ -0,0 +1,5 @@
+module Main
+  ( module Autoexporter
+  ) where
+
+import Autoexporter (main)
diff --git a/source/library/Autoexporter.hs b/source/library/Autoexporter.hs
new file mode 100644
--- /dev/null
+++ b/source/library/Autoexporter.hs
@@ -0,0 +1,72 @@
+module Autoexporter where
+
+import qualified Data.List as List
+import qualified Data.Maybe as Maybe
+import qualified Distribution.ModuleName as ModuleName
+import qualified Distribution.Text as Text
+import qualified System.Directory as Directory
+import qualified System.Environment as Environment
+import qualified System.FilePath as FilePath
+
+main :: IO ()
+main = do
+  args <- Environment.getArgs
+  mainWithArgs args
+
+mainWithArgs :: [String] -> IO ()
+mainWithArgs args =
+  case args of
+    [name, inputFile, outputFile] -> autoexport name inputFile outputFile
+    _ -> fail ("unexpected arguments: " ++ show args)
+
+autoexport :: String -> FilePath -> FilePath -> IO ()
+autoexport name inputFile outputFile = do
+  let moduleName = makeModuleName name
+  let directory = FilePath.dropExtension inputFile
+  files <- Directory.getDirectoryContents directory
+  let output = makeOutput moduleName directory files
+  writeFile outputFile output
+
+makeModuleName :: FilePath -> String
+makeModuleName name =
+  let path = FilePath.dropExtension name
+      parts = FilePath.splitDirectories path
+      rest = takeWhileEnd isModuleName parts
+  in List.intercalate "." rest
+
+takeWhileEnd :: (a -> Bool) -> [a] -> [a]
+takeWhileEnd p xs = reverse (takeWhile p (reverse xs))
+
+isModuleName :: String -> Bool
+isModuleName x = Maybe.isJust (parseModuleName x)
+
+parseModuleName :: String -> Maybe ModuleName.ModuleName
+parseModuleName = Text.simpleParse
+
+makeOutput :: String -> FilePath -> [FilePath] -> String
+makeOutput moduleName directory files =
+  let haskellFiles = filter isHaskellFile files
+      paths = map (directory FilePath.</>) haskellFiles
+      modules = List.sort (map makeModuleName paths)
+  in renderModule moduleName modules
+
+isHaskellFile :: FilePath -> Bool
+isHaskellFile x = List.isSuffixOf ".hs" x || List.isSuffixOf ".lhs" x
+
+renderModule :: String -> [String] -> String
+renderModule name modules =
+  unlines'
+    [ unwords ["module", name, "("]
+    , unlines' (map renderExport modules)
+    , ") where"
+    , unlines' (map renderImport modules)
+    ]
+
+unlines' :: [String] -> String
+unlines' = List.intercalate "\n"
+
+renderExport :: String -> String
+renderExport x = "  module " ++ x ++ ","
+
+renderImport :: String -> String
+renderImport x = "import " ++ x
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,3 +1,1 @@
-install-ghc: true
-require-stack-version: ! '>=1.0.4'
-resolver: lts-5.12
+resolver: lts-7.14
