fix(coding-agent): guarded pasted shell prompts from python

- Detected copied shell-prompt transcripts before the Python shortcut router.
- Forwarded OMP terminal chrome pastes through normal prompt submission.
- Added regression coverage for the #4678 transcript shape.

Fixes #4678
This commit is contained in:
roboomp
2026-07-06 06:13:55 +00:00
parent 674cf01762
commit 2d180b885f
3 changed files with 51 additions and 1 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed pasted terminal transcripts beginning with a shell prompt (`$ ...`) being mistaken for local Python shortcuts instead of being submitted as normal prompts ([#4678](https://github.com/can1357/oh-my-pi/issues/4678)).
## [16.3.9] - 2026-07-06
### Added
@@ -86,6 +86,20 @@ function hasPasteText(value: unknown): value is PasteTarget {
return typeof value === "object" && value !== null && typeof (value as PasteTarget).pasteText === "function";
}
const SHELL_PROMPT_COMMAND_RE =
/^(?:\.{0,2}\/|~\/|cd(?:\s|$)|sudo(?:\s|$)|git(?:\s|$)|bun(?:\s|$)|npm(?:\s|$)|pnpm(?:\s|$)|yarn(?:\s|$)|node(?:\s|$)|python\d*(?:\s|$)|cargo(?:\s|$)|go(?:\s|$)|make(?:\s|$)|docker(?:\s|$)|kubectl(?:\s|$))/;
const SHELL_PROMPT_OPERATOR_RE = /(?:^|\s)(?:&&|\|\||\||2>&1|[<>]{1,2})(?:\s|$)/;
const OMP_STATUS_LINE_RE = /^\s*in:\s+\d+\s+out:\s+\d+(?:\s+cache\s+\S+)?\s+t:\s+\S+\s+tok\/s:\s+\S+/m;
function looksLikePastedShellPrompt(code: string): boolean {
const firstLine = code.split("\n", 1)[0]?.trimStart() ?? "";
return (
SHELL_PROMPT_COMMAND_RE.test(firstLine) ||
SHELL_PROMPT_OPERATOR_RE.test(firstLine) ||
OMP_STATUS_LINE_RE.test(code)
);
}
function pythonCommandPrefixLength(trimmedText: string): 0 | 1 | 2 {
if (trimmedText.charCodeAt(0) !== 36 /* $ */) return 0;
if (trimmedText.charCodeAt(1) === 123 /* { */) return 0;
@@ -100,8 +114,10 @@ function parsePythonCommandInput(text: string): { code: string; isExcluded: bool
const trimmed = text.trimStart();
const prefixLength = pythonCommandPrefixLength(trimmed);
if (prefixLength === 0) return undefined;
const code = trimmed.slice(prefixLength).trim();
if (prefixLength === 1 && looksLikePastedShellPrompt(code)) return undefined;
return {
code: trimmed.slice(prefixLength).trim(),
code,
isExcluded: prefixLength === 2,
};
}
@@ -109,6 +109,36 @@ describe("InputController Python prompt prefix", () => {
]);
});
it("submits pasted shell-prompt transcripts with OMP chrome as a normal prompt", async () => {
const transcript =
"$ cd ~/project && sudo ./build-and-push.sh o5.7 2>&1 | tail -4\n" +
" |\n" +
" in: 282 out: 152 cache 344K t: 3.3s tok/s: 351.9/s\n" +
" is this command stuck in limbo";
const { ctx, editor, handlePythonCommand, onInputCallback, startPendingSubmission, submitted } = createContext();
const controller = new InputController(ctx);
controller.setupEditorSubmitHandler();
await editor.onSubmit?.(transcript);
expect(handlePythonCommand).not.toHaveBeenCalled();
expect(startPendingSubmission).toHaveBeenCalledWith({
text: transcript,
images: undefined,
imageLinks: undefined,
streamingBehavior: "steer",
});
expect(onInputCallback).toHaveBeenCalledTimes(1);
expect(submitted).toEqual([
{
text: transcript,
images: undefined,
imageLinks: undefined,
streamingBehavior: "steer",
},
]);
});
it("keeps space-separated Python shortcuts available", async () => {
const { ctx, editor, handlePythonCommand, onInputCallback } = createContext();
const controller = new InputController(ctx);