feat: Reorganize CLI into hierarchical command structure

- Add system, module, and package command groups for clear subsystem separation
- Create SystemCommand, ModuleCommand, and PackageCommand classes
- Maintain backward compatibility with existing flat commands
- Enhanced help system with contextual guidance at each level
- Updated main CLI to show organized command groups
- Added comprehensive documentation for CLI reorganization

New structure:
- tito system (info, doctor, jupyter)
- tito module (status, test, notebooks)
- tito package (sync, reset, nbdev)

Benefits:
- Clear subsystem separation
- Intuitive command discovery
- Better extensibility for future commands
- Reduced cognitive load for users
This commit is contained in:
Vijay Janapa Reddi
2025-07-11 22:37:35 -04:00
parent b92642a96e
commit f4fbdcac8e
7 changed files with 587 additions and 6 deletions

View File

@@ -0,0 +1,266 @@
# CLI Reorganization: Hierarchical Command Structure
TinyTorch CLI has been reorganized into a clear hierarchical structure with three main command groups: `system`, `module`, and `package`. This provides better organization and makes it clear which subsystem each command operates on.
## New Command Structure
### System Commands (`tito system`)
**Environment, configuration, and system tools**
- `tito system info` - Show system information and course navigation
- `tito system doctor` - Run environment diagnosis
- `tito system jupyter` - Start Jupyter notebook server
### Module Commands (`tito module`)
**Development workflow and module management**
- `tito module status` - Check status of all modules
- `tito module test` - Run module tests
- `tito module notebooks` - Build notebooks from Python files
### Package Commands (`tito package`)
**nbdev integration and package management**
- `tito package sync` - Export notebook code to Python package
- `tito package reset` - Reset tinytorch package to clean state
- `tito package nbdev` - nbdev notebook development commands
## Benefits of Hierarchical Structure
### 1. **Clear Subsystem Separation**
Each command group operates on a specific subsystem:
- **System**: Environment and configuration
- **Module**: Individual module development
- **Package**: Overall package management
### 2. **Intuitive Command Discovery**
Users can explore commands by subsystem:
```bash
tito system # Shows all system commands
tito module # Shows all module commands
tito package # Shows all package commands
```
### 3. **Extensibility**
New commands can be easily categorized:
- Adding deployment tools? → `tito system deploy`
- Adding module generators? → `tito module create`
- Adding package publishing? → `tito package publish`
### 4. **Reduced Cognitive Load**
Instead of remembering 10+ flat commands, users think in terms of:
- What subsystem am I working with?
- What do I want to do in that subsystem?
## Usage Examples
### System Operations
```bash
# Check environment setup
tito system doctor
# Get system information
tito system info
# Start development environment
tito system jupyter
```
### Module Development
```bash
# Check module status with metadata
tito module status --metadata
# Test all modules
tito module test --all
# Test specific module
tito module test --module tensor
# Generate notebooks
tito module notebooks --module tensor
```
### Package Management
```bash
# Export modules to package
tito package sync
# Export specific module
tito package sync --module tensor
# Reset package to clean state
tito package reset
# Run nbdev commands
tito package nbdev --export
```
## Backward Compatibility
The old flat command structure is still supported for backward compatibility:
```bash
# These still work (legacy commands)
tito status
tito test --all
tito sync
tito info
```
However, the new hierarchical structure is recommended for new usage.
## Migration Guide
### For Users
**Old → New Command Mapping:**
```bash
# System commands
tito info → tito system info
tito doctor → tito system doctor
tito jupyter → tito system jupyter
# Module commands
tito status → tito module status
tito test → tito module test
tito notebooks → tito module notebooks
# Package commands
tito sync → tito package sync
tito reset → tito package reset
tito nbdev → tito package nbdev
```
### For Scripts
Update automation scripts to use the new structure:
```bash
# Old
./scripts/test-all.sh:
tito test --all
# New (recommended)
./scripts/test-all.sh:
tito module test --all
```
## Implementation Details
### Command Group Classes
Each command group is implemented as a separate class:
- `SystemCommand` - Handles system subcommands
- `ModuleCommand` - Handles module subcommands
- `PackageCommand` - Handles package subcommands
### Argument Parsing
Uses argparse subparsers for clean hierarchical structure:
```python
# Main parser
parser = argparse.ArgumentParser(prog="tito")
subparsers = parser.add_subparsers(dest='command')
# System group
system_parser = subparsers.add_parser('system')
system_subparsers = system_parser.add_subparsers(dest='system_command')
# System subcommands
system_subparsers.add_parser('info')
system_subparsers.add_parser('doctor')
system_subparsers.add_parser('jupyter')
```
### Help System
Each level provides contextual help:
```bash
tito # Shows command groups
tito system # Shows system subcommands
tito module # Shows module subcommands
tito package # Shows package subcommands
```
## Future Enhancements
### Additional Command Groups
The structure supports adding new command groups:
```bash
# Deployment commands
tito deploy status
tito deploy docker
tito deploy cloud
# Research commands
tito research benchmark
tito research profile
tito research compare
```
### Command Aliases
Short aliases for common commands:
```bash
# System
tito sys info # alias for tito system info
# Module
tito mod status # alias for tito module status
tito mod test # alias for tito module test
# Package
tito pkg sync # alias for tito package sync
```
### Interactive Mode
Enhanced interactive command discovery:
```bash
tito interactive
# → Shows menu of command groups
# → Allows drilling down into subcommands
# → Provides guided command building
```
## Best Practices
### 1. **Use Hierarchical Commands**
Prefer the new structure for clarity:
```bash
# Good
tito module status --metadata
# Avoid (legacy)
tito status --metadata
```
### 2. **Think in Subsystems**
When adding new functionality, consider which subsystem it belongs to:
- Environment/config → `system`
- Individual modules → `module`
- Overall package → `package`
### 3. **Consistent Naming**
Use consistent naming patterns within each group:
- `status` for checking state
- `test` for running tests
- `sync` for synchronization
- `reset` for cleanup
### 4. **Help Documentation**
Always provide clear help text for new commands:
```python
parser.add_parser('new-command', help='Clear description of what this does')
```
## Conclusion
The hierarchical CLI structure provides:
- **Better organization** with clear subsystem separation
- **Improved discoverability** through logical grouping
- **Enhanced extensibility** for future commands
- **Maintained compatibility** with existing workflows
This structure scales well as TinyTorch grows and provides a professional CLI experience that matches industry standards.

View File

@@ -2,9 +2,12 @@
CLI Commands package.
Each command is implemented as a separate module with proper separation of concerns.
Commands are organized into logical groups: system, module, and package.
"""
from .base import BaseCommand
# Individual commands (for backward compatibility)
from .notebooks import NotebooksCommand
from .info import InfoCommand
from .test import TestCommand
@@ -15,8 +18,14 @@ from .jupyter import JupyterCommand
from .nbdev import NbdevCommand
from .status import StatusCommand
# Command groups
from .system import SystemCommand
from .module import ModuleCommand
from .package import PackageCommand
__all__ = [
'BaseCommand',
# Individual commands
'NotebooksCommand',
'InfoCommand',
'TestCommand',
@@ -26,4 +35,8 @@ __all__ = [
'JupyterCommand',
'NbdevCommand',
'StatusCommand',
# Command groups
'SystemCommand',
'ModuleCommand',
'PackageCommand',
]

View File

@@ -81,13 +81,13 @@ class InfoCommand(BaseCommand):
# Command Reference Panel
cmd_text = Text()
cmd_text.append("📊 Module Status: ", style="dim")
cmd_text.append("tito status\n", style="bold cyan")
cmd_text.append("tito module status\n", style="bold cyan")
cmd_text.append("🧪 Run Tests: ", style="dim")
cmd_text.append("tito test --all\n", style="bold cyan")
cmd_text.append("tito module test --all\n", style="bold cyan")
cmd_text.append("🔄 Export Code: ", style="dim")
cmd_text.append("tito sync\n", style="bold cyan")
cmd_text.append("tito package sync\n", style="bold cyan")
cmd_text.append("🩺 Check Environment: ", style="dim")
cmd_text.append("tito doctor", style="bold cyan")
cmd_text.append("tito system doctor", style="bold cyan")
console.print(Panel(cmd_text, title="📋 Quick Commands", border_style="bright_magenta"))

88
tito/commands/module.py Normal file
View File

@@ -0,0 +1,88 @@
"""
Module command group for TinyTorch CLI: development workflow and module management.
"""
from argparse import ArgumentParser, Namespace
from rich.panel import Panel
from .base import BaseCommand
from .status import StatusCommand
from .test import TestCommand
from .notebooks import NotebooksCommand
class ModuleCommand(BaseCommand):
@property
def name(self) -> str:
return "module"
@property
def description(self) -> str:
return "Module development and management commands"
def add_arguments(self, parser: ArgumentParser) -> None:
subparsers = parser.add_subparsers(
dest='module_command',
help='Module subcommands',
metavar='SUBCOMMAND'
)
# Status subcommand
status_parser = subparsers.add_parser(
'status',
help='Check status of all modules'
)
status_cmd = StatusCommand(self.config)
status_cmd.add_arguments(status_parser)
# Test subcommand
test_parser = subparsers.add_parser(
'test',
help='Run module tests'
)
test_cmd = TestCommand(self.config)
test_cmd.add_arguments(test_parser)
# Notebooks subcommand
notebooks_parser = subparsers.add_parser(
'notebooks',
help='Build notebooks from Python files'
)
notebooks_cmd = NotebooksCommand(self.config)
notebooks_cmd.add_arguments(notebooks_parser)
def run(self, args: Namespace) -> int:
console = self.console
if not hasattr(args, 'module_command') or not args.module_command:
console.print(Panel(
"[bold cyan]Module Commands[/bold cyan]\n\n"
"Available subcommands:\n"
" • [bold]status[/bold] - Check status of all modules\n"
" • [bold]test[/bold] - Run module tests\n"
" • [bold]notebooks[/bold] - Build notebooks from Python files\n\n"
"[dim]Examples:[/dim]\n"
"[dim] tito module status --metadata[/dim]\n"
"[dim] tito module test --all[/dim]\n"
"[dim] tito module notebooks --module tensor[/dim]",
title="Module Command Group",
border_style="bright_cyan"
))
return 0
# Execute the appropriate subcommand
if args.module_command == 'status':
cmd = StatusCommand(self.config)
return cmd.execute(args)
elif args.module_command == 'test':
cmd = TestCommand(self.config)
return cmd.execute(args)
elif args.module_command == 'notebooks':
cmd = NotebooksCommand(self.config)
return cmd.execute(args)
else:
console.print(Panel(
f"[red]Unknown module subcommand: {args.module_command}[/red]",
title="Error",
border_style="red"
))
return 1

88
tito/commands/package.py Normal file
View File

@@ -0,0 +1,88 @@
"""
Package command group for TinyTorch CLI: nbdev integration and package management.
"""
from argparse import ArgumentParser, Namespace
from rich.panel import Panel
from .base import BaseCommand
from .sync import SyncCommand
from .reset import ResetCommand
from .nbdev import NbdevCommand
class PackageCommand(BaseCommand):
@property
def name(self) -> str:
return "package"
@property
def description(self) -> str:
return "Package management and nbdev integration commands"
def add_arguments(self, parser: ArgumentParser) -> None:
subparsers = parser.add_subparsers(
dest='package_command',
help='Package subcommands',
metavar='SUBCOMMAND'
)
# Sync subcommand
sync_parser = subparsers.add_parser(
'sync',
help='Export notebook code to Python package'
)
sync_cmd = SyncCommand(self.config)
sync_cmd.add_arguments(sync_parser)
# Reset subcommand
reset_parser = subparsers.add_parser(
'reset',
help='Reset tinytorch package to clean state'
)
reset_cmd = ResetCommand(self.config)
reset_cmd.add_arguments(reset_parser)
# Nbdev subcommand
nbdev_parser = subparsers.add_parser(
'nbdev',
help='nbdev notebook development commands'
)
nbdev_cmd = NbdevCommand(self.config)
nbdev_cmd.add_arguments(nbdev_parser)
def run(self, args: Namespace) -> int:
console = self.console
if not hasattr(args, 'package_command') or not args.package_command:
console.print(Panel(
"[bold cyan]Package Commands[/bold cyan]\n\n"
"Available subcommands:\n"
" • [bold]sync[/bold] - Export notebook code to Python package\n"
" • [bold]reset[/bold] - Reset tinytorch package to clean state\n"
" • [bold]nbdev[/bold] - nbdev notebook development commands\n\n"
"[dim]Examples:[/dim]\n"
"[dim] tito package sync --module tensor[/dim]\n"
"[dim] tito package reset --force[/dim]\n"
"[dim] tito package nbdev --export[/dim]",
title="Package Command Group",
border_style="bright_cyan"
))
return 0
# Execute the appropriate subcommand
if args.package_command == 'sync':
cmd = SyncCommand(self.config)
return cmd.execute(args)
elif args.package_command == 'reset':
cmd = ResetCommand(self.config)
return cmd.execute(args)
elif args.package_command == 'nbdev':
cmd = NbdevCommand(self.config)
return cmd.execute(args)
else:
console.print(Panel(
f"[red]Unknown package subcommand: {args.package_command}[/red]",
title="Error",
border_style="red"
))
return 1

85
tito/commands/system.py Normal file
View File

@@ -0,0 +1,85 @@
"""
System command group for TinyTorch CLI: environment, configuration, and system tools.
"""
from argparse import ArgumentParser, Namespace
from rich.panel import Panel
from .base import BaseCommand
from .info import InfoCommand
from .doctor import DoctorCommand
from .jupyter import JupyterCommand
class SystemCommand(BaseCommand):
@property
def name(self) -> str:
return "system"
@property
def description(self) -> str:
return "System environment and configuration commands"
def add_arguments(self, parser: ArgumentParser) -> None:
subparsers = parser.add_subparsers(
dest='system_command',
help='System subcommands',
metavar='SUBCOMMAND'
)
# Info subcommand
info_parser = subparsers.add_parser(
'info',
help='Show system information and course navigation'
)
info_cmd = InfoCommand(self.config)
info_cmd.add_arguments(info_parser)
# Doctor subcommand
doctor_parser = subparsers.add_parser(
'doctor',
help='Run environment diagnosis'
)
doctor_cmd = DoctorCommand(self.config)
doctor_cmd.add_arguments(doctor_parser)
# Jupyter subcommand
jupyter_parser = subparsers.add_parser(
'jupyter',
help='Start Jupyter notebook server'
)
jupyter_cmd = JupyterCommand(self.config)
jupyter_cmd.add_arguments(jupyter_parser)
def run(self, args: Namespace) -> int:
console = self.console
if not hasattr(args, 'system_command') or not args.system_command:
console.print(Panel(
"[bold cyan]System Commands[/bold cyan]\n\n"
"Available subcommands:\n"
" • [bold]info[/bold] - Show system information and course navigation\n"
" • [bold]doctor[/bold] - Run environment diagnosis\n"
" • [bold]jupyter[/bold] - Start Jupyter notebook server\n\n"
"[dim]Example: tito system info[/dim]",
title="System Command Group",
border_style="bright_cyan"
))
return 0
# Execute the appropriate subcommand
if args.system_command == 'info':
cmd = InfoCommand(self.config)
return cmd.execute(args)
elif args.system_command == 'doctor':
cmd = DoctorCommand(self.config)
return cmd.execute(args)
elif args.system_command == 'jupyter':
cmd = JupyterCommand(self.config)
return cmd.execute(args)
else:
console.print(Panel(
f"[red]Unknown system subcommand: {args.system_command}[/red]",
title="Error",
border_style="red"
))
return 1

View File

@@ -18,6 +18,7 @@ from typing import Dict, Type
from .core.config import CLIConfig
from .core.console import get_console, print_banner, print_error
from .core.exceptions import TinyTorchCLIError
from rich.panel import Panel
from .commands.base import BaseCommand
from .commands.notebooks import NotebooksCommand
from .commands.info import InfoCommand
@@ -28,6 +29,9 @@ from .commands.reset import ResetCommand
from .commands.jupyter import JupyterCommand
from .commands.nbdev import NbdevCommand
from .commands.status import StatusCommand
from .commands.system import SystemCommand
from .commands.module import ModuleCommand
from .commands.package import PackageCommand
# Configure logging
logging.basicConfig(
@@ -49,6 +53,11 @@ class TinyTorchCLI:
self.config = CLIConfig.from_project_root()
self.console = get_console()
self.commands: Dict[str, Type[BaseCommand]] = {
# New hierarchical command groups
'system': SystemCommand,
'module': ModuleCommand,
'package': PackageCommand,
# Legacy flat commands (for backward compatibility)
'notebooks': NotebooksCommand,
'info': InfoCommand,
'test': TestCommand,
@@ -65,7 +74,21 @@ class TinyTorchCLI:
parser = argparse.ArgumentParser(
prog="tito",
description="TinyTorch CLI - Build ML systems from scratch",
formatter_class=argparse.RawDescriptionHelpFormatter
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Command Groups:
system System environment and configuration commands
module Module development and management commands
package Package management and nbdev integration commands
Examples:
tito system info Show system information
tito module status --metadata Module status with metadata
tito package sync Export notebooks to package
Legacy commands (deprecated, use grouped commands above):
tito info, tito status, tito test, tito sync, etc.
"""
)
# Global options
@@ -143,7 +166,25 @@ class TinyTorchCLI:
# Handle no command
if not parsed_args.command:
parser.print_help()
# Show enhanced help with command groups
self.console.print(Panel(
"[bold cyan]TinyTorch CLI - Build ML Systems from Scratch[/bold cyan]\n\n"
"[bold]Command Groups:[/bold]\n"
" [bold green]system[/bold green] - System environment and configuration\n"
" [bold green]module[/bold green] - Module development and management\n"
" [bold green]package[/bold green] - Package management and nbdev integration\n\n"
"[bold]Quick Start:[/bold]\n"
" [dim]tito system info[/dim] - Show system information\n"
" [dim]tito module status --metadata[/dim] - Module status with metadata\n"
" [dim]tito package sync[/dim] - Export notebooks to package\n\n"
"[bold]Get Help:[/bold]\n"
" [dim]tito system[/dim] - Show system subcommands\n"
" [dim]tito module[/dim] - Show module subcommands\n"
" [dim]tito package[/dim] - Show package subcommands\n"
" [dim]tito --help[/dim] - Show full help",
title="TinyTorch CLI",
border_style="bright_blue"
))
return 0
# Execute command