@oxecli/oxe 1.0.58 → 1.0.60
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/tools.js +37 -9
- package/dist/ui.js +32 -15
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -4,6 +4,7 @@ import { execFile, spawn } from "node:child_process";
|
|
|
4
4
|
import { structuredPatch } from "diff";
|
|
5
5
|
import { max_diff_source_chars, max_diff_lines, max_diff_context_lines, max_diff_line_chars, max_read_line_chars, max_read_file_bytes, max_read_lines, max_output_chars, max_bash_timeout_seconds, max_grep_file_bytes, strict_max_properties, ignoredDirs, } from "./config.js";
|
|
6
6
|
import { toolLoadSkill } from "./skills.js";
|
|
7
|
+
import { truncateStyled, plainLen, terminalWidth } from "./ui.js";
|
|
7
8
|
export { toolLoadSkill };
|
|
8
9
|
// ---------------------------------------------------------------------------
|
|
9
10
|
// Path sanitization helper
|
|
@@ -52,7 +53,6 @@ function displayDiff(pathName, oldContent, newContent) {
|
|
|
52
53
|
const removed = contentLines.filter((c) => c.kind === "-").length;
|
|
53
54
|
if (!added && !removed)
|
|
54
55
|
return;
|
|
55
|
-
process.stdout.write(`\x1b[90m┌─\x1b[0m \x1b[1m${pathName}\x1b[0m \x1b[32m+${added}\x1b[0m \x1b[31m-${removed}\x1b[0m\n`);
|
|
56
56
|
const shown = [];
|
|
57
57
|
let run = [];
|
|
58
58
|
let lastKind = null;
|
|
@@ -77,18 +77,46 @@ function displayDiff(pathName, oldContent, newContent) {
|
|
|
77
77
|
run.push(c);
|
|
78
78
|
}
|
|
79
79
|
flush();
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
// Build the lines, stripping the leading + / - / space marker for display.
|
|
81
|
+
const visible = shown.map((l) => {
|
|
82
|
+
const kind = l[0] === "+" ? "+" : l[0] === "-" ? "-" : " ";
|
|
83
|
+
return { kind, body: l.slice(1) };
|
|
84
|
+
});
|
|
85
|
+
// A "… (N lines hidden) …" summary line is neutral context.
|
|
86
|
+
const termW = terminalWidth();
|
|
87
|
+
let contentW = 0;
|
|
88
|
+
for (const v of visible) {
|
|
89
|
+
contentW = Math.max(contentW, plainLen(v.body));
|
|
90
|
+
}
|
|
91
|
+
const headerPlain = `${pathName} +${added} -${removed}`;
|
|
92
|
+
const boxW = Math.min(Math.max(contentW, plainLen(headerPlain)) + 6, Math.max(termW - 2, 20));
|
|
93
|
+
const innerW = Math.max(boxW - 4, 1);
|
|
94
|
+
const maxPath = Math.max(10, boxW - 2 - plainLen(` +${added} -${removed}`) - 3);
|
|
95
|
+
let pathDisp = pathName;
|
|
96
|
+
if (plainLen(pathDisp) > maxPath)
|
|
97
|
+
pathDisp = truncateStyled(pathDisp, Math.max(maxPath - 1, 1)) + "…";
|
|
98
|
+
const header = `\x1b[1m${pathDisp}\x1b[0m \x1b[32m+${added}\x1b[0m \x1b[31m-${removed}\x1b[0m`;
|
|
99
|
+
const headerStr = `─ ${header} `;
|
|
100
|
+
const top = `╭${headerStr}${"─".repeat(Math.max(0, boxW - 2 - plainLen(headerStr)))}╮`;
|
|
101
|
+
process.stdout.write(`\x1b[90m${top}\x1b[0m\n`);
|
|
102
|
+
for (const v of visible) {
|
|
103
|
+
let line;
|
|
104
|
+
if (v.kind === "+") {
|
|
105
|
+
const body = truncateStyled(v.body, innerW - 2);
|
|
106
|
+
line = `\x1b[32m+ ${body}\x1b[0m`;
|
|
107
|
+
}
|
|
108
|
+
else if (v.kind === "-") {
|
|
109
|
+
const body = truncateStyled(v.body, innerW - 2);
|
|
110
|
+
line = `\x1b[31m- ${body}\x1b[0m`;
|
|
86
111
|
}
|
|
87
112
|
else {
|
|
88
|
-
|
|
113
|
+
const body = truncateStyled(v.body, innerW - 2);
|
|
114
|
+
line = `\x1b[2m ${body}\x1b[0m`;
|
|
89
115
|
}
|
|
116
|
+
const fill = Math.max(0, innerW - plainLen(line));
|
|
117
|
+
process.stdout.write(`\x1b[90m│\x1b[0m ${line}${" ".repeat(fill)} \x1b[90m│\x1b[0m\n`);
|
|
90
118
|
}
|
|
91
|
-
process.stdout.write(`\x1b[90m
|
|
119
|
+
process.stdout.write(`\x1b[90m╰${"─".repeat(boxW - 2)}╯\x1b[0m\n`);
|
|
92
120
|
}
|
|
93
121
|
export function truncateToolOutput(output, maxChars = max_output_chars) {
|
|
94
122
|
if (output.length > maxChars) {
|
package/dist/ui.js
CHANGED
|
@@ -213,9 +213,9 @@ function formatMarkdownTable(tableLines) {
|
|
|
213
213
|
colWidths.push(Math.max(mw, 3));
|
|
214
214
|
}
|
|
215
215
|
const out = [];
|
|
216
|
-
const topBorder = `\x1b[90m
|
|
216
|
+
const topBorder = `\x1b[90m╭${colWidths.map((w) => "─".repeat(w + 2)).join("┬")}╮\x1b[0m`;
|
|
217
217
|
const midBorder = `\x1b[90m├${colWidths.map((w) => "─".repeat(w + 2)).join("┼")}┤\x1b[0m`;
|
|
218
|
-
const botBorder = `\x1b[90m
|
|
218
|
+
const botBorder = `\x1b[90m╰${colWidths.map((w) => "─".repeat(w + 2)).join("┴")}╯\x1b[0m`;
|
|
219
219
|
out.push(topBorder);
|
|
220
220
|
const headCells = header.map((h, i) => {
|
|
221
221
|
const formatted = `\x1b[1;37m${formatInlineMarkdown(h)}\x1b[0m`;
|
|
@@ -302,20 +302,20 @@ export function markdownToAnsi(text) {
|
|
|
302
302
|
continue;
|
|
303
303
|
}
|
|
304
304
|
let line = raw;
|
|
305
|
-
// Headers
|
|
305
|
+
// Headers (render as styled headings with the '#' markers stripped)
|
|
306
306
|
const h1 = line.match(/^#\s+(.+)$/);
|
|
307
307
|
if (h1) {
|
|
308
|
-
out.push(`\x1b[1;36m
|
|
308
|
+
out.push(`\x1b[1;36m${formatInlineMarkdown(h1[1])}\x1b[0m`);
|
|
309
309
|
continue;
|
|
310
310
|
}
|
|
311
311
|
const h2 = line.match(/^##\s+(.+)$/);
|
|
312
312
|
if (h2) {
|
|
313
|
-
out.push(`\x1b[1;34m
|
|
313
|
+
out.push(`\x1b[1;34m${formatInlineMarkdown(h2[1])}\x1b[0m`);
|
|
314
314
|
continue;
|
|
315
315
|
}
|
|
316
316
|
const h3 = line.match(/^###\s+(.+)$/);
|
|
317
317
|
if (h3) {
|
|
318
|
-
out.push(`\x1b[1;37m
|
|
318
|
+
out.push(`\x1b[1;37m${formatInlineMarkdown(h3[1])}\x1b[0m`);
|
|
319
319
|
continue;
|
|
320
320
|
}
|
|
321
321
|
// Blockquote
|
|
@@ -728,11 +728,25 @@ function wrapRuns(runs, width) {
|
|
|
728
728
|
while (rest.length > 0) {
|
|
729
729
|
if (curLen >= width)
|
|
730
730
|
flush();
|
|
731
|
-
let take
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
731
|
+
let take;
|
|
732
|
+
if (curLen === 0) {
|
|
733
|
+
// Start of a line: take a whole word (or one char for a lone space,
|
|
734
|
+
// or a full-width chunk for an overlong word).
|
|
735
|
+
const sp = rest.indexOf(" ");
|
|
736
|
+
take = sp === -1 ? rest.length : sp;
|
|
737
|
+
if (take === 0)
|
|
738
|
+
take = 1;
|
|
739
|
+
take = Math.min(take, width);
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
// Mid-line: append the next whole word (plus its trailing space) only
|
|
743
|
+
// if it fits; otherwise move the entire word to the next line.
|
|
744
|
+
const sp = rest.indexOf(" ");
|
|
745
|
+
take = sp === -1 ? rest.length : sp + 1;
|
|
746
|
+
if (take > width - curLen) {
|
|
747
|
+
flush();
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
736
750
|
}
|
|
737
751
|
const chunk = rest.slice(0, take);
|
|
738
752
|
cur += run.style ? run.style + chunk + "\x1b[0m" : chunk;
|
|
@@ -874,9 +888,6 @@ export function isShiftEnterKey(str, key) {
|
|
|
874
888
|
seq === "\x1bOM") {
|
|
875
889
|
return true;
|
|
876
890
|
}
|
|
877
|
-
if (!key.ctrl && (str === "\n" || seq === "\n" || key.name === "linefeed")) {
|
|
878
|
-
return true;
|
|
879
|
-
}
|
|
880
891
|
return false;
|
|
881
892
|
}
|
|
882
893
|
export const isNewlineKey = isShiftEnterKey;
|
|
@@ -887,7 +898,6 @@ export function isSubmitEnterKey(str, key) {
|
|
|
887
898
|
return false;
|
|
888
899
|
const seq = key.sequence || str || "";
|
|
889
900
|
if ((key.name === "return" ||
|
|
890
|
-
key.name === "enter" ||
|
|
891
901
|
seq === "\r" ||
|
|
892
902
|
seq === "\x1b[13u" ||
|
|
893
903
|
seq === "\x1b[13;1u") &&
|
|
@@ -1374,6 +1384,13 @@ export function askBottomPrompt(label = "You", prefix = "❯", history = []) {
|
|
|
1374
1384
|
repaint();
|
|
1375
1385
|
return;
|
|
1376
1386
|
}
|
|
1387
|
+
// Ctrl+J sends a bare LF (0x0a). Node reports it as name "enter" or
|
|
1388
|
+
// "linefeed" with no shift modifier. Only Shift+Enter should create a
|
|
1389
|
+
// new row, so ignore a lone LF (it is not a real Enter — a real Enter
|
|
1390
|
+
// arrives as CR / name "return").
|
|
1391
|
+
if (key && !key.shift && (key.name === "enter" || key.name === "linefeed")) {
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1377
1394
|
if (str) {
|
|
1378
1395
|
const isPaste = str.length > 1;
|
|
1379
1396
|
insert(str.replace(/\r\n/g, "\n").replace(/\r/g, "\n"), isPaste);
|