In some case WordPress theme needs to auto install plugin that is a hard dependencies to their theme, They can try to use the TGMPlugin installation class to help in notifying user that a plugin needs to be installed in order for the theme to work perfectly, But sadly the class won't provide "fully automatic"  installation process.

So is it that hard to tell WordPress to auto install a plugin from theme?

If you examine the class-wp-upgrader.php file from standard WordPress file, it is cleary defined that WordPress already given us tools to easily installing a plugin, we just need to hook into the right place and invoke the class correctly.

 

 <?php 
  // Hooking to wp after theme installation for installing our plugin
  // Place this on your theme functions.php
  add_action('after_switch_theme', 'my_install_function');
 ?>

Then when user is Activating the theme, the hook will fire and WordPress will call our custom function, In the function we will embed the custom theme installation.

 <?php
 
  // Hook callback function
  // Place this on functions.php
  function my_install_function($oldtheme) {

    $plugin = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'myplugin/myplugin.php' ;
    $pluginPath = 'mythemepath/myplugin.zip';
    $mythemename = 'my_cool_theme';

    // Determine if we REALLY need to install the plugin
    if (@file_exists($plugin)
        || $oldtheme == $mythemename) {

      return;
    }

    // Load additional wordpress file
    if (!function_exists('get_plugins')) {
      require_once ABSPATH . 'wp-admin/includes/plugin.php';
    }
    
    // Retrieve installed plugin from database
    $installed_plugins = get_plugins();

    // Do installation if requirement passed
    // You can also inject function to check for plugin version and compare it here.
    if (!isset($installed_plugins[$plugin])
        && !file_exists($plugin)
        && is_admin()
        && current_user_can('install_plugins')) {

      // Load additional files
      require_once ABSPATH . 'wp-admin/includes/template.php';
      require_once ABSPATH . 'wp-admin/includes/misc.php';
      require_once ABSPATH . 'wp-admin/includes/file.php';
      require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';

      // Invoke the wordpress upgrader class
      $upgrader = new WP_Upgrader();

      // Nuke messages
      // We probably want a "silent operation"
      $upgrader->strings['unpack_package'] = '';
      $upgrader->strings['installing_package'] = '';
      $upgrader->strings['remove_old'] = '';
      $upgrader->skin->set_upgrader($upgrader);


      // Only serves server with proper filesystem
      // installed for other method they can use
      // the TGMPlugin instead or call in the credentials system here
      $access = get_filesystem_method();

      // Silently installing the core package
      if ($access == 'direct') {
       
        // connect to filesystem to get the working temporary directory
        $res = $upgrader->fs_connect( array(WP_CONTENT_DIR, $pluginPath));

        // Unpack the zips
        $working_dir = $upgrader->unpack_package($pluginPath, false);

        // Perform the installation process
        $result = $upgrader->install_package(array(
          'source' => $working_dir,
          'destination' => WP_PLUGIN_DIR,
          'clear_destination' => true,
          'abort_if_destination_exists' => true,
          'clear_working' => true,
          'hook_extra' => array(),
        ));
      }
    }

    // Just activate the plugin if it is installed
    if (is_plugin_inactive($plugin)) {
      activate_plugin($plugin);
    }
  }

Not that hard is it?

The sample is a very simplified version of plugin installation process, it ignores the WP_Upgrader skinning system and credentials system.

You can extend this further by adding credentials, check for user who must use FTP system to add plugin, check for plugin version before attempting to install new one.....