diff --git a/Shelly.hs b/Shelly.hs
--- a/Shelly.hs
+++ b/Shelly.hs
@@ -21,7 +21,7 @@
 module Shelly
        (
          -- * Entering ShIO.
-         ShIO, shelly, sub, silently, verbosely, jobs, print_commands
+         ShIO, shelly, sub, silently, verbosely, print_stdout, print_commands
 
          -- * Running external commands.
          , run, run_, cmd, (-|-), lastStderr, setStdin
@@ -36,6 +36,7 @@
 
          -- * Printing
          , echo, echo_n, echo_err, echo_n_err, inspect
+         , tag, trace
 
          -- * Querying filesystem.
          , ls, ls', test_e, test_f, test_d, test_s, which, find
@@ -48,7 +49,7 @@
          , readfile, writefile, appendfile, withTmpDir
 
          -- * Running external commands asynchronously.
-         , background, getBgResult, BgResult
+         , jobs, background, getBgResult, BgResult
 
          -- * exiting the program
          , exit, errorExit, terror
@@ -68,6 +69,7 @@
 
 -- TODO:
 -- shebang runner that puts wrappers in and invokes
+-- perhaps also adds monadloc
 -- convenience for commands that use record arguments
 {-
       let oFiles = ("a.o", "b.o")
@@ -120,7 +122,8 @@
 import System.PosixCompat.Files( getSymbolicLinkStatus, isSymbolicLink )
 import System.Directory ( setPermissions, getPermissions, Permissions(..), getTemporaryDirectory, findExecutable ) 
 
-{- GHC won't default to Text with this
+{- GHC won't default to Text with this, even with extensions!
+ - see: http://hackage.haskell.org/trac/ghc/ticket/6030
 class ShellArgs a where
   toTextArgs :: a -> [Text]
 
@@ -242,11 +245,25 @@
                      , sStdin :: Maybe Text -- ^ stdin for the command to be run
                      , sStderr :: Text
                      , sDirectory :: FilePath
-                     , sVerbose :: Bool
-                     , sPrintCommands :: Bool -- ^ print out command
+                     , sPrintStdout :: Bool   -- ^ print stdout of command that is executed
+                     , sPrintCommands :: Bool -- ^ print command that is executed
                      , sRun :: FilePath -> [Text] -> ShIO (Handle, Handle, Handle, ProcessHandle)
-                     , sEnvironment :: [(String, String)] }
+                     , sEnvironment :: [(String, String)]
+                     , sTrace :: B.Builder
+                     }
 
+-- | same as 'trace', but use it combinator style
+tag :: ShIO a -> Text -> ShIO a
+tag action msg = do
+  result <- action
+  trace msg
+  return result
+
+
+-- | log actions that occur
+trace :: Text -> ShIO ()
+trace msg = modify $ \st -> st { sTrace = sTrace st `mappend` "\n" `mappend` B.fromLazyText msg }
+
 type ShIO a = ReaderT (IORef State) IO a
 
 get :: ShIO State
@@ -274,7 +291,7 @@
   liftIO $ runInteractiveProcess (unpack exe)
     (map LT.unpack args)
     (Just $ unpack $ sDirectory st)
-    (Just $ sEnvironment st)
+    (Just $ sEnvironment st) -- TODO: is PATH buggy?
 
 {-
 -- | use for commands requiring usage of sudo. see 'run_sudo'.
@@ -308,6 +325,7 @@
 -- operations. You will want to handle these issues differently in those cases.
 cd :: FilePath -> ShIO ()
 cd dir = do dir' <- absPath dir
+            trace $ "cd " `mappend` toTextIgnore dir'
             modify $ \st -> st { sDirectory = dir' }
 
 -- | "cd", execute a ShIO action in the new directory and then pop back to the original directory
@@ -350,22 +368,25 @@
 mv :: FilePath -> FilePath -> ShIO ()
 mv a b = do a' <- absPath a
             b' <- absPath b
+            trace $ "mv " `mappend` toTextIgnore a' `mappend` " " `mappend` toTextIgnore b'
             liftIO $ rename a' b'
 
 -- | Get back [Text] instead of [FilePath]
 ls' :: FilePath -> ShIO [Text]
 ls' fp = do
+    trace $ "ls " `mappend` toTextIgnore fp
     efiles <- ls fp
     mapM toTextWarn efiles
 
 -- | List directory contents. Does *not* include \".\" and \"..\", but it does
 -- include (other) hidden files.
 ls :: FilePath -> ShIO [FilePath]
-ls = path >=> liftIO . listDirectory
+ls = path >=> \fp -> (liftIO $ listDirectory fp) `tag` ("ls " `mappend` toTextIgnore fp) 
 
 -- | List directory recursively (like the POSIX utility "find").
 find :: FilePath -> ShIO [FilePath]
-find dir = do bits <- ls dir
+find dir = do trace ("find " `mappend` toTextIgnore dir)
+              bits <- ls dir
               subDir <- forM bits $ \x -> do
                 ex <- test_d $ dir FP.</> x
                 sym <- test_s $ dir FP.</> x
@@ -402,12 +423,16 @@
 
 -- | Create a new directory (fails if the directory exists).
 mkdir :: FilePath -> ShIO ()
-mkdir = absPath >=> liftIO . createDirectory False
+mkdir = absPath >=> \fp -> do
+  trace $ "mkdir " `mappend` toTextIgnore fp
+  liftIO $ createDirectory False fp `catchany` (\e -> throwIO e >> return ())
 
 -- | Create a new directory, including parents (succeeds if the directory
 -- already exists).
 mkdir_p :: FilePath -> ShIO ()
-mkdir_p = absPath >=> liftIO . createTree
+mkdir_p = absPath >=> \fp -> do
+  trace $ "mkdir_p " `mappend` toTextIgnore fp
+  liftIO $ createTree fp
 
 -- | Get a full path to an executable on @PATH@, if exists. FIXME does not
 -- respect setenv'd environment and uses @PATH@ inherited from the process
@@ -468,7 +493,9 @@
 -- | Remove a file. Does not fail if the file already is not there. Does fail
 -- if the file is not a file.
 rm_f :: FilePath -> ShIO ()
-rm_f f = whenM (test_e f) $ absPath f >>= liftIO . removeFile
+rm_f f = whenM (test_e f) $ absPath f >>= \fp -> do
+  trace $ "rm_f " `mappend` toTextIgnore fp
+  liftIO $ removeFile fp
 
 -- | Set an environment variable. The environment is maintained in ShIO
 -- internally, and is passed to any external commands to be executed.
@@ -500,12 +527,15 @@
 
 -- | Create a sub-ShIO in which external command outputs are not echoed. See "sub".
 silently :: ShIO a -> ShIO a
-silently a = sub $ modify (\x -> x { sVerbose = False }) >> a
+silently a = sub $ modify (\x -> x { sPrintStdout = False, sPrintCommands = False }) >> a
 
 -- | Create a sub-ShIO in which external command outputs are echoed. See "sub".
 verbosely :: ShIO a -> ShIO a
-verbosely a = sub $ modify (\x -> x { sVerbose = True }) >> a
+verbosely a = sub $ modify (\x -> x { sPrintStdout = True, sPrintCommands = True }) >> a
 
+print_stdout :: ShIO a -> ShIO a
+print_stdout a = sub $ modify (\x -> x { sPrintStdout = True }) >> a
+
 -- | Create a sub-ShIO which has a 'limit' on the max number of background tasks.
 -- an invocation of jobs is independent of any others, and not tied to the ShIO monad in any way.
 -- This blocks the execution of the program until all 'background' jobs are finished.
@@ -563,7 +593,9 @@
 sub :: ShIO a -> ShIO a
 sub a = do
   state <- get
-  r <- a `catch_sh` (\(e :: SomeException) -> put state >> throw e)
+  r <- a `catchany_sh` (\e -> do
+	put state
+	liftIO $ throwIO e)
   put state
   return r
 
@@ -572,19 +604,23 @@
 -- processwide working directory or environment are not reflected in the
 -- running ShIO.
 shelly :: MonadIO m => ShIO a -> m a
-shelly a = do
+shelly action = do
   env <- liftIO getEnvironment
   dir <- liftIO getWorkingDirectory
   let def  = State { sCode = 0
                    , sStdin = Nothing
                    , sStderr = LT.empty
-                   , sVerbose = True
+                   , sPrintStdout = True
                    , sPrintCommands = False
                    , sRun = runInteractiveProcess'
                    , sEnvironment = env
+                   , sTrace = B.fromText ""
                    , sDirectory = dir }
   stref <- liftIO $ newIORef def
-  liftIO $ runReaderT a stref
+  let caught =
+        action `catchany_sh` \e ->
+          get >>= liftIO . throwIO . ReThrownException e . LT.unpack .  B.toLazyText . sTrace
+  liftIO $ runReaderT caught stref
 
 data RunFailed = RunFailed FilePath [Text] Int Text deriving (Typeable)
 
@@ -592,10 +628,15 @@
   show (RunFailed exe args code errs) =
     "error running " ++
       unpack exe ++ " " ++ show args ++
-      ": exit status " ++ show code ++ ":\n" ++ LT.unpack errs
+      "\nexit status: " ++ show code ++ "\nstderr: " ++ LT.unpack errs
 
 instance Exception RunFailed
 
+data Exception e => ReThrownException e = ReThrownException e String deriving (Typeable)
+instance Exception e => Exception (ReThrownException e)
+instance Exception e => Show (ReThrownException e) where
+  show (ReThrownException ex msg) =
+    "Exception: " ++ show ex ++ "\n" ++ msg
 
 -- | Execute an external command. Takes the command name (no shell allowed,
 -- just a name of something that can be found via @PATH@; FIXME: setenv'd
@@ -654,7 +695,7 @@
 
     errV <- liftIO newEmptyMVar
     outV <- liftIO newEmptyMVar
-    if sVerbose state
+    if sPrintStdout state
       then do
         liftIO_ $ forkIO $ printGetContent errH stderr >>= putMVar errV
         liftIO_ $ forkIO $ printFoldHandleLines start cb outH stdout >>= putMVar outV
@@ -684,7 +725,8 @@
     }
     case ex of
       ExitSuccess   -> return outs
-      ExitFailure n -> throw $ RunFailed exe args n errs
+      ExitFailure n ->
+		liftIO $ throwIO $ RunFailed exe args n errs
 
 -- | The output of last external command. See "run".
 lastStderr :: ShIO Text
@@ -697,7 +739,7 @@
 -- | Pipe operator. set the stdout the first command as the stdin of the second.
 (-|-) :: ShIO Text -> ShIO b -> ShIO b
 one -|- two = do
-  res <- one
+  res <- silently one
   setStdin res
   two
 
@@ -735,9 +777,12 @@
 -- | Copy a file, or a directory recursively.
 cp_r :: FilePath -> FilePath -> ShIO ()
 cp_r from to = do
-    whenM (test_d from) $
-      mkdir to >> ls from >>= mapM_ (\item -> cp_r (from FP.</> item) (to FP.</> item))
-    whenM (test_f from) $ cp from to
+    trace $ "cp_r " `mappend` toTextIgnore from `mappend` " " `mappend` toTextIgnore to
+    from_d <- (test_d from)
+    if not from_d then cp from to else do
+		 unlessM (test_d to) $ mkdir to
+		 ls from >>= mapM_
+		   (\item -> cp_r (from FP.</> filename item) (to FP.</> filename item))
 
 -- | Copy a file. The second path could be a directory, in which case the
 -- original file name is used, in that directory.
@@ -745,8 +790,14 @@
 cp from to = do
   from' <- absPath from
   to' <- absPath to
+  trace $ "cp " `mappend` toTextIgnore from' `mappend` " " `mappend` toTextIgnore to'
   to_dir <- test_d to
-  liftIO $ copyFile from' $ if to_dir then to' FP.</> filename from else to'
+  let to_loc = if to_dir then to' FP.</> filename from else to'
+  liftIO $ copyFile from' to_loc `catchany` (\e -> throwIO $
+	  ReThrownException e (extraMsg to_loc from')
+	)
+  where
+    extraMsg t f = "when copying from: " ++ unpack f ++ " to: " ++ unpack t
 
 class PredicateLike pattern hay where
   match :: pattern -> hay -> Bool
@@ -758,7 +809,7 @@
   match pat = (pat `isInfixOf`)
 
 -- | Like filter, but more conveniently used with String lists, where a
--- substring match (TODO: also provide regexps, and maybe globs) is expressed as
+-- substring match (TODO: also provide globs) is expressed as
 --  @grep \"needle\" [ \"the\", \"stack\", \"of\", \"hay\" ]@. Boolean
 -- predicates just like with "filter" are supported too:
 -- @grep (\"fun\" `isPrefixOf`) [...]@.
@@ -773,6 +824,7 @@
 -- computation. The directory is nuked afterwards.
 withTmpDir :: (FilePath -> ShIO a) -> ShIO a
 withTmpDir act = do
+  trace "withTmpDir"
   dir <- liftIO getTemporaryDirectory
   tid <- liftIO myThreadId
   (pS, handle) <- liftIO $ openTempFile dir ("tmp"++filter isAlphaNum (show tid))
@@ -780,21 +832,27 @@
   liftIO $ hClose handle -- required on windows
   rm_f p
   mkdir p
-  a <- act p`catch_sh` \(e :: SomeException) -> rm_rf p >> throw e
+  a <- act p`catchany_sh` \e -> do
+	rm_rf p >> liftIO (throwIO e)
   rm_rf p
   return a
 
 -- | Write a Lazy Text to a file.
 writefile :: FilePath -> Text -> ShIO ()
-writefile f bits = absPath f >>= \f' -> liftIO (TIO.writeFile (unpack f') bits)
+writefile f bits = absPath f >>= \f' -> do
+  trace $ "writefile " `mappend` toTextIgnore f'
+  liftIO (TIO.writeFile (unpack f') bits)
 
 -- | Append a Lazy Text to a file.
 appendfile :: FilePath -> Text -> ShIO ()
-appendfile f bits = absPath f >>= \f' -> liftIO (TIO.appendFile (unpack f') bits)
+appendfile f bits = absPath f >>= \f' -> do
+  trace $ "appendfile " `mappend` toTextIgnore f'
+  liftIO (TIO.appendFile (unpack f') bits)
 
 -- | (Strictly) read file into a Text.
 -- All other functions use Lazy Text.
 -- So Internally this reads a file as strict text and then converts it to lazy text, which is inefficient
 readfile :: FilePath -> ShIO Text
-readfile =
-  absPath >=> fmap LT.fromStrict . liftIO . STIO.readFile . unpack
+readfile = absPath >=> \fp -> do
+  trace $ "appendfile " `mappend` toTextIgnore fp
+  (fmap LT.fromStrict . liftIO . STIO.readFile . unpack) fp
diff --git a/shelly.cabal b/shelly.cabal
--- a/shelly.cabal
+++ b/shelly.cabal
@@ -1,22 +1,25 @@
 Name:       shelly
 
-Version:     0.7.1
+Version:     0.8.0.1
 Synopsis:    shell-like (systems) programming in Haskell
 
-Description: Shelly is a package provides a single module for convenient
+Description: Shelly provides a single module for convenient
              systems programming in Haskell, similar in spirit to POSIX
-             shells.
+             shells. Shelly:
              .
-               * Shelly is aimed at getting things done rather than
+               * is aimed at convenience and getting things done rather than
                  being a demonstration of elegance.
              .
-               * Shelly maintains its own environment, making it thread-safe.
+               * has detailed and useful error messages
              .
-               * Shelly is modern. It uses Text and system-filepath/system-fileio
+               * maintains its own environment, making it thread-safe.
              .
-               * Shelly is aimed at convenience and newer Haskell users
+               * is modern. It uses Text and system-filepath/system-fileio
              .
-             Shelly is a fork of Shellish that features low memory usage, bug fixes, and modernization
+               * is aimed at convenience and newer Haskell users
+             .
+             Shelly is forked from the Shellish package.
+
 
 Homepage:            https://github.com/yesodweb/Shelly.hs
 License:             BSD3
