diff --git a/src/shared/startup-command-submission.test.ts b/src/shared/startup-command-submission.test.ts index 9da31225996..1006e63aa05 100644 --- a/src/shared/startup-command-submission.test.ts +++ b/src/shared/startup-command-submission.test.ts @@ -17,6 +17,12 @@ describe('buildStartupCommandSubmission', () => { ).toBe('claude\n') }) + it('treats a CRLF-terminated single-line command as single-line', () => { + expect( + buildStartupCommandSubmission('claude\r\n', { submit: '\r', bracketedPasteSafe: true }) + ).toBe('claude\r\n') + }) + it('wraps a multiline command in bracketed paste with a trailing submit byte', () => { const command = "claude 'first\nsecond'" expect(buildStartupCommandSubmission(command, { submit: '\n', bracketedPasteSafe: true })).toBe( diff --git a/src/shared/startup-command-submission.ts b/src/shared/startup-command-submission.ts index 103175909e6..7d97f59a271 100644 --- a/src/shared/startup-command-submission.ts +++ b/src/shared/startup-command-submission.ts @@ -32,8 +32,11 @@ export function buildStartupCommandSubmission( command: string, { submit, bracketedPasteSafe }: StartupCommandSubmissionOptions ): string { - const endsWithSubmit = command.endsWith('\r') || command.endsWith('\n') - const body = endsWithSubmit ? command.slice(0, -1) : command + // Strip a full CRLF (or lone CR/LF) terminator so a single-line command ending + // in \r\n isn't misread as multiline by the \r/\n body check below. + const trailingTerminator = /\r\n$|\r$|\n$/.exec(command)?.[0] ?? '' + const endsWithSubmit = trailingTerminator.length > 0 + const body = endsWithSubmit ? command.slice(0, -trailingTerminator.length) : command if (bracketedPasteSafe && (body.includes('\n') || body.includes('\r'))) { return `${BRACKETED_PASTE_START}${body}${BRACKETED_PASTE_END}${submit}` }