@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 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
- process.stdout.write(`\x1b[31m${text}\x1b[0m\n`);
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("", [], "You", "", hints);
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, "You", "", hints));
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, renderPanel, renderWarning, Spinner, hideCursor, dockAppendContent, dockReplaceTransient, dockTransientStart, } from "./ui.js";
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
- renderPanel(text, "Error", "", true, "31");
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), using the text-presentation warning icon
489
- * instead of an emoji. The first sentence is emphasized; the rest is plain.
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]${WARNING_ICON} ${head}[/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).toFixed(1);
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 (and any transient row above it),
747
- * writes the new frame, and parks the cursor at the caret. Preserves exactly
748
- * one blank row between the last content and the box.
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).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxecli/oxe",
3
- "version": "1.0.99",
3
+ "version": "1.0.101",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },