diff --git a/ahoy.yml b/ahoy.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9f0cb9bc1a21dd57208820accd83dab6ffe73554
--- /dev/null
+++ b/ahoy.yml
@@ -0,0 +1,14 @@
+ahoyapi: v2
+commands:
+  install:
+    cmd: composer lakedrops-theme-install "$@"
+    usage: Install custom theme
+  update:
+    cmd: composer lakedrops-theme-update "$@"
+    usage: Update custom theme
+  reset:
+    cmd: composer lakedrops-theme-reset "$@"
+    usage: Reset custom theme
+  overwrite:
+    cmd: composer lakedrops-theme-overwrite "$@"
+    usage: Overwrite custom theme
diff --git a/composer.json b/composer.json
index 494a497b6224f63d7bb5a1d6638a794d183ee671..61cf3c67dcf4c98c40dbc68ffcc3e566256ae080 100644
--- a/composer.json
+++ b/composer.json
@@ -61,8 +61,8 @@
         "drupal/bootstrap": "^3.6",
         "drupal/chosen": "^2.4",
         "harvesthq/chosen": "^1.8",
-        "lakedrops/composer-json-utils": "^1.0",
-        "lakedrops/dotenv": "^1.0",
+        "lakedrops/composer-json-utils": "^1.1.0",
+        "lakedrops/dotenv": "^1.0.0",
         "matthiasmullie/path-converter": "^1.0"
     },
     "require-dev": {
@@ -98,6 +98,12 @@
                     "callback": "LakeDrops\\Drupal8Theme\\SASS\\Plugin::overwrite",
                     "description": "Update and overwrite the LakeDrops based SASS theme."
                 }
+            },
+            "ahoy": {
+                "theme": {
+                    "usage": "Drupal SASS Theme",
+                    "imports": ["ahoy.yml"]
+                }
             }
         }
     },
diff --git a/src/Handler.php b/src/Handler.php
index a7c805590e3377a6936bd719758b041eec222461..b77b3e05322de31f103029e2147d7414720033b9 100644
--- a/src/Handler.php
+++ b/src/Handler.php
@@ -6,6 +6,7 @@ use Composer\Package\Package;
 use Composer\Composer;
 use Composer\IO\IOInterface;
 use Composer\Script\Event;
+use LakeDrops\Component\Composer\BaseHandler;
 use LakeDrops\Component\Composer\Utils;
 use LakeDrops\Component\Dotenv\Dotenv;
 use MatthiasMullie\PathConverter\Converter;
@@ -17,21 +18,7 @@ use Symfony\Component\Finder\Finder;
  *
  * @package LakeDrops\Drupal8Theme\SASS
  */
-class Handler {
-
-  /**
-   * The composer object running this session.
-   *
-   * @var \Composer\Composer
-   */
-  protected $composer;
-
-  /**
-   * The input-output object for the composer session.
-   *
-   * @var \Composer\IO\IOInterface
-   */
-  protected $io;
+class Handler extends BaseHandler {
 
   /**
    * The Twig loader array.
@@ -76,27 +63,15 @@ class Handler {
   protected $overwrite;
 
   /**
-   * Handler constructor.
-   *
-   * @param \Composer\Composer $composer
-   *   The composer object.
-   * @param \Composer\IO\IOInterface $io
-   *   The input-output object.
-   * @param bool $update
-   *   Whether to update (TRUE) or initially installing (FALSE).
-   * @param bool $reset
-   *   Whether to reset the theme.
-   * @param bool $overwrite
-   *   Whether to overwrite existing files.
+   * {@inheritdoc}
    */
-  public function __construct(Composer $composer, IOInterface $io, $update = FALSE, $reset = FALSE, $overwrite = FALSE) {
-    $this->composer = $composer;
-    $this->io = $io;
+  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 = $update;
-    $this->reset = $reset;
-    $this->overwrite = $overwrite;
+    $this->update = FALSE;
+    $this->reset = FALSE;
+    $this->overwrite = FALSE;
   }
 
   /**
@@ -112,6 +87,32 @@ class Handler {
     return $this;
   }
 
+  /**
+   * Set whether to reset (TRUE) the theme or not (FALSE).
+   *
+   * @param bool $flag
+   *   The flag.
+   *
+   * @return $this
+   */
+  public function setReset($flag) {
+    $this->reset = $flag;
+    return $this;
+  }
+
+  /**
+   * Set whether to overwrite (TRUE) existing files or not (FALSE).
+   *
+   * @param bool $flag
+   *   The flag.
+   *
+   * @return $this
+   */
+  public function setOverwrite($flag) {
+    $this->overwrite = $flag;
+    return $this;
+  }
+
   /**
    * Get the composer installation manager.
    *
@@ -158,21 +159,6 @@ class Handler {
     return $installationPath;
   }
 
-  /**
-   * Retrieve a package from the current composer process.
-   *
-   * @param string $name
-   *   Name of the package to get from the current composer installation.
-   *
-   * @return \Composer\Package\PackageInterface
-   *   The package.
-   */
-  protected function getPackage($name) {
-    return $this->composer->getRepositoryManager()
-      ->getLocalRepository()
-      ->findPackage($name, '*');
-  }
-
   /**
    * Retrieve configuration for this package.
    *
@@ -230,7 +216,7 @@ class Handler {
         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') {
+            if ($type === 'js') {
               $jsFiles[$asset['name']] = [];
               $finder = new Finder();
               $finder->ignoreDotFiles(FALSE);
@@ -239,7 +225,7 @@ class Handler {
                 $jsFiles[$asset['name']][] = $file->getRelativePathname();
               }
             }
-            if ($this->reset && in_array($type, ['fonts', 'js'])) {
+            if ($this->reset && \in_array($type, ['fonts', 'js'])) {
               $path = implode(DIRECTORY_SEPARATOR, [$to, $type, $asset['name']]);
               if ($fs->exists($path)) {
                 $fs->remove($path);
@@ -351,6 +337,9 @@ class Handler {
    *
    * @return string
    *   The processed string.
+   * @throws \Twig_Error_Loader
+   * @throws \Twig_Error_Runtime
+   * @throws \Twig_Error_Syntax
    */
   protected function twig($string) {
     if (empty($string)) {
@@ -418,6 +407,9 @@ class Handler {
    *
    * @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) {
     $files = [];
@@ -439,29 +431,29 @@ class Handler {
     foreach ($finder as $file) {
 
       // Do not copy .gitkeep files.
-      if ($file->getFilename() == '.gitkeep') {
+      if ($file->getFilename() === '.gitkeep') {
         continue;
       }
 
       // Determine relative filename of destination.
       $relFilename = $file->getRelativePathname();
-      if ($file->getExtension() == 'twig') {
+      if ($file->getExtension() === 'twig') {
         $relFilename = substr($relFilename, 0, strrpos($relFilename, '.twig'));
       }
       $tweakedRelFilename = $this->tweaks($relFilename);
       $filename = $destination . DIRECTORY_SEPARATOR . $this->twig($relFilename);
 
       // Do not copy ignored files.
-      if (in_array($tweakedRelFilename, $this->getIgnoredFiles())) {
+      if (\in_array($tweakedRelFilename, $this->getIgnoredFiles(), true)) {
         continue;
       }
       // Do not update custom files.
-      if (!$this->overwrite && $fs->exists($filename) && in_array($tweakedRelFilename, $this->getCustomFiles())) {
+      if (!$this->overwrite && $fs->exists($filename) && \in_array($tweakedRelFilename, $this->getCustomFiles(), true)) {
         continue;
       }
 
       // Now create destination.
-      if (in_array($tweakedRelFilename, $this->getBinaryFiles())) {
+      if (\in_array($tweakedRelFilename, $this->getBinaryFiles(), true)) {
         $fs->copy($file->getRealPath(), $filename);
       }
       else {
@@ -478,6 +470,7 @@ class Handler {
    *
    * @param \Composer\Script\Event $event
    *   The event that triggered the plugin.
+   * @throws \Exception
    */
   public function updateTheme(Event $event) {
 
@@ -526,7 +519,7 @@ class Handler {
     }
 
     // Handle Bower if not installed globally.
-    if (!empty($this->getOption('bower_assets')['dependencies']) && $fs->exists($destination . DIRECTORY_SEPARATOR . 'bower.json')) {
+    if ($fs->exists($destination . DIRECTORY_SEPARATOR . 'bower.json') && !empty($this->getOption('bower_assets')['dependencies'])) {
       if ($this->update) {
         $this->bower('update');
       }
diff --git a/src/Plugin.php b/src/Plugin.php
index c57a4f8f678dcb9e28540cba346026c2c98f411c..ba0fa9794a959c879d17de9f1fb0023b5769df83 100644
--- a/src/Plugin.php
+++ b/src/Plugin.php
@@ -2,30 +2,20 @@
 
 namespace LakeDrops\Drupal8Theme\SASS;
 
-use Composer\Composer;
-use Composer\EventDispatcher\EventSubscriberInterface;
-use Composer\IO\IOInterface;
-use Composer\Plugin\PluginInterface;
 use Composer\Script\Event;
 use Composer\Script\ScriptEvents;
+use LakeDrops\Component\Composer\BasePlugin;
 
 /**
  * Composer plugin for handling drupal sass theme template.
  */
-class Plugin implements PluginInterface, EventSubscriberInterface {
-
-  /**
-   * The handler object for events.
-   *
-   * @var \LakeDrops\Drupal8Theme\SASS\Handler
-   */
-  protected $handler;
+class Plugin extends BasePlugin {
 
   /**
    * {@inheritdoc}
    */
-  public function activate(Composer $composer, IOInterface $io) {
-    $this->handler = new Handler($composer, $io);
+  public function getHandlerClass() {
+    return Handler::class;
   }
 
   /**
@@ -73,10 +63,15 @@ class Plugin implements PluginInterface, EventSubscriberInterface {
    *   Whether to reset the theme.
    * @param bool $overwrite
    *   Whether to overwrite existing files.
+   * @throws \Exception
    */
   public static function init(Event $event, $update = FALSE, $reset = FALSE, $overwrite = FALSE) {
-    $handler = new Handler($event->getComposer(), $event->getIO(), $update, $reset, $overwrite);
-    $handler->updateTheme($event);
+    $handler = new Handler($event->getComposer(), $event->getIO());
+    $handler
+      ->setUpdate($update)
+      ->setReset($reset)
+      ->setOverwrite($overwrite)
+      ->updateTheme($event);
   }
 
   /**
@@ -84,6 +79,7 @@ class Plugin implements PluginInterface, EventSubscriberInterface {
    *
    * @param \Composer\Script\Event $event
    *   The event that triggered the plugin.
+   * @throws \Exception
    */
   public static function update(Event $event) {
     self::init($event, TRUE);
@@ -94,6 +90,7 @@ class Plugin implements PluginInterface, EventSubscriberInterface {
    *
    * @param \Composer\Script\Event $event
    *   The event that triggered the plugin.
+   * @throws \Exception
    */
   public static function reset(Event $event) {
     self::init($event, FALSE, TRUE);
@@ -104,6 +101,7 @@ class Plugin implements PluginInterface, EventSubscriberInterface {
    *
    * @param \Composer\Script\Event $event
    *   The event that triggered the plugin.
+   * @throws \Exception
    */
   public static function overwrite(Event $event) {
     self::init($event, FALSE, FALSE, TRUE);