diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,29 @@
+# 1.12.0
+
+Andreas Abel, 2023-02-27
+* Rework `ShellCmd` and `ShellCommand` instances to support `String` arguments:
+  Issue [#143](https://github.com/gregwebs/Shelly.hs/issues/143)
+  fixed by Cunning Defenstrator in
+  PR [#221](https://github.com/gregwebs/Shelly.hs/pull/221).
+
+  This involves a **breaking change** in classes `CmdArg` and `ShellArg`:
+  Method `toTextArg` has been replaced by `toTextArgs`.
+
+  Sample migration:
+  ```haskell
+  #if MIN_VERSION_shelly(1,12,0)
+  -- new
+  import Shelly (toTextArgs)
+  snoc opts arg = opts ++ toTextArgs arg
+  #else
+  -- old
+  import Shelly (toTextArg)
+  snoc opts arg = opts ++ [ toTextArg arg ]
+  #endif
+  ```
+* Dropped GHC 8.0 to get rid of deprecated `LANGUAGE IncoherentInstances`.
+* Builds with GHC 8.2 - 9.6.
+
 # 1.11.0
 
 Andreas Abel, 2023-01-24
diff --git a/shelly.cabal b/shelly.cabal
--- a/shelly.cabal
+++ b/shelly.cabal
@@ -1,33 +1,33 @@
-Name:       shelly
+cabal-version: 2.0
 
-Version:     1.11.0
-Synopsis:    shell-like (systems) programming in Haskell
+Name:          shelly
+Version:       1.12.0
 
-Description: Shelly provides convenient systems programming in Haskell,
-             similar in spirit to POSIX shells. Shelly:
-             .
-               * is aimed at convenience and getting things done rather than
-                 being a demonstration of elegance,
-             .
-               * has detailed and useful error messages,
-             .
-               * maintains its own environment, making it thread-safe.
-             .
-             Shelly is originally forked from the Shellish package.
-             .
-             See the shelly-extra package for additional functionality.
-             .
-             An overview is available in the README: <https://github.com/gregwebs/Shelly.hs/blob/master/README.md>
+Synopsis:      shell-like (systems) programming in Haskell
 
+Description:   Shelly provides convenient systems programming in Haskell,
+               similar in spirit to POSIX shells. Shelly:
+               .
+                 * is aimed at convenience and getting things done rather than
+                   being a demonstration of elegance,
+               .
+                 * has detailed and useful error messages,
+               .
+                 * maintains its own environment, making it thread-safe.
+               .
+               Shelly is originally forked from the Shellish package.
+               .
+               See the shelly-extra package for additional functionality.
+               .
+               An overview is available in the README: <https://github.com/gregwebs/Shelly.hs/blob/master/README.md>
 
-Homepage:            https://github.com/gregwebs/Shelly.hs
-License:             BSD3
-License-file:        LICENSE
-Author:              Greg Weber, Petr Rockai
-Maintainer:          Andreas Abel
-Category:            Development
-Build-type:          Simple
-Cabal-version:       >=1.10
+Homepage:      https://github.com/gregwebs/Shelly.hs
+License:       BSD3
+License-file:  LICENSE
+Author:        Greg Weber, Petr Rockai
+Maintainer:    Andreas Abel
+Category:      Development
+Build-type:    Simple
 
 tested-with:
   GHC == 9.4.4
@@ -38,8 +38,11 @@
   GHC == 8.6.5
   GHC == 8.4.4
   GHC == 8.2.2
-  GHC == 8.0.2
 
+extra-doc-files:
+  README.md
+  ChangeLog.md
+
 -- for the sdist of the test suite
 extra-source-files:
   test/src/*.hs
@@ -50,8 +53,6 @@
   test/data/symlinked_dir/hoge_file
   test/data/hello.sh
   test/testall
-  README.md
-  ChangeLog.md
 
 Library
   Exposed-modules:
@@ -67,30 +68,34 @@
 
   hs-source-dirs: src
 
-  -- Andreas Abel, 2021-11-20:
-  -- Unless other constraints exist, lower bounds are chosen
-  -- such that all versions that build with GHC 8
-  -- (according to matrix.hackage.haskell.org) are included.
+  -- Andreas Abel, 2021-11-20, 2023-02-27:
+  -- Unless other constraints exist, lower bounds (lb) are chosen
+  -- as suggested by `cabal gen-bounds` with GHC 8.2, with some fixes:
+  --   * lb version should exist on hackage
+  --   * need to respect the ghc-shipped version (e.g. containers).
+  -- Upper bounds should be omitted in general,
+  -- unless breakage with major version bumps is expected.
+  -- Upper bounds can always be added after the fact via (bulk) hackage revisions.
   Build-depends:
-      base                      >= 4.9       && < 5
-        -- support GHC >= 8
-    , async
-    , bytestring                >= 0.10.6.0
-    , containers                >= 0.5.7.0
+      base                      >= 4.10      && < 5
+        -- support GHC >= 8.2
+    , async                     >= 2.2.3
+    , bytestring                >= 0.10.8.0
+    , containers                >= 0.5.10.2
     , directory                 >= 1.3.0.0   && < 1.4
-    , enclosed-exceptions
-    , exceptions                >= 0.8.2.1
-    , filepath
-    , lifted-async
+    , enclosed-exceptions       >= 1.0.1
+    , exceptions                >= 0.10.0
+    , filepath                  >= 1.4.1.1
+    , lifted-async              >= 0.10.2
     , lifted-base               >= 0.2.3.2
     , monad-control             >= 0.3.2     && < 1.1
     , mtl                       >= 2.2.2
-    , process                   >= 1.4
-    , text                      >= 1.2.2.0
+    , process                   >= 1.6.1.0
+    , text                      >= 1.2.3.1
     , time                      >= 1.3       && < 1.13
-    , transformers              >= 0.5.0.0
-    , transformers-base
-    , unix-compat               >= 0.4.1.1   && < 0.7
+    , transformers              >= 0.5.2.0
+    , transformers-base         >= 0.4.5
+    , unix-compat               >= 0.4.1.1   && < 0.8
 
   ghc-options:
     -Wall
@@ -124,6 +129,7 @@
     Help
     LiftedSpec
     MoveSpec
+    PipeSpec
     ReadFileSpec
     RmSpec
     RunSpec
diff --git a/src/Shelly.hs b/src/Shelly.hs
--- a/src/Shelly.hs
+++ b/src/Shelly.hs
@@ -1,7 +1,6 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE IncoherentInstances #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeFamilies #-}
@@ -150,34 +149,20 @@
   , ProcessHandle, StdStream(..)
   )
 
-{- GHC won't default to Text with this, even with extensions!
- - see: http://hackage.haskell.org/trac/ghc/ticket/6030
-class CmdArgs a where
+-- | Argument converter for the variadic argument version of 'run' called 'cmd'.
+-- Useful for a type signature of a function that uses 'cmd'.
+class CmdArg a where
+  -- | @since 1.12.0
   toTextArgs :: a -> [Text]
 
-instance CmdArgs Text       where toTextArgs t = [t]
-instance CmdArgs FilePath   where toTextArgs t = [toTextIgnore t]
-instance CmdArgs [Text]     where toTextArgs = id
-instance CmdArgs [FilePath] where toTextArgs = map toTextIgnore
-
-instance CmdArgs (Text, Text) where
-  toTextArgs (t1,t2) = [t1, t2]
-instance CmdArgs (FilePath, FilePath) where
-  toTextArgs (fp1,fp2) = [toTextIgnore fp1, toTextIgnore fp2]
-instance CmdArgs (Text, FilePath) where
-  toTextArgs (t1, fp1) = [t1, toTextIgnore fp1]
-instance CmdArgs (FilePath, Text) where
-  toTextArgs (fp1,t1) = [toTextIgnore fp1, t1]
+instance CmdArg Text   where
+  toTextArgs = (: [])
 
-cmd :: (CmdArgs args) => FilePath -> args -> Sh Text
-cmd fp args = run fp $ toTextArgs args
--}
+instance CmdArg String where
+  toTextArgs = (: []) . T.pack
 
--- | Argument converter for the variadic argument version of 'run' called 'cmd'.
--- Useful for a type signature of a function that uses 'cmd'.
-class CmdArg a where toTextArg :: a -> Text
-instance CmdArg Text     where toTextArg = id
-instance CmdArg String   where toTextArg = T.pack
+instance {-# OVERLAPPABLE #-} CmdArg a => CmdArg [a] where
+  toTextArgs = concatMap toTextArgs
 
 -- | For the variadic function 'cmd'.
 --
@@ -185,23 +170,17 @@
 class ShellCmd t where
     cmdAll :: FilePath -> [Text] -> t
 
-instance ShellCmd (Sh Text) where
-    cmdAll = run
+-- This is the only candidate for `_ <- cmd path x y z` so marking it incoherent will return it and
+-- terminate the search immediately.  This also removes the warning for do { cmd path x y z ; .. }
+-- as GHC will infer `Sh ()` instead of `Sh Text` as before.
+instance {-# INCOHERENT #-} s ~ () => ShellCmd (Sh s) where
+    cmdAll = run_
 
-instance (s ~ Text, Show s) => ShellCmd (Sh s) where
+instance ShellCmd (Sh Text) where
     cmdAll = run
 
--- note that Sh () actually doesn't work for its case (_<- cmd) when there is no type signature
-instance ShellCmd (Sh ()) where
-    cmdAll = run_
-
 instance (CmdArg arg, ShellCmd result) => ShellCmd (arg -> result) where
-    cmdAll fp acc x = cmdAll fp (acc ++ [toTextArg x])
-
-instance (CmdArg arg, ShellCmd result) => ShellCmd ([arg] -> result) where
-    cmdAll fp acc x = cmdAll fp (acc ++ map toTextArg x)
-
-
+    cmdAll fp acc x = cmdAll fp (acc ++ toTextArgs x)
 
 -- | Variadic argument version of 'run'.
 -- Please see the documenation for 'run'.
diff --git a/src/Shelly/Pipe.hs b/src/Shelly/Pipe.hs
--- a/src/Shelly/Pipe.hs
+++ b/src/Shelly/Pipe.hs
@@ -610,27 +610,30 @@
 -- polyvariadic vodoo
 
 -- | Converter for the variadic argument version of 'run' called 'cmd'.
-class ShellArg a where toTextArg :: a -> Text
-instance ShellArg Text     where toTextArg = id
-instance ShellArg FilePath where toTextArg = toTextIgnore
+class ShellArg a where
+  -- | @since 1.12.0
+  toTextArgs :: a -> [Text]
 
+instance ShellArg Text where
+  toTextArgs = (: [])
 
--- Voodoo to create the variadic function 'cmd'
+instance ShellArg String where
+  toTextArgs = (: []) . T.pack
+
+instance {-# OVERLAPPABLE #-} ShellArg a => ShellArg [a] where
+  toTextArgs = Prelude.concatMap toTextArgs
+
 class ShellCommand t where
     cmdAll :: FilePath -> [Text] -> t
 
-instance ShellCommand (Sh Text) where
-    cmdAll fp args = run fp args
-
-instance (s ~ Text, Show s) => ShellCommand (Sh s) where
-    cmdAll fp args = run fp args
+instance {-# INCOHERENT #-} s ~ () => ShellCommand (Sh s) where
+    cmdAll = run_
 
--- note that Sh () actually doesn't work for its case (_<- cmd) when there is no type signature
-instance ShellCommand (Sh ()) where
-    cmdAll fp args = run_ fp args
+instance ShellCommand (Sh Text) where
+    cmdAll = run
 
 instance (ShellArg arg, ShellCommand result) => ShellCommand (arg -> result) where
-    cmdAll fp acc = \x -> cmdAll fp (acc ++ [toTextArg x])
+    cmdAll fp acc x = cmdAll fp (acc ++ toTextArgs x)
 
 -- | see 'S.cmd'
 cmd :: (ShellCommand result) => FilePath -> result
diff --git a/test/examples/color.hs b/test/examples/color.hs
--- a/test/examples/color.hs
+++ b/test/examples/color.hs
@@ -8,5 +8,5 @@
 default (Text)
 
 main = shelly $ do
-  void $ liftIO $ rawSystem "ls" ["--color=auto", "../dist"]
-  run_ "ls" ["--color=auto", "../dist"]
+  void $ liftIO $ rawSystem "ls" ["--color=auto", "test"]
+  run_ "ls" ["--color=auto", "test"]
diff --git a/test/examples/drain.hs b/test/examples/drain.hs
--- a/test/examples/drain.hs
+++ b/test/examples/drain.hs
@@ -8,7 +8,7 @@
 
 main :: IO ()
 main = do
-  let exDir = "./examples"
+  let exDir = "./test/examples"
   void $ shelly $ do
     let strs = ["a", "b"] :: [String]
     let texts = ["a", "b"] :: [Text]
diff --git a/test/examples/run-handles.hs b/test/examples/run-handles.hs
--- a/test/examples/run-handles.hs
+++ b/test/examples/run-handles.hs
@@ -3,6 +3,6 @@
 -- This test runs, but causes this error to show up:
 -- Exception: cannot access an inherited pipe
 main = shelly $
-  runHandles "bash" ["examples/test.sh"] handles doNothing
+  runHandles "bash" ["test/examples/test.sh"] handles doNothing
   where handles = [InHandle Inherit, OutHandle Inherit, ErrorHandle Inherit]
         doNothing _ _ _ = return ""
diff --git a/test/src/FindSpec.hs b/test/src/FindSpec.hs
--- a/test/src/FindSpec.hs
+++ b/test/src/FindSpec.hs
@@ -51,7 +51,7 @@
       res <- shelly $ cd "test/src" >> ls "."
       sort res @?= map toWindowsStyleIfNecessary ["./CopySpec.hs", "./EnvSpec.hs", "./FailureSpec.hs",
                     "./FindSpec.hs", "./Help.hs", "./LiftedSpec.hs", "./LogWithSpec.hs", "./MoveSpec.hs",
-                    "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs", "./SshSpec.hs",
+                    "./PipeSpec.hs", "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs", "./SshSpec.hs",
                     "./TestInit.hs", "./TestMain.hs",
                     "./WhichSpec.hs", "./WriteSpec.hs", "./sleep.hs"]
 
@@ -59,7 +59,7 @@
       res <- shelly $ cd "test" >> ls "src"
       sort res @?=  map toWindowsStyleIfNecessary ["src/CopySpec.hs", "src/EnvSpec.hs", "src/FailureSpec.hs",
                     "src/FindSpec.hs", "src/Help.hs", "src/LiftedSpec.hs", "src/LogWithSpec.hs", "src/MoveSpec.hs",
-                    "src/ReadFileSpec.hs", "src/RmSpec.hs", "src/RunSpec.hs", "src/SshSpec.hs",
+                    "src/PipeSpec.hs", "src/ReadFileSpec.hs", "src/RmSpec.hs", "src/RunSpec.hs", "src/SshSpec.hs",
                     "src/TestInit.hs", "src/TestMain.hs",
                     "src/WhichSpec.hs", "src/WriteSpec.hs", "src/sleep.hs"]
 
@@ -67,7 +67,7 @@
       res <- shelly $ cd "test/src" >> find "."
       sort res @?= map toWindowsStyleIfNecessary ["./CopySpec.hs", "./EnvSpec.hs", "./FailureSpec.hs",
                     "./FindSpec.hs", "./Help.hs", "./LiftedSpec.hs", "./LogWithSpec.hs", "./MoveSpec.hs",
-                    "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs", "./SshSpec.hs",
+                    "./PipeSpec.hs", "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs", "./SshSpec.hs",
                     "./TestInit.hs", "./TestMain.hs",
                     "./WhichSpec.hs", "./WriteSpec.hs", "./sleep.hs"]
 
@@ -86,13 +86,13 @@
       if isWindows
         then sort res @?= ["test/src\\CopySpec.hs", "test/src\\EnvSpec.hs", "test/src\\FailureSpec.hs",
                     "test/src\\FindSpec.hs", "test/src\\Help.hs", "test/src\\LiftedSpec.hs",
-                    "test/src\\LogWithSpec.hs", "test/src\\MoveSpec.hs", "test/src\\ReadFileSpec.hs",
+                    "test/src\\LogWithSpec.hs", "test/src\\MoveSpec.hs", "test/src\\PipeSpec.hs", "test/src\\ReadFileSpec.hs",
                     "test/src\\RmSpec.hs", "test/src\\RunSpec.hs", "test/src\\SshSpec.hs",
                     "test/src\\TestInit.hs", "test/src\\TestMain.hs", "test/src\\WhichSpec.hs", "test/src\\WriteSpec.hs",
                     "test/src\\sleep.hs"]
         else sort res @?= ["test/src/CopySpec.hs", "test/src/EnvSpec.hs", "test/src/FailureSpec.hs",
                     "test/src/FindSpec.hs", "test/src/Help.hs", "test/src/LiftedSpec.hs",
-                    "test/src/LogWithSpec.hs", "test/src/MoveSpec.hs", "test/src/ReadFileSpec.hs",
+                    "test/src/LogWithSpec.hs", "test/src/MoveSpec.hs", "test/src/PipeSpec.hs", "test/src/ReadFileSpec.hs",
                     "test/src/RmSpec.hs", "test/src/RunSpec.hs", "test/src/SshSpec.hs",
                     "test/src/TestInit.hs", "test/src/TestMain.hs", "test/src/WhichSpec.hs", "test/src/WriteSpec.hs",
                     "test/src/sleep.hs"]
@@ -101,8 +101,8 @@
       res <- shelly $ relPath "test/src" >>= find >>= mapM (relativeTo "test/src")
       sort res @?= ["CopySpec.hs", "EnvSpec.hs", "FailureSpec.hs", "FindSpec.hs",
                     "Help.hs", "LiftedSpec.hs", "LogWithSpec.hs", "MoveSpec.hs",
-                    "ReadFileSpec.hs", "RmSpec.hs", "RunSpec.hs", "SshSpec.hs",
-                    "TestInit.hs", "TestMain.hs",
+                    "PipeSpec.hs", "ReadFileSpec.hs", "RmSpec.hs", "RunSpec.hs",
+                    "SshSpec.hs", "TestInit.hs", "TestMain.hs",
                     "WhichSpec.hs", "WriteSpec.hs", "sleep.hs"]
 
     unless isWindows $ before createSymlinkForTest $ do
diff --git a/test/src/PipeSpec.hs b/test/src/PipeSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/src/PipeSpec.hs
@@ -0,0 +1,97 @@
+module PipeSpec ( pipeSpec ) where
+
+import TestInit
+
+import Data.Text (Text)
+import qualified Shelly.Pipe as P
+
+pipeSpec :: Spec
+pipeSpec = do
+  describe "P.cmd" $ do
+    let shouldBeTxt res t = res @?= [t :: Text]
+
+    it "with Text" $ do
+      res <- P.shelly $ P.cmd "echo" ("wibble" :: Text)
+      res `shouldBeTxt` "wibble\n"
+
+    it "with String" $ do
+      res <- P.shelly $ P.cmd "echo" "wibble"
+      res `shouldBeTxt` "wibble\n"
+
+    it "with [Text]" $ do
+      res <- P.shelly $ P.cmd "echo" (["wibble"] :: [Text])
+      res `shouldBeTxt` "wibble\n"
+
+    it "with [String]" $ do
+      res <- P.shelly $ P.cmd "echo" ["wibble"]
+      res `shouldBeTxt` "wibble\n"
+
+    -- Check all two argument permutations (with replacement) of { Text, String, [Text], [String] }.
+    it "with Text and Text" $ do
+      res <- P.shelly $ P.cmd "echo" ("wibble" :: Text) ("wobble" :: Text)
+      res `shouldBeTxt` "wibble wobble\n"
+
+    it "with Text and String" $ do
+      res <- P.shelly $ P.cmd "echo" ("wibble" :: Text) "wobble"
+      res `shouldBeTxt` "wibble wobble\n"
+
+    it "with Text and [Text]" $ do
+      res <- P.shelly $ P.cmd "echo" ("wibble" :: Text) (["wobble", "wurble"] :: [Text])
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with Text and [String]" $ do
+      res <- P.shelly $ P.cmd "echo" ("wibble" :: Text) ["wobble", "wurble"]
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with String and Text" $ do
+      res <- P.shelly $ P.cmd "echo" "wibble" ("wobble" :: Text)
+      res `shouldBeTxt` "wibble wobble\n"
+
+    it "with String and String" $ do
+      res <- P.shelly $ P.cmd "echo" "wibble" "wobble"
+      res `shouldBeTxt` "wibble wobble\n"
+
+    it "with String and [Text]" $ do
+      res <- P.shelly $ P.cmd "echo" "wibble" (["wobble", "wurble"] :: [Text])
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [Text] and Text" $ do
+      res <- P.shelly $ P.cmd "echo" (["wibble", "wobble"] :: [Text]) ("wurble" :: Text)
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [Text] and String" $ do
+      res <- P.shelly $ P.cmd "echo" (["wibble", "wobble"] :: [Text]) "wurble"
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [Text] and [Text]" $ do
+      res <- P.shelly $ P.cmd "echo" (["wibble", "wobble"] :: [Text]) (["wurble"] :: [Text])
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [Text] and [String]" $ do
+      res <- P.shelly $ P.cmd "echo" (["wibble", "wobble"] :: [Text]) ["wurble"]
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [String] and Text " $ do
+      res <- P.shelly $ P.cmd "echo" ["wibble", "wobble"] ("wurble" :: Text)
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [String] and String " $ do
+      res <- P.shelly $ P.cmd "echo" ["wibble", "wobble"] "wurble"
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [String] and [Text] " $ do
+      res <- P.shelly $ P.cmd "echo" ["wibble", "wobble"] (["wurble"] :: [Text])
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [String] and [String] " $ do
+      res <- P.shelly $ P.cmd "echo" ["wibble", "wobble"] ["wurble"]
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    -- Check unit cases
+    it "returns [()]" $ do
+      res <- P.shelly $ P.cmd "echo" "wibble" "wobble"
+      res @?= [()]
+
+    it "works with underscore" $ do
+      _ <- P.shelly $ P.cmd "echo" "wibble" "wobble"
+      True `shouldBe` True
diff --git a/test/src/RunSpec.hs b/test/src/RunSpec.hs
--- a/test/src/RunSpec.hs
+++ b/test/src/RunSpec.hs
@@ -3,6 +3,7 @@
 import TestInit
 
 import qualified Data.Text as T
+import Data.Text (Text)
 import System.IO
 
 runSpec :: Spec
@@ -34,6 +35,100 @@
           run_ "chmod" ["+x", "test/data/hello.sh"]
           run "./test/data/hello.sh" []
         res @?= "Hello!\n"
+
+  describe "cmd" $ do
+    let shouldBeTxt res t = res @?= (t :: Text)
+
+    it "with Text" $ do
+      res <- shelly $ cmd "echo" ("wibble" :: Text)
+      res `shouldBeTxt` "wibble\n"
+
+    it "with String" $ do
+      res <- shelly $ cmd "echo" "wibble"
+      res `shouldBeTxt` "wibble\n"
+
+    it "with [Text]" $ do
+      res <- shelly $ cmd "echo" (["wibble"] :: [Text])
+      res `shouldBeTxt` "wibble\n"
+
+    it "with [String]" $ do
+      res <- shelly $ cmd "echo" ["wibble"]
+      res `shouldBeTxt` "wibble\n"
+
+    -- Check all two argument permutations (with replacement) of { Text, String, [Text], [String] }.
+    it "with Text and Text" $ do
+      res <- shelly $ cmd "echo" ("wibble" :: Text) ("wobble" :: Text)
+      res `shouldBeTxt` "wibble wobble\n"
+
+    it "with Text and String" $ do
+      res <- shelly $ cmd "echo" ("wibble" :: Text) "wobble"
+      res `shouldBeTxt` "wibble wobble\n"
+
+    it "with Text and [Text]" $ do
+      res <- shelly $ cmd "echo" ("wibble" :: Text) (["wobble", "wurble"] :: [Text])
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with Text and [String]" $ do
+      res <- shelly $ cmd "echo" ("wibble" :: Text) ["wobble", "wurble"]
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with String and Text" $ do
+      res <- shelly $ cmd "echo" "wibble" ("wobble" :: Text)
+      res `shouldBeTxt` "wibble wobble\n"
+
+    it "with String and String" $ do
+      res <- shelly $ cmd "echo" "wibble" "wobble"
+      res `shouldBeTxt` "wibble wobble\n"
+
+    it "with String and [Text]" $ do
+      res <- shelly $ cmd "echo" "wibble" (["wobble", "wurble"] :: [Text])
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [Text] and Text" $ do
+      res <- shelly $ cmd "echo" (["wibble", "wobble"] :: [Text]) ("wurble" :: Text)
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [Text] and String" $ do
+      res <- shelly $ cmd "echo" (["wibble", "wobble"] :: [Text]) "wurble"
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [Text] and [Text]" $ do
+      res <- shelly $ cmd "echo" (["wibble", "wobble"] :: [Text]) (["wurble"] :: [Text])
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [Text] and [String]" $ do
+      res <- shelly $ cmd "echo" (["wibble", "wobble"] :: [Text]) ["wurble"]
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [String] and Text " $ do
+      res <- shelly $ cmd "echo" ["wibble", "wobble"] ("wurble" :: Text)
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [String] and String " $ do
+      res <- shelly $ cmd "echo" ["wibble", "wobble"] "wurble"
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [String] and [Text] " $ do
+      res <- shelly $ cmd "echo" ["wibble", "wobble"] (["wurble"] :: [Text])
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    it "with [String] and [String] " $ do
+      res <- shelly $ cmd "echo" ["wibble", "wobble"] ["wurble"]
+      res `shouldBeTxt` "wibble wobble wurble\n"
+
+    -- Check unit cases
+    it "returns Unit" $ do
+      res <- shelly $ cmd "echo" "wibble" "wobble"
+      res @?= ()
+
+    it "works with underscore" $ do
+      _ <- shelly $ cmd "echo" "wibble" "wobble"
+      True `shouldBe` True
+
+    -- This should now compile without a warning since ghc should infer Sh () instead of Sh Text.
+    it "defaults to Unit" $ do
+      shelly $ cmd "echo" "wibble" "wobble"
+      True `shouldBe` True
 
   -- Bash-related commands
   describe "bash" $ do
diff --git a/test/src/TestMain.hs b/test/src/TestMain.hs
--- a/test/src/TestMain.hs
+++ b/test/src/TestMain.hs
@@ -13,6 +13,7 @@
 import LiftedSpec
 import RunSpec
 import SshSpec
+import PipeSpec
 
 import Test.Hspec
 
@@ -30,3 +31,4 @@
     liftedSpec
     runSpec
     sshSpec
+    pipeSpec
