diff --git a/scripts/b4-wrapper-poky.py b/scripts/b4-wrapper-poky.py
new file mode 100755
index 0000000000000000000000000000000000000000..4fb9c08dc9d63bc2cbf28526e51ef9228648d11f
--- /dev/null
+++ b/scripts/b4-wrapper-poky.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python3
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+# This script is to be called by b4:
+# - through the b4.prep-perpatch-check-cmd with "prep-perpatch-check-cmd" as
+#   first argument,
+# - through b4.send-auto-cc-cmd with "send-auto-cc-cmd" as first argument,
+#
+# When prep-perpatch-check-cmd is passsed:
+#
+#  This checks that a patch makes changes to at most one project in the poky
+#  combo repo (that is, out of yocto-docs, bitbake, openembedded-core combined
+#  into poky and the poky-specific files).
+#
+#  Printing something to stdout in this file will result in b4 prep --check fail
+#  for the currently parsed patch.
+#
+#  It checks that all patches in the series make changes to at most one project.
+#
+# When send-auto-cc-cmd is passed:
+#
+#  This returns the list of Cc recipients for a patch.
+#
+# This script takes as stdin a patch.
+
+import pathlib
+import re
+import subprocess
+import sys
+
+cmd = sys.argv[1]
+
+patch = sys.stdin.readlines()
+
+# Subject field is used to identify the last patch as this script is called for
+# each patch. We edit the same file in a series by using the References field
+# unique identifier to check which projects are modified by earlier patches in
+# the series. To avoid cluttering the disk, the last patch in the list removes
+# that shared file.
+re_subject = re.compile(r'^Subject:.*\[.*PATCH.*\s(\d+)/\1')
+re_ref = re.compile(r'^References: <(.*)>$')
+
+subject = None
+ref = None
+
+if subprocess.call(["which", "lsdiff"], stdout=subprocess.DEVNULL) != 0:
+    print("lsdiff missing from host, please install patchutils")
+    sys.exit(-1)
+
+try:
+    one_patch_series = False
+    for line in patch:
+        subject = re_subject.match(line)
+        if subject:
+            # Handle [PATCH 1/1]
+            if subject.group(1) == 1:
+                one_patch_series = True
+            break
+        if re.match(r'^Subject: .*\[.*PATCH[^/]*\]', line):
+            # Single patch is named [PATCH] but if there are prefix, it could be
+            # [PATCH prefix], so handle everything that doesn't have a /
+            # character which is used as separator between current patch number
+            # and total patch number
+            one_patch_series = True
+            break
+
+    if cmd == "prep-perpatch-check-cmd" and not one_patch_series:
+        for line in patch:
+            ref = re_ref.match(line)
+            if ref:
+                break
+
+        if not ref:
+            print("Failed to find ref to cover letter (References:)...")
+            sys.exit(-2)
+
+        ref = ref.group(1)
+        series_check = pathlib.Path(f".tmp-{ref}")
+
+    patch = "".join(patch)
+
+    project_paths = {
+            "bitbake": ["bitbake/*"],
+            "yocto-docs": ["documentation/*"],
+            "poky": [
+                "meta-poky/*",
+                "meta-yocto-bsp/*",
+                "README.hardware.md",
+                "README.poky.md",
+                ],
+    }
+
+    if cmd == "send-auto-cc-cmd":
+        # Patches to BitBake documentation should also go to yocto-docs mailing list
+        project_paths["yocto-docs"] += ["bitbake/doc/*"]
+
+    # List of projects touched by this patch
+    projs = []
+
+    # Any file not matched by any path in project_paths means it is from
+    # OE-Core.
+    # When matching some path in project_paths, remove the matched files from
+    # that list.
+    files_left = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"],
+                                         input=patch, text=True)
+    files_left = set(files_left)
+
+    for proj, proj_paths in project_paths.items():
+        lsdiff_args = [f"--include={path}" for path in proj_paths]
+        files = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"] + lsdiff_args,
+                                        input=patch, text=True)
+        if len(files):
+            files_left = files_left - set(files)
+            projs.append(proj)
+            continue
+
+        # Handle patches made with --no-prefix
+        files = subprocess.check_output(["lsdiff"] + lsdiff_args,
+                                        input=patch, text=True)
+        if len(files):
+            files_left = files_left - set(files)
+            projs.append(proj)
+
+    # Catch-all for everything not poky-specific or in bitbake/yocto-docs
+    if len(files_left):
+        projs.append("openembedded-core")
+
+    if cmd == "prep-perpatch-check-cmd":
+        if len(projs) > 1:
+            print(f"Diff spans more than one project ({', '.join(sorted(projs))}), split into multiple commits...")
+            sys.exit(-3)
+
+        # No need to check other patches in the series as there aren't any
+        if one_patch_series:
+            sys.exit(0)
+
+        # This should be replaced once b4 supports prep-perseries-check-cmd (or something similar)
+
+        if series_check.exists():
+            # NOT race-free if b4 decides to parallelize prep-perpatch-check-cmd
+            series_projs = series_check.read_text().split('\n')
+        else:
+            series_projs = []
+
+        series_projs += projs
+        uniq_series_projs = set(series_projs)
+        # NOT race-free, if b4 decides to parallelize prep-perpatch-check-cmd
+        series_check.write_text('\n'.join(uniq_series_projs))
+
+        if len(uniq_series_projs) > 1:
+            print(f"Series spans more than one project ({', '.join(sorted(uniq_series_projs))}), split into multiple series...")
+            sys.exit(-4)
+    else:  # send-auto-cc-cmd
+        ml_projs = {
+            "bitbake": "bitbake-devel@lists.openembedded.org",
+            "yocto-docs": "docs@lists.yoctoproject.org",
+            "poky": "poky@lists.yoctoproject.org",
+            "openembedded-core": "openembedded-core@lists.openembedded.org",
+        }
+
+        print("\n".join([ml_projs[ml] for ml in projs]))
+
+    sys.exit(0)
+finally:
+    # Last patch in the series, cleanup tmp file
+    if subject and ref and series_check.exists():
+        series_check.unlink()
