# TaskerBridges: Phred CLI Interoperability Adapters TaskerBridges is the adapter layer that allows commands written against `Phred\ConsoleContracts` to run inside multiple CLI runtimes with a native experience. Bridges translate Phred's runner-agnostic contracts into the APIs of specific console toolkits. Project Phase: Idea (NOTES.md) ## 1. Core Vision - Interoperability first: Write a command once, run it anywhere (Tasker native runner, Symfony Console, Laravel Artisan, etc.). - Contracts-driven: Bridges never change command code; they only adapt `ConsoleContracts` to the target runtime. - Minimal footprint: Optional, per-bridge dependencies and lazy wiring. No heavy transitive dependencies for users who don't need a given bridge. - Native UX: When running under a given toolkit, output, helpers, and ergonomics should feel native to that toolkit. ## 2. Scope & Responsibilities Bridges provide a cohesive set of adapters: - Command adapter: Wraps `CommandInterface` and exposes a native command entrypoint. - IO adapters: Implement `InputInterface`/`OutputInterface` on top of the target toolkit’s input/output. - Helper adapters: Implement optional helper contracts (`InteractionInterface`, `ProgressBarInterface`, `TableInterface`, `MarkdownConverterInterface`). - Markup translator: Converts Phred’s fixed markup tags (``, ``, ``, ``, ``, ``, ``, ``) to the target toolkit’s styles or to raw ANSI. - Policy alignment: Honor `Verbosity` levels, non-interactive mode, TTY/color detection, and `sysexits.h` exit code semantics. Non-goals: - Defining new business logic or command semantics. - Extending the fixed markup set (bridges must support the core set only). ## 3. Supported Bridges (Initial Set) - SymfonyBridge: Adapter for `symfony/console` (targeting Symfony 6/7 lines). - PhredBridge (Native): Minimal adapters used by the Tasker core runner. - LaravelBridge: Adapter for Laravel's Artisan console (`illuminate/console`). ### 3.1 Future / Potential Bridges (Roadmap) The following bridges are identified as high-value for future development or upon community request: - **LaminasBridge**: Adapter for `laminas/laminas-cli`. - **CakePHPBridge**: Adapter for the CakePHP Console/Shell ecosystem. - **YiiBridge**: Adapter for Yii 2.0 console commands (Note: Yii 3.0 uses `symfony/console` and may be covered by `SymfonyBridge`). - **AuraCliBridge**: Adapter for `Aura.Cli` for users of the Aura library suite. - **RichOutputBridge**: A bridge leveraging a library like `CLImate` for advanced terminal UI features (colors, progress bars) when the native Phred runner is used. Each bridge lives under `Phred\TaskerBridges\{BridgeName}\...` and can be installed/used independently via Composer suggests. ## 4. SymfonyBridge — Design ### 4.1 High-Level Mapping - Command metadata - `CommandInterface::getName()` -> `Symfony\Component\Console\Command\Command::setName()` - `getDescription()` -> `setDescription()` - `getArguments()` -> loop `addArgument($name, InputArgument::OPTIONAL|REQUIRED, $description, $default)` - `getOptions()` -> loop `addOption($name, $shortcut, InputOption::VALUE_NONE|VALUE_OPTIONAL|VALUE_REQUIRED, $description, $default)` - Note: `ConsoleContracts` currently models arguments/options as `name => description` (DX-minimal). Bridge MAY enhance by honoring defaults/modes if/when added to contracts in future specs. - Execution - The adapter class (e.g., `SymfonyCommandAdapter`) extends `Symfony\Component\Console\Command\Command` and wraps a `CommandInterface` instance. - In `execute(InputInterface $in, OutputInterface $out)`, it: 1) Adapts Symfony input/output to Phred `InputInterface`/`OutputInterface` via IO adapters. 2) Invokes `$wrapped->execute($phredInput, $phredOutput)`. 3) Returns the integer exit code from the wrapped command. ### 4.2 IO Adapters - `SymfonyInputAdapter` implements `GetPhred\ConsoleContracts\InputInterface` by delegating to `Symfony\Component\Console\Input\InputInterface` methods (`getArgument`, `getOption`, `hasOption`). - `SymfonyOutputAdapter` implements `GetPhred\ConsoleContracts\OutputInterface` by delegating to `Symfony\Component\Console\Output\OutputInterface`/`Style` and applying markup translation. Verbosity mapping: - Phred `Verbosity::QUIET|NORMAL|VERBOSE|VERY_VERBOSE|DEBUG` <-> Symfony `OutputInterface::VERBOSITY_*`. Non-interactive: - Respect a runner-provided flag; adapters should avoid prompts and ensure helpers also respect non-interactive mode. ### 4.3 Markup Translation (Fixed Set) - Translate Phred tags into Symfony formatter equivalents or raw ANSI where Symfony lacks a native tag. - `` -> Symfony style (green, bold) using `SymfonyStyle` or custom formatter style. - `` -> Symfony `` (blue where applicable) or custom style to match Phred’s blue guideline. - `` -> white on red background. - `` -> black on yellow background. - `` -> yellow. - `` -> bold; `` -> italic; `` -> underline (use ANSI sequences if formatter lacks direct styles). - If output is not decorated (no TTY or colors disabled), strip tags to plain text. ### 4.4 Helper Adapters - `InteractionInterface` -> wrap `QuestionHelper` (ask, confirm, secret, choice), ensuring non-interactive fallback behavior (return defaults, never block). - `ProgressBarInterface` -> wrap `Symfony\Component\Console\Helper\ProgressBar`. - `TableInterface` -> wrap `Symfony\Component\Console\Helper\Table`. - `MarkdownConverterInterface` -> use a lightweight adapter strategy: - Prefer Symfony-native formatting if available; otherwise convert Markdown to Phred markup (headings -> ``, emphasis -> ``/``, links -> underline/text), then pass through normal markup translation. ### 4.5 Error & Exit Codes - Return integer exit codes from wrapped command directly. - Exceptions: Allow the runner to handle globally; optionally map known `ConsoleExceptionInterface` to `ExitCode::USAGE|CONFIG|UNAVAILABLE|SOFTWARE` as appropriate. ## 5. PhredBridge (Native) — Design - Provide minimal in-house adapters usable by the Tasker runner. - `PhredOutputAdapter`: translates fixed markup to ANSI escape sequences: - `` -> green (e.g., `\e[32m`), `` -> blue (`\e[34m`), `` -> white on red (`\e[97;41m`), `` -> black on yellow (`\e[30;43m`), `` -> yellow (`\e[33m`), `` -> bold (`\e[1m`), `` -> italic (`\e[3m`), `` -> underline (`\e[4m`). Reset with `\e[0m`. - TTY detection and `--no-interaction`/`--no-ansi` handling (strip tags when colors disabled or piping to files). - Provide simple `InteractionInterface`, `ProgressBarInterface`, `TableInterface`, and `MarkdownConverterInterface` implementations with zero external dependencies (basic, but reliable). ## 6. LaravelBridge — Design ### 6.1 Registration & Discovery - Provide a `TaskerServiceProvider` for Laravel applications. - Leverage Laravel Package Auto-Discovery via `composer.json` to register the provider automatically. - The provider will: - Discover Phred commands via `extra.phred-tasker` or explicit container binding. - Register them with the Artisan application. - Provide configuration for non-interactive defaults and formatting. ### 6.2 Container & Execution - Resolve `CommandInterface` instances via the Laravel Service Container (`app()`), favoring constructor injection. - Wrap commands in a `LaravelCommandAdapter` that extends `Illuminate\Console\Command`. - Delegate execution and return integer exit codes. ### 6.3 IO & Markup - Reuse `SymfonyBridge` IO adapters internally (Artisan's IO is built on Symfony). - Ensure Phred’s fixed markup set (``, ``, ``, ``, ``, ``, ``, ``) renders natively in Artisan. ### 6.4 Helper Adapters - `InteractionInterface` -> Map to Laravel's native console methods (`ask`, `confirm`, `secret`, `choice`). - `ProgressBarInterface` / `TableInterface` -> Reuse SymfonyBridge adapters via Artisan. - `MarkdownConverterInterface` -> Standard Phred implementation (lightweight regex/mapping). ## 7. Dependency & Packaging Strategy - Composer `suggest` dependencies per bridge: - `symfony/console` for SymfonyBridge. - `illuminate/console` for LaravelBridge. - Hard requirements: only `php:^8.2` and `getphred/console-contracts`. - Conditional wiring: Bridge factory/services check for class existence before enabling a bridge. - Namespacing: `Phred\TaskerBridges\Symfony\...`, `Phred\TaskerBridges\Phred\...`, `Phred\TaskerBridges\Laravel\...`. ## 8. Registration & Discovery - Consumers may register bridges explicitly in DI (preferred for clarity). - For Laravel, auto-discovery via `TaskerServiceProvider` is the primary path. - For Symfony apps, provide a small integration guide to register `SymfonyCommandAdapter` instances in the application. - For Tasker, PhredBridge is used by default; other bridges are opt-in and instantiated only if their dependencies are present. ## 9. Middleware Compatibility - Bridges must compose with `ConsoleMiddlewareInterface` stacks managed by the runner. The adapter boundary is after middleware resolution so that middlewares see Phred `InputInterface`/`OutputInterface` consistently. ## 10. Performance & Lazy Loading - Metadata access should be O(1) with minimal reflection at runtime. If attributes are used (`#[Cmd]`, `#[Arg]`, `#[Opt]`) and surfaced via `HasAttributes`, bridges continue to consume only the `CommandInterface` methods — no extra reflection needed in bridges. - For toolkits that support lazy command discovery, prefer deferring heavy initialization until execution. ### 11. Testing Strategy (for later phases) - Unit tests: Markup translation, verbosity mapping, non-interactive behavior, helpers integration. - Integration tests: Execute a sample Phred command through SymfonyBridge, LaravelBridge, and PhredBridge, verify identical behavior and exit codes. ## 12. Architectural Decisions (Resolved) ### 12.1 Global Options & Flags - Bridges will map toolkit-specific flags (e.g., Symfony’s `--ansi`, `--no-interaction`) to the Phred `InputInterface` and `OutputInterface` state. - **Purity**: Commands must NOT depend on toolkit-specific options. Instead, they should rely on the standardized Phred flags: - `-v`, `-vv`, `-vvv`, `-q` (Verbosity) - `-n` or `--no-interaction` (Non-interactive) - `--no-ansi` (Color/Decoration) - **Passthrough**: Toolkit-specific global options remain at the application layer and are consumed only by the bridge to configure the IO adapters. ### 12.2 Error Mapping Policy - Bridges are responsible for a "Standard Translation" of common exceptions into `sysexits.h` exit codes. - **Mapping Strategy**: - `InvalidArgumentException` -> `ExitCode::USAGE` (64) - `RuntimeException` -> `ExitCode::SOFTWARE` (70) - `ConsoleExceptionInterface` (from Contracts) -> Mapped according to category (e.g., `UNAVAILABLE`, `CONFIG`). - **Global Handler**: The primary runner (Tasker) or Bridge wrapper provides the top-level `try/catch` to ensure these codes are returned to the shell. ### 12.3 Helper Feature Parity - **Guaranteed Minimum API**: Bridges must implement the full `ProgressBarInterface` and `TableInterface` as defined in `ConsoleContracts`. - **Progressive Enhancement**: Bridges MAY support advanced features (e.g., table styling, progress bar steps) if the underlying toolkit supports it, but commands should avoid depending on these for core functionality. - **Fallback**: In non-TTY or non-supported environments, bridges must provide a "silent" or "degraded" implementation (e.g., printing table as a list, silencing progress bars) to prevent command failure. ## Brainstorming / Open Questions (All current open questions have been resolved and moved to Architectural Decisions.)