@metisos/cascade-cli 0.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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const config_js_1 = require("../config.js");
6
+ const status_js_1 = require("../commands/status.js");
7
+ const entities_js_1 = require("../commands/entities.js");
8
+ const events_js_1 = require("../commands/events.js");
9
+ const prices_js_1 = require("../commands/prices.js");
10
+ const chat_js_1 = require("../commands/chat.js");
11
+ const portfolio_js_1 = require("../commands/portfolio.js");
12
+ const graph_js_1 = require("../commands/graph.js");
13
+ const skills_js_1 = require("../commands/skills.js");
14
+ const config_js_2 = require("../commands/config.js");
15
+ const program = new commander_1.Command();
16
+ program
17
+ .name("cascade")
18
+ .description("Cascade CLI — Real-time geopolitical and economic intelligence")
19
+ .version("0.1.0");
20
+ // Global format option
21
+ function addFormat(cmd) {
22
+ return cmd.option("-f, --format <format>", "Output format: table, json, csv", (0, config_js_1.loadConfig)().output || "table");
23
+ }
24
+ // Status
25
+ addFormat(program.command("status").description("System health, step, entity count")).action((opts) => (0, status_js_1.statusCommand)(opts).catch(handleError));
26
+ // Entities
27
+ addFormat(program.command("entities").description("List all entities with deviations")).action((opts) => (0, entities_js_1.entitiesCommand)(opts).catch(handleError));
28
+ addFormat(program
29
+ .command("entity <name>")
30
+ .description("Entity detail + connections")
31
+ .option("--history", "Show deviation trajectory")
32
+ .option("--steps <n>", "History steps", "30")).action((name, opts) => (0, entities_js_1.entityCommand)(name, { ...opts, steps: parseInt(opts.steps) }).catch(handleError));
33
+ addFormat(program.command("search <query>").description("Search entities")).action((query, opts) => (0, entities_js_1.searchCommand)(query, opts).catch(handleError));
34
+ // Events
35
+ addFormat(program
36
+ .command("events")
37
+ .description("Recent event feed")
38
+ .option("--limit <n>", "Number of events", "20")
39
+ .option("--type <type>", "Filter by event type")
40
+ .option("--watch", "Live streaming mode")).action((opts) => (0, events_js_1.eventsCommand)({ ...opts, limit: parseInt(opts.limit) }).catch(handleError));
41
+ // Prices
42
+ addFormat(program.command("prices").description("All tracked tickers")).action((opts) => (0, prices_js_1.pricesCommand)(opts).catch(handleError));
43
+ addFormat(program
44
+ .command("price <entity>")
45
+ .description("Live price for an entity")
46
+ .option("--period <period>", "Price period: 1d, 5d, 1mo, 3mo", "5d")).action((entity, opts) => (0, prices_js_1.priceCommand)(entity, opts).catch(handleError));
47
+ // Chat
48
+ addFormat(program.command("chat <message>").description("Ask Cascade AI")).action((message, opts) => (0, chat_js_1.chatCommand)(message, opts).catch(handleError));
49
+ // Dossier
50
+ addFormat(program
51
+ .command("dossier")
52
+ .description("Generate intelligence report")
53
+ .requiredOption("--entities <list>", "Comma-separated entity names")
54
+ .option("--type <type>", "Report type", "strategic_assessment")).action((opts) => (0, chat_js_1.dossierCommand)(opts).catch(handleError));
55
+ // Portfolio
56
+ addFormat(program.command("portfolio").description("Portfolio entities with risk")).action((opts) => (0, portfolio_js_1.portfolioCommand)(opts).catch(handleError));
57
+ // Graph
58
+ addFormat(program.command("graph").description("Network node + edge counts")).action((opts) => (0, graph_js_1.graphCommand)(opts).catch(handleError));
59
+ addFormat(program
60
+ .command("correlations")
61
+ .description("Pairwise correlation matrix")
62
+ .option("--dim <n>", "Dimension: 0=Stability, 1=Econ, 2=Political, 3=Social", "1")).action((opts) => (0, graph_js_1.correlationsCommand)({ ...opts, dim: parseInt(opts.dim) }).catch(handleError));
63
+ addFormat(program
64
+ .command("lags")
65
+ .description("Lead/lag relationship matrix")
66
+ .option("--dim <n>", "Dimension", "1")).action((opts) => (0, graph_js_1.lagsCommand)({ ...opts, dim: parseInt(opts.dim) }).catch(handleError));
67
+ // Skills
68
+ program
69
+ .command("skills")
70
+ .description("Generate CASCADE_SKILLS.md for AI agents")
71
+ .option("--output <path>", "Write to file instead of stdout")
72
+ .option("--static", "Static output without live context")
73
+ .action((opts) => (0, skills_js_1.skillsCommand)(opts).catch(handleError));
74
+ // Config
75
+ program
76
+ .command("config <action> [key] [value]")
77
+ .description("Manage configuration (show, set, path)")
78
+ .action((action, key, value) => (0, config_js_2.configCommand)(action, key, value));
79
+ // Error handler
80
+ function handleError(err) {
81
+ console.error(`Error: ${err.message}`);
82
+ process.exit(1);
83
+ }
84
+ // Parse
85
+ program.parse();
@@ -0,0 +1,37 @@
1
+ export interface ApiResponse<T = unknown> {
2
+ data: T;
3
+ meta: {
4
+ step?: number;
5
+ timestamp: string;
6
+ org_id?: string;
7
+ };
8
+ }
9
+ export interface ApiError {
10
+ error: {
11
+ code: string;
12
+ message: string;
13
+ retry_after?: number;
14
+ };
15
+ }
16
+ export declare class CascadeClient {
17
+ private endpoint;
18
+ private apiKey;
19
+ constructor(endpoint?: string, apiKey?: string);
20
+ private request;
21
+ getState(): Promise<ApiResponse<Record<string, unknown>>>;
22
+ getEvents(limit?: number): Promise<ApiResponse<Record<string, unknown>>>;
23
+ getGraph(): Promise<ApiResponse<Record<string, unknown>>>;
24
+ getEntity(name: string): Promise<ApiResponse<Record<string, unknown>>>;
25
+ getEntityHistory(name: string, steps?: number): Promise<ApiResponse<Record<string, unknown>>>;
26
+ searchEntities(query: string): Promise<ApiResponse<unknown[]>>;
27
+ getPrices(): Promise<ApiResponse<Record<string, unknown>>>;
28
+ getPrice(entity: string, period?: string): Promise<ApiResponse<Record<string, unknown>>>;
29
+ getPortfolio(): Promise<ApiResponse<unknown[]>>;
30
+ getCorrelations(dim?: number): Promise<ApiResponse<Record<string, unknown>>>;
31
+ getLags(dim?: number): Promise<ApiResponse<Record<string, unknown>>>;
32
+ chat(message: string): Promise<ApiResponse<{
33
+ content: string;
34
+ }>>;
35
+ dossier(entities: string[], reportType?: string): Promise<ApiResponse<Record<string, unknown>>>;
36
+ pollSources(): Promise<ApiResponse<Record<string, unknown>>>;
37
+ }
package/dist/client.js ADDED
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CascadeClient = void 0;
4
+ const config_js_1 = require("./config.js");
5
+ class CascadeClient {
6
+ endpoint;
7
+ apiKey;
8
+ constructor(endpoint, apiKey) {
9
+ const config = (0, config_js_1.loadConfig)();
10
+ this.endpoint = endpoint || config.endpoint;
11
+ this.apiKey = apiKey || config.api_key;
12
+ if (!this.apiKey) {
13
+ throw new Error("No API key configured. Run: cascade config set api-key <your-key>");
14
+ }
15
+ }
16
+ async request(path, opts = {}) {
17
+ const url = `${this.endpoint}/api/v1${path}`;
18
+ const headers = {
19
+ Authorization: `Bearer ${this.apiKey}`,
20
+ "Content-Type": "application/json",
21
+ ...opts.headers,
22
+ };
23
+ const res = await fetch(url, { ...opts, headers });
24
+ const body = await res.json();
25
+ if (!res.ok) {
26
+ const err = body;
27
+ const msg = err.error?.message || `HTTP ${res.status}`;
28
+ const code = err.error?.code || "unknown";
29
+ throw new Error(`[${code}] ${msg}`);
30
+ }
31
+ return body;
32
+ }
33
+ async getState() {
34
+ return this.request("/state");
35
+ }
36
+ async getEvents(limit = 20) {
37
+ return this.request(`/events?limit=${limit}`);
38
+ }
39
+ async getGraph() {
40
+ return this.request("/graph");
41
+ }
42
+ async getEntity(name) {
43
+ return this.request(`/entities/${encodeURIComponent(name)}`);
44
+ }
45
+ async getEntityHistory(name, steps = 30) {
46
+ return this.request(`/entities/${encodeURIComponent(name)}/history?steps=${steps}`);
47
+ }
48
+ async searchEntities(query) {
49
+ return this.request(`/entities/search?q=${encodeURIComponent(query)}`);
50
+ }
51
+ async getPrices() {
52
+ return this.request("/prices");
53
+ }
54
+ async getPrice(entity, period = "5d") {
55
+ return this.request(`/prices/${encodeURIComponent(entity)}?period=${period}`);
56
+ }
57
+ async getPortfolio() {
58
+ return this.request("/portfolio");
59
+ }
60
+ async getCorrelations(dim = 1) {
61
+ return this.request(`/calibration/correlations?dim=${dim}`);
62
+ }
63
+ async getLags(dim = 1) {
64
+ return this.request(`/calibration/lags?dim=${dim}`);
65
+ }
66
+ async chat(message) {
67
+ return this.request("/chat", {
68
+ method: "POST",
69
+ body: JSON.stringify({ message }),
70
+ });
71
+ }
72
+ async dossier(entities, reportType = "strategic_assessment") {
73
+ return this.request("/dossier", {
74
+ method: "POST",
75
+ body: JSON.stringify({ entities, report_type: reportType }),
76
+ });
77
+ }
78
+ async pollSources() {
79
+ return this.request("/sources/poll", {
80
+ method: "POST",
81
+ body: "{}",
82
+ });
83
+ }
84
+ }
85
+ exports.CascadeClient = CascadeClient;
@@ -0,0 +1,9 @@
1
+ import { type OutputFormat } from "../formatters/index.js";
2
+ export declare function chatCommand(message: string, opts: {
3
+ format: OutputFormat;
4
+ }): Promise<void>;
5
+ export declare function dossierCommand(opts: {
6
+ format: OutputFormat;
7
+ entities: string;
8
+ type?: string;
9
+ }): Promise<void>;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.chatCommand = chatCommand;
4
+ exports.dossierCommand = dossierCommand;
5
+ const client_js_1 = require("../client.js");
6
+ const index_js_1 = require("../formatters/index.js");
7
+ async function chatCommand(message, opts) {
8
+ const client = new client_js_1.CascadeClient();
9
+ const { data } = await client.chat(message);
10
+ if (opts.format === "json") {
11
+ console.log((0, index_js_1.formatOutput)(data, "json"));
12
+ return;
13
+ }
14
+ console.log();
15
+ console.log(data.content);
16
+ console.log();
17
+ }
18
+ async function dossierCommand(opts) {
19
+ const entityList = opts.entities.split(",").map((e) => e.trim());
20
+ const client = new client_js_1.CascadeClient();
21
+ const { data } = await client.dossier(entityList, opts.type);
22
+ if (opts.format === "json") {
23
+ console.log((0, index_js_1.formatOutput)(data, "json"));
24
+ return;
25
+ }
26
+ const report = data;
27
+ console.log(`\n ${report.title}`);
28
+ console.log(` ${report.generated_at} | Step ${report.step} | ${report.system_health}\n`);
29
+ const sections = report.sections ?? [];
30
+ for (const s of sections) {
31
+ console.log(` ${s.heading.toUpperCase()}`);
32
+ console.log(` ${s.content}\n`);
33
+ }
34
+ }
@@ -0,0 +1 @@
1
+ export declare function configCommand(action: string, key?: string, value?: string): void;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configCommand = configCommand;
4
+ const config_js_1 = require("../config.js");
5
+ function configCommand(action, key, value) {
6
+ if (action === "show" || action === "list") {
7
+ const config = (0, config_js_1.loadConfig)();
8
+ console.log(` Config: ${(0, config_js_1.getConfigPath)()}\n`);
9
+ console.log(` api-key: ${config.api_key ? config.api_key.slice(0, 16) + "..." : "(not set)"}`);
10
+ console.log(` endpoint: ${config.endpoint}`);
11
+ console.log(` output: ${config.output}`);
12
+ return;
13
+ }
14
+ if (action === "set" && key && value) {
15
+ const keyMap = {
16
+ "api-key": "api_key",
17
+ "apikey": "api_key",
18
+ "key": "api_key",
19
+ "endpoint": "endpoint",
20
+ "url": "endpoint",
21
+ "output": "output",
22
+ "format": "output",
23
+ };
24
+ const configKey = keyMap[key.toLowerCase()];
25
+ if (!configKey) {
26
+ console.error(`Unknown config key: ${key}`);
27
+ console.error(`Valid keys: api-key, endpoint, output`);
28
+ process.exit(1);
29
+ }
30
+ (0, config_js_1.saveConfig)({ [configKey]: value });
31
+ console.log(` Set ${key} = ${configKey === "api_key" ? value.slice(0, 16) + "..." : value}`);
32
+ return;
33
+ }
34
+ if (action === "path") {
35
+ console.log((0, config_js_1.getConfigPath)());
36
+ return;
37
+ }
38
+ console.error("Usage: cascade config <show|set|path> [key] [value]");
39
+ process.exit(1);
40
+ }
@@ -0,0 +1,12 @@
1
+ import { type OutputFormat } from "../formatters/index.js";
2
+ export declare function entitiesCommand(opts: {
3
+ format: OutputFormat;
4
+ }): Promise<void>;
5
+ export declare function entityCommand(name: string, opts: {
6
+ format: OutputFormat;
7
+ history?: boolean;
8
+ steps?: number;
9
+ }): Promise<void>;
10
+ export declare function searchCommand(query: string, opts: {
11
+ format: OutputFormat;
12
+ }): Promise<void>;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.entitiesCommand = entitiesCommand;
4
+ exports.entityCommand = entityCommand;
5
+ exports.searchCommand = searchCommand;
6
+ const client_js_1 = require("../client.js");
7
+ const index_js_1 = require("../formatters/index.js");
8
+ async function entitiesCommand(opts) {
9
+ const client = new client_js_1.CascadeClient();
10
+ const { data } = await client.getState();
11
+ const nodes = data.graph?.nodes ?? [];
12
+ const rows = nodes
13
+ .map((n) => {
14
+ const devs = n.deviation_pct ?? [];
15
+ const avg = devs.length > 0
16
+ ? devs.reduce((s, v) => s + Math.abs(v), 0) / devs.length
17
+ : 0;
18
+ return {
19
+ name: String(n.name).replace(/_/g, " "),
20
+ category: n.category,
21
+ deviation: `${avg.toFixed(1)}%`,
22
+ econ: devs[1] !== undefined ? `${devs[1] >= 0 ? "+" : ""}${devs[1].toFixed(1)}%` : "—",
23
+ };
24
+ })
25
+ .sort((a, b) => parseFloat(b.deviation) - parseFloat(a.deviation));
26
+ console.log((0, index_js_1.formatOutput)(rows, opts.format));
27
+ }
28
+ async function entityCommand(name, opts) {
29
+ const client = new client_js_1.CascadeClient();
30
+ if (opts.history) {
31
+ const { data } = await client.getEntityHistory(name, opts.steps ?? 30);
32
+ console.log((0, index_js_1.formatOutput)(data, opts.format));
33
+ return;
34
+ }
35
+ const { data } = await client.getEntity(name);
36
+ console.log((0, index_js_1.formatOutput)(data, opts.format));
37
+ }
38
+ async function searchCommand(query, opts) {
39
+ const client = new client_js_1.CascadeClient();
40
+ const { data } = await client.searchEntities(query);
41
+ console.log((0, index_js_1.formatOutput)(data, opts.format));
42
+ }
@@ -0,0 +1,7 @@
1
+ import { type OutputFormat } from "../formatters/index.js";
2
+ export declare function eventsCommand(opts: {
3
+ format: OutputFormat;
4
+ limit?: number;
5
+ type?: string;
6
+ watch?: boolean;
7
+ }): Promise<void>;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.eventsCommand = eventsCommand;
4
+ const client_js_1 = require("../client.js");
5
+ const index_js_1 = require("../formatters/index.js");
6
+ async function eventsCommand(opts) {
7
+ const client = new client_js_1.CascadeClient();
8
+ if (opts.watch) {
9
+ console.log("Watching events (Ctrl+C to stop)...\n");
10
+ const seen = new Set();
11
+ const poll = async () => {
12
+ const { data } = await client.getEvents(opts.limit ?? 10);
13
+ const events = data.events ?? [];
14
+ for (const ev of events) {
15
+ const id = `${ev.timestamp ?? ev.time}-${ev.event_type ?? ev.type}-${ev.entity_name ?? ""}`;
16
+ if (seen.has(id))
17
+ continue;
18
+ seen.add(id);
19
+ const time = String(ev.timestamp ?? ev.time ?? "").slice(11, 19);
20
+ const sev = Math.round((ev.severity ?? 0) * 100);
21
+ const entity = String(ev.entity_name ?? (ev.entities ?? []).join(", "));
22
+ const evType = String(ev.event_type ?? ev.type ?? "").replace(/_/g, " ");
23
+ const sevLabel = sev >= 70 ? "CRITICAL" : sev >= 30 ? "WARNING" : "info";
24
+ console.log(` ${time} ${sevLabel.padEnd(8)} ${evType.padEnd(22)} ${entity} (${sev}%)`);
25
+ }
26
+ };
27
+ await poll();
28
+ const interval = setInterval(poll, 5000);
29
+ process.on("SIGINT", () => { clearInterval(interval); process.exit(0); });
30
+ // Keep alive
31
+ await new Promise(() => { });
32
+ return;
33
+ }
34
+ const { data } = await client.getEvents(opts.limit ?? 20);
35
+ let events = data.events ?? [];
36
+ if (opts.type) {
37
+ events = events.filter((e) => String(e.event_type ?? e.type ?? "").toLowerCase().includes(opts.type.toLowerCase()));
38
+ }
39
+ const rows = events.map((e) => ({
40
+ time: String(e.timestamp ?? e.time ?? "").slice(11, 19),
41
+ type: (e.event_type ?? e.type ?? "").toString().replace(/_/g, " "),
42
+ entity: String(e.entity_name ?? (e.entities ?? []).join(", ")),
43
+ severity: `${Math.round((e.severity ?? 0) * 100)}%`,
44
+ }));
45
+ console.log((0, index_js_1.formatOutput)(rows, opts.format));
46
+ }
@@ -0,0 +1,12 @@
1
+ import { type OutputFormat } from "../formatters/index.js";
2
+ export declare function graphCommand(opts: {
3
+ format: OutputFormat;
4
+ }): Promise<void>;
5
+ export declare function correlationsCommand(opts: {
6
+ format: OutputFormat;
7
+ dim?: number;
8
+ }): Promise<void>;
9
+ export declare function lagsCommand(opts: {
10
+ format: OutputFormat;
11
+ dim?: number;
12
+ }): Promise<void>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.graphCommand = graphCommand;
4
+ exports.correlationsCommand = correlationsCommand;
5
+ exports.lagsCommand = lagsCommand;
6
+ const client_js_1 = require("../client.js");
7
+ const index_js_1 = require("../formatters/index.js");
8
+ async function graphCommand(opts) {
9
+ const client = new client_js_1.CascadeClient();
10
+ const { data } = await client.getGraph();
11
+ if (opts.format === "json") {
12
+ console.log((0, index_js_1.formatOutput)(data, "json"));
13
+ return;
14
+ }
15
+ const g = data;
16
+ console.log(` Nodes: ${(g.nodes ?? []).length}`);
17
+ console.log(` Edges: ${(g.edges ?? []).length}`);
18
+ }
19
+ async function correlationsCommand(opts) {
20
+ const client = new client_js_1.CascadeClient();
21
+ const { data } = await client.getCorrelations(opts.dim ?? 1);
22
+ console.log((0, index_js_1.formatOutput)(data, opts.format));
23
+ }
24
+ async function lagsCommand(opts) {
25
+ const client = new client_js_1.CascadeClient();
26
+ const { data } = await client.getLags(opts.dim ?? 1);
27
+ console.log((0, index_js_1.formatOutput)(data, opts.format));
28
+ }
@@ -0,0 +1,4 @@
1
+ import { type OutputFormat } from "../formatters/index.js";
2
+ export declare function portfolioCommand(opts: {
3
+ format: OutputFormat;
4
+ }): Promise<void>;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.portfolioCommand = portfolioCommand;
4
+ const client_js_1 = require("../client.js");
5
+ const index_js_1 = require("../formatters/index.js");
6
+ async function portfolioCommand(opts) {
7
+ const client = new client_js_1.CascadeClient();
8
+ const { data } = await client.getPortfolio();
9
+ console.log((0, index_js_1.formatOutput)(data, opts.format));
10
+ }
@@ -0,0 +1,8 @@
1
+ import { type OutputFormat } from "../formatters/index.js";
2
+ export declare function pricesCommand(opts: {
3
+ format: OutputFormat;
4
+ }): Promise<void>;
5
+ export declare function priceCommand(entity: string, opts: {
6
+ format: OutputFormat;
7
+ period?: string;
8
+ }): Promise<void>;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pricesCommand = pricesCommand;
4
+ exports.priceCommand = priceCommand;
5
+ const client_js_1 = require("../client.js");
6
+ const index_js_1 = require("../formatters/index.js");
7
+ async function pricesCommand(opts) {
8
+ const client = new client_js_1.CascadeClient();
9
+ const { data } = await client.getPrices();
10
+ const tickers = data.tickers ?? [];
11
+ const rows = tickers.map((t) => ({
12
+ name: String(t.name).replace(/_/g, " "),
13
+ symbol: t.symbol,
14
+ category: t.category,
15
+ }));
16
+ console.log((0, index_js_1.formatOutput)(rows, opts.format));
17
+ }
18
+ async function priceCommand(entity, opts) {
19
+ const client = new client_js_1.CascadeClient();
20
+ const { data } = await client.getPrice(entity, opts.period ?? "5d");
21
+ const bars = data.data ?? [];
22
+ if (opts.format === "json") {
23
+ console.log((0, index_js_1.formatOutput)(data, "json"));
24
+ return;
25
+ }
26
+ if (bars.length === 0) {
27
+ console.log(" No price data available");
28
+ return;
29
+ }
30
+ const last = bars[bars.length - 1];
31
+ const first = bars[0];
32
+ const change = last.close - first.close;
33
+ const changePct = (change / first.close) * 100;
34
+ console.log(` ${String(data.entity).replace(/_/g, " ")} (${data.symbol})`);
35
+ console.log(` Price: ${last.close.toFixed(2)}`);
36
+ console.log(` Change: ${change >= 0 ? "+" : ""}${change.toFixed(2)} (${changePct >= 0 ? "+" : ""}${changePct.toFixed(2)}%)`);
37
+ console.log(` High: ${last.high.toFixed(2)}`);
38
+ console.log(` Low: ${last.low.toFixed(2)}`);
39
+ console.log(` Period: ${bars.length} bars (${data.period})`);
40
+ }
@@ -0,0 +1,4 @@
1
+ export declare function skillsCommand(opts: {
2
+ output?: string;
3
+ static?: boolean;
4
+ }): Promise<void>;
@@ -0,0 +1,169 @@
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.skillsCommand = skillsCommand;
7
+ const config_js_1 = require("../config.js");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ async function skillsCommand(opts) {
10
+ const config = (0, config_js_1.loadConfig)();
11
+ const endpoint = config.endpoint || "https://cascade.metisos.co";
12
+ const content = generateSkillsFile(endpoint, opts.static);
13
+ if (opts.output) {
14
+ fs_1.default.writeFileSync(opts.output, content);
15
+ console.log(`Skills file written to ${opts.output}`);
16
+ }
17
+ else {
18
+ console.log(content);
19
+ }
20
+ }
21
+ function generateSkillsFile(endpoint, isStatic) {
22
+ return `# Cascade Intelligence — AI Agent Skills
23
+
24
+ > These skills give you access to real-time geopolitical and economic
25
+ > intelligence from the Cascade event propagation system.
26
+ > Run commands via shell to query live data.
27
+ > Endpoint: ${endpoint}
28
+
29
+ ## Setup
30
+
31
+ Cascade CLI is configured and authenticated. Run commands directly.
32
+ All commands support \`--format json\` for structured output.
33
+
34
+ ## Available Skills
35
+
36
+ ### Get System Status
37
+ \`\`\`bash
38
+ cascade status --format json
39
+ \`\`\`
40
+ Returns: system health (NOMINAL/WARNING/CRITICAL), simulation step,
41
+ entity count, source status, peak impact.
42
+ Use when: user asks about system state, health, or whether data is flowing.
43
+
44
+ ### List All Entities with Deviations
45
+ \`\`\`bash
46
+ cascade entities --format json
47
+ \`\`\`
48
+ Returns: all monitored entities with category, average deviation %, and
49
+ economic deviation. Sorted by deviation (most impacted first).
50
+ Use when: user asks "what's most impacted?", "show me the network", or
51
+ wants an overview of all entities.
52
+
53
+ ### Get Entity Detail
54
+ \`\`\`bash
55
+ cascade entity <name> --format json
56
+ \`\`\`
57
+ Returns: current 4D deviation (Stability, Econ, Political, Social),
58
+ category, connections, recent events affecting this entity.
59
+ Use when: user asks about a specific country, sector, or asset.
60
+ Example: \`cascade entity Crude_Oil --format json\`
61
+ Note: entity names use underscores (e.g., United_States, Crude_Oil, SP500).
62
+
63
+ ### Get Entity Trajectory
64
+ \`\`\`bash
65
+ cascade entity <name> --history --steps 30 --format json
66
+ \`\`\`
67
+ Returns: historical deviation trajectory over N steps.
68
+ Use when: user asks about trends, "is it getting worse?", recovery timing,
69
+ or when a move started.
70
+
71
+ ### Search Entities
72
+ \`\`\`bash
73
+ cascade search "<query>" --format json
74
+ \`\`\`
75
+ Returns: matching entities by name, category, or tag.
76
+ Use when: user mentions an entity but you're not sure of the exact name.
77
+
78
+ ### Get Live Prices
79
+ \`\`\`bash
80
+ cascade price <entity> --format json
81
+ cascade prices --format json
82
+ \`\`\`
83
+ Returns: current market price, previous close, % change, recent history
84
+ for tracked financial entities (stocks, indices, commodities, crypto).
85
+ Use when: user asks about current prices, market levels, or price changes.
86
+ Available tickers: SP500, DowJones, NASDAQ, FTSE100, Nikkei225, Crude_Oil,
87
+ Gold, Bitcoin, VIX, US_10Y_Yield, US_Dollar, HangSeng.
88
+
89
+ ### Get Event Feed
90
+ \`\`\`bash
91
+ cascade events --limit 20 --format json
92
+ cascade events --type "geopolitical_crisis" --format json
93
+ \`\`\`
94
+ Returns: recent events ingested from RSS, stock, and weather feeds.
95
+ Use when: user asks "what happened?", "any recent events?", or wants
96
+ to understand what's driving deviations.
97
+ Event types: sanction, energy_shock, geopolitical_crisis, market_crash,
98
+ stock_rise, stock_drop, policy_bundle, port_closure, pandemic.
99
+
100
+ ### Get Portfolio Risk
101
+ \`\`\`bash
102
+ cascade portfolio --format json
103
+ \`\`\`
104
+ Returns: portfolio entities with dollar-at-risk, risk scores, deviations.
105
+ Use when: user asks about portfolio exposure or risk.
106
+
107
+ ### Get Correlation Data
108
+ \`\`\`bash
109
+ cascade correlations --format json
110
+ cascade lags --format json
111
+ \`\`\`
112
+ Returns: pairwise correlation and lead/lag matrices between entities.
113
+ Use when: user asks "how are X and Y related?", "what leads what?",
114
+ or wants to understand co-movement patterns.
115
+
116
+ ### Get Network Graph
117
+ \`\`\`bash
118
+ cascade graph --format json
119
+ \`\`\`
120
+ Returns: full graph structure with all nodes and edges.
121
+ Use when: user wants to understand the network topology or connection counts.
122
+
123
+ ### Ask Cascade AI
124
+ \`\`\`bash
125
+ cascade chat "your question here" --format json
126
+ \`\`\`
127
+ Ask the Cascade AI analyst about current geopolitical risks, entity
128
+ analysis, portfolio impact, or any intelligence question. Has access
129
+ to live simulation data, news search, and entity analysis tools.
130
+ Use when: user needs analysis, interpretation, or forward-looking assessment
131
+ rather than raw data.
132
+
133
+ ### Generate Intelligence Report
134
+ \`\`\`bash
135
+ cascade dossier --entities "Entity1,Entity2,Entity3" --format json
136
+ \`\`\`
137
+ Generates a multi-section intelligence briefing with live web sources.
138
+ Use when: user asks for a report, briefing, or comprehensive analysis.
139
+
140
+ ### Watch Live Events
141
+ \`\`\`bash
142
+ cascade events --watch
143
+ \`\`\`
144
+ Streams new events as they arrive (polls every 5s). Ctrl+C to stop.
145
+ Use when: user wants to monitor events in real-time.
146
+
147
+ ## When to Use Cascade
148
+
149
+ - User asks about geopolitical risk, sanctions, market impact
150
+ - User needs to understand how events cascade across economies/sectors
151
+ - User asks about commodity prices, market correlations, portfolio risk
152
+ - User wants to know which entities are most impacted right now
153
+ - User asks about trends, recovery timing, or forward outlook
154
+ - User needs an intelligence briefing on a region or sector
155
+ - User asks "what's happening?" or "what's driving X?"
156
+
157
+ ## Entity Name Format
158
+
159
+ Use underscores for multi-word names: \`United_States\`, \`Crude_Oil\`,
160
+ \`Energy_Sector\`, \`Saudi_Arabia\`. Single-word names are plain: \`Iran\`,
161
+ \`Gold\`, \`Bitcoin\`, \`VIX\`.
162
+
163
+ ## Output
164
+
165
+ All commands support \`--format json\` for structured output that you
166
+ can parse and reference in your responses. Always use \`--format json\`
167
+ when calling these tools so you can extract specific values.
168
+ `;
169
+ }
@@ -0,0 +1,4 @@
1
+ import { type OutputFormat } from "../formatters/index.js";
2
+ export declare function statusCommand(opts: {
3
+ format: OutputFormat;
4
+ }): Promise<void>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.statusCommand = statusCommand;
4
+ const client_js_1 = require("../client.js");
5
+ const index_js_1 = require("../formatters/index.js");
6
+ async function statusCommand(opts) {
7
+ const client = new client_js_1.CascadeClient();
8
+ const { data } = await client.getState();
9
+ const nodes = data.graph?.nodes ?? [];
10
+ const summary = {
11
+ status: data.status ?? "active",
12
+ step: data.step,
13
+ entities: nodes.length,
14
+ health: getHealth(nodes),
15
+ };
16
+ if (opts.format === "json") {
17
+ console.log((0, index_js_1.formatOutput)(summary, "json"));
18
+ }
19
+ else {
20
+ console.log(` CASCADE STATUS`);
21
+ console.log(` Step: ${summary.step}`);
22
+ console.log(` Entities: ${summary.entities}`);
23
+ console.log(` Health: ${summary.health}`);
24
+ }
25
+ }
26
+ function getHealth(nodes) {
27
+ let maxDev = 0;
28
+ for (const n of nodes) {
29
+ const devs = n.deviation_pct;
30
+ if (devs) {
31
+ const avg = devs.reduce((s, v) => s + Math.abs(v), 0) / devs.length;
32
+ if (avg > maxDev)
33
+ maxDev = avg;
34
+ }
35
+ }
36
+ return maxDev > 15 ? "CRITICAL" : maxDev > 8 ? "WARNING" : "NOMINAL";
37
+ }
@@ -0,0 +1,8 @@
1
+ export interface CascadeConfig {
2
+ api_key: string;
3
+ endpoint: string;
4
+ output: "table" | "json" | "csv";
5
+ }
6
+ export declare function loadConfig(): CascadeConfig;
7
+ export declare function saveConfig(config: Partial<CascadeConfig>): void;
8
+ export declare function getConfigPath(): string;
package/dist/config.js ADDED
@@ -0,0 +1,41 @@
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.loadConfig = loadConfig;
7
+ exports.saveConfig = saveConfig;
8
+ exports.getConfigPath = getConfigPath;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const os_1 = __importDefault(require("os"));
12
+ const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), ".cascade");
13
+ const CONFIG_FILE = path_1.default.join(CONFIG_DIR, "config.json");
14
+ const DEFAULTS = {
15
+ api_key: "",
16
+ endpoint: "https://cascade.metisos.co",
17
+ output: "table",
18
+ };
19
+ function loadConfig() {
20
+ try {
21
+ if (fs_1.default.existsSync(CONFIG_FILE)) {
22
+ const raw = fs_1.default.readFileSync(CONFIG_FILE, "utf-8");
23
+ return { ...DEFAULTS, ...JSON.parse(raw) };
24
+ }
25
+ }
26
+ catch {
27
+ // fall through
28
+ }
29
+ return { ...DEFAULTS };
30
+ }
31
+ function saveConfig(config) {
32
+ const existing = loadConfig();
33
+ const merged = { ...existing, ...config };
34
+ if (!fs_1.default.existsSync(CONFIG_DIR)) {
35
+ fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
36
+ }
37
+ fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2) + "\n");
38
+ }
39
+ function getConfigPath() {
40
+ return CONFIG_FILE;
41
+ }
@@ -0,0 +1,2 @@
1
+ export type OutputFormat = "table" | "json" | "csv";
2
+ export declare function formatOutput(data: unknown, format: OutputFormat): string;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatOutput = formatOutput;
4
+ function formatOutput(data, format) {
5
+ switch (format) {
6
+ case "json":
7
+ return JSON.stringify(data, null, 2);
8
+ case "csv":
9
+ return toCsv(data);
10
+ case "table":
11
+ default:
12
+ return toTable(data);
13
+ }
14
+ }
15
+ function toCsv(data) {
16
+ if (Array.isArray(data) && data.length > 0 && typeof data[0] === "object") {
17
+ const keys = Object.keys(data[0]);
18
+ const header = keys.join(",");
19
+ const rows = data.map((row) => {
20
+ const r = row;
21
+ return keys.map((k) => {
22
+ const v = r[k];
23
+ const s = v === null || v === undefined ? "" : String(v);
24
+ return s.includes(",") || s.includes('"') ? `"${s.replace(/"/g, '""')}"` : s;
25
+ }).join(",");
26
+ });
27
+ return [header, ...rows].join("\n");
28
+ }
29
+ return JSON.stringify(data);
30
+ }
31
+ function toTable(data) {
32
+ if (Array.isArray(data) && data.length > 0 && typeof data[0] === "object") {
33
+ const keys = Object.keys(data[0]);
34
+ const widths = keys.map((k) => Math.max(k.length, ...data.map((r) => {
35
+ const v = r[k];
36
+ return v === null || v === undefined ? 1 : String(v).length;
37
+ })));
38
+ const sep = widths.map((w) => "─".repeat(w + 2)).join("┼");
39
+ const header = keys
40
+ .map((k, i) => ` ${k.padEnd(widths[i])} `)
41
+ .join("│");
42
+ const rows = data.map((row) => {
43
+ const r = row;
44
+ return keys
45
+ .map((k, i) => {
46
+ const v = r[k];
47
+ const s = v === null || v === undefined ? "—" : String(v);
48
+ return ` ${s.padEnd(widths[i])} `;
49
+ })
50
+ .join("│");
51
+ });
52
+ return [header, sep, ...rows].join("\n");
53
+ }
54
+ if (typeof data === "object" && data !== null) {
55
+ const entries = Object.entries(data);
56
+ const maxKeyLen = Math.max(...entries.map(([k]) => k.length), 5);
57
+ return entries
58
+ .map(([k, v]) => {
59
+ const val = typeof v === "object" ? JSON.stringify(v) : String(v ?? "—");
60
+ return ` ${k.padEnd(maxKeyLen)} ${val}`;
61
+ })
62
+ .join("\n");
63
+ }
64
+ return String(data);
65
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@metisos/cascade-cli",
3
+ "version": "0.1.0",
4
+ "description": "Cascade CLI — Real-time geopolitical and economic intelligence from your terminal",
5
+ "bin": {
6
+ "cascade": "./dist/bin/cascade.js"
7
+ },
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "dev": "tsx src/bin/cascade.ts",
11
+ "start": "node dist/bin/cascade.js"
12
+ },
13
+ "files": [
14
+ "dist/**/*"
15
+ ],
16
+ "dependencies": {
17
+ "commander": "^12.0.0",
18
+ "chalk": "^5.3.0",
19
+ "ora": "^8.0.0",
20
+ "ink": "^5.0.1",
21
+ "ink-text-input": "^6.0.0",
22
+ "react": "^19.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "typescript": "^5.5.0",
26
+ "@types/node": "^20.0.0",
27
+ "@types/react": "^19.0.0",
28
+ "tsx": "^4.0.0"
29
+ },
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "license": "MIT",
34
+ "publishConfig": {
35
+ "access": "public"
36
+ }
37
+ }