# agentmap

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/raymondchins/agentmap  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/agentmap

## Description
TS/JS-accurate repo map for coding agents: a ts-morph import/symbol graph, 8 MCP query tools.

## Claude Desktop Quick Installation
Heuristic fallback — verify the package name and runner against the repository README before running it. Uses `npx` (confidence: low):

```json
"mcpServers": {
  "agentmap": {
    "command": "npx",
    "args": ["-y","agentmap"]
  }
}
```

## Documentation & README

<p align="center">
  <img src="https://raw.githubusercontent.com/raymondchins/agentmap/HEAD/assets/hero.png" alt="agentmap — 98% fewer tokens for a coding agent to find your code" width="100%">
</p>

<p align="center">
  <a href="https://www.npmjs.com/package/@raymondchins/agentmap"><img src="https://img.shields.io/npm/v/@raymondchins/agentmap" alt="npm"></a>
  <a href="https://github.com/raymondchins/agentmap/actions/workflows/ci.yml"><img src="https://github.com/raymondchins/agentmap/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://github.com/raymondchins/agentmap/blob/HEAD/LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="License: MIT"></a>
  <a href="#"><img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen" alt="node >= 20"></a>
  <a href="#"><img src="https://img.shields.io/badge/runtime%20deps-1-2EE0A8" alt="1 runtime dependency"></a>
  <a href="#"><img src="https://img.shields.io/badge/network%20calls-0-2EE0A8" alt="zero network calls"></a>
</p>

# agentmap

### Your agent burns most of its context just finding code. This gives it the answer in one line.

```bash
npx @raymondchins/agentmap --relates lib/db/schema.ts
```

```
relates: lib/db/schema.ts  (pr 0.073744)
dependents (21): lib/types.ts, lib/utils.ts, lib/db/queries.ts,
                 components/chat/message.tsx, app/(chat)/api/chat/route.ts, …
```

Every file on that list really imports it. `grep` gets **40% of them wrong**.

---

## 💸 What it saves

Token cost of the hidden first step in every agent task — *find the relevant code* —
on a real 154-file Next.js app ([vercel/ai-chatbot](https://github.com/vercel/ai-chatbot), sha `2becdb4`):

| The agent needs to know… | Reading files | agentmap | Saved |
|---|---:|---:|:---:|
| Does a helper for this already exist? | 14,740 | 19 | **99.9%** |
| Load the whole repo into context | 150,281 | 1,127 | **99.3%** |
| What breaks if I change this file? | 81,038 | 616 | **99.2%** |
| Where is this symbol defined? | 1,950 | 20 | **99%** |
| What files make up this feature? | 6,121 | 1,025 | **83.3%** |
| Give me a repo overview | 3,065 | 1,127 | **63.2%** |
| What does this one file import? | 583 | 517 | **11.3%** |
| **All 7 combined** | **257,778** | **4,451** | **98.3%** |

Holds on [zod](https://github.com/colinhacks/zod) too (367 files, **99.2%**) and
[taxonomy](https://github.com/shadcn-ui/taxonomy) (125 files, **96.0%**).
Captured output, pinned shas → [`benchmark/RESULTS.md`](https://github.com/raymondchins/agentmap/blob/HEAD/benchmark/RESULTS.md)

## 🎯 …and it's still right

Fewer tokens is worthless if they're the wrong ones. Separate eval, ground truth derived
live from real repos:

| | agentmap | `git grep` |
|---|:---:|:---:|
| What depends on this file? | **100%** precision | 59.9% precision |
| Where is this defined? *(top-1)* | **100%** | 32% |
| Where is this defined? *(top-3)* | **100%** | 80% |
| Tokens to find a definition | **1.9× fewer** | — |

<sub>n=42 dependents / n=75 definitions across <a href="https://github.com/colinhacks/zod">zod</a>, <a href="https://github.com/pmndrs/zustand">zustand</a>, <a href="https://github.com/honojs/hono">hono</a>. Re-run: <code>npm run eval</code> · method → <a href="https://github.com/raymondchins/agentmap/blob/HEAD/EVAL.md">EVAL.md</a></sub>

---

## ⚡ The five commands

| You want | Run | Saves |
|---|---|:---:|
| "Do we already have this?" | `agentmap --find formatCurrency` | 99.9% |
| "What breaks if I touch this?" | `agentmap --relates lib/auth.ts` | 99.2% |
| "Where is this defined?" | `agentmap --find ChatMessage` | 99% |
| "Give me the repo, cheap" | `agentmap --map --tokens 2000` | 99.3% |
| Don't want to pick? | `agentmap --any <anything>` | — |

`--any` routes it for you: file → symbol → feature → live content search.

Cold build **~1.2s**. Cached query **~0.1s**. No server, no vector DB, no API key.

---

## 🔌 Setup

```bash
npx @raymondchins/agentmap --install-hooks   # rebuild on commit + steer the agent to the map
npx @raymondchins/agentmap --install-skill   # Claude Code · Cursor · Codex · Gemini · OpenCode · Copilot
```

Most repo-map tools stop at building the map. These two hooks are why it stays useful: the
map **rebuilds itself after every commit**, and the agent gets **nudged to the map the
moment it reaches for a dependency-shaped grep**. Claude Code users can get both from the
[plugin](#4-claude-code-plugin-one-command-bundle).

> **100% local.** Zero network calls, zero telemetry — not one `fetch`/`http` in the source.
> ⚠️ Install the **scoped** name; unscoped `npx agentmap` is someone else's package.

<details>
<summary>Where the cache lives, and how it stays fresh</summary>

<br>

First run caches to `.claude/agentmap/map.json` (`--install-hooks` gitignores it). Later runs
serve that cache **only** on a clean tree at an unchanged `HEAD` — with uncommitted
`.ts/.tsx/.js/…` edits it silently rebuilds, so you never query a stale snapshot.

```
$ npx @raymondchins/agentmap
agentmap: 154 files | 4 features | top hub: lib/utils.ts (deg 52, pr 0.105171)
```

From a checkout, every command also works as `node agentmap.mjs …`.

</details>

---

## 🧠 Why the answers are right

Built on **`ts-morph` — the real TypeScript compiler**, not text matching or tree-sitter
guessing. It resolves `tsconfig` path aliases, `vite`/`webpack` aliases, `#imports` subpaths,
and monorepo workspaces. Where `grep` sees a string, agentmap sees the resolved module.

That's also why barrels don't fool it: `export * from "./x"` looks identical to a real
definition to a text search, so your agent edits the re-export and changes nothing. agentmap
follows the chain and names the file that actually declares it.

<details>
<summary><b>The honest asterisks</b> — read these before quoting a number</summary>

<br>

- **The win scales with the work.** The 63% and 11% rows are the floor. A *trivial
  single-file* lookup can cost **more** than `cat` + `grep` — taxonomy's file-import task hit
  **−313%**, and it stays in the table.
- **The 98.3% headline is carried by its two biggest rows** — repo dump (150,281 → 1,127) and
  blast radius (81,038 → 616). Drop the repo dump and it's **96.9%**; drop both and it's
  **89.8%** here, **93.7%** pooled across all three repos, and **73.1%** on the smallest one.
  All of those are real — they answer different questions. The headline is the common worst
  case: an agent dumping the repo at session start.
- **`--relates` returns the full blast radius**, so it costs *more* than a bare `grep -l` file
  list. That's why the same command reads as 99.2% *saved* in the benchmark and *more
  expensive* in the eval: the benchmark's baseline is an agent that `cat`s all 65 dependent
  files, the eval's is a file list nobody reads. Against the list, agentmap trades tokens for
  precision — 100% vs 59.9%, so ~4 in 10 files on the grep list don't belong.
  Complete-and-correct over short-and-wrong, but it is a trade → [EVAL.md](https://github.com/raymondchins/agentmap/blob/HEAD/EVAL.md).
- **Numbers are context-token volume**, not answer quality or wall-clock.
- **Token counts are estimates** (`chars / 4`), applied identically to both sides.
- **TypeScript/JavaScript only** (+ Vue SFC) — see [Scope & limitations](#scope--limitations).

</details>

---

## Why it's different

Many "repo context" tools are a photocopy: they dump your repository (or a slice of it) into
the prompt once and walk away — the copy goes stale the moment you edit a file, and nothing
makes the agent actually read it. agentmap is queryable and ranked instead: the agent
interrogates it flag-by-flag rather than swallowing a dump.

It also reports an `edgeCoverage` map-health signal and warns loudly when a repo's imports
mostly *don't* resolve, so a broken map is never quietly framed as success.

The self-refreshing side — a post-commit rebuild plus a `PreToolUse` hook that steers the agent
to the map before it serial-greps — is genuinely useful, but it isn't unique: **CodeGraph**
([colbymchenry/codegraph](https://github.com/colbymchenry/codegraph), ~62k★ (2026-07-26)) ships a native
OS-event file watcher (FSEvents/inotify) with debounced auto-sync and an installer that
auto-configures eight agent CLIs. agentmap's honest edge over the multi-language graph tools is
narrower and sharper: **TS/JS resolution the others approximate, with a published accuracy eval.**

| | **agentmap** | [Aider repo map](https://github.com/Aider-AI/aider) | [RepoMapper](https://github.com/nuptcode/repomapper) | [Repomix](https://github.com/yamadashy/repomix) | [code2prompt](https://github.com/mufeedvh/code2prompt) |
| --- | --- | --- | --- | --- | --- |
| **Ranking algorithm** | Personalized PageRank (file + symbol graphs) | PageRank (graph ranking) | Importance heuristics | None (file order) | None (file order) |
| **Languages** | TS/JS + Vue SFC (via ts-morph) | Many (tree-sitter) | Many (tree-sitter) | Language-agnostic (text) | Language-agnostic (text) |
| **Token-budget output** | Yes — `--map [--tokens N]` ranked digest | Yes (built into Aider's context) | Partial | Yes (size caps) | Yes (templates/caps) |
| **TS/JS resolution depth** | **Compiler-grade — `tsconfig` paths + `vite`/`webpack` alias + `#imports` + workspaces (ts-morph)** | Basename/regex heuristics | Basename/regex heuristics | N/A (text) | N/A (text) |
| **Retrieval-accuracy eval** | **Yes — published [`EVAL.md`](https://github.com/raymondchins/agentmap/blob/HEAD/EVAL.md) vs live ground truth** | No | No | No | No |
| **Agent-loop wiring** | Yes — post-commit auto-refresh + PreToolUse hook | In-process (Aider only) | No | MCP server (no auto-refresh, no nudge) | No |
| **Dependencies** | `ts-morph` only | Python + tree-sitter stack | Python + tree-sitter | Node | Rust binary |
| **Install** | `npx @raymondchins/agentmap` | `pip install aider-chat` | `pip install` | `npx`/global | `cargo`/binary |

<sub>Comparison as of <b>2026-07-27</b>, from each project's own docs. These are moving targets — if a
cell is out of date, that's a bug: <a href="https://github.com/raymondchins/agentmap/issues">open an issue</a>.</sub>

What that table is **not** claiming: agentmap is TS/JS-only (the others are multi-language),
and it's a **file-level import graph**, not a full call-site/reference resolver (see
[Scope & limitations](#scope--limitations)). The differentiators are narrow and honest:
**(1)** compiler-grade TS/JS resolution (aliases, `vite`/`webpack`, `#imports`, workspaces) with a
published accuracy eval, and **(2)** the `--any` router. The agent-loop wiring is real and
convenient but **not** unique — [CodeGraph](https://github.com/colbymchenry/codegraph) and others
auto-sync and auto-configure agent CLIs too; we don't claim it as a moat.

---

## The agent loop (staying current, staying used)

A common failure of repo-map tools: they build a beautiful map, and then the
agent forgets it exists and greps anyway. A map the agent doesn't open is just dead weight.

agentmap closes that loop. Two hooks (in [`./hooks/`](https://github.com/raymondchins/agentmap/blob/HEAD/hooks/)) do the work: the map
**refreshes itself after every commit**, and the agent gets **nudged to query it before it
serial-greps**. You wire it once — then it stays current on its own, and stays used.

> This wiring is table stakes, not the moat — [CodeGraph](https://github.com/colbymchenry/codegraph)
> and other tools also auto-sync (via native OS file watchers) and auto-configure agent CLIs.
> agentmap ships it because it's genuinely useful; the actual point of agentmap is the
> **compiler-grade TS/JS accuracy** the map is built on.

### 1. Auto-refresh on commit

[`hooks/post-commit`](https://github.com/raymondchins/agentmap/blob/HEAD/hooks/post-commit) rebuilds `.claude/agentmap/map.json` after each
commit, detached + silenced so it never slows the commit. It skips during
rebase/merge/cherry-pick and no-ops if Node is missing.

The hooks ship inside the npm package. The simplest setup:

```bash
npx @raymondchins/agentmap --install-hooks
```

This copies `hooks/post-commit` into `.git/hooks/`, sets it executable, ensures
`.claude/agentmap/` is in `.gitignore`, and **auto-wires the `PreToolUse` nudge
hook into `.claude/settings.json`** (merge-safe + idempotent) so map enforcement is
on by default — no manual paste. Manual alternative for just the post-commit hook:

```bash
# from your repo root
cp hooks/post-commit .git/hooks/post-commit
chmod +x .git/hooks/post-commit
```

The hook resolves the builder to the **installed** package — `node_modules/.bin/agentmap`,
a PATH `agentmap` binary verified to be `@raymondchins/agentmap`, then
`npx @raymondchins/agentmap`. It never runs a repo-local `./agentmap.mjs` unless you opt in
with `AGENTMAP_HOOK_ALLOW_LOCAL=1` (for developing agentmap itself), so an
attacker-planted `agentmap.mjs` can't execute on your next commit.

### 2. Force the agent to use it — `PreToolUse` hook

[`hooks/agentmap-nudge.mjs`](https://github.com/raymondchins/agentmap/blob/HEAD/hooks/agentmap-nudge.mjs) is a **non-blocking** hook for
Claude Code that covers **both** the `Grep` tool and raw Bash text-searchers
(`grep`/`rg`/`egrep`/`fgrep`/`ag`/`ack`). When either looks like a dependency /
who-imports / component-usage / reuse / where-is-symbol search, it injects a reminder
steering the agent to `agentmap --any` first. It never denies the call, and stays silent
for raw-string / Tailwind-class / lowercase-HTML-tag sweeps and for pipe-filtered commands
like `ps aux | grep node` — so it's high-signal, not nagging.

**Fires on:** `import`/`require`/`export`/`from '...'` patterns, JSX component tags
(`<Hero`, `<ProviderCard`), explicit intent words (`where is`, `who imports`, `reuse`,
`existing component`), and — in both the `Grep` tool and the Bash branch — bare multi-hump
PascalCase identifiers (`ProviderCard`, `TopProviders`) that almost always mean "where is
this symbol / who uses it". The Bash branch additionally only fires when the searcher is the *primary* command (at the start,
or after `;`/`&&`); piped log-filters stay silent.

All four nudge/gate variants (this one, Codex, Gemini, OpenCode) also **self-gate on
project presence**: since they ship at user/global scope too (plugin bundle, `~/.gemini`,
`~/.codex`, `~/.config/opencode`), they walk up from the tool call's cwd to the
filesystem root looking for `node_modules/@raymondchins/agentmap` or a built
`.claude/agentmap/map.json` before doing anything else, so a repo with no agentmap stays
silent instead of nagging (or, for Codex, denying a grep it has no business denying).

`--install-hooks` writes both matchers into `.claude/settings.json` for you (merge-safe —
preserves existing settings, won't duplicate on re-run). The single hook file dispatches
internally on `tool_name`. For reference, or to wire it by hand:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Grep",
        "hooks": [{ "type": "command", "command": "node ./hooks/agentmap-nudge.mjs" }]
      },
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "node ./hooks/agentmap-nudge.mjs" }]
      }
    ]
  }
}
```

That's the "forced to use it" in the tagline: the map stays current on its own, and the
agent is steered to it the moment it reaches for a dependency-shaped grep or Bash search.

### 3. Agent skills (Cursor, Claude Code, Codex, OpenCode, Gemini, Antigravity, Copilot)

```bash
npx @raymondchins/agentmap --install-skill
```

…or grab just the skill (no agentmap flags) via the [`skills`](https://github.com/vercel-labs/skills)
CLI — agentmap ships the `skills/agentmap/SKILL.md` layout it expects:

```bash
npx skills add raymondchins/agentmap
```

`--install-skill` copies packaged **SKILL.md** files and a **Cursor rule** (`.cursor/rules/agentmap.mdc`,
`alwaysApply: true`) into the current repo or global agent directories. Paths follow
each platform's official skill-directory conventions. Options:

```bash
agentmap --install-skill --platform cursor           # Cursor rule + .cursor/hooks.json shell gate (project)
agentmap --install-skill --platform claude           # .claude/skills/agentmap/SKILL.md
agentmap --install-skill --platform codex            # .codex/skills/ (project) or ~/.codex/skills/ (global)
agentmap --install-skill --platform opencode         # .opencode/skills/ (project) or ~/.config/opencode/skills/ (global)
agentmap --install-skill --platform gemini           # .gemini/skills/ (project); global ~/.gemini/skills/ (Windows global: ~/.agents/skills/)
agentmap --install-skill --platform antigravity      # .agents/skills/ (project) or ~/.gemini/config/skills/ (global)
agentmap --install-skill --platform copilot          # .copilot/skills/ or ~/.copilot/skills/
agentmap --install-skill --global --platform claude  # ~/.claude/skills/...
agentmap --install-skill --platform agents           # legacy .agents/skills/ (project or global); excluded from default `all`
agentmap --install-skill --dry-run                   # preview paths, no writes
```

`--platform all` installs: claude, cursor, codex, opencode, gemini, antigravity, copilot (not legacy `agents`).

Some platforms also get **always-on** docs and hooks in the same command:

| `--platform` | Skill | Also installs (project) | Global docs |
|--------------|-------|-------------------------|-------------|
| `gemini` | `.gemini/skills/…/SKILL.md` | `GEMINI.md` + `.gemini/settings.json` BeforeTool nudge | `~/.gemini/GEMINI.md` |
| `codex` | `.codex/skills/…/SKILL.md` | `AGENTS.md` merge-safe `<!-- agentmap:begin/end -->` block | `~/.codex/AGENTS.md` |
| `opencode` | `.opencode/skills/…/SKILL.md` | `AGENTS.md` + `.opencode/plugins/agentmap-nudge.js` | `~/.config/opencode/AGENTS.md` |
| `cursor` | `.cursor/rules/agentmap.mdc` | `.cursor/hooks.json` `beforeShellExecution` gate + `.cursor/hooks/agentmap-cursor-nudge.mjs` | — (project-scope only) |

Codex and OpenCode share one repo-root `AGENTS.md` on project install. Existing content outside the marked block is preserved.

Pair with `--install-hooks` (Claude Code) or `--mcp` (Cursor MCP).

### 4. Claude Code plugin (one-command bundle)

Prefer the plugin over `--install-skill`/`--install-hooks` if you're on Claude Code and
want the skill, the `PreToolUse` grep/Bash nudge, and the stdio MCP server in a single
install that auto-updates:

```bash
# in Claude Code
/plugin marketplace add raymondchins/agentmap
/plugin install agentmap@agentmap
```

The plugin bundles: the packaged **SKILL.md**, the **PreToolUse nudge** (both the `Grep`
tool and Bash text-searchers, via `${CLAUDE_PLUGIN_ROOT}`), and the **stdio MCP server**
(`npx -y @raymondchins/agentmap --mcp`, so `ts-morph` is fetched on demand — the plugin
cache ships no `node_modules`).

> **One thing the plugin can't do: install the git `post-commit` hook.** Claude Code
> plugins can't write into `.git/hooks/`, so the auto-refresh-on-commit still needs a
> one-time `npx @raymondchins/agentmap --install-hooks` in each repo (it also wires the
> nudge into `.claude/settings.json`, harmlessly redundant with the plugin's copy).
> Without it the map still rebuilds on any dirty query — you just lose the commit-time
> refresh.

### Onboarding by platform

Enforcement isn't uniform — some CLIs get a **live hook** that actively steers grep to
agentmap, some get an **MCP server** the agent can call, and some are **docs-only** (a
skill/rule the agent may or may not consult). Honest matrix:

| Platform | Install | Enforcement | Known gaps |
|----------|---------|-------------|------------|
| **Claude Code** | `/plugin install agentmap@agentmap` (or `--install-hooks`) | **live hook** — `PreToolUse` nudge on `Grep` + Bash searchers | non-blocking (never denies grep); bare-symbol `Grep` nudge requires the #3 hook fix |
| **Gemini CLI** | `--install-skill --platform gemini` | **live hook** — `.gemini/settings.json` nudge | fires on `BeforeTool` and emits a top-level `systemMessage`; Gemini parses and then **drops** `hookSpecificOutput.additionalContext` on `BeforeTool`, which is why the nudge used to vanish silently |
| **OpenCode** | `--install-skill --platform opencode` | **log-only** — `.opencode/plugins/agentmap-nudge.js` writes to the log, does not inject context | plugin can't steer the model; relies on the `AGENTS.md` block being read |
| **Cursor** | `--install-skill --platform cursor` + `.cursor/mcp.json` (below) | **live gate** — `.cursor/hooks.json` `beforeShellExecution` hook, plus the `alwaysApply` rule and the MCP server | denies only high-confidence structural greps; allow-fallback for logs/pipes/non-TS-JS; `AGENTMAP_CURSOR_GATE=0` bypasses; project-scope only |
| **Codex CLI** | `--install-skill --platform codex` | **live gate** — `.codex/config.toml` PreToolUse hook | denies only high-confidence structural greps; allow-fallback for logs/pipes/non-TS-JS; `AGENTMAP_CODEX_GATE=0` bypasses; needs a trusted dir + Codex hooks-GA |
| **Copilot CLI** | `--install-skill --platform copilot` | **docs-only** — `.copilot/skills/` | same as Codex — no live hook yet |

**Cursor MCP — copy-paste `.cursor/mcp.json`** (Cursor's `--mcp` wiring is a documented
dead-end otherwise; drop this at your repo root):

```json
{
  "mcpServers": {
    "agentmap": {
      "command": "npx",
      "args": ["-y", "@raymondchins/agentmap", "--mcp"]
    }
  }
}
```

Then Cursor exposes the 11 query tools (`any`, `find`, `relates`, `map`, `hubs`,
`features`, `feature`, `symbols`, `search`, `callers`, `calls`). Run `agentmap --doctor` any time to see what's wired
vs missing.

### Uninstall

agentmap only writes files into your repo/home — remove them to fully uninstall. `agentmap
--doctor` lists every path it wrote, and every docs merge lives inside an
`<!-- agentmap:begin/end -->` (or `# agentmap:begin/end`) fence, so deleting just that block
leaves the rest of your `AGENTS.md` / `GEMINI.md` intact.

| Platform | Remove |
|----------|--------|
| Claude Code | `.claude/skills/agentmap/` + the agentmap `PreToolUse` block in `.claude/settings.json` |
| Cursor | `.cursor/rules/agentmap.mdc`, `.cursor/hooks/agentmap-cursor-nudge.mjs`, the `beforeShellExecution` entry in `.cursor/hooks.json`, + the `agentmap` entry in `.cursor/mcp.json` |
| Codex | `.codex/skills/agentmap/`, the `# agentmap:begin/end` block in `.codex/config.toml`, `.codex/hooks/agentmap-codex-nudge.mjs`, and the fenced block in `AGENTS.md` |
| OpenCode | `.opencode/skills/agentmap/`, `.opencode/plugins/agentmap-nudge.js`, the `AGENTS.md` block |
| Gemini | `.gemini/skills/agentmap/`, `.gemini/hooks/agentmap-nudge.mjs`, the `BeforeTool` hook in `.gemini/settings.json`, the `GEMINI.md` block |
| All | map cache `rm -rf .claude/agentmap/`; npm devDep `npm rm @raymondchins/agentmap`; the agentmap block in `.git/hooks/post-commit` |

### Troubleshooting

| Symptom | Cause / fix |
|---------|-------------|
| `features (0)` | `--features` only detects Next.js `app/` routes; a TanStack `src/routes/` repo legitimately shows 0. Use `--map` / `--symbols` instead. |
| Empty or wrong map | Usually no `tsconfig.json` / resolvable aliases in the target repo, so no edges resolved — run `agentmap --doctor` and check `edgeCoverage` in `--json`. |
| Stale-looking results | By design the map rebuilds from disk on a dirty tree / SHA mismatch. Force a rebuild by just running `agentmap`. |
| Codex/Gemini nudge never fires | Codex's gate is opt-in — set `[features] hooks = true` in `.codex/config.toml` (`AGENTMAP_CODEX_GATE=0` disables it). Gemini needs the `BeforeTool` hook that `--install-skill` writes. |
| Cursor gate blocks a grep you meant | Re-run the same command with `AGENTMAP_CURSOR_GATE=0` prefixed. It only denies high-confidence structural searches; logs, pipes and non-structural sweeps already fall through. Remove the `beforeShellExecution` entry from `.cursor/hooks.json` to turn it off for good. |
| Cursor gate never fires | It is project-scope only, so it must be installed from the repo root (`--install-skill --platform cursor`, not `--global`), and Cursor reads `.cursor/hooks.json` at startup — restart Cursor after installing. |
| Installed the wrong `agentmap` | This is **`@raymondchins/agentmap`** (npm scope) — not the unrelated unscoped `agentmap` packages. |
| Cursor MCP tools missing | `--mcp` doesn't auto-wire Cursor; add the copy-paste `.cursor/mcp.json` from the matrix above and restart Cursor. |
| Hook works in your shell, not in the agent | Almost always **nvm**. Your interactive shell sources `~/.nvm/nvm.sh`; the git hook and the agent's tool runner do not, so `node` isn't on their `PATH`. Point the hook at an absolute node (`which node`) or install a system-wide node. |
| `JavaScript heap out of memory` | Raise the ceiling — the parse peaks and there is no in-process warning that can fire in time (the process dies inside a single call, with heap use still at ~40% one sample earlier). Re-run as `NODE_OPTIONS=--max-old-space-size=8192 npx @raymondchins/agentmap`. Repo **size is not the axis**: measured, a 252-file Next.js app peaks at 683 MB while 4,000 dependency-free files peak at 756 MB, because the dependency `.d.ts` closure (~300 MB, ~1,800 extra program files on a 393-file app) dominates. A small repo with heavy `@types` can need more than a large plain one. |
| Skill file looks out of date | Each installed skill dir carries a `.agentmap_version`. `agentmap --doctor` compares it against the running version and flags the drift; `--install-skill` again overwrites it. |
| `0 files mapped` | agentmap indexes `git ls-files --cached --others --exclude-standard`, so uncommitted files *are* included but **`.gitignore`d ones are not** — a source tree matched by an ignore rule maps to nothing, as does a directory that is not a git repo at all. Confirm with `git ls-files --others --exclude-standard \| head`. |

---

## The `--any` router

Don't want to learn eight flags? You don't have to. Throw anything at `--any` — a filename, a
function, a feature, even a raw string — and it figures out what you meant, returning the first
layer that hits:

```
--any <query>
   │
   ├─ 1. FILE     exact path → unique basename → unique substring
   ├─ 2. SYMBOL   exported name contains the query (across all files)
   ├─ 3. FEATURE  app/-router feature name contains the query
   └─ 4. CONTENT  live `git grep` (tracked + untracked) — never stale
```

Layers 1–3 read the cached structural map (fast, ranked). Layer 4 is a **live disk read**
via `git grep -F`, so raw strings, copy, Tailwind classes, and config values the structural
graph never indexes still resolve instead of coming up empty.

**Symbol hit** (query resolved to a symbol → full block):

```
$ node agentmap.mjs --any cn
[structure] 1 symbol, 0 feature match for "cn"
  lib/utils.ts → cn (FunctionDeclaration)
```

**Ambiguous file hit** (query matched multiple files → narrow it):

```
$ node agentmap.mjs --any utils
[structure] "utils" matched 3 files — narrow it:
  lib/utils.ts
  lib/db/utils.ts
  tests/prompts/utils.ts
```

**Content fallback** (no file/symbol/feature match → live git-grep):

```
$ node agentmap.mjs --any streamText
[content] 13 lines:
app/(chat)/api/chat/route.ts:8:  streamText,
app/(chat)/api/chat/route.ts:194:        const result = streamText({
artifacts/code/server.ts:1:import { streamText } from "ai";
artifacts/code/server.ts:18:    const { fullStream } = streamText({
artifacts/code/server.ts:40:    const { fullStream } = streamText({
artifacts/sheet/server.ts:1:import { streamText } from "ai";
artifacts/sheet/server.ts:11:    const { fullStream } = streamText({
```

---

## Commands

Every snippet below is **representative output** (long lists trimmed) from running agentmap against the public
154-file Next.js repo [vercel/ai-chatbot](https://github.com/vercel/ai-chatbot) (sha 2becdb4).

### `--any <q>` — the router (file → symbol → feature → live content)

See [The `--any` router](#the---any-router) above. Default first move for any
"where/what/who" question.

### `--find <q>` — reuse-before-rebuild symbol search

Find every symbol whose name contains the query — exported symbols **plus** non-exported
top-level declarations. Use it before writing a new util or component to check what already
exists (a private helper counts as reusable too).

```
$ node agentmap.mjs --find Message
find "Message": 55 match
  hooks/use-messages.tsx → useMessages (FunctionDeclaration)
  lib/errors.ts → getMessageByErrorCode (FunctionDeclaration)
  lib/types.ts → messageMetadataSchema (VariableDeclaration)
  lib/types.ts → MessageMetadata (TypeAliasDeclaration)
  lib/types.ts → ChatMessage (TypeAliasDeclaration)
  lib/utils.ts → convertToUIMessages (FunctionDeclaration)
  lib/utils.ts → getTextFromMessage (FunctionDeclaration)
  tests/helpers.ts → generateTestMessage (FunctionDeclaration)
  app/(chat)/actions.ts → generateTitleFromUserMessage (FunctionDeclaration)
  …
```

**Barrels don't hide the real file.** When a match is reached through a re-export
(`export * from "./x"`, or a named/renamed re-export, at any depth), the output names the
file that actually declares it. The TypeScript checker resolves the chain, so this works
where a name search can't — `rg` sees the barrel and the origin as two equal hits with no
way to tell which one you can edit. An origin outside the repo reports
`→ defined outside the repo`; a `node_modules` path is never printed.

```
$ node agentmap.mjs --find useComposedRefs     # radix-ui/primitives@579c5b84
find "useComposedRefs": 3 match
  packages/react/compose-refs/src/index.ts → useComposedRefs (FunctionDeclaration) → defined in packages/react/compose-refs/src/compose-refs.tsx
  packages/react/compose-refs/src/compose-refs.tsx → useComposedRefs (FunctionDeclaration)
  packages/react/radix-ui/src/internal.ts → useComposedRefs (?)
```

In `--json` this is `definedIn: "<path>"` or `external: true` on the match, present only
when the entry is a pass-through — a real definition carries neither.

### `--search <q>` — BM25 lexical search for vague queries

When you don't know the exact symbol name — the query an agent actually types — `--search`
ranks symbols by **BM25 lexical relevance** over split-identifier tokens (the symbol name,
its file's path segments, feature, and kind), fused with file PageRank so a strong hit in an
important file wins ties. No embeddings, no vector DB; the index is built into `map.json`.
The same ranker is wired into `--any` as a rung that fires **only** when exact file/symbol
matching found nothing, so exact routing is unchanged.

```
$ node agentmap.mjs --search "auth retry logic"
search "auth retry logic": 3 match
  src/authRetry.ts → retryWithBackoff (FunctionDeclaration)  [6.83]
  …
```

Stopwords (`the`, `that`, `of`, …) are dropped, so `--search "the function that dedupes
symbols"` works. Also available as the `search` MCP tool.

### `--relates <path>` — blast radius + transitive relevance

The file's own block (exports / imports / direct dependents) **plus** a random-walk
relevance list (personalized PageRank on the bidirectional import graph) — the files most
related to the target, transitively, not just its direct importers.

```
$ node agentmap.mjs --relates lib/db/schema.ts
relates: lib/db/schema.ts  (pr 0.073744)
exports (14): user(VariableDeclaration), User(TypeAliasDeclaration), chat(VariableDeclaration), Chat(TypeAliasDeclaration), message(VariableDeclaration), DBMessage(TypeAliasDeclaration), …
imports (0): —
dependents (21): hooks/use-active-chat.tsx, lib/types.ts, lib/utils.ts, components/chat/artifact.tsx, components/chat/message.tsx, lib/db/queries.ts, app/(chat)/api/chat/route.ts, …
related (random-walk relevance):
  lib/utils.ts (0.0476)
  lib/types.ts (0.0376)
  components/chat/artifact.tsx (0.0372)
  components/chat/icons.tsx (0.0264)
  components/chat/message.tsx (0.0237)
  lib/db/queries.ts (0.0225)
  app/(chat)/api/chat/route.ts (0.0218)
  …
```

**Type-only dependencies are listed separately, not silently dropped.** `dependents` means
"would break at runtime". A file imported only via `import type` has no runtime dependents
at all — but renaming or deleting its exports still breaks every consumer at compile time.
Those appear under `type-only dependents`, so a types module stops reading like an orphan:

```
$ node agentmap.mjs --relates lib/types.ts     # vercel/chatbot@c2f8235e
relates: lib/types.ts  (pr 0.002898)
exports (7): messageMetadataSchema(VariableDeclaration), MessageMetadata(TypeAliasDeclaration), …
imports (0): —
dependents (0): —
type-only imports (6): components/chat/artifact.tsx, lib/ai/tools/create-document.ts, …
type-only dependents (23): hooks/use-active-chat.tsx, hooks/use-auto-resume.ts, lib/utils.ts, …
```

22.4% of that repo's import statements are type-only. The fields are
`typeOnlyImports` / `typeOnlyDependents` in `--json`, omitted entirely when empty, and they
never enter PageRank, `--hubs`, symbol ranking or `--export` — the ranking graph stays a
runtime graph.

For a file carrying a React Server Components directive prologue, the output adds one more
line — `boundary: 'use client' (client component)` or `boundary: 'use server' (server module/actions)`
(`rsc: 'client' | 'server'` in `--json`) — right after `dependents`. This is additive and
optional: repos with no `'use client'`/`'use server'` directives never see the line.

### `--callers <sym>` — compiler-accurate call graph (experimental)

Who actually **calls** a symbol, resolved by the TypeScript language service (`ts-morph`
`findReferencesAsNodes`) — not tree-sitter name-matching. This is symbol-level blast radius:
a type-position mention (`typeof foo`), a re-export, a bare value reference (`const x = foo`),
or a same-named private local in another file is a *different* symbol and is never
mis-attributed. `--in <path>` disambiguates a name defined in more than one file (exported
definitions win over same-named private locals); results are ranked by caller-file PageRank
and capped.

```
$ node agentmap.mjs --callers getMessageByErrorCode
callers of getMessageByErrorCode  [lib/errors.ts]: 3 call sites
  app/(chat)/api/chat/route.ts:88 → POST
  lib/db/queries.ts:142 → saveMessage
  components/chat/message.tsx:57 → PureMessage
```

**JSX counts as a call site.** `<Foo />` compiles to `React.createElement(Foo, …)` (classic
runtime) or `jsx(Foo, …)` (automatic runtime) — either way it's an invocation, so a
component's callers include everywhere it's rendered, not just plain `foo()` calls.
`<Foo.Bar />` resolves to `Bar`, not the `Foo` namespace; `<Foo>...</Foo>` counts once (the
closing tag isn't a second call site); an intrinsic tag (`<div>`) resolves to nothing
in-project and produces no edge.

```
$ node agentmap.mjs --callers Button
callers of Button  [components/ui/button.tsx]: 25 call sites
  components/ai-elements/message.tsx:93 → MessageAction
  components/ai-elements/message.tsx:263 → MessageBranchPrevious
  components/ui/sidebar.tsx:249 → SidebarTrigger
  components/ui/alert-dialog.tsx:158 → AlertDialogAction
  components/ui/dialog.tsx:72 → DialogContent
  …
```

Before 0.17.0, JSX wasn't a recognized call shape at all, so that same query returned
**0 call sites** — a plain `rg '<Button'` beat the tool outright. Captured on
[vercel/ai-chatbot](https://github.com/vercel/ai-chatbot) at `c2f8235`; reproduce it by
running the query against that commit.

A deliberate **deep query**: it lazily spins up the TS type-checker (a few seconds on a large
repo) *only* when invoked — the map build and every other query never pay that cost, and
nothing is persisted. Accurate on statically-resolvable calls; dynamic dispatch, reflection,
and string-keyed access are beyond any static tool. Also available as the `callers` MCP tool.

### `--calls <sym>` — outgoing call graph (experimental)

The companion to `--callers`: which in-project symbols a symbol **invokes**. Each call and
`new X()` site inside its body is resolved by the type checker (`getDefinitionNodes`), which
follows an imported / re-exported binding through to the real declaration — so a same-named
local elsewhere is never confused for the imported one. `node_modules` and TypeScript
built-ins (`console.log`, `Array.map`, …) are excluded; dynamic dispatch, computed member
access, and higher-order callees are honestly skipped.

```
$ node agentmap.mjs --calls extractFacts
extractFacts calls  [agentmap.mjs]: 15 in-project targets
  agentmap.mjs:756 → makeProject (FunctionDeclaration)
  agentmap.mjs:944 → rel (VariableDeclaration)
  agentmap.mjs:952 → excluded (VariableDeclaration)
  …
```

**JSX counts as an outgoing call too**, for the mirror-image reason: a component whose body
is nothing but `return <Container><Sidebar /></Container>` has no `CallExpression` in it, so
before this fix it reported **zero** outgoing calls even though it clearly depends on both.
Each `<Foo />` / `<Foo>...</Foo>` in the body now resolves to its target declaration the same
way a plain call does — the printed `(kind)` is the target's own declaration kind
(`FunctionDeclaration`, etc.), not "JSX", since resolution is unchanged, only call-site
detection is:

```
$ node agentmap.mjs --calls AppSidebar
AppSidebar calls  [components/chat/app-sidebar.tsx]: 35 in-project targets
  components/ui/tooltip.tsx:21 → Tooltip (FunctionDeclaration)
  components/ui/tooltip.tsx:33 → TooltipContent (FunctionDeclaration)
  components/ui/sidebar.tsx:144 → Sidebar (FunctionDeclaration)
  components/ui/sidebar.tsx:379 → SidebarContent (FunctionDeclaration)
  …
```

Same repo and commit: this returned **6** targets before 0.17.0 — only the plain hook and
helper calls — and 35 after, because the 29 components it renders now count too.

Same lazy, out-of-band model as `--callers` (builds a Project only on the query, nothing
persisted). Also the `calls` MCP tool. JSX closes a real gap here — it doesn't change what's
still out of reach: the `node_modules`/dynamic-dispatch/computed-member/higher-order limits
above still apply.

**Going transitive — `--depth N`.** Both `--callers` and `--calls` accept `--depth N`
(default 1, max 5) for an N-hop closure: `--callers foo --depth 3` is the transitive
blast radius ("everything that reaches `foo`, up to 3 hops"); `--calls foo --depth 3` is
the dependency cone ("everything `foo` pulls in"). It BFS-traverses the same single warm
Project — no extra build — with cycle detection and node caps so a hub can't explode; each
result is tagged with its `depth` and a `via` parent. `--depth 1` is the default single-hop
query.

```
$ node agentmap.mjs --callers leaf --depth 2
callers of leaf  [src/chain.ts]: 2 callers within depth 2
  src/chain.ts:2 → mid [depth 1]
  src/chain.ts:3 → top [depth 2]
```

### `--feature <name>` — files that make up a feature

Resolves a Next.js `app/`-router feature to its file set, plus the external files that
depend on it.

```
$ node agentmap.mjs --feature api
feature "api": 11 files
  app/(chat)/api/chat/route.ts
  app/(chat)/api/chat/schema.ts
  app/(chat)/api/document/route.ts
  app/(chat)/api/history/route.ts
  app/(chat)/api/messages/route.ts
  app/(chat)/api/models/route.ts
  app/(chat)/api/suggestions/route.ts
  app/(chat)/api/vote/route.ts
  app/(auth)/api/auth/guest/route.ts
  app/(chat)/api/files/upload/route.ts
  app/(chat)/api/chat/[id]/stream/route.ts
external dependents (0): —
```

### `--features` — list features by size

```
$ node agentmap.mjs --features
features (4):
  api (11 files)
  login (1 files)
  register (1 files)
  chat (1 files)
```

### `--affected <path>` — which tests cover this file

Walks the reverse-dependency closure and reports the test files that reach the target,
with hop distance. The useful answer is often the empty one: *nothing covers this*, which
is what you want to know **before** a risky edit, not after CI.

Type-only importers count. Changing an exported type breaks every `import type` consumer
at compile time, so the walk follows `dependents` **and** `typeOnlyDependents`.

```
$ node agentmap.mjs --affected agentmap.mjs
affected by agentmap.mjs: 3 test files (of 4 transitive dependents)
  test/doctor.test.mjs  [1 hop]
  test/pkg-imports.test.mjs  [1 hop]
  test/unit.test.mjs  [1 hop]
```

A file with no reachable test says so in words, and `--json` carries `covered: false`.

### `--routes` — the App Router route table

Every URL the repo serves and the file that serves it. **Next.js App Router only** — on any
other repo it exits 1 with an explicit `reason` rather than an empty list, so an agent can
tell "not applicable" from "nothing found".

```
$ node agentmap.mjs --routes
routes: no app/ or src/app/ directory — not a Next.js App Router project
```

### `--route <url>` — resolve a URL to the code that serves it

Goes from a bug report naming a URL straight to the handler, its layout chain, and the
server modules it can reach.

```
$ node agentmap.mjs --route /dashboard/settings
/dashboard/settings  (page)
  serves: app/dashboard/settings/page.tsx  boundary: 'client'
  layouts (outer->inner): app/layout.tsx -> app/dashboard/layout.tsx
  server modules: lib/server/settings.ts
```

### `--kind <k>` — narrow `--find` / `--search` by declaration kind

A modifier, not a command. Matched loosely and case-insensitively against the ts-morph kind
name, so `--kind function` finds `FunctionDeclaration` and `--kind type` finds
`TypeAliasDeclaration` — you never have to know the enum spelling.

```
$ node agentmap.mjs --find pagerank --kind function
find "pagerank" kind~function: 1 match
  agentmap.mjs → pagerank (FunctionDeclaration)
```

Used alone it is a usage error (exit 2) — it has nothing to narrow.

### `--hubs` — most important files (PageRank)

The files that matter most, ranked by PageRank importance (raw dependent degree shown
alongside).

```
$ node agentmap.mjs --hubs
agentmap: 154 files (sha 2becdb4)
hubs (PageRank importance):
  lib/utils.ts (deg 52, pr 0.105171)
  lib/db/schema.ts (deg 21, pr 0.073744)
  lib/types.ts (deg 23, pr 0.067589)
  components/chat/artifact.tsx (deg 15, pr 0.036882)
  components/chat/icons.tsx (deg 27, pr 0.035378)
  lib/errors.ts (deg 9, pr 0.032787)
  lib/db/queries.ts (deg 14, pr 0.030085)
  …
```

### `--symbols [N]` — top ranked symbols (Aider-style)

The most important individual symbols across the repo, ranked by the identifier graph
(defaults to 30).

```
$ node agentmap.mjs --symbols 10
top 10 ranked symbols (Aider-style):
  0.109902  lib/utils.ts → cn (FunctionDeclaration)
  0.036013  lib/types.ts → ChatMessage (TypeAliasDeclaration)
  0.025686  components/chat/artifact.tsx → ArtifactKind (TypeAliasDeclaration)
  0.022461  lib/errors.ts → ChatbotError (ClassDeclaration)
  0.021068  lib/types.ts → CustomUIDataTypes (TypeAliasDeclaration)
  0.020872  lib/db/schema.ts → Document (TypeAliasDeclaration)
  0.020555  components/ai-elements/suggestion.tsx → Suggestion (VariableDeclaration)
  0.020555  lib/db/schema.ts → Suggestion (TypeAliasDeclaration)
  0.018124  lib/db/schema.ts → DBMessage (TypeAliasDeclaration)
  0.015034  lib/errors.ts → ErrorCode (TypeAliasDeclaration)
```

`map.json` persists the top 80. Asking for more re-ranks from the cached map rather than
truncating, so `--symbols 200` really does return 200 where the repo has them. When a repo
has fewer ranked symbols than you asked for, the header says so and `--json` carries
`requested` / `shown` / `truncated`:

```
$ node agentmap.mjs --symbols 200
top 62 ranked symbols (Aider-style) — asked for 200, this repo only ranks 62:
```

### `--map [--tokens N] [--focus <path>]` — token-budgeted ranked digest

The token-budgeted digest (Aider's killer feature): a ranked, files-and-symbols summary
that fits a token budget. Default budget is 8192 (1024 with `--focus`). `--focus <path>`
personalizes the ranking toward a file you're working on.

```
$ node agentmap.mjs --map --tokens 400
# agentmap (154 files, sha 2becdb4) — focus: global, budget ~400 tok

lib/utils.ts:
  cn (FunctionDeclaration)
  generateUUID (FunctionDeclaration)

lib/types.ts:
  ChatMessage (TypeAliasDeclaration)
  CustomUIDataTypes (TypeAliasDeclaration)
  ChatTools (TypeAliasDeclaration)
  Attachment (TypeAliasDeclaration)

components/chat/artifact.tsx:
  ArtifactKind (TypeAliasDeclaration)
  UIArtifact (TypeAliasDeclaration)
  Artifact (VariableDeclaration)

lib/errors.ts:
  ChatbotError (ClassDeclaration)
  ErrorCode (TypeAliasDeclaration)

lib/db/schema.ts:
  Document (TypeAliasDeclaration)
  Suggestion (TypeAliasDeclaration)
  DBMessage (TypeAliasDeclaration)

# ~387 tokens (14 files shown)
```

Focused on a working file — the ranking re-centers on what `lib/db/queries.ts` actually touches:

```
$ node agentmap.mjs --map --focus lib/db/queries.ts --tokens 350
# agentmap (154 files, sha 2becdb4) — focus: lib/db/queries.ts, budget ~350 tok

lib/utils.ts:
  cn (FunctionDeclaration)
  generateUUID (FunctionDeclaration)
  getDocumentTimestampByIndex (FunctionDeclaration)
  fetcher (VariableDeclaration)
  getTextFromMessage (FunctionDeclaration)
  convertToUIMessages (FunctionDeclaration)
  fetchWithErrorHandlers (FunctionDeclaration)
  sanitizeText (FunctionDeclaration)

lib/db/schema.ts:
  DBMessage (TypeAliasDeclaration)
  Suggestion (TypeAliasDeclaration)
  Document (TypeAliasDeclaration)
  Chat (TypeAliasDeclaration)
  User (TypeAliasDeclaration)
  chat (VariableDeclaration)
  document (VariableDeclaration)
  message (VariableDeclaration)

lib/errors.ts:
  ChatbotError (ClassDeclaration)
  ErrorCode (TypeAliasDeclaration)

# ~324 tokens (8 files shown)
```

### `--print` — full map as JSON

Dumps the cached map (`hubs`, `features`, `rankedSymbols`, `files`) as one JSON object —
for piping into other tools. Also includes a top-level `fileCount`.

```
$ node agentmap.mjs --print | jq '.hubs[0]'
"lib/utils.ts (deg 52, pr 0.105171)"
```

### `--export <mermaid|dot>` — visualize the import graph

Serializes the file import graph (nodes = files, edges = imports, top-N by PageRank, with
three light style tiers) as **Graphviz DOT** or **Mermaid** — paste straight into
[mermaid.live](https://mermaid.live), a GitHub README mermaid block, or `dot -Tsvg`.
`--focus <path>` scopes to a file's 1-hop neighborhood. It reads the cached map only (no
ts-morph Project), and prints graph text to stdout (so it isn't combined with `--json`).

```
$ node agentmap.mjs --export mermaid --focus lib/auth.ts
%% agentmap import graph — 154 files, sha a1b2c3d, focus lib/auth.ts
flowchart TD
  classDef hub fill:#d9d9d9,stroke:#333,stroke-width:2px;
  n0["lib/auth.ts"]:::hub
  …
```

### Global flags

| Flag | Description |
|------|-------------|
| `--help` / `-h` | Print a usage block listing every flag and exit 0. |
| `--version` / `-v` | Print the version from `package.json` and exit 0. |
| `--json` | **Global modifier.** When present, every command prints exactly one JSON object to stdout (no prose). Shapes vary per command: `--json --hubs` → `{command,fileCount,sha,hubs:[string]}`, `--json --find X` → `{command,query,matches:[{file,name,kind}]}`, `--json --relates X` → `{command,file,pagerank,exports,imports,dependents,related}`, `--json --any X` → `{command,query,kind,…payload}`, etc. Bare `--json` (no query flag) → `{command:"build",fileCount,features,topHub}`. |
| `--no-locals` | Hide non-exported top-level declarations from `--find`/`--any` results (shown by default). Never affects `--map`/`--symbols`/`--hubs` ranking. |
| `--include-dts` | Include `.d.ts` declaration files in the symbol/ranking pass (excluded by default so generated types don't flood `--find`/`--symbols`/`--hubs`). |
| `--install-hooks` `[--dry-run]` | Copy `hooks/post-commit` into `.git/hooks/` (chmod 0755), ensure `.claude/agentmap/` is in `.gitignore`, and auto-wire the Claude Code `PreToolUse(Grep)` nudge into `.claude/settings.json` (merge-safe + idempotent). `--dry-run` previews without writing. Exit 0 on success, stderr + exit 3 on failure. |
| `--hook-status` | Report whether the post-commit hook, PreToolUse nudge, and `.gitignore` entry are installed (no writes). |
| `--doctor` | Read-only harness health report: git/Claude hook wiring, installed skills + Cursor rule freshness vs `package.json` version, MCP config entries for OpenCode/Antigravity, and map-cache presence/freshness hints. Always exits 0; suggests fix commands (`agentmap --install-hooks`, `--install-skill`, `--setup-mcp`, `agentmap`) but never runs them. Combine with `--json` for a structured report. |
| `--install-skill` | Install skills + always-on docs/hooks per platform (`--platform claude\|cursor\|codex\|opencode\|gemini\|antigravity\|copilot\|agents\|all`, default `all`; `--project` default, or `--global`; `--dry-run` preview). |
| `--setup-mcp` `[--dry-run]` | Configure agentmap as an MCP server for OpenCode and the Antigravity IDE (merge-safe). `--dry-run` previews without writing. |
| `--mcp` | Start agentmap as a **stdio MCP server** so non-Claude-Code agents (Cursor, Cline, any MCP client) can query the map. Exposes 11 query tools — `any`, `find`, `relates`, `map`, `hubs`, `features`, `feature`, `symbols`, `search`, `callers`, `calls`. |

**Exit-code contract:** `0` = success / match / help / version; `1` = query returned zero results (`--any`, `--find`, `--relates`, `--feature` with no match, or `--map --focus` that resolves to no file — the global digest still prints, with `focusResolved:false` in `--json`); `2` = usage error (missing required arg, unknown flag, two commands at once, or a sub-flag without its parent command); `3` = maintenance command failed (`--install-hooks`, `--install-skill`, `--setup-mcp`, `--hook-status`, `--mcp`). Any token starting with `-` that matches no known flag prints an error to stderr and exits 2.

---

## Scope & limitations

Honesty first — this is deliberately a small, sharp tool, not a universal code-graph.

- **TS/JS (+ Vue SFC), by design.** Built on `ts-morph`. Indexes `.ts/.tsx/.mts/.cts/`
  `.js/.jsx/.mjs/.cjs` and the `<script>` blocks of `.vue` single-file components
  (best-effort). No Python, Go, Rust, etc. — if your repo isn't TypeScript/JavaScript, use a
  tree-sitter-based tool instead. **Want another language?
  [Vote in #43](https://github.com/raymondchins/agentmap/issues/43)** — and read
  [what a non-TS language would actually get](#what-another-language-would-actually-get)
  first, because it would not be the same product.
- **The persisted map is a file-level import graph; the call graph is opt-in.** The
  cached map's edges come from static `import` / re-export declarations and the named
  symbols crossing them — `--relates` answers the file-level question ("who imports this
  module"). Symbol-level, compiler-accurate call-site resolution is available on demand via
  `--callers` (who calls a symbol) and `--calls` (what a symbol invokes) — both experimental,
  lazy, out-of-band queries that spin up the type-checker only when invoked and are never
  folded into the fast map build. The file-level graph additionally records a React Server
  Components client/server boundary tag (from `'use client'`/`'use server'` directive
  prologues) where present.
- **Alias & workspace resolution.** Resolves `tsconfig`/`jsconfig` `paths`, `vite`/`vitest`/
  `webpack` `resolve.alias` (string entries, parsed from the AST — the config is **never
  executed**), and pnpm/npm/yarn workspace cross-package imports (`@org/pkg` → its source).
  A build reports `edgeCoverage` (the share of repo-local imports that resolved) and prints a
  one-line warning when a repo's imports mostly *don't* resolve — so a broken/empty map is
  never silently framed as success.
- **Scoping — `.agentmapignore` + `.d.ts`.** Generated `.d.ts` declaration files are excluded
  from the symbol ranking **by default** (so a 200-symbol generated types file, or
  `next-env.d.ts`, doesn't flood `--find`/`--symbols`/`--hubs`); `--include-dts` restores
  them, and they stay live import-resolution targets either way. A repo-root `.agentmapignore`
  (gitignore-style subset: anchored `/`, dir `/`, `*` globs, `#` comments) excludes extra paths.
- **PageRank + symbol ranking are real and implemented** (damping 0.85, deterministic
  power iteration; personalized variants for `--relates` and `--map --focus`). The symbol
  ranking is a faithful port of Aider's identifier-graph approach (credit:
  [Aider](https://github.com/Aider-AI/aider), Apache-2.0).
- **Feature detection assumes the Next.js `app/` router.** `--feature` / `--features`
  derive features from the first real route segment under `app/` (or `src/app/`), skipping
  route groups `(...)`, dynamic `[...]`, and parallel `@...` segments. Repos without an
  `app/` directory simply report zero features — every other command still works.
- **Token counts are estimates** (`chars / 4`), not a real BPE tokenizer. Treat
  `--map`/`--tokens` budgets as approximate (±10%).
- The PreToolUse hook is **Claude Code-specific** (it speaks Claude Code's hook JSON). The
  post-commit hook is generic git.

---

## What another language would actually get

Published *before* any of it is built, because the cheapest way to find out you're
being asked for a different product is to describe the product accurately first.

agentmap's accuracy comes from `ts-morph` — a real TypeScript compiler with a type
checker. Another language would be parsed with tree-sitter: syntax, no types, no
module resolver. That difference decides what each query can honestly return.

| Query | TS / JS today | Another language |
|---|---|---|
| `--relates` (blast radius) | full | **full** — ports best; this is the one to lead with |
| `--search` (BM25) | full | **full** |
| `--hubs` / `--map` / `--symbols` (PageRank) | full | **full** |
| `--print` / `--export` | full | **full** — reads the cached map only |
| `--find` | full | **partial** — no transitive re-export/barrel chains |
| `--any` | full | **partial** |
| `--callers` / `--calls` | full | **none — refuses, explicitly** |
| `--features` / `--feature` | Next.js App Router | **none** |
| incremental rebuild | yes | **no — full rebuild every time** |

`--callers` / `--calls` are ~259 lines of TypeScript language-service calls.
Tree-sitter cannot reproduce them. The options are to refuse loudly or to guess by
name-matching, and **name-matching will not ship**: a silently mis-wired graph is
worse than no graph, and it would falsify every accuracy claim here.

Two non-TS languages is the honest ceiling for one part-time maintainer — each one
is an ongoing tax, not a one-off. Reasoning, including the arguments *against*
doing this at all, is in [ROADMAP.md Part II](https://github.com/raymondchins/agentmap/blob/HEAD/ROADMAP.md#part-ii--make-it-useful-for-the-majority-2026-07-26).

**No telemetry.** agentmap makes zero network calls. When an unsupported language
dominates your repo it counts the files locally, prints a one-line pointer to the
vote, and forgets. That count never leaves your machine. Silence it with
`AGENTMAP_NO_CENSUS=1`.

## Forks & ports

Two people ported agentmap to another language rather than open an issue, which is
the strongest evidence that the "nobody asked" reading was wrong:

- [`rifanid98/agentmap-go`](https://github.com/rifanid98/agentmap-go) — Go
- [`dstwn/agentmap-php`](https://github.com/dstwn/agentmap-php) — PHP / Laravel

Neither is affiliated with this repo and neither is endorsed — listed because
pretending they don't exist would be dishonest about demand. If you maintain one:
upstreaming beats competing, please open an issue.

---

## Contributing

Issues and PRs welcome. High-value directions:

- Retrieval-accuracy eval — **done** ([`EVAL.md`](https://github.com/raymondchins/agentmap/blob/HEAD/EVAL.md), `npm run eval`). Next: a
  type-aware dependents mode (the eval excludes type-only edges to match the value-import
  graph) and an `app/`-router fixture so `--feature` retrieval can be scored too.
- A real tokenizer behind the `--map` budget.
- Hardening feature detection for non-`app/`-router layouts.

Keep the dependency footprint minimal — `ts-morph` is the only runtime dependency (it bundles
the TypeScript compiler, ~10 MB installed), and keeping it that way is a feature.

## License

[MIT](https://github.com/raymondchins/agentmap/blob/HEAD/LICENSE). Symbol-ranking algorithm credit: [Aider](https://github.com/Aider-AI/aider) (Apache-2.0).

