Skip to content

Search

search

SearchTool — semantic symbol search.

SearchTool

Bases: AXMTool

Search symbols by name, return type, kind, or base class.

Registered as ast_search via axm.tools entry point.

Source code in packages/axm-ast/src/axm_ast/tools/search.py
Python
class SearchTool(AXMTool):
    """Search symbols by name, return type, kind, or base class.

    Registered as ``ast_search`` via axm.tools entry point.
    """

    agent_hint: str = (
        "Find symbols by name, return type, kind, or base class"
        " — AST-precise, replaces grep."
        " Ask 'functions returning X' or 'classes inheriting Y'."
    )

    _load_package = staticmethod(_load_package)
    _validate_kind = staticmethod(_validate_kind)
    format_symbol = staticmethod(format_symbol)
    find_suggestions = staticmethod(find_suggestions)

    _format_func_line = staticmethod(format_func_line)
    _format_variable_line = staticmethod(format_variable_line)

    _render_text = staticmethod(render_text)

    @property
    def name(self) -> str:
        """Return tool name for registry lookup."""
        return "ast_search"

    @safe_execute
    def execute(
        self,
        *,
        path: str = ".",
        name: str | None = None,
        returns: str | None = None,
        kind: str | None = None,
        inherits: str | None = None,
        **kwargs: object,
    ) -> ToolResult:
        """Search symbols across a package.

        Args:
            path: Path to package directory.
            name: Filter by symbol name (substring match).
            returns: Filter by return type.
            kind: Filter by kind (function, method, property,
                classmethod, staticmethod, abstract, class, variable).
            inherits: Filter by base class name.

        Returns:
            ToolResult with matching symbols.
        """
        pkg = _load_package(path)
        if isinstance(pkg, ToolResult):
            return pkg

        kind_enum = _validate_kind(kind)
        if isinstance(kind_enum, ToolResult):
            return kind_enum

        return _search(
            pkg,
            name=name,
            returns=returns,
            kind=kind_enum,
            inherits=inherits,
        )
name property

Return tool name for registry lookup.

execute(*, path='.', name=None, returns=None, kind=None, inherits=None, **kwargs)

Search symbols across a package.

Parameters:

Name Type Description Default
path str

Path to package directory.

'.'
name str | None

Filter by symbol name (substring match).

None
returns str | None

Filter by return type.

None
kind str | None

Filter by kind (function, method, property, classmethod, staticmethod, abstract, class, variable).

None
inherits str | None

Filter by base class name.

None

Returns:

Type Description
ToolResult

ToolResult with matching symbols.

Source code in packages/axm-ast/src/axm_ast/tools/search.py
Python
@safe_execute
def execute(
    self,
    *,
    path: str = ".",
    name: str | None = None,
    returns: str | None = None,
    kind: str | None = None,
    inherits: str | None = None,
    **kwargs: object,
) -> ToolResult:
    """Search symbols across a package.

    Args:
        path: Path to package directory.
        name: Filter by symbol name (substring match).
        returns: Filter by return type.
        kind: Filter by kind (function, method, property,
            classmethod, staticmethod, abstract, class, variable).
        inherits: Filter by base class name.

    Returns:
        ToolResult with matching symbols.
    """
    pkg = _load_package(path)
    if isinstance(pkg, ToolResult):
        return pkg

    kind_enum = _validate_kind(kind)
    if isinstance(kind_enum, ToolResult):
        return kind_enum

    return _search(
        pkg,
        name=name,
        returns=returns,
        kind=kind_enum,
        inherits=inherits,
    )

find_suggestions(pkg, name, *, kind=None)

Find fuzzy suggestions for a symbol name query.

Passes pkg.root to _collect_module_candidates so module names are resolved even when the parser leaves mod.name unset.

Source code in packages/axm-ast/src/axm_ast/tools/search.py
Python
def find_suggestions(
    pkg: PackageInfo, name: str, *, kind: str | None = None
) -> list[Suggestion]:
    """Find fuzzy suggestions for a symbol name query.

    Passes ``pkg.root`` to ``_collect_module_candidates`` so module
    names are resolved even when the parser leaves ``mod.name`` unset.
    """
    candidates: dict[str, list[tuple[str, str, str]]] = {}

    for mod in pkg.modules:
        _collect_module_candidates(mod, kind, candidates, root=pkg.root)

    if not candidates:
        return []

    matches = get_close_matches(name.lower(), list(candidates.keys()), n=10, cutoff=0.6)

    seen: dict[str, Suggestion] = {}
    for match_key in matches:
        for original_name, sym_kind, module in candidates[match_key]:
            score = round(SequenceMatcher(None, name.lower(), match_key).ratio(), 2)
            if original_name not in seen or score > seen[original_name]["score"]:
                seen[original_name] = {
                    "name": original_name,
                    "score": score,
                    "kind": sym_kind,
                    "module": module,
                }

    return sorted(seen.values(), key=lambda s: s["score"], reverse=True)

format_symbol(sym, module_name)

Format an AST symbol into a serialized dict entry.

Returns a dict with keys name, module, and optionally signature, return_type, kind (function/method/property/ classmethod/staticmethod/abstract/class/variable), annotation, and value_repr.

Source code in packages/axm-ast/src/axm_ast/tools/search.py
Python
def format_symbol(
    sym: FunctionInfo | ClassInfo | VariableInfo, module_name: str
) -> SearchResultEntry:
    """Format an AST symbol into a serialized dict entry.

    Returns a dict with keys ``name``, ``module``, and optionally
    ``signature``, ``return_type``, ``kind`` (function/method/property/
    classmethod/staticmethod/abstract/class/variable), ``annotation``,
    and ``value_repr``.
    """
    from axm_ast.models.nodes import ClassInfo, FunctionInfo, VariableInfo

    entry: SearchResultEntry = {
        "name": sym.name,
        "module": module_name,
    }
    if hasattr(sym, "signature"):
        entry["signature"] = sym.signature
    if hasattr(sym, "return_type"):
        entry["return_type"] = sym.return_type
    match sym:
        case FunctionInfo():
            entry["kind"] = sym.kind.value
        case ClassInfo():
            entry["kind"] = "class"
        case VariableInfo():
            entry["kind"] = "variable"
            _add_variable_fields(entry, sym)
        case _:
            kind = _resolve_kind(sym)
            if kind is not None:
                entry["kind"] = kind
    return entry