diff --git a/modules/salesforce_mapping/src/Commands/SalesforceMappingCommands.php b/modules/salesforce_mapping/src/Commands/SalesforceMappingCommands.php
index 288af9a0c96c9eb5a791100c3b1edf5276e29266..7be495ee95b67c03dbac46e9a89d5e8dc880fbab 100644
--- a/modules/salesforce_mapping/src/Commands/SalesforceMappingCommands.php
+++ b/modules/salesforce_mapping/src/Commands/SalesforceMappingCommands.php
@@ -5,7 +5,6 @@ namespace Drupal\salesforce_mapping\Commands;
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\salesforce\Commands\SalesforceCommandsBase;
 use Drupal\salesforce\Rest\RestClient;
 use Drupal\salesforce\SelectQuery;
 use Drush\Exceptions\UserAbortException;
@@ -23,7 +22,7 @@ use Symfony\Component\Console\Output\Output;
  *   - http://cgit.drupalcode.org/devel/tree/src/Commands/DevelCommands.php
  *   - http://cgit.drupalcode.org/devel/tree/drush.services.yml
  */
-class SalesforceMappingCommands extends SalesforceCommandsBase {
+class SalesforceMappingCommands extends SalesforceMappingCommandsBase {
 
   protected $salesforceConfig;
   protected $database;
diff --git a/modules/salesforce_mapping/src/Commands/SalesforceMappingCommandsBase.php b/modules/salesforce_mapping/src/Commands/SalesforceMappingCommandsBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..f544e070055c1b3e6ec3408ab9684fc97bd6ef1d
--- /dev/null
+++ b/modules/salesforce_mapping/src/Commands/SalesforceMappingCommandsBase.php
@@ -0,0 +1,209 @@
+<?php
+
+namespace Drupal\salesforce_mapping\Commands;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\salesforce\Rest\RestClient;
+use Drush\Drush;
+use Drush\Exceptions\UserAbortException;
+use Symfony\Component\Console\Input\Input;
+use Symfony\Component\Console\Output\Output;
+use Drupal\salesforce\Commands\SalesforceCommandsBase;
+use Drupal\salesforce\Commands\QueryResult;
+use Drupal\salesforce\Commands\QueryResultTableFormatter;
+
+/**
+ * Shared command base for Salesforce Drush commands.
+ */
+abstract class SalesforceMappingCommandsBase extends SalesforceCommandsBase {
+
+  /**
+   * Salesforce Mapping storage handler.
+   *
+   * @var \Drupal\salesforce_mapping\SalesforceMappingStorage
+   */
+  protected $mappingStorage;
+
+  /**
+   * Mapped Object storage handler.
+   *
+   * @var \Drupal\salesforce_mapping\MappedObjectStorage
+   */
+  protected $mappedObjectStorage;
+
+  /**
+   * SalesforceMappingCommandsBase constructor.
+   *
+   * @param \Drupal\salesforce\Rest\RestClient $client
+   *   SF client.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $etm
+   *   Entity type manager.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  public function __construct(RestClient $client, EntityTypeManagerInterface $etm) {
+    parent::__construct($client, $etm);
+
+    $this->mappingStorage = $etm->getStorage('salesforce_mapping');
+    $this->mappedObjectStorage = $etm->getStorage('salesforce_mapped_object');
+  }
+
+  /**
+   * Collect a salesforce mapping interactively.
+   */
+  protected function interactMapping(Input $input, Output $output, $message = 'Choose a Salesforce mapping', $allOption = FALSE, $dir = NULL) {
+    if ($name = $input->getArgument('name')) {
+      if (strtoupper($name) == 'ALL') {
+        $input->setArgument('name', 'ALL');
+        return;
+      }
+      /** @var \Drupal\salesforce_mapping\Entity\SalesforceMapping $mapping */
+      $mapping = $this->mappingStorage->load($name);
+      if (!$mapping) {
+        $this->logger()->error(dt('Mapping %name does not exist.', ['%name' => $name]));
+      }
+      elseif ($dir == 'push' && !$mapping->doesPush()) {
+        $this->logger()->error(dt('Mapping %name does not push.', ['%name' => $name]));
+      }
+      elseif ($dir == 'pull' && !$mapping->doesPull()) {
+        $this->logger()->error(dt('Mapping %name does not push.', ['%name' => $name]));
+      }
+      else {
+        return;
+      }
+    }
+    if ($dir == 'pull') {
+      $options = $this->mappingStorage->loadPullMappings();
+    }
+    elseif ($dir == 'push') {
+      $options = $this->mappingStorage->loadPushMappings();
+    }
+    else {
+      $options = $this->mappingStorage->loadMultiple();
+    }
+    $this->doMappingNameOptions($input, array_keys($options), $message, $allOption);
+
+  }
+
+  /**
+   * Collect a salesforce mapping name, and set it to a "name" argument.
+   */
+  protected function interactPushMappings(Input $input, Output $output, $message = 'Choose a Salesforce mapping', $allOption = FALSE) {
+    return $this->interactMapping($input, $output, $message, $allOption, 'push');
+  }
+
+  /**
+   * Collect a salesforce mapping name, and set it to a "name" argument.
+   */
+  protected function interactPullMappings(Input $input, Output $output, $message = 'Choose a Salesforce mapping', $allOption = FALSE) {
+    return $this->interactMapping($input, $output, $message, $allOption, 'pull');
+  }
+
+  /**
+   * Helper method to collect the choice from user, given a set of options.
+   */
+  protected function doMappingNameOptions(Input $input, array $options, $message, $allOption = FALSE) {
+    $options = array_combine($options, $options);
+    if ($allOption) {
+      $options['ALL'] = $allOption;
+    }
+    if (!$answer = $this->io()->choice($message, $options)) {
+      throw new UserAbortException();
+    }
+    $input->setArgument('name', $answer);
+  }
+
+  /**
+   * Given a mapping name (and optional direction), get an array of mappings.
+   *
+   * @param string $name
+   *   'ALL' to load all mappings, or a mapping id.
+   * @param string $dir
+   *   'push'|'pull'|NULL to load limit mappings by push or pull types.
+   *
+   * @return \Drupal\salesforce_mapping\Entity\SalesforceMappingInterface[]
+   *   The mappings.
+   */
+  protected function getMappingsFromName($name, $dir = NULL) {
+    $mappings = [];
+    if ($name == 'ALL') {
+      if ($dir == 'pull') {
+        $mappings = $this->mappingStorage->loadPullMappings();
+      }
+      elseif ($dir == 'push') {
+        $mappings = $this->mappingStorage->loadPushMappings();
+      }
+      else {
+        $mappings = $this->mappingStorage->loadMultiple();
+      }
+    }
+    else {
+      $mapping = $this->mappingStorage->load($name);
+      if ($dir == 'push' && !$mapping->doesPush()) {
+        throw new \Exception(dt("Mapping !name does not push.", ['!name' => $name]));
+      }
+      elseif ($dir == 'pull' && !$mapping->doesPull()) {
+        throw new \Exception(dt("Mapping !name does not pull.", ['!name' => $name]));
+      }
+      $mappings = [$mapping];
+    }
+    $mappings = array_filter($mappings);
+    if (empty($mappings)) {
+      if ($dir == 'push') {
+        throw new \Exception(dt('No push mappings loaded'));
+      }
+      if ($dir == 'pull') {
+        throw new \Exception(dt('No pull mappings loaded'));
+      }
+    }
+    return $mappings;
+  }
+
+  /**
+   * Given a mapping name, get an array of matching push mappings.
+   *
+   * @param string $name
+   *   The mapping name.
+   *
+   * @return \Drupal\salesforce_mapping\Entity\SalesforceMappingInterface[]
+   *   The matching mappings.
+   *
+   * @throws \Exception
+   */
+  protected function getPushMappingsFromName($name) {
+    return $this->getMappingsFromName($name, 'push');
+  }
+
+  /**
+   * Given a mappin gname, get an array of matching pull mappings.
+   *
+   * @param string $name
+   *   The mapping name.
+   *
+   * @return \Drupal\salesforce_mapping\Entity\SalesforceMappingInterface[]
+   *   The pull mappings.
+   *
+   * @throws \Exception
+   */
+  protected function getPullMappingsFromName($name) {
+    return $this->getMappingsFromName($name, 'pull');
+  }
+
+  /**
+   * Pass-through helper to add appropriate formatters for a query result.
+   *
+   * @param \Drupal\salesforce\Commands\QueryResult $query
+   *   The query result.
+   *
+   * @return \Drupal\salesforce\Commands\QueryResult
+   *   The same, unchanged query result.
+   */
+  protected function returnQueryResult(QueryResult $query) {
+    $formatter = new QueryResultTableFormatter();
+    $formatterManager = Drush::getContainer()->get('formatterManager');
+    $formatterManager->addFormatter('table', $formatter);
+    return $query;
+  }
+
+}
diff --git a/modules/salesforce_pull/src/Commands/SalesforcePullCommands.php b/modules/salesforce_pull/src/Commands/SalesforcePullCommands.php
index 2b180bbbbbd4f422d5eee8d0c78a5615405a8113..1c21518cbe7001b4cfeaffc215abf27529664af7 100644
--- a/modules/salesforce_pull/src/Commands/SalesforcePullCommands.php
+++ b/modules/salesforce_pull/src/Commands/SalesforcePullCommands.php
@@ -4,7 +4,7 @@ namespace Drupal\salesforce_pull\Commands;
 
 use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\salesforce\Commands\SalesforceCommandsBase;
+use Drupal\salesforce_mapping\Commands\SalesforceMappingCommandsBase;
 use Drupal\salesforce\Event\SalesforceEvents;
 use Drupal\salesforce\Rest\RestClient;
 use Drupal\salesforce\SFID;
@@ -24,7 +24,7 @@ use Symfony\Component\Console\Output\Output;
  *   - http://cgit.drupalcode.org/devel/tree/src/Commands/DevelCommands.php
  *   - http://cgit.drupalcode.org/devel/tree/drush.services.yml
  */
-class SalesforcePullCommands extends SalesforceCommandsBase {
+class SalesforcePullCommands extends SalesforceMappingCommandsBase {
 
   /**
    * Pull queue handler service.
diff --git a/modules/salesforce_push/src/Commands/SalesforcePushCommands.php b/modules/salesforce_push/src/Commands/SalesforcePushCommands.php
index 829ddfee073c92c3965f8cb7b0b12416bfa94298..458fdb884e8c0fe637b748e62bbf3ed90e7be92d 100644
--- a/modules/salesforce_push/src/Commands/SalesforcePushCommands.php
+++ b/modules/salesforce_push/src/Commands/SalesforcePushCommands.php
@@ -4,7 +4,7 @@ namespace Drupal\salesforce_push\Commands;
 
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\salesforce\Commands\SalesforceCommandsBase;
+use Drupal\salesforce_mapping\Commands\SalesforceMappingCommandsBase;
 use Drupal\salesforce\Rest\RestClient;
 use Drupal\salesforce_push\PushQueue;
 use Symfony\Component\Console\Input\Input;
@@ -21,7 +21,7 @@ use Symfony\Component\Console\Output\Output;
  *   - http://cgit.drupalcode.org/devel/tree/src/Commands/DevelCommands.php
  *   - http://cgit.drupalcode.org/devel/tree/drush.services.yml
  */
-class SalesforcePushCommands extends SalesforceCommandsBase {
+class SalesforcePushCommands extends SalesforceMappingCommandsBase {
 
   /**
    * Database service.
diff --git a/src/Commands/SalesforceCommands.php b/src/Commands/SalesforceCommands.php
index 8ca48d6bdc4e2ad4c88fc95e0e29678fb51e80cd..22fb0df284453e207892a8f234df49ce7a13db07 100644
--- a/src/Commands/SalesforceCommands.php
+++ b/src/Commands/SalesforceCommands.php
@@ -595,7 +595,7 @@ class SalesforceCommands extends SalesforceCommandsBase {
    * @return \Drupal\salesforce\Commands\QueryResult
    *   The query result.
    *
-   * @command salesforcef:query-object
+   * @command salesforce:query-object
    * @aliases sfqo,sf-query-object
    */
   public function queryObject($object, array $options = [
diff --git a/src/Commands/SalesforceCommandsBase.php b/src/Commands/SalesforceCommandsBase.php
index 243e6814cda8e665445381b4da48bd8ecb54d5f1..79403e882727813dc21f526e2728095cd8cde8b9 100644
--- a/src/Commands/SalesforceCommandsBase.php
+++ b/src/Commands/SalesforceCommandsBase.php
@@ -29,20 +29,6 @@ abstract class SalesforceCommandsBase extends DrushCommands {
    */
   protected $etm;
 
-  /**
-   * Salesforce Mapping storage handler.
-   *
-   * @var \Drupal\salesforce_mapping\SalesforceMappingStorage
-   */
-  protected $mappingStorage;
-
-  /**
-   * Mapped Object storage handler.
-   *
-   * @var \Drupal\salesforce_mapping\MappedObjectStorage
-   */
-  protected $mappedObjectStorage;
-
   /**
    * SalesforceCommandsBase constructor.
    *
@@ -57,8 +43,6 @@ abstract class SalesforceCommandsBase extends DrushCommands {
   public function __construct(RestClient $client, EntityTypeManagerInterface $etm) {
     $this->client = $client;
     $this->etm = $etm;
-    $this->mappingStorage = $etm->getStorage('salesforce_mapping');
-    $this->mappedObjectStorage = $etm->getStorage('salesforce_mapped_object');
   }
 
   /**
@@ -77,142 +61,6 @@ abstract class SalesforceCommandsBase extends DrushCommands {
     }
   }
 
-  /**
-   * Collect a salesforce mapping interactively.
-   */
-  protected function interactMapping(Input $input, Output $output, $message = 'Choose a Salesforce mapping', $allOption = FALSE, $dir = NULL) {
-    if ($name = $input->getArgument('name')) {
-      if (strtoupper($name) == 'ALL') {
-        $input->setArgument('name', 'ALL');
-        return;
-      }
-      /** @var \Drupal\salesforce_mapping\Entity\SalesforceMapping $mapping */
-      $mapping = $this->mappingStorage->load($name);
-      if (!$mapping) {
-        $this->logger()->error(dt('Mapping %name does not exist.', ['%name' => $name]));
-      }
-      elseif ($dir == 'push' && !$mapping->doesPush()) {
-        $this->logger()->error(dt('Mapping %name does not push.', ['%name' => $name]));
-      }
-      elseif ($dir == 'pull' && !$mapping->doesPull()) {
-        $this->logger()->error(dt('Mapping %name does not push.', ['%name' => $name]));
-      }
-      else {
-        return;
-      }
-    }
-    if ($dir == 'pull') {
-      $options = $this->mappingStorage->loadPullMappings();
-    }
-    elseif ($dir == 'push') {
-      $options = $this->mappingStorage->loadPushMappings();
-    }
-    else {
-      $options = $this->mappingStorage->loadMultiple();
-    }
-    $this->doMappingNameOptions($input, array_keys($options), $message, $allOption);
-
-  }
-
-  /**
-   * Collect a salesforce mapping name, and set it to a "name" argument.
-   */
-  protected function interactPushMappings(Input $input, Output $output, $message = 'Choose a Salesforce mapping', $allOption = FALSE) {
-    return $this->interactMapping($input, $output, $message, $allOption, 'push');
-  }
-
-  /**
-   * Collect a salesforce mapping name, and set it to a "name" argument.
-   */
-  protected function interactPullMappings(Input $input, Output $output, $message = 'Choose a Salesforce mapping', $allOption = FALSE) {
-    return $this->interactMapping($input, $output, $message, $allOption, 'pull');
-  }
-
-  /**
-   * Helper method to collect the choice from user, given a set of options.
-   */
-  protected function doMappingNameOptions(Input $input, array $options, $message, $allOption = FALSE) {
-    $options = array_combine($options, $options);
-    if ($allOption) {
-      $options['ALL'] = $allOption;
-    }
-    if (!$answer = $this->io()->choice($message, $options)) {
-      throw new UserAbortException();
-    }
-    $input->setArgument('name', $answer);
-  }
-
-  /**
-   * Given a mapping name (and optional direction), get an array of mappings.
-   *
-   * @param string $name
-   *   'ALL' to load all mappings, or a mapping id.
-   * @param string $dir
-   *   'push'|'pull'|NULL to load limit mappings by push or pull types.
-   *
-   * @return \Drupal\salesforce_mapping\Entity\SalesforceMappingInterface[]
-   *   The mappings.
-   */
-  protected function getMappingsFromName($name, $dir = NULL) {
-    $mappings = [];
-    if ($name == 'ALL') {
-      if ($dir == 'pull') {
-        $mappings = $this->mappingStorage->loadPullMappings();
-      }
-      elseif ($dir == 'push') {
-        $mappings = $this->mappingStorage->loadPushMappings();
-      }
-      else {
-        $mappings = $this->mappingStorage->loadMultiple();
-      }
-    }
-    else {
-      $mapping = $this->mappingStorage->load($name);
-      if ($dir == 'push' && !$mapping->doesPush()) {
-        throw new \Exception(dt("Mapping !name does not push.", ['!name' => $name]));
-      }
-      elseif ($dir == 'pull' && !$mapping->doesPull()) {
-        throw new \Exception(dt("Mapping !name does not pull.", ['!name' => $name]));
-      }
-      $mappings = [$mapping];
-    }
-    $mappings = array_filter($mappings);
-    if (empty($mappings)) {
-      throw new \Exception(dt('No push mappings loaded'));
-    }
-    return $mappings;
-  }
-
-  /**
-   * Given a mapping name, get an array of matching push mappings.
-   *
-   * @param string $name
-   *   The mapping name.
-   *
-   * @return \Drupal\salesforce_mapping\Entity\SalesforceMapping[]
-   *   The matching mappings.
-   *
-   * @throws \Exception
-   */
-  protected function getPushMappingsFromName($name) {
-    return $this->getMappingsFromName($name, 'push');
-  }
-
-  /**
-   * Given a mappin gname, get an array of matching pull mappings.
-   *
-   * @param string $name
-   *   The mapping name.
-   *
-   * @return \Drupal\salesforce_mapping\Entity\SalesforceMapping[]
-   *   The pull mappings.
-   *
-   * @throws \Exception
-   */
-  protected function getPullMappingsFromName($name) {
-    return $this->getMappingsFromName($name, 'pull');
-  }
-
   /**
    * Pass-through helper to add appropriate formatters for a query result.
    *