using ModelContextProtocol;
namespace ClaudeDo.Worker.Lifecycle;
///
/// Reports MCP/hub progress for a long-running operation so a waiting caller doesn't hit an
/// idle-silence abort (Claude Code's MCP client aborts an MCP call after ~300s of silence).
///
internal static class ProgressReporter
{
///
/// Awaits while reporting progress every .
/// rides the same cadence for non-MCP callers (e.g. the Hub, which
/// turns it into an OperationProgress broadcast). No-op passthrough when both are null.
///
public static async Task RunAsync(
Task work, TimeSpan interval, IProgress? progress, string message,
Action? onTick = null)
{
if (progress is null && onTick is null) return await work;
var sw = System.Diagnostics.Stopwatch.StartNew();
while (true)
{
var finished = await Task.WhenAny(work, Task.Delay(interval));
if (finished == work) return await work;
progress?.Report(new ProgressNotificationValue { Progress = 0, Message = $"{message} ({sw.Elapsed:mm\\:ss})" });
onTick?.Invoke(sw.Elapsed);
}
}
///
/// Reports progress for one processed item out of a known total, e.g. "cleaning worktrees
/// (3/12)". For callers iterating a batch (cleanup_task_worktree, get_task_diff,
/// preview_merge_set) instead of awaiting one long-running task.
///
public static void ReportItem(
IProgress? progress, string action, int current, int total)
{
progress?.Report(new ProgressNotificationValue
{
Progress = current,
Total = total,
Message = $"{action} ({current}/{total})",
});
}
}