# Architecture

## Overview

`axm-ast` follows a layered architecture: CLI → core engines → models. The core layer is entirely I/O-free — it operates on Pydantic models produced by tree-sitter parsing.

```mermaid
graph TD
    subgraph "User Interface"
        CLI["CLI (cyclopts)"]
    end

    subgraph "Core Engines"
        Parser["Parser (tree-sitter)"]
        Analyzer["Analyzer"]
        Cache["Cache"]
        Ranker["Ranker (PageRank)"]
        Callers["Caller Analysis"]
        Context["Context (one-shot)"]
        Impact["Impact Analysis"]
        GitCoupling["Git Coupling"]
        StructDiff["Structural Diff"]
        Workspace["Workspace"]
        Docs["Docs Discovery"]
        Formatters["Formatters"]
    end

    subgraph "Models (Pydantic)"
        ModuleInfo["ModuleInfo"]
        PackageInfo["PackageInfo"]
        WorkspaceInfo["WorkspaceInfo"]
        CallSite["CallSite"]
    end

    subgraph "External"
        TreeSitter["tree-sitter-python"]
        FS["File System"]
        PyProject["pyproject.toml"]
        Git["git log"]
    end

    CLI --> Cache
    CLI --> Context
    CLI --> Impact
    CLI --> Callers
    CLI --> Formatters
    Cache --> Analyzer
    Cache --> PackageInfo
    Analyzer --> Parser
    Ranker --> PackageInfo
    Callers --> Parser
    Context --> Cache
    Context --> Ranker
    Impact --> Callers
    Impact --> Cache
    Impact --> GitCoupling
    GitCoupling --> Git
    StructDiff --> Git
    StructDiff --> Analyzer
    Parser --> TreeSitter
    Parser --> FS
    Parser --> ModuleInfo
    Analyzer --> PackageInfo
    Callers --> CallSite
    Context --> PyProject
    CLI --> Docs
    CLI --> Workspace
    Workspace --> Cache
    Workspace --> Callers
    Workspace --> Impact
    Workspace --> WorkspaceInfo
    Docs --> FS
```

## Layers

### 1. CLI (`cli.py`)

Cyclopts-based commands with input validation and formatted output (text + JSON). Each command follows the pattern: parse arguments → call core → format output.

### 2. Core Engines (`core/`)

Independent, composable analysis engines:

| Engine | Purpose | Key Function |
|---|---|---|
| `parser.py` | Tree-sitter AST parsing → `ModuleInfo` | `extract_module_info()` |
| `analyzer.py` | Package discovery, import graph (absolute + relative), search, stubs | `analyze_package()` |
| `cache.py` | Thread-safe caching of `PackageInfo` — avoids redundant parsing | `get_package()`, `clear_cache()` |
| `ranker.py` | PageRank symbol importance | `rank_symbols()` |
| `callers.py` | Call-site detection | `find_callers()`, `find_callers_workspace()` |
| `context.py` | One-shot project dump | `build_context()` |
| `impact.py` | Change blast radius (callers + reexports + tests + git coupling + cross-package) | `analyze_impact()`, `analyze_impact_workspace()` |
| `git_coupling.py` | Git co-change coupling analysis (6-month history) | `git_coupled_files()` |
| `structural_diff.py` | Symbol-level branch diff via git worktrees | `structural_diff()` |
| `workspace.py` | Multi-package workspace detection and analysis | `detect_workspace()`, `analyze_workspace()` |
| `docs.py` | Documentation tree discovery | `discover_docs()` |
| `dead_code.py` | Dead code detection with test/lazy-import/base-class scanning | `find_dead_code()`, `DeadSymbol` |
| `flows.py` | Entry point detection, BFS flow tracing, source enrichment | `find_entry_points()`, `trace_flow()` |

### 3. Formatters (`formatters.py`)

Output formatting with multiple detail levels:

| Function | Purpose |
|---|---|
| `format_text()` | Human-readable text (summary / detailed / full) |
| `format_compressed()` | AI-friendly compressed view |
| `format_json()` | Machine-readable JSON |
| `format_toc()` | Table-of-contents: module names + counts only |
| `filter_modules()` | Case-insensitive substring filter on module names |
| `format_mermaid()` | Mermaid dependency graph |

### 4. Models (`models/`)

Pydantic models for structured data exchange between layers:

| Model | Purpose |
|---|---|
| `ModuleInfo` | Full introspection result for a single module |
| `PackageInfo` | Full introspection result for a package |
| `FunctionInfo` | Function metadata (params, return type, decorators) |
| `ClassInfo` | Class metadata (bases, methods, docstring) |
| `ParameterInfo` | Function parameter (name, type, default) |
| `VariableInfo` | Module-level variable / constant |
| `ImportInfo` | Import statement (absolute/relative, names) |
| `CallSite` | Call-site location (module, line, context) |
| `WorkspaceInfo` | Multi-package workspace (packages, dependency edges) |

### 5. Hooks (`hooks/`)

Protocol hooks registered via `axm.hooks` entry points. These are called by `axm-engine` as pre/post-hooks in protocol execution.

| Hook | Entry Point | Purpose |
|---|---|---|
| `TraceSourceHook` | `ast:trace-source` | Run `trace_flow(detail="source")` and inject trace into session context |
| `SourceBodyHook` | `ast:source-body` | Fetch raw source body for a symbol and inject it into session context |
| `FileHeaderHook` | `ast:file-header` | Extract file-level header (module docstring, `__all__`, top-level imports) and inject into session context |

## Design Decisions

| Decision | Rationale |
|---|---|
| tree-sitter for parsing | Fast, incremental, handles broken files gracefully |
| Pydantic models | Validation, serialization, JSON output for free |
| PageRank for ranking | Graph-based importance adapts to any project structure |
| Composable engines | `impact` = `callers` + `analyzer` + `ranker` + test mapping + git coupling |
| Session cache | `PackageCache` avoids redundant tree-sitter parsing across chained tool calls |
| Workspace auto-detect | `[tool.uv.workspace]` triggers multi-package mode transparently |
| `src/` layout | PEP 621 best practice, no import conflicts |
