@oxecli/oxe 1.0.74 → 1.0.75
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 +10 -4
- package/dist/engine.js +4 -6
- package/dist/ui.js +73 -51
- 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, } 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,
|
|
7
|
+
import { clearScreen, enableAnsi, askBottomPrompt, userDisplayText, aiMarkdown, mutedMarkdown, messageText, toolCallLabel, formatToolResult, truncateEllipsis, stripAnsi, 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");
|
|
@@ -46,9 +46,15 @@ export class CLI {
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
printToolEntry(started, text, status) {
|
|
49
|
+
const label = started && String(started).trim();
|
|
50
|
+
const body = String(text).trim();
|
|
51
|
+
if (label && stripAnsi(body).startsWith("⎿")) {
|
|
52
|
+
process.stdout.write(`${label}\n${body}\n`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
49
55
|
const icon = status === "failed" ? "\x1b[31m✗\x1b[0m" : "\x1b[32m✓\x1b[0m";
|
|
50
56
|
const style = status === "failed" ? "\x1b[31m" : "\x1b[32m";
|
|
51
|
-
process.stdout.write(`${icon} ${style}${truncateEllipsis(
|
|
57
|
+
process.stdout.write(`${icon} ${style}${truncateEllipsis(body, max_action_chars, "text")}\x1b[0m\n`);
|
|
52
58
|
}
|
|
53
59
|
printAssistantBlock(text) {
|
|
54
60
|
const rendered = aiMarkdown(text);
|
|
@@ -153,8 +159,8 @@ export class CLI {
|
|
|
153
159
|
const argumentsJson = it["arguments"] ?? "";
|
|
154
160
|
renders.push({
|
|
155
161
|
kind: "tool",
|
|
156
|
-
started:
|
|
157
|
-
text:
|
|
162
|
+
started: toolCallLabel(name, argumentsJson),
|
|
163
|
+
text: formatToolResult(rawResult, failed),
|
|
158
164
|
status: failed ? "failed" : "ok",
|
|
159
165
|
});
|
|
160
166
|
}
|
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 { getSystemPrompt } from "./system.js";
|
|
4
4
|
import { buildTools, truncateToolOutput, toolReadFile, toolWriteFile, toolEditFile, toolBash, toolGlob, toolGrep, toolLoadSkill, } from "./tools.js";
|
|
5
|
-
import { mutedMarkdown, aiMarkdown, tickDuration, displayRows, safeCommitPoint, printAiChunk,
|
|
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
8
|
import { flushPendingDiffOutput } from "./tools.js";
|
|
@@ -576,7 +576,7 @@ export class InferenceEngine {
|
|
|
576
576
|
}
|
|
577
577
|
for (const c of calls) {
|
|
578
578
|
this.workActive = false;
|
|
579
|
-
const started =
|
|
579
|
+
const started = toolCallLabel(c.name, c.arguments);
|
|
580
580
|
const toolSpinner = new Spinner();
|
|
581
581
|
toolSpinner.start(started);
|
|
582
582
|
const rawResult = await this.runTool(c.name, c.arguments);
|
|
@@ -584,16 +584,14 @@ export class InferenceEngine {
|
|
|
584
584
|
if (this.interrupted)
|
|
585
585
|
throw new Error("interrupt");
|
|
586
586
|
const failed = toolOutputFailed(c.name, rawResult);
|
|
587
|
-
const action =
|
|
587
|
+
const action = formatToolResult(rawResult, failed);
|
|
588
588
|
story.push({
|
|
589
589
|
type: "tool",
|
|
590
590
|
started,
|
|
591
591
|
text: action,
|
|
592
592
|
status: failed ? "failed" : "ok",
|
|
593
593
|
});
|
|
594
|
-
|
|
595
|
-
const actionStyle = failed ? "\x1b[31m" : "\x1b[32m";
|
|
596
|
-
process.stdout.write(`${icon} ${actionStyle}${action}\x1b[0m\n\n`);
|
|
594
|
+
process.stdout.write(`${started}\n${action}\n\n`);
|
|
597
595
|
// Any diff the tool produced is flushed only now, after the spinner
|
|
598
596
|
// has stopped, so the box renders cleanly below the action line.
|
|
599
597
|
flushPendingDiffOutput();
|
package/dist/ui.js
CHANGED
|
@@ -613,9 +613,22 @@ export class Spinner {
|
|
|
613
613
|
}
|
|
614
614
|
}
|
|
615
615
|
// ---------------------------------------------------------------------------
|
|
616
|
-
// Tool
|
|
616
|
+
// Tool Call & Result Formatter
|
|
617
617
|
// ---------------------------------------------------------------------------
|
|
618
|
-
|
|
618
|
+
const TOOL_DISPLAY_NAMES = {
|
|
619
|
+
read_file: "Read",
|
|
620
|
+
write_file: "Write",
|
|
621
|
+
edit_file: "Edit",
|
|
622
|
+
bash: "Bash",
|
|
623
|
+
glob: "Glob",
|
|
624
|
+
grep: "Grep",
|
|
625
|
+
load_skill: "Load",
|
|
626
|
+
};
|
|
627
|
+
const TOOL_RESULT_MAX_CHARS = 400;
|
|
628
|
+
const TOOL_RESULT_MAX_LINES = 6;
|
|
629
|
+
const TOOL_RESULT_MAX_LINE_CHARS = 160;
|
|
630
|
+
/** First line of a tool entry: `Bash(rm -f "…")` — cyan tool name, primary arg in parens. */
|
|
631
|
+
export function toolCallLabel(name, argumentsJson) {
|
|
619
632
|
let args = {};
|
|
620
633
|
try {
|
|
621
634
|
args = argumentsJson ? JSON.parse(argumentsJson) : {};
|
|
@@ -623,58 +636,67 @@ export function formatToolAction(name, argumentsJson, status = "ok") {
|
|
|
623
636
|
catch {
|
|
624
637
|
args = {};
|
|
625
638
|
}
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
if (status === "failed")
|
|
630
|
-
return `${verbFail} ${detail}`;
|
|
631
|
-
return `${verbOk} ${detail}`;
|
|
632
|
-
};
|
|
633
|
-
const p = args["path"] ?? "";
|
|
639
|
+
const display = TOOL_DISPLAY_NAMES[name] ??
|
|
640
|
+
(name ? name[0].toUpperCase() + name.slice(1) : "Tool");
|
|
641
|
+
let arg = "";
|
|
634
642
|
switch (name) {
|
|
635
|
-
case "
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
? ` lines ${start ?? 1}-${end ?? "end"}`
|
|
640
|
-
: "";
|
|
641
|
-
return phrase("Read", "Reading", "Failed to read", `${p}${span}`);
|
|
642
|
-
}
|
|
643
|
+
case "bash":
|
|
644
|
+
arg = String(args["command"] ?? "");
|
|
645
|
+
break;
|
|
646
|
+
case "read_file":
|
|
643
647
|
case "write_file":
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
return `Running command${location}: ${command}`;
|
|
655
|
-
if (status === "failed")
|
|
656
|
-
return `Command failed${location}: ${command}`;
|
|
657
|
-
return `Ran command${location}: ${command}`;
|
|
658
|
-
}
|
|
659
|
-
case "glob": {
|
|
660
|
-
const detail = String(args["pattern"] ?? "");
|
|
661
|
-
if (status === "failed")
|
|
662
|
-
return `Search failed for: ${detail}`;
|
|
663
|
-
return phrase("Searched files for", "Searching files for", "Search failed for", detail);
|
|
664
|
-
}
|
|
665
|
-
case "grep": {
|
|
666
|
-
const detail = String(args["pattern"] ?? "");
|
|
667
|
-
if (status === "failed")
|
|
668
|
-
return `Grep failed for: ${detail}`;
|
|
669
|
-
return phrase("Grepped for", "Grepping for", "Grep failed for", detail);
|
|
670
|
-
}
|
|
671
|
-
case "load_skill": {
|
|
672
|
-
const skill = String(args["skill_name"] ?? "");
|
|
673
|
-
return phrase("Loaded skill", "Loading skill", "Failed to load skill", skill);
|
|
674
|
-
}
|
|
675
|
-
default:
|
|
676
|
-
return phrase("Executed", "Executing", "Failed to execute", name);
|
|
648
|
+
case "edit_file":
|
|
649
|
+
arg = String(args["path"] ?? "");
|
|
650
|
+
break;
|
|
651
|
+
case "glob":
|
|
652
|
+
case "grep":
|
|
653
|
+
arg = String(args["pattern"] ?? "");
|
|
654
|
+
break;
|
|
655
|
+
case "load_skill":
|
|
656
|
+
arg = String(args["skill_name"] ?? "");
|
|
657
|
+
break;
|
|
677
658
|
}
|
|
659
|
+
arg = arg.trim();
|
|
660
|
+
if (!arg)
|
|
661
|
+
return `\x1b[1;36m${display}\x1b[0m`;
|
|
662
|
+
if (arg.length > 400)
|
|
663
|
+
arg = arg.slice(0, 400) + "…";
|
|
664
|
+
return `\x1b[1;36m${display}\x1b[0m(${arg})`;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Second line of a tool entry: `⎿ Done` on a quiet success, otherwise the
|
|
668
|
+
* tool's output / error, collapsed and truncated so it never floods the screen.
|
|
669
|
+
*/
|
|
670
|
+
export function formatToolResult(rawResult, failed) {
|
|
671
|
+
const raw = stripAnsi(rawResult || "")
|
|
672
|
+
.replace(/\r\n/g, "\n")
|
|
673
|
+
.trim();
|
|
674
|
+
const code = raw.match(/^exit code: (\d+)/)?.[1] ?? null;
|
|
675
|
+
const clean = raw.replace(/^exit code: \d+\n?/, "").trim();
|
|
676
|
+
const corner = "\x1b[90m⎿\x1b[0m ";
|
|
677
|
+
if (!failed && (!clean || clean === "(no output)")) {
|
|
678
|
+
return `${corner}\x1b[32mDone\x1b[0m`;
|
|
679
|
+
}
|
|
680
|
+
if (failed && (!clean || clean === "(no output)") && code) {
|
|
681
|
+
return `${corner}\x1b[31mexit code: ${code}\x1b[0m`;
|
|
682
|
+
}
|
|
683
|
+
let body = clean;
|
|
684
|
+
const clipped = body.length > TOOL_RESULT_MAX_CHARS;
|
|
685
|
+
if (clipped)
|
|
686
|
+
body = body.slice(0, TOOL_RESULT_MAX_CHARS);
|
|
687
|
+
const lines = body
|
|
688
|
+
.split("\n")
|
|
689
|
+
.slice(0, TOOL_RESULT_MAX_LINES)
|
|
690
|
+
.map((ln) => ln.length > TOOL_RESULT_MAX_LINE_CHARS
|
|
691
|
+
? ln.slice(0, TOOL_RESULT_MAX_LINE_CHARS) + "…"
|
|
692
|
+
: ln);
|
|
693
|
+
if (clipped || body.split("\n").length > TOOL_RESULT_MAX_LINES)
|
|
694
|
+
lines.push("…");
|
|
695
|
+
const styleOpen = failed ? "\x1b[31m" : "\x1b[90m";
|
|
696
|
+
const inner = lines
|
|
697
|
+
.map((ln, i) => (i === 0 ? corner : " ") + styleOpen + ln)
|
|
698
|
+
.join("\n");
|
|
699
|
+
return `${inner}\x1b[0m`;
|
|
678
700
|
}
|
|
679
701
|
// ---------------------------------------------------------------------------
|
|
680
702
|
// Prompt Editing & Interactive Input
|