@oxecli/oxe 1.0.64 → 1.0.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +5 -4
  2. package/dist/ui.js +37 -11
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -466,7 +466,10 @@ export class CLI {
466
466
  this.story[this.story.length - 1]["type"] === "user") {
467
467
  this.story.pop();
468
468
  }
469
- if (this.interruptPending) {
469
+ // Exit cleanly on: a second Ctrl+C, or a first Ctrl+C while idle
470
+ // (no active query). Only an in-flight query's first Ctrl+C keeps
471
+ // running so the agent can be interrupted without a stray key exiting.
472
+ if (this.interruptPending || !wasActive) {
470
473
  this.saveSession(engine);
471
474
  await engine.cleanupStoredResponses();
472
475
  process.stdout.write("\nClosing terminal session. Goodbye!\n\n");
@@ -474,9 +477,7 @@ export class CLI {
474
477
  }
475
478
  this.interruptPending = true;
476
479
  process.stdout.write("\n");
477
- process.stdout.write(wasActive
478
- ? "\x1b[2mInterrupted agent. Press Ctrl+C again to exit, or type a new prompt.\x1b[0m\n"
479
- : "\x1b[2mNo active query. Press Ctrl+C again to exit.\x1b[0m\n");
480
+ process.stdout.write("\x1b[2mInterrupted agent. Press Ctrl+C again to exit, or type a new prompt.\x1b[0m\n");
480
481
  continue;
481
482
  }
482
483
  throw err;
package/dist/ui.js CHANGED
@@ -902,10 +902,37 @@ export function renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor
902
902
  const totalRows = body.length + 2;
903
903
  return { frame, cursorRow, cursorCol, totalRows };
904
904
  }
905
+ /**
906
+ * Generic CSI Shift+Enter detection. The modifier encoding used by CSI-u,
907
+ * legacy and kitty/modifyOtherKeys is a bitmask + 1, where Shift is bit0
908
+ * (so modifier 2 = shift, 4 = shift+alt, 6 = shift+ctrl, ...). Enter has
909
+ * key code 13. Handles:
910
+ * CSI-u / legacy: ESC [ 13 ; M u and ESC [ 13 ; M ~
911
+ * modifyOtherKeys: ESC [ 27 ; M ; 13 ~
912
+ * application: ESC O M
913
+ */
914
+ function csiShiftEnter(seq) {
915
+ if (seq === "\x1bOM")
916
+ return true;
917
+ // modifyOtherKeys: ESC [ 27 ; M ; 13 ~
918
+ const mod = seq.match(/^\x1b\[27;(\d+);13[~_]$/);
919
+ if (mod) {
920
+ const m = parseInt(mod[1], 10);
921
+ return Number.isInteger(m) && ((m - 1) & 1) === 1;
922
+ }
923
+ // CSI-u / legacy: ESC [ 13 ; M u (or ~ or _)
924
+ const csi = seq.match(/^\x1b\[13;(\d+)[~u_]$/);
925
+ if (csi) {
926
+ const m = parseInt(csi[1], 10);
927
+ return Number.isInteger(m) && ((m - 1) & 1) === 1;
928
+ }
929
+ return false;
930
+ }
905
931
  export function isShiftEnterKey(str, key) {
906
932
  if (!key)
907
933
  return false;
908
934
  const seq = key.sequence || str || "";
935
+ // Shift modifier reported by readline on a return/enter/linefeed key.
909
936
  if (key.shift &&
910
937
  (key.name === "return" ||
911
938
  key.name === "enter" ||
@@ -914,13 +941,8 @@ export function isShiftEnterKey(str, key) {
914
941
  seq === "\n")) {
915
942
  return true;
916
943
  }
917
- if (seq === "\x1b[13;2u" ||
918
- seq === "\x1b[13;2~" ||
919
- seq === "\x1b[13;2_" ||
920
- seq === "\x1b[27;2;13~" ||
921
- seq === "\x1bOM") {
944
+ if (csiShiftEnter(seq))
922
945
  return true;
923
- }
924
946
  return false;
925
947
  }
926
948
  export const isNewlineKey = isShiftEnterKey;
@@ -973,6 +995,9 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
973
995
  if (process.stdin.isTTY) {
974
996
  process.stdin.setRawMode(true);
975
997
  process.stdout.write("\x1b[?2004h");
998
+ // Enable CSI-u "disambiguate" (flags=1) so a terminal that supports it
999
+ // reports Shift+Enter as a distinct `ESC [ 13 ; 2 u` code instead of
1000
+ // collapsing it into a plain Enter.
976
1001
  process.stdout.write("\x1b[>1u");
977
1002
  }
978
1003
  process.stdin.resume();
@@ -1003,6 +1028,7 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
1003
1028
  clearBox();
1004
1029
  process.stdin.removeListener("keypress", onKeypress);
1005
1030
  process.stdin.pause();
1031
+ showCursor();
1006
1032
  if (isErr)
1007
1033
  reject(value);
1008
1034
  else
@@ -1319,17 +1345,17 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
1319
1345
  const onKeypress = (str, key) => {
1320
1346
  // Reassemble a modifier-prefixed CSI sequence that readline splits up,
1321
1347
  // e.g. Shift+Enter arriving as `\x1b[27;2;` + "1" + "3" + "~".
1322
- if (key?.sequence === "\x1b[27;2;") {
1323
- modSeq = "\x1b[27;2;";
1348
+ if (key?.sequence === "\x1b[27;" || key?.sequence === "\x1b[27;2;") {
1349
+ modSeq = key.sequence;
1324
1350
  return;
1325
1351
  }
1326
1352
  if (modSeq) {
1327
1353
  modSeq += str || key?.sequence || "";
1328
- if (modSeq.endsWith("~")) {
1354
+ if (/[~u_]/.test(modSeq.slice(-1)) || modSeq.endsWith("~")) {
1329
1355
  const full = modSeq;
1330
1356
  modSeq = "";
1331
- // `\x1b[27;2;13~` is Shift+Enter (modifier 2 = shift, key 13 = Enter).
1332
- if (full === "\x1b[27;2;13~") {
1357
+ // e.g. `\x1b[27;2;13~` = Shift+Enter.
1358
+ if (csiShiftEnter(full)) {
1333
1359
  insert("\n");
1334
1360
  repaint();
1335
1361
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.64",
3
+ "version": "1.0.66",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },