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.
- package/bin/aether-code.js +1 -1
- package/package.json +1 -1
- package/src/box-input.js +29 -30
- package/src/repl.js +16 -16
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.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.
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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 = [];
|
|
80
|
+
const clipImages = [];
|
|
84
81
|
|
|
85
|
-
const
|
|
82
|
+
const termW = () => Math.max(30, (process.stdout.columns || 80) - 4);
|
|
86
83
|
|
|
87
84
|
const draw = (first) => {
|
|
88
|
-
const W =
|
|
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
|
|
93
|
-
const
|
|
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 +
|
|
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
|
-
|
|
101
|
-
|
|
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 <
|
|
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
|
-
|
|
112
|
-
const
|
|
113
|
-
const bot = c.cyan("╰" + "─".repeat(W + 4) + "╯");
|
|
115
|
+
// Prompt line — bare "> " like Claude Code
|
|
116
|
+
const promptLine = prefix + content;
|
|
114
117
|
|
|
115
|
-
//
|
|
116
|
-
const
|
|
117
|
-
|
|
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(
|
|
122
|
-
drawn =
|
|
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;
|
|
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,
|
|
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.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
|
|
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
|
-
//
|
|
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) {
|