@oxecli/oxe 1.0.59 → 1.0.61
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 +31 -14
- 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
|
@@ -183,11 +183,14 @@ function highlightCodeLine(line, _lang = "") {
|
|
|
183
183
|
}
|
|
184
184
|
function formatInlineMarkdown(text) {
|
|
185
185
|
let line = text;
|
|
186
|
+
// Links MUST be converted first: the other rules inject ANSI escape codes,
|
|
187
|
+
// and the link pattern `\[[^\]]+\]\([^)]+\)` would otherwise match the `[`
|
|
188
|
+
// inside an already-inserted escape (e.g. `\x1b[1m`) and corrupt the output.
|
|
189
|
+
line = line.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "\x1b[4;36m$1\x1b[0m \x1b[2m($2)\x1b[0m");
|
|
186
190
|
line = line.replace(/\*\*\*([^*]+)\*\*\*/g, "\x1b[1;3m$1\x1b[0m");
|
|
187
191
|
line = line.replace(/\*\*([^*]+)\*\*/g, "\x1b[1m$1\x1b[0m");
|
|
188
192
|
line = line.replace(/(^|[^\w*])\*([^*\n]+)\*(?=$|[^\w*])/g, "$1\x1b[3m$2\x1b[0m");
|
|
189
193
|
line = line.replace(/`([^`]+)`/g, "\x1b[1;36m`$1`\x1b[0m");
|
|
190
|
-
line = line.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "\x1b[4;36m$1\x1b[0m \x1b[2m($2)\x1b[0m");
|
|
191
194
|
return line;
|
|
192
195
|
}
|
|
193
196
|
// ---------------------------------------------------------------------------
|
|
@@ -213,9 +216,9 @@ function formatMarkdownTable(tableLines) {
|
|
|
213
216
|
colWidths.push(Math.max(mw, 3));
|
|
214
217
|
}
|
|
215
218
|
const out = [];
|
|
216
|
-
const topBorder = `\x1b[90m
|
|
219
|
+
const topBorder = `\x1b[90m╭${colWidths.map((w) => "─".repeat(w + 2)).join("┬")}╮\x1b[0m`;
|
|
217
220
|
const midBorder = `\x1b[90m├${colWidths.map((w) => "─".repeat(w + 2)).join("┼")}┤\x1b[0m`;
|
|
218
|
-
const botBorder = `\x1b[90m
|
|
221
|
+
const botBorder = `\x1b[90m╰${colWidths.map((w) => "─".repeat(w + 2)).join("┴")}╯\x1b[0m`;
|
|
219
222
|
out.push(topBorder);
|
|
220
223
|
const headCells = header.map((h, i) => {
|
|
221
224
|
const formatted = `\x1b[1;37m${formatInlineMarkdown(h)}\x1b[0m`;
|
|
@@ -251,12 +254,12 @@ export function markdownToAnsi(text) {
|
|
|
251
254
|
return;
|
|
252
255
|
const badge = codeLang ? ` \x1b[1;36m${codeLang}\x1b[0m ` : " ";
|
|
253
256
|
const barLen = Math.max(10, Math.min(width - plainLen(badge) - 4, 60));
|
|
254
|
-
out.push(`\x1b[90m
|
|
257
|
+
out.push(`\x1b[90m╭─${badge}${"─".repeat(barLen)}╮\x1b[0m`);
|
|
255
258
|
for (const cline of codeBuffer) {
|
|
256
259
|
const highlighted = highlightCodeLine(cline, codeLang);
|
|
257
260
|
out.push(`\x1b[90m│\x1b[0m ${highlighted}`);
|
|
258
261
|
}
|
|
259
|
-
out.push(`\x1b[90m
|
|
262
|
+
out.push(`\x1b[90m╰${"─".repeat(barLen + plainLen(badge) + 2)}╯\x1b[0m`);
|
|
260
263
|
codeBuffer = [];
|
|
261
264
|
codeLang = "";
|
|
262
265
|
};
|
|
@@ -302,20 +305,20 @@ export function markdownToAnsi(text) {
|
|
|
302
305
|
continue;
|
|
303
306
|
}
|
|
304
307
|
let line = raw;
|
|
305
|
-
// Headers
|
|
308
|
+
// Headers (render as styled headings with the '#' markers stripped)
|
|
306
309
|
const h1 = line.match(/^#\s+(.+)$/);
|
|
307
310
|
if (h1) {
|
|
308
|
-
out.push(`\x1b[1;36m
|
|
311
|
+
out.push(`\x1b[1;36m${formatInlineMarkdown(h1[1])}\x1b[0m`);
|
|
309
312
|
continue;
|
|
310
313
|
}
|
|
311
314
|
const h2 = line.match(/^##\s+(.+)$/);
|
|
312
315
|
if (h2) {
|
|
313
|
-
out.push(`\x1b[1;34m
|
|
316
|
+
out.push(`\x1b[1;34m${formatInlineMarkdown(h2[1])}\x1b[0m`);
|
|
314
317
|
continue;
|
|
315
318
|
}
|
|
316
319
|
const h3 = line.match(/^###\s+(.+)$/);
|
|
317
320
|
if (h3) {
|
|
318
|
-
out.push(`\x1b[1;37m
|
|
321
|
+
out.push(`\x1b[1;37m${formatInlineMarkdown(h3[1])}\x1b[0m`);
|
|
319
322
|
continue;
|
|
320
323
|
}
|
|
321
324
|
// Blockquote
|
|
@@ -728,11 +731,25 @@ function wrapRuns(runs, width) {
|
|
|
728
731
|
while (rest.length > 0) {
|
|
729
732
|
if (curLen >= width)
|
|
730
733
|
flush();
|
|
731
|
-
let take
|
|
732
|
-
if (
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
734
|
+
let take;
|
|
735
|
+
if (curLen === 0) {
|
|
736
|
+
// Start of a line: take a whole word (or one char for a lone space,
|
|
737
|
+
// or a full-width chunk for an overlong word).
|
|
738
|
+
const sp = rest.indexOf(" ");
|
|
739
|
+
take = sp === -1 ? rest.length : sp;
|
|
740
|
+
if (take === 0)
|
|
741
|
+
take = 1;
|
|
742
|
+
take = Math.min(take, width);
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
// Mid-line: append the next whole word (plus its trailing space) only
|
|
746
|
+
// if it fits; otherwise move the entire word to the next line.
|
|
747
|
+
const sp = rest.indexOf(" ");
|
|
748
|
+
take = sp === -1 ? rest.length : sp + 1;
|
|
749
|
+
if (take > width - curLen) {
|
|
750
|
+
flush();
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
736
753
|
}
|
|
737
754
|
const chunk = rest.slice(0, take);
|
|
738
755
|
cur += run.style ? run.style + chunk + "\x1b[0m" : chunk;
|