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:
@@ -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 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))
|
||||
- **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))
|
||||
- **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))
|
||||
@@ -119,7 +119,7 @@ Just ask Claude naturally:
|
||||
- **Need a thinking partner?** → `chat` (brainstorm ideas, get second opinions, validate approaches)
|
||||
- **Need deeper thinking?** → `think_deeper` (extends Claude's analysis, finds edge cases)
|
||||
- **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)
|
||||
- **Want to understand code?** → `analyze` (architecture, patterns, dependencies)
|
||||
- **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
|
||||
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
|
||||
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
|
||||
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
|
||||
@@ -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
|
||||
|
||||
### 4. `review_pending_changes` - Pre-Commit Validation
|
||||
### 4. `review_changes` - Pre-Commit Validation
|
||||
**Comprehensive review of staged/unstaged git changes across multiple repositories**
|
||||
|
||||
#### Example Prompts:
|
||||
|
||||
@@ -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
|
||||
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.
|
||||
|
||||
IMPORTANT: If you need additional context (e.g., related files not in the diff, test files, configuration)
|
||||
|
||||
@@ -26,8 +26,8 @@ from tools import (
|
||||
AnalyzeTool,
|
||||
ChatTool,
|
||||
DebugIssueTool,
|
||||
ReviewChanges,
|
||||
ReviewCodeTool,
|
||||
ReviewPendingChanges,
|
||||
ThinkDeeperTool,
|
||||
)
|
||||
|
||||
@@ -45,7 +45,7 @@ TOOLS = {
|
||||
"debug_issue": DebugIssueTool(),
|
||||
"analyze": AnalyzeTool(),
|
||||
"chat": ChatTool(),
|
||||
"review_pending_changes": ReviewPendingChanges(),
|
||||
"review_changes": ReviewChanges(),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Tests for the review_pending_changes tool
|
||||
Tests for the review_changes tool
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -7,23 +7,23 @@ from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from tools.review_pending_changes import (
|
||||
ReviewPendingChanges,
|
||||
ReviewPendingChangesRequest,
|
||||
from tools.review_changes import (
|
||||
ReviewChanges,
|
||||
ReviewChangesRequest,
|
||||
)
|
||||
|
||||
|
||||
class TestReviewPendingChangesTool:
|
||||
"""Test the review_pending_changes tool"""
|
||||
class TestReviewChangesTool:
|
||||
"""Test the review_changes tool"""
|
||||
|
||||
@pytest.fixture
|
||||
def tool(self):
|
||||
"""Create tool instance"""
|
||||
return ReviewPendingChanges()
|
||||
return ReviewChanges()
|
||||
|
||||
def test_tool_metadata(self, tool):
|
||||
"""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 "pre-commit review" in tool.get_description()
|
||||
|
||||
@@ -37,7 +37,7 @@ class TestReviewPendingChangesTool:
|
||||
|
||||
def test_request_model_defaults(self):
|
||||
"""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.original_request is None
|
||||
assert request.compare_to is None
|
||||
@@ -77,21 +77,21 @@ class TestReviewPendingChangesTool:
|
||||
assert "./relative/path" in response["content"]
|
||||
|
||||
@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):
|
||||
"""Test when no git repositories are found"""
|
||||
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)
|
||||
|
||||
assert result == "No git repositories found in the specified path."
|
||||
mock_find_repos.assert_called_once_with("/absolute/path/no-git", 5)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("tools.review_pending_changes.find_git_repositories")
|
||||
@patch("tools.review_pending_changes.get_git_status")
|
||||
@patch("tools.review_pending_changes.run_git_command")
|
||||
@patch("tools.review_changes.find_git_repositories")
|
||||
@patch("tools.review_changes.get_git_status")
|
||||
@patch("tools.review_changes.run_git_command")
|
||||
async def test_no_changes_found(
|
||||
self, mock_run_git, mock_status, mock_find_repos, tool
|
||||
):
|
||||
@@ -112,15 +112,15 @@ class TestReviewPendingChangesTool:
|
||||
(True, ""), # unstaged files (empty)
|
||||
]
|
||||
|
||||
request = ReviewPendingChangesRequest(path="/absolute/repo/path")
|
||||
request = ReviewChangesRequest(path="/absolute/repo/path")
|
||||
result = await tool.prepare_prompt(request)
|
||||
|
||||
assert result == "No pending changes found in any of the git repositories."
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("tools.review_pending_changes.find_git_repositories")
|
||||
@patch("tools.review_pending_changes.get_git_status")
|
||||
@patch("tools.review_pending_changes.run_git_command")
|
||||
@patch("tools.review_changes.find_git_repositories")
|
||||
@patch("tools.review_changes.get_git_status")
|
||||
@patch("tools.review_changes.run_git_command")
|
||||
async def test_staged_changes_review(
|
||||
self,
|
||||
mock_run_git,
|
||||
@@ -149,7 +149,7 @@ class TestReviewPendingChangesTool:
|
||||
(True, ""), # unstaged files (empty)
|
||||
]
|
||||
|
||||
request = ReviewPendingChangesRequest(
|
||||
request = ReviewChangesRequest(
|
||||
path="/absolute/repo/path",
|
||||
original_request="Add hello message",
|
||||
review_type="security",
|
||||
@@ -166,9 +166,9 @@ class TestReviewPendingChangesTool:
|
||||
assert "## Git Diffs" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("tools.review_pending_changes.find_git_repositories")
|
||||
@patch("tools.review_pending_changes.get_git_status")
|
||||
@patch("tools.review_pending_changes.run_git_command")
|
||||
@patch("tools.review_changes.find_git_repositories")
|
||||
@patch("tools.review_changes.get_git_status")
|
||||
@patch("tools.review_changes.run_git_command")
|
||||
async def test_compare_to_invalid_ref(
|
||||
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
|
||||
]
|
||||
|
||||
request = ReviewPendingChangesRequest(
|
||||
request = ReviewChangesRequest(
|
||||
path="/absolute/repo/path", compare_to="invalid-branch"
|
||||
)
|
||||
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
|
||||
|
||||
@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):
|
||||
"""Test execute method integration"""
|
||||
# Mock the execute to return a standardized response
|
||||
@@ -212,9 +212,9 @@ class TestReviewPendingChangesTool:
|
||||
assert tool.get_default_temperature() == TEMPERATURE_ANALYTICAL
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch("tools.review_pending_changes.find_git_repositories")
|
||||
@patch("tools.review_pending_changes.get_git_status")
|
||||
@patch("tools.review_pending_changes.run_git_command")
|
||||
@patch("tools.review_changes.find_git_repositories")
|
||||
@patch("tools.review_changes.get_git_status")
|
||||
@patch("tools.review_changes.run_git_command")
|
||||
async def test_mixed_staged_unstaged_changes(
|
||||
self,
|
||||
mock_run_git,
|
||||
@@ -240,7 +240,7 @@ class TestReviewPendingChangesTool:
|
||||
(True, "diff --git a/file2.py..."), # diff for file2.py
|
||||
]
|
||||
|
||||
request = ReviewPendingChangesRequest(
|
||||
request = ReviewChangesRequest(
|
||||
path="/absolute/repo/path",
|
||||
focus_on="error handling",
|
||||
severity_filter="high",
|
||||
@@ -25,7 +25,7 @@ class TestServerTools:
|
||||
assert "debug_issue" in tool_names
|
||||
assert "analyze" 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 "get_version" in tool_names
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from .analyze import AnalyzeTool
|
||||
from .chat import ChatTool
|
||||
from .debug_issue import DebugIssueTool
|
||||
from .review_code import ReviewCodeTool
|
||||
from .review_pending_changes import ReviewPendingChanges
|
||||
from .review_changes import ReviewChanges
|
||||
from .think_deeper import ThinkDeeperTool
|
||||
|
||||
__all__ = [
|
||||
@@ -15,5 +15,5 @@ __all__ = [
|
||||
"DebugIssueTool",
|
||||
"AnalyzeTool",
|
||||
"ChatTool",
|
||||
"ReviewPendingChanges",
|
||||
"ReviewChanges",
|
||||
]
|
||||
|
||||
@@ -86,7 +86,7 @@ class BaseTool(ABC):
|
||||
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 not os.path.isabs(request.path):
|
||||
return (
|
||||
|
||||
@@ -9,15 +9,15 @@ from typing import Any, Dict, Literal, Optional
|
||||
from pydantic import Field
|
||||
|
||||
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.token_utils import estimate_tokens
|
||||
|
||||
from .base import BaseTool, ToolRequest
|
||||
|
||||
|
||||
class ReviewPendingChangesRequest(ToolRequest):
|
||||
"""Request model for review_pending_changes tool"""
|
||||
class ReviewChangesRequest(ToolRequest):
|
||||
"""Request model for review_changes tool"""
|
||||
|
||||
path: str = Field(
|
||||
...,
|
||||
@@ -65,11 +65,11 @@ class ReviewPendingChangesRequest(ToolRequest):
|
||||
)
|
||||
|
||||
|
||||
class ReviewPendingChanges(BaseTool):
|
||||
"""Tool for reviewing pending git changes across multiple repositories."""
|
||||
class ReviewChanges(BaseTool):
|
||||
"""Tool for reviewing git changes across multiple repositories."""
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "review_pending_changes"
|
||||
return "review_changes"
|
||||
|
||||
def get_description(self) -> str:
|
||||
return (
|
||||
@@ -86,10 +86,10 @@ class ReviewPendingChanges(BaseTool):
|
||||
return self.get_request_model().model_json_schema()
|
||||
|
||||
def get_system_prompt(self) -> str:
|
||||
return REVIEW_PENDING_CHANGES_PROMPT
|
||||
return REVIEW_CHANGES_PROMPT
|
||||
|
||||
def get_request_model(self):
|
||||
return ReviewPendingChangesRequest
|
||||
return ReviewChangesRequest
|
||||
|
||||
def get_default_temperature(self) -> float:
|
||||
"""Use analytical temperature for code review."""
|
||||
@@ -106,7 +106,7 @@ class ReviewPendingChanges(BaseTool):
|
||||
# Limit length to avoid filesystem issues
|
||||
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."""
|
||||
# Find all git repositories
|
||||
repositories = find_git_repositories(request.path, request.max_depth)
|
||||
Reference in New Issue
Block a user