# `Reach.Plugin`
[🔗](https://github.com/elixir-vibe/reach/blob/v2.2.0/lib/reach/plugin.ex#L1)

Behaviour for library-specific analysis plugins.

Plugins extend Reach in three ways:

1. **Graph edges** — `analyze/2` and `analyze_project/3` add domain-specific
   edges to the dependence graph (framework dispatch, message routing, etc.)

2. **Effect classification** — `classify_effect/1` teaches the effect
   classifier about framework-specific calls (Ecto queries are pure,
   Repo writes are `:write`, etc.)

3. **Embedded IR** — `analyze_embedded/2` extracts code from string
   literals (e.g. JS inside QuickBEAM.eval) and returns additional IR
   nodes plus cross-language edges.

4. **Framework presentation/patterns** — optional callbacks provide
   framework-specific trace presets, behaviour labels, and visualization
   edge filtering.

## Implementing a plugin

    defmodule MyPlugin do
      @behaviour Reach.Plugin

      @impl true
      def analyze(all_nodes, _opts), do: []

      @impl true
      def classify_effect(%Reach.IR.Node{type: :call, meta: %{function: :my_pure_fn}}), do: :pure
      def classify_effect(_), do: nil
    end

## Built-in plugins

Plugins for Phoenix, Ecto, Oban, GenStage, Jido, and OpenTelemetry
are included and auto-detected at runtime. Override with the
`:plugins` option:

    Reach.Project.from_mix_project(plugins: [Reach.Plugins.Ecto])

Disable auto-detection:

    Reach.string_to_graph!(source, plugins: [])

# `edge_spec`

```elixir
@type edge_spec() :: {Reach.IR.Node.id(), Reach.IR.Node.id(), term()}
```

# `embedded_result`

```elixir
@type embedded_result() :: {[Reach.IR.Node.t()], [edge_spec()]}
```

# `analyze`

```elixir
@callback analyze(all_nodes :: [Reach.IR.Node.t()], opts :: keyword()) :: [edge_spec()]
```

Analyzes IR nodes from a single module and returns edges to add.

# `analyze_embedded`
*optional* 

```elixir
@callback analyze_embedded(all_nodes :: [Reach.IR.Node.t()], opts :: keyword()) ::
  embedded_result()
```

Extracts embedded code from IR nodes (e.g. JS strings passed to
QuickBEAM.eval) and returns additional IR nodes plus edges
connecting them to the host graph.

# `analyze_project`
*optional* 

```elixir
@callback analyze_project(
  modules :: %{required(module()) =&gt; map()},
  all_nodes :: [Reach.IR.Node.t()],
  opts :: keyword()
) :: [edge_spec()]
```

Analyzes IR nodes across all modules in a project.

Only needed for cross-module patterns like router→controller
dispatch or job enqueue→perform flow.

# `behaviour_label`
*optional* 

```elixir
@callback behaviour_label(callbacks :: [atom()]) :: String.t() | nil
```

# `classify_effect`
*optional* 

```elixir
@callback classify_effect(node :: Reach.IR.Node.t()) :: atom() | nil
```

Classifies the effect of a call node.

Return an effect atom (`:pure`, `:read`, `:write`, `:io`, `:send`,
`:exception`) or `nil` to defer to the next classifier.

# `ignore_call_edge?`
*optional* 

```elixir
@callback ignore_call_edge?(Graph.Edge.t()) :: boolean()
```

# `trace_pattern`
*optional* 

```elixir
@callback trace_pattern(pattern :: String.t()) :: (Reach.IR.Node.t() -&gt; boolean()) | nil
```

# `behaviour_label`

Infers a framework-specific behaviour label from callback names.

# `classify_effect`

Asks each plugin to classify a call node's effect.

Returns the first non-nil result, or `nil` if no plugin matches.

# `detect`

Returns the list of auto-detected plugins based on loaded dependencies.

# `ignore_call_edge?`

Returns true when a plugin marks a call-graph edge as visualization noise.

# `resolve`

Resolves plugins from options, falling back to auto-detection.

# `run_analyze`

Runs module-local analysis hooks for the configured plugins.

# `run_analyze_embedded`

Runs embedded-node analysis hooks for plugins that provide them.

# `run_analyze_project`

Runs project-level analysis hooks for plugins that provide them.

# `trace_pattern`

Compiles a framework-specific trace pattern, if a plugin recognizes it.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
