diff --git a/.github/wiki b/.github/wiki
index 02bb3c4c9c..bce2ecf64a 160000
--- a/.github/wiki
+++ b/.github/wiki
@@ -1 +1 @@
-Subproject commit 02bb3c4c9cc8df45bddda258c4f0ee7bbd29928c
+Subproject commit bce2ecf64a026c396a0c3a2dc9b37eb837abdba2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6dfed9aee8..118212e3ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Keep global `dev-tools:sync` runs machine-independent by removing only deprecated DevTools-managed Composer GrumPHP default-path metadata while wiring packaged Git hooks to prefer a project-local `grumphp.yml` and otherwise use the active packaged DevTools config path resolved at sync time (#288)
+- Complete global runtime fallback support for phpdoc, phpunit, and phpmetrics commands, including packaged phpDocumentor templates used by `docs` and `wiki` (#297)
## [1.24.2] - 2026-04-30
diff --git a/src/Console/Command/DocsCommand.php b/src/Console/Command/DocsCommand.php
index 5cc9a460b9..9ad95fa9cf 100644
--- a/src/Console/Command/DocsCommand.php
+++ b/src/Console/Command/DocsCommand.php
@@ -25,6 +25,7 @@
use FastForward\DevTools\Console\Input\HasJsonOption;
use Twig\Environment;
use FastForward\DevTools\Filesystem\FilesystemInterface;
+use FastForward\DevTools\Path\DevToolsPathResolver;
use FastForward\DevTools\Process\ProcessBuilderInterface;
use FastForward\DevTools\Process\ProcessQueueInterface;
use FastForward\DevTools\Path\ManagedWorkspace;
@@ -57,6 +58,11 @@ final class DocsCommand extends Command
use HasJsonOption;
use LogsCommandResults;
+ /**
+ * @var string the default phpDocumentor template path relative to the consumer project
+ */
+ private const string DEFAULT_TEMPLATE = 'vendor/fast-forward/phpdoc-bootstrap-template';
+
/**
* Creates a new DocsCommand instance.
*
@@ -114,7 +120,7 @@ protected function configure(): void
name: 'template',
mode: InputOption::VALUE_OPTIONAL,
description: 'Path to the template directory for the generated HTML documentation.',
- default: 'vendor/fast-forward/phpdoc-bootstrap-template',
+ default: self::DEFAULT_TEMPLATE,
);
}
@@ -136,6 +142,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$source = $this->filesystem->getAbsolutePath($input->getOption('source'));
$target = $this->filesystem->getAbsolutePath($input->getOption('target'));
$cacheDir = $this->filesystem->getAbsolutePath($input->getOption('cache-dir'));
+ $template = (string) $input->getOption('template');
+
+ if (self::DEFAULT_TEMPLATE === $template) {
+ $template = DevToolsPathResolver::getPreferredVendorPath(self::DEFAULT_TEMPLATE);
+ }
$this->logger->info('Generating API documentation...', [
'input' => $input,
@@ -150,7 +161,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$config = $this->createPhpDocumentorConfig(
source: $source,
target: $target,
- template: $input->getOption('template'),
+ template: $template,
cacheDir: $cacheEnabled ? $cacheDir : sys_get_temp_dir(),
);
@@ -167,7 +178,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$processBuilder = $processBuilder->withArgument('--no-progress');
}
- $phpdoc = $processBuilder->build('vendor/bin/phpdoc');
+ $phpdoc = $processBuilder->build([DevToolsPathResolver::getPreferredToolBinaryPath('phpdoc')]);
$this->processQueue->add(process: $phpdoc, label: 'Generating API Docs with phpDocumentor');
diff --git a/src/Console/Command/MetricsCommand.php b/src/Console/Command/MetricsCommand.php
index 6b7dc54f61..972b47358b 100644
--- a/src/Console/Command/MetricsCommand.php
+++ b/src/Console/Command/MetricsCommand.php
@@ -21,6 +21,7 @@
use FastForward\DevTools\Console\Command\Traits\LogsCommandResults;
use FastForward\DevTools\Console\Input\HasJsonOption;
+use FastForward\DevTools\Path\DevToolsPathResolver;
use FastForward\DevTools\Process\ProcessBuilderInterface;
use FastForward\DevTools\Process\ProcessQueueInterface;
use FastForward\DevTools\Path\ManagedWorkspace;
@@ -45,9 +46,9 @@ final class MetricsCommand extends Command
use LogsCommandResults;
/**
- * @var string the bundled PhpMetrics binary path relative to the consumer root
+ * @var string the PhpMetrics binary name resolved through the runtime-aware tooling lookup
*/
- private const string BINARY = 'vendor/bin/phpmetrics';
+ private const string BINARY = 'phpmetrics';
/**
* @var int the PHP error reporting mask that suppresses deprecations emitted by PhpMetrics internals
@@ -161,7 +162,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
\PHP_BINARY,
'-derror_reporting=' . self::PHP_ERROR_REPORTING,
'-ddefault_socket_timeout=' . self::PHP_DEFAULT_SOCKET_TIMEOUT,
- self::BINARY,
+ DevToolsPathResolver::getPreferredToolBinaryPath(self::BINARY),
]),
label: 'Generating Metrics with PhpMetrics',
);
diff --git a/src/Console/Command/TestsCommand.php b/src/Console/Command/TestsCommand.php
index a7f72e4e9b..2ce856a7cb 100644
--- a/src/Console/Command/TestsCommand.php
+++ b/src/Console/Command/TestsCommand.php
@@ -24,6 +24,7 @@
use FastForward\DevTools\Console\Input\HasJsonOption;
use FastForward\DevTools\Composer\Json\ComposerJsonInterface;
use FastForward\DevTools\Filesystem\FilesystemInterface;
+use FastForward\DevTools\Path\DevToolsPathResolver;
use FastForward\DevTools\PhpUnit\Bootstrap\BootstrapShimGenerator;
use FastForward\DevTools\PhpUnit\Coverage\CoverageSummaryLoaderInterface;
use FastForward\DevTools\Process\ProcessBuilderInterface;
@@ -209,7 +210,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->processQueue->add(
process: $processBuilder
->withArgument($input->getArgument('path'))
- ->build('vendor/bin/phpunit'),
+ ->build([DevToolsPathResolver::getPreferredToolBinaryPath('phpunit')]),
label: 'Running PHPUnit Tests',
);
diff --git a/src/Console/Command/WikiCommand.php b/src/Console/Command/WikiCommand.php
index 01d7394022..e78cc39505 100644
--- a/src/Console/Command/WikiCommand.php
+++ b/src/Console/Command/WikiCommand.php
@@ -25,6 +25,7 @@
use FastForward\DevTools\Console\Input\HasJsonOption;
use FastForward\DevTools\Filesystem\FilesystemInterface;
use FastForward\DevTools\Git\GitClientInterface;
+use FastForward\DevTools\Path\DevToolsPathResolver;
use FastForward\DevTools\Process\ProcessBuilderInterface;
use FastForward\DevTools\Process\ProcessQueueInterface;
use FastForward\DevTools\Path\ManagedWorkspace;
@@ -54,6 +55,11 @@ final class WikiCommand extends Command
use HasJsonOption;
use LogsCommandResults;
+ /**
+ * @var string the default phpDocumentor Markdown template path relative to the consumer project
+ */
+ private const string DEFAULT_TEMPLATE = 'vendor/saggre/phpdocumentor-markdown/themes/markdown';
+
/**
* Creates a new WikiCommand instance.
*
@@ -138,7 +144,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$processBuilder = $this->processBuilder
->withArgument('--ansi')
->withArgument('--visibility', 'public,protected')
- ->withArgument('--template', 'vendor/saggre/phpdocumentor-markdown/themes/markdown')
+ ->withArgument('--template', DevToolsPathResolver::getPreferredVendorPath(self::DEFAULT_TEMPLATE))
->withArgument('--title', $this->composer->getDescription())
->withArgument('--target', $target);
@@ -157,7 +163,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
$this->processQueue->add(
- process: $processBuilder->build('vendor/bin/phpdoc'),
+ process: $processBuilder->build([DevToolsPathResolver::getPreferredToolBinaryPath('phpdoc')]),
label: 'Generating Wiki with phpDocumentor',
);
diff --git a/src/Path/DevToolsPathResolver.php b/src/Path/DevToolsPathResolver.php
index a48e1dd142..021c280afa 100644
--- a/src/Path/DevToolsPathResolver.php
+++ b/src/Path/DevToolsPathResolver.php
@@ -128,6 +128,27 @@ public static function getRuntimeToolBinaryPath(string $binary, string $packageP
return Path::join($packagePath, 'vendor', 'bin', $binary);
}
+ /**
+ * Returns the active Composer vendor path for the current DevTools installation mode.
+ *
+ * Relative vendor paths MAY be passed either with or without a leading
+ * `vendor/` prefix.
+ *
+ * @param string $path the vendor-relative path to resolve
+ * @param string $packagePath an optional package root path; defaults to the current package root
+ */
+ public static function getRuntimeVendorPath(string $path, string $packagePath = ''): string
+ {
+ $packagePath = Path::canonicalize('' === $packagePath ? self::getPackagePath() : $packagePath);
+ $vendorPath = self::normalizeVendorRelativePath($path);
+
+ if (self::isInstalledAsDependency($packagePath)) {
+ return Path::canonicalize(Path::join($packagePath, '..', '..', $vendorPath));
+ }
+
+ return Path::join($packagePath, 'vendor', $vendorPath);
+ }
+
/**
* Returns the preferred tooling binary path for the active project and DevTools runtime.
*
@@ -154,6 +175,33 @@ public static function getPreferredToolBinaryPath(
return self::getRuntimeToolBinaryPath($binary, $packagePath);
}
+ /**
+ * Returns the preferred Composer vendor path for the active project and DevTools runtime.
+ *
+ * Consumer projects SHOULD take precedence when they provide the requested
+ * vendor path locally. If the path is absent locally, the method MUST fall
+ * back to the active DevTools runtime vendor path.
+ *
+ * @param string $path the vendor-relative path to resolve
+ * @param string $projectPath an optional project root path; defaults to the working project root
+ * @param string $packagePath an optional package root path; defaults to the current package root
+ */
+ public static function getPreferredVendorPath(
+ string $path,
+ string $projectPath = '',
+ string $packagePath = '',
+ ): string {
+ $projectPath = '' === $projectPath ? WorkingProjectPathResolver::getProjectPath() : $projectPath;
+ $vendorPath = self::normalizeVendorRelativePath($path);
+ $projectVendorPath = Path::join($projectPath, 'vendor', $vendorPath);
+
+ if (file_exists($projectVendorPath)) {
+ return $projectVendorPath;
+ }
+
+ return self::getRuntimeVendorPath($vendorPath, $packagePath);
+ }
+
/**
* Detects whether the provided path belongs to an installed vendor copy of DevTools.
*
@@ -175,4 +223,20 @@ public static function isRepositoryCheckout(string $packagePath = ''): bool
{
return ! self::isInstalledAsDependency($packagePath);
}
+
+ /**
+ * Normalizes a path relative to the Composer vendor root.
+ *
+ * @param string $path the vendor-relative path to normalize
+ */
+ private static function normalizeVendorRelativePath(string $path): string
+ {
+ $path = Path::canonicalize($path);
+
+ if (str_starts_with($path, 'vendor/')) {
+ return substr($path, 7);
+ }
+
+ return $path;
+ }
}
diff --git a/tests/Console/Command/DocsCommandTest.php b/tests/Console/Command/DocsCommandTest.php
index 1ea196cc1d..6840e2b118 100644
--- a/tests/Console/Command/DocsCommandTest.php
+++ b/tests/Console/Command/DocsCommandTest.php
@@ -23,9 +23,11 @@
use FastForward\DevTools\Console\Command\Traits\LogsCommandResults;
use FastForward\DevTools\Console\Command\DocsCommand;
use FastForward\DevTools\Filesystem\FilesystemInterface;
+use FastForward\DevTools\Path\DevToolsPathResolver;
use FastForward\DevTools\Process\ProcessBuilderInterface;
use FastForward\DevTools\Process\ProcessQueueInterface;
use FastForward\DevTools\Path\ManagedWorkspace;
+use FastForward\DevTools\Path\WorkingProjectPathResolver;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
@@ -43,7 +45,9 @@
use Twig\Environment;
#[CoversClass(DocsCommand::class)]
+#[UsesClass(DevToolsPathResolver::class)]
#[UsesClass(ManagedWorkspace::class)]
+#[UsesClass(WorkingProjectPathResolver::class)]
#[UsesTrait(LogsCommandResults::class)]
final class DocsCommandTest extends TestCase
{
@@ -128,12 +132,19 @@ protected function setUp(): void
]);
$this->composer->getName()
->willReturn('fast-forward/dev-tools');
- $this->renderer->render('phpdocumentor.xml', Argument::type('array'))->willReturn('');
+ $this->renderer->render(
+ 'phpdocumentor.xml',
+ Argument::that(
+ static fn(array $context): bool => DevToolsPathResolver::getPreferredVendorPath(
+ 'vendor/fast-forward/phpdoc-bootstrap-template'
+ ) === $context['template']
+ )
+ )->willReturn('');
$this->processBuilder->withArgument(Argument::any())->willReturn($this->processBuilder->reveal());
$this->processBuilder->withArgument(Argument::any(), Argument::any())->willReturn(
$this->processBuilder->reveal()
);
- $this->processBuilder->build('vendor/bin/phpdoc')
+ $this->processBuilder->build([DevToolsPathResolver::getPreferredToolBinaryPath('phpdoc')])
->willReturn($this->process->reveal());
$this->command = new DocsCommand(
diff --git a/tests/Console/Command/MetricsCommandTest.php b/tests/Console/Command/MetricsCommandTest.php
index 3e2a91cb12..6f7fc68075 100644
--- a/tests/Console/Command/MetricsCommandTest.php
+++ b/tests/Console/Command/MetricsCommandTest.php
@@ -21,9 +21,11 @@
use FastForward\DevTools\Console\Command\MetricsCommand;
use FastForward\DevTools\Console\Command\Traits\LogsCommandResults;
+use FastForward\DevTools\Path\DevToolsPathResolver;
use FastForward\DevTools\Process\ProcessBuilderInterface;
use FastForward\DevTools\Process\ProcessQueueInterface;
use FastForward\DevTools\Path\ManagedWorkspace;
+use FastForward\DevTools\Path\WorkingProjectPathResolver;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
@@ -42,7 +44,9 @@
use function Safe\putenv;
#[CoversClass(MetricsCommand::class)]
+#[UsesClass(DevToolsPathResolver::class)]
#[UsesClass(ManagedWorkspace::class)]
+#[UsesClass(WorkingProjectPathResolver::class)]
#[UsesTrait(LogsCommandResults::class)]
final class MetricsCommandTest extends TestCase
{
@@ -99,7 +103,8 @@ protected function setUp(): void
$this->processBuilder->build(Argument::that(static fn(array $command): bool => \PHP_BINARY === $command[0]
&& str_starts_with((string) $command[1], '-derror_reporting=')
&& '-ddefault_socket_timeout=1' === $command[2]
- && 'vendor/bin/phpmetrics' === $command[3]))->willReturn($this->process->reveal());
+ && DevToolsPathResolver::getPreferredToolBinaryPath('phpmetrics') === $command[3]))
+ ->willReturn($this->process->reveal());
$this->command = new MetricsCommand(
$this->processBuilder->reveal(),
$this->processQueue->reveal(),
diff --git a/tests/Console/Command/TestsCommandTest.php b/tests/Console/Command/TestsCommandTest.php
index 791a63bffa..12a8a2a5ea 100644
--- a/tests/Console/Command/TestsCommandTest.php
+++ b/tests/Console/Command/TestsCommandTest.php
@@ -30,6 +30,7 @@
use FastForward\DevTools\Process\ProcessQueueInterface;
use FastForward\DevTools\Path\ManagedWorkspace;
use FastForward\DevTools\Path\DevToolsPathResolver;
+use FastForward\DevTools\Path\WorkingProjectPathResolver;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
@@ -54,6 +55,7 @@
#[UsesClass(DevToolsPathResolver::class)]
#[UsesClass(ProcessBuilder::class)]
#[UsesClass(ManagedWorkspace::class)]
+#[UsesClass(WorkingProjectPathResolver::class)]
#[UsesTrait(LogsCommandResults::class)]
final class TestsCommandTest extends TestCase
{
@@ -147,6 +149,9 @@ public function executeWillRunPhpUnitProcessWithConfigFile(): void
) && str_contains(
$process->getCommandLine(),
'--bootstrap=' . $generatedBootstrapPath,
+ ) && str_contains(
+ $process->getCommandLine(),
+ DevToolsPathResolver::getPreferredToolBinaryPath('phpunit'),
) && str_contains($process->getCommandLine(), '--cache-result') && str_contains(
$process->getCommandLine(),
'--cache-directory=' . getcwd() . '/.dev-tools/cache/phpunit',
diff --git a/tests/Console/Command/WikiCommandTest.php b/tests/Console/Command/WikiCommandTest.php
index 69030c563d..80f742e548 100644
--- a/tests/Console/Command/WikiCommandTest.php
+++ b/tests/Console/Command/WikiCommandTest.php
@@ -24,9 +24,11 @@
use FastForward\DevTools\Console\Command\WikiCommand;
use FastForward\DevTools\Filesystem\FilesystemInterface;
use FastForward\DevTools\Git\GitClientInterface;
+use FastForward\DevTools\Path\DevToolsPathResolver;
use FastForward\DevTools\Process\ProcessBuilderInterface;
use FastForward\DevTools\Process\ProcessQueueInterface;
use FastForward\DevTools\Path\ManagedWorkspace;
+use FastForward\DevTools\Path\WorkingProjectPathResolver;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
@@ -42,7 +44,9 @@
use Symfony\Component\Process\Process;
#[CoversClass(WikiCommand::class)]
+#[UsesClass(DevToolsPathResolver::class)]
#[UsesClass(ManagedWorkspace::class)]
+#[UsesClass(WorkingProjectPathResolver::class)]
#[UsesTrait(LogsCommandResults::class)]
final class WikiCommandTest extends TestCase
{
@@ -107,7 +111,8 @@ protected function setUp(): void
$this->processBuilder->withArgument(Argument::any(), Argument::any())->willReturn(
$this->processBuilder->reveal()
);
- $this->processBuilder->build(Argument::any())->willReturn($this->process->reveal());
+ $this->processBuilder->build([DevToolsPathResolver::getPreferredToolBinaryPath('phpdoc')])
+ ->willReturn($this->process->reveal());
$this->command = new WikiCommand(
$this->processBuilder->reveal(),
@@ -125,6 +130,12 @@ protected function setUp(): void
#[Test]
public function executeWillReturnSuccessWhenProcessQueueSucceeds(): void
{
+ $this->processBuilder->withArgument(
+ '--template',
+ DevToolsPathResolver::getPreferredVendorPath('vendor/saggre/phpdocumentor-markdown/themes/markdown')
+ )
+ ->willReturn($this->processBuilder->reveal())
+ ->shouldBeCalled();
$this->processBuilder->withArgument(
'--cache-folder',
ManagedWorkspace::getCacheDirectory(ManagedWorkspace::PHPDOC)
diff --git a/tests/Path/DevToolsPathResolverTest.php b/tests/Path/DevToolsPathResolverTest.php
index 3d78af5a28..c82ff3f9f2 100644
--- a/tests/Path/DevToolsPathResolverTest.php
+++ b/tests/Path/DevToolsPathResolverTest.php
@@ -51,6 +51,10 @@ public function itWillExposeCanonicalPackagePaths(): void
\dirname(__DIR__, 2) . '/vendor/bin/ecs',
DevToolsPathResolver::getRuntimeToolBinaryPath('ecs')
);
+ self::assertSame(
+ \dirname(__DIR__, 2) . '/vendor/fast-forward/phpdoc-bootstrap-template',
+ DevToolsPathResolver::getRuntimeVendorPath('vendor/fast-forward/phpdoc-bootstrap-template')
+ );
self::assertSame(
\dirname(__DIR__, 2) . '/resources/phpdocumentor.xml',
DevToolsPathResolver::getResourcesPath('phpdocumentor.xml')
@@ -93,7 +97,7 @@ public function itWillDetectWhetherDevToolsRunsFromVendorOrRepositoryCheckout():
* @return void
*/
#[Test]
- public function itWillResolveRuntimeAutoloadPathsForRepositoryAndDependencyInstalls(): void
+ public function itWillResolveRuntimeAutoloadAndVendorPathsForRepositoryAndDependencyInstalls(): void
{
self::assertSame(
'/workspaces/dev-tools/vendor/autoload.php',
@@ -103,6 +107,13 @@ public function itWillResolveRuntimeAutoloadPathsForRepositoryAndDependencyInsta
'/workspaces/project/vendor/autoload.php',
DevToolsPathResolver::getRuntimeAutoloadPath('/workspaces/project/vendor/fast-forward/dev-tools')
);
+ self::assertSame(
+ '/workspaces/project/vendor/saggre/phpdocumentor-markdown/themes/markdown',
+ DevToolsPathResolver::getRuntimeVendorPath(
+ 'vendor/saggre/phpdocumentor-markdown/themes/markdown',
+ '/workspaces/project/vendor/fast-forward/dev-tools'
+ )
+ );
}
/**
@@ -112,6 +123,9 @@ public function itWillResolveRuntimeAutoloadPathsForRepositoryAndDependencyInsta
*/
#[Test]
#[TestWith(['php-cs-fixer'])]
+ #[TestWith(['phpdoc'])]
+ #[TestWith(['phpmetrics'])]
+ #[TestWith(['phpunit'])]
#[TestWith(['rector'])]
#[TestWith(['ecs'])]
#[TestWith(['jack'])]
@@ -172,4 +186,50 @@ public function itWillFallbackToRuntimeToolBinariesWhenTheProjectDoesNotProvideT
)
);
}
+
+ /**
+ * @return void
+ */
+ #[Test]
+ public function itWillPreferProjectVendorPathsWhenTheyExist(): void
+ {
+ $projectPath = sys_get_temp_dir() . '/dev-tools-vendor-path-resolver-' . bin2hex(random_bytes(4));
+ $vendorPath = $projectPath . '/vendor/saggre/phpdocumentor-markdown/themes/markdown';
+
+ mkdir($vendorPath, 0o777, true);
+
+ try {
+ self::assertSame(
+ $vendorPath,
+ DevToolsPathResolver::getPreferredVendorPath(
+ 'vendor/saggre/phpdocumentor-markdown/themes/markdown',
+ $projectPath,
+ '/Users/example/.composer/vendor/fast-forward/dev-tools'
+ )
+ );
+ } finally {
+ rmdir($vendorPath);
+ rmdir($projectPath . '/vendor/saggre/phpdocumentor-markdown/themes');
+ rmdir($projectPath . '/vendor/saggre/phpdocumentor-markdown');
+ rmdir($projectPath . '/vendor/saggre');
+ rmdir($projectPath . '/vendor');
+ rmdir($projectPath);
+ }
+ }
+
+ /**
+ * @return void
+ */
+ #[Test]
+ public function itWillFallbackToRuntimeVendorPathsWhenTheProjectDoesNotProvideThem(): void
+ {
+ self::assertSame(
+ '/Users/example/.composer/vendor/fast-forward/phpdoc-bootstrap-template',
+ DevToolsPathResolver::getPreferredVendorPath(
+ 'vendor/fast-forward/phpdoc-bootstrap-template',
+ '/workspaces/project',
+ '/Users/example/.composer/vendor/fast-forward/dev-tools'
+ )
+ );
+ }
}