aether-code 0.17.0 → 0.18.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.
@@ -26,7 +26,7 @@ import {
26
26
  import readline from "node:readline";
27
27
  import { c, errorLine, divider } from "../src/render.js";
28
28
 
29
- const VERSION = "0.17.0";
29
+ const VERSION = "0.18.0";
30
30
 
31
31
  /**
32
32
  * Try to start MCP servers from ~/.aether/mcp.json. Returns a started
@@ -84,10 +84,11 @@ ${c.bold("EXAMPLES")}
84
84
  aether --cwd ./my-project "fix the failing tests"
85
85
 
86
86
  ${c.bold("FLAGS")}
87
- --yes Auto-approve all writes and shell commands. Use with care.
87
+ --review Confirm (y/N) before each write + shell command.
88
+ Default is auto-approve (skip-permissions).
89
+ --sandbox Restrict file access to --cwd. Default is full access.
88
90
  --cwd <path> Working directory for the agent (default: current dir).
89
91
  --max-turns <n> Maximum turns before stopping (default: 25).
90
- --unsafe-paths Allow the agent to read/write outside cwd.
91
92
  --help, -h Show this help.
92
93
  --version, -v Print version.
93
94
 
@@ -96,9 +97,10 @@ ${c.bold("CONFIG")}
96
97
  Get a key at ${c.blue("https://trynoguard.com/account")}.
97
98
 
98
99
  ${c.bold("SAFETY")}
99
- - File writes show a unified diff and require y/N confirmation by default.
100
- - Shell commands show what's about to run and require y/N confirmation.
101
- - Paths are clamped to ${c.cyan("--cwd")} (override with ${c.cyan("--unsafe-paths")}).
100
+ - By DEFAULT aether runs in skip-permissions mode (like Claude's
101
+ --dangerously-skip-permissions): it auto-approves file writes + shell
102
+ commands and can access your whole filesystem, so it just builds.
103
+ - Use ${c.cyan("--review")} to approve each action, ${c.cyan("--sandbox")} to clamp paths to --cwd.
102
104
  - Each shell command has a 2-minute hard timeout.
103
105
 
104
106
  ${c.gray(`v${VERSION}`)}
@@ -143,8 +145,12 @@ async function main() {
143
145
  }
144
146
 
145
147
  const cwd = args.flags.cwd ? path.resolve(args.flags.cwd) : process.cwd();
146
- const autoYes = !!args.flags.yes;
147
- const unsafePaths = !!args.flags.unsafePaths;
148
+ // Skip-permissions by DEFAULT (like `claude --dangerously-skip-permissions`):
149
+ // auto-approve writes/shell and don't sandbox file paths, so the agent can
150
+ // just build. Opt back in with --review (y/N before each action) and
151
+ // --sandbox (clamp file access to --cwd).
152
+ const autoYes = !args.flags.review;
153
+ const unsafePaths = !args.flags.sandbox;
148
154
  const maxTurns = Number.isInteger(args.flags.maxTurns) ? args.flags.maxTurns : 25;
149
155
 
150
156
  // Subcommand routing — these shadow the "task as positional arg" mode
@@ -177,7 +183,7 @@ async function main() {
177
183
  if (!prompt) {
178
184
  if (cwd !== process.cwd()) process.chdir(cwd);
179
185
  const mcpManager = await bootMcp();
180
- await runRepl({ cwd, autoYes, maxTurns, mcpManager });
186
+ await runRepl({ cwd, autoYes, unsafePaths, maxTurns, mcpManager });
181
187
  return;
182
188
  }
183
189
 
@@ -189,7 +195,8 @@ async function main() {
189
195
  }
190
196
 
191
197
  console.log(divider());
192
- console.log(c.magenta(c.bold("aether-code")) + c.gray(` · cwd ${cwd}${autoYes ? " · auto-yes" : ""}${unsafePaths ? " · unsafe-paths" : ""}`));
198
+ const modeLabel = autoYes && unsafePaths ? " · skip-permissions" : `${autoYes ? " · auto-yes" : " · review"}${unsafePaths ? "" : " · sandboxed"}`;
199
+ console.log(c.magenta(c.bold("aether-code")) + c.gray(` · cwd ${cwd}${modeLabel}`));
193
200
  console.log(c.gray(`task: `) + prompt);
194
201
  console.log(divider());
195
202
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aether-code",
3
- "version": "0.17.0",
3
+ "version": "0.18.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
@@ -2,6 +2,8 @@
2
2
  // in real-time, executes any tool calls, loops until the model returns no
3
3
  // tool calls (task done) or max-turns is reached.
4
4
 
5
+ import os from "node:os";
6
+ import path from "node:path";
5
7
  import { agentTurnStream, AetherError } from "./api.js";
6
8
  import { TOOL_DEFINITIONS, executeTool } from "./tools.js";
7
9
  import { unnamespaceToolName } from "./mcp.js";
@@ -10,6 +12,26 @@ import { c, divider, turn, toolLabel, toolSummary, makeTokenStripper, errorLine
10
12
 
11
13
  const DEFAULT_MAX_TURNS = 25;
12
14
 
15
+ // Environment block prepended to the first user message so the model can
16
+ // resolve named locations to real absolute paths.
17
+ function envContext(cwd) {
18
+ const home = os.homedir();
19
+ const desktop = path.join(home, "Desktop");
20
+ const documents = path.join(home, "Documents");
21
+ return (
22
+ `[environment]\n` +
23
+ `os: ${process.platform}\n` +
24
+ `cwd: ${cwd}\n` +
25
+ `home: ${home}\n` +
26
+ `desktop: ${desktop}\n` +
27
+ `documents: ${documents}\n` +
28
+ `When the user names a location ("my desktop", "home", "documents"), write to ` +
29
+ `the matching ABSOLUTE path above (e.g. desktop -> ${desktop}). Otherwise ` +
30
+ `work under the cwd. Use absolute paths when a specific location is named.\n` +
31
+ `[/environment]\n\n`
32
+ );
33
+ }
34
+
13
35
  export async function runAgent({
14
36
  initialPrompt,
15
37
  priorMessages,
@@ -43,9 +65,14 @@ export async function runAgent({
43
65
  const referencedPaths = [];
44
66
  // Two callers: one-shot (initialPrompt only, fresh conversation) and REPL
45
67
  // (priorMessages + initialPrompt to continue an ongoing chat).
68
+ // On the FIRST message of a session, prepend an environment block so the
69
+ // model knows real absolute paths (cwd / home / desktop). Without it, "build
70
+ // X on my desktop" became `mkdir X` in whatever dir aether was launched from
71
+ // (e.g. C:\WINDOWS\system32). Only prepended once — later turns carry it in
72
+ // history.
46
73
  const messages = priorMessages
47
74
  ? [...priorMessages, { role: "user", content: initialPrompt }]
48
- : [{ role: "user", content: initialPrompt }];
75
+ : [{ role: "user", content: envContext(cwd) + initialPrompt }];
49
76
  let totalCredits = 0;
50
77
  let totalIn = 0;
51
78
  let totalOut = 0;
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.17.0";
20
+ const VERSION = "0.18.0";
21
21
  const MODEL_NAME = "Aether Core";
22
22
 
23
23
  const SHORTCUTS = `
@@ -34,10 +34,11 @@ ${c.gray("Anything else is sent to the agent as your next message.")}
34
34
  ${c.gray("Conversation history is kept across messages until you /clear.")}
35
35
  `;
36
36
 
37
- export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, maxTurns: initialMaxTurns, mcpManager = null }) {
37
+ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, unsafePaths: initialUnsafePaths, maxTurns: initialMaxTurns, mcpManager = null }) {
38
38
  const state = {
39
39
  cwd: initialCwd,
40
40
  autoYes: !!initialAutoYes,
41
+ unsafePaths: !!initialUnsafePaths,
41
42
  maxTurns: initialMaxTurns ?? 25,
42
43
  messages: [], // accumulates across turns
43
44
  balance: null,
@@ -170,6 +171,7 @@ export async function runRepl({ cwd: initialCwd, autoYes: initialAutoYes, maxTur
170
171
  priorMessages: state.messages.length > 0 ? state.messages : undefined,
171
172
  cwd: state.cwd,
172
173
  autoYes: state.autoYes,
174
+ unsafePaths: state.unsafePaths,
173
175
  maxTurns: state.maxTurns,
174
176
  mcpManager,
175
177
  });
@@ -275,7 +277,7 @@ function printBanner(state) {
275
277
  // Brand-coloured rules top + bottom; content is indented with no right
276
278
  // border, so nothing can misalign regardless of terminal font/width.
277
279
  const rule = c.magenta("─".repeat(W));
278
- const mode = state.autoYes ? "auto-yes" : "review mode";
280
+ const mode = state.autoYes ? (state.unsafePaths ? "skip-permissions" : "auto-yes") : "review mode";
279
281
 
280
282
  console.log("");
281
283
  console.log(rule);
package/src/tools.js CHANGED
@@ -789,8 +789,8 @@ function renderTodos(todos) {
789
789
  t.status === "completed"
790
790
  ? c.green("✓")
791
791
  : t.status === "in_progress"
792
- ? c.yellow("")
793
- : c.dim("");
792
+ ? c.yellow("")
793
+ : c.dim("·");
794
794
  const text =
795
795
  t.status === "completed"
796
796
  ? c.dim(t.content)