aether-code 0.33.2 → 0.33.4

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.33.2";
31
+ const VERSION = "0.33.4";
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.33.2",
3
+ "version": "0.33.4",
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/box-input.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Minimal prompt input — raw-mode, cmd.exe-safe.
2
- // No box/border — just a bare "> " prompt like Claude Code.
2
+ // Bare "> " prompt with bottom status bar, matching Claude Code's layout.
3
3
  //
4
4
  // Features:
5
5
  // - Ctrl+V / Alt+V: paste image from clipboard
@@ -23,7 +23,6 @@ const HIDE = "\x1b[?25l";
23
23
  const SHOW = "\x1b[?25h";
24
24
  const INV = (s) => `\x1b[7m${s}\x1b[27m`;
25
25
 
26
- // Save clipboard image to a temp file. Returns the file path or null.
27
26
  function grabClipboardImage() {
28
27
  const tmpFile = path.join(os.tmpdir(), `aether-paste-${Date.now()}.png`);
29
28
 
@@ -62,7 +61,7 @@ function grabClipboardImage() {
62
61
  return null;
63
62
  }
64
63
 
65
- export function promptBox({ history = [], placeholder = "Message Aether…" } = {}) {
64
+ export function promptBox({ history = [], placeholder = "Message Aether...", mode = "review" } = {}) {
66
65
  return new Promise((resolve) => {
67
66
  if (!process.stdin.isTTY) {
68
67
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -79,16 +78,15 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
79
78
  let lastSigint = 0;
80
79
  const clipImages = [];
81
80
 
82
- const termW = () => Math.max(30, (process.stdout.columns || 80) - 4);
81
+ const termW = () => Math.max(30, (process.stdout.columns || 80) - 2);
83
82
 
84
83
  const draw = (first) => {
85
84
  const W = termW();
86
85
  if (!first) process.stdout.write(`\x1b[${drawn}A`);
87
86
  process.stdout.write("\x1b[J");
88
87
 
89
- // Horizontal scroll
90
- const prefix = c.cyan("> ");
91
- const prefixLen = 2; // "> " visible chars
88
+ const prefixStr = "> ";
89
+ const prefixLen = 2;
92
90
  const maxVisible = W - prefixLen;
93
91
  const start = pos > maxVisible ? pos - maxVisible : 0;
94
92
  const view = buf.slice(start, start + maxVisible);
@@ -112,12 +110,16 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
112
110
  content = s;
113
111
  }
114
112
 
115
- // Prompt line — bare "> " like Claude Code
116
- const promptLine = prefix + content;
113
+ // Bare prompt — just "> " like Claude Code
114
+ const promptLine = c.dim(prefixStr) + content;
117
115
 
118
- // Status bar — mode + shortcuts, like Claude's bottom bar
119
- const imgHint = clipImages.length > 0 ? " · " + c.cyan("images queued") : "";
120
- const statusBar = c.dim(" ? for help · @file for images · Alt+V paste") + imgHint;
116
+ // Bottom status bar — matches Claude's "manual mode on · ? for shortcuts · + for agents"
117
+ const imgNote = clipImages.length > 0 ? c.cyan(" images queued") + c.dim(" \xB7 ") : "";
118
+ const statusBar = c.dim(
119
+ mode + " mode" +
120
+ " \xB7 " + "? for shortcuts" +
121
+ " \xB7 " + "@file for images",
122
+ ) + imgNote;
121
123
 
122
124
  process.stdout.write(promptLine + "\n" + statusBar + "\n");
123
125
  drawn = 2;
@@ -162,7 +164,6 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
162
164
  }
163
165
  if (key.ctrl && k === "d") return finish(buf.length === 0 ? EXIT_SIGNAL : buf);
164
166
 
165
- // Alt+V or Ctrl+V: paste image from clipboard
166
167
  if ((key.meta && k === "v") || (key.ctrl && k === "v")) {
167
168
  const imgPath = grabClipboardImage();
168
169
  if (imgPath) {
package/src/repl.js CHANGED
@@ -9,7 +9,7 @@ import { c, errorLine } from "./render.js";
9
9
  import { autoUpdate } from "./update-check.js";
10
10
  import { promptBox, EXIT_SIGNAL } from "./box-input.js";
11
11
 
12
- const VERSION = "0.33.2";
12
+ const VERSION = "0.33.4";
13
13
  const MODEL_NAME = "Aether Core";
14
14
 
15
15
  const MODEL_ALIASES = {
@@ -49,7 +49,6 @@ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, unsafe
49
49
  sessionOut: 0,
50
50
  };
51
51
 
52
- // Auto-update + balance fetch in parallel — zero startup latency added.
53
52
  const updatePromise = autoUpdate().catch(() => null);
54
53
 
55
54
  let needsSetup = false;
@@ -78,15 +77,26 @@ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, unsafe
78
77
  } catch { /* tolerate */ }
79
78
  }
80
79
 
80
+ // Clear screen — clean start, no leftover shell text (matches Claude Code)
81
+ process.stdout.write("\x1b[2J\x1b[H");
82
+
81
83
  printBanner(state);
84
+
82
85
  const updateMsg = await updatePromise;
83
- if (updateMsg) console.log(updateMsg);
84
- console.log("");
86
+ let extraLines = 0;
87
+ if (updateMsg) { console.log(updateMsg); extraLines++; }
88
+
89
+ // Push prompt to bottom of screen — big empty gap like Claude Code
90
+ const rows = process.stdout.rows || 24;
91
+ const bannerLines = 5; // 1 blank + 3 logo lines + 1 blank after
92
+ const promptLines = 2; // "> " input + status bar
93
+ const filler = Math.max(0, rows - bannerLines - extraLines - promptLines - 1);
94
+ for (let i = 0; i < filler; i++) console.log("");
85
95
 
86
96
  const inputHistory = [];
87
97
 
88
98
  while (true) {
89
- const raw = await promptBox({ history: inputHistory });
99
+ const raw = await promptBox({ history: inputHistory, mode: state.autoYes ? "auto-yes" : "review" });
90
100
  if (raw === EXIT_SIGNAL) { console.log(c.gray("bye.")); return; }
91
101
  const line = (raw ?? "").trim();
92
102
  if (!line) continue;
@@ -96,7 +106,6 @@ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, unsafe
96
106
  if (line.startsWith("/") || line === "?") {
97
107
  const handled = await handleSlash(line, state);
98
108
  if (handled === "exit") return;
99
- printStatusLine(state);
100
109
  continue;
101
110
  }
102
111
 
@@ -120,8 +129,6 @@ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, unsafe
120
129
  if (!result.ok && result.error) {
121
130
  console.log("\n" + errorLine(result.error.message || String(result.error)));
122
131
  }
123
-
124
- printStatusLine(state);
125
132
  }
126
133
  }
127
134
 
@@ -156,7 +163,7 @@ async function handleSlash(line, state) {
156
163
  state.plan = me.plan || "FREE";
157
164
  console.log(
158
165
  c.cyan(`balance: ${me.balance.toLocaleString()} credits`) +
159
- c.gray(` · plan: ${me.plan || "FREE"} (${me.planCredits ?? 0} plan + ${me.topupCredits ?? 0} topup)`),
166
+ c.gray(` \xB7 plan: ${me.plan || "FREE"} (${me.planCredits ?? 0} plan + ${me.topupCredits ?? 0} topup)`),
160
167
  );
161
168
  } catch (err) {
162
169
  console.log(errorLine(err.message || String(err)));
@@ -169,7 +176,7 @@ async function handleSlash(line, state) {
169
176
  try {
170
177
  process.chdir(next);
171
178
  state.cwd = next;
172
- console.log(c.gray(`cwd → ${state.cwd}`));
179
+ console.log(c.gray(`cwd -> ${state.cwd}`));
173
180
  } catch (err) {
174
181
  console.log(errorLine(err.message));
175
182
  }
@@ -201,7 +208,7 @@ async function handleSlash(line, state) {
201
208
  state.modelName = MODEL_ALIASES[key].name;
202
209
  const premium = state.model !== null;
203
210
  console.log(
204
- c.gray(`model → ${c.cyan(state.modelName)}`) +
211
+ c.gray(`model -> ${c.cyan(state.modelName)}`) +
205
212
  (premium ? c.gray(" (premium — needs purchased credits, else falls back to Core)") : ""),
206
213
  );
207
214
  } else {
@@ -215,35 +222,30 @@ async function handleSlash(line, state) {
215
222
  return null;
216
223
  }
217
224
 
218
- /* ───────── banner + status ───────── */
225
+ /* ───────── banner ───────── */
219
226
 
220
227
  const shortenPath = (p, max) => (p.length <= max ? p : "..." + p.slice(p.length - max + 3));
221
228
 
222
229
  function printBanner(state) {
223
230
  const credits = state.balance != null ? `${state.balance.toLocaleString()} credits` : "";
224
- const mode = state.autoYes ? (state.unsafePaths ? "skip-permissions" : "auto-yes") : "review";
231
+ const plan = state.plan && state.plan !== "FREE" ? state.plan : "";
225
232
 
226
- // No boxes — plain text, exactly like Claude Code
227
- console.log("");
228
- console.log(
229
- " " + c.bold(c.cyan("✦")) + " " +
230
- c.bold("Aether Code") + " " + c.dim("v" + VERSION),
231
- );
232
- console.log(
233
- " " + state.modelName +
234
- (credits ? c.dim(" · ") + credits : "") +
235
- (state.plan && state.plan !== "FREE" ? c.dim(" · ") + c.cyan(state.plan) : ""),
236
- );
237
- console.log(" " + c.dim(shortenPath(state.cwd, 60)));
238
- }
233
+ // Block logo using ANSI background colors — renders correctly in ANY terminal.
234
+ // Each block = 2 spaces with colored background. No special chars needed.
235
+ const blk = (code) => `\x1b[${code}m \x1b[0m`;
236
+ const C = blk(46); // cyan background
237
+ const B = blk(44); // blue background
239
238
 
240
- function printStatusLine(state) {
241
- const parts = [];
242
- parts.push(c.gray(`session: ${state.sessionCredits} cr · ${state.sessionIn}→${state.sessionOut} tokens`));
243
- if (state.balance != null) parts.push(c.gray(`balance: ${state.balance.toLocaleString()}`));
244
- if (state.messages.length > 0) {
245
- parts.push(c.gray(`history: ${state.messages.length} msg${state.messages.length === 1 ? "" : "s"}`));
246
- }
247
- parts.push(c.cyan(state.autoYes ? "auto-yes" : "review"));
248
- console.log(c.dim(parts.join(" · ")));
239
+ const gap = " ";
240
+
241
+ const modelParts = [state.modelName];
242
+ if (credits) modelParts.push(credits);
243
+ if (plan) modelParts.push(c.cyan(plan));
244
+ const modelLine = modelParts.join(c.dim(" \xB7 "));
245
+
246
+ console.log("");
247
+ console.log(` ${C}${B} ${C}${gap}${c.bold("Aether Code")} ${c.dim("v" + VERSION)}`);
248
+ console.log(` ${B}${C} ${B}${gap}${modelLine}`);
249
+ console.log(` ${C}${B} ${C}${gap}${c.dim(shortenPath(state.cwd, 55))}`);
250
+ console.log("");
249
251
  }