From 9b9c74e1589e59b759dc4cfde960cf1c33f0fbbc Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 4 May 2026 20:39:27 -0700 Subject: [PATCH] fix(new-workspace): type linked URL into agent input without auto-submit Co-authored-by: Orca --- src/renderer/src/hooks/useComposerState.ts | 21 ++++++++----- src/renderer/src/lib/new-workspace.ts | 35 ++++++++++++++++++++-- src/renderer/src/lib/tui-agent-startup.ts | 6 ++++ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/renderer/src/hooks/useComposerState.ts b/src/renderer/src/hooks/useComposerState.ts index 315a63fa5b1..cbe472cf70d 100644 --- a/src/renderer/src/hooks/useComposerState.ts +++ b/src/renderer/src/hooks/useComposerState.ts @@ -1365,14 +1365,15 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS ...(trimmedNote ? { comment: trimmedNote } : {}) }) - const quickPrompt = linkedWorkItem - ? linkedWorkItem.number === 0 && trimmedNote - ? trimmedNote - : renderIssueCommandTemplate(DEFAULT_ISSUE_COMMAND_TEMPLATE, { - issueNumber: parsedLinkedIssueNumber, - artifactUrl: linkedWorkItem.url - }) - : '' + // Why: when a linked work item is selected in the quick flow, launch + // the agent with a blank prompt and type the URL into its input as a + // draft (no trailing Enter). This lets the user review/edit before + // sending instead of auto-executing a "Complete " template. + // Falls back to the trimmed note when the linked item carries no + // number/URL (Linear typed-only entries). + const isLinearTypedOnly = linkedWorkItem?.number === 0 && Boolean(trimmedNote) + const quickPrompt = isLinearTypedOnly && trimmedNote ? trimmedNote : '' + const quickDraftPrompt = linkedWorkItem && !isLinearTypedOnly ? linkedWorkItem.url : null const startupPlan = agent === null @@ -1385,6 +1386,10 @@ export function useComposerState(options: UseComposerStateOptions): UseComposerS allowEmptyPromptLaunch: true }) + if (startupPlan && quickDraftPrompt) { + startupPlan.draftPrompt = quickDraftPrompt + } + activateAndRevealWorktree(worktree.id, { setup: result.setup, ...(startupPlan ? { startup: { command: startupPlan.launchCommand } } : {}) diff --git a/src/renderer/src/lib/new-workspace.ts b/src/renderer/src/lib/new-workspace.ts index 619c30e03d3..df4d857aeb1 100644 --- a/src/renderer/src/lib/new-workspace.ts +++ b/src/renderer/src/lib/new-workspace.ts @@ -200,11 +200,13 @@ export async function ensureAgentStartupInTerminal(args: { startup: AgentStartupPlan }): Promise { const { worktreeId, startup } = args - if (startup.followupPrompt === null) { + const draftPrompt = startup.draftPrompt ?? null + if (startup.followupPrompt === null && draftPrompt === null) { return } let promptInjected = false + let draftInjected = false for (let attempt = 0; attempt < 30; attempt += 1) { if (attempt > 0) { @@ -232,10 +234,21 @@ export async function ensureAgentStartupInTerminal(args: { if (agentOwnsForeground && !promptInjected && startup.followupPrompt) { window.api.pty.write(ptyId, `${startup.followupPrompt}\r`) promptInjected = true - return } - if (agentOwnsForeground && promptInjected) { + // Why: type the draft into the live agent input WITHOUT a trailing + // carriage return. The user wants the URL pre-filled so they can edit + // before sending — appending \r would auto-submit and defeat the point. + if (agentOwnsForeground && !draftInjected && draftPrompt) { + window.api.pty.write(ptyId, draftPrompt) + draftInjected = true + } + + if ( + agentOwnsForeground && + (promptInjected || startup.followupPrompt === null) && + (draftInjected || draftPrompt === null) + ) { return } @@ -253,6 +266,22 @@ export async function ensureAgentStartupInTerminal(args: { // live session instead of launching the binary a second time. window.api.pty.write(ptyId, `${startup.followupPrompt}\r`) promptInjected = true + } + if ( + !draftInjected && + draftPrompt && + hasChildProcesses && + !isShellProcess(foreground) && + attempt >= 4 + ) { + window.api.pty.write(ptyId, draftPrompt) + draftInjected = true + } + + if ( + (promptInjected || startup.followupPrompt === null) && + (draftInjected || draftPrompt === null) + ) { return } } catch { diff --git a/src/renderer/src/lib/tui-agent-startup.ts b/src/renderer/src/lib/tui-agent-startup.ts index 59c64c0f04d..a0e9a627094 100644 --- a/src/renderer/src/lib/tui-agent-startup.ts +++ b/src/renderer/src/lib/tui-agent-startup.ts @@ -5,6 +5,12 @@ export type AgentStartupPlan = { launchCommand: string expectedProcess: string followupPrompt: string | null + /** Why: text to type into the live agent input WITHOUT submitting it (no + * trailing \r). Used by the quick-create flow to pre-fill a linked work + * item URL so the user can edit/add to it before sending. Independent from + * `followupPrompt` so the call site can choose: type-and-submit (followup) + * or type-and-leave-pending (draft). */ + draftPrompt?: string | null } function quoteStartupArg(value: string, platform: NodeJS.Platform): string {