{
  "markdown": "<p align=\"center\">\n  <img src=\"assets/hero.png\" alt=\"agentmap — 98% fewer tokens for a coding agent to find your code\" width=\"100%\">\n</p>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/@raymondchins/agentmap\"><img src=\"https://img.shields.io/npm/v/@raymondchins/agentmap\" alt=\"npm\"></a>\n  <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>\n  <a href=\"./LICENSE\"><img src=\"https://img.shields.io/badge/License-MIT-green\" alt=\"License: MIT\"></a>\n  <a href=\"#\"><img src=\"https://img.shields.io/badge/node-%3E%3D20-brightgreen\" alt=\"node >= 20\"></a>\n  <a href=\"#\"><img src=\"https://img.shields.io/badge/runtime%20deps-1-2EE0A8\" alt=\"1 runtime dependency\"></a>\n  <a href=\"#\"><img src=\"https://img.shields.io/badge/network%20calls-0-2EE0A8\" alt=\"zero network calls\"></a>\n</p>\n\n# agentmap\n\n### Your agent burns most of its context just finding code. This gives it the answer in one line.\n\n```bash\nnpx @raymondchins/agentmap --relates lib/db/schema.ts\n```\n\n```\nrelates: lib/db/schema.ts  (pr 0.073744)\ndependents (21): lib/types.ts, lib/utils.ts, lib/db/queries.ts,\n                 components/chat/message.tsx, app/(chat)/api/chat/route.ts, …\n```\n\nEvery file on that list really imports it. `grep` gets **40% of them wrong**.\n\n---\n\n## 💸 What it saves\n\nToken cost of the hidden first step in every agent task — *find the relevant code* —\non a real 154-file Next.js app ([vercel/ai-chatbot](https://github.com/vercel/ai-chatbot), sha `2becdb4`):\n\n| The agent needs to know… | Reading files | agentmap | Saved |\n|---|---:|---:|:---:|\n| Does a helper for this already exist? | 14,740 | 19 | **99.9%** |\n| Load the whole repo into context | 150,281 | 1,127 | **99.3%** |\n| What breaks if I change this file? | 81,038 | 616 | **99.2%** |\n| Where is this symbol defined? | 1,950 | 20 | **99%** |\n| What files make up this feature? | 6,121 | 1,025 | **83.3%** |\n| Give me a repo overview | 3,065 | 1,127 | **63.2%** |\n| What does this one file import? | 583 | 517 | **11.3%** |\n| **All 7 combined** | **257,778** | **4,451** | **98.3%** |\n\nHolds on [zod](https://github.com/colinhacks/zod) too (367 files, **99.2%**) and\n[taxonomy](https://github.com/shadcn-ui/taxonomy) (125 files, **96.0%**).\nCaptured output, pinned shas → [`benchmark/RESULTS.md`](./benchmark/RESULTS.md)\n\n## 🎯 …and it's still right\n\nFewer tokens is worthless if they're the wrong ones. Separate eval, ground truth derived\nlive from real repos:\n\n| | agentmap | `git grep` |\n|---|:---:|:---:|\n| What depends on this file? | **100%** precision | 59.9% precision |\n| Where is this defined? *(top-1)* | **100%** | 32% |\n| Where is this defined? *(top-3)* | **100%** | 80% |\n| Tokens to find a definition | **1.9× fewer** | — |\n\n<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=\"./EVAL.md\">EVAL.md</a></sub>\n\n---\n\n## ⚡ The five commands\n\n| You want | Run | Saves |\n|---|---|:---:|\n| \"Do we already have this?\" | `agentmap --find formatCurrency` | 99.9% |\n| \"What breaks if I touch this?\" | `agentmap --relates lib/auth.ts` | 99.2% |\n| \"Where is this defined?\" | `agentmap --find ChatMessage` | 99% |\n| \"Give me the repo, cheap\" | `agentmap --map --tokens 2000` | 99.3% |\n| Don't want to pick? | `agentmap --any <anything>` | — |\n\n`--any` routes it for you: file → symbol → feature → live content search.\n\nCold build **~1.2s**. Cached query **~0.1s**. No server, no vector DB, no API key.\n\n---\n\n## 🔌 Setup\n\n```bash\nnpx @raymondchins/agentmap --install-hooks   # rebuild on commit + steer the agent to the map\nnpx @raymondchins/agentmap --install-skill   # Claude Code · Cursor · Codex · Gemini · OpenCode · Copilot\n```\n\nMost repo-map tools stop at building the map. These two hooks are why it stays useful: the\nmap **rebuilds itself after every commit**, and the agent gets **nudged to the map the\nmoment it reaches for a dependency-shaped grep**. Claude Code users can get both from the\n[plugin](#4-claude-code-plugin-one-command-bundle).\n\n> **100% local.** Zero network calls, zero telemetry — not one `fetch`/`http` in the source.\n> ⚠️ Install the **scoped** name; unscoped `npx agentmap` is someone else's package.\n\n<details>\n<summary>Where the cache lives, and how it stays fresh</summary>\n\n<br>\n\nFirst run caches to `.claude/agentmap/map.json` (`--install-hooks` gitignores it). Later runs\nserve that cache **only** on a clean tree at an unchanged `HEAD` — with uncommitted\n`.ts/.tsx/.js/…` edits it silently rebuilds, so you never query a stale snapshot.\n\n```\n$ npx @raymondchins/agentmap\nagentmap: 154 files | 4 features | top hub: lib/utils.ts (deg 52, pr 0.105171)\n```\n\nFrom a checkout, every command also works as `node agentmap.mjs …`.\n\n</details>\n\n---\n\n## 🧠 Why the answers are right\n\nBuilt on **`ts-morph` — the real TypeScript compiler**, not text matching or tree-sitter\nguessing. It resolves `tsconfig` path aliases, `vite`/`webpack` aliases, `#imports` subpaths,\nand monorepo workspaces. Where `grep` sees a string, agentmap sees the resolved module.\n\nThat's also why barrels don't fool it: `export * from \"./x\"` looks identical to a real\ndefinition to a text search, so your agent edits the re-export and changes nothing. agentmap\nfollows the chain and names the file that actually declares it.\n\n<details>\n<summary><b>The honest asterisks</b> — read these before quoting a number</summary>\n\n<br>\n\n- **The win scales with the work.** The 63% and 11% rows are the floor. A *trivial\n  single-file* lookup can cost **more** than `cat` + `grep` — taxonomy's file-import task hit\n  **−313%**, and it stays in the table.\n- **The 98.3% headline is carried by its two biggest rows** — repo dump (150,281 → 1,127) and\n  blast radius (81,038 → 616). Drop the repo dump and it's **96.9%**; drop both and it's\n  **89.8%** here, **93.7%** pooled across all three repos, and **73.1%** on the smallest one.\n  All of those are real — they answer different questions. The headline is the common worst\n  case: an agent dumping the repo at session start.\n- **`--relates` returns the full blast radius**, so it costs *more* than a bare `grep -l` file\n  list. That's why the same command reads as 99.2% *saved* in the benchmark and *more\n  expensive* in the eval: the benchmark's baseline is an agent that `cat`s all 65 dependent\n  files, the eval's is a file list nobody reads. Against the list, agentmap trades tokens for\n  precision — 100% vs 59.9%, so ~4 in 10 files on the grep list don't belong.\n  Complete-and-correct over short-and-wrong, but it is a trade → [EVAL.md](./EVAL.md).\n- **Numbers are context-token volume**, not answer quality or wall-clock.\n- **Token counts are estimates** (`chars / 4`), applied identically to both sides.\n- **TypeScript/JavaScript only** (+ Vue SFC) — see [Scope & limitations](#scope--limitations).\n\n</details>\n\n---\n\n## Why it's different\n\nMany \"repo context\" tools are a photocopy: they dump your repository (or a slice of it) into\nthe prompt once and walk away — the copy goes stale the moment you edit a file, and nothing\nmakes the agent actually read it. agentmap is queryable and ranked instead: the agent\ninterrogates it flag-by-flag rather than swallowing a dump.\n\nIt also reports an `edgeCoverage` map-health signal and warns loudly when a repo's imports\nmostly *don't* resolve, so a broken map is never quietly framed as success.\n\nThe self-refreshing side — a post-commit rebuild plus a `PreToolUse` hook that steers the agent\nto the map before it serial-greps — is genuinely useful, but it isn't unique: **CodeGraph**\n([colbymchenry/codegraph](https://github.com/colbymchenry/codegraph), ~62k★ (2026-07-26)) ships a native\nOS-event file watcher (FSEvents/inotify) with debounced auto-sync and an installer that\nauto-configures eight agent CLIs. agentmap's honest edge over the multi-language graph tools is\nnarrower and sharper: **TS/JS resolution the others approximate, with a published accuracy eval.**\n\n| | **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) |\n| --- | --- | --- | --- | --- | --- |\n| **Ranking algorithm** | Personalized PageRank (file + symbol graphs) | PageRank (graph ranking) | Importance heuristics | None (file order) | None (file order) |\n| **Languages** | TS/JS + Vue SFC (via ts-morph) | Many (tree-sitter) | Many (tree-sitter) | Language-agnostic (text) | Language-agnostic (text) |\n| **Token-budget output** | Yes — `--map [--tokens N]` ranked digest | Yes (built into Aider's context) | Partial | Yes (size caps) | Yes (templates/caps) |\n| **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) |\n| **Retrieval-accuracy eval** | **Yes — published [`EVAL.md`](./EVAL.md) vs live ground truth** | No | No | No | No |\n| **Agent-loop wiring** | Yes — post-commit auto-refresh + PreToolUse hook | In-process (Aider only) | No | MCP server (no auto-refresh, no nudge) | No |\n| **Dependencies** | `ts-morph` only | Python + tree-sitter stack | Python + tree-sitter | Node | Rust binary |\n| **Install** | `npx @raymondchins/agentmap` | `pip install aider-chat` | `pip install` | `npx`/global | `cargo`/binary |\n\n<sub>Comparison as of <b>2026-07-27</b>, from each project's own docs. These are moving targets — if a\ncell is out of date, that's a bug: <a href=\"https://github.com/raymondchins/agentmap/issues\">open an issue</a>.</sub>\n\nWhat that table is **not** claiming: agentmap is TS/JS-only (the others are multi-language),\nand it's a **file-level import graph**, not a full call-site/reference resolver (see\n[Scope & limitations](#scope--limitations)). The differentiators are narrow and honest:\n**(1)** compiler-grade TS/JS resolution (aliases, `vite`/`webpack`, `#imports`, workspaces) with a\npublished accuracy eval, and **(2)** the `--any` router. The agent-loop wiring is real and\nconvenient but **not** unique — [CodeGraph](https://github.com/colbymchenry/codegraph) and others\nauto-sync and auto-configure agent CLIs too; we don't claim it as a moat.\n\n---\n\n## The agent loop (staying current, staying used)\n\nA common failure of repo-map tools: they build a beautiful map, and then the\nagent forgets it exists and greps anyway. A map the agent doesn't open is just dead weight.\n\nagentmap closes that loop. Two hooks (in [`./hooks/`](./hooks/)) do the work: the map\n**refreshes itself after every commit**, and the agent gets **nudged to query it before it\nserial-greps**. You wire it once — then it stays current on its own, and stays used.\n\n> This wiring is table stakes, not the moat — [CodeGraph](https://github.com/colbymchenry/codegraph)\n> and other tools also auto-sync (via native OS file watchers) and auto-configure agent CLIs.\n> agentmap ships it because it's genuinely useful; the actual point of agentmap is the\n> **compiler-grade TS/JS accuracy** the map is built on.\n\n### 1. Auto-refresh on commit\n\n[`hooks/post-commit`](./hooks/post-commit) rebuilds `.claude/agentmap/map.json` after each\ncommit, detached + silenced so it never slows the commit. It skips during\nrebase/merge/cherry-pick and no-ops if Node is missing.\n\nThe hooks ship inside the npm package. The simplest setup:\n\n```bash\nnpx @raymondchins/agentmap --install-hooks\n```\n\nThis copies `hooks/post-commit` into `.git/hooks/`, sets it executable, ensures\n`.claude/agentmap/` is in `.gitignore`, and **auto-wires the `PreToolUse` nudge\nhook into `.claude/settings.json`** (merge-safe + idempotent) so map enforcement is\non by default — no manual paste. Manual alternative for just the post-commit hook:\n\n```bash\n# from your repo root\ncp hooks/post-commit .git/hooks/post-commit\nchmod +x .git/hooks/post-commit\n```\n\nThe hook resolves the builder to the **installed** package — `node_modules/.bin/agentmap`,\na PATH `agentmap` binary verified to be `@raymondchins/agentmap`, then\n`npx @raymondchins/agentmap`. It never runs a repo-local `./agentmap.mjs` unless you opt in\nwith `AGENTMAP_HOOK_ALLOW_LOCAL=1` (for developing agentmap itself), so an\nattacker-planted `agentmap.mjs` can't execute on your next commit.\n\n### 2. Force the agent to use it — `PreToolUse` hook\n\n[`hooks/agentmap-nudge.mjs`](./hooks/agentmap-nudge.mjs) is a **non-blocking** hook for\nClaude Code that covers **both** the `Grep` tool and raw Bash text-searchers\n(`grep`/`rg`/`egrep`/`fgrep`/`ag`/`ack`). When either looks like a dependency /\nwho-imports / component-usage / reuse / where-is-symbol search, it injects a reminder\nsteering the agent to `agentmap --any` first. It never denies the call, and stays silent\nfor raw-string / Tailwind-class / lowercase-HTML-tag sweeps and for pipe-filtered commands\nlike `ps aux | grep node` — so it's high-signal, not nagging.\n\n**Fires on:** `import`/`require`/`export`/`from '...'` patterns, JSX component tags\n(`<Hero`, `<ProviderCard`), explicit intent words (`where is`, `who imports`, `reuse`,\n`existing component`), and — in both the `Grep` tool and the Bash branch — bare multi-hump\nPascalCase identifiers (`ProviderCard`, `TopProviders`) that almost always mean \"where is\nthis symbol / who uses it\". The Bash branch additionally only fires when the searcher is the *primary* command (at the start,\nor after `;`/`&&`); piped log-filters stay silent.\n\nAll four nudge/gate variants (this one, Codex, Gemini, OpenCode) also **self-gate on\nproject presence**: since they ship at user/global scope too (plugin bundle, `~/.gemini`,\n`~/.codex`, `~/.config/opencode`), they walk up from the tool call's cwd to the\nfilesystem root looking for `node_modules/@raymondchins/agentmap` or a built\n`.claude/agentmap/map.json` before doing anything else, so a repo with no agentmap stays\nsilent instead of nagging (or, for Codex, denying a grep it has no business denying).\n\n`--install-hooks` writes both matchers into `.claude/settings.json` for you (merge-safe —\npreserves existing settings, won't duplicate on re-run). The single hook file dispatches\ninternally on `tool_name`. For reference, or to wire it by hand:\n\n```json\n{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Grep\",\n        \"hooks\": [{ \"type\": \"command\", \"command\": \"node ./hooks/agentmap-nudge.mjs\" }]\n      },\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [{ \"type\": \"command\", \"command\": \"node ./hooks/agentmap-nudge.mjs\" }]\n      }\n    ]\n  }\n}\n```\n\nThat's the \"forced to use it\" in the tagline: the map stays current on its own, and the\nagent is steered to it the moment it reaches for a dependency-shaped grep or Bash search.\n\n### 3. Agent skills (Cursor, Claude Code, Codex, OpenCode, Gemini, Antigravity, Copilot)\n\n```bash\nnpx @raymondchins/agentmap --install-skill\n```\n\n…or grab just the skill (no agentmap flags) via the [`skills`](https://github.com/vercel-labs/skills)\nCLI — agentmap ships the `skills/agentmap/SKILL.md` layout it expects:\n\n```bash\nnpx skills add raymondchins/agentmap\n```\n\n`--install-skill` copies packaged **SKILL.md** files and a **Cursor rule** (`.cursor/rules/agentmap.mdc`,\n`alwaysApply: true`) into the current repo or global agent directories. Paths follow\neach platform's official skill-directory conventions. Options:\n\n```bash\nagentmap --install-skill --platform cursor           # Cursor rule + .cursor/hooks.json shell gate (project)\nagentmap --install-skill --platform claude           # .claude/skills/agentmap/SKILL.md\nagentmap --install-skill --platform codex            # .codex/skills/ (project) or ~/.codex/skills/ (global)\nagentmap --install-skill --platform opencode         # .opencode/skills/ (project) or ~/.config/opencode/skills/ (global)\nagentmap --install-skill --platform gemini           # .gemini/skills/ (project); global ~/.gemini/skills/ (Windows global: ~/.agents/skills/)\nagentmap --install-skill --platform antigravity      # .agents/skills/ (project) or ~/.gemini/config/skills/ (global)\nagentmap --install-skill --platform copilot          # .copilot/skills/ or ~/.copilot/skills/\nagentmap --install-skill --global --platform claude  # ~/.claude/skills/...\nagentmap --install-skill --platform agents           # legacy .agents/skills/ (project or global); excluded from default `all`\nagentmap --install-skill --dry-run                   # preview paths, no writes\n```\n\n`--platform all` installs: claude, cursor, codex, opencode, gemini, antigravity, copilot (not legacy `agents`).\n\nSome platforms also get **always-on** docs and hooks in the same command:\n\n| `--platform` | Skill | Also installs (project) | Global docs |\n|--------------|-------|-------------------------|-------------|\n| `gemini` | `.gemini/skills/…/SKILL.md` | `GEMINI.md` + `.gemini/settings.json` BeforeTool nudge | `~/.gemini/GEMINI.md` |\n| `codex` | `.codex/skills/…/SKILL.md` | `AGENTS.md` merge-safe `<!-- agentmap:begin/end -->` block | `~/.codex/AGENTS.md` |\n| `opencode` | `.opencode/skills/…/SKILL.md` | `AGENTS.md` + `.opencode/plugins/agentmap-nudge.js` | `~/.config/opencode/AGENTS.md` |\n| `cursor` | `.cursor/rules/agentmap.mdc` | `.cursor/hooks.json` `beforeShellExecution` gate + `.cursor/hooks/agentmap-cursor-nudge.mjs` | — (project-scope only) |\n\nCodex and OpenCode share one repo-root `AGENTS.md` on project install. Existing content outside the marked block is preserved.\n\nPair with `--install-hooks` (Claude Code) or `--mcp` (Cursor MCP).\n\n### 4. Claude Code plugin (one-command bundle)\n\nPrefer the plugin over `--install-skill`/`--install-hooks` if you're on Claude Code and\nwant the skill, the `PreToolUse` grep/Bash nudge, and the stdio MCP server in a single\ninstall that auto-updates:\n\n```bash\n# in Claude Code\n/plugin marketplace add raymondchins/agentmap\n/plugin install agentmap@agentmap\n```\n\nThe plugin bundles: the packaged **SKILL.md**, the **PreToolUse nudge** (both the `Grep`\ntool and Bash text-searchers, via `${CLAUDE_PLUGIN_ROOT}`), and the **stdio MCP server**\n(`npx -y @raymondchins/agentmap --mcp`, so `ts-morph` is fetched on demand — the plugin\ncache ships no `node_modules`).\n\n> **One thing the plugin can't do: install the git `post-commit` hook.** Claude Code\n> plugins can't write into `.git/hooks/`, so the auto-refresh-on-commit still needs a\n> one-time `npx @raymondchins/agentmap --install-hooks` in each repo (it also wires the\n> nudge into `.claude/settings.json`, harmlessly redundant with the plugin's copy).\n> Without it the map still rebuilds on any dirty query — you just lose the commit-time\n> refresh.\n\n### Onboarding by platform\n\nEnforcement isn't uniform — some CLIs get a **live hook** that actively steers grep to\nagentmap, some get an **MCP server** the agent can call, and some are **docs-only** (a\nskill/rule the agent may or may not consult). Honest matrix:\n\n| Platform | Install | Enforcement | Known gaps |\n|----------|---------|-------------|------------|\n| **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 |\n| **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 |\n| **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 |\n| **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 |\n| **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 |\n| **Copilot CLI** | `--install-skill --platform copilot` | **docs-only** — `.copilot/skills/` | same as Codex — no live hook yet |\n\n**Cursor MCP — copy-paste `.cursor/mcp.json`** (Cursor's `--mcp` wiring is a documented\ndead-end otherwise; drop this at your repo root):\n\n```json\n{\n  \"mcpServers\": {\n    \"agentmap\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@raymondchins/agentmap\", \"--mcp\"]\n    }\n  }\n}\n```\n\nThen Cursor exposes the 11 query tools (`any`, `find`, `relates`, `map`, `hubs`,\n`features`, `feature`, `symbols`, `search`, `callers`, `calls`). Run `agentmap --doctor` any time to see what's wired\nvs missing.\n\n### Uninstall\n\nagentmap only writes files into your repo/home — remove them to fully uninstall. `agentmap\n--doctor` lists every path it wrote, and every docs merge lives inside an\n`<!-- agentmap:begin/end -->` (or `# agentmap:begin/end`) fence, so deleting just that block\nleaves the rest of your `AGENTS.md` / `GEMINI.md` intact.\n\n| Platform | Remove |\n|----------|--------|\n| Claude Code | `.claude/skills/agentmap/` + the agentmap `PreToolUse` block in `.claude/settings.json` |\n| 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` |\n| 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` |\n| OpenCode | `.opencode/skills/agentmap/`, `.opencode/plugins/agentmap-nudge.js`, the `AGENTS.md` block |\n| Gemini | `.gemini/skills/agentmap/`, `.gemini/hooks/agentmap-nudge.mjs`, the `BeforeTool` hook in `.gemini/settings.json`, the `GEMINI.md` block |\n| All | map cache `rm -rf .claude/agentmap/`; npm devDep `npm rm @raymondchins/agentmap`; the agentmap block in `.git/hooks/post-commit` |\n\n### Troubleshooting\n\n| Symptom | Cause / fix |\n|---------|-------------|\n| `features (0)` | `--features` only detects Next.js `app/` routes; a TanStack `src/routes/` repo legitimately shows 0. Use `--map` / `--symbols` instead. |\n| 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`. |\n| Stale-looking results | By design the map rebuilds from disk on a dirty tree / SHA mismatch. Force a rebuild by just running `agentmap`. |\n| 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. |\n| 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. |\n| 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. |\n| Installed the wrong `agentmap` | This is **`@raymondchins/agentmap`** (npm scope) — not the unrelated unscoped `agentmap` packages. |\n| Cursor MCP tools missing | `--mcp` doesn't auto-wire Cursor; add the copy-paste `.cursor/mcp.json` from the matrix above and restart Cursor. |\n| 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. |\n| `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. |\n| 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. |\n| `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`. |\n\n---\n\n## The `--any` router\n\nDon't want to learn eight flags? You don't have to. Throw anything at `--any` — a filename, a\nfunction, a feature, even a raw string — and it figures out what you meant, returning the first\nlayer that hits:\n\n```\n--any <query>\n   │\n   ├─ 1. FILE     exact path → unique basename → unique substring\n   ├─ 2. SYMBOL   exported name contains the query (across all files)\n   ├─ 3. FEATURE  app/-router feature name contains the query\n   └─ 4. CONTENT  live `git grep` (tracked + untracked) — never stale\n```\n\nLayers 1–3 read the cached structural map (fast, ranked). Layer 4 is a **live disk read**\nvia `git grep -F`, so raw strings, copy, Tailwind classes, and config values the structural\ngraph never indexes still resolve instead of coming up empty.\n\n**Symbol hit** (query resolved to a symbol → full block):\n\n```\n$ node agentmap.mjs --any cn\n[structure] 1 symbol, 0 feature match for \"cn\"\n  lib/utils.ts → cn (FunctionDeclaration)\n```\n\n**Ambiguous file hit** (query matched multiple files → narrow it):\n\n```\n$ node agentmap.mjs --any utils\n[structure] \"utils\" matched 3 files — narrow it:\n  lib/utils.ts\n  lib/db/utils.ts\n  tests/prompts/utils.ts\n```\n\n**Content fallback** (no file/symbol/feature match → live git-grep):\n\n```\n$ node agentmap.mjs --any streamText\n[content] 13 lines:\napp/(chat)/api/chat/route.ts:8:  streamText,\napp/(chat)/api/chat/route.ts:194:        const result = streamText({\nartifacts/code/server.ts:1:import { streamText } from \"ai\";\nartifacts/code/server.ts:18:    const { fullStream } = streamText({\nartifacts/code/server.ts:40:    const { fullStream } = streamText({\nartifacts/sheet/server.ts:1:import { streamText } from \"ai\";\nartifacts/sheet/server.ts:11:    const { fullStream } = streamText({\n```\n\n---\n\n## Commands\n\nEvery snippet below is **representative output** (long lists trimmed) from running agentmap against the public\n154-file Next.js repo [vercel/ai-chatbot](https://github.com/vercel/ai-chatbot) (sha 2becdb4).\n\n### `--any <q>` — the router (file → symbol → feature → live content)\n\nSee [The `--any` router](#the---any-router) above. Default first move for any\n\"where/what/who\" question.\n\n### `--find <q>` — reuse-before-rebuild symbol search\n\nFind every symbol whose name contains the query — exported symbols **plus** non-exported\ntop-level declarations. Use it before writing a new util or component to check what already\nexists (a private helper counts as reusable too).\n\n```\n$ node agentmap.mjs --find Message\nfind \"Message\": 55 match\n  hooks/use-messages.tsx → useMessages (FunctionDeclaration)\n  lib/errors.ts → getMessageByErrorCode (FunctionDeclaration)\n  lib/types.ts → messageMetadataSchema (VariableDeclaration)\n  lib/types.ts → MessageMetadata (TypeAliasDeclaration)\n  lib/types.ts → ChatMessage (TypeAliasDeclaration)\n  lib/utils.ts → convertToUIMessages (FunctionDeclaration)\n  lib/utils.ts → getTextFromMessage (FunctionDeclaration)\n  tests/helpers.ts → generateTestMessage (FunctionDeclaration)\n  app/(chat)/actions.ts → generateTitleFromUserMessage (FunctionDeclaration)\n  …\n```\n\n**Barrels don't hide the real file.** When a match is reached through a re-export\n(`export * from \"./x\"`, or a named/renamed re-export, at any depth), the output names the\nfile that actually declares it. The TypeScript checker resolves the chain, so this works\nwhere a name search can't — `rg` sees the barrel and the origin as two equal hits with no\nway to tell which one you can edit. An origin outside the repo reports\n`→ defined outside the repo`; a `node_modules` path is never printed.\n\n```\n$ node agentmap.mjs --find useComposedRefs     # radix-ui/primitives@579c5b84\nfind \"useComposedRefs\": 3 match\n  packages/react/compose-refs/src/index.ts → useComposedRefs (FunctionDeclaration) → defined in packages/react/compose-refs/src/compose-refs.tsx\n  packages/react/compose-refs/src/compose-refs.tsx → useComposedRefs (FunctionDeclaration)\n  packages/react/radix-ui/src/internal.ts → useComposedRefs (?)\n```\n\nIn `--json` this is `definedIn: \"<path>\"` or `external: true` on the match, present only\nwhen the entry is a pass-through — a real definition carries neither.\n\n### `--search <q>` — BM25 lexical search for vague queries\n\nWhen you don't know the exact symbol name — the query an agent actually types — `--search`\nranks symbols by **BM25 lexical relevance** over split-identifier tokens (the symbol name,\nits file's path segments, feature, and kind), fused with file PageRank so a strong hit in an\nimportant file wins ties. No embeddings, no vector DB; the index is built into `map.json`.\nThe same ranker is wired into `--any` as a rung that fires **only** when exact file/symbol\nmatching found nothing, so exact routing is unchanged.\n\n```\n$ node agentmap.mjs --search \"auth retry logic\"\nsearch \"auth retry logic\": 3 match\n  src/authRetry.ts → retryWithBackoff (FunctionDeclaration)  [6.83]\n  …\n```\n\nStopwords (`the`, `that`, `of`, …) are dropped, so `--search \"the function that dedupes\nsymbols\"` works. Also available as the `search` MCP tool.\n\n### `--relates <path>` — blast radius + transitive relevance\n\nThe file's own block (exports / imports / direct dependents) **plus** a random-walk\nrelevance list (personalized PageRank on the bidirectional import graph) — the files most\nrelated to the target, transitively, not just its direct importers.\n\n```\n$ node agentmap.mjs --relates lib/db/schema.ts\nrelates: lib/db/schema.ts  (pr 0.073744)\nexports (14): user(VariableDeclaration), User(TypeAliasDeclaration), chat(VariableDeclaration), Chat(TypeAliasDeclaration), message(VariableDeclaration), DBMessage(TypeAliasDeclaration), …\nimports (0): —\ndependents (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, …\nrelated (random-walk relevance):\n  lib/utils.ts (0.0476)\n  lib/types.ts (0.0376)\n  components/chat/artifact.tsx (0.0372)\n  components/chat/icons.tsx (0.0264)\n  components/chat/message.tsx (0.0237)\n  lib/db/queries.ts (0.0225)\n  app/(chat)/api/chat/route.ts (0.0218)\n  …\n```\n\n**Type-only dependencies are listed separately, not silently dropped.** `dependents` means\n\"would break at runtime\". A file imported only via `import type` has no runtime dependents\nat all — but renaming or deleting its exports still breaks every consumer at compile time.\nThose appear under `type-only dependents`, so a types module stops reading like an orphan:\n\n```\n$ node agentmap.mjs --relates lib/types.ts     # vercel/chatbot@c2f8235e\nrelates: lib/types.ts  (pr 0.002898)\nexports (7): messageMetadataSchema(VariableDeclaration), MessageMetadata(TypeAliasDeclaration), …\nimports (0): —\ndependents (0): —\ntype-only imports (6): components/chat/artifact.tsx, lib/ai/tools/create-document.ts, …\ntype-only dependents (23): hooks/use-active-chat.tsx, hooks/use-auto-resume.ts, lib/utils.ts, …\n```\n\n22.4% of that repo's import statements are type-only. The fields are\n`typeOnlyImports` / `typeOnlyDependents` in `--json`, omitted entirely when empty, and they\nnever enter PageRank, `--hubs`, symbol ranking or `--export` — the ranking graph stays a\nruntime graph.\n\nFor a file carrying a React Server Components directive prologue, the output adds one more\nline — `boundary: 'use client' (client component)` or `boundary: 'use server' (server module/actions)`\n(`rsc: 'client' | 'server'` in `--json`) — right after `dependents`. This is additive and\noptional: repos with no `'use client'`/`'use server'` directives never see the line.\n\n### `--callers <sym>` — compiler-accurate call graph (experimental)\n\nWho actually **calls** a symbol, resolved by the TypeScript language service (`ts-morph`\n`findReferencesAsNodes`) — not tree-sitter name-matching. This is symbol-level blast radius:\na type-position mention (`typeof foo`), a re-export, a bare value reference (`const x = foo`),\nor a same-named private local in another file is a *different* symbol and is never\nmis-attributed. `--in <path>` disambiguates a name defined in more than one file (exported\ndefinitions win over same-named private locals); results are ranked by caller-file PageRank\nand capped.\n\n```\n$ node agentmap.mjs --callers getMessageByErrorCode\ncallers of getMessageByErrorCode  [lib/errors.ts]: 3 call sites\n  app/(chat)/api/chat/route.ts:88 → POST\n  lib/db/queries.ts:142 → saveMessage\n  components/chat/message.tsx:57 → PureMessage\n```\n\n**JSX counts as a call site.** `<Foo />` compiles to `React.createElement(Foo, …)` (classic\nruntime) or `jsx(Foo, …)` (automatic runtime) — either way it's an invocation, so a\ncomponent's callers include everywhere it's rendered, not just plain `foo()` calls.\n`<Foo.Bar />` resolves to `Bar`, not the `Foo` namespace; `<Foo>...</Foo>` counts once (the\nclosing tag isn't a second call site); an intrinsic tag (`<div>`) resolves to nothing\nin-project and produces no edge.\n\n```\n$ node agentmap.mjs --callers Button\ncallers of Button  [components/ui/button.tsx]: 25 call sites\n  components/ai-elements/message.tsx:93 → MessageAction\n  components/ai-elements/message.tsx:263 → MessageBranchPrevious\n  components/ui/sidebar.tsx:249 → SidebarTrigger\n  components/ui/alert-dialog.tsx:158 → AlertDialogAction\n  components/ui/dialog.tsx:72 → DialogContent\n  …\n```\n\nBefore 0.17.0, JSX wasn't a recognized call shape at all, so that same query returned\n**0 call sites** — a plain `rg '<Button'` beat the tool outright. Captured on\n[vercel/ai-chatbot](https://github.com/vercel/ai-chatbot) at `c2f8235`; reproduce it by\nrunning the query against that commit.\n\nA deliberate **deep query**: it lazily spins up the TS type-checker (a few seconds on a large\nrepo) *only* when invoked — the map build and every other query never pay that cost, and\nnothing is persisted. Accurate on statically-resolvable calls; dynamic dispatch, reflection,\nand string-keyed access are beyond any static tool. Also available as the `callers` MCP tool.\n\n### `--calls <sym>` — outgoing call graph (experimental)\n\nThe companion to `--callers`: which in-project symbols a symbol **invokes**. Each call and\n`new X()` site inside its body is resolved by the type checker (`getDefinitionNodes`), which\nfollows an imported / re-exported binding through to the real declaration — so a same-named\nlocal elsewhere is never confused for the imported one. `node_modules` and TypeScript\nbuilt-ins (`console.log`, `Array.map`, …) are excluded; dynamic dispatch, computed member\naccess, and higher-order callees are honestly skipped.\n\n```\n$ node agentmap.mjs --calls extractFacts\nextractFacts calls  [agentmap.mjs]: 15 in-project targets\n  agentmap.mjs:756 → makeProject (FunctionDeclaration)\n  agentmap.mjs:944 → rel (VariableDeclaration)\n  agentmap.mjs:952 → excluded (VariableDeclaration)\n  …\n```\n\n**JSX counts as an outgoing call too**, for the mirror-image reason: a component whose body\nis nothing but `return <Container><Sidebar /></Container>` has no `CallExpression` in it, so\nbefore this fix it reported **zero** outgoing calls even though it clearly depends on both.\nEach `<Foo />` / `<Foo>...</Foo>` in the body now resolves to its target declaration the same\nway a plain call does — the printed `(kind)` is the target's own declaration kind\n(`FunctionDeclaration`, etc.), not \"JSX\", since resolution is unchanged, only call-site\ndetection is:\n\n```\n$ node agentmap.mjs --calls AppSidebar\nAppSidebar calls  [components/chat/app-sidebar.tsx]: 35 in-project targets\n  components/ui/tooltip.tsx:21 → Tooltip (FunctionDeclaration)\n  components/ui/tooltip.tsx:33 → TooltipContent (FunctionDeclaration)\n  components/ui/sidebar.tsx:144 → Sidebar (FunctionDeclaration)\n  components/ui/sidebar.tsx:379 → SidebarContent (FunctionDeclaration)\n  …\n```\n\nSame repo and commit: this returned **6** targets before 0.17.0 — only the plain hook and\nhelper calls — and 35 after, because the 29 components it renders now count too.\n\nSame lazy, out-of-band model as `--callers` (builds a Project only on the query, nothing\npersisted). Also the `calls` MCP tool. JSX closes a real gap here — it doesn't change what's\nstill out of reach: the `node_modules`/dynamic-dispatch/computed-member/higher-order limits\nabove still apply.\n\n**Going transitive — `--depth N`.** Both `--callers` and `--calls` accept `--depth N`\n(default 1, max 5) for an N-hop closure: `--callers foo --depth 3` is the transitive\nblast radius (\"everything that reaches `foo`, up to 3 hops\"); `--calls foo --depth 3` is\nthe dependency cone (\"everything `foo` pulls in\"). It BFS-traverses the same single warm\nProject — no extra build — with cycle detection and node caps so a hub can't explode; each\nresult is tagged with its `depth` and a `via` parent. `--depth 1` is the default single-hop\nquery.\n\n```\n$ node agentmap.mjs --callers leaf --depth 2\ncallers of leaf  [src/chain.ts]: 2 callers within depth 2\n  src/chain.ts:2 → mid [depth 1]\n  src/chain.ts:3 → top [depth 2]\n```\n\n### `--feature <name>` — files that make up a feature\n\nResolves a Next.js `app/`-router feature to its file set, plus the external files that\ndepend on it.\n\n```\n$ node agentmap.mjs --feature api\nfeature \"api\": 11 files\n  app/(chat)/api/chat/route.ts\n  app/(chat)/api/chat/schema.ts\n  app/(chat)/api/document/route.ts\n  app/(chat)/api/history/route.ts\n  app/(chat)/api/messages/route.ts\n  app/(chat)/api/models/route.ts\n  app/(chat)/api/suggestions/route.ts\n  app/(chat)/api/vote/route.ts\n  app/(auth)/api/auth/guest/route.ts\n  app/(chat)/api/files/upload/route.ts\n  app/(chat)/api/chat/[id]/stream/route.ts\nexternal dependents (0): —\n```\n\n### `--features` — list features by size\n\n```\n$ node agentmap.mjs --features\nfeatures (4):\n  api (11 files)\n  login (1 files)\n  register (1 files)\n  chat (1 files)\n```\n\n### `--affected <path>` — which tests cover this file\n\nWalks the reverse-dependency closure and reports the test files that reach the target,\nwith hop distance. The useful answer is often the empty one: *nothing covers this*, which\nis what you want to know **before** a risky edit, not after CI.\n\nType-only importers count. Changing an exported type breaks every `import type` consumer\nat compile time, so the walk follows `dependents` **and** `typeOnlyDependents`.\n\n```\n$ node agentmap.mjs --affected agentmap.mjs\naffected by agentmap.mjs: 3 test files (of 4 transitive dependents)\n  test/doctor.test.mjs  [1 hop]\n  test/pkg-imports.test.mjs  [1 hop]\n  test/unit.test.mjs  [1 hop]\n```\n\nA file with no reachable test says so in words, and `--json` carries `covered: false`.\n\n### `--routes` — the App Router route table\n\nEvery URL the repo serves and the file that serves it. **Next.js App Router only** — on any\nother repo it exits 1 with an explicit `reason` rather than an empty list, so an agent can\ntell \"not applicable\" from \"nothing found\".\n\n```\n$ node agentmap.mjs --routes\nroutes: no app/ or src/app/ directory — not a Next.js App Router project\n```\n\n### `--route <url>` — resolve a URL to the code that serves it\n\nGoes from a bug report naming a URL straight to the handler, its layout chain, and the\nserver modules it can reach.\n\n```\n$ node agentmap.mjs --route /dashboard/settings\n/dashboard/settings  (page)\n  serves: app/dashboard/settings/page.tsx  boundary: 'client'\n  layouts (outer->inner): app/layout.tsx -> app/dashboard/layout.tsx\n  server modules: lib/server/settings.ts\n```\n\n### `--kind <k>` — narrow `--find` / `--search` by declaration kind\n\nA modifier, not a command. Matched loosely and case-insensitively against the ts-morph kind\nname, so `--kind function` finds `FunctionDeclaration` and `--kind type` finds\n`TypeAliasDeclaration` — you never have to know the enum spelling.\n\n```\n$ node agentmap.mjs --find pagerank --kind function\nfind \"pagerank\" kind~function: 1 match\n  agentmap.mjs → pagerank (FunctionDeclaration)\n```\n\nUsed alone it is a usage error (exit 2) — it has nothing to narrow.\n\n### `--hubs` — most important files (PageRank)\n\nThe files that matter most, ranked by PageRank importance (raw dependent degree shown\nalongside).\n\n```\n$ node agentmap.mjs --hubs\nagentmap: 154 files (sha 2becdb4)\nhubs (PageRank importance):\n  lib/utils.ts (deg 52, pr 0.105171)\n  lib/db/schema.ts (deg 21, pr 0.073744)\n  lib/types.ts (deg 23, pr 0.067589)\n  components/chat/artifact.tsx (deg 15, pr 0.036882)\n  components/chat/icons.tsx (deg 27, pr 0.035378)\n  lib/errors.ts (deg 9, pr 0.032787)\n  lib/db/queries.ts (deg 14, pr 0.030085)\n  …\n```\n\n### `--symbols [N]` — top ranked symbols (Aider-style)\n\nThe most important individual symbols across the repo, ranked by the identifier graph\n(defaults to 30).\n\n```\n$ node agentmap.mjs --symbols 10\ntop 10 ranked symbols (Aider-style):\n  0.109902  lib/utils.ts → cn (FunctionDeclaration)\n  0.036013  lib/types.ts → ChatMessage (TypeAliasDeclaration)\n  0.025686  components/chat/artifact.tsx → ArtifactKind (TypeAliasDeclaration)\n  0.022461  lib/errors.ts → ChatbotError (ClassDeclaration)\n  0.021068  lib/types.ts → CustomUIDataTypes (TypeAliasDeclaration)\n  0.020872  lib/db/schema.ts → Document (TypeAliasDeclaration)\n  0.020555  components/ai-elements/suggestion.tsx → Suggestion (VariableDeclaration)\n  0.020555  lib/db/schema.ts → Suggestion (TypeAliasDeclaration)\n  0.018124  lib/db/schema.ts → DBMessage (TypeAliasDeclaration)\n  0.015034  lib/errors.ts → ErrorCode (TypeAliasDeclaration)\n```\n\n`map.json` persists the top 80. Asking for more re-ranks from the cached map rather than\ntruncating, so `--symbols 200` really does return 200 where the repo has them. When a repo\nhas fewer ranked symbols than you asked for, the header says so and `--json` carries\n`requested` / `shown` / `truncated`:\n\n```\n$ node agentmap.mjs --symbols 200\ntop 62 ranked symbols (Aider-style) — asked for 200, this repo only ranks 62:\n```\n\n### `--map [--tokens N] [--focus <path>]` — token-budgeted ranked digest\n\nThe token-budgeted digest (Aider's killer feature): a ranked, files-and-symbols summary\nthat fits a token budget. Default budget is 8192 (1024 with `--focus`). `--focus <path>`\npersonalizes the ranking toward a file you're working on.\n\n```\n$ node agentmap.mjs --map --tokens 400\n# agentmap (154 files, sha 2becdb4) — focus: global, budget ~400 tok\n\nlib/utils.ts:\n  cn (FunctionDeclaration)\n  generateUUID (FunctionDeclaration)\n\nlib/types.ts:\n  ChatMessage (TypeAliasDeclaration)\n  CustomUIDataTypes (TypeAliasDeclaration)\n  ChatTools (TypeAliasDeclaration)\n  Attachment (TypeAliasDeclaration)\n\ncomponents/chat/artifact.tsx:\n  ArtifactKind (TypeAliasDeclaration)\n  UIArtifact (TypeAliasDeclaration)\n  Artifact (VariableDeclaration)\n\nlib/errors.ts:\n  ChatbotError (ClassDeclaration)\n  ErrorCode (TypeAliasDeclaration)\n\nlib/db/schema.ts:\n  Document (TypeAliasDeclaration)\n  Suggestion (TypeAliasDeclaration)\n  DBMessage (TypeAliasDeclaration)\n\n# ~387 tokens (14 files shown)\n```\n\nFocused on a working file — the ranking re-centers on what `lib/db/queries.ts` actually touches:\n\n```\n$ node agentmap.mjs --map --focus lib/db/queries.ts --tokens 350\n# agentmap (154 files, sha 2becdb4) — focus: lib/db/queries.ts, budget ~350 tok\n\nlib/utils.ts:\n  cn (FunctionDeclaration)\n  generateUUID (FunctionDeclaration)\n  getDocumentTimestampByIndex (FunctionDeclaration)\n  fetcher (VariableDeclaration)\n  getTextFromMessage (FunctionDeclaration)\n  convertToUIMessages (FunctionDeclaration)\n  fetchWithErrorHandlers (FunctionDeclaration)\n  sanitizeText (FunctionDeclaration)\n\nlib/db/schema.ts:\n  DBMessage (TypeAliasDeclaration)\n  Suggestion (TypeAliasDeclaration)\n  Document (TypeAliasDeclaration)\n  Chat (TypeAliasDeclaration)\n  User (TypeAliasDeclaration)\n  chat (VariableDeclaration)\n  document (VariableDeclaration)\n  message (VariableDeclaration)\n\nlib/errors.ts:\n  ChatbotError (ClassDeclaration)\n  ErrorCode (TypeAliasDeclaration)\n\n# ~324 tokens (8 files shown)\n```\n\n### `--print` — full map as JSON\n\nDumps the cached map (`hubs`, `features`, `rankedSymbols`, `files`) as one JSON object —\nfor piping into other tools. Also includes a top-level `fileCount`.\n\n```\n$ node agentmap.mjs --print | jq '.hubs[0]'\n\"lib/utils.ts (deg 52, pr 0.105171)\"\n```\n\n### `--export <mermaid|dot>` — visualize the import graph\n\nSerializes the file import graph (nodes = files, edges = imports, top-N by PageRank, with\nthree light style tiers) as **Graphviz DOT** or **Mermaid** — paste straight into\n[mermaid.live](https://mermaid.live), a GitHub README mermaid block, or `dot -Tsvg`.\n`--focus <path>` scopes to a file's 1-hop neighborhood. It reads the cached map only (no\nts-morph Project), and prints graph text to stdout (so it isn't combined with `--json`).\n\n```\n$ node agentmap.mjs --export mermaid --focus lib/auth.ts\n%% agentmap import graph — 154 files, sha a1b2c3d, focus lib/auth.ts\nflowchart TD\n  classDef hub fill:#d9d9d9,stroke:#333,stroke-width:2px;\n  n0[\"lib/auth.ts\"]:::hub\n  …\n```\n\n### Global flags\n\n| Flag | Description |\n|------|-------------|\n| `--help` / `-h` | Print a usage block listing every flag and exit 0. |\n| `--version` / `-v` | Print the version from `package.json` and exit 0. |\n| `--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}`. |\n| `--no-locals` | Hide non-exported top-level declarations from `--find`/`--any` results (shown by default). Never affects `--map`/`--symbols`/`--hubs` ranking. |\n| `--include-dts` | Include `.d.ts` declaration files in the symbol/ranking pass (excluded by default so generated types don't flood `--find`/`--symbols`/`--hubs`). |\n| `--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. |\n| `--hook-status` | Report whether the post-commit hook, PreToolUse nudge, and `.gitignore` entry are installed (no writes). |\n| `--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. |\n| `--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). |\n| `--setup-mcp` `[--dry-run]` | Configure agentmap as an MCP server for OpenCode and the Antigravity IDE (merge-safe). `--dry-run` previews without writing. |\n| `--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`. |\n\n**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.\n\n---\n\n## Scope & limitations\n\nHonesty first — this is deliberately a small, sharp tool, not a universal code-graph.\n\n- **TS/JS (+ Vue SFC), by design.** Built on `ts-morph`. Indexes `.ts/.tsx/.mts/.cts/`\n  `.js/.jsx/.mjs/.cjs` and the `<script>` blocks of `.vue` single-file components\n  (best-effort). No Python, Go, Rust, etc. — if your repo isn't TypeScript/JavaScript, use a\n  tree-sitter-based tool instead. **Want another language?\n  [Vote in #43](https://github.com/raymondchins/agentmap/issues/43)** — and read\n  [what a non-TS language would actually get](#what-another-language-would-actually-get)\n  first, because it would not be the same product.\n- **The persisted map is a file-level import graph; the call graph is opt-in.** The\n  cached map's edges come from static `import` / re-export declarations and the named\n  symbols crossing them — `--relates` answers the file-level question (\"who imports this\n  module\"). Symbol-level, compiler-accurate call-site resolution is available on demand via\n  `--callers` (who calls a symbol) and `--calls` (what a symbol invokes) — both experimental,\n  lazy, out-of-band queries that spin up the type-checker only when invoked and are never\n  folded into the fast map build. The file-level graph additionally records a React Server\n  Components client/server boundary tag (from `'use client'`/`'use server'` directive\n  prologues) where present.\n- **Alias & workspace resolution.** Resolves `tsconfig`/`jsconfig` `paths`, `vite`/`vitest`/\n  `webpack` `resolve.alias` (string entries, parsed from the AST — the config is **never\n  executed**), and pnpm/npm/yarn workspace cross-package imports (`@org/pkg` → its source).\n  A build reports `edgeCoverage` (the share of repo-local imports that resolved) and prints a\n  one-line warning when a repo's imports mostly *don't* resolve — so a broken/empty map is\n  never silently framed as success.\n- **Scoping — `.agentmapignore` + `.d.ts`.** Generated `.d.ts` declaration files are excluded\n  from the symbol ranking **by default** (so a 200-symbol generated types file, or\n  `next-env.d.ts`, doesn't flood `--find`/`--symbols`/`--hubs`); `--include-dts` restores\n  them, and they stay live import-resolution targets either way. A repo-root `.agentmapignore`\n  (gitignore-style subset: anchored `/`, dir `/`, `*` globs, `#` comments) excludes extra paths.\n- **PageRank + symbol ranking are real and implemented** (damping 0.85, deterministic\n  power iteration; personalized variants for `--relates` and `--map --focus`). The symbol\n  ranking is a faithful port of Aider's identifier-graph approach (credit:\n  [Aider](https://github.com/Aider-AI/aider), Apache-2.0).\n- **Feature detection assumes the Next.js `app/` router.** `--feature` / `--features`\n  derive features from the first real route segment under `app/` (or `src/app/`), skipping\n  route groups `(...)`, dynamic `[...]`, and parallel `@...` segments. Repos without an\n  `app/` directory simply report zero features — every other command still works.\n- **Token counts are estimates** (`chars / 4`), not a real BPE tokenizer. Treat\n  `--map`/`--tokens` budgets as approximate (±10%).\n- The PreToolUse hook is **Claude Code-specific** (it speaks Claude Code's hook JSON). The\n  post-commit hook is generic git.\n\n---\n\n## What another language would actually get\n\nPublished *before* any of it is built, because the cheapest way to find out you're\nbeing asked for a different product is to describe the product accurately first.\n\nagentmap's accuracy comes from `ts-morph` — a real TypeScript compiler with a type\nchecker. Another language would be parsed with tree-sitter: syntax, no types, no\nmodule resolver. That difference decides what each query can honestly return.\n\n| Query | TS / JS today | Another language |\n|---|---|---|\n| `--relates` (blast radius) | full | **full** — ports best; this is the one to lead with |\n| `--search` (BM25) | full | **full** |\n| `--hubs` / `--map` / `--symbols` (PageRank) | full | **full** |\n| `--print` / `--export` | full | **full** — reads the cached map only |\n| `--find` | full | **partial** — no transitive re-export/barrel chains |\n| `--any` | full | **partial** |\n| `--callers` / `--calls` | full | **none — refuses, explicitly** |\n| `--features` / `--feature` | Next.js App Router | **none** |\n| incremental rebuild | yes | **no — full rebuild every time** |\n\n`--callers` / `--calls` are ~259 lines of TypeScript language-service calls.\nTree-sitter cannot reproduce them. The options are to refuse loudly or to guess by\nname-matching, and **name-matching will not ship**: a silently mis-wired graph is\nworse than no graph, and it would falsify every accuracy claim here.\n\nTwo non-TS languages is the honest ceiling for one part-time maintainer — each one\nis an ongoing tax, not a one-off. Reasoning, including the arguments *against*\ndoing this at all, is in [ROADMAP.md Part II](ROADMAP.md#part-ii--make-it-useful-for-the-majority-2026-07-26).\n\n**No telemetry.** agentmap makes zero network calls. When an unsupported language\ndominates your repo it counts the files locally, prints a one-line pointer to the\nvote, and forgets. That count never leaves your machine. Silence it with\n`AGENTMAP_NO_CENSUS=1`.\n\n## Forks & ports\n\nTwo people ported agentmap to another language rather than open an issue, which is\nthe strongest evidence that the \"nobody asked\" reading was wrong:\n\n- [`rifanid98/agentmap-go`](https://github.com/rifanid98/agentmap-go) — Go\n- [`dstwn/agentmap-php`](https://github.com/dstwn/agentmap-php) — PHP / Laravel\n\nNeither is affiliated with this repo and neither is endorsed — listed because\npretending they don't exist would be dishonest about demand. If you maintain one:\nupstreaming beats competing, please open an issue.\n\n---\n\n## Contributing\n\nIssues and PRs welcome. High-value directions:\n\n- Retrieval-accuracy eval — **done** ([`EVAL.md`](./EVAL.md), `npm run eval`). Next: a\n  type-aware dependents mode (the eval excludes type-only edges to match the value-import\n  graph) and an `app/`-router fixture so `--feature` retrieval can be scored too.\n- A real tokenizer behind the `--map` budget.\n- Hardening feature detection for non-`app/`-router layouts.\n\nKeep the dependency footprint minimal — `ts-morph` is the only runtime dependency (it bundles\nthe TypeScript compiler, ~10 MB installed), and keeping it that way is a feature.\n\n## License\n\n[MIT](./LICENSE). Symbol-ranking algorithm credit: [Aider](https://github.com/Aider-AI/aider) (Apache-2.0).\n",
  "bytes": 55799,
  "sha": "cb35f2a8e6e1b6c6ed19ccdd18758cf28c6e33f5cf9841ffa983fbcc1887d021",
  "repo_slug": "raymondchins/agentmap",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_raymondchins_agentmap_4d2438cb/readme"
}