diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 Currently, the best way to install is
 
 ```
-cabal new-install atspkg --symlink-bindor ~/.local/bin
+cabal new-install ats-pkg --symlink-bindir ~/.local/bin
 ```
 
 Make sure that `~/.local/bin` is on your `PATH`.
diff --git a/ats-pkg.cabal b/ats-pkg.cabal
--- a/ats-pkg.cabal
+++ b/ats-pkg.cabal
@@ -1,5 +1,5 @@
 name:                ats-pkg
-version:             0.1.0.0
+version:             0.2.0.0
 synopsis:            Package manager for ATS
 description:         A collection of scripts to make building ATS projects easy.
 homepage:            https://github.com/vmchale/ats-pkg#readme
@@ -42,7 +42,9 @@
                      , process
                      , unix
                      , dhall
-                     , shake-ext >= 0.4.0.1
+                     , mtl
+                     , shake-ext >= 0.4.1.0
+                     , composition-prelude >= 1.1.0.1
   default-language:    Haskell2010
   if flag(development)
     ghc-options: -Werror
@@ -51,7 +53,7 @@
 executable atspkg
   hs-source-dirs:      app
   main-is:             Main.hs
-  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -with-rtsopts=-I0
   build-depends:       base
                      , ats-pkg
   default-language:    Haskell2010
@@ -69,16 +71,6 @@
   ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates -Wcompat
   default-language:    Haskell2010
 
-benchmark ats-pkg-bench
-  type:                exitcode-stdio-1.0
-  hs-source-dirs:      bench
-  main-is:             Bench.hs
-  build-depends:       base
-                     , ats-pkg
-                     , criterion
-  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -O3 -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates -Wcompat
-  default-language:    Haskell2010
-
 source-repository head
   type:     git
-  location: https://github.com/vmchale/ats-pkg
+  location: git@github.com:vmchale/atspkg.git
diff --git a/bench/Bench.hs b/bench/Bench.hs
deleted file mode 100644
--- a/bench/Bench.hs
+++ /dev/null
@@ -1,9 +0,0 @@
-module Main where
-
-import Criterion.Main
-
-main :: IO ()
-main =
-    defaultMain [ bgroup "head"
-                      [ bench "head" $ whnf head [1..] ]
-                ]
diff --git a/src/Language/ATS/Package/Type.hs b/src/Language/ATS/Package/Type.hs
--- a/src/Language/ATS/Package/Type.hs
+++ b/src/Language/ATS/Package/Type.hs
@@ -7,29 +7,93 @@
                                  , printConfig
                                  , pkgToAction
                                  , mkPkg
+                                 , mkManpage
                                  ) where
 
-import qualified Data.Text.Lazy        as TL
+import           Control.Composition
+import           Control.Monad.IO.Class     (MonadIO)
+import           Data.Maybe                 (fromMaybe)
+import           Data.Semigroup             (Semigroup (..))
+import qualified Data.Text.Lazy             as TL
 import           Development.Shake
 import           Development.Shake.ATS
+import           Development.Shake.FilePath
+import           Development.Shake.Man
 import           Dhall
 
+options :: ShakeOptions
+options = shakeOptions { shakeFiles = ".atspkg"
+                       , shakeThreads = 4
+                       }
+
 mkPkg :: IO ()
-mkPkg = shake shakeOptions
-    (pkgToAction =<< liftIO (input auto "./atspkg.dhall"))
+mkPkg = shakeArgs options $
+    mkTest >>
+    mkClean >>
+    mkManpage >>
+    mkInstall >>
+    (pkgToAction =<< getConfig)
 
+mkManpage :: Rules ()
+mkManpage = do
+    c <- getConfig
+    case man c of
+        Just _ -> manpages
+        _      -> pure ()
+
+getConfig :: MonadIO m => m Pkg
+getConfig = liftIO (input auto "./atspkg.dhall")
+
+asTarget :: Text -> FilePath
+asTarget m = TL.unpack m -<.> "1"
+
+mkInstall :: Rules ()
+mkInstall =
+    "install" ~> do
+        config <- getConfig
+        bins <- fmap (TL.unpack . target) . bin <$> getConfig
+        need bins
+        home <- fromMaybe "" <$> getEnv "HOME"
+        let binDest = fmap (((home <> "/.local/bin/") <>) . takeBaseName) bins
+        void $ zipWithM copyFile' bins binDest
+        case man config of
+            Just mt -> do
+                let mt' = asTarget mt
+                    manDest = (home <> "/.local/share/man/man1/") <> mt'
+                need [mt']
+                copyFile' mt' manDest
+            Nothing -> pure ()
+
+mkClean :: Rules ()
+mkClean =
+    "clean" ~> do
+    removeFilesAfter "." ["//*.1","//*.c", "tags"]
+    removeFilesAfter ".shake" ["//*"]
+    removeFilesAfter "target" ["//*"]
+
+mkTest :: Rules ()
+mkTest =
+    "test" ~> do
+        config <- getConfig
+        let tests = fmap (TL.unpack . target) . test $ config
+        need tests
+        mapM_ cmd_ tests
+
 -- TODO need @atspkg.dhall@
 pkgToAction :: Pkg -> Rules ()
-pkgToAction (Pkg bs ts) =
-    mapM_ g (bs ++ ts) >>
-    want (TL.unpack . target <$> bs)
+pkgToAction (Pkg bs ts mt) = do
+    mapM_ g (bs ++ ts)
+    let bins = TL.unpack . target <$> bs
+    case mt of
+        (Just m) -> want (asTarget m : bins)
+        Nothing  -> want bins
 
     where g (Bin s t ls) = atsBin (TL.unpack <$> ls) (TL.unpack s) (TL.unpack t)
 
 data Bin = Bin { src :: Text, target :: Text, libs :: [Text] }
     deriving (Show, Eq, Generic, Interpret)
 
-data Pkg = Pkg { bin :: [Bin], test :: [Bin] }
+data Pkg = Pkg { bin :: [Bin], test :: [Bin], man :: Maybe Text }
     deriving (Show, Eq, Generic, Interpret)
 
 printConfig :: IO ()
