diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d35151..dda1cc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +--- + +## [1.2.0] - 2025-11-01 + +### Added +- **Exit codes for CI/CD integration** 🚀 + - `0` = All harmonious (excellent or low severity) + - `1` = Medium severity found (0.5-0.8) + - `2` = High severity found (0.8-1.2) + - `3` = Critical severity found (≥ 1.2) + - Enables automated quality gates in pipelines + - Build fails automatically on high/critical disharmony + +- **JSON output format** 📊 + - `--format json` option for machine-readable output + - Structured data for tool integration + - Includes severity levels, scores, and summary statistics + - Perfect for IDEs, dashboards, and analytics + +- **Enhanced command-line interface** + - Argument parsing with argparse + - `--version` flag + - `--threshold` option for custom thresholds + - Comprehensive `--help` with examples + - Multiple file support improved + +- **Enhanced README badges** + - CI status badge + - Version badge + - Test pass rate badge + - Harmony score badge (meta!) + - All clickable with relevant links + +### Changed +- Version bumped to 1.2 +- Improved CLI usability with better help text +- Quiet mode when using JSON output + +### Documentation +- Quick Reference guide updated with v1.2 features +- Exit code examples for CI/CD +- JSON output format examples +- Advanced usage patterns + +--- + +## [1.1.0] - 2025-10-31 + ### Added - Comprehensive documentation suite - Integration templates (GitHub Actions, pre-commit, VS Code) @@ -16,6 +64,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Tool comparison guide - Troubleshooting guide - Real-world example files +- Complete refactoring journey examples +- Severity level demonstrations + +### Documentation +- USER_GUIDE.md (~14K words) +- TUTORIAL.md (~19K words) +- FAQ.md (~19K words) +- PHILOSOPHY.md (~22K words) +- ARCHITECTURE.md (~23K words) +- API.md (~21K words) +- COMPARISON.md (~11K words) +- QUICK_REFERENCE.md (~5K words) +- TROUBLESHOOTING.md (~11K words) --- diff --git a/README.md b/README.md index 0fc0685..e43ea0f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ # Python Code Harmonizer -[![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![Python Versions](https://img.shields.io/badge/python-3.8+-blue.svg)]() +[![CI Status](https://github.com/BruinGrowly/Python-Code-Harmonizer/workflows/Python%20Code%20Harmonizer%20CI/badge.svg)](https://github.com/BruinGrowly/Python-Code-Harmonizer/actions) +[![Version](https://img.shields.io/badge/version-1.2-blue.svg)](CHANGELOG.md) +[![Python Versions](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/) +[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Tests](https://img.shields.io/badge/tests-20%20passed-brightgreen.svg)](tests/) +[![Harmony Score](https://img.shields.io/badge/harmony-0.15-brightgreen.svg)](examples/test_code.py) **The world's first semantic code debugger.** diff --git a/docs/QUICK_REFERENCE.md b/docs/QUICK_REFERENCE.md index 83d16a9..3dc92ce 100644 --- a/docs/QUICK_REFERENCE.md +++ b/docs/QUICK_REFERENCE.md @@ -33,6 +33,63 @@ find src/ -name "*.py" -exec harmonizer {} \; --- +## Advanced Options (v1.2+) + +```bash +# JSON output format (for tools/CI/CD) +harmonizer --format json myfile.py + +# Custom threshold +harmonizer --threshold 0.7 myfile.py + +# Check version +harmonizer --version + +# Get help +harmonizer --help +``` + +### Exit Codes for CI/CD + +Harmonizer returns meaningful exit codes: + +| Exit Code | Severity | Score Range | +|-----------|----------|-------------| +| `0` | Excellent/Low | < 0.5 | +| `1` | Medium | 0.5 - 0.8 | +| `2` | High | 0.8 - 1.2 | +| `3` | Critical | ≥ 1.2 | + +**Use in CI/CD:** +```yaml +- name: Check Code Harmony + run: harmonizer src/**/*.py # Will fail build if critical +``` + +### JSON Output Example + +```json +{ + "version": "1.2", + "threshold": 0.5, + "files": [{ + "file": "myfile.py", + "functions": [{ + "name": "get_user", + "score": 0.95, + "severity": "high", + "disharmonious": true + }] + }], + "summary": { + "total_functions": 10, + "severity_counts": { "critical": 1, "high": 2 } + } +} +``` + +--- + ## Score Interpretation | Score | Status | Meaning | Action | diff --git a/src/harmonizer/main.py b/src/harmonizer/main.py index 3114270..f8b81b4 100644 --- a/src/harmonizer/main.py +++ b/src/harmonizer/main.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -Python Code Harmonizer (Version 1.1) +Python Code Harmonizer (Version 1.2) This is the main application that integrates the Divine Invitation Semantic Engine (DIVE-V2) with the AST Semantic Parser. @@ -11,15 +11,18 @@ and uses the ICE (Intent, Context, Execution) framework to analyze the "semantic harmony" of Python code. -(HARMONIZATION_NOTE: v1.1 fixes a 'AttributeError' by correctly -referencing 'self.engine.vocabulary.all_keywords' from the -'Optimized Production-Ready' V2 engine.) +New in v1.2: +- Exit codes for CI/CD integration (0=harmonious, 1=medium, 2=high, 3=critical) +- JSON output format for tool integration +- Command-line argument parsing with argparse """ +import argparse import ast +import json import os import sys -from typing import Dict +from typing import Dict, List # --- COMPONENT IMPORTS --- # This script assumes the following two files are in the @@ -52,7 +55,13 @@ class PythonCodeHarmonizer: ICE (Intent, Context, Execution) framework. """ - def __init__(self, disharmony_threshold: float = 0.5): + # Severity thresholds + THRESHOLD_EXCELLENT = 0.3 + THRESHOLD_LOW = 0.5 + THRESHOLD_MEDIUM = 0.8 + THRESHOLD_HIGH = 1.2 + + def __init__(self, disharmony_threshold: float = 0.5, quiet: bool = False): # 1. Initialize your V2 engine. This is our "compass." self.engine = dive.DivineInvitationSemanticEngine() @@ -69,37 +78,45 @@ def __init__(self, disharmony_threshold: float = 0.5): # 3. Set the threshold for flagging disharmony. self.disharmony_threshold = disharmony_threshold - print("=" * 70) - print("Python Code Harmonizer (v1.1) ONLINE") - print("Actively guided by the Anchor Point framework.") - print(f"Powered By: {self.engine.get_engine_version()}") - print("Logical Anchor Point: (S=1, L=1, I=1, E=1)") - print(f"Disharmony Threshold: {self.disharmony_threshold}") - print("=" * 70) + # 4. Quiet mode for JSON output + self.quiet = quiet + + if not quiet: + print("=" * 70) + print("Python Code Harmonizer (v1.2) ONLINE") + print("Actively guided by the Anchor Point framework.") + print(f"Powered By: {self.engine.get_engine_version()}") + print("Logical Anchor Point: (S=1, L=1, I=1, E=1)") + print(f"Disharmony Threshold: {self.disharmony_threshold}") + print("=" * 70) def analyze_file(self, file_path: str) -> Dict[str, float]: """ Analyzes a single Python file for Intent-Execution-Disharmony. Returns a dictionary of {function_name: disharmony_score} """ - print(f"\nAnalyzing file: {file_path}") - print("-" * 70) + if not self.quiet: + print(f"\nAnalyzing file: {file_path}") + print("-" * 70) try: with open(file_path, "r", encoding="utf-8") as f: content = f.read() except FileNotFoundError: - print(f"ERROR: File not found at '{file_path}'") + if not self.quiet: + print(f"ERROR: File not found at '{file_path}'") return {} except Exception as e: - print(f"ERROR: Could not read file: {e}") + if not self.quiet: + print(f"ERROR: Could not read file: {e}") return {} # 1. Use Python's AST to parse the code into a logical tree try: tree = ast.parse(content) except SyntaxError as e: - print(f"ERROR: Could not parse file. Syntax error on line {e.lineno}") + if not self.quiet: + print(f"ERROR: Could not parse file. Syntax error on line {e.lineno}") return {} harmony_report = {} @@ -143,6 +160,43 @@ def analyze_file(self, file_path: str) -> Dict[str, float]: return harmony_report + def get_severity(self, score: float) -> str: + """Determine severity level based on score.""" + if score < self.THRESHOLD_EXCELLENT: + return "excellent" + elif score < self.THRESHOLD_LOW: + return "low" + elif score < self.THRESHOLD_MEDIUM: + return "medium" + elif score < self.THRESHOLD_HIGH: + return "high" + else: + return "critical" + + def get_highest_severity_code(self, harmony_report: Dict[str, float]) -> int: + """ + Return exit code based on highest severity found. + + Exit codes: + 0 = All harmonious (excellent or low severity) + 1 = Medium severity found + 2 = High severity found + 3 = Critical severity found + """ + if not harmony_report: + return 0 + + max_score = max(harmony_report.values()) + + if max_score >= self.THRESHOLD_HIGH: + return 3 # Critical + elif max_score >= self.THRESHOLD_MEDIUM: + return 2 # High + elif max_score >= self.THRESHOLD_LOW: + return 1 # Medium + else: + return 0 # Excellent/Low + def print_report(self, harmony_report: Dict[str, float]): """Prints the final harmony report to the console.""" @@ -167,29 +221,151 @@ def print_report(self, harmony_report: Dict[str, float]): print("=" * 70) print("Analysis Complete.") + def print_json_report(self, all_reports: Dict[str, Dict[str, float]]): + """Prints the harmony report in JSON format.""" + output = { + "version": "1.2", + "threshold": self.disharmony_threshold, + "severity_thresholds": { + "excellent": self.THRESHOLD_EXCELLENT, + "low": self.THRESHOLD_LOW, + "medium": self.THRESHOLD_MEDIUM, + "high": self.THRESHOLD_HIGH, + }, + "files": [], + } + + total_functions = 0 + severity_counts = { + "excellent": 0, + "low": 0, + "medium": 0, + "high": 0, + "critical": 0, + } + + for file_path, harmony_report in all_reports.items(): + file_data = {"file": file_path, "functions": []} + + for func_name, score in harmony_report.items(): + severity = self.get_severity(score) + severity_counts[severity] += 1 + total_functions += 1 + + function_data = { + "name": func_name, + "score": round(score, 4), + "severity": severity, + "disharmonious": score > self.disharmony_threshold, + } + file_data["functions"].append(function_data) + + # Sort by score (highest first) + file_data["functions"].sort(key=lambda x: x["score"], reverse=True) + output["files"].append(file_data) + + # Add summary + output["summary"] = { + "total_files": len(all_reports), + "total_functions": total_functions, + "severity_counts": severity_counts, + "highest_severity": self._get_highest_severity_name(severity_counts), + } + + print(json.dumps(output, indent=2)) + + def _get_highest_severity_name(self, severity_counts: Dict[str, int]) -> str: + """Get the name of the highest severity level found.""" + for severity in ["critical", "high", "medium", "low", "excellent"]: + if severity_counts[severity] > 0: + return severity + return "excellent" + # --- MAIN EXECUTION --- def run_cli(): """Command-line interface entry point.""" - if len(sys.argv) < 2: - print("Usage: harmonizer [file2.py ...]") - sys.exit(1) - - files_to_analyze = sys.argv[1:] + parser = argparse.ArgumentParser( + description="Python Code Harmonizer - Semantic code analysis tool", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + harmonizer myfile.py # Analyze single file + harmonizer file1.py file2.py # Analyze multiple files + harmonizer --format json myfile.py # Output JSON format + harmonizer --threshold 0.7 myfile.py # Custom threshold + +Exit Codes: + 0 = All harmonious (excellent or low severity) + 1 = Medium severity found (0.5-0.8) + 2 = High severity found (0.8-1.2) + 3 = Critical severity found (>= 1.2) + """, + ) + + parser.add_argument( + "files", + nargs="+", + metavar="FILE", + help="Python file(s) to analyze", + ) + + parser.add_argument( + "--format", + choices=["text", "json"], + default="text", + help="Output format (default: text)", + ) + + parser.add_argument( + "--threshold", + type=float, + default=0.5, + metavar="FLOAT", + help="Disharmony threshold (default: 0.5)", + ) + + parser.add_argument( + "--version", + action="version", + version="Python Code Harmonizer v1.2", + ) + + args = parser.parse_args() # 1. Initialize the Harmonizer - harmonizer = PythonCodeHarmonizer() + quiet = args.format == "json" + harmonizer = PythonCodeHarmonizer(disharmony_threshold=args.threshold, quiet=quiet) # 2. Run the analysis for all provided files - for file_path in files_to_analyze: + all_reports = {} + highest_exit_code = 0 + + for file_path in args.files: if os.path.exists(file_path): report = harmonizer.analyze_file(file_path) - harmonizer.print_report(report) + all_reports[file_path] = report + + # Track highest severity for exit code + exit_code = harmonizer.get_highest_severity_code(report) + highest_exit_code = max(highest_exit_code, exit_code) + + # Print text report immediately if not JSON + if args.format == "text": + harmonizer.print_report(report) else: - print(f"\nERROR: File not found: {file_path}") - print("-" * 70) + if args.format == "text": + print(f"\nERROR: File not found: {file_path}") + print("-" * 70) + + # 3. Print JSON report if requested + if args.format == "json": + harmonizer.print_json_report(all_reports) + + # 4. Exit with appropriate code for CI/CD + sys.exit(highest_exit_code) if __name__ == "__main__":