feat: Make ast-stats command work without file argument - show AST subsystem statistics
Enhanced ast-stats command to follow the established *-stats convention where subsystem
commands show system-level statistics when called without specific targets.
## Changes Made:
### 1. Made file_path Argument Optional
- Changed from required to optional: `@click.argument('file_path', required=False)`
- Updated help text to show `[FILE_PATH]` indicating optional parameter
- Enhanced docstring with clear examples for both usage patterns
### 2. Implemented AST Subsystem Statistics
- New function `_show_ast_subsystem_stats()` for system-level statistics
- Comprehensive statistics collection including:
* **AST Cache**: Directory path, cached files count, cache size
* **Processing Metrics**: Total files processed, recent activity (7 days)
* **System Information**: Service availability, working directory, Python version
### 3. Dual Functionality Support
```bash
markitect ast-stats # Shows AST subsystem statistics
markitect ast-stats document.md # Shows file-specific analysis (preserved)
```
### 4. Consistent Output Formats
- Supports all formats: table, json, yaml, simple
- Table format: Organized with emoji icons and status indicators
- JSON/YAML: Structured data for programmatic use
- Simple: Key-value pairs for scripting
### 5. Robust Error Handling
- Graceful degradation when cache/database unavailable
- Clear status indicators (✅ Available, ❌ Unavailable, ⚠️ Warning)
- Detailed error messages in verbose mode
## Implementation Details:
The command now detects when no file is provided and automatically switches to
subsystem mode, maintaining full backward compatibility with existing file
analysis functionality.
Benefits:
- **Consistent CLI Experience**: Matches cache-stats, db-stats, config-stats patterns
- **System Monitoring**: Easy way to check AST subsystem health
- **Backward Compatible**: Existing scripts continue to work unchanged
- **Professional Interface**: Clear separation between system and file-level stats
All functionality tested and working correctly with comprehensive error handling.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
169
markitect/cli.py
169
markitect/cli.py
@@ -1081,25 +1081,180 @@ def ast_query(config, file_path, jsonpath, format):
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _show_ast_subsystem_stats(config, format):
|
||||
"""Display AST subsystem statistics including cache and processing metrics."""
|
||||
try:
|
||||
# Import dependencies
|
||||
from .ast_cache import ASTCache
|
||||
from .cache_service import CacheDirectoryService
|
||||
|
||||
# Collect AST subsystem statistics
|
||||
stats = {}
|
||||
|
||||
# AST Cache information
|
||||
try:
|
||||
cache_service = CacheDirectoryService()
|
||||
cache_stats = cache_service.get_cache_stats()
|
||||
stats['ast_cache'] = {
|
||||
'directory': cache_stats.get('cache_directory', 'Unknown'),
|
||||
'total_files': cache_stats.get('total_files', 0),
|
||||
'cache_size_bytes': cache_stats.get('cache_size_bytes', 0),
|
||||
'cache_size_human': cache_stats.get('cache_size_human', '0 B'),
|
||||
'available': True
|
||||
}
|
||||
except Exception as e:
|
||||
stats['ast_cache'] = {
|
||||
'available': False,
|
||||
'error': str(e)
|
||||
}
|
||||
|
||||
# Database statistics (files processed)
|
||||
try:
|
||||
db_manager = config.get('db_manager')
|
||||
if db_manager:
|
||||
# Get count of files in database (processed files)
|
||||
conn = db_manager.get_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT COUNT(*) FROM markdown_files")
|
||||
total_files = cursor.fetchone()[0]
|
||||
|
||||
# Get recent processing info
|
||||
cursor.execute("""
|
||||
SELECT COUNT(*) FROM markdown_files
|
||||
WHERE created_at >= datetime('now', '-7 days')
|
||||
""")
|
||||
recent_files = cursor.fetchone()[0]
|
||||
|
||||
stats['processing'] = {
|
||||
'total_files_processed': total_files,
|
||||
'files_processed_last_7_days': recent_files,
|
||||
'database_available': True
|
||||
}
|
||||
else:
|
||||
stats['processing'] = {
|
||||
'database_available': False,
|
||||
'message': 'Database not initialized'
|
||||
}
|
||||
except Exception as e:
|
||||
stats['processing'] = {
|
||||
'database_available': False,
|
||||
'error': str(e)
|
||||
}
|
||||
|
||||
# System information
|
||||
stats['system'] = {
|
||||
'ast_service_available': True, # If we got here, it's available
|
||||
'working_directory': os.getcwd(),
|
||||
'python_version': sys.version.split()[0]
|
||||
}
|
||||
|
||||
# Format output
|
||||
if format == 'json':
|
||||
click.echo(json.dumps(stats, indent=2))
|
||||
elif format == 'yaml':
|
||||
click.echo(yaml.dump(stats, default_flow_style=False))
|
||||
elif format == 'simple':
|
||||
# Simple key-value output
|
||||
if stats['ast_cache']['available']:
|
||||
cache = stats['ast_cache']
|
||||
click.echo(f"ast_cache_directory: {cache['directory']}")
|
||||
click.echo(f"ast_cache_files: {cache['total_files']}")
|
||||
click.echo(f"ast_cache_size: {cache['cache_size_human']}")
|
||||
else:
|
||||
click.echo(f"ast_cache_available: False")
|
||||
|
||||
if stats['processing']['database_available']:
|
||||
proc = stats['processing']
|
||||
click.echo(f"total_files_processed: {proc['total_files_processed']}")
|
||||
click.echo(f"recent_files_processed: {proc['files_processed_last_7_days']}")
|
||||
else:
|
||||
click.echo("database_available: False")
|
||||
|
||||
sys_info = stats['system']
|
||||
click.echo(f"working_directory: {sys_info['working_directory']}")
|
||||
click.echo(f"python_version: {sys_info['python_version']}")
|
||||
else: # table format (default)
|
||||
click.echo("📊 AST Subsystem Statistics")
|
||||
click.echo("=" * 50)
|
||||
|
||||
# AST Cache section
|
||||
click.echo("\n🗃️ AST Cache:")
|
||||
if stats['ast_cache']['available']:
|
||||
cache = stats['ast_cache']
|
||||
click.echo(f" Directory: {cache['directory']}")
|
||||
click.echo(f" Cached Files: {cache['total_files']}")
|
||||
click.echo(f" Cache Size: {cache['cache_size_human']}")
|
||||
if cache['total_files'] == 0:
|
||||
click.echo(" Status: ⚠️ No files cached yet")
|
||||
else:
|
||||
click.echo(f" Status: ✅ Active")
|
||||
else:
|
||||
click.echo(" Status: ❌ Unavailable")
|
||||
if 'error' in stats['ast_cache']:
|
||||
click.echo(f" Error: {stats['ast_cache']['error']}")
|
||||
|
||||
# Processing section
|
||||
click.echo("\n⚙️ Processing Metrics:")
|
||||
if stats['processing']['database_available']:
|
||||
proc = stats['processing']
|
||||
click.echo(f" Total Files Processed: {proc['total_files_processed']}")
|
||||
click.echo(f" Files Processed (Last 7 Days): {proc['files_processed_last_7_days']}")
|
||||
if proc['total_files_processed'] == 0:
|
||||
click.echo(" Status: ⚠️ No files processed yet")
|
||||
else:
|
||||
click.echo(" Status: ✅ Active")
|
||||
else:
|
||||
click.echo(" Status: ❌ Database unavailable")
|
||||
if 'error' in stats['processing']:
|
||||
click.echo(f" Error: {stats['processing']['error']}")
|
||||
elif 'message' in stats['processing']:
|
||||
click.echo(f" Note: {stats['processing']['message']}")
|
||||
|
||||
# System section
|
||||
click.echo("\n🖥️ System Information:")
|
||||
sys_info = stats['system']
|
||||
click.echo(f" AST Service: ✅ Available")
|
||||
click.echo(f" Working Directory: {sys_info['working_directory']}")
|
||||
click.echo(f" Python Version: {sys_info['python_version']}")
|
||||
|
||||
except Exception as e:
|
||||
click.echo(f"Error gathering AST subsystem statistics: {e}", err=True)
|
||||
if config.get('verbose'):
|
||||
import traceback
|
||||
click.echo(traceback.format_exc(), err=True)
|
||||
|
||||
|
||||
@cli.command('ast-stats')
|
||||
@click.argument('file_path', type=click.Path(exists=False))
|
||||
@click.argument('file_path', type=click.Path(exists=False), required=False)
|
||||
@click.option('--format', '-f', type=click.Choice(['table', 'json', 'yaml', 'simple']), default='table', help='Output format')
|
||||
@pass_config
|
||||
def ast_stats(config, file_path, format):
|
||||
"""
|
||||
Show AST statistics (headings, links, etc.).
|
||||
Show AST statistics for files or AST subsystem information.
|
||||
|
||||
Analyze markdown file structure and provide comprehensive statistics
|
||||
about document elements, organization, and content patterns.
|
||||
When called with a file: Analyze markdown file structure and provide
|
||||
comprehensive statistics about document elements, organization, and content patterns.
|
||||
|
||||
FILE_PATH: Path to the markdown file to analyze
|
||||
When called without a file: Show AST subsystem statistics including cache
|
||||
information, processing metrics, and system status.
|
||||
|
||||
FILE_PATH: Optional path to the markdown file to analyze
|
||||
|
||||
Examples:
|
||||
markitect ast-stats document.md
|
||||
markitect ast-stats # Show AST subsystem statistics
|
||||
markitect ast-stats document.md # Analyze specific file
|
||||
markitect ast-stats document.md --format json
|
||||
markitect ast-stats document.md --format yaml
|
||||
"""
|
||||
try:
|
||||
# If no file provided, show AST subsystem statistics
|
||||
if not file_path:
|
||||
if config.get('verbose'):
|
||||
click.echo("Displaying AST subsystem statistics", err=True)
|
||||
|
||||
_show_ast_subsystem_stats(config, format)
|
||||
return
|
||||
|
||||
# File-specific analysis (existing behavior)
|
||||
if config.get('verbose'):
|
||||
click.echo(f"Calculating statistics for: {file_path}", err=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user