@oxecli/oxe 1.0.99 → 1.0.101
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/README.md +38 -0
- package/dist/cli.js +4 -4
- package/dist/engine.js +2 -2
- package/dist/ui.js +42 -11
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
**Oxe**
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
AI-powered coding agent for your terminal.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Oxe is a terminal-based AI coding agent built for developers who want to work with AI directly inside their development environment. Give Oxe a task and let it inspect your codebase, edit files, run commands, and iterate with you.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
**Installation**
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
Install Oxe globally with npm:
|
|
18
|
+
|
|
19
|
+
* npm install -g @oxecli/oxe
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
Features
|
|
24
|
+
|
|
25
|
+
* Terminal-native — Work directly from your CLI.
|
|
26
|
+
* Codebase-aware — Inspect and understand your project before making changes.
|
|
27
|
+
* File editing — Create, modify, and refactor files.
|
|
28
|
+
* Command execution — Run development commands and inspect their output.
|
|
29
|
+
* Interactive prompts — Continue typing and interacting while the agent works.
|
|
30
|
+
* Diff viewer — Review file changes directly in the terminal.
|
|
31
|
+
* Tool execution — See what Oxe is doing as it works.
|
|
32
|
+
* AI-powered — Use the AI model configured for your Oxe deployment.
|
|
33
|
+
* Minimal interface — Designed to stay out of the way while you code.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Oxe can inspect your project, make the required changes, run commands, and show you what it is doing along the way.
|
|
38
|
+
|
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, plainLen, truncateStyled, terminalWidth, TOOL_RESULT_MAX_LINES, MAX_COMMAND_DISPLAY_CHARS, renderPanel, renderTableString, renderWarning, enableRawStdin, disableRawStdin, waitRawKey, dockAppendContent, dockSetInactive, dockTearDown, dockRenderPrompt, startDraftCapture, } 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, renderWarning, renderError, enableRawStdin, disableRawStdin, waitRawKey, dockAppendContent, dockSetInactive, dockTearDown, dockRenderPrompt, startDraftCapture, } 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");
|
|
@@ -251,7 +251,7 @@ export class CLI {
|
|
|
251
251
|
renderWarning(text);
|
|
252
252
|
}
|
|
253
253
|
else if (typ === "error") {
|
|
254
|
-
|
|
254
|
+
renderError(text);
|
|
255
255
|
}
|
|
256
256
|
else {
|
|
257
257
|
process.stdout.write(mutedMarkdown(text) + "\n");
|
|
@@ -429,7 +429,7 @@ export class CLI {
|
|
|
429
429
|
input = this.autoSubmit.input;
|
|
430
430
|
spans = this.autoSubmit.spans;
|
|
431
431
|
this.autoSubmit = null;
|
|
432
|
-
dockRenderPrompt("", [], "
|
|
432
|
+
dockRenderPrompt("", [], "❯", "You", hints);
|
|
433
433
|
}
|
|
434
434
|
else {
|
|
435
435
|
const got = await askBottomPrompt("You", "❯", this.promptHistory, hints, this.lastPrompt);
|
|
@@ -494,7 +494,7 @@ export class CLI {
|
|
|
494
494
|
// next one; Enter submits it as a new prompt.
|
|
495
495
|
this.lastPrompt = "";
|
|
496
496
|
queryStarted = true;
|
|
497
|
-
const stopCapture = startDraftCapture(() => engine.interrupt(), (buf, sp) => dockRenderPrompt(buf, sp, "
|
|
497
|
+
const stopCapture = startDraftCapture(() => engine.interrupt(), (buf, sp) => dockRenderPrompt(buf, sp, "❯", "You", hints));
|
|
498
498
|
try {
|
|
499
499
|
await engine.executeQuery(input, this.inputItems, this.story, spans);
|
|
500
500
|
}
|
package/dist/engine.js
CHANGED
|
@@ -2,7 +2,7 @@ import OpenAI from "openai";
|
|
|
2
2
|
import { max_output_tokens, max_empty_retries, max_output_continuations, 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, safeCommitPoint, printAiChunk, toolCallLabel, formatToolResult,
|
|
5
|
+
import { mutedMarkdown, aiMarkdown, tickDuration, safeCommitPoint, printAiChunk, toolCallLabel, formatToolResult, renderWarning, renderError, Spinner, hideCursor, dockAppendContent, dockReplaceTransient, dockTransientStart, } from "./ui.js";
|
|
6
6
|
import { reportUsage } from "./api.js";
|
|
7
7
|
import { stripOrphanCalls, persistCompactionSummary, toolOutputFailed, } from "./sessions.js";
|
|
8
8
|
import { takePendingDiffOutput } from "./tools.js";
|
|
@@ -674,7 +674,7 @@ export class InferenceEngine {
|
|
|
674
674
|
}
|
|
675
675
|
function renderErrorPanel(msg, story) {
|
|
676
676
|
const text = msg.replace(/\[bold yellow\]|\[yellow\]|\[\/.*?\]/g, "");
|
|
677
|
-
|
|
677
|
+
renderError(text);
|
|
678
678
|
story.push({ type: "error", text: `Error: ${text}` });
|
|
679
679
|
}
|
|
680
680
|
export { renderErrorPanel };
|
package/dist/ui.js
CHANGED
|
@@ -479,25 +479,29 @@ function panelString(content, title = "", subtitle = "", expand = true, borderSt
|
|
|
479
479
|
export function renderPanel(content, title = "", subtitle = "", expand = true, borderStyle = "90", titleAlign = "center") {
|
|
480
480
|
dockAppendContent(panelString(content, title, subtitle, expand, borderStyle, titleAlign) + "\n");
|
|
481
481
|
}
|
|
482
|
-
/** Warning sign with the text-presentation variation selector, so it renders
|
|
483
|
-
* as a monochrome icon (matching the app's ✓/✗ symbols) rather than a color
|
|
484
|
-
* emoji that varies by platform. */
|
|
485
|
-
export const WARNING_ICON = "\u26A0\uFE0E";
|
|
486
482
|
/**
|
|
487
483
|
* Render a warning as a framed panel (consistent shape whether shown live or
|
|
488
|
-
* replayed from a resumed session)
|
|
489
|
-
*
|
|
484
|
+
* replayed from a resumed session). The first sentence is emphasized; the rest
|
|
485
|
+
* is plain. No icon — just the panel frame and text.
|
|
490
486
|
*/
|
|
491
487
|
export function renderWarning(message) {
|
|
492
488
|
const text = String(message ?? "").replace(/^[\s\u26A0\uFE0E]+/, "").trim();
|
|
493
489
|
const idx = text.indexOf(". ");
|
|
494
490
|
const head = idx === -1 ? text : text.slice(0, idx + 1);
|
|
495
491
|
const body = idx === -1 ? "" : text.slice(idx + 2).trim();
|
|
496
|
-
let content = `[bold yellow]${
|
|
492
|
+
let content = `[bold yellow]${head}[/bold yellow]`;
|
|
497
493
|
if (body)
|
|
498
494
|
content += `\n[yellow]${body}[/yellow]`;
|
|
499
495
|
renderPanel(content, "Warning", "", false, "33");
|
|
500
496
|
}
|
|
497
|
+
/**
|
|
498
|
+
* Render an error as a framed panel (consistent shape whether shown live or
|
|
499
|
+
* replayed from a resumed session), matching renderWarning's framing.
|
|
500
|
+
*/
|
|
501
|
+
export function renderError(message) {
|
|
502
|
+
const text = String(message ?? "").replace(/^Error:\s*/i, "").trim();
|
|
503
|
+
renderPanel(`[red]${text}[/red]`, "Error", "", false, "31");
|
|
504
|
+
}
|
|
501
505
|
// ---------------------------------------------------------------------------
|
|
502
506
|
// Table Panel Rendering
|
|
503
507
|
// ---------------------------------------------------------------------------
|
|
@@ -535,10 +539,13 @@ export function renderTableString(rows, opts = {}) {
|
|
|
535
539
|
export function formatDuration(seconds) {
|
|
536
540
|
const s = Math.max(0, seconds);
|
|
537
541
|
if (s < 60) {
|
|
542
|
+
// Sub-minute: update at 0.1s resolution.
|
|
538
543
|
return `${s.toFixed(1)}s`;
|
|
539
544
|
}
|
|
545
|
+
// Past one minute: update at whole-second resolution so the timer reads
|
|
546
|
+
// "1m 0s", "1m 1s", ... (not fractional seconds under a minutes prefix).
|
|
540
547
|
const mins = Math.floor(s / 60);
|
|
541
|
-
const remSecs = (s % 60)
|
|
548
|
+
const remSecs = Math.floor(s % 60);
|
|
542
549
|
return `${mins}m ${remSecs}s`;
|
|
543
550
|
}
|
|
544
551
|
export function tickDuration(seconds) {
|
|
@@ -743,15 +750,39 @@ function dockRenderBox() {
|
|
|
743
750
|
/**
|
|
744
751
|
* Redraw the docked prompt box with the given buffer/caret, keeping whatever
|
|
745
752
|
* content already streams above it. Used to make the prompt field live-edit
|
|
746
|
-
* while a query runs. Erases the box
|
|
747
|
-
*
|
|
748
|
-
*
|
|
753
|
+
* while a query runs. Erases the box, writes the new frame, and parks the
|
|
754
|
+
* cursor at the caret. Preserves exactly one blank row between the last content
|
|
755
|
+
* and the box. When a transient (thinking/working spinner) is parked above the
|
|
756
|
+
* box, it is rebuilt above the new frame and the physical cursor stays on that
|
|
757
|
+
* transient row, so streaming content is never pulled into the prompt.
|
|
749
758
|
*/
|
|
750
759
|
export function dockRenderPrompt(buffer, pasteSpans, prefix, label, hints) {
|
|
751
760
|
if (!docked || dockBoxRows < 1)
|
|
752
761
|
return;
|
|
762
|
+
// If a thinking/working spinner is currently parked above the box, typing
|
|
763
|
+
// must NOT disturb it. dockEraseBox() wipes that transient row and resets
|
|
764
|
+
// its state; the still-running spinner then keeps drawing at the physical
|
|
765
|
+
// caret (now inside the box), pulling streaming content into the prompt.
|
|
766
|
+
// Capture the transient before erasing so we can rebuild it above the frame
|
|
767
|
+
// and park the cursor back on the transient row, keeping the box intact.
|
|
768
|
+
const wasTransient = dockTransient;
|
|
769
|
+
const wasRel = dockRel;
|
|
753
770
|
const { frame, cursorRow, cursorCol, totalRows } = renderBufferWithCursor(buffer, pasteSpans, prefix, label, buffer.length, hints);
|
|
754
771
|
dockEraseBox();
|
|
772
|
+
if (wasTransient) {
|
|
773
|
+
// Rebuild the transient row(s) and gap above the new frame, then park the
|
|
774
|
+
// physical cursor at the transient row so the running spinner keeps
|
|
775
|
+
// drawing THERE instead of inside the prompt box.
|
|
776
|
+
process.stdout.write("\n".repeat(wasRel) + frame);
|
|
777
|
+
process.stdout.write(`\x1b[${totalRows - 1 + wasRel}A\r`);
|
|
778
|
+
dockSetFrame(frame, totalRows);
|
|
779
|
+
docked = true;
|
|
780
|
+
dockGap = false;
|
|
781
|
+
dockTransient = true;
|
|
782
|
+
dockRel = wasRel;
|
|
783
|
+
dockCaretBelow = 0;
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
755
786
|
process.stdout.write(frame);
|
|
756
787
|
// Park the cursor at the caret inside the box (cursorRow is 1-indexed, so
|
|
757
788
|
// the caret sits cursorRow rows below boxTop).
|