⚠️ Early Development — MVP Testing Phase This project is under active development and is currently being tested as an MVP. APIs, behaviour, and file formats may change. Not recommended for production use yet. Feedback and bug reports welcome.
A pnpm-inspired global store for Composer packages. Share packages across projects. Stop duplicating
vendor/.
Every Laravel/PHP project has its own vendor/ directory. With 100 projects, laravel/framework is downloaded and stored 100 times. pnpm solved this for Node.js — CompoStore brings the same idea to Composer.
~/.composer-store/packages/
laravel+framework@11.0.0/ ← stored ONCE
filament+filament@3.2.0/ ← stored ONCE
project-a/vendor/laravel/framework/ ← hard linked to store (inode: 269463130)
project-b/vendor/laravel/framework/ ← hard linked to same file (inode: 269463130)
project-c/vendor/laravel/framework/ ← hard linked to same file (inode: 269463130)
Hard links = same inode, no disk duplication, but each project sees its own copy.
vendor/composer/ (autoloader) is always per-project — never shared.
Add CompoStore to your project and composer install works transparently:
{
"require": {
"compostore/composer-store": "*@dev"
},
"config": {
"allow-plugins": {
"compostore/composer-store": true
}
}
}Then just run composer install as usual — CompoStore intercepts library package installs and routes them through the global store automatically.
git clone https://github.com/CompoStore/composer-store
cd composer-store && composer install
# Install packages for a project
./bin/compostore install /path/to/project
# Check store status
./bin/compostore status
# Prune unused packages
./bin/compostore prune --scan ~/projects --dry-runThe current CLI binary name is compostore.
Reads composer.lock, syncs packages to the global store, and hard links them into vendor/.
compostore install # current directory
compostore install /path/to/project
compostore install --no-dev # skip dev dependenciesSupported dist types: zip, tar, tgz/tar.gz, and path.
Shows store location, total packages, and disk usage.
Store Info
Location: /Users/you/.composer-store
Total packages: 142
Total size: 1.2 GB
Stored Packages
✓ laravel+framework@11.0.0
✓ filament+filament@3.2.0
Removes packages from the store that are no longer referenced by any project.
compostore prune --dry-run --scan ~/projects # preview
compostore prune --scan ~/projects # actually removeAll integration fixtures now live under integration/projects/ (no root example folders).
- 10 separate projects using the Composer Plugin approach (
compostore/composer-storeinrequire) - Popular public packages across Symfony, Guzzle, Monolog, Doctrine, Flysystem, and Laravel
- One local private package (
acme/private-toolkit) installed inproject-05 - Hard-link verification for shared files (
psr/log) across projects
integration/
projects/
project-01 ... project-10
private-packages/
acme-private-toolkit/
results/
latest-summary.md
./bin/run-integration-matrix --cleanThis generates per-project logs in integration/results/ and a summary file:
integration/results/latest-summary.md.
- Projects tested: 10
- Successful installs: 10
- Failed installs: 0
- Total elapsed: 62s
- Store packages: 41
- Store size: 14M
- Private package installed: yes (
project-05) - Hard link verification (
psr/log): verified
In addition to fixture projects, a live smoke test was executed with two real laravel/laravel projects created under /tmp, both configured to use CompoStore via Composer Plugin.
- Both Laravel projects completed dependency install with CompoStore plugin enabled
- Both apps ran concurrently with
php artisan serveon separate ports127.0.0.1:8101127.0.0.1:8102
- Health endpoints returned success on both apps
GET /up->200on both
- Both temporary Laravel projects and runtime artifacts were deleted after test completion
- No repository files were changed by this smoke test run
src/
Application.php ← Symfony Console bootstrap
Commands/
InstallCommand.php ← compostore install
StatusCommand.php ← compostore status
PruneCommand.php ← compostore prune
Store/
GlobalStore.php ← manages ~/.composer-store
PackageDownloader.php ← syncs package archives/path repos into store (with integrity checks)
PackageInspector.php ← detects packages with scripts (copy instead of link)
Linker/
VendorLinker.php ← hard links store → vendor/ (or copies for script packages)
AutoloaderGenerator.php ← runs composer dump-autoload
Parser/
LockFileParser.php ← parses composer.lock
Plugin/
CompoStorePlugin.php ← Composer plugin entry point
IOOutputAdapter.php ← bridges Composer IO to Symfony Output
Installer/
CompoStoreInstaller.php ← custom installer for Composer plugin
tests/
Linker/VendorLinkerTest.php
Parser/LockFileParserTest.php
Store/GlobalStoreTest.php
Store/PackageDownloaderTest.php
Store/PackageInspectorTest.php
bin/
compostore ← CLI entry point
run-integration-matrix ← 10-project Composer plugin test runner
integration/
projects/ ← 10 integration fixture projects
private-packages/ ← local private package fixture
results/ ← matrix logs + summary output
- User adds
compostore/composer-storeto their project'scomposer.json composer installinstalls CompoStore and its dependencies first (normal Composer flow)- Plugin activates and registers a custom installer (
CompoStoreInstaller) - All subsequent
librarytype packages go through the store:- Sync phase: package is downloaded/extracted (or copied for
pathrepos) to~/.composer-store/packages/ - Install phase: files are hard linked from store into
vendor/
- Sync phase: package is downloaded/extracted (or copied for
- On re-install: packages already in the store are linked instantly (zero downloads)
- Source-only VCS installs (no dist archive URL) fall back to Composer's default installer
- Packages with
scriptsin theircomposer.jsonare copied, not hard-linked (safe but uses extra space) - Windows not yet supported
- No parallel downloads (sequential)
- Plugin's own dependencies (symfony/console, etc.) install via Composer's default flow
| Phase | Status | Goal |
|---|---|---|
| 1 | Done | CLI MVP — install, status, prune |
| 2 | Done | Composer Plugin — transparent composer install integration |
| 3 | Done | Integrity checks, post-install script safety, PHPUnit test suite |
| 4 | Planned | Windows support, Packagist release, parallel downloads |
- PHP 8.1+
- Symfony Console 6/7
- Composer Plugin API 2.0
- PHPUnit 10+
This project was initially generated and developed with the assistance of Claude (Anthropic) and Codex (OpenAI).
AI tools were used throughout: architecture design, code generation, and documentation. All code has been reviewed, tested, and is maintained by human developers. The ideas, decisions, and responsibility for this software remain with the authors.
MIT