AST-Grep Agent

AST-based structural code search and rewrite tools for AI agents.

Diagram

AST-Grep Agent

📋 User Request

⚙️ AST-Grep Agent

✅ Result

AST-Grep Tools

Use AST-Grep Tools to search, analyze, and rewrite code using structural patterns instead of regex.

  1. Install Dependencies

    Install PraisonAI Agents and ast-grep:

    bash
    pip install praisonaiagents
    pip install ast-grep-cli
  2. 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
  3. 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],
    )
  4. Run Agent

    Start the agent:

    python
    result = agent.start("Find all async function definitions in ./src")

How It Works

Diagram
AST-Grep AgentAgentUserAST-Grep AgentAgentUserRequestProcess requestResultResponse

The user describes a refactor or search; the agent runs AST-grep search, rewrite, or scan tools on the codebase.

Diagram

Codebase

AST-Grep Tools

Code Agent

pattern + lang

pattern + replacement

rules YAML

🤖 Agent

🔍 Search

✏️ Rewrite

📋 Scan

Source Files

Quick Start

  1. 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")
  2. 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

python
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_available

Function Details

ast_grep_search(pattern, lang, path=”.”, json_output=True)

Searches code using AST patterns. Returns structured JSON results.

ParameterTypeDefaultDescription
patternstrrequiredAST pattern ($VAR = single node, $$$ = multiple)
langstrrequiredLanguage: python, javascript, typescript, rust, go, java, c, cpp
pathstr"."File or directory to search
json_outputboolTrueReturn JSON format
python
# 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).

ParameterTypeDefaultDescription
patternstrrequiredAST pattern to match
replacementstrrequiredReplacement pattern (can reference $VAR captures)
langstrrequiredProgramming language
pathstr"."File or directory
dry_runboolTruePreview only — set False to apply

Return values

CaseReturn 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=TrueNo matches found for rewrite
Pattern matched nothing, dry_run=FalseNo changes made
sg exited non-zeroError: <stderr or stdout>
python
# 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.

ParameterTypeDefaultDescription
pathstr"."File or directory to scan
rule_filestrNonePath to YAML rule file (uses sgconfig.yml if not set)
python
# 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.

Diagram

yes

no

📥 path arg

🛡️ inside workspace?

⚙️ sg runs

❌ Error: outside the workspace

python
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.

Diagram

yes

no

📤 sg output

📏 ≤ 10,000 chars?

✅ returned unchanged

⚙️ write full buffer to artifact

✅ head + tail + Full output saved to: path

SituationWhat the agent sees
Output ≤ 10,000 charsThe full output, unchanged
Output > 10,000 charsHead + tail preview, the word truncated, and a line Full output saved to: <path>
python
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 varEffect
PRAISONAI_TOOL_OUTPUT_DIRWhen 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:

python
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:

python
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

PatternMeaningExample
$VARMatch single AST nodedef $FN() matches any no-arg function
$$$Match multiple nodesdef $FN($$$) matches any function
Literal codeMatch exactlyprint("hello") matches that exact call

Pattern Examples by Language

Python
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
javascript
// 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
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
python
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
python
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
python
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

PackageRequiredInstall
praisonaiagentsYespip install praisonaiagents
ast-grep-cliOptionalpip 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 — returns Error: <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) or No 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.

Related