aether-code 0.18.0 → 0.20.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.
- package/bin/aether-code.js +10 -9
- package/package.json +1 -1
- package/src/agent.js +11 -9
- package/src/diff.js +6 -5
- package/src/render.js +12 -5
- package/src/repl.js +1 -1
- package/src/setup.js +2 -2
- package/src/tools.js +1 -1
package/bin/aether-code.js
CHANGED
|
@@ -24,9 +24,9 @@ import {
|
|
|
24
24
|
suggestSimilar,
|
|
25
25
|
} from "../src/mcp-registry.js";
|
|
26
26
|
import readline from "node:readline";
|
|
27
|
-
import { c, errorLine, divider } from "../src/render.js";
|
|
27
|
+
import { c, errorLine, divider, setTerminalTitle } from "../src/render.js";
|
|
28
28
|
|
|
29
|
-
const VERSION = "0.
|
|
29
|
+
const VERSION = "0.20.0";
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Try to start MCP servers from ~/.aether/mcp.json. Returns a started
|
|
@@ -134,6 +134,7 @@ function die(msg, code = 1) {
|
|
|
134
134
|
|
|
135
135
|
async function main() {
|
|
136
136
|
const args = parseArgs(process.argv.slice(2));
|
|
137
|
+
setTerminalTitle("Aether");
|
|
137
138
|
|
|
138
139
|
if (args.flags.help) {
|
|
139
140
|
process.stdout.write(HELP);
|
|
@@ -213,12 +214,12 @@ async function main() {
|
|
|
213
214
|
|
|
214
215
|
console.log("\n" + divider());
|
|
215
216
|
if (result.ok) {
|
|
216
|
-
console.log(c.green(c.bold("
|
|
217
|
+
console.log(c.green(c.bold("● Done")) + c.gray(` ${result.turns} turn${result.turns === 1 ? "" : "s"} · ${result.totalCredits} credits · ${result.totalIn}→${result.totalOut} tokens`));
|
|
217
218
|
if (typeof result.balance === "number") {
|
|
218
219
|
console.log(c.gray(` balance: ${result.balance.toLocaleString()} credits`));
|
|
219
220
|
}
|
|
220
221
|
} else {
|
|
221
|
-
console.log(c.red(c.bold("
|
|
222
|
+
console.log(c.red(c.bold("× Stopped")) + c.gray(` ${result.totalCredits} credits used · ${result.totalIn}→${result.totalOut} tokens`));
|
|
222
223
|
if (result.error) console.log(errorLine(result.error.message));
|
|
223
224
|
}
|
|
224
225
|
console.log(divider());
|
|
@@ -241,14 +242,14 @@ async function handleConfig(rest) {
|
|
|
241
242
|
process.stderr.write(c.yellow("warning: keys normally start with ak_live_; saving anyway.\n"));
|
|
242
243
|
}
|
|
243
244
|
writeConfigFile({ apiKey: key });
|
|
244
|
-
console.log(`${c.green("
|
|
245
|
+
console.log(`${c.green("●")} API key saved to ${CONFIG_PATH}`);
|
|
245
246
|
return;
|
|
246
247
|
}
|
|
247
248
|
if (sub === "set-base") {
|
|
248
249
|
const url = rest[1];
|
|
249
250
|
if (!url) die("config set-base: missing URL argument.");
|
|
250
251
|
writeConfigFile({ baseUrl: url });
|
|
251
|
-
console.log(`${c.green("
|
|
252
|
+
console.log(`${c.green("●")} Base URL saved.`);
|
|
252
253
|
return;
|
|
253
254
|
}
|
|
254
255
|
if (sub === "path") {
|
|
@@ -341,7 +342,7 @@ async function handleMcp(rest) {
|
|
|
341
342
|
const cmdArgs = post.slice(1);
|
|
342
343
|
try {
|
|
343
344
|
const entry = addServer({ name, command, args: cmdArgs, env });
|
|
344
|
-
console.log(`${c.green("
|
|
345
|
+
console.log(`${c.green("●")} Added MCP server "${c.cyan(name)}".`);
|
|
345
346
|
const argsStr = entry.args && entry.args.length > 0 ? " " + entry.args.join(" ") : "";
|
|
346
347
|
console.log(c.gray(` ${entry.command}${argsStr}`));
|
|
347
348
|
console.log(c.gray("Restart the agent (or run `aether`) to attach it."));
|
|
@@ -356,7 +357,7 @@ async function handleMcp(rest) {
|
|
|
356
357
|
if (!name) die("aether mcp remove: missing <name>");
|
|
357
358
|
try {
|
|
358
359
|
removeServer({ name });
|
|
359
|
-
console.log(`${c.green("
|
|
360
|
+
console.log(`${c.green("●")} Removed MCP server "${c.cyan(name)}".`);
|
|
360
361
|
} catch (e) {
|
|
361
362
|
die(e.message || String(e));
|
|
362
363
|
}
|
|
@@ -429,7 +430,7 @@ async function handleMcp(rest) {
|
|
|
429
430
|
args: resolved.args,
|
|
430
431
|
env: resolved.env,
|
|
431
432
|
});
|
|
432
|
-
console.log(`${c.green("
|
|
433
|
+
console.log(`${c.green("●")} Installed MCP server "${c.cyan(entry.id)}".`);
|
|
433
434
|
console.log(c.gray(` ${added.command}${added.args ? " " + added.args.join(" ") : ""}`));
|
|
434
435
|
console.log(c.gray("Restart aether (or run `aether`) to attach it."));
|
|
435
436
|
} catch (e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aether-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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/agent.js
CHANGED
|
@@ -79,7 +79,8 @@ export async function runAgent({
|
|
|
79
79
|
let lastBalance = null;
|
|
80
80
|
|
|
81
81
|
for (let i = 0; i < maxTurns; i++) {
|
|
82
|
-
|
|
82
|
+
// A blank line separates steps (no noisy "turn N" headers).
|
|
83
|
+
process.stdout.write("\n");
|
|
83
84
|
|
|
84
85
|
// Stream the assistant's response. Print text deltas as they arrive,
|
|
85
86
|
// along with tool-call announcements as soon as the model commits to
|
|
@@ -106,7 +107,7 @@ export async function runAgent({
|
|
|
106
107
|
const clean = stripper.push(text);
|
|
107
108
|
if (!clean) return;
|
|
108
109
|
if (!lastWasText) {
|
|
109
|
-
process.stdout.write("
|
|
110
|
+
process.stdout.write(c.cyan("● "));
|
|
110
111
|
lastWasText = true;
|
|
111
112
|
}
|
|
112
113
|
process.stdout.write(clean);
|
|
@@ -132,7 +133,7 @@ export async function runAgent({
|
|
|
132
133
|
// Flush any held-back partial token, then close the line.
|
|
133
134
|
const tail = stripper.flush();
|
|
134
135
|
if (tail) {
|
|
135
|
-
if (!lastWasText) { process.stdout.write("
|
|
136
|
+
if (!lastWasText) { process.stdout.write(c.cyan("● ")); lastWasText = true; }
|
|
136
137
|
process.stdout.write(tail);
|
|
137
138
|
}
|
|
138
139
|
if (lastWasText) process.stdout.write("\n");
|
|
@@ -141,9 +142,8 @@ export async function runAgent({
|
|
|
141
142
|
totalOut += res.usage?.completion_tokens ?? 0;
|
|
142
143
|
if (typeof res.balanceAfter === "number") lastBalance = res.balanceAfter;
|
|
143
144
|
onTokens({ totalCredits, totalIn, totalOut, balance: lastBalance });
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
);
|
|
145
|
+
// Per-turn cost line removed for a cleaner look — the session summary at the
|
|
146
|
+
// end carries the totals.
|
|
147
147
|
|
|
148
148
|
// Push assistant message into history
|
|
149
149
|
messages.push({
|
|
@@ -162,8 +162,9 @@ export async function runAgent({
|
|
|
162
162
|
for (const call of toolCalls) {
|
|
163
163
|
let args = {};
|
|
164
164
|
try { args = JSON.parse(call.function.arguments || "{}"); } catch { /* leave empty */ }
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
// todo_write renders its own Plan box, so it returns an empty label.
|
|
166
|
+
const label = toolLabel(call.function.name, args);
|
|
167
|
+
if (label) console.log("\n" + c.cyan("●") + " " + label);
|
|
167
168
|
|
|
168
169
|
// Route to MCP if the tool name is namespaced (mcp__server__tool);
|
|
169
170
|
// otherwise execute the built-in tool. unnamespaceToolName returns
|
|
@@ -181,7 +182,8 @@ export async function runAgent({
|
|
|
181
182
|
if (call.function.name === "read_file" || call.function.name === "edit_file" || call.function.name === "write_file") {
|
|
182
183
|
if (typeof args.path === "string") referencedPaths.push(args.path);
|
|
183
184
|
}
|
|
184
|
-
|
|
185
|
+
const summary = toolSummary(call.function.name, result);
|
|
186
|
+
if (summary) console.log(summary);
|
|
185
187
|
|
|
186
188
|
messages.push({
|
|
187
189
|
role: "tool",
|
package/src/diff.js
CHANGED
|
@@ -30,11 +30,12 @@ export function unifiedDiff(oldText, newText, filename) {
|
|
|
30
30
|
for (const l of changedNew) lines.push(c.green(`+ ${l}`));
|
|
31
31
|
if (suffix > 0) lines.push(c.gray(` …${suffix} unchanged line${suffix === 1 ? "" : "s"} below…`));
|
|
32
32
|
|
|
33
|
-
// Cap output so
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
33
|
+
// Cap output so writes don't flood the terminal — a short preview is enough
|
|
34
|
+
// (skip-permissions auto-applies; the full file is on disk to inspect).
|
|
35
|
+
const MAX = 14;
|
|
36
|
+
if (lines.length > MAX) {
|
|
37
|
+
const shown = lines.slice(0, MAX - 1);
|
|
38
|
+
return [...shown, c.gray(` …${lines.length - shown.length} more lines (see the file)…`)].join("\n");
|
|
38
39
|
}
|
|
39
40
|
return lines.join("\n");
|
|
40
41
|
}
|
package/src/render.js
CHANGED
|
@@ -53,17 +53,23 @@ export function toolLabel(name, args) {
|
|
|
53
53
|
case "run_shell": return `${verb("run")} ${arg(a.command)}`;
|
|
54
54
|
case "web_search": return `${verb("search web")} ${arg(JSON.stringify(a.query ?? ""))}`;
|
|
55
55
|
case "web_fetch": return `${verb("fetch")} ${arg(a.url)}`;
|
|
56
|
-
case "todo_write": return
|
|
56
|
+
case "todo_write": return ""; // its Plan box is the label
|
|
57
57
|
default: return `${verb(name)} ${arg(JSON.stringify(a))}`;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// Set the terminal window/tab title (so cmd.exe shows "Aether", not the node
|
|
62
|
+
// path). OSC 0 — supported by cmd.exe, Windows Terminal, and POSIX terminals.
|
|
63
|
+
export function setTerminalTitle(title) {
|
|
64
|
+
if (process.stdout.isTTY) process.stdout.write(`\x1b]0;${title}\x07`);
|
|
65
|
+
}
|
|
66
|
+
|
|
61
67
|
// Terse one-line result summary instead of dumping raw JSON / file contents.
|
|
62
68
|
// Tools whose handlers already render rich output (diffs, the shell stream, the
|
|
63
69
|
// plan) just get a check — the detail was already printed.
|
|
64
70
|
export function toolSummary(name, result) {
|
|
65
71
|
const ok = result.ok;
|
|
66
|
-
const mark = ok ? c.green("
|
|
72
|
+
const mark = ok ? c.green("●") : c.red("×");
|
|
67
73
|
const out = result.output ?? "";
|
|
68
74
|
const firstLine = out.split("\n").find((l) => l.trim()) ?? "";
|
|
69
75
|
|
|
@@ -72,8 +78,9 @@ export function toolSummary(name, result) {
|
|
|
72
78
|
try { code = JSON.parse(out).exit_code; } catch { /* ignore */ }
|
|
73
79
|
return ` ${mark} ${c.gray(code === null ? (ok ? "done" : "failed") : `exit ${code}`)}`;
|
|
74
80
|
}
|
|
75
|
-
if (name === "
|
|
76
|
-
|
|
81
|
+
if (name === "todo_write") return ""; // the Plan box is its own feedback
|
|
82
|
+
if (name === "write_file" || name === "edit_file") {
|
|
83
|
+
// Handler already printed the diff; echo its short status line.
|
|
77
84
|
return ` ${mark} ${c.gray(ellip(firstLine, 100))}`;
|
|
78
85
|
}
|
|
79
86
|
let summary = "";
|
|
@@ -129,7 +136,7 @@ export function makeTokenStripper() {
|
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
export function toolResult(text, ok = true) {
|
|
132
|
-
const prefix = ok ? c.green("
|
|
139
|
+
const prefix = ok ? c.green(" ● ") : c.red(" × ");
|
|
133
140
|
// First line bold-ish, then dim continuation
|
|
134
141
|
const lines = text.split("\n");
|
|
135
142
|
const head = lines[0].slice(0, 200);
|
package/src/repl.js
CHANGED
|
@@ -17,7 +17,7 @@ import { c, errorLine } from "./render.js";
|
|
|
17
17
|
import { checkForUpdate } from "./update-check.js";
|
|
18
18
|
import { promptBoxed, EXIT_SIGNAL } from "./ink-input.js";
|
|
19
19
|
|
|
20
|
-
const VERSION = "0.
|
|
20
|
+
const VERSION = "0.20.0";
|
|
21
21
|
const MODEL_NAME = "Aether Core";
|
|
22
22
|
|
|
23
23
|
const SHORTCUTS = `
|
package/src/setup.js
CHANGED
|
@@ -112,7 +112,7 @@ export async function runSetup() {
|
|
|
112
112
|
process.stdout.write(c.gray("Verifying..."));
|
|
113
113
|
try {
|
|
114
114
|
const me = await fetchBalance();
|
|
115
|
-
console.log(c.green("
|
|
115
|
+
console.log(c.green(" ●"));
|
|
116
116
|
console.log("");
|
|
117
117
|
console.log(c.green(c.bold("Setup complete.")));
|
|
118
118
|
console.log(
|
|
@@ -123,7 +123,7 @@ export async function runSetup() {
|
|
|
123
123
|
saved = true;
|
|
124
124
|
break;
|
|
125
125
|
} catch (err) {
|
|
126
|
-
console.log(c.red("
|
|
126
|
+
console.log(c.red(" ×"));
|
|
127
127
|
if (err instanceof AetherError && err.status === 401) {
|
|
128
128
|
console.log(errorLine("Server rejected that key (401). Double-check you copied it correctly."));
|
|
129
129
|
} else {
|