diff --git a/Network/Wai/Application/Devel.hs b/Network/Wai/Application/Devel.hs
new file mode 100644
--- /dev/null
+++ b/Network/Wai/Application/Devel.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Network.Wai.Application.Devel
+    ( -- * Types
+      AppHolder
+    , AppRunner
+    , WithAppRunner
+      -- * Functions
+    , initAppHolder
+    , swapApp
+    , swapAppSimple
+    , toApp
+    ) where
+
+import Control.Concurrent (forkIO)
+import Control.Concurrent.MVar
+    ( MVar, newEmptyMVar, newMVar
+    , takeMVar, putMVar, readMVar
+    )
+import Network.Wai (Application, responseLBS)
+import Network.HTTP.Types (status500)
+import Data.ByteString.Lazy.Char8 ()
+import Control.Monad.IO.Class (liftIO)
+
+type AppHolder = MVar (Application, MVar ())
+type AppRunner = Application -> IO ()
+type WithAppRunner = AppRunner -> IO ()
+
+initAppHolder :: IO AppHolder
+initAppHolder = do
+    flag <- newEmptyMVar
+    newMVar (initApp, flag)
+  where
+    initApp _ = return
+              $ responseLBS status500 [("Content-Type", "text/plain")]
+              $ "No app has yet been loaded"
+
+swapAppSimple :: Application -> AppHolder -> IO ()
+swapAppSimple app =
+    swapApp war
+  where
+    war f = f app
+
+swapApp :: WithAppRunner -> AppHolder -> IO ()
+swapApp war ah = void $ forkIO $ war $ \app -> do
+    (_, oldFlag) <- takeMVar ah
+    -- allow the old app to cleanup
+    putMVar oldFlag ()
+    -- now place the new app into the AppHolder, waiting for a termination
+    -- signal
+    flag <- newEmptyMVar
+    putMVar ah (app, flag)
+    takeMVar flag -- this causes execution to hang until we are terminated
+  where
+    void x = x >> return ()
+
+toApp :: AppHolder -> Application
+toApp ah req = do
+    (app, _) <- liftIO $ readMVar ah
+    app req
diff --git a/Scaffold/Build.hs b/Scaffold/Build.hs
new file mode 100644
--- /dev/null
+++ b/Scaffold/Build.hs
@@ -0,0 +1,125 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Scaffold.Build
+    ( build
+    , getDeps
+    , touchDeps
+    , findHaskellFiles
+    ) where
+
+-- FIXME there's a bug when getFileStatus applies to a file temporary deleted (e.g., Vim saving a file)
+
+import qualified Distribution.Simple.Build as B
+import System.Directory (getDirectoryContents, doesDirectoryExist)
+import Data.List (isSuffixOf)
+import Distribution.Simple.Setup (defaultBuildFlags)
+import Distribution.Simple.Configure (getPersistBuildConfig)
+import Distribution.Simple.LocalBuildInfo
+import qualified Data.Attoparsec.Text.Lazy as A
+import qualified Data.Text.Lazy.IO as TIO
+import Control.Applicative ((<|>))
+import Data.Char (isSpace)
+import Data.Maybe (mapMaybe)
+import Data.Monoid (mappend)
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import System.PosixCompat.Files (accessTime, modificationTime, getFileStatus, setFileTimes)
+
+build :: IO ()
+build = do
+    {-
+    cabal <- defaultPackageDesc normal
+    gpd <- readPackageDescription normal cabal
+    putStrLn $ showPackageDescription $ packageDescription gpd
+    -}
+    hss <- findHaskellFiles "."
+    deps' <- mapM determineHamletDeps hss
+    let deps = fixDeps $ zip hss deps'
+    touchDeps deps
+
+    lbi <- getPersistBuildConfig "dist"
+    B.build
+        (localPkgDescr lbi)
+        lbi
+        defaultBuildFlags
+        []
+
+type Deps = Map.Map FilePath (Set.Set FilePath)
+
+getDeps :: IO Deps
+getDeps = do
+    hss <- findHaskellFiles "."
+    deps' <- mapM determineHamletDeps hss
+    return $ fixDeps $ zip hss deps'
+
+touchDeps :: Deps -> IO ()
+touchDeps =
+    mapM_ go . Map.toList
+  where
+    go (x, ys) = do
+        fs <- getFileStatus x
+        flip mapM_ (Set.toList ys) $ \y -> do
+            fs' <- getFileStatus y
+            if modificationTime fs' < modificationTime fs
+                then do
+                    putStrLn $ "Touching " ++ y ++ " because of " ++ x
+                    setFileTimes y (accessTime fs') (modificationTime fs)
+                else return ()
+
+fixDeps :: [(FilePath, [FilePath])] -> Deps
+fixDeps =
+    Map.unionsWith mappend . map go
+  where
+    go :: (FilePath, [FilePath]) -> Deps
+    go (x, ys) = Map.fromList $ map (\y -> (y, Set.singleton x)) ys
+
+findHaskellFiles :: FilePath -> IO [FilePath]
+findHaskellFiles path = do
+    contents <- getDirectoryContents path
+    fmap concat $ mapM go contents
+  where
+    go ('.':_) = return []
+    go "dist" = return []
+    go x = do
+        let y = path ++ '/' : x
+        d <- doesDirectoryExist y
+        if d
+            then findHaskellFiles y
+            else if ".hs" `isSuffixOf` x || ".lhs" `isSuffixOf` x
+                    then return [y]
+                    else return []
+
+data TempType = Hamlet | Cassius | Lucius | Julius | Widget | Verbatim
+    deriving Show
+
+determineHamletDeps :: FilePath -> IO [FilePath]
+determineHamletDeps x = do
+    y <- TIO.readFile x
+    let z = A.parse (A.many $ (parser <|> (A.anyChar >> return Nothing))) y
+    case z of
+        A.Fail{} -> return []
+        A.Done _ r -> return $ mapMaybe go r
+  where
+    go (Just (Hamlet, f)) = Just $ "hamlet/" ++ f ++ ".hamlet"
+    go (Just (Widget, f)) = Just $ "hamlet/" ++ f ++ ".hamlet"
+    go (Just (Verbatim, f)) = Just f
+    go _ = Nothing
+    parser = do
+        ty <- (A.string "$(hamletFile " >> return Hamlet)
+           <|> (A.string "$(cassiusFile " >> return Cassius)
+           <|> (A.string "$(luciusFile " >> return Lucius)
+           <|> (A.string "$(juliusFile " >> return Julius)
+           <|> (A.string "$(widgetFile " >> return Widget)
+           <|> (A.string "$(Settings.hamletFile " >> return Hamlet)
+           <|> (A.string "$(Settings.cassiusFile " >> return Cassius)
+           <|> (A.string "$(Settings.luciusFile " >> return Lucius)
+           <|> (A.string "$(Settings.juliusFile " >> return Julius)
+           <|> (A.string "$(Settings.widgetFile " >> return Widget)
+           <|> (A.string "$(persistFile " >> return Verbatim)
+           <|> (A.string "$(parseRoutesFile " >> return Verbatim)
+        A.skipWhile isSpace
+        _ <- A.char '"'
+        y <- A.many1 $ A.satisfy (/= '"')
+        _ <- A.char '"'
+        A.skipWhile isSpace
+        _ <- A.char ')'
+        return $ Just (ty, y)
diff --git a/Scaffold/Devel.hs b/Scaffold/Devel.hs
new file mode 100644
--- /dev/null
+++ b/Scaffold/Devel.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Scaffold.Devel
+    ( devel
+    ) where
+
+import qualified Distribution.Simple.Build as B
+import Distribution.Simple.Configure (configure)
+import Distribution.Simple.Setup (defaultConfigFlags, configConfigurationsFlags, configUserInstall, Flag (..), defaultBuildFlags, defaultCopyFlags, defaultRegisterFlags)
+import Distribution.Simple.Utils (defaultPackageDesc, defaultHookedPackageDesc)
+import Distribution.Simple.Program (defaultProgramConfiguration)
+import Distribution.Verbosity (normal)
+import Distribution.PackageDescription.Parse (readPackageDescription, readHookedBuildInfo)
+import Distribution.PackageDescription (FlagName (FlagName), package, emptyHookedBuildInfo)
+import Distribution.Simple.LocalBuildInfo (localPkgDescr)
+import Scaffold.Build (getDeps, touchDeps, findHaskellFiles)
+import System.Plugins (loadDynamic)
+import Network.Wai.Handler.Warp (run)
+import Network.Wai.Application.Devel
+import Network.Wai.Middleware.Debug (debug)
+import Data.Dynamic (fromDynamic)
+import Distribution.Text (display)
+import Distribution.Simple.Install (install)
+import Distribution.Simple.Register (register)
+import Control.Concurrent (forkIO, threadDelay)
+import Control.Exception (try, SomeException)
+import System.PosixCompat.Files (modificationTime, getFileStatus)
+import qualified Data.Map as Map
+import System.Posix.Types (EpochTime)
+import Blaze.ByteString.Builder.Char.Utf8 (fromString)
+import Network.Wai (Application, Response (ResponseBuilder))
+import Network.HTTP.Types (status500)
+import Control.Monad (when)
+
+devel :: IO ()
+devel = do
+    appHolder <- initAppHolder
+    _ <- forkIO $ run 3000 $ debug $ toApp appHolder
+
+    cabal <- defaultPackageDesc normal
+    gpd <- readPackageDescription normal cabal
+
+    mhpd <- defaultHookedPackageDesc
+    hooked <-
+        case mhpd of
+            Nothing -> return emptyHookedBuildInfo
+            Just fp -> readHookedBuildInfo normal fp
+
+    lbi <- configure (gpd, hooked) (defaultConfigFlags defaultProgramConfiguration)
+        { configConfigurationsFlags = [(FlagName "devel", True)]
+        , configUserInstall = Flag True
+        }
+
+    let myTry :: IO (Either String x) -> IO (Either String x)
+        myTry f = try f >>= \x -> return $ case x of
+                                    Left e -> Left $ show (e :: SomeException)
+                                    Right y -> y
+    let getNewApp :: IO (Either String WithAppRunner)
+        getNewApp = myTry $ do
+            deps <- getDeps
+            touchDeps deps
+
+            B.build
+                (localPkgDescr lbi)
+                lbi
+                defaultBuildFlags
+                []
+
+            install (localPkgDescr lbi) lbi defaultCopyFlags
+            register (localPkgDescr lbi) lbi defaultRegisterFlags
+
+            let pi' = display $ package $ localPkgDescr lbi
+            dyn <- loadDynamic (pi', "Controller", "withDevelApp")
+            return $ case fmap fromDynamic dyn of
+                Nothing -> Left "withDevelApp not found"
+                Just Nothing -> Left "Not a withApp"
+                Just (Just withApp) -> Right withApp
+
+    loop Map.empty appHolder getNewApp
+
+type FileList = Map.Map FilePath EpochTime
+
+getFileList :: IO FileList
+getFileList = do
+    files <- findHaskellFiles "."
+    deps <- getDeps
+    let files' = files ++ map fst (Map.toList deps)
+    fmap Map.fromList $ flip mapM files' $ \f -> do
+        fs <- getFileStatus f
+        return (f, modificationTime fs)
+
+loop :: FileList -> AppHolder -> IO (Either String WithAppRunner) -> IO ()
+loop oldList appHolder getNewApp = do
+    newList <- getFileList
+    when (newList /= oldList) $ do
+        res <- getNewApp
+        case res of
+            Left s -> swapAppSimple (errApp s) appHolder
+            Right x -> swapApp x appHolder
+    threadDelay 1000000
+    loop newList appHolder getNewApp
+
+errApp :: String -> Application
+errApp s _ = return $ ResponseBuilder status500 [("Content-Type", "text/plain")] $ fromString s
diff --git a/Yesod.hs b/Yesod.hs
--- a/Yesod.hs
+++ b/Yesod.hs
@@ -3,12 +3,7 @@
 -- | This module simply re-exports from other modules for your convenience.
 module Yesod
     ( -- * Re-exports from yesod-core
-      module Yesod.Request
-    , module Yesod.Content
-    , module Yesod.Core
-    , module Yesod.Handler
-    , module Yesod.Dispatch
-    , module Yesod.Widget
+      module Yesod.Core
     , module Yesod.Form
     , module Yesod.Json
     , module Yesod.Persist
@@ -20,7 +15,7 @@
     , Application
     , lift
     , liftIO
-    , MonadPeelIO
+    , MonadControlIO
       -- * Utilities
     , showIntegral
     , readIntegral
@@ -35,6 +30,7 @@
     , string
     , preEscapedString
     , cdata
+    , toHtml
       -- ** Julius
     , julius
     , Julius
@@ -45,16 +41,11 @@
     , renderCassius
     ) where
 
-import Yesod.Content
-import Yesod.Dispatch
 import Yesod.Core
-import Yesod.Handler hiding (runHandler)
 import Text.Hamlet
 import Text.Cassius
 import Text.Julius
 
-import Yesod.Request
-import Yesod.Widget
 import Yesod.Form
 import Yesod.Json
 import Yesod.Persist
@@ -62,7 +53,7 @@
 import Network.Wai.Middleware.Debug
 import Control.Monad.Trans.Class (lift)
 import Control.Monad.IO.Class (liftIO)
-import Control.Monad.IO.Peel (MonadPeelIO)
+import Control.Monad.IO.Control (MonadControlIO)
 
 import Network.Wai.Handler.Warp (run)
 import System.IO (stderr, hPutStrLn)
diff --git a/scaffold.hs b/scaffold.hs
--- a/scaffold.hs
+++ b/scaffold.hs
@@ -10,10 +10,14 @@
 import qualified Data.ByteString.Lazy as L
 import qualified Data.Text.Lazy as LT
 import qualified Data.Text.Lazy.Encoding as LT
-import Control.Monad (when)
+import Control.Monad (when, unless)
+import System.Environment (getArgs)
 
+import Scaffold.Build (build)
+import Scaffold.Devel (devel)
+
 qq :: String
-#if GHC7
+#if __GLASGOW_HASKELL__ >= 700
 qq = ""
 #else
 qq = "$"
@@ -30,6 +34,20 @@
 
 main :: IO ()
 main = do
+    args <- getArgs
+    case args of
+        ["init"] -> scaffold
+        ["build"] -> build
+        ["devel"] -> devel
+        _ -> do
+            putStrLn "Usage: yesod <command>"
+            putStrLn "Available commands:"
+            putStrLn "    init         Scaffold a new site"
+            putStrLn "    build        Build project (performs TH dependency analysis)"
+            putStrLn "    devel        Run project with the devel server"
+
+scaffold :: IO ()
+scaffold = do
     putStr $(codegen "welcome")
     hFlush stdout
     name <- getLine
@@ -53,7 +71,7 @@
     putStr $(codegen "site-arg")
     hFlush stdout
     let isUpperAZ c = 'A' <= c && c <= 'Z'
-    sitearg <- prompt $ \s -> not (null s) && all validPN s && isUpperAZ (head s)
+    sitearg <- prompt $ \s -> not (null s) && all validPN s && isUpperAZ (head s) && s /= "Main"
 
     putStr $(codegen "database")
     hFlush stdout
@@ -80,29 +98,31 @@
     mkDir "Handler"
     mkDir "hamlet"
     mkDir "cassius"
+    mkDir "lucius"
     mkDir "julius"
     mkDir "static"
+    mkDir "config"
 
-    writeFile' "test.hs" $(codegen "test_hs")
-    writeFile' "production.hs" $(codegen "production_hs")
-    writeFile' "devel-server.hs" $(codegen "devel-server_hs")
+    writeFile' ("config/" ++ project ++ ".hs") $(codegen "test_hs")
     writeFile' (project ++ ".cabal") $ if backendS == "m" then $(codegen "mini-cabal") else $(codegen "cabal")
     writeFile' "LICENSE" $(codegen "LICENSE")
     writeFile' (sitearg ++ ".hs") $ if backendS == "m" then $(codegen "mini-sitearg_hs") else $(codegen "sitearg_hs")
     writeFile' "Controller.hs" $ if backendS == "m" then $(codegen "mini-Controller_hs") else $(codegen "Controller_hs")
     writeFile' "Handler/Root.hs" $ if backendS == "m" then $(codegen "mini-Root_hs") else $(codegen "Root_hs")
     when (backendS /= "m") $ writeFile' "Model.hs" $(codegen "Model_hs")
-    writeFile' "Settings.hs" $ if backendS == "m" then $(codegen "mini-Settings_hs") else $(codegen "Settings_hs")
-    writeFile' "StaticFiles.hs" $(codegen "StaticFiles_hs")
+    writeFile' "config/Settings.hs" $ if backendS == "m" then $(codegen "mini-Settings_hs") else $(codegen "Settings_hs")
+    writeFile' "config/StaticFiles.hs" $(codegen "StaticFiles_hs")
     writeFile' "cassius/default-layout.cassius"
         $(codegen "default-layout_cassius")
     writeFile' "hamlet/default-layout.hamlet"
         $(codegen "default-layout_hamlet")
     writeFile' "hamlet/homepage.hamlet" $ if backendS == "m" then $(codegen "mini-homepage_hamlet") else $(codegen "homepage_hamlet")
+    writeFile' "config/routes" $ if backendS == "m" then $(codegen "mini-routes") else $(codegen "routes")
     writeFile' "cassius/homepage.cassius" $(codegen "homepage_cassius")
     writeFile' "julius/homepage.julius" $(codegen "homepage_julius")
+    unless (backendS == "m") $ writeFile' "config/models" $(codegen "entities")
   
-    S.writeFile (dir ++ "/favicon.ico")
+    S.writeFile (dir ++ "/config/favicon.ico")
         $(runIO (S.readFile "scaffold/favicon_ico.cg") >>= \bs -> do
             pack <- [|S.pack|]
             return $ pack `AppE` LitE (StringL $ S.unpack bs))
diff --git a/scaffold/Controller_hs.cg b/scaffold/Controller_hs.cg
--- a/scaffold/Controller_hs.cg
+++ b/scaffold/Controller_hs.cg
@@ -4,6 +4,7 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Controller
     ( with~sitearg~
+    , withDevelApp
     ) where
 
 import ~sitearg~
@@ -12,6 +13,7 @@
 import Yesod.Helpers.Auth
 import Database.Persist.GenericSql
 import Data.ByteString (ByteString)
+import Data.Dynamic (Dynamic, toDyn)
 
 -- Import all relevant handler modules here.
 import Handler.Root
@@ -24,7 +26,7 @@
 -- Some default handlers that ship with the Yesod site template. You will
 -- very rarely need to modify this.
 getFaviconR :: Handler ()
-getFaviconR = sendFile "image/x-icon" "favicon.ico"
+getFaviconR = sendFile "image/x-icon" "config/favicon.ico"
 
 getRobotsR :: Handler RepPlain
 getRobotsR = return $ RepPlain $ toContent ("User-agent: *" :: ByteString)
@@ -40,4 +42,7 @@
     toWaiApp h >>= f
   where
     s = static Settings.staticdir
+
+withDevelApp :: Dynamic
+withDevelApp = toDyn (with~sitearg~ :: (Application -> IO ()) -> IO ())
 
diff --git a/scaffold/Model_hs.cg b/scaffold/Model_hs.cg
--- a/scaffold/Model_hs.cg
+++ b/scaffold/Model_hs.cg
@@ -2,21 +2,11 @@
 module Model where
 
 import Yesod
-import Database.Persist.TH (share2)
-import Database.Persist.GenericSql (mkMigrate)
+import Data.Text (Text)
 
--- You can define all of your database entities here. You can find more
--- information on persistent and how to declare entities at:
--- http://docs.yesodweb.com/book/persistent/
-share2 mkPersist (mkMigrate "migrateAll") [~qq~persist|
-User
-    ident String
-    password String Maybe Update
-    UniqueUser ident
-Email
-    email String
-    user UserId Maybe Update
-    verkey String Maybe Update
-    UniqueEmail email
-|]
+-- You can define all of your database entities in the entities file.
+-- You can find more information on persistent and how to declare entities
+-- at:
+-- http://www.yesodweb.com/book/persistent/
+share [mkPersist, mkMigrate "migrateAll"] $(persistFile "config/models")
 
diff --git a/scaffold/Settings_hs.cg b/scaffold/Settings_hs.cg
--- a/scaffold/Settings_hs.cg
+++ b/scaffold/Settings_hs.cg
@@ -1,5 +1,6 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE OverloadedStrings #-}
 -- | Settings are centralized, as much as possible, into this file. This
 -- includes database connection settings, static file locations, etc.
 -- In addition, you can configure a number of different aspects of Yesod
@@ -9,6 +10,7 @@
     ( hamletFile
     , cassiusFile
     , juliusFile
+    , luciusFile
     , widgetFile
     , connStr
     , ConnectionPool
@@ -22,16 +24,18 @@
 import qualified Text.Hamlet as H
 import qualified Text.Cassius as H
 import qualified Text.Julius as H
+import qualified Text.Lucius as H
 import Language.Haskell.TH.Syntax
 ~importDB~
-import Yesod (MonadPeelIO, addWidget, addCassius, addJulius)
-import Data.Monoid (mempty)
+import Yesod (MonadControlIO, addWidget, addCassius, addJulius, addLucius)
+import Data.Monoid (mempty, mappend)
 import System.Directory (doesFileExist)
+import Data.Text (Text)
 
 -- | The base URL for your application. This will usually be different for
 -- development and production. Yesod automatically constructs URLs for you,
 -- so this value must be accurate to create valid links.
-approot :: String
+approot :: Text
 #ifdef PRODUCTION
 -- You probably want to change this. If your domain name was "yesod.com",
 -- you would probably want it to be:
@@ -60,12 +64,12 @@
 -- have to make a corresponding change here.
 --
 -- To see how this value is used, see urlRenderOverride in ~sitearg~.hs
-staticroot :: String
-staticroot = approot ++ "/static"
+staticroot :: Text
+staticroot = approot `mappend` "/static"
 
 -- | The database connection string. The meaning of this string is backend-
 -- specific.
-connStr :: String
+connStr :: Text
 #ifdef PRODUCTION
 connStr = "~connstr2~"
 #else
@@ -102,10 +106,11 @@
 -- used; to get the same auto-loading effect, it is recommended that you
 -- use the devel server.
 
-toHamletFile, toCassiusFile, toJuliusFile :: String -> FilePath
+toHamletFile, toCassiusFile, toJuliusFile, toLuciusFile :: String -> FilePath
 toHamletFile x = "hamlet/" ++ x ++ ".hamlet"
 toCassiusFile x = "cassius/" ++ x ++ ".cassius"
 toJuliusFile x = "julius/" ++ x ++ ".julius"
+toLuciusFile x = "lucius/" ++ x ++ ".lucius"
 
 hamletFile :: FilePath -> Q Exp
 hamletFile = H.hamletFile . toHamletFile
@@ -117,6 +122,13 @@
 cassiusFile = H.cassiusFileDebug . toCassiusFile
 #endif
 
+luciusFile :: FilePath -> Q Exp
+#ifdef PRODUCTION
+luciusFile = H.luciusFile . toLuciusFile
+#else
+luciusFile = H.luciusFileDebug . toLuciusFile
+#endif
+
 juliusFile :: FilePath -> Q Exp
 #ifdef PRODUCTION
 juliusFile = H.juliusFile . toJuliusFile
@@ -129,7 +141,8 @@
     let h = unlessExists toHamletFile hamletFile
     let c = unlessExists toCassiusFile cassiusFile
     let j = unlessExists toJuliusFile juliusFile
-    [|addWidget $h >> addCassius $c >> addJulius $j|]
+    let l = unlessExists toLuciusFile luciusFile
+    [|addWidget $h >> addCassius $c >> addJulius $j >> addLucius $l|]
   where
     unlessExists tofn f = do
         e <- qRunIO $ doesFileExist $ tofn x
@@ -139,9 +152,9 @@
 -- database actions using a pool, respectively. It is used internally
 -- by the scaffolded application, and therefore you will rarely need to use
 -- them yourself.
-withConnectionPool :: MonadPeelIO m => (ConnectionPool -> m a) -> m a
+withConnectionPool :: MonadControlIO m => (ConnectionPool -> m a) -> m a
 withConnectionPool = with~upper~Pool connStr connectionCount
 
-runConnectionPool :: MonadPeelIO m => SqlPersist m a -> ConnectionPool -> m a
+runConnectionPool :: MonadControlIO m => SqlPersist m a -> ConnectionPool -> m a
 runConnectionPool = runSqlPool
 
diff --git a/scaffold/cabal.cg b/scaffold/cabal.cg
--- a/scaffold/cabal.cg
+++ b/scaffold/cabal.cg
@@ -16,12 +16,38 @@
     Description:   Build the production executable.
     Default:       False
 
-executable         ~project~-test
-    if flag(production)
+Flag devel
+    Description:   Build for use with "yesod devel"
+    Default:       False
+
+library
+    if flag(devel)
+        Buildable: True
+    else
         Buildable: False
-    main-is:       test.hs
+    exposed-modules: Controller
+    hs-source-dirs: ., config
+    other-modules:   ~sitearg~
+                     Model
+                     Settings
+                     StaticFiles
+                     Handler.Root
+
+executable         ~project~
+    if flag(devel)
+        Buildable: False
+
+    if flag(production)
+        cpp-options:   -DPRODUCTION
+        ghc-options:   -Wall -threaded -O2
+    else
+        ghc-options:   -Wall -threaded
+
+    main-is:       config/~project~.hs
+    hs-source-dirs: ., config
+
     build-depends: base         >= 4       && < 5
-                 , yesod        >= 0.7     && < 0.8
+                 , yesod        >= 0.8     && < 0.9
                  , yesod-auth
                  , yesod-static
                  , mime-mail
@@ -30,27 +56,13 @@
                  , bytestring
                  , text
                  , persistent
-                 , persistent-~lower~ >= 0.4 && < 0.5
+                 , persistent-template
+                 , persistent-~lower~ >= 0.5 && < 0.6
                  , template-haskell
                  , hamlet
                  , web-routes
                  , hjsmin
                  , transformers
                  , warp
-    ghc-options:   -Wall -threaded
-
-executable         ~project~-production
-    if flag(production)
-        Buildable: True
-    else
-        Buildable: False
-    cpp-options:   -DPRODUCTION
-    main-is:       production.hs
-    ghc-options:   -Wall -threaded
-
-executable         ~project~-devel
-    if flag(production)
-        Buildable: False
-    main-is:       devel-server.hs
-    ghc-options:   -Wall -O2 -threaded
+                 , blaze-builder
 
diff --git a/scaffold/devel-server_hs.cg b/scaffold/devel-server_hs.cg
deleted file mode 100644
--- a/scaffold/devel-server_hs.cg
+++ /dev/null
@@ -1,3 +0,0 @@
-main :: IO ()
-main = putStrLn "Please run: wai-handler-devel 3000 Controller with~sitearg~ --yesod"
-
diff --git a/scaffold/entities.cg b/scaffold/entities.cg
new file mode 100644
--- /dev/null
+++ b/scaffold/entities.cg
@@ -0,0 +1,10 @@
+User
+    ident Text
+    password Text Maybe Update
+    UniqueUser ident
+Email
+    email Text
+    user UserId Maybe Update
+    verkey Text Maybe Update
+    UniqueEmail email
+
diff --git a/scaffold/mini-Controller_hs.cg b/scaffold/mini-Controller_hs.cg
--- a/scaffold/mini-Controller_hs.cg
+++ b/scaffold/mini-Controller_hs.cg
@@ -4,6 +4,7 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Controller
     ( with~sitearg~
+    , withDevelApp
     ) where
 
 import ~sitearg~
@@ -11,6 +12,7 @@
 import Yesod.Helpers.Static
 import Data.ByteString (ByteString)
 import Network.Wai (Application)
+import Data.Dynamic (Dynamic, toDyn)
 
 -- Import all relevant handler modules here.
 import Handler.Root
@@ -23,7 +25,7 @@
 -- Some default handlers that ship with the Yesod site template. You will
 -- very rarely need to modify this.
 getFaviconR :: Handler ()
-getFaviconR = sendFile "image/x-icon" "favicon.ico"
+getFaviconR = sendFile "image/x-icon" "config/favicon.ico"
 
 getRobotsR :: Handler RepPlain
 getRobotsR = return $ RepPlain $ toContent ("User-agent: *" :: ByteString)
@@ -38,3 +40,7 @@
     toWaiApp h >>= f
   where
     s = static Settings.staticdir
+
+withDevelApp :: Dynamic
+withDevelApp = toDyn (with~sitearg~ :: (Application -> IO ()) -> IO ())
+
diff --git a/scaffold/mini-Settings_hs.cg b/scaffold/mini-Settings_hs.cg
--- a/scaffold/mini-Settings_hs.cg
+++ b/scaffold/mini-Settings_hs.cg
@@ -1,5 +1,6 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE OverloadedStrings #-}
 -- | Settings are centralized, as much as possible, into this file. This
 -- includes database connection settings, static file locations, etc.
 -- In addition, you can configure a number of different aspects of Yesod
@@ -9,6 +10,7 @@
     ( hamletFile
     , cassiusFile
     , juliusFile
+    , luciusFile
     , widgetFile
     , approot
     , staticroot
@@ -18,15 +20,17 @@
 import qualified Text.Hamlet as H
 import qualified Text.Cassius as H
 import qualified Text.Julius as H
+import qualified Text.Lucius as H
 import Language.Haskell.TH.Syntax
-import Yesod.Widget (addWidget, addCassius, addJulius)
-import Data.Monoid (mempty)
+import Yesod.Widget (addWidget, addCassius, addJulius, addLucius)
+import Data.Monoid (mempty, mappend)
 import System.Directory (doesFileExist)
+import Data.Text (Text)
 
 -- | The base URL for your application. This will usually be different for
 -- development and production. Yesod automatically constructs URLs for you,
 -- so this value must be accurate to create valid links.
-approot :: String
+approot :: Text
 #ifdef PRODUCTION
 -- You probably want to change this. If your domain name was "yesod.com",
 -- you would probably want it to be:
@@ -55,8 +59,8 @@
 -- have to make a corresponding change here.
 --
 -- To see how this value is used, see urlRenderOverride in ~project~.hs
-staticroot :: String
-staticroot = approot ++ "/static"
+staticroot :: Text
+staticroot = approot `mappend` "/static"
 
 -- The rest of this file contains settings which rarely need changing by a
 -- user.
@@ -74,10 +78,11 @@
 -- used; to get the same auto-loading effect, it is recommended that you
 -- use the devel server.
 
-toHamletFile, toCassiusFile, toJuliusFile :: String -> FilePath
+toHamletFile, toCassiusFile, toJuliusFile, toLuciusFile :: String -> FilePath
 toHamletFile x = "hamlet/" ++ x ++ ".hamlet"
 toCassiusFile x = "cassius/" ++ x ++ ".cassius"
 toJuliusFile x = "julius/" ++ x ++ ".julius"
+toLuciusFile x = "lucius/" ++ x ++ ".lucius"
 
 hamletFile :: FilePath -> Q Exp
 hamletFile = H.hamletFile . toHamletFile
@@ -89,6 +94,13 @@
 cassiusFile = H.cassiusFileDebug . toCassiusFile
 #endif
 
+luciusFile :: FilePath -> Q Exp
+#ifdef PRODUCTION
+luciusFile = H.luciusFile . toLuciusFile
+#else
+luciusFile = H.luciusFileDebug . toLuciusFile
+#endif
+
 juliusFile :: FilePath -> Q Exp
 #ifdef PRODUCTION
 juliusFile = H.juliusFile . toJuliusFile
@@ -101,7 +113,8 @@
     let h = unlessExists toHamletFile hamletFile
     let c = unlessExists toCassiusFile cassiusFile
     let j = unlessExists toJuliusFile juliusFile
-    [|addWidget $h >> addCassius $c >> addJulius $j|]
+    let l = unlessExists toLuciusFile luciusFile
+    [|addWidget $h >> addCassius $c >> addJulius $j >> addLucius $l|]
   where
     unlessExists tofn f = do
         e <- qRunIO $ doesFileExist $ tofn x
diff --git a/scaffold/mini-cabal.cg b/scaffold/mini-cabal.cg
--- a/scaffold/mini-cabal.cg
+++ b/scaffold/mini-cabal.cg
@@ -16,12 +16,37 @@
     Description:   Build the production executable.
     Default:       False
 
-executable         ~project~-test
-    if flag(production)
+Flag devel
+    Description:   Build for use with "yesod devel"
+    Default:       False
+
+library
+    if flag(devel)
+        Buildable: True
+    else
         Buildable: False
-    main-is:       test.hs
+    exposed-modules: Controller
+    hs-source-dirs: ., config
+    other-modules:   ~sitearg~
+                     Settings
+                     StaticFiles
+                     Handler.Root
+
+executable         ~project~
+    if flag(devel)
+        Buildable: False
+
+    if flag(production)
+        cpp-options:   -DPRODUCTION
+        ghc-options:   -Wall -threaded -O2
+    else
+        ghc-options:   -Wall -threaded
+
+    main-is:       config/~project~.hs
+    hs-source-dirs: ., config
+
     build-depends: base         >= 4       && < 5
-                 , yesod-core   >= 0.7     && < 0.8
+                 , yesod-core   >= 0.8     && < 0.9
                  , yesod-static
                  , wai-extra
                  , directory
@@ -33,19 +58,6 @@
                  , transformers
                  , wai
                  , warp
-    ghc-options:   -Wall -threaded
-
-executable         ~project~-production
-    if flag(production)
-        Buildable: True
-    else
-        Buildable: False
-    cpp-options:   -DPRODUCTION
-    main-is:       production.hs
+                 , blaze-builder
     ghc-options:   -Wall -threaded
 
-executable         ~project~-devel
-    if flag(production)
-        Buildable: False
-    main-is:       devel-server.hs
-    ghc-options:   -Wall -O2 -threaded
diff --git a/scaffold/mini-routes.cg b/scaffold/mini-routes.cg
new file mode 100644
--- /dev/null
+++ b/scaffold/mini-routes.cg
@@ -0,0 +1,7 @@
+/static StaticR Static getStatic
+
+/favicon.ico FaviconR GET
+/robots.txt RobotsR GET
+
+/ RootR GET
+
diff --git a/scaffold/mini-sitearg_hs.cg b/scaffold/mini-sitearg_hs.cg
--- a/scaffold/mini-sitearg_hs.cg
+++ b/scaffold/mini-sitearg_hs.cg
@@ -1,26 +1,19 @@
 {-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies #-}
+{-# LANGUAGE OverloadedStrings #-}
 module ~sitearg~
     ( ~sitearg~ (..)
     , ~sitearg~Route (..)
     , resources~sitearg~
     , Handler
     , Widget
-    , module Yesod.Handler
-    , module Yesod.Widget
-    , module Yesod.Dispatch
     , module Yesod.Core
-    , module Yesod.Content
     , module Settings
     , StaticRoute (..)
     , lift
     , liftIO
     ) where
 
-import Yesod.Handler
-import Yesod.Widget
-import Yesod.Dispatch
 import Yesod.Core
-import Yesod.Content
 import Yesod.Helpers.Static
 import qualified Settings
 import System.Directory
@@ -30,6 +23,7 @@
 import Control.Monad (unless)
 import Control.Monad.Trans.Class (lift)
 import Control.Monad.IO.Class (liftIO)
+import qualified Data.Text as T
 
 -- | The site argument for your application. This can be a good place to
 -- keep settings and values requiring initialization before your application
@@ -66,14 +60,7 @@
 -- for our application to be in scope. However, the handler functions
 -- usually require access to the ~sitearg~Route datatype. Therefore, we
 -- split these actions into two functions and place them in separate files.
-mkYesodData "~sitearg~" [parseRoutes|
-/static StaticR Static getStatic
-
-/favicon.ico FaviconR GET
-/robots.txt RobotsR GET
-
-/ RootR GET
-|]
+mkYesodData "~sitearg~" $(parseRoutesFile "config/routes")
 
 -- Please see the documentation for the Yesod typeclass. There are a number
 -- of settings which can be configured by overriding methods here.
@@ -98,10 +85,10 @@
     -- expiration dates to be set far in the future without worry of
     -- users receiving stale content.
     addStaticContent ext' _ content = do
-        let fn = base64md5 content ++ '.' : ext'
+        let fn = base64md5 content ++ '.' : T.unpack ext'
         let statictmp = Settings.staticdir ++ "/tmp/"
         liftIO $ createDirectoryIfMissing True statictmp
         let fn' = statictmp ++ fn
         exists <- liftIO $ doesFileExist fn'
         unless exists $ liftIO $ L.writeFile fn' content
-        return $ Just $ Right (StaticR $ StaticRoute ["tmp", fn] [], [])
+        return $ Just $ Right (StaticR $ StaticRoute ["tmp", T.pack fn] [], [])
diff --git a/scaffold/production_hs.cg b/scaffold/production_hs.cg
deleted file mode 100644
--- a/scaffold/production_hs.cg
+++ /dev/null
@@ -1,6 +0,0 @@
-import Controller (with~sitearg~)
-import Network.Wai.Handler.Warp (run)
-
-main :: IO ()
-main = with~sitearg~ $ run 3000
-
diff --git a/scaffold/routes.cg b/scaffold/routes.cg
new file mode 100644
--- /dev/null
+++ b/scaffold/routes.cg
@@ -0,0 +1,8 @@
+/static StaticR Static getStatic
+/auth   AuthR   Auth   getAuth
+
+/favicon.ico FaviconR GET
+/robots.txt RobotsR GET
+
+/ RootR GET
+
diff --git a/scaffold/sitearg_hs.cg b/scaffold/sitearg_hs.cg
--- a/scaffold/sitearg_hs.cg
+++ b/scaffold/sitearg_hs.cg
@@ -1,4 +1,5 @@
 {-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies #-}
+{-# LANGUAGE OverloadedStrings #-}
 module ~sitearg~
     ( ~sitearg~ (..)
     , ~sitearg~Route (..)
@@ -32,6 +33,7 @@
 import qualified Data.Text.Lazy
 import qualified Data.Text.Lazy.Encoding
 import Text.Jasmine (minifym)
+import qualified Data.Text as T
 
 -- | The site argument for your application. This can be a good place to
 -- keep settings and values requiring initialization before your application
@@ -69,15 +71,7 @@
 -- for our application to be in scope. However, the handler functions
 -- usually require access to the ~sitearg~Route datatype. Therefore, we
 -- split these actions into two functions and place them in separate files.
-mkYesodData "~sitearg~" [~qq~parseRoutes|
-/static StaticR Static getStatic
-/auth   AuthR   Auth   getAuth
-
-/favicon.ico FaviconR GET
-/robots.txt RobotsR GET
-
-/ RootR GET
-|]
+mkYesodData "~sitearg~" $(parseRoutesFile "config/routes")
 
 -- Please see the documentation for the Yesod typeclass. There are a number
 -- of settings which can be configured by overriding methods here.
@@ -105,7 +99,7 @@
     -- expiration dates to be set far in the future without worry of
     -- users receiving stale content.
     addStaticContent ext' _ content = do
-        let fn = base64md5 content ++ '.' : ext'
+        let fn = base64md5 content ++ '.' : T.unpack ext'
         let content' =
                 if ext' == "js"
                     then case minifym content of
@@ -117,7 +111,7 @@
         let fn' = statictmp ++ fn
         exists <- liftIO $ doesFileExist fn'
         unless exists $ liftIO $ L.writeFile fn' content'
-        return $ Just $ Right (StaticR $ StaticRoute ["tmp", fn] [], [])
+        return $ Just $ Right (StaticR $ StaticRoute ["tmp", T.pack fn] [], [])
 
 -- How to run database actions.
 instance YesodPersist ~sitearg~ where
@@ -140,9 +134,6 @@
             Nothing -> do
                 fmap Just $ insert $ User (credsIdent creds) Nothing
 
-    showAuthId _ = showIntegral
-    readAuthId _ = readIntegral
-
     authPlugins = [ authOpenId
                   , authEmail
                   ]
@@ -150,9 +141,6 @@
 instance YesodAuthEmail ~sitearg~ where
     type AuthEmailId ~sitearg~ = EmailId
 
-    showAuthEmailId _ = showIntegral
-    readAuthEmailId _ = readIntegral
-
     addUnverified email verkey =
         runDB $ insert $ Email email Nothing $ Just verkey
     sendVerifyEmail email _ verurl = liftIO $ renderSendMail Mail
@@ -169,10 +157,10 @@
             , partEncoding = None
             , partFilename = Nothing
             , partContent = Data.Text.Lazy.Encoding.encodeUtf8
-                          $ Data.Text.Lazy.pack $ unlines
+                          $ Data.Text.Lazy.unlines
                 [ "Please confirm your email address by clicking on the link below."
                 , ""
-                , verurl
+                , Data.Text.Lazy.fromChunks [verurl]
                 , ""
                 , "Thank you"
                 ]
diff --git a/scaffold/test_hs.cg b/scaffold/test_hs.cg
--- a/scaffold/test_hs.cg
+++ b/scaffold/test_hs.cg
@@ -1,4 +1,12 @@
+{-# LANGUAGE CPP #-}
+#if PRODUCTION
 import Controller (with~sitearg~)
+import Network.Wai.Handler.Warp (run)
+
+main :: IO ()
+main = with~sitearg~ $ run 3000
+#else
+import Controller (with~sitearg~)
 import System.IO (hPutStrLn, stderr)
 import Network.Wai.Middleware.Debug (debug)
 import Network.Wai.Handler.Warp (run)
@@ -8,4 +16,5 @@
     let port = 3000
     hPutStrLn stderr $ "Application launched, listening on port " ++ show port
     with~sitearg~ $ run port . debug
+#endif
 
diff --git a/yesod.cabal b/yesod.cabal
--- a/yesod.cabal
+++ b/yesod.cabal
@@ -1,5 +1,5 @@
 name:            yesod
-version:         0.7.3
+version:         0.8.0
 license:         BSD3
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
@@ -24,19 +24,19 @@
         cpp-options:     -DGHC7
     else
         build-depends:   base                  >= 4        && < 4.3
-    build-depends:   yesod-core                >= 0.7.0.2  && < 0.8
-                   , yesod-auth                >= 0.3      && < 0.4
-                   , yesod-json                >= 0.0.0.1  && < 0.1
-                   , yesod-persistent          >= 0.0.0.1  && < 0.1
-                   , yesod-static              >= 0.0      && < 0.1
-                   , yesod-form                >= 0.0.0.1  && < 0.1
-                   , monad-peel                >= 0.1      && < 0.2
+    build-depends:   yesod-core                >= 0.8      && < 0.9
+                   , yesod-auth                >= 0.4      && < 0.5
+                   , yesod-json                >= 0.1      && < 0.2
+                   , yesod-persistent          >= 0.1      && < 0.2
+                   , yesod-static              >= 0.1      && < 0.2
+                   , yesod-form                >= 0.1      && < 0.2
+                   , monad-control             >= 0.2      && < 0.3
                    , transformers              >= 0.2      && < 0.3
-                   , wai                       >= 0.3      && < 0.4
-                   , wai-extra                 >= 0.3.2    && < 0.4
-                   , hamlet                    >= 0.7.3    && < 0.8
-                   , warp                      >= 0.3.3    && < 0.4
-                   , mime-mail                 >= 0.1.0.1  && < 0.2
+                   , wai                       >= 0.4      && < 0.5
+                   , wai-extra                 >= 0.4      && < 0.5
+                   , hamlet                    >= 0.8      && < 0.9
+                   , warp                      >= 0.4      && < 0.5
+                   , mime-mail                 >= 0.3      && < 0.4
                    , hjsmin                    >= 0.0.13   && < 0.1
     exposed-modules: Yesod
     ghc-options:     -Wall
@@ -53,10 +53,19 @@
                      , time               >= 1.1.4        && < 1.3
                      , template-haskell
                      , directory          >= 1.0          && < 1.2
-    ghc-options:       -Wall
+                     , Cabal              >= 1.8          && < 1.11
+                     , unix-compat        >= 0.2          && < 0.3
+                     , containers         >= 0.2          && < 0.5
+                     , attoparsec-text    >= 0.8.5        && < 0.9
+                     , http-types         >= 0.6.1        && < 0.7
+                     , blaze-builder      >= 0.2          && < 0.4
+                     , direct-plugins     >= 1.1          && < 1.2
+    ghc-options:       -Wall -threaded
     main-is:           scaffold.hs
     other-modules:     CodeGen
-    extensions:        TemplateHaskell
+                       Scaffold.Build
+                       Scaffold.Devel
+                       Network.Wai.Application.Devel
     if flag(ghc7)
         cpp-options:     -DGHC7
 
