diff --git a/HISTORY.rst b/HISTORY.rst
index 0f9ae33..08d2ad2 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,6 +1,10 @@
 next
 ~~~~
 
+**Features**
+
+- Improved command parser (#53)
+
 **Bug fixes**
 
 - Do not attempt to build wheels for uninstallable addons.
diff --git a/src/oca_github_bot/commands.py b/src/oca_github_bot/commands.py
index e377d69..7aba1e7 100644
--- a/src/oca_github_bot/commands.py
+++ b/src/oca_github_bot/commands.py
@@ -6,7 +6,7 @@ import re
 from .tasks import merge_bot
 
 BOT_COMMAND_RE = re.compile(
-    r"/ocabot +(?P<command>\w+)( +(?P<options>.*?))? *$", re.MULTILINE
+    r"/ocabot[ \t]+(?P<command>\w+)(?P<options>[ \t\w]*)(\W|\r?$)", re.MULTILINE
 )
 
 
@@ -23,6 +23,7 @@ class InvalidOptionsError(Exception):
 class BotCommand:
     def __init__(self, name, options):
         self.name = name
+        self.options = options
         self.parse_options(options)
 
     @classmethod
@@ -46,8 +47,8 @@ class BotCommandMerge(BotCommand):
     def parse_options(self, options):
         if not options:
             return
-        if options in ("major", "minor", "patch"):
-            self.bumpversion = options
+        if len(options) == 1 and options[0] in ("major", "minor", "patch"):
+            self.bumpversion = options[0]
         else:
             raise InvalidOptionsError(self.name, options)
 
@@ -60,4 +61,6 @@ class BotCommandMerge(BotCommand):
 def parse_commands(text):
     """ Parse a text and return an iterator of BotCommand objects. """
     for mo in BOT_COMMAND_RE.finditer(text):
-        yield BotCommand.create(mo.group("command"), mo.group("options"))
+        yield BotCommand.create(
+            mo.group("command"), mo.group("options").strip().split()
+        )

diff --git a/tests/test_commands.py b/tests/test_commands.py
index 1195b53..2091988 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -22,11 +22,38 @@ def test_parse_command_multi():
                 /ocabot merge major
                 /ocabot   merge   patch
                 /ocabot merge patch
+                /ocabot merge, please
+                /ocabot merge  minor, please
+                /ocabot merge minor, please
+                /ocabot merge.
+                /ocabot merge patch. blah
+                /ocabot merge minor # ignored
                 ...
             """
         )
     )
-    assert len(cmds) == 3
+    assert [(cmd.name, cmd.options) for cmd in cmds] == [
+        ("merge", ["major"]),
+        ("merge", ["patch"]),
+        ("merge", ["patch"]),
+        ("merge", []),
+        ("merge", ["minor"]),
+        ("merge", ["minor"]),
+        ("merge", []),
+        ("merge", ["patch"]),
+        ("merge", ["minor"]),
+    ]
+
+
+def test_parse_command_2():
+    cmds = list(
+        parse_commands(
+            "Great contribution, thanks!\r\n\r\n"
+            "/ocabot merge\r\n\r\n"
+            "Please forward port it to 12.0."
+        )
+    )
+    assert [(cmd.name, cmd.options) for cmd in cmds] == [("merge", [])]
 
 
 def test_parse_command_merge():

merge bot: parse command inside a greater message
For now, the merge bot only reacts to the merge command putting that comment in an isolated message (example of greater message: https://github.com/OCA/account-invoicing/pull/552#issuecomment-507203903).
tests/test_commands.py::test_parse_command_multi tests/test_commands.py::test_parse_command_2
tests/test_commands.py::test_parse_command_not_a_command tests/test_commands.py::test_parse_command_merge