feat(ui): add Run interactively action to task context menu
Spawns Windows Terminal in the list working directory running `claude --permission-mode auto` with the task title and description prefilled as the initial prompt. Reuses the planning launcher infrastructure but skips worktree, system prompt, and MCP setup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -346,6 +346,12 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub
|
||||
return ctx;
|
||||
}
|
||||
|
||||
public async Task OpenInteractiveTerminalAsync(string taskId)
|
||||
{
|
||||
var ctx = await _planning.OpenInteractiveAsync(taskId, Context.ConnectionAborted);
|
||||
await _launcher.LaunchInteractiveAsync(ctx, Context.ConnectionAborted);
|
||||
}
|
||||
|
||||
public async Task DiscardPlanningSessionAsync(string taskId)
|
||||
{
|
||||
await _planning.DiscardAsync(taskId, Context.ConnectionAborted);
|
||||
|
||||
@@ -4,6 +4,7 @@ public interface IPlanningTerminalLauncher
|
||||
{
|
||||
Task LaunchStartAsync(PlanningSessionStartContext ctx, CancellationToken cancellationToken);
|
||||
Task LaunchResumeAsync(PlanningSessionResumeContext ctx, CancellationToken cancellationToken);
|
||||
Task LaunchInteractiveAsync(InteractiveLaunchContext ctx, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed class PlanningLaunchException : Exception
|
||||
|
||||
6
src/ClaudeDo.Worker/Planning/InteractiveLaunchContext.cs
Normal file
6
src/ClaudeDo.Worker/Planning/InteractiveLaunchContext.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace ClaudeDo.Worker.Planning;
|
||||
|
||||
public sealed record InteractiveLaunchContext(
|
||||
string TaskId,
|
||||
string WorkingDir,
|
||||
string InitialPrompt);
|
||||
@@ -138,6 +138,40 @@ public sealed class PlanningSessionManager
|
||||
Files: files);
|
||||
}
|
||||
|
||||
public async Task<InteractiveLaunchContext> OpenInteractiveAsync(string taskId, CancellationToken ct)
|
||||
{
|
||||
var (tasks, lists, _, ctx) = CreateRepos();
|
||||
await using var __ = ctx;
|
||||
|
||||
var task = await tasks.GetByIdAsync(taskId, ct)
|
||||
?? throw new InvalidOperationException($"Task {taskId} not found.");
|
||||
|
||||
var list = await lists.GetByIdAsync(task.ListId, ct)
|
||||
?? throw new InvalidOperationException($"List {task.ListId} not found.");
|
||||
|
||||
var workingDir = list.WorkingDir;
|
||||
if (string.IsNullOrWhiteSpace(workingDir) || !Directory.Exists(workingDir))
|
||||
throw new InvalidOperationException(
|
||||
$"List '{list.Name}' has no valid working directory configured.");
|
||||
|
||||
return new InteractiveLaunchContext(
|
||||
TaskId: taskId,
|
||||
WorkingDir: workingDir,
|
||||
InitialPrompt: BuildInteractivePrompt(task));
|
||||
}
|
||||
|
||||
private static string BuildInteractivePrompt(TaskEntity task)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"# Task: {task.Title}");
|
||||
if (!string.IsNullOrWhiteSpace(task.Description))
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(task.Description);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public async Task<int> FinalizeAsync(string taskId, bool queueAgentTasks, CancellationToken ct)
|
||||
{
|
||||
var (tasks, lists, settings, ctx) = CreateRepos();
|
||||
@@ -287,25 +321,31 @@ public sealed class PlanningSessionManager
|
||||
}
|
||||
""";
|
||||
|
||||
private static string BuildSystemPrompt() =>
|
||||
"""
|
||||
You are a planning assistant for ClaudeDo.
|
||||
Your role is to help break down a task into smaller, actionable subtasks.
|
||||
Your final goal WILL ALWAYS be the creation of Subtasks
|
||||
private static string BuildSystemPrompt()
|
||||
{
|
||||
var fromFile = PromptFiles.ReadOrNull(PromptKind.Planning);
|
||||
if (fromFile is not null) return fromFile;
|
||||
|
||||
ALWAYS invoke the `superpowers:brainstorming` skill via the Skill tool at the
|
||||
start of every planning session, and follow its process end-to-end. It guides
|
||||
you through clarifying questions, approach exploration, and design approval
|
||||
BEFORE any subtasks are created. Do not create child tasks until the user has
|
||||
approved a design.
|
||||
|
||||
NEVER Change files yourself.
|
||||
|
||||
ALWAYS Use the available MCP tools (mcp__claudedo__*) to create child tasks once the
|
||||
design is approved. When you are done planning, finalize the session.
|
||||
return
|
||||
"""
|
||||
You are a planning assistant for ClaudeDo.
|
||||
Your role is to help break down a task into smaller, actionable subtasks.
|
||||
Your final goal WILL ALWAYS be the creation of Subtasks
|
||||
|
||||
Be concise and focused. Each subtask should be independently executable.
|
||||
""";
|
||||
ALWAYS invoke the `superpowers:brainstorming` skill via the Skill tool at the
|
||||
start of every planning session, and follow its process end-to-end. It guides
|
||||
you through clarifying questions, approach exploration, and design approval
|
||||
BEFORE any subtasks are created. Do not create child tasks until the user has
|
||||
approved a design.
|
||||
|
||||
NEVER Change files yourself.
|
||||
|
||||
ALWAYS Use the available MCP tools (mcp__claudedo__*) to create child tasks once the
|
||||
design is approved. When you are done planning, finalize the session.
|
||||
|
||||
Be concise and focused. Each subtask should be independently executable.
|
||||
""";
|
||||
}
|
||||
|
||||
private static string BuildInitialPrompt(TaskEntity task)
|
||||
{
|
||||
|
||||
@@ -74,6 +74,40 @@ public sealed class WindowsTerminalPlanningLauncher : IPlanningTerminalLauncher
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task LaunchInteractiveAsync(InteractiveLaunchContext ctx, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!Directory.Exists(ctx.WorkingDir))
|
||||
throw new PlanningLaunchException($"Working directory does not exist: {ctx.WorkingDir}");
|
||||
|
||||
var resolvedWt = Resolve(_wtPath)
|
||||
?? throw new PlanningLaunchException($"Windows Terminal not found: {_wtPath}");
|
||||
var resolvedClaude = Resolve(_claudePath)
|
||||
?? throw new PlanningLaunchException($"claude executable not found: {_claudePath}");
|
||||
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = resolvedWt,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = false,
|
||||
};
|
||||
|
||||
psi.Environment["MAX_THINKING_TOKENS"] = "20000";
|
||||
|
||||
psi.ArgumentList.Add("-d");
|
||||
psi.ArgumentList.Add(ctx.WorkingDir);
|
||||
psi.ArgumentList.Add(resolvedClaude);
|
||||
psi.ArgumentList.Add("--model");
|
||||
psi.ArgumentList.Add(Model);
|
||||
psi.ArgumentList.Add("--permission-mode");
|
||||
psi.ArgumentList.Add("auto");
|
||||
psi.ArgumentList.Add(ctx.InitialPrompt);
|
||||
|
||||
var proc = Process.Start(psi)
|
||||
?? throw new PlanningLaunchException("Failed to start Windows Terminal process.");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task LaunchResumeAsync(PlanningSessionResumeContext ctx, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!Directory.Exists(ctx.WorkingDir))
|
||||
|
||||
Reference in New Issue
Block a user