diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,9 @@
+## [1.0.2](https://github.com/haskell-github-trust/effectful-cli/compare/1.0.1...1.0.2) (2026-10-26)
+
+## [1.0.1](https://github.com/haskell-github-trust/effectful-cli/compare/1.0.0...1.0.1) (2026-10-25)
+
+## 1.0.0 (2026-10-20)
+
+### Features
+
+* initial commit ([a158e16](https://github.com/haskell-github-trust/effectful-cli/commit/a158e160d175b2ca552b78c426c9c7f2263208e4))
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -4,8 +4,6 @@
 effect constraints, concurrency, prompts, sleep/time, optparse-applicative helpers and a
 [monad-logger](https://hackage.haskell.org/package/monad-logger) bridge (stderr or OpenTelemetry).
 
-Port of the private `polysemy-cli` package to effectful.
-
 ## Build
 
 ```sh
diff --git a/effectful-cli.cabal b/effectful-cli.cabal
--- a/effectful-cli.cabal
+++ b/effectful-cli.cabal
@@ -1,11 +1,11 @@
-cabal-version: 1.12
+cabal-version: 1.18
 
 -- This file has been generated from package.yaml by hpack version 0.39.6.
 --
 -- see: https://github.com/sol/hpack
 
 name:           effectful-cli
-version:        0.1.0.1
+version:        0.1.0.2
 synopsis:       CLI helpers for the effectful ecosystem
 description:    Please see the README at https://github.com/haskell-github-trust/effectful-cli
 category:       CLI
@@ -17,6 +17,8 @@
 build-type:     Simple
 extra-source-files:
     README.md
+extra-doc-files:
+    CHANGELOG.md
 
 library
   exposed-modules:
diff --git a/src/Data/CLI/Args.hs b/src/Data/CLI/Args.hs
--- a/src/Data/CLI/Args.hs
+++ b/src/Data/CLI/Args.hs
@@ -1,3 +1,4 @@
+-- | Global command-line options shared by all commands.
 module Data.CLI.Args (
   CLIArgs (..),
   getLogLevel,
@@ -11,21 +12,36 @@
 import Data.Maybe (fromMaybe)
 import Options.Applicative
 
--- | @since 0.1.0.0
+-- | Parsed global options, with the command of type @c@.
+--
+-- @since 0.1.0.0
 data CLIArgs c = CLIArgs
   { _logLevel :: Maybe LogLevel
+  -- ^ @-l@/@--log-level@
   , _debug :: Bool
+  -- ^ @-d@/@--debug@
   , _autoAccept :: Bool
+  -- ^ @-y@/@--yes@
   , _autoDecline :: Bool
+  -- ^ @-n@/@--no@
   , _config :: Maybe FilePath
+  -- ^ @-c@/@--config@
   , _command :: Either () c
+  -- ^ 'Left' for @-v@/@--version@, otherwise the parsed command
   }
 
 logLevels :: [(String, LogLevel)]
 logLevels =
-  [("debug", LevelDebug), ("info", LevelInfo), ("warn", LevelWarn), ("error", LevelError)]
+  [ ("debug", LevelDebug)
+  , ("info", LevelInfo)
+  , ("warn", LevelWarn)
+  , ("error", LevelError)
+  ]
 
--- | Effective log level: @--log-level@ if given, else debug with @--debug@, else info.
+-- | Effective log level:
+--   * @--log-level@ if given
+--   * debug with @--debug@
+--   * info otherwise
 --
 -- @since 0.1.0.0
 getLogLevel :: CLIArgs c -> LogLevel
@@ -40,7 +56,11 @@
 -- | Print the version if @--version@ was passed, otherwise run the parsed command.
 --
 -- @since 0.1.0.0
-versionOrCommand :: String -> CLIArgs c -> (c -> IO ()) -> IO ()
+versionOrCommand
+  :: String
+  -> CLIArgs c
+  -> (c -> IO ())
+  -> IO ()
 versionOrCommand v a run = either (const (showVersion v)) run (_command a)
 
 -- | Parser for the common CLI options, wrapping the given command parser.
diff --git a/src/Data/CLI/Class.hs b/src/Data/CLI/Class.hs
--- a/src/Data/CLI/Class.hs
+++ b/src/Data/CLI/Class.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE FunctionalDependencies #-}
 
+-- | Classes implemented by application configs and commands.
 module Data.CLI.Class (
   CLIConfig (..),
   Command (..),
@@ -9,11 +10,28 @@
 import Effectful.CLI (CLIEffects)
 import Options.Applicative (Parser)
 
+-- | Application config exposing the prompt auto-answer flags.
+--
+-- @since 0.1.0.0
 class CLIConfig a where
-  autoAccept, autoDecline :: a -> Bool
+  -- | Answer yes to all confirmation prompts.
+  autoAccept :: a -> Bool
 
+  -- | Answer no to all confirmation prompts; takes precedence over 'autoAccept'.
+  autoDecline :: a -> Bool
+
+-- | A subcommand @a@ running with config @c@ and application error @e@.
+--
+-- @since 0.1.0.0
 class Command c e a | a -> c e where
+  -- | Subcommand name on the command line.
   cmdname :: a -> String
+
+  -- | One-line description shown in @--help@.
   doc :: a -> String
+
+  -- | Parser for the subcommand's arguments.
   parser :: a -> Parser a
+
+  -- | Execute the subcommand.
   runner :: CLIEffects c e es => a -> Eff es ()
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
@@ -1,3 +1,4 @@
+-- | Helpers for building optparse-applicative parsers and subcommands.
 module Data.CLI.Impl (
   unboundEnumError,
   fromParsec,
@@ -88,7 +89,7 @@
   :: Bounded a => Command c e a => Command c e b => Enum a => (a -> b) -> Mod CommandFields b
 mkCommands f = foldMap (mkCommand . f) (sortOn cmdname [minBound ..])
 
--- | Top-level 'ParserInfo' with @--help@ and a header showing name, description and version.
+-- | Top-level t'ParserInfo' with @--help@ and a header showing name, description and version.
 --
 -- @since 0.1.0.0
 usage :: String -> String -> String -> Parser a -> ParserInfo a
diff --git a/src/Data/Error/CLI.hs b/src/Data/Error/CLI.hs
--- a/src/Data/Error/CLI.hs
+++ b/src/Data/Error/CLI.hs
@@ -1,14 +1,21 @@
+-- | Errors common to CLI applications.
 module Data.Error.CLI (
   CLIError (..),
   displayCLIError,
 ) where
 
--- | @since 0.1.0.0
+-- | Errors raised by CLI helpers.
+--
+-- @since 0.1.0.0
 data CLIError
-  = InputError String
-  | NotFoundError String
-  | NotImplemented String
-  | UserCanceled (Maybe String)
+  = -- | Invalid user input.
+    InputError String
+  | -- | Requested item does not exist.
+    NotFoundError String
+  | -- | Feature not implemented yet.
+    NotImplemented String
+  | -- | User declined a confirmation, with an optional reason.
+    UserCanceled (Maybe String)
   deriving (Eq, Show)
 
 -- | Render a 'CLIError' as a human-readable message.
diff --git a/src/Effectful/CLI.hs b/src/Effectful/CLI.hs
--- a/src/Effectful/CLI.hs
+++ b/src/Effectful/CLI.hs
@@ -1,3 +1,4 @@
+-- | Effect stack available to command runners.
 module Effectful.CLI (
   CLIEffects,
 ) where
@@ -9,7 +10,9 @@
 import Effectful.Logger (Logger)
 import Effectful.Reader.Static (Reader)
 
--- | @since 0.1.0.0
+-- | Effects available to a command runner, with config @c@ and application error @e@.
+--
+-- @since 0.1.0.0
 type CLIEffects c e es =
   ( IOE :> es
   , Reader c :> es
diff --git a/src/Effectful/CLI/Conc.hs b/src/Effectful/CLI/Conc.hs
--- a/src/Effectful/CLI/Conc.hs
+++ b/src/Effectful/CLI/Conc.hs
@@ -1,3 +1,4 @@
+-- | Concurrency helpers built on "Effectful.Concurrent.Async".
 module Effectful.CLI.Conc (
   concurrently,
   concurrently_,
@@ -29,37 +30,63 @@
 -- | Run all actions concurrently and collect their results.
 --
 -- @since 0.1.0.0
-runConcurrently :: Concurrent :> es => Traversable t => t (Eff es a) -> Eff es (t a)
+runConcurrently
+  :: Concurrent :> es
+  => Traversable t
+  => t (Eff es a)
+  -> Eff es (t a)
 runConcurrently = mapConcurrently id
 
 -- | Run all actions concurrently, discarding results.
 --
 -- @since 0.1.0.0
-runConcurrently_ :: Concurrent :> es => Foldable t => t (Eff es a) -> Eff es ()
+runConcurrently_
+  :: Concurrent :> es
+  => Foldable t
+  => t (Eff es a)
+  -> Eff es ()
 runConcurrently_ = mapConcurrently_ id
 
 -- | Map over a list with at most @n@ concurrent actions.
 --
 -- @since 0.1.0.0
-batchConcurrently :: Concurrent :> es => Int -> (a -> Eff es b) -> [a] -> Eff es [b]
+batchConcurrently
+  :: Concurrent :> es
+  => Int
+  -> (a -> Eff es b)
+  -> [a]
+  -> Eff es [b]
 batchConcurrently = pooledMapConcurrentlyN
 
 -- | Like 'batchConcurrently', discarding results.
 --
 -- @since 0.1.0.0
-batchConcurrently_ :: Concurrent :> es => Int -> (a -> Eff es b) -> [a] -> Eff es ()
+batchConcurrently_
+  :: Concurrent :> es
+  => Int
+  -> (a -> Eff es b)
+  -> [a]
+  -> Eff es ()
 batchConcurrently_ = pooledMapConcurrentlyN_
 
 -- | Like 'batchConcurrently', also passing each element's 1-based index.
 --
 -- @since 0.1.0.0
 batchConcurrentlyWithIndex
-  :: Concurrent :> es => Int -> (Int -> a -> Eff es b) -> [a] -> Eff es [b]
+  :: Concurrent :> es
+  => Int
+  -> (Int -> a -> Eff es b)
+  -> [a]
+  -> Eff es [b]
 batchConcurrentlyWithIndex n f = batchConcurrently n (uncurry f) . zip [1 ..]
 
 -- | Like 'batchConcurrentlyWithIndex', discarding results.
 --
 -- @since 0.1.0.0
 batchConcurrentlyWithIndex_
-  :: Concurrent :> es => Int -> (Int -> a -> Eff es b) -> [a] -> Eff es ()
+  :: Concurrent :> es
+  => Int
+  -> (Int -> a -> Eff es b)
+  -> [a]
+  -> Eff es ()
 batchConcurrentlyWithIndex_ n f = batchConcurrently_ n (uncurry f) . zip [1 ..]
diff --git a/src/Effectful/CLI/IO.hs b/src/Effectful/CLI/IO.hs
--- a/src/Effectful/CLI/IO.hs
+++ b/src/Effectful/CLI/IO.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE AllowAmbiguousTypes #-}
 
+-- | Terminal output, prompts and confirmations.
 module Effectful.CLI.IO (
   printOutput,
   printOutputLn,
diff --git a/src/Effectful/CLI/Sleep.hs b/src/Effectful/CLI/Sleep.hs
--- a/src/Effectful/CLI/Sleep.hs
+++ b/src/Effectful/CLI/Sleep.hs
@@ -1,3 +1,4 @@
+-- | Logged sleeping helpers.
 module Effectful.CLI.Sleep (
   msleep,
   sleep,
diff --git a/src/Effectful/CLI/Time.hs b/src/Effectful/CLI/Time.hs
--- a/src/Effectful/CLI/Time.hs
+++ b/src/Effectful/CLI/Time.hs
@@ -1,3 +1,4 @@
+-- | Current time helpers, in 'IO' and 'Eff'.
 module Effectful.CLI.Time (
   getLocalTimeIO,
   getBeginningOfDayIO,
diff --git a/src/Effectful/Logger.hs b/src/Effectful/Logger.hs
--- a/src/Effectful/Logger.hs
+++ b/src/Effectful/Logger.hs
@@ -34,29 +34,33 @@
 import OpenTelemetry.Log.Core qualified as OTel
 import System.IO (stderr)
 
--- | @since 0.1.0.0
+-- | monad-logger style log callback.
+--
+-- @since 0.1.0.0
 type LogFunc = Loc -> LogSource -> LogLevel -> LogStr -> IO ()
 
--- | @since 0.1.0.0
+-- | Static effect providing a 'LogFunc'.
+--
+-- @since 0.1.0.0
 data Logger :: Effect
 
 type instance DispatchOf Logger = Static WithSideEffects
 
 newtype instance StaticRep Logger = Logger LogFunc
 
--- | Run the 'Logger' effect with the given log callback.
+-- | Run the t'Logger' effect with the given log callback.
 --
 -- @since 0.1.0.0
 runLogger :: IOE :> es => LogFunc -> Eff (Logger : es) a -> Eff es a
 runLogger = evalStaticRep . Logger
 
--- | Run the 'Logger' effect, writing to stderr.
+-- | Run the t'Logger' effect, writing to stderr.
 --
 -- @since 0.1.0.0
 runStderrLogger :: IOE :> es => Eff (Logger : es) a -> Eff es a
 runStderrLogger = runLogger (defaultOutput stderr)
 
--- | Run the 'Logger' effect, emitting OpenTelemetry log records.
+-- | Run the t'Logger' effect, emitting OpenTelemetry log records.
 --
 -- @since 0.1.0.0
 runOTelLogger :: IOE :> es => OTel.Logger -> Eff (Logger : es) a -> Eff es a
