diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## [2.4.0](https://github.com/haskell-github-trust/effectful-cli/compare/2.3.0...2.4.0) (2026-10-31)
+
+### Features
+
+* make default command print help ([4968cc4](https://github.com/haskell-github-trust/effectful-cli/commit/4968cc4866b941b12c26fda3ad98eef30f9d8ae3))
+
 ## [2.3.0](https://github.com/haskell-github-trust/effectful-cli/compare/2.2.0...2.3.0) (2026-10-28)
 
 ### Features
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -9,3 +9,13 @@
 ```sh
 stack build --test
 ```
+
+## Release
+
+Requires [just](https://just.systems) and a Hackage token in `HACKAGE_KEY`.
+
+```sh
+just docs       # build Haddock tarball
+just candidate  # upload package + docs as Hackage candidate
+just release    # publish package + docs to Hackage
+```
diff --git a/effectful-cli.cabal b/effectful-cli.cabal
--- a/effectful-cli.cabal
+++ b/effectful-cli.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           effectful-cli
-version:        0.2.3.0
+version:        0.2.4.0
 synopsis:       CLI helpers for the effectful ecosystem
 description:    Please see the README at https://github.com/haskell-github-trust/effectful-cli
 category:       CLI
diff --git a/src/Data/CLI/Impl.hs b/src/Data/CLI/Impl.hs
--- a/src/Data/CLI/Impl.hs
+++ b/src/Data/CLI/Impl.hs
@@ -90,13 +90,14 @@
   ttys <- and <$> traverse hIsTerminalDevice [stdout, stderr]
   pure (ttys && not noColor)
 
--- | Like 'execParser', stripping help styles when 'colorEnabled' is false.
+-- | Like 'execParser', printing help when run without arguments and stripping help styles when
+-- 'colorEnabled' is false.
 --
 -- @since 0.2.2.0
 execCLIParser :: ParserInfo a -> IO a
 execCLIParser pinfo = do
   c <- colorEnabled
-  res <- execParserPure defaultPrefs pinfo <$> getArgs
+  res <- execParserPure (prefs showHelpOnEmpty) pinfo <$> getArgs
   handleParseResult (if c then res else strip res)
  where
   strip (Failure (ParserFailure f)) = Failure (ParserFailure (\p -> let (h, e, w) = f p in (plain h, e, w)))
diff --git a/src/Effectful/Logger.hs b/src/Effectful/Logger.hs
--- a/src/Effectful/Logger.hs
+++ b/src/Effectful/Logger.hs
@@ -3,8 +3,8 @@
 {-# LANGUAGE UndecidableInstances #-}
 {-# OPTIONS_GHC -Wno-orphans #-}
 
--- | monad-logger bridge: a static effect holding a log callback, plus a
--- 'MonadLogger' instance for 'Eff' so the usual @logInfoN@ etc. work.
+-- | [monad-logger](https://hackage.haskell.org/package/monad-logger) bridge: a static
+-- effect holding a log callback, plus a 'MonadLogger' instance for 'Eff' so the usual @logInfoN@ etc. work.
 module Effectful.Logger (
   Logger,
   LogFunc,
@@ -34,7 +34,7 @@
 import OpenTelemetry.Log.Core qualified as OTel
 import System.IO (stderr)
 
--- | monad-logger style log callback.
+-- | [monad-logger](https://hackage.haskell.org/package/monad-logger) style log callback.
 --
 -- @since 0.1.0.0
 type LogFunc = Loc -> LogSource -> LogLevel -> LogStr -> IO ()
