From fd66d6784922a4153649653b84dec23db29340c9 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 30 Sep 2025 22:25:30 +0200 Subject: [PATCH] feat: Make ast-stats command work without file argument - show AST subsystem statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- markitect/cli.py | 169 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 7 deletions(-) diff --git a/markitect/cli.py b/markitect/cli.py index acfed948..8846bcb4 100644 --- a/markitect/cli.py +++ b/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)