diff --git a/.gitignore b/.gitignore
index a6a33af3b567165268354d5f14ceccb59fb60466..bf7290c8b5febf2b34f6e8c1bd96d4c5715127bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,4 +67,4 @@ eslint-report.html
 /locale/**/LC_MESSAGES
 /locale/**/*.time_stamp
 /.rspec
-/plugins/*
+/plugins/enabled/*
diff --git a/config/application.rb b/config/application.rb
index 65d63d1b450ccb62a6fa996ccedb2ca7f8bb1ce5..da00f469cc4cd72cfd88e702613bc02a35ecbaaa 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -30,7 +30,6 @@ class Application < Rails::Application
     config.eager_load_paths.push(*%W[#{config.root}/lib
                                      #{config.root}/app/models/hooks
                                      #{config.root}/app/models/members
-                                     #{config.root}/plugins
                                      #{config.root}/app/models/project_services
                                      #{config.root}/app/workers/concerns
                                      #{config.root}/app/services/concerns
diff --git a/generator_templates/plugins/template.rb b/generator_templates/plugins/template.rb
deleted file mode 100644
index 16c87f2c2b26e60662855f3bcd642ff96e44048e..0000000000000000000000000000000000000000
--- a/generator_templates/plugins/template.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# Requirements
-# * File name must end with _s.rb. For example, jenkins_plugin.rb.
-# * All code should be inside class. No code should be executed on file load.
-# * Class name must be same as file name.
-#   If file name is jenkins_plugin.rb then class name must be JenkinsPlugin.
-#
-# Reccomendations
-# * Code should not depend on or use GitLab classes and other code.
-# * Consider contributing your plugin to GitLab source code so we can test it
-#   and make sure it will work in further version.
-#
-class $NAMEPlugin
-  def execute(data)
-    # TODO: Implement me
-  end
-end
diff --git a/lib/gitlab/plugin.rb b/lib/gitlab/plugin.rb
index 9604cac4b208357bb4f4c9ca4f8fd6c8b5c22d00..1035d2589078161dd2d16d9a45649cf8f24eb971 100644
--- a/lib/gitlab/plugin.rb
+++ b/lib/gitlab/plugin.rb
@@ -1,23 +1,35 @@
 module Gitlab
   module Plugin
     def self.files
-      Dir.glob(Rails.root.join('plugins', '*_plugin.rb'))
+      Dir.glob(Rails.root.join('plugins/enabled/*'))
     end
 
     def self.execute_all_async(data)
       files.each do |file|
+        puts file
         PluginWorker.perform_async(file, data)
       end
     end
 
     def self.execute(file, data)
-      # TODO: Implement
-      #
-      # Reuse some code from gitlab-shell https://gitlab.com/gitlab-org/gitlab-shell/blob/master/lib/gitlab_custom_hook.rb#L40
-      # Pass data as STDIN (or JSON encode?)
-      #
-      # 1. Return true if 0 exit code
-      # 2. Return false if non-zero exit code
+      # Prepare the hook subprocess. Attach a pipe to its stdin, and merge
+      # both its stdout and stderr into our own stdout.
+      stdin_reader, stdin_writer = IO.pipe
+      hook_pid = spawn({}, file, in: stdin_reader, err: :out)
+      stdin_reader.close
+
+      # Submit changes to the hook via its stdin.
+      begin
+        IO.copy_stream(StringIO.new(data.to_json), stdin_writer)
+      rescue Errno::EPIPE
+        # It is not an error if the hook does not consume all of its input.
+      end
+
+      # Close the pipe to let the hook know there is no further input.
+      stdin_writer.close
+
+      Process.wait(hook_pid)
+      $?.success?
     end
   end
 end
diff --git a/lib/tasks/plugins.rake b/lib/tasks/plugins.rake
index 9c9f1fece85f1e66b043f731513c5fe24c372f1f..f4d7edb2eb25c309adc5eab6e3edaa14fe678e15 100644
--- a/lib/tasks/plugins.rake
+++ b/lib/tasks/plugins.rake
@@ -1,28 +1,4 @@
 namespace :plugins do
-  desc 'Generate skeleton for new plugin'
-  task generate: :environment do
-    ARGV.each { |a| task a.to_sym { } }
-    name = ARGV[1]
-
-    unless name.present?
-      puts 'Error. You need to specify a name for the plugin'
-      exit 1
-    end
-
-    class_name = name.classify
-    param = name.underscore
-    file_path = Rails.root.join('plugins', param + '_plugin.rb')
-    template = File.read(Rails.root.join('generator_templates', 'plugins', 'template.rb'))
-    template.gsub!('$NAME', class_name)
-
-    if File.write(file_path, template)
-      puts "Done. Your plugin saved under #{file_path}."
-      puts 'Feel free to edit it.'
-    else
-      puts "Failed to save #{file_path}."
-    end
-  end
-
   desc 'Validate existing plugins'
   task validate: :environment do
     puts 'Validating plugins from /plugins directory'
diff --git a/plugins/available/save_to_file.clj b/plugins/available/save_to_file.clj
new file mode 100755
index 0000000000000000000000000000000000000000..a59d83749d35167b8b9ca99022c8aece97323bd9
--- /dev/null
+++ b/plugins/available/save_to_file.clj
@@ -0,0 +1,3 @@
+#!/usr/bin/env clojure
+(let [in (slurp *in*)]
+  (spit "/tmp/clj-data.txt" in))
diff --git a/plugins/available/save_to_file.rb b/plugins/available/save_to_file.rb
new file mode 100755
index 0000000000000000000000000000000000000000..61b0df9bfd6e33c62c54d9eba2080672463a215e
--- /dev/null
+++ b/plugins/available/save_to_file.rb
@@ -0,0 +1,3 @@
+#!/usr/bin/env ruby
+x = STDIN.read
+File.write('/tmp/rb-data.txt', x)
diff --git a/plugins/enabled/.gitkeep b/plugins/enabled/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391