Contribution guide

Candidates for a new package manager come from mpm’s own coverage map. The Benchmark holds one row per tool any comparable wrapper drives, so a blank cell in its mpm column marks a tool nobody has assessed yet: that is the worklist. Unsupported managers is the other half of the map, recording each tool already declined and the reason, which is worth checking before proposing one.

Document a new package manager

Not a coder? No problem.

You can still provide invaluable information. Open a new issue and fill in the form with raw output of CLI calls to your manager. Armed with this critical data, a contributor or maintainer can attempt a blind implementation. From there we’ll collectively iterate until we reach a usable level.

This is often the best approach, as it is sometimes hard to create the same environment as the users.

Code support for a new package manager

If you’re a Python developer, see the Add a new package manager guide for the full implementation checklist: module structure, registration, testing, and documentation updates.

Development environment

Setup environment

Check out latest development branch:

$ git clone [email protected]:kdeldycke/meta-package-manager.git
$ cd ./meta-package-manager
$ git checkout main

Install package in editable mode with all development dependencies:

$ python -m pip install uv
$ uv venv
$ source .venv/bin/activate
$ uv sync --all-extras --all-groups

Test mpm development version

After the steps above, you are free to play with the bleeding edge version of mpm:

$ uv run -- mpm --version
(...)
mpm, version 4.13.0

Unit-tests

Run unit-tests with:

$ uv sync --group test
$ uv run -- pytest

Which should be the same as running non-destructive unit-tests in parallel with:

$ uv run pytest --numprocesses=auto --skip-destructive

Destructive tests mess with the package managers on your system. The safe local invocation runs them sequentially:

$ uv run pytest --numprocesses=0 --skip-non-destructive --run-destructive

The sequential command cannot interleave sudo prompts and spares a workstation the parallel load. CI runs the destructive tests in parallel instead, one scheduling group per backend lock: tests.destructive_plan and the collection hook of tests.conftest hold the details.

Type checking

The typing group carries the stub packages alone, so mypy itself rides along as an overlay:

$ uv run --with mypy --group typing mypy meta_package_manager

Documentation

Build Sphinx documentation locally:

$ uv sync --group docs
$ uv run -- sphinx-build -b html ./docs ./docs/html

The docs group resolves on Python 3.14 and above. A venv built on an older interpreter resolves the group to nothing, so sphinx-build is absent rather than broken: see the comment on dependency-groups.docs in the [tool.uv] section of pyproject.toml.

Add --fresh-env after an edit to a generator of meta_package_manager/_docs.py, to a manager docstring or to changelog.md: an incremental build renders the previous output otherwise.

The generation of API documentation is covered by a dedicated workflow.

Stability policy

This project more or less follows Semantic Versioning.

Which boils down to the following these rules of thumb regarding stability:

  • Patch releases: 0.x.n → 0.x.(n+1) upgrades

    Are bug-fix only. These releases must not break anything and keep backward-compatibility with 0.x.* and 0.(x-1).* series.

  • Minor releases: 0.n.* → 0.(n+1).0 upgrades

    Includes any non-bugfix changes. These releases must be backward-compatible with any 0.n.* version but are allowed to drop compatibility with the 0.(n-1).* series and below.

  • Major releases: n.*.* → (n+1).0.0 upgrades

    Make no promises about backwards-compatibility. Any API change requires a new major release.

  • Unmaintained managers: managers whose unmaintained flag is set

    Are exempt from the rules above. An unmaintained manager may be removed, in part or in full, in any release and without notice, once keeping it working becomes too burdensome. It is hidden from the default selection and kept out of the test matrices. The criteria for the flag are those of the unmaintained attribute, and each flagged manager states its evidence on its own page.

claude.md file

Project-specific guidance for working in this repository. The generic coding conventions load from the maintainer’s machine configuration and are deliberately not duplicated here: this file carries only what is specific to mpm, and only what has no closer home.

Project overview

Meta Package Manager (mpm) is a CLI that wraps multiple package managers (Homebrew, apt, pip, npm, etc.) behind a unified interface. It can list, search, install, upgrade, and remove packages across all supported managers simultaneously, and snapshot the whole inventory to a single file that restores it on another machine.

Upstream conventions

This repository uses reusable workflows from kdeldycke/repomatic and follows the conventions established there. Propose a gap or an improvement in those workflows at kdeldycke/repomatic.

Where the rules live

Each rule sits beside the code or the page it governs: a docstring, a comment, a test or a docs page. Read the home of an area before an edit in that area. A new rule goes to its home, and this file gets one row at most.

Area

Home

Development commands, stability policy

docs/contributing.md

New manager, declined tool, manager docstrings, argv conventions

.claude/skills/add-manager/SKILL.md

Manager attributes, unmaintained policy

meta_package_manager/manager.py

Docs generators, mirror regions, glyph scale, page layout (MANAGER_SECTIONS)

meta_package_manager/_docs.py

Docstring fixtures: shell-session against console

meta_package_manager/docstring_corpus.py

Fan-out, lock families (SHARED_LOCK_FAMILIES), ✓/✘ trail

meta_package_manager/dispatch.py

Spinner, live line, timeouts, --plan capture

meta_package_manager/execution.py

Logging tiers, exit codes

meta_package_manager/cli.py

Cooldown vocabulary of the product

meta_package_manager/cooldown.py, docs/cooldown.md

Labels and labeller rules (MANAGER_LABELS)

meta_package_manager/labels.py

Brewfile export

meta_package_manager/brewfile.py, docs/dump.md

Benchmark data and cell evidence

docs/benchmark.toml, docs/benchmark.md

Packaging channels, install tabs, distributor jobs

docs/add-packaging-channel.md, .github/workflows/tests-install.yaml

Brand assets, vendored logos

docs/brand_update.py, docs/logos_update.py

Captured screenshots

.github/workflows/docs-screenshots.yaml and its two drivers in docs/

Test suite conventions

tests/__init__.py, tests/conftest.py, tests/test_cli.py, tests/destructive_plan.py

Manager member order (CANONICAL_ATTRS)

tests/test_managers.py

Test matrix, coverage floor

pyproject.toml

Cooldown on every install

mpm --cooldown applies the same idea to a different subject, and the two are easy to conflate here. That flag is a user-facing feature, gating the packages mpm installs on the user’s machine, and docs/cooldown.md is its documentation and its inventory of managers. This section covers what CI resolves onto a runner while building mpm itself. A comment or changelog entry naming one should not read as the other.

Documented exemptions

Three installs deliberately bypass the window:

  • The upstream toolkit’s own pin. Every uvx call carrying the repomatic pin passes --exclude-newer-package repomatic=P0D beside it: the pin moves in lockstep with the uses: refs, so a release must install the minute it is published.

  • A fresh click-extra or extra-platforms, on the day it ships. Both share a maintainer with mpm, and most mpm releases raise one of their floors. [tool.uv] exclude-newer-package names the package with the midnight following its upload, an absolute timestamp that covers that release and nothing after it. The entry is transient: repomatic’s sync-uv-lock prunes it once the release ages past the window.

  • The tests-install.yaml workflow. Its subject is the freshly published artifact. Its header comment holds the rationale.

Documentation conventions

  • Example data. Do not reach for software-engineering or packaging vocabulary for a placeholder, and never invent a plausible-looking package or manager name: this project’s whole domain is package metadata, so a made-up foo-lib 1.2.3 in a docstring is indistinguishable from a real fixture and will eventually be read as one. The [samples] fixtures and the harvested shell-session blocks are captured CLI output: those are data, not examples.

  • Changelog scope tags. Every bullet of changelog.md opens with a [scope] tag that selects the pages it renders on. The docstring of scope_changelog() in meta_package_manager/_docs.py holds the rules to choose one.

  • readme.md. Update the relevant section when a change reaches the public surface.

  • Generated content. Never edit by hand a stub of docs/managers/ or a <!-- mirror --> region: docs/docs_update.py and click-extra refresh-directives own them.

  • Manager IDs link to their pages. A manager named as a code span in docs/*.md links to its own page, once per paragraph: a name repeated in the next sentence stays plain, and an enumeration is uniformly linked. Three places keep the bare span: a heading, where a link would rewrite the anchor other pages cross-reference; the column headers and competitor cells of the benchmark, which name rival tools; and the docs/cooldown.md cells that the manager pages reuse verbatim, where a relative target lands nowhere.

Code conventions

  • Commit prefix. A [bracketed] commit prefix is reserved for a mechanism that parses it back, and only [changelog] … qualifies, matched literally by repomatic’s auto-tagging job. The [scope] tags of changelog.md are an unrelated vocabulary.

  • Workflow file naming. Related workflows share a prefix for visual grouping in the file listing: tests.yaml and tests-install.yaml. Apply the same pattern to a new workflow file.