The repository map

A bird's-eye view of the active charm, ranked by reference importance, that the agent carries into every turn.

Why the agent needs a map

A typical Juju charm is too big to paste into the LLM's context window in full, but small enough that the model benefits from knowing the rough shape rather than blindly grepping its way around. Without any structural cue, the first move on every turn becomes “list the directory, open three files, look at imports” — tokens spent on navigation that could have been spent on the actual change.

Cantrip mirrors the aider-style “repository map” pattern: a compact summary of the charm's most important files and the symbols they define, injected into the system prompt on every turn. When the agent reaches for code, it already knows where to look.

How the map is built

Three layers of analysis combine into a single ranking:

  1. Symbol extraction. Each source file is parsed for the symbols it defines — classes, functions, and module-level constants. Charm YAML files contribute their declared interfaces (relations, configs, actions).
  2. Reference graph. Within each file Cantrip records the symbols it references from elsewhere: callers point at callees, importers point at imports, charm-side YAML interface names link to the Python that handles them. The result is a directed graph over files-by-symbol.
  3. PageRank. Files are ranked by PageRank over the reference graph. A small utility module that every other file imports rises in the ranking even if its own line count is modest; a long but unreferenced scaffolding file falls.

The output is a per-file summary — the file's primary symbol with a +N more hint — ordered top to bottom by rank.

When it reaches the LLM

The map injects automatically into the system prompt under a configurable token budget (default 1500). Two adaptive thresholds keep it from crowding out room to think:

The map is regenerated incrementally: only files whose mtime changed get reparsed. A working session that touches a handful of files per turn pays a few milliseconds for the map; a clean clone or a large rename triggers a full rebuild.

Inspecting it with /map

Two slash commands expose the same map the agent sees:

/map
Compact summary — one line per top-ranked file. Useful before asking the agent to navigate, to confirm it has the right mental model of the codebase.
/map full
The wall-of-text version — every file with every symbol. Same view the agent receives. Overwhelming as a chat-panel default, but invaluable when you want to understand precisely what's in scope.

/map-refresh (and /map-refresh full) discards the cache and reparses every source file from scratch. Reach for it after a large rename or when the cache looks stale.

Caching and refresh

The map persists at .cantrip-repomap.json in the charm root, alongside the rest of Cantrip's per-charm state. The file is intentionally a JSON document so that it can be inspected with standard tools and is small enough to gitignore without losing meaningful state — the cache rebuilds in seconds on a fresh clone.

Because the map is keyed by file mtime, a git checkout that changes file timestamps invalidates the relevant entries automatically. The full rebuild trigger (/map-refresh) exists for the cases where filesystem mtimes lie — a tarball extraction that preserves the original timestamps, for instance.

See also