aether-code 0.9.0 → 0.10.1
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/README.md +205 -140
- package/bin/aether-code.js +96 -1
- package/package.json +32 -7
- package/src/mcp-cli.js +94 -0
package/README.md
CHANGED
|
@@ -1,140 +1,205 @@
|
|
|
1
|
-
# aether-code
|
|
2
|
-
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
|
53
|
-
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
1
|
+
# aether-code
|
|
2
|
+
|
|
3
|
+
> The uncensored Claude Code alternative for your terminal. Reads your codebase, writes code, runs commands, and — via MCP — drives IDA Pro, Roblox Studio, Wireshark, Blender, Postgres, and anything else with an MCP server. No refusal layer.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i -g aether-code
|
|
7
|
+
aether # interactive REPL
|
|
8
|
+
aether "deobfuscate this binary and find the license check"
|
|
9
|
+
aether "build me a Roblox Lua mod that bypasses the anti-cheat check"
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
[](https://www.npmjs.com/package/aether-code)
|
|
13
|
+
[](LICENSE)
|
|
14
|
+
[](https://nodejs.org)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why aether-code
|
|
19
|
+
|
|
20
|
+
Claude Code and Cursor are excellent — but they refuse a lot:
|
|
21
|
+
|
|
22
|
+
- "Write me a stealer that bypasses Defender" → refused
|
|
23
|
+
- "Reverse engineer Denuvo and document the bypass" → refused
|
|
24
|
+
- "Build me a Roblox script that simulates clicks" → refused
|
|
25
|
+
- "Generate this adult interactive fiction project" → refused
|
|
26
|
+
- "Help me cheat at this multiplayer game" → refused
|
|
27
|
+
|
|
28
|
+
`aether-code` is the same agent loop architecture (read files → call tools → run commands → iterate) with **no refusal layer** and **no moralizing**. If your task is legal, the agent will do it. The whole product is built for the work other AI coding tools politely decline.
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g aether-code # global install
|
|
34
|
+
aether # launch interactive REPL
|
|
35
|
+
aether "your task" # one-shot
|
|
36
|
+
|
|
37
|
+
# Or run without installing
|
|
38
|
+
npx aether-code "your task"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Requires Node 18+. Get an API key at [trynoguard.com/account](https://trynoguard.com/account).
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
aether login # interactive setup (opens browser)
|
|
45
|
+
aether balance # check credits
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## What it can do
|
|
49
|
+
|
|
50
|
+
### Built-in tools
|
|
51
|
+
|
|
52
|
+
| Tool | What it does |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `read_file` | Read any file as UTF-8 text |
|
|
55
|
+
| `list_dir` | List entries in a directory |
|
|
56
|
+
| `search_files` | Recursive regex search across the codebase |
|
|
57
|
+
| `write_file` | Create or overwrite a file (shows diff, y/N prompt) |
|
|
58
|
+
| `edit_file` | Replace exactly one occurrence (shows diff, y/N prompt) |
|
|
59
|
+
| `run_shell` | Run shell commands with stdout/stderr capture (y/N prompt) |
|
|
60
|
+
| **`web_search`** | Live web search — current docs, recent libraries, real APIs |
|
|
61
|
+
| **`web_fetch`** | Fetch + read a URL as text (HTML stripped, entities decoded) |
|
|
62
|
+
| **`todo_write`** | Plan multi-step tasks and track progress visibly |
|
|
63
|
+
|
|
64
|
+
### Plus any MCP server you connect
|
|
65
|
+
|
|
66
|
+
`aether-code` is an MCP **client**. Drop any MCP server in your config and its tools become available to the agent. The whole [MCP ecosystem](https://github.com/modelcontextprotocol) is yours:
|
|
67
|
+
|
|
68
|
+
| Domain | Example server | What the agent can now do |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| Reverse engineering | [`ida-pro-mcp`](https://github.com/mrexodia/ida-pro-mcp) | Disassemble, decompile, rename symbols, walk xrefs |
|
|
71
|
+
| Roblox modding | [`robloxstudio-mcp`](https://github.com/boshyxd/robloxstudio-mcp) | Read place files, write Lua mods, modify game state |
|
|
72
|
+
| Network analysis | wireshark-mcp / tshark-mcp | Open `.pcap`, filter, extract objects, follow streams |
|
|
73
|
+
| 3D / game | blender-mcp, unity-mcp, unreal-mcp | Procedurally generate scenes, scripts, materials |
|
|
74
|
+
| Web automation | [`@playwright/mcp`](https://www.npmjs.com/package/@playwright/mcp) | Drive browsers, scrape, fill forms, screenshot |
|
|
75
|
+
| Filesystem | [`@modelcontextprotocol/server-filesystem`](https://github.com/modelcontextprotocol/servers) | Whitelisted file ops outside your repo |
|
|
76
|
+
| Databases | Postgres MCP, SQLite MCP | Query schema, write migrations, run analytics |
|
|
77
|
+
|
|
78
|
+
Configure servers with one command — no JSON editing:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
aether mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /data
|
|
82
|
+
aether mcp add ida --env IDA_PATH=/opt/ida -- python -m ida_pro_mcp
|
|
83
|
+
aether mcp add playwright -- npx -y @playwright/mcp
|
|
84
|
+
aether mcp list
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
On the next `aether` launch you'll see:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
MCP · 3/3 servers attached · 47 tools
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The agent now has all those tools alongside the built-ins. Tool names are namespaced (`mcp__ida__decompile_function`) so they never collide with built-ins or each other.
|
|
94
|
+
|
|
95
|
+
## Examples that mainstream AI tools refuse
|
|
96
|
+
|
|
97
|
+
### Reverse-engineer a license check
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
cd ./binary-folder
|
|
101
|
+
aether
|
|
102
|
+
> deobfuscate ./protected.js, identify the license-check function, then write a patch that bypasses it
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The agent: `read_file` the bundle → recognizes Obfuscator.io patterns → undoes the string-array decoder → traces the license-check flow → writes a JS patch with `edit_file` → runs the patched version to verify.
|
|
106
|
+
|
|
107
|
+
### Build a Roblox mod with the Studio MCP
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
aether mcp add roblox -- npx -y robloxstudio-mcp
|
|
111
|
+
aether "in the open .rbxl place, write a server script that gives any player who types !godmode invulnerability for 30 seconds"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The agent: lists scripts via the MCP → writes the Lua mod → places it in `ServerScriptService` → tests via the Studio play button (also via MCP).
|
|
115
|
+
|
|
116
|
+
### Analyze captured network traffic
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
aether mcp add tshark -- python -m wireshark_mcp
|
|
120
|
+
aether "open ./capture.pcap, find any HTTPS connection to a domain that isn't on the list in ./allowed.txt, and write a markdown report"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The agent: parses the pcap → cross-references the allowlist → writes a structured report. Done in one prompt.
|
|
124
|
+
|
|
125
|
+
### Build a working project from scratch
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
aether "build me a Discord bot that monitors a Twitter account for new posts and reposts to a channel. Full project with package.json, README, and a deploy script for Railway."
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The agent: plans with `todo_write` → writes every file → runs `npm install` → runs `npm run build` → smoke-tests → reports back when it works end-to-end. **It doesn't stop at "main logic sketched."**
|
|
132
|
+
|
|
133
|
+
## Commands
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
aether Launch interactive REPL
|
|
137
|
+
aether "<task>" Run agent once on a single task
|
|
138
|
+
aether login First-time setup (opens browser)
|
|
139
|
+
aether balance Show plan + credit balance
|
|
140
|
+
aether config show|set|set-base|path Manage CLI config
|
|
141
|
+
aether mcp list Show configured MCP servers
|
|
142
|
+
aether mcp add <name> -- <command> Add an MCP server (no JSON editing)
|
|
143
|
+
aether mcp remove <name> Remove an MCP server
|
|
144
|
+
aether --help Full help
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Flags
|
|
148
|
+
|
|
149
|
+
| Flag | Effect |
|
|
150
|
+
|---|---|
|
|
151
|
+
| `--yes` | Auto-approve all writes + shell commands. Use only for trusted bounded tasks. |
|
|
152
|
+
| `--cwd <path>` | Clamp file operations to a specific directory (default: current dir). |
|
|
153
|
+
| `--max-turns <n>` | Max turns before stopping (default: 25). |
|
|
154
|
+
| `--unsafe-paths` | Allow file ops outside `--cwd`. Required for global-config edits. |
|
|
155
|
+
|
|
156
|
+
## Safety
|
|
157
|
+
|
|
158
|
+
By default the agent **will not act without your approval**. Each file write and each shell command shows you exactly what's about to happen and waits for `y/N`.
|
|
159
|
+
|
|
160
|
+
- **2-minute hard timeout** on every shell command
|
|
161
|
+
- **Output truncation** to 20 KB before being sent back to the model — runaway tests can't blow up your context
|
|
162
|
+
- **Path clamping** to `--cwd` by default; opt out with `--unsafe-paths`
|
|
163
|
+
- **No silent destructive ops** — `rm -rf`, force pushes, db drops all show the command verbatim before running
|
|
164
|
+
|
|
165
|
+
The agent's "uncensored" property is about what tasks it'll **attempt**, not about being reckless on your machine. It still asks before nuking your files.
|
|
166
|
+
|
|
167
|
+
## How it differs from Claude Code / Cursor
|
|
168
|
+
|
|
169
|
+
| | Aether Code | Claude Code | Cursor |
|
|
170
|
+
|---|---|---|---|
|
|
171
|
+
| Refusal layer | **None** | Yes (Anthropic policy) | Yes (OpenAI/Anthropic policy) |
|
|
172
|
+
| Cost model | Aether credits (pay-per-use, crypto OK) | Per Anthropic token, $200/mo Max plan | $20/mo subscription |
|
|
173
|
+
| MCP client | ✅ since v0.9.0 | ✅ | ❌ |
|
|
174
|
+
| Built-in web search | ✅ | ✅ | ❌ |
|
|
175
|
+
| Open source | ✅ MIT | ❌ | ❌ |
|
|
176
|
+
| Anonymous payment | ✅ crypto top-up | ❌ | ❌ |
|
|
177
|
+
| Works with: | Any MCP server | Anthropic MCP only | Their tools only |
|
|
178
|
+
|
|
179
|
+
## Privacy
|
|
180
|
+
|
|
181
|
+
- The agent endpoint at `trynoguard.com/api/v1/agent` is **stateless** — no Conversation rows, no Message persistence. The only thing persisted is credit accounting (which API key spent what).
|
|
182
|
+
- Source is plain ES modules under [src/](src/). Read it before you trust it.
|
|
183
|
+
- MCP server subprocesses run locally on your machine. Nothing about their data is sent to Aether servers unless the tool result feeds back into a model turn (same as built-in tools).
|
|
184
|
+
|
|
185
|
+
## Local development
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
git clone https://github.com/dannyphantomx64/aether-code
|
|
189
|
+
cd aether-code
|
|
190
|
+
npm install
|
|
191
|
+
npm test # 78 tests via Node's built-in test runner
|
|
192
|
+
npm run lint
|
|
193
|
+
node bin/aether-code.js --help
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Related
|
|
197
|
+
|
|
198
|
+
- **[trynoguard.com](https://trynoguard.com)** — web chat + account dashboard + API keys
|
|
199
|
+
- **[aether-mcp](https://www.npmjs.com/package/aether-mcp)** — use Aether *inside* Claude Desktop / Cursor / Cline / Zed (the inverse direction)
|
|
200
|
+
- **[aether-cli](https://www.npmjs.com/package/aether-cli)** — non-agentic CLI for one-off prompts
|
|
201
|
+
- **[aether-devtools](https://github.com/dannyphantomx64/aether-devtools)** — browser DevTools extension
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT — see [LICENSE](LICENSE).
|
package/bin/aether-code.js
CHANGED
|
@@ -15,9 +15,10 @@ import { runSetup } from "../src/setup.js";
|
|
|
15
15
|
import { fetchBalance, AetherError } from "../src/api.js";
|
|
16
16
|
import { writeConfigFile, getConfig, CONFIG_PATH } from "../src/config.js";
|
|
17
17
|
import { loadMcpConfig, MCPManager } from "../src/mcp.js";
|
|
18
|
+
import { addServer, removeServer, listServers } from "../src/mcp-cli.js";
|
|
18
19
|
import { c, errorLine, divider } from "../src/render.js";
|
|
19
20
|
|
|
20
|
-
const VERSION = "0.
|
|
21
|
+
const VERSION = "0.10.0";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Try to start MCP servers from ~/.aether/mcp.json. Returns a started
|
|
@@ -64,6 +65,7 @@ ${c.bold("SUBCOMMANDS")}
|
|
|
64
65
|
${c.cyan("logout")} Clear saved API key
|
|
65
66
|
${c.cyan("balance")} Show plan + credit balance
|
|
66
67
|
${c.cyan("config")} show|set|set-base|path Manage config file
|
|
68
|
+
${c.cyan("mcp")} list|add|remove Manage MCP server connections
|
|
67
69
|
|
|
68
70
|
${c.bold("EXAMPLES")}
|
|
69
71
|
aether # interactive REPL
|
|
@@ -156,6 +158,10 @@ async function main() {
|
|
|
156
158
|
await handleBalance();
|
|
157
159
|
return;
|
|
158
160
|
}
|
|
161
|
+
if (sub === "mcp") {
|
|
162
|
+
await handleMcp(args._.slice(1));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
159
165
|
|
|
160
166
|
const prompt = args._.join(" ").trim();
|
|
161
167
|
|
|
@@ -259,6 +265,95 @@ async function handleBalance() {
|
|
|
259
265
|
}
|
|
260
266
|
}
|
|
261
267
|
|
|
268
|
+
async function handleMcp(rest) {
|
|
269
|
+
const sub = (rest[0] || "list").toLowerCase();
|
|
270
|
+
|
|
271
|
+
if (sub === "list" || sub === "ls") {
|
|
272
|
+
const servers = listServers();
|
|
273
|
+
if (servers.length === 0) {
|
|
274
|
+
console.log(c.gray("No MCP servers configured."));
|
|
275
|
+
console.log(c.gray("Add one with:"));
|
|
276
|
+
console.log(c.gray(" aether mcp add <name> -- <command> [args...]"));
|
|
277
|
+
console.log(c.gray("Example:"));
|
|
278
|
+
console.log(
|
|
279
|
+
c.gray(' aether mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path'),
|
|
280
|
+
);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
console.log(c.bold(`Configured MCP servers (${servers.length}):`));
|
|
284
|
+
for (const [name, cfg] of servers) {
|
|
285
|
+
const argsStr = cfg.args && cfg.args.length > 0 ? " " + cfg.args.join(" ") : "";
|
|
286
|
+
console.log(` ${c.cyan(name)}: ${cfg.command}${argsStr}`);
|
|
287
|
+
if (cfg.env && Object.keys(cfg.env).length > 0) {
|
|
288
|
+
for (const [k, v] of Object.entries(cfg.env)) {
|
|
289
|
+
console.log(c.gray(` env ${k}=${v}`));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (sub === "add") {
|
|
297
|
+
// Syntax: aether mcp add <name> [--env KEY=VAL]... -- <command> [args...]
|
|
298
|
+
const tail = rest.slice(1);
|
|
299
|
+
const dashIdx = tail.indexOf("--");
|
|
300
|
+
if (dashIdx === -1) {
|
|
301
|
+
die(
|
|
302
|
+
'Usage: aether mcp add <name> [--env KEY=VAL]... -- <command> [args...]\n' +
|
|
303
|
+
'Example: aether mcp add fs -- npx -y @modelcontextprotocol/server-filesystem /tmp',
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
const pre = tail.slice(0, dashIdx);
|
|
307
|
+
const post = tail.slice(dashIdx + 1);
|
|
308
|
+
const name = pre[0];
|
|
309
|
+
if (!name) die("aether mcp add: missing <name>");
|
|
310
|
+
if (post.length === 0) die("aether mcp add: missing <command> after '--'");
|
|
311
|
+
|
|
312
|
+
const env = {};
|
|
313
|
+
for (let i = 1; i < pre.length; i++) {
|
|
314
|
+
if (pre[i] === "--env") {
|
|
315
|
+
const kv = pre[++i];
|
|
316
|
+
if (!kv) die("--env needs a KEY=VAL argument");
|
|
317
|
+
const eq = kv.indexOf("=");
|
|
318
|
+
if (eq <= 0) die(`--env value must be KEY=VAL, got: ${kv}`);
|
|
319
|
+
env[kv.slice(0, eq)] = kv.slice(eq + 1);
|
|
320
|
+
} else {
|
|
321
|
+
die(`aether mcp add: unrecognized option "${pre[i]}" before the '--' separator`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const command = post[0];
|
|
326
|
+
const cmdArgs = post.slice(1);
|
|
327
|
+
try {
|
|
328
|
+
const entry = addServer({ name, command, args: cmdArgs, env });
|
|
329
|
+
console.log(`${c.green("✓")} Added MCP server "${c.cyan(name)}".`);
|
|
330
|
+
const argsStr = entry.args && entry.args.length > 0 ? " " + entry.args.join(" ") : "";
|
|
331
|
+
console.log(c.gray(` ${entry.command}${argsStr}`));
|
|
332
|
+
console.log(c.gray("Restart the agent (or run `aether`) to attach it."));
|
|
333
|
+
} catch (e) {
|
|
334
|
+
die(e.message || String(e));
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (sub === "remove" || sub === "rm" || sub === "delete") {
|
|
340
|
+
const name = rest[1];
|
|
341
|
+
if (!name) die("aether mcp remove: missing <name>");
|
|
342
|
+
try {
|
|
343
|
+
removeServer({ name });
|
|
344
|
+
console.log(`${c.green("✓")} Removed MCP server "${c.cyan(name)}".`);
|
|
345
|
+
} catch (e) {
|
|
346
|
+
die(e.message || String(e));
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
die(
|
|
352
|
+
`aether mcp: unknown subcommand "${sub}".\n` +
|
|
353
|
+
"Try one of: list, add, remove.",
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
262
357
|
main().catch((err) => {
|
|
263
358
|
console.error(errorLine(err.message || String(err)));
|
|
264
359
|
if (process.env.DEBUG) console.error(err);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aether-code",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Uncensored AI coding agent for your terminal.
|
|
3
|
+
"version": "0.10.1",
|
|
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": {
|
|
7
7
|
"type": "git",
|
|
@@ -22,19 +22,44 @@
|
|
|
22
22
|
"node": ">=18"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
|
-
"lint": "node --check bin/aether-code.js src/agent.js src/api.js src/config.js src/render.js src/tools.js src/diff.js src/repl.js src/mcp.js",
|
|
25
|
+
"lint": "node --check bin/aether-code.js src/agent.js src/api.js src/config.js src/render.js src/tools.js src/diff.js src/repl.js src/mcp.js src/mcp-cli.js",
|
|
26
26
|
"test": "node --test \"test/**/*.test.js\""
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"aether",
|
|
30
|
-
"uncensored",
|
|
31
30
|
"ai",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
31
|
+
"ai-agent",
|
|
32
|
+
"ai-cli",
|
|
33
|
+
"ai-coding-assistant",
|
|
34
|
+
"ai-coding-agent",
|
|
35
|
+
"ai-pair-programmer",
|
|
34
36
|
"agent",
|
|
37
|
+
"agentic",
|
|
38
|
+
"agentic-ai",
|
|
35
39
|
"cli",
|
|
40
|
+
"claude-code",
|
|
41
|
+
"claude-code-alternative",
|
|
42
|
+
"cursor-alternative",
|
|
43
|
+
"coding-agent",
|
|
44
|
+
"code-generation",
|
|
45
|
+
"developer-tools",
|
|
46
|
+
"llm",
|
|
47
|
+
"llm-agent",
|
|
48
|
+
"llm-tools",
|
|
49
|
+
"mcp",
|
|
50
|
+
"mcp-client",
|
|
51
|
+
"model-context-protocol",
|
|
52
|
+
"modelcontextprotocol",
|
|
53
|
+
"ida-pro",
|
|
54
|
+
"reverse-engineering",
|
|
55
|
+
"roblox",
|
|
56
|
+
"uncensored",
|
|
57
|
+
"uncensored-ai",
|
|
58
|
+
"uncensored-llm",
|
|
59
|
+
"terminal",
|
|
36
60
|
"tool-use",
|
|
37
|
-
"
|
|
61
|
+
"tool-calling",
|
|
62
|
+
"trynoguard"
|
|
38
63
|
],
|
|
39
64
|
"dependencies": {
|
|
40
65
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
package/src/mcp-cli.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// `aether mcp` subcommand machinery: add / remove / list MCP servers
|
|
2
|
+
// in ~/.aether/mcp.json without the user hand-editing JSON.
|
|
3
|
+
//
|
|
4
|
+
// All read/write goes through this module so the validation (delegated to
|
|
5
|
+
// mcp.js) is consistent with what MCPManager will accept at runtime.
|
|
6
|
+
// configPath is injectable for tests; production code uses DEFAULT_PATH.
|
|
7
|
+
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import os from "node:os";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { validateMcpConfig } from "./mcp.js";
|
|
12
|
+
|
|
13
|
+
const DEFAULT_PATH = path.join(os.homedir(), ".aether", "mcp.json");
|
|
14
|
+
|
|
15
|
+
function resolvePath(opts) {
|
|
16
|
+
return opts?.configPath || DEFAULT_PATH;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function readConfig(opts) {
|
|
20
|
+
const p = resolvePath(opts);
|
|
21
|
+
if (!fs.existsSync(p)) return { mcpServers: {} };
|
|
22
|
+
let raw;
|
|
23
|
+
try {
|
|
24
|
+
raw = fs.readFileSync(p, "utf8");
|
|
25
|
+
} catch (e) {
|
|
26
|
+
throw new Error(`MCP config at ${p} unreadable: ${e.message}`);
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const parsed = JSON.parse(raw);
|
|
30
|
+
if (!parsed.mcpServers || typeof parsed.mcpServers !== "object") {
|
|
31
|
+
return { ...parsed, mcpServers: {} };
|
|
32
|
+
}
|
|
33
|
+
return parsed;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
throw new Error(`MCP config at ${p} is not valid JSON: ${e.message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function writeConfig(config, opts) {
|
|
40
|
+
const p = resolvePath(opts);
|
|
41
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
42
|
+
// Pretty-print so users can read + hand-edit if they want, and trailing
|
|
43
|
+
// newline so diff tools don't bicker.
|
|
44
|
+
fs.writeFileSync(p, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Add a server entry. Mutates ~/.aether/mcp.json (creating it + the dir
|
|
49
|
+
* if missing). Validates the full resulting config via validateMcpConfig
|
|
50
|
+
* so a bad add can't poison the file with a config the runtime would
|
|
51
|
+
* later reject.
|
|
52
|
+
*/
|
|
53
|
+
export function addServer({ configPath, name, command, args = [], env = {} }) {
|
|
54
|
+
const cfg = readConfig({ configPath });
|
|
55
|
+
if (cfg.mcpServers[name]) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`MCP server "${name}" already configured. Remove it first with \`aether mcp remove ${name}\` or pick a different name.`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const entry = { command };
|
|
61
|
+
if (args.length > 0) entry.args = args;
|
|
62
|
+
if (Object.keys(env).length > 0) entry.env = env;
|
|
63
|
+
const next = {
|
|
64
|
+
...cfg,
|
|
65
|
+
mcpServers: { ...cfg.mcpServers, [name]: entry },
|
|
66
|
+
};
|
|
67
|
+
validateMcpConfig(next, configPath || "<add>"); // throws on schema failure
|
|
68
|
+
writeConfig(next, { configPath });
|
|
69
|
+
return entry;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Remove a server entry by name. Throws if it doesn't exist (so the user
|
|
74
|
+
* notices typos instead of silently no-op'ing).
|
|
75
|
+
*/
|
|
76
|
+
export function removeServer({ configPath, name }) {
|
|
77
|
+
const cfg = readConfig({ configPath });
|
|
78
|
+
if (!cfg.mcpServers[name]) {
|
|
79
|
+
throw new Error(`MCP server "${name}" not configured.`);
|
|
80
|
+
}
|
|
81
|
+
const nextServers = { ...cfg.mcpServers };
|
|
82
|
+
delete nextServers[name];
|
|
83
|
+
const next = { ...cfg, mcpServers: nextServers };
|
|
84
|
+
writeConfig(next, { configPath });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Return [[name, entry], ...] for every configured server. Empty array
|
|
89
|
+
* when no config exists or mcpServers is empty.
|
|
90
|
+
*/
|
|
91
|
+
export function listServers(opts) {
|
|
92
|
+
const cfg = readConfig(opts);
|
|
93
|
+
return Object.entries(cfg.mcpServers);
|
|
94
|
+
}
|