diff --git a/.ahoy.yml b/.ahoy.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b40d4b4e6d2b666356e875ca95a4989d9071f480
--- /dev/null
+++ b/.ahoy.yml
@@ -0,0 +1,6 @@
+ahoyapi: v2
+commands:
+  theme:
+    imports:
+      - ahoy.yml
+    usage: Drupal SASS Theme
diff --git a/composer.json b/composer.json
index da4c1d3515dd25abdff0ae1538cceb68a582c82d..7bf2b0aeaf3c61305fde2d7ada9c1e7639d7b4f4 100644
--- a/composer.json
+++ b/composer.json
@@ -61,14 +61,12 @@
         "drupal/bootstrap": "^3.6",
         "drupal/chosen": "^2.4",
         "harvesthq/chosen": "1.8.7",
-        "lakedrops/composer-json-utils": "^1.1.0",
-        "lakedrops/dotenv": "^1.0.0",
+        "lakedrops/composer-json-utils": "^1.7||dev-master",
         "matthiasmullie/path-converter": "^1.0"
     },
     "require-dev": {
         "roave/security-advisories": "dev-master",
         "composer/composer": "^1||^2",
-        "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3",
         "drupal/coder": "^8.2",
         "phpunit/phpunit": "^8.4"
     },
@@ -80,14 +78,6 @@
         }
     },
     "extra": {
-        "class": "LakeDrops\\Drupal8Theme\\SASS\\Plugin",
-        "lakedrops": {
-            "ahoy": {
-                "theme": {
-                    "usage": "Drupal SASS Theme",
-                    "imports": ["ahoy.yml"]
-                }
-            }
-        }
+        "class": "LakeDrops\\Drupal8Theme\\SASS\\Plugin"
     }
 }
diff --git a/src/Handler.php b/src/Handler.php
index 50468bc17c66c39f14bfb69346fdd7f119f4a26f..5a44cca32a570b1d826ed1075e0b255fc74dd1b4 100644
--- a/src/Handler.php
+++ b/src/Handler.php
@@ -4,21 +4,12 @@ namespace LakeDrops\Drupal8Theme\SASS;
 
 use Composer\Installer\InstallationManager;
 use Composer\Package\Package;
-use Composer\Composer;
-use Composer\IO\IOInterface;
 use Exception;
 use LakeDrops\Component\Composer\BaseHandler;
-use LakeDrops\Component\Composer\Utils;
-use LakeDrops\Component\Dotenv\Dotenv;
 use MatthiasMullie\PathConverter\Converter;
 use RuntimeException;
 use Symfony\Component\Filesystem\Filesystem;
 use Symfony\Component\Finder\Finder;
-use Twig_Environment;
-use Twig_Error_Loader;
-use Twig_Error_Runtime;
-use Twig_Error_Syntax;
-use Twig_Loader_Array;
 
 /**
  * Class Handler.
@@ -28,57 +19,106 @@ use Twig_Loader_Array;
 class Handler extends BaseHandler {
 
   /**
-   * The Twig loader array.
-   *
-   * @var Twig_Loader_Array
-   */
-  protected $twigLoader;
-
-  /**
-   * The Twig Environment.
+   * Whether to update (TRUE) or initially installing (FALSE).
    *
-   * @var Twig_Environment
+   * @var bool
    */
-  protected $twig;
+  protected $update = FALSE;
 
   /**
-   * The options array for this installation.
+   * Whether to reset the theme.
    *
-   * @var array
+   * @var bool
    */
-  protected $options;
+  protected $reset = FALSE;
 
   /**
-   * Whether to update (TRUE) or initially installing (FALSE).
+   * Whether to overwrite existing files.
    *
    * @var bool
    */
-  protected $update;
+  protected $overwrite = FALSE;
 
   /**
-   * Whether to reset the theme.
-   *
-   * @var bool
+   * {@inheritdoc}
    */
-  protected $reset;
+  public function configId(): string {
+    return 'theme-d8-sass';
+  }
 
   /**
-   * Whether to overwrite existing files.
-   *
-   * @var bool
+   * {@inheritdoc}
    */
-  protected $overwrite;
+  protected function configDefault(): array {
+    return [
+      'base_theme' => [
+        'name' => 'bartik',
+        'import' => [],
+      ],
+      'bower_assets' => [],
+    ];
+  }
 
   /**
    * {@inheritdoc}
    */
-  public function __construct(Composer $composer, IOInterface $io) {
-    parent::__construct($composer, $io);
-    $this->twigLoader = new Twig_Loader_Array([]);
-    $this->twig = new Twig_Environment($this->twigLoader);
-    $this->update = FALSE;
-    $this->reset = FALSE;
-    $this->overwrite = FALSE;
+  protected function postInit(): void {
+
+    // If we don't have a project name by now, lets get some magic at work.
+    $projectName = $this->config->readValue('project_name');
+    if ($projectName === NULL) {
+      $defaultName = str_replace([' ', '-', '_', '.'], '', basename(getcwd())) . '_theme';
+      $projectName = $this->env->receive('name', 'Name of custom theme', $defaultName);
+      $this->config->setValue(['project_name' => $projectName]);
+    }
+
+    $fs = new Filesystem();
+
+    $dependencies = [];
+    $jsFiles = [];
+    $to = $this->buildInstallationPath($projectName);
+    $bowerAssets = $this->config->readValue('bower_assets') ?? [];
+    foreach ($bowerAssets as $key => $asset) {
+      $package = $this->getPackage('bower-asset/' . $key);
+      $version = $package ? $package->getVersion() : ($asset['version'] ?? 'latest');
+      if ($package) {
+        // Installed globally as Composer package.
+        $from = $this->getInstallationManager()->getInstallPath($package);
+        if (!$fs->exists($from)) {
+          // Global package is missing.
+          continue;
+        }
+      }
+      else {
+        // Will be installed locally via Bower.
+        $from = implode(DIRECTORY_SEPARATOR, [$to, 'bower_components', $key]);
+        $dependencies[] = '"' . $key . '": "' . $version . '"';
+      }
+      $converter = new Converter($from, $to);
+      foreach (['fonts', 'sass', 'js'] as $type) {
+        if (isset($asset[$type])) {
+          $bowerAssets[$key][$type]['relative'] = $converter->convert($asset[$type]['src']);
+          if ($type === 'js') {
+            $jsFiles[$asset['name']] = [];
+            $finder = new Finder();
+            $finder->ignoreDotFiles(FALSE);
+            $finder->files()->in($from . DIRECTORY_SEPARATOR . $asset[$type]['src']);
+            foreach ($finder as $file) {
+              $jsFiles[$asset['name']][] = $file->getRelativePathname();
+            }
+          }
+          if ($this->reset && in_array($type, ['fonts', 'js'])) {
+            $path = implode(DIRECTORY_SEPARATOR, [$to, $type, $asset['name']]);
+            if ($fs->exists($path)) {
+              $fs->remove($path);
+            }
+          }
+        }
+      }
+    }
+    $bowerAssets['dependencies'] = implode(",\n    ", $dependencies);
+    $bowerAssets['jsfiles'] = $jsFiles;
+    $this->config->setValue(['bower_assets' => $bowerAssets]);
   }
 
   /**
@@ -89,7 +129,7 @@ class Handler extends BaseHandler {
    *
    * @return $this
    */
-  public function setUpdate($flag): self {
+  public function setUpdate(bool $flag): self {
     $this->update = $flag;
     return $this;
   }
@@ -102,7 +142,7 @@ class Handler extends BaseHandler {
    *
    * @return $this
    */
-  public function setReset($flag): self {
+  public function setReset(bool $flag): self {
     $this->reset = $flag;
     return $this;
   }
@@ -115,7 +155,7 @@ class Handler extends BaseHandler {
    *
    * @return $this
    */
-  public function setOverwrite($flag): self {
+  public function setOverwrite(bool $flag): self {
     $this->overwrite = $flag;
     return $this;
   }
@@ -144,7 +184,7 @@ class Handler extends BaseHandler {
    * @return string
    *   The installation path for the named package.
    */
-  protected function buildInstallationPath($name): string {
+  protected function buildInstallationPath(string $name): string {
     $package = new Package($name, '1.0.0', '1.0.0');
     $package->setType('drupal-custom-theme');
     return $this->getInstallationManager()
@@ -161,106 +201,11 @@ class Handler extends BaseHandler {
     static $installationPath;
 
     if (!isset($installationPath)) {
-      $installationPath = $this->buildInstallationPath($this->getOption('project_name'));
+      $installationPath = $this->buildInstallationPath($this->config->readValue('project_name'));
     }
     return $installationPath;
   }
 
-  /**
-   * Retrieve configuration for this package.
-   *
-   * @return array
-   *   The settings from the extra configuration.
-   */
-  protected function getOptions(): array {
-    if (!isset($this->options)) {
-      $extra = $this->composer->getPackage()
-        ->getExtra() + ['theme-d8-sass' => []];
-      $this->options = array_replace_recursive([
-        'base_theme' => [
-          'name' => 'bartik',
-          'import' => [],
-        ],
-        'bower_assets' => [],
-      ], $extra['theme-d8-sass']);
-
-      // If we don't have a project name by now, lets get some magic at work.
-      if (empty($this->options['project_name'])) {
-        $defaultName = str_replace([' ', '-', '_', '.'], '', basename(getcwd())) . '_theme';
-        $env = new Dotenv('custom_theme', $this->io);
-        $this->options['project_name'] = $env->receive('name', 'Name of custom theme', $defaultName);
-      }
-
-      // Update composer.jon if necessary.
-      $composerUtils = new Utils($this->composer);
-      $composerUtils
-        ->setSubSection('extra', 'theme-d8-sass', $this->options)
-        ->write();
-
-      $fs = new Filesystem();
-
-      $dependencies = [];
-      $jsFiles = [];
-      $to = $this->buildInstallationPath($this->options['project_name']);
-      foreach ($this->options['bower_assets'] as $key => $asset) {
-        $package = $this->getPackage('bower-asset/' . $key);
-        $version = $package ? $package->getVersion() : ($asset['version'] ?? 'latest');
-        if ($package) {
-          // Installed globally as Composer package.
-          $from = $this->getInstallationManager()->getInstallPath($package);
-          if (!$fs->exists($from)) {
-            // Global package is missing.
-            unset($this->options['bower_assets'][$key]);
-            continue;
-          }
-        }
-        else {
-          // Will be installed locally via Bower.
-          $from = implode(DIRECTORY_SEPARATOR, [$to, 'bower_components', $key]);
-          $dependencies[] = '"' . $key . '": "' . $version . '"';
-        }
-        $converter = new Converter($from, $to);
-        foreach (['fonts', 'sass', 'js'] as $type) {
-          if (isset($asset[$type])) {
-            $this->options['bower_assets'][$key][$type]['relative'] = $converter->convert($asset[$type]['src']);
-            if ($type === 'js') {
-              $jsFiles[$asset['name']] = [];
-              $finder = new Finder();
-              $finder->ignoreDotFiles(FALSE);
-              $finder->files()->in($from . DIRECTORY_SEPARATOR . $asset[$type]['src']);
-              foreach ($finder as $file) {
-                $jsFiles[$asset['name']][] = $file->getRelativePathname();
-              }
-            }
-            if ($this->reset && in_array($type, ['fonts', 'js'])) {
-              $path = implode(DIRECTORY_SEPARATOR, [$to, $type, $asset['name']]);
-              if ($fs->exists($path)) {
-                $fs->remove($path);
-              }
-            }
-          }
-        }
-      }
-      $this->options['bower_assets']['dependencies'] = implode(",\n    ", $dependencies);
-      $this->options['bower_assets']['jsfiles'] = $jsFiles;
-    }
-    return $this->options;
-  }
-
-  /**
-   * Get a specific option.
-   *
-   * @param string $key
-   *   The key of the option to retrieve.
-   *
-   * @return string|array
-   *   The option.
-   */
-  protected function getOption($key) {
-    $options = $this->getOptions();
-    return $options[$key];
-  }
-
   /**
    * Get the list of custom files.
    *
@@ -293,7 +238,7 @@ class Handler extends BaseHandler {
    *   The file list.
    */
   protected function getIgnoredFiles(): array {
-    return array_merge((empty($this->getOption('bower_assets')['dependencies']) ? ['bower.json'] : []), [
+    return array_merge((empty($this->config->readValue('bower_assets')['dependencies']) ? ['bower.json'] : []), [
       'templates/README.md',
       'README.md',
       '{{ project_name }}.starterkit.yml',
@@ -323,10 +268,10 @@ class Handler extends BaseHandler {
    * @return string
    *   The tweaked string.
    */
-  protected function tweaks($string): string {
+  protected function tweaks(string $string): string {
     $string = str_replace('THEMENAME', '{{ project_name }}', $string);
 
-    $baseTheme = $this->getOption('base_theme');
+    $baseTheme = $this->config->readValue('base_theme');
     if (isset($baseTheme['sasspath']) && strpos($string, $baseTheme['sasspath']) === 0) {
       $string = 'sass' . substr($string, strlen($baseTheme['sasspath']));
     }
@@ -342,18 +287,14 @@ class Handler extends BaseHandler {
    *
    * @return string
    *   The processed string.
-   * @throws Twig_Error_Loader
-   * @throws Twig_Error_Runtime
-   * @throws Twig_Error_Syntax
    */
-  protected function twig($string): string {
+  protected function twig(string $string): string {
+    $this->init();
     if (empty($string)) {
       return '';
     }
 
-    $this->twigLoader->setTemplate('fixed', $this->tweaks($string));
-    /** @noinspection PhpTemplateMissingInspection */
-    return str_replace('"', '"', $this->twig->render('fixed', $this->getOptions()));
+    return str_replace('"', '"', $this->config->render('fixed', $this->tweaks($string)));
   }
 
   /**
@@ -362,9 +303,9 @@ class Handler extends BaseHandler {
    * @param string $command
    *   NPM command name, arguments and/or options.
    *
-   * @throws RuntimeException
+   * @throws \RuntimeException
    */
-  protected function npm($command) {
+  protected function npm(string $command): void {
     passthru(escapeshellcmd('npm ' . $command), $exit_code);
     if ($exit_code !== 0) {
       throw new RuntimeException('NPM returned a non-zero exit code');
@@ -377,9 +318,9 @@ class Handler extends BaseHandler {
    * @param string $command
    *   Bower command name, arguments and/or options.
    *
-   * @throws RuntimeException
+   * @throws \RuntimeException
    */
-  protected function bower($command) {
+  protected function bower(string $command): void {
     passthru(escapeshellcmd('bower --allow-root ' . $command), $exit_code);
     if ($exit_code !== 0) {
       throw new RuntimeException('Bower returned a non-zero exit code');
@@ -392,9 +333,9 @@ class Handler extends BaseHandler {
    * @param string $command
    *   Gulp command name, arguments and/or options.
    *
-   * @throws RuntimeException
+   * @throws \RuntimeException
    */
-  protected function gulp($command) {
+  protected function gulp(string $command): void {
     passthru(escapeshellcmd('node_modules/.bin/gulp ' . $command), $exit_code);
     if ($exit_code !== 0) {
       throw new RuntimeException('Gulp returned a non-zero exit code');
@@ -413,11 +354,8 @@ class Handler extends BaseHandler {
    *
    * @return array
    *   List of processed file names.
-   * @throws Twig_Error_Loader
-   * @throws Twig_Error_Runtime
-   * @throws Twig_Error_Syntax
    */
-  protected function copyFiles(FileSystem $fs, $source, $destination): array {
+  protected function copyFiles(FileSystem $fs, string $source, string $destination): array {
     $files = [];
 
     // Make sure all directories do exist.
@@ -476,7 +414,8 @@ class Handler extends BaseHandler {
    *
    * @throws Exception
    */
-  public function updateTheme() {
+  public function updateTheme(): void {
+    $this->init();
 
     $fs = new Filesystem();
     $destination = $this->getInstallationPath();
@@ -488,17 +427,17 @@ class Handler extends BaseHandler {
     $this->copyFiles($fs, $pluginRoot . '/templates', $destination);
 
     // Handle optional base theme.
-    $baseTheme = $this->getOption('base_theme');
+    $baseTheme = $this->config->readValue('base_theme');
     if (isset($baseTheme['package'])) {
       $baseThemePackage = $this->getPackage($baseTheme['package']);
       if ($baseThemePackage && isset($baseTheme['starterkit'])) {
         $starterkitRoot = $this->getInstallationManager()->getInstallPath($baseThemePackage) . DIRECTORY_SEPARATOR . $baseTheme['starterkit'];
-        $this->options['base_theme_files'] = $this->copyFiles($fs, $starterkitRoot, $destination);
+        $this->config->setValue(['base_theme_files' => $this->copyFiles($fs, $starterkitRoot, $destination)]);
       }
     }
 
     // Remove bower stuff if that's available globally.
-    if (empty($this->getOption('bower_assets')['dependencies'])) {
+    if ($this->config->readValue('bower_assets/dependencies') === NULL) {
       if ($fs->exists($destination . DIRECTORY_SEPARATOR . 'bower.json')) {
         $fs->remove($destination . DIRECTORY_SEPARATOR . 'bower.json');
       }
@@ -523,7 +462,7 @@ class Handler extends BaseHandler {
     }
 
     // Handle Bower if not installed globally.
-    if ($fs->exists($destination . DIRECTORY_SEPARATOR . 'bower.json') && !empty($this->getOption('bower_assets')['dependencies'])) {
+    if ($fs->exists($destination . DIRECTORY_SEPARATOR . 'bower.json') && $this->config->readValue('bower_assets/dependencies') !== NULL) {
       if ($this->update) {
         $this->bower('update');
       }
diff --git a/src/InitCommand.php b/src/InitCommand.php
index c7f4a48d4c1715f53da929e2e57fcaded2c9391a..9a0117190d765953a8da2e155f29f210948afa6b 100644
--- a/src/InitCommand.php
+++ b/src/InitCommand.php
@@ -11,7 +11,7 @@ class InitCommand extends BaseCommand {
   /**
    * {@inheritdoc}
    */
-  protected function configure() {
+  protected function configure(): void {
     parent::configure();
     $this
       ->setName('lakedrops:theme:init')
@@ -21,7 +21,7 @@ class InitCommand extends BaseCommand {
   /**
    * {@inheritdoc}
    */
-  public function getHandlerClass() {
+  public function getHandlerClass(): string {
     return Handler::class;
   }
 
@@ -30,7 +30,7 @@ class InitCommand extends BaseCommand {
    *
    * @throws \Exception
    */
-  protected function execute(InputInterface $input, OutputInterface $output) {
+  protected function execute(InputInterface $input, OutputInterface $output): int {
     parent::execute($input, $output);
     /** @var Handler $handler */
     $handler = $this->handler;
@@ -39,6 +39,7 @@ class InitCommand extends BaseCommand {
       ->setReset(FALSE)
       ->setOverwrite(FALSE)
       ->updateTheme();
+    return 0;
   }
 
 }
diff --git a/src/OverwriteCommand.php b/src/OverwriteCommand.php
index 53b7591646660669b59ed4dd3265de2499b92e28..2d87efd05699949e53c72cbc0942ebf20febeff1 100644
--- a/src/OverwriteCommand.php
+++ b/src/OverwriteCommand.php
@@ -11,7 +11,7 @@ class OverwriteCommand extends BaseCommand {
   /**
    * {@inheritdoc}
    */
-  protected function configure() {
+  protected function configure(): void {
     parent::configure();
     $this
       ->setName('lakedrops:theme:overwrite')
@@ -21,7 +21,7 @@ class OverwriteCommand extends BaseCommand {
   /**
    * {@inheritdoc}
    */
-  public function getHandlerClass() {
+  public function getHandlerClass(): string {
     return Handler::class;
   }
 
@@ -30,7 +30,7 @@ class OverwriteCommand extends BaseCommand {
    *
    * @throws \Exception
    */
-  protected function execute(InputInterface $input, OutputInterface $output) {
+  protected function execute(InputInterface $input, OutputInterface $output): int {
     parent::execute($input, $output);
     /** @var Handler $handler */
     $handler = $this->handler;
@@ -39,6 +39,7 @@ class OverwriteCommand extends BaseCommand {
       ->setReset(FALSE)
       ->setOverwrite(TRUE)
       ->updateTheme();
+    return 0;
   }
 
 }
diff --git a/src/Plugin.php b/src/Plugin.php
index c86f657065ba48f24c5b56ac119da7d032c54300..d765507613c26865cf0bbc9b1a9fdbc1ff1d1de5 100644
--- a/src/Plugin.php
+++ b/src/Plugin.php
@@ -2,6 +2,7 @@
 
 namespace LakeDrops\Drupal8Theme\SASS;
 
+use Composer\Plugin\Capability\CommandProvider;
 use Composer\Script\Event;
 use Composer\Script\ScriptEvents;
 use Exception;
@@ -15,7 +16,7 @@ class Plugin extends BasePlugin {
   /**
    * {@inheritdoc}
    */
-  public function getHandlerClass() {
+  public function getHandlerClass(): string {
     return Handler::class;
   }
 
@@ -24,7 +25,7 @@ class Plugin extends BasePlugin {
    */
   public function getCapabilities(): array {
     return [
-      \Composer\Plugin\Capability\CommandProvider::class => CommandProvider::class,
+      CommandProvider::class => CommandProvider::class,
     ];
   }
 
@@ -45,7 +46,7 @@ class Plugin extends BasePlugin {
    *   The event that triggered the plugin.
    * @throws Exception
    */
-  public function initThemeEvent(Event $event) {
+  public function initThemeEvent(Event $event): void {
     $this->updateThemeEvent($event, FALSE);
   }
 
@@ -58,7 +59,7 @@ class Plugin extends BasePlugin {
    *   Whether to update (TRUE) or initially installing (FALSE).
    * @throws Exception
    */
-  public function updateThemeEvent(Event $event, $update = TRUE) {
+  public function updateThemeEvent(Event $event, $update = TRUE): void {
     /** @var Handler $handler */
     $handler = $this->handler;
     $handler
diff --git a/src/ResetCommand.php b/src/ResetCommand.php
index 0a9bfcfe5a34aee9ddd56c07d909e0ec282f4e8a..b78c58b3e4f169014003b0f905ca12a7886c333f 100644
--- a/src/ResetCommand.php
+++ b/src/ResetCommand.php
@@ -11,7 +11,7 @@ class ResetCommand extends BaseCommand {
   /**
    * {@inheritdoc}
    */
-  protected function configure() {
+  protected function configure(): void {
     parent::configure();
     $this
       ->setName('lakedrops:theme:reset')
@@ -21,7 +21,7 @@ class ResetCommand extends BaseCommand {
   /**
    * {@inheritdoc}
    */
-  public function getHandlerClass() {
+  public function getHandlerClass(): string {
     return Handler::class;
   }
 
@@ -30,7 +30,7 @@ class ResetCommand extends BaseCommand {
    *
    * @throws \Exception
    */
-  protected function execute(InputInterface $input, OutputInterface $output) {
+  protected function execute(InputInterface $input, OutputInterface $output): int {
     parent::execute($input, $output);
     /** @var Handler $handler */
     $handler = $this->handler;
@@ -39,6 +39,7 @@ class ResetCommand extends BaseCommand {
       ->setReset(TRUE)
       ->setOverwrite(FALSE)
       ->updateTheme();
+    return 0;
   }
 
 }
diff --git a/src/UpdateCommand.php b/src/UpdateCommand.php
index 3cc7664710eff226f22641257858332b5043fb7d..9088bbca4966da3da33e10c46f10303b389dbe43 100644
--- a/src/UpdateCommand.php
+++ b/src/UpdateCommand.php
@@ -11,7 +11,7 @@ class UpdateCommand extends BaseCommand {
   /**
    * {@inheritdoc}
    */
-  protected function configure() {
+  protected function configure(): void {
     parent::configure();
     $this
       ->setName('lakedrops:theme:update')
@@ -21,7 +21,7 @@ class UpdateCommand extends BaseCommand {
   /**
    * {@inheritdoc}
    */
-  public function getHandlerClass() {
+  public function getHandlerClass(): string {
     return Handler::class;
   }
 
@@ -30,7 +30,7 @@ class UpdateCommand extends BaseCommand {
    *
    * @throws \Exception
    */
-  protected function execute(InputInterface $input, OutputInterface $output) {
+  protected function execute(InputInterface $input, OutputInterface $output): int {
     parent::execute($input, $output);
     /** @var Handler $handler */
     $handler = $this->handler;
@@ -39,6 +39,7 @@ class UpdateCommand extends BaseCommand {
       ->setReset(FALSE)
       ->setOverwrite(FALSE)
       ->updateTheme();
+    return 0;
   }
 
 }