estimate-maestro
estimate-maestro
Персональный MCP-сервер и инструмент командной строки для записи оценок и реального времени, чтобы оценки в итоге можно было сверять с результатами, а не с ощущениями.
Это инструмент сбора данных для калибровки оценок, а не оценщик. Он никогда ничего не оценивает за вас. Он записывает, во что вы оценили работу, каким оказался план и сколько реального времени заняла работа. Что вы делаете с этой записью — сравниваете, замечаете отклонения, решаете, что вы систематически оптимистичны в отношении определённого типа работ, — зависит от вас и от того, что потом читает хранилище. Задача инструмента заканчивается на честной записи.
Модель
Записываются четыре вида сущностей:
Effort — часть работы, которую стоит отслеживать отдельно: пул-реквест, мега-PR, всё, у чего есть план и размер. Effort существует с момента, когда у него есть артефакт плана (спека,
.PR.md, где бы план ни жил) и первоначальная оценка. До этого момента записывать ещё нечего.Slice — часть effort'а, обычно под-PR. Слайсы добавляются, закрываются по пул-реквесту, отбрасываются или переименовываются. Слайс, добавленный до начала effort'а, является частью исходного плана; добавленный после — это рост объёма, и в записи эти два случая остаются различимыми.
Interval — отрезок времени с включённым таймером, обычно по слайсу. Интервалы закрываются вручную, автоматически при завершении сессии с ещё работающим таймером или выводятся задним числом из временных меток коммитов, когда ничего не было засечено.
Absence — время, когда работа не велась вовсе: отпуск, приём, посторонняя встреча. Отсутствия записываются ретроспективно, и это ожидаемо. Они вычитаются из любого интервала, с которым пересекаются.
Related MCP server: time-tracker-ai-mcp
Оценки: тройки PERT в стори-пойнтах
Каждая оценка — это тройка PERT — оптимистичная, вероятная, пессимистичная — в абстрактной шкале пойнтов, никогда не в часах. Ожидаемое значение и стандартное отклонение выводятся из тройки при чтении; нигде не хранится одно число «оценка равна N», потому что голое число выбрасывает неопределённость, которая и делает оценку оценкой, а не фактом.
Пойнты привязываются сравнением текущей работы с предыдущими effort'ами похожей формы, а не переводом ощущаемого количества часов в пойнты в голове. Весь смысл абстрактной шкалы — сравнивать подобное с подобным между effort'ами, чью реальную длительность вы ещё не знаете.
Effort оценивается дважды:
Первоначальная оценка, сделанная по плану в его первом виде, при том же количестве слайсов, с которого план начинался.
Финальная оценка, сделанная на ретроспективе по плану, каким он фактически оказался — включая все слайсы, добавленные по ходу. Это контрфактическая оценка: «учитывая всё, что ты теперь знаешь о том, как это реально развернулось, что бы ты оценил?» — и никогда не «сколько это заняло?», и не измерение чего-либо.
Разделение оценки на два прохода позволяет после факта отделить два способа, которыми оценка может оказаться неверной. Первый проход может быть неверен из-за ошибки инвентаризации — существовала работа, о которой никто не знал, или работа, которая предполагалась существующей, оказалась несуществующей. Финальный проход, сделанный по плану, каким он фактически оказался, изолирует ошибку калибровки размера — элементы, о которых знали всё время и которые просто оценили неверно. Одно число на effort смешивает эти два; два числа, по одному на проход, держат их раздельно.
Пересмотренная оценка также может быть записана в середине effort'а — обычно когда добавляется слайс, которого не было в исходном плане, — не дожидаясь ретроспективы.
Модель времени: предполагается обычный день
По умолчанию предполагается обычный рабочий день, и записываются только отклонения от него. Включение и выключение таймера — исключение, а не рутина; инструмент не ожидает, что секундомер будет идти восемь часов каждый день. Конкретно:
Вручную засечённое время засчитывается полностью, в какие бы часы оно ни попало. Вечера и выходные включены: если вы включили таймер в 22:00 в субботу, это реальное время, и оно записывается как есть, без корректировки на рабочие часы.
Модель рабочих часов применяется только там, где реальное время не было записано. Есть ровно два места, где она используется:
Ограничение забытого таймера. Если сессия заканчивается с всё ещё работающим таймером, интервал закрывается в настроенное время окончания рабочего дня (или, вне настроенных рабочих дней и часов, ограничивается фиксированной длиной сессии), а не остаётся открытым бесконечно или не угадывается до минуты.
Восстановление интервалов из временных меток коммитов, когда для слайса никогда не было засечено время. Кластер близких коммитов становится производной рабочей сессией, и модель рабочих часов используется, чтобы решить, какая часть промежутка между ними считается работой, а не ночным разрывом.
Отсутствие, записанное за промежуток, вычитается из любого интервала — засечённого или производного — с которым оно пересекается: полностью для вручную засечённого интервала и через модель рабочих часов для производного.
Категории инструментов и правило заражения
Каждый инструмент, который предоставляет MCP-сервер, относится ровно к одной категории:
recording —
start_effort,add_slice,close_slice,drop_slice,rename_slice,clock_in,clock_out,annotate_interval,repair_interval,record_absence,record_revision,record_final_plan,harvest_commits,record_landed_commit,remap_commits,finish_effort.estimation —
reference_efforts, который показывает предыдущие effort'ы для сравнения, пока делается оценка.status —
current, который сообщает, что засечено и с какого времени.calibration —
effort_table,effort_detail,summary, которые читают накопленную запись обратно.
Инструменты recording, estimation и status никогда не возвращают длительность или агрегат. Только инструменты calibration могут. Это обеспечивается структурно: каждая модель ответа, используемая инструментом recording, estimation или status, проверяется на наличие имён полей, содержащих слова вроде hours, minutes, duration, wall, average или share, и проверка завершается ошибкой, если такое поле найдено.
Причина не в опрятности. Оценка, сделанная после того, как вы увидели реальную длительность работы, — это заякоренное эхо этой длительности, а не независимое суждение: число, которое вы выдадите, заражено тем, что вы только что видели ответ. Правило заражения не позволяет значению реального времени всплыть в ответе инструмента, пока делается оценка или идёт таймер, чтобы сравнение «первоначальная против финальной» оставалось честным. Инструменты calibration освобождены от этого, потому что их единственная цель — сообщать именно эти цифры, когда оценка уже завершена.
Переменные окружения
Все необязательны; у каждой есть значение по умолчанию. Читайте из src/estimate_maestro/config.py, а не из этого списка, если они когда-либо расходятся.
Переменная | По умолчанию | Значение |
|
| Путь к каталогу хранилища. |
|
| Начало рабочего дня (время ISO 8601). |
|
| Конец рабочего дня (время ISO 8601). |
|
| Начало ежедневного обеденного перерыва, исключается из рабочих секунд. |
|
| Конец ежедневного обеденного перерыва. |
|
| Дни недели через запятую, считающиеся рабочими (понедельник |
|
| Максимальная длина, присваиваемая ограниченной сессии, начавшейся вне окна рабочих часов. |
|
| Время, добавляемое перед самым ранним коммитом в производной сессии, чтобы покрыть работу до первого коммита. |
|
| Разрыв в рабочем времени между коммитами, после которого производная сессия считается завершённой и начатой новой. |
|
| Минимальное количество записанных effort'ов, прежде чем |
ESTIMATE_MAESTRO_DERIVE_LEAD_IN_MINUTES не должен превышать
ESTIMATE_MAESTRO_DERIVE_SESSION_GAP_MINUTES; в противном случае сервер отказывается запускаться.
Инструмент командной строки
estimate-maestro-cli покрывает повседневные действия записи, не требующие разговора: clock-in, clock-out, current, record-absence,
effort-table и summary. Действия, которые имеют смысл только как часть разговора об оценке — запуск effort'а, пересмотр оценки, запись финального плана, сбор коммитов — являются только инструментами MCP, а не командами CLI, намеренно: они должны происходить внутри сессии, показывающей сопоставимые предыдущие effort'ы, а не как голая команда, введённая без этого контекста.
Коды выхода
Код | Значение |
| Успех. |
| Произошла ошибка. |
| Только для |
Установка
Ни одно из следующих действий не было выполнено за вас — скопируйте команды ниже и выполните их сами.
Зарегистрируйте MCP-сервер:
claude mcp add --scope user maestro-estimate -- uv --directory ~/mcp/estimate-maestro run estimate-maestroСоздайте символические ссылки на два навыка в вашем личном каталоге навыков:
ln -s ~/mcp/estimate-maestro/skills/recording-work-time ~/.claude/skills/recording-work-time
ln -s ~/mcp/estimate-maestro/skills/estimating-work ~/.claude/skills/estimating-workСделайте хранилище git-репозиторием. Его файлы — обычный текст, и они только дополняются (append-only), поэтому хранилище можно коммитить и переносить между машинами; это единственная сохранившаяся копия того, что в нём записано: потерянное или неверное число невозможно восстановить больше ниоткуда. Держите его приватным — в нём хранятся названия проектов, имена веток и сведения о том, сколько времени заняли задачи, и ничего из этого не должно оказаться в публичном репозитории.
mkdir -p ~/.local/share/estimate-maestro
git -C ~/.local/share/estimate-maestro initРасположение хранилища по умолчанию следует спецификации XDG Base Directory Specification и не требует настройки. Задавайте ESTIMATE_MAESTRO_STORE только если вы хотите разместить хранилище в другом месте — например, рядом с вашими личными репозиториями, чтобы было проще не забывать коммитить и пушить. Переменная должна быть видна и серверу, и вашей оболочке, поэтому поместите её туда, откуда читают обе стороны (обычно это профиль вашей оболочки):
export ESTIMATE_MAESTRO_STORE=~/somewhere/elseПодключите два скрипта-хука в ~/.claude/settings.json: hooks/session-end.sh как хук SessionEnd (закрывает забытый таймер, ограничивая его настроенным рабочим днём) и hooks/session-start.sh как хук SessionStart (сообщает, запущен ли уже таймер):
{
"hooks": {
"SessionStart": [
{
"hooks": [
{ "type": "command", "command": "~/mcp/estimate-maestro/hooks/session-start.sh" }
]
}
],
"SessionEnd": [
{
"hooks": [
{ "type": "command", "command": "~/mcp/estimate-maestro/hooks/session-end.sh" }
]
}
]
}
}Объедините это с существующим блоком hooks в ~/.claude/settings.json, а не перезаписывайте его, если такой блок уже есть.
Разработка
uv sync --locked
uv run ruff format --check
uv run ruff check
uv run pyright
uv run pytest src/ -vAvailable Tools
21 toolsadd_sliceC
Add a slice. One added while the effort is active is scope growth.
Inputs: title (required), effort (required). Returns: recorded, identifier
annotate_intervalC
Attach meeting participants or a note to an interval after the fact.
Inputs: effort (required), interval (required), note, external, +2 more. Returns: recorded, identifier
clock_inC
Start an interval. Refused if one is already open.
Inputs: effort (required), kind, note, external, +3 more. Returns: recorded, identifier
clock_outC
Close the open interval.
Inputs: at, note. Returns: recorded, identifier
close_sliceC
Close a slice against its pull request.
Inputs: effort (required), work_slice (required), pull_request (required). Returns: recorded, identifier
currentB
What is clocked in, and since when.
Inputs: no named parameters. Returns: since, effort, interval, work_slice
drop_sliceC
Drop a slice that turned out to be unnecessary.
Inputs: effort (required), reason (required), work_slice (required). Returns: recorded, identifier
effort_detailC
One effort's full event history.
Inputs: effort (required). Returns: row, meta, slices, commits, +2 more
effort_tableC
One near-raw row per effort: estimates, wall hours, slice counts.
Inputs: no named parameters. Returns: result
finish_effortC
Mark an effort done.
Inputs: effort (required). Returns: recorded, identifier
harvest_commitsC
Copy commit metadata from a working tree into the store.
Inputs: path (required), since (required), until (required), effort (required), +1 more. Returns: recorded, identifier
record_absenceC
Declare time not spent working. Retroactive by design.
Inputs: kind (required), stop (required), start (required), note. Returns: recorded, identifier
record_final_planD
Record the final plan and the counterfactual estimate against it.
Inputs: effort (required), likely (required), plan_path (required), rationale (required), +3 more. Returns: recorded, identifier
record_landed_commitC
Record the durable commit that landed on the default branch.
Inputs: effort (required), identifier (required), repository (required), work_slice (required). Returns: recorded, identifier
record_revisionC
Record a revised estimate, typically when a slice is inserted.
Inputs: effort (required), likely (required), rationale (required), optimistic (required), +1 more. Returns: recorded, identifier
reference_effortsC
Prior efforts with their summaries and estimates, for comparison. No actuals.
Inputs: kind, limit. Returns: result
remap_commitsC
Map a rewritten commit id onto its replacement.
Inputs: new (required), old (required), effort (required), reason (required). Returns: recorded, identifier
rename_sliceD
Rename a slice.
Inputs: title (required), effort (required), work_slice (required). Returns: recorded, identifier
repair_intervalB
Correct an interval's bounds. Appends; never rewrites.
Inputs: effort (required), interval (required), stop, start. Returns: recorded, identifier
start_effortC
Create an effort from a plan artefact and an initial PERT estimate.
Inputs: kind (required), title (required), likely (required), plan_path (required), +5 more. Returns: recorded, identifier
summaryB
Aggregate calibration, or an honest refusal when there is too little data.
Inputs: no named parameters. Returns: note, efforts_counted, mean_hours_per_point, mean_inventory_growth, +2 more
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 21 tools
Every tool has a clearly distinct purpose. Even overlapping domains like commits (harvest, record, remap) and intervals (clock, annotate, repair) are separated by specific actions and targets. No two tools could be easily confused for the same operation.
The majority of tools follow a consistent verb_noun pattern (e.g., repair_interval, start_effort, add_slice). A few query tools are named as nouns (current, summary, effort_table, effort_detail), which is a minor deviation but still readable and sensible for retrieval operations.
With 21 tools, the surface is substantial but justified by the domain's complexity—covering efforts, intervals, slices, commits, estimates, and queries. Each tool adds distinct functionality, though the count is on the higher end and could feel heavy for simpler use cases.
The toolset provides comprehensive lifecycle coverage: starting and finishing efforts, managing slices (add/close/drop/rename), tracking time (clock in/out, absence), revising estimates, recording commits, and rich reporting (current, table, detail, summary). No obvious gaps or dead ends are apparent.
Maintenance
Related MCP Connectors
Personal time tracker: start timers, log time and summarize your hours by activity.
Time tracking you talk to: log, check and fix your work hours from your AI assistant.
Save and retrieve your chosen context, manage shared workspaces, and track project time.
Privacy-first work tracking with summaries, reports, coaching, and AI-ready long-term memory.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables time tracking in Tempo with automatic timesheet generation from git commits, worklog management with roles and accounts, and integration with Jira for seamless project time logging.1MIT
- AlicenseAqualityCmaintenanceEnables tracking work time with start/stop timers, logging entries, and generating reports.48 npm28 PyPIMIT
- FlicenseNot gradedqualityBmaintenanceEnables tracking worked hours by client, project, and task, with monthly/daily reports and natural language interaction through Claude.-
- AlicenseNot gradedqualityCmaintenanceEnables local-first time tracking with timers and time entries stored in a SQLite database that sync to Intervals in the background, with local-only reporting and queries.24 npmMIT