diff --git a/shakers.cabal b/shakers.cabal
--- a/shakers.cabal
+++ b/shakers.cabal
@@ -1,5 +1,5 @@
 name:                  shakers
-version:               0.0.2
+version:               0.0.3
 synopsis:              Shake helpers.
 description:           Primitive helpers for Shake files.
 homepage:              https://github.com/swift-nav/shakers
diff --git a/src/Development/Shakers.hs b/src/Development/Shakers.hs
--- a/src/Development/Shakers.hs
+++ b/src/Development/Shakers.hs
@@ -7,23 +7,32 @@
   ( module Exports
   , fakeFile
   , metaFile
+  , mirrorDir
   , cmdArgs
   , cmdArgs_
+  , cmdArgsDir
+  , cmdArgsDir_
   , stack
+  , stackExec
   , git
   , m4
+  , touchFile
+  , copyFileChanged'
   , fake
   , fake'
   , meta
   , preprocess
+  , mirror
   , stackRules
   , cabalRules
   , shakeMain
   ) where
 
-import BasicPrelude      as Exports hiding ((*>))
+import BasicPrelude               as Exports hiding ((*>))
 import Data.Char
-import Development.Shake as Exports
+import Development.Shake          as Exports
+import Development.Shake.FilePath
+import System.Directory
 
 -- | File used for version change detection.
 --
@@ -60,6 +69,11 @@
 stackDir :: FilePath
 stackDir = ".stack-work"
 
+-- | Mirror directory of current parent directory.
+--
+mirrorDir :: Action FilePath
+mirrorDir = liftIO $ (buildDir </>) . takeFileName <$> getCurrentDirectory
+
 -- | Remove right excess on string.
 --
 rstrip :: String -> String
@@ -75,11 +89,26 @@
 cmdArgs_ :: String -> [String] -> Action ()
 cmdArgs_ c as = unit $ cmd c as
 
+-- | Run commands in a dir with return string.
+--
+cmdArgsDir :: FilePath -> String -> [String] -> Action String
+cmdArgsDir d c as = rstrip . fromStdout <$> cmd (Cwd d) c as
+
+-- | Run commands in a dir with no return.
+--
+cmdArgsDir_ :: FilePath -> String -> [String] -> Action ()
+cmdArgsDir_ d c as = unit $ cmd (Cwd d) c as
+
 -- | Stack command.
 --
 stack :: [String] -> Action ()
 stack = cmdArgs_ "stack"
 
+-- | Stack exec command.
+--
+stackExec :: String -> [String] -> Action ()
+stackExec cmd' args = stack $ "exec" : cmd' : "--" : args
+
 -- | Sylish command.
 --
 stylish :: [String] -> Action ()
@@ -110,6 +139,13 @@
 touchFile :: FilePath -> Action ()
 touchFile = flip writeFile' mempty
 
+-- | Copy a file if changed, creating parent directories.
+--
+copyFileChanged' :: FilePath -> FilePath -> Action ()
+copyFileChanged' a b = do
+  liftIO $ createDirectoryIfMissing True $ dropFileName b
+  copyFileChanged a b
+
 -- | Use a fake file to keep track of the last time an file-free action ran.
 --
 fake :: [FilePattern] -> String -> ([FilePath] -> Action ()) -> Rules ()
@@ -150,6 +186,14 @@
     content <- m4 $ template : (uncurry f <$> macros')
     writeFileChanged out content
 
+-- | Mirror files to the mirror directory.
+--
+mirror :: [FilePattern] -> Rules ()
+mirror pats =
+  fake' pats "mirror" $ mapM_ $ \file -> do
+    dir <- mirrorDir
+    copyFileChanged' file $ dir </> file
+
 -- | Haskell source rules
 --
 hsRules :: Rules ()
@@ -167,7 +211,7 @@
   fake' pats "lint" $ \files ->
     lint files
 
--- | Build-in rules.
+-- | Built-in rules.
 --
 shakeRules :: Rules ()
 shakeRules = do
