Skip to main content
Glama

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, а не из этого списка, если они когда-либо расходятся.

Переменная

По умолчанию

Значение

ESTIMATE_MAESTRO_STORE

$XDG_DATA_HOME/estimate-maestro, при отсутствии — ~/.local/share/estimate-maestro

Путь к каталогу хранилища.

ESTIMATE_MAESTRO_DAY_START

09:00

Начало рабочего дня (время ISO 8601).

ESTIMATE_MAESTRO_DAY_END

17:00

Конец рабочего дня (время ISO 8601).

ESTIMATE_MAESTRO_LUNCH_START

12:00

Начало ежедневного обеденного перерыва, исключается из рабочих секунд.

ESTIMATE_MAESTRO_LUNCH_END

13:00

Конец ежедневного обеденного перерыва.

ESTIMATE_MAESTRO_WORKDAYS

0,1,2,3,4

Дни недели через запятую, считающиеся рабочими (понедельник 0 … воскресенье 6).

ESTIMATE_MAESTRO_OFF_HOURS_SESSION_CAP_MINUTES

240 (4 часа)

Максимальная длина, присваиваемая ограниченной сессии, начавшейся вне окна рабочих часов.

ESTIMATE_MAESTRO_DERIVE_LEAD_IN_MINUTES

30

Время, добавляемое перед самым ранним коммитом в производной сессии, чтобы покрыть работу до первого коммита.

ESTIMATE_MAESTRO_DERIVE_SESSION_GAP_MINUTES

120 (2 часа)

Разрыв в рабочем времени между коммитами, после которого производная сессия считается завершённой и начатой новой.

ESTIMATE_MAESTRO_MINIMUM_EFFORTS

5

Минимальное количество записанных effort'ов, прежде чем summary сообщит цифры, а не честный отказ.

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'ы, а не как голая команда, введённая без этого контекста.

Коды выхода

Код

Значение

0

Успех.

1

Произошла ошибка.

3

Только для clock-out: ничего не было засечено. Ожидаемо и часто, не сбой — скрипт, подключающий clock-out к автоматическому хуку, должен обрабатывать 3 так же, как 0.

Установка

Ни одно из следующих действий не было выполнено за вас — скопируйте команды ниже и выполните их сами.

Зарегистрируйте 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/ -v

Available Tools

21 tools
add_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

    C2.8/5.0

    Scored across 21 tools

    Disambiguation5/5

    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.

    Naming Consistency4/5

    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.

    Tool Count4/5

    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.

    Completeness5/5

    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

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers