@oxecli/oxe 1.0.22 → 1.0.24
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.
- package/dist/cli.js +7 -6
- package/dist/ui.js +97 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -219,12 +219,13 @@ export class CLI {
|
|
|
219
219
|
const label = truncateLabel(rec["label"]);
|
|
220
220
|
const items = rec["input_items"] ?? [];
|
|
221
221
|
const toks = estimateTokens(items).toLocaleString();
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
222
|
+
const selectedRow = idx === selected;
|
|
223
|
+
const content = `${selectedRow ? "\x1b[1m\x1b[37m" : "\x1b[1m"}${sid.padStart(4)}\x1b[0m` +
|
|
224
|
+
`${selectedRow ? "\x1b[2m\x1b[36m" : "\x1b[2m"} ${updated}\x1b[0m` +
|
|
225
|
+
`${selectedRow ? "\x1b[37m" : ""} ${label}\x1b[0m` +
|
|
226
|
+
`${selectedRow ? "\x1b[2m\x1b[34m" : "\x1b[2m"} ${items.length} items · ${toks} tok\x1b[0m`;
|
|
225
227
|
rows.push({
|
|
226
|
-
cells: [
|
|
227
|
-
style: idx === selected ? "\x1b[1m" : undefined,
|
|
228
|
+
cells: [selectedRow ? "\x1b[1m\x1b[36m❯\x1b[0m" : "", content],
|
|
228
229
|
});
|
|
229
230
|
}
|
|
230
231
|
const below = total - hi;
|
|
@@ -318,7 +319,7 @@ export class CLI {
|
|
|
318
319
|
this.sessionId = sid;
|
|
319
320
|
engine.reasoningEffort = String(rec["reasoning_effort"] ?? default_reasoning_effort);
|
|
320
321
|
clearScreen();
|
|
321
|
-
renderPanel("AI CODING AGENT", "",
|
|
322
|
+
renderPanel("AI CODING AGENT", "", "Engine: Ready", false);
|
|
322
323
|
process.stdout.write("\n");
|
|
323
324
|
process.stdout.write(`\x1b[2mResumed:\x1b[0m ${truncateLabel(rec["label"])}\n`);
|
|
324
325
|
process.stdout.write("\n");
|
package/dist/ui.js
CHANGED
|
@@ -728,6 +728,10 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
728
728
|
const hist = history.slice();
|
|
729
729
|
let histIdx = hist.length;
|
|
730
730
|
let draft = null;
|
|
731
|
+
let bracketedPaste = false;
|
|
732
|
+
let bracketedPasteBuffer = "";
|
|
733
|
+
let pasteBurst = false;
|
|
734
|
+
let pasteBurstTimer = null;
|
|
731
735
|
readline.emitKeypressEvents(process.stdin);
|
|
732
736
|
if (process.stdin.isTTY) {
|
|
733
737
|
process.stdin.setRawMode(true);
|
|
@@ -763,6 +767,8 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
763
767
|
return;
|
|
764
768
|
done = true;
|
|
765
769
|
process.stdin.setRawMode(false);
|
|
770
|
+
if (pasteBurstTimer)
|
|
771
|
+
clearTimeout(pasteBurstTimer);
|
|
766
772
|
clearBox();
|
|
767
773
|
process.stdin.removeListener("keypress", onKeypress);
|
|
768
774
|
process.stdin.pause();
|
|
@@ -863,6 +869,20 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
863
869
|
draft = { buffer, cursor, spans: pasteSpans.slice() };
|
|
864
870
|
histIdx = hist.length;
|
|
865
871
|
}
|
|
872
|
+
// A collapsed paste is one editable unit. Backspace removes the entire
|
|
873
|
+
// pasted block, matching the Python prompt rather than deleting only its
|
|
874
|
+
// final character.
|
|
875
|
+
const containing = pasteSpans.find(([s, e]) => s < cursor && cursor <= e);
|
|
876
|
+
if (containing) {
|
|
877
|
+
const [start, end] = containing;
|
|
878
|
+
buffer = buffer.slice(0, start) + buffer.slice(end);
|
|
879
|
+
const delta = start - end;
|
|
880
|
+
pasteSpans = pasteSpans
|
|
881
|
+
.filter(([s, e]) => s !== start || e !== end)
|
|
882
|
+
.map(([s, e]) => (s >= end ? [s + delta, e + delta] : [s, e]));
|
|
883
|
+
cursor = start;
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
866
886
|
// remove one grapheme before cursor
|
|
867
887
|
const before = Array.from(buffer.slice(0, cursor));
|
|
868
888
|
before.pop();
|
|
@@ -974,6 +994,8 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
974
994
|
if (!key)
|
|
975
995
|
return false;
|
|
976
996
|
// Bare \n / Ctrl+J = newline.
|
|
997
|
+
if (key.ctrl && key.name === "j")
|
|
998
|
+
return true;
|
|
977
999
|
if (key.name === "enter")
|
|
978
1000
|
return !!(key.shift || key.ctrl || key.meta);
|
|
979
1001
|
// Enter with a modifier (Shift/Ctrl/Alt/Meta) = newline.
|
|
@@ -1005,7 +1027,69 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
1005
1027
|
return true;
|
|
1006
1028
|
return false;
|
|
1007
1029
|
};
|
|
1030
|
+
const consumeBracketedPaste = (str, key) => {
|
|
1031
|
+
const start = "\x1b[200~";
|
|
1032
|
+
const end = "\x1b[201~";
|
|
1033
|
+
const sequence = key?.sequence || "";
|
|
1034
|
+
const value = str || "";
|
|
1035
|
+
if (!bracketedPaste && (sequence === start || value === start)) {
|
|
1036
|
+
bracketedPaste = true;
|
|
1037
|
+
bracketedPasteBuffer = "";
|
|
1038
|
+
const inline = value === start ? "" : value.startsWith(start) ? value.slice(start.length) : "";
|
|
1039
|
+
if (inline)
|
|
1040
|
+
bracketedPasteBuffer = inline;
|
|
1041
|
+
if (inline.includes(end)) {
|
|
1042
|
+
const endAt = inline.indexOf(end);
|
|
1043
|
+
const pasted = inline.slice(0, endAt).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1044
|
+
bracketedPaste = false;
|
|
1045
|
+
bracketedPasteBuffer = "";
|
|
1046
|
+
if (pasted)
|
|
1047
|
+
insert(pasted, true);
|
|
1048
|
+
repaint();
|
|
1049
|
+
}
|
|
1050
|
+
return true;
|
|
1051
|
+
}
|
|
1052
|
+
if (!bracketedPaste)
|
|
1053
|
+
return false;
|
|
1054
|
+
if (sequence === end || value === end) {
|
|
1055
|
+
const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1056
|
+
bracketedPaste = false;
|
|
1057
|
+
bracketedPasteBuffer = "";
|
|
1058
|
+
if (pasted)
|
|
1059
|
+
insert(pasted, true);
|
|
1060
|
+
repaint();
|
|
1061
|
+
return true;
|
|
1062
|
+
}
|
|
1063
|
+
const chunk = value || (sequence && !sequence.startsWith("\x1b[") ? sequence : "");
|
|
1064
|
+
if (chunk) {
|
|
1065
|
+
const endAt = chunk.indexOf(end);
|
|
1066
|
+
if (endAt >= 0) {
|
|
1067
|
+
bracketedPasteBuffer += chunk.slice(0, endAt);
|
|
1068
|
+
const pasted = bracketedPasteBuffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1069
|
+
bracketedPaste = false;
|
|
1070
|
+
bracketedPasteBuffer = "";
|
|
1071
|
+
if (pasted)
|
|
1072
|
+
insert(pasted, true);
|
|
1073
|
+
repaint();
|
|
1074
|
+
}
|
|
1075
|
+
else {
|
|
1076
|
+
bracketedPasteBuffer += chunk;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return true;
|
|
1080
|
+
};
|
|
1081
|
+
const markPasteBurst = () => {
|
|
1082
|
+
pasteBurst = true;
|
|
1083
|
+
if (pasteBurstTimer)
|
|
1084
|
+
clearTimeout(pasteBurstTimer);
|
|
1085
|
+
pasteBurstTimer = setTimeout(() => {
|
|
1086
|
+
pasteBurst = false;
|
|
1087
|
+
pasteBurstTimer = null;
|
|
1088
|
+
}, 90);
|
|
1089
|
+
};
|
|
1008
1090
|
const onKeypress = (str, key) => {
|
|
1091
|
+
if (consumeBracketedPaste(str, key))
|
|
1092
|
+
return;
|
|
1009
1093
|
if (key && key.ctrl && key.name === "c") {
|
|
1010
1094
|
settle(new Error("interrupt"), true);
|
|
1011
1095
|
return;
|
|
@@ -1014,6 +1098,15 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
1014
1098
|
settle(new Error("eof"), true);
|
|
1015
1099
|
return;
|
|
1016
1100
|
}
|
|
1101
|
+
// Any modified Enter (Shift/Ctrl+Enter) inserts a newline. This check
|
|
1102
|
+
// must happen before plain submit detection because some terminals expose
|
|
1103
|
+
// Shift+Enter as `return` while keeping the modifier only in the escape
|
|
1104
|
+
// sequence.
|
|
1105
|
+
if (isNewlineKey(key) || (pasteBurst && isSubmitEnterKey(key))) {
|
|
1106
|
+
insert("\n");
|
|
1107
|
+
repaint();
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1017
1110
|
// Plain Enter submits; any modified Enter (Shift/Ctrl+Enter) inserts a
|
|
1018
1111
|
// newline, mirroring the Python original's _read_input_event (which maps
|
|
1019
1112
|
// \x1b[13;2u / \x1b[13;5u / Shift+\r to "\n").
|
|
@@ -1023,11 +1116,6 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
1023
1116
|
}
|
|
1024
1117
|
return;
|
|
1025
1118
|
}
|
|
1026
|
-
if (isNewlineKey(key)) {
|
|
1027
|
-
insert("\n");
|
|
1028
|
-
repaint();
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
1119
|
if (key && key.name === "backspace") {
|
|
1032
1120
|
backspace();
|
|
1033
1121
|
repaint();
|
|
@@ -1066,7 +1154,10 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
1066
1154
|
if (str) {
|
|
1067
1155
|
// Multi-char sequence = a paste (single-char typed keys arrive one per
|
|
1068
1156
|
// event). Tag it so the prompt box collapses it when it meets the rules.
|
|
1069
|
-
|
|
1157
|
+
const isPaste = str.length > 1;
|
|
1158
|
+
insert(str.replace(/\r\n/g, "\n").replace(/\r/g, "\n"), isPaste);
|
|
1159
|
+
if (isPaste)
|
|
1160
|
+
markPasteBurst();
|
|
1070
1161
|
repaint();
|
|
1071
1162
|
}
|
|
1072
1163
|
};
|