diff --git a/.cspell.json b/.cspell.json
new file mode 100644
index 0000000000000000000000000000000000000000..368b755a17868f53cf92edf4323b51355b976da7
--- /dev/null
+++ b/.cspell.json
@@ -0,0 +1,10 @@
+{
+  "dictionaries": ["drupal","companies", "fonts", "html", "php", "softwareTerms", "automatic_updates"],
+  "dictionaryDefinitions": [
+    { "name": "automatic_updates", "path": "./dictionary.txt"}
+  ],
+  "ignorePaths": [
+    "pcre.ini",
+    "README.md"
+  ]
+}
diff --git a/drupalci.yml b/drupalci.yml
index be5be1e4a074080560237a7b175d8f96f7e77d7a..422d14d781e7aa035d8e2e450ae2dc060d8653aa 100644
--- a/drupalci.yml
+++ b/drupalci.yml
@@ -9,42 +9,14 @@ build:
       # Run code quality checks.
       container_command.commit-checks:
         commands:
-          # Copy core's checking script into this contrib module.
-          - "cp /var/www/html/core/scripts/dev/commit-code-check.sh modules/contrib/automatic_updates/"
-          # Comply with core's checking script file permissions expectations.
-          - chmod 644 modules/contrib/automatic_updates/
-          # Rewrite $TOP_LEVEL/core since $TOP_LEVEL now refers to the contrib module.
-          - sed -i "s/\$TOP_LEVEL\/core/\/var\/www\/html\/core/" modules/contrib/automatic_updates/commit-code-check.sh
-          # Ensure the remainder of the script runs from the script's directory and not the current working directory.
-          - sed -i "s/# Gets list of files to check./cd \"\$\(dirname \"\$0\"\)\";/" modules/contrib/automatic_updates/commit-code-check.sh
-          # When constructing $FILES, ignore the `commit-code-check.sh` file we just copied.
-          - sed -i "s/--exclude=vendor/--exclude=vendor --exclude=commit-code-check.sh/" modules/contrib/automatic_updates/commit-code-check.sh
-          # vendor/bin/phpcs now needs to be prefixed. And $TOP_LEVEL/vendor needs to be rewritten. And add verbose output.
-          - sed -i "s/vendor\/bin\/phpcs/\/var\/www\/html\/vendor\/bin\/phpcs/" modules/contrib/automatic_updates/commit-code-check.sh
-          - sed -i "s/\$TOP_LEVEL\/vendor/\/var\/www\/html\/vendor/" modules/contrib/automatic_updates/commit-code-check.sh
-          - sed -i "s/phpcs -ps/phpcs \$TOP_LEVEL -ps/" modules/contrib/automatic_updates/commit-code-check.sh
-          # Disable all JS compile checks until we've matched core's build process.
-          # @todo Remove this in https://www.drupal.org/project/automatic_updates/issues/3221082.
-          - sed -i "s/COMPILE_CHECK=1/COMPILE_CHECK=0/" modules/contrib/automatic_updates/commit-code-check.sh
-          - sed -i "s/core\/scripts\/js/js/" modules/contrib/automatic_updates/commit-code-check.sh
-          # Uncomment to Check all files
-          #- sed -i "s/git diff --name-only HEAD~1 HEAD/find \* -type f -not -path \"\.\/\.git\*\"/" modules/contrib/automatic_updates/commit-code-check.sh
-          # Make cspell examine our files.
-          - sed -i "s/yarn run -s spellcheck/yarn run -s spellcheck --root \$TOP_LEVEL/" modules/contrib/automatic_updates/commit-code-check.sh
-          # Add our words to the dictionary.
-          - cat modules/contrib/automatic_updates/dictionary.txt >> core/misc/cspell/dictionary.txt
-          # Ensure we have the full path to PHPStan.
-          - sed -i "s/vendor\/bin\/phpstan/\/var\/www\/html\/vendor\/bin\/phpstan/" modules/contrib/automatic_updates/commit-code-check.sh
-          # After all of the shenanigans above, we're finally ready to run core's `commit-code-check.sh`! :)
-          - "modules/contrib/automatic_updates/commit-code-check.sh --drupalci"
-          # Restore the original permissions.
-          - chmod 777 modules/contrib/automatic_updates/
           # Disable the PCRE engine's JIT, since it causes Composer to die during the
           # update process, but only on Drupal CI, and for reasons that are essentially
           # impossible to trace into. The PCRE JIT is not necessary for Automatic Updates
           # to work correctly, and disabling it is a known workaround.
           # @see pcre.ini
           - sudo cp modules/contrib/automatic_updates/pcre.ini /usr/local/etc/php/conf.d
+          # @todo Replace in favor of commit-code-check.sh once https://www.drupal.org/project/drupal/issues/3314100 lands.
+          - modules/contrib/automatic_updates/scripts/commit-code-check.sh --drupalci
         halt-on-fail: true
       # run_tests task is executed several times in order of performance speeds.
       # halt-on-fail can be set on the run_tests tasks in order to fail fast.
diff --git a/package_manager/tests/src/Kernel/ComposerInspectorTest.php b/package_manager/tests/src/Kernel/ComposerInspectorTest.php
index a96cc368604446eb85da5a827008343aad726a17..2e918ad2d751df11a1db5df22e3151f4f92f74cc 100644
--- a/package_manager/tests/src/Kernel/ComposerInspectorTest.php
+++ b/package_manager/tests/src/Kernel/ComposerInspectorTest.php
@@ -7,7 +7,6 @@ namespace Drupal\Tests\package_manager\Kernel;
 use Composer\Json\JsonFile;
 use Drupal\Component\Serialization\Json;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\KernelTests\KernelTestBase;
 use Drupal\package_manager\InstalledPackage;
 use PhpTuf\ComposerStager\Domain\Exception\RuntimeException;
 
diff --git a/scripts/commit-code-check.sh b/scripts/commit-code-check.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3c77f47f2840e8c2b94621fbf692a1fe5799d03d
--- /dev/null
+++ b/scripts/commit-code-check.sh
@@ -0,0 +1,121 @@
+#!/usr/bin/env bash
+
+# NAME
+#     commit-code-check.sh - Run all code quality checks.
+#
+# SYNOPSIS
+#     bash scripts/commit-code-check.sh
+#
+# DESCRIPTION
+#     Performs the following quality checks, closely matching core's commit-code-check.sh:
+#     - Spell checking.
+#     - PHPCS checks PHP and YAML files.
+#     - PHPStan checks PHP files.
+#     - ESLint checks YAML files.
+
+# cSpell:disable
+
+cd "$(dirname "$0")/../" || exit;
+
+DRUPALCI=0
+while test $# -gt 0; do
+  case "$1" in
+    -h|--help)
+      echo "Drupal code quality checks"
+      echo " "
+      echo "options:"
+      echo "-h, --help                show brief help"
+      echo "--drupalci                a special mode for DrupalCI"
+      echo " "
+      exit 0
+      ;;
+    --drupalci)
+      DRUPALCI=1
+      shift
+      ;;
+    *)
+      break
+      ;;
+  esac
+done
+
+MODULE_DIRECTORY=$(pwd)
+
+# Find the site root directory. Check up to three directories above.
+DIR=$(pwd)
+for i in {0..3}; do
+  DIR=$(dirname "$DIR")
+  if test -f "$DIR/core/composer.json"; then
+    CORE_DIRECTORY="$DIR"
+    break
+  fi
+done
+
+# Set up variables to make colored output simple. Color output is disabled on
+# DrupalCI because it is breaks reporting.
+# @todo https://www.drupal.org/project/drupalci_testbot/issues/3181869
+if [[ "$DRUPALCI" == "1" ]]; then
+  red=""
+  green=""
+  reset=""
+else
+  red=$(tput setaf 1 && tput bold)
+  green=$(tput setaf 2)
+  title=$(tput setaf 4 && tput bold)
+  reset=$(tput sgr0)
+fi
+
+# This script assumes that composer install and yarn install have already been
+# run and all dependencies are updated.
+FINAL_STATUS=0
+
+print_separator() {
+  printf "\n${title}"
+  printf -- '-%.0s' {1..100}
+  printf "${reset}\n"
+}
+print_title() {
+  print_separator
+  printf "${title}$1${reset}"
+  print_separator
+}
+print_results() {
+  RESULTS=$1
+  LABEL=$2
+  if [ "$RESULTS" -ne "0" ]; then
+    # If there are failures set the status to a number other than 0.
+    FINAL_STATUS=1
+    printf "\n${title}$LABEL: ${red}failed${reset}\n"
+  else
+    printf "\n${title}$LABEL: ${green}passed${reset}\n"
+  fi
+}
+
+print_title "[1/4] PHPCS"
+$CORE_DIRECTORY/vendor/bin/phpcs $MODULE_DIRECTORY -ps --standard="$CORE_DIRECTORY/core/phpcs.xml.dist"
+print_results $? "PHPCS"
+
+# @todo Uncomment in https://drupal.org/i/3342120
+# print_title "[2/4] PHPStan"
+# php -d apc.enabled=0 -d apc.enable_cli=0 $CORE_DIRECTORY/vendor/bin/phpstan analyze --no-progress --configuration="$CORE_DIRECTORY/core/phpstan.neon.dist" $MODULE_DIRECTORY
+# print_results $? "PHPStan"
+
+print_title "[3/4] CSpell"
+cd $CORE_DIRECTORY/core && yarn run -s spellcheck --no-progress --root $MODULE_DIRECTORY -c .cspell.json "**" && cd -
+print_results $? "CSpell"
+
+print_title "[4/4] eslint:yaml"
+cd $CORE_DIRECTORY/core && yarn eslint --resolve-plugins-relative-to . --ext .yml $MODULE_DIRECTORY && cd -
+print_results $? "eslint:yaml"
+print_separator
+
+if [[ "$FINAL_STATUS" == "1" ]]; then
+  printf "${red}Drupal code quality checks failed.${reset}"
+  if [[ "$DRUPALCI" == "1" ]]; then
+    printf "\nTo reproduce this output locally:\n"
+    printf "* Apply the change as a patch, or from the merge request branch\n"
+    printf "* Run this command locally: sh ./scripts/commit-code-check.sh"
+  fi
+  print_separator
+fi
+exit $FINAL_STATUS