Specialized Commands
Use the standalone commands when you want one tool instead of the full
standards
pipeline.
External Changelog CLI
Run the latest published independent Fast Forward Changelog CLI through
cpx --skip-local
. CPX and PHP 8.5 must be available on its execution path;
the consumer's PHP 8.3 requirement stays unchanged.
cpx --skip-local fast-forward/changelog add "Fixes release-note rendering." --category=fixed --no-interaction
cpx --skip-local fast-forward/changelog check --no-interaction
cpx --skip-local fast-forward/changelog status --source=tags --no-interaction
Contributions use independent .changelog/*.md
files. Inspect external command
help for its options and output contracts. See External Fast Forward Changelog for
runtime requirements and release-preview boundaries.
tests
Runs PHPUnit with the resolved phpunit.xml
.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" tests
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" tests --filter=EventTracerTest
Important details:
- local
phpunit.xmlis preferred over the packaged default; --coverage=<path>creates HTML, Testdox, Clover, and raw coverage output;--coverage-summarykeeps coverage text output to PHPUnit's summary;- cache stays enabled by default;
--cacheforces it on and--no-cacheforces it off for a single run; - when
--cache-diris omitted, PHPUnit keeps its default cache directory; --cache-dironly affects PHPUnit's result cache when caching is enabled;- progress output is disabled by default;
--progressre-enables PHPUnit progress output in text mode;--jsonand--pretty-jsonstill suppress PHPUnit progress output automatically;--pretty-jsonstays valid JSON and does not add ANSI color escapes;- in agent-driven runs, the command also captures PHPUnit output in structured
mode automatically and stores the bundled PHPUnit agent-reporter payload in
output.--jsonand--pretty-jsontherefore keep the same structured shape, withraw_outputpreserved underoutputwhen PHPUnit writes extra text before the final JSON; - the packaged configuration registers the DevTools PHPUnit extension.
dependencies
Analyzes missing, unused, misplaced, and outdated Composer dependencies.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" dependencies
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" dependencies --max-outdated=10
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" dependencies --max-outdated=-1
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" dependencies --dev
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" dependencies --dump-usage=symfony/console
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" dependencies --show-shadow-dependencies
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" dependencies --upgrade --dev
Important details:
- it ships
shipmonk/composer-dependency-analyserandrector/swiss-knifeas direct dependencies offast-forward/dev-tools; - it uses
composer-dependency-analyserfor missing, unused, and misplaced dependency checks, with a packaged config that consumer repositories can override locally; --dump-usage=<package>forwards tocomposer-dependency-analyser --dump-usages <package> --show-all-usages;--show-shadow-dependencieskeeps shadow dependency findings visible for audits; without it, DevTools hides intentional Fast Forward dependency-group shadows so CI does not fail on ecosystem or meta packages that deliberately install related dependencies for consumers;- it uses
swiss-knife breakpoint --limit=<max-outdated>to fail when too many outdated dependencies accumulate; --max-outdated=-1keeps the Rector Swiss Knife outdated report in the output but ignores Rector Swiss Knife's failure so only dependency-analyser findings fail the command;- the packaged
tests.ymlworkflow uses--max-outdated=-1by default, so dependency health stays required in CI while outdated-package counts are reported without failing the workflow on their own; - it previews
swiss-knife raise-to-installedandswiss-knife open-versionsbefore the analyzers; --upgraderunsswiss-knife raise-to-installed,swiss-knife open-versions,composer update -W, andcomposer normalizebefore the analyzers;- it returns a non-zero exit code when missing, unused, misplaced, or too many outdated dependencies are found.
metrics
Analyzes code metrics with PhpMetrics.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" metrics
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" metrics --target=.dev-tools/metrics
FAST_FORWARD_WORKSPACE_DIR=.artifacts cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" metrics
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" --working-dir=packages/example metrics
Important details:
- it ships
phpmetrics/phpmetricsas a direct dependency offast-forward/dev-tools; - when invoked through Composer, you MAY use Composer's inherited
--working-diroption to analyze another checkout without changing directories first; --targetstores the HTML report plusreport.jsonandreport-summary.jsonin the same directory for CI artifacts or manual review;- when
--targetis omitted, metrics output is written under the selected DevTools workspace, usually.dev-tools/metrics; --jsonand--pretty-jsonkeep the top-level DevTools response structured while forwarding JSON or quieter modes to the wrapped tools where available;- it suppresses deprecation notices emitted by the PhpMetrics dependency itself so the command output stays readable;
- it keeps PhpMetrics' Composer analysis enabled so report generation includes root package metadata, while default exclusions keep nested fixture projects out of that Composer scan;
- it limits PhpMetrics' per-package Packagist socket wait so package freshness enrichment cannot leave report generation stuck indefinitely.
code-style
Runs Composer Normalize and ECS.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" code-style
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" code-style --fix
Important details:
- it does not refresh the lockfile or run dependency updates first;
- without
--fix, Composer Normalize runs in--dry-runmode; - ECS uses local
ecs.phpwhen present, otherwise the packaged fallback; - progress output is disabled by default;
--progressre-enables it for text mode; --jsonand--pretty-jsonforward JSON mode to ECS and suppress its progress bar.
refactor
Runs Rector against the current project.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" refactor --fix
Important details:
- without
--fix, Rector runs in dry-run mode; - local
rector.phpis preferred when present; - the packaged default includes Fast Forward custom Rector rules plus shared Rector sets;
- progress output is disabled by default;
--progressre-enables it for text mode; --jsonand--pretty-jsonforward--output-format jsonto Rector and disable its progress bar.
phpdoc
Coordinates PHP-CS-Fixer and a focused Rector pass for missing method PHPDoc.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" phpdoc
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" phpdoc --fix
Important details:
- it creates
.docheaderfrom the packaged template when the file is missing; - it uses
.php-cs-fixer.dist.phpandrector.phpthrough the same local-first fallback logic; - the Rector phase explicitly runs
FastForward\DevTools\Rector\AddMissingMethodPhpDocRector; - cache stays enabled by default;
--cacheforces it on and--no-cacheforces it off for the PHP-CS-Fixer phase; - when
--cache-diris omitted, PHP-CS-Fixer keeps its default cache directory; --cache-dironly affects PHP-CS-Fixer when caching is enabled;- progress output is disabled by default;
--progressre-enables it for text mode; --jsonand--pretty-jsonswitch PHP-CS-Fixer to JSON output with progress disabled and run the Rector phase in JSON mode without its progress bar.
docs
Builds the HTML documentation site with phpDocumentor.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" docs --source=docs --target=.dev-tools
Important details:
docs/must exist unless you pass another--sourcedirectory;- API pages are built from the PSR-4 paths declared in
composer.json; - guide pages are built from the selected source directory;
--templatedefaults tovendor/fast-forward/phpdoc-bootstrap-template;- cache stays enabled by default;
--cacheforces it on and--no-cacheforces it off for a single run; - when
--cache-diris omitted, phpDocumentor keeps its default cache directory; --cache-dironly affects phpDocumentor when caching is enabled;- progress output is disabled by default;
--progressre-enables it for text mode; --jsonand--pretty-jsonalso disable phpDocumentor's progress output.
wiki
Builds Markdown API pages for a GitHub wiki.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" wiki
Important details:
- the default output directory is
.github/wiki; - normal
dev-tools:syncand the direct Wiki workflow explicitly use.dev-tools/wiki-generatedwithout initializing a submodule; --initremains an explicit legacy option for submodule users;- it uses the Markdown template from
vendor/saggre/phpdocumentor-markdown/themes/markdown; - cache stays enabled by default;
--cacheforces it on and--no-cacheforces it off for a single run; - when
--cache-diris omitted, phpDocumentor keeps its default cache directory; --cache-dironly affects phpDocumentor when caching is enabled;- it is especially useful together with the reusable wiki workflow.
reports
Runs the documentation and test-report pipeline used by GitHub Pages.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" reports
Important details:
- it calls
docs,tests --coverage, andmetricswith defaults rooted in the selected DevTools workspace; - with the default workspace, that is equivalent to
docs --target .dev-tools,tests --coverage .dev-tools/coverage --coverage-summary, andmetrics --target .dev-tools/metrics --junit .dev-tools/coverage/junit.xml; docsremains detached, whiletestsandmetricsrun in sequence so PhpMetrics can reuse the JUnit report generated by PHPUnit;- cache stays enabled by default for the nested
docsandtestssteps;--cacheforces that explicit intent on and--no-cacheforces it off; - when
--cache-diris omitted, nested tools keep their own default cache directories; - progress output stays disabled by default across the nested steps, and
--progressre-enables it for human-readable runs; --jsonand--pretty-jsonare propagated to each subprocess, while their progress output is suppressed where supported;- human-readable runs keep nested command output grouped with concise local section boundaries, pass color-friendly environment variables to subprocesses, and forward explicit ANSI flags to Symfony Console tools;
- queued subprocesses run with
XDEBUG_MODE=offwhen Xdebug is loaded but the command does not need Xdebug for coverage, or when PCOV can provide coverage instead; - it is the reporting stage used by
standards.
Skill installation
Use LLM Skills; see Installing Packaged Skills. DevTools no longer installs skill or agent links.
funding
Synchronizes supported funding metadata between Composer and GitHub formats.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" funding
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" funding --dry-run
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" funding --check
Important details:
- it keeps
composer.jsonfundingentries and.github/FUNDING.ymlin sync for GitHub Sponsors andcustomURLs; - it preserves unsupported Composer funding providers and unsupported YAML keys instead of rewriting them;
- it creates
.github/FUNDING.ymlwhen Composer already declares supported funding metadata; - it supports
--dry-run,--check, and--interactiveso funding drift can be surfaced in CI and reviewed locally.
codeowners
Generates managed CODEOWNERS files from repository metadata.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" codeowners
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" codeowners --dry-run
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" codeowners --interactive
Important details:
- it inspects
composer.jsonauthor homepages to infer GitHub handles for.github/CODEOWNERS; - when direct ownership cannot be inferred, it renders a commented fallback instead of copying hard-coded owners into the consumer repository;
--interactivelets maintainers provide explicit owners before writing the catch-all*rule;dev-tools:syncrunscodeownersautomatically alongside the other consumer bootstrap steps.
dev-tools:sync
Synchronizes consumer-facing automation and defaults.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" dev-tools:sync
Important details:
- it updates
composer.jsonscripts and removes only deprecated DevTools-managedextra.grumphp.config-default-pathvalues; - it calls
fundingso supported funding metadata stays aligned betweencomposer.jsonand.github/FUNDING.yml; - it copies missing workflow stubs,
.editorconfig, anddependabot.yml; - it calls
codeownersto generate.github/CODEOWNERSfrom local metadata; - in normal mode it generates local API Markdown in
.dev-tools/wiki-generatedwithout creating a Git submodule; - it calls
gitignoreto merge the canonical .gitignore with the project's .gitignore; - it calls
gitattributesto manage export-ignore rules in .gitattributes; - it copies a missing local
grumphp.ymland refreshes hooks that prefer local GrumPHP, otherwise CPXphpro/grumphp-shim:2.26.0; temporary runtime/cache paths are not stored in hooks;
gitattributes
Manages .gitattributes export-ignore rules for leaner Composer package archives.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" gitattributes
Important details:
- it adds export-ignore entries for repository-only files and directories;
- it only adds entries for paths that actually exist in the repository;
- it respects the
extra.gitattributes.keep-in-exportconfiguration to keep specific paths in exported archives; - it preserves existing custom .gitattributes rules;
- it deduplicates equivalent entries and sorts them with directories before files, then alphabetically;
- it uses CandidateProvider, ExistenceChecker, ExportIgnoreFilter, Merger, Reader, and Writer components from the GitAttributes namespace.
gitignore
Merges and synchronizes .gitignore files.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" gitignore --source=/path/to/source/.gitignore --target=/path/to/target/.gitignore
Important details:
- it reads the canonical .gitignore from dev-tools and merges with the project's existing .gitignore;
- by default, the source is the packaged .gitignore and the target is the project's root .gitignore;
- duplicates are removed and entries are sorted alphabetically;
- it uses the Reader, Merger, and Writer components from the GitIgnore namespace.
license
Generates a LICENSE file from composer.json license information.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" license
Important details:
- it reads the
licensefield fromcomposer.json; - it supports common open-source licenses (MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, GPL-3.0, LGPL-3.0, and MPL-2.0);
- it resolves placeholders such as
[year],[author], and[project]using information fromcomposer.json; - it uses template files from
resources/license-templates/; - it skips generation if a LICENSE file already exists.
copy-resource
Copies packaged or local resources into the consumer repository.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" copy-resource --source <path> --target <path> --overwrite
Important details:
- source is resolved using
FileLocatorInterface; - target is resolved to an absolute path using
FilesystemInterface; - both
--sourceand--targetare required options; - without
--overwrite, existing target files are skipped; - supports both files and directories as sources.
git-hooks
Installs packaged Fast Forward Git hooks.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" git-hooks
Important details:
- copies hook files from source to target directory;
- sets executable permissions on copied hooks with
0o755; - removes an existing drifted hook before recopying it so stale target permissions do not block replacements;
--sourcedefaults toresources/git-hooks;--targetdefaults to.git/hooks;--no-overwritepreserves existing hook files.
update-composer-json
Updates composer.json with Fast Forward dev-tools scripts and metadata.
cpx --skip-local "fast-forward/dev-tools:$DEV_TOOLS_VERSION" update-composer-json --file=composer.json
Important details:
- adds
dev-toolsscript entrypoint to composer.json; - adds
dev-tools:fixscript for automated fixing; - removes only deprecated DevTools-managed
extra.grumphp.config-default-pathvalues; - if the target file does not exist, exits silently with code 0;
- existing scripts with the same name are overwritten;
- preserves consumer-owned GrumPHP settings.
Normalization runtime
Composer Normalize executes as a plugin from the isolated runtime root against
the consumer's absolute manifest path with --no-update-lock
. The consumer
does not need that plugin installed. An explicitly requested dependency upgrade
may still update its Composer lockfile; normalization itself does not.