TaskerBridges/NOTES.md

12 KiB
Raw Blame History

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 toolkits input/output.
  • Helper adapters: Implement optional helper contracts (InteractionInterface, ProgressBarInterface, TableInterface, MarkdownConverterInterface).
  • Markup translator: Converts Phreds fixed markup tags (<success>, <info>, <error>, <warning>, <comment>, <b>, <i>, <u>) to the target toolkits 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.
    • <success> -> Symfony style (green, bold) using SymfonyStyle or custom formatter style.
    • <info> -> Symfony <info> (blue where applicable) or custom style to match Phreds 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 -> 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 -> <b>, emphasis -> <i>/<b>, 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:
    • <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-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 Phreds 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 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., Symfonys --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.)