Skip to main content

agentcodemap

Tree-sitter harness для навигации и поиска по коду, рассчитанный на LLM-агентов. Возвращает компактные, машиночитаемые срезы кода вместо целых файлов. Установка: uv tool install agentcodemap. Команда после установки — codenav.

Проверенное фактическое поведение и ограничения: аудит CLI.

Команды

Общий приём: команды, которые сканируют каталог-корень (symbol, impact, trace, path, info, grep, astgrep, doctor), принимают несколько корней одним флагом --root DIR.... Индексируются только перечисленные каталоги — соседние директории того же уровня с кодом не попадают в поиск. Например, --root project tests ищет ровно в project/ и tests/, игнорируя прочие директории текущего уровня. По умолчанию корень — текущий каталог.

codenav outline PATH... [--top-level] [--deps] [--lines] [--filter REGEX...] [--max-chars N] [--pages SPEC]

PATH — файл или каталог (каталог обходится рекурсивно, outline каждого модуля). Компактная карта символов файла:

$ codenav outline project/mymodule.py
project.mymodule:
A MY_MODULE_ATTR

F my_func

C MyClass
 A my_attr
 M my_method

Модули без символов не выводятся. Дандеры не печатаются: магические методы (__init__, __repr__, __call__ и т.п.) и атрибуты-метаданные (__all__, __version__) — служебная механика, а не карта кода (магические методы при этом остаются в связях impact/trace).

Буквы: C класс, M метод, F функция, A атрибут/константа/тип. --lines добавляет L<start>-<end> к каждой записи.

--top-level сокращает вывод до верхнеуровневых символов модуля: вложенные члены (атрибуты, методы классов, внутренние классы/функции) не печатаются. Например, тот же файл с этим флагом:

$ codenav outline project/mymodule.py --top-level
project.mymodule:
A MY_MODULE_ATTR

F my_func

C MyClass

Флаг сочетается с --lines и действует на каждый модуль при обходе каталога.

Флаг --deps печатает под каждым символом его зависимости — символы, на которые ссылается тело этого символа, в виде -> имя [виды] (виды — короткие метки типов связи: call, inh, par, ref, ret, str, см. «Типы связей»). Ссылки разрешаются по индексу репозитория, поэтому связи видны и между файлами; зависимость приписывается ближайшему символу, который её содержит: класс не повторяет зависимости своих методов. Для PATH-файла индексируется содержащий его каталог. По умолчанию под каждым символом печатается одна строка; флаг --deps добавляет под ним зависимости:

$ codenav outline project/mymodule.py --deps
project.mymodule:
A MY_MODULE_ATTR

F my_func
 -> MY_MODULE_ATTR [ref]

C MyClass
 A my_attr
 M my_method
  -> MyClass.my_attr [ref]

Зависимости сочетаются с --top-level (тогда показаны зависимости только верхнеуровневых символов) и --lines. Символы без зависимостей печатаются как обычно, лишних строк нет.

Порядок и пагинация рассчитаны на большие проекты: модули сортируются от корня вглубь (сначала файлы в корне PATH, затем на один уровень глубже и т.д.) и нарезаются на страницы по --max-chars символов (по умолчанию 10000; строки никогда не разрезаются, разрыв страницы проходит между модулями). По умолчанию команда печатает первую страницу и сообщает, сколько страниц всего и как запросить остаток:

(page 1 of 4; 3 more: --pages 2-4)

--pages SPEC печатает выбранные страницы за один вызов: отдельный номер, диапазон или список (--pages 3, --pages 2-4, --pages 1,3; флаг можно повторять — страницы объединяются и печатаются по возрастанию). Так после первого запроса агент сразу берёт весь вывод:

$ codenav outline src --pages 2-4

Каждая страница заканчивается строкой (page N of M; …): последняя страница — (page M of M), остальные — с подсказкой, сколько частей и каким SPEC осталось запросить. Если один модуль больше размера страницы, он печатается на своей странице до границы строки, а его путь честно помечается в конце (not fully shown: … (module(s) larger than page size …)); чтобы прочитать такой модуль целиком, поднимите --max-chars.

--filter REGEX оставляет только модули, путь которых совпадает с регуляркой (проверка по пути файла, как в grep). Флаг можно повторять и перечислять несколько значений за раз: --filter schemas models — модуль остаётся, если совпал хотя бы один паттерн (OR). Например: --filter 'schemas|services'. Пагинация применяется уже к отфильтрованному набору.

Если после фильтрации ничего не нашлось или каталог пуст, команда завершается успешно с сообщением (no modules found) / (no modules match: …), а не пустым выводом и не ошибкой.

codenav diff [PATH] [--lines SPEC] [--lang LANG] [--repo DIR]

Нарезка кода по диффу: изменённые строки разворачиваются до содержащих их символов, соседние символы склеиваются (зазор ≤ 5 строк).

Источник изменений выбирается явно; флаги взаимоисключающие — два источника в одном вызове отклоняются:

$ codenav diff --working-tree --repo ../code-master  # tracked-изменения относительно HEAD (staged + unstaged), исходники — с диска
$ codenav diff --staged --repo ../code-master        # изменения индекса относительно HEAD, исходники — из индекса
$ codenav diff --base main --repo ../code-master     # main...HEAD (от merge base), исходники — из ревизии HEAD
$ git diff HEAD | codenav diff --stdin --repo .      # unified diff из stdin, даже на терминале
$ codenav diff src                                   # без флагов (прежнее поведение): терминал → git diff HEAD, pipe → stdin
$ codenav diff src/codenav/cli.py                    # PATH ограничивает diff одним файлом/каталогом
$ codenav diff src --lines '10,15-20'                # изменённые строки задаются явно (нужен PATH)

При явно выбранном режиме результат одинаков с терминала и без терминала. --repo DIR — каталог, в котором выполняются git-команды, поэтому результат не зависит от текущего каталога; пути из git-диффа разрешаются от корня репозитория, а для --stdin/--lines — от --repo (по умолчанию от текущего каталога). untracked-файлы git не диффит — в нарезку они не попадут. Без PATH нарезаются все изменённые файлы (блоки разделяются ---, не-код файлы пропускаются), с PATH — только указанный файл. Полностью удалённые и новые модули не нарезаются — короткая пометка MODULE DELETED / NEW MODULE; при отсутствии изменений — (no changes).

codenav symbol NAME... [--root DIR...]

Исходник каждого символа по имени (простому или квалифицированному, например MyClass.my_method). Можно перечислить несколько имён в одном вызове — индекс строится один раз. Поиск идёт только по перечисленным --root (по умолчанию текущий каталог).

При нескольких совпадениях текущая реализация печатает первое в порядке обхода; для точного выбора используйте квалифицированное имя, когда оно однозначно. Если хотя бы одно имя не найдено, команда не печатает ничего и завершается с ошибкой, перечисляя отсутствующие имена.

Поиск ссылок именной; строки-литералы тоже сканируются (DI-регистрации вида "pkg.mod:Symbol" — тип str; кавычечные forward-аннотации — типы par/ret по позиции), docstring исключены.

Типы связей

Отношения в impact, trace, path, info и outline помечаются типом ссылки, которая их создала — по тому, как имя употреблено в исходнике. Печатается короткая метка (полные имена съедали бы контекст), полное имя принимает --kind:

Метка Тип Место ссылки
call call имя вызывается: helper(), obj.method()
inh inheritance имя в списке базовых классов: class A(B)
par param имя в аннотации типа вне позиции возврата: параметр f(x: Service), поле dep: Service, локальная переменная x: Service, в том числе кавычечная forward-ссылка body: "Service"
ref reference прочие упоминания имени (чтение, значение)
ret return имя в позиции возврата: аннотация -> Service или выражение return x / return build() — сторона производителя данных
str string слово из DI-строки ("pkg.mod:Symbol" в LazyService(...)); текстовый кандидат, синтаксисом не подтверждён

ret и par разделяют стороны потока данных по объекту: у trace Data --kind ret остаются только функции, которые его возвращают, у --kind par — только принимающие его на вход.

Если имя встречается в разных местах, связь получает сразу несколько типов ([inh,par]), они выводятся в алфавитном порядке.

Флаг --kind (impact, trace, path, info) оставляет только связи выбранных типов; несколько типов указываются сразу (--kind call param) или повтором флага (--kind call --kind str). Принимается и короткая метка (--kind par). В impact фильтр отсекает связи и оставляет в метках только выбранные типы; в trace/path/info он действует и на обход, поэтому цепочка не проходит через ребро отфильтрованного типа.

$ codenav trace Base --root project --kind call
Base:
make -[call]-> Base

codenav impact NAME... [--root DIR...] [--detailed] [--kind KIND...]

Цепочка влияния каждого символа (несколько имён за один обход индекса):

В обычном выводе пути не печатаются: выводятся отсортированные и уникальные ID объектов, к каждому — типы связей в квадратных скобках. --detailed добавляет путь, строки, тип сущности и места ссылок в виде метка@строка.

  • depends-on — пользовательские символы, на которые ссылается цель (включая всё её поддерево: методы и атрибуты класса);
  • dependents — символы, чьи тела ссылаются на цель.

Номера строк в --detailed принадлежат ссылающейся стороне: для depends-on это файл цели (он указан в заголовке), для dependents — сам перечисленный символ.

Связи учитывают кавычечные forward-аннотации (job_store: "JobStore" — тип par) и DI-строки в атрибутах класса (LazyService("pkg.mod:Symbol") — str). Docstring и произвольные строки в телах функций связей не дают.

$ codenav impact CodeReviewService --root project
impact chain for CodeReviewService:
- depends-on:
Constants [ref]
...
- dependents:
Services [str]

$ codenav impact my_func --root src --detailed
impact chain for my_func (src/mod.py:20-22):
- depends-on:
src/mod.py:10-12::mid function  [call@21]
- dependents:
src/cmd.py:30-33::cmd_outline function  [call@31]

codenav trace NAME... [--root DIR...] [--direction DIRECTION] [--depth N] [--max-paths K] [--kind KIND...]

Цепочки влияния через каждый символ (несколько имён за один обход индекса); ребро A -[call]-> B означает «A ссылается на B», а метка ребра — тип ссылки (см. «Типы связей»). Рёбра разрешаются тем же qualified/type-aware анализом, что и в impact.

--direction выбирает, какие стороны обходить (по умолчанию both):

  • both — цепочки через символ в обе стороны: кто на него ссылается и что он тянет за собой; бюджет --depth общий на обе стороны;
  • down — только вглубь того, что символ тянет за собой; цель первой в цепочке, поэтому весь бюджет --depth уходит в одну сторону и глубина не съедается «шумом» от потребителей слева;
  • up — только потребители: цепочки печатаются в порядке стрелки (referrer -[call]-> NAME).

--depth ограничивает глубину каждой отдельной цепочки — сколько символов цепочки будет выведено; глубина считается в символах: --depth 3 печатает цепочки не длиннее трёх символов, например side -[call]-> my_func (по умолчанию 3). --depth не ограничивает количество найденных цепочек — для этого есть отдельный страховочный потолок --max-paths. Цепочка, целиком содержащаяся в более длинной, убирается как дубликат. Одноимённые функции в разных файлах остаются разными узлами графа. Для списка прямых зависимостей — impact.

$ codenav trace my_func --root src
my_func:
side -[call]-> my_func -[call]-> mid -[call]-> base
top -[call]-> my_func -[call]-> mid -[call]-> base

$ codenav trace _collect_code_files --root src/codenav --direction down
_collect_code_files:
_collect_code_files -[ref]-> SKIP_DIRS
_collect_code_files -[call]-> detect_language

Если цепочек нет, команда печатает no influence data (0 paths) (при --direction down — no dependency chains (0 paths); усечение печатается как not shown: N paths (max_paths=…)).

codenav path SOURCE TARGET [--root DIR...] [--depth N] [--max-paths K] [--kind KIND...]

Ответ на вопрос «как SOURCE связан с TARGET»: все самые короткие цепочки от SOURCE до TARGET вдоль зависимостей (ребро A -[call]-> B означает «A ссылается на B», тип ребра — тип ссылки). Показываются только цепочки минимальной длины — без посторонних ветвей, которыми перегружен trace. Направление одно — от SOURCE к TARGET; обратный вопрос задаётся тем же способом: codenav path TARGET SOURCE. --depth считает символы цепочки, оба конца включены, и не длиннее --depth (по умолчанию 10): маршрут вне глубины — честно пустой ответ. --kind фильтрует ребра, как в trace.

$ codenav path _print_graph impact_entity --root src/codenav
_print_graph -> impact_entity:
_print_graph -[par]-> RepoIndex -[call,ret]-> impact_entity

Если пути нет (или он длиннее --depth), команда печатает SOURCE -> TARGET: no chains (0 paths) и завершается с кодом 0; усечение по --max-paths — как в trace: not shown: N paths (max_paths=…). Имя, которое нигде не найдено, — ошибка, как у symbol/trace.

codenav info NAME... [--root DIR...] [--depth N] [--max-paths K] [--kind KIND...]

Один обход индекса вместо трёх: аккумулированный ответ из symbol (полный исходник), trace (цепочки влияния) и impact (depends-on/dependents) для каждого имени. Секции идут в порядке symbol → trace → impact и повторяют формат соответствующих команд. Для части цепочек действуют собственные дефолты: --depth 20 и --max-paths 50 (флаги можно переопределить; --depth — та же семантика, что у trace). --kind фильтрует обе части: и цепочки, и список прямых зависимостей.

Каждая секция честно сообщает о невыведенной информации: отсутствующие зависимости помечаются (none found), отсутствие путей — no influence data (0 paths), усечение цепочек — not shown: N paths (max_paths=…). Если имя нигде не найдено, команда, как и symbol/impact/trace, завершается с ошибкой, перечисляющей отсутствующие имена.

$ codenav info my_func --root src
### my_func  (src/mod.py:20-22, function)
20  def my_func():
21      return mid()

my_func:
side -[call]-> my_func -[call]-> mid -[call]-> base
top -[call]-> my_func -[call]-> mid -[call]-> base

impact chain for my_func:
- depends-on:
mid [call]
- dependents:
cmd_outline [call]

codenav grep PATTERN... [--root DIR...] [--lang LANG] [--max-chars N] [--pages SPEC]

Короткая версия поиска по регулярке. Можно передать несколько выражений: символ, в теле которого совпало хотя бы одно из них, печатается один раз. Область поиска задаётся --root (по умолчанию текущий каталог).

Печатаются только совпавшие строки, зато заголовок блока несёт границы символа — путь:начало-конец::имя вид (тот же формат локации, что в impact --detailed), так что видно, в каком символе найдено и где он начинается и кончается. Обычный grep границ символа не знает.

$ codenav grep 'entity.kind' --root src
src/codenav/cli.py:438-474::_print_grep function
463	                f"{entity.qualified_name} {entity.kind}"
---
src/codenav/cli.py:351-408::_print_impact function
381	                    f"{entity.qualified_name} {entity.kind}"

codenav astgrep PATTERN... [--root DIR...] [--lang LANG] [--max-chars N] [--pages SPEC]

Расширенная версия того же поиска: вместо отдельных строк печатается полный исходник каждого найденного символа по его границам. Заголовок здесь — просто путь: границы видны по номерам строк самого исходника.

$ codenav astgrep 'def _grep_blocks' --root src
src/codenav/cli.py
411	def _grep_blocks(files: list[str], patterns: Sequence[str], lang: str | None):
412	    """Regex hits grouped by smallest enclosing symbol, in file order.
...
433	        for key in order:
434	            entity, matched_lines = blocks[key]
435	            yield file, entity, matched_lines, parsed.content_lines

Обе команды группируют хиты по наименьшей объемлющей сущности (метод, а не весь класс) и разделяют блоки ---; строки вне символов (импорты, константы модуля) в обеих версиях печатаются как есть — у них нет символа, чьи границы можно назвать.

Пагинация та же, что у outline: вывод режется на страницы по --max-chars (по умолчанию 10000 символов; границы блоков не режутся), печатается первая страница и подсказка (page 1 of N; …: --pages 2-N). --pages SPEC выбирает страницы (2, 2-4, 1,3; флаг повторяется). Блок крупнее страницы получает свою страницу целиком — совпавшие строки не обрезаются.

Если ничего не нашлось, команда завершается успешно с сообщением (no matches for: …) (или (no code files found), когда в корнях нет исходников), а не пустым выводом: агент отличает отработавший поиск без совпадений от сбоя.

codenav doctor [--root DIR...] [--verbose]

Диагностика индексации: что обход корней прочитал и почему остальное осталось за бортом. Отвечает на то, что «ничего не найдено» не различает: символа нет в исходниках — или файл вообще не индексировался (незнакомое расширение, лимит размера, файл не читается как UTF-8, разбор не удался, каталог вырезан обходом, корень не существует).

$ codenav doctor --root project tests
roots:
  project -> /work/project: 37 files indexed
  tests -> /work/tests: 12 files indexed
  vendor: does not exist
files: 49 indexed of 53 code files; 15 non-code files skipped
languages: python 40 files/780 symbols, javascript 9 files/14 symbols
skipped: too large 2, unreadable 1, parse failed 1, ignored dirs 4
extraction: 794 symbols; 3 <unknown> names in 1 files; syntax errors in 2 files; 5 files parsed without symbols
(paths behind these counts: --verbose)

Обход здесь ровно тот же, что у остальных команд (RepoIndex.scan_tree), поэтому отчёт описывает именно тот набор файлов, который они ищут, а не похожий: те же вырезанные каталоги (.git, node_modules, .venv, … и любые скрытые), тот же лимит размера файла (512 KiB), та же проверка языка по расширению, тот же разбор.

  • roots — каждый --root, как он задан: куда ведёт (realpath) и сколько файлов дал. Корень, которого нет (does not exist), файл вместо каталога (not a directory) и корень, уже покрытый другим (redundant (covered by another root)), названы явно; первые два к тому же завершают команду кодом 1 — опечатка в корне не должна выглядеть как успешно проиндексированный пустой проект.
  • files — сколько файлов попало в индекс из всех файлов с поддержанным расширением; файлы без поддержанного расширения считаются отдельно.
  • languages — файлы и извлечённые символы по языкам.
  • skipped — почему файл с поддержанным расширением не попал в индекс: too large, unreadable, parse failed, а также вырезанные каталоги (ignored dirs — один такой каталог скрывает целое поддерево).
  • extraction — измеренные признаки неполноты извлечения: имена <unknown> (объявление найдено, имя — нет), файлы, где tree-sitter оставил ERROR/MISSING-узлы, и модули, разобранные без единого символа. Успешный разбор здесь не подаётся как доказательство полноты — команда печатает только то, что измерила.

Сбой одного файла (не читается, не разбирается) не скрывает остальные: файл попадает в счётчик с причиной, а обход идёт дальше.

Обычный режим — сводка; --verbose допечатывает пути за каждым счётчиком (блок details:).

Команда описывает текущие возможности экстракторов, а не улучшает их: ноль символов у языка — сообщение о том, что искать в нём пока нечего.

Языки

Python, JavaScript/TypeScript/TSX, Go, Rust, Java, Scala, Ruby, PHP, C#, C/C++. Язык определяется по расширению; переопределяется флагом --lang.

Грамматики всех перечисленных языков загружаются, но полнота извлечения символов пока различается. Например, smoke-тест выявил <unknown> для части объявлений Go/C/C++ и пропущенный метод PHP; подробности есть в аудите CLI. Найти такие дыры на своём проекте помогает codenav doctor (см. выше): он считает <unknown>, модули с ERROR-узлами tree-sitter и модули, разобранные без единого символа.

Скилл и сабагент

  • .agents/skills/codenav-research/SKILL.md — скилл для агента: какую команду брать под какой вопрос, какие флаги режут вывод, какой бюджет держать на один результат.
  • .omp/agents/codenav-researcher.md — read-only сабагент (omp): ищет через codenav и возвращает отчёт с symbol — path:line — тип связи.
  • Замер A/B (codenav против read/grep/glob на одних и тех же задачах): benchmarks/codebase-research/ab_tokens.py.

Установка в пользовательский конфиг — симлинками, чтобы копии не расходились:

ln -sfn "$PWD/.agents/skills/codenav-research" ~/.agents/skills/codenav-research
ln -sfn "$PWD/.omp/agents/codenav-researcher.md" ~/.omp/agent/agents/codenav-researcher.md

Бюджет в скилле выведен из замеров на репозитории ~220 модулей: grep ≈ 200 символов, impact --detailed --kind call ≈ 2.4k, symbol Class.method ≈ 4.7k, symbol Class ≈ 24k, info ≈ 34k. astgrep отдаёт полный исходник каждого совпавшего символа, поэтому его счёт — как у symbol, умноженный на число попавших символов; для шага обнаружения он дорог.

Разработка

uv sync --group dev
uv run pytest

Metadata

Release files for agentcodemap 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agentcodemap 1.0.0
File Size Uploaded
agentcodemap-1.0.0.tar.gz 56.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agentcodemap 1.0.0
File Interpreter ABI Platform
agentcodemap-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 108.6 kB

Release files / agentcodemap-1.0.0.tar.gz

Download URL agentcodemap-1.0.0.tar.gz
Size 56.0 kB
Tags Source
SHA-256 checksum
How to use checksums
1786f31f3e9b72d5e7e69d416e10819629e20d5fa4a47607a6381ae78acf7df5
BLAKE2b-256 checksum
How to use checksums
9aa9ffb5ee68c075213b8a38fa3ca571941a2e7aaf8420f393c38ee09304b91c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.11 {"installer":{"name":"uv","version":"0.12.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / agentcodemap-1.0.0-py3-none-any.whl

Download URL agentcodemap-1.0.0-py3-none-any.whl
Size 52.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5307c3cb4f850d362e6304c6e2d5e18aa053cef9776b6bb989297db65dfdd0f0
BLAKE2b-256 checksum
How to use checksums
0419a15077c5152def8c63eb3e5043ff99369eaa603a31c62181e37e64b2c299
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.11 {"installer":{"name":"uv","version":"0.12.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page