ADR 2025007: CLI Plugin API Implementation

Status

Accepted – Implementation in Progress

Context

Following the successful modernization of OmegaT’s CLI with PicoCLI (ADR-2024001), the current system still lacks plugin integration capabilities. Historically, this created architectural problems where core CLI functionality depended on plugin modules, requiring “reflection hacks” and tight coupling.

The Aligner module exemplified this limitation: it was a bundled subproject with its own entry points, but the core CLI would have needed reflection to call it, violating separation of concerns.

Current State

  • Modern CLI structure with org.omegat.cli package.

  • BaseSubCommand Architecture: A lightweight bridge has been implemented to allow subcommands to be registered by modules and plugins at runtime.

  • Consolidation: This ADR now consolidates the proposed Plugin API with the implemented BaseSubCommand architecture.

  • Registry: SubCommands class manages the mapping between command names and BaseSubCommand implementations.

  • CLIParameters: The parser has been extended to support subcommands and raw arguments, allowing for commands like OmegaT team init or OmegaT align.

  • PicoCLI Status: PicoCLI is the long-term goal for full CLI modernization, but the current implementation uses a manual bridge to provide immediate plugin extensibility.

Decision

We will implement a CLI Plugin API that supports two distinct integration patterns: Command Contribution (already partially implemented via BaseSubCommand) and Event Hook Integration (planned), eliminating architectural debt and maintaining proper separation of concerns.

Core Components

1. Command Contribution API

Plugins and modules contribute CLI functionality by extending BaseSubCommand and registering it with the Core registry.

  • BaseSubCommand: Abstract base class implementing Callable<Integer>.

  • Registration: Plugins call Core.registerConsoleCommand(String name, Class<? extends BaseSubCommand> clazz) during initialization.

  • Execution: Main.java utilizes the SubCommands registry to instantiate and execute the command.

2. Command Implementation Pattern

Plugin modules provide their own command classes extending the base:

// In Aligner plugin module
public class AlignCommand extends BaseSubCommand {
    @Override
    public Integer call() throws Exception {
        // Direct call to Aligner functionality
        // Access parameters via the 'params' field inherited from BaseSubCommand
        String alignDir = params.get(CLIParameters.ALIGNDIR);
        // ... implementation ...
        return 0;
    }
}

3. Extend CLIParameters parser to accept subcommand (Phase 2)

The CLIParameters parser has been extended to accept subcommands and raw arguments, allowing the Main class to parse the command line arguments and identify the intended execution mode. Main.main() currently uses a switch statement that dispatches to the appropriate runMode based on the --mode argument or the identified subcommand. We will eventually replace this with a lookup in the SubCommands registry.

4. Migrate team init feature to use subcommands (Phase 3)

The TeamTool class will be refactored to use the new BaseSubCommand architecture.

5. Future PicoCLI Migration (Phase 4)

As we transition to PicoCLI (ADR-2024001), the BaseSubCommand architecture will be adapted:

  • BaseSubCommand implementations will be decorated with PicoCLI @Command and @Option annotations.

  • The registry will be integrated with the PicoCLI CommandLine instance for dynamic subcommand discovery.

Implementation Roadmap

Phase 1: Modular SubCommands (Completed)

  • Implement BaseSubCommand and SubCommands registry.

  • Add Core.registerConsoleCommand API.

  • Refactor AlignCommand to use this architecture, removing reflection hacks.

Phase 2: Extend CLIParameters parser to accept subcommand (In Progress)

  • Extend CLIParameters parser to accept subcommands.

  • Migrate GUI/CLI dispatch from runMode switch to subcommand registry lookup in Main class.

Phase 3: Migrate team init feature to use subcommands (Planned)

  • Migrate TeamTool.main to use subcommand architecture.

  • Deprecate --mode=<runMode> CLI argument and remove in favor of subcommand dispatch.

Phase 4: Full PicoCLI Integration (Planned)

  • Integrate SubCommands registry with PicoCLI.

  • Migrate Main argument parsing to PicoCLI.

Consequences

Positive

  • Decoupling: Core CLI remains independent of plugin implementations.

  • Extensibility: Plugins can contribute first-class CLI commands.

  • Bridge to Future: Provides an immediate solution while maintaining a path to full PicoCLI modernization.

Negative

  • Hybrid State: The coexistence of manual parsing and a subcommand registry adds temporary complexity.