Discover contrib plugins via Composer metadata; custom from .polymer/plugins (PWT-115)#77
Conversation
…plugins findExtensions() now merges two sources so plugins are found regardless of where they are installed: - Contrib plugins: located via Composer\InstalledVersions::getInstalledPackagesByType( "polymer-plugin") + getInstallPath(), so they are discovered from the normal vendor/ install location with no installer-path configuration or extender. - Custom plugins: the existing fixed-depth glob scan of <repo>/.polymer/plugins, for project-local, committed plugins. Local (custom) entries take precedence by extension id. Guards when InstalledVersions / getInstalledPackagesByType is unavailable. Refs PWT-115. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request refactors extension discovery in ExtensionDiscovery.php by separating the logic into two methods: findComposerInstalledExtensions(), which leverages Composer's InstalledVersions metadata to locate plugins, and findLocalExtensions(), which scans local directories. Feedback suggests using the union operator (+) instead of array_merge to prevent key reindexing and enforce precedence, checking for the existence of getInstallPath to ensure compatibility with older Composer versions, and using basename() with a suffix parameter to safely extract extension names.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
With contrib plugins discovered by type from their normal vendor install location, Polymer no longer places packages at custom install paths, so the installer machinery is unnecessary. Remove oomphinc/composer-installers-extender (archived upstream) and mnsami/composer-custom-directory-installer from require and allow-plugins. Refs PWT-115. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Combine discovery sources with the union operator instead of array_merge, so local entries take precedence and numeric-looking extension ids are not reindexed. - Also guard for InstalledVersions::getInstallPath() (added in Composer 2.1). - Strip the .poly_info.yml suffix with basename($file, ...) rather than str_replace. Refs PWT-115. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Makes plugin discovery independent of where plugins are installed, so contrib plugins can live in the normal
vendor/location and project-local custom plugins live under.polymer/plugins— no per-project installer-path configuration required.Refs PWT-115.
What changed
ExtensionDiscovery::findExtensions()now merges two sources:Composer\InstalledVersions::getInstalledPackagesByType('polymer-plugin')+getInstallPath(), then reading each package's*.poly_info.yml. They're discovered wherever Composer installs them (vendor/by default), so noinstaller-paths/extender machinery is needed.<repo>/.polymer/pluginsfor project-local, committed plugins.Local (custom) entries take precedence by extension id. Guarded when the Composer runtime API (
getInstalledPackagesByType) is unavailable.Context
PWT-115 originally aimed at remappable installer paths. While implementing, two heavier approaches were explored and rejected after verification:
activate()— doesn't work (composer/installers-extender readsinstaller-typesat its own activation; plugin ordering means the injection is too late and the package falls back tovendor/).extension_pathsdiscovery key — dropped, since managing install locations turned out not to be desired.The accepted design above is simpler and removes the need to manage install locations at all.
Testing
installer-pathsremoved from the projectcomposer.json,composer installplaces contrib plugins invendor/digitalpolygon/*, andpolymer plugin:listdiscovers them from there; all extension commands register;plugin:enable/disablegating still works (0 ↔ 7 commands).🤖 Generated with Claude Code