CLI System Design¶
This document specifies the command-line interface design following the subcommand architecture established in ADR-004 and using Tyro’s dataclass-based approach.
Overview¶
The CLI system implements a verb-based subcommand architecture with clean separation between command logic and CLI orchestration. Each subcommand handles a distinct operational mode with isolated option namespaces, following the patterns established in the CLI discussion and subcommand architecture decision.
Design Principles¶
Subcommand Isolation: Each operational mode (check, fix, configure, describe, serve) has dedicated command classes with specific option sets to prevent namespace conflicts.
Type Safety: Comprehensive type annotations using Tyro’s dataclass integration provide automatic help generation and runtime type validation.
Protocol-Based Design: Common command interface enables consistent execution patterns and future extensibility.
Integration Architecture: Seamless coordination with established configuration, engine, and reporting systems.
Module Organization¶
The CLI system follows the established filesystem architecture:
sources/vibelinter/
├── cli.py # Main CLI orchestration and Tyro integration
├── subcommands/ # Individual subcommand implementations
│ ├── __init__.py # Subcommand registry and common patterns
│ ├── __.py # Centralized imports for subcommand implementations
│ ├── check.py # Analysis and violation reporting
│ ├── fix.py # Auto-fix with safety controls
│ ├── configure.py # Non-destructive configuration management
│ ├── describe.py # Rule documentation and discovery
│ └── serve.py # Protocol server modes (future)
└── interfaces.py # CLI-related type definitions and contracts
Type System Specification¶
Core Type Annotations¶
The CLI system defines comprehensive type annotations following practices guidelines:
# Type aliases for CLI parameters
OutputFormat: __.typx.TypeAlias = __.typx.Literal[ 'text', 'json', 'structured' ]
DiffFormat: __.typx.TypeAlias = __.typx.Literal[ 'unified', 'context' ]
RuleSelectorArgument: __.typx.TypeAlias = __.typx.Annotated[
str,
__.ddoc.Doc( "Comma-separated VBL rule codes (e.g. VBL101,VBL201)." )
]
PathsArgument: __.typx.TypeAlias = __.typx.Annotated[
tuple[ str, ... ],
__.ddoc.Doc( "Files or directories to analyze." )
]
ContextLinesArgument: __.typx.TypeAlias = __.typx.Annotated[
int,
__.ddoc.Doc( "Number of context lines around violations." )
]
Command Protocol Interface¶
All subcommands implement a common protocol:
class _CliCommand( __.immut.DataclassProtocol, __.typx.Protocol ):
''' CLI command protocol for subcommand implementations. '''
@__.abc.abstractmethod
async def __call__(
self,
configuration: Configuration, # From configuration system
engine: Engine, # From core engine
) -> int:
''' Executes command with configuration and engine context.
Returns appropriate exit code (0=success, 1=violations, 2+=errors).
'''
raise NotImplementedError
Subcommand Specifications¶
Check Command (Default)¶
Primary analysis functionality with configurable output and context display:
class CheckCommand( _CliCommand, decorators = ( __.standard_tyro_class, ) ):
''' Analyzes code and reports violations. '''
paths: PathsArgument = ( '.' )
select: __.Absential[ RuleSelectorArgument ] = __.absent
report_format: __.typx.Annotated[
OutputFormat,
__.ddoc.Doc( "Output format for violation reporting." )
] = 'text'
context: __.typx.Annotated[
int,
__.ddoc.Doc( "Show context lines around violations." )
] = 0
jobs: __.typx.Annotated[
__.typx.Union[ int, __.typx.Literal[ 'auto' ] ],
__.ddoc.Doc( "Number of parallel processing jobs." )
] = 'auto'
async def __call__(
self,
configuration: Configuration,
engine: Engine,
) -> int: ...
Fix Command¶
Auto-fix functionality with safety controls and simulation capabilities:
class FixCommand( _CliCommand, decorators = ( __.standard_tyro_class, ) ):
''' Applies automated fixes with safety controls. '''
paths: PathsArgument = ( '.' )
select: __.Absential[ RuleSelectorArgument ] = __.absent
simulate: __.typx.Annotated[
bool,
__.ddoc.Doc( "Preview changes without applying them." )
] = False
diff_format: __.typx.Annotated[
DiffFormat,
__.ddoc.Doc( "Diff visualization format." )
] = 'unified'
apply_dangerous: __.typx.Annotated[
bool,
__.ddoc.Doc( "Enable potentially unsafe fixes." )
] = False
async def __call__(
self,
configuration: Configuration,
engine: Engine,
) -> int: ...
Configure Command¶
Non-destructive configuration management following ADR-003 approach:
class ConfigureCommand( _CliCommand, decorators = ( __.standard_tyro_class, ) ):
''' Manages configuration without destructive file editing. '''
validate: __.typx.Annotated[
bool,
__.ddoc.Doc( "Validate existing configuration without analysis." )
] = False
interactive: __.typx.Annotated[
bool,
__.ddoc.Doc( "Interactive configuration wizard." )
] = False
display_effective: __.typx.Annotated[
bool,
__.ddoc.Doc( "Display effective merged configuration." )
] = False
async def __call__(
self,
configuration: Configuration,
engine: Engine,
) -> int: ...
Configuration Display Behavior:
The display_effective option provides comprehensive configuration
transparency:
Default behavior (validation only):
$ linter configure --validate
✓ Configuration is valid
✓ All enabled rules have valid parameters
With effective configuration display:
$ linter configure --display-effective
Configuration Sources:
• CLI arguments: --display-effective=true
• pyproject.toml: /path/to/project/pyproject.toml
• Built-in defaults
Effective Configuration:
[tool.vibelinter]
output-format = "text" # default
jobs = "auto" # default
[tool.vibelinter.rules]
VBL101 = true # pyproject.toml
VBL102 = false # pyproject.toml
VBL201 = true # default
VBL301 = true # default
[tool.vibelinter.rules.VBL102]
similarity-threshold = 0.9 # pyproject.toml
allow-digits = false # pyproject.toml
Rule Status:
✓ VBL101 (blank-line-elimination): enabled, severity=error
✗ VBL102 (simple-naming-conventions): disabled
✓ VBL201 (function-ordering): enabled, severity=warning
✓ VBL301 (collection-type-variance): enabled, severity=error
This comprehensive view supports debugging configuration precedence, team onboarding, CI/CD troubleshooting, and configuration validation workflows.
Describe Command¶
Rule discovery and documentation with context-aware information display:
class DescribeRulesCommand( _CliCommand, decorators = ( __.standard_tyro_class, ) ):
''' Lists all available rules with descriptions. '''
details: __.typx.Annotated[
bool,
__.ddoc.Doc( "Display detailed rule information including configuration status." )
] = False
async def __call__(
self,
configuration: Configuration,
engine: Engine,
) -> int: ...
class DescribeRuleCommand( _CliCommand, decorators = ( __.standard_tyro_class, ) ):
''' Displays detailed information for a specific rule. '''
rule_id: __.tyro.conf.Positional[ str ]
details: __.typx.Annotated[
bool,
__.ddoc.Doc( "Display detailed rule information including configuration status." )
] = False
async def __call__(
self,
configuration: Configuration,
engine: Engine,
) -> int: ...
class DescribeCommand( __.immut.DataclassObject, decorators = ( __.simple_tyro_class, ) ):
''' Displays rule information and documentation. '''
subcommand: __.typx.Union[
__.typx.Annotated[
DescribeRulesCommand,
__.tyro.conf.subcommand( 'rules', prefix_name = False ),
],
__.typx.Annotated[
DescribeRuleCommand,
__.tyro.conf.subcommand( 'rule', prefix_name = False ),
],
]
async def __call__(
self,
configuration: Configuration,
engine: Engine,
) -> int:
''' Delegates to selected subcommand. '''
return await self.subcommand( configuration, engine )
Main CLI Orchestrator¶
CLI Controller Design¶
The main CLI class orchestrates subcommand execution following Librovore patterns:
class LinterCli( __.immut.DataclassObject, decorators = ( __.simple_tyro_class, ) ):
''' Linter command-line interface. '''
command: __.typx.Union[
__.typx.Annotated[
CheckCommand,
__.tyro.conf.subcommand( 'check', prefix_name = False ),
],
__.typx.Annotated[
FixCommand,
__.tyro.conf.subcommand( 'fix', prefix_name = False ),
],
__.typx.Annotated[
ConfigureCommand,
__.tyro.conf.subcommand( 'configure', prefix_name = False ),
],
__.typx.Annotated[
DescribeCommand,
__.tyro.conf.subcommand( 'describe', prefix_name = False ),
],
]
verbose: __.typx.Annotated[
bool,
__.ddoc.Doc( "Enable verbose output." )
] = False
async def __call__( self ) -> None:
''' Invokes selected subcommand after system preparation. '''
System Integration Design¶
The CLI orchestrator integrates with core architecture components:
Configuration Integration: - Uses the configuration system from ADR-003 for discovery and loading - Supports non-destructive management approach with precedence rules - Handles CLI argument overrides of file-based settings
Engine Integration: - Coordinates with LinterEngine for analysis and reporting operations - Manages rule selection and filtering based on CLI options - Controls parallel processing coordination
Reporting Integration: - Uses reporting system for output formatting across multiple formats - Manages context display and violation presentation patterns - Supports deterministic output ordering for CI/CD consistency
Exception Hierarchy¶
CLI-Specific Exceptions¶
CLI operations use the existing package exception hierarchy in
sources/vibelinter/exceptions.py:
class ConfigurationInvalidity( Omnierror, ValueError ):
''' Invalid configuration detected. '''
def __init__( self, source: str, problem: str ): ...
class RuleDiscoverFailure( Omnierror, ImportError ):
''' Rule discovery or loading failed. '''
def __init__( self, rule_id: str ): ...
class FileDiscoverFailure( Omnierror, OSError ):
''' File discovery or access failed. '''
def __init__( self, path: str ): ...
Entry Point Design¶
CLI Execution Pattern¶
def execute( ) -> None:
''' Entry point for CLI execution. '''
# Tyro configuration and exception handling patterns
Exit Code Strategy¶
Standard linter conventions with BSD sysexits for runtime issues:
Exit Code 0: Clean execution with no violations detected Exit Code 1: Violations found during analysis Exit Code 66 (EX_NOINPUT): Cannot open input files Exit Code 70 (EX_SOFTWARE): Internal software error Exit Code 74 (EX_IOERR): Input/output error during processing Exit Code 78 (EX_CONFIG): Configuration file syntax or validation errors
This approach follows established linter patterns (0=clean, 1=violations) while using BSD sysexits codes for precise runtime error reporting. Scripts expecting standard linter behavior work correctly, while automated tooling can distinguish between different types of runtime failures.
Extensibility¶
The design accommodates additional subcommands through protocol-specific isolation, maintaining clean separation of concerns without affecting existing subcommand namespaces.
Design Rationale¶
Architecture Objectives¶
This CLI design achieves several key architectural objectives:
Clean Separation: Each subcommand operates in isolation with distinct option namespaces, following ADR-004 requirements for avoiding conflicts between operational modes.
Type Safety: Dataclass-based commands with comprehensive type annotations provide automatic help generation and runtime validation through Tyro integration.
Future Extensibility: Protocol-based design enables addition of server modes and new subcommands without affecting existing functionality.
Error Handling: Comprehensive exception hierarchy with appropriate exit codes supports robust CI/CD integration patterns.
Configuration Integration: Seamless coordination with established configuration architecture maintains consistency across the system.
Practices Compliance: Design follows all established Python practices for imports, naming, module organization, and exception handling patterns.
Implementation Guidelines¶
Subcommand Implementation: Each subcommand focuses solely on its operational mode, delegating core functionality to engine and configuration systems.
Option Validation: Use Tyro’s built-in validation for type checking and help generation, with custom validation for business logic constraints.
Resource Management: Follow established patterns for configuration loading, engine initialization, and proper cleanup in exception scenarios.
Output Consistency: All subcommands coordinate through the reporting system to ensure consistent formatting and presentation across operational modes.
The interface specifications provide clear contracts for implementation while maintaining architectural decisions and following established Python development patterns.