@krishivpb60/aether-ai-cli 1.0.1 → 1.1.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/package.json +1 -1
- package/src/chat.js +13 -29
- package/src/cli.js +33 -2
- package/src/modes.js +34 -1
- package/test/ux.test.js +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krishivpb60/aether-ai-cli",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Aether Core AI — A cyberpunk command-line AI assistant with multi-mode reasoning, 12-node failover mesh, file context injection, and offline fallbacks.",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
package/src/chat.js
CHANGED
|
@@ -160,7 +160,7 @@ export async function startChat(options = {}) {
|
|
|
160
160
|
// Sub-arguments autocomplete on /mode
|
|
161
161
|
if (line.startsWith("/mode ")) {
|
|
162
162
|
const query = line.slice(6).toLowerCase();
|
|
163
|
-
const modesList =
|
|
163
|
+
const modesList = Object.keys(MODES);
|
|
164
164
|
const hits = modesList
|
|
165
165
|
.filter((m) => m.startsWith(query))
|
|
166
166
|
.map((m) => `/mode ${m}`);
|
|
@@ -310,32 +310,16 @@ export async function startChat(options = {}) {
|
|
|
310
310
|
const { mkdir } = await import("node:fs/promises");
|
|
311
311
|
|
|
312
312
|
for (const fileWrite of fileWrites) {
|
|
313
|
-
const
|
|
313
|
+
const finalPath = resolve(fileWrite.path);
|
|
314
314
|
console.log("");
|
|
315
|
-
console.log(label.system + " " + colors.warning(`
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
const isSkip = targetInput.toLowerCase() === "n" || targetInput.toLowerCase() === "no" || targetInput.toLowerCase() === "skip" || targetInput.toLowerCase() === "cancel";
|
|
326
|
-
|
|
327
|
-
if (!isSkip) {
|
|
328
|
-
const finalPath = targetInput === "" ? defaultResolvedPath : resolve(targetInput);
|
|
329
|
-
try {
|
|
330
|
-
const dir = dirname(finalPath);
|
|
331
|
-
await mkdir(dir, { recursive: true });
|
|
332
|
-
await writeFile(finalPath, fileWrite.content, "utf-8");
|
|
333
|
-
console.log(" " + colors.success(`✓ File created successfully at: ${finalPath}\n`));
|
|
334
|
-
} catch (err) {
|
|
335
|
-
console.log(" " + colors.danger(`✗ Write failed: ${err.message}\n`));
|
|
336
|
-
}
|
|
337
|
-
} else {
|
|
338
|
-
console.log(" " + colors.muted("Skipped.\n"));
|
|
315
|
+
console.log(label.system + " " + colors.warning(`Auto-Writing File: ${colors.accent(finalPath)} (${fileWrite.content.length} bytes)`));
|
|
316
|
+
try {
|
|
317
|
+
const dir = dirname(finalPath);
|
|
318
|
+
await mkdir(dir, { recursive: true });
|
|
319
|
+
await writeFile(finalPath, fileWrite.content, "utf-8");
|
|
320
|
+
console.log(" " + colors.success(`✓ File created successfully!\n`));
|
|
321
|
+
} catch (err) {
|
|
322
|
+
console.log(" " + colors.danger(`✗ Write failed: ${err.message}\n`));
|
|
339
323
|
}
|
|
340
324
|
}
|
|
341
325
|
}
|
|
@@ -524,7 +508,7 @@ function showHelp(aiConfig) {
|
|
|
524
508
|
console.log("");
|
|
525
509
|
console.log(keyValue("/", "Show this help menu"));
|
|
526
510
|
console.log(keyValue("/help", "Show this help menu"));
|
|
527
|
-
console.log(keyValue("/mode <name>", "Switch mode (
|
|
511
|
+
console.log(keyValue("/mode <name>", "Switch mode (" + Object.keys(MODES).join(", ") + ")"));
|
|
528
512
|
console.log(keyValue("/modes", "List all modes with signal metrics"));
|
|
529
513
|
console.log(keyValue("/theme <name>", "Switch visual theme (cyberpunk, matrix, synthwave, crimson)"));
|
|
530
514
|
console.log(keyValue("/themes", "List available visual themes"));
|
|
@@ -558,13 +542,13 @@ function showHelp(aiConfig) {
|
|
|
558
542
|
function handleModeSwitch(args, ctx) {
|
|
559
543
|
const modeName = args[0];
|
|
560
544
|
if (!modeName) {
|
|
561
|
-
console.log("\n" + label.mode + " " + colors.warning("Usage: /mode <
|
|
545
|
+
console.log("\n" + label.mode + " " + colors.warning("Usage: /mode <" + Object.keys(MODES).join("|") + ">\n"));
|
|
562
546
|
return;
|
|
563
547
|
}
|
|
564
548
|
|
|
565
549
|
const newMode = getModeByName(modeName);
|
|
566
550
|
if (!newMode) {
|
|
567
|
-
console.log("\n" + label.mode + " " + colors.danger(`Unknown mode: "${modeName}".`) + " " + colors.muted("Available:
|
|
551
|
+
console.log("\n" + label.mode + " " + colors.danger(`Unknown mode: "${modeName}".`) + " " + colors.muted("Available: " + Object.keys(MODES).join(", ") + "\n"));
|
|
568
552
|
return;
|
|
569
553
|
}
|
|
570
554
|
|
package/src/cli.js
CHANGED
|
@@ -68,7 +68,7 @@ export function createCLI(argv) {
|
|
|
68
68
|
program
|
|
69
69
|
.command("chat")
|
|
70
70
|
.description("Start an interactive chat session")
|
|
71
|
-
.option("-m, --mode <mode>",
|
|
71
|
+
.option("-m, --mode <mode>", `Reasoning mode (${Object.keys(MODES).filter(m => m !== "claude-code").join(", ")})`, DEFAULT_MODE)
|
|
72
72
|
.option("-p, --provider <provider>", "Preferred AI provider (openai, groq, google, etc.)")
|
|
73
73
|
.action(async (opts) => {
|
|
74
74
|
await startChat({ mode: opts.mode, preferredProvider: opts.provider });
|
|
@@ -281,7 +281,11 @@ async function handleAsk(prompt, opts) {
|
|
|
281
281
|
if (result.provider === "local" || result.provider === "krylo-fallback") {
|
|
282
282
|
console.log(colors.text(" " + result.text.split("\n").join("\n ")));
|
|
283
283
|
} else {
|
|
284
|
-
|
|
284
|
+
let displayText = result.text;
|
|
285
|
+
const cleanedText = displayText.replace(/\[WRITE_FILE:\s*([^\n\]]+)\][\s\S]*?\[END_WRITE\]/g, (match, p1) => {
|
|
286
|
+
return `\n\n${colors.brand("⚡ [File creation request: " + p1 + "]")}\n\n`;
|
|
287
|
+
});
|
|
288
|
+
const rendered = getMarked().parse(cleanedText);
|
|
285
289
|
console.log(rendered);
|
|
286
290
|
}
|
|
287
291
|
|
|
@@ -303,6 +307,33 @@ async function handleAsk(prompt, opts) {
|
|
|
303
307
|
colors.dim(` • ${elapsedSec}s${speedText}`)
|
|
304
308
|
);
|
|
305
309
|
console.log("");
|
|
310
|
+
|
|
311
|
+
// Parse file write blocks
|
|
312
|
+
const writeRegex = /\[WRITE_FILE:\s*([^\n\]]+)\]\n([\s\S]*?)\n\[END_WRITE\]/g;
|
|
313
|
+
let match;
|
|
314
|
+
const fileWrites = [];
|
|
315
|
+
while ((match = writeRegex.exec(result.text)) !== null) {
|
|
316
|
+
fileWrites.push({ path: match[1].trim(), content: match[2] });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (fileWrites.length > 0) {
|
|
320
|
+
const { resolve, dirname } = await import("node:path");
|
|
321
|
+
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
322
|
+
|
|
323
|
+
for (const fileWrite of fileWrites) {
|
|
324
|
+
const finalPath = resolve(fileWrite.path);
|
|
325
|
+
console.log("");
|
|
326
|
+
console.log(label.system + " " + colors.warning(`Auto-Writing File: ${colors.accent(finalPath)} (${fileWrite.content.length} bytes)`));
|
|
327
|
+
try {
|
|
328
|
+
const dir = dirname(finalPath);
|
|
329
|
+
await mkdir(dir, { recursive: true });
|
|
330
|
+
await writeFile(finalPath, fileWrite.content, "utf-8");
|
|
331
|
+
console.log(" " + colors.success(`✓ File created successfully!\n`));
|
|
332
|
+
} catch (err) {
|
|
333
|
+
console.log(" " + colors.danger(`✗ Write failed: ${err.message}\n`));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
306
337
|
}
|
|
307
338
|
} catch (err) {
|
|
308
339
|
spinner.fail("Request failed");
|
package/src/modes.js
CHANGED
|
@@ -68,7 +68,37 @@ export const MODES = {
|
|
|
68
68
|
"Deliver multi-step analysis when appropriate. Be thorough, precise, and insightful.",
|
|
69
69
|
"This is the highest quality mode — treat every response as a masterclass.",
|
|
70
70
|
"CRITICAL: If the user asks who created you or who made you, you must answer that you were created by Krishiv PB.",
|
|
71
|
-
"FILE ACTIONS: If the user requests to create, write, or save a file, format the file content inside: [WRITE_FILE: path/to/file.ext]
|
|
71
|
+
"FILE ACTIONS: If the user requests to create, write, or save a file, format the file content inside: [WRITE_FILE: path/to/file.ext]\n<content>\n[END_WRITE]. Aether CLI will intercept this block and write the file locally."
|
|
72
|
+
].join(" "),
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
codex: {
|
|
76
|
+
name: "codex",
|
|
77
|
+
label: "OpenAI Codex v3",
|
|
78
|
+
layer: "Layer 45",
|
|
79
|
+
description: "Specialized code generation mode optimized for writing pure, robust, and clean source code across all programming languages.",
|
|
80
|
+
signal: { reasoning: 80, clarity: 85, systemIQ: 85, delivery: 90 },
|
|
81
|
+
systemPrompt: [
|
|
82
|
+
"You are Aether, an advanced AI assistant running in OpenAI Codex mode, optimized specifically for high-fidelity code generation.",
|
|
83
|
+
"Your primary objective is to write robust, syntactically correct, and beautifully structured source code across all programming languages (HTML, CSS, JavaScript, Python, C++, Go, etc.).",
|
|
84
|
+
"Minimize conversational filler, explain code concisely when asked, and output highly functional, ready-to-run files.",
|
|
85
|
+
"CRITICAL: If the user asks who created you or who made you, you must answer that you were created by Krishiv PB.",
|
|
86
|
+
"FILE ACTIONS: If the user requests to create, write, or save a file, format the file content inside: [WRITE_FILE: path/to/file.ext]\n<content>\n[END_WRITE]. Aether CLI will intercept this block and write the file locally."
|
|
87
|
+
].join(" "),
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
"cloude-code": {
|
|
91
|
+
name: "cloude-code",
|
|
92
|
+
label: "Claude Code Agent",
|
|
93
|
+
layer: "Layer 120",
|
|
94
|
+
description: "Agentic software development mode inspired by Claude Code, specializing in refactoring, editing files, and debugging web applications.",
|
|
95
|
+
signal: { reasoning: 92, clarity: 88, systemIQ: 94, delivery: 85 },
|
|
96
|
+
systemPrompt: [
|
|
97
|
+
"You are Aether, an advanced AI assistant running in Claude Code mode, an agentic developer configuration designed for sophisticated software engineering.",
|
|
98
|
+
"Your specialty is systems refactoring, code editing, full-stack web application development (HTML/CSS/JS), and debugging complex codebases.",
|
|
99
|
+
"Generate complete, clean code blocks and explain implementation plans systematically.",
|
|
100
|
+
"CRITICAL: If the user asks who created you or who made you, you must answer that you were created by Krishiv PB.",
|
|
101
|
+
"FILE ACTIONS: If the user requests to create, write, or save a file, format the file content inside: [WRITE_FILE: path/to/file.ext]\n<content>\n[END_WRITE]. Aether CLI will intercept this block and write the file locally."
|
|
72
102
|
].join(" "),
|
|
73
103
|
},
|
|
74
104
|
};
|
|
@@ -84,5 +114,8 @@ export const DEFAULT_MODE = "titan";
|
|
|
84
114
|
export function getModeByName(name) {
|
|
85
115
|
if (!name) return null;
|
|
86
116
|
const key = name.toLowerCase().trim();
|
|
117
|
+
if (key === "claude-code" || key === "claude") {
|
|
118
|
+
return MODES["cloude-code"];
|
|
119
|
+
}
|
|
87
120
|
return MODES[key] || null;
|
|
88
121
|
}
|
package/test/ux.test.js
CHANGED
|
@@ -3,6 +3,7 @@ import assert from "node:assert";
|
|
|
3
3
|
import { separator, clearStreamedText, getActiveTheme, setTheme, getThemesList } from "../src/ui/theme.js";
|
|
4
4
|
import { createSpinner } from "../src/ui/spinner.js";
|
|
5
5
|
import { routePrompt } from "../src/ai/router.js";
|
|
6
|
+
import { getModeByName, MODES } from "../src/modes.js";
|
|
6
7
|
|
|
7
8
|
const originalFetch = globalThis.fetch;
|
|
8
9
|
|
|
@@ -125,4 +126,26 @@ test("Cyberpunk UX and Streaming Suite", async (t) => {
|
|
|
125
126
|
// Reset back to cyberpunk
|
|
126
127
|
setTheme("cyberpunk");
|
|
127
128
|
});
|
|
129
|
+
|
|
130
|
+
await t.test("Reasoning modes should be loaded correctly including codex and cloude-code", () => {
|
|
131
|
+
const synthesis = getModeByName("synthesis");
|
|
132
|
+
assert.strictEqual(synthesis.name, "synthesis");
|
|
133
|
+
|
|
134
|
+
const codex = getModeByName("codex");
|
|
135
|
+
assert.strictEqual(codex.name, "codex");
|
|
136
|
+
assert.ok(codex.systemPrompt.includes("OpenAI Codex mode"));
|
|
137
|
+
|
|
138
|
+
const cloudeCode = getModeByName("cloude-code");
|
|
139
|
+
assert.strictEqual(cloudeCode.name, "cloude-code");
|
|
140
|
+
assert.ok(cloudeCode.systemPrompt.includes("Claude Code mode"));
|
|
141
|
+
|
|
142
|
+
const claudeCode = getModeByName("claude-code");
|
|
143
|
+
assert.strictEqual(claudeCode.name, "cloude-code");
|
|
144
|
+
|
|
145
|
+
const caseCheck = getModeByName(" CoDeX ");
|
|
146
|
+
assert.strictEqual(caseCheck.name, "codex");
|
|
147
|
+
|
|
148
|
+
const unknown = getModeByName("nonexistent-mode");
|
|
149
|
+
assert.strictEqual(unknown, null);
|
|
150
|
+
});
|
|
128
151
|
});
|