diff --git a/ahoy.test.yml b/ahoy.test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a0cf5c43629f9194ac8ac702b6e1066c64916f67
--- /dev/null
+++ b/ahoy.test.yml
@@ -0,0 +1,14 @@
+ahoyapi: v2
+commands:
+  phpcs:
+    cmd: phpcs --standard=DrupalPractice $(pwd)/web/modules/custom/ $(pwd)/web/profiles/custom/ $(pwd)/web/themes/custom/
+    usage: PHP coding standards
+  phpunit:test:
+    cmd: vendor/bin/phpunit --configuration $(pwd)/tests/phpunit.xml.dist
+    usage: PHP unit tests
+  phpunit:list:suites:
+    cmd: vendor/bin/phpunit --configuration $(pwd)/tests/phpunit.xml.dist --list-suites
+    usage: List available test suites of PHP unit tests
+  phpunit:list:groups:
+    cmd: vendor/bin/phpunit --configuration $(pwd)/tests/phpunit.xml.dist --list-groups
+    usage: List available test groups of PHP unit tests
diff --git a/ahoy.yml b/ahoy.yml
index 7cd5b8dedb7b48eec820ace56b837dc0cddf6b55..34a5a1cfb3cf74ee6039d7a7b92028b382ae4744 100644
--- a/ahoy.yml
+++ b/ahoy.yml
@@ -1,8 +1,14 @@
 ahoyapi: v2
 commands:
+  exec:
+    cmd: docker run -it -v /var/run/docker.sock:/var/run/docker.sock -v ${PWD}:/drupal -v $SSH_AUTH_SOCK:/ssh-agent -v ${HOME}/.traefik:/root/.traefik -e SSH_AUTH_SOCK=/ssh-agent -w /drupal registry.lakedrops.com/docker/gitlab-drupal-ci "$@"
+    hide: true
   up:
-    cmd: docker run -it -v /var/run/docker.sock:/var/run/docker.sock -v ${PWD}:/drupal -v $SSH_AUTH_SOCK:/ssh-agent -v ${HOME}/.traefik:/root/.traefik -e SSH_AUTH_SOCK=/ssh-agent -w /drupal registry.lakedrops.com/docker/gitlab-drupal-ci /usr/bin/fish
+    cmd: ahoy dev exec /usr/bin/fish
     usage: Start container with all development tools
   update:
     cmd: docker pull registry.lakedrops.com/docker/gitlab-drupal-ci
     usage: Update image with the latest development tools
+  scaffold:
+    cmd: ahoy dev exec composer lakedrops:scaffold
+    usage: (Re-)Configure this project for developers
diff --git a/composer.json b/composer.json
index a4a79b2ed749395346c3d3e1c5010dc7989c4695..7cae9fb8d2d74a5838d9a4bec6eb67056e1cd891 100644
--- a/composer.json
+++ b/composer.json
@@ -36,7 +36,7 @@
         "drupal/coder": "^8.2",
         "lakedrops/ahoy": "^1.0.0",
         "lakedrops/behat4drupal": "^1.0.0",
-        "lakedrops/composer-json-utils": "^1.2.0",
+        "lakedrops/composer-json-utils": "^1.3.0",
         "lakedrops/composer-scripts": "^1.1.0",
         "lakedrops/docker4drupal": "^1.0.0",
         "lakedrops/dorgflow": "^1.0.0",
@@ -60,27 +60,18 @@
         }
     },
     "extra": {
-        "temp": {
-        },
         "class": "LakeDrops\\Drupal8ScaffoldDeveloper\\Plugin",
         "lakedrops": {
-            "scripts": {
-                "lakedrops": {
-                    "callback": "LakeDrops\\Drupal8ScaffoldDeveloper\\Plugin::scaffold",
-                    "description": "(Re-)Configure this project for developers."
-                }
-            },
             "ahoy": {
                 "dev": {
                     "usage": "Development tools for Drupal",
                     "imports": ["ahoy.yml"]
+                },
+                "test": {
+                    "usage": "Test tools",
+                    "imports": ["ahoy.test.yml"]
                 }
             }
         }
-    },
-    "scripts": {
-        "install-codestandards": ["Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run"],
-        "post-install-cmd": ["@install-codestandards"],
-        "post-update-cmd": ["@install-codestandards"]
     }
 }
diff --git a/src/CommandProvider.php b/src/CommandProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..41173cf732ace25d0c8e452a8207b9a81cd91129
--- /dev/null
+++ b/src/CommandProvider.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace LakeDrops\Drupal8ScaffoldDeveloper;
+
+use Composer\Plugin\Capability\CommandProvider as CommandProviderCapability;
+use Composer\Command\BaseCommand;
+
+class CommandProvider implements CommandProviderCapability {
+
+  /**
+   * Retrieves an array of commands
+   *
+   * @return BaseCommand[]
+   */
+  public function getCommands(): array {
+    return [
+      new ScaffoldCommand(),
+    ];
+  }
+}
diff --git a/src/Handler.php b/src/Handler.php
index 3d406db206ee5767e5009e6e2686b4da4ebcd1bc..56c23a76f205a1222c95d691fbc91d995397b96c 100644
--- a/src/Handler.php
+++ b/src/Handler.php
@@ -2,9 +2,6 @@
 
 namespace LakeDrops\Drupal8ScaffoldDeveloper;
 
-use Composer\Composer;
-use Composer\IO\IOInterface;
-use Composer\Script\Event;
 use LakeDrops\Component\Composer\BaseHandler;
 use LakeDrops\Component\Dotenv\Dotenv;
 use LakeDrops\Docker4Drupal\Handler as D4D;
@@ -20,17 +17,15 @@ class Handler extends BaseHandler {
   /**
    * Post project create event to execute the scaffolding.
    *
-   * @param \Composer\Script\Event $event
-   *   The event that triggered the plugin.
-   *
    * @throws \Twig_Error_Loader
    * @throws \Twig_Error_Runtime
    * @throws \Twig_Error_Syntax
+   * @throws \Exception
    */
-  public function setupLakeDropsProject(Event $event) {
+  public function setupLakeDropsProject() {
 
     // We only do the fancy stuff for developers.
-    if (!$event->isDevMode()) {
+    if (!$this->isDevMode()) {
       return;
     }
 
@@ -39,6 +34,7 @@ class Handler extends BaseHandler {
     $installationManager = $this->composer->getInstallationManager();
 
     $drupalCorePackage = $this->getDrupalCorePackage();
+    /** @noinspection NullPointerExceptionInspection */
     $corePath = $installationManager->getInstallPath($drupalCorePackage);
 
     // Directory where the root project is being created.
@@ -161,8 +157,8 @@ class Handler extends BaseHandler {
 
     // If available, also configure Docker4Drupal.
     if ($this->getPackage('lakedrops/docker4drupal')) {
-      $handler = new D4D($event->getComposer(), $event->getIO());
-      $handler->configureProject($event);
+      $handler = new D4D($this->composer, $this->io);
+      $handler->configureProject();
     }
   }
 
@@ -172,7 +168,7 @@ class Handler extends BaseHandler {
    * @return array
    *   The settings from the extra configuration.
    */
-  protected function getOptions() {
+  protected function getOptions(): array {
     $extra = $this->composer->getPackage()->getExtra() + ['d8-project-scaffold' => []];
     $options = $extra['d8-project-scaffold'] + [
       'chg-acl' => FALSE,
diff --git a/src/Plugin.php b/src/Plugin.php
index a22b7d7322a330a2339df35f59921413ea01bcbb..84c0dada92aaff3e54d055d6903ed22e74bc4358 100644
--- a/src/Plugin.php
+++ b/src/Plugin.php
@@ -39,38 +39,23 @@ class Plugin extends BasePlugin {
   /**
    * {@inheritdoc}
    */
-  public static function getSubscribedEvents() {
-    return [
-      ScriptEvents::POST_CREATE_PROJECT_CMD => 'postCreateProject',
-      ScriptEvents::POST_UPDATE_CMD => 'postUpdate',
-    ];
+  public function getCapabilities(): array {
+    return array(
+      \Composer\Plugin\Capability\CommandProvider::class => CommandProvider::class,
+    );
   }
 
   /**
-   * Post create project event callback.
-   *
-   * @param \Composer\Script\Event $event
-   *   The event that triggered the plugin.
-   */
-  public function postCreateProject(Event $event) {
-    $this->scaffoldHandler->downloadScaffold();
-    $this->scaffoldHandler->generateAutoload();
-    $this->handler->setupLakeDropsProject($event);
-  }
-
-  /**
-   * Post update event callback.
-   *
-   * @param \Composer\Script\Event $event
-   *   The event that triggered the plugin.
+   * {@inheritdoc}
    */
-  public function postUpdate(Event $event) {
-    $this->scaffoldHandler->downloadScaffold();
-    $this->scaffoldHandler->generateAutoload();
+  public static function getSubscribedEvents(): array {
+    return [
+      ScriptEvents::POST_CREATE_PROJECT_CMD => 'postCreateProject',
+    ];
   }
 
   /**
-   * Callback to setup the project.
+   * Post create project event callback.
    *
    * @param \Composer\Script\Event $event
    *   The event that triggered the plugin.
@@ -79,9 +64,12 @@ class Plugin extends BasePlugin {
    * @throws \Twig_Error_Runtime
    * @throws \Twig_Error_Syntax
    */
-  public static function scaffold(Event $event) {
-    $handler = new Handler($event->getComposer(), $event->getIO());
-    $handler->setupLakeDropsProject($event);
+  public function postCreateProject(Event $event) {
+    /** @var Handler $handler */
+    $handler = $this->handler;
+    $handler
+      ->setEvent($event)
+      ->setupLakeDropsProject();
   }
 
 }
diff --git a/src/ScaffoldCommand.php b/src/ScaffoldCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..c5037b6b399491955402549728b13695b537956f
--- /dev/null
+++ b/src/ScaffoldCommand.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace LakeDrops\Drupal8ScaffoldDeveloper;
+
+use LakeDrops\Component\Composer\BaseCommand;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ScaffoldCommand extends BaseCommand {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function configure() {
+    $this->setName('lakedrops:scaffold');
+    $this->setDescription('(Re-)Configure this project for developers.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getHandlerClass() {
+    return Handler::class;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @throws \Twig_Error_Loader
+   * @throws \Twig_Error_Runtime
+   * @throws \Twig_Error_Syntax
+   */
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    parent::execute($input, $output);
+    /** @var Handler $handler */
+    $handler = $this->handler;
+    $handler->setupLakeDropsProject();
+  }
+
+}