@metisos/cascade-cli 0.1.1 → 0.3.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.
@@ -12,6 +12,7 @@ const portfolio_js_1 = require("../commands/portfolio.js");
12
12
  const graph_js_1 = require("../commands/graph.js");
13
13
  const skills_js_1 = require("../commands/skills.js");
14
14
  const config_js_2 = require("../commands/config.js");
15
+ const welcome_js_1 = require("../tui/welcome.js");
15
16
  const BANNER = `
16
17
  \x1b[33m ██████╗ █████╗ ███████╗ ██████╗ █████╗ ██████╗ ███████╗\x1b[0m
17
18
  \x1b[33m ██╔════╝██╔══██╗██╔════╝██╔════╝██╔══██╗██╔══██╗██╔════╝\x1b[0m
@@ -27,7 +28,7 @@ const program = new commander_1.Command();
27
28
  program
28
29
  .name("cascade")
29
30
  .description("Cascade CLI — Real-time geopolitical and economic intelligence")
30
- .version("0.1.1")
31
+ .version("0.3.0")
31
32
  .addHelpText("beforeAll", BANNER);
32
33
  // Global format option
33
34
  function addFormat(cmd) {
@@ -93,10 +94,14 @@ function handleError(err) {
93
94
  console.error(`Error: ${err.message}`);
94
95
  process.exit(1);
95
96
  }
96
- // No args → show help (banner included via addHelpText)
97
+ // No args → launch interactive welcome menu
97
98
  if (process.argv.length <= 2) {
98
- program.outputHelp();
99
- process.exit(0);
99
+ (0, welcome_js_1.startWelcome)().catch((err) => {
100
+ console.error(err.message);
101
+ process.exit(1);
102
+ });
103
+ }
104
+ else {
105
+ // Subcommand provided → run command mode
106
+ program.parse();
100
107
  }
101
- // Parse
102
- program.parse();
@@ -0,0 +1 @@
1
+ export declare function startWelcome(): Promise<void>;
@@ -0,0 +1,519 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.startWelcome = startWelcome;
7
+ const readline_1 = __importDefault(require("readline"));
8
+ const child_process_1 = require("child_process");
9
+ const config_js_1 = require("../config.js");
10
+ const client_js_1 = require("../client.js");
11
+ const BANNER = `
12
+ \x1b[33m ██████╗ █████╗ ███████╗ ██████╗ █████╗ ██████╗ ███████╗\x1b[0m
13
+ \x1b[33m ██╔════╝██╔══██╗██╔════╝██╔════╝██╔══██╗██╔══██╗██╔════╝\x1b[0m
14
+ \x1b[33m ██║ ███████║███████╗██║ ███████║██║ ██║█████╗ \x1b[0m
15
+ \x1b[33m ██║ ██╔══██║╚════██║██║ ██╔══██║██║ ██║██╔══╝ \x1b[0m
16
+ \x1b[33m ╚██████╗██║ ██║███████║╚██████╗██║ ██║██████╔╝███████╗\x1b[0m
17
+ \x1b[33m ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═════╝ ╚══════╝\x1b[0m
18
+ \x1b[2m Real-Time Event Propagation Intelligence\x1b[0m
19
+ `;
20
+ function dim(s) { return `\x1b[2m${s}\x1b[0m`; }
21
+ function bold(s) { return `\x1b[1m${s}\x1b[0m`; }
22
+ function gold(s) { return `\x1b[33m${s}\x1b[0m`; }
23
+ function green(s) { return `\x1b[32m${s}\x1b[0m`; }
24
+ function red(s) { return `\x1b[31m${s}\x1b[0m`; }
25
+ function cyan(s) { return `\x1b[36m${s}\x1b[0m`; }
26
+ function clearScreen() { process.stdout.write("\x1b[2J\x1b[H"); }
27
+ function hideCursor() { process.stdout.write("\x1b[?25l"); }
28
+ function showCursor() { process.stdout.write("\x1b[?25h"); }
29
+ function promptLine(question) {
30
+ const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
31
+ return new Promise((resolve) => {
32
+ rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); });
33
+ });
34
+ }
35
+ function pause(ms) {
36
+ return new Promise((resolve) => setTimeout(resolve, ms));
37
+ }
38
+ function waitForKey() {
39
+ return new Promise((resolve) => {
40
+ const wasRaw = process.stdin.isRaw;
41
+ process.stdin.setRawMode(true);
42
+ process.stdin.resume();
43
+ process.stdin.once("data", () => {
44
+ process.stdin.setRawMode(wasRaw ?? false);
45
+ process.stdin.pause();
46
+ resolve();
47
+ });
48
+ });
49
+ }
50
+ function getClient() {
51
+ try {
52
+ return new client_js_1.CascadeClient();
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ }
58
+ // ── Actions ──
59
+ async function signInWithBrowser() {
60
+ showCursor();
61
+ const config = (0, config_js_1.loadConfig)();
62
+ const endpoint = config.endpoint;
63
+ console.log();
64
+ console.log(bold(" Sign in with browser"));
65
+ console.log(dim(" Opening your browser to authorize the CLI..."));
66
+ console.log();
67
+ let code;
68
+ try {
69
+ const res = await fetch(`${endpoint}/api/cli-auth`, { method: "POST" });
70
+ const data = await res.json();
71
+ code = data.code;
72
+ }
73
+ catch {
74
+ console.log(red(" ✗ Failed to reach Cascade. Check your endpoint."));
75
+ hideCursor();
76
+ await pause(2000);
77
+ return;
78
+ }
79
+ const authUrl = `${endpoint}/cli-auth?code=${code}`;
80
+ console.log(dim(` URL: ${authUrl}`));
81
+ console.log();
82
+ try {
83
+ const platform = process.platform;
84
+ if (platform === "darwin")
85
+ (0, child_process_1.execSync)(`open "${authUrl}"`);
86
+ else if (platform === "win32")
87
+ (0, child_process_1.execSync)(`start "${authUrl}"`);
88
+ else
89
+ (0, child_process_1.execSync)(`xdg-open "${authUrl}" 2>/dev/null || echo ""`);
90
+ console.log(dim(" Browser opened. Approve the request, then come back here."));
91
+ }
92
+ catch {
93
+ console.log(` Open this URL in your browser:`);
94
+ console.log(gold(` ${authUrl}`));
95
+ }
96
+ console.log();
97
+ console.log(dim(" Waiting for approval..."));
98
+ const startTime = Date.now();
99
+ while (Date.now() - startTime < 10 * 60 * 1000) {
100
+ await pause(2000);
101
+ try {
102
+ const res = await fetch(`${endpoint}/api/cli-auth?code=${code}`);
103
+ const data = await res.json();
104
+ if (data.status === "approved" && data.apiKey) {
105
+ (0, config_js_1.saveConfig)({ api_key: data.apiKey });
106
+ console.log();
107
+ console.log(green(" ✓ Authenticated! API key saved."));
108
+ hideCursor();
109
+ await pause(1500);
110
+ return;
111
+ }
112
+ if (data.status === "expired") {
113
+ console.log(red(" ✗ Session expired. Try again."));
114
+ hideCursor();
115
+ await pause(2000);
116
+ return;
117
+ }
118
+ process.stdout.write(dim("."));
119
+ }
120
+ catch { /* keep trying */ }
121
+ }
122
+ console.log();
123
+ console.log(red(" ✗ Timed out."));
124
+ hideCursor();
125
+ await pause(2000);
126
+ }
127
+ async function setupApiKey() {
128
+ showCursor();
129
+ console.log();
130
+ console.log(bold(" Paste API Key"));
131
+ console.log(dim(" Get your key from Portal → Settings → Developer & API"));
132
+ console.log();
133
+ const key = await promptLine(" API key: ");
134
+ if (key && key.startsWith("csk_")) {
135
+ (0, config_js_1.saveConfig)({ api_key: key });
136
+ console.log(green(" ✓ Saved to " + (0, config_js_1.getConfigPath)()));
137
+ }
138
+ else if (key) {
139
+ console.log(red(" ✗ Invalid format. Keys start with csk_"));
140
+ }
141
+ hideCursor();
142
+ await pause(1500);
143
+ }
144
+ async function setupEndpoint() {
145
+ showCursor();
146
+ console.log();
147
+ const config = (0, config_js_1.loadConfig)();
148
+ console.log(dim(` Current: ${config.endpoint}`));
149
+ const ep = await promptLine(" Endpoint URL: ");
150
+ if (ep) {
151
+ (0, config_js_1.saveConfig)({ endpoint: ep });
152
+ console.log(green(" ✓ Set to " + ep));
153
+ }
154
+ hideCursor();
155
+ await pause(1000);
156
+ }
157
+ async function testConnection() {
158
+ console.log();
159
+ console.log(dim(" Testing..."));
160
+ try {
161
+ const client = new client_js_1.CascadeClient();
162
+ const { data } = await client.getState();
163
+ const state = data;
164
+ const nodes = (state.graph?.nodes ?? []);
165
+ console.log(green(` ✓ Connected — Step ${state.step}, ${nodes.length} entities`));
166
+ }
167
+ catch (err) {
168
+ console.log(red(` ✗ ${err instanceof Error ? err.message : "Failed"}`));
169
+ }
170
+ await pause(2000);
171
+ }
172
+ async function viewDashboard() {
173
+ const client = getClient();
174
+ if (!client) {
175
+ console.log(red("\n Not configured. Set API key first."));
176
+ await pause(1500);
177
+ return;
178
+ }
179
+ try {
180
+ const { data } = await client.getState();
181
+ const state = data;
182
+ const nodes = (state.graph?.nodes ?? []);
183
+ let maxDev = 0;
184
+ const movers = [];
185
+ for (const n of nodes) {
186
+ const devs = n.deviation_pct ?? [];
187
+ const avg = devs.reduce((s, v) => s + Math.abs(v), 0) / (devs.length || 1);
188
+ if (avg > maxDev)
189
+ maxDev = avg;
190
+ movers.push({ name: String(n.name), dev: avg });
191
+ }
192
+ movers.sort((a, b) => b.dev - a.dev);
193
+ const health = maxDev > 15 ? red("CRITICAL") : maxDev > 8 ? gold("WARNING") : green("NOMINAL");
194
+ console.log();
195
+ console.log(` ${bold("System Status")} ${health}`);
196
+ console.log(` ${dim("Step")} ${state.step} ${dim("Entities")} ${nodes.length} ${dim("Peak")} ${maxDev.toFixed(1)}%`);
197
+ console.log();
198
+ for (const m of movers.slice(0, 8)) {
199
+ const bar = "█".repeat(Math.min(Math.round(m.dev / 2), 20));
200
+ const color = m.dev > 10 ? red : m.dev > 5 ? gold : green;
201
+ console.log(` ${m.name.replace(/_/g, " ").padEnd(24)} ${color(bar)} ${m.dev.toFixed(1)}%`);
202
+ }
203
+ }
204
+ catch (err) {
205
+ console.log(red(`\n ${err instanceof Error ? err.message : "Failed"}`));
206
+ }
207
+ console.log();
208
+ console.log(dim(" Press any key..."));
209
+ await waitForKey();
210
+ }
211
+ async function viewEntities() {
212
+ const client = getClient();
213
+ if (!client) {
214
+ console.log(red("\n Not configured."));
215
+ await pause(1500);
216
+ return;
217
+ }
218
+ try {
219
+ const { data } = await client.getState();
220
+ const nodes = data.graph?.nodes ?? [];
221
+ const rows = nodes.map((n) => {
222
+ const devs = n.deviation_pct ?? [];
223
+ const avg = devs.reduce((s, v) => s + Math.abs(v), 0) / (devs.length || 1);
224
+ return { name: String(n.name), cat: String(n.category), dev: avg, econ: devs[1] ?? 0 };
225
+ }).sort((a, b) => b.dev - a.dev);
226
+ console.log();
227
+ console.log(` ${dim("Entity".padEnd(26))} ${dim("Category".padEnd(10))} ${dim("Deviation".padEnd(10))} ${dim("Econ")}`);
228
+ console.log(` ${"─".repeat(60)}`);
229
+ for (const r of rows.slice(0, 20)) {
230
+ const color = r.dev > 10 ? red : r.dev > 5 ? gold : green;
231
+ console.log(` ${r.name.replace(/_/g, " ").padEnd(26)} ${r.cat.padEnd(10)} ${color(r.dev.toFixed(1).padStart(5) + "%")} ${r.econ >= 0 ? "+" : ""}${r.econ.toFixed(1)}%`);
232
+ }
233
+ if (rows.length > 20)
234
+ console.log(dim(`\n ...and ${rows.length - 20} more`));
235
+ }
236
+ catch (err) {
237
+ console.log(red(`\n ${err instanceof Error ? err.message : "Failed"}`));
238
+ }
239
+ console.log();
240
+ console.log(dim(" Press any key..."));
241
+ await waitForKey();
242
+ }
243
+ async function viewEvents() {
244
+ const client = getClient();
245
+ if (!client) {
246
+ console.log(red("\n Not configured."));
247
+ await pause(1500);
248
+ return;
249
+ }
250
+ try {
251
+ const { data } = await client.getEvents(15);
252
+ const events = data.events ?? [];
253
+ console.log();
254
+ console.log(` ${dim("Time".padEnd(10))} ${dim("Type".padEnd(22))} ${dim("Entity".padEnd(20))} ${dim("Sev")}`);
255
+ console.log(` ${"─".repeat(60)}`);
256
+ for (const e of events) {
257
+ const time = String(e.timestamp ?? "").slice(11, 19);
258
+ const type = String(e.event_type ?? "").replace(/_/g, " ");
259
+ const entity = String(e.entity_name ?? "");
260
+ const sev = Math.round((e.severity ?? 0) * 100);
261
+ const color = sev >= 70 ? red : sev >= 30 ? gold : green;
262
+ console.log(` ${time.padEnd(10)} ${type.padEnd(22)} ${entity.replace(/_/g, " ").padEnd(20)} ${color(sev + "%")}`);
263
+ }
264
+ }
265
+ catch (err) {
266
+ console.log(red(`\n ${err instanceof Error ? err.message : "Failed"}`));
267
+ }
268
+ console.log();
269
+ console.log(dim(" Press any key..."));
270
+ await waitForKey();
271
+ }
272
+ async function viewPrices() {
273
+ const client = getClient();
274
+ if (!client) {
275
+ console.log(red("\n Not configured."));
276
+ await pause(1500);
277
+ return;
278
+ }
279
+ try {
280
+ const { data } = await client.getPrices();
281
+ const tickers = data.tickers ?? [];
282
+ console.log();
283
+ for (const t of tickers) {
284
+ const name = String(t.name).replace(/_/g, " ");
285
+ const symbol = String(t.symbol);
286
+ try {
287
+ const { data: priceData } = await client.getPrice(String(t.name), "5d");
288
+ const bars = priceData.data ?? [];
289
+ if (bars.length >= 2) {
290
+ const last = bars[bars.length - 1];
291
+ const first = bars[0];
292
+ const price = last.close.toFixed(2);
293
+ const change = (last.close - first.close);
294
+ const pct = (change / first.close) * 100;
295
+ const color = change >= 0 ? green : red;
296
+ console.log(` ${name.padEnd(18)} ${dim(symbol.padEnd(10))} ${price.padStart(10)} ${color((change >= 0 ? "+" : "") + pct.toFixed(2) + "%")}`);
297
+ }
298
+ else {
299
+ console.log(` ${name.padEnd(18)} ${dim(symbol.padEnd(10))} ${dim("no data")}`);
300
+ }
301
+ }
302
+ catch {
303
+ console.log(` ${name.padEnd(18)} ${dim(symbol.padEnd(10))} ${dim("—")}`);
304
+ }
305
+ }
306
+ }
307
+ catch (err) {
308
+ console.log(red(`\n ${err instanceof Error ? err.message : "Failed"}`));
309
+ }
310
+ console.log();
311
+ console.log(dim(" Press any key..."));
312
+ await waitForKey();
313
+ }
314
+ async function interactiveChat() {
315
+ const client = getClient();
316
+ if (!client) {
317
+ console.log(red("\n Not configured."));
318
+ await pause(1500);
319
+ return;
320
+ }
321
+ showCursor();
322
+ clearScreen();
323
+ console.log(BANNER);
324
+ console.log(bold(" Cascade Intelligence Chat"));
325
+ console.log(dim(" Ask about entities, events, risks, prices. Type 'exit' to return.\n"));
326
+ const rl = readline_1.default.createInterface({
327
+ input: process.stdin,
328
+ output: process.stdout,
329
+ prompt: gold(" You: "),
330
+ });
331
+ rl.prompt();
332
+ rl.on("line", async (line) => {
333
+ const msg = line.trim();
334
+ if (!msg) {
335
+ rl.prompt();
336
+ return;
337
+ }
338
+ if (msg.toLowerCase() === "exit" || msg.toLowerCase() === "quit") {
339
+ rl.close();
340
+ hideCursor();
341
+ return;
342
+ }
343
+ console.log(dim(" Thinking..."));
344
+ try {
345
+ const { data } = await client.chat(msg);
346
+ console.log();
347
+ // Word wrap the response
348
+ const content = data.content;
349
+ const lines = content.split("\n");
350
+ for (const l of lines) {
351
+ const words = l.split(" ");
352
+ let line = " ";
353
+ for (const w of words) {
354
+ if (line.length + w.length > 90) {
355
+ console.log(cyan(line));
356
+ line = " " + w + " ";
357
+ }
358
+ else {
359
+ line += w + " ";
360
+ }
361
+ }
362
+ if (line.trim())
363
+ console.log(cyan(line));
364
+ }
365
+ console.log();
366
+ }
367
+ catch (err) {
368
+ console.log(red(` Error: ${err instanceof Error ? err.message : "Failed"}`));
369
+ console.log();
370
+ }
371
+ rl.prompt();
372
+ });
373
+ rl.on("close", () => {
374
+ // Return to menu handled by caller
375
+ });
376
+ // Wait for chat to end
377
+ await new Promise((resolve) => { rl.on("close", resolve); });
378
+ }
379
+ async function viewCorrelations() {
380
+ const client = getClient();
381
+ if (!client) {
382
+ console.log(red("\n Not configured."));
383
+ await pause(1500);
384
+ return;
385
+ }
386
+ try {
387
+ const { data } = await client.getCorrelations(1);
388
+ const d = data;
389
+ const entities = d.entities ?? [];
390
+ const nObs = d.n_observations ?? 0;
391
+ console.log();
392
+ console.log(` ${bold("Correlation Matrix")} ${dim(`(Econ, ${nObs} observations)`)}`);
393
+ if (nObs < 30) {
394
+ console.log(dim(`\n Need ${30 - nObs} more observations for correlation data.`));
395
+ }
396
+ else {
397
+ const matrix = d.empirical ?? [];
398
+ // Show top correlated pairs
399
+ const pairs = [];
400
+ for (let i = 0; i < entities.length; i++) {
401
+ for (let j = i + 1; j < entities.length; j++) {
402
+ pairs.push({ a: entities[i], b: entities[j], corr: matrix[i]?.[j] ?? 0 });
403
+ }
404
+ }
405
+ pairs.sort((a, b) => Math.abs(b.corr) - Math.abs(a.corr));
406
+ console.log(`\n ${dim("Strongest relationships:")}`);
407
+ for (const p of pairs.slice(0, 12)) {
408
+ const color = p.corr > 0.3 ? green : p.corr < -0.3 ? red : dim;
409
+ const sign = p.corr >= 0 ? "+" : "";
410
+ console.log(` ${p.a.replace(/_/g, " ").padEnd(20)} ↔ ${p.b.replace(/_/g, " ").padEnd(20)} ${color(sign + p.corr.toFixed(2))}`);
411
+ }
412
+ }
413
+ }
414
+ catch (err) {
415
+ console.log(red(`\n ${err instanceof Error ? err.message : "Failed"}`));
416
+ }
417
+ console.log();
418
+ console.log(dim(" Press any key..."));
419
+ await waitForKey();
420
+ }
421
+ async function generateSkills() {
422
+ showCursor();
423
+ console.log();
424
+ const { skillsCommand } = await import("../commands/skills.js");
425
+ await skillsCommand({ output: "CASCADE_SKILLS.md" });
426
+ hideCursor();
427
+ await pause(1500);
428
+ }
429
+ // ── Main Menu ──
430
+ function buildMenu() {
431
+ const config = (0, config_js_1.loadConfig)();
432
+ const hasKey = !!config.api_key;
433
+ const items = [];
434
+ if (hasKey) {
435
+ items.push({ label: "Dashboard", hint: "System status and top movers", action: viewDashboard });
436
+ items.push({ label: "Chat", hint: "Ask Cascade AI anything", action: interactiveChat });
437
+ items.push({ label: "Entities", hint: "All entities with deviations", action: viewEntities });
438
+ items.push({ label: "Events", hint: "Recent event feed", action: viewEvents });
439
+ items.push({ label: "Prices", hint: "Live market data", action: viewPrices });
440
+ items.push({ label: "Correlations", hint: "Entity co-movement", action: viewCorrelations });
441
+ items.push({ label: "Generate Skills", hint: "CASCADE_SKILLS.md for AI agents", action: generateSkills, separator: true });
442
+ }
443
+ items.push({ label: "Sign in with browser", hint: "Authenticate via Cascade portal", action: signInWithBrowser });
444
+ items.push({ label: "Paste API Key", hint: hasKey ? `Current: ${config.api_key.slice(0, 16)}...` : "Manually enter a key", action: setupApiKey });
445
+ items.push({ label: "Set Endpoint", hint: config.endpoint, action: setupEndpoint });
446
+ if (hasKey) {
447
+ items.push({ label: "Test Connection", hint: "Verify key and endpoint", action: testConnection });
448
+ }
449
+ items.push({ label: "Quit", hint: "", action: () => { showCursor(); clearScreen(); process.exit(0); } });
450
+ return items;
451
+ }
452
+ async function startWelcome() {
453
+ let items = buildMenu();
454
+ let selected = 0;
455
+ const render = () => {
456
+ clearScreen();
457
+ console.log(BANNER);
458
+ const config = (0, config_js_1.loadConfig)();
459
+ if (config.api_key) {
460
+ console.log(` ${green("●")} Connected ${dim("Key:")} ${config.api_key.slice(0, 16)}... ${dim("Endpoint:")} ${config.endpoint}`);
461
+ }
462
+ else {
463
+ console.log(` ${red("●")} Not configured ${dim("Sign in or paste API key to get started")}`);
464
+ }
465
+ console.log();
466
+ for (let i = 0; i < items.length; i++) {
467
+ const item = items[i];
468
+ if (item.separator && i > 0)
469
+ console.log();
470
+ if (i === selected) {
471
+ console.log(` ${gold("▸")} ${bold(item.label.padEnd(22))} ${dim(item.hint)}`);
472
+ }
473
+ else {
474
+ console.log(` ${dim(item.label.padEnd(22))} ${dim(item.hint)}`);
475
+ }
476
+ }
477
+ console.log();
478
+ console.log(dim(" ↑↓ Navigate Enter Select q Quit"));
479
+ };
480
+ hideCursor();
481
+ render();
482
+ process.stdin.setRawMode(true);
483
+ process.stdin.resume();
484
+ const handleKey = async (data) => {
485
+ const key = data.toString();
486
+ if (key === "q" || key === "\x03") {
487
+ showCursor();
488
+ clearScreen();
489
+ process.exit(0);
490
+ }
491
+ if (key === "\x1b[A") {
492
+ selected = (selected - 1 + items.length) % items.length;
493
+ render();
494
+ return;
495
+ }
496
+ if (key === "\x1b[B") {
497
+ selected = (selected + 1) % items.length;
498
+ render();
499
+ return;
500
+ }
501
+ if (key === "\r" || key === "\n") {
502
+ process.stdin.removeListener("data", handleKey);
503
+ process.stdin.setRawMode(false);
504
+ process.stdin.pause();
505
+ clearScreen();
506
+ console.log(BANNER);
507
+ await items[selected].action();
508
+ // Rebuild menu (config may have changed)
509
+ items = buildMenu();
510
+ if (selected >= items.length)
511
+ selected = 0;
512
+ process.stdin.setRawMode(true);
513
+ process.stdin.resume();
514
+ process.stdin.on("data", handleKey);
515
+ render();
516
+ }
517
+ };
518
+ process.stdin.on("data", handleKey);
519
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metisos/cascade-cli",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Cascade CLI — Real-time geopolitical and economic intelligence from your terminal",
5
5
  "bin": {
6
6
  "cascade": "./dist/bin/cascade.js"