aether-code 0.33.1 → 0.33.2

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.1";
31
+ const VERSION = "0.33.2";
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.1",
3
+ "version": "0.33.2",
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,7 +1,8 @@
1
- // Bordered single-line text input — raw-mode, cmd.exe-safe.
1
+ // Minimal prompt input — raw-mode, cmd.exe-safe.
2
+ // No box/border — just a bare "> " prompt like Claude Code.
2
3
  //
3
4
  // Features:
4
- // - Ctrl+V / Alt+V: paste image from clipboard (saves to temp, attaches)
5
+ // - Ctrl+V / Alt+V: paste image from clipboard
5
6
  // - @file.png: reference images by path (parsed by agent.js)
6
7
  // - Arrow keys, Home/End, Ctrl+A/E, Ctrl+U, history navigation
7
8
  // - Horizontal scroll for long input
@@ -23,7 +24,6 @@ const SHOW = "\x1b[?25h";
23
24
  const INV = (s) => `\x1b[7m${s}\x1b[27m`;
24
25
 
25
26
  // Save clipboard image to a temp file. Returns the file path or null.
26
- // Platform-specific: PowerShell on Windows, pngpaste on macOS, xclip on Linux.
27
27
  function grabClipboardImage() {
28
28
  const tmpFile = path.join(os.tmpdir(), `aether-paste-${Date.now()}.png`);
29
29
 
@@ -42,9 +42,8 @@ function grabClipboardImage() {
42
42
  try {
43
43
  execFileSync("pngpaste", [tmpFile], { timeout: 3000, stdio: "pipe" });
44
44
  if (fs.existsSync(tmpFile) && fs.statSync(tmpFile).size > 0) return tmpFile;
45
- } catch { /* pngpaste not installed — hint below */ }
45
+ } catch { /* pngpaste not installed */ }
46
46
  } else {
47
- // Linux: xclip
48
47
  try {
49
48
  const fd = fs.openSync(tmpFile, "w");
50
49
  try {
@@ -59,14 +58,12 @@ function grabClipboardImage() {
59
58
  }
60
59
  } catch { /* any failure — fall through */ }
61
60
 
62
- // Clean up empty/failed temp file
63
61
  try { if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile); } catch { /* ignore */ }
64
62
  return null;
65
63
  }
66
64
 
67
65
  export function promptBox({ history = [], placeholder = "Message Aether…" } = {}) {
68
66
  return new Promise((resolve) => {
69
- // Non-interactive (piped / tests): plain readline, no box.
70
67
  if (!process.stdin.isTTY) {
71
68
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
72
69
  rl.question(c.cyan("> "), (a) => { rl.close(); resolve(a); });
@@ -80,46 +77,50 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
80
77
  let draft = "";
81
78
  let drawn = 0;
82
79
  let lastSigint = 0;
83
- const clipImages = []; // temp file paths from clipboard pastes
80
+ const clipImages = [];
84
81
 
85
- const innerW = () => Math.max(24, Math.min(process.stdout.columns || 80, 100) - 6);
82
+ const termW = () => Math.max(30, (process.stdout.columns || 80) - 4);
86
83
 
87
84
  const draw = (first) => {
88
- const W = innerW();
85
+ const W = termW();
89
86
  if (!first) process.stdout.write(`\x1b[${drawn}A`);
90
87
  process.stdout.write("\x1b[J");
91
88
 
92
- // Horizontal scroll: keep the caret within the visible window.
93
- const maxVisible = W - 1;
89
+ // Horizontal scroll
90
+ const prefix = c.cyan("> ");
91
+ const prefixLen = 2; // "> " visible chars
92
+ const maxVisible = W - prefixLen;
94
93
  const start = pos > maxVisible ? pos - maxVisible : 0;
95
- const view = buf.slice(start, start + W);
94
+ const view = buf.slice(start, start + maxVisible);
96
95
  const rel = pos - start;
97
96
 
98
97
  let content;
99
- if (buf.length === 0) {
100
- const ph = placeholder.slice(0, W - 1);
101
- content = INV(" ") + c.dim(ph) + " ".repeat(Math.max(0, W - 1 - ph.length));
98
+ if (buf.length === 0 && clipImages.length === 0) {
99
+ content = INV(" ") + c.dim(placeholder.slice(0, maxVisible - 1));
100
+ } else if (buf.length === 0 && clipImages.length > 0) {
101
+ const tag = `[${clipImages.length} image${clipImages.length > 1 ? "s" : ""} pasted]`;
102
+ content = INV(" ") + c.cyan(tag);
102
103
  } else {
103
104
  let s = "";
104
- for (let i = 0; i < W; i++) {
105
+ for (let i = 0; i < maxVisible; i++) {
105
106
  const ch = view[i] ?? " ";
106
107
  s += i === rel ? INV(ch) : ch;
107
108
  }
109
+ if (clipImages.length > 0) {
110
+ s += " " + c.cyan(`[+${clipImages.length} img]`);
111
+ }
108
112
  content = s;
109
113
  }
110
114
 
111
- const top = c.cyan("╭" + "─".repeat(W + 4) + "╮");
112
- const mid = c.cyan("│") + " " + c.cyan(">") + " " + content + " " + c.cyan("│");
113
- const bot = c.cyan("╰" + "─".repeat(W + 4) + "╯");
115
+ // Prompt line — bare "> " like Claude Code
116
+ const promptLine = prefix + content;
114
117
 
115
- // Hint line — show paste indicator when images are queued
116
- const imgTag = clipImages.length > 0
117
- ? " " + c.cyan(`[${clipImages.length} image${clipImages.length > 1 ? "s" : ""} pasted]`)
118
- : "";
119
- const hint = c.dim(" Enter send · Alt+V paste image · @file · /help") + imgTag;
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;
120
121
 
121
- process.stdout.write(top + "\n" + mid + "\n" + bot + "\n" + hint + "\n");
122
- drawn = 4;
122
+ process.stdout.write(promptLine + "\n" + statusBar + "\n");
123
+ drawn = 2;
123
124
  };
124
125
 
125
126
  const finish = (result) => {
@@ -128,7 +129,6 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
128
129
  process.stdin.pause();
129
130
  process.stdout.write(`\x1b[${drawn}A\x1b[J` + SHOW);
130
131
 
131
- // Append clipboard image temp paths so agent.js's @-parser picks them up.
132
132
  let finalResult = result;
133
133
  if (result !== null && result !== EXIT_SIGNAL && clipImages.length > 0) {
134
134
  const userText = (result || "").trim();
@@ -136,7 +136,6 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
136
136
  finalResult = userText ? `${userText} ${imgRefs}` : `What is in this image? ${imgRefs}`;
137
137
  }
138
138
 
139
- // Echo the submitted line so it persists in scrollback.
140
139
  if (finalResult && finalResult !== EXIT_SIGNAL) {
141
140
  const echoText = (result || "").trim();
142
141
  const suffix = clipImages.length > 0
@@ -195,7 +194,7 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
195
194
  buf = buf.slice(0, pos) + str + buf.slice(pos);
196
195
  pos += str.length;
197
196
  }
198
- else return; // ignore unhandled keys without a redraw
197
+ else return;
199
198
  draw(false);
200
199
  };
201
200
 
package/src/repl.js CHANGED
@@ -5,11 +5,11 @@ import path from "node:path";
5
5
  import { runAgent } from "./agent.js";
6
6
  import { fetchBalance, AetherError } from "./api.js";
7
7
  import { runSetup } from "./setup.js";
8
- import { c, boxLines, errorLine } from "./render.js";
8
+ 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.1";
12
+ const VERSION = "0.33.2";
13
13
  const MODEL_NAME = "Aether Core";
14
14
 
15
15
  const MODEL_ALIASES = {
@@ -80,7 +80,8 @@ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, unsafe
80
80
 
81
81
  printBanner(state);
82
82
  const updateMsg = await updatePromise;
83
- if (updateMsg) console.log(updateMsg + "\n");
83
+ if (updateMsg) console.log(updateMsg);
84
+ console.log("");
84
85
 
85
86
  const inputHistory = [];
86
87
 
@@ -220,21 +221,20 @@ const shortenPath = (p, max) => (p.length <= max ? p : "..." + p.slice(p.length
220
221
 
221
222
  function printBanner(state) {
222
223
  const credits = state.balance != null ? `${state.balance.toLocaleString()} credits` : "";
224
+ const mode = state.autoYes ? (state.unsafePaths ? "skip-permissions" : "auto-yes") : "review";
223
225
 
224
- // Single clean banner box — Claude Code style
225
- const lines = [
226
- c.bold(c.cyan("✦ Aether Code")) + c.gray(" v" + VERSION),
227
- "",
228
- c.gray(" " + state.modelName + (credits ? " · " + credits : "")),
229
- c.gray(" cwd: " + shortenPath(state.cwd, 52)),
230
- "",
231
- c.dim(" Ctrl+V paste · @file image · /help"),
232
- ];
233
-
234
- const banner = boxLines(lines, { color: c.cyan, padX: 1, minInner: 50 });
235
- console.log("");
236
- console.log(banner.join("\n"));
226
+ // No boxes — plain text, exactly like Claude Code
237
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
238
  }
239
239
 
240
240
  function printStatusLine(state) {