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 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:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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)
|
||||||
Reference in New Issue
Block a user