diff --git a/app/console/src/Commands/ExtensionGenerateCommand.php b/app/console/src/Commands/ExtensionGenerateCommand.php
new file mode 100644
index 000000000..0b773930c
--- /dev/null
+++ b/app/console/src/Commands/ExtensionGenerateCommand.php
@@ -0,0 +1,129 @@
+addArgument('name', InputArgument::OPTIONAL, 'Extension Name');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $options = $this->getGeneratorOptions($output);
+ $this->generateTemplateFiles($options);
+ }
+
+ protected function generateTemplateFiles($options) {
+ $templateDirectory = $this->container->path() . '/app/console/src/Templates/Extension';
+ $outputDirectory = $this->container->path() . '/packages/' . $options['name'];
+
+
+ if (is_dir($outputDirectory)) {
+ // don't create the bundle if it already exists
+ $this->line('The extension already exists');
+ return;
+ } else {
+ // create the directory for the extension
+ mkdir($outputDirectory, 0777, true);
+ mkdir($outputDirectory . '/src/Controller', 0777, true);
+ }
+
+ $twig = new \Twig_Environment(new \Twig_Loader_Filesystem($templateDirectory), array(
+ 'debug' => true,
+ 'cache' => false,
+ 'strict_variables' => true,
+ 'autoescape' => true
+ ));
+
+ $this->line('Generating template files');
+
+ // get the template files
+ $files = $files = Finder::create()->files()->in($templateDirectory);
+ $files->in($templateDirectory . '/src/Controller');
+ $files = $files->name('*.twig');
+
+ $progress = new ProgressBar($this->output, count($files));
+ $progress->start();
+
+ // fill in the templates, write to the extension directory
+ foreach ($files as $file) {
+ $relativeFilePath = str_replace($templateDirectory, '', $file->getPathname());
+ $destinationFilePath = $outputDirectory . '/' . str_replace('.twig', '', $relativeFilePath);
+
+ file_put_contents($destinationFilePath, $twig->render($relativeFilePath, $options));
+ $progress->advance();
+ }
+ $progress->finish();
+ $this->line('Your extension has been created in packages/' . $options['name']);
+ }
+
+ protected function getGeneratorOptions(OutputInterface $output)
+ {
+ $options = array();
+ $dialog = $this->getHelper('dialog');
+ $options['name'] = $this->argument('name');
+
+ if (empty($options['name'])) {
+ $options['name'] = $dialog->ask(
+ $output,
+ 'Please enter the name of the extension ',
+ 'my_extension'
+ );
+ }
+
+ $options['title'] = $dialog->ask(
+ $output,
+ 'Enter a human-readable name for extension ',
+ 'My Extension'
+ );
+
+ $options['author'] = $dialog->ask(
+ $output,
+ 'Enter your name ',
+ false
+ );
+
+ $options['email'] = $dialog->ask(
+ $output,
+ 'Enter your email ',
+ false
+ );
+
+ $options['namespace'] = $dialog->ask(
+ $output,
+ 'PHP namespace of the extension, eg Pagekit\Hello. ',
+ false
+ );
+
+ return $options;
+ }
+}
diff --git a/app/console/src/Templates/Extension/README.md.twig b/app/console/src/Templates/Extension/README.md.twig
new file mode 100644
index 000000000..a150cfae8
--- /dev/null
+++ b/app/console/src/Templates/Extension/README.md.twig
@@ -0,0 +1 @@
+# Your Extension
diff --git a/app/console/src/Templates/Extension/composer.json.twig b/app/console/src/Templates/Extension/composer.json.twig
new file mode 100644
index 000000000..0610e5c4a
--- /dev/null
+++ b/app/console/src/Templates/Extension/composer.json.twig
@@ -0,0 +1,17 @@
+{% block json %}
+{
+ "name": "{{ name }}",
+ "type": "pagekit-extension",
+ "version": "0.1.0",
+ "title": "{{ title }}",
+ "description": "An extension by {{ author }}",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "{{ author }}",
+ "email": "{{ email }}",
+ "homepage": ""
+ }
+ ]
+}
+{% endblock json %}
diff --git a/app/console/src/Templates/Extension/index.php.twig b/app/console/src/Templates/Extension/index.php.twig
new file mode 100644
index 000000000..8e01efaee
--- /dev/null
+++ b/app/console/src/Templates/Extension/index.php.twig
@@ -0,0 +1,155 @@
+ '{{ name }}',
+
+ /*
+ * Define the type of this module.
+ * Has to be 'extension' here. Can be 'theme' for a theme.
+ */
+ 'type' => 'extension',
+
+ /*
+ * Main entry point. Called when your extension is both installed and activated.
+ * Either assign an closure or a string that points to a PHP class.
+ * Example: 'main' => '{{ namespace }}\\HelloWorldExtension'
+ */
+ 'main' => function (Application $app) {
+
+ // bootstrap code
+ },
+
+ /*
+ * Register all namespaces to be loaded.
+ * Map from namespace to folder where the classes are located.
+ * Remember to escape backslashes with a second backslash.
+ */
+ 'autoload' => [
+
+ '{{ namespace }}' => 'src'
+
+ ],
+
+ /*
+ * Define nodes. A node is similar to a route with the difference
+ * that it can be placed anywhere in the menu structure. The
+ * resulting route is therefore determined on runtime.
+ */
+ 'nodes' => [],
+
+
+ /*
+ * Define routes.
+ */
+ 'routes' => [
+
+ '/{{ name }}' => [
+ 'name' => '@{{ name }}/admin',
+ 'controller' => [
+ '{{ namespace }}\\Controller\\Controller'
+ ]
+ ]
+
+ ],
+
+ /*
+ * Define menu items for the backend.
+ */
+ 'menu' => [
+
+ // name, can be used for menu hierarchy
+ '{{ name }}' => [
+
+ // Label to display
+ 'label' => '{{ title }}',
+
+ // Icon to display
+ 'icon' => '{{ name }}:icon.svg',
+
+ // URL this menu item links to
+ 'url' => '@{{ name }}/admin',
+
+ // Optional: Expression to check if menu item is active on current url
+ // 'active' => '@{{ name }}*'
+
+ // Optional: Limit access to roles which have specific permission assigned
+ // 'access' => '{{ name }}: manage hellos'
+ ],
+
+ '{{ name }}: panel' => [
+
+ // Parent menu item, makes this appear on 2nd level
+ 'parent' => '{{ name }}',
+
+ // See above
+ 'label' => '{{ title }}',
+ 'icon' => '{{ name }}:icon.svg',
+ 'url' => '@{{ name }}/admin'
+ // 'access' => '{{ name }}: manage hellos'
+ ],
+
+ '{{ name }}: settings' => [
+ 'parent' => '{{ name }}',
+ 'label' => 'Settings',
+ 'url' => '@{{ name }}/admin/settings',
+ 'access' => 'system: manage settings'
+ ]
+
+ ],
+
+ /*
+ * Define permissions.
+ * Will be listed in backend and can then be assigned to certain roles.
+ */
+ 'permissions' => [
+
+ // Unique name.
+ // Convention: extension name and speaking name of this permission (spaces allowd)
+ '{{ name }}: manage settings' => [
+ 'title' => 'Manage settings'
+ ],
+
+ ],
+
+ /*
+ * Link to a settings screen from the extensions listing.
+ */
+ 'settings' => '@{{ name }}/admin/settings',
+
+ /*
+ * Default module configuration.
+ * Can be overwritten by changed config during runtime.
+ */
+ 'config' => [
+
+ 'default' => 'World'
+
+ ],
+
+ /*
+ * Listen to events.
+ */
+ 'events' => [
+
+ 'view.scripts' => function ($event, $scripts) {
+ $scripts->register('hello-settings', 'hello:app/bundle/settings.js', '~extensions');
+ $scripts->register('hello-site', 'hello:app/bundle/site.js', '~site-edit');
+ $scripts->register('hello-link', 'hello:app/bundle/link.js', '~panel-link');
+ }
+
+ ]
+
+];
+{% endblock array_configuration %}
diff --git a/app/console/src/Templates/Extension/src/Controller/Controller.php.twig b/app/console/src/Templates/Extension/src/Controller/Controller.php.twig
new file mode 100644
index 000000000..1fe17c5eb
--- /dev/null
+++ b/app/console/src/Templates/Extension/src/Controller/Controller.php.twig
@@ -0,0 +1,28 @@
+