Plugins
Composer plugins are PHP code that hooks into Composer's own process; viv has no PHP runtime, so it can't run one as written. Instead:
- A small set of common plugins —
composer/installers, the WordPress core installers, and native adapters for Yii2, Craft, Drupal scaffolding, Symfony runtime, phpcs, PHPStan and more — are reimplemented in viv itself, so the outcome matches Composer's. - A few plugins are known to only affect Composer commands viv doesn't implement; viv ignores them, same as Composer does when a plugin is disabled.
- Any other plugin stops the install with an error naming the plugin.
--no-pluginsturns that into a warning and installs the way Composer's own--no-pluginswould.
The full list of which plugin falls into which category is documented separately.
Inventory
Plugins found in the lock files available on 2026-09-06 (four public projects, the Laravel benchmark lock, two test fixtures and one private WordPress project):
| Plugin | What it changes | Portable? |
|---|---|---|
| composer/installers | Install path per package type from extra.installer-paths |
Native (src/plugins/installers.rs), pure path mapping |
| johnpbloch/wordpress-core-installer | Install path of wordpress-core packages from extra.wordpress-install-dir |
Native (src/plugins/wordpress_core.rs), pure path mapping |
| dealerdirect/phpcodesniffer-composer-installer | Points phpcs's installed_paths at every installed standard |
Native (src/plugins/phpcs.rs); writes CodeSniffer.conf directly rather than shelling out to phpcs --config-set, so an install needs no PHP runtime (#218) |
| phpstan/extension-installer | Writes GeneratedConfig.php listing every extra.phpstan package |
Native (src/plugins/phpstan.rs) |
| php-http/discovery | Adds packages to the resolver and generates a discovery file | Native (src/plugins/discovery.rs), preAutoloadDump/extra.discovery only; the resolver half (postUpdate) isn't ported |
| tbachert/spi | Generates a service-provider map file after autoload dump | Native (src/plugins/spi.rs), extra.spi only |
| cweagans/composer-patches | Applies patches from extra.patches/a patches file |
Native (src/plugins/patches.rs), git-apply path only (#53) |
| yiisoft/yii2-composer | Writes vendor/yiisoft/extensions.php listing every yii2-extension package |
Native (src/plugins/yii2.rs) (#92) |
| craftcms/plugin-installer | Writes vendor/craftcms/plugins.php listing every craft-plugin package |
Native (src/plugins/craft.rs) (#92) |
| pestphp/pest-plugin | Writes vendor/pest-plugins.json listing every package's extra.pest.plugins, root last |
Native (src/plugins/pest.rs) (#131) |
| ffraenz/private-composer-installer | Substitutes {%NAME}/{%version} placeholders in a dist URL from the environment/.env right before download |
Native (src/plugins/private_installer.rs) (#98) |
| codeception/c3 | Copies its bundled c3.php into the project root on install/update, unless an existing, edited one is there |
Native (src/plugins/c3.rs) (#126) |
| drupal/core-composer-scaffold | Copies scaffold files (index.php, .htaccess, settings.php, …) from every allowed package, manages .gitignore, writes vendor/drupal/DrupalInstalled.php and points the root classmap at it |
Native (src/plugins/drupal_scaffold.rs) (#93) |
| drupal/core-project-message | Prints a message to stdout after create-project/install, no filesystem effect |
Known inert — Composer prints a message viv does not |
| drupal/core-recipe-unpack | Unpacks a required drupal-recipe package's own dependencies into the root composer.json |
Known inert for install/update — only subscribes to POST_UPDATE_CMD/POST_CREATE_PROJECT_CMD (via composer require/create-project), never plain install |
| symfony/runtime | Writes vendor/autoload_runtime.php from a fixed template plus extra.runtime options, after autoload dump |
Native (src/plugins/symfony_runtime.rs) (#93) |
Plugins named in issue #12 but not seen in any lock yet: symfony/flex
(rewrites composer.json and recipes, not portable), bamarni/composer-bin-plugin
(nested installs, portable by running viv in each vendor-bin/*).
Rule
For every package of type composer-plugin in the lock that is enabled by
config.allow-plugins:
- Native adapter exists: viv applies the equivalent behaviour itself. Output must be byte-identical to Composer running the real plugin.
- Known inert: the plugin only affects Composer's own commands that viv does not implement (for example ergebnis/composer-normalize). viv ignores it silently. The list lives in code.
- Anything else: viv refuses with an error naming the plugin and pointing
at this page.
--no-pluginsturns the refusal into a warning and installs as Composer would with--no-plugins. Silent wrong installs are worse than a loud stop. The refusal also says whether--no-pluginsis safe: plugins inBY_DESIGN_REFUSALS(src/plugins/mod.rs) get "by design, you lose nothing" with the one-clause reason from this page; everything else gets the generic "not adapted yet, this skips real work" (#224).
Plugins that allow-plugins sets to false, or that are absent from the map,
are ignored, as Composer ignores them.