diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,14 +5,19 @@
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
-## [Unreleased]
+## 0.2.0 - 2021-04-21
 
-## 0.1.0
+### Added
 
-Added
+ - `hci secret add`: Add `--json-env` and `--string-env`: more secure alternative for literals
 
- - First version of the `hci` command
+### Changes
 
+ - Remove `-h` and `--help` from tab completion and help text.
+ - User-friendly error when `ci.nix` or similar can not be found.
 
-[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...HEAD
+## 0.1.0
 
+### Added
+
+ - First version of the `hci` command
diff --git a/hercules-ci-cli.cabal b/hercules-ci-cli.cabal
--- a/hercules-ci-cli.cabal
+++ b/hercules-ci-cli.cabal
@@ -1,7 +1,7 @@
 cabal-version: 1.12
 
 name:           hercules-ci-cli
-version:        0.1.0
+version:        0.2.0
 synopsis:       The hci command for working with Hercules CI
 homepage:       https://docs.hercules-ci.com
 bug-reports:    https://github.com/hercules-ci/hercules-ci-agent/issues
diff --git a/src/Hercules/CLI/Effect.hs b/src/Hercules/CLI/Effect.hs
--- a/src/Hercules/CLI/Effect.hs
+++ b/src/Hercules/CLI/Effect.hs
@@ -14,7 +14,7 @@
 import Hercules.CLI.Exception (exitMsg)
 import Hercules.CLI.Git (getAllBranches, getHypotheticalRefs)
 import Hercules.CLI.Nix (attrByPath, callCiNix, ciNixAttributeCompleter, withNix)
-import Hercules.CLI.Options (flatCompleter, mkCommand)
+import Hercules.CLI.Options (flatCompleter, mkCommand, subparser)
 import Hercules.CLI.Project (ProjectPath, getProjectIdAndPath, projectOption)
 import Hercules.CLI.Secret (getSecretsFilePath)
 import Hercules.CNix (Store)
@@ -35,7 +35,7 @@
 
 commandParser, runParser :: Optparse.Parser (IO ())
 commandParser =
-  Optparse.subparser
+  subparser
     ( mkCommand
         "run"
         (Optparse.progDesc "Run an effect")
diff --git a/src/Hercules/CLI/JSON.hs b/src/Hercules/CLI/JSON.hs
--- a/src/Hercules/CLI/JSON.hs
+++ b/src/Hercules/CLI/JSON.hs
@@ -12,9 +12,11 @@
 import qualified Data.HashMap.Strict as HM
 import qualified Data.List.NonEmpty as NEL
 import qualified Data.Text as T
+import Hercules.CLI.Exception (UserException (UserException))
 import qualified Options.Applicative as Optparse
 import Protolude
 import System.AtomicWrite.Writer.ByteString (atomicWriteFile)
+import System.Environment (getEnvironment, lookupEnv)
 
 mergePaths :: [([Text], Value)] -> Either Text Value
 mergePaths = mergeLeafPaths [] . toLeafPaths
@@ -94,48 +96,98 @@
               <> Optparse.completer2 (Optparse.bashCompleter "file")
           )
       )
+  stringEnvs <-
+    many
+      ( Optparse.biOption
+          Optparse.str
+          Optparse.str
+          ( Optparse.long "string-env"
+              <> Optparse.help "Define a string at dot-separated PATH in the secret data, by reading environment variable ENV_NAME"
+              <> Optparse.metavar "PATH"
+              <> Optparse.metavar2 "ENV_NAME"
+              <> Optparse.completer2 envCompleter
+          )
+      )
+  jsonEnvs <-
+    many
+      ( Optparse.biOption
+          Optparse.str
+          Optparse.str
+          ( Optparse.long "json-env"
+              <> Optparse.help "Define a JSON value at dot-separated PATH in the secret data, by reading environment variable ENV_NAME"
+              <> Optparse.metavar "PATH"
+              <> Optparse.metavar2 "ENV_NAME"
+              <> Optparse.completer2 envCompleter
+          )
+      )
   pure do
     fileStrings <- for stringFiles readFileWithKey
     fileJsons <- for jsonFiles readJsonFileWithKey
+    envStrings <- for stringEnvs readEnvWithKey
+    envJsons <- for jsonEnvs readJsonEnvWithKey
     validJsons <-
       for
         jsons
         ( \(key, value) ->
             case eitherDecode $ BL.fromStrict $ encodeUtf8 value of
-              Left e -> throwIO $ FatalError $ "Value for key " <> key <> " is not valid JSON: " <> show e
+              Left e -> throwIO $ UserException $ "Value for key " <> key <> " is not valid JSON: " <> show e
               Right r -> pure (key, r :: Value)
         )
     let items =
-          (fmap String <$> strings) <> (fmap String <$> fileStrings) <> validJsons <> fileJsons
-            & map (first split)
+          map (first split) $
+            (fmap String <$> strings)
+              <> (fmap String <$> envStrings)
+              <> (fmap String <$> fileStrings)
+              <> validJsons
+              <> envJsons
+              <> fileJsons
+
         split "." = []
         split "" = []
         split p = T.split (== '.') p
     when (items & any \(path, _) -> path & any (T.any (== '\"'))) do
       throwIO $ FatalError "Quotes in field names are not allowed, so proper quotation can be implemented in the future. Write the field name in the value of --json or --json-file instead."
     case mergePaths items of
-      Left e -> throwIO $ FatalError $ show e
+      Left e -> throwIO $ UserException $ show e
       Right r -> pure r
 
+readJsonEnvWithKey :: (Text, [Char]) -> IO (Text, Value)
+readJsonEnvWithKey (key, envVar) =
+  lookupEnv envVar >>= \case
+    Nothing -> throwIO $ UserException $ "Environment variable does not exist: " <> show envVar
+    Just x -> case eitherDecode (BL.fromStrict $ encodeUtf8 $ toS x) of
+      Left e -> throwIO $ UserException $ "Environment variable " <> show envVar <> " has invalid JSON: " <> show e
+      Right r -> pure (key, r)
+
+readEnvWithKey :: (Text, [Char]) -> IO (Text, Text)
+readEnvWithKey (key, envVar) =
+  lookupEnv envVar >>= \case
+    Nothing -> throwIO $ UserException $ "Environment variable does not exist: " <> show envVar
+    Just x -> pure (key, toS x)
+
+envCompleter :: Optparse.Completer
+envCompleter = Optparse.listIOCompleter do
+  map fst <$> getEnvironment
+
 readFileWithKey :: (Text, FilePath) -> IO (Text, Text)
 readFileWithKey (key, file) = do
   bs <- BS.readFile file
   case decodeUtf8' bs of
-    Left _e -> throwIO $ FatalError $ "File " <> show file <> " for key " <> key <> " is not valid UTF-8."
+    Left _e -> throwIO $ UserException $ "File " <> show file <> " for key " <> key <> " is not valid UTF-8."
     Right s -> pure (key, s)
 
 readJsonFileWithKey :: FromJSON b => (Text, FilePath) -> IO (Text, b)
 readJsonFileWithKey (key, file) = do
   bs <- BS.readFile file
   case eitherDecode (BL.fromStrict bs) of
-    Left e -> throwIO $ FatalError $ "File " <> show file <> " for key " <> key <> " is not valid JSON: " <> show e
+    Left e -> throwIO $ UserException $ "File " <> show file <> " for key " <> key <> " is not valid JSON: " <> show e
     Right s -> pure (key, s)
 
 readJsonFile :: FromJSON b => FilePath -> IO b
 readJsonFile file = do
   bs <- BS.readFile file
   case eitherDecode (BL.fromStrict bs) of
-    Left e -> throwIO $ FatalError $ "File " <> show file <> " is not valid JSON: " <> show e
+    Left e -> throwIO $ UserException $ "File " <> show file <> " is not valid JSON: " <> show e
     Right s -> pure s
 
 writeJsonFile :: ToJSON a => FilePath -> a -> IO ()
diff --git a/src/Hercules/CLI/Main.hs b/src/Hercules/CLI/Main.hs
--- a/src/Hercules/CLI/Main.hs
+++ b/src/Hercules/CLI/Main.hs
@@ -9,7 +9,7 @@
 import qualified Hercules.CLI.Effect as Effect
 import qualified Hercules.CLI.Exception as Exception
 import qualified Hercules.CLI.Login as Login
-import Hercules.CLI.Options (mkCommand)
+import Hercules.CLI.Options (execParser, helper, mkCommand, subparser)
 import qualified Hercules.CLI.Secret as Secret
 import qualified Hercules.CLI.State as State
 import qualified Options.Applicative as Optparse
@@ -20,7 +20,7 @@
   prettyPrintErrors $
     Exception.handleUserException $
       prettyPrintHttpErrors $ do
-        join $ Optparse.execParser opts
+        join $ execParser opts
 
 prettyPrintErrors :: IO a -> IO a
 prettyPrintErrors = handle \e ->
@@ -33,12 +33,12 @@
 opts :: Optparse.ParserInfo (IO ())
 opts =
   Optparse.info
-    (commands <**> Optparse.helper)
+    (commands <**> helper)
     (Optparse.fullDesc <> Optparse.header "Command line interface to Hercules CI")
 
 commands :: Optparse.Parser (IO ())
 commands =
-  Optparse.subparser
+  subparser
     ( mkCommand
         "login"
         (Optparse.progDesc "Configure token for authentication to hercules-ci.com")
diff --git a/src/Hercules/CLI/Nix.hs b/src/Hercules/CLI/Nix.hs
--- a/src/Hercules/CLI/Nix.hs
+++ b/src/Hercules/CLI/Nix.hs
@@ -6,6 +6,7 @@
 import qualified Data.Map as M
 import qualified Data.Text as T
 import Hercules.Agent.NixFile (findNixFile)
+import Hercules.CLI.Exception (UserException (UserException))
 import Hercules.CLI.Git (getGitRoot, getRef, getRev)
 import Hercules.CLI.Options (scanOption)
 import Hercules.CNix (Store)
@@ -20,7 +21,7 @@
   gitRoot <- getGitRoot
   gitRev <- getRev
   gitRef <- getRef
-  nixFile <- findNixFile gitRoot >>= escalateAs FatalError
+  nixFile <- findNixFile gitRoot >>= escalateAs UserException
   let ref = fromMaybe gitRef passedRef
   args <- evalArgs evalState ["--arg", "src", "{ ref = ''" <> encodeUtf8 ref <> "''; rev = ''" <> encodeUtf8 gitRev <> "''; outPath = ''" <> encodeUtf8 (toS gitRoot) <> "''; }"]
   rootValueOrFunction <- evalFile evalState nixFile
diff --git a/src/Hercules/CLI/Options.hs b/src/Hercules/CLI/Options.hs
--- a/src/Hercules/CLI/Options.hs
+++ b/src/Hercules/CLI/Options.hs
@@ -4,8 +4,20 @@
 
 import qualified Data.Attoparsec.Text as A
 import qualified Data.Text as T
-import Options.Applicative
+import Options.Applicative hiding (helper)
+import qualified Options.Applicative as Optparse
 import Protolude
+
+-- | Custom execParser that provides help text when input is incomplete.
+execParser :: ParserInfo a -> IO a
+execParser = Optparse.customExecParser (Optparse.prefs Optparse.showHelpOnEmpty)
+
+-- | We omit --help from the help text and completion.
+helper :: Parser (a -> a)
+helper = helperWith (short 'h' <> long "help" <> internal)
+
+subparser :: Mod CommandFields a -> Parser a
+subparser = Optparse.subparser
 
 mkCommand ::
   [Char] ->
diff --git a/src/Hercules/CLI/Secret.hs b/src/Hercules/CLI/Secret.hs
--- a/src/Hercules/CLI/Secret.hs
+++ b/src/Hercules/CLI/Secret.hs
@@ -8,7 +8,7 @@
 import qualified Data.Text as T
 import Hercules.CLI.Common (exitMsg, runAuthenticated)
 import Hercules.CLI.JSON as JSON
-import Hercules.CLI.Options (mkCommand)
+import Hercules.CLI.Options (mkCommand, subparser)
 import Hercules.CLI.Project (ProjectPath (projectPathOwner, projectPathSite), getProjectPath, projectOption)
 import qualified Options.Applicative as Optparse
 import Protolude
@@ -17,7 +17,7 @@
 
 commandParser, initLocal, add :: Optparse.Parser (IO ())
 commandParser =
-  Optparse.subparser
+  subparser
     ( mkCommand
         "init-local"
         (Optparse.progDesc "Create a local secrets file in ~/.config/hercules-ci/secrets/<site>/<owner>")
@@ -40,9 +40,9 @@
         liftIO $ writeFile secretsFilePath "{}"
         putErrText $ "hci: Secrets file created. Path: " <> toS secretsFilePath
 add = do
-  projectOptionMaybe <- optional projectOption
-  mkJson <- JSON.options
   secretName <- Optparse.strArgument (Optparse.metavar "SECRET_NAME" <> Optparse.help "Organization/account-wide name for the secret")
+  mkJson <- JSON.options
+  projectOptionMaybe <- optional projectOption
   pure $ runAuthenticated do
     secretData <- liftIO mkJson
     projectPath <- getProjectPath projectOptionMaybe
diff --git a/src/Hercules/CLI/State.hs b/src/Hercules/CLI/State.hs
--- a/src/Hercules/CLI/State.hs
+++ b/src/Hercules/CLI/State.hs
@@ -8,7 +8,7 @@
 import Hercules.API.State
 import Hercules.CLI.Client
 import Hercules.CLI.Common (runAuthenticated)
-import Hercules.CLI.Options (mkCommand)
+import Hercules.CLI.Options (mkCommand, subparser)
 import Hercules.CLI.Project (findProject, projectOption)
 import Options.Applicative (bashCompleter, completer, help, long, metavar, strOption)
 import qualified Options.Applicative as Optparse
@@ -19,7 +19,7 @@
 
 commandParser, getCommandParser, putCommandParser :: Optparse.Parser (IO ())
 commandParser =
-  Optparse.subparser
+  subparser
     ( mkCommand
         "get"
         (Optparse.progDesc "Download a state file")
