@repomem/map
v0.1.5
Published
Structure map for repomem. Answers what/where: what each file is about and where a concept lives. Optional companion to @repomem/memory.
Maintainers
Readme
@repomem/map
npm package:
@repomem/map· CLI command:repomem-map(unchanged for convenience).
Status: MVP. Structure map for
@repomem/memory. It answers "what and where": what each file is about and where a concept lives. It is an optional companion to therepomem-memorymemory server.
The map stores one LLM-written summary per source file, committed to the repo, and serves them over MCP so an agent can search the codebase and read a module's summary. Summaries are generated by the agent that invokes the server — there is no LLM provider or API key configured in the map.
MVP scope
Included: in-agent summary generation, a committed JSON summary per file, an
in-memory MiniSearch index, and MCP tools (request_summaries,
save_summary, find_code, get_module) with a stale flag when a file
changed since its summary.
Also included: an in-memory dependency graph (file→file imports) with
blast-radius tools (get_dependencies, get_dependents) and an interactive
web viewer (open_graph / repomem-map graph). The graph is derived from
static imports and is independent of summaries — it works even if no summaries
have been generated.
Not yet (planned): symbol parsing, repo_overview, --check, merging with
memory (memory_for_path), embeddings, more languages in the dependency parser,
and an optional LLM-derived semantic dependency layer
(for relationships that are not static imports, e.g. dependency injection or
cross-service calls). The current graph is intentionally parse-based and
deterministic, which is what blast-radius measurement needs.
Setup — two levels
1. Register the MCP server (infra, once, manual)
Add the server to your MCP client (e.g. Kiro) alongside repomem-memory:
{
"mcpServers": {
"repomem-memory": { "command": "npx", "args": ["-y", "--package", "@repomem/memory", "repomem-memory", "serve"] },
"repomem-map": { "command": "npx", "args": ["-y", "--package", "@repomem/map", "repomem-map", "serve", "--root", "."] }
}
}repomem-map serves over stdio. Pass --root <dir> to point at the repository
root (defaults to the current working directory).
Instead of editing the config by hand, run the one-step setup from your repository root:
npx --package @repomem/map repomem-map setup --agent kiroIt installs a repomem-map-structure skill and merges the repomem-map server
into both .kiro/settings/mcp.json and the shared .kiro/agents/repomem.json
agent config. The merge is additive, so running it alongside the memory setup
leaves both servers and both skills side by side. It is idempotent and never
overwrites your existing entries (pass --force to refresh its own entry and
skill to the current version).
Only kiro is supported today.
Or configure both repomem servers at once with the installer: npx @repomem/cli setup.
2. Initialize the data (ask the agent)
Summaries are generated in-agent. Just ask your agent, e.g.:
"Initialize the repo map."
The agent then runs this loop using the map's tools:
request_summaries(limit?) -> { pending: [{ path, hash }], prompt, cap }
for each pending file:
(agent reads the file, writes a summary following `prompt`)
save_summary(path, summary, search_terms, hash)
repeat request_summaries() until pending is emptyThe summary prompt lives in prompts/summary.md and can be edited and reviewed
in a PR.
Tools
request_summaries(limit?)— files needing a summary (missing or stale), plus the prompt and a per-run cap (default 20). Does not send file contents; the agent reads files itself.save_summary(path, summary, search_terms, hash, model?)— persist one summary. Thehashmust come fromrequest_summariesand is re-validated against the file's current content.find_code(query)— search summaries, search terms and paths; returns compact hits.get_module(path)— the summary for a path, withstale: truewhen the file changed since the summary was generated;found: falsewhen there is none. Always includes the file's directdependenciesanddependentsfrom the import graph (even when there is no summary).get_dependents(path, depth?)— the files that depend onpath(its blast radius), each with its hopdistance. Transitive by default;depth: 1returns only direct dependents.get_dependencies(path, depth?)— the filespathdepends on, transitively, each with its hopdistance.depth: 1returns only direct dependencies.open_graph(port?)— start the web viewer and return its URL.
The dependency tools and the viewer are derived from the import graph and work without any summaries; summaries are optional and only enrich the viewer's side panel.
Dependency graph & web viewer
The map builds an in-memory file→file dependency graph by
scanning every supported source file and parsing its imports. TypeScript and
JavaScript (.ts, .tsx, .js, .jsx, .mjs, .cjs) are parsed with the
TypeScript AST; Python (.py, .pyi) is parsed with a built-in, dependency-free
scanner. The graph is never committed. It is built in the background right after
the server starts, then kept current while the server runs: when a tool asks
for the graph it re-checks the repository (at most once per second) and updates
it incrementally — one stat per source file, and only files whose mtime or
size changed are re-read and re-parsed. Files created, edited or deleted during
a session therefore show up in get_dependents without a restart. Nothing runs
in the background: if no tool queries the graph, there is no work.
- Nodes: every scannable TS/JS or Python file (independent of summaries).
- Edges: resolved static imports.
- TS/JS: relative imports (with implicit extensions and
index.*) andtsconfigpath aliases (paths/baseUrl) are resolved to repo files. - Python: relative imports (
from . import x,from ..pkg import y) and absolute dot-paths that point inside the repo (pkg.sub.mod→pkg/sub/mod.pyorpkg/sub/mod/__init__.py) are resolved. - Node builtins, npm packages, and the Python standard library / third-party packages are not edges.
- TS/JS: relative imports (with implicit extensions and
- Metrics per node: fan-in, fan-out, number of imports and exports. Python
exports are counted from
__all__when declared, otherwise from public top-level symbols (def/class/assignments not starting with_). - Cycles: dependency cycles are detected and highlighted.
Open the viewer either from the CLI or via the open_graph tool:
repomem-map graph --root . [--port 7700]It serves a local, interactive force-directed graph (D3). Click a node to
highlight its neighborhood and see its summary; dependency cycles are shown in
red; node size reflects either graph degree or imports+exports; and you can
filter by directory (scope), or focus on one file to a given depth. This
makes it quick to eyeball architectural smells — cycles and over-connected hubs.
The viewer frontend is a TypeScript app built with Vite and bundled locally (no CDN); the HTTP server is separate from the MCP stdio transport.
Storage
One JSON per source file under .repomem/map/summaries/, mirroring the code
path (e.g. src/db/client.ts → .repomem/map/summaries/src/db/client.ts.json).
Each record has hash, summary, search_terms, model, generated_at.
These files are marked linguist-generated in .gitattributes. The search
index and staleness are computed in memory at startup and never committed;
save_summary also updates the in-memory index, so a summary you save is
searchable in the same session.
Getting agents to actually use it
Like repomem-memory, the map reinforces its protocol through MCP so it also
works with a generic agent: the server sends instructions on initialize
(check get_dependents before changing a shared symbol, find_code before
editing unfamiliar code, refresh summaries when done) and every tool's
description says when to call it. The repomem-map-structure skill carries the
longer playbook. This is guidance, not enforcement, and it relies on the client
surfacing server instructions and tool descriptions to the model.
