How the GitNexus Rename Tool Performs Multi-File Refactoring Using Graph and Text Search
The GitNexus rename tool executes multi-file refactoring by first querying the knowledge graph for high-confidence symbol relationships, then running ripgrep to discover additional references via regex text search, tagging each edit with graph or text_search confidence levels before optionally applying changes in a preview-safe workflow.
The GitNexus rename tool provides a safe, coordinated approach to refactoring symbols across entire repositories by combining semantic graph analysis with brute-force text search. Unlike simple find-and-replace operations, this tool leverages the knowledge graph built during indexing to identify precise relationships while using ripgrep to catch edge cases like string literals or dynamic imports. Understanding how GitNexus handles multi-file refactoring using graph and text search helps developers confidently rename functions, classes, and variables without breaking downstream dependencies.
Tool Declaration and Schema Definition
The tool's interface and user guidance are defined in src/mcp/tools.ts, which specifies the JSON schema and behavioral contract. The schema requires new_name as the only mandatory parameter while allowing either symbol_name (string lookup) or symbol_uid (direct UID from prior tool results) for target identification.
Key parameters include:
dry_run: Boolean defaulting totrue, enabling safe preview modefile_path: Optional disambiguator for common symbol namesrepo: Optional repository handle for multi-repo environments
The description explicitly tags each confidence level: graph for knowledge graph matches and text_search for regex-discovered references, advising users to review text_search results carefully.
Two-Phase Discovery Architecture
The implementation in src/mcp/local/local-backend.ts (lines 1151-1314) operates through complementary discovery phases to maximize reference coverage while maintaining precision.
Phase 1: Graph-Based Reference Discovery (High Confidence)
The tool first invokes the context lookup method to resolve the target symbol and fetch its incoming graph edges (calls, imports, extends, implements). This phase processes:
- The symbol's definition location
- All documented relationships from the knowledge graph
Edits collected during this phase receive the graph confidence tag. The implementation stores these in a Map<string, { file_path, edits[] }> keyed by file path, adding only the first occurrence per file to avoid duplicates. This logic spans lines 1121-1247 in local-backend.ts.
// Definition edit (high-confidence)
if (sym.filePath && sym.startLine) { … addEdit(..., 'graph'); }
// Incoming references from graph (callers, imports, extends, implements)
for (const ref of allIncoming) { … addEdit(..., 'graph'); }
Phase 2: Text Search for Missed References (Lower Confidence)
To catch dynamic imports, string literals, or undocumented call sites, the tool executes a repository-wide ripgrep search. The implementation in lines 1250-1284 constructs the following command:
const rgArgs = [
'-l',
'--type-add', 'code:*.{ts,tsx,js,jsx,py,go,rs,java,c,h,cpp,cc,cxx,hpp,hxx,hh,cs,php,swift}',
'-t', 'code',
`\\b${oldName}\\b`,
'.',
];
const output = execFileSync('rg', rgArgs, {
cwd: repo.repoPath,
encoding: 'utf-8',
timeout: 5000
});
The tool skips files already modified via graph edits (if (graphFiles.has(normalizedFile)) continue;), then performs line-by-line regex scans on remaining files. Matches receive the text_search confidence tag, indicating lower certainty requiring manual review.
Safety Mechanisms and Preview Mode
Before processing, assertSafePath (lines 71-78) validates that all file paths remain within the repository root, preventing path-traversal attacks. The rename logic also validates that new_name differs from the old name (lines 97-99), returning an error if they match.
The preview versus apply logic (lines 1304-1314) respects the dry_run parameter:
- When
dry_run: true(default): Returns a structured report without writing changes - When
dry_run: false: Reads each file, applies global regex replacement (new RegExp(\\b${oldName}\\b, 'g')), and writes back to disk
The return object includes:
files_affected: Count of distinct files touchedgraph_editsvstext_search_edits: Breakdown by confidencechanges: Array of edit details with line numbers and confidence tagsapplied: Boolean indicating whether files were modified
Error Handling and Edge Cases
The implementation handles several failure modes explicitly:
| Situation | Response |
|---|---|
Neither symbol_name nor symbol_uid provided |
Returns error: "Either symbol_name or symbol_uid is required" (lines 67-69) |
| Symbol not found or ambiguous | Propagates the lookup result for caller resolution |
| Identical old and new names | Returns error: "New name is the same as the current name" (lines 97-99) |
| Path traversal attempt | Throws "Path traversal blocked" via assertSafePath |
Test Coverage Validation
The tool's contract is verified through unit tests in two key files:
test/unit/tools.test.ts(lines 64-67): Validates thatnew_nameis a required schema parametertest/unit/calltool-dispatch.test.ts(lines 31-48): Mocks symbol lookup, executes dry-run rename, and verifies error handling for missing identifiers
These tests ensure the implementation respects the schema defined in src/mcp/tools.ts and maintains the expected dispatch behavior.
Practical Usage Examples
Dry-Run Preview (Recommended First Step)
await backend.callTool('rename', {
symbol_name: 'fetchUser',
new_name: 'retrieveUser',
dry_run: true, // Safe preview mode
});
Expected response structure:
{
"status": "success",
"old_name": "fetchUser",
"new_name": "retrieveUser",
"files_affected": 3,
"total_edits": 5,
"graph_edits": 2,
"text_search_edits": 3,
"changes": [
{
"file_path": "src/services/user.ts",
"edits": [
{
"line": 12,
"old_text": "function fetchUser(id) {",
"new_text": "function retrieveUser(id) {",
"confidence": "graph"
}
]
}
],
"applied": false
}
Applying the Refactor
After reviewing the preview, apply changes by setting dry_run: false:
await backend.callTool('rename', {
symbol_uid: 'func:fetchUser',
new_name: 'retrieveUser',
dry_run: false,
});
Post-execution, verify no unintended side effects occurred:
await backend.callTool('detect_changes', { repo: 'my-repo' });
Summary
- Graph-based discovery provides high-confidence edits by analyzing knowledge graph relationships (calls, imports, inheritance) defined during indexing.
- Text search supplementation uses ripgrep with word-boundary regex to catch dynamic or undocumented references, tagged with lower confidence for manual review.
- Safety-first design requires explicit opt-in (
dry_run: false) to modify files and includes path-traversal protection viaassertSafePath. - Comprehensive metadata returns detailed edit reports distinguishing between
graphandtext_searchconfidence levels, enabling informed validation before application. - Source locations include
src/mcp/tools.tsfor schema definition andsrc/mcp/local/local-backend.ts(lines 1151-1314) for the core implementation.
Frequently Asked Questions
What distinguishes graph confidence from text_search confidence in GitNexus?
Graph confidence indicates the reference was discovered through the knowledge graph's semantic relationships (function calls, imports, class inheritance), offering high certainty that the symbol represents the actual code entity being renamed. Text_search confidence indicates the reference was found via regex pattern matching against the raw source code, which may include string literals, comments, or dynamic references that require human verification before acceptance.
How does the rename tool prevent security vulnerabilities?
The tool implements assertSafePath validation (lines 71-78 of local-backend.ts) to ensure all file paths remain within the repository root directory, blocking path-traversal attacks. Additionally, the default dry_run: true setting prevents accidental file modifications until the user explicitly reviews and approves the proposed changes.
Can I preview changes before modifying files?
Yes, the dry_run parameter defaults to true, causing the tool to return a complete report of proposed edits—including file paths, line numbers, old and new text, and confidence tags—without writing any changes to disk. Set dry_run: false only after validating the preview output matches your intended refactoring scope.
What should I do after executing a rename operation?
Run the detect_changes tool immediately after applying a rename (when dry_run: false) to verify no unexpected side effects or broken references occurred. This follow-up step validates the integrity of the codebase after the multi-file refactoring is complete.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →