aether-code 0.33.1 → 0.33.3
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/bin/aether-code.js +1 -1
- package/package.json +1 -1
- package/src/box-input.js +33 -33
- package/src/repl.js +32 -35
package/bin/aether-code.js
CHANGED
|
@@ -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.
|
|
31
|
+
const VERSION = "0.33.3";
|
|
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.
|
|
3
|
+
"version": "0.33.3",
|
|
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
|
-
//
|
|
1
|
+
// Minimal prompt input — raw-mode, cmd.exe-safe.
|
|
2
|
+
// Bare "> " prompt with bottom status bar, matching Claude Code's layout.
|
|
2
3
|
//
|
|
3
4
|
// Features:
|
|
4
|
-
// - Ctrl+V / Alt+V: paste image from clipboard
|
|
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
|
|
@@ -22,8 +23,6 @@ const HIDE = "\x1b[?25l";
|
|
|
22
23
|
const SHOW = "\x1b[?25h";
|
|
23
24
|
const INV = (s) => `\x1b[7m${s}\x1b[27m`;
|
|
24
25
|
|
|
25
|
-
// 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
26
|
function grabClipboardImage() {
|
|
28
27
|
const tmpFile = path.join(os.tmpdir(), `aether-paste-${Date.now()}.png`);
|
|
29
28
|
|
|
@@ -42,9 +41,8 @@ function grabClipboardImage() {
|
|
|
42
41
|
try {
|
|
43
42
|
execFileSync("pngpaste", [tmpFile], { timeout: 3000, stdio: "pipe" });
|
|
44
43
|
if (fs.existsSync(tmpFile) && fs.statSync(tmpFile).size > 0) return tmpFile;
|
|
45
|
-
} catch { /* pngpaste not installed
|
|
44
|
+
} catch { /* pngpaste not installed */ }
|
|
46
45
|
} else {
|
|
47
|
-
// Linux: xclip
|
|
48
46
|
try {
|
|
49
47
|
const fd = fs.openSync(tmpFile, "w");
|
|
50
48
|
try {
|
|
@@ -59,14 +57,12 @@ function grabClipboardImage() {
|
|
|
59
57
|
}
|
|
60
58
|
} catch { /* any failure — fall through */ }
|
|
61
59
|
|
|
62
|
-
// Clean up empty/failed temp file
|
|
63
60
|
try { if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile); } catch { /* ignore */ }
|
|
64
61
|
return null;
|
|
65
62
|
}
|
|
66
63
|
|
|
67
|
-
export function promptBox({ history = [], placeholder = "Message Aether
|
|
64
|
+
export function promptBox({ history = [], placeholder = "Message Aether...", mode = "review" } = {}) {
|
|
68
65
|
return new Promise((resolve) => {
|
|
69
|
-
// Non-interactive (piped / tests): plain readline, no box.
|
|
70
66
|
if (!process.stdin.isTTY) {
|
|
71
67
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
72
68
|
rl.question(c.cyan("> "), (a) => { rl.close(); resolve(a); });
|
|
@@ -80,46 +76,53 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
|
|
|
80
76
|
let draft = "";
|
|
81
77
|
let drawn = 0;
|
|
82
78
|
let lastSigint = 0;
|
|
83
|
-
const clipImages = [];
|
|
79
|
+
const clipImages = [];
|
|
84
80
|
|
|
85
|
-
const
|
|
81
|
+
const termW = () => Math.max(30, (process.stdout.columns || 80) - 2);
|
|
86
82
|
|
|
87
83
|
const draw = (first) => {
|
|
88
|
-
const W =
|
|
84
|
+
const W = termW();
|
|
89
85
|
if (!first) process.stdout.write(`\x1b[${drawn}A`);
|
|
90
86
|
process.stdout.write("\x1b[J");
|
|
91
87
|
|
|
92
|
-
|
|
93
|
-
const
|
|
88
|
+
const prefixStr = "> ";
|
|
89
|
+
const prefixLen = 2;
|
|
90
|
+
const maxVisible = W - prefixLen;
|
|
94
91
|
const start = pos > maxVisible ? pos - maxVisible : 0;
|
|
95
|
-
const view = buf.slice(start, start +
|
|
92
|
+
const view = buf.slice(start, start + maxVisible);
|
|
96
93
|
const rel = pos - start;
|
|
97
94
|
|
|
98
95
|
let content;
|
|
99
|
-
if (buf.length === 0) {
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
if (buf.length === 0 && clipImages.length === 0) {
|
|
97
|
+
content = INV(" ") + c.dim(placeholder.slice(0, maxVisible - 1));
|
|
98
|
+
} else if (buf.length === 0 && clipImages.length > 0) {
|
|
99
|
+
const tag = `[${clipImages.length} image${clipImages.length > 1 ? "s" : ""} pasted]`;
|
|
100
|
+
content = INV(" ") + c.cyan(tag);
|
|
102
101
|
} else {
|
|
103
102
|
let s = "";
|
|
104
|
-
for (let i = 0; i <
|
|
103
|
+
for (let i = 0; i < maxVisible; i++) {
|
|
105
104
|
const ch = view[i] ?? " ";
|
|
106
105
|
s += i === rel ? INV(ch) : ch;
|
|
107
106
|
}
|
|
107
|
+
if (clipImages.length > 0) {
|
|
108
|
+
s += " " + c.cyan(`[+${clipImages.length} img]`);
|
|
109
|
+
}
|
|
108
110
|
content = s;
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
const bot = c.cyan("╰" + "─".repeat(W + 4) + "╯");
|
|
113
|
+
// Bare prompt — just "> " like Claude Code
|
|
114
|
+
const promptLine = c.dim(prefixStr) + content;
|
|
114
115
|
|
|
115
|
-
//
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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;
|
|
120
123
|
|
|
121
|
-
process.stdout.write(
|
|
122
|
-
drawn =
|
|
124
|
+
process.stdout.write(promptLine + "\n" + statusBar + "\n");
|
|
125
|
+
drawn = 2;
|
|
123
126
|
};
|
|
124
127
|
|
|
125
128
|
const finish = (result) => {
|
|
@@ -128,7 +131,6 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
|
|
|
128
131
|
process.stdin.pause();
|
|
129
132
|
process.stdout.write(`\x1b[${drawn}A\x1b[J` + SHOW);
|
|
130
133
|
|
|
131
|
-
// Append clipboard image temp paths so agent.js's @-parser picks them up.
|
|
132
134
|
let finalResult = result;
|
|
133
135
|
if (result !== null && result !== EXIT_SIGNAL && clipImages.length > 0) {
|
|
134
136
|
const userText = (result || "").trim();
|
|
@@ -136,7 +138,6 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
|
|
|
136
138
|
finalResult = userText ? `${userText} ${imgRefs}` : `What is in this image? ${imgRefs}`;
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
// Echo the submitted line so it persists in scrollback.
|
|
140
141
|
if (finalResult && finalResult !== EXIT_SIGNAL) {
|
|
141
142
|
const echoText = (result || "").trim();
|
|
142
143
|
const suffix = clipImages.length > 0
|
|
@@ -163,7 +164,6 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
|
|
|
163
164
|
}
|
|
164
165
|
if (key.ctrl && k === "d") return finish(buf.length === 0 ? EXIT_SIGNAL : buf);
|
|
165
166
|
|
|
166
|
-
// Alt+V or Ctrl+V: paste image from clipboard
|
|
167
167
|
if ((key.meta && k === "v") || (key.ctrl && k === "v")) {
|
|
168
168
|
const imgPath = grabClipboardImage();
|
|
169
169
|
if (imgPath) {
|
|
@@ -195,7 +195,7 @@ export function promptBox({ history = [], placeholder = "Message Aether…" } =
|
|
|
195
195
|
buf = buf.slice(0, pos) + str + buf.slice(pos);
|
|
196
196
|
pos += str.length;
|
|
197
197
|
}
|
|
198
|
-
else return;
|
|
198
|
+
else return;
|
|
199
199
|
draw(false);
|
|
200
200
|
};
|
|
201
201
|
|
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,
|
|
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.
|
|
12
|
+
const VERSION = "0.33.3";
|
|
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;
|
|
@@ -80,12 +79,13 @@ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, unsafe
|
|
|
80
79
|
|
|
81
80
|
printBanner(state);
|
|
82
81
|
const updateMsg = await updatePromise;
|
|
83
|
-
if (updateMsg) console.log(updateMsg
|
|
82
|
+
if (updateMsg) console.log(updateMsg);
|
|
83
|
+
console.log("");
|
|
84
84
|
|
|
85
85
|
const inputHistory = [];
|
|
86
86
|
|
|
87
87
|
while (true) {
|
|
88
|
-
const raw = await promptBox({ history: inputHistory });
|
|
88
|
+
const raw = await promptBox({ history: inputHistory, mode: state.autoYes ? "auto-yes" : "review" });
|
|
89
89
|
if (raw === EXIT_SIGNAL) { console.log(c.gray("bye.")); return; }
|
|
90
90
|
const line = (raw ?? "").trim();
|
|
91
91
|
if (!line) continue;
|
|
@@ -95,7 +95,6 @@ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, unsafe
|
|
|
95
95
|
if (line.startsWith("/") || line === "?") {
|
|
96
96
|
const handled = await handleSlash(line, state);
|
|
97
97
|
if (handled === "exit") return;
|
|
98
|
-
printStatusLine(state);
|
|
99
98
|
continue;
|
|
100
99
|
}
|
|
101
100
|
|
|
@@ -119,8 +118,6 @@ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, unsafe
|
|
|
119
118
|
if (!result.ok && result.error) {
|
|
120
119
|
console.log("\n" + errorLine(result.error.message || String(result.error)));
|
|
121
120
|
}
|
|
122
|
-
|
|
123
|
-
printStatusLine(state);
|
|
124
121
|
}
|
|
125
122
|
}
|
|
126
123
|
|
|
@@ -168,7 +165,7 @@ async function handleSlash(line, state) {
|
|
|
168
165
|
try {
|
|
169
166
|
process.chdir(next);
|
|
170
167
|
state.cwd = next;
|
|
171
|
-
console.log(c.gray(`cwd
|
|
168
|
+
console.log(c.gray(`cwd -> ${state.cwd}`));
|
|
172
169
|
} catch (err) {
|
|
173
170
|
console.log(errorLine(err.message));
|
|
174
171
|
}
|
|
@@ -200,7 +197,7 @@ async function handleSlash(line, state) {
|
|
|
200
197
|
state.modelName = MODEL_ALIASES[key].name;
|
|
201
198
|
const premium = state.model !== null;
|
|
202
199
|
console.log(
|
|
203
|
-
c.gray(`model
|
|
200
|
+
c.gray(`model -> ${c.cyan(state.modelName)}`) +
|
|
204
201
|
(premium ? c.gray(" (premium — needs purchased credits, else falls back to Core)") : ""),
|
|
205
202
|
);
|
|
206
203
|
} else {
|
|
@@ -214,36 +211,36 @@ async function handleSlash(line, state) {
|
|
|
214
211
|
return null;
|
|
215
212
|
}
|
|
216
213
|
|
|
217
|
-
/* ───────── banner
|
|
214
|
+
/* ───────── banner ───────── */
|
|
218
215
|
|
|
219
216
|
const shortenPath = (p, max) => (p.length <= max ? p : "..." + p.slice(p.length - max + 3));
|
|
220
217
|
|
|
221
218
|
function printBanner(state) {
|
|
222
219
|
const credits = state.balance != null ? `${state.balance.toLocaleString()} credits` : "";
|
|
220
|
+
const plan = state.plan && state.plan !== "FREE" ? state.plan : "";
|
|
223
221
|
|
|
224
|
-
//
|
|
225
|
-
const
|
|
226
|
-
|
|
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"));
|
|
237
|
-
console.log("");
|
|
238
|
-
}
|
|
222
|
+
// Block logo — CP437-safe colored squares, like Claude Code's logo
|
|
223
|
+
const b1 = c.cyan("\xDB\xDB"); // ██ full blocks
|
|
224
|
+
const b2 = c.blue("\xDB\xDB"); // ██ darker accent
|
|
225
|
+
const gap = " ";
|
|
239
226
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
227
|
+
// 3-line logo + text, no boxes — matches Claude Code layout
|
|
228
|
+
console.log("");
|
|
229
|
+
console.log(
|
|
230
|
+
" " + b1 + " " + b2 + gap +
|
|
231
|
+
c.bold("Aether Code") + " " + c.dim("v" + VERSION),
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const modelParts = [state.modelName];
|
|
235
|
+
if (credits) modelParts.push(credits);
|
|
236
|
+
if (plan) modelParts.push(c.cyan(plan));
|
|
237
|
+
console.log(
|
|
238
|
+
" " + b2 + " " + b1 + gap +
|
|
239
|
+
modelParts.join(c.dim(" \xB7 ")),
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
console.log(
|
|
243
|
+
" " + b1 + " " + b2 + gap +
|
|
244
|
+
c.dim(shortenPath(state.cwd, 60)),
|
|
245
|
+
);
|
|
249
246
|
}
|