Ouroboros Tracing
Записывает, как код на самом деле исполнялся — вызовы, доводы, возвращённые значения, исключения, длительности. Обмазывает исходник на восьми языках — Python, JavaScript/TypeScript, C, C++, Elixir, Go, Java и C# — так, что запуск обмазанного кода дописывает по две строки JSONL на вызов в файл debug.info, и даёт средства читать этот файл с отбором и постранично. Берите, когда надо понять поведение работающей программы: разобрать чужой код, найти ошибку, которой не видно по итоговому выводу, снять снимок «было так» перед правкой, найти медленные вызовы или зависшие. Для C и C++ есть шесть средств навигации по дереву на clangd и clang-tidy, чтобы выбрать, что обмазывать. Не берите, чтобы узнать, как код ДОЛЖЕН работать, и не считайте записи доказательством правильности.
Skill metadata
| Source | Bundled (installed by default) |
| Path | skills/software-development/ouroboros-tracing |
| Version | 2.1.0 |
| Author | Digitable |
| License | BSD-2-Clause |
| Platforms | linux, macos |
| Tags | ouroboros, tracing, instrumentation, mcp, debugging |
Reference: full SKILL.md
The following is the complete skill definition that Digit loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
Уроборос: записи о вызовах
О чём это
Инструмент правит исходник так, что каждая функция при вызове дописывает две
строки в файл debug.info: одну на входе, одну на выходе. По ним видно, что
звали, с чем, что вернулось, что упало и сколько это заняло.
Работает на восьми языках: Python, JavaScript/TypeScript, C, C++, Elixir, Go, Java, C#.
Есть два входа: командная строка ouroboros (17 подкоманд) и сервер MCP
ouroboros-mcp (17 средств). Это одна и та же машина, просто с двух сторон.
Все пути вида docs/…, bench/…, scripts/… и SPEC.md ниже — это файлы в
хранилище инструмента: <https://github.com/digitable-lol/ouroboros>. Там же лежит
и подлинник этого навыка, skill/SKILL.md, из которого сделана эта копия: правки
вносят туда, а не сюда.
Читайте раздел «Границы» до того, как делать выводы из записей. Записи говорят, как код себя вёл, а не как он должен себя вести. Программа с ошибкой даёт записи, в которых ошибка выглядит нормой.
Когда брать
- Надо понять, как работает незнакомый код, — а читать его целиком дорого.
- Ошибка не видна ни по итоговому выводу, ни по отслеживанию стека: неверное значение рождается в середине цепочки и до вывода не доходит.
- Программа идёт долго и молчит, и непонятно, где она сейчас.
- Нужен снимок «до правки» — чтобы после правки сверить, что поведение то же.
- Нужно найти медленные вызовы или те, что вошли и не вернулись.
Когда не брать
- Надо узнать, что код должен делать. Спросите человека или спецификацию.
- Надо доказать, что код правильный. Записи этого не могут по устройству.
- Ошибка уже видна по выводу или по отслеживанию стека — дешевле посмотреть туда.
- Горячий путь, где важна каждая микросекунда, и обмазать надо всё.
Что нужно поставить
Обязательно: Python 3.12 или новее. Всё остальное — только под тот язык, который собираетесь обмазывать и запускать.
| зачем | что |
|---|---|
| обмазать и читать записи на любом языке | Python ≥ 3.12 |
| запустить обмазанный JavaScript/TypeScript | Node |
| собрать и запустить обмазанный C/C++ | gcc или clang |
| собрать и запустить обмазанный Elixir | Elixir и Erlang/OTP |
| обмазать, собрать и запустить обмазанный Go | Go (разбор идёт его же средствами) |
| собрать и запустить обмазанный Java | JDK (javac и java) |
| обмазать, собрать и запустить обмазанный C# | .NET SDK (разбор берёт Roslyn из самого SDK) |
| шесть средств навигации по C/C++ | clangd и clang-tidy на PATH |
| черновик и чистовик | git |
libclang (нужен разбору C и C++) и @babel/parser (нужен разбору JavaScript)
приезжают вместе с пакетом. Отдельно ставить не надо.
Установка
uv tool install git+https://github.com/digitable-lol/ouroboros
Installed 2 executables: ouroboros, ouroboros-mcp
Через Homebrew — одной строкой, Python доставится сам:
brew install digitable-lol/tap/ouroboros
Через asdf — когда рядом нужно держать несколько выпусков и переключать их:
asdf plugin add ouroboros https://github.com/digitable-lol/ouroboros.git
asdf install ouroboros latest
asdf set ouroboros latest
Эти три способа — Homebrew, asdf и uv tool install — основные. Всё описанное
здесь снято с выпуска 0.5.0.
На PyPI пакета нет, поэтому pip install ouroboros-logger не сработает.
Из локального хранилища ставится обычным pip install <путь к хранилищу>.
Остальные способы (один файл-программа, образ) — docs/install.md.
Проверка, что встало:
ouroboros languages
{"languages": ["python", "javascript", "c", "cpp", "elixir", "go", "java", "csharp"]}
Ставятся две команды: ouroboros и ouroboros-mcp. Других нет.
Для C и C++ нужен ещё компилятор C. Разговор с libclang идёт через маленькую родную программу, которая собирается один раз на машине при первом обмазывании файла на C или C++. Заголовки llvm для этого не нужны, компилятор — нужен. На машине, где обмазывают C, он и так есть; на машине, где обмазывают только Python или JavaScript, ничего доставлять не надо.
Подключить сервер MCP
{ "mcpServers": { "ouroboros": { "type": "stdio", "command": "ouroboros-mcp" } } }
Из исходников, без установки:
{ "mcpServers": { "ouroboros": {
"type": "stdio", "command": "uv",
"args": ["run", "--directory", "<путь к хранилищу>", "ouroboros-mcp"] } } }
Проверка: спросите у агента список средств. Должно прийти семнадцать имён.
Порядок работы
обмазать -> запустить -> прочитать
Обмазать. Три способа, по нарастанию осторожности:
| средство | что делает |
|---|---|
wrap_code_snippet(code, language) | обмазывает строку в памяти, на диск не пишет |
wrap_file(path) | обмазывает файл на месте, все функции |
wrap_functions(path, functions) | обмазывает только названные функции |
На большом или горячем файле берите wrap_functions: обмазка всего файла
затопит файл записей, и нужное в нём утонет.
Повторная обмазка безопасна: уже обмазанная функция пропускается, новая добавляется.
Запустить. Как обычно — своей командой, своими проверками. Файл записей
задаётся переменной OUROBOROS_DEBUG_INFO; если не задана, записи идут в
./debug.info рядом с рабочим каталогом.
Обмазка кладёт рядом с файлом помощника — его имя приходит в ответе полем
runtime_header (ouroboros_runtime.py, .go, .hpp и так далее). Его надо
собирать и запускать вместе с программой. У Python и C++ это выходит само;
у Go и Java — нет.
- Go:
go run main.goне найдёт помощника и упадёт наundefined: _ouroEnter. Запускайтеgo run .в модуле (тогда помощник подхватывается сам) либо перечислите его явно:go run main.go ouroboros_runtime.go. - Java: помощник — класс
ouroboros.OuroborosRuntime, то есть он объявляет пакетouroboros. Простоеjavac App.java OuroborosRuntime.javaсоберётся, но при запуске упадёт наNoClassDefFoundError: ouroboros/OuroborosRuntime: без-dкласс лёг рядом с исходником, а не в папку своего пакета. Собирайтеjavac -d . App.java OuroborosRuntime.java. - C#: ничего делать не надо — SDK сам подбирает все
.csв проекте.
Прочитать.
| средство | что делает |
|---|---|
read_trace(path, …) | отбирает записи: по имени функции, по содержимому, по исходу, по длительности, по потоку; выдаёт постранично |
trace_stats(path, …) | сводка: сколько раз какую функцию звали, настоящие длительности, что вошло и не вернулось |
Начинайте с trace_stats — он даёт карту. read_trace уже по ней.
Полезные отборы: min_duration находит медленные вызовы, outcome: "raised" —
упавшие, in_flight в сводке — те, что вошли и не вернулись (зависание или
падение).
Черновик и чистовик
Отдельный способ работы, когда код пишете вы (или агент), а не берёте готовый.
create_project -> write_file -> execute -> finish
черновик обмазка при запуск копия в
с git сохранении чистовик
create_project(base)заводит<base>/черновик/— обычное хранилище git.write_file(base, rel_path, content)обмазывает содержимое перед записью на диск и делает свой оборот. Код, который не разбирается, не сохраняется — средство отвечает отказом с сообщением разборщика.execute(base, command)запускает команду внутри черновика, подставивOUROBOROS_DEBUG_INFO, и дописывает в тот же файл строку о самом запуске.finish(base)копирует черновик в<base>/чистовик/.
finishне снимает обмазку. Копия в чистовике обмазана ровно так же, как черновик. Необмазанного текста нигде нет:write_fileобмазывает буфер до того, как байты попадут на диск, так что ни в черновике, ни в его истории git подлинника автора никогда не было. Средство честно называется копированием, и в ответе стоит"instrumentation_removed": false.Что копия не уносит:
.git,debug.info, кеши средств и всё, что выглядит собранным (двоичные файлы, объектные файлы, дампы памяти). Каждое такое имя перечислено вskippedс причиной — если там оказалось нужное, перенесите сами.
Выбрать, что обмазывать в дереве C и C++
Шесть средств поверх clangd и clang-tidy. Они нужны до wrap_functions,
чтобы выбрать функции, которые стоит обмазать, вместо обмазки целого горячего
файла.
| средство | вопрос, на который отвечает |
|---|---|
symbol_search(query, root) | где в дереве это имя |
document_symbols(path) | что определяет вот этот файл |
references(path, symbol) | кто это использует |
call_hierarchy(path, symbol) | кто это зовёт (или кого зовёт оно) |
describe_symbol(path, symbol) | где определено и с какой подписью |
lint_file(path) | что говорит clang-tidy про настоящие ошибки |
Три вещи, на которых здесь спотыкаются:
- Пяти средствам из шести нужен полный путь. Относительный путь даёт отказ
unresolvable URI at (root).textDocument.uri— это ответ clangd, не наш. Исключение —lint_file: ему относительный путь годится. - Всему, что смотрит за пределы одного файла, нужен
compile_commands.json. Путь к каталогу с ним передаётся доводомcompile_commands_dir. - Первый вызов на свежем дереве платит за построение указателя. Дальше он
лежит на диске. На большом дереве поднимайте
index_timeout. В ответе естьindex_complete: если тамfalse, ответ неполный.
Нет clangd или clang-tidy — средства отвечают понятным отказом, а не падают:
{"ok": false, "error": "clangd not found on PATH"}
Все семнадцать средств
| средство | обязательные доводы |
|---|---|
wrap_code_snippet | code, language |
wrap_file | path |
wrap_functions | path, functions |
read_trace | path |
trace_stats | path |
create_project | base |
write_file | base, rel_path, content |
read_file | base, rel_path |
list_files | base |
execute | base, command |
finish | base |
lint_file | path |
symbol_search | query, root |
document_symbols | path |
references | path, symbol |
call_hierarchy | path, symbol |
describe_symbol | path, symbol |
Все семнадцать — обычные средства MCP с обычными схемами. Никакого указателя
возможностей, никакого разбора схем по запросу, никакой подачи операций через
одно средство здесь нет. Полный справочник со всеми необязательными доводами —
docs/mcp-tools.md в хранилище; он снят разговором с живым сервером, а не
написан руками.
У командной строки тоже семнадцать подкоманд:
wrap-file, wrap-functions, wrap-snippet, trace, trace-stats, lint,
symbols, doc-symbols, refs, callers, describe, create, write,
execute, finish, languages, mcp.
Но это не те же самые семнадцать — совпадение чисел случайно. Общих пятнадцать, а по краям расходится:
- только у MCP:
read_fileиlist_files— прочитать файл в черновике и перечислить его файлы. Из командной строки для этого берут обычныеcatиls, отдельных подкоманд нет; - только у командной строки:
languages(какие языки поддержаны) иmcp(запустить сам сервер MCP). Средствами MCP их вызывать неоткуда.
Что лежит в записи
Две строки на вызов, связанные полем id:
{"p":"in","t":"2026-08-29T08:30:14.227","id":"7b0bd3b0-…","ci":-1,"th":"1175211.128643830919680","fn":"add","a":"0, 0","k":""}
{"p":"out","id":"7b0bd3b0-…","fn":"add","r":"0","d":2e-06}
{"p":"out","id":"e81a5517-…","fn":"div","x":"ZeroDivisionError: division by zero","d":5e-06}
| поле | где | что значит |
|---|---|---|
p | обе | in — вошли, out — вышли |
t | in | время входа |
id | обе | номер вызова; связывает вход с выходом |
fn | обе | имя функции |
a | in | значения доводов по позиции, через запятую |
k | in | именованные доводы как имя=значение |
r | out | возвращённое значение |
x | out | исключение как Вид: сообщение |
d | out | длительность в секундах |
ci | in | номер ядра |
th | in | метка потока |
Строка входа без пары — вызов, который вошёл и не вернулся. Зависание, падение, жёсткий выход. Это то, чего однострочная запись «по завершении» не могла бы показать вовсе.
Границы
Каждый пункт ниже проверен прогоном.
Чего в записях нет по устройству
- Того, что не выполнялось. Ветвь, в которую не зашли, записей не даст — и не пожалуется. Отсутствие записей читается как «тут всё спокойно», а значит оно «тут никто не был».
- Намерения. Функция вернула
-1— это код ошибки или ответ? В записи этого нет. - Вызовов через функцию-значение в Go.
func(x int) int { ... }в переменной или доводе не обмазывается, и её вызовы в записях не появятся. У Python вложеннаяdefобмазывается и видна (outer.<locals>.inner), у Go такой же по смыслу код молчит. Обмазываются только объявленные функции и методы. - Имён позиционных доводов — ни у одного из восьми языков. В поле
aвезде одни значения:2, 3. У C, C++ и Elixir подпись при обмазке разобрана и имена известны, но раньше эти три писалиa=2, b=3, и тогда поле значило у трёх языков одно, а у двух другое. Сейчас имён нет нигде, и по одной записи их не восстановить. - Номера ядра — ни у одного из восьми языков. Поле
ciв обычной программе всегда-1(в разборе —null). У Python потому, что функцииos.sched_getcpuв CPython нет вовсе; у остальных — потому, что переносимого способа узнать ядро в их средах нет. Настоящий номер бывает только в сборке C внутри ядра операционной системы. - Значений длиннее 200 знаков. Обрезаются, хвост не восстанавливается. Списки и словари вдобавок обрезаются по числу элементов — первые десять.
- Настоящей цены вызова. Поле
dмеряет обмазанный прогон, который идёт дольше обычного. Профилировщик записи не заменяют.
Что записывается не полностью
- C++ не записывает возвращённый объект классового типа — в поле
rстоит(no value). Провести такой возврат через помощник значило бы отменить пропуск копирования, который C++17 гарантирует: программа, считающая свои конструкторы, начала бы печатать перемещение, которого без обмазки не было, а тип с удалёнными копированием и перемещением перестал бы собираться. Доводы, длительность и факт исключения записываются как обычно. - C не записывает возвращённую структуру — то же
(no value), по той же причине: вида дляprintfу структуры нет. - У C не бывает поля
x— в языке нет исключений. - Длительность у C округлена до микросекунды, поэтому вызовы короче
микросекунды показывают
0.000000. - C# оставляет нетронутыми пять видов частей, потому что обмазанный вариант
не собрался бы: с
yield, возврат по ссылке, указатели, ссылочные структуры и свойства с телом-выражением. Про каждую такую часть обмазка возвращает предупреждение с причиной — молча они не пропадают. Доводoutне попадает в снимок доводов на входе: там он ещё не присвоен. - Java пропускает то, у чего нет тела — отвлечённые и родные методы, объявления в договоре. Замыкания и безымянные классы сами не обмазываются.
- Короткий вид записи для C (
--minimal) пишет только строку входа — без доводов, без возвращённого значения, без номера вызова и без длительности. Обычные средства чтения такие строки вызовами не считают: каждая попадает в список незавершённых. Он годится, чтобы увидеть дерево вызовов и глубину, и ни для чего другого.
Где обмазка меняет поведение программы
- Go: непойманная паника печатает не то же самое. Единственное место во всех
восьми языках, где меняется вывод программы. Чтобы записать вид и текст
паники, замыкание вызывает
recover()и паникует заново, поэтому программа, чью панику никто не ловит, печатает в поток ошибокpanic: bad [recovered, repanicked]вместоpanic: bad. Код возврата тот же, обычный вывод тот же, всякая пойманная паника приходит тем же значением. - Python: плюс кадр стека на каждый вызов. Замерено: при пределе рекурсии 200 достижимая глубина 199 без обмазки и 95 с обмазкой; при пределе 1000 — 999 и 495. Примерно вдвое мельче. Программа с тесно подогнанным пределом рекурсии может упереться там, где не упиралась. Для глубокой рекурсии обмазывайте не саму рекурсивную функцию, а тех, кто её зовёт.
- Изображение значения зовёт код самой программы. Чтобы записать довод,
надо спросить у него, как он выглядит, — а это ваш код:
__repr__в Python,toJSONв JavaScript,ToStringв Java и C#. Если такой метод что-то считает или меняет, обмазанная программа поведёт себя иначе. Проверено прогоном: программа, печатавшая «вызовов repr в самой программе: 0», после обмазки печатает «>0». Починить это нечем — записать значение, не спросив у значения, невозможно. Смягчено одно: бросок внутри такого метода не роняет программу, в запись идёт<Тип toString threw ...>. - Обмазка остаётся в файле, пока вы её не уберёте. Обратной команды нет, и
finishеё не делает. - В отслеживании стека появляются кадры обёртки между вашими.
Правило, которое стоит выполнять всегда: прогоняйте свои проверки ПОСЛЕ обмазки, а не только до неё. Зелёные и на обмазанном коде — правка для вашей программы безобидна. Полного перебора того, что ещё может сломаться, никто не делал, и утверждать, что список выше исчерпывающий, нельзя.
Чего стоит по времени и по месту
Замерено на одной функции add(a, b), 20 002 вызова, Linux, ext4. Все восемь
строк сняты одним прогоном scripts/measure/run.sh:
| язык | добавка на вызов | байт на вызов |
|---|---|---|
| C# | 15,6 мкс | 251 |
| C | 19,9 мкс | 248 |
| JavaScript | 20,4 мкс | 237 |
| Java | 20,9 мкс | 251 |
| C++ | 22,2 мкс | 269 |
| Go | 28,9 мкс | 243 |
| Python | 58,2 мкс | 253 |
| Elixir | около 190 мкс | 254 |
Не смешивайте эти числа с числами прошлых прогонов. Они заметно гуляют: при неизменном коде Python дал 63,5 микросекунды в прошлый раз и 58,2 в этот. Сравнивать языки между собой можно только внутри одной таблицы, снятой одним прогоном.
Что отсюда стоит унести:
- Дороже всего сама запись, а не язык. Четыре языка из восьми — C, JavaScript, Java и C++ — уложились в 20–22 микросекунды, хотя устроены совершенно по-разному. Столько стоит дописать в файл две строки на вызов.
- C# дешевле всех — 15,6 мкс, и это не про язык. Его помощник, как и у Java, держит файл открытым, тогда как помощники Python, C и C++ открывают и закрывают его на каждую запись.
- Go — 28,9 мкс, из них 3,3 стоит поле
th(номер горутины). Цена эта растёт вместе с глубиной стека: на глубине 100 один такой опрос обходится уже в 67 микросекунд. В глубоко вложенной программе добавка будет заметно больше. - Python дороже втрое (58 мкс): обёртка написана на самом Python, и каждая
запись собирается через
json.dumpsиreprlib. - Elixir дороже всех, и замер у него самый неустойчивый — отдельные прогоны дали от 161 до 202 микросекунд. Здесь верен порядок величины, а не третий знак.
- Миллион вызовов — примерно четверть гигабайта. От 237 до 269 байт на вызов при коротких доводах. Java и C# разошлись на один байт за пять мегабайт.
Считать по этим числам, «во сколько раз медленнее», нельзя: подопытная программа не делает ничего, кроме вызовов, и отношение выходит от 6 до 313 в зависимости только от того, насколько быстрым был пустой цикл на этом языке. Осмысленное число — добавка на вызов, и у семи языков из восьми она лежит в пределах 16–58 микросекунд.
Как повторить замер: scripts/measure/run.sh в хранилище.
Сколько от этого пользы — по замеру
Спрашивали так: дать отвечающему чужую программу и спросить, что она сделала на
этом запуске, — много ли добавляет трасса. Двенадцать программ, по пять
вопросов на программу, две группы. Мера и разбор ответов записаны до первого
прогона. Обе группы получали исходник целиком, команду запуска и то, что
программа напечатала; опытная вдобавок — debug.info того же запуска. То есть
контрольной группе хватало всего, чтобы вывести ответ, прочитав код.
Опыт лежит в scripts/measure/trace-help/, пересдаётся его run.sh. Снят он на
шести языках из восьми — Java и C# в него пока не входят, и про них эти
числа ничего не говорят.
Чем слабее отвечающий, тем больше польза
| кто отвечал | без трассы | с трассой | разница и её промежуток |
|---|---|---|---|
qwen3.5:4b (600 ответов) | 44,0 % | 78,3 % | +34,3 (19,7 … 47,7) |
qwen2.5:14b-instruct (600) | 61,0 % | 84,7 % | +23,7 (12,3 … 35,0) |
qwen3:32b (600) | 66,7 % | 90,3 % | +23,6 (10,3 … 36,3) |
| агент Claude Opus 5 (120) | 95,0 % | 98,3 % | +3,3 (0,0 … 8,3) |
На сильной модели польза не подтвердилась. У агента ноль попадает в промежуток, а порог был объявлен до прогона: если промежуток накрывает ноль — это не находка. Больше того, все четыре его ошибки из ста двадцати — «нет» вместо
false: верное значение в неверном виде. На этих двенадцати коротких программах сильная модель и так считает в уме, и трасса ей не нужна.
Это главное, что нужно унести о пользе, и звучит оно не в пользу инструмента.
Польза узкая даже там, где она есть
На средней модели прибавка сидит не везде, а только на вопросах о значениях:
| о чём вопрос | без трассы | с трассой |
|---|---|---|
| вызывалась ли функция вообще | 100 % | 100 % |
| кто бросил исключение | 100 % | 100 % |
| сколько раз вызвана функция | 58,9 % | 68,9 % |
| что вернул такой-то вызов | 46,4 % | 87,1 % |
| с чем позвали функцию | 50,0 % | 100 % |
На вопросе «вызывалась ли функция вообще» трасса не даёт ровно ничего — сто процентов в обеих группах. Берите записи ради значений: с чем позвали и что вернулось.
Когда запись не влезает в запрос
Пятый прогон — те же программы на входе в сотни раз большем: запись выходит 1,62 миллиона знаков против 4 670 знаков исходника, то есть в 347 раз больше самой программы. В запрос кладут обрезок в 8 000 знаков.
| ответ лежит | без трассы | с урезанной трассой |
|---|---|---|
| вызов уцелел в обрезке | 9,1 % | 65,5 % |
| вызов вырезан | 0,0 % | 10,8 % |
| нужна вся запись целиком | 0,0 % | 10,0 % |
Обрезка спасает только то, что в неё попало. Общий счёт вырос с 13,9 до
37,2 %, но по разбивке видно, что вся прибавка — на уцелевших вызовах. На
длинной программе не рассчитывайте прочитать трассу целиком: отбирайте
read_trace и trace_stats, а не кладите файл в запрос.
Отдельно: окупается ли она агенту по деньгам
Два других опыта (bench/RESULTS.md, bench/RESULTS_debug.md, по три прогона на
руку) мерили не понимание, а цену работы. Там, где ошибка видна по итоговому
выводу, инструмент не взяли ни разу из трёх — и это был верный выбор. Там, где не
видна, он побил ручную расстановку печати вдвое по длине ответа и на треть по
времени, но простое чтение кода оказалось не хуже.
Коротко
Записи окупаются, когда вопрос про значения, отвечающий не самый сильный и программу не прочитать глазами. Когда ответ виден по выводу, когда код короткий или когда читает сильная модель — не окупаются.
Что проверить, прежде чем считать работу сделанной
- Проверки программы прогнаны после обмазки и зелёные.
- Записи прочитаны средствами (
trace_stats,read_trace), а не глазами по файлу: файл на миллион вызовов читать глазами нечем. - Незавершённые вызовы (
in_flight) просмотрены — там зависания и падения. - Никто не считает «записи есть» доказательством «код правильный».
- Никто не считает «записей нет» доказательством «сюда не ходят»: возможно, сюда просто не зашли в этом прогоне.
- Если обмазан чужой код — решено, что с обмазкой делать дальше: она остаётся в файлах, пока её не уберут.
Где что лежит
| страница | о чём |
|---|---|
docs/install.md | все способы установки, подключение сервера MCP, переменные среды |
docs/getting-started.md | все команды и порядок работы |
docs/trace-existing-code.md | по шагам: прологировать чужой код |
docs/languages.md | чем восемь языков отличаются в записи |
docs/limits.md | границы — самая важная страница |
docs/measurements.md | замеры по восьми языкам и как их повторить |
docs/mcp-tools.md | справочник всех семнадцати средств |
scripts/measure/trace-help/ | опыт на 600 ответах: помогает ли трасса понять чужой код |
bench/RESULTS.md, bench/RESULTS_debug.md | два опыта про цену работы агента |
SPEC.md | схема записи |