diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,68 @@
 # Changelog
 
+## 1.0.0
+
+Release notes:
+
+*  We're calling this version 1.0.0 in preparation for Stackage
+   LTS 4.  Note, however, that this does not mean the code's API
+   will be stable as this is primarily an end-user tool.
+
+Enhancements:
+
+* Added flag `--profile` flag: passed with `stack build`, it will
+  enable profiling, and for `--bench` and `--test` it will generate a
+  profiling report by passing `+RTS -p` to the executable(s). Great
+  for using like `stack build --bench --profile` (remember that
+  enabling profile will slow down your benchmarks by >4x). Run `stack
+  build --bench` again to disable the profiling and get proper speeds
+* Added flag `--trace` flag: just like `--profile`, it enables
+  profiling, but instead of generating a report for `--bench` and
+  `--test`, prints out a stack trace on exception. Great for using
+  like `stack build --test --trace`
+* Nix: all options can be overriden on command line
+  [#1483](https://github.com/commercialhaskell/stack/issues/1483)
+* Nix: build environments (shells) are now pure by default.
+* Make verbosity silent by default in script interpreter mode
+  [#1472](https://github.com/commercialhaskell/stack/issues/1472)
+* Show a message when resetting git commit fails
+  [#1453](https://github.com/commercialhaskell/stack/issues/1453)
+* Improve Unicode handling in project/package names
+  [#1337](https://github.com/commercialhaskell/stack/issues/1337)
+* Fix ambiguity between a stack command and a filename to execute (prefer
+  `stack` subcommands)
+  [#1471](https://github.com/commercialhaskell/stack/issues/1471)
+* Support multi line interpreter directive comments
+  [#1394](https://github.com/commercialhaskell/stack/issues/1394)
+* Handle space separated pids in ghc-pkg dump (for GHC HEAD)
+  [#1509](https://github.com/commercialhaskell/stack/issues/1509)
+* Add ghci --no-package-hiding option
+  [#1517](https://github.com/commercialhaskell/stack/issues/1517)
+* `stack new` can download templates from URL
+  [#1466](https://github.com/commercialhaskell/stack/issues/1466)
+
+Bug fixes:
+
+* Nix: stack exec options are passed properly to the stack sub process
+  [#1538](https://github.com/commercialhaskell/stack/issues/1538)
+* Nix: specifying a shell-file works in any current working directory
+  [#1547](https://github.com/commercialhaskell/stack/issues/1547)
+* Nix: use `--resolver` argument
+* Docker: fix missing image message and '--docker-auto-pull'
+* No HTML escaping for "stack new" template params
+  [#1475](https://github.com/commercialhaskell/stack/issues/1475)
+* Set permissions for generated .ghci script
+  [#1480](https://github.com/commercialhaskell/stack/issues/1480)
+* Restrict commands allowed in interpreter mode
+  [#1504](https://github.com/commercialhaskell/stack/issues/1504)
+* stack ghci doesn't see preprocessed files for executables
+  [#1347](https://github.com/commercialhaskell/stack/issues/1347)
+* All test suites run even when only one is requested
+  [#1550](https://github.com/commercialhaskell/stack/pull/1550)
+* Edge cases in broken templates give odd errors
+  [#1535](https://github.com/commercialhaskell/stack/issues/1535)
+* Fix test coverage bug on windows
+
 ## 0.1.10.1
 
 Bug fixes:
diff --git a/src/Data/Attoparsec/Args.hs b/src/Data/Attoparsec/Args.hs
--- a/src/Data/Attoparsec/Args.hs
+++ b/src/Data/Attoparsec/Args.hs
@@ -1,27 +1,16 @@
 {-# LANGUAGE OverloadedStrings #-}
--- | Parsing argument-like things.
+-- | Parsing of stack command line arguments
 
 module Data.Attoparsec.Args
     ( EscapingMode(..)
     , argsParser
     , parseArgs
-    , withInterpreterArgs
     ) where
 
 import           Control.Applicative
 import           Data.Attoparsec.Text ((<?>))
 import qualified Data.Attoparsec.Text as P
-import           Data.Attoparsec.Types (Parser)
-import           Data.ByteString (ByteString)
-import qualified Data.ByteString as S
-import           Data.Conduit
-import qualified Data.Conduit.Binary as CB
-import qualified Data.Conduit.List as CL
 import           Data.Text (Text)
-import           Data.Text.Encoding (decodeUtf8')
-import           System.Directory (doesFileExist)
-import           System.Environment (getArgs, withArgs)
-import           System.IO (IOMode (ReadMode), withBinaryFile)
 
 -- | Mode for parsing escape characters.
 data EscapingMode
@@ -35,7 +24,7 @@
 
 -- | A basic argument parser. It supports space-separated text, and
 -- string quotation with identity escaping: \x -> x.
-argsParser :: EscapingMode -> Parser Text [String]
+argsParser :: EscapingMode -> P.Parser [String]
 argsParser mode = many (P.skipSpace *> (quoted <|> unquoted)) <*
                   P.skipSpace <* (P.endOfInput <?> "unterminated string")
   where
@@ -45,54 +34,5 @@
                      Escaping -> escaped <|> nonquote
                      NoEscaping -> nonquote)
     escaped = P.char '\\' *> P.anyChar
-    nonquote = P.satisfy (not . (=='"'))
+    nonquote = P.satisfy (/= '"')
     naked = P.satisfy (not . flip elem ("\" " :: String))
-
--- | Use 'withArgs' on result of 'getInterpreterArgs'.
-withInterpreterArgs :: String -> ([String] -> Bool -> IO a) -> IO a
-withInterpreterArgs progName inner = do
-    (args, isInterpreter) <- getInterpreterArgs progName
-    withArgs args $ inner args isInterpreter
-
--- | Check if command-line looks like it's being used as a script interpreter,
--- and if so look for a @-- progName ...@ comment that contains additional
--- arguments.
-getInterpreterArgs :: String -> IO ([String], Bool)
-getInterpreterArgs progName = do
-    args0 <- getArgs
-    case args0 of
-        (x:_) -> do
-            isFile <- doesFileExist x
-            if isFile
-                then do
-                    margs <-
-                        withBinaryFile x ReadMode $ \h ->
-                        CB.sourceHandle h
-                            $= CB.lines
-                            $= CL.map killCR
-                            $$ sinkInterpreterArgs progName
-                    return $ case margs of
-                        Nothing -> (args0, True)
-                        Just args -> (args ++ "--" : args0, True)
-                else return (args0, False)
-        _ -> return (args0, False)
-  where
-    killCR bs
-        | S.null bs || S.last bs /= 13 = bs
-        | otherwise = S.init bs
-
-sinkInterpreterArgs :: Monad m => String -> Sink ByteString m (Maybe [String])
-sinkInterpreterArgs progName =
-    await >>= maybe (return Nothing) checkShebang
-  where
-    checkShebang bs
-        | "#!" `S.isPrefixOf` bs = fmap (maybe Nothing parseArgs') await
-        | otherwise = return (parseArgs' bs)
-
-    parseArgs' bs =
-        case decodeUtf8' bs of
-            Left _ -> Nothing
-            Right t ->
-                case P.parseOnly (argsParser Escaping) t of
-                    Right ("--":progName':rest) | progName' == progName -> Just rest
-                    _ -> Nothing
diff --git a/src/Data/Attoparsec/Interpreter.hs b/src/Data/Attoparsec/Interpreter.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Attoparsec/Interpreter.hs
@@ -0,0 +1,136 @@
+{-# LANGUAGE OverloadedStrings #-}
+{- |  This module implements parsing of additional arguments embedded in a
+      comment when stack is invoked as a script interpreter
+
+  ===Specifying arguments in script interpreter mode
+  @/stack/@ can execute a Haskell source file using @/runghc/@ and if required
+  it can also install and setup the compiler and any package dependencies
+  automatically.
+
+  For using a Haskell source file as an executable script on a Unix like OS,
+  the first line of the file must specify @stack@ as the interpreter using a
+  shebang directive e.g.
+
+  > #!/usr/bin/env stack
+
+  Additional arguments can be specified in a haskell comment following the
+  @#!@ line. The contents inside the comment must be a single valid stack
+  command line, starting with @stack@ as the command and followed by the
+  options to use for executing this file.
+
+  The comment must be on the line immediately following the @#!@ line. The
+  comment must start in the first column of the line. When using a block style
+  comment the command can be split on multiple lines.
+
+  Here is an example of a single line comment:
+
+  > #!/usr/bin/env stack
+  > -- stack --resolver lts-3.14 --install-ghc runghc --package random
+
+  Here is an example of a multi line block comment:
+
+@
+  #!\/usr\/bin\/env stack
+  {\- stack
+    --resolver lts-3.14
+    --install-ghc
+    runghc
+    --package random
+  -\}
+@
+
+  When the @#!@ line is not present, the file can still be executed
+  using @stack \<file name\>@ command if the file starts with a valid stack
+  interpreter comment. This can be used to execute the file on Windows for
+  example.
+
+  Nested block comments are not supported.
+-}
+
+module Data.Attoparsec.Interpreter
+    ( interpreterArgsParser -- for unit tests
+    , getInterpreterArgs
+    ) where
+
+import           Control.Applicative
+import           Data.Attoparsec.Args
+import           Data.Attoparsec.Text ((<?>))
+import qualified Data.Attoparsec.Text as P
+import           Data.Char (isSpace)
+import           Data.Conduit
+import           Data.Conduit.Attoparsec
+import qualified Data.Conduit.Binary as CB
+import           Data.Conduit.Text(decodeUtf8)
+import           Data.List (intercalate)
+import           Data.Text (pack)
+import           Stack.Constants
+import           System.IO (IOMode (ReadMode), withBinaryFile, stderr, hPutStrLn)
+
+-- | Parser to extract the stack command line embedded inside a comment
+-- after validating the placement and formatting rules for a valid
+-- interpreter specification.
+interpreterArgsParser :: String -> P.Parser String
+interpreterArgsParser progName = P.option "" sheBangLine *> interpreterComment
+  where
+    sheBangLine =   P.string "#!"
+                 *> P.manyTill P.anyChar P.endOfLine
+
+    commentStart str =   (P.string str <?> (progName ++ " options comment"))
+                      *> P.skipSpace
+                      *> (P.string (pack progName) <?> show progName)
+
+    -- Treat newlines as spaces inside the block comment
+    anyCharNormalizeSpace = let normalizeSpace c = if isSpace c then ' ' else c
+                            in P.satisfyWith normalizeSpace $ const True
+
+    comment start end = commentStart start
+      *> ((end >> return "")
+          <|> (P.space *> (P.manyTill anyCharNormalizeSpace end <?> "-}")))
+
+    lineComment =  comment "--" (P.endOfLine <|> P.endOfInput)
+    blockComment = comment "{-" (P.string "-}")
+    interpreterComment = lineComment <|> blockComment
+
+-- | Extract stack arguments from a correctly placed and correctly formatted
+-- comment when it is being used as an interpreter
+getInterpreterArgs :: String -> IO [String]
+getInterpreterArgs file = do
+  eArgStr <- withBinaryFile file ReadMode parseFile
+  case eArgStr of
+    Left err -> handleFailure $ decodeError err
+    Right str -> parseArgStr str
+  where
+    parseFile h =
+      CB.sourceHandle h
+      =$= decodeUtf8
+      $$ sinkParserEither (interpreterArgsParser stackProgName)
+
+    -- FIXME We should print anything only when explicit verbose mode is
+    -- specified by the user on command line. But currently the
+    -- implementation does not accept or parse any command line flags in
+    -- interpreter mode. We can only invoke the interpreter as
+    -- "stack <file name>" strictly without any options.
+    stackWarn s = hPutStrLn stderr $ stackProgName ++ ": WARNING! " ++ s
+
+    handleFailure err = do
+      mapM_ stackWarn (lines err)
+      stackWarn "Missing or unusable stack options specification"
+      stackWarn "Using runghc without any additional stack options"
+      return ["runghc"]
+
+    parseArgStr str =
+      case P.parseOnly (argsParser Escaping) (pack str) of
+        Left err -> handleFailure ("Error parsing command specified in the \
+                        \stack options comment: " ++ err)
+        Right [] -> handleFailure ("Empty argument list in stack options \
+                        \comment")
+        Right args -> return args
+
+    decodeError e =
+      case e of
+        ParseError ctxs _ (Position line col) ->
+          if length ctxs == 0
+          then "Parse error"
+          else ("Expecting " ++ (intercalate " or " ctxs))
+          ++ " at line " ++ (show line) ++ ", column " ++ (show col)
+        DivergentParser -> "Divergent parser"
diff --git a/src/Options/Applicative/Complicated.hs b/src/Options/Applicative/Complicated.hs
--- a/src/Options/Applicative/Complicated.hs
+++ b/src/Options/Applicative/Complicated.hs
@@ -132,7 +132,7 @@
 hsubparser' :: Mod CommandFields a -> Parser a
 hsubparser' m = mkParser d g rdr
   where
-    Mod _ d g = m `mappend` metavar "COMMAND"
+    Mod _ d g = m `mappend` metavar "COMMAND|FILE"
     (cmds, subs) = mkCommand m
     rdr = CmdReader cmds (fmap add_helper . subs)
     add_helper pinfo = pinfo
diff --git a/src/Stack/Build.hs b/src/Stack/Build.hs
--- a/src/Stack/Build.hs
+++ b/src/Stack/Build.hs
@@ -201,7 +201,7 @@
     localExes =
         collect
             [ (exe,packageName pkg)
-            | pkg <- map (lpPackage) locals
+            | pkg <- map lpPackage locals
             , exe <- Set.toList (packageExes pkg)
             ]
     collect :: Ord k => [(k,v)] -> Map k (NonEmpty v)
diff --git a/src/Stack/Build/ConstructPlan.hs b/src/Stack/Build/ConstructPlan.hs
--- a/src/Stack/Build/ConstructPlan.hs
+++ b/src/Stack/Build/ConstructPlan.hs
@@ -19,7 +19,6 @@
 import           Control.Monad.Logger (MonadLogger, logWarn)
 import           Control.Monad.RWS.Strict
 import           Control.Monad.Trans.Resource
-import qualified Data.ByteString.Char8 as S8
 import           Data.Either
 import           Data.Function
 import           Data.List
@@ -180,7 +179,7 @@
         , combinedMap = combineMap sourceMap installedMap
         , toolToPackages = \ (Dependency name _) ->
           maybe Map.empty (Map.fromSet (const anyVersion)) $
-          Map.lookup (S8.pack . packageNameString . fromCabalPackageName $ name) toolMap
+          Map.lookup (T.pack . packageNameString . fromCabalPackageName $ name) toolMap
         , ctxEnvConfig = econfig
         , callStack = []
         , extraToBuild = extraToBuild0
diff --git a/src/Stack/Build/Execute.hs b/src/Stack/Build/Execute.hs
--- a/src/Stack/Build/Execute.hs
+++ b/src/Stack/Build/Execute.hs
@@ -24,19 +24,20 @@
 import           Control.Concurrent.STM
 import           Control.Exception.Enclosed (catchIO, tryIO)
 import           Control.Exception.Lifted
-import           Control.Monad (liftM, when, unless, void, join, guard, filterM, (<=<))
+import           Control.Monad (liftM, when, unless, void, join, filterM, (<=<))
 import           Control.Monad.Catch (MonadCatch, MonadMask)
 import           Control.Monad.IO.Class
 import           Control.Monad.Logger
 import           Control.Monad.Reader (MonadReader, asks)
 import           Control.Monad.Trans.Control (liftBaseWith)
 import           Control.Monad.Trans.Resource
-import           Data.ByteString (ByteString)
+import           Data.Attoparsec.Text
 import qualified Data.ByteString as S
-import qualified Data.ByteString.Char8 as S8
 import           Data.Conduit
 import qualified Data.Conduit.Binary as CB
 import qualified Data.Conduit.List as CL
+import qualified Data.Conduit.Text as CT
+import           Data.Either (isRight)
 import           Data.Foldable (forM_, any)
 import           Data.Function
 import           Data.IORef.RunOnce (runOnce)
@@ -55,7 +56,6 @@
 import           Data.Text.Encoding (decodeUtf8)
 import           Data.Time.Clock (getCurrentTime)
 import           Data.Traversable (forM)
-import           Data.Word8 (_colon)
 import qualified Distribution.PackageDescription as C
 import           Distribution.System            (OS (Windows),
                                                  Platform (Platform))
@@ -222,7 +222,7 @@
 getSetupExe setupHs tmpdir = do
     wc <- getWhichCompiler
     econfig <- asks getEnvConfig
-    platformDir <- platformVariantRelDir
+    platformDir <- platformGhcRelDir
     let config = getConfig econfig
         baseNameS = concat
             [ "setup-Simple-Cabal-"
@@ -487,7 +487,7 @@
         let total = length actions
             loop prev
                 | prev == total =
-                    runInBase $ $logStickyDone ("Completed all " <> T.pack (show total) <> " actions.")
+                    runInBase $ $logStickyDone ("Completed " <> T.pack (show total) <> " action(s).")
                 | otherwise = do
                     when terminal $ runInBase $
                         $logSticky ("Progress: " <> T.pack (show prev) <> "/" <> T.pack (show total))
@@ -626,13 +626,19 @@
         if boptsReconfigure eeBuildOpts
             then return True
             else do
+                -- We can ignore the components portion of the config
+                -- cache, because it's just used to inform 'construct
+                -- plan that we need to plan to build additional
+                -- components. These components don't affect the actual
+                -- package configuration.
+                let ignoreComponents cc = cc { configCacheComponents = Set.empty }
                 -- Determine the old and new configuration in the local directory, to
                 -- determine if we need to reconfigure.
                 mOldConfigCache <- tryGetConfigCache pkgDir
 
                 mOldCabalMod <- tryGetCabalMod pkgDir
 
-                return $ fmap configCacheOpts mOldConfigCache /= Just (configCacheOpts newConfigCache)
+                return $ fmap ignoreComponents mOldConfigCache /= Just (ignoreComponents newConfigCache)
                       || mOldCabalMod /= Just newCabalMod
     let ConfigureOpts dirs nodirs = configCacheOpts newConfigCache
     when needConfig $ withMVar eeConfigureLock $ \_ -> do
@@ -828,6 +834,7 @@
                                         liftIO $ hClose h
                                         runResourceT
                                             $ CB.sourceFile (toFilePath logFile)
+                                            =$= CT.decodeUtf8
                                             $$ mungeBuildOutput stripTHLoading makeAbsolute pkgDir
                                             =$ CL.consume
                             throwM $ CabalExitedUnsuccessfully
@@ -1172,7 +1179,14 @@
             hpcDir <- hpcDirFromDir pkgDir
             when needHpc (createTree hpcDir)
 
-            errs <- liftM Map.unions $ forM (Map.toList (packageTests package)) $ \(testName, suiteInterface) -> do
+            let suitesToRun
+                  = [ testSuitePair
+                    | testSuitePair <- Map.toList $ packageTests package
+                    , let testName = fst testSuitePair
+                    , testName `elem` testsToRun
+                    ]
+
+            errs <- liftM Map.unions $ forM suitesToRun $ \(testName, suiteInterface) -> do
                 let stestName = T.unpack testName
                 (testName', isTestTypeLib) <-
                     case suiteInterface of
@@ -1180,7 +1194,10 @@
                         C.TestSuiteExeV10{} -> return (stestName, False)
                         interface -> throwM (TestSuiteTypeUnsupported interface)
 
-                exeName <- testExeName testName'
+                let exeName = testName' ++
+                        case configPlatform config of
+                            Platform _ Windows -> ".exe"
+                            _ -> ""
                 tixPath <- liftM (pkgDir </>) $ parseRelFile $ exeName ++ ".tix"
                 exePath <- liftM (buildDir </>) $ parseRelFile $ "build/" ++ testName' ++ "/" ++ exeName
                 exists <- fileExists exePath
@@ -1204,18 +1221,24 @@
                                             [] -> ""
                                             _ -> ", args: " <> T.intercalate " " (map showProcessArgDebug args)
                         announce $ "test (suite: " <> testName <> argsDisplay <> ")"
-                        let cp = (proc (toFilePath exePath) args)
+
+                        -- Clear "Progress: ..." message before
+                        -- redirecting output.
+                        when (isNothing mlogFile) $ do
+                            $logStickyDone ""
+                            liftIO $ hFlush stdout
+                            liftIO $ hFlush stderr
+
+                        let output =
+                                case mlogFile of
+                                    Nothing -> Inherit
+                                    Just (_, h) -> UseHandle h
+                            cp = (proc (toFilePath exePath) args)
                                 { cwd = Just $ toFilePath pkgDir
                                 , Process.env = envHelper menv
                                 , std_in = CreatePipe
-                                , std_out =
-                                    case mlogFile of
-                                        Nothing -> Inherit
-                                        Just (_, h) -> UseHandle h
-                                , std_err =
-                                    case mlogFile of
-                                        Nothing -> Inherit
-                                        Just (_, h) -> UseHandle h
+                                , std_out = output
+                                , std_err = output
                                 }
 
                         -- Use createProcess_ to avoid the log file being closed afterwards
@@ -1226,11 +1249,14 @@
                             liftIO $ hPutStr inH $ show (logPath, testName)
                         liftIO $ hClose inH
                         ec <- liftIO $ waitForProcess ph
+                        -- Add a trailing newline, incase the test
+                        -- output didn't finish with a newline.
+                        when (isNothing mlogFile) ($logInfo "")
                         -- Move the .tix file out of the package
                         -- directory into the hpc work dir, for
                         -- tidiness.
                         when needHpc $
-                            updateTixFile (packageName package) tixPath
+                            updateTixFile (packageName package) tixPath testName'
                         return $ case ec of
                             ExitSuccess -> Map.empty
                             _ -> Map.singleton testName $ Just ec
@@ -1297,7 +1323,8 @@
 -- | Grab all output from the given @Handle@ and log it, stripping
 -- Template Haskell "Loading package" lines and making paths absolute.
 -- thread.
-printBuildOutput :: (MonadIO m, MonadBaseControl IO m, MonadLogger m)
+printBuildOutput :: (MonadIO m, MonadBaseControl IO m, MonadLogger m,
+                     MonadThrow m)
                  => Bool -- ^ exclude TH loading?
                  -> Bool -- ^ convert paths to absolute?
                  -> Path Abs Dir -- ^ package's root directory
@@ -1306,7 +1333,8 @@
                  -> m ()
 printBuildOutput excludeTHLoading makeAbsolute pkgDir level outH = void $
     CB.sourceHandle outH
-    $$ mungeBuildOutput excludeTHLoading makeAbsolute pkgDir
+    $$ CT.decodeUtf8
+    =$ mungeBuildOutput excludeTHLoading makeAbsolute pkgDir
     =$ CL.mapM_ (monadLoggerLog $(TH.location >>= liftLoc) "" level)
 
 -- | Strip Template Haskell "Loading package" lines and making paths absolute.
@@ -1314,52 +1342,47 @@
                  => Bool -- ^ exclude TH loading?
                  -> Bool -- ^ convert paths to absolute?
                  -> Path Abs Dir -- ^ package's root directory
-                 -> ConduitM ByteString ByteString m ()
+                 -> ConduitM Text Text m ()
 mungeBuildOutput excludeTHLoading makeAbsolute pkgDir = void $
-    CB.lines
+    CT.lines
     =$ CL.map stripCarriageReturn
     =$ CL.filter (not . isTHLoading)
     =$ CL.mapM toAbsolutePath
   where
     -- | Is this line a Template Haskell "Loading package" line
     -- ByteString
-    isTHLoading :: S8.ByteString -> Bool
+    isTHLoading :: Text -> Bool
     isTHLoading _ | not excludeTHLoading = False
     isTHLoading bs =
-        "Loading package " `S8.isPrefixOf` bs &&
-        ("done." `S8.isSuffixOf` bs || "done.\r" `S8.isSuffixOf` bs)
+        "Loading package " `T.isPrefixOf` bs &&
+        ("done." `T.isSuffixOf` bs || "done.\r" `T.isSuffixOf` bs)
 
     -- | Convert GHC error lines with file paths to have absolute file paths
     toAbsolutePath bs | not makeAbsolute = return bs
     toAbsolutePath bs = do
-        let (x, y) = S.break (== _colon) bs
+        let (x, y) = T.break (== ':') bs
         mabs <-
             if isValidSuffix y
                 then do
-                    efp <- liftIO $ tryIO $ resolveFile pkgDir (S8.unpack x)
+                    efp <- liftIO $ tryIO $ resolveFile pkgDir (T.unpack x)
                     case efp of
                         Left _ -> return Nothing
-                        Right fp -> return $ Just $ S8.pack (toFilePath fp)
+                        Right fp -> return $ Just $ T.pack (toFilePath fp)
                 else return Nothing
         case mabs of
             Nothing -> return bs
-            Just fp -> return $ fp `S.append` y
+            Just fp -> return $ fp `T.append` y
 
     -- | Match the line:column format at the end of lines
-    isValidSuffix bs0 = maybe False (const True) $ do
-        guard $ not $ S.null bs0
-        guard $ S.head bs0 == _colon
-        (_, bs1) <- S8.readInt $ S.drop 1 bs0
-
-        guard $ not $ S.null bs1
-        guard $ S.head bs1 == _colon
-        (_, bs2) <- S8.readInt $ S.drop 1 bs1
-
-        guard $ (bs2 == ":" || bs2 == ": Warning:")
+    isValidSuffix = isRight . parseOnly (lineCol <* endOfInput)
+    lineCol = char ':' >> (decimal :: Parser Int)
+           >> char ':' >> (decimal :: Parser Int)
+           >> (string ":" <|> string ": Warning:")
+           >> return ()
 
     -- | Strip @\r@ characters from the byte vector. Used because Windows.
-    stripCarriageReturn :: ByteString -> ByteString
-    stripCarriageReturn = S8.filter (not . (=='\r'))
+    stripCarriageReturn :: Text -> Text
+    stripCarriageReturn = T.filter (/= '\r')
 
 -- | Find the Setup.hs or Setup.lhs in the given directory. If none exists,
 -- throw an exception.
diff --git a/src/Stack/Build/Installed.hs b/src/Stack/Build/Installed.hs
--- a/src/Stack/Build/Installed.hs
+++ b/src/Stack/Build/Installed.hs
@@ -21,6 +21,7 @@
 import           Data.Conduit
 import qualified Data.Conduit.List            as CL
 import           Data.Function
+import qualified Data.Foldable                as F
 import qualified Data.HashSet                 as HashSet
 import           Data.List
 import           Data.Map.Strict              (Map)
@@ -87,9 +88,7 @@
         loadDatabase' (Just (InstalledTo Local, localDBPath)) installedLibs2
     let installedLibs = M.fromList $ map lhPair installedLibs3
 
-    case mcache of
-        Nothing -> return ()
-        Just pcache -> saveInstalledCache (configInstalledCache bconfig) pcache
+    F.forM_ mcache (saveInstalledCache (configInstalledCache bconfig))
 
     -- Add in the executables that are installed, making sure to only trust a
     -- listed installation under the right circumstances (see below)
@@ -191,8 +190,8 @@
         [ "Ignoring package "
         , packageNameText (fst (lhPair lh))
         ] ++
-        (maybe [] (\db -> [", from ", T.pack (show db), ","]) mdb) ++
-        [ " due to "
+        maybe [] (\db -> [", from ", T.pack (show db), ","]) mdb ++
+        [ " due to"
         , case reason of
             Allowed -> " the impossible?!?!"
             NeedsProfiling -> " it needing profiling."
diff --git a/src/Stack/Build/Source.hs b/src/Stack/Build/Source.hs
--- a/src/Stack/Build/Source.hs
+++ b/src/Stack/Build/Source.hs
@@ -145,7 +145,7 @@
                  in (packageName p, PSLocal lp)
             , extraDeps3
             , flip fmap (mbpPackages mbp) $ \mpi ->
-                (PSUpstream (mpiVersion mpi) Snap (mpiFlags mpi))
+                PSUpstream (mpiVersion mpi) Snap (mpiFlags mpi)
             ] `Map.difference` Map.fromList (map (, ()) (HashSet.toList wiredInPackages))
 
     return (targets, mbp, locals, nonLocalTargets, sourceMap)
@@ -205,8 +205,7 @@
     -> Map PackageName a -- ^ locals
     -> [PackageName] -- ^ packages referenced by a flag
     -> m (Map PackageName Version)
-convertSnapshotToExtra snapshot extra0 locals flags0 =
-    go Map.empty flags0
+convertSnapshotToExtra snapshot extra0 locals = go Map.empty
   where
     go !extra [] = return extra
     go extra (flag:flags)
@@ -417,7 +416,7 @@
         $ Set.fromList unusedFlags
 
 -- | All flags for a local package
-localFlags :: (Map (Maybe PackageName) (Map FlagName Bool))
+localFlags :: Map (Maybe PackageName) (Map FlagName Bool)
            -> BuildConfig
            -> PackageName
            -> Map FlagName Bool
diff --git a/src/Stack/BuildPlan.hs b/src/Stack/BuildPlan.hs
--- a/src/Stack/BuildPlan.hs
+++ b/src/Stack/BuildPlan.hs
@@ -53,6 +53,7 @@
 import           Data.Monoid
 import           Data.Set (Set)
 import qualified Data.Set as Set
+import           Data.Text (Text)
 import qualified Data.Text as T
 import           Data.Text.Encoding (encodeUtf8)
 import           Data.Time (Day)
@@ -84,7 +85,7 @@
 data BuildPlanException
     = UnknownPackages
         (Path Abs File) -- stack.yaml file
-        (Map PackageName (Maybe Version, (Set PackageName))) -- truly unknown
+        (Map PackageName (Maybe Version, Set PackageName)) -- truly unknown
         (Map PackageName (Set PackageIdentifier)) -- shadowed
     | SnapshotNotFound SnapName
     deriving (Typeable)
@@ -283,7 +284,7 @@
                     }
                 name = packageIdentifierName ident
                 pd = resolvePackageDescription packageConfig gpd
-                exes = Set.fromList $ map (ExeName . S8.pack . exeName) $ executables pd
+                exes = Set.fromList $ map (ExeName . T.pack . exeName) $ executables pd
                 notMe = Set.filter (/= name) . Map.keysSet
             return (name, MiniPackageInfo
                 { mpiVersion = packageIdentifierVersion ident
@@ -368,7 +369,7 @@
 type ToolMap = Map ByteString (Set PackageName)
 
 -- | Map from tool name to package providing it
-getToolMap :: MiniBuildPlan -> Map ByteString (Set PackageName)
+getToolMap :: MiniBuildPlan -> Map Text (Set PackageName)
 getToolMap mbp =
       Map.unionsWith Set.union
 
@@ -406,8 +407,7 @@
     parseJSON = withObject "Snapshots" $ \o -> Snapshots
         <$> (o .: "nightly" >>= parseNightly)
         <*> (fmap IntMap.unions
-                $ mapM parseLTS
-                $ map snd
+                $ mapM (parseLTS . snd)
                 $ filter (isLTS . fst)
                 $ HM.toList o)
       where
diff --git a/src/Stack/Config.hs b/src/Stack/Config.hs
--- a/src/Stack/Config.hs
+++ b/src/Stack/Config.hs
@@ -151,13 +151,12 @@
 
      configDocker <-
          dockerOptsFromMonoid (fmap fst mproject) configStackRoot mresolver configMonoidDockerOpts
-     configNix <- nixOptsFromMonoid (fmap fst mproject) configStackRoot configMonoidNixOpts
+     configNix <- nixOptsFromMonoid (fmap fst mproject) mresolver configMonoidNixOpts os
 
      rawEnv <- liftIO getEnvironment
-     origEnv <- mkEnvOverride configPlatform
-              $ augmentPathMap (map toFilePath configMonoidExtraPath)
-              $ Map.fromList
-              $ map (T.pack *** T.pack) rawEnv
+     pathsEnv <- augmentPathMap (map toFilePath configMonoidExtraPath)
+                                (Map.fromList (map (T.pack *** T.pack) rawEnv))
+     origEnv <- mkEnvOverride configPlatform pathsEnv
      let configEnvOverride _ = return origEnv
 
      platformOnlyDir <- runReaderT platformOnlyRelDir (configPlatform,configPlatformVariant)
@@ -204,6 +203,7 @@
          configRebuildGhcOptions = fromMaybe False configMonoidRebuildGhcOptions
          configApplyGhcOptions = fromMaybe AGOLocals configMonoidApplyGhcOptions
          configAllowNewer = fromMaybe False configMonoidAllowNewer
+         configDefaultTemplate = configMonoidDefaultTemplate
 
      return Config {..}
 
@@ -277,7 +277,7 @@
            -- ^ Config monoid from parsed command-line arguments
            -> Maybe (Path Abs File)
            -- ^ Override stack.yaml
-           -> Maybe (AbstractResolver)
+           -> Maybe AbstractResolver
            -- ^ Override resolver
            -> m (LoadConfig m)
 loadConfig configArgs mstackYaml mresolver = do
@@ -477,7 +477,8 @@
                     Nothing
                 readInNull dirTmp commandName menv
                     (resetCommand ++ [T.unpack commit])
-                    Nothing
+                    (Just $ "Please ensure that commit " <> commit <>
+                      " exists within " <> url)
 
         case remotePackageType of
             RPTHttpTarball -> do
diff --git a/src/Stack/Config/Nix.hs b/src/Stack/Config/Nix.hs
--- a/src/Stack/Config/Nix.hs
+++ b/src/Stack/Config/Nix.hs
@@ -6,31 +6,49 @@
        ,StackNixException(..)
        ) where
 
-import Data.Text (pack)
+import Control.Monad (when)
+import qualified Data.Text as T
 import Data.Maybe
 import Data.Typeable
-import Path
+import Distribution.System (OS (..))
 import Stack.Types
 import Control.Exception.Lifted
 import Control.Monad.Catch (throwM,MonadCatch)
 
 
 -- | Interprets NixOptsMonoid options.
-nixOptsFromMonoid :: (Monad m, MonadCatch m) => Maybe Project -> Path Abs Dir -> NixOptsMonoid -> m NixOpts
-nixOptsFromMonoid mproject _stackRoot NixOptsMonoid{..} = do
+nixOptsFromMonoid
+    :: (Monad m, MonadCatch m)
+    => Maybe Project
+    -> Maybe AbstractResolver
+    -> NixOptsMonoid
+    -> OS
+    -> m NixOpts
+nixOptsFromMonoid mproject maresolver NixOptsMonoid{..} os = do
     let nixEnable = fromMaybe nixMonoidDefaultEnable nixMonoidEnable
+        defaultPure = case os of
+          OSX -> False
+          _ -> True
+        nixPureShell = fromMaybe defaultPure nixMonoidPureShell
+        mresolver = case maresolver of
+          Just (ARResolver resolver) -> Just resolver
+          Just _ -> Nothing
+          Nothing -> fmap projectResolver mproject
+        pkgs = fromMaybe [] nixMonoidPackages
         nixPackages = case mproject of
-           Nothing -> nixMonoidPackages
-           Just p -> nixMonoidPackages ++ [case projectResolver p of
-              ResolverSnapshot (LTS x y) ->
-                pack ("haskell.packages.lts-" ++ show x ++ "_" ++ show y ++ ".ghc")
-              _ -> pack "ghc"]
+           Nothing -> pkgs
+           Just _ -> pkgs ++ [case mresolver of
+              Just (ResolverSnapshot (LTS x y)) ->
+                T.pack ("haskell.packages.lts-" ++ show x ++ "_" ++ show y ++ ".ghc")
+              _ -> T.pack "ghc"]
         nixInitFile = nixMonoidInitFile
-        nixShellOptions = nixMonoidShellOptions
-    if not (null nixMonoidPackages) && isJust nixInitFile then
+        nixShellOptions = fromMaybe [] nixMonoidShellOptions
+                          ++ prefixAll (T.pack "-I") (fromMaybe [] nixMonoidPath)
+    when (not (null pkgs) && isJust nixInitFile) $
        throwM NixCannotUseShellFileAndPackagesException
-       else return ()
     return NixOpts{..}
+  where prefixAll p (x:xs) = p : x : prefixAll p xs
+        prefixAll _ _      = []
 
 -- Exceptions thown specifically by Stack.Nix
 data StackNixException
diff --git a/src/Stack/Constants.hs b/src/Stack/Constants.hs
--- a/src/Stack/Constants.hs
+++ b/src/Stack/Constants.hs
@@ -212,7 +212,7 @@
                 => m (Path Rel Dir)
 distRelativeDir = do
     cabalPkgVer <- asks (envConfigCabalVersion . getEnvConfig)
-    platform <- platformVariantRelDir
+    platform <- platformGhcRelDir
     wc <- getWhichCompiler
     -- Cabal version, suffixed with "_ghcjs" if we're using GHCJS.
     envDir <-
diff --git a/src/Stack/Coverage.hs b/src/Stack/Coverage.hs
--- a/src/Stack/Coverage.hs
+++ b/src/Stack/Coverage.hs
@@ -8,7 +8,6 @@
 module Stack.Coverage
     ( deleteHpcReports
     , updateTixFile
-    , testExeName
     , generateHpcReport
     , HpcReportOpts(..)
     , generateHpcReportForTargets
@@ -28,7 +27,6 @@
 import           Data.Foldable (forM_, asum, toList)
 import           Data.Function
 import           Data.List
-import           Data.List.Extra (stripSuffix)
 import qualified Data.Map.Strict as Map
 import           Data.Maybe
 import           Data.Maybe.Extra (mapMaybeM)
@@ -39,7 +37,6 @@
 import qualified Data.Text.IO as T
 import qualified Data.Text.Lazy as LT
 import           Data.Traversable (forM)
-import           Distribution.System (OS (Windows), Platform (Platform))
 import           Network.HTTP.Download (HasHttpManager)
 import           Path
 import           Path.Extra (toFilePathNoTrailingSep)
@@ -66,33 +63,21 @@
 -- | Move a tix file into a sub-directory of the hpc report directory. Deletes the old one if one is
 -- present.
 updateTixFile :: (MonadIO m,MonadReader env m,HasConfig env,MonadLogger m,MonadBaseControl IO m,MonadCatch m,HasEnvConfig env)
-            => PackageName -> Path Abs File -> m ()
-updateTixFile pkgName tixSrc = do
-    case stripSuffix ".tix" (toFilePath (filename tixSrc)) of
-        Nothing -> error "Invariant violated: updateTixFile expected a tix filepath."
-        Just testName -> do
-            exists <- fileExists tixSrc
-            when exists $ do
-                tixDest <- tixFilePath pkgName testName
-                removeFileIfExists tixDest
-                createTree (parent tixDest)
-                -- Remove exe modules because they are problematic. This could be revisited if there's a GHC
-                -- version that fixes https://ghc.haskell.org/trac/ghc/ticket/1853
-                mtix <- readTixOrLog tixSrc
-                case mtix of
-                    Nothing -> $logError $ "Failed to read " <> T.pack (toFilePath tixSrc)
-                    Just tix -> do
-                        liftIO $ writeTix (toFilePath tixDest) (removeExeModules tix)
-                        removeFileIfExists tixSrc
-
-testExeName :: (MonadReader env m,HasConfig env) => String -> m String
-testExeName testName = do
-    config <- asks getConfig
-    let exeExtension =
-            case configPlatform config of
-                Platform _ Windows -> ".exe"
-                _ -> ""
-    return $ testName ++ exeExtension
+            => PackageName -> Path Abs File -> String -> m ()
+updateTixFile pkgName tixSrc testName = do
+    exists <- fileExists tixSrc
+    when exists $ do
+        tixDest <- tixFilePath pkgName testName
+        removeFileIfExists tixDest
+        createTree (parent tixDest)
+        -- Remove exe modules because they are problematic. This could be revisited if there's a GHC
+        -- version that fixes https://ghc.haskell.org/trac/ghc/ticket/1853
+        mtix <- readTixOrLog tixSrc
+        case mtix of
+            Nothing -> $logError $ "Failed to read " <> T.pack (toFilePath tixSrc)
+            Just tix -> do
+                liftIO $ writeTix (toFilePath tixDest) (removeExeModules tix)
+                removeFileIfExists tixSrc
 
 -- | Get the directory used for hpc reports for the given pkgId.
 hpcPkgPath :: (MonadIO m,MonadReader env m,HasConfig env,MonadLogger m,MonadBaseControl IO m,MonadCatch m,HasEnvConfig env)
@@ -108,8 +93,7 @@
             => PackageName -> String ->  m (Path Abs File)
 tixFilePath pkgName testName = do
     pkgPath <- hpcPkgPath pkgName
-    exeName <- testExeName testName
-    tixRel <- parseRelFile (testName ++ "/" ++ exeName ++ ".tix")
+    tixRel <- parseRelFile (testName ++ "/" ++ testName ++ ".tix")
     return (pkgPath </> tixRel)
 
 -- | Generates the HTML coverage report and shows a textual coverage summary for a package.
@@ -202,7 +186,7 @@
                     generateHpcErrorReport reportDir (msg True)
                 else do
                     -- Print output, stripping @\r@ characters because Windows.
-                    forM_ outputLines ($logInfo . T.decodeUtf8 . S8.filter (not . (=='\r')))
+                    forM_ outputLines ($logInfo . T.decodeUtf8 . S8.filter (/= '\r'))
                     $logInfo
                         ("The " <> report <> " is available at " <>
                          T.pack (toFilePath (reportDir </> $(mkRelFile "hpc_index.html"))))
diff --git a/src/Stack/Docker.hs b/src/Stack/Docker.hs
--- a/src/Stack/Docker.hs
+++ b/src/Stack/Docker.hs
@@ -9,6 +9,7 @@
   ,CleanupAction(..)
   ,dockerCleanupCmdName
   ,dockerCmdName
+  ,dockerHelpOptName
   ,dockerPullCmdName
   ,entrypoint
   ,preventInContainer
@@ -37,7 +38,7 @@
 import           Data.Char (isSpace,toUpper,isAscii,isDigit)
 import           Data.Conduit.List (sinkNull)
 import           Data.List (dropWhileEnd,intercalate,isPrefixOf,isInfixOf,foldl')
-import           Data.List.Extra (trim,nubOrd)
+import           Data.List.Extra (trim)
 import           Data.Map.Strict (Map)
 import qualified Data.Map.Strict as Map
 import           Data.Maybe
@@ -67,7 +68,6 @@
 import           System.Environment (getEnv,getProgName,getArgs,getExecutablePath,lookupEnv)
 import           System.Exit (exitSuccess, exitWith)
 import qualified System.FilePath as FP
-import qualified System.FilePath.Posix as Posix
 import           System.IO (stderr,stdin,stdout,hIsTerminalDevice)
 import           System.IO.Error (isDoesNotExistError)
 import           System.IO.Unsafe (unsafePerformIO)
@@ -288,16 +288,16 @@
                          -- This is fixed in Docker 1.9.1, but will leave the workaround
                          -- in place for now, for users who haven't upgraded yet.
                          (isTerm || (isNothing bamboo && isNothing jenkins))
-         newPathEnv = intercalate [Posix.searchPathSeparator] $
-                      nubOrd $
-                      [hostBinDir
-                      ,toFilePathNoTrailingSep $ sandboxHomeDir </> $(mkRelDir ".local/bin")] ++
-                      maybe [] Posix.splitSearchPath (lookupImageEnv "PATH" imageEnvVars)
+     newPathEnv <- augmentPath
+                      [ hostBinDir
+                      , toFilePathNoTrailingSep $ sandboxHomeDir
+                                            </> $(mkRelDir ".local/bin")]
+                      (T.pack <$> lookupImageEnv "PATH" imageEnvVars)
      (cmnd,args,envVars,extraMount) <- getCmdArgs docker envOverride imageInfo isRemoteDocker
      pwd <- getWorkingDir
      liftIO
        (do updateDockerImageLastUsed config iiId (toFilePath projectRoot)
-           mapM_ createTree ([sandboxHomeDir, stackRoot]))
+           mapM_ createTree [sandboxHomeDir, stackRoot])
      containerID <- (trim . decodeUtf8) <$> readDockerProcess
        envOverride
        (concat
@@ -307,7 +307,7 @@
           ,"-e",stackRootEnvVar ++ "=" ++ toFilePathNoTrailingSep stackRoot
           ,"-e",platformVariantEnvVar ++ "=dk" ++ platformVariant
           ,"-e","HOME=" ++ toFilePathNoTrailingSep sandboxHomeDir
-          ,"-e","PATH=" ++ newPathEnv
+          ,"-e","PATH=" ++ T.unpack newPathEnv
           ,"-v",toFilePathNoTrailingSep stackRoot ++ ":" ++ toFilePathNoTrailingSep stackRoot
           ,"-v",toFilePathNoTrailingSep projectRoot ++ ":" ++ toFilePathNoTrailingSep projectRoot
           ,"-v",toFilePathNoTrailingSep sandboxHomeDir ++ ":" ++ toFilePathNoTrailingSep sandboxHomeDir
@@ -332,7 +332,7 @@
      before
 #ifndef WINDOWS
      runInBase <- liftBaseWith $ \run -> return (void . run)
-     oldHandlers <- forM ([sigINT,sigABRT,sigHUP,sigPIPE,sigTERM,sigUSR1,sigUSR2]) $ \sig -> do
+     oldHandlers <- forM [sigINT,sigABRT,sigHUP,sigPIPE,sigTERM,sigUSR1,sigUSR2] $ \sig -> do
        let sigHandler = runInBase $ do
              readProcessNull Nothing envOverride "docker"
                              ["kill","--signal=" ++ show sig,containerID]
@@ -530,10 +530,10 @@
             Nothing -> return ()
         sortCreated =
             sortWith (\(_,_,x) -> Down x) .
-            (mapMaybe (\(h,r) ->
+             mapMaybe (\(h,r) ->
                 case Map.lookup h inspectMap of
                     Nothing -> Nothing
-                    Just ii -> Just (h,r,iiCreated ii)))
+                    Just ii -> Just (h,r,iiCreated ii))
         buildSection sectionHead items itemBuilder =
           do let (anyWrote,b) = runWriter (forM items itemBuilder)
              when (or anyWrote) $
@@ -571,7 +571,7 @@
                      projectPath)
         buildInspect hash =
           case Map.lookup hash inspectMap of
-            Just (Inspect{iiCreated,iiVirtualSize}) ->
+            Just Inspect{iiCreated,iiVirtualSize} ->
               buildInfo ("Created " ++
                          showDaysAgo iiCreated ++
                          maybe ""
@@ -629,7 +629,9 @@
          case eitherDecode (LBS.pack (filter isAscii (decodeUtf8 inspectOut))) of
            Left msg -> throwM (InvalidInspectOutputException msg)
            Right results -> return (Map.fromList (map (\r -> (iiId r,r)) results))
-       Left e -> throwM (e :: ReadProcessException)
+       Left (ReadProcessException _ _ _ err)
+         | "Error: No such image" `LBS.isPrefixOf` err -> return Map.empty
+       Left e -> throwM e
 
 -- | Pull latest version of configured Docker image from registry.
 pull :: M env m => m ()
@@ -843,6 +845,9 @@
 -- | Command-line argument for "docker"
 dockerCmdName :: String
 dockerCmdName = "docker"
+
+dockerHelpOptName :: String
+dockerHelpOptName = dockerCmdName ++ "-help"
 
 -- | Command-line argument for @docker pull@.
 dockerPullCmdName :: String
diff --git a/src/Stack/Docker/GlobalDB.hs b/src/Stack/Docker/GlobalDB.hs
--- a/src/Stack/Docker/GlobalDB.hs
+++ b/src/Stack/Docker/GlobalDB.hs
@@ -16,10 +16,11 @@
   where
 
 import           Control.Exception (IOException,catch,throwIO)
-import           Control.Monad (forM_)
+import           Control.Monad (forM_, when)
 import           Control.Monad.Logger (NoLoggingT)
 import           Control.Monad.Trans.Resource (ResourceT)
 import           Data.List (sortBy, isInfixOf, stripPrefix)
+import           Data.List.Extra (stripSuffix)
 import qualified Data.Map.Strict as Map
 import           Data.Maybe (fromMaybe)
 import qualified Data.Text as T
@@ -75,11 +76,11 @@
 pruneDockerImagesLastUsed :: Config -> [String] -> IO ()
 pruneDockerImagesLastUsed config existingHashes =
   withGlobalDB config go
-  where go = do l <- selectList [] []
-                forM_ l (\(Entity k (DockerImageProject{dockerImageProjectImageHash = h})) ->
-                           if h `elem` existingHashes
-                             then return ()
-                             else delete k)
+  where
+    go = do
+        l <- selectList [] []
+        forM_ l (\(Entity k DockerImageProject{dockerImageProjectImageHash = h}) ->
+            when (h `notElem` existingHashes) $ delete k)
 
 -- | Get the record of whether an executable is compatible with a Docker image
 getDockerImageExe :: Config -> String -> FilePath -> UTCTime -> IO (Maybe Bool)
@@ -105,7 +106,6 @@
                    action)
          `catch` \ex -> do
              let str = show ex
-                 stripSuffix x = fmap reverse . stripPrefix x . reverse
                  str' = fromMaybe str $ stripPrefix "user error (" $
                         fromMaybe str $ stripSuffix ")" str
              if "ErrorReadOnly" `isInfixOf` str
diff --git a/src/Stack/GhcPkg.hs b/src/Stack/GhcPkg.hs
--- a/src/Stack/GhcPkg.hs
+++ b/src/Stack/GhcPkg.hs
@@ -134,7 +134,7 @@
 findGhcPkgVersion menv wc pkgDbs name = do
     mv <- findGhcPkgField menv wc pkgDbs (packageNameString name) "version"
     case mv of
-        Just !v -> return (parseVersion (T.encodeUtf8 v))
+        Just !v -> return (parseVersion v)
         _ -> return Nothing
 
 unregisterGhcPkgId :: (MonadIO m, MonadLogger m, MonadThrow m, MonadCatch m, MonadBaseControl IO m)
diff --git a/src/Stack/Ghci.hs b/src/Stack/Ghci.hs
--- a/src/Stack/Ghci.hs
+++ b/src/Stack/Ghci.hs
@@ -4,6 +4,7 @@
 {-# LANGUAGE TupleSections #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE CPP #-}
 
 -- | Run a GHCi configured with the user's package(s).
 
@@ -52,6 +53,10 @@
 import           Stack.Types.Internal
 import           System.Directory (getTemporaryDirectory)
 
+#ifndef WINDOWS
+import qualified System.Posix.Files as Posix
+#endif
+
 -- | Command-line options for GHC.
 data GhciOpts = GhciOpts
     { ghciNoBuild            :: !Bool
@@ -61,6 +66,7 @@
     , ghciAdditionalPackages :: ![String]
     , ghciMainIs             :: !(Maybe Text)
     , ghciSkipIntermediate   :: !Bool
+    , ghciHidePackages       :: !Bool
     , ghciBuildOpts          :: !BuildOpts
     } deriving Show
 
@@ -93,8 +99,11 @@
     mainFile <- figureOutMainFile bopts mainIsTargets targets pkgs
     wc <- getWhichCompiler
     let pkgopts = hidePkgOpt ++ genOpts ++ ghcOpts
-        hidePkgOpt = if null pkgs then [] else ["-hide-all-packages"]
-        genOpts = nubOrd (concatMap (concatMap (bioOneWordOpts . snd) . ghciPkgOpts) pkgs)
+        hidePkgOpt = if null pkgs || not ghciHidePackages then [] else ["-hide-all-packages"]
+        oneWordOpts bio
+            | ghciHidePackages = bioOneWordOpts bio ++ bioPackageFlags bio
+            | otherwise = bioOneWordOpts bio
+        genOpts = nubOrd (concatMap (concatMap (oneWordOpts . snd) . ghciPkgOpts) pkgs)
         (omittedOpts, ghcOpts) = partition badForGhci $
             concatMap (concatMap (bioOpts . snd) . ghciPkgOpts) pkgs ++
             getUserOptions Nothing ++
@@ -139,6 +148,7 @@
                     loadModules = ":load " <> unwords (map show thingsToLoad)
                     bringIntoScope = ":module + " <> unwords modulesToLoad
                 liftIO (writeFile fp (unlines [loadModules,bringIntoScope]))
+                setScriptPerms fp
                 execGhci (macrosOpts ++ ["-ghci-script=" <> fp])
 
 -- | Figure out the main-is file to load based on the targets. Sometimes there
@@ -438,8 +448,22 @@
 
 preprocessCabalMacros :: MonadIO m => [GhciPkgInfo] -> Path Abs File -> m [String]
 preprocessCabalMacros pkgs out = liftIO $ do
-    let fps = nubOrd (concatMap (catMaybes . map (bioCabalMacros . snd) . ghciPkgOpts) pkgs)
+    let fps = nubOrd (concatMap (mapMaybe (bioCabalMacros . snd) . ghciPkgOpts) pkgs)
     files <- mapM (S8.readFile . toFilePath) fps
     if null files then return [] else do
         S8.writeFile (toFilePath out) $ S8.intercalate "\n#undef CURRENT_PACKAGE_KEY\n" files
         return ["-optP-include", "-optP" <> toFilePath out]
+
+setScriptPerms :: MonadIO m => FilePath -> m ()
+#ifdef WINDOWS
+setScriptPerms _ = do
+    return ()
+#else
+setScriptPerms fp = do
+    liftIO $ Posix.setFileMode fp $ foldl1 Posix.unionFileModes
+        [ Posix.ownerReadMode
+        , Posix.ownerWriteMode
+        , Posix.groupReadMode
+        , Posix.otherReadMode
+        ]
+#endif
diff --git a/src/Stack/Ide.hs b/src/Stack/Ide.hs
--- a/src/Stack/Ide.hs
+++ b/src/Stack/Ide.hs
@@ -100,6 +100,7 @@
     let ghcOptions bio =
             bioOneWordOpts bio ++
             bioOpts bio ++
+            bioPackageFlags bio ++
             maybe [] (\cabalMacros -> ["-optP-include", "-optP" <> toFilePath cabalMacros]) (bioCabalMacros bio)
     return
         ( ("--dist-dir=" <> toFilePathNoTrailingSep dist) :
diff --git a/src/Stack/Image.hs b/src/Stack/Image.hs
--- a/src/Stack/Image.hs
+++ b/src/Stack/Image.hs
@@ -95,13 +95,17 @@
 imageName :: Path Abs Dir -> String
 imageName = map toLower . toFilePathNoTrailingSep . dirname
 
+mkDockerConfig :: (HasConfig e, MonadReader e m) => m (Maybe ImageDockerOpts)
+mkDockerConfig = do
+  config <- asks getConfig
+  return (imgDocker (configImage config))
+
 -- | Create a general purpose docker image from the temporary
 -- directory of executables & static content.
 createDockerImage :: Assemble e m => Path Abs Dir -> m ()
 createDockerImage dir = do
     menv <- getMinimalEnvOverride
-    config <- asks getConfig
-    let dockerConfig = imgDocker (configImage config)
+    dockerConfig <- mkDockerConfig
     case imgDockerBase =<< dockerConfig of
         Nothing -> throwM StackImageDockerBaseUnspecifiedException
         Just base -> do
@@ -125,8 +129,7 @@
 extendDockerImageWithEntrypoint :: Assemble e m => Path Abs Dir -> m ()
 extendDockerImageWithEntrypoint dir = do
     menv <- getMinimalEnvOverride
-    config <- asks getConfig
-    let dockerConfig = imgDocker (configImage config)
+    dockerConfig <- mkDockerConfig
     let dockerImageName = fromMaybe
                 (imageName (parent (parent dir)))
                 (imgDockerImageName =<< dockerConfig)
diff --git a/src/Stack/Init.hs b/src/Stack/Init.hs
--- a/src/Stack/Init.hs
+++ b/src/Stack/Init.hs
@@ -130,7 +130,7 @@
             \# system-ghc: true\n\n\
             \# Require a specific version of stack, using version ranges\n\
             \# require-stack-version: -any # Default\n\
-            \# require-stack-version: >= 0.1.10.0\n\n\
+            \# require-stack-version: >= 1.0.0\n\n\
             \# Override the architecture used by stack, especially useful on Windows\n\
             \# arch: i386\n\
             \# arch: x86_64\n\n\
diff --git a/src/Stack/New.hs b/src/Stack/New.hs
--- a/src/Stack/New.hs
+++ b/src/Stack/New.hs
@@ -16,6 +16,7 @@
     , listTemplates)
     where
 
+import           Control.Applicative
 import           Control.Monad
 import           Control.Monad.Catch
 import           Control.Monad.IO.Class
@@ -27,9 +28,11 @@
 import qualified Data.ByteString.Lazy as LB
 import qualified Data.ByteString.Lazy.Char8 as L8
 import           Data.Conduit
+import           Data.Foldable (asum)
 import           Data.List
 import           Data.Map.Strict (Map)
 import qualified Data.Map.Strict as M
+import           Data.Maybe (fromMaybe)
 import           Data.Maybe.Extra (mapMaybeM)
 import           Data.Monoid
 import           Data.Set (Set)
@@ -45,6 +48,7 @@
 import           Network.HTTP.Types.Status
 import           Path
 import           Path.IO
+import           Prelude
 import           Stack.Constants
 import           Stack.Types
 import           Stack.Types.TemplateName
@@ -62,7 +66,7 @@
     -- ^ Name of the project to create.
     , newOptsCreateBare   :: Bool
     -- ^ Whether to create the project without a directory.
-    , newOptsTemplate     :: TemplateName
+    , newOptsTemplate     :: Maybe TemplateName
     -- ^ Name of the template to use.
     , newOptsNonceParams  :: Map Text Text
     -- ^ Nonce parameters specified just for this invocation.
@@ -70,7 +74,7 @@
 
 -- | Create a new project with the given options.
 new
-    :: (HasConfig r, MonadReader r m, MonadLogger m, MonadCatch m, MonadThrow m, MonadIO m, HasHttpManager r)
+    :: (HasConfig r, MonadReader r m, MonadLogger m, MonadCatch m, MonadThrow m, MonadIO m, HasHttpManager r, Functor m, Applicative m)
     => NewOpts -> m (Path Abs Dir)
 new opts = do
     pwd <- getWorkingDir
@@ -78,11 +82,14 @@
                       else do relDir <- parseRelDir (packageNameString project)
                               liftM (pwd </>) (return relDir)
     exists <- dirExists absDir
+    configTemplate <- configDefaultTemplate <$> asks getConfig
+    let template = fromMaybe defaultTemplateName $ asum [ cliOptionTemplate
+                                                        , configTemplate
+                                                        ]
     if exists && not bare
         then throwM (AlreadyExists absDir)
         else do
-            logUsing absDir
-            templateText <- loadTemplate template
+            templateText <- loadTemplate template (logUsing absDir template)
             files <-
                 applyTemplate
                     project
@@ -94,12 +101,16 @@
             runTemplateInits absDir
             return absDir
   where
-    template = newOptsTemplate opts
+    cliOptionTemplate = newOptsTemplate opts
     project = newOptsProjectName opts
     bare = newOptsCreateBare opts
-    logUsing absDir =
+    logUsing absDir template templateFrom =
+        let loading = case templateFrom of
+                          LocalTemp -> "Loading local"
+                          RemoteTemp -> "Downloading"
+         in
         $logInfo
-            ("Downloading template \"" <> templateName template <>
+            (loading <> " template \"" <> templateName template <>
              "\" to create project \"" <>
              packageNameText project <>
              "\" in " <>
@@ -107,44 +118,56 @@
                      else T.pack (toFilePath (dirname absDir)) <>
              " ...")
 
+data TemplateFrom = LocalTemp | RemoteTemp
+
 -- | Download and read in a template's text content.
 loadTemplate
     :: forall m r.
-       (HasConfig r, HasHttpManager r, MonadReader r m, MonadIO m, MonadThrow m, MonadCatch m)
-    => TemplateName -> m Text
-loadTemplate name =
+       (HasConfig r, HasHttpManager r, MonadReader r m, MonadIO m, MonadThrow m, MonadCatch m, MonadLogger m, Functor m, Applicative m)
+    => TemplateName -> (TemplateFrom -> m ()) -> m Text
+loadTemplate name logIt = do
+    templateDir <- templatesDir <$> asks getConfig
     case templatePath name of
-        Left absFile -> loadLocalFile absFile
-        Right relFile ->
+        AbsPath absFile -> logIt LocalTemp >> loadLocalFile absFile
+        UrlPath s -> do
+            let req = fromMaybe (error "impossible happened: already valid \
+                                       \URL couldn't be parsed")
+                                (parseUrl s)
+                rel = fromMaybe backupUrlRelPath (parseRelFile s)
+            downloadTemplate req (templateDir </> rel)
+        RelPath relFile ->
             catch
-                (loadLocalFile relFile)
-                (\(_ :: NewException) ->
-                      downloadTemplate relFile)
+                (loadLocalFile relFile <* logIt LocalTemp)
+                (\(e :: NewException) ->
+                      case relRequest relFile of
+                        Just req -> downloadTemplate req
+                                                     (templateDir </> relFile)
+                        Nothing -> throwM e
+                )
   where
     loadLocalFile :: Path b File -> m Text
     loadLocalFile path = do
+        $logDebug ("Opening local template: \"" <> T.pack (toFilePath path)
+                                                <> "\"")
         exists <- fileExists path
         if exists
             then liftIO (T.readFile (toFilePath path))
             else throwM (FailedToLoadTemplate name (toFilePath path))
-    downloadTemplate :: Path Rel File -> m Text
-    downloadTemplate rel = do
-        config <- asks getConfig
-        req <- parseUrl (defaultTemplateUrl <> "/" <> toFilePath rel)
-        let path :: Path Abs File
-            path = templatesDir config </> rel
+    relRequest :: MonadThrow n => Path Rel File -> n Request
+    relRequest rel = parseUrl (defaultTemplateUrl <> "/" <> toFilePath rel)
+    downloadTemplate :: Request -> Path Abs File -> m Text
+    downloadTemplate req path = do
+        logIt RemoteTemp
         _ <-
             catch
                 (redownload req path)
                 (throwM . FailedToDownloadTemplate name)
-        exists <- fileExists path
-        if exists
-            then liftIO (T.readFile (toFilePath path))
-            else throwM (FailedToLoadTemplate name (toFilePath path))
+        loadLocalFile path
+    backupUrlRelPath = $(mkRelFile "downloaded.template.file.hsfiles")
 
 -- | Apply and unpack a template into a directory.
 applyTemplate
-    :: (MonadIO m, MonadThrow m, MonadReader r m, HasConfig r, MonadLogger m)
+    :: (MonadIO m, MonadThrow m, MonadCatch m, MonadReader r m, HasConfig r, MonadLogger m)
     => PackageName
     -> TemplateName
     -> Map Text Text
@@ -160,15 +183,23 @@
     (applied,missingKeys) <-
         runWriterT
             (hastacheStr
-                 defaultConfig
+                 defaultConfig { muEscapeFunc = id }
                  templateText
                  (mkStrContextM (contextFunction context)))
     unless (S.null missingKeys)
          ($logInfo (T.pack (show (MissingParameters project template missingKeys (configUserConfigPath config)))))
     files :: Map FilePath LB.ByteString <-
-        execWriterT $
-        yield (T.encodeUtf8 (LT.toStrict applied)) $$
-        unpackTemplate receiveMem id
+        catch (execWriterT $
+               yield (T.encodeUtf8 (LT.toStrict applied)) $$
+               unpackTemplate receiveMem id
+              )
+              (\(e :: ProjectTemplateException) ->
+                   throwM (InvalidTemplate template (show e)))
+    when (M.null files) $
+         throwM (InvalidTemplate template "Template does not contain any files")
+    unless (any (".cabal" `isSuffixOf`) . M.keys $ files) $
+         throwM (InvalidTemplate template "Template does not contain a .cabal\
+                                          \ file")
     liftM
         M.fromList
         (mapM
@@ -296,6 +327,7 @@
     | BadTemplatesJSON !String !LB.ByteString
     | AlreadyExists !(Path Abs Dir)
     | MissingParameters !PackageName !TemplateName !(Set String) !(Path Abs File)
+    | InvalidTemplate !TemplateName !String
     deriving (Typeable)
 
 instance Exception NewException
@@ -350,3 +382,7 @@
                        (\key ->
                              "-p \"" <> key <> ":value\"")
                        (S.toList missingKeys))]
+    show (InvalidTemplate name why) =
+        "The template \"" <> T.unpack (templateName name) <>
+        "\" is invalid and could not be used. " <>
+        "The error was: \"" <> why <> "\""
diff --git a/src/Stack/Nix.hs b/src/Stack/Nix.hs
--- a/src/Stack/Nix.hs
+++ b/src/Stack/Nix.hs
@@ -1,14 +1,19 @@
 {-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
 
 -- | Run commands in a nix-shell
 module Stack.Nix
   (reexecWithOptionalShell
   ,nixCmdName
+  ,nixHelpOptName
   ) where
 
 import           Control.Applicative
+import           Control.Arrow ((***))
+import           Control.Exception (Exception,throw)
 import           Control.Monad
 import           Control.Monad.Catch (try,MonadCatch)
 import           Control.Monad.IO.Class (MonadIO,liftIO)
@@ -17,15 +22,19 @@
 import           Control.Monad.Trans.Control (MonadBaseControl)
 import           Data.Char (toUpper)
 import           Data.List (intercalate)
+import           Data.Traversable
 import           Data.Maybe
 import           Data.Monoid
 import           Data.Streaming.Process (ProcessExitedUnsuccessfully(..))
 import qualified Data.Text as T
 import           Data.Version (showVersion)
+import           Data.Typeable (Typeable)
 import           Network.HTTP.Client.Conduit (HasHttpManager)
+import           Path
+import           Path.IO
 import qualified Paths_stack as Meta
 import           Prelude -- Fix redundant import warnings
-import           Stack.Constants (stackProgName)
+import           Stack.Constants (stackProgName,platformVariantEnvVar)
 import           Stack.Docker (reExecArgName)
 import           Stack.Exec (exec)
 import           System.Process.Read (getEnvOverride)
@@ -39,14 +48,15 @@
 -- Otherwise, runs the inner action.
 reexecWithOptionalShell
     :: M env m
-    => IO ()
+    => Maybe (Path Abs Dir)
+    -> IO ()
     -> m ()
-reexecWithOptionalShell inner =
+reexecWithOptionalShell mprojectRoot inner =
   do config <- asks getConfig
      inShell <- getInShell
      isReExec <- asks getReExec
      if nixEnable (configNix config) && not inShell && not isReExec
-       then runShellAndExit getCmdArgs
+       then runShellAndExit mprojectRoot getCmdArgs
        else liftIO inner
   where
     getCmdArgs = do
@@ -57,22 +67,28 @@
         exePath <- liftIO getExecutablePath
         return (exePath, args)
 
-runShellAndExit :: M env m
-                => m (String, [String])
-                -> m ()
-runShellAndExit getCmdArgs = do
+runShellAndExit
+    :: M env m
+    => Maybe (Path Abs Dir)
+    -> m (String, [String])
+    -> m ()
+runShellAndExit mprojectRoot getCmdArgs = do
      config <- asks getConfig
      envOverride <- getEnvOverride (configPlatform config)
-     (cmnd,args) <- getCmdArgs
-     let mshellFile = nixInitFile (configNix config)
-         pkgsInConfig = nixPackages (configNix config)
+     (cmnd,args) <- fmap (escape *** map escape) getCmdArgs
+     mshellFile <-
+         traverse (resolveFile (fromMaybeProjectRoot mprojectRoot)) $
+         nixInitFile (configNix config)
+     let pkgsInConfig = nixPackages (configNix config)
+         pureShell = nixPureShell (configNix config)
          nixopts = case mshellFile of
-           Just filePath -> [filePath]
+           Just fp -> [toFilePath fp]
            Nothing -> ["-E", T.unpack $ T.intercalate " " $ concat
                               [["with (import <nixpkgs> {});"
                                ,"runCommand \"myEnv\" {"
                                ,"buildInputs=lib.optional stdenv.isLinux glibcLocales ++ ["],pkgsInConfig,["];"
-                               ,T.pack inShellEnvVar,"=1 ;"
+                               ,T.pack platformVariantEnvVar <> "=''nix'';"
+                               ,T.pack inShellEnvVar <> "=1;"
                                ,"STACK_IN_NIX_EXTRA_ARGS=''"]
                                ,      (map (\p -> T.concat
                                                   ["--extra-lib-dirs=${",p,"}/lib"
@@ -80,15 +96,15 @@
                                            pkgsInConfig), ["'' ;"
                                ,"} \"\""]]]
                     -- glibcLocales is necessary on Linux to avoid warnings about GHC being incapable to set the locale.
-         fullArgs = concat [ -- ["--pure"],
+         fullArgs = concat [if pureShell then ["--pure"] else [],
                             map T.unpack (nixShellOptions (configNix config))
                            ,nixopts
-                           ,["--command", intercalate " " (map escape (cmnd:args))
-                                          ++ " $STACK_IN_NIX_EXTRA_ARGS"]
+                           ,["--command", intercalate " " (cmnd:"$STACK_IN_NIX_EXTRA_ARGS":args)]
                            ]
+
      $logDebug $
          "Using a nix-shell environment " <> (case mshellFile of
-            Just filePath -> "from file: " <> (T.pack filePath)
+            Just path -> "from file: " <> (T.pack (toFilePath path))
             Nothing -> "with nix packages: " <> (T.intercalate ", " pkgsInConfig))
      e <- try (exec envOverride "nix-shell" fullArgs)
      case e of
@@ -102,6 +118,10 @@
                                  else (c:)) "" str
                  ++ "'"
 
+-- | Fail with friendly error if project root not set.
+fromMaybeProjectRoot :: Maybe (Path Abs Dir) -> Path Abs Dir
+fromMaybeProjectRoot = fromMaybe (throw CannotDetermineProjectRootException)
+
 -- | 'True' if we are currently running inside a Nix.
 getInShell :: (MonadIO m) => m Bool
 getInShell = liftIO (isJust <$> lookupEnv inShellEnvVar)
@@ -115,6 +135,21 @@
 -- | Command-line argument for "nix"
 nixCmdName :: String
 nixCmdName = "nix"
+
+nixHelpOptName :: String
+nixHelpOptName = nixCmdName ++ "-help"
+
+-- | Exceptions thrown by "Stack.Nix".
+data StackNixException
+  = CannotDetermineProjectRootException
+    -- ^ Can't determine the project root (location of the shell file if any).
+  deriving (Typeable)
+
+instance Exception StackNixException
+
+instance Show StackNixException where
+  show CannotDetermineProjectRootException =
+    "Cannot determine project root directory."
 
 type M env m =
   (MonadIO m
diff --git a/src/Stack/Options.hs b/src/Stack/Options.hs
--- a/src/Stack/Options.hs
+++ b/src/Stack/Options.hs
@@ -80,13 +80,51 @@
 buildOptsParser :: Command
                 -> Parser BuildOpts
 buildOptsParser cmd =
+            transform <$> trace <*> profile <*> options
+  where transform tracing profiling = enable
+          where enable opts
+                  | tracing || profiling =
+                      opts {boptsLibProfile = True
+                           ,boptsExeProfile = True
+                           ,boptsGhcOptions = ["-auto-all","-caf-all"]
+                           ,boptsBenchmarkOpts =
+                                bopts {beoAdditionalArgs =
+                                           beoAdditionalArgs bopts <>
+                                           Just (unwords additionalArgs)}
+                           ,boptsTestOpts =
+                                topts {toAdditionalArgs =
+                                           (toAdditionalArgs topts) <>
+                                           additionalArgs}}
+                  | otherwise = opts
+                  where bopts = boptsBenchmarkOpts opts
+                        topts = boptsTestOpts opts
+                        additionalArgs = [" +RTS ", trac, prof]
+                        trac = if tracing
+                                  then " -xc "
+                                  else ""
+                        prof = if profiling
+                                  then " -p "
+                                  else ""
+        profile =
+            flag False True
+             ( long "profile"
+            <> help "Enable profiling in libraries, executables, etc. \
+                    \for all expressions and generate a profiling report\
+                    \ in exec or benchmarks")
+        trace =
+            flag False True
+             ( long "trace"
+            <> help "Enable profiling in libraries, executables, etc. \
+                    \for all expressions and generate a backtrace on \
+                    \exception")
+        options =
             BuildOpts <$> target <*> libProfiling <*> exeProfiling <*>
             haddock <*> haddockDeps <*> dryRun <*> ghcOpts <*>
             flags <*> copyBins <*> preFetch <*> buildSubset <*>
             fileWatch' <*> keepGoing <*> forceDirty <*> tests <*>
             testOptsParser <*> benches <*> benchOptsParser <*>
             many exec <*> onlyConfigure <*> reconfigure <*> cabalVerbose
-  where target =
+        target =
            many (textArgument
                    (metavar "TARGET" <>
                     help "If none specified, use all packages"))
@@ -320,22 +358,40 @@
   where hide = hideMods hide0
 
 nixOptsParser :: Bool -> Parser NixOptsMonoid
-nixOptsParser hide0 =
-  NixOptsMonoid
+nixOptsParser hide0 = overrideActivation <$>
+  (NixOptsMonoid
   <$> pure False
   <*> maybeBoolFlags nixCmdName
-                     "using a Nix-shell"
+                     "use of a Nix-shell"
                      hide
-  <*> pure []
-  <*> pure Nothing
-  <*> ((map T.pack . fromMaybe [])
+  <*> maybeBoolFlags "nix-pure"
+                     "use of a pure Nix-shell"
+                     hide
+  <*> (fmap (map T.pack)
+       <$> optional (argsOption (long "nix-packages" <>
+                                 metavar "NAMES" <>
+                                 help "List of packages that should be available in the nix-shell (space separated)" <>
+                                 hide)))
+  <*> optional (option str (long "nix-shell-file" <>
+                            metavar "FILEPATH" <>
+                            help "Nix file to be used to launch a nix-shell (for regular Nix users)" <>
+                            hide))
+  <*> (fmap (map T.pack)
        <$> optional (argsOption (long "nix-shell-options" <>
-                                 metavar "OPTION" <>
+                                 metavar "OPTIONS" <>
                                  help "Additional options passed to nix-shell" <>
                                  hide)))
+  <*> (fmap (map T.pack)
+       <$> optional (argsOption (long "nix-path" <>
+                                 metavar "PATH_OPTIONS" <>
+                                 help "Additional options to override NIX_PATH parts (notably 'nixpkgs')" <>
+                                 hide)))
+  )
   where
     hide = hideMods hide0
-
+    overrideActivation m =
+      if m /= mempty then m { nixMonoidEnable = Just True }
+      else m
 
 -- | Options parser configuration for Docker.
 dockerOptsParser :: Bool -> Parser DockerOptsMonoid
@@ -397,7 +453,7 @@
                         hide <>
                         metavar "PATH" <>
                         help "Location of image usage tracking database")
-    <*> optional (option str
+    <*> maybeStrOption
             (long(dockerOptName dockerStackExeArgName) <>
              hide <>
              metavar (intercalate "|"
@@ -407,7 +463,7 @@
                           , "PATH" ]) <>
              help (concat [ "Location of "
                           , stackProgName
-                          , " executable used in container" ])))
+                          , " executable used in container" ]))
     <*> maybeBoolFlags (dockerOptName dockerSetUserArgName)
                        "setting user in container to match host"
                        hide
@@ -513,6 +569,7 @@
                                  \module to load, such as for an executable for \
                                  \test suite or benchmark."))
              <*> switch (long "skip-intermediate-deps" <> help "Skip loading intermediate target dependencies")
+             <*> boolFlags True "package-hiding" "package hiding" idm
              <*> buildOptsParser Build
 
 -- | Parser for exec command
@@ -565,12 +622,12 @@
                            help "Use an unmodified environment (only useful with Docker)")
 
 -- | Parser for global command-line options.
-globalOptsParser :: Bool -> Parser GlobalOptsMonoid
-globalOptsParser hide0 =
+globalOptsParser :: Bool -> Maybe LogLevel -> Parser GlobalOptsMonoid
+globalOptsParser hide0 defLogLevel =
     GlobalOptsMonoid <$>
     optional (strOption (long Docker.reExecArgName <> hidden <> internal)) <*>
     optional (option auto (long dockerEntrypointArgName <> hidden <> internal)) <*>
-    logLevelOptsParser hide0 <*>
+    logLevelOptsParser hide0 defLogLevel <*>
     configOptsParser hide0 <*>
     optional (abstractResolverOptsParser hide0) <*>
     optional (compilerOptsParser hide0) <*>
@@ -629,8 +686,8 @@
          help "Use the given resolver, even if not all dependencies are met")
 
 -- | Parser for a logging level.
-logLevelOptsParser :: Bool -> Parser (Maybe LogLevel)
-logLevelOptsParser hide =
+logLevelOptsParser :: Bool -> Maybe LogLevel -> Parser (Maybe LogLevel)
+logLevelOptsParser hide defLogLevel =
   fmap (Just . parse)
        (strOption (long "verbosity" <>
                    metavar "VERBOSITY" <>
@@ -640,7 +697,7 @@
        (short 'v' <> long "verbose" <>
         help ("Enable verbose mode: verbosity level \"" <> showLevel verboseLevel <> "\"") <>
         hideMods hide) <|>
-  pure Nothing
+  pure defLogLevel
   where verboseLevel = LevelDebug
         showLevel l =
           case l of
@@ -746,11 +803,11 @@
         switch
             (long "bare" <>
              help "Do not create a subdirectory for the project") <*>
-        templateNameArgument
+        optional (templateNameArgument
             (metavar "TEMPLATE_NAME" <>
-             help "Name of a template or a local template in a subdirectory,\
-                  \ for example: foo or foo.hsfiles" <>
-             value defaultTemplateName) <*>
+             help "Name of a template or a local template in a file or a URL.\
+                  \ For example: foo or foo.hsfiles or ~/foo or\
+                  \ https://example.com/foo.hsfiles")) <*>
         fmap
             M.fromList
             (many
diff --git a/src/Stack/Package.hs b/src/Stack/Package.hs
--- a/src/Stack/Package.hs
+++ b/src/Stack/Package.hs
@@ -316,7 +316,8 @@
         --
         -- See https://github.com/commercialhaskell/stack/issues/1255
         , bioOneWordOpts = nubOrd $ concat
-            [extOpts b, srcOpts, includeOpts, deps, extra b, extraDirs, fworks b, cObjectFiles]
+            [extOpts b, srcOpts, includeOpts, extra b, extraDirs, fworks b, cObjectFiles]
+        , bioPackageFlags = deps
         , bioCabalMacros = mcabalMacros
         }
   where
@@ -348,7 +349,9 @@
             (("-i" <>) . toFilePathNoTrailingSep)
             ([cabalDir | null (hsSourceDirs b)] <>
              mapMaybe toIncludeDir (hsSourceDirs b) <>
-             [autogenDir distDir,buildDir distDir]) ++
+             [autogenDir distDir,buildDir distDir] <>
+             [makeGenDir (buildDir distDir)
+             | Just makeGenDir <- [fileGenDirFromComponentName componentName]]) ++
         ["-stubdir=" ++ toFilePathNoTrailingSep (buildDir distDir)]
     toIncludeDir "." = Just cabalDir
     toIncludeDir x = fmap (cabalDir </>) (parseRelDir x)
@@ -410,19 +413,23 @@
     relCFilePath <- stripDir cabalDir cFilePath
     relOFilePath <-
         parseRelFile (replaceExtension (toFilePath relCFilePath) "o")
-    addComponentPrefix <- fromComponentName
+    addComponentPrefix <- fileGenDirFromComponentName namedComponent
     return (addComponentPrefix (buildDir distDir) </> relOFilePath)
-  where
-    fromComponentName =
-        case namedComponent of
-            CLib -> return id
-            CExe name -> makeTmp name
-            CTest name -> makeTmp name
-            CBench name -> makeTmp name
-    makeTmp name = do
-        prefix <- parseRelDir (T.unpack name <> "/" <> T.unpack name <> "-tmp")
-        return (</> prefix)
 
+-- | The directory where generated files are put like .o or .hs (from .x files).
+fileGenDirFromComponentName
+    :: MonadThrow m
+    => NamedComponent -> m (Path b Dir -> Path b Dir)
+fileGenDirFromComponentName namedComponent =
+    case namedComponent of
+        CLib -> return id
+        CExe name -> makeTmp name
+        CTest name -> makeTmp name
+        CBench name -> makeTmp name
+  where makeTmp name = do
+            prefix <- parseRelDir (T.unpack name <> "/" <> T.unpack name <> "-tmp")
+            return (</> prefix)
+
 -- | Make the autogen dir.
 autogenDir :: Path Abs Dir -> Path Abs Dir
 autogenDir distDir = buildDir distDir </> $(mkRelDir "autogen")
@@ -445,10 +452,10 @@
   allBuildInfo'
 
 -- | Get all build tool dependencies of the package (buildable targets only).
-packageToolDependencies :: PackageDescription -> Map BS.ByteString VersionRange
+packageToolDependencies :: PackageDescription -> Map Text VersionRange
 packageToolDependencies =
   M.fromList .
-  concatMap (fmap (packageNameByteString . depName &&& depRange) .
+  concatMap (fmap (packageNameText . depName &&& depRange) .
              buildTools) .
   allBuildInfo'
 
diff --git a/src/Stack/PackageDump.hs b/src/Stack/PackageDump.hs
--- a/src/Stack/PackageDump.hs
+++ b/src/Stack/PackageDump.hs
@@ -35,12 +35,9 @@
 import           Data.Attoparsec.Args
 import           Data.Attoparsec.Text as P
 import           Data.Binary.VersionTagged
-import           Data.ByteString (ByteString)
-import qualified Data.ByteString as S
-import qualified Data.ByteString.Char8 as S8
 import           Data.Conduit
-import qualified Data.Conduit.Binary as CB
 import qualified Data.Conduit.List as CL
+import qualified Data.Conduit.Text as CT
 import           Data.Either (partitionEithers)
 import           Data.IORef
 import           Data.Map (Map)
@@ -48,7 +45,8 @@
 import           Data.Maybe (catMaybes, listToMaybe)
 import           Data.Maybe.Extra (mapMaybeM)
 import qualified Data.Set as Set
-import qualified Data.Text.Encoding as T
+import qualified Data.Text as T
+import           Data.Text (Text)
 import           Data.Typeable (Typeable)
 import           GHC.Generics (Generic)
 import           Path
@@ -83,7 +81,7 @@
     => EnvOverride
     -> WhichCompiler
     -> [Path Abs Dir] -- ^ if empty, use global
-    -> Sink ByteString IO a
+    -> Sink Text IO a
     -> m a
 ghcPkgDump = ghcPkgCmdArgs ["dump"]
 
@@ -94,7 +92,7 @@
     -> EnvOverride
     -> WhichCompiler
     -> [Path Abs Dir] -- ^ if empty, use global
-    -> Sink ByteString IO a
+    -> Sink Text IO a
     -> m a
 ghcPkgDescribe pkgName = ghcPkgCmdArgs ["describe", "--simple-output", packageNameString pkgName]
 
@@ -105,13 +103,13 @@
     -> EnvOverride
     -> WhichCompiler
     -> [Path Abs Dir] -- ^ if empty, use global
-    -> Sink ByteString IO a
+    -> Sink Text IO a
     -> m a
 ghcPkgCmdArgs cmd menv wc mpkgDbs sink = do
     case reverse mpkgDbs of
         (pkgDb:_) -> createDatabase menv wc pkgDb -- TODO maybe use some retry logic instead?
         _ -> return ()
-    sinkProcessStdout Nothing menv (ghcPkgExeName wc) args sink
+    sinkProcessStdout Nothing menv (ghcPkgExeName wc) args sink'
   where
     args = concat
         [ case mpkgDbs of
@@ -121,6 +119,7 @@
         , cmd
         , ["--expand-pkgroot"]
         ]
+    sink' = CT.decodeUtf8 =$= sink
 
 -- | Create a new, empty @InstalledCache@
 newInstalledCache :: MonadIO m => m InstalledCache
@@ -233,12 +232,12 @@
         return dp { dpProfiling = p }
 
 isProfiling :: FilePath -- ^ entry in directory
-            -> ByteString -- ^ name of library
+            -> Text -- ^ name of library
             -> Bool
 isProfiling content lib =
-    prefix `S.isPrefixOf` S8.pack content
+    prefix `T.isPrefixOf` T.pack content
   where
-    prefix = S.concat ["lib", lib, "_p"]
+    prefix = T.concat ["lib", lib, "_p"]
 
 -- | Add haddock information to the stream of @DumpPackage@s
 addHaddock :: MonadIO m
@@ -268,7 +267,7 @@
     { dpGhcPkgId :: !GhcPkgId
     , dpPackageIdent :: !PackageIdentifier
     , dpLibDirs :: ![FilePath]
-    , dpLibraries :: ![ByteString]
+    , dpLibraries :: ![Text]
     , dpHasExposedModules :: !Bool
     , dpDepends :: ![GhcPkgId]
     , dpHaddockInterfaces :: ![FilePath]
@@ -280,8 +279,8 @@
     deriving (Show, Eq, Ord)
 
 data PackageDumpException
-    = MissingSingleField ByteString (Map ByteString [Line])
-    | Couldn'tParseField ByteString [Line]
+    = MissingSingleField Text (Map Text [Line])
+    | Couldn'tParseField Text [Line]
     deriving Typeable
 instance Exception PackageDumpException
 instance Show PackageDumpException where
@@ -298,7 +297,7 @@
 
 -- | Convert a stream of bytes into a stream of @DumpPackage@s
 conduitDumpPackage :: MonadThrow m
-                   => Conduit ByteString m (DumpPackage () ())
+                   => Conduit Text m (DumpPackage () ())
 conduitDumpPackage = (=$= CL.catMaybes) $ eachSection $ do
     pairs <- eachPair (\k -> (k, ) <$> CL.consume) =$= CL.consume
     let m = Map.fromList pairs
@@ -310,15 +309,15 @@
         -- https://github.com/fpco/stack/issues/182
         parseM k = Map.findWithDefault [] k m
 
-        parseDepend :: MonadThrow m => ByteString -> m (Maybe GhcPkgId)
+        parseDepend :: MonadThrow m => Text -> m (Maybe GhcPkgId)
         parseDepend "builtin_rts" = return Nothing
         parseDepend bs =
             liftM Just $ parseGhcPkgId bs'
           where
             (bs', _builtinRts) =
-                case stripSuffixBS " builtin_rts" bs of
+                case stripSuffixText " builtin_rts" bs of
                     Nothing ->
-                        case stripPrefixBS "builtin_rts " bs of
+                        case stripPrefixText "builtin_rts " bs of
                             Nothing -> (bs, False)
                             Just x -> (x, True)
                     Just x -> (x, True)
@@ -334,10 +333,10 @@
                 libraries = parseM "hs-libraries"
                 exposedModules = parseM "exposed-modules"
                 exposed = parseM "exposed"
-            depends <- mapMaybeM parseDepend $ parseM "depends"
+            depends <- mapMaybeM parseDepend $ concatMap T.words $ parseM "depends"
 
             let parseQuoted key =
-                    case mapM (P.parseOnly (argsParser NoEscaping) . T.decodeUtf8) val of
+                    case mapM (P.parseOnly (argsParser NoEscaping)) val of
                         Left{} -> throwM (Couldn'tParseField key val)
                         Right dirs -> return (concat dirs)
                   where
@@ -350,7 +349,7 @@
                 { dpGhcPkgId = ghcPkgId
                 , dpPackageIdent = PackageIdentifier name version
                 , dpLibDirs = libDirPaths
-                , dpLibraries = S8.words $ S8.unwords libraries
+                , dpLibraries = T.words $ T.unwords libraries
                 , dpHasExposedModules = not (null libraries || null exposedModules)
                 , dpDepends = depends
                 , dpHaddockInterfaces = haddockInterfaces
@@ -360,34 +359,33 @@
                 , dpIsExposed = exposed == ["True"]
                 }
 
-stripPrefixBS :: ByteString -> ByteString -> Maybe ByteString
-stripPrefixBS x y
-    | x `S.isPrefixOf` y = Just $ S.drop (S.length x) y
+stripPrefixText :: Text -> Text -> Maybe Text
+stripPrefixText x y
+    | x `T.isPrefixOf` y = Just $ T.drop (T.length x) y
     | otherwise = Nothing
 
-stripSuffixBS :: ByteString -> ByteString -> Maybe ByteString
-stripSuffixBS x y
-    | x `S.isSuffixOf` y = Just $ S.take (S.length y - S.length x) y
+stripSuffixText :: Text -> Text -> Maybe Text
+stripSuffixText x y
+    | x `T.isSuffixOf` y = Just $ T.take (T.length y - T.length x) y
     | otherwise = Nothing
 
 -- | A single line of input, not including line endings
-type Line = ByteString
+type Line = Text
 
 -- | Apply the given Sink to each section of output, broken by a single line containing ---
 eachSection :: Monad m
             => Sink Line m a
-            -> Conduit ByteString m a
+            -> Conduit Text m a
 eachSection inner =
-    CL.map (S.filter (/= _cr)) =$= CB.lines =$= start
+    CL.map (T.filter (/= '\r')) =$= CT.lines =$= start
   where
-    _cr = 13
 
-    peekBS = await >>= maybe (return Nothing) (\bs ->
-        if S.null bs
-            then peekBS
+    peekText = await >>= maybe (return Nothing) (\bs ->
+        if T.null bs
+            then peekText
             else leftover bs >> return (Just bs))
 
-    start = peekBS >>= maybe (return ()) (const go)
+    start = peekText >>= maybe (return ()) (const go)
 
     go = do
         x <- toConsumer $ takeWhileC (/= "---") =$= inner
@@ -397,25 +395,22 @@
 
 -- | Grab each key/value pair
 eachPair :: Monad m
-         => (ByteString -> Sink Line m a)
+         => (Text -> Sink Line m a)
          -> Conduit Line m a
 eachPair inner =
     start
   where
     start = await >>= maybe (return ()) start'
 
-    _colon = 58
-    _space = 32
-
     start' bs1 =
         toConsumer (valSrc =$= inner key) >>= yield >> start
       where
-        (key, bs2) = S.break (== _colon) bs1
-        (spaces, bs3) = S.span (== _space) $ S.drop 1 bs2
-        indent = S.length key + 1 + S.length spaces
+        (key, bs2) = T.break (== ':') bs1
+        (spaces, bs3) = T.span (== ' ') $ T.drop 1 bs2
+        indent = T.length key + 1 + T.length spaces
 
         valSrc
-            | S.null bs3 = noIndent
+            | T.null bs3 = noIndent
             | otherwise = yield bs3 >> loopIndent indent
 
     noIndent = do
@@ -423,12 +418,12 @@
         case mx of
             Nothing -> return ()
             Just bs -> do
-                let (spaces, val) = S.span (== _space) bs
-                if S.length spaces == 0
+                let (spaces, val) = T.span (== ' ') bs
+                if T.length spaces == 0
                     then leftover val
                     else do
                         yield val
-                        loopIndent (S.length spaces)
+                        loopIndent (T.length spaces)
 
     loopIndent i =
         loop
@@ -436,11 +431,11 @@
         loop = await >>= maybe (return ()) go
 
         go bs
-            | S.length spaces == i && S.all (== _space) spaces =
+            | T.length spaces == i && T.all (== ' ') spaces =
                 yield val >> loop
             | otherwise = leftover bs
           where
-            (spaces, val) = S.splitAt i bs
+            (spaces, val) = T.splitAt i bs
 
 -- | General purpose utility
 takeWhileC :: Monad m => (a -> Bool) -> Conduit a m a
diff --git a/src/Stack/PackageIndex.hs b/src/Stack/PackageIndex.hs
--- a/src/Stack/PackageIndex.hs
+++ b/src/Stack/PackageIndex.hs
@@ -34,10 +34,6 @@
 
 import           Data.Aeson.Extended
 import           Data.Binary.VersionTagged
-import qualified Data.Word8 as Word8
-import qualified Data.ByteString as S
-import qualified Data.ByteString.Unsafe as SU
-import qualified Data.ByteString.Char8 as S8
 import qualified Data.ByteString.Lazy as L
 import           Data.Conduit (($$), (=$))
 import           Data.Conduit.Binary                   (sinkHandle,
@@ -50,6 +46,7 @@
 import           Data.Monoid
 import           Data.Text (Text)
 import qualified Data.Text as T
+import           Data.Text.Unsafe (unsafeTail)
 
 import           Data.Traversable (forM)
 
@@ -139,19 +136,19 @@
                     m
 
     breakSlash x
-        | S.null z = Nothing
-        | otherwise = Just (y, SU.unsafeTail z)
+        | T.null z = Nothing
+        | otherwise = Just (y, unsafeTail z)
       where
-        (y, z) = S.break (== Word8._slash) x
+        (y, z) = T.break (== '/') x
 
     parseNameVersion t1 = do
         (p', t3) <- breakSlash
-                  $ S.map (\c -> if c == Word8._backslash then Word8._slash else c)
-                  $ S8.pack t1
+                  $ T.map (\c -> if c == '\\' then '/' else c)
+                  $ T.pack t1
         p <- parsePackageName p'
         (v', t5) <- breakSlash t3
         v <- parseVersion v'
-        let (t6, suffix) = S.break (== Word8._period) t5
+        let (t6, suffix) = T.break (== '.') t5
         if t6 == p'
             then return (PackageIdentifier p v, suffix)
             else Nothing
diff --git a/src/Stack/Setup.hs b/src/Stack/Setup.hs
--- a/src/Stack/Setup.hs
+++ b/src/Stack/Setup.hs
@@ -211,9 +211,8 @@
     -- Modify the initial environment to include the GHC path, if a local GHC
     -- is being used
     menv0 <- getMinimalEnvOverride
-    let env = removeHaskellEnvVars
-            $ augmentPathMap (maybe [] edBins mghcBin)
-            $ unEnvOverride menv0
+    env <- removeHaskellEnvVars
+             <$> augmentPathMap (maybe [] edBins mghcBin) (unEnvOverride menv0)
 
     menv <- mkEnvOverride platform env
     compilerVer <- getCompilerVersion menv wc
@@ -232,8 +231,8 @@
     mkDirs <- runReaderT extraBinDirs envConfig0
     let mpath = Map.lookup "PATH" env
         mkDirs' = map toFilePath . mkDirs
-        depsPath = augmentPath (mkDirs' False) mpath
-        localsPath = augmentPath (mkDirs' True) mpath
+    depsPath <- augmentPath (mkDirs' False) mpath
+    localsPath <- augmentPath (mkDirs' True) mpath
 
     deps <- runReaderT packageDatabaseDeps envConfig0
     createDatabase menv wc deps
@@ -417,7 +416,7 @@
             Nothing -> return menv0
             Just ed -> do
                 config <- asks getConfig
-                let m = augmentPathMap (edBins ed) (unEnvOverride menv0)
+                m <- augmentPathMap (edBins ed) (unEnvOverride menv0)
                 mkEnvOverride (configPlatform config) (removeHaskellEnvVars m)
 
     when (soptsUpgradeCabal sopts) $ do
@@ -998,7 +997,7 @@
     ebs <- tryProcessStdout Nothing menv "cabal" ["--numeric-version"]
     case ebs of
         Left _ -> return Nothing
-        Right bs -> Just <$> parseVersion (T.encodeUtf8 (T.dropWhileEnd isSpace (T.decodeUtf8 bs)))
+        Right bs -> Just <$> parseVersion (T.dropWhileEnd isSpace (T.decodeUtf8 bs))
 
 -- | Check if given processes appear to be present, throwing an exception if
 -- missing.
@@ -1066,10 +1065,9 @@
 
     platform <- asks getPlatform
     menv0 <- getMinimalEnvOverride
-    let oldEnv = unEnvOverride menv0
-        newEnv = augmentPathMap
-            [toFilePath $ destDir </> $(mkRelDir "usr") </> $(mkRelDir "bin")]
-            oldEnv
+    newEnv <- augmentPathMap [toFilePath $ destDir </> $(mkRelDir "usr")
+                                                   </> $(mkRelDir "bin")]
+                             (unEnvOverride menv0)
     menv <- mkEnvOverride platform newEnv
 
     -- I couldn't find this officially documented anywhere, but you need to run
diff --git a/src/Stack/Setup/Installed.hs b/src/Stack/Setup/Installed.hs
--- a/src/Stack/Setup/Installed.hs
+++ b/src/Stack/Setup/Installed.hs
@@ -28,6 +28,7 @@
 import           Data.Monoid
 import           Data.Text (Text)
 import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
 import           Distribution.System (Platform (..))
 import qualified Distribution.System as Cabal
 import           Path
@@ -89,14 +90,14 @@
         Ghc -> do
             bs <- readProcessStdout Nothing menv "ghc" ["--numeric-version"]
             let (_, ghcVersion) = versionFromEnd bs
-            GhcVersion <$> parseVersion ghcVersion
+            GhcVersion <$> parseVersion (T.decodeUtf8 ghcVersion)
         Ghcjs -> do
             -- Output looks like
             --
             -- The Glorious Glasgow Haskell Compilation System for JavaScript, version 0.1.0 (GHC 7.10.2)
             bs <- readProcessStdout Nothing menv "ghcjs" ["--version"]
-            let (rest, ghcVersion) = versionFromEnd bs
-                (_, ghcjsVersion) = versionFromEnd rest
+            let (rest, ghcVersion) = T.decodeUtf8 <$> versionFromEnd bs
+                (_, ghcjsVersion) = T.decodeUtf8 <$> versionFromEnd rest
             GhcjsVersion <$> parseVersion ghcjsVersion <*> parseVersion ghcVersion
   where
     versionFromEnd = S8.spanEnd isValid . fst . S8.breakEnd isValid
diff --git a/src/Stack/Solver.hs b/src/Stack/Solver.hs
--- a/src/Stack/Solver.hs
+++ b/src/Stack/Solver.hs
@@ -69,9 +69,9 @@
                         $logInfo $ "No GHC on path, selecting: " <>
                                    T.pack (versionString version)
                         ed <- extraDirs $ Tool $ PackageIdentifier ghcName version
-                        mkEnvOverride platform
-                            $ augmentPathMap (edBins ed)
-                            $ unEnvOverride menv0
+                        pathsEnv <- augmentPathMap (edBins ed)
+                                                   (unEnvOverride menv0)
+                        mkEnvOverride platform pathsEnv
     mcabal <- findExecutable menv "cabal"
     case mcabal of
         Nothing -> throwM SolverMissingCabalInstall
diff --git a/src/Stack/Types/Build.hs b/src/Stack/Types/Build.hs
--- a/src/Stack/Types/Build.hs
+++ b/src/Stack/Types/Build.hs
@@ -110,7 +110,7 @@
         (Path Abs File)  -- cabal Executable
         [String]         -- cabal arguments
         (Maybe (Path Abs File)) -- logfiles location
-        [S.ByteString]     -- log contents
+        [Text]     -- log contents
   | ExecutionFailure [SomeException]
   | LocalPackageDoesn'tMatchTarget
         PackageName
@@ -256,7 +256,7 @@
            logLocations ++
            (if null bss
                 then ""
-                else "\n\n" ++ doubleIndent (map (T.unpack . decodeUtf8With lenientDecode) bss))
+                else "\n\n" ++ doubleIndent (map T.unpack bss))
          where
           doubleIndent = dropWhileEnd isSpace . unlines . fmap (\line -> "    " ++ line)
           dropQuotes = filter ('\"' /=)
diff --git a/src/Stack/Types/BuildPlan.hs b/src/Stack/Types/BuildPlan.hs
--- a/src/Stack/Types/BuildPlan.hs
+++ b/src/Stack/Types/BuildPlan.hs
@@ -32,8 +32,6 @@
                                                   object, withObject, withText,
                                                   (.!=), (.:), (.:?), (.=))
 import           Data.Binary.VersionTagged
-import           Data.ByteString                 (ByteString)
-import qualified Data.ByteString.Char8           as S8
 import           Data.Hashable                   (Hashable)
 import qualified Data.HashMap.Strict             as HashMap
 import           Data.Map                        (Map)
@@ -44,7 +42,6 @@
 import           Data.String                     (IsString, fromString)
 import           Data.Text                       (Text, pack, unpack)
 import qualified Data.Text                       as T
-import           Data.Text.Encoding              (encodeUtf8)
 import Data.Text.Read (decimal)
 import           Data.Time                       (Day)
 import qualified Data.Traversable                as T
@@ -248,13 +245,13 @@
     deriving (Show, Eq, Ord, Hashable, ToJSON, FromJSON, IsString)
 
 -- | Name of an executable.
-newtype ExeName = ExeName { unExeName :: ByteString }
+newtype ExeName = ExeName { unExeName :: Text }
     deriving (Show, Eq, Ord, Hashable, IsString, Generic, Binary, NFData)
 instance HasStructuralInfo ExeName
 instance ToJSON ExeName where
-    toJSON = toJSON . S8.unpack . unExeName
+    toJSON = toJSON . unExeName
 instance FromJSON ExeName where
-    parseJSON = withText "ExeName" $ return . ExeName . encodeUtf8
+    parseJSON = withText "ExeName" $ return . ExeName
 
 -- | A simplified package description that tracks:
 --
@@ -363,9 +360,9 @@
         Nightly <$> readMay (T.unpack t1)
 
 instance ToJSON a => ToJSON (Map ExeName a) where
-  toJSON = toJSON . Map.mapKeysWith const (S8.unpack . unExeName)
+  toJSON = toJSON . Map.mapKeysWith const unExeName
 instance FromJSON a => FromJSON (Map ExeName a) where
-    parseJSON = fmap (Map.mapKeysWith const (ExeName . encodeUtf8)) . parseJSON
+    parseJSON = fmap (Map.mapKeysWith const ExeName) . parseJSON
 
 -- | A simplified version of the 'BuildPlan' + cabal file.
 data MiniBuildPlan = MiniBuildPlan
@@ -383,11 +380,11 @@
     { mpiVersion :: !Version
     , mpiFlags :: !(Map FlagName Bool)
     , mpiPackageDeps :: !(Set PackageName)
-    , mpiToolDeps :: !(Set ByteString)
+    , mpiToolDeps :: !(Set Text)
     -- ^ Due to ambiguity in Cabal, it is unclear whether this refers to the
     -- executable name, the package name, or something else. We have to guess
     -- based on what's available, which is why we store this is an unwrapped
-    -- 'ByteString'.
+    -- 'Text'.
     , mpiExes :: !(Set ExeName)
     -- ^ Executables provided by this package
     , mpiHasLibrary :: !Bool
diff --git a/src/Stack/Types/Config.hs b/src/Stack/Types/Config.hs
--- a/src/Stack/Types/Config.hs
+++ b/src/Stack/Types/Config.hs
@@ -102,7 +102,7 @@
   ,packageDatabaseExtra
   ,packageDatabaseLocal
   ,platformOnlyRelDir
-  ,platformVariantRelDir
+  ,platformGhcRelDir
   ,useShaPathOnWindows
   ,getWorkDir
   -- * Command-specific types
@@ -167,6 +167,7 @@
 import           Stack.Types.PackageIdentifier
 import           Stack.Types.PackageIndex
 import           Stack.Types.PackageName
+import           Stack.Types.TemplateName
 import           Stack.Types.Version
 import           System.PosixCompat.Types (UserID, GroupID)
 import           System.Process.Read (EnvOverride)
@@ -273,6 +274,9 @@
          ,configAllowNewer          :: !Bool
          -- ^ Ignore version ranges in .cabal files. Funny naming chosen to
          -- match cabal.
+         ,configDefaultTemplate     :: !(Maybe TemplateName)
+         -- ^ The default template to use when none is specified.
+         -- (If Nothing, the default default is used.)
          }
 
 -- | Which packages to ghc-options on the command line apply to?
@@ -793,6 +797,9 @@
     -- ^ See 'configApplyGhcOptions'
     ,configMonoidAllowNewer          :: !(Maybe Bool)
     -- ^ See 'configMonoidAllowNewer'
+    ,configMonoidDefaultTemplate     :: !(Maybe TemplateName)
+    -- ^ The default template to use when none is specified.
+    -- (If Nothing, the default default is used.)
     }
   deriving Show
 
@@ -831,6 +838,7 @@
     , configMonoidRebuildGhcOptions = Nothing
     , configMonoidApplyGhcOptions = Nothing
     , configMonoidAllowNewer = Nothing
+    , configMonoidDefaultTemplate = Nothing
     }
   mappend l r = ConfigMonoid
     { configMonoidWorkDir = configMonoidWorkDir l <|> configMonoidWorkDir r
@@ -867,6 +875,7 @@
     , configMonoidRebuildGhcOptions = configMonoidRebuildGhcOptions l <|> configMonoidRebuildGhcOptions r
     , configMonoidApplyGhcOptions = configMonoidApplyGhcOptions l <|> configMonoidApplyGhcOptions r
     , configMonoidAllowNewer = configMonoidAllowNewer l <|> configMonoidAllowNewer r
+    , configMonoidDefaultTemplate = configMonoidDefaultTemplate l <|> configMonoidDefaultTemplate r
     }
 
 instance FromJSON (ConfigMonoid, [JSONWarning]) where
@@ -930,6 +939,7 @@
     configMonoidRebuildGhcOptions <- obj ..:? configMonoidRebuildGhcOptionsName
     configMonoidApplyGhcOptions <- obj ..:? configMonoidApplyGhcOptionsName
     configMonoidAllowNewer <- obj ..:? configMonoidAllowNewerName
+    configMonoidDefaultTemplate <- obj ..:? configMonoidDefaultTemplateName
 
     return ConfigMonoid {..}
   where
@@ -1055,6 +1065,9 @@
 configMonoidAllowNewerName :: Text
 configMonoidAllowNewerName = "allow-newer"
 
+configMonoidDefaultTemplateName :: Text
+configMonoidDefaultTemplateName = "default-template"
+
 data ConfigException
   = ParseConfigFileException (Path Abs File) ParseException
   | ParseResolverException Text
@@ -1187,7 +1200,7 @@
 snapshotsDir :: (MonadReader env m, HasConfig env, HasGHCVariant env, MonadThrow m) => m (Path Abs Dir)
 snapshotsDir = do
     config <- asks getConfig
-    platform <- platformVariantRelDir
+    platform <- platformGhcRelDir
     return $ configStackRoot config </> $(mkRelDir "snapshots") </> platform
 
 -- | Installation root for dependencies
@@ -1212,16 +1225,16 @@
     => m (Path Rel Dir)
 platformSnapAndCompilerRel = do
     bc <- asks getBuildConfig
-    platform <- platformVariantRelDir
+    platform <- platformGhcRelDir
     name <- parseRelDir $ T.unpack $ resolverName $ bcResolver bc
     ghc <- compilerVersionDir
     useShaPathOnWindows (platform </> name </> ghc)
 
 -- | Relative directory for the platform identifier
-platformVariantRelDir
+platformGhcRelDir
     :: (MonadReader env m, HasPlatform env, HasGHCVariant env, MonadThrow m)
     => m (Path Rel Dir)
-platformVariantRelDir = do
+platformGhcRelDir = do
     platform <- asks getPlatform
     platformVariant <- asks getPlatformVariant
     ghcVariant <- asks getGHCVariant
@@ -1278,7 +1291,7 @@
                          -> m (Path Abs File)
 configMiniBuildPlanCache name = do
     root <- asks getStackRoot
-    platform <- platformVariantRelDir
+    platform <- platformGhcRelDir
     file <- parseRelFile $ T.unpack (renderSnapName name) ++ ".cache"
     -- Yes, cached plans differ based on platform
     return (root </> $(mkRelDir "build-plan-cache") </> platform </> file)
diff --git a/src/Stack/Types/FlagName.hs b/src/Stack/Types/FlagName.hs
--- a/src/Stack/Types/FlagName.hs
+++ b/src/Stack/Types/FlagName.hs
@@ -24,20 +24,17 @@
 import           Control.Applicative
 import           Control.Monad.Catch
 import           Data.Aeson.Extended
-import           Data.Attoparsec.ByteString.Char8
+import           Data.Attoparsec.Text
 import           Data.Attoparsec.Combinators
 import           Data.Binary.VersionTagged
-import qualified Data.ByteString as S
-import           Data.ByteString.Char8 (ByteString)
-import qualified Data.ByteString.Char8 as S8
-import           Data.Char (isLetter)
+import           Data.Char (isLetter, isDigit, toLower)
 import           Data.Data
 import           Data.Hashable
 import           Data.Map (Map)
 import qualified Data.Map as Map
 import           Data.Text (Text)
-import qualified Data.Text.Encoding as T
-import qualified Data.Word8 as Word8
+import qualified Data.Text as T
+import           Data.Text.Binary ()
 import qualified Distribution.PackageDescription as Cabal
 import           GHC.Generics
 import           Language.Haskell.TH
@@ -45,7 +42,7 @@
 
 -- | A parse fail.
 data FlagNameParseFail
-  = FlagNameParseFail ByteString
+  = FlagNameParseFail Text
   deriving (Typeable)
 instance Exception FlagNameParseFail
 instance Show FlagNameParseFail where
@@ -53,22 +50,22 @@
 
 -- | A flag name.
 newtype FlagName =
-  FlagName ByteString
+  FlagName Text
   deriving (Typeable,Data,Generic,Hashable,Binary,NFData)
 instance HasStructuralInfo FlagName
 instance Eq FlagName where
     x == y = compare x y == EQ
 instance Ord FlagName where
     compare (FlagName x) (FlagName y) =
-        compare (S.map Word8.toLower x) (S.map Word8.toLower y)
+        compare (T.map toLower x) (T.map toLower y)
 
 instance Lift FlagName where
   lift (FlagName n) =
     appE (conE 'FlagName)
-         (stringE (S8.unpack n))
+         (stringE (T.unpack n))
 
 instance Show FlagName where
-  show (FlagName n) = S8.unpack n
+  show (FlagName n) = T.unpack n
 
 instance FromJSON FlagName where
   parseJSON j =
@@ -78,10 +75,10 @@
            fail ("Couldn't parse flag name: " ++ s)
          Just ver -> return ver
 
--- | Attoparsec parser for a flag name from bytestring.
+-- | Attoparsec parser for a flag name
 flagNameParser :: Parser FlagName
 flagNameParser =
-  fmap (FlagName . S8.pack)
+  fmap (FlagName . T.pack)
        (appending (many1 (satisfy isLetter))
                   (concating (many (alternating
                                       (pured (satisfy isAlphaNum))
@@ -97,36 +94,36 @@
     Nothing -> error ("Invalid flag name: " ++ show s)
     Just pn -> [|pn|]
 
--- | Convenient way to parse a flag name from a bytestring.
-parseFlagName :: MonadThrow m => ByteString -> m FlagName
+-- | Convenient way to parse a flag name from a 'Text'.
+parseFlagName :: MonadThrow m => Text -> m FlagName
 parseFlagName x = go x
   where go =
           either (const (throwM (FlagNameParseFail x))) return .
           parseOnly (flagNameParser <* endOfInput)
 
--- | Migration function.
+-- | Convenience function for parsing from a 'String'
 parseFlagNameFromString :: MonadThrow m => String -> m FlagName
 parseFlagNameFromString =
-  parseFlagName . S8.pack
+  parseFlagName . T.pack
 
 -- | Produce a string representation of a flag name.
 flagNameString :: FlagName -> String
-flagNameString (FlagName n) = S8.unpack n
+flagNameString (FlagName n) = T.unpack n
 
 -- | Produce a string representation of a flag name.
 flagNameText :: FlagName -> Text
-flagNameText (FlagName n) = T.decodeUtf8 n
+flagNameText (FlagName n) = n
 
 -- | Convert from a Cabal flag name.
 fromCabalFlagName :: Cabal.FlagName -> FlagName
 fromCabalFlagName (Cabal.FlagName name) =
-  let !x = S8.pack name
+  let !x = T.pack name
   in FlagName x
 
 -- | Convert to a Cabal flag name.
 toCabalFlagName :: FlagName -> Cabal.FlagName
 toCabalFlagName (FlagName name) =
-  let !x = S8.unpack name
+  let !x = T.unpack name
   in Cabal.FlagName x
 
 instance ToJSON a => ToJSON (Map FlagName a) where
diff --git a/src/Stack/Types/GhcPkgId.hs b/src/Stack/Types/GhcPkgId.hs
--- a/src/Stack/Types/GhcPkgId.hs
+++ b/src/Stack/Types/GhcPkgId.hs
@@ -13,27 +13,26 @@
 import           Control.Applicative
 import           Control.Monad.Catch
 import           Data.Aeson.Extended
-import           Data.Attoparsec.ByteString.Char8 as A8
+import           Data.Attoparsec.Text
 import           Data.Binary (getWord8, putWord8)
 import           Data.Binary.VersionTagged
-import           Data.ByteString.Char8 (ByteString)
-import qualified Data.ByteString.Char8 as S8
 import           Data.Data
 import           Data.Hashable
-import           Data.Text.Encoding (encodeUtf8)
+import           Data.Text (Text)
+import qualified Data.Text as T
 import           GHC.Generics
 import           Prelude -- Fix AMP warning
 
 -- | A parse fail.
 data GhcPkgIdParseFail
-  = GhcPkgIdParseFail ByteString
+  = GhcPkgIdParseFail Text
   deriving Typeable
 instance Show GhcPkgIdParseFail where
     show (GhcPkgIdParseFail bs) = "Invalid package ID: " ++ show bs
 instance Exception GhcPkgIdParseFail
 
 -- | A ghc-pkg package identifier.
-newtype GhcPkgId = GhcPkgId ByteString
+newtype GhcPkgId = GhcPkgId Text
   deriving (Eq,Ord,Data,Typeable,Generic)
 
 instance Hashable GhcPkgId
@@ -59,7 +58,7 @@
 
 instance FromJSON GhcPkgId where
   parseJSON = withText "GhcPkgId" $ \t ->
-    case parseGhcPkgId $ encodeUtf8 t of
+    case parseGhcPkgId t of
       Left e -> fail $ show (e, t)
       Right x -> return x
 
@@ -67,8 +66,8 @@
   toJSON g =
     toJSON (ghcPkgIdString g)
 
--- | Convenient way to parse a package name from a bytestring.
-parseGhcPkgId :: MonadThrow m => ByteString -> m GhcPkgId
+-- | Convenient way to parse a package name from a 'Text'.
+parseGhcPkgId :: MonadThrow m => Text -> m GhcPkgId
 parseGhcPkgId x = go x
   where go =
           either (const (throwM (GhcPkgIdParseFail x))) return .
@@ -77,16 +76,8 @@
 -- | A parser for a package-version-hash pair.
 ghcPkgIdParser :: Parser GhcPkgId
 ghcPkgIdParser =
-    fmap GhcPkgId (A8.takeWhile isValid)
-  where
-    isValid c =
-        ('A' <= c && c <= 'Z') ||
-        ('a' <= c && c <= 'z') ||
-        ('0' <= c && c <= '9') ||
-        c == '.' ||
-        c == '-' ||
-        c == '_'
+    GhcPkgId . T.pack <$> many1 (choice [digit, letter, satisfy (`elem` "_.-")])
 
 -- | Get a string representation of GHC package id.
 ghcPkgIdString :: GhcPkgId -> String
-ghcPkgIdString (GhcPkgId x) = S8.unpack x
+ghcPkgIdString (GhcPkgId x) = T.unpack x
diff --git a/src/Stack/Types/Nix.hs b/src/Stack/Types/Nix.hs
--- a/src/Stack/Types/Nix.hs
+++ b/src/Stack/Types/Nix.hs
@@ -8,15 +8,18 @@
 
 import Control.Applicative
 import Data.Aeson.Extended
-import Data.Monoid
 import Data.Text (Text)
+import Data.Monoid
 
+import Prelude
+
 -- | Nix configuration.
 data NixOpts = NixOpts
   {nixEnable   :: !Bool
+  ,nixPureShell :: !Bool
   ,nixPackages :: ![Text]
     -- ^ The system packages to be installed in the environment before it runs
-  ,nixInitFile :: !(Maybe String)
+  ,nixInitFile :: !(Maybe FilePath)
     -- ^ The path of a file containing preconfiguration of the environment (e.g shell.nix)
   ,nixShellOptions :: ![Text]
     -- ^ Options to be given to the nix-shell command line
@@ -30,46 +33,60 @@
     -- ^ Should nix-shell be defaulted to enabled (does @nix:@ section exist in the config)?
   ,nixMonoidEnable :: !(Maybe Bool)
     -- ^ Is using nix-shell enabled?
-  ,nixMonoidPackages :: ![Text]
+  ,nixMonoidPureShell :: !(Maybe Bool)
+    -- ^ Should the nix-shell be pure
+  ,nixMonoidPackages :: !(Maybe [Text])
     -- ^ System packages to use (given to nix-shell)
-  ,nixMonoidInitFile :: !(Maybe String)
+  ,nixMonoidInitFile :: !(Maybe FilePath)
     -- ^ The path of a file containing preconfiguration of the environment (e.g shell.nix)
-  ,nixMonoidShellOptions :: ![Text]
+  ,nixMonoidShellOptions :: !(Maybe [Text])
     -- ^ Options to be given to the nix-shell command line
+  ,nixMonoidPath :: !(Maybe [Text])
+    -- ^ Override parts of NIX_PATH (notably 'nixpkgs')
   }
-  deriving (Show)
+  deriving (Eq, Show)
 
 -- | Decode uninterpreted nix options from JSON/YAML.
 instance FromJSON (NixOptsMonoid, [JSONWarning]) where
-  parseJSON = withObjectWarnings "DockerOptsMonoid"
+  parseJSON = withObjectWarnings "NixOptsMonoid"
     (\o -> do nixMonoidDefaultEnable <- pure True
-              nixMonoidEnable <- o ..:? nixEnableArgName
-              nixMonoidPackages <- o ..:? nixPackagesArgName ..!= []
-              nixMonoidInitFile <- o ..:? nixInitFileArgName
-              nixMonoidShellOptions <- o ..:? nixShellOptsArgName ..!= []
+              nixMonoidEnable        <- o ..:? nixEnableArgName
+              nixMonoidPureShell     <- o ..:? nixPureShellArgName
+              nixMonoidPackages      <- o ..:? nixPackagesArgName
+              nixMonoidInitFile      <- o ..:? nixInitFileArgName
+              nixMonoidShellOptions  <- o ..:? nixShellOptsArgName
+              nixMonoidPath          <- o ..:? nixPathArgName
               return NixOptsMonoid{..})
 
--- | Left-biased combine nix options
+-- | Left-biased combine Nix options
 instance Monoid NixOptsMonoid where
   mempty = NixOptsMonoid
     {nixMonoidDefaultEnable = False
-    ,nixMonoidEnable = Nothing
-    ,nixMonoidPackages = []
-    ,nixMonoidInitFile = Nothing
-    ,nixMonoidShellOptions = []
+    ,nixMonoidEnable        = Nothing
+    ,nixMonoidPureShell     = Nothing
+    ,nixMonoidPackages      = Nothing
+    ,nixMonoidInitFile      = Nothing
+    ,nixMonoidShellOptions  = Nothing
+    ,nixMonoidPath          = Nothing
     }
   mappend l r = NixOptsMonoid
     {nixMonoidDefaultEnable = nixMonoidDefaultEnable l || nixMonoidDefaultEnable r
-    ,nixMonoidEnable = nixMonoidEnable l <|> nixMonoidEnable r
-    ,nixMonoidPackages = nixMonoidPackages l <> nixMonoidPackages r
-    ,nixMonoidInitFile = nixMonoidInitFile l <|> nixMonoidInitFile r
-    ,nixMonoidShellOptions = nixMonoidShellOptions l <> nixMonoidShellOptions r
+    ,nixMonoidEnable        = nixMonoidEnable l <|> nixMonoidEnable r
+    ,nixMonoidPureShell     = nixMonoidPureShell l <|> nixMonoidPureShell r
+    ,nixMonoidPackages      = nixMonoidPackages l <|> nixMonoidPackages r
+    ,nixMonoidInitFile      = nixMonoidInitFile l <|> nixMonoidInitFile r
+    ,nixMonoidShellOptions  = nixMonoidShellOptions l <|> nixMonoidShellOptions r
+    ,nixMonoidPath          = nixMonoidPath l <|> nixMonoidPath r
     }
 
 -- | Nix enable argument name.
 nixEnableArgName :: Text
 nixEnableArgName = "enable"
 
+-- | Nix run in pure shell argument name.
+nixPureShellArgName :: Text
+nixPureShellArgName = "pure"
+
 -- | Nix packages (build inputs) argument name.
 nixPackagesArgName :: Text
 nixPackagesArgName = "packages"
@@ -81,3 +98,7 @@
 -- | Extra options for the nix-shell command argument name.
 nixShellOptsArgName :: Text
 nixShellOptsArgName = "nix-shell-options"
+
+-- | NIX_PATH override argument name
+nixPathArgName :: Text
+nixPathArgName = "path"
diff --git a/src/Stack/Types/Package.hs b/src/Stack/Types/Package.hs
--- a/src/Stack/Types/Package.hs
+++ b/src/Stack/Types/Package.hs
@@ -117,6 +117,7 @@
 data BuildInfoOpts = BuildInfoOpts
     { bioOpts :: [String]
     , bioOneWordOpts :: [String]
+    , bioPackageFlags :: [String]
     -- ^ These options can safely have 'nubOrd' applied to them, as
     -- there are no multi-word options (see
     -- https://github.com/commercialhaskell/stack/issues/1255)
diff --git a/src/Stack/Types/PackageIdentifier.hs b/src/Stack/Types/PackageIdentifier.hs
--- a/src/Stack/Types/PackageIdentifier.hs
+++ b/src/Stack/Types/PackageIdentifier.hs
@@ -21,15 +21,12 @@
 import           Control.Exception (Exception)
 import           Control.Monad.Catch (MonadThrow, throwM)
 import           Data.Aeson.Extended
-import           Data.Attoparsec.ByteString.Char8
+import           Data.Attoparsec.Text
 import           Data.Binary.VersionTagged (Binary, HasStructuralInfo)
-import           Data.ByteString (ByteString)
-import qualified Data.ByteString.Char8 as S8
 import           Data.Data
 import           Data.Hashable
 import           Data.Text (Text)
 import qualified Data.Text as T
-import           Data.Text.Encoding (encodeUtf8)
 import           GHC.Generics
 import           Prelude hiding (FilePath)
 import           Stack.Types.PackageName
@@ -37,7 +34,7 @@
 
 -- | A parse fail.
 data PackageIdentifierParseFail
-  = PackageIdentifierParseFail ByteString
+  = PackageIdentifierParseFail Text
   deriving (Typeable)
 instance Show PackageIdentifierParseFail where
     show (PackageIdentifierParseFail bs) = "Invalid package identifier: " ++ show bs
@@ -66,7 +63,7 @@
   toJSON = toJSON . packageIdentifierString
 instance FromJSON PackageIdentifier where
   parseJSON = withText "PackageIdentifier" $ \t ->
-    case parsePackageIdentifier $ encodeUtf8 t of
+    case parsePackageIdentifier t of
       Left e -> fail $ show (e, t)
       Right x -> return x
 
@@ -82,21 +79,21 @@
 packageIdentifierParser :: Parser PackageIdentifier
 packageIdentifierParser =
   do name <- packageNameParser
-     char8 '-'
+     char '-'
      version <- versionParser
      return (PackageIdentifier name version)
 
--- | Convenient way to parse a package identifier from a bytestring.
-parsePackageIdentifier :: MonadThrow m => ByteString -> m PackageIdentifier
+-- | Convenient way to parse a package identifier from a 'Text'.
+parsePackageIdentifier :: MonadThrow m => Text -> m PackageIdentifier
 parsePackageIdentifier x = go x
   where go =
           either (const (throwM (PackageIdentifierParseFail x))) return .
           parseOnly (packageIdentifierParser <* endOfInput)
 
--- | Migration function.
+-- | Convenience function for parsing from a 'String'.
 parsePackageIdentifierFromString :: MonadThrow m => String -> m PackageIdentifier
 parsePackageIdentifierFromString =
-  parsePackageIdentifier . S8.pack
+  parsePackageIdentifier . T.pack
 
 -- | Get a string representation of the package identifier; name-ver.
 packageIdentifierString :: PackageIdentifier -> String
diff --git a/src/Stack/Types/PackageName.hs b/src/Stack/Types/PackageName.hs
--- a/src/Stack/Types/PackageName.hs
+++ b/src/Stack/Types/PackageName.hs
@@ -14,7 +14,6 @@
   ,packageNameParser
   ,parsePackageName
   ,parsePackageNameFromString
-  ,packageNameByteString
   ,packageNameString
   ,packageNameText
   ,fromCabalPackageName
@@ -29,19 +28,17 @@
 import           Control.Monad
 import           Control.Monad.Catch
 import           Data.Aeson.Extended
-import           Data.Attoparsec.ByteString.Char8
+import           Data.Attoparsec.Text
 import           Data.Attoparsec.Combinators
 import           Data.Binary.VersionTagged (Binary, HasStructuralInfo)
-import           Data.ByteString.Char8 (ByteString)
-import qualified Data.ByteString.Char8 as S8
-import           Data.Char (isLetter)
 import           Data.Data
 import           Data.Hashable
 import           Data.List (intercalate)
 import           Data.Map (Map)
 import qualified Data.Map as Map
 import           Data.Text (Text)
-import qualified Data.Text.Encoding as T
+import qualified Data.Text as T
+import           Data.Text.Binary ()
 import qualified Distribution.Package as Cabal
 import           GHC.Generics
 import           Language.Haskell.TH
@@ -51,7 +48,7 @@
 
 -- | A parse fail.
 data PackageNameParseFail
-  = PackageNameParseFail ByteString
+  = PackageNameParseFail Text
   | CabalFileNameParseFail FilePath
   | CabalFileNameInvalidPackageName FilePath
   deriving (Typeable)
@@ -63,16 +60,16 @@
 
 -- | A package name.
 newtype PackageName =
-  PackageName ByteString
+  PackageName Text
   deriving (Eq,Ord,Typeable,Data,Generic,Hashable,Binary,NFData)
 
 instance Lift PackageName where
   lift (PackageName n) =
     appE (conE 'PackageName)
-         (stringE (S8.unpack n))
+         (stringE (T.unpack n))
 
 instance Show PackageName where
-  show (PackageName n) = S8.unpack n
+  show (PackageName n) = T.unpack n
 
 instance HasStructuralInfo PackageName
 
@@ -86,16 +83,15 @@
            fail ("Couldn't parse package name: " ++ s)
          Just ver -> return ver
 
--- | Attoparsec parser for a package name from bytestring.
+-- | Attoparsec parser for a package name
 packageNameParser :: Parser PackageName
 packageNameParser =
-  fmap (PackageName . S8.pack . intercalate "-")
+  fmap (PackageName . T.pack . intercalate "-")
        (sepBy1 word (char '-'))
   where
     word = concat <$> sequence [many digit,
                                 pured letter,
                                 many (alternating letter digit)]
-    letter = satisfy isLetter
 
 -- | Make a package name.
 mkPackageName :: String -> Q Exp
@@ -104,40 +100,36 @@
     Nothing -> error ("Invalid package name: " ++ show s)
     Just pn -> [|pn|]
 
--- | Convenient way to parse a package name from a bytestring.
-parsePackageName :: MonadThrow m => ByteString -> m PackageName
+-- | Parse a package name from a 'Text'.
+parsePackageName :: MonadThrow m => Text -> m PackageName
 parsePackageName x = go x
   where go =
           either (const (throwM (PackageNameParseFail x))) return .
           parseOnly (packageNameParser <* endOfInput)
 
--- | Migration function.
+-- | Parse a package name from a 'String'.
 parsePackageNameFromString :: MonadThrow m => String -> m PackageName
 parsePackageNameFromString =
-  parsePackageName . S8.pack
-
--- | Produce a bytestring representation of a package name.
-packageNameByteString :: PackageName -> ByteString
-packageNameByteString (PackageName n) = n
+  parsePackageName . T.pack
 
 -- | Produce a string representation of a package name.
 packageNameString :: PackageName -> String
-packageNameString (PackageName n) = S8.unpack n
+packageNameString (PackageName n) = T.unpack n
 
 -- | Produce a string representation of a package name.
 packageNameText :: PackageName -> Text
-packageNameText (PackageName n) = T.decodeUtf8 n
+packageNameText (PackageName n) = n
 
 -- | Convert from a Cabal package name.
 fromCabalPackageName :: Cabal.PackageName -> PackageName
 fromCabalPackageName (Cabal.PackageName name) =
-  let !x = S8.pack name
+  let !x = T.pack name
   in PackageName x
 
 -- | Convert to a Cabal package name.
 toCabalPackageName :: PackageName -> Cabal.PackageName
 toCabalPackageName (PackageName name) =
-  let !x = S8.unpack name
+  let !x = T.unpack name
   in Cabal.PackageName x
 
 -- | Parse a package name from a file path.
diff --git a/src/Stack/Types/Sig.hs b/src/Stack/Types/Sig.hs
--- a/src/Stack/Types/Sig.hs
+++ b/src/Stack/Types/Sig.hs
@@ -26,7 +26,6 @@
 import           Data.String (IsString(..))
 import           Data.Text (Text)
 import qualified Data.Text as T
-import qualified Data.Text.Encoding as T
 import           Data.Typeable (Typeable)
 import           Stack.Types.PackageName
 
@@ -68,7 +67,7 @@
 instance FromJSON (Aeson PackageName) where
     parseJSON j = do
         s <- parseJSON j
-        case (parsePackageName . T.encodeUtf8) s of
+        case parsePackageName s of
             Just name -> return (Aeson name)
             Nothing -> fail ("Invalid package name: " <> T.unpack s)
 
diff --git a/src/Stack/Types/TemplateName.hs b/src/Stack/Types/TemplateName.hs
--- a/src/Stack/Types/TemplateName.hs
+++ b/src/Stack/Types/TemplateName.hs
@@ -7,18 +7,37 @@
 
 module Stack.Types.TemplateName where
 
+import           Control.Error.Safe (justErr)
+import           Control.Applicative
+import           Data.Aeson.Extended (FromJSON, withText, parseJSON)
+import           Data.Foldable (asum)
 import           Data.Monoid
 import           Data.Text (Text)
 import qualified Data.Text as T
 import           Language.Haskell.TH
+import           Network.HTTP.Client (parseUrl)
 import qualified Options.Applicative as O
 import           Path
 import           Path.Internal
+import           Prelude
 
 -- | A template name.
-data TemplateName = TemplateName !Text !(Either (Path Abs File) (Path Rel File))
+data TemplateName = TemplateName !Text !TemplatePath
   deriving (Ord,Eq,Show)
 
+data TemplatePath = AbsPath (Path Abs File)
+                  -- ^ an absolute path on the filesystem
+                  | RelPath (Path Rel File)
+                  -- ^ a relative path on the filesystem, or relative to
+                  -- the template repository
+                  | UrlPath String
+                  -- ^ a full URL
+  deriving (Eq, Ord, Show)
+
+instance FromJSON TemplateName where
+    parseJSON = withText "TemplateName" $
+        either fail return . parseTemplateNameFromString . T.unpack
+
 -- | An argument which accepts a template name of the format
 -- @foo.hsfiles@ or @foo@, ultimately normalized to @foo@.
 templateNameArgument :: O.Mod O.ArgumentFields TemplateName
@@ -46,17 +65,19 @@
 parseTemplateNameFromString :: String -> Either String TemplateName
 parseTemplateNameFromString fname =
     case T.stripSuffix ".hsfiles" (T.pack fname) of
-        Nothing -> parseValidFile (T.pack fname) (fname <> ".hsfiles")
-        Just prefix -> parseValidFile prefix fname
+        Nothing -> parseValidFile (T.pack fname) (fname <> ".hsfiles") fname
+        Just prefix -> parseValidFile prefix fname fname
   where
-    parseValidFile prefix str =
-        case parseRelFile str of
-            Nothing ->
-                case parseAbsFile str of
-                    Nothing -> Left expected
-                    Just fp -> return (TemplateName prefix (Left fp))
-            Just fp -> return (TemplateName prefix (Right fp))
-    expected = "Expected a template filename like: foo or foo.hsfiles"
+    parseValidFile prefix hsf orig = justErr expected
+                                           $ asum (validParses prefix hsf orig)
+    validParses prefix hsf orig =
+        -- NOTE: order is important
+        [ TemplateName (T.pack orig) . UrlPath <$> (parseUrl orig *> Just orig)
+        , TemplateName prefix        . AbsPath <$> parseAbsFile hsf
+        , TemplateName prefix        . RelPath <$> parseRelFile hsf
+        ]
+    expected = "Expected a template like: foo or foo.hsfiles or\
+               \ https://example.com/foo.hsfiles"
 
 -- | Make a template name.
 mkTemplateName :: String -> Q Exp
@@ -67,13 +88,14 @@
             [|TemplateName (T.pack prefix) $(pn)|]
             where pn =
                       case p of
-                          Left (Path fp) -> [|Left (Path fp)|]
-                          Right (Path fp) -> [|Right (Path fp)|]
+                          AbsPath (Path fp) -> [|AbsPath (Path fp)|]
+                          RelPath (Path fp) -> [|RelPath (Path fp)|]
+                          UrlPath fp -> [|UrlPath fp|]
 
 -- | Get a text representation of the template name.
 templateName :: TemplateName -> Text
 templateName (TemplateName prefix _) = prefix
 
 -- | Get the path of the template.
-templatePath :: TemplateName -> Either (Path Abs File) (Path Rel File)
+templatePath :: TemplateName -> TemplatePath
 templatePath (TemplateName _ fp) = fp
diff --git a/src/Stack/Types/Version.hs b/src/Stack/Types/Version.hs
--- a/src/Stack/Types/Version.hs
+++ b/src/Stack/Types/Version.hs
@@ -33,10 +33,8 @@
 import           Control.DeepSeq
 import           Control.Monad.Catch
 import           Data.Aeson.Extended
-import           Data.Attoparsec.ByteString.Char8
+import           Data.Attoparsec.Text
 import           Data.Binary.VersionTagged (Binary, HasStructuralInfo)
-import           Data.ByteString (ByteString)
-import qualified Data.ByteString.Char8 as S8
 import           Data.Data
 import           Data.Hashable
 import           Data.List
@@ -47,6 +45,7 @@
 import qualified Data.Set as Set
 import           Data.Text (Text)
 import qualified Data.Text as T
+import           Data.Text.Binary ()
 import           Data.Vector.Binary ()
 import           Data.Vector.Unboxed (Vector)
 import qualified Data.Vector.Unboxed as V
@@ -61,7 +60,7 @@
 
 -- | A parse fail.
 data VersionParseFail =
-  VersionParseFail ByteString
+  VersionParseFail Text
   deriving (Typeable)
 instance Exception VersionParseFail
 instance Show VersionParseFail where
@@ -106,7 +105,7 @@
             k' <- either (fail . show) return $ parseVersionFromString k
             return (k', v)
 
--- | Attoparsec parser for a package version from bytestring.
+-- | Attoparsec parser for a package version.
 versionParser :: Parser Version
 versionParser =
   do ls <- ((:) <$> num <*> many num')
@@ -116,8 +115,8 @@
         num' = point *> num
         point = satisfy (== '.')
 
--- | Convenient way to parse a package version from a bytestring.
-parseVersion :: MonadThrow m => ByteString -> m Version
+-- | Convenient way to parse a package version from a 'Text'.
+parseVersion :: MonadThrow m => Text -> m Version
 parseVersion x = go x
   where go =
           either (const (throwM (VersionParseFail x))) return .
@@ -126,7 +125,7 @@
 -- | Migration function.
 parseVersionFromString :: MonadThrow m => String -> m Version
 parseVersionFromString =
-  parseVersion . S8.pack
+  parseVersion . T.pack
 
 -- | Get a string representation of a package version.
 versionString :: Version -> String
diff --git a/src/System/Process/Read.hs b/src/System/Process/Read.hs
--- a/src/System/Process/Read.hs
+++ b/src/System/Process/Read.hs
@@ -38,7 +38,7 @@
 import           Control.Arrow ((***), first)
 import           Control.Concurrent.Async (Concurrently (..))
 import           Control.Exception hiding (try, catch)
-import           Control.Monad (join, liftM)
+import           Control.Monad (join, liftM, unless)
 import           Control.Monad.Catch (MonadThrow, MonadCatch, throwM, try, catch)
 import           Control.Monad.IO.Class (MonadIO, liftIO)
 import           Control.Monad.Logger (MonadLogger, logError)
@@ -53,7 +53,7 @@
 import           Data.IORef
 import           Data.Map (Map)
 import qualified Data.Map as Map
-import           Data.Maybe (isJust)
+import           Data.Maybe (isJust, maybeToList)
 import           Data.Monoid
 import           Data.Text (Text)
 import qualified Data.Text as T
@@ -141,7 +141,7 @@
     sinkProcessStdout wd menv name args CL.sinkNull
 
 -- | Run the given command in the given directory. If it exits with anything
--- but success, prints an error and then calls 'exitWith' to exit the program.
+-- but success, print an error and then call 'exitWith' to exit the program.
 readInNull :: (MonadIO m, MonadLogger m, MonadBaseControl IO m, MonadCatch m)
            => Path Abs Dir -- ^ Directory to run in
            -> FilePath -- ^ Command to run
@@ -152,18 +152,12 @@
 readInNull wd cmd menv args errMsg = do
     result <- try (readProcessNull (Just wd) menv cmd args)
     case result of
-        Left (ProcessExitedUnsuccessfully _ ec) -> do
-            $logError $
-                T.pack $
-                concat
-                    [ "Exit code "
-                    , show ec
-                    , " while running "
-                    , show (cmd : args)
-                    , " in "
-                    , toFilePath wd]
-            forM_ errMsg $logError
-            liftIO (exitWith ec)
+        Left ex -> do
+            $logError (T.pack (show ex))
+            case ex of
+                ReadProcessException{} -> forM_ errMsg $logError
+                _ -> return ()
+            liftIO exitFailure
         Right () -> return ()
 
 -- | Try to produce a strict 'S.ByteString' from the stdout of a
@@ -342,7 +336,7 @@
                                 else testFPs fps
                     testFPs fps0
             epath <- loop $ eoPath eo
-            !() <- atomicModifyIORef (eoExeCache eo) $ \m' ->
+            () <- atomicModifyIORef (eoExeCache eo) $ \m' ->
                 (Map.insert name epath m', ())
             return epath
     return $ either throwM return epath
@@ -355,17 +349,32 @@
           mkEnvOverride platform
         . Map.fromList . map (T.pack *** T.pack)
 
+data PathException = PathsInvalidInPath [FilePath]
+    deriving Typeable
+
+instance Exception PathException
+instance Show PathException where
+    show (PathsInvalidInPath paths) = unlines $
+        [ "Would need to add some paths to the PATH environment variable \
+          \to continue, but they would be invalid because they contain a "
+          ++ show FP.searchPathSeparator ++ "."
+        , "Please fix the following paths and try again:"
+        ] ++ paths
+
 -- | Augment the PATH environment variable with the given extra paths.
-augmentPath :: [FilePath] -> Maybe Text -> Text
+augmentPath :: MonadThrow m => [FilePath] -> Maybe Text -> m Text
 augmentPath dirs mpath =
-    T.intercalate (T.singleton FP.searchPathSeparator)
-    $ map (T.pack . FP.dropTrailingPathSeparator) dirs
-   ++ maybe [] return mpath
+  do let illegal = filter (FP.searchPathSeparator `elem`) dirs
+     unless (null illegal) (throwM $ PathsInvalidInPath illegal)
+     return $ T.intercalate (T.singleton FP.searchPathSeparator)
+            $ map (T.pack . FP.dropTrailingPathSeparator) dirs
+            ++ maybeToList mpath
 
 -- | Apply 'augmentPath' on the PATH value in the given Map.
-augmentPathMap :: [FilePath] -> Map Text Text -> Map Text Text
-augmentPathMap paths origEnv =
-    Map.insert "PATH" path origEnv
+augmentPathMap :: MonadThrow m => [FilePath] -> Map Text Text
+                               -> m (Map Text Text)
+augmentPathMap dirs origEnv =
+  do path <- augmentPath dirs mpath
+     return $ Map.insert "PATH" path origEnv
   where
     mpath = Map.lookup "PATH" origEnv
-    path = augmentPath paths mpath
diff --git a/src/main/Main.hs b/src/main/Main.hs
--- a/src/main/Main.hs
+++ b/src/main/Main.hs
@@ -18,7 +18,10 @@
 import           Control.Monad.Logger
 import           Control.Monad.Reader (ask, asks, runReaderT)
 import           Control.Monad.Trans.Control (MonadBaseControl)
-import           Data.Attoparsec.Args (withInterpreterArgs, parseArgs, EscapingMode (Escaping))
+import           Control.Monad.Trans.Either (EitherT)
+import           Control.Monad.Trans.Writer (Writer)
+import           Data.Attoparsec.Args (parseArgs, EscapingMode (Escaping))
+import           Data.Attoparsec.Interpreter (getInterpreterArgs)
 import qualified Data.ByteString.Lazy as L
 import           Data.IORef
 import           Data.List
@@ -43,12 +46,13 @@
 import           Network.HTTP.Client
 import           Options.Applicative
 import           Options.Applicative.Args
+import           Options.Applicative.Help (errorHelp, stringChunk, vcatChunks)
 import           Options.Applicative.Builder.Extra
 import           Options.Applicative.Complicated
 #ifdef USE_GIT_INFO
 import           Options.Applicative.Simple (simpleVersion)
 #endif
-import           Options.Applicative.Types (readerAsk)
+import           Options.Applicative.Types (readerAsk, ParserHelp(..))
 import           Path
 import           Path.Extra (toFilePathNoTrailingSep)
 import           Path.IO
@@ -86,10 +90,10 @@
 import qualified Stack.Upload as Upload
 import           System.Directory (canonicalizePath, doesFileExist, doesDirectoryExist, createDirectoryIfMissing)
 import qualified System.Directory as Directory (findExecutable)
-import           System.Environment (getEnvironment, getProgName)
+import           System.Environment (getEnvironment, getProgName, getArgs, withArgs)
 import           System.Exit
 import           System.FileLock (lockFile, tryLockFile, unlockFile, SharedExclusive(Exclusive), FileLock)
-import           System.FilePath (searchPathSeparator)
+import           System.FilePath (pathSeparator, searchPathSeparator)
 import           System.IO (hIsTerminalDevice, stderr, stdin, stdout, hSetBuffering, BufferMode(..), hPutStrLn, Handle, hGetEncoding, hSetEncoding)
 import           System.Process.Read
 
@@ -105,364 +109,422 @@
               hSetEncoding h enc'
         _ -> return ()
 
--- | Commandline dispatcher.
-main :: IO ()
-main = withInterpreterArgs stackProgName $ \args isInterpreter -> do
-     -- Line buffer the output by default, particularly for non-terminal runs.
-     -- See https://github.com/commercialhaskell/stack/pull/360
-     hSetBuffering stdout LineBuffering
-     hSetBuffering stdin  LineBuffering
-     hSetBuffering stderr LineBuffering
-     hSetTranslit stdout
-     hSetTranslit stderr
-     progName <- getProgName
-     isTerminal <- hIsTerminalDevice stdout
-     execExtraHelp args
-                   dockerHelpOptName
-                   (dockerOptsParser False)
-                   ("Only showing --" ++ Docker.dockerCmdName ++ "* options.")
-     execExtraHelp args
-                   nixHelpOptName
-                   (nixOptsParser False)
-                   ("Only showing --" ++ Nix.nixCmdName ++ "* options.")
+versionString' :: String
 #ifdef USE_GIT_INFO
-     let commitCount = $gitCommitCount
-         versionString' = concat $ concat
-            [ [$(simpleVersion Meta.version)]
-              -- Leave out number of commits for --depth=1 clone
-              -- See https://github.com/commercialhaskell/stack/issues/792
-            , [" (" ++ commitCount ++ " commits)" | commitCount /= ("1"::String) &&
-                                                    commitCount /= ("UNKNOWN" :: String)]
-            , [" ", display buildArch]
-            ]
+versionString' = concat $ concat
+    [ [$(simpleVersion Meta.version)]
+      -- Leave out number of commits for --depth=1 clone
+      -- See https://github.com/commercialhaskell/stack/issues/792
+    , [" (" ++ commitCount ++ " commits)" | commitCount /= ("1"::String) &&
+                                          commitCount /= ("UNKNOWN" :: String)]
+    , [" ", display buildArch]
+    ]
+    where commitCount = $gitCommitCount
 #else
-     let versionString' = showVersion Meta.version ++ ' ' : display buildArch
+versionString' = showVersion Meta.version ++ ' ' : display buildArch
 #endif
 
-     let globalOpts hide =
-             extraHelpOption hide progName (Docker.dockerCmdName ++ "*") dockerHelpOptName <*>
-             extraHelpOption hide progName (Nix.nixCmdName ++ "*") nixHelpOptName <*>             
-             globalOptsParser hide
-         addCommand' cmd title footerStr constr =
-             addCommand cmd title footerStr constr (globalOpts True)
-         addSubCommands' cmd title footerStr =
-             addSubCommands cmd title footerStr (globalOpts True)
-     eGlobalRun <- try $
-       complicatedOptions
-         Meta.version
-         (Just versionString')
-         "stack - The Haskell Tool Stack"
-         ""
-         (globalOpts False)
-         -- when there's a parse failure
-         (Just $ \f as ->
-           -- fall-through to external executables in `git` style if they exist
-           -- (i.e. `stack something` looks for `stack-something` before
-           -- failing with "Invalid argument `something'")
-           case stripPrefix "Invalid argument" (fst (renderFailure f "")) of
-             Just _ -> do
-               mExternalExec <- Directory.findExecutable ("stack-" ++ head as)
-               case mExternalExec of
-                 Just ex -> do
-                   menv <- getEnvOverride buildPlatform
-                   runNoLoggingT (exec menv ex (tail as))
-                 Nothing -> handleParseResult (Failure f)
-             Nothing -> handleParseResult (Failure f)
-         )
-         (do addCommand' "build"
-                        "Build the package(s) in this directory/configuration"
-                        cmdFooter
-                        buildCmd
-                        (buildOptsParser Build)
-             addCommand' "install"
-                        "Shortcut for 'build --copy-bins'"
-                        cmdFooter
-                        buildCmd
-                        (buildOptsParser Install)
-             addCommand' "uninstall"
-                        "DEPRECATED: This command performs no actions, and is present for documentation only"
-                        cmdFooter
-                        uninstallCmd
-                        (many $ strArgument $ metavar "IGNORED")
-             addCommand' "test"
-                        "Shortcut for 'build --test'"
-                        cmdFooter
-                        buildCmd
-                        (buildOptsParser Test)
-             addCommand' "bench"
-                        "Shortcut for 'build --bench'"
-                        cmdFooter
-                        buildCmd
-                        (buildOptsParser Bench)
-             addCommand' "haddock"
-                        "Shortcut for 'build --haddock'"
-                        cmdFooter
-                        buildCmd
-                        (buildOptsParser Haddock)
-             addCommand' "new"
-                        "Create a new project from a template. Run `stack templates' to see available templates."
-                        cmdFooter
-                        newCmd
-                        newOptsParser
-             addCommand' "templates"
-                        "List the templates available for `stack new'."
-                        cmdFooter
-                        templatesCmd
-                        (pure ())
-             addCommand' "init"
-                        "Initialize a stack project based on one or more cabal packages"
-                        cmdFooter
-                        initCmd
-                        initOptsParser
-             addCommand' "solver"
-                        "Use a dependency solver to try and determine missing extra-deps"
-                        cmdFooter
-                        solverCmd
-                        solverOptsParser
-             addCommand' "setup"
-                        "Get the appropriate GHC for your project"
-                        cmdFooter
-                        setupCmd
-                        setupParser
-             addCommand' "path"
-                        "Print out handy path information"
-                        cmdFooter
-                        pathCmd
-                        (mapMaybeA
-                            (\(desc,name,_) ->
-                                 flag Nothing
-                                      (Just name)
-                                      (long (T.unpack name) <>
-                                       help desc))
-                            paths)
-             addCommand' "unpack"
-                        "Unpack one or more packages locally"
-                        cmdFooter
-                        unpackCmd
-                        (some $ strArgument $ metavar "PACKAGE")
-             addCommand' "update"
-                        "Update the package index"
-                        cmdFooter
-                        updateCmd
-                        (pure ())
-             addCommand' "upgrade"
-                        "Upgrade to the latest stack (experimental)"
-                        cmdFooter
-                        upgradeCmd
-                        ((,) <$> switch
-                                  ( long "git"
-                                 <> help "Clone from Git instead of downloading from Hackage (more dangerous)" )
-                             <*> strOption
-                                  ( long "git-repo"
-                                 <> help "Clone from specified git repository"
-                                 <> value "https://github.com/commercialhaskell/stack"
-                                 <> showDefault ))
-             addCommand' "upload"
-                        "Upload a package to Hackage"
-                        cmdFooter
-                        uploadCmd
-                        ((,,,)
-                         <$> many (strArgument $ metavar "TARBALL/DIR")
-                         <*> optional pvpBoundsOption
-                         <*> ignoreCheckSwitch
-                         <*> flag False True
-                              (long "sign" <>
-                               help "GPG sign & submit signature"))
-             addCommand' "sdist"
-                        "Create source distribution tarballs"
-                        cmdFooter
-                        sdistCmd
-                        ((,,)
-                         <$> many (strArgument $ metavar "DIR")
-                         <*> optional pvpBoundsOption
-                         <*> ignoreCheckSwitch)
-             addCommand' "dot"
-                        "Visualize your project's dependency graph using Graphviz dot"
-                        cmdFooter
-                        dotCmd
-                        dotOptsParser
-             addCommand' "exec"
-                        "Execute a command"
-                        cmdFooter
-                        execCmd
-                        (execOptsParser Nothing)
-             addCommand' "ghc"
-                        "Run ghc"
-                        cmdFooter
-                        execCmd
-                        (execOptsParser $ Just ExecGhc)
-             addCommand' "ghci"
-                        "Run ghci in the context of package(s) (experimental)"
-                        cmdFooter
-                        ghciCmd
-                        ghciOptsParser
-             addCommand' "repl"
-                        "Run ghci in the context of package(s) (experimental) (alias for 'ghci')"
-                        cmdFooter
-                        ghciCmd
-                        ghciOptsParser
-             addCommand' "runghc"
-                        "Run runghc"
-                        cmdFooter
-                        execCmd
-                        (execOptsParser $ Just ExecRunGhc)
-             addCommand' "runhaskell"
-                        "Run runghc (alias for 'runghc')"
-                        cmdFooter
-                        execCmd
-                        (execOptsParser $ Just ExecRunGhc)
-             addCommand' "eval"
-                        "Evaluate some haskell code inline. Shortcut for 'stack exec ghc -- -e CODE'"
-                        cmdFooter
-                        evalCmd
-                        (evalOptsParser "CODE")
-             addCommand' "clean"
-                        "Clean the local packages"
-                        cmdFooter
-                        cleanCmd
-                        cleanOptsParser
-             addCommand' "list-dependencies"
-                        "List the dependencies"
-                        cmdFooter
-                        listDependenciesCmd
-                        (textOption (long "separator" <>
-                                     metavar "SEP" <>
-                                     help ("Separator between package name " <>
-                                           "and package version.") <>
-                                     value " " <>
-                                     showDefault))
-             addCommand' "query"
-                        "Query general build information (experimental)"
-                        cmdFooter
-                        queryCmd
-                        (many $ strArgument $ metavar "SELECTOR...")
-             addSubCommands'
-                 "ide"
-                 "IDE-specific commands"
-                 cmdFooter
-                 (do addCommand'
-                         "start"
-                         "Start the ide-backend service"
-                         cmdFooter
-                         ideCmd
-                         ((,) <$> many (textArgument
-                                          (metavar "TARGET" <>
-                                           help ("If none specified, use all " <>
-                                                 "packages defined in current directory")))
-                              <*> argsOption (long "ghc-options" <>
-                                              metavar "OPTION" <>
-                                              help "Additional options passed to GHCi" <>
-                                              value []))
-                     addCommand'
-                         "packages"
-                         "List all available local loadable packages"
-                         cmdFooter
-                         packagesCmd
-                         (pure ())
-                     addCommand'
-                         "load-targets"
-                         "List all load targets for a package target"
-                         cmdFooter
-                         targetsCmd
-                         (textArgument
-                            (metavar "TARGET")))
-             addSubCommands'
-               Docker.dockerCmdName
-               "Subcommands specific to Docker use"
-               cmdFooter
-               (do addCommand' Docker.dockerPullCmdName
-                              "Pull latest version of Docker image from registry"
-                              cmdFooter
-                              dockerPullCmd
-                              (pure ())
-                   addCommand' "reset"
-                              "Reset the Docker sandbox"
-                              cmdFooter
-                              dockerResetCmd
-                              (switch (long "keep-home" <>
-                                       help "Do not delete sandbox's home directory"))
-                   addCommand' Docker.dockerCleanupCmdName
-                              "Clean up Docker images and containers"
-                              cmdFooter
-                              dockerCleanupCmd
-                              dockerCleanupOptsParser)
-             addSubCommands'
-                ConfigCmd.cfgCmdName
-                "Subcommands specific to modifying stack.yaml files"
-                cmdFooter
-                (addCommand' ConfigCmd.cfgCmdSetName
-                            "Sets a field in the project's stack.yaml to value"
-                            cmdFooter
-                            cfgSetCmd
-                            configCmdSetParser)
-             addSubCommands'
-               Image.imgCmdName
-               "Subcommands specific to imaging (EXPERIMENTAL)"
-               cmdFooter
-               (addCommand' Image.imgDockerCmdName
-                "Build a Docker image for the project"
-                cmdFooter
-                imgDockerCmd
-                (boolFlags True
-                    "build"
-                    "building the project before creating the container"
-                    idm))
-             addSubCommands'
-               "hpc"
-               "Subcommands specific to Haskell Program Coverage"
-               cmdFooter
-               (addCommand' "report"
-                            "Generate HPC report a combined HPC report"
-                            cmdFooter
-                            hpcReportCmd
-                            hpcReportOptsParser)
-             addSubCommands'
-               Sig.sigCmdName
-               "Subcommands specific to package signatures (EXPERIMENTAL)"
-               cmdFooter
-               (addSubCommands'
-                  Sig.sigSignCmdName
-                  "Sign a a single package or all your packages"
-                  cmdFooter
-                  (addCommand'
-                     Sig.sigSignSdistCmdName
-                     "Sign a single sdist package file"
-                     cmdFooter
-                     sigSignSdistCmd
-                     Sig.sigSignSdistOpts)))
-     case eGlobalRun of
-       Left (exitCode :: ExitCode) -> do
-         when isInterpreter $
-           hPutStrLn stderr $ concat
-             [ "\nIf you are trying to use "
-             , stackProgName
-             , " as a script interpreter, a\n'-- "
-             , stackProgName
-             , " [options] runghc [options]' comment is required."
-             , "\nSee https://github.com/commercialhaskell/stack/blob/release/doc/GUIDE.md#ghcrunghc" ]
-         throwIO exitCode
-       Right (globalMonoid,run) -> do
-         let global = globalOptsFromMonoid isTerminal globalMonoid
-         when (globalLogLevel global == LevelDebug) $ hPutStrLn stderr versionString'
-         case globalReExecVersion global of
-             Just expectVersion
-                 | expectVersion /= showVersion Meta.version ->
-                     throwIO $ InvalidReExecVersion expectVersion (showVersion Meta.version)
-             _ -> return ()
-         run global `catch` \e ->
-            -- This special handler stops "stack: " from being printed before the
-            -- exception
-            case fromException e of
-                Just ec -> exitWith ec
-                Nothing -> do
-                    printExceptionStderr e
-                    exitFailure
+main :: IO ()
+main = do
+  -- Line buffer the output by default, particularly for non-terminal runs.
+  -- See https://github.com/commercialhaskell/stack/pull/360
+  hSetBuffering stdout LineBuffering
+  hSetBuffering stdin  LineBuffering
+  hSetBuffering stderr LineBuffering
+  hSetTranslit stdout
+  hSetTranslit stderr
+  args <- getArgs
+  progName <- getProgName
+  isTerminal <- hIsTerminalDevice stdout
+  execExtraHelp args
+                Docker.dockerHelpOptName
+                (dockerOptsParser False)
+                ("Only showing --" ++ Docker.dockerCmdName ++ "* options.")
+  execExtraHelp args
+                Nix.nixHelpOptName
+                (nixOptsParser False)
+                ("Only showing --" ++ Nix.nixCmdName ++ "* options.")
+
+  eGlobalRun <- try $ commandLineHandler progName False
+  case eGlobalRun of
+    Left (exitCode :: ExitCode) -> do
+      throwIO exitCode
+    Right (globalMonoid,run) -> do
+      let global = globalOptsFromMonoid isTerminal globalMonoid
+      when (globalLogLevel global == LevelDebug) $ hPutStrLn stderr versionString'
+      case globalReExecVersion global of
+          Just expectVersion
+              | expectVersion /= showVersion Meta.version ->
+                  throwIO $ InvalidReExecVersion expectVersion (showVersion Meta.version)
+          _ -> return ()
+      run global `catch` \e ->
+          -- This special handler stops "stack: " from being printed before the
+          -- exception
+          case fromException e of
+              Just ec -> exitWith ec
+              Nothing -> do
+                  printExceptionStderr e
+                  exitFailure
+
+-- Vertically combine only the error component of the first argument with the
+-- error component of the second.
+vcatErrorHelp :: ParserHelp -> ParserHelp -> ParserHelp
+vcatErrorHelp (ParserHelp e1 _ _ _ _) (ParserHelp e2 h2 u2 b2 f2) =
+  ParserHelp (vcatChunks [e2, e1]) h2 u2 b2 f2
+
+commandLineHandler
+  :: String
+  -> Bool
+  -> IO (GlobalOptsMonoid, GlobalOpts -> IO ())
+commandLineHandler progName isInterpreter = complicatedOptions
+  Meta.version
+  (Just versionString')
+  "stack - The Haskell Tool Stack"
+  ""
+  (globalOpts False)
+  (Just failureCallback)
+  (addCommands (globalOpts True) isInterpreter)
   where
-    ignoreCheckSwitch = switch (long "ignore-check" <> help "Do not check package for common mistakes")
-    dockerHelpOptName = Docker.dockerCmdName ++ "-help"
-    nixHelpOptName    = Nix.nixCmdName ++ "-help"
-    cmdFooter = "Run 'stack --help' for global options that apply to all subcommands."
+    failureCallback f args =
+      case stripPrefix "Invalid argument" (fst (renderFailure f "")) of
+          Just _ -> if isInterpreter
+                    then parseResultHandler args f
+                    else secondaryCommandHandler args f
+                        >>= interpreterHandler args
+          Nothing -> parseResultHandler args f
 
+    parseResultHandler args f =
+      if isInterpreter
+      then do
+        let hlp = errorHelp $ stringChunk
+              (unwords ["Error executing interpreter command:"
+                        , progName
+                        , unwords args])
+        handleParseResult (overFailure (vcatErrorHelp hlp) (Failure f))
+      else handleParseResult (Failure f)
+
+    globalOpts hide =
+      extraHelpOption hide progName (Docker.dockerCmdName ++ "*") Docker.dockerHelpOptName <*>
+      extraHelpOption hide progName (Nix.nixCmdName ++ "*") Nix.nixHelpOptName <*>
+      globalOptsParser hide (if isInterpreter
+                             then Just $ LevelOther "silent"
+                             else Nothing)
+
+globalFooter :: String
+globalFooter = "Run 'stack --help' for global options that apply to all subcommands."
+
+addCommands
+  :: Monoid c
+  => Parser c
+  -> Bool
+  -> EitherT (GlobalOpts -> IO ())
+             (Writer (Mod CommandFields (GlobalOpts -> IO (), c)))
+             ()
+addCommands globalOpts isInterpreter = do
+  when (not isInterpreter) (do
+    addCommand' "build"
+                "Build the package(s) in this directory/configuration"
+                buildCmd
+                (buildOptsParser Build)
+    addCommand' "install"
+                "Shortcut for 'build --copy-bins'"
+                buildCmd
+                (buildOptsParser Install)
+    addCommand' "uninstall"
+                "DEPRECATED: This command performs no actions, and is present for documentation only"
+                uninstallCmd
+                (many $ strArgument $ metavar "IGNORED")
+    addCommand' "test"
+                "Shortcut for 'build --test'"
+                buildCmd
+                (buildOptsParser Test)
+    addCommand' "bench"
+                "Shortcut for 'build --bench'"
+                buildCmd
+                (buildOptsParser Bench)
+    addCommand' "haddock"
+                "Shortcut for 'build --haddock'"
+                buildCmd
+                (buildOptsParser Haddock)
+    addCommand' "new"
+                "Create a new project from a template. Run `stack templates' to see available templates."
+                newCmd
+                newOptsParser
+    addCommand' "templates"
+                "List the templates available for `stack new'."
+                templatesCmd
+                (pure ())
+    addCommand' "init"
+                "Initialize a stack project based on one or more cabal packages"
+                initCmd
+                initOptsParser
+    addCommand' "solver"
+                "Use a dependency solver to try and determine missing extra-deps"
+                solverCmd
+                solverOptsParser
+    addCommand' "setup"
+                "Get the appropriate GHC for your project"
+                setupCmd
+                setupParser
+    addCommand' "path"
+                "Print out handy path information"
+                pathCmd
+                (mapMaybeA
+                    (\(desc,name,_) ->
+                         flag Nothing
+                              (Just name)
+                              (long (T.unpack name) <>
+                               help desc))
+                    paths)
+    addCommand' "unpack"
+                "Unpack one or more packages locally"
+                unpackCmd
+                (some $ strArgument $ metavar "PACKAGE")
+    addCommand' "update"
+                "Update the package index"
+                updateCmd
+                (pure ())
+    addCommand' "upgrade"
+                "Upgrade to the latest stack (experimental)"
+                upgradeCmd
+                ((,) <$> switch
+                          ( long "git"
+                         <> help "Clone from Git instead of downloading from Hackage (more dangerous)" )
+                     <*> strOption
+                          ( long "git-repo"
+                         <> help "Clone from specified git repository"
+                         <> value "https://github.com/commercialhaskell/stack"
+                         <> showDefault ))
+    addCommand' "upload"
+                "Upload a package to Hackage"
+                uploadCmd
+                ((,,,)
+                 <$> many (strArgument $ metavar "TARBALL/DIR")
+                 <*> optional pvpBoundsOption
+                 <*> ignoreCheckSwitch
+                 <*> flag False True
+                      (long "sign" <>
+                       help "GPG sign & submit signature"))
+    addCommand' "sdist"
+                "Create source distribution tarballs"
+                sdistCmd
+                ((,,)
+                 <$> many (strArgument $ metavar "DIR")
+                 <*> optional pvpBoundsOption
+                 <*> ignoreCheckSwitch)
+    addCommand' "dot"
+                "Visualize your project's dependency graph using Graphviz dot"
+                dotCmd
+                dotOptsParser
+    addCommand' "exec"
+                "Execute a command"
+                execCmd
+                (execOptsParser Nothing)
+    addCommand' "ghc"
+                "Run ghc"
+                execCmd
+                (execOptsParser $ Just ExecGhc)
+    addCommand' "ghci"
+                "Run ghci in the context of package(s) (experimental)"
+                ghciCmd
+                ghciOptsParser
+    addCommand' "repl"
+                "Run ghci in the context of package(s) (experimental) (alias for 'ghci')"
+                ghciCmd
+                ghciOptsParser
+    )
+
+  -- These two are the only commands allowed in interpreter mode as well
+  addCommand' "runghc"
+              "Run runghc"
+              execCmd
+              (execOptsParser $ Just ExecRunGhc)
+  addCommand' "runhaskell"
+              "Run runghc (alias for 'runghc')"
+              execCmd
+              (execOptsParser $ Just ExecRunGhc)
+
+  when (not isInterpreter) (do
+    addCommand' "eval"
+                "Evaluate some haskell code inline. Shortcut for 'stack exec ghc -- -e CODE'"
+                evalCmd
+                (evalOptsParser "CODE")
+    addCommand' "clean"
+                "Clean the local packages"
+                cleanCmd
+                cleanOptsParser
+    addCommand' "list-dependencies"
+                "List the dependencies"
+                listDependenciesCmd
+                (textOption (long "separator" <>
+                             metavar "SEP" <>
+                             help ("Separator between package name " <>
+                                   "and package version.") <>
+                             value " " <>
+                             showDefault))
+    addCommand' "query"
+                "Query general build information (experimental)"
+                queryCmd
+                (many $ strArgument $ metavar "SELECTOR...")
+    addSubCommands'
+        "ide"
+        "IDE-specific commands"
+        (do addCommand'
+                "start"
+                "Start the ide-backend service"
+                ideCmd
+                ((,) <$> many (textArgument
+                                  (metavar "TARGET" <>
+                                   help ("If none specified, use all " <>
+                                         "packages defined in current directory")))
+                      <*> argsOption (long "ghc-options" <>
+                                    metavar "OPTION" <>
+                                    help "Additional options passed to GHCi" <>
+                                    value []))
+            addCommand'
+                "packages"
+                "List all available local loadable packages"
+                packagesCmd
+                (pure ())
+            addCommand'
+                "load-targets"
+                "List all load targets for a package target"
+                targetsCmd
+                (textArgument
+                    (metavar "TARGET")))
+    addSubCommands'
+      Docker.dockerCmdName
+      "Subcommands specific to Docker use"
+      (do addCommand' Docker.dockerPullCmdName
+                      "Pull latest version of Docker image from registry"
+                      dockerPullCmd
+                      (pure ())
+          addCommand' "reset"
+                      "Reset the Docker sandbox"
+                      dockerResetCmd
+                      (switch (long "keep-home" <>
+                               help "Do not delete sandbox's home directory"))
+          addCommand' Docker.dockerCleanupCmdName
+                      "Clean up Docker images and containers"
+                      dockerCleanupCmd
+                      dockerCleanupOptsParser)
+    addSubCommands'
+        ConfigCmd.cfgCmdName
+        "Subcommands specific to modifying stack.yaml files"
+        (addCommand' ConfigCmd.cfgCmdSetName
+                    "Sets a field in the project's stack.yaml to value"
+                    cfgSetCmd
+                    configCmdSetParser)
+    addSubCommands'
+      Image.imgCmdName
+      "Subcommands specific to imaging (EXPERIMENTAL)"
+      (addCommand' Image.imgDockerCmdName
+        "Build a Docker image for the project"
+        imgDockerCmd
+        (boolFlags True
+            "build"
+            "building the project before creating the container"
+            idm))
+    addSubCommands'
+      "hpc"
+      "Subcommands specific to Haskell Program Coverage"
+      (addCommand' "report"
+                    "Generate HPC report a combined HPC report"
+                    hpcReportCmd
+                    hpcReportOptsParser)
+    addSubCommands'
+      Sig.sigCmdName
+      "Subcommands specific to package signatures (EXPERIMENTAL)"
+      (addSubCommands'
+          Sig.sigSignCmdName
+          "Sign a a single package or all your packages"
+          (addCommand'
+            Sig.sigSignSdistCmdName
+            "Sign a single sdist package file"
+            sigSignSdistCmd
+            Sig.sigSignSdistOpts))
+    )
+  where
+    ignoreCheckSwitch =
+        switch (long "ignore-check"
+                <> help "Do not check package for common mistakes")
+
+    -- addCommand hiding global options
+    addCommand' cmd title constr =
+        addCommand cmd title globalFooter constr globalOpts
+
+    addSubCommands' cmd title =
+        addSubCommands cmd title globalFooter globalOpts
+
+secondaryCommandHandler
+  :: [String]
+  -> ParserFailure ParserHelp
+  -> IO (ParserFailure ParserHelp)
+
+-- fall-through to external executables in `git` style if they exist
+-- (i.e. `stack something` looks for `stack-something` before
+-- failing with "Invalid argument `something'")
+secondaryCommandHandler args f =
+    -- don't even try when the argument looks like a path
+    if elem pathSeparator cmd
+       then return f
+    else do
+      mExternalExec <- Directory.findExecutable cmd
+      case mExternalExec of
+        Just ex -> do
+          menv <- getEnvOverride buildPlatform
+          -- TODO show the command in verbose mode
+          -- hPutStrLn stderr $ unwords $
+          --   ["Running", "[" ++ ex, unwords (tail args) ++ "]"]
+          _ <- runNoLoggingT (exec menv ex (tail args))
+          return f
+        Nothing -> return $ fmap (vcatErrorHelp (noSuchCmd cmd)) f
+  where
+    -- FIXME this is broken when any options are specified before the command
+    -- e.g. stack --verbosity silent cmd
+    cmd = stackProgName ++ "-" ++ (head args)
+    noSuchCmd name = errorHelp $ stringChunk
+      ("Auxiliary command not found in path `" ++ name ++ "'")
+
+interpreterHandler
+  :: Monoid t
+  => [String]
+  -> ParserFailure ParserHelp
+  -> IO (GlobalOptsMonoid, (GlobalOpts -> IO (), t))
+interpreterHandler args f = do
+  isFile <- doesFileExist file
+  if isFile
+  then runInterpreterCommand file
+  else parseResultHandler (errorCombine (noSuchFile file))
+  where
+    file = head args
+
+    -- if the filename contains a path separator then we know that it is not a
+    -- command it is a file to be interpreted. In that case we only show the
+    -- interpreter error message and exclude the command related error messages.
+    errorCombine =
+      if elem pathSeparator file
+      then overrideErrorHelp
+      else vcatErrorHelp
+
+    overrideErrorHelp (ParserHelp e1 _ _ _ _) (ParserHelp _ h2 u2 b2 f2) =
+      ParserHelp e1 h2 u2 b2 f2
+
+    parseResultHandler fn = handleParseResult (overFailure fn (Failure f))
+    noSuchFile name = errorHelp $ stringChunk
+      ("File does not exist or is not a regular file `" ++ name ++ "'")
+
+    runInterpreterCommand path = do
+      progName <- getProgName
+      iargs <- getInterpreterArgs path
+      let parseCmdLine = commandLineHandler progName True
+      let cmdArgs = iargs ++ "--" : args
+       -- TODO show the command in verbose mode
+       -- hPutStrLn stderr $ unwords $
+       --   ["Running", "[" ++ progName, unwords cmdArgs ++ "]"]
+      (a,b) <- withArgs cmdArgs parseCmdLine
+      return (a,(b,mempty))
+
 -- | Print out useful path information in a human-readable format (and
 -- support others later).
 pathCmd :: [Text] -> GlobalOpts -> IO ()
@@ -643,7 +705,7 @@
           (lcProjectRoot lc)
           Nothing
           (runStackTGlobal manager (lcConfig lc) go $
-           Nix.reexecWithOptionalShell $
+           Nix.reexecWithOptionalShell (lcProjectRoot lc) $
            runStackLoggingTGlobal manager go $ do
               (wantedCompiler, compilerCheck, mstack) <-
                   case scoCompilerVersion of
@@ -809,7 +871,7 @@
                  (lcProjectRoot lc)
                  mbefore
                  (runStackTGlobal manager (lcConfig lc) go $
-                    Nix.reexecWithOptionalShell (inner'' lk0))
+                    Nix.reexecWithOptionalShell (lcProjectRoot lc) (inner'' lk0))
                  mafter
                  (Just $ liftIO $
                       do lk' <- readIORef curLk
@@ -951,6 +1013,7 @@
                         config <- asks getConfig
                         menv <- liftIO $ configEnvOverride config plainEnvSettings
                         Nix.reexecWithOptionalShell
+                            (lcProjectRoot lc)
                             (runStackTGlobal manager (lcConfig lc) go $
                                 exec menv cmd args))
                     Nothing
diff --git a/src/test/Stack/ArgsSpec.hs b/src/test/Stack/ArgsSpec.hs
--- a/src/test/Stack/ArgsSpec.hs
+++ b/src/test/Stack/ArgsSpec.hs
@@ -2,21 +2,29 @@
 
 module Stack.ArgsSpec where
 
+import Control.Applicative
+import Control.Exception.Base (assert)
 import Control.Monad
+import Data.Attoparsec.Interpreter (interpreterArgsParser)
+import qualified Data.Attoparsec.Text as P
+import Data.Text (pack)
 import Options.Applicative.Args
+import Stack.Constants (stackProgName)
 import Test.Hspec
 
 -- | Test spec.
 spec :: Spec
-spec =
-    forM_
-        tests
-        (\(input,output) ->
-              it input (parseArgsFromString input == output))
+spec = do
+    argsSpec
+    interpreterArgsSpec
 
+argsSpec :: Spec
+argsSpec = forM_ argsInputOutput
+    (\(input,output) -> it input (parseArgsFromString input == output))
+
 -- | Fairly comprehensive checks.
-tests :: [(String, Either String [String])]
-tests =
+argsInputOutput :: [(String, Either String [String])]
+argsInputOutput =
     [ ("x", Right ["x"])
     , ("x y z", Right ["x", "y", "z"])
     , ("aaa bbb ccc", Right ["aaa", "bbb", "ccc"])
@@ -29,3 +37,97 @@
     , ("\"aa\\\"a\" bbb ccc \"ddd\"", Right ["aa\"a", "bbb", "ccc", "ddd"])
     , ("\"aa\\\"a\" bb\\b ccc \"ddd\"", Right ["aa\"a", "bb\\b", "ccc", "ddd"])
     , ("\"\" \"\" c", Right ["","","c"])]
+
+interpreterArgsSpec :: Spec
+interpreterArgsSpec =
+    describe "Script interpreter parser" $ do
+      describe "Success cases" $ do
+        describe "Line comments" $ do
+          checkLines ""
+          checkLines " --x"
+          checkLines " --x --y"
+        describe "Block comments" $ do
+          checkBlocks ""
+          checkBlocks "\n"
+          checkBlocks " --x"
+          checkBlocks "\n--x"
+          checkBlocks " --x --y"
+          checkBlocks "\n--x\n--y"
+          checkBlocks "\n\t--x\n\t--y"
+      describe "Failure cases" $ do
+        checkFailures
+    where
+      parse s = P.parseOnly (interpreterArgsParser stackProgName) (pack s)
+
+      acceptSuccess args s = case parse s of
+                               Right x | words x == words args -> True
+                               _ -> False
+
+      acceptFailure _ s =  case parse s of
+                           Left _ -> True
+                           Right _ -> False
+
+      showInput i = "BEGIN =>" ++ i ++ "<= END"
+      testAndCheck checker out inp = it (showInput inp) $ checker out inp
+
+      checkLines args = forM_
+        (interpreterGenValid lineComment args)
+        (testAndCheck acceptSuccess args)
+
+      checkBlocks args = forM_
+        (interpreterGenValid blockComment args)
+        (testAndCheck acceptSuccess args)
+
+      checkFailures = forM_
+        interpreterGenInvalid
+        (testAndCheck acceptFailure "unused")
+
+      -- Generate a set of acceptable inputs for given format and args
+      interpreterGenValid fmt args = shebang <++> newLine <++> (fmt args)
+
+      -- Generate a set of Invalid inputs
+      interpreterGenInvalid =
+        ["-stack\n"] -- random input
+        -- just the shebang
+        <|> shebang <++> ["\n"]
+        -- invalid shebang
+        <|> blockSpace <++> [head (interpreterGenValid lineComment args)]
+        -- something between shebang and stack comment
+        <|> shebang
+            <++> newLine
+            <++> blockSpace
+            <++> ([head (lineComment args)] <|> [head (blockComment args)])
+        -- unterminated block comment
+        -- just chop the closing chars from a valid block comment
+        <|> shebang
+            <++> ["\n"]
+            <++> let
+                    c = head (blockComment args)
+                    l = length c - 2
+                 in [assert (drop l c == "-}") (take l c)]
+        -- nested block comment
+        <|> shebang
+            <++> ["\n"]
+            <++> [head (blockComment "--x {- nested -} --y")]
+        where args = " --x --y"
+      (<++>) = liftA2 (++)
+
+      -- Generative grammar for the interpreter comments
+      shebang = ["#!/usr/bin/env stack"]
+      newLine = ["\n"] <|> ["\r\n"]
+
+      -- A comment may be the last line or followed by something else
+      postComment = [""] <|> newLine
+
+      -- A command starts with zero or more whitespace followed by "stack"
+      makeComment maker space args =
+        let makePrefix s = (s <|> [""]) <++> [stackProgName]
+        in (maker <$> ((makePrefix space) <++> [args])) <++> postComment
+
+      lineSpace = [" "] <|> ["\t"]
+      lineComment = makeComment makeLine lineSpace
+        where makeLine s = "--" ++ s
+
+      blockSpace = lineSpace <|> newLine
+      blockComment = makeComment makeBlock blockSpace
+        where makeBlock s = "{-" ++ s ++ "-}"
diff --git a/src/test/Stack/DotSpec.hs b/src/test/Stack/DotSpec.hs
--- a/src/test/Stack/DotSpec.hs
+++ b/src/test/Stack/DotSpec.hs
@@ -5,7 +5,6 @@
 module Stack.DotSpec where
 
 import           Control.Monad (filterM)
-import           Data.ByteString.Char8 (ByteString)
 import           Data.Foldable as F
 import           Data.Functor.Identity
 import           Data.List ((\\))
@@ -13,6 +12,7 @@
 import           Data.Maybe (fromMaybe)
 import           Data.Set (Set)
 import qualified Data.Set as Set
+import           Data.Text (Text)
 import           Stack.Types
 import           Test.Hspec
 import           Test.Hspec.QuickCheck (prop)
@@ -74,7 +74,7 @@
 sublistOf = filterM (\_ -> choose (False, True))
 
 -- Unsafe internal helper to create a package name
-pkgName :: ByteString -> PackageName
+pkgName :: Text -> PackageName
 pkgName = fromMaybe failure . parsePackageName
   where
    failure = error "Internal error during package name creation in DotSpec.pkgName"
diff --git a/src/test/Stack/PackageDumpSpec.hs b/src/test/Stack/PackageDumpSpec.hs
--- a/src/test/Stack/PackageDumpSpec.hs
+++ b/src/test/Stack/PackageDumpSpec.hs
@@ -6,6 +6,7 @@
 import Data.Conduit
 import qualified Data.Conduit.List as CL
 import qualified Data.Conduit.Binary as CB
+import           Data.Conduit.Text (decodeUtf8)
 import Control.Monad.Trans.Resource (runResourceT)
 import Stack.PackageDump
 import Stack.Types
@@ -63,6 +64,7 @@
         it "ghc 7.8" $ do
             haskell2010:_ <- runResourceT
                 $ CB.sourceFile "test/package-dump/ghc-7.8.txt"
+              =$= decodeUtf8
                $$ conduitDumpPackage
                =$ CL.consume
             ghcPkgId <- parseGhcPkgId "haskell2010-1.1.2.0-05c8dd51009e08c6371c82972d40f55a"
@@ -89,6 +91,7 @@
         it "ghc 7.10" $ do
             haskell2010:_ <- runResourceT
                 $ CB.sourceFile "test/package-dump/ghc-7.10.txt"
+              =$= decodeUtf8
                $$ conduitDumpPackage
                =$ CL.consume
             ghcPkgId <- parseGhcPkgId "ghc-7.10.1-325809317787a897b7a97d646ceaa3a3"
@@ -125,6 +128,7 @@
         it "ghc 7.8.4 (osx)" $ do
             hmatrix:_ <- runResourceT
                 $ CB.sourceFile "test/package-dump/ghc-7.8.4-osx.txt"
+              =$= decodeUtf8
                $$ conduitDumpPackage
                =$ CL.consume
             ghcPkgId <- parseGhcPkgId "hmatrix-0.16.1.5-12d5d21f26aa98774cdd8edbc343fbfe"
@@ -156,6 +160,36 @@
                 , dpHaddock = ()
                 , dpIsExposed = True
                 }
+        it "ghc HEAD" $ do
+          ghcBoot:_ <- runResourceT
+              $ CB.sourceFile "test/package-dump/ghc-head.txt"
+            =$= decodeUtf8
+             $$ conduitDumpPackage
+             =$ CL.consume
+          ghcPkgId <- parseGhcPkgId "ghc-boot-0.0.0.0"
+          pkgId <- parsePackageIdentifier "ghc-boot-0.0.0.0"
+          depends <- mapM parseGhcPkgId
+            [ "base-4.9.0.0"
+            , "binary-0.7.5.0"
+            , "bytestring-0.10.7.0"
+            , "directory-1.2.5.0"
+            , "filepath-1.4.1.0"
+            ]
+          ghcBoot `shouldBe` DumpPackage
+            { dpGhcPkgId = ghcPkgId
+            , dpPackageIdent = pkgId
+            , dpLibDirs =
+                  ["/opt/ghc/head/lib/ghc-7.11.20151213/ghc-boot-0.0.0.0"]
+            , dpHaddockInterfaces = ["/opt/ghc/head/share/doc/ghc/html/libraries/ghc-boot-0.0.0.0/ghc-boot.haddock"]
+            , dpHaddockHtml = Just "/opt/ghc/head/share/doc/ghc/html/libraries/ghc-boot-0.0.0.0"
+            , dpDepends = depends
+            , dpLibraries = ["HSghc-boot-0.0.0.0"]
+            , dpHasExposedModules = True
+            , dpProfiling = ()
+            , dpHaddock = ()
+            , dpIsExposed = True
+            }
+
 
     it "ghcPkgDump + addProfiling + addHaddock" $ (id :: IO () -> IO ()) $ runNoLoggingT $ do
         menv' <- getEnvOverride buildPlatform
diff --git a/stack.cabal b/stack.cabal
--- a/stack.cabal
+++ b/stack.cabal
@@ -1,5 +1,5 @@
 name: stack
-version: 0.1.10.1
+version: 1.0.0
 cabal-version: >=1.10
 build-type: Simple
 license: BSD3
@@ -119,19 +119,20 @@
         System.Process.Run
         Network.HTTP.Download.Verified
         Data.Attoparsec.Args
+        Data.Attoparsec.Interpreter
         Data.Maybe.Extra
         Path.IO
         Path.Extra
     build-depends:
         Cabal >=1.18.1.5 && <1.23,
-        aeson >=0.8.0.2 && <0.10,
+        aeson >=0.8.0.2 && <0.11,
         ansi-terminal >=0.6.2.3 && <0.7,
         async >=2.0.2 && <2.1,
         attoparsec >=0.12.1.5 && <0.14,
         base >=4.7 && <5,
         base16-bytestring >=0.1.1.6 && <0.2,
         base64-bytestring >=1.0.0.1 && <1.1,
-        bifunctors >=4.2.1 && <5.1,
+        bifunctors >=4.2.1 && <5.2,
         binary ==0.7.*,
         binary-tagged >=0.1.1 && <0.2,
         blaze-builder >=0.4.0.1 && <0.5,
@@ -147,6 +148,7 @@
         edit-distance ==0.2.*,
         either >=4.4.1 && <4.5,
         enclosed-exceptions >=1.0.1.1 && <1.1,
+        errors >=2.0.1 && <2.1,
         exceptions >=0.8.0.2 && <0.9,
         extra >=1.4.2 && <1.5,
         fast-logger >=2.3.1 && <2.5,
@@ -158,14 +160,14 @@
         http-client >=0.4.17 && <0.5,
         http-client-tls >=0.2.2 && <0.3,
         http-conduit >=2.1.7 && <2.2,
-        http-types >=0.8.6 && <0.9,
+        http-types >=0.8.6 && <0.10,
         lifted-base >=0.2.3.6 && <0.3,
         monad-control >=1.0.0.4 && <1.1,
         monad-logger >=0.3.13.1 && <0.4,
         monad-loops >=0.4.2.1 && <0.5,
         mtl >=2.1.3.1 && <2.3,
         old-locale >=1.0.0.6 && <1.1,
-        optparse-applicative >=0.11.0.2 && <0.12,
+        optparse-applicative >=0.11.0.2 && <0.13,
         path >=0.5.1 && <0.6,
         persistent >=2.1.2 && <2.3,
         persistent-sqlite >=2.1.4 && <2.3,
@@ -175,7 +177,7 @@
         resourcet >=1.1.4.1 && <1.2,
         retry >=0.6 && <0.8,
         safe ==0.3.*,
-        semigroups >=0.5 && <0.18,
+        semigroups >=0.5 && <0.19,
         split >=0.2.2 && <0.3,
         stm >=2.4.4 && <2.5,
         streaming-commons >=0.1.10.0 && <0.2,
@@ -183,6 +185,7 @@
         template-haskell >=2.9.0.0 && <2.11,
         temporary >=1.2.0.3 && <1.3,
         text >=1.2.0.4 && <1.3,
+        text-binary >=0.2.1 && <0.3,
         time >=1.4.2 && <1.6,
         transformers >=0.3.0.0 && <0.5,
         transformers-base >=0.4.4 && <0.5,
@@ -192,7 +195,7 @@
         vector-binary-instances >=0.2.1.0 && <0.3,
         void ==0.7.*,
         yaml >=0.8.10.1 && <0.9,
-        zlib >=0.5.4.2 && <0.6,
+        zlib >=0.5.4.2 && <0.7,
         deepseq ==1.4.*,
         file-embed >=0.0.9 && <0.1,
         word8 >=0.1.2 && <0.2,
@@ -243,11 +246,11 @@
         monad-logger >=0.3.13.1 && <0.4,
         mtl >=2.1.3.1 && <2.3,
         old-locale >=1.0.0.6 && <1.1,
-        optparse-applicative >=0.11.0.2 && <0.12,
-        path >=0.5.2 && <0.6,
+        optparse-applicative >=0.11.0.2 && <0.13,
+        path >=0.5.3 && <0.6,
         process >=1.2.3.0 && <1.3,
         resourcet >=1.1.4.1 && <1.2,
-        stack >=0.1.10.1 && <0.2,
+        stack >=1.0.0 && <1.1,
         text >=1.2.0.4 && <1.3,
         either >=4.4.1 && <4.5,
         directory >=1.2.2.0 && <1.3,
@@ -269,13 +272,14 @@
     build-depends:
         base >=4.7 && <5,
         hspec >=2.1.10 && <2.3,
+        attoparsec >=0.12.1.6 && <0.14,
         containers >=0.5.5.1 && <0.6,
         directory >=1.2.2.0 && <1.3,
         exceptions >=0.8.0.2 && <0.9,
         filepath >=1.4.0.0 && <1.5,
-        path >=0.5.2 && <0.6,
+        path >=0.5.3 && <0.6,
         temporary >=1.2.0.3 && <1.3,
-        stack >=0.1.10.1 && <0.2,
+        stack >=1.0.0 && <1.1,
         monad-logger >=0.3.15 && <0.4,
         http-conduit >=2.1.8 && <2.2,
         cryptohash >=0.11.6 && <0.12,
@@ -285,7 +289,7 @@
         resourcet >=1.1.6 && <1.2,
         Cabal >=1.22.4.0 && <1.23,
         text >=1.2.1.3 && <1.3,
-        optparse-applicative >=0.11.0.2 && <0.12,
+        optparse-applicative >=0.11.0.2 && <0.13,
         bytestring >=0.10.6.0 && <0.11,
         QuickCheck >=2.8.1 && <2.9,
         retry >=0.6 && <0.8
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,6 +1,12 @@
-resolver: lts-3.14
+resolver: lts-3.16
 image:
   container:
     base: "fpco/ubuntu-with-libgmp:14.04"
     entrypoints:
       - stack
+
+nix:
+  # --nix on the command-line to enable.
+  enable: false
+  packages:
+  - zlib
diff --git a/test/integration/lib/StackTest.hs b/test/integration/lib/StackTest.hs
--- a/test/integration/lib/StackTest.hs
+++ b/test/integration/lib/StackTest.hs
@@ -8,6 +8,7 @@
 import System.IO
 import System.Process
 import System.Exit
+import System.Info (os)
 
 run' :: FilePath -> [String] -> IO ExitCode
 run' cmd args = do
@@ -114,3 +115,9 @@
   where special '"' = True
         special ' ' = True
         special _ = False
+
+-- | Extension of executables
+exeExt = if isWindows then ".exe" else ""
+
+-- | Is the OS Windows?
+isWindows = os == "mingw32"
