diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..5a07474da4b4e84d35dc54d9ba7e7290bbceea53
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,32 @@
+{
+    "name": "wpdesk/wp-builder",
+    "authors": [
+        {
+            "name": "Krzysiek",
+            "email": "krzysiek@wpdesk.pl"
+        }
+    ],
+    "require": {
+        "php": ">=5.5"
+    },
+    "require-dev": {
+        "phpunit/phpunit": "<7",
+        "wp-coding-standards/wpcs": "^0.14.1",
+        "squizlabs/php_codesniffer": "^3.0.2",
+        "mockery/mockery": "*",
+        "10up/wp_mock": "*",
+        "wimg/php-compatibility": "^8"
+    },
+    "autoload": {
+		"psr-4": {"WPDesk\\PluginBuilder\\": "src/"}
+    },
+    "autoload-dev": {
+    },
+    "scripts": {
+        "phpcs": "phpcs",
+        "phpunit-unit": "phpunit --configuration phpunit-unit.xml --coverage-text --colors=never",
+        "phpunit-unit-fast": "phpunit --configuration phpunit-unit.xml --no-coverage",
+        "phpunit-integration": "phpunit --configuration phpunit-integration.xml --coverage-text --colors=never",
+        "phpunit-integration-fast": "phpunit --configuration phpunit-integration.xml --no-coverage"
+    }
+}
diff --git a/src/Builder/AbstractBuilder.php b/src/Builder/AbstractBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..951e9a06102dc41f2f5fafb161f241cb65e5fb25
--- /dev/null
+++ b/src/Builder/AbstractBuilder.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace WPDesk\PluginBuilder\Builder;
+
+use WPDesk\PluginBuilder\Plugin\AbstractPlugin;
+
+class AbstractBuilder
+{
+    const FILTER_PLUGIN_CLASS = 'wpdesk_plugin_class';
+	const HOOK_BEFORE_PLUGIN_INIT = 'wpdesk_before_plugin_init';
+	const HOOK_AFTER_PLUGIN_INIT = 'wpdesk_before_afterinit';
+
+	protected static $instances = [];
+
+	/**
+	 * @param string $class
+	 * @param AbstractPlugin $object
+	 */
+	protected function addToStorage($class, $object) {
+		if (isset(self::$instances[$class])) {
+			throw new Exception\ClassAlreadyExists("Class {$class} already exists");
+		}
+		self::$instances[$class] = $object;
+	}
+
+	/**
+	 * @param string $class
+	 *
+	 * @return AbstractPlugin
+	 */
+	protected function getFromStorage($class) {
+		if (isset(self::$instances[$class])) {
+			return self::$instances[$class];
+		} else {
+			throw new Exception\ClassNotExists("Class {$class} not exists in storage");
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/Builder/Exception/ClassAlreadyExists.php b/src/Builder/Exception/ClassAlreadyExists.php
new file mode 100644
index 0000000000000000000000000000000000000000..2b69111bc281e6f2882ea2a0f1d2570d3867f500
--- /dev/null
+++ b/src/Builder/Exception/ClassAlreadyExists.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace WPDesk\PluginBuilder\Builder\Exception;
+
+class ClassAlreadyExists extends \RuntimeException
+{
+
+}
\ No newline at end of file
diff --git a/src/Builder/Exception/ClassNotExists.php b/src/Builder/Exception/ClassNotExists.php
new file mode 100644
index 0000000000000000000000000000000000000000..bbcfe32efed7b4946626d581d5844f25e6bfae20
--- /dev/null
+++ b/src/Builder/Exception/ClassNotExists.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace WPDesk\PluginBuilder\Builder\Exception;
+
+class ClassNotExists extends \RuntimeException
+{
+
+}
\ No newline at end of file
diff --git a/src/Builder/InfoBuilder.php b/src/Builder/InfoBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..be03ad7646ec7b1a0462c2b3c88fa7c5044c66c8
--- /dev/null
+++ b/src/Builder/InfoBuilder.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace WPDesk\PluginBuilder\Builder;
+
+use WPDesk\PluginBuilder\Plugin\AbstractPlugin;
+
+class InfoBuilder extends AbstractBuilder
+{
+    const FILTER_PLUGIN_CLASS = 'wpdesk_plugin_class';
+	const HOOK_BEFORE_PLUGIN_INIT = 'wpdesk_before_plugin_init';
+	const HOOK_AFTER_PLUGIN_INIT = 'wpdesk_before_afterinit';
+
+    /**
+     * Builds instance of plugin
+     *
+     * @return AbstractPlugin
+     */
+    public function build_from_info(\WPDesk_Plugin_Info $info)
+    {
+	    $class_name = apply_filters( self::FILTER_PLUGIN_CLASS, $info->get_class_name() );
+
+	    /** @var AbstractPlugin $plugin */
+        $plugin = new $class_name($info);
+        $this->addToStorage($info->get_class_name(), $plugin);
+
+        do_action(self::HOOK_BEFORE_PLUGIN_INIT, $plugin);
+	    $plugin->init();
+	    do_action(self::HOOK_AFTER_PLUGIN_INIT, $plugin);
+
+        return $plugin;
+    }
+
+	/**
+	 * @param string $class
+	 *
+	 * @return AbstractPlugin
+	 */
+    public function get_plugin_instance($class) {
+		return $this->getFromStorage($class);
+    }
+}
\ No newline at end of file
diff --git a/src/Plugin/AbstractPlugin.php b/src/Plugin/AbstractPlugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..1c10b1c933097346c9f73acc8a07ed799877d429
--- /dev/null
+++ b/src/Plugin/AbstractPlugin.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace WPDesk\PluginBuilder\Plugin;
+
+/**
+ * Base plugin class for WP Desk plugins
+ *
+ * @author Grzegorz
+ *
+ */
+abstract class AbstractPlugin {
+
+	/** @var \WPDesk_PluginInfo */
+    protected $plugin_info;
+
+    /** @var string */
+	protected $plugin_namespace;
+
+	/** @var string */
+	protected $plugin_url;
+
+	/** @var string */
+	protected $docs_url;
+
+	/** @var string */
+	protected $settings_url;
+	
+	/**
+	 * AbstractPlugin constructor.
+	 *
+	 * @param \WPDesk_Plugin_Info $plugin_info
+	 */
+    public function __construct( $plugin_info ) {
+		$this->plugin_info = $plugin_info;
+		$this->plugin_namespace = strtolower($plugin_info->get_class_name()); // ?? NOT SURE
+    }
+
+    public function init() {
+    	$this->init_base_variables();
+    	$this->load_plugin_text_domain();
+    	$this->hooks();
+    }
+
+    /**
+     * @return $this
+     */
+    public function get_plugin() {
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function get_text_domain() {
+        return $this->get_namespace();
+    }
+
+    /**
+     * @return void
+     */
+    public function load_plugin_text_domain() {
+        $plugin_translation = load_plugin_textdomain( $this->get_text_domain(), false, $this->get_namespace() . '/lang/' );
+    }
+
+    public function init_base_variables( ) {
+        $this->plugin_url = plugin_dir_url( $this->plugin_info->get_plugin_dir() );
+    }
+
+    /**
+     * @return void
+     */
+    protected function hooks() {
+        add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
+
+        add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
+
+        add_action( 'plugins_loaded', array( $this, 'load_plugin_text_domain' ) );
+        add_filter( 'plugin_action_links_' . plugin_basename( $this->get_plugin_file_path() ), array(
+            $this,
+            'links_filter'
+        ) );
+
+    }
+
+    /**
+     *
+     * @return string
+     */
+    public function get_plugin_url() {
+        return esc_url( trailingslashit( $this->plugin_url ) );
+    }
+
+    public function get_plugin_assets_url() {
+        return esc_url( trailingslashit( $this->get_plugin_url() . 'assets' ) );
+    }
+
+    /**
+     * @return string
+     */
+    public function get_plugin_file_path() {
+        return $this->plugin_info->get_plugin_dir();
+    }
+
+    /**
+     * @return string
+     */
+    public function get_namespace() {
+        return $this->plugin_namespace;
+    }
+
+    public function admin_enqueue_scripts( ) {
+    }
+
+    public function wp_enqueue_scripts() {
+    }
+
+    /**
+     * action_links function.
+     *
+     * @access public
+     *
+     * @param mixed $links
+     *
+     * @return array
+     */
+    public function links_filter( $links ) {
+        $support_link = get_locale() === 'pl_PL' ? 'https://www.wpdesk.pl/support/' : 'https://www.wpdesk.net/support';
+
+        $plugin_links = array(
+            '<a href="' . $support_link . '">' . __( 'Support', 'wpdesk-plugin' ) . '</a>',
+        );
+        $links        = array_merge( $plugin_links, $links );
+
+        if ( $this->docs_url ) {
+            $plugin_links = array(
+                '<a href="' . $this->docs_url . '">' . __( 'Docs', 'wpdesk-plugin' ) . '</a>',
+            );
+            $links        = array_merge( $plugin_links, $links );
+        }
+
+        if ( $this->settings_url ) {
+            $plugin_links = array(
+                '<a href="' . $this->settings_url . '">' . __( 'Settings', 'wpdesk-plugin' ) . '</a>',
+            );
+            $links        = array_merge( $plugin_links, $links );
+        }
+
+        return $links;
+    }
+
+}
+