diff --git a/markitect/cli.py b/markitect/cli.py index 8846bcb4..3d5643f6 100644 --- a/markitect/cli.py +++ b/markitect/cli.py @@ -267,23 +267,234 @@ def ingest(config, file_path): sys.exit(1) -@cli.command() -@click.argument('file_path', type=str) -@pass_config -def status(config, file_path): - """ - Show processing status and metadata for a file. +def _show_core_system_stats(config, format): + """Display core MarkiTect system statistics and health information.""" + try: + # Collect core system statistics + stats = {} - Displays information about a file's processing status, metadata, + # Database Statistics + try: + db_manager = config.get('db_manager') + if db_manager: + # Get database file info + db_path = config.get('database_path', 'Unknown') + db_exists = Path(db_path).exists() if db_path != 'Unknown' else False + + if db_exists: + db_size = Path(db_path).stat().st_size + db_size_human = format_file_size(db_size) + else: + db_size = 0 + db_size_human = '0 B' + + # Get file counts from database + try: + conn = db_manager.get_connection() + cursor = conn.cursor() + + # Total files + cursor.execute("SELECT COUNT(*) FROM markdown_files") + total_files = cursor.fetchone()[0] + + # Recent files (last 7 days) + cursor.execute(""" + SELECT COUNT(*) FROM markdown_files + WHERE created_at >= datetime('now', '-7 days') + """) + recent_files = cursor.fetchone()[0] + + # Schema count + cursor.execute("SELECT COUNT(*) FROM schema_files") + schema_count = cursor.fetchone()[0] + + stats['database'] = { + 'path': db_path, + 'exists': db_exists, + 'size_bytes': db_size, + 'size_human': db_size_human, + 'total_markdown_files': total_files, + 'recent_files_7_days': recent_files, + 'schema_files': schema_count, + 'available': True + } + except Exception as db_error: + stats['database'] = { + 'path': db_path, + 'exists': db_exists, + 'size_bytes': db_size, + 'size_human': db_size_human, + 'available': False, + 'error': str(db_error) + } + else: + stats['database'] = { + 'available': False, + 'message': 'Database manager not initialized' + } + except Exception as e: + stats['database'] = { + 'available': False, + 'error': str(e) + } + + # Cache Statistics + try: + from .cache_service import CacheDirectoryService + cache_service = CacheDirectoryService() + cache_stats = cache_service.get_cache_stats() + + stats['cache'] = { + 'directory': cache_stats.get('cache_directory', 'Unknown'), + 'total_files': cache_stats.get('total_files', 0), + 'size_bytes': cache_stats.get('cache_size_bytes', 0), + 'size_human': cache_stats.get('cache_size_human', '0 B'), + 'available': True + } + except Exception as e: + stats['cache'] = { + 'available': False, + 'error': str(e) + } + + # System Information + stats['system'] = { + 'working_directory': os.getcwd(), + 'python_version': sys.version.split()[0], + 'config_file': config.get('config_file', 'None specified'), + 'verbose_mode': config.get('verbose', False), + 'execution_mode': detect_execution_mode() + } + + # Subsystem Health Check + subsystems = { + 'database': stats['database']['available'], + 'cache': stats['cache']['available'], + 'ast_service': True, # Available if we got here + } + + healthy_count = sum(subsystems.values()) + total_count = len(subsystems) + + stats['health'] = { + 'subsystems': subsystems, + 'healthy_subsystems': healthy_count, + 'total_subsystems': total_count, + 'health_percentage': round((healthy_count / total_count) * 100, 1), + 'overall_status': 'healthy' if healthy_count == total_count else 'degraded' + } + + # 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['database']['available']: + db = stats['database'] + click.echo(f"database_files: {db['total_markdown_files']}") + click.echo(f"database_size: {db['size_human']}") + click.echo(f"database_recent_files: {db['recent_files_7_days']}") + else: + click.echo("database_available: False") + + if stats['cache']['available']: + cache = stats['cache'] + click.echo(f"cache_files: {cache['total_files']}") + click.echo(f"cache_size: {cache['size_human']}") + else: + click.echo("cache_available: False") + + health = stats['health'] + click.echo(f"system_health: {health['health_percentage']}%") + click.echo(f"overall_status: {health['overall_status']}") + else: # table format (default) + click.echo("šŸ“Š MarkiTect Core System Statistics") + click.echo("=" * 50) + + # System Health Overview + health = stats['health'] + health_icon = "āœ…" if health['overall_status'] == 'healthy' else "āš ļø" + click.echo(f"\nšŸ„ System Health: {health_icon} {health['overall_status'].title()} ({health['health_percentage']}%)") + click.echo(f" Healthy Subsystems: {health['healthy_subsystems']}/{health['total_subsystems']}") + + # Database section + click.echo("\nšŸ—„ļø Database:") + if stats['database']['available']: + db = stats['database'] + click.echo(f" Path: {db['path']}") + click.echo(f" Status: āœ… Available ({db['size_human']})") + click.echo(f" Markdown Files: {db['total_markdown_files']}") + click.echo(f" Schema Files: {db['schema_files']}") + click.echo(f" Recent Activity (7 days): {db['recent_files_7_days']} files") + else: + click.echo(" Status: āŒ Unavailable") + if 'error' in stats['database']: + click.echo(f" Error: {stats['database']['error']}") + elif 'message' in stats['database']: + click.echo(f" Note: {stats['database']['message']}") + + # Cache section + click.echo("\nšŸ—ƒļø Cache:") + if stats['cache']['available']: + cache = stats['cache'] + click.echo(f" Directory: {cache['directory']}") + click.echo(f" Status: āœ… Available ({cache['size_human']})") + click.echo(f" Cached Files: {cache['total_files']}") + else: + click.echo(" Status: āŒ Unavailable") + if 'error' in stats['cache']: + click.echo(f" Error: {stats['cache']['error']}") + + # System section + click.echo("\nšŸ–„ļø System Information:") + sys_info = stats['system'] + click.echo(f" Working Directory: {sys_info['working_directory']}") + click.echo(f" Python Version: {sys_info['python_version']}") + click.echo(f" Execution Mode: {sys_info['execution_mode']}") + click.echo(f" Config File: {sys_info['config_file']}") + click.echo(f" Verbose Mode: {sys_info['verbose_mode']}") + + except Exception as e: + click.echo(f"Error gathering core system statistics: {e}", err=True) + if config.get('verbose'): + import traceback + click.echo(traceback.format_exc(), err=True) + + +@cli.command('stats') +@click.argument('file_path', type=str, required=False) +@click.option('--format', '-f', type=click.Choice(['table', 'json', 'yaml', 'simple']), + default=lambda: get_default_format(['table', 'json', 'yaml', 'simple']), help='Output format') +@pass_config +def stats(config, file_path, format): + """ + Show core system statistics or file-specific information. + + When called with a file: Display file's processing status, metadata, and front matter content from the database. - FILE_PATH: Path or name of the file to check + When called without a file: Show core MarkiTect system statistics + including database status, processing metrics, and subsystem health. + + FILE_PATH: Optional path or name of the file to check Examples: - markitect status README.md - markitect status docs/guide.md + markitect stats # Show core system statistics + markitect stats README.md # Show file-specific status + markitect stats docs/guide.md --format json """ try: + # If no file provided, show core system statistics + if not file_path: + if config.get('verbose'): + click.echo("Displaying core system statistics", err=True) + + _show_core_system_stats(config, format) + return + + # File-specific status (existing behavior) if config['verbose']: click.echo(f"Checking status for: {file_path}")