refactor: rename review_pending_changes to review_changes

- Renamed tool from review_pending_changes to review_changes for brevity
- Enhanced tool descriptions for better MCP auto-discovery
- Updated all references throughout codebase including:
  - Tool implementation (tools/review_changes.py)
  - Test files (tests/test_review_changes.py)
  - Server registration and imports
  - Documentation in README.md
  - Tool prompts in prompts/tool_prompts.py
- Enhanced review_changes description to emphasize pre-commit usage
- All tests pass, linting and formatting checks pass

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Fahad
2025-06-09 14:37:03 +04:00
parent dc366d3a23
commit fd6e2f9b64
8 changed files with 48 additions and 48 deletions

View File

@@ -14,7 +14,7 @@ Claude is brilliant, but sometimes you need:
- **A senior developer partner** to validate and extend ideas ([`chat`](#1-chat---general-development-chat--collaborative-thinking)) - **A senior developer partner** to validate and extend ideas ([`chat`](#1-chat---general-development-chat--collaborative-thinking))
- **A second opinion** on complex architectural decisions - augment Claude's extended thinking with Gemini's perspective ([`think_deeper`](#2-think_deeper---extended-reasoning-partner)) - **A second opinion** on complex architectural decisions - augment Claude's extended thinking with Gemini's perspective ([`think_deeper`](#2-think_deeper---extended-reasoning-partner))
- **Professional code reviews** with actionable feedback across entire repositories ([`review_code`](#3-review_code---professional-code-review)) - **Professional code reviews** with actionable feedback across entire repositories ([`review_code`](#3-review_code---professional-code-review))
- **Pre-commit validation** with deep analysis that finds edge cases, validates your implementation against original requirements, and catches subtle bugs Claude might miss ([`review_pending_changes`](#4-review_pending_changes---pre-commit-validation)) - **Pre-commit validation** with deep analysis that finds edge cases, validates your implementation against original requirements, and catches subtle bugs Claude might miss ([`review_changes`](#4-review_changes---pre-commit-validation))
- **Expert debugging** for tricky issues with full system context ([`debug_issue`](#5-debug_issue---expert-debugging-assistant)) - **Expert debugging** for tricky issues with full system context ([`debug_issue`](#5-debug_issue---expert-debugging-assistant))
- **Massive context window** (1M tokens) - Gemini 2.5 Pro can analyze entire codebases, read hundreds of files at once, and provide comprehensive insights ([`analyze`](#6-analyze---smart-file-analysis)) - **Massive context window** (1M tokens) - Gemini 2.5 Pro can analyze entire codebases, read hundreds of files at once, and provide comprehensive insights ([`analyze`](#6-analyze---smart-file-analysis))
- **Deep code analysis** across massive codebases that exceed Claude's context limits ([`analyze`](#6-analyze---smart-file-analysis)) - **Deep code analysis** across massive codebases that exceed Claude's context limits ([`analyze`](#6-analyze---smart-file-analysis))
@@ -119,7 +119,7 @@ Just ask Claude naturally:
- **Need a thinking partner?** → `chat` (brainstorm ideas, get second opinions, validate approaches) - **Need a thinking partner?** → `chat` (brainstorm ideas, get second opinions, validate approaches)
- **Need deeper thinking?** → `think_deeper` (extends Claude's analysis, finds edge cases) - **Need deeper thinking?** → `think_deeper` (extends Claude's analysis, finds edge cases)
- **Code needs review?** → `review_code` (bugs, security, performance issues) - **Code needs review?** → `review_code` (bugs, security, performance issues)
- **Pre-commit validation?** → `review_pending_changes` (validate git changes before committing) - **Pre-commit validation?** → `review_changes` (validate git changes before committing)
- **Something's broken?** → `debug_issue` (root cause analysis, error tracing) - **Something's broken?** → `debug_issue` (root cause analysis, error tracing)
- **Want to understand code?** → `analyze` (architecture, patterns, dependencies) - **Want to understand code?** → `analyze` (architecture, patterns, dependencies)
- **Check models?** → `list_models` (see available Gemini models) - **Check models?** → `list_models` (see available Gemini models)
@@ -129,7 +129,7 @@ Just ask Claude naturally:
1. [`chat`](#1-chat---general-development-chat--collaborative-thinking) - Collaborative thinking and development conversations 1. [`chat`](#1-chat---general-development-chat--collaborative-thinking) - Collaborative thinking and development conversations
2. [`think_deeper`](#2-think_deeper---extended-reasoning-partner) - Extended reasoning and problem-solving 2. [`think_deeper`](#2-think_deeper---extended-reasoning-partner) - Extended reasoning and problem-solving
3. [`review_code`](#3-review_code---professional-code-review) - Professional code review with severity levels 3. [`review_code`](#3-review_code---professional-code-review) - Professional code review with severity levels
4. [`review_pending_changes`](#4-review_pending_changes---pre-commit-validation) - Validate git changes before committing 4. [`review_changes`](#4-review_changes---pre-commit-validation) - Validate git changes before committing
5. [`debug_issue`](#5-debug_issue---expert-debugging-assistant) - Root cause analysis and debugging 5. [`debug_issue`](#5-debug_issue---expert-debugging-assistant) - Root cause analysis and debugging
6. [`analyze`](#6-analyze---smart-file-analysis) - General-purpose file and code analysis 6. [`analyze`](#6-analyze---smart-file-analysis) - General-purpose file and code analysis
7. [`list_models`](#7-list_models---see-available-gemini-models) - List available Gemini models 7. [`list_models`](#7-list_models---see-available-gemini-models) - List available Gemini models
@@ -244,7 +244,7 @@ make any necessary adjustments and show me the final secure implementation."
**Triggers:** review code, check for issues, find bugs, security check **Triggers:** review code, check for issues, find bugs, security check
### 4. `review_pending_changes` - Pre-Commit Validation ### 4. `review_changes` - Pre-Commit Validation
**Comprehensive review of staged/unstaged git changes across multiple repositories** **Comprehensive review of staged/unstaged git changes across multiple repositories**
#### Example Prompts: #### Example Prompts:

View File

@@ -137,7 +137,7 @@ the ideal thinking partner who helps explore ideas deeply, validates approaches,
insights that might be missed in solo analysis. Think step by step through complex problems insights that might be missed in solo analysis. Think step by step through complex problems
and don't hesitate to explore tangential but relevant considerations.""" and don't hesitate to explore tangential but relevant considerations."""
REVIEW_PENDING_CHANGES_PROMPT = """You are an expert code change analyst specializing in pre-commit review of git diffs. REVIEW_CHANGES_PROMPT = """You are an expert code change analyst specializing in pre-commit review of git diffs.
Your role is to act as a seasoned senior developer performing a final review before code is committed. Your role is to act as a seasoned senior developer performing a final review before code is committed.
IMPORTANT: If you need additional context (e.g., related files not in the diff, test files, configuration) IMPORTANT: If you need additional context (e.g., related files not in the diff, test files, configuration)

View File

@@ -26,8 +26,8 @@ from tools import (
AnalyzeTool, AnalyzeTool,
ChatTool, ChatTool,
DebugIssueTool, DebugIssueTool,
ReviewChanges,
ReviewCodeTool, ReviewCodeTool,
ReviewPendingChanges,
ThinkDeeperTool, ThinkDeeperTool,
) )
@@ -45,7 +45,7 @@ TOOLS = {
"debug_issue": DebugIssueTool(), "debug_issue": DebugIssueTool(),
"analyze": AnalyzeTool(), "analyze": AnalyzeTool(),
"chat": ChatTool(), "chat": ChatTool(),
"review_pending_changes": ReviewPendingChanges(), "review_changes": ReviewChanges(),
} }

View File

@@ -1,5 +1,5 @@
""" """
Tests for the review_pending_changes tool Tests for the review_changes tool
""" """
import json import json
@@ -7,23 +7,23 @@ from unittest.mock import Mock, patch
import pytest import pytest
from tools.review_pending_changes import ( from tools.review_changes import (
ReviewPendingChanges, ReviewChanges,
ReviewPendingChangesRequest, ReviewChangesRequest,
) )
class TestReviewPendingChangesTool: class TestReviewChangesTool:
"""Test the review_pending_changes tool""" """Test the review_changes tool"""
@pytest.fixture @pytest.fixture
def tool(self): def tool(self):
"""Create tool instance""" """Create tool instance"""
return ReviewPendingChanges() return ReviewChanges()
def test_tool_metadata(self, tool): def test_tool_metadata(self, tool):
"""Test tool metadata""" """Test tool metadata"""
assert tool.get_name() == "review_pending_changes" assert tool.get_name() == "review_changes"
assert "REVIEW PENDING GIT CHANGES" in tool.get_description() assert "REVIEW PENDING GIT CHANGES" in tool.get_description()
assert "pre-commit review" in tool.get_description() assert "pre-commit review" in tool.get_description()
@@ -37,7 +37,7 @@ class TestReviewPendingChangesTool:
def test_request_model_defaults(self): def test_request_model_defaults(self):
"""Test request model default values""" """Test request model default values"""
request = ReviewPendingChangesRequest(path="/some/absolute/path") request = ReviewChangesRequest(path="/some/absolute/path")
assert request.path == "/some/absolute/path" assert request.path == "/some/absolute/path"
assert request.original_request is None assert request.original_request is None
assert request.compare_to is None assert request.compare_to is None
@@ -77,21 +77,21 @@ class TestReviewPendingChangesTool:
assert "./relative/path" in response["content"] assert "./relative/path" in response["content"]
@pytest.mark.asyncio @pytest.mark.asyncio
@patch("tools.review_pending_changes.find_git_repositories") @patch("tools.review_changes.find_git_repositories")
async def test_no_repositories_found(self, mock_find_repos, tool): async def test_no_repositories_found(self, mock_find_repos, tool):
"""Test when no git repositories are found""" """Test when no git repositories are found"""
mock_find_repos.return_value = [] mock_find_repos.return_value = []
request = ReviewPendingChangesRequest(path="/absolute/path/no-git") request = ReviewChangesRequest(path="/absolute/path/no-git")
result = await tool.prepare_prompt(request) result = await tool.prepare_prompt(request)
assert result == "No git repositories found in the specified path." assert result == "No git repositories found in the specified path."
mock_find_repos.assert_called_once_with("/absolute/path/no-git", 5) mock_find_repos.assert_called_once_with("/absolute/path/no-git", 5)
@pytest.mark.asyncio @pytest.mark.asyncio
@patch("tools.review_pending_changes.find_git_repositories") @patch("tools.review_changes.find_git_repositories")
@patch("tools.review_pending_changes.get_git_status") @patch("tools.review_changes.get_git_status")
@patch("tools.review_pending_changes.run_git_command") @patch("tools.review_changes.run_git_command")
async def test_no_changes_found( async def test_no_changes_found(
self, mock_run_git, mock_status, mock_find_repos, tool self, mock_run_git, mock_status, mock_find_repos, tool
): ):
@@ -112,15 +112,15 @@ class TestReviewPendingChangesTool:
(True, ""), # unstaged files (empty) (True, ""), # unstaged files (empty)
] ]
request = ReviewPendingChangesRequest(path="/absolute/repo/path") request = ReviewChangesRequest(path="/absolute/repo/path")
result = await tool.prepare_prompt(request) result = await tool.prepare_prompt(request)
assert result == "No pending changes found in any of the git repositories." assert result == "No pending changes found in any of the git repositories."
@pytest.mark.asyncio @pytest.mark.asyncio
@patch("tools.review_pending_changes.find_git_repositories") @patch("tools.review_changes.find_git_repositories")
@patch("tools.review_pending_changes.get_git_status") @patch("tools.review_changes.get_git_status")
@patch("tools.review_pending_changes.run_git_command") @patch("tools.review_changes.run_git_command")
async def test_staged_changes_review( async def test_staged_changes_review(
self, self,
mock_run_git, mock_run_git,
@@ -149,7 +149,7 @@ class TestReviewPendingChangesTool:
(True, ""), # unstaged files (empty) (True, ""), # unstaged files (empty)
] ]
request = ReviewPendingChangesRequest( request = ReviewChangesRequest(
path="/absolute/repo/path", path="/absolute/repo/path",
original_request="Add hello message", original_request="Add hello message",
review_type="security", review_type="security",
@@ -166,9 +166,9 @@ class TestReviewPendingChangesTool:
assert "## Git Diffs" in result assert "## Git Diffs" in result
@pytest.mark.asyncio @pytest.mark.asyncio
@patch("tools.review_pending_changes.find_git_repositories") @patch("tools.review_changes.find_git_repositories")
@patch("tools.review_pending_changes.get_git_status") @patch("tools.review_changes.get_git_status")
@patch("tools.review_pending_changes.run_git_command") @patch("tools.review_changes.run_git_command")
async def test_compare_to_invalid_ref( async def test_compare_to_invalid_ref(
self, mock_run_git, mock_status, mock_find_repos, tool self, mock_run_git, mock_status, mock_find_repos, tool
): ):
@@ -181,7 +181,7 @@ class TestReviewPendingChangesTool:
(False, "fatal: not a valid ref"), # rev-parse fails (False, "fatal: not a valid ref"), # rev-parse fails
] ]
request = ReviewPendingChangesRequest( request = ReviewChangesRequest(
path="/absolute/repo/path", compare_to="invalid-branch" path="/absolute/repo/path", compare_to="invalid-branch"
) )
result = await tool.prepare_prompt(request) result = await tool.prepare_prompt(request)
@@ -190,7 +190,7 @@ class TestReviewPendingChangesTool:
assert "No pending changes found in any of the git repositories." in result assert "No pending changes found in any of the git repositories." in result
@pytest.mark.asyncio @pytest.mark.asyncio
@patch("tools.review_pending_changes.ReviewPendingChanges.execute") @patch("tools.review_changes.ReviewChanges.execute")
async def test_execute_integration(self, mock_execute, tool): async def test_execute_integration(self, mock_execute, tool):
"""Test execute method integration""" """Test execute method integration"""
# Mock the execute to return a standardized response # Mock the execute to return a standardized response
@@ -212,9 +212,9 @@ class TestReviewPendingChangesTool:
assert tool.get_default_temperature() == TEMPERATURE_ANALYTICAL assert tool.get_default_temperature() == TEMPERATURE_ANALYTICAL
@pytest.mark.asyncio @pytest.mark.asyncio
@patch("tools.review_pending_changes.find_git_repositories") @patch("tools.review_changes.find_git_repositories")
@patch("tools.review_pending_changes.get_git_status") @patch("tools.review_changes.get_git_status")
@patch("tools.review_pending_changes.run_git_command") @patch("tools.review_changes.run_git_command")
async def test_mixed_staged_unstaged_changes( async def test_mixed_staged_unstaged_changes(
self, self,
mock_run_git, mock_run_git,
@@ -240,7 +240,7 @@ class TestReviewPendingChangesTool:
(True, "diff --git a/file2.py..."), # diff for file2.py (True, "diff --git a/file2.py..."), # diff for file2.py
] ]
request = ReviewPendingChangesRequest( request = ReviewChangesRequest(
path="/absolute/repo/path", path="/absolute/repo/path",
focus_on="error handling", focus_on="error handling",
severity_filter="high", severity_filter="high",

View File

@@ -25,7 +25,7 @@ class TestServerTools:
assert "debug_issue" in tool_names assert "debug_issue" in tool_names
assert "analyze" in tool_names assert "analyze" in tool_names
assert "chat" in tool_names assert "chat" in tool_names
assert "review_pending_changes" in tool_names assert "review_changes" in tool_names
assert "list_models" in tool_names assert "list_models" in tool_names
assert "get_version" in tool_names assert "get_version" in tool_names

View File

@@ -6,7 +6,7 @@ from .analyze import AnalyzeTool
from .chat import ChatTool from .chat import ChatTool
from .debug_issue import DebugIssueTool from .debug_issue import DebugIssueTool
from .review_code import ReviewCodeTool from .review_code import ReviewCodeTool
from .review_pending_changes import ReviewPendingChanges from .review_changes import ReviewChanges
from .think_deeper import ThinkDeeperTool from .think_deeper import ThinkDeeperTool
__all__ = [ __all__ = [
@@ -15,5 +15,5 @@ __all__ = [
"DebugIssueTool", "DebugIssueTool",
"AnalyzeTool", "AnalyzeTool",
"ChatTool", "ChatTool",
"ReviewPendingChanges", "ReviewChanges",
] ]

View File

@@ -86,7 +86,7 @@ class BaseTool(ABC):
f"Please provide the full absolute path starting with '/'" f"Please provide the full absolute path starting with '/'"
) )
# Check if request has 'path' attribute (for review_pending_changes) # Check if request has 'path' attribute (for review_changes)
if hasattr(request, "path") and request.path: if hasattr(request, "path") and request.path:
if not os.path.isabs(request.path): if not os.path.isabs(request.path):
return ( return (

View File

@@ -9,15 +9,15 @@ from typing import Any, Dict, Literal, Optional
from pydantic import Field from pydantic import Field
from config import MAX_CONTEXT_TOKENS from config import MAX_CONTEXT_TOKENS
from prompts.tool_prompts import REVIEW_PENDING_CHANGES_PROMPT from prompts.tool_prompts import REVIEW_CHANGES_PROMPT
from utils.git_utils import find_git_repositories, get_git_status, run_git_command from utils.git_utils import find_git_repositories, get_git_status, run_git_command
from utils.token_utils import estimate_tokens from utils.token_utils import estimate_tokens
from .base import BaseTool, ToolRequest from .base import BaseTool, ToolRequest
class ReviewPendingChangesRequest(ToolRequest): class ReviewChangesRequest(ToolRequest):
"""Request model for review_pending_changes tool""" """Request model for review_changes tool"""
path: str = Field( path: str = Field(
..., ...,
@@ -65,11 +65,11 @@ class ReviewPendingChangesRequest(ToolRequest):
) )
class ReviewPendingChanges(BaseTool): class ReviewChanges(BaseTool):
"""Tool for reviewing pending git changes across multiple repositories.""" """Tool for reviewing git changes across multiple repositories."""
def get_name(self) -> str: def get_name(self) -> str:
return "review_pending_changes" return "review_changes"
def get_description(self) -> str: def get_description(self) -> str:
return ( return (
@@ -86,10 +86,10 @@ class ReviewPendingChanges(BaseTool):
return self.get_request_model().model_json_schema() return self.get_request_model().model_json_schema()
def get_system_prompt(self) -> str: def get_system_prompt(self) -> str:
return REVIEW_PENDING_CHANGES_PROMPT return REVIEW_CHANGES_PROMPT
def get_request_model(self): def get_request_model(self):
return ReviewPendingChangesRequest return ReviewChangesRequest
def get_default_temperature(self) -> float: def get_default_temperature(self) -> float:
"""Use analytical temperature for code review.""" """Use analytical temperature for code review."""
@@ -106,7 +106,7 @@ class ReviewPendingChanges(BaseTool):
# Limit length to avoid filesystem issues # Limit length to avoid filesystem issues
return name[:100] return name[:100]
async def prepare_prompt(self, request: ReviewPendingChangesRequest) -> str: async def prepare_prompt(self, request: ReviewChangesRequest) -> str:
"""Prepare the prompt with git diff information.""" """Prepare the prompt with git diff information."""
# Find all git repositories # Find all git repositories
repositories = find_git_repositories(request.path, request.max_depth) repositories = find_git_repositories(request.path, request.max_depth)