Back to projects
Active Started Nov 2025

NavGator

Architecture connection tracker that maps your stack before you change it.

TypeScript Commander ts-morph Glob Graphology Next.js

Install

claude plugin marketplace add tyroneross/RossLabs-AI-Toolkit
claude plugin install navgator@rosslabs-ai-toolkit

The Problem

AI coding assistants make changes without full context of how components connect. You ask for a database schema change and the migration file gets updated, while the 12 API routes that query the table, the 3 background jobs that write to it, and the dashboard that aggregates it do not. Modern apps are connection graphs, not isolated modules. Without a map, you are navigating architecture blindfolded.

What I Built

NavGator scans a codebase, builds a typed connection graph, and answers “what breaks if I change this?” before you change it. It also annotates each component with the runtime identity it ships under (service name, queue, endpoint, deploy target) so a runtime failure can be traced back to the code that produced it.

Architecture

NavGator is a static-analysis pipeline, not an AI system. It never calls a model to understand a codebase — every component, connection, and score comes from file reads, glob walks, AST parsing, and graph algorithms. It does track LLM usage inside the scanned target repo (which files call OpenAI, Anthropic, Groq, etc.), but that is pattern-matching against known SDK imports and call shapes, not an LLM asking an LLM.

flowchart LR
  A["CLI / MCP call<br/>navgator scan"] --> B["Phase 1: Bootstrap<br/>package.json, lockfiles,<br/>requirements.txt (&lt;1s)"]
  B --> C["Phase 2: Deep scan<br/>glob walk + pattern match"]
  C --> D["ts-morph AST<br/>(TS/JS, optional)"]
  C --> E["Swift / Rust /<br/>SCIP source scanners"]
  D --> F["Typed component +<br/>connection records"]
  E --> F
  F --> G["Runtime topology enrichment<br/>(env vars, deploy configs)"]
  G --> H["LLM use-case dedup<br/>(pattern-based, no model call)"]
  H --> I["Graphology metrics<br/>PageRank + Louvain (fixed seed)"]
  I --> J[("File-based JSON/JSONL store<br/>.navgator/architecture/*")]
  J --> K["CLI + 10 MCP tools +<br/>13 slash commands"]
  J --> L["Next.js web dashboard"]
  M["PostToolUse hook<br/>dirty-set ledger"] -.background drainer.-> C

End-to-end data flow:

  1. Entry. A scan starts from the CLI (navgator scan), an MCP tool call from Claude Code or Codex, or the PostToolUse freshness hook after an agent edits a file.
  2. Phase 1 — Bootstrap (< 1s). Reads package.json, lockfiles, requirements.txt, Package.swift, Cargo.toml to detect the stack: frameworks, databases, queues, providers.
  3. Phase 2 — Deep scan (5–30s). glob walks the source tree against a default ignore list plus an optional .navgatorignore, then pattern-matches service calls, DB operations, imports, and AI prompts.
  4. AST / source-language scanners (optional, additive). ts-morph parses TypeScript/JavaScript for dynamic imports and reflection-based calls that pattern matching misses; dedicated Swift and Rust scanners extract types, protocol conformance, and use/import graphs directly from source; @sourcegraph/scip-typescript supplies an optional cross-reference index layer.
  5. Incremental diffing. SHA-256 hashes of every scanned file are compared against the prior hashes.json; only changed files re-analyze, and connections for deleted files are dropped automatically.
  6. Parallel merge. Four worker threads (default) partition the file set, build partial graphs, and merge — the mechanism that keeps 10,000+ file repos scanning without memory pressure.
  7. Runtime topology enrichment. Service names, endpoint URLs, queue names, and deploy targets are extracted from Dockerfiles, vercel.json/railway.json, and env vars, then annotated onto the matching graph nodes — no polling, no live health checks.
  8. LLM use-case dedup. A 3-layer pipeline (filter test/dev/import-only sites → group by prompt-match, function name, or call-type+model → merge groups connected via the import graph) collapses raw call sites (e.g. 154) into distinct use cases (e.g. 8). Entirely heuristic; no model is called to do the grouping.
  9. Graph metrics. graphology builds a DirectedGraph; graphology-metrics computes PageRank centrality and graphology-communities-louvain computes community clusters, both under a fixed Mulberry32 seed for reproducible output (suppressed below 20 nodes, where the algorithms degenerate). Scores back-annotate each component and drive the hotspot-module rule.
  10. Storage. Everything lands as JSON/JSONL under <project>/.navgator/architecture/components.full.jsonl and connections.full.jsonl are canonical; graph.json, index.json, file_map.json, reverse-deps.json, and NAVSUMMARY.md are derived views. There is no database — the file store is the datastore.
  11. Freshness loop. A non-blocking PostToolUse hook appends to a dirty-file ledger as an agent edits; a background drainer coalesces entries, holds a single-writer scan lock, and rescans only the changed surface; every read consults a freshness stamp with an in-process debounce to avoid scan storms.
  12. Consumption. The CLI, 10 MCP tools, 13 Claude Code slash commands, 4 subagents, 6 skills, Codex’s skill/MCP surface, and the Next.js web dashboard all read the same JSON store to answer impact, trace, diagram, dead, schema, and rules queries.

Models. None. NavGator’s own execution path never calls an LLM, local or hosted — component detection, connection classification, LLM-use-case grouping, and hotspot scoring are all deterministic pattern matching and graph algorithms. This is a deliberate choice, not an omission: determinism means the tool works on broken/unbuildable code, runs safely in CI, and produces identical output on identical input (the graph algorithms are seeded for exactly this reason). The “LLM” it talks about in its own output is always the target repo’s usage, detected by matching known provider SDK names and call shapes.

Tools & infra.

  • Commander — CLI argument parsing and command routing for the navgator binary.
  • glob — file discovery across the scanned repo, respecting ignore patterns; chosen because both scan phases are pure file reads with no build step.
  • ts-morph — optional TypeScript/JavaScript AST parsing, used only when accuracy on dynamic imports/reflection is worth ~3x the scan time.
  • @sourcegraph/scip-typescript — optional SCIP index generation for deeper cross-reference resolution.
  • graphology + graphology-metrics + graphology-communities-louvain — directed graph, PageRank centrality, and Louvain community detection over the connection graph; powers hotspot and dead-component rules.
  • File-based JSON/JSONL store.navgator/architecture/*, chosen over a database because the tool ships as a zero-install npm package/CLI plugin with no server to provision; a Map<string, Set<string>> adjacency list keeps a 1,000-file/5,000-connection graph under ~2MB.
  • Next.js (v16, bundled standalone into the npm package) — the navgator ui web dashboard for browsing components, connections, and diagrams visually.
  • MCP server (dist/mcp/server.js) — exposes 10 read-only tools so Claude Code and Codex query the graph without shelling out to the CLI.

Impact Analysis Leads

NavGator web dashboard showing Architecture Status with 136 components, 288 connections, and 20 LLM calls tracked ::border

The core command is navgator impact "<thing>". It returns every file that imports, calls, references, or types against the named component, with file:line so each connection is verifiable. Migrating from Stripe SDK v11 to v12 might surface 47 affected files split across API routes, components, webhook handlers, utilities, jobs, and a CLI script. Pre-change, that is a checklist. Post-change, the same query verifies nothing was missed.

Impact analysis view with component cards for selecting what to analyze

How Scanning Works

Two phases keep the tradeoff honest:

PhaseSourceTimeWhat it finds
1 — Bootstrappackage.json, lockfiles, requirements.txt< 1sStack: frameworks, databases, queues, providers
2 — DeepStatic analysis + AST (optional ts-morph)5–30sService calls, DB ops, prompts, typed signatures

Both phases are pure file reads, no execution. NavGator scans broken codebases that won’t build. SHA-256 hashing makes subsequent scans incremental — only changed files re-analyze, and deletions remove their connections automatically.

Freshness Pipeline

A graph that goes stale silently is worse than no graph. The freshness layer keeps the answer honest without forcing a manual re-scan loop:

  • Dirty-set ledger — append-only, atomic writes. A non-blocking PostToolUse hook marks files dirty as the coding agent edits them.
  • Background drainer — coalesces dirty entries, holds a single-writer scan lock with stale/dead detection, scans only the changed surface, and stamps the result.
  • Honesty contract — every read consults the stamp. An in-process debounce on autoRefreshIfStale prevents scan storms when ten queries land in the same second.

The result: when you ask navgator impact "X", the answer reflects the repo as it stands, not as it stood the last time someone remembered to run scan.

Runtime Topology — Code to Production Identity

A static dependency graph tells you which file calls which function. It does not tell you that the function runs inside the email-queue BullMQ worker on Railway, behind the notifications.api service URL. When that worker backs up at 2am, you bridge two worlds by hand.

NavGator’s runtime topology layer closes that gap by extracting runtime identity from code and config — service names from deploy manifests, endpoint URLs from env vars, queue names from BullMQ definitions, container targets from Dockerfiles — and annotating the existing graph nodes with them. A failure ticket that says “Railway worker email-queue is failing” maps back to producer code, consumer code, env-var dependencies, and deploy target in one query.

This is not live monitoring. NavGator does not poll services, hit health endpoints, or query metrics APIs. That is observability tooling territory (Datadog, Grafana, Railway logs). NavGator stays static — it just makes the static map speak the same language as the runtime.

LLM Use-Case Deduplication

NavGator AI Flow view showing Output/Summary layer with orange-bordered component pills, Response endpoint, and Files with AI Prompts list below ::border

Counting raw import and new OpenAI() sites is the wrong number. One real project showed 154 “LLM connections” — but only 8 distinct use cases (article summarization, entity extraction, embeddings, search routing, classification, and a few more). The other 146 were the same use case re-imported across script files, test files, and standalone runners. Some were imported but never called.

NavGator groups LLM call sites by purpose, not by file, and filters by the existing production-vs-test classification before counting. The headline becomes “8 LLM use cases across 3 providers” instead of “154 service calls.” That is the right granularity for the actual question — what LLM use cases do I need to govern, monitor, or consolidate — because you tune use cases, not call sites.

Files with AI Prompts showing 27 tracked files with prompt counts and file paths ::border

The prompt detector pulls prompt content, variable bindings, and call type (chat, summarization, extraction, classification, generation), which is what makes purpose-level clustering possible. A file with 10 prompts attached jumps out as a consolidation candidate.

Tiered Memory

NavGator writes its analysis into a layered store so AI assistants load only what they need:

LayerFileLoaded
Hot contextSUMMARY.md (40–150 lines)First, every session
Structuredindex.json, graph.json, file_map.jsonOn programmatic queries
Detailedcomponents/, connections/ per-fileOn demand only

SUMMARY.md carries timestamps and invalidates when package.json is newer, so Phase 1 re-runs without manual intervention. file_map.json keys on relative paths so a directory rename does not nuke hours of analysis.

Host Surfaces

Claude Code gets /navgator:* slash commands: map, plan, scan, trace, impact, test, review, llm-map, schema, dead, lessons. The architecture-planner agent picks up /navgator:plan "<intent>", checks graph freshness, runs an incremental scan if stale, then dispatches read-only NavGator tools and aggregates findings.

Codex has its own additive plugin surface — skills and MCP tools — with native marketplace metadata at .codex-plugin/plugin.json and .agents/plugins/marketplace.json. Claude remains the authoritative host for hooks, slash commands, and agent wiring.

Visualization

Architecture diagram with color-coded layers — Frontend (Next.js, React), Backend (payments.ts, users.ts), Data Layer (PostgreSQL, Redis), External Services (OpenAI, Stripe) — connected by labeled flow arrows ::border

Diagrams group components by layer with color-coded categories, showing API → Database → Cache flows with labeled connection types. File-level diagrams show import relationships. Output is Mermaid, JSON, or CSV.

Components table showing packages with type, layer, version, and connection counts

Tech stack

TypeScript, Commander (CLI), glob (file discovery), ts-morph (optional TS/JS AST), @sourcegraph/scip-typescript (optional cross-reference index), Graphology + graphology-metrics + graphology-communities-louvain (PageRank/Louvain graph metrics), file-based JSON/JSONL storage (no database), Next.js (web dashboard), MCP server (10 tools). No LLM in the execution path.

Results

Figures below are drawn from the write-ups and screenshots elsewhere on this page; rows marked illustrative are worked examples of the tool’s output shape, not measured runs.

MetricFigureSource
LLM call-site deduplication154 raw sites → 8 use cases (roughly 95% reduction)One project’s import/new OpenAI() count collapsed to 8 distinct use cases across 3 providers after purpose-level grouping
Blast-radius query (illustrative)e.g. ~47 files for a Stripe SDK v11→v12 migrationIllustrative navgator impact walkthrough — an example of the scope a query spans (API routes, components, webhook handlers, utilities, jobs, a CLI script), not a measured run
Reference-project scan136 components, 288 connections, 20 LLM calls trackedArchitecture Status dashboard snapshot
Bootstrap scan (Phase 1)< 1spackage.json/lockfile parsing
Deep scan (Phase 2)5–30sStatic analysis + optional ts-morph AST
ts-morph AST mode~50% accuracy gain at ~3x scan timeTradeoff vs. pattern-matching alone
Graph memory footprintunder ~2MBAdjacency-list (Map<string, Set<string>>) for a 1,000-file / 5,000-connection project
Parallel scaling10,000+ filesDefault 4 worker threads, partition-and-merge, no reported memory pressure

⚠️ no benchmark yet for end-to-end time saved per incident or adoption across teams — the figures above cover scan mechanics, graph size, and one worked migration example, not a controlled before/after study.

Technical Decisions

No execution, ever. Both scan phases are static analysis. Runtime crashes in the target app do not break NavGator. This is also what lets it run in CI on PR branches that haven’t been built.

Optional ts-morph for TypeScript. AST scanning catches dynamic imports, computed property access, and reflection-based calls that pattern matching misses. Roughly 50% accuracy gain at ~3x the scan time. Heavy-metaprogramming projects benefit most; thin projects do not need it.

Adjacency-list graph. A Map<string, Set<string>> representation keeps a 1,000-file / 5,000-connection project under ~2MB. A dense matrix would burn 1M cells on a sparse graph.

Parallel workers. Default 4 worker threads partition the file set, build partial graphs, and merge. Scales to 10,000+ files without memory pressure.

Boundaries. NavGator annotates the graph with runtime identity extracted from code and config. It does not poll, scrape, or hit live infra. That keeps the tool deterministic, offline-capable, and safe to run anywhere.