AST-Grep Agent
AST-based structural code search and rewrite tools for AI agents.
AST-Grep Tools
Use AST-Grep Tools to search, analyze, and rewrite code using structural patterns instead of regex.
- Install Dependencies
Install PraisonAI Agents and ast-grep:
bash pip install praisonaiagents pip install ast-grep-cli - Import Components
Import the AST-grep tools:
python from praisonaiagents import Agent from praisonaiagents.tools import ast_grep_search, ast_grep_rewrite, ast_grep_scan - Create Agent
Create a code analysis agent:
python agent = Agent( name="CodeAnalyzer", instructions="Analyze and refactor code using AST patterns.", tools=[ast_grep_search, ast_grep_rewrite, ast_grep_scan], ) - Run Agent
Start the agent:
python result = agent.start("Find all async function definitions in ./src")
How It Works
The user describes a refactor or search; the agent runs AST-grep search, rewrite, or scan tools on the codebase.
Quick Start
- Simple Usage
python from praisonaiagents import Agent from praisonaiagents.tools import ast_grep_search agent = Agent( name="CodeSearcher", instructions="Search code for structural patterns.", tools=[ast_grep_search], ) result = agent.start("Find all function definitions in ./src") - With Configuration
python from praisonaiagents import Agent from praisonaiagents.tools import ast_grep_search, ast_grep_rewrite agent = Agent( name="Refactorer", instructions="Search and refactor code patterns.", tools=[ast_grep_search, ast_grep_rewrite], ) result = agent.start("Rename all instances of \"old_function\" to \"new_function\" in Python files under ./src")
Available Functions
from praisonaiagents.tools import ast_grep_search
from praisonaiagents.tools import ast_grep_rewrite
from praisonaiagents.tools import ast_grep_scan
from praisonaiagents.tools import get_ast_grep_tools
from praisonaiagents.tools import is_ast_grep_availableFunction Details
ast_grep_search(pattern, lang, path=”.”, json_output=True)
Searches code using AST patterns. Returns structured JSON results.
| Parameter | Type | Default | Description |
|---|---|---|---|
pattern | str | required | AST pattern ($VAR = single node, $$$ = multiple) |
lang | str | required | Language: python, javascript, typescript, rust, go, java, c, cpp |
path | str | "." | File or directory to search |
json_output | bool | True | Return JSON format |
# Find all Python function definitions
result = ast_grep_search("def $FN($$$)", lang="python", path="./src")
# Find all console.log calls in JavaScript
result = ast_grep_search("console.log($$$)", lang="javascript")
# Find all async functions
result = ast_grep_search("async def $FN($$$)", lang="python")
# Find all class definitions with inheritance
result = ast_grep_search("class $NAME($BASE):", lang="python")ast_grep_rewrite(pattern, replacement, lang, path=”.”, dry_run=True)
Rewrites code matching an AST pattern. Dry-run by default (shows changes without modifying files).
| Parameter | Type | Default | Description |
|---|---|---|---|
pattern | str | required | AST pattern to match |
replacement | str | required | Replacement pattern (can reference $VAR captures) |
lang | str | required | Programming language |
path | str | "." | File or directory |
dry_run | bool | True | Preview only — set False to apply |
Return values
| Case | Return string |
|---|---|
Real rewrite applied (dry_run=False) | Applied N changes (from sg‘s stderr receipt) |
Preview only (dry_run=True) with matches | [DRY RUN] <sg output> |
Pattern matched nothing, dry_run=True | No matches found for rewrite |
Pattern matched nothing, dry_run=False | No changes made |
sg exited non-zero | Error: <stderr or stdout> |
# Preview: rename a function (dry run)
result = ast_grep_rewrite(
"def old_name($$$)",
"def new_name($$$)",
lang="python",
path="./src"
)
# Actually apply: replace console.log with logger.info
result = ast_grep_rewrite(
"console.log($MSG)",
"logger.info($MSG)",
lang="javascript",
dry_run=False
)ast_grep_scan(path=”.”, rule_file=None)
Scans code using YAML-based lint rules.
| Parameter | Type | Default | Description |
|---|---|---|---|
path | str | "." | File or directory to scan |
rule_file | str | None | Path to YAML rule file (uses sgconfig.yml if not set) |
# Scan with default rules
result = ast_grep_scan(path="./src")
# Scan with custom rules
result = ast_grep_scan(path="./src", rule_file="./rules/security.yml")Path Confinement
path and rule_file are constrained to the current workspace. Anything that resolves outside is rejected before sg runs — no destructive rewrite can escape the project.
from praisonaiagents.tools import ast_grep_search, ast_grep_rewrite, ast_grep_scan
# Rejected — absolute path outside the workspace
ast_grep_search("def $F($$$)", lang="python", path="/etc")
# -> "Error: path '/etc' is outside the workspace"
# Rejected — traversal outside the workspace
ast_grep_rewrite("a", "b", lang="python", path="../outside", dry_run=False)
# -> "Error: path '../outside' is outside the workspace"
# Rejected — rule file outside the workspace
ast_grep_scan(path=".", rule_file="/tmp/rules.yml")
# -> "Error: rule_file '/tmp/rules.yml' is outside the workspace"Output Limits
Each call returns at most 10,000 characters. When sg produces more (millions of matches against a big tree are common), the full buffer is written to a workspace-local artifact and the returned string is a head + tail preview plus an unmissable pointer.
| Situation | What the agent sees |
|---|---|
| Output ≤ 10,000 chars | The full output, unchanged |
| Output > 10,000 chars | Head + tail preview, the word truncated, and a line Full output saved to: <path> |
from praisonaiagents.tools import ast_grep_search
result = ast_grep_search("def $F($$$)", lang="python", path=".")
# For a huge tree the tail of `result` looks like:
# ...
# Full output saved to: .praisonai_tool_output/ast_grep_search-<id>.txt
# The advertised path is inside the workspace, so the agent's read_file / grep
# tools can open it directly to inspect the full buffer.| Env var | Effect |
|---|---|
PRAISONAI_TOOL_OUTPUT_DIR | When set, spill artifacts land here instead of the default .praisonai_tool_output/ under the workspace root |
Autonomy Mode
AST-grep tools are automatically included when using autonomy mode:
from praisonaiagents import Agent
# Tools auto-included via get_autonomy_default_tools()
agent = Agent(autonomy=True)
agent.start("Refactor the codebase to use consistent naming")Graceful Fallback
AST-grep tools work safely even when ast-grep is not installed:
from praisonaiagents.tools import is_ast_grep_available, ast_grep_search
# Check availability
if is_ast_grep_available():
result = ast_grep_search("def $FN($$$)", lang="python")
else:
print("ast-grep not installed")
# Or just call it — returns helpful install instructions
result = ast_grep_search("def $FN($$$)", lang="python")
# If not installed, returns:
# "ast-grep is not installed or not available in PATH.
# To install: pip install ast-grep-cli"Pattern Syntax
| Pattern | Meaning | Example |
|---|---|---|
$VAR | Match single AST node | def $FN() matches any no-arg function |
$$$ | Match multiple nodes | def $FN($$$) matches any function |
| Literal code | Match exactly | print("hello") matches that exact call |
Pattern Examples by Language
Python
# All function definitions
"def $FN($$$)"
# All class definitions
"class $NAME($$$):"
# All if statements
"if $COND: $$$"
# All async functions
"async def $FN($$$)"
# All list comprehensions
"[$EXPR for $VAR in $ITER]"JavaScript / TypeScript
// All arrow functions
"($$$) => $BODY"
// All console.log calls
"console.log($$$)"
// All async functions
"async function $FN($$$) { $$$ }"
// All React useState hooks
"const [$STATE, $SETTER] = useState($$$)"
// All fetch calls
"fetch($URL, $$$)"Rust
// All function definitions
"fn $FN($$$) -> $RET { $$$ }"
// All impl blocks
"impl $TYPE { $$$ }"
// All match expressions
"match $EXPR { $$$ }"
// All unsafe blocks
"unsafe { $$$ }"Examples
Security Audit Agent
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.tools import ast_grep_search, ast_grep_scan
security_agent = Agent(
name="SecurityAuditor",
instructions="""Audit code for security vulnerabilities:
- Find eval() and exec() calls
- Find SQL string concatenation
- Find hardcoded credentials""",
tools=[ast_grep_search, ast_grep_scan],
)
audit_task = Task(
description="Audit ./src for security issues: eval/exec usage, SQL injection, hardcoded secrets.",
expected_output="Security report with findings and recommendations.",
agent=security_agent,
name="security_audit"
)
team = AgentTeam(agents=[security_agent], tasks=[audit_task], process="sequential")
team.start()Code Migration Agent
from praisonaiagents import Agent
from praisonaiagents.tools import ast_grep_search, ast_grep_rewrite
migrator = Agent(
name="Migrator",
instructions="""Migrate code patterns:
- Replace deprecated APIs with new ones
- Always preview first (dry_run=True)
- Only apply changes after confirming the preview""",
tools=[ast_grep_search, ast_grep_rewrite],
)
result = migrator.start(
"Replace all console.log calls with logger.info in JavaScript files under ./app"
)Multi-Agent Code Review
from praisonaiagents import Agent, Task, AgentTeam
from praisonaiagents.tools import ast_grep_search, ast_grep_scan
# Searcher finds patterns
searcher = Agent(
name="PatternFinder",
instructions="Find code patterns and anti-patterns using AST search.",
tools=[ast_grep_search],
)
# Reviewer analyzes findings
reviewer = Agent(
name="CodeReviewer",
instructions="Review code quality findings and provide recommendations.",
)
search_task = Task(
description="Find all functions longer than 50 lines and deeply nested conditionals.",
agent=searcher,
name="pattern_search"
)
review_task = Task(
description="Review the search findings and provide refactoring recommendations.",
agent=reviewer,
name="code_review"
)
team = AgentTeam(
agents=[searcher, reviewer],
tasks=[search_task, review_task],
process="sequential"
)
team.start()Dependencies
| Package | Required | Install |
|---|---|---|
praisonaiagents | Yes | pip install praisonaiagents |
ast-grep-cli | Optional | pip install ast-grep-cli or npm install -g @ast-grep/cli |
Error Handling
All functions return errors as strings — they never crash the agent:
- Not installed — returns install instructions (unchanged).
- Empty pattern / empty replacement — returns a clear error string (unchanged).
- Path outside the workspace — returns
Error: path '<value>' is outside the workspace. See Path Confinement. - Rule file outside the workspace (scan) — returns
Error: rule_file '<value>' is outside the workspace. - Non-zero exit from
sg— returnsError: <stderr or stdout>(broadened from stderr-only). - No matches (search / scan) — returns
No matches found/No issues found. - No matches (rewrite) —
No matches found for rewrite(dry_run=True) orNo changes made(dry_run=False). - Rewrite applied — returns
sg‘s stderr receipt, e.g.Applied N changes. - Timeout — 60s for search, 120s for rewrite and scan.
- Output > 10,000 chars — bounded preview plus
Full output saved to: <path>pointer. See Output Limits.