feat(planning): run interactive planning sessions via embedded ConPTY
Planning start/resume opened an external Windows Terminal (wt) window. Route them through the embedded ConPTY Command Center pane instead, matching the existing interactive-session UX (no external window). - Extract bare planning arg builders (BuildPlanningStartArgs/ResumeArgs) from WindowsTerminalLauncher; the wt path still uses them (kept, not removed). - InteractiveLaunchSpecService.BuildPlanningStart/Resume map a planning context into a LaunchSpec (planning args + env: MAX_THINKING_TOKENS, CLAUDEDO_PLANNING_TOKEN). Hub GetPlanningStart/ResumeLaunchSpec run StartAsync/ResumeAsync then return the spec. - UI: OpenPlanningSession + the resume branch raise OpenPlanningConPtyRequested; the shell opens Mission Control and hosts a planning ConPTY pane. Env is process-global by design (sequential human-paced sessions). wt planning code retained. Tests added for the arg/env mapping.
This commit is contained in:
@@ -76,6 +76,12 @@ sealed class FakeWorkerClient : IWorkerClient
|
||||
=> Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
public Task<LaunchSpec> GetAdHocLaunchSpecAsync(string directory, CancellationToken ct = default)
|
||||
=> Task.FromResult(new LaunchSpec(directory, "claude", Array.Empty<string>(), new Dictionary<string, string>()));
|
||||
public int PlanningStartSpecCalls { get; private set; }
|
||||
public int PlanningResumeSpecCalls { get; private set; }
|
||||
public Task<LaunchSpec> GetPlanningStartLaunchSpecAsync(string taskId, CancellationToken ct = default)
|
||||
{ PlanningStartSpecCalls++; return Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>())); }
|
||||
public Task<LaunchSpec> GetPlanningResumeLaunchSpecAsync(string taskId, CancellationToken ct = default)
|
||||
{ PlanningResumeSpecCalls++; return Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>())); }
|
||||
public Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default) => Task.CompletedTask;
|
||||
public Task ResumePlanningSessionAsync(string taskId, CancellationToken ct = default) { ResumePlanningCalls++; return Task.CompletedTask; }
|
||||
public Task<DiscardPlanningOutcome> DiscardPlanningSessionAsync(string taskId, bool dequeueQueuedChildren = false, CancellationToken ct = default)
|
||||
@@ -224,25 +230,29 @@ public class TasksIslandViewModelPlanningTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OpenPlanningSession_IgnoresNonIdleRow()
|
||||
public void OpenPlanningSession_IgnoresNonIdleRow()
|
||||
{
|
||||
var row = MakeRow("t1", TaskStatus.Queued);
|
||||
var (vm, worker) = VmFactory.Create([row]);
|
||||
var (vm, _) = VmFactory.Create([row]);
|
||||
(string TaskId, bool Resume)? raised = null;
|
||||
vm.OpenPlanningConPtyRequested += (id, resume) => raised = (id, resume);
|
||||
|
||||
await ((IAsyncRelayCommand<TaskRowViewModel?>)vm.OpenPlanningSessionCommand).ExecuteAsync(row);
|
||||
vm.OpenPlanningSessionCommand.Execute(row);
|
||||
|
||||
Assert.Equal(0, worker.StartPlanningCalls);
|
||||
Assert.Null(raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OpenPlanningSession_CallsWorkerForIdleRow()
|
||||
public void OpenPlanningSession_RequestsConPtyPaneForIdleRow()
|
||||
{
|
||||
var row = MakeRow("t1", TaskStatus.Idle);
|
||||
var (vm, worker) = VmFactory.Create([row]);
|
||||
var (vm, _) = VmFactory.Create([row]);
|
||||
(string TaskId, bool Resume)? raised = null;
|
||||
vm.OpenPlanningConPtyRequested += (id, resume) => raised = (id, resume);
|
||||
|
||||
await ((IAsyncRelayCommand<TaskRowViewModel?>)vm.OpenPlanningSessionCommand).ExecuteAsync(row);
|
||||
vm.OpenPlanningSessionCommand.Execute(row);
|
||||
|
||||
Assert.Equal(1, worker.StartPlanningCalls);
|
||||
Assert.Equal(("t1", false), raised);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user