Graph-sitter enables you to create reusable code transformations using Python functions decorated with @graph_sitter.function. These codemods can be shared, versioned, and run by your team.
Creating Codemods
The easiest way to create a new codemod is using the CLI create command:
gs create rename-function .
This creates a new codemod in your .codegen/codemods directory:
import graph_sitter
from graph_sitter import Codebase
@graph_sitter.function("rename-function")
def run(codebase: Codebase):
"""Add a description of what this codemod does."""
# Add your code here
pass
Codemods are stored in .codegen/codemods/name/name.py and are tracked in Git for easy sharing.
AI-Powered Generation with -d
You can use AI to generate an initial implementation by providing a description:
gs create rename-function . -d "Rename the getUserData function to fetchUserProfile"
This will:
- Generate an implementation based on your description
- Create a custom system prompt that you can provide to an IDE chat assistant (learn more about working with AI)
- Place both files in the codemod directory
Running Codemods
Once created, run your codemod using:
The execution flow:
- Graph-sitter parses your codebase into a graph representation
- Your codemod function is executed against this graph
- Changes are tracked and applied to your filesystem
- A diff preview shows what changed
Codemod Structure
A codemod consists of three main parts:
- The
@graph_sitter.function decorator that names your codemod
- A
run function that takes a Codebase parameter
- Your transformation logic using the Codebase API
import graph_sitter
from graph_sitter import Codebase
@graph_sitter.function("update-imports")
def run(codebase: Codebase):
"""Update import statements to use new package names."""
for file in codebase.files:
for imp in file.imports:
if imp.module == "old_package":
imp.rename("new_package")
codebase.commit()
Arguments
Codemods can accept arguments using Pydantic models:
from pydantic import BaseModel
class RenameArgs(BaseModel):
old_name: str
new_name: str
@graph_sitter.function("rename-function")
def run(codebase: Codebase, arguments: RenameArgs):
"""Rename a function across the codebase."""
old_func = codebase.get_function(arguments.old_name)
if old_func:
old_func.rename(arguments.new_name)
codebase.commit()
Run it with:
gs run rename-function --arguments '{"old_name": "getUserData", "new_name": "fetchUserProfile"}'
Directory Structure
Your codemods live in a dedicated directory structure:
.codegen/
└── codemods/
└── rename_function/
├── rename_function.py # The codemod implementation
└── rename_function_prompt.md # System prompt (if using AI)