Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0] - 2025-10-28

### Added
- **Processing Modes System**: Introduced modular architecture for different diffgraph generation strategies
- Created `BaseProcessor` abstract class for implementing custom processing modes
- Added processor registry and factory pattern for mode instantiation
- Implemented `@register_processor` decorator for automatic mode registration
- **OpenAI Agents Dependency Graph Mode**: Refactored existing AI analysis into `openai-agents-dependency-graph` mode
- Maintains all existing functionality as the default processing mode
- Analyzes code at component level (classes, functions, methods)
- Generates dependency graphs showing component relationships
- **CLI Enhancements**:
- Added `--mode` / `-m` option to select processing mode
- Added `--list-modes` flag to display available processing modes
- Default mode: `openai-agents-dependency-graph` (backward compatible)
- **Documentation**:
- Added comprehensive developer guide: `docs/ADDING_PROCESSING_MODES.md`
- Updated README.md with processing modes information
- Documented how to create custom processing modes

### Changed
- Refactored `CodeAnalysisAgent` into modular `OpenAIAgentsProcessor`
- Removed direct dependency on `ai_analysis.py` in CLI (now uses processor factory)
- Improved extensibility for adding new analysis approaches (Tree-sitter, data flow, etc.)

### Removed
- `diffgraph/ai_analysis.py` - Functionality moved to `diffgraph/processing_modes/openai_agents_dependency.py`


## [1.0.0] - 2025-08-06

### Changed
Expand Down
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,53 @@ This will:
- `--api-key`: Specify your OpenAI API key (defaults to OPENAI_API_KEY environment variable)
- `--output` or `-o`: Specify the output HTML file path (default: diffgraph.html)
- `--no-open`: Don't automatically open the HTML report in browser
- `--mode` or `-m`: Select processing mode for diffgraph generation (default: openai-agents-dependency-graph)
- `--list-modes`: List all available processing modes
- `--version`: Show version information

Example:
Examples:
```bash
wild --output my-report.html --no-open
# Generate report with default mode
wild diff

# Generate report with custom output path
wild diff --output my-report.html --no-open

# List available processing modes
wild diff --list-modes

# Use a specific processing mode
wild diff --mode openai-agents-dependency-graph
```

## 🔧 Processing Modes

DiffGraph supports multiple processing modes for analyzing code changes. Each mode provides a different perspective on your code:

### Available Modes

#### `openai-agents-dependency-graph` (default)
Uses OpenAI Agents SDK to analyze code and generate component-level dependency graphs. This mode:
- Identifies classes, functions, and methods in your changes
- Analyzes dependencies between components
- Generates a visual dependency graph showing how components relate to each other
- Best for understanding architectural changes and component interactions

### Future Modes

The architecture is designed to support additional processing modes:
- **tree-sitter-dependency-graph**: AST-based analysis using Tree-sitter
- **data-flow-analysis**: Focus on data flow and transformations
- **user-context-analysis**: Analyze changes from a user interaction perspective
- **architecture-analysis**: System-level architectural insights

### Adding Custom Processing Modes

Developers can extend DiffGraph by creating custom processing modes. See the `diffgraph/processing_modes/` directory for examples. Each processor must:
1. Inherit from `BaseProcessor`
2. Implement the `analyze_changes()` method
3. Register itself using the `@register_processor` decorator

## 📊 Example Output

The generated HTML report includes:
Expand Down
2 changes: 1 addition & 1 deletion diffgraph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
DiffGraph - A CLI tool for visualizing code changes with AI
"""

__version__ = "0.1.0"
__version__ = "1.1.0"
31 changes: 24 additions & 7 deletions diffgraph/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from click_spinner import spinner
from typing import List, Dict
import os
from diffgraph.ai_analysis import CodeAnalysisAgent
from diffgraph.html_report import generate_html_report, AnalysisResult
from diffgraph.env_loader import load_env_file, debug_environment
from diffgraph.utils import sanitize_diff_args, involves_working_tree
from diffgraph.processing_modes import get_processor, list_available_modes

# Load environment variables
load_env_file()
Expand Down Expand Up @@ -136,9 +136,21 @@ def load_file_contents(changed_files: List[Dict[str, str]], diff_args: List[str]
@click.option('--output', '-o', default='diffgraph.html', help='Output HTML file path')
@click.option('--no-open', is_flag=True, help='Do not open the HTML report automatically')
@click.option('--debug-env', is_flag=True, help='Debug environment variable loading')
def main(args, api_key: str, output: str, no_open: bool, debug_env: bool):
@click.option('--mode', '-m', default='openai-agents-dependency-graph',
help='Processing mode for diffgraph generation (default: openai-agents-dependency-graph)')
@click.option('--list-modes', is_flag=True, help='List available processing modes and exit')
def main(args, api_key: str, output: str, no_open: bool, debug_env: bool, mode: str, list_modes: bool):
"""wild - Git wrapper CLI with DiffGraph for diff commands."""

# Handle --list-modes flag
if list_modes:
click.echo("Available processing modes:\n")
modes = list_available_modes()
for mode_name, description in modes.items():
click.echo(f" • {mode_name}")
click.echo(f" {description}\n")
return

# Check if this is a diff command
if args and args[0] == 'diff':
# Handle diff command with custom logic
Expand Down Expand Up @@ -167,9 +179,14 @@ def main(args, api_key: str, output: str, no_open: bool, debug_env: bool):
files_with_content = load_file_contents(files, diff_args)

try:
# Initialize the AI analysis agent
click.echo("🤖 Initializing AI analysis...")
agent = CodeAnalysisAgent(api_key=api_key)
# Initialize the processor based on selected mode
click.echo(f"🤖 Initializing {mode} processor...")
try:
processor = get_processor(mode, api_key=api_key)
except ValueError as e:
click.echo(f"❌ Error: {e}", err=True)
click.echo("\nUse --list-modes to see available processing modes.", err=True)
sys.exit(1)

# Define progress callback
def progress_callback(current_file, total_files, status):
Expand All @@ -178,7 +195,7 @@ def progress_callback(current_file, total_files, status):
return

file_name = os.path.basename(current_file)
current_index = len(agent.graph_manager.processed_files) + 1
current_index = len(processor.graph_manager.processed_files) + 1

if status == "processing":
click.echo(f"🔄 Processing {file_name} ({current_index}/{total_files})...")
Expand All @@ -193,7 +210,7 @@ def progress_callback(current_file, total_files, status):

# Analyze the changes with progress updates
click.echo("🧠 Starting code analysis...")
analysis = agent.analyze_changes(files_with_content, progress_callback)
analysis = processor.analyze_changes(files_with_content, progress_callback)

# Create analysis result
click.echo("📊 Creating analysis result...")
Expand Down
99 changes: 99 additions & 0 deletions diffgraph/processing_modes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""
Processing modes module for different diffgraph generation strategies.

This module provides a registry of available processing modes and factory
functions to create processor instances.
"""

from typing import Dict, Type, Optional
from .base import BaseProcessor, DiffAnalysis

# Registry of available processing modes
_PROCESSOR_REGISTRY: Dict[str, Type[BaseProcessor]] = {}


def register_processor(mode_name: str):
"""
Decorator to register a processor class.

Args:
mode_name: The name identifier for this processing mode

Example:
@register_processor("openai-agents-dependency-graph")
class OpenAIAgentsProcessor(BaseProcessor):
...
"""
def decorator(cls: Type[BaseProcessor]):
_PROCESSOR_REGISTRY[mode_name] = cls
return cls
return decorator


def get_processor(mode_name: str, **kwargs) -> BaseProcessor:
"""
Factory function to create a processor instance.

Args:
mode_name: The name of the processing mode
**kwargs: Configuration parameters for the processor

Returns:
An instance of the requested processor

Raises:
ValueError: If the mode_name is not registered
"""
if mode_name not in _PROCESSOR_REGISTRY:
available_modes = ", ".join(_PROCESSOR_REGISTRY.keys())
raise ValueError(
f"Unknown processing mode: '{mode_name}'. "
f"Available modes: {available_modes}"
)

processor_class = _PROCESSOR_REGISTRY[mode_name]
return processor_class(**kwargs)


def list_available_modes() -> Dict[str, str]:
"""
Get a dictionary of available processing modes and their descriptions.

Returns:
Dictionary mapping mode names to descriptions
"""
modes = {}
for mode_name, processor_class in _PROCESSOR_REGISTRY.items():
# Get description by creating a minimal instance
try:
# Try to create instance without required parameters to get description
# Most processors should allow getting description without full initialization
temp_instance = processor_class.__new__(processor_class)
if hasattr(temp_instance, 'description'):
desc = temp_instance.description
if isinstance(desc, property):
# For property descriptors, we need to access via class
description = processor_class.description.fget(temp_instance)
else:
description = desc
else:
description = "No description available"
except Exception as e:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Replace unused exception variable.

The caught exception is never used. Replace e with _ to indicate it's intentionally ignored.

Apply this diff:

-        except Exception as e:
+        except Exception:
             # Fallback: try to get from docstring or use default
             description = processor_class.__doc__.split('\n')[0] if processor_class.__doc__ else "No description available"
🧰 Tools
🪛 Ruff (0.14.2)

81-81: Do not catch blind exception: Exception

(BLE001)


81-81: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

🤖 Prompt for AI Agents
In diffgraph/processing_modes/__init__.py around line 81, the except clause
declares an unused variable `e`; replace it with `_` to signal the exception is
intentionally ignored by changing `except Exception as e:` to `except Exception
as _:` (or simply `except Exception:`) and ensure no references to `e` remain in
that block.

# Fallback: try to get from docstring or use default
description = processor_class.__doc__.split('\n')[0] if processor_class.__doc__ else "No description available"
modes[mode_name] = description
return modes


# Import processors to trigger registration
# This will be populated as we add more processors
from . import openai_agents_dependency # noqa: F401, E402


__all__ = [
"BaseProcessor",
"DiffAnalysis",
"register_processor",
"get_processor",
"list_available_modes",
]
77 changes: 77 additions & 0 deletions diffgraph/processing_modes/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
Base processor interface for different diffgraph generation modes.

This module defines the abstract base class that all processing modes must implement.
"""

from abc import ABC, abstractmethod
from typing import List, Dict, Optional, Callable
from pydantic import BaseModel


class DiffAnalysis(BaseModel):
"""Model representing the analysis of code changes."""
summary: str
mermaid_diagram: str


class BaseProcessor(ABC):
"""
Abstract base class for diffgraph processors.

Each processing mode (e.g., OpenAI Agents, Tree-sitter, etc.) should inherit
from this class and implement the analyze_changes method.
"""

def __init__(self, **kwargs):
"""
Initialize the processor with configuration options.

Args:
**kwargs: Configuration parameters specific to the processor
"""
self.config = kwargs

@abstractmethod
def analyze_changes(
self,
files_with_content: List[Dict[str, str]],
progress_callback: Optional[Callable] = None
) -> DiffAnalysis:
"""
Analyze code changes and generate a diffgraph.

Args:
files_with_content: List of dictionaries containing:
- path: File path
- status: Change status (modified, untracked, etc.)
- content: File content or diff
progress_callback: Optional callback function to report progress.
Should accept (current_file, total_files, status)

Returns:
DiffAnalysis object containing summary and mermaid diagram
"""
pass

@property
@abstractmethod
def name(self) -> str:
"""Return the name/identifier of this processing mode."""
pass

@property
@abstractmethod
def description(self) -> str:
"""Return a human-readable description of this processing mode."""
pass

@classmethod
def get_required_config(cls) -> List[str]:
"""
Return list of required configuration parameters for this processor.

Returns:
List of configuration parameter names
"""
return []
Loading