diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## [2.9.1](https://github.com/haskell-github-trust/effectful-cli/compare/2.9.0...2.9.1) (2026-11-12)
+
+### Bug Fixes
+
+* command sub-tree ends with empty line ([ef39f09](https://github.com/haskell-github-trust/effectful-cli/commit/ef39f0919ac65c6711afec26f51bf4fc04a78355))
+* fix alignment issues when command names are too long ([791f618](https://github.com/haskell-github-trust/effectful-cli/commit/791f618226d4eccba2062ea2ef8d47b1656b5e98))
+
 ## [2.9.0](https://github.com/haskell-github-trust/effectful-cli/compare/2.8.0...2.9.0) (2026-11-09)
 
 ### Features
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.9.0
+version:        0.2.9.1
 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
@@ -313,13 +313,16 @@
 commandTree = vsep . commands
  where
   commands :: Parser x -> [Doc]
-  commands = concat . mapParser (const entries)
-  entries :: Option x -> [Doc]
+  commands p = (\(n, b) -> fill w (pretty n) <+> b) <$> cs
+   where
+    cs = concat (mapParser (const entries) p)
+    w = maximum (0 : fmap (length . fst) cs)
+  entries :: Option x -> [(String, Doc)]
   entries o = case optMain o of
-    CmdReader _ cs -> uncurry entry <$> cs
+    CmdReader _ cs -> fmap body <$> cs
     _ -> []
-  entry :: String -> ParserInfo x -> Doc
-  entry n i =
-    fill 12 (pretty n) <+> align (fromMaybe mempty (unChunk (infoProgDesc i))) <> case commands (infoParser i) of
+  body :: ParserInfo x -> Doc
+  body i =
+    align (fromMaybe mempty (unChunk (infoProgDesc i))) <> case commands (infoParser i) of
       [] -> mempty
-      sub -> line <> indent 2 (vsep sub)
+      sub -> line <> indent 2 (vsep sub) <> line
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -137,7 +137,7 @@
             subparser (leaf "a")
               <|> subparser (hidden <> command "b" (info (subparser (leaf "c")) (progDesc "b desc")))
        in show (commandTree p)
-            `shouldBe` "a            a desc\nb            b desc\n  c            c desc"
+            `shouldBe` "a a desc\nb b desc\n  c c desc\n"
 
   describe "config keys" $ do
     it "parses TOML literals, falling back to strings" $
