@oxecli/oxe 1.0.83 → 1.0.85
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 +5 -3
- package/dist/engine.js +9 -5
- package/dist/tools.js +18 -13
- package/dist/ui.js +13 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -45,11 +45,13 @@ export class CLI {
|
|
|
45
45
|
reasoning_effort: engine.reasoningEffort,
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
|
-
printToolEntry(started, text, status) {
|
|
48
|
+
printToolEntry(started, text, status, diff = "") {
|
|
49
49
|
const label = started && String(started).trim();
|
|
50
50
|
const body = String(text).trim();
|
|
51
|
-
if (label && stripAnsi(body)
|
|
51
|
+
if (label && /^[⎿╰]/.test(stripAnsi(body))) {
|
|
52
52
|
process.stdout.write(`${label}\n${body}\n`);
|
|
53
|
+
if (diff)
|
|
54
|
+
process.stdout.write(diff + "\n\n");
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
57
|
const icon = status === "failed" ? "\x1b[31m✗\x1b[0m" : "\x1b[32m✓\x1b[0m";
|
|
@@ -213,7 +215,7 @@ export class CLI {
|
|
|
213
215
|
this.printAssistantBlock(text);
|
|
214
216
|
}
|
|
215
217
|
else if (typ === "tool") {
|
|
216
|
-
this.printToolEntry(e["started"], text, e["status"] ?? "");
|
|
218
|
+
this.printToolEntry(e["started"], text, e["status"] ?? "", e["diff"] ?? "");
|
|
217
219
|
}
|
|
218
220
|
else if (typ === "footer") {
|
|
219
221
|
process.stdout.write(mutedMarkdown(text) + "\n");
|
package/dist/engine.js
CHANGED
|
@@ -5,7 +5,7 @@ import { buildTools, truncateToolOutput, toolReadFile, toolWriteFile, toolEditFi
|
|
|
5
5
|
import { mutedMarkdown, aiMarkdown, tickDuration, displayRows, safeCommitPoint, printAiChunk, toolCallLabel, formatToolResult, renderPanel, Spinner, hideCursor, } from "./ui.js";
|
|
6
6
|
import { reportUsage } from "./api.js";
|
|
7
7
|
import { stripOrphanCalls, persistCompactionSummary, toolOutputFailed, } from "./sessions.js";
|
|
8
|
-
import {
|
|
8
|
+
import { takePendingDiffOutput } from "./tools.js";
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// Token estimation
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
@@ -581,10 +581,10 @@ export class InferenceEngine {
|
|
|
581
581
|
this.queryCalledTool = true;
|
|
582
582
|
const started = toolCallLabel(c.name, c.arguments);
|
|
583
583
|
// Print the tool label the instant the command starts, then show a
|
|
584
|
-
// "
|
|
584
|
+
// "╰─ Working..." dots line that swaps to the real result in place.
|
|
585
585
|
process.stdout.write(`${started}\n`);
|
|
586
586
|
const toolSpinner = new Spinner();
|
|
587
|
-
toolSpinner.startDots("\x1b[90m
|
|
587
|
+
toolSpinner.startDots("\x1b[90m╰─\x1b[0m Working");
|
|
588
588
|
const rawResult = await this.runTool(c.name, c.arguments);
|
|
589
589
|
if (this.interrupted) {
|
|
590
590
|
toolSpinner.stop();
|
|
@@ -592,17 +592,21 @@ export class InferenceEngine {
|
|
|
592
592
|
}
|
|
593
593
|
const failed = toolOutputFailed(c.name, rawResult);
|
|
594
594
|
const action = formatToolResult(rawResult, failed);
|
|
595
|
+
const diff = takePendingDiffOutput();
|
|
595
596
|
story.push({
|
|
596
597
|
type: "tool",
|
|
597
598
|
started,
|
|
598
599
|
text: action,
|
|
599
600
|
status: failed ? "failed" : "ok",
|
|
601
|
+
diff,
|
|
600
602
|
});
|
|
601
|
-
// Swap the "
|
|
603
|
+
// Swap the "╰─ Working..." line for the real result. If the tool
|
|
602
604
|
// produced a diff it renders directly below the action line with no
|
|
603
605
|
// gap; otherwise a blank line separates the action from what follows.
|
|
604
606
|
toolSpinner.replaceWith(action);
|
|
605
|
-
if (
|
|
607
|
+
if (diff)
|
|
608
|
+
process.stdout.write(diff + "\n\n");
|
|
609
|
+
else
|
|
606
610
|
process.stdout.write("\n");
|
|
607
611
|
const truncatedResult = c.name === "read_file"
|
|
608
612
|
? truncateToolOutput(rawResult, max_read_file_stored_chars)
|
package/dist/tools.js
CHANGED
|
@@ -34,16 +34,19 @@ function truncateDiffLine(line) {
|
|
|
34
34
|
// Diff output is deferred (not written straight to stdout) so it doesn't
|
|
35
35
|
// collide with the tool spinner that is still animating while the tool runs.
|
|
36
36
|
const pendingDiffOutput = [];
|
|
37
|
-
export function
|
|
37
|
+
export function takePendingDiffOutput() {
|
|
38
38
|
if (!pendingDiffOutput.length)
|
|
39
|
-
return
|
|
40
|
-
|
|
39
|
+
return "";
|
|
40
|
+
const out = pendingDiffOutput.join("\n");
|
|
41
41
|
pendingDiffOutput.length = 0;
|
|
42
|
-
return
|
|
42
|
+
return out;
|
|
43
43
|
}
|
|
44
44
|
function displayDiff(pathName, oldContent, newContent) {
|
|
45
|
+
// Indent every diff row 3 columns so the diff's left edge lines up with the
|
|
46
|
+
// tool result text above it ("╰─ Wrote N chars").
|
|
47
|
+
const ind = " ";
|
|
45
48
|
if (oldContent.length + newContent.length > max_diff_source_chars) {
|
|
46
|
-
pendingDiffOutput.push(`\x1b[90m${pathName}: ${oldContent.length.toLocaleString()} chars -> ${newContent.length.toLocaleString()} chars ` +
|
|
49
|
+
pendingDiffOutput.push(`\x1b[90m${ind}${pathName}: ${oldContent.length.toLocaleString()} chars -> ${newContent.length.toLocaleString()} chars ` +
|
|
47
50
|
`(diff hidden: exceeds ${max_diff_source_chars.toLocaleString()} char limit)\x1b[0m`);
|
|
48
51
|
return null;
|
|
49
52
|
}
|
|
@@ -123,12 +126,12 @@ function displayDiff(pathName, oldContent, newContent) {
|
|
|
123
126
|
maxNum = Math.max(maxNum, v.newNum);
|
|
124
127
|
}
|
|
125
128
|
const numW = String(maxNum).length;
|
|
126
|
-
const bodyMax = Math.max(termW - numW - 4, 20);
|
|
129
|
+
const bodyMax = Math.max(termW - ind.length - numW - 4, 20);
|
|
127
130
|
const gap = " ".repeat(numW);
|
|
128
131
|
for (const v of visible) {
|
|
129
132
|
if (v.kind === "~") {
|
|
130
133
|
const body = truncateStyled(v.body, bodyMax);
|
|
131
|
-
pendingDiffOutput.push(`\x1b[90m${gap} ${body}\x1b[0m`);
|
|
134
|
+
pendingDiffOutput.push(`\x1b[90m${ind}${gap} ${body}\x1b[0m`);
|
|
132
135
|
continue;
|
|
133
136
|
}
|
|
134
137
|
const num = v.kind === "+" ? v.newNum : v.oldNum;
|
|
@@ -145,15 +148,17 @@ function displayDiff(pathName, oldContent, newContent) {
|
|
|
145
148
|
// the whole body.
|
|
146
149
|
const raw = highlightCodeLine(v.body);
|
|
147
150
|
const body = truncateStyled(raw.replace(/\x1b\[0m/g, `\x1b[0m${bg}\x1b[97m`), bodyMax);
|
|
148
|
-
const fill = Math.max(0, termW - numW - 4 - plainLen(body));
|
|
149
|
-
pendingDiffOutput.push(`${bg}${fg}${numStr} ${sign} \x1b[0m${bg}\x1b[97m${body}${bg}\x1b[97m${" ".repeat(fill)}\x1b[0m`);
|
|
151
|
+
const fill = Math.max(0, termW - ind.length - numW - 4 - plainLen(body));
|
|
152
|
+
pendingDiffOutput.push(`${ind}${bg}${fg}${numStr} ${sign} \x1b[0m${bg}\x1b[97m${body}${bg}\x1b[97m${" ".repeat(fill)}\x1b[0m`);
|
|
150
153
|
}
|
|
151
154
|
else {
|
|
152
155
|
const raw = v.kind === "\\" ? v.body : highlightCodeLine(v.body);
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
// Unchanged lines render their content normal white like the changed
|
|
157
|
+
// lines (only the number gutter stays dim). highlightCodeLine resets to
|
|
158
|
+
// default after each token, so re-apply the base after every reset.
|
|
159
|
+
const base = v.kind === "\\" ? "\x1b[90m" : "\x1b[97m";
|
|
160
|
+
const body = truncateStyled(raw.replace(/\x1b\[0m/g, `\x1b[0m${base}`), bodyMax);
|
|
161
|
+
pendingDiffOutput.push(`\x1b[90m${ind}${numStr} \x1b[0m${base}${body}\x1b[0m`);
|
|
157
162
|
}
|
|
158
163
|
}
|
|
159
164
|
return { added, removed };
|
package/dist/ui.js
CHANGED
|
@@ -240,14 +240,15 @@ export function markdownToAnsi(text) {
|
|
|
240
240
|
const flushCodeBlock = () => {
|
|
241
241
|
if (!codeBuffer.length && !codeLang)
|
|
242
242
|
return;
|
|
243
|
-
const badge = codeLang ? ` \x1b[1;36m${codeLang}\x1b[0m ` : "
|
|
244
|
-
const barLen = Math.max(10, Math.min(width - plainLen(badge) - 4, 60));
|
|
245
|
-
|
|
243
|
+
const badge = codeLang ? ` \x1b[1;36m${codeLang}\x1b[0m ` : "";
|
|
244
|
+
const barLen = Math.max(10, Math.min(width - plainLen(badge) - (codeLang ? 4 : 2), 60));
|
|
245
|
+
const topPrefix = codeLang ? "─" : "";
|
|
246
|
+
out.push(`\x1b[90m╭${topPrefix}${badge}${"─".repeat(barLen)}╮\x1b[0m`);
|
|
246
247
|
for (const cline of codeBuffer) {
|
|
247
248
|
const highlighted = highlightCodeLine(cline, codeLang);
|
|
248
249
|
out.push(`\x1b[90m│\x1b[0m ${highlighted}`);
|
|
249
250
|
}
|
|
250
|
-
out.push(`\x1b[90m╰${"─".repeat(barLen + plainLen(badge) + 1)}╯\x1b[0m`);
|
|
251
|
+
out.push(`\x1b[90m╰${"─".repeat(barLen + plainLen(badge) + (codeLang ? 1 : 0))}╯\x1b[0m`);
|
|
251
252
|
codeBuffer = [];
|
|
252
253
|
codeLang = "";
|
|
253
254
|
};
|
|
@@ -720,7 +721,7 @@ export function toolCallLabel(name, argumentsJson) {
|
|
|
720
721
|
return `\x1b[1;36m${display}\x1b[0m(${arg})`;
|
|
721
722
|
}
|
|
722
723
|
/**
|
|
723
|
-
* Second line of a tool entry:
|
|
724
|
+
* Second line of a tool entry: `╰─ Done` on a quiet success, otherwise the
|
|
724
725
|
* tool's output / error, collapsed and truncated so it never floods the screen.
|
|
725
726
|
*/
|
|
726
727
|
export function formatToolResult(rawResult, failed) {
|
|
@@ -729,7 +730,7 @@ export function formatToolResult(rawResult, failed) {
|
|
|
729
730
|
.trim();
|
|
730
731
|
const code = raw.match(/^exit code: (\d+)/)?.[1] ?? null;
|
|
731
732
|
const clean = raw.replace(/^exit code: \d+\n?/, "").trim();
|
|
732
|
-
const corner = "\x1b[90m
|
|
733
|
+
const corner = "\x1b[90m╰─\x1b[0m ";
|
|
733
734
|
if (!failed && (!clean || clean === "(no output)")) {
|
|
734
735
|
return `${corner}\x1b[32mDone\x1b[0m`;
|
|
735
736
|
}
|
|
@@ -871,10 +872,10 @@ export function renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor
|
|
|
871
872
|
const boxW = Math.max(termW - 4, 16);
|
|
872
873
|
const innerW = boxW - 4;
|
|
873
874
|
const runs = [];
|
|
874
|
-
runs.push({ text: prefix, style: "\x1b[
|
|
875
|
+
runs.push({ text: prefix, style: "\x1b[90m" });
|
|
875
876
|
runs.push({ text: " ", style: "" });
|
|
876
877
|
if (!buffer) {
|
|
877
|
-
runs.push({ text: PROMPT_CARET, style: "\x1b[
|
|
878
|
+
runs.push({ text: PROMPT_CARET, style: "\x1b[90m" });
|
|
878
879
|
runs.push({ text: PROMPT_PLACEHOLDER, style: "\x1b[90m" });
|
|
879
880
|
}
|
|
880
881
|
else {
|
|
@@ -933,14 +934,14 @@ export function renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor
|
|
|
933
934
|
runs.push({ text: disp, style: "\x1b[1;36m" });
|
|
934
935
|
if (!endsWithWs(disp))
|
|
935
936
|
runs.push({ text: " ", style: "" });
|
|
936
|
-
runs.push({ text: PROMPT_CARET, style: "\x1b[
|
|
937
|
+
runs.push({ text: PROMPT_CARET, style: "\x1b[90m" });
|
|
937
938
|
}
|
|
938
939
|
else {
|
|
939
940
|
const before = disp.slice(0, inside);
|
|
940
941
|
const after = disp.slice(inside);
|
|
941
942
|
if (before)
|
|
942
943
|
runs.push({ text: before, style: "" });
|
|
943
|
-
runs.push({ text: PROMPT_CARET, style: "\x1b[
|
|
944
|
+
runs.push({ text: PROMPT_CARET, style: "\x1b[90m" });
|
|
944
945
|
if (after)
|
|
945
946
|
runs.push({ text: after, style: "" });
|
|
946
947
|
}
|
|
@@ -955,8 +956,8 @@ export function renderBufferWithCursor(buffer, pasteSpans, prefix, label, cursor
|
|
|
955
956
|
}
|
|
956
957
|
const { rows, caretRow, caretCol } = wrapRuns(runs, innerW);
|
|
957
958
|
const labelLen = plainLen(label);
|
|
958
|
-
const topPad = Math.max(0, boxW - labelLen -
|
|
959
|
-
const top = `\x1b[90m
|
|
959
|
+
const topPad = Math.max(0, boxW - labelLen - 7);
|
|
960
|
+
const top = `\x1b[90m╭─── \x1b[1m\x1b[36m${label}\x1b[0m\x1b[90m ${"─".repeat(topPad)}╮\x1b[0m`;
|
|
960
961
|
const body = [];
|
|
961
962
|
for (const l of rows) {
|
|
962
963
|
const plain = stripAnsi(l);
|