@krishivpb60/aether-ai-cli 1.3.11 → 1.4.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/HIGHLIGHTS.md CHANGED
@@ -1,3 +1,18 @@
1
+ # Aether CLI v1.4.1 Highlights
2
+ - **Krylo Companion Bot Removal**:
3
+ - Removes the fictional Krylo companion terminal response lines entirely from local failbacks and mesh failures.
4
+ - Retains and isolates the fast local offline Math solver fallback.
5
+ - Implements clean, professional offline/configuration error alerts when no API keys are active or fail to respond.
6
+
7
+ # Aether CLI v1.4.0 Highlights
8
+ - **Microphone Audio Input & Dynamic Nerd Font Glyphs (`/mic`)**:
9
+ - Adds `/mic` voice command to record audio directly from your microphone inside the terminal session.
10
+ - Implements native zero-dependency audio recording on Windows using the WinMM Multimedia Control Interface (MCI) via PowerShell.
11
+ - Automatically transcribes speech using Google Gemini (base64 inlineData), Groq Whisper, or OpenAI Whisper.
12
+ - Fixes readline interface raw mode pausing blockages to ensure Enter keypress resolves transcription correctly.
13
+ - Introduces dynamic `getIcon` helper supporting high-definition vector icons in the terminal.
14
+ - Adds optional `"NERD_FONTS"` configuration parameter (`aether config set NERD_FONTS true`) to automatically switch between standard emojis and Nerd Font glyphs (like FontAwesome microphones, folders, gears, and branch trees) based on your preferences.
15
+
1
16
  # Aether CLI v1.3.11 Highlights
2
17
  - **Microphone Audio Input Non-TTY Safety & Transcription (`/mic`)**:
3
18
  - Adds `/mic` voice command to record audio directly from your microphone inside the terminal session.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krishivpb60/aether-ai-cli",
3
- "version": "1.3.11",
3
+ "version": "1.4.1",
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": {
@@ -1,17 +1,8 @@
1
1
  // ═══════════════════════════════════════════════════════════
2
2
  // AETHER AI CLI — Local Fallback Engine
3
- // Math Solver + Krylo Companion Bot
3
+ // Math Solver & Offline Fallback
4
4
  // ═══════════════════════════════════════════════════════════
5
5
 
6
- const KRYLO_REPLIES = [
7
- "Affirmative, commander. Systems are running at peak cybernetic capacity.",
8
- "Neon grids initialized. Matrix color modulates are at nominal density.",
9
- "Warning: Solar flare activity detected. Detuning audio synth harmonics by 18.4% to compensate.",
10
- "Neural nodes synchronized. Analyzing the portfolio's glassmorphic boundaries.",
11
- "I am Krylo, your holographic companion terminal. Ready to warp index nodes.",
12
- "Ecosystem diagnostics complete. 0 memory leaks, 100% premium responsive UI."
13
- ];
14
-
15
6
  /**
16
7
  * Detects if a prompt is a pure mathematical expression.
17
8
  * Supports basic operators, parentheses, standard math functions, and constants.
@@ -108,72 +99,16 @@ export function runMainframeHack() {
108
99
  }
109
100
 
110
101
  /**
111
- * Generates a local Krylo companion reply based on keywords.
102
+ * Generates a local offline/error reply when no AI keys are configured or fail.
112
103
  * @param {string} prompt - The user prompt
113
104
  * @returns {{ text: string, type: string }}
114
105
  */
115
- export function generateKryloReply(prompt) {
116
- const clean = prompt.toLowerCase();
117
-
118
- if (clean.includes("help") || clean.includes("shortcut") || clean.includes("command")) {
119
- return {
120
- text: [
121
- "💡 [SYSTEM DECK CHEAT SHEET]",
122
- " • Use `Ctrl + K` to open the Portal Search.",
123
- " • Use `Ctrl + Shift + L` to open the Links Directory.",
124
- " • Trigger Konami Code `↑↑↓↓←→←→BA` to launch Matrix mode!",
125
- " • Type `/mode <name>` to switch reasoning modes.",
126
- " • Type `/attach <file>` to inject file context.",
127
- " • Type `/export` to save the conversation.",
128
- ].join("\n"),
129
- type: "krylo-local",
130
- };
131
- }
132
-
133
- if (clean.includes("status") || clean.includes("hud") || clean.includes("cpu") || clean.includes("ping") || clean.includes("diagnostics")) {
134
- return {
135
- text: [
136
- "📊 [LIVE DIAGNOSTIC READOUT]",
137
- " • CPU Core Load: 15.4% (Optimized)",
138
- " • Ping Latency: 12ms (Hyper-Fast)",
139
- " • Memory Usage: 247MB / 8192MB",
140
- " • Canvas Sparklines: Active and tracking vectors",
141
- " • Failover Mesh: All 12 nodes standing by",
142
- ].join("\n"),
143
- type: "krylo-local",
144
- };
145
- }
146
-
147
- if (clean.includes("matrix") || clean.includes("rain") || clean.includes("color")) {
148
- return {
149
- text: [
150
- "⚡ [NEURAL GRIDS MODULATION]",
151
- " • Five stream channels active:",
152
- " Classic Green, Cyber Cyan, Neon Purple,",
153
- " Overdrive Red, Golden Matrix.",
154
- " • Detuned Web Audio frequency active.",
155
- " • Matrix rain density: 94.2%",
156
- ].join("\n"),
157
- type: "krylo-local",
158
- };
159
- }
160
-
161
- if (clean.includes("who") || clean.includes("name") || clean.includes("creator")) {
162
- return {
163
- text: [
164
- "🤖 [HOLOGRAPHIC COMPANION PROTOCOL]",
165
- " • Identification: Krylo (Nexus Companion)",
166
- " • Purpose: Pair-programming assistant & Commander companion",
167
- " • Creator: Krishiv PB — The Master Coder",
168
- " • Version: Aether Core AI v110 — Fusion Build",
169
- ].join("\n"),
170
- type: "krylo-local",
171
- };
172
- }
173
-
174
- const index = Math.floor(Math.random() * KRYLO_REPLIES.length);
106
+ export function generateOfflineReply(prompt) {
175
107
  return {
176
- text: `🤖 [KRYLO TERMINAL RESPONSE]\n ${KRYLO_REPLIES[index]}`,
177
- type: "krylo-local",
108
+ text: [
109
+ "⚠️ No active API keys configured. Please set GOOGLE_API_KEY, GROQ_API_KEY, or OPENAI_API_KEY in your config to start chatting.",
110
+ " Example: aether config set GOOGLE_API_KEY <your-key>"
111
+ ].join("\n"),
112
+ type: "offline-error"
178
113
  };
179
114
  }
package/src/ai/router.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // Routes through ALL configured providers automatically
4
4
  // ═══════════════════════════════════════════════════════════
5
5
 
6
- import { detectMathExpression, solveMath, generateKryloReply } from "./fallback.js";
6
+ import { detectMathExpression, solveMath, generateOfflineReply } from "./fallback.js";
7
7
  import { PROVIDERS, getActiveProviders } from "./providers.js";
8
8
  import {
9
9
  callOpenAICompatible,
@@ -59,16 +59,16 @@ export async function routePrompt(prompt, systemPrompt, config, onToken, history
59
59
  }
60
60
  }
61
61
 
62
- // ── No providers configured → Krylo ────────────────────
62
+ // ── No providers configured → Offline ───────────────────
63
63
  if (active.length === 0) {
64
64
  const startTime = performance.now();
65
- const kryloReply = generateKryloReply(prompt);
65
+ const offlineReply = generateOfflineReply(prompt);
66
66
  const latencyMs = performance.now() - startTime;
67
67
  const pTokens = estimateTokens(systemPrompt + prompt);
68
- const cTokens = estimateTokens(kryloReply.text);
69
- const usage = recordTokenUsage("krylo-local", pTokens, cTokens);
70
- recordLatency("krylo-fallback", "local", latencyMs, pTokens, cTokens, true);
71
- return { ...kryloReply, provider: "krylo-fallback", node: 0, usage };
68
+ const cTokens = estimateTokens(offlineReply.text);
69
+ const usage = recordTokenUsage("offline-local", pTokens, cTokens);
70
+ recordLatency("offline-fallback", "local", latencyMs, pTokens, cTokens, true);
71
+ return { ...offlineReply, provider: "offline-fallback", node: 0, usage };
72
72
  }
73
73
 
74
74
  // ── Try each provider in order ──────────────────────────
@@ -128,17 +128,17 @@ export async function routePrompt(prompt, systemPrompt, config, onToken, history
128
128
  }
129
129
  }
130
130
 
131
- // ── Final Fallback: Krylo Companion ─────────────────────
132
- const startTimeKrylo = performance.now();
133
- const kryloReply = generateKryloReply(prompt);
134
- const latencyMsKrylo = performance.now() - startTimeKrylo;
131
+ // ── Final Fallback: Offline Fallback ────────────────────
132
+ const startTimeOffline = performance.now();
133
+ const offlineReply = generateOfflineReply(prompt);
134
+ const latencyMsOffline = performance.now() - startTimeOffline;
135
135
  const pTokens = estimateTokens(systemPrompt + prompt + history.map(h => h.content).join(""));
136
- const cTokens = estimateTokens(kryloReply.text);
137
- const usage = recordTokenUsage("krylo-local", pTokens, cTokens);
138
- recordLatency("krylo-fallback", "local", latencyMsKrylo, pTokens, cTokens, true);
136
+ const cTokens = estimateTokens(offlineReply.text);
137
+ const usage = recordTokenUsage("offline-local", pTokens, cTokens);
138
+ recordLatency("offline-fallback", "local", latencyMsOffline, pTokens, cTokens, true);
139
139
  return {
140
- ...kryloReply,
141
- provider: "krylo-fallback",
140
+ ...offlineReply,
141
+ provider: "offline-fallback",
142
142
  node: 0,
143
143
  errors,
144
144
  usage,
package/src/chat.js CHANGED
@@ -25,7 +25,8 @@ import {
25
25
  getActiveTheme,
26
26
  setTheme,
27
27
  getThemesList,
28
- interactiveMenu
28
+ interactiveMenu,
29
+ getIcon
29
30
  } from "./ui/theme.js";
30
31
  import { createSpinner } from "./ui/spinner.js";
31
32
  import { showBanner } from "./ui/banner.js";
@@ -116,7 +117,7 @@ export async function startChat(options = {}) {
116
117
  label.mesh + " " +
117
118
  colors.accent("Failover mesh online: ") +
118
119
  colors.text(unique.join(" → ")) +
119
- colors.muted(" → Krylo fallback")
120
+ colors.muted(" → Offline fallback")
120
121
  );
121
122
  console.log(
122
123
  " " + colors.dim(`${active.length} node(s) active across ${unique.length} provider(s)`) + "\n"
@@ -297,7 +298,7 @@ export async function startChat(options = {}) {
297
298
  console.log(separator("─"));
298
299
  console.log("");
299
300
 
300
- if (result.provider === "local" || result.provider === "krylo-fallback") {
301
+ if (result.provider === "local" || result.provider === "offline-fallback") {
301
302
  console.log(colors.text(" " + result.text.split("\n").join("\n ")));
302
303
  } else {
303
304
  let displayText = result.text;
@@ -896,7 +897,7 @@ function showActiveProviders(aiConfig) {
896
897
  for (const { provider } of active) {
897
898
  console.log(" " + colors.success("✓ ") + colors.text(provider.name) + colors.dim(` • ${provider.defaultModel}`));
898
899
  }
899
- console.log(" " + colors.success("✓ ") + colors.text("Krylo Companion") + colors.dim(" • Local fallback"));
900
+ console.log(" " + colors.success("✓ ") + colors.text("Offline Fallback") + colors.dim(" • Local fallback"));
900
901
  console.log(" " + colors.success("✓ ") + colors.text("Math Solver") + colors.dim(" • Local"));
901
902
  console.log("");
902
903
  }
@@ -1183,7 +1184,7 @@ function providerBadge(result) {
1183
1184
  "perplexity": chalk.bgHex("#1a2a2a").hex("#6ce8ff")(" Perplexity "),
1184
1185
  "fireworks ai": chalk.bgHex("#2a1a1a").hex("#ff6b8d")(" Fireworks "),
1185
1186
  "local": chalk.bgHex("#1a2a1a").hex("#67ffb0")(" Math Solver "),
1186
- "krylo-fallback": chalk.bgHex("#0c1825").hex("#6ce8ff")(" Krylo "),
1187
+ "offline-fallback": chalk.bgHex("#2a0a14").hex("#ff6b6b")(" Offline "),
1187
1188
  };
1188
1189
 
1189
1190
  const badge = badges[result.provider] || colors.muted(` ${result.provider} `);
@@ -2256,7 +2257,7 @@ export async function handleMicInput(ctx) {
2256
2257
  return;
2257
2258
  }
2258
2259
 
2259
- console.log("\n" + label.system + " " + colors.brand("🎤 AUDIO VOICE INPUT"));
2260
+ console.log("\n" + label.system + " " + colors.brand(getIcon("mic", ctx.aiConfig) + "AUDIO VOICE INPUT"));
2260
2261
  console.log(separator("─"));
2261
2262
  console.log(colors.accent(" Recording started..."));
2262
2263
  console.log(" " + colors.muted("Speak into your microphone."));
package/src/cli.js CHANGED
@@ -312,7 +312,7 @@ async function handleAsk(prompt, opts) {
312
312
  console.log(separator("─"));
313
313
  console.log("");
314
314
 
315
- if (result.provider === "local" || result.provider === "krylo-fallback") {
315
+ if (result.provider === "local" || result.provider === "offline-fallback") {
316
316
  console.log(colors.text(" " + result.text.split("\n").join("\n ")));
317
317
  } else {
318
318
  let displayText = result.text;
@@ -577,11 +577,11 @@ async function handleStatus() {
577
577
  console.log("");
578
578
  console.log(colors.accent(" ◈ Local Fallbacks:"));
579
579
  console.log(keyValue(" Math Solver", colors.success("✓ Active")));
580
- console.log(keyValue(" Krylo Companion", colors.success("✓ Standing By")));
580
+ console.log(keyValue(" Offline Fallback", colors.success("✓ Standing By")));
581
581
 
582
582
  console.log("");
583
583
  console.log(colors.accent(" ◈ Failover Mesh:"));
584
- const totalNodes = 1 + active.length; // +1 for Krylo
584
+ const totalNodes = 1 + active.length; // +1 for local offline fallback
585
585
  console.log(keyValue(" Active Nodes", `${totalNodes}`));
586
586
  console.log(keyValue(" Mesh Status", active.length > 0 ? colors.success("✓ Online") : colors.warning("⚠ Local Only")));
587
587
  console.log("");
@@ -739,7 +739,7 @@ async function handleSetup() {
739
739
  console.log(" " + colors.muted("Start chatting: ") + colors.accent("aether chat"));
740
740
  console.log(" " + colors.muted("Quick query: ") + colors.accent('aether ask "Hello!"'));
741
741
  } else {
742
- console.log("\n " + colors.warning("No providers configured. Aether will use Krylo fallback mode."));
742
+ console.log("\n " + colors.warning("No providers configured. Aether will use local offline fallback mode."));
743
743
  console.log(" " + colors.muted("Run ") + colors.accent("aether setup") + colors.muted(" again anytime."));
744
744
  }
745
745
  console.log("");
package/src/config.js CHANGED
@@ -176,7 +176,7 @@ export function isValidConfigKey(key) {
176
176
  const allowedSpecialKeys = [
177
177
  "THEME", "CUSTOM_COMMANDS", "AUTOPILOT",
178
178
  "AUTO_UPDATE", "SHOW_HIGHLIGHTS", "LAST_UPDATE_CHECK", "LAST_NOTIFIED_VERSION",
179
- "SHOW_TOKENS", "DIAGNOSE_CMD"
179
+ "SHOW_TOKENS", "DIAGNOSE_CMD", "NERD_FONTS"
180
180
  ];
181
181
  if (upper.endsWith("_API_KEY") || upper.endsWith("_API_KEYS") || upper.endsWith("_MODEL") || allowedSpecialKeys.includes(upper)) {
182
182
  return true;
@@ -536,9 +536,9 @@ const HTML_CONTENT = `<!DOCTYPE html>
536
536
  topoList.appendChild(createTopologyElement({
537
537
  name: "Local Solver Node",
538
538
  configured: true,
539
- defaultModel: "Offline Math + Krylo Companion",
539
+ defaultModel: "Offline Math & Logic",
540
540
  tier: "free",
541
- description: "Zero-latency mathematical reasoning & local assistant fallbacks."
541
+ description: "Zero-latency mathematical reasoning & local offline fallbacks."
542
542
  }, "Node 0 (Local)"));
543
543
 
544
544
  mesh.forEach((provider, idx) => {
package/src/ui/banner.js CHANGED
@@ -7,7 +7,7 @@ import { readFileSync, existsSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
  import { homedir } from "node:os";
9
9
  import chalk from "chalk";
10
- import { colors, separator, modeBadge } from "./theme.js";
10
+ import { colors, separator, modeBadge, getIcon } from "./theme.js";
11
11
  import { getActiveProviders } from "../ai/providers.js";
12
12
  import { MODES } from "../modes.js";
13
13
 
@@ -137,11 +137,11 @@ export function showBanner(currentMode = "titan") {
137
137
  : "npm (@krishivpb60/aether-ai-cli)";
138
138
 
139
139
  const rows = [
140
- formatRow(` ${colors.muted("📂 Workspace")}`, colors.text(workspaceValue)),
141
- formatRow(` ${colors.muted("🧠 Mode")}`, modeRowValue),
142
- formatRow(` ${colors.muted("🟢 Network")}`, meshStatusText),
143
- formatRow(` ${colors.muted("⚙️ Engine")}`, colors.text(engineValue)),
144
- formatRow(` ${colors.muted("📦 Packager")}`, colors.text(packagerText)),
140
+ formatRow(` ${colors.muted(getIcon("workspace", config) + "Workspace")}`, colors.text(workspaceValue)),
141
+ formatRow(` ${colors.muted(getIcon("mode", config) + "Mode")}`, modeRowValue),
142
+ formatRow(` ${colors.muted(getIcon("network", config) + "Network")}`, meshStatusText),
143
+ formatRow(` ${colors.muted(getIcon("engine", config) + "Engine")}`, colors.text(engineValue)),
144
+ formatRow(` ${colors.muted(getIcon("package", config) + "Packager")}`, colors.text(packagerText)),
145
145
  ];
146
146
 
147
147
  console.log(`\n ⚡ ${colors.brand("AETHER COMMAND STATION v" + version)} • Welcome back, ${colors.accent(username)}`);
@@ -76,7 +76,7 @@
76
76
  }
77
77
 
78
78
  .hud-frame::after {
79
- content: "AETHER CLI V1.3.11";
79
+ content: "AETHER CLI V1.4.1";
80
80
  position: absolute;
81
81
  bottom: -12px;
82
82
  right: 20px;
package/src/ui/theme.js CHANGED
@@ -486,3 +486,29 @@ export async function interactiveMenu(headerText, items) {
486
486
  stdin.on("data", handleKey);
487
487
  });
488
488
  }
489
+
490
+ /**
491
+ * Returns either a premium Nerd Font glyph or a standard Unicode emoji fallback
492
+ * depending on whether NERD_FONTS is enabled in the configuration.
493
+ * @param {string} name - Icon name
494
+ * @param {object} config - Active configuration object
495
+ * @returns {string}
496
+ */
497
+ export function getIcon(name, config) {
498
+ const useNerd = config?.NERD_FONTS === true || config?.NERD_FONTS === "true";
499
+
500
+ const icons = {
501
+ workspace: useNerd ? "\uf07c " : "📂 ",
502
+ mode: useNerd ? "\uf0e0 " : "🧠 ", // brain / envelope-like modes icon
503
+ network: useNerd ? "\uf6ff " : "🟢 ", // network icon
504
+ engine: useNerd ? "\uf013 " : "⚙️ ", // gear icon
505
+ package: useNerd ? "\uf1b2 " : "📦 ", // package icon
506
+ mic: useNerd ? "\uf130 " : "🎤 ", // microphone icon
507
+ git: useNerd ? "\uf113 " : "🌿 ", // git/leaf icon
508
+ dashboard: useNerd ? "\uf201 " : "📊 ", // chart icon
509
+ bolt: useNerd ? "\uf0e7 " : "⚡ ", // lightning bolt icon
510
+ };
511
+
512
+ return icons[name] || "";
513
+ }
514
+
@@ -148,6 +148,7 @@ test("Configuration Loading Suite", async (t) => {
148
148
  assert.strictEqual(isValidConfigKey("GOOGLE_API_KEYS"), true);
149
149
  assert.strictEqual(isValidConfigKey("THEME"), true);
150
150
  assert.strictEqual(isValidConfigKey("CUSTOM_COMMANDS"), true);
151
+ assert.strictEqual(isValidConfigKey("NERD_FONTS"), true);
151
152
  assert.strictEqual(isValidConfigKey("INVALID_KEY_NAME"), false);
152
153
  });
153
154
 
@@ -3,7 +3,7 @@ import assert from "node:assert";
3
3
  import {
4
4
  detectMathExpression,
5
5
  solveMath,
6
- generateKryloReply,
6
+ generateOfflineReply,
7
7
  runMainframeHack,
8
8
  } from "../src/ai/fallback.js";
9
9
 
@@ -50,38 +50,10 @@ test("Offline Math Fallback & Krylo Suite", async (t) => {
50
50
  assert.strictEqual(solveMath("console.log(1)"), null);
51
51
  });
52
52
 
53
- await t.test("generateKryloReply responds to help and shortcut keywords", () => {
54
- const reply = generateKryloReply("I need help with commands");
55
- assert.strictEqual(reply.type, "krylo-local");
56
- assert.ok(reply.text.includes("[SYSTEM DECK CHEAT SHEET]"));
57
- assert.ok(reply.text.includes("Ctrl + K"));
58
- });
59
-
60
- await t.test("generateKryloReply responds to status and diagnostic keywords", () => {
61
- const reply = generateKryloReply("What is the CPU status?");
62
- assert.strictEqual(reply.type, "krylo-local");
63
- assert.ok(reply.text.includes("[LIVE DIAGNOSTIC READOUT]"));
64
- assert.ok(reply.text.includes("Failover Mesh"));
65
- });
66
-
67
- await t.test("generateKryloReply responds to matrix/rain/color keywords", () => {
68
- const reply = generateKryloReply("change matrix color");
69
- assert.strictEqual(reply.type, "krylo-local");
70
- assert.ok(reply.text.includes("[NEURAL GRIDS MODULATION]"));
71
- assert.ok(reply.text.includes("Classic Green"));
72
- });
73
-
74
- await t.test("generateKryloReply responds to who/name/creator keywords", () => {
75
- const reply = generateKryloReply("who is your creator?");
76
- assert.strictEqual(reply.type, "krylo-local");
77
- assert.ok(reply.text.includes("[HOLOGRAPHIC COMPANION PROTOCOL]"));
78
- assert.ok(reply.text.includes("Krishiv PB"));
79
- });
80
-
81
- await t.test("generateKryloReply falls back to random terminal responses", () => {
82
- const reply = generateKryloReply("Unrelated query");
83
- assert.strictEqual(reply.type, "krylo-local");
84
- assert.ok(reply.text.includes("[KRYLO TERMINAL RESPONSE]"));
53
+ await t.test("generateOfflineReply returns offline error formatting", () => {
54
+ const reply = generateOfflineReply("any query");
55
+ assert.strictEqual(reply.type, "offline-error");
56
+ assert.ok(reply.text.includes("No active API keys configured"));
85
57
  });
86
58
 
87
59
  await t.test("detectMathExpression and solveMath support trig, logs, square root and constants", () => {
@@ -134,19 +134,19 @@ test("Universal AI Router Suite", async (t) => {
134
134
  assert.ok(fetchCalls[1].url.includes("key=key-success"));
135
135
  });
136
136
 
137
- await t.test("routePrompt falls back to Krylo companion when no providers are configured", async () => {
137
+ await t.test("routePrompt falls back to Offline fallback when no providers are configured", async () => {
138
138
  globalThis.fetch = async () => {
139
139
  throw new Error("Fetch should not be called");
140
140
  };
141
141
 
142
142
  const result = await routePrompt("status", "Sys prompt", {});
143
- assert.strictEqual(result.provider, "krylo-fallback");
143
+ assert.strictEqual(result.provider, "offline-fallback");
144
144
  assert.strictEqual(result.node, 0);
145
- assert.strictEqual(result.type, "krylo-local");
146
- assert.ok(result.text.includes("[LIVE DIAGNOSTIC READOUT]"));
145
+ assert.strictEqual(result.type, "offline-error");
146
+ assert.ok(result.text.includes("No active API keys configured"));
147
147
  });
148
148
 
149
- await t.test("routePrompt falls back to Krylo companion when all providers fail", async () => {
149
+ await t.test("routePrompt falls back to Offline fallback when all providers fail", async () => {
150
150
  globalThis.fetch = async (url, options) => {
151
151
  fetchCalls.push({ url, options });
152
152
  return {
@@ -164,7 +164,7 @@ test("Universal AI Router Suite", async (t) => {
164
164
 
165
165
  const result = await routePrompt("Hello", "Sys prompt", config);
166
166
 
167
- assert.strictEqual(result.provider, "krylo-fallback");
167
+ assert.strictEqual(result.provider, "offline-fallback");
168
168
  assert.strictEqual(result.node, 0);
169
169
  assert.ok(result.errors);
170
170
  assert.strictEqual(result.errors.length, 2);
package/test/ux.test.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { test, beforeEach, afterEach } from "node:test";
2
2
  import assert from "node:assert";
3
3
  import { ReadableStream } from "node:stream/web";
4
- import { separator, clearStreamedText, StreamFilter, stripCodeFences, getActiveTheme, setTheme, getThemesList } from "../src/ui/theme.js";
4
+ import { separator, clearStreamedText, StreamFilter, stripCodeFences, getActiveTheme, setTheme, getThemesList, getIcon } from "../src/ui/theme.js";
5
5
  import { createSpinner } from "../src/ui/spinner.js";
6
6
  import { routePrompt } from "../src/ai/router.js";
7
7
  import { getModeByName, MODES } from "../src/modes.js";
@@ -183,4 +183,20 @@ test("Cyberpunk UX and Streaming Suite", async (t) => {
183
183
  const noFenceBlock = "console.log('hi');";
184
184
  assert.strictEqual(stripCodeFences(noFenceBlock), "console.log('hi');");
185
185
  });
186
+
187
+ await t.test("getIcon returns Nerd Font glyphs if enabled, else defaults to emojis", () => {
188
+ const nerdConfig = { NERD_FONTS: true };
189
+ const emojiConfig = { NERD_FONTS: false };
190
+ const defaultConfig = {};
191
+
192
+ // 1. Nerd Font Enabled
193
+ assert.strictEqual(getIcon("mic", nerdConfig), "\uf130 ");
194
+ assert.strictEqual(getIcon("git", nerdConfig), "\uf113 ");
195
+ assert.strictEqual(getIcon("dashboard", nerdConfig), "\uf201 ");
196
+
197
+ // 2. Nerd Font Disabled / Empty
198
+ assert.strictEqual(getIcon("mic", emojiConfig), "🎤 ");
199
+ assert.strictEqual(getIcon("git", emojiConfig), "🌿 ");
200
+ assert.strictEqual(getIcon("mic", defaultConfig), "🎤 ");
201
+ });
186
202
  });