feat(interactive): seed fresh task session with the task prompt

A fresh (non-resume) task-based ConPTY session now opens claude on the task's
prompt (title + description) as the positional argument, so the session starts on
the task instead of an empty prompt. Resume sessions and ad-hoc sessions are
unchanged.
This commit is contained in:
mika kuns
2026-07-23 16:47:16 +02:00
parent d9a4627a1f
commit d8194ad57e
2 changed files with 43 additions and 4 deletions
@@ -89,9 +89,12 @@ public sealed class InteractiveLaunchSpecService : IInteractiveLaunchSpecService
var resolvedClaude = WindowsTerminalLauncher.Resolve(_claudePath)
?? throw new InvalidOperationException($"claude executable not found: {_claudePath}");
// Resume an existing session as-is; for a fresh session, seed the interactive TUI with
// the task's prompt (title + description) as claude's positional prompt so it starts on
// the task -- the user then supervises/answers rather than retyping it.
var args = run?.SessionId is { Length: > 0 } sessionId
? WindowsTerminalLauncher.BuildResumeArgs(sessionId)
: Array.Empty<string>();
: BuildFreshPromptArgs(task);
// Same run environment variable ClaudeProcess sets for every headless run: the
// AskUser MCP tool call caps at 60s unless raised, and lifting it is harmless for
@@ -120,6 +123,22 @@ public sealed class InteractiveLaunchSpecService : IInteractiveLaunchSpecService
return Task.FromResult(new LaunchSpec(directory, resolvedClaude, Array.Empty<string>(), env));
}
// The positional prompt claude opens the interactive session on. Empty (no positional arg)
// if the task has neither a title nor a description.
private static IReadOnlyList<string> BuildFreshPromptArgs(TaskEntity task)
{
var title = task.Title?.Trim();
var description = task.Description?.Trim();
var prompt = (string.IsNullOrEmpty(title), string.IsNullOrEmpty(description)) switch
{
(false, false) => $"{title}\n\n{description}",
(false, true) => title!,
(true, false) => description!,
_ => string.Empty,
};
return string.IsNullOrEmpty(prompt) ? Array.Empty<string>() : new[] { prompt };
}
private async Task<IReadOnlyList<string>> FilterToInstalledSkillsAsync(IReadOnlyList<string> requested, CancellationToken ct)
{
if (requested.Count == 0) return requested;