diff --git a/python-libs/poulpe/cli.py b/python-libs/poulpe/cli.py
index 6961bdbc1e69a75bdfe4bd5735811b586fbfa46b_cHl0aG9uLWxpYnMvcG91bHBlL2NsaS5weQ==..ce879b4595821908cb197f090dae13fa5013d9e6_cHl0aG9uLWxpYnMvcG91bHBlL2NsaS5weQ== 100644
--- a/python-libs/poulpe/cli.py
+++ b/python-libs/poulpe/cli.py
@@ -3,4 +3,5 @@
 
 import click
 
+BINARIES = resources.files("poulpe.bin")
 
@@ -6,4 +7,30 @@
 
+helptext = """Wrapper around Poulpe binaries for ease of calling.
+
+\b
+Available Poulpe commands:
+
+{subcommands}"""
+
+
+def set_dynamic_help():
+    """Dynamically set the helptext to populate with available commands
+    in `poulpe.bin`."""
+    # XXX This is kinda bad and manual, but it's the price we pay
+    # for ensuring we have independent binaries.
+    # We can do better (completion, factored help, etc.) later.
+
+    def decorator(command):
+        subcommands = ", ".join(
+            f.name for f in BINARIES.iterdir() if f.is_file()
+        )
+        command.help = helptext.format(subcommands=subcommands)
+        return command
+
+    return decorator
+
+
+@set_dynamic_help()
 @click.command(
     no_args_is_help=True,
     context_settings={
@@ -14,8 +41,8 @@
 @click.argument('subcommand')
 @click.argument('subcommand_args', nargs=-1, type=click.UNPROCESSED)
 def exec_subcommand(subcommand, subcommand_args):
-    """Wrapper around Poulpe binaries for ease of calling"""
-    subcommand_file = resources.files("poulpe.bin").joinpath(subcommand)
+    """Help is set dynamically, see `set_dynamic_help`"""
+    subcommand_file = BINARIES.joinpath(subcommand)
 
     if not subcommand_file.is_file():
         raise click.BadParameter(f"Subcommand `{subcommand}` does not exist.")