AST-Delegated Local Refactoring (Scripts over Token Rewrites)
Overview
When tasked with a repository-wide codebase migration ("Rename method getUserById to fetchUserRecord across all 45 microservice files"), naive AI agents attempt to open, edit, and rewrite all 45 files sequentially through LLM tool calls.
This naive approach consumes 100,000+ tokens, takes 5 minutes of streaming latency, and frequently introduces subtle hallucinated syntax regressions.
The AST-Delegated Refactoring Protocol directs the agent to write a single 20-line local AST transformer script (using Python libcst, TypeScript ts-morph, or jscodeshift) and execute it locally via run_command. The entire codebase is refactored deterministically in 200 milliseconds using only 350 tokens.
LLM Multi-File Rewriting vs. AST Local Delegation
┌─────────────────────────────────────────────────────────────┐
│ Refactoring Architecture Comparison │
│ │
│ Naive LLM File-by-File Rewrite (45 Files): │
│ • 45 sequential `replace_file_content` tool calls │
│ • ~120,000 tokens billed │
│ • 4-6 minutes execution latency │
│ • High risk of missed edge cases or broken imports │
│ │
│ AST-Delegated Local Execution: │
│ 1. Agent writes 1 local Python / TS AST script (350 tokens)│
│ 2. Agent runs: `python scratch/refactor.py` (Local CPU) │
│ ↳ 45 files mutated in 180ms with 100% syntactic precision │
│ ↳ 99.7% Token Reduction ($1.80 $\rightarrow$ $0.005) │
└─────────────────────────────────────────────────────────────┘
Production AST Transformer Recipes
1. Python libcst Argument Renaming Script
Use when renaming function arguments or decorators across multiple Python modules while preserving exact comments and formatting:
# scratch/refactor_args.py
import libcst as cst
from pathlib import Path
class RenameParamTransformer(cst.CSTTransformer):
def leave_Param(self, original_node: cst.Param, updated_node: cst.Param) -> cst.Param:
if original_node.name.value == "old_user_id":
return updated_node.with_changes(name=cst.Name("user_uuid"))
return updated_node
def refactor_repository(root_dir: str):
transformer = RenameParamTransformer()
for py_file in Path(root_dir).rglob("*.py"):
if "venv" in str(py_file) or ".git" in str(py_file):
continue
source_code = py_file.read_text(encoding="utf-8")
cst_tree = cst.parse_module(source_code)
modified_tree = cst_tree.visit(transformer)
if modified_tree.code != source_code:
py_file.write_text(modified_tree.code, encoding="utf-8")
print(f"Refactored: {py_file}")
if __name__ == "__main__":
refactor_repository("src/")
2. TypeScript ts-morph Import & Method Renamer
Use for mass refactoring across React / Next.js / Node.js TypeScript projects:
// scratch/refactor_imports.ts
import { Project } from "ts-morph";
const project = new Project({
tsConfigFilePath: "tsconfig.json",
});
const sourceFiles = project.getSourceFiles("src/**/*.ts*");
for (const file of sourceFiles) {
// 1. Rename import paths
const declarations = file.getImportDeclarations();
for (const decl of declarations) {
if (decl.getModuleSpecifierValue() === "@/legacy/api") {
decl.setModuleSpecifier("@/services/api/v2");
}
}
// 2. Rename specific function call identifier
file.forEachDescendant((node) => {
if (node.getText() === "fetchLegacyData") {
// @ts-ignore
node.replaceWithText("fetchModernData");
}
});
}
project.saveSync();
console.log("TypeScript AST Migration Complete.");
Decision Matrix: When to Delegate to AST
┌───────────────────────────────────────────────────────────────────────────┐
│ 🟢 USE LOCAL AST SCRIPT IF: │
│ • Refactoring spans $> 3$ files │
│ • Renaming variables, imports, methods, or decorator signatures │
│ • Adding a standard header / license / strict mode to all files │
│ │
│ 🟡 USE LLM TOOL CALL (`replace_file_content`) IF: │
│ • Complex semantic rewrite of 1 isolated function in 1 file │
│ • Logic requires natural language comprehension and algorithmic redesign │
└───────────────────────────────────────────────────────────────────────────┘
Benchmark Comparison
| Metric | LLM Multi-File Edits (50 Files) | AST Script Execution | Improvement |
|---|---|---|---|
| Total Tokens Billed | ~145,000 tokens | 380 tokens | 99.7% Reduction |
| Execution Time | 320 seconds | 0.25 seconds | 1,280x Faster |
| Syntactic Regressions | 3 syntax bugs (hallucinated commas) | 0 (Guaranteed by AST parser) | 100% Deterministic |
Agent Operational Directive
MANDATORY: Whenever a refactoring task affects more than 3 files with a repetitive structural pattern, autonomous agents MUST write a one-off AST script to
scratch/and execute it locally via CLI rather than editing files individually through LLM calls.