`defineLibraryPlugin` has taken a `launchers()` hook since kit 0.3.0 — entries that open the LAUNCHER rather than a title (design D4) — and nothing implemented it. The host has resolved `launcher_ui` valued `"lutris"` since the same release. So the whole path existed end to end with no producer at either end. One entry, config-toggled and on by default: it is the one tile that lets someone install or fix a game from the couch, and it costs a single card. `launcher_ui` is valued by STORE ID, never a command: the host turns "lutris" into the command that opens the window (D1). That is also the only shape available to a plugin at all — the 2026-08-05 review made `launch.kind = "command"` operator-only, so a plugin publishing one has its entire reconcile refused. Deliberately art-less. Lutris ships a square app icon and every client cover-crops a 2:3 tile, so an icon would arrive as a cropped strip. The clients render an art-less launcher entry on purpose — accent face, the launcher named — which reads as "opens Lutris" rather than as a game whose poster failed to load. `launcherEntries` is exported for the same reason `artFile` is: so the shape is testable without standing up the whole plugin. Gates: tsc --noEmit clean, 5 tests pass (2 new), biome clean. Needs a host carrying the M2 wire (`role` + `launcher_ui`), which is on main.
@punktfunk/plugin-lutris
Adds your installed Lutris games to a Punktfunk host's library — with their cover art, and with the session ending when you quit the game.
Install it from the host's plugin store (Library → Game sources → Add a source), or:
punktfunk-host plugins add lutris
Nothing to configure: it finds pga.db in the usual places (XDG data dir, ~/.local/share/lutris,
Flatpak) and syncs from there. If yours lives somewhere else, set the path in
Library → Game sources → Lutris → Settings.
What it replaces
Punktfunk used to scan Lutris from inside the host. This plugin is that scanner, lifted out — so you install it only if you use Lutris, and it can be updated without updating the host.
It claims the lutris store, which means your games keep exactly the ids they had before:
Moonlight pins, cached box art, and your enabled/disabled state all carry over, and the host's
built-in scanner switches itself off while this is installed. Remove the plugin and the built-in
comes straight back.
Commands
punktfunk-plugin-lutris detect # is Lutris installed on this host?
punktfunk-plugin-lutris scan --preview # what WOULD be synced, as JSON (works with no host)
punktfunk-plugin-lutris uninstall # drop the games and release the store claim
Parity — the release gate
Unit tests pin the parsing. What actually has to hold is that this plugin reproduces the in-host
scanner it replaces, down to the ids — a plugin that parses perfectly but emits lutris:12.0
instead of lutris:12 breaks every Moonlight pin on the host, and no unit test notices.
So, on a box with Lutris installed, before installing this plugin:
punktfunk-plugin-lutris parity --snapshot before.json # host still on its built-in scanner
punktfunk-plugin-lutris parity --compare before.json # offline; exits non-zero on any difference
--compare runs this plugin's own scan and diffs it: ids, titles, launch recipes, roles and
metadata exactly; art by presence, not value — the representation legitimately changes here
(the host's scanner inlined covers as data: URLs, this emits file:// paths the host proxies),
so spot-check a few covers by eye once.
Do not publish a version whose parity run is red.
Development
bun install
bun run typecheck && bun test && bun run build
The framework — store claim, sync engine, __config, CLI verbs, and the launcher-file parsers —
lives in @punktfunk/plugin-kit/library.
This package is the Lutris-specific part: find pga.db, read it, map rows to entries. That is the
whole design — see design/library-scanner-plugins.md in the main repo.
License
MIT OR Apache-2.0.