diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## 0.2.3 (2026-02-11)
+- hide output of empty lists and object by default (behind --show-empty)
+- add 'contributors' command
+- use pagureGroupRepos from pagure-0.2.1
+
 ## 0.2.2 (2024-05-06)
 - new add group and groupinfo commands
 - help: order commands with better grouping
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -30,10 +30,10 @@
 
 Count projects of a user:
 
-`$ pagure user --server pagure.io --count mattdm`
+`$ pagure user --server pagure.io --count jspaleta`
 
 ```
-120
+11
 ```
 
 ### List groups:
@@ -47,7 +47,13 @@
 `$ pagure group budgie-sig`
 
 ```
+rpms/bluejay
+rpms/budgie-desktop-services
+rpms/budgie-display-configurator
+rpms/gtklock
+rpms/gtk-session-lock
 rpms/libxfce4windowing
+rpms/pocillo-gtk-theme
 rpms/sassc
 rpms/wlrctl
 ```
@@ -55,7 +61,7 @@
 `$ pagure group -c rust-sig`
 
 ```
-3093
+4266
 ```
 
 ## Help
@@ -63,7 +69,7 @@
 `$ pagure --version`
 
 ```
-0.2.2
+0.2.3
 ```
 
 `$ pagure --help`
@@ -86,6 +92,7 @@
   project                  show project details
   branches                 list project branches
   git-url                  show project repo's git urls
+  contributors             show project repo's contributors
   issues                   list project issues
   issue                    show project issue
   users                    list users
@@ -98,7 +105,7 @@
 
 
 ## Installation
-pagure-cli is packaged in Fedora and EPEL 9: <https://src.fedoraproject.org/rpms/pagure-cli>
+pagure-cli is packaged in Fedora and EPEL: <https://src.fedoraproject.org/rpms/pagure-cli>
 
 ## Build from source
 To build the latest release you will need cabal-install and ghc, or stack:
diff --git a/pagure-cli.cabal b/pagure-cli.cabal
--- a/pagure-cli.cabal
+++ b/pagure-cli.cabal
@@ -1,6 +1,6 @@
 cabal-version:       1.18
 name:                pagure-cli
-version:             0.2.2
+version:             0.2.3
 synopsis:            A Pagure gitforge query tool
 description:         A command-line Pagure (gitforge) client for querying
                      and listing projects, users, groups, and issues.
@@ -10,14 +10,15 @@
 license-file:        LICENSE
 author:              Jens Petersen <juhpetersen@gmail.com>
 maintainer:          Jens Petersen <juhpetersen@gmail.com>
-copyright:           2019-2020,2022,2024 Jens Petersen
+copyright:           2019-2020,2022,2024-2026 Jens Petersen
 category:            Utility
 build-type:          Simple
 extra-doc-files:     README.md
                      CHANGELOG.md
 tested-with:         GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5,
                      GHC == 8.8.4, GHC == 8.10.4, GHC == 9.0.2, GHC == 9.2.8,
-                     GHC == 9.4.8, GHC == 9.6.5, GHC == 9.8.2
+                     GHC == 9.4.8, GHC == 9.6.6, GHC == 9.8.4, GHC == 9.10.3,
+                     GHC == 9.12.3
 
 source-repository head
   type:                git
@@ -30,14 +31,16 @@
                      , aeson-pretty
                      , base < 5
                      , bytestring
+                     , extra
                      , filepath
                      , http-query
                      , optparse-applicative
-                     , pagure >= 0.2
+                     , pagure >= 0.2.1
                      , simple-cmd
                      , simple-cmd-args >= 0.1.6
                      , text
                      , unordered-containers
+                     , vector
                      , yaml
 
   if impl(ghc<8.0)
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -11,7 +11,7 @@
                             (<|>)
 #endif
                            )
-import Control.Monad (unless, when)
+import Control.Monad.Extra (unless, when, whenJust)
 import Data.Aeson.Encode.Pretty (encodePretty)
 import Data.Aeson.Types
 #if MIN_VERSION_aeson(2,0,0)
@@ -25,6 +25,7 @@
 import Data.Text (Text)
 import qualified Data.Text as T
 import qualified Data.Text.IO as T
+import qualified Data.Vector as V
 import Data.Yaml (encode)
 import Network.HTTP.Query ((+/+))
 import SimpleCmd (error', (+-+))
@@ -41,6 +42,7 @@
     listProjects
     <$> serverOpt
     <*> countOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> forksOpt
     <*> optional namespaceOpt
@@ -60,22 +62,32 @@
   , Subcommand "project" "show project details" $
     projectInfo
     <$> serverOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> strArg "PROJECT"
   , Subcommand "branches" "list project branches" $
     repoBranches
     <$> serverOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> strArg "REPO"
   , Subcommand "git-url" "show project repo's git urls" $
     gitUrl
     <$> serverOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> strArg "REPO"
+  , Subcommand "contributors" "show project repo's contributors" $
+    contributors
+    <$> serverOpt
+    <*> nohideOpt
+    <*> formatOpt
+    <*> strArg "REPO"
   , Subcommand "issues" "list project issues" $
     projectIssues
     <$> serverOpt
     <*> countOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> strArg "REPO"
     <*> switchWith 'A' "all" "list Open and Closed issues"
@@ -85,39 +97,46 @@
   , Subcommand "issue" "show project issue" $
     projectIssue
     <$> serverOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> strArg "REPO"
     <*> argumentWith auto "ISSUE"
   , Subcommand "users" "list users" $
     users
     <$> serverOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> strArg "PATTERN"
   , Subcommand "username" "fullname of user" $
     username
     <$> serverOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> strArg "USERNAME"
   , Subcommand "userinfo" "show user details" $
     userInfo
     <$> serverOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> strArg "USERNAME"
   , Subcommand "groups" "list groups" $
     groups
     <$> serverOpt
     <*> countOpt
+    <*> nohideOpt
     <*> formatOpt
     <*> optional (strArg "PATTERN")
   , Subcommand "groupinfo" "show group details" $
     groupInfo
     <$> serverOpt
     <*> switchWith 'p' "projects" "List projects"
+    <*> nohideOpt
     <*> formatOpt
     <*> strArg "GROUP"
   ]
   where
     countOpt = switchWith 'c' "count" "Show number only"
+    nohideOpt = switchWith 'e' "show-empty" "Display empty lists and objects"
     formatOpt = flagWith' FormatJson 'j' "json" "Output JSON" <|> flagWith FormatDefault FormatYaml 'y' "yaml" "YAML output"
     namespaceOpt = strOptionWith 'n' "namespace" "NAMESPACE" "Specify project repo namespace"
     packagerOpt = Owner <$> ownerOpt <|> Committer <$> usernameOpt
@@ -136,19 +155,20 @@
 srcFedoraprojectOrg :: String
 srcFedoraprojectOrg = "src.fedoraproject.org"
 
-defaultPrinter :: OutputFormat -> (Object -> IO ()) -> Object -> IO ()
-defaultPrinter FormatDefault pr = pr
-defaultPrinter FormatJson _ = BL.putStrLn . encodePretty
-defaultPrinter FormatYaml _ = B.putStrLn . encode
+defaultPrinter :: Bool -> OutputFormat -> (Object -> IO ()) -> Object -> IO ()
+defaultPrinter _ FormatDefault pr = pr
+defaultPrinter nohide fmt _ = yamlPrinter nohide fmt
 
-listProjects :: String -> Bool -> OutputFormat -> Forks -> Maybe String -> Maybe Packager -> Maybe String -> IO ()
-listProjects server count format forks mnamespace mpackager mpattern = do
+listProjects :: String -> Bool -> Bool -> OutputFormat -> Forks -> Maybe String
+             -> Maybe Packager -> Maybe String -> IO ()
+listProjects server count nohide format forks mnamespace mpackager mpattern = do
   unless (count || isJust mpackager || isJust mpattern) $
     error' "Please give a package pattern, --count, or --owner/--username"
   let path = "projects"
       params = makeKey "short" "1" ++ fork ++ packager ++ maybeKey "namespace" mnamespace ++ maybeKey "pattern" mpattern
   pages <- queryPagureCountPaged server count path params ("pagination", "page")
-  mapM_ (defaultPrinter format printPage) pages
+  let projects = concatMap (lookupKey' "projects") pages
+  mapM_ (defaultPrinter nohide format printProject) projects
   where
     -- (!orphan only works on pagure >=0.29)
     packager = case mpackager of
@@ -161,11 +181,10 @@
       IncludeForks -> []
       OnlyForks -> makeKey "fork" "1"
 
-    printPage :: Object -> IO ()
-    printPage result = do
+    printProject :: Object -> IO ()
+    printProject project = do
       let key' = if isJust mnamespace then "name" else "fullname"
-          projects = lookupKey' "projects" result
-      (mapM_ T.putStrLn . mapMaybe (lookupKey key')) projects
+      whenJust (lookupKey key' project) T.putStrLn
 
 -- FIXME duplicates subset of listProjects
 userRepos :: String -> Bool -> Bool -> String -> IO ()
@@ -187,13 +206,14 @@
 boolKey k True val = makeKey k val
 
 -- FIXME limit max number of issues
-projectIssues :: String -> Bool -> OutputFormat -> String -> Bool -> Maybe String -> Maybe String -> Maybe String -> IO ()
-projectIssues server count format repo allstatus mauthor msince mpat = do
+projectIssues :: String -> Bool -> Bool -> OutputFormat -> String -> Bool
+              -> Maybe String -> Maybe String -> Maybe String -> IO ()
+projectIssues server count nohide format repo allstatus mauthor msince mpat = do
   let path = repo +/+ "issues"
       params = [makeItem "status" "all" | allstatus] ++
                maybeKey "author" mauthor ++ maybeKey "since" msince
   pages <- queryPagureCountPaged server count path params ("pagination", "page")
-  mapM_ (defaultPrinter format printIssues) pages
+  mapM_ (defaultPrinter nohide format printIssues) pages
   where
     printIssues :: Object -> IO ()
     printIssues result = do
@@ -217,54 +237,54 @@
         status <- obj .: "status"
         return (id',title,status)
 
-repoBranches :: String -> OutputFormat -> String -> IO ()
-repoBranches server format repo = do
+repoBranches :: String -> Bool -> OutputFormat -> String -> IO ()
+repoBranches server nohide format repo = do
   let namespace =
         if server == srcFedoraprojectOrg && '/' `notElem` repo
         then "rpms/" else ""
       path = namespace ++ repo +/+ "git/branches"
   eres <- queryPagureSingle server path []
-  either error' (defaultPrinter format (printKeyList "branches")) eres
+  either error' (defaultPrinter nohide format (printKeyList "branches")) eres
 
-users :: String -> OutputFormat -> String -> IO ()
-users server format pat = do
+users :: String -> Bool -> OutputFormat -> String -> IO ()
+users server nohide format pat = do
   let path = "users"
       params = makeKey "pattern" pat
   res <- queryPagure server path params
-  defaultPrinter format (printKeyList "users") res
+  defaultPrinter nohide format (printKeyList "users") res
 
-username :: String -> OutputFormat -> String -> IO ()
-username server format user = do
+username :: String -> Bool -> OutputFormat -> String -> IO ()
+username server nohide format user = do
   let path = "user" +/+ user
   eres <- queryPagureSingle server path $ makeKey "per_page" "1"
   case eres of
     Left err -> error' err
-    Right res -> defaultPrinter format printName res
+    Right res -> defaultPrinter nohide format printName res
   where
     printName res =
       case lookupKey "user" res >>= lookupKey "fullname" of
         Nothing -> error' "User fullname not found"
         Just fn -> T.putStrLn fn
 
-groups :: String -> Bool -> OutputFormat -> Maybe String -> IO ()
-groups server count format mpat = do
+groups :: String -> Bool -> Bool -> OutputFormat -> Maybe String -> IO ()
+groups server count nohide format mpat = do
   let path = "groups"
       params = maybeKey "pattern" mpat
   pages <- queryPagureCountPaged server count path params ("pagination", "page")
-  mapM_ (defaultPrinter format (printKeyList "groups")) pages
+  mapM_ (defaultPrinter nohide format (printKeyList "groups")) pages
 
 printKeyList :: String -> Object -> IO ()
 printKeyList key' res =
   mapM_ T.putStrLn (lookupKey' (T.pack key') res :: [Text])
 
-gitUrl :: String -> OutputFormat -> String -> IO ()
-gitUrl server format repo = do
+gitUrl :: String -> Bool -> OutputFormat -> String -> IO ()
+gitUrl server nohide format repo = do
   let namespace =
         if server == srcFedoraprojectOrg && '/' `notElem` repo
         then "rpms/" else ""
       path = namespace ++ repo +/+ "git/urls"
   res <- queryPagure server path []
-  defaultPrinter format printURLs res
+  defaultPrinter nohide format printURLs res
   where
     printURLs result =
       mapM_ T.putStrLn $
@@ -278,51 +298,68 @@
 #endif
     localLookupKey = lookupKey' . T.pack
 
-yamlPrinter :: OutputFormat -> Object -> IO ()
-yamlPrinter FormatDefault = yamlPrinter FormatYaml
-yamlPrinter FormatJson = BL.putStrLn . encodePretty
-yamlPrinter FormatYaml = B.putStrLn . encode
+contributors :: String -> Bool -> OutputFormat -> String -> IO ()
+contributors server nohide format repo = do
+  let namespace =
+        if server == srcFedoraprojectOrg && '/' `notElem` repo
+        then "rpms/" else ""
+      path = namespace ++ repo +/+ "contributors"
+  res <- queryPagure server path []
+  yamlPrinter nohide format res
 
-projectInfo :: String -> OutputFormat -> String -> IO ()
-projectInfo server format repo = do
+yamlPrinter :: Bool -> OutputFormat -> Object -> IO ()
+yamlPrinter nohide format =
+  case format of
+    FormatDefault -> yamlPrinter nohide FormatYaml
+    FormatJson -> BL.putStrLn . encodePretty . hideEmpty
+    FormatYaml -> B.putStrLn . encode . hideEmpty
+  where
+    hideEmpty = if nohide then id else cleanObject
+
+cleanObject :: Object -> Object
+cleanObject = M.mapMaybe go
+  where
+    go :: Value -> Maybe Value
+    -- drop empty lists
+    go (Array v) | V.null v = Nothing
+                 | otherwise = Just $ Array (V.map (wrap go) v)
+                 where
+                   wrap f (Object o) = Object (M.mapMaybe f o)
+                   wrap f (Array a)  = Array (V.map (wrap f) a)
+                   wrap _ x          = x
+    -- drop empty objects
+    go (Object o) =
+        let cleaned = M.mapMaybe go o
+        in if M.null cleaned then Nothing else Just (Object cleaned)
+    go other = Just other
+
+projectInfo :: String -> Bool -> OutputFormat -> String -> IO ()
+projectInfo server nohide format repo = do
   let namespace =
         if server == srcFedoraprojectOrg && '/' `notElem` repo
         then "rpms/" else ""
       path = namespace ++ repo
   eval <- pagureProjectInfo server path
-  either error' (yamlPrinter format) eval
+  either error' (yamlPrinter nohide format) eval
 
-projectIssue :: String -> OutputFormat -> String -> Int -> IO ()
-projectIssue server format repo issue = do
+projectIssue :: String -> Bool -> OutputFormat -> String -> Int -> IO ()
+projectIssue server nohide format repo issue = do
   eval <- pagureProjectIssueInfo server repo issue
-  either error' (yamlPrinter format) eval
+  either error' (yamlPrinter nohide format) eval
 
-userInfo :: String -> OutputFormat -> String -> IO ()
-userInfo server format user = do
+userInfo :: String -> Bool -> OutputFormat -> String -> IO ()
+userInfo server nohide format user = do
   eval <- pagureUserInfo server user []
-  either error' (yamlPrinter format) eval
+  either error' (yamlPrinter nohide format) eval
 
-groupInfo :: String -> Bool -> OutputFormat -> String -> IO ()
-groupInfo server projects format group = do
+groupInfo :: String -> Bool -> Bool -> OutputFormat -> String -> IO ()
+groupInfo server projects nohide format group = do
   let params = [makeItem "projects" "1" | projects]
   eval <- pagureGroupInfo server group params
-  either error' (yamlPrinter format) eval
+  either error' (yamlPrinter nohide format) eval
 
 -- FIXME support acl parameter (admin, commit or ticket)
 groupProjects :: String -> Bool -> String -> IO ()
-groupProjects server count group = do
-  let path = "group" +/+ group
-      params = makeKey "projects" "1"
-  pages <- queryPagureCountPaged server count path params ("pagination", "page")
-  mapM_ (defaultPrinter FormatDefault printPage) pages
-  where
-    -- acl
-    -- packager = case mpackager of
-    --   Nothing -> boolKey "owner" (server == srcFedoraprojectOrg) "!orphan"
-    --   Just (Owner o) -> makeKey "owner" o
-    --   Just (Committer c) -> makeKey "username" c
-
-    printPage :: Object -> IO ()
-    printPage result = do
-     let projects = lookupKey' "projects" result
-     (mapM_ T.putStrLn . mapMaybe (lookupKey "fullname")) projects
+groupProjects server count group =
+  pagureGroupRepos server count group >>=
+  mapM_ T.putStrLn
