Search by

devkit / composer-link

stuarttodd-dev

Link local Composer packages via path repositories. Composer plugin: `composer link`, `composer add`, `link-help`, `local-bootstrap`, `local-install`, and related commands.

Package info

github.com/Half-Shell-Studios/devkit-composer-link

Type:composer-plugin

pkg:composer/devkit/composer-link

Fund package maintenance!

Other

Statistics

Installs: 1 984

Dependents: 0

Suggesters: 0

Stars: 4

Open Issues: 0

v1.1.0 2026-09-13 16:33 UTC

This package is auto-updated.

Last update: 2026-09-13 16:49:33 UTC


README

Composer Link

devkit-composer-link

Local packages. Fewer emotional incidents.

PHP Packagist License Tests

Package: devkit/composer-link

Table of Contents

Why this exists

You know the dance.

You need to change a package. You open the app’s composer.json, add a path repository, force a constraint, run Composer, test the thing, then carefully undo everything so main doesn’t ship ../Desktop/my-fork forever.

It’s fine once. It’s miserable every day.

Composer Link exists so you can point dependencies at real folders without hand-editing the committed manifest like it’s 2004 — and come back tomorrow with the same setup still waiting for you.

How it works

Composer Link is a Composer plugin installed in your app. It keeps two worlds separate:

Shared with the team Yours only (gitignore these)
composer.json packages-local.json — link state
composer.lock composer.local.json — local manifest with path repos
composer.local.lock — lock for that local manifest

Commands like link / add write to the local files. CI and teammates keep using the committed baseline. Locally you run composer local-install (or set COMPOSER=composer.local.json) so vendor/ matches your checkout.

When you’re done experimenting, unlink or promote puts things back on a published constraint — without the archaeology.

Run everything from your application root (where the committed composer.json lives). The plugin only registers when devkit/composer-link is installed as a dependency of that app.

Use case — test a package without wrecking main

Imagine your app depends on your-vendor/payments: ^1.5. You’ve got a local checkout with a bugfix. You want to prove it in the real app before you tag.

# once per clone (or first link/add creates it for you)
composer local-bootstrap

# point the existing dependency at your checkout
composer link your-vendor/payments ../packages/payments

# install against the local manifest
composer local-install

# sanity check
composer linked
composer link-doctor

Need @dev because the local branch doesn’t satisfy ^1.5?

composer link your-vendor/payments ../packages/payments --constraint=@dev

Bootstrapping something that isn’t in the committed manifest yet?

composer add your-vendor/new-thing ../packages/new-thing

Later, when it’s on Packagist:

composer promote your-vendor/new-thing ^1.0

A whole folder of checkouts? Scan them:

composer link-scan /opt/packages --vendor=your-vendor

Prerequisites

Requirement Version
PHP 8.5+
Composer 2.2+ (plugin allow-list)

Install

Install in the consuming application (not inside the library you’re editing):

composer require --dev devkit/composer-link

Allow the plugin (Composer 2.2+):

{
  "config": {
    "allow-plugins": {
      "devkit/composer-link": true
    }
  }
}

Then:

composer link-help

Commands at a glance

Command In one sentence
link Override an existing dependency to a local path.
add Bootstrap a local package that isn’t in the committed manifest yet.
unlink Drop local override state; restore or remove the requirement.
promote Switch a locally managed package to a published constraint.
linked List everything Composer Link is currently managing.
refresh Rebuild managed path repos from packages-local.json.
link-doctor Check ignore rules, paths, and managed repositories.
local-bootstrap Copy committed manifest/lock into local files.
local-install composer install via composer.local.json.
link-scan Discover matching packages in a directory and link / add them.
link-help Terminal cheat sheet for arguments and options.

Configuration

Optional settings live under extra.composer-link in the app’s root composer.json:

{
  "extra": {
    "composer-link": {
      "overrides_file": "packages-local.json",
      "local_composer_json": "composer.local.json"
    }
  }
}
Key Role
overrides_file Local state file (packages, paths, mode, constraints).
local_composer_json Local Composer manifest used for path installs.

Files and folders

packages-local.json     <- plugin state (gitignore)
composer.local.json     <- local manifest + path repos (gitignore)
composer.local.lock     <- local lockfile (gitignore)
composer.json           <- committed baseline
composer.lock           <- committed baseline

Legacy note: if packages-local.json is missing/empty, composer.local-packages.json is still read as a fallback; the next write saves to packages-local.json.

Commands

Use composer help <command> for full flag docs.

link — override an existing dependency

composer link my-vendor/my-package ../packages/my-package
composer link my-vendor/my-package ../packages/my-package --constraint=@dev
composer link my-vendor/my-package ../packages/my-package --no-update
composer link my-vendor/my-package ../packages/my-package --no-symlink

add — bootstrap a new local dependency

composer add my-vendor/new-lib ./libs/new-lib
composer add my-vendor/new-lib ./libs/new-lib --no-dev
composer add my-vendor/new-lib ./libs/new-lib --constraint=^0.1

unlink — stop managing a package locally

composer unlink my-vendor/my-package
composer unlink my-vendor/experimental-package --remove   # required for bootstrap packages

promote — move to a published constraint

composer promote my-vendor/my-package ^1.5
composer promote my-vendor/my-package ~2.3.0 --no-update

linked — show managed packages

composer linked

refresh — rebuild managed path repos

composer refresh
composer refresh --no-update

link-doctor — validate setup

composer link-doctor

Checks ignore rules, linked path existence, and managed path-repo count.

local-bootstrap — create local manifest files

composer local-bootstrap
composer local-bootstrap --force

local-install — install via local manifest

composer local-install
composer local-install --no-dev
composer local-install --prefer-dist

Equivalent to:

COMPOSER=composer.local.json composer install

link-scan — discover and link a directory of checkouts

composer link-scan /opt/packages
composer link-scan ../ --vendor=halfshell --vendor=halfshellstudios

Each matching package is linked if it’s already in the committed manifest, or added as require-dev if it isn’t. The host project itself is skipped.

link-help — command summary in terminal

composer link-help

Typical workflows

Override a released package

composer link your-vendor/your-package ../packages/your-package

Bootstrap before Packagist

composer add your-vendor/new-package ../packages/new-package
composer promote your-vendor/new-package ^1.0   # later

Day-to-day local loop

composer local-bootstrap
composer link vendor/package ../path/to/package
composer local-install

Version control and safety

Commit the shared baseline. Keep local override artifacts out of Git.

packages-local.json is Composer Link state only — Composer itself reads whichever manifest COMPOSER points at.

Before merging release work, align committed composer.json / composer.lock with the published constraints the team should install (promote, unlink, or normal edits).

Support

If this project saves you time (and a few Friday-afternoon incidents):

Buy Me a Coffee

Changelog

See CHANGELOG.md for release notes by tag.

License

MIT