@oxecli/oxe 1.0.85 → 1.0.86

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 +23 -5
  2. package/dist/ui.js +19 -17
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  import { loadOrPrompt, default_reasoning_effort, max_action_chars, max_resume_history_items, runtimeOsSummary, max_context_tokens, context_overhead_margin, } from "./config.js";
5
5
  import { InferenceEngine, estimateTokens } from "./engine.js";
6
6
  import { saveSession, loadSession, listSessions, nextSessionId, conversationLabel, COMMAND_HELP, stripOrphanCalls, toolOutputFailed, } from "./sessions.js";
7
- import { clearScreen, enableAnsi, askBottomPrompt, userDisplayText, aiMarkdown, mutedMarkdown, messageText, toolCallLabel, formatToolResult, truncateEllipsis, stripAnsi, renderPanel, renderTableString, enableRawStdin, disableRawStdin, waitRawKey, } from "./ui.js";
7
+ import { clearScreen, enableAnsi, askBottomPrompt, userDisplayText, aiMarkdown, mutedMarkdown, messageText, toolCallLabel, formatToolResult, truncateEllipsis, stripAnsi, plainLen, truncateStyled, terminalWidth, TOOL_RESULT_MAX_LINES, MAX_COMMAND_DISPLAY_CHARS, renderPanel, renderTableString, enableRawStdin, disableRawStdin, waitRawKey, } from "./ui.js";
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
9
  const require = createRequire(import.meta.url);
10
10
  const packageInfo = require("../package.json");
@@ -49,14 +49,32 @@ export class CLI {
49
49
  const label = started && String(started).trim();
50
50
  const body = String(text).trim();
51
51
  if (label && /^[⎿╰]/.test(stripAnsi(body))) {
52
- process.stdout.write(`${label}\n${body}\n`);
53
- if (diff)
54
- process.stdout.write(diff + "\n\n");
52
+ // The result body was formatted against the terminal width at tool time,
53
+ // but the window may be narrower on resume — re-truncate defensively so
54
+ // long persisted lines never run off the screen.
55
+ const lineMax = Math.max(terminalWidth() - 4, 20);
56
+ const allLines = body.split("\n");
57
+ const lines = allLines
58
+ .slice(0, TOOL_RESULT_MAX_LINES)
59
+ .map((ln) => plainLen(ln) > lineMax ? truncateStyled(ln, lineMax) + "…\x1b[0m" : ln);
60
+ if (allLines.length > TOOL_RESULT_MAX_LINES) {
61
+ lines.push("\x1b[90m…\x1b[0m");
62
+ }
63
+ const labelLine = plainLen(label) > lineMax ? truncateStyled(label, lineMax) + "…\x1b[0m" : label;
64
+ process.stdout.write(`${labelLine}\n${lines.join("\n")}\n`);
65
+ if (diff) {
66
+ const diffLines = diff.split("\n").map((ln) => plainLen(ln) > lineMax ? truncateStyled(ln, lineMax) + "…\x1b[0m" : ln);
67
+ process.stdout.write(diffLines.join("\n") + "\n\n");
68
+ }
55
69
  return;
56
70
  }
57
71
  const icon = status === "failed" ? "\x1b[31m✗\x1b[0m" : "\x1b[32m✓\x1b[0m";
58
72
  const style = status === "failed" ? "\x1b[31m" : "\x1b[32m";
59
- process.stdout.write(`${icon} ${style}${truncateEllipsis(body, max_action_chars, "text")}\x1b[0m\n`);
73
+ const truncated = truncateEllipsis(body, Math.min(max_action_chars, MAX_COMMAND_DISPLAY_CHARS), "text");
74
+ // truncateEllipsis appends a "…(text truncated: N chars total)" suffix, so
75
+ // re-truncate to the current width to keep the legacy action on one row.
76
+ const displayMax = Math.max(terminalWidth() - 6, 20);
77
+ process.stdout.write(`${icon} ${style}${truncateStyled(truncated, displayMax)}\x1b[0m\n`);
60
78
  }
61
79
  printAssistantBlock(text) {
62
80
  const rendered = aiMarkdown(text);
package/dist/ui.js CHANGED
@@ -681,10 +681,13 @@ const TOOL_DISPLAY_NAMES = {
681
681
  grep: "Grep",
682
682
  load_skill: "Load",
683
683
  };
684
- const TOOL_RESULT_MAX_CHARS = 400;
685
- const TOOL_RESULT_MAX_LINES = 6;
686
- const TOOL_RESULT_MAX_LINE_CHARS = 160;
687
- /** First line of a tool entry: `Bash(rm -f "…")` — cyan tool name, primary arg in parens. */
684
+ export const TOOL_RESULT_MAX_LINES = 6;
685
+ /** Cap on the visible command/arg shown in a tool label, so the entry always
686
+ * fits on a single row regardless of terminal width. */
687
+ export const MAX_COMMAND_DISPLAY_CHARS = 100;
688
+ /** First line of a tool entry: `Bash(rm -f "…")` — cyan tool name, primary arg
689
+ * in parens. Newlines collapse to spaces and the arg is width-capped so the
690
+ * label never wraps past one row. */
688
691
  export function toolCallLabel(name, argumentsJson) {
689
692
  let args = {};
690
693
  try {
@@ -713,11 +716,12 @@ export function toolCallLabel(name, argumentsJson) {
713
716
  arg = String(args["skill_name"] ?? "");
714
717
  break;
715
718
  }
716
- arg = arg.trim();
719
+ arg = arg.trim().replace(/\s*\r?\n\s*/g, " ");
717
720
  if (!arg)
718
721
  return `\x1b[1;36m${display}\x1b[0m`;
719
- if (arg.length > 400)
720
- arg = arg.slice(0, 400) + "…";
722
+ const cap = Math.max(Math.min(terminalWidth() - plainLen(display) - 4, MAX_COMMAND_DISPLAY_CHARS), 20);
723
+ if (plainLen(arg) > cap)
724
+ arg = truncateStyled(arg, cap) + "…";
721
725
  return `\x1b[1;36m${display}\x1b[0m(${arg})`;
722
726
  }
723
727
  /**
@@ -737,17 +741,15 @@ export function formatToolResult(rawResult, failed) {
737
741
  if (failed && (!clean || clean === "(no output)") && code) {
738
742
  return `${corner}\x1b[31mexit code: ${code}\x1b[0m`;
739
743
  }
740
- let body = clean;
741
- const clipped = body.length > TOOL_RESULT_MAX_CHARS;
742
- if (clipped)
743
- body = body.slice(0, TOOL_RESULT_MAX_CHARS);
744
- const lines = body
745
- .split("\n")
744
+ // Truncate to the terminal width (rows OR per-line length, whichever trips
745
+ // first) so long tool output — e.g. Glob paths — never runs off the screen.
746
+ const termW = terminalWidth();
747
+ const lineMax = Math.max(termW - 4, 20);
748
+ const allLines = clean.split("\n");
749
+ const lines = allLines
746
750
  .slice(0, TOOL_RESULT_MAX_LINES)
747
- .map((ln) => ln.length > TOOL_RESULT_MAX_LINE_CHARS
748
- ? ln.slice(0, TOOL_RESULT_MAX_LINE_CHARS) + "…"
749
- : ln);
750
- if (clipped || body.split("\n").length > TOOL_RESULT_MAX_LINES)
751
+ .map((ln) => (ln.length > lineMax ? ln.slice(0, lineMax) + "…" : ln));
752
+ if (allLines.length > TOOL_RESULT_MAX_LINES)
751
753
  lines.push("…");
752
754
  const styleOpen = failed ? "\x1b[31m" : "\x1b[90m";
753
755
  const inner = lines
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.85",
3
+ "version": "1.0.86",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },