docs: add project milestones, specs, and notes
Some checks are pending
CI / console-contracts (8.2) (push) Waiting to run
CI / console-contracts (8.3) (push) Waiting to run

This commit is contained in:
Funky Waddle 2026-02-21 19:13:45 -06:00
parent 621fd7e25b
commit c06e557620
3 changed files with 468 additions and 0 deletions

25
MILESTONES.md Normal file
View file

@ -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.

270
NOTES.md Normal file
View file

@ -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<string, string> Name => Description
*/
public function getArguments(): array;
/**
* Returns an array of options available for the command.
* @return array<string, string> 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:
- `<success>...</success>`: Green text.
- `<info>...</info>`: Blue text.
- `<error>...</error>`: White text on a red background.
- `<warning>...</warning>`: Black text on a yellow background.
- `<comment>...</comment>`: Yellow text.
- `<b>...</b>`: Bold text.
- `<i>...</i>`: Italicized text.
- `<u>...</u>`: 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 `<info>` to Symfony's `<info>` 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.)

173
SPECS.md Normal file
View file

@ -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:
- `<success>`: Green
- `<info>`: Blue
- `<error>`: White on Red
- `<warning>`: Black on Yellow
- `<comment>`: Yellow
- `<b>`: Bold
- `<i>`: Italic
- `<u>`: Underline