Plugins

tn-venv has a small, deliberately narrow plugin system. It exists for one job: letting third-party packages hook into the creation pipeline (and the CLI) without monkey-patching tn-venv’s internals. If you want to react to a virtual environment being created, extend the CLI, or contribute to --help, you write a Plugin.

This directory is the authoritative reference for everything plugin-shaped in tn-venv. The other docs (docs/guide/plugins.md, docs/reference/python-api.md, docs/reference/cli.md, docs/development/architecture.md) only point at it.

Contents at a glance

Page

What it covers

Quickstart

Minimal “write your first plugin” recipe. Start here.

Discovery

How plugins are loaded (built-ins, entry points, TN_VENV_PLUGINS), deduplication, error isolation.

Hooks

The six lifecycle hooks and the HELP_EPILOG hook: when they fire, what payload they get, and how priority works.

Authoring

The full Plugin class API: register(), HookContext, PluginSource, HookRegistry.

CLI integration

tn-venv --list-plugins, customizing --help via HELP_EPILOG, monkey-patching cli_run for subcommands.

Reference

One-screen API table for everything in tn_venv.plugins.

Plugin registry

Plugin registry: tiered rating (Maintenance / Stability / Documentation / Adoption), the Official Authorized badge, monthly selections.

Public API surface

Everything below is importable from tn_venv.plugins and considered backward-compatible. Anything not listed here is internal.

from tn_venv.plugins import (
    # Discovery
    Plugin,                # base class — subclass and override register()
    load_plugins,          # build a populated HookRegistry
    parse_plugin_spec,     # split a TN_VENV_PLUGINS-style value
    require_plugin,        # look up a built-in by name

    # Hooks
    HookName,              # enum of all hook names
    HookContext,           # passed to every lifecycle-hook listener
    HookRegistry,          # dispatcher with priority + fault isolation

    # Provenance
    PluginSource,          # dataclass describing where a plugin came from

    # Constants
    PLUGIN_ENTRY_POINT,    # "tn_venv.plugins"
    PLUGIN_ENV_VAR,        # "TN_VENV_PLUGINS"
    DEFAULT_PRIORITY,      # 100

    # Built-ins
    VersionStampPlugin,    # writes tn-venv-version into pyvenv.cfg
)

The same names are also re-exported from tn_venv itself for programmatic callers:

from tn_venv import (
    Plugin, HookContext, HookName,
    VersionStampPlugin, load_plugins,
)

When to use a plugin

Reach for a plugin when you want to:

  • React to a virtual environment being created (SESSION_START, SESSION_END, …). Useful for emitting telemetry, stamping metadata into files, kicking off post-creation installers, etc.

  • Add an extra --flag or --no-prompt — wait, you can’t. Options live in tn_venv.config.spec. Use a CLI subcommand instead.

  • Add a CLI subcommand like tn-venv gui …. Plugins that wrap tn_venv.cli.cli_run directly are supported but should be treated as a transitional API (see CLI integration).

  • Customise the text printed by tn-venv --help. Use the HELP_EPILOG hook.

Reach for a seeder instead if you want to swap the package installer (uv, poetry, …). Seeders are a separate extension mechanism; see Extending tn-venv.

What plugins are not

Plugins are deliberately limited. They are not:

  • A way to register new CLI flags. Option specs are static; if you need a new --flag, send a PR to tn-venv.config.spec.

  • A way to swap the creator, the activator, or any other stage directly. Those have their own protocol.

  • Threadsafe. The pipeline is single-threaded; concurrent calls from the same process are not supported.

  • A remote-execution surface. Plugins run in-process, in the same interpreter that called tn_venv.create_venv().

Versioning policy

The plugin contract is part of tn-venv’s public API surface. Anything documented here is covered by the project’s versioning policy — i.e. breaking changes ship behind a 0.x → 0.(x+1) bump or 1.x.y → 1.(x+1).0. The Plugin class, HookName enum, HookContext dataclass, and HookRegistry API will not break without a deprecation cycle.

The Plugin._tn_venv_source attribute is part of the public API as of 1.1.0 (it’s read by tn-venv --list-plugins), but the leading underscore signals “set by the loader, not by the plugin author”. Plugins should treat it as read-only.

Adding your plugin to the registry

If you have a plugin for this project, add it to the Plugin registry registry by opening a PR against docs/plugins/community.md. The maintainers will review it within 14 days and fill in the four rating columns (Maintenance / Stability / Documentation / Adoption), the tier badge, and — if appropriate — the Official Authorized cell.

A typical plugin README badge block looks like this:

![Maintenance 4/5](https://img.shields.io/badge/maintenance-4%2F5-brightgreen)
![Stability 5/5](../_static/badges/a786e34a0be2.svg)
![Documentation 5/5](../_static/badges/875446cd1469.svg)
![Adoption 4/5](../_static/badges/0b36c57e2902.svg)
![Tier platinum](../_static/badges/f33246a09b87.svg)
![Official Authorized](../_static/OfficialAuthorized.svg)

The exact badge URLs for each row in the registry are listed on the Plugin registry page. Copy the row that corresponds to your plugin’s current tier; the maintainers will re-sync your README badges when they refresh the registry at the start of each monthly review cycle.