diff --git a/bin/tools b/bin/tools
new file mode 120000
index 0000000000000000000000000000000000000000..a4b735778c94f71b83ae24e7e4e3a8363b599e2c
--- /dev/null
+++ b/bin/tools
@@ -0,0 +1 @@
+../tools/bin/tools
\ No newline at end of file
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..48b8bf9072d8716346ec810e5a1808305c97d50f
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1 @@
+vendor/
diff --git a/tools/README.md b/tools/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..164884629ebefd3c8b7c0c31d3767c2fcc43948b
--- /dev/null
+++ b/tools/README.md
@@ -0,0 +1,3 @@
+# tools
+
+This directory contains a small Symfony app which provides a command line utility for doing things like generating content and testing content. 
diff --git a/tools/bin/tools b/tools/bin/tools
new file mode 100755
index 0000000000000000000000000000000000000000..12cd40457af9180d077d4093200a631ee6892391
--- /dev/null
+++ b/tools/bin/tools
@@ -0,0 +1,11 @@
+#!/usr/bin/env php
+<?php
+
+require __DIR__ . '/../vendor/autoload.php';
+
+use Symfony\Component\Console\Application;
+use AppBundle\Command\HookListCommand;
+
+$application = new Application('Developer Guide tools');
+$application->add(new HookListCommand());
+$application->run();
diff --git a/tools/composer.json b/tools/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..a31a3ee859ff09509f695a97cc0b3b364677476d
--- /dev/null
+++ b/tools/composer.json
@@ -0,0 +1,11 @@
+{
+    "require": {
+        "symfony/console": "^3.3",
+        "symfony/yaml": "^3.3"
+    },
+    "autoload": {
+        "psr-4": {
+            "": "src/"
+        }
+    }
+}
diff --git a/tools/composer.lock b/tools/composer.lock
new file mode 100644
index 0000000000000000000000000000000000000000..0f09cd904270cc29a63dce960fbec6c08a756175
--- /dev/null
+++ b/tools/composer.lock
@@ -0,0 +1,304 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "2ab7e9af936c5a1de589c4b1208a77ff",
+    "packages": [
+        {
+            "name": "psr/log",
+            "version": "1.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
+                "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "Psr/Log/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "time": "2016-10-10T12:19:37+00:00"
+        },
+        {
+            "name": "symfony/console",
+            "version": "v3.3.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/console.git",
+                "reference": "70d2a29b2911cbdc91a7e268046c395278238b2e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/console/zipball/70d2a29b2911cbdc91a7e268046c395278238b2e",
+                "reference": "70d2a29b2911cbdc91a7e268046c395278238b2e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.9",
+                "symfony/debug": "~2.8|~3.0",
+                "symfony/polyfill-mbstring": "~1.0"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<3.3"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "~3.3",
+                "symfony/dependency-injection": "~3.3",
+                "symfony/event-dispatcher": "~2.8|~3.0",
+                "symfony/filesystem": "~2.8|~3.0",
+                "symfony/http-kernel": "~2.8|~3.0",
+                "symfony/process": "~2.8|~3.0"
+            },
+            "suggest": {
+                "psr/log": "For using the console logger",
+                "symfony/event-dispatcher": "",
+                "symfony/filesystem": "",
+                "symfony/process": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Console\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Console Component",
+            "homepage": "https://symfony.com",
+            "time": "2017-06-02T19:24:58+00:00"
+        },
+        {
+            "name": "symfony/debug",
+            "version": "v3.3.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/debug.git",
+                "reference": "e9c50482841ef696e8fa1470d950a79c8921f45d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/e9c50482841ef696e8fa1470d950a79c8921f45d",
+                "reference": "e9c50482841ef696e8fa1470d950a79c8921f45d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.9",
+                "psr/log": "~1.0"
+            },
+            "conflict": {
+                "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
+            },
+            "require-dev": {
+                "symfony/http-kernel": "~2.8|~3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Debug\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Debug Component",
+            "homepage": "https://symfony.com",
+            "time": "2017-06-01T21:01:25+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "f29dca382a6485c3cbe6379f0c61230167681937"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937",
+                "reference": "f29dca382a6485c3cbe6379f0c61230167681937",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2017-06-09T14:24:12+00:00"
+        },
+        {
+            "name": "symfony/yaml",
+            "version": "v3.3.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/yaml.git",
+                "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/9752a30000a8ca9f4b34b5227d15d0101b96b063",
+                "reference": "9752a30000a8ca9f4b34b5227d15d0101b96b063",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.9"
+            },
+            "require-dev": {
+                "symfony/console": "~2.8|~3.0"
+            },
+            "suggest": {
+                "symfony/console": "For validating YAML files using the lint command"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Yaml\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Yaml Component",
+            "homepage": "https://symfony.com",
+            "time": "2017-06-02T22:05:06+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": []
+}
diff --git a/tools/src/AppBundle/Command/HookListCommand.php b/tools/src/AppBundle/Command/HookListCommand.php
new file mode 100644
index 0000000000000000000000000000000000000000..1f1680694a148dae7d28cef571c7a82844a08329
--- /dev/null
+++ b/tools/src/AppBundle/Command/HookListCommand.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace AppBundle\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use AppBundle\Utils\Book;
+
+class HookListCommand extends Command {
+
+  protected function configure() {
+    $this
+      ->setName('generate:hook-list')
+      ->setDescription('Generates a summary listing page of all hooks');
+  }
+
+  protected function execute(InputInterface $input, OutputInterface $output) {
+    $hooks = Book::pages()['Hooks'];
+
+    //$output->write(var_export($hooks, TRUE));
+
+  }
+
+}
diff --git a/tools/src/AppBundle/Utils/ArrayUtils.php b/tools/src/AppBundle/Utils/ArrayUtils.php
new file mode 100644
index 0000000000000000000000000000000000000000..7467a25b3c8b3ed0481155e074255a7cfe50921a
--- /dev/null
+++ b/tools/src/AppBundle/Utils/ArrayUtils.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace AppBundle\Utils;
+
+class ArrayUtils {
+
+  /**
+   * If we have an array of arrays, we can use this function to merge all the
+   * child arrays
+   *
+   * @param mixed $array
+   *   Typically an array of arrays, but will accept anything.
+   *
+   *   Example:
+   *   [
+   *     0 => [
+   *       'foo' => 'FOO',
+   *       'bar' => 'BAR',
+   *     ],
+   *     1 => [
+   *       'baz' => 'BAZ'
+   *     ],
+   *   ]
+   *
+   * @return mixed
+   *   The array with elements merged. If a non-array is passed in, then it will
+   *   be returned without error.
+   *
+   *   Output for above example:
+   *   [
+   *     'foo' => 'FOO',
+   *     'bar' => 'BAR',
+   *     'baz' => 'BAZ',
+   *   ]
+   */
+  public static function merge_inner($array) {
+    return is_array($array)
+      ? call_user_func_array('array_merge', $array)
+      : $array;
+  }
+
+  /**
+   * Works like ArrayUtils::merge_inner but recursively.
+   *
+   * @param mixed $array
+   *
+   * @return mixed
+   *
+   */
+  public static function merge_inner_recursive($array) {
+    if (is_array($array)) {
+      $result = self::merge_inner($array);
+      $result = array_map('self::merge_inner_recursive', $result);
+      return $result;
+    }
+    else {
+      return $array;
+    }
+  }
+
+}
diff --git a/tools/src/AppBundle/Utils/Book.php b/tools/src/AppBundle/Utils/Book.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2b744964b4641db9a6351db752cb47bab2ef491
--- /dev/null
+++ b/tools/src/AppBundle/Utils/Book.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace AppBundle\Utils;
+
+use Symfony\Component\Yaml;
+
+class Book {
+
+  /**
+   * @return array
+   */
+  public static function pages() {
+    $mkdocs = __DIR__ . '/../../../../mkdocs.yml';
+    $navigation = (new Yaml\Parser())->parse(file_get_contents($mkdocs));
+
+    // Need to do some funky array stuff to flatten out the structure here
+    // because mkdocs.yml stores things in arrays without keys
+    $pages = ArrayUtils::merge_inner_recursive($navigation['pages']);
+
+    return $pages;
+  }
+
+}