Performance improvements when embedding files:
- Exit early at MCP boundary if files won't fit within given context of chosen model - Encourage claude to re-run with better context - Check file sizes before embedding - Drop files from older conversations when building continuations and give priority to newer files - List and mention excluded files to Claude on return - Improved tests - Improved precommit prompt - Added a new Low severity to precommit - Improved documentation of file embedding strategy - Refactor
This commit is contained in:
@@ -135,6 +135,14 @@ class AnalyzeTool(BaseTool):
|
||||
if updated_files is not None:
|
||||
request.files = updated_files
|
||||
|
||||
# MCP boundary check - STRICT REJECTION
|
||||
if request.files:
|
||||
file_size_check = self.check_total_file_size(request.files)
|
||||
if file_size_check:
|
||||
from tools.models import ToolOutput
|
||||
|
||||
raise ValueError(f"MCP_SIZE_CHECK:{ToolOutput(**file_size_check).model_dump_json()}")
|
||||
|
||||
# Use centralized file processing logic
|
||||
continuation_id = getattr(request, "continuation_id", None)
|
||||
file_content, processed_files = self._prepare_file_content_for_prompt(request.files, continuation_id, "Files")
|
||||
|
||||
@@ -936,6 +936,49 @@ When recommending searches, be specific about what information you need and why
|
||||
}
|
||||
return None
|
||||
|
||||
def estimate_tokens_smart(self, file_path: str) -> int:
|
||||
"""
|
||||
Estimate tokens for a file using file-type aware ratios.
|
||||
|
||||
Args:
|
||||
file_path: Path to the file
|
||||
|
||||
Returns:
|
||||
int: Estimated token count
|
||||
"""
|
||||
from utils.file_utils import estimate_file_tokens
|
||||
|
||||
return estimate_file_tokens(file_path)
|
||||
|
||||
def check_total_file_size(self, files: list[str]) -> Optional[dict[str, Any]]:
|
||||
"""
|
||||
Check if total file sizes would exceed token threshold before embedding.
|
||||
|
||||
IMPORTANT: This performs STRICT REJECTION at MCP boundary.
|
||||
No partial inclusion - either all files fit or request is rejected.
|
||||
This forces Claude to make better file selection decisions.
|
||||
|
||||
Args:
|
||||
files: List of file paths to check
|
||||
|
||||
Returns:
|
||||
Dict with MCP_CODE_TOO_LARGE response if too large, None if acceptable
|
||||
"""
|
||||
if not files:
|
||||
return None
|
||||
|
||||
# Get current model name for context-aware thresholds
|
||||
model_name = getattr(self, "_current_model_name", None)
|
||||
if not model_name:
|
||||
from config import DEFAULT_MODEL
|
||||
|
||||
model_name = DEFAULT_MODEL
|
||||
|
||||
# Use centralized file size checking with model context
|
||||
from utils.file_utils import check_total_file_size as check_file_size_utility
|
||||
|
||||
return check_file_size_utility(files, model_name)
|
||||
|
||||
def handle_prompt_file(self, files: Optional[list[str]]) -> tuple[Optional[str], Optional[list[str]]]:
|
||||
"""
|
||||
Check for and handle prompt.txt in the files list.
|
||||
|
||||
@@ -178,6 +178,14 @@ class CodeReviewTool(BaseTool):
|
||||
if updated_files is not None:
|
||||
request.files = updated_files
|
||||
|
||||
# MCP boundary check - STRICT REJECTION
|
||||
if request.files:
|
||||
file_size_check = self.check_total_file_size(request.files)
|
||||
if file_size_check:
|
||||
from tools.models import ToolOutput
|
||||
|
||||
raise ValueError(f"MCP_SIZE_CHECK:{ToolOutput(**file_size_check).model_dump_json()}")
|
||||
|
||||
# Check user input size at MCP transport boundary (before adding internal content)
|
||||
user_content = request.prompt
|
||||
size_check = self.check_prompt_size(user_content)
|
||||
|
||||
@@ -150,6 +150,14 @@ class DebugIssueTool(BaseTool):
|
||||
if updated_files is not None:
|
||||
request.files = updated_files
|
||||
|
||||
# MCP boundary check - STRICT REJECTION
|
||||
if request.files:
|
||||
file_size_check = self.check_total_file_size(request.files)
|
||||
if file_size_check:
|
||||
from tools.models import ToolOutput
|
||||
|
||||
raise ValueError(f"MCP_SIZE_CHECK:{ToolOutput(**file_size_check).model_dump_json()}")
|
||||
|
||||
# Build context sections
|
||||
context_parts = [f"=== ISSUE DESCRIPTION ===\n{request.prompt}\n=== END DESCRIPTION ==="]
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ class ToolOutput(BaseModel):
|
||||
"refactor_analysis_complete",
|
||||
"trace_complete",
|
||||
"resend_prompt",
|
||||
"code_too_large",
|
||||
"continuation_available",
|
||||
] = "success"
|
||||
content: Optional[str] = Field(None, description="The main content/response from the tool")
|
||||
@@ -142,6 +143,15 @@ class RefactorAnalysisComplete(BaseModel):
|
||||
next_actions_for_claude: list[RefactorAction] = Field(..., description="Specific actions for Claude to implement")
|
||||
|
||||
|
||||
class CodeTooLargeRequest(BaseModel):
|
||||
"""Request to reduce file selection due to size constraints"""
|
||||
|
||||
status: Literal["code_too_large"] = "code_too_large"
|
||||
content: str = Field(..., description="Message explaining the size constraint")
|
||||
content_type: Literal["text"] = "text"
|
||||
metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class ResendPromptRequest(BaseModel):
|
||||
"""Request to resend prompt via file due to size limits"""
|
||||
|
||||
@@ -284,6 +294,7 @@ SPECIAL_STATUS_MODELS = {
|
||||
"refactor_analysis_complete": RefactorAnalysisComplete,
|
||||
"trace_complete": TraceComplete,
|
||||
"resend_prompt": ResendPromptRequest,
|
||||
"code_too_large": CodeTooLargeRequest,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ class PrecommitRequest(ToolRequest):
|
||||
)
|
||||
prompt: Optional[str] = Field(
|
||||
None,
|
||||
description="The original user request description for the changes. Provides critical context for the review. If original request is limited or not available, Claude MUST study the changes carefully, think deeply about the implementation intent, analyze patterns across all modifications, infer the logic and requirements from the code changes and provide a thorough starting point.",
|
||||
description="The original user request description for the changes. Provides critical context for the review. If original request is limited or not available, you MUST study the changes carefully, think deeply about the implementation intent, analyze patterns across all modifications, infer the logic and requirements from the code changes and provide a thorough starting point.",
|
||||
)
|
||||
compare_to: Optional[str] = Field(
|
||||
None,
|
||||
@@ -57,7 +57,7 @@ class PrecommitRequest(ToolRequest):
|
||||
review_type: Literal["full", "security", "performance", "quick"] = Field(
|
||||
"full", description="Type of review to perform on the changes."
|
||||
)
|
||||
severity_filter: Literal["critical", "high", "medium", "all"] = Field(
|
||||
severity_filter: Literal["critical", "high", "medium", "low", "all"] = Field(
|
||||
"all",
|
||||
description="Minimum severity level to report on the changes.",
|
||||
)
|
||||
@@ -117,7 +117,7 @@ class Precommit(BaseTool):
|
||||
"model": self.get_model_field_schema(),
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"description": "The original user request description for the changes. Provides critical context for the review. If original request is limited or not available, Claude MUST study the changes carefully, think deeply about the implementation intent, analyze patterns across all modifications, infer the logic and requirements from the code changes and provide a thorough starting point.",
|
||||
"description": "The original user request description for the changes. Provides critical context for the review. If original request is limited or not available, you MUST study the changes carefully, think deeply about the implementation intent, analyze patterns across all modifications, infer the logic and requirements from the code changes and provide a thorough starting point.",
|
||||
},
|
||||
"compare_to": {
|
||||
"type": "string",
|
||||
@@ -145,7 +145,7 @@ class Precommit(BaseTool):
|
||||
},
|
||||
"severity_filter": {
|
||||
"type": "string",
|
||||
"enum": ["critical", "high", "medium", "all"],
|
||||
"enum": ["critical", "high", "medium", "low", "all"],
|
||||
"default": "all",
|
||||
"description": "Minimum severity level to report on the changes.",
|
||||
},
|
||||
@@ -227,6 +227,14 @@ class Precommit(BaseTool):
|
||||
translated_path = translate_path_for_environment(request.path)
|
||||
translated_files = translate_file_paths(request.files)
|
||||
|
||||
# MCP boundary check - STRICT REJECTION (check original files before translation)
|
||||
if request.files:
|
||||
file_size_check = self.check_total_file_size(request.files)
|
||||
if file_size_check:
|
||||
from tools.models import ToolOutput
|
||||
|
||||
raise ValueError(f"MCP_SIZE_CHECK:{ToolOutput(**file_size_check).model_dump_json()}")
|
||||
|
||||
# Check if the path translation resulted in an error path
|
||||
if translated_path.startswith("/inaccessible/"):
|
||||
raise ValueError(
|
||||
@@ -540,4 +548,20 @@ class Precommit(BaseTool):
|
||||
|
||||
def format_response(self, response: str, request: PrecommitRequest, model_info: Optional[dict] = None) -> str:
|
||||
"""Format the response with commit guidance"""
|
||||
return f"{response}\n\n---\n\n**Commit Status:** If no critical issues found, changes are ready for commit. Otherwise, address issues first and re-run review. Check with user before proceeding with any commit."
|
||||
# Base response
|
||||
formatted_response = response
|
||||
|
||||
# Add footer separator
|
||||
formatted_response += "\n\n---\n\n"
|
||||
|
||||
# Add commit status instruction
|
||||
formatted_response += (
|
||||
"COMMIT STATUS: You MUST provide a clear summary of ALL issues found to the user. "
|
||||
"If no critical or high severity issues found, changes are ready for commit. "
|
||||
"If critical issues are found, you MUST fix them first and then run the precommit tool again "
|
||||
"to validate the fixes before proceeding. "
|
||||
"Medium to low severity issues should be addressed but may not block commit. "
|
||||
"You MUST always CONFIRM with user and show them a CLEAR summary of ALL issues before proceeding with any commit."
|
||||
)
|
||||
|
||||
return formatted_response
|
||||
|
||||
@@ -143,6 +143,14 @@ class ThinkDeepTool(BaseTool):
|
||||
if updated_files is not None:
|
||||
request.files = updated_files
|
||||
|
||||
# MCP boundary check - STRICT REJECTION
|
||||
if request.files:
|
||||
file_size_check = self.check_total_file_size(request.files)
|
||||
if file_size_check:
|
||||
from tools.models import ToolOutput
|
||||
|
||||
raise ValueError(f"MCP_SIZE_CHECK:{ToolOutput(**file_size_check).model_dump_json()}")
|
||||
|
||||
# Build context parts
|
||||
context_parts = [f"=== CLAUDE'S CURRENT ANALYSIS ===\n{current_analysis}\n=== END ANALYSIS ==="]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user