feat(interactive): fresh-worktree-on-demand + ad-hoc launch specs

BuildForTaskAsync now creates a worktree on demand (via WorktreeManager.CreateAsync,
the same path TaskRunner uses) when a task has a configured working dir but no
Active/Kept worktree, returning a fresh-start spec -- so never-run tasks can be
opened interactively. Adds BuildForDirectoryAsync + GetAdHocLaunchSpec hub/client
for ad-hoc sessions in an arbitrary directory (no task, no worktree, no skill seeding).
This commit is contained in:
mika kuns
2026-07-23 16:47:15 +02:00
parent 0513265c49
commit 9ab48d7094
8 changed files with 148 additions and 11 deletions
@@ -2,9 +2,17 @@ namespace ClaudeDo.Worker.Runner;
public interface IInteractiveLaunchSpecService
{
/// <summary>Builds a LaunchSpec for opening an interactive session in an existing task's
/// worktree. Throws KeyNotFoundException if the task doesn't exist, InvalidOperationException
/// if it's Running/Queued or has no usable worktree. A task that has never run (no persisted
/// SessionId) is not an error -- the spec omits --resume for a fresh start.</summary>
/// <summary>Builds a LaunchSpec for opening an interactive session in a task's worktree.
/// Throws KeyNotFoundException if the task doesn't exist, InvalidOperationException
/// if it's Running/Queued. If the task has no usable worktree yet, one is created on
/// demand (same mechanism as an autonomous run) provided the task's list has a working
/// directory pointing at a git repo -- otherwise throws InvalidOperationException. A task
/// that has never run, or whose worktree was just created fresh, gets a fresh-start spec
/// (no --resume); an existing worktree with a persisted SessionId gets --resume.</summary>
Task<LaunchSpec> BuildForTaskAsync(string taskId, CancellationToken ct);
/// <summary>Builds a LaunchSpec for an ad-hoc interactive session in an arbitrary directory --
/// no task, no worktree, no session-skills seeding. Throws InvalidOperationException if the
/// directory doesn't exist.</summary>
Task<LaunchSpec> BuildForDirectoryAsync(string directory, CancellationToken ct);
}