npm package: @reactgraph/cli
Description: Pre-computed React project graph for AI-assisted development
Latest version: 0.1.2

# ReactGraph

Pre-computed React project graph for AI-assisted development. Stop Claude Code from searching — start it navigating.

## The Problem

When Claude Code works on a React project, more than half its time and tokens go to file discovery — grep/cat cycles burning 30,000–60,000 tokens per task just to understand the codebase.

## The Solution

ReactGraph parses your React project with tree-sitter, builds a graph of components, hooks, state stores, context providers, routes, and API calls, then exposes it via an MCP server or a flat file. Claude reads one compact project map and starts coding immediately.

**Before ReactGraph:** 40+ tool calls to understand the codebase → 60K tokens wasted
**After ReactGraph:** 1 MCP call → ~2–4K token project map → start coding immediately

## Quick Start

```bash
# Install
bun install -g reactgraph

# In your React project
cd my-react-app
reactgraph init      # detects framework, creates .reactgraph/
reactgraph index     # builds the graph (~2-5 seconds)
```

After indexing, `.reactgraph/ai-context.md` is generated automatically — a flat file Claude can read with zero setup.

## Connect to Claude Code

### Option 1: MCP Server (recommended)

```bash
claude mcp add reactgraph -- reactgraph serve
```

Or add to `.mcp.json`:

```json
{
  "mcpServers": {
    "reactgraph": {
      "command": "reactgraph",
      "args": ["serve"]
    }
  }
}
```

Use `--watch` to automatically update the graph when files change:

```json
{
  "mcpServers": {
    "reactgraph": {
      "command": "reactgraph",
      "args": ["serve", "--watch"]
    }
  }
}
```

### Option 2: Flat File (zero setup)

Add to your `CLAUDE.md`:

```markdown
Read .reactgraph/ai-context.md at session start instead of exploring files.
```

## CLAUDE.md Integration

Add these navigation rules to your project's `CLAUDE.md`:

```markdown
## Navigation Rules (ReactGraph)

- ALWAYS call reactgraph.get_map() at session start instead of exploring files
- Use reactgraph.get_subgraph(name) before modifying any component or hook
- Use reactgraph.get_file_context(path) for detailed file info before editing
- Use reactgraph.find_nodes({ query: "name" }) to find a node and its connections
- Use reactgraph.impact(target) before refactoring to know what breaks
- Do NOT use grep/find/glob to discover project structure — the graph has it
- If ReactGraph MCP is not available, read .reactgraph/ai-context.md instead
```

## MCP Tools

### `get_map()`

Returns a compact overview of the entire project — routes, components sorted by connectivity, hooks, state stores, context providers, and shared modules.

```
# REACTGRAPH MAP | 127 components, 43 hooks, 8 stores, 12 routes
# Indexed: 2026-03-22T14:30:00Z | 342 files

## ROUTES
/                  → DashboardPage  [src/app/page.tsx]
/settings          → SettingsPage   [src/app/settings/page.tsx]
POST /api/auth     [src/app/api/auth/route.ts]

## COMPONENTS (top 30 by connectivity)
Dashboard  [src/components/Dashboard.tsx:5]
  renders: MetricsCard, FilterBar
  hooks: useAuth, useDashboardData
  context: ThemeCtx(read)
  state: dashboardStore(read)
  props: none (page component)

## HOOKS
useAuth  [src/hooks/useAuth.ts:3]
  returns: { user, login, logout, isLoading }
  state: authStore(read/write)
  fetches: GET /api/auth/me
  used-by: Dashboard, Sidebar, Header (23 total)

## STATE STORES
authStore  [zustand] [src/stores/auth.ts]
  shape: { user, token, isAuthenticated }
  writers: useAuth
  readers: Dashboard, Sidebar, Header

## CONTEXT PROVIDERS
ThemeCtx  [src/contexts/Theme.tsx] provided-at: App
  consumers: Dashboard, Sidebar, Header (12 total)
```

### `get_subgraph(target, depth?)`

Zoom into a specific component, hook, or store. Shows all connections up to the given depth.

```
> get_subgraph("useAuth", depth=2)

useAuth [src/hooks/useAuth.ts:12]
  ├─ reads_store → authStore [src/stores/auth.ts]
  │   └─ reads_store → Dashboard, Sidebar, Header
  ├─ fetches → GET /api/auth/me
  ├─ uses_hook → useState, useEffect
  used-by (23):
  │ ← uses_hook ← Dashboard [src/pages/Dashboard.tsx]
  │ ← uses_hook ← Sidebar [src/components/Sidebar.tsx]
  │ ← uses_hook ← Header [src/components/Header.tsx]
```

### `get_file_context(path)`

Everything about a specific file — exports, imports, components, hooks, stores, effects, and all relationships.

```
> get_file_context("src/components/Dashboard.tsx")

FILE: src/components/Dashboard.tsx
EXPORTS: Dashboard (default)
IMPORTS:
  useAuth ← src/hooks/useAuth.ts
  MetricsCard ← src/components/MetricsCard.tsx

COMPONENT: Dashboard (line 5)
  RENDERS: MetricsCard
  HOOKS: useAuth → { user, isLoading }
  STATE: authStore(read)
  CONTEXT: ThemeCtx(read)
  RENDERED BY: App
```

### `trace_flow(from, to)`

Trace the data flow path between two nodes. Shows how data gets from A to B.

```
> trace_flow("LoginForm", "/api/auth/login")

LoginForm → POST /api/auth/login

LoginForm [src/components/LoginForm.tsx]
  └─ uses_hook → useAuth [src/hooks/useAuth.ts]
      └─ fetches → endpoint:POST:/api/auth/login
```

### `impact(target)`

Analyze what would break if a component, hook, or store is changed.

```
> impact("useAuth")

IMPACT ANALYSIS: useAuth [src/hooks/useAuth.ts]

DIRECT (23 dependents):
  uses_hook ← Dashboard [src/pages/Dashboard.tsx]
  uses_hook ← Sidebar [src/components/Sidebar.tsx]
  uses_hook ← Header [src/components/Header.tsx]

INDIRECT (via components using useAuth):
  App [src/App.tsx]

SAFE TO CHANGE:
  Internal implementation (same return type + same store writes = no breakage)
RISKY:
  Return type shape — 23 consumers destructure { user, login, logout, isLoading }
```

### `find_nodes(filters)`

Query the graph. Use `query` to find a node and see all its connections. Use other filters for bulk searches.

```
> find_nodes({ query: "useUIStore" })

useUIStore [Store] [src/lib/ui-store.ts:69]

reads_store ← Sidebar [src/components/Sidebar.tsx], Header [src/components/Header.tsx]
writes_store ← useUIActions [src/hooks/useUIActions.ts]

> find_nodes({ kind: "Hook", fetches: true })

Found 4 nodes:
  useAuth [Hook] [src/hooks/useAuth.ts:3]
  useDashboardData [Hook] [src/hooks/useDashboardData.ts:5]

> find_nodes({ kind: "Component", orphan: true })

Found 2 nodes:
  DeprecatedBanner [Component] [src/components/DeprecatedBanner.tsx:1]
  OldUserCard [Component] [src/components/OldUserCard.tsx:1]
```

**Available filters:** `query`, `name`, `kind`, `using_hook`, `using_store`, `fetches`, `orphan`

## CLI Commands

| Command | Description |
|---------|-------------|
| `reactgraph init` | Detect framework, create `.reactgraph/` config |
| `reactgraph index` | Build the project graph with progress display |
| `reactgraph serve` | Start the MCP server (stdio transport) |
| `reactgraph serve --watch` | Start MCP server + auto-update on file changes |
| `reactgraph unused` | Find orphan components, unused hooks, dead exports |

## How It Works

1. **Walk** — finds all `.ts`, `.tsx`, `.js`, `.jsx` files, respecting `.gitignore`
2. **Parse** — tree-sitter builds ASTs (fast, handles broken code)
3. **Extract** — nine extractors run per file in order:
   - **imports** — import/export graph, path resolution
   - **components** — function, arrow, memo-wrapped, class components
   - **hooks** — custom hook definitions + all hook call sites
   - **jsx-tree** — which components render which, prop passing
   - **context** — createContext, Provider, useContext tracking
   - **state** — Zustand, Redux Toolkit, Jotai, Recoil store detection
   - **api-calls** — fetch, axios, API wrapper call extraction
   - **routes** — React Router, Next.js App/Pages Router
   - **effects** — useEffect dependency arrays, cleanup detection
4. **Resolve** — two-pass indexing ensures cross-file references (stores, contexts, components) are fully linked regardless of file processing order
5. **Persist** — graph saved to `.reactgraph/graph.json` with SHA256 file hashes for incremental re-indexing
6. **Format** — token-optimized structured text output for AI consumption

## What It Detects

| Category | What's Tracked |
|----------|---------------|
| Components | Function, arrow, class, memo/forwardRef wrapped |
| Hooks | Custom definitions, all call sites, destructured returns |
| Render tree | Which components render which, prop passing |
| Context | createContext, Provider wrapping, useContext consumers |
| State | Zustand `create()`, Redux `createSlice()`, Jotai `atom()`, Recoil `atom()` |
| Store usage | Readers (`useStore`, `useSelector`, `useAtomValue`) and writers (`setState`, `dispatch`, `useSetAtom`) |
| API calls | `fetch()`, `axios.*()`, custom API wrappers |
| Routes | React Router `<Route>`, Next.js file-based routing (App + Pages) |
| Effects | `useEffect`/`useLayoutEffect` deps, cleanup, triggers |
| Imports | Full import/export graph with path resolution |

## Graph Schema

**Node kinds:** Component, Hook, BuiltinHook, Context, Route, Module, Store, ApiEndpoint, Type, Util, Constant

**Edge kinds:** imports, renders, passes_prop, uses_hook, provides, consumes, calls, fetches, reads_store, writes_store, routes_to, type_of

## Configuration

After `reactgraph init`, settings are stored in `.reactgraph/config.json`:

```json
{
  "srcDirs": ["src"],
  "framework": "next",
  "exclude": ["**/*.test.*", "**/*.spec.*", "**/*.stories.*"]
}
```

`.reactgraph/` is automatically added to `.gitignore` — it's a local cache, not a shared artifact.

## Tech Stack

| Layer | Choice |
|-------|--------|
| Parser | tree-sitter (TS/TSX/JS/JSX) |
| Graph | Custom in-memory with secondary indexes |
| CLI | Commander + Ink |
| MCP | @modelcontextprotocol/sdk (stdio) |
| File watching | chokidar (incremental re-parse) |
| Persistence | JSON with file hash caching |

Five runtime dependencies. No databases, no API keys, no Docker.

## Development

```bash
bun install
bun test           # run tests (37 tests)
bun run build      # compile TypeScript
bun run dev        # run CLI directly
```

## License

MIT