diff --git a/MILESTONES.md b/MILESTONES.md new file mode 100644 index 0000000..2e834db --- /dev/null +++ b/MILESTONES.md @@ -0,0 +1,25 @@ +# ConsoleContracts Milestones + +This document defines the implementation milestones for the `getphred/console-contracts` package. + +## 1. Project Infrastructure +- [x] Initialize repository and `composer.json` with zero runtime dependencies. +- [x] Configure PHPUnit and static analysis (PHPStan). +- [x] Set up GitHub Actions for continuous integration. + +## 2. Core Interfaces +- [x] Implement `CommandInterface` and metadata methods. +- [x] Implement `InputInterface` for argument and option access. +- [x] Implement `OutputInterface` with verbosity and semantic methods. +- [x] Implement `ConsoleMiddlewareInterface` for execution wrapping. +- [x] Implement `ConsoleExceptionInterface` for standardized errors. + +## 3. Standardized Constants +- [x] Implement `Verbosity` level constants. +- [x] Implement `ExitCode` semantic constants (sysexits.h). + +## 4. Helper & DX Contracts +- [x] Implement `InteractionInterface` for portable interactivity. +- [x] Implement `ProgressBarInterface`, `TableInterface`, and `MarkdownConverterInterface`. +- [x] Implement PHP 8 Attributes (`#[Cmd]`, `#[Arg]`, `#[Opt]`). +- [x] Implement `HasAttributes` trait for attribute-to-interface bridging. diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..c62700c --- /dev/null +++ b/NOTES.md @@ -0,0 +1,270 @@ +# ConsoleContracts: Phred Console Interfaces + +ConsoleContracts provides a minimal, framework-agnostic set of interfaces for building CLI tools. It is designed to be the foundation for the Tasker ecosystem, allowing library authors to define CLI commands without depending on a heavy runner or a specific framework. + +## Core Vision +- **Zero Dependencies**: The package must have zero runtime dependencies (other than PHP 8.2+). +- **Framework Agnostic**: Interfaces should not assume any specific runner (Tasker, Symfony, etc.). +- **Portable Commands**: Commands defined using these interfaces should be runnable in any environment that supports the Phred Console standard. + +## Architectural Decisions + +### 1. Command Interface +The `CommandInterface` is the primary contract for any CLI tool. It separates metadata (name, description, arguments, options) from execution logic. + +#### CommandInterface +```php +namespace GetPhred\ConsoleContracts; + +interface CommandInterface +{ + /** + * The unique name of the command (e.g., 'db:migrate'). + */ + public function getName(): string; + + /** + * A brief description of what the command does. + */ + public function getDescription(): string; + + /** + * Returns an array of arguments expected by the command. + * @return array Name => Description + */ + public function getArguments(): array; + + /** + * Returns an array of options available for the command. + * @return array Name => Description + */ + public function getOptions(): array; + + /** + * Executes the command logic. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int Exit code (see ExitCode constants) + */ + public function execute(InputInterface $input, OutputInterface $output): int; +} +``` + +### 2. Input & Output Interfaces +To ensure portability, commands interact with the environment only through these interfaces. + +#### InputInterface +```php +namespace GetPhred\ConsoleContracts; + +interface InputInterface +{ + /** + * Retrieve the value of a specific argument. + */ + public function getArgument(string $name, mixed $default = null): mixed; + + /** + * Retrieve the value of a specific option. + */ + public function getOption(string $name, mixed $default = null): mixed; + + /** + * Check if a specific option was provided. + */ + public function hasOption(string $name): bool; +} +``` + +#### OutputInterface +A lean interface for writing messages to the console. + +```php +namespace GetPhred\ConsoleContracts; + +interface OutputInterface +{ + public function write(string $message): void; + public function writeln(string $message): void; + + // Semantic output methods for common use cases + public function success(string $message): void; + public function error(string $message): void; + public function warning(string $message): void; + public function info(string $message): void; + + /** + * Set the verbosity level of the output. + */ + public function setVerbosity(int $level): void; + + /** + * Get the current verbosity level. + */ + public function getVerbosity(): int; +} +``` + +### 3. Interactivity & Portability +Interactive features (asking questions, confirmation prompts) are moved to an optional `InteractionInterface`. This ensures that a command remains runnable in non-interactive environments (CI/CD) by providing a mock or fallback implementation of this interface. + +#### InteractionInterface (Proposed) +```php +namespace GetPhred\ConsoleContracts; + +interface InteractionInterface +{ + /** + * Ask a simple question and return the answer. + */ + public function ask(string $question, string $default = null): string; + + /** + * Ask for confirmation (yes/no). + */ + public function confirm(string $question, bool $default = true): bool; + + /** + * Ask for a sensitive value (input hidden). + */ + public function secret(string $question): string; + + /** + * Provide a list of choices and return the selected one. + */ + public function choice(string $question, array $choices, mixed $default = null): mixed; +} +``` + +### 4. Console Middleware +Middleware should be defined at the **contract level**. This allows for cross-cutting concerns (logging, locking, environment guards) to be implemented in a runner-agnostic way. + +#### ConsoleMiddlewareInterface +```php +namespace GetPhred\ConsoleContracts; + +interface ConsoleMiddlewareInterface +{ + /** + * Handle the command execution lifecycle. + * + * @param CommandInterface $command + * @param InputInterface $input + * @param OutputInterface $output + * @param callable $next The next middleware or the command execution itself. + * @return int Exit code + */ + public function handle( + CommandInterface $command, + InputInterface $input, + OutputInterface $output, + callable $next + ): int; +} +``` + +### 5. Constants & Standards + +#### Verbosity Levels +Standardized verbosity levels to control output detail. +- `Verbosity::QUIET` (0) +- `Verbosity::NORMAL` (1) +- `Verbosity::VERBOSE` (2) +- `Verbosity::VERY_VERBOSE` (3) +- `Verbosity::DEBUG` (4) + +#### Exit Codes +Strict adherence to `sysexits.h` via `ExitCode` constants (defined in Tasker but standardized here). + +### 6. Exception Handling +A base `ConsoleExceptionInterface` should be defined to allow runners to catch and handle CLI-specific errors gracefully (e.g., CommandNotFound, InvalidArgument). + +### 7. Lazy Loading Readiness +The `CommandInterface` is specifically designed to support lazy loading. By separating metadata retrieval (`getName()`, `getDescription()`, `getArguments()`, `getOptions()`) from the execution logic (`execute()`), runners can display help information or command lists without instantiating dependencies required only for the command's execution. + +### 8. Metadata via Attributes (Optional) +To enhance the Developer Experience (DX), PHP 8 attributes are supported for defining command metadata. While the `CommandInterface` remains the primary contract, attributes provide a declarative way to satisfy the interface methods. + +#### Attributes (Proposed) +- `#[Cmd(name: '...', description: '...')]` +- `#[Arg(name: '...', description: '...')]` +- `#[Opt(name: '...', description: '...')]` + +```php +use Phred\ConsoleContracts\Attributes\Cmd; +use Phred\ConsoleContracts\Attributes\Arg; +use Phred\ConsoleContracts\Attributes\Opt; +use Phred\ConsoleContracts\CommandInterface; + +#[Cmd(name: 'db:migrate', description: 'Run database migrations')] +#[Arg(name: 'step', description: 'Number of migrations to run')] +#[Opt(name: 'force', description: 'Force the operation')] +class MigrateCommand implements CommandInterface +{ +use HasAttributes; // A trait that handles the Reflection logic + + public function execute(InputInterface $input, OutputInterface $output): int + { + // Logic here... + return 0; + } +} +``` + +#### Implementation Strategy +The `ConsoleContracts` will provide an optional `HasAttributes` trait (or similar utility) that uses Reflection to read these attributes and return the appropriate values through the `getName()`, `getDescription()`, `getArguments()`, and `getOptions()` methods. + +This approach is: +- **Attribute-Aware**: Modern, declarative DX. +- **Interface-Driven**: The runner only interacts with the `CommandInterface` methods. +- **Optional**: Developers can still override the methods manually without any attributes. + +### 9. Output Formatting (Minimal Markup) +The `OutputInterface` should support a minimal, standardized set of markup tags to ensure consistent styling across different runners. + +#### Core Tag Set (Fixed) +To ensure consistent behavior across all runners and bridges, the set of supported markup tags is **fixed**. Developers should NOT attempt to use custom tags, as bridges are only required to implement this standardized core set: +- `...`: Green text. +- `...`: Blue text. +- `...`: White text on a red background. +- `...`: Black text on a yellow background. +- `...`: Yellow text. +- `...`: Bold text. +- `...`: Italicized text. +- `...`: Underlined text. + +#### Bridge Responsibility +Each bridge (e.g., `SymfonyBridge`, `LaminasBridge`) is responsible for translating this fixed set of Phred tags into the native formatting syntax of the underlying CLI tool. +- **SymfonyBridge**: Maps `` to Symfony's `` tag in its `Formatter`. +- **PhredBridge (Native)**: Translates tags into ANSI escape sequences (e.g., `\e[32m` for green). + +This ensures that a command's output remains visually consistent whether it's running via Tasker or inside a Symfony application. + +### 10. Helper Contracts (Optional) +Complex CLI components like **Progress Bars** and **Tables** are defined as separate, optional interfaces. This keeps the core `OutputInterface` lean and allows commands to only depend on the specific features they need. + +#### ProgressBarInterface (Proposed) +Commands can inject this interface to display progress for long-running tasks. +- `start(int $max)` +- `advance(int $step = 1)` +- `finish()` + +#### TableInterface (Proposed) +Commands can inject this interface to render tabular data. +- `setHeaders(array $headers)` +- `addRow(array $row)` +- `render()` + +#### MarkdownConverterInterface (Proposed) +Commands can inject this interface to convert a string or a file containing Markdown into a string formatted with Phred's standardized markup tags. +- `convert(string $markdown): string` +- `convertFile(string $path): string` + +### Implementation Strategy for Helpers +Like the `InteractionInterface`, these helpers are satisfied by the runner or bridge. +- **SymfonyBridge**: Maps these to Symfony's `ProgressBar` and `Table` helper classes. For Markdown, it could use an existing library or Symfony's native Markdown support. +- **Native (Tasker)**: Provides basic ANSI-based implementations and a lightweight regex-based Markdown parser. + +## Brainstorming / Open Questions +(All current open questions have been resolved and moved to Architectural Decisions.) diff --git a/SPECS.md b/SPECS.md new file mode 100644 index 0000000..dd81cc3 --- /dev/null +++ b/SPECS.md @@ -0,0 +1,173 @@ +# ConsoleContracts Specification + +This document defines the technical specifications for the `getphred/console-contracts` package. These interfaces and constants form the foundational standard for the Phred CLI ecosystem. + +## 1. Namespace & Constants + +**Namespace**: `Phred\ConsoleContracts` + +### 1.1 Verbosity Levels +The `Verbosity` class (or final class with constants) defines the following levels: +- `Verbosity::QUIET = 0` +- `Verbosity::NORMAL = 1` +- `Verbosity::VERBOSE = 2` +- `Verbosity::VERY_VERBOSE = 3` +- `Verbosity::DEBUG = 4` + +### 1.2 Exit Codes +The `ExitCode` class (or final class with constants) follows the `sysexits.h` standard: +- `ExitCode::OK = 0` +- `ExitCode::USAGE = 64` +- `ExitCode::DATAERR = 65` +- `ExitCode::NOINPUT = 66` +- `ExitCode::UNAVAILABLE = 69` +- `ExitCode::SOFTWARE = 70` +- `ExitCode::IOERR = 74` +- `ExitCode::PERM = 77` +- `ExitCode::CONFIG = 78` + +## 2. Core Interfaces + +### 2.1 CommandInterface +Defines the metadata and execution logic for a CLI command. +```php +namespace Phred\ConsoleContracts; + +interface CommandInterface +{ + public function getName(): string; + public function getDescription(): string; + public function getArguments(): array; + public function getOptions(): array; + public function execute(InputInterface $input, OutputInterface $output): int; +} +``` + +### 2.2 InputInterface +Provides access to command-line arguments and options. +```php +namespace Phred\ConsoleContracts; + +interface InputInterface +{ + public function getArgument(string $name, mixed $default = null): mixed; + public function getOption(string $name, mixed $default = null): mixed; + public function hasOption(string $name): bool; +} +``` + +### 2.3 OutputInterface +Standard interface for console output with semantic methods and verbosity control. +```php +namespace Phred\ConsoleContracts; + +interface OutputInterface +{ + public function write(string $message): void; + public function writeln(string $message): void; + public function success(string $message): void; + public function info(string $message): void; + public function error(string $message): void; + public function warning(string $message): void; + public function comment(string $message): void; + public function setVerbosity(int $level): void; + public function getVerbosity(): int; +} +``` + +### 2.4 InteractionInterface +Optional interface for interactive features. +```php +namespace Phred\ConsoleContracts; + +interface InteractionInterface +{ + public function ask(string $question, ?string $default = null): string; + public function confirm(string $question, bool $default = true): bool; + public function secret(string $question): string; + public function choice(string $question, array $choices, mixed $default = null): mixed; +} +``` + +### 2.5 ConsoleMiddlewareInterface +Standardized interface for wrapping command execution. +```php +namespace Phred\ConsoleContracts; + +interface ConsoleMiddlewareInterface +{ + public function handle( + CommandInterface $command, + InputInterface $input, + OutputInterface $output, + callable $next + ): int; +} +``` + +### 2.6 ConsoleExceptionInterface +Base interface for all console-related exceptions. +```php +namespace Phred\ConsoleContracts; + +interface ConsoleExceptionInterface extends \Throwable +{ +} +``` + +## 3. Helper Contracts (Optional) + +### 3.1 ProgressBarInterface +```php +namespace Phred\ConsoleContracts\Helpers; + +interface ProgressBarInterface +{ + public function start(int $max = 0): void; + public function advance(int $step = 1): void; + public function finish(): void; +} +``` + +### 3.2 TableInterface +```php +namespace Phred\ConsoleContracts\Helpers; + +interface TableInterface +{ + public function setHeaders(array $headers): void; + public function addRow(array $row): void; + public function render(): void; +} +``` + +### 3.3 MarkdownConverterInterface +```php +namespace Phred\ConsoleContracts\Helpers; + +interface MarkdownConverterInterface +{ + public function convert(string $markdown): string; + public function convertFile(string $path): string; +} +``` + +## 4. Attributes (Optional DX) + +Defined in `Phred\ConsoleContracts\Attributes`: + +- `Cmd`: `#[Cmd(string $name, string $description = '')]` +- `Arg`: `#[Arg(string $name, string $description = '')]` +- `Opt`: `#[Opt(string $name, string $description = '')]` + +## 5. Output Formatting Standard + +Bridges must translate the following tags: +- ``: Green +- ``: Blue +- ``: White on Red +- ``: Black on Yellow +- ``: Yellow +- ``: Bold +- ``: Italic +- ``: Underline