name: ast-grep-refactor description: "Use when you need to perform code refactoring, semantic transformations, or structural rewrites across multiple files. Ideal for API migrations, code modernization, breaking change adoption, or systematic code transformations."
ast-grep Refactor Skill
Overview
This skill enables systematic code refactoring and semantic transformations using ast-grep. Unlike simple search-and-replace, ast-grep performs structural transformations that respect code semantics, making it safe for complex refactoring tasks across large codebases.
When to Use
Invoke this skill when you need to:
- Migrate APIs - Update code to use new library versions or changed APIs
- Modernize code - Transform old patterns to modern equivalents (e.g., var → const/let)
- Enforce breaking changes - Help library users adopt API changes
- Bulk refactor patterns - Replace specific coding patterns systematically
- Apply code transformations - Implement architectural changes across files
- Create reusable transformation rules - Build lint rules or migration scripts
Core Transformation Concepts
Rewrite Syntax
Use --rewrite or -r to specify the replacement pattern:
sg -p 'var $X = $Y' -r 'const $X = $Y' -l js ./src
Preserving Structure
Metavariables in patterns are captured and can be referenced in rewrites:
# Pattern captures $A and $B
sg -p '$A && $A()' -r '$A?.()' -l ts ./src
Comby Operators in Rewrites
$CAPTURE- References a captured metavariable$$$or$$- Matches zero or more nodes (ellipsis)$CAPTURE:regex(...)- Applies regex transformation to captured text
Essential Refactoring Commands
Basic Rewrite
# Replace console.log with logger.debug
sg -p 'console.log($$$ARGS)' -r 'logger.debug($$$ARGS)' -l ts ./src
# Transform to optional chaining
sg -p '$A && $A()' -r '$A?.()' -l ts ./src
# Modernize variable declarations
sg -p 'var $X = $Y' -r 'const $X = $Y' -l js ./src
Interactive Mode (Recommended)
# Review each change before applying
sg -p '$A && $A()' -r '$A?.()' -l ts -i ./src
# Controls:
# y = accept this change
# n = skip this change
# e = open in editor
# q = quit
Batch Transformation
# Apply all changes without confirmation (use with caution!)
sg -p 'console.log($$$ARGS)' -r 'logger.debug($$$ARGS)' -l ts -U ./src
# Check what would change (dry run)
sg -p 'console.log($$$ARGS)' -r 'logger.debug($$$ARGS)' -l ts ./src
Refactoring Workflows
Workflow 1: API Migration
When a library changes its API, migrate all usages systematically:
Analyze current usage (use ast-grep-explorer first)
# Find all old API calls sg -p 'oldLibrary.$METHOD($$$ARGS)' -l ts ./srcCreate transformation pattern
# Transform to new API sg -p 'oldLibrary.fetch($URL, $CONFIG)' -r 'newClient.get($URL, $CONFIG)' -l ts -i ./srcHandle complex transformations with rule files
# sg-rules/migrate-api.yml id: migrate-api language: ts rule: pattern: oldLibrary.$METHOD($URL, $CONFIG) fix: newClient.$METHOD($URL, $CONFIG)sg scan -r sg-rules/migrate-api.yml -i ./src
Workflow 2: Code Modernization
Transform legacy patterns to modern equivalents:
Transform to optional chaining
# Multiple patterns to modernize sg -p '$A && $A.$B' -r '$A?.$B' -l ts -i ./src sg -p '$A && $A()' -r '$A?.()' -l ts -i ./src sg -p '$A && $A[$B]' -r '$A?.[$B]' -l ts -i ./srcTransform to nullish coalescing
sg -p '$A !== null && $A !== undefined ? $A : $B' -r '$A ?? $B' -l ts -i ./srcModernize React patterns
# Transform class components to function components (complex, use rule files) # Extract commonly used patterns sg -p 'componentDidMount() { $$$BODY }' -r 'useEffect(() => { $$$BODY }, [])' -l ts -i ./src
Workflow 3: Breaking Change Adoption
Help library users adopt breaking changes:
Rename renamed exports
# Old import → New import sg -p 'import { $OLD as $NEW } from "library"' -r 'import { $NEW } from "library"' -l ts -i ./srcTransform function signatures
# Old signature: function(oldArg1, oldArg2) # New signature: function({newArg1, newArg2}) sg -p 'oldFunction($A, $B)' -r 'newFunction({ arg1: $A, arg2: $B })' -l ts -i ./srcTransform method chains
# Old fluent API → New fluent API sg -p 'builder.setA($A).setB($B)' -r 'builder.withConfig({ a: $A, b: $B })' -l ts -i ./src
Workflow 4: Reusable Rule-Based Refactoring
Create reusable YAML rules for complex transformations:
Create rule file structure
# Initialize ast-grep project sg new --config sgconfig.yml mkdir -p sg-rulesWrite transformation rule
# sg-rules/transform-error-handling.yml id: transform-error-handling language: ts message: "Transform old error handling to new pattern" severity: warning rule: pattern: | try { $$$TRY } catch ($ERR) { console.error($ERR); $$$CATCH } fix: | try { $$$TRY } catch ($ERR) { logger.error($ERR); $$$CATCH }Test the rule
# Dry run sg scan -r sg-rules/transform-error-handling.yml ./src # Interactive application sg scan -r sg-rules/transform-error-handling.yml -i ./src # Apply all sg scan -r sg-rules/transform-error-handling.yml -U ./srcCreate multiple related rules
# sgconfig.yml ruleDirs: - sg-rules testConfigs: - testDir: sg-tests
Advanced Transformation Techniques
Conditional Transformations
Use YAML rules with constraints for conditional logic:
# sg-rules/conditional-transform.yml
id: conditional-transform
language: ts
rule:
pattern: $OBJ.$METHOD($$$ARGS)
constraints:
# Only transform if object name starts with 'legacy'
OBJ:
regex: ^legacy.*
fix: newObj.$METHOD($$$ARGS)
Multi-Step Transformations
For complex transformations that require multiple steps:
# Step 1: Extract transformation data
sg -p 'oldPattern($A, $B)' --json -l ts ./src > /tmp/matches.json
# Step 2: Generate fix from data
jq -r '.[] | "sed -i 's/oldPattern\(.text\)/newPattern(\\1)/g"' /tmp/matches.json
# Step 3: Apply step-by-step with interactive review
sg -p 'oldPattern($A, $B)' -r 'intermediate($A)' -l ts -i ./src
sg -p 'intermediate($A)' -r 'newPattern($A, defaultValue)' -l ts -i ./src
Using Contextual Transformations
Transform based on surrounding context:
# Transform based on parent node
id: contextual-transform
language: ts
rule:
pattern: console.log($$$ARGS)
inside:
kind: function_declaration
stopBy: end
fix: logger.debug($$$ARGS)
Refactoring Examples
Example 1: Import Path Migration
# Migrate from relative imports to absolute imports
# Before: import { X } from "../../utils/helper"
# After: import { X } from "@/utils/helper"
sg -p 'import { $$$ITEMS } from "$PATH"' -r 'import { $$$ITEMS } from "@/$PATH"' -l ts -i ./src
Example 2: Type Transformation
# Transform interface to type alias (or vice versa)
sg -p 'interface $NAME { $$$BODY }' -r 'type $NAME = { $$$BODY }' -l ts -i ./src
# Add readonly to properties
sg -p '$PROP: $TYPE' -r 'readonly $PROP: $TYPE' -l ts -i ./src
# Transform to use utility types
sg -p '{ $PROP: $TYPE | null }' -r '{ $PROP?: $TYPE }' -l ts -i ./src
Example 3: Function Transformation
# Transform function declarations to arrow functions
sg -p 'function $NAME($$$PARAMS) { $$$BODY }' -r 'const $NAME = ($$$PARAMS) => { $$$BODY }' -l ts -i ./src
# Add error handling wrapper
sg -p 'async function $NAME($$$PARAMS) { $$$BODY }' -r 'async function $NAME($$$PARAMS) { try { $$$BODY } catch (err) { handleError(err); throw; } }' -l ts -i ./src
Example 4: Framework Migration
# sg-rules/vue-to-react.yml
id: vue-to-react
language: ts
message: "Migrate Vue component to React"
severity: hint
rule:
pattern: |
export default {
data() {
return $$$DATA
},
methods: {
$$$METHODS
}
}
fix: |
const Component = () => {
const [$$$DATA] = useState($$$DATA)
$$$METHODS
return (
<div></div>
)
}
export default Component
Safety and Validation
Pre-Refactoring Checklist
Before applying transformations:
- Run tests - Ensure existing tests pass
- Create backup - Use version control (commit before refactoring)
- Review matches - Dry run to see all matches first
- Start small - Test on a single file or directory first
- Use interactive mode - Review each change manually
Validation Commands
# Count matches before transformation
sg -p 'pattern' -l ts --json ./src | jq 'length'
# See context around matches
sg -p 'pattern' -l ts -A 3 -B 3 ./src
# Verify no syntax errors after transformation
# (Run language-specific linter/TypeScript check)
npx tsc --noEmit
python -m py_compile file.py
Rollback Strategy
# Use git to see what changed
git diff
# Revert if needed
git checkout -- ./src
# Or revert specific files
git checkout -- ./src/specific-file.ts
Integration with CI/CD
GitHub Action for Automated Refactoring
# .github/workflows/refactor.yml
name: Code Refactoring
on:
schedule:
- cron: '0 0 * * 0' # Weekly
workflow_dispatch:
jobs:
refactor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install ast-grep
run: npm install -g @ast-grep/cli
- name: Run refactoring rules
run: sg scan -U
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
commit-message: "Apply automated refactoring"
title: "Automated code refactoring"
branch: automated-refactoring
Common Refactoring Patterns
Variable and Declaration Transformations
# var to const/let
sg -p 'var $X = $Y' -r 'const $X = $Y' -l js -i ./src
# String concatenation to template literals
sg -p '"$A" + $B + "$C"' -r '`${$A}${$B}${$C}`' -l js -i ./src
# Object property shorthand
sg -p '{ $PROP: $PROP }' -r '{ $PROP }' -l js -i ./src
Function Transformations
# Anonymous to arrow functions
sg -p 'function($$$PARAMS) { $$$BODY }' -r '($$$PARAMS) => { $$$BODY }' -l js -i ./src
# Callback to async/await
sg -p '.then($F).catch($E)' -r 'try { await $F } catch ($E) {}' -l js -i ./src
Import/Export Transformations
# CommonJS to ES modules
sg -p 'const $X = require("$M")' -r 'import * as $X from "$M"' -l js -i ./src
sg -p 'module.exports = $X' -r 'export default $X' -l js -i ./src
# Named export restructuring
sg -p 'export { $$$ITEMS }' -r 'export { $$$ITEMS }' -l ts -i ./src
Tips for Effective Refactoring
- Always use interactive mode first - Review changes individually
- Test on a subset - Run on one file or directory before the full codebase
- Write tests first - Ensure your transformations don't break functionality
- Use version control - Commit before large refactorings
- Document patterns - Save complex rules for reuse
- Combine with linting - Run linters after transformation
- Use rule files for complex logic - YAML rules support more sophisticated logic
Common Pitfalls
Over-matching: Patterns that match unintended code
- Solution: Use more specific patterns or constraints
Context loss: Transformations that lose important context
- Solution: Preserve surrounding code with $$$ captures
Syntax errors: Rewrites that produce invalid syntax
- Solution: Test on a single file first, verify with language tooling
Unintended global changes: -U flag applying too broadly
- Solution: Always use -i (interactive) first, then -U only when confident
Integration with Other Skills
This skill works synergistically with:
- ast-grep-explorer: Explore patterns first, then refactor
- test agent: Generate tests to verify transformations
- linter: Run linters after refactoring to catch issues
Remember: Refactoring is transformative but risky. Always backup, test, and verify.