@oxecli/oxe 1.0.11 → 1.0.13

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 CHANGED
@@ -46,7 +46,13 @@ export class CLI {
46
46
  showHelp() {
47
47
  process.stdout.write("\n");
48
48
  for (const [cmd, desc] of COMMAND_HELP) {
49
- process.stdout.write(`\x1b[1m${cmd.padEnd(18)}\x1b[0m\x1b[2m${desc}\x1b[0m\n`);
49
+ // The whole command column is bold; any `<arg>` placeholder (e.g. <n>,
50
+ // <level>) is additionally cyan — matching the original's
51
+ // `[bold cyan]<n>[/bold cyan]`. Pad based on the plain text length so the
52
+ // ANSI codes don't skew the column alignment.
53
+ const styled = cmd.replace(/(<[^>]+>)/g, "\x1b[36m$1\x1b[0m");
54
+ const pad = Math.max(18 - cmd.length, 0);
55
+ process.stdout.write(`\x1b[1m${styled}${" ".repeat(pad)}\x1b[0m\x1b[2m${desc}\x1b[0m\n`);
50
56
  }
51
57
  process.stdout.write("\n");
52
58
  }
@@ -222,7 +228,7 @@ export class CLI {
222
228
  if (lower === "/exit" || lower === "/quit") {
223
229
  this.saveSession(engine);
224
230
  await engine.cleanupStoredResponses();
225
- process.stdout.write("\nClosing terminal session. Goodbye!\n");
231
+ process.stdout.write("\nClosing terminal session. Goodbye!\n\n");
226
232
  break;
227
233
  }
228
234
  if (lower === "/help") {
@@ -286,7 +292,7 @@ export class CLI {
286
292
  if (this.interruptPending) {
287
293
  this.saveSession(engine);
288
294
  await engine.cleanupStoredResponses();
289
- process.stdout.write("\nClosing terminal session. Goodbye!\n");
295
+ process.stdout.write("\nClosing terminal session. Goodbye!\n\n");
290
296
  break;
291
297
  }
292
298
  this.interruptPending = true;
@@ -314,9 +320,9 @@ export async function main() {
314
320
  catch (err) {
315
321
  if (err?.message === "interrupt" || err?.message === "eof") {
316
322
  // cleanup() already moved to a fresh line; the leading newline below
317
- // leaves that row as a blank, giving exactly one blank under the prompt,
318
- // and the trailing newline gives one blank under the goodbye message.
319
- process.stdout.write("\nClosing terminal session. Goodbye!\n");
323
+ // leaves that row as a blank (one under the prompt), and the trailing
324
+ // double-newline leaves a blank row under the message too.
325
+ process.stdout.write("\nClosing terminal session. Goodbye!\n\n");
320
326
  process.exit(0);
321
327
  return;
322
328
  }
package/dist/engine.js CHANGED
@@ -2,7 +2,7 @@ import OpenAI from "openai";
2
2
  import { max_output_tokens, max_empty_retries, max_agent_steps, max_context_tokens, context_overhead_margin, compact_keep_recent_turns, max_summary_source_chars, max_read_file_stored_chars, } from "./config.js";
3
3
  import { SYSTEM_PROMPT } from "./system.js";
4
4
  import { buildTools, truncateToolOutput, TOOL_IMPLEMENTATIONS } from "./tools.js";
5
- import { mutedMarkdown, aiMarkdown, tickDuration, displayRows, safeCommitPoint, printAiChunk, formatToolAction, renderPanel, Spinner, hideCursor, showCursor, } from "./ui.js";
5
+ import { mutedMarkdown, aiMarkdown, tickDuration, displayRows, safeCommitPoint, printAiChunk, formatToolAction, renderPanel, Spinner, hideCursor, } from "./ui.js";
6
6
  import { reportUsage } from "./api.js";
7
7
  import { stripOrphanCalls, persistCompactionSummary, toolOutputFailed, } from "./sessions.js";
8
8
  // ---------------------------------------------------------------------------
@@ -548,7 +548,6 @@ export class InferenceEngine {
548
548
  }
549
549
  finally {
550
550
  this.inQuery = false;
551
- showCursor();
552
551
  if (stats["tokens"] > 0 && this.keyData) {
553
552
  const userId = this.keyData["user_id"];
554
553
  if (userId) {
package/dist/oxe.js CHANGED
@@ -1,5 +1,15 @@
1
1
  import { main } from "./cli.js";
2
2
  export { main };
3
+ // The TUI keeps the terminal cursor hidden except inside a prompt field. Always
4
+ // restore it on exit so the shell isn't left with an invisible cursor.
5
+ process.on("exit", () => {
6
+ try {
7
+ process.stdout.write("\x1b[?25h");
8
+ }
9
+ catch {
10
+ /* ignore */
11
+ }
12
+ });
3
13
  main().catch((err) => {
4
14
  const msg = err?.message;
5
15
  if (msg === "interrupt" || msg === "eof") {
package/dist/ui.js CHANGED
@@ -588,6 +588,7 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
588
588
  let done = false;
589
589
  let lastCursorRow = 0;
590
590
  let lastTotalRows = 0;
591
+ let prevFrameLines = null;
591
592
  // Erase the prompt region: a leading blank line + the box. Cursor sits at
592
593
  // lastCursorRow inside the box; move up to the box's top border, erase the
593
594
  // box rows, then erase the leading blank line, leaving the cursor on that
@@ -609,7 +610,6 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
609
610
  return;
610
611
  done = true;
611
612
  process.stdin.setRawMode(false);
612
- showCursor();
613
613
  clearBox();
614
614
  process.stdin.removeListener("keypress", onKeypress);
615
615
  process.stdin.pause();
@@ -620,9 +620,7 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
620
620
  };
621
621
  const repaint = (isFirst = false) => {
622
622
  const { frame, cursorRow, cursorCol, totalRows } = renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor);
623
- lastCursorRow = cursorRow;
624
- lastTotalRows = totalRows;
625
- const frameLines = frame.split("\n");
623
+ const newLines = frame.split("\n");
626
624
  if (isFirst) {
627
625
  // The caller's content always ends with "\n", so the cursor sits at the
628
626
  // start of a fresh (blank) line — that line is the leading blank
@@ -630,20 +628,47 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
630
628
  // one line so the box starts below that blank; do NOT emit a second
631
629
  // blank here (it caused a double gap above the box / on cancel).
632
630
  process.stdout.write("\n");
631
+ for (let i = 0; i < newLines.length; i++) {
632
+ process.stdout.write(newLines[i]);
633
+ if (i < newLines.length - 1)
634
+ process.stdout.write("\n");
635
+ }
636
+ prevFrameLines = newLines;
637
+ lastCursorRow = cursorRow;
638
+ lastTotalRows = totalRows;
639
+ // Cursor sits on the bottom border line; move it up to the input row.
640
+ const up = totalRows - 1 - cursorRow;
641
+ process.stdout.write(`\x1b[${up}A\r\x1b[${cursorCol}C`);
642
+ return;
633
643
  }
634
644
  else {
635
- // Cursor currently sits at cursorRow (inside the box). Move to the top
636
- // border, then clear+rewrite each line so old content is fully removed.
637
- process.stdout.write(`\x1b[${cursorRow}A`);
638
- }
639
- for (let i = 0; i < frameLines.length; i++) {
640
- process.stdout.write("\r\x1b[K" + frameLines[i]);
641
- if (i < frameLines.length - 1)
642
- process.stdout.write("\n");
645
+ // Cursor currently sits at the previous input position (lastCursorRow
646
+ // inside the box). Move up to the top border, then rewrite ONLY the
647
+ // lines whose content actually changed, leaving the static borders
648
+ // untouched — this avoids the whole-box flicker on every keystroke.
649
+ process.stdout.write(`\x1b[${lastCursorRow}A\r`);
650
+ const oldLines = prevFrameLines ?? [];
651
+ const maxLen = Math.max(oldLines.length, newLines.length);
652
+ for (let i = 0; i < maxLen; i++) {
653
+ if (newLines[i] !== oldLines[i]) {
654
+ process.stdout.write("\r\x1b[K" + (newLines[i] ?? ""));
655
+ }
656
+ if (i < maxLen - 1)
657
+ process.stdout.write("\n");
658
+ }
659
+ // Cursor now sits at line (maxLen-1), column 0. Move it to the target
660
+ // input row (cursorRow), handling line-count changes in either direction.
661
+ const atLine = maxLen - 1;
662
+ if (atLine > cursorRow)
663
+ process.stdout.write(`\x1b[${atLine - cursorRow}A`);
664
+ else if (atLine < cursorRow)
665
+ process.stdout.write(`\x1b[${cursorRow - atLine}B`);
666
+ process.stdout.write(`\r\x1b[${cursorCol}C`);
667
+ prevFrameLines = newLines;
668
+ lastCursorRow = cursorRow;
669
+ lastTotalRows = totalRows;
670
+ return;
643
671
  }
644
- // Reposition the cursor inside the box.
645
- const fromBottom = totalRows - 1 - cursorRow;
646
- process.stdout.write(`\x1b[${fromBottom}A\r\x1b[${cursorCol}C`);
647
672
  };
648
673
  const insert = (text) => {
649
674
  if (histIdx !== hist.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },