aether-code 0.25.0 → 0.26.0

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.
@@ -28,7 +28,7 @@ import {
28
28
  import readline from "node:readline";
29
29
  import { c, errorLine, divider, setTerminalTitle } from "../src/render.js";
30
30
 
31
- const VERSION = "0.25.0";
31
+ const VERSION = "0.26.0";
32
32
 
33
33
  /**
34
34
  * Try to start MCP servers from ~/.aether/mcp.json. Returns a started
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aether-code",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "Uncensored AI coding agent for your terminal — Claude Code alternative with MCP support. Reads code, writes files, runs commands. Drives IDA Pro, Roblox Studio, Wireshark, Blender, and any MCP server. No refusal layer.",
5
5
  "homepage": "https://trynoguard.com",
6
6
  "repository": {
package/src/render.js CHANGED
@@ -26,17 +26,35 @@ export function divider() {
26
26
 
27
27
  const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
28
28
 
29
- // Draw a bordered box around content lines. Light box-drawing chars — same
30
- // Unicode block as the rules we already render, so it's safe in cmd.exe.
31
- export function box(lines, { color = c.gray, padX = 2 } = {}) {
32
- const inner = Math.max(0, ...lines.map((l) => stripAnsi(l).length));
29
+ // Draw a bordered box around content lines, returning the rows as an array (so
30
+ // boxes can be composed side by side). Rounded box-drawing chars.
31
+ export function boxLines(lines, { color = c.gray, padX = 2, minInner = 0 } = {}) {
32
+ const inner = Math.max(minInner, 0, ...lines.map((l) => stripAnsi(l).length));
33
33
  const w = inner + padX * 2;
34
34
  const top = color("╭" + "─".repeat(w) + "╮");
35
35
  const bot = color("╰" + "─".repeat(w) + "╯");
36
36
  const mid = lines.map(
37
37
  (l) => color("│") + " ".repeat(padX) + l + " ".repeat(inner - stripAnsi(l).length + padX) + color("│"),
38
38
  );
39
- return [top, ...mid, bot].join("\n");
39
+ return [top, ...mid, bot];
40
+ }
41
+
42
+ export function box(lines, opts) {
43
+ return boxLines(lines, opts).join("\n");
44
+ }
45
+
46
+ // Join two blocks of lines horizontally with a gap, padding the left block to a
47
+ // uniform width so the right block aligns. Shorter block is padded with blanks.
48
+ export function sideBySide(a, b, gap = 3) {
49
+ const wa = Math.max(0, ...a.map((l) => stripAnsi(l).length));
50
+ const h = Math.max(a.length, b.length);
51
+ const out = [];
52
+ for (let i = 0; i < h; i++) {
53
+ const la = a[i] ?? "";
54
+ const lb = b[i] ?? "";
55
+ out.push(la + " ".repeat(wa - stripAnsi(la).length + gap) + lb);
56
+ }
57
+ return out.join("\n");
40
58
  }
41
59
 
42
60
  export function turn(n) {
package/src/repl.js CHANGED
@@ -12,11 +12,11 @@ import path from "node:path";
12
12
  import { runAgent } from "./agent.js";
13
13
  import { fetchBalance, AetherError } from "./api.js";
14
14
  import { runSetup } from "./setup.js";
15
- import { c, errorLine, box } from "./render.js";
15
+ import { c, errorLine, boxLines, sideBySide } from "./render.js";
16
16
  import { checkForUpdate } from "./update-check.js";
17
17
  import { promptBox, EXIT_SIGNAL } from "./box-input.js";
18
18
 
19
- const VERSION = "0.25.0";
19
+ const VERSION = "0.26.0";
20
20
  const MODEL_NAME = "Aether Core";
21
21
 
22
22
  const SHORTCUTS = `
@@ -213,16 +213,40 @@ async function handleSlash(line, state) {
213
213
  const visLen = (s) => s.replace(/\x1b\[[0-9;]*m/g, "").length;
214
214
  const shortenPath = (p, max) => (p.length <= max ? p : "..." + p.slice(p.length - max + 3));
215
215
 
216
+ // Short, hand-curated highlights shown in the "What's new" box. Update with
217
+ // notable releases — keep it to ~3 lines so it balances the banner height.
218
+ const WHATS_NEW = [
219
+ "Files now save to your Desktop",
220
+ "Live thinking spinner + loop guard",
221
+ "Asks you when a task is unclear",
222
+ ];
223
+
216
224
  function printBanner(state) {
217
225
  const mode = state.autoYes ? (state.unsafePaths ? "skip-permissions" : "auto-yes") : "review mode";
218
- const credits = state.balance != null ? `${state.balance.toLocaleString()} credits · ` : "";
219
- const lines = [
220
- c.bold(c.magenta("aether")) + c.gray(" · ") + c.bold(MODEL_NAME) + c.gray(" v" + VERSION),
221
- c.gray("1M context · uncensored · " + mode),
222
- c.gray(credits + shortenPath(state.cwd, 52)),
223
- ];
226
+ const credits = state.balance != null ? `${state.balance.toLocaleString()} credits` : "";
227
+ const left = boxLines(
228
+ [
229
+ c.bold(c.magenta("aether")) + c.gray(" · ") + c.bold(MODEL_NAME) + c.gray(" v" + VERSION),
230
+ c.gray("uncensored AI coding agent"),
231
+ c.gray((credits ? credits + " · " : "") + mode),
232
+ c.gray(shortenPath(state.cwd, 52)),
233
+ ],
234
+ { color: c.magenta, padX: 2 },
235
+ );
236
+ const right = boxLines(
237
+ [c.bold(c.cyan("What's new")), ...WHATS_NEW.map((s) => c.gray("• " + s))],
238
+ { color: c.cyan, padX: 2 },
239
+ );
240
+
224
241
  console.log("");
225
- console.log(box(lines, { color: c.magenta, padX: 2 }));
242
+ const cols = process.stdout.columns || 80;
243
+ // Only place the two boxes side by side when there's room; otherwise the
244
+ // banner alone (narrow terminals would wrap and tear the second box).
245
+ if (cols >= visLen(left[0]) + visLen(right[0]) + 4) {
246
+ console.log(sideBySide(left, right, 3));
247
+ } else {
248
+ console.log(left.join("\n"));
249
+ }
226
250
  console.log("");
227
251
  }
228
252