12 KiB
12 KiB
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
ConsoleContractsto 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
CommandInterfaceand exposes a native command entrypoint. - IO adapters: Implement
InputInterface/OutputInterfaceon 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 (
<success>,<info>,<error>,<warning>,<comment>,<b>,<i>,<u>) to the target toolkit’s styles or to raw ANSI. - Policy alignment: Honor
Verbositylevels, non-interactive mode, TTY/color detection, andsysexits.hexit 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/consoleand may be covered bySymfonyBridge). - AuraCliBridge: Adapter for
Aura.Clifor users of the Aura library suite. - RichOutputBridge: A bridge leveraging a library like
CLImatefor 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()-> loopaddArgument($name, InputArgument::OPTIONAL|REQUIRED, $description, $default)getOptions()-> loopaddOption($name, $shortcut, InputOption::VALUE_NONE|VALUE_OPTIONAL|VALUE_REQUIRED, $description, $default)- Note:
ConsoleContractscurrently models arguments/options asname => 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) extendsSymfony\Component\Console\Command\Commandand wraps aCommandInterfaceinstance. - In
execute(InputInterface $in, OutputInterface $out), it:- Adapts Symfony input/output to Phred
InputInterface/OutputInterfacevia IO adapters. - Invokes
$wrapped->execute($phredInput, $phredOutput). - Returns the integer exit code from the wrapped command.
- Adapts Symfony input/output to Phred
- The adapter class (e.g.,
4.2 IO Adapters
SymfonyInputAdapterimplementsGetPhred\ConsoleContracts\InputInterfaceby delegating toSymfony\Component\Console\Input\InputInterfacemethods (getArgument,getOption,hasOption).SymfonyOutputAdapterimplementsGetPhred\ConsoleContracts\OutputInterfaceby delegating toSymfony\Component\Console\Output\OutputInterface/Styleand applying markup translation.
Verbosity mapping:
- Phred
Verbosity::QUIET|NORMAL|VERBOSE|VERY_VERBOSE|DEBUG<-> SymfonyOutputInterface::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.
<success>-> Symfony style (green, bold) usingSymfonyStyleor custom formatter style.<info>-> Symfony<info>(blue where applicable) or custom style to match Phred’s blue guideline.<error>-> white on red background.<warning>-> black on yellow background.<comment>-> yellow.<b>-> bold;<i>-> italic;<u>-> 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-> wrapQuestionHelper(ask, confirm, secret, choice), ensuring non-interactive fallback behavior (return defaults, never block).ProgressBarInterface-> wrapSymfony\Component\Console\Helper\ProgressBar.TableInterface-> wrapSymfony\Component\Console\Helper\Table.MarkdownConverterInterface-> use a lightweight adapter strategy:- Prefer Symfony-native formatting if available; otherwise convert Markdown to Phred markup (headings ->
<b>, emphasis -><i>/<b>, links -> underline/text), then pass through normal markup translation.
- Prefer Symfony-native formatting if available; otherwise convert Markdown to Phred markup (headings ->
4.5 Error & Exit Codes
- Return integer exit codes from wrapped command directly.
- Exceptions: Allow the runner to handle globally; optionally map known
ConsoleExceptionInterfacetoExitCode::USAGE|CONFIG|UNAVAILABLE|SOFTWAREas appropriate.
5. PhredBridge (Native) — Design
- Provide minimal in-house adapters usable by the Tasker runner.
PhredOutputAdapter: translates fixed markup to ANSI escape sequences:<success>-> green (e.g.,\e[32m),<info>-> blue (\e[34m),<error>-> white on red (\e[97;41m),<warning>-> black on yellow (\e[30;43m),<comment>-> yellow (\e[33m),<b>-> bold (\e[1m),<i>-> italic (\e[3m),<u>-> underline (\e[4m). Reset with\e[0m.
- TTY detection and
--no-interaction/--no-ansihandling (strip tags when colors disabled or piping to files). - Provide simple
InteractionInterface,ProgressBarInterface,TableInterface, andMarkdownConverterInterfaceimplementations with zero external dependencies (basic, but reliable).
6. LaravelBridge — Design
6.1 Registration & Discovery
- Provide a
TaskerServiceProviderfor Laravel applications. - Leverage Laravel Package Auto-Discovery via
composer.jsonto register the provider automatically. - The provider will:
- Discover Phred commands via
extra.phred-taskeror explicit container binding. - Register them with the Artisan application.
- Provide configuration for non-interactive defaults and formatting.
- Discover Phred commands via
6.2 Container & Execution
- Resolve
CommandInterfaceinstances via the Laravel Service Container (app()), favoring constructor injection. - Wrap commands in a
LaravelCommandAdapterthat extendsIlluminate\Console\Command. - Delegate execution and return integer exit codes.
6.3 IO & Markup
- Reuse
SymfonyBridgeIO adapters internally (Artisan's IO is built on Symfony). - Ensure Phred’s fixed markup set (
<success>,<info>,<error>,<warning>,<comment>,<b>,<i>,<u>) 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
suggestdependencies per bridge:symfony/consolefor SymfonyBridge.illuminate/consolefor LaravelBridge.
- Hard requirements: only
php:^8.2andgetphred/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
TaskerServiceProvideris the primary path. - For Symfony apps, provide a small integration guide to register
SymfonyCommandAdapterinstances 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
ConsoleMiddlewareInterfacestacks managed by the runner. The adapter boundary is after middleware resolution so that middlewares see PhredInputInterface/OutputInterfaceconsistently.
10. Performance & Lazy Loading
- Metadata access should be O(1) with minimal reflection at runtime. If attributes are used (
#[Cmd],#[Arg],#[Opt]) and surfaced viaHasAttributes, bridges continue to consume only theCommandInterfacemethods — 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 PhredInputInterfaceandOutputInterfacestate. - Purity: Commands must NOT depend on toolkit-specific options. Instead, they should rely on the standardized Phred flags:
-v,-vv,-vvv,-q(Verbosity)-nor--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.hexit 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/catchto ensure these codes are returned to the shell.
12.3 Helper Feature Parity
- Guaranteed Minimum API: Bridges must implement the full
ProgressBarInterfaceandTableInterfaceas defined inConsoleContracts. - 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.)