chainlesschain 0.37.9 → 0.37.11

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.
Files changed (84) hide show
  1. package/README.md +309 -19
  2. package/bin/chainlesschain.js +4 -0
  3. package/package.json +1 -1
  4. package/src/commands/a2a.js +374 -0
  5. package/src/commands/audit.js +286 -0
  6. package/src/commands/auth.js +387 -0
  7. package/src/commands/bi.js +240 -0
  8. package/src/commands/browse.js +184 -0
  9. package/src/commands/cowork.js +317 -0
  10. package/src/commands/did.js +376 -0
  11. package/src/commands/economy.js +375 -0
  12. package/src/commands/encrypt.js +233 -0
  13. package/src/commands/evolution.js +398 -0
  14. package/src/commands/export.js +125 -0
  15. package/src/commands/git.js +215 -0
  16. package/src/commands/hmemory.js +273 -0
  17. package/src/commands/hook.js +260 -0
  18. package/src/commands/import.js +259 -0
  19. package/src/commands/init.js +184 -0
  20. package/src/commands/instinct.js +202 -0
  21. package/src/commands/llm.js +155 -4
  22. package/src/commands/lowcode.js +320 -0
  23. package/src/commands/mcp.js +302 -0
  24. package/src/commands/memory.js +282 -0
  25. package/src/commands/note.js +187 -0
  26. package/src/commands/org.js +505 -0
  27. package/src/commands/p2p.js +274 -0
  28. package/src/commands/plugin.js +451 -0
  29. package/src/commands/sandbox.js +366 -0
  30. package/src/commands/search.js +237 -0
  31. package/src/commands/session.js +238 -0
  32. package/src/commands/skill.js +254 -201
  33. package/src/commands/sync.js +249 -0
  34. package/src/commands/tokens.js +214 -0
  35. package/src/commands/wallet.js +416 -0
  36. package/src/commands/workflow.js +359 -0
  37. package/src/commands/zkp.js +277 -0
  38. package/src/index.js +93 -1
  39. package/src/lib/a2a-protocol.js +371 -0
  40. package/src/lib/agent-coordinator.js +273 -0
  41. package/src/lib/agent-economy.js +369 -0
  42. package/src/lib/app-builder.js +377 -0
  43. package/src/lib/audit-logger.js +364 -0
  44. package/src/lib/bi-engine.js +299 -0
  45. package/src/lib/bm25-search.js +322 -0
  46. package/src/lib/browser-automation.js +216 -0
  47. package/src/lib/cowork/ab-comparator-cli.js +180 -0
  48. package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
  49. package/src/lib/cowork/debate-review-cli.js +144 -0
  50. package/src/lib/cowork/decision-kb-cli.js +153 -0
  51. package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
  52. package/src/lib/cowork-adapter.js +106 -0
  53. package/src/lib/crypto-manager.js +246 -0
  54. package/src/lib/did-manager.js +270 -0
  55. package/src/lib/ensure-utf8.js +59 -0
  56. package/src/lib/evolution-system.js +508 -0
  57. package/src/lib/git-integration.js +220 -0
  58. package/src/lib/hierarchical-memory.js +471 -0
  59. package/src/lib/hook-manager.js +387 -0
  60. package/src/lib/instinct-manager.js +190 -0
  61. package/src/lib/knowledge-exporter.js +302 -0
  62. package/src/lib/knowledge-importer.js +293 -0
  63. package/src/lib/llm-providers.js +325 -0
  64. package/src/lib/mcp-client.js +413 -0
  65. package/src/lib/memory-manager.js +211 -0
  66. package/src/lib/note-versioning.js +244 -0
  67. package/src/lib/org-manager.js +424 -0
  68. package/src/lib/p2p-manager.js +317 -0
  69. package/src/lib/pdf-parser.js +96 -0
  70. package/src/lib/permission-engine.js +374 -0
  71. package/src/lib/plan-mode.js +333 -0
  72. package/src/lib/plugin-manager.js +430 -0
  73. package/src/lib/project-detector.js +53 -0
  74. package/src/lib/response-cache.js +156 -0
  75. package/src/lib/sandbox-v2.js +503 -0
  76. package/src/lib/service-container.js +183 -0
  77. package/src/lib/session-manager.js +189 -0
  78. package/src/lib/skill-loader.js +274 -0
  79. package/src/lib/sync-manager.js +347 -0
  80. package/src/lib/token-tracker.js +200 -0
  81. package/src/lib/wallet-manager.js +348 -0
  82. package/src/lib/workflow-engine.js +503 -0
  83. package/src/lib/zkp-engine.js +241 -0
  84. package/src/repl/agent-repl.js +259 -124
@@ -0,0 +1,387 @@
1
+ /**
2
+ * Auth / RBAC commands
3
+ * chainlesschain auth roles|grant|revoke|check|permissions|users
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import { logger } from "../lib/logger.js";
8
+ import { bootstrap, shutdown } from "../runtime/bootstrap.js";
9
+ import {
10
+ getRoles,
11
+ createRole,
12
+ deleteRole,
13
+ grantRole,
14
+ revokeRole,
15
+ grantPermission,
16
+ revokePermission,
17
+ getUserPermissions,
18
+ checkPermission,
19
+ listUserRoles,
20
+ PERMISSION_SCOPES,
21
+ } from "../lib/permission-engine.js";
22
+
23
+ export function registerAuthCommand(program) {
24
+ const auth = program
25
+ .command("auth")
26
+ .description("RBAC permission management");
27
+
28
+ // auth roles
29
+ auth
30
+ .command("roles", { isDefault: true })
31
+ .description("List all roles")
32
+ .option("--json", "Output as JSON")
33
+ .action(async (options) => {
34
+ try {
35
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
36
+ if (!ctx.db) {
37
+ logger.error("Database not available");
38
+ process.exit(1);
39
+ }
40
+ const db = ctx.db.getDatabase();
41
+ const roles = getRoles(db);
42
+
43
+ if (options.json) {
44
+ console.log(JSON.stringify(roles, null, 2));
45
+ } else if (roles.length === 0) {
46
+ logger.info("No roles defined");
47
+ } else {
48
+ logger.log(chalk.bold(`Roles (${roles.length}):\n`));
49
+ for (const role of roles) {
50
+ const tag = role.isBuiltin
51
+ ? chalk.gray(" [built-in]")
52
+ : chalk.blue(" [custom]");
53
+ logger.log(
54
+ ` ${chalk.cyan(role.name.padEnd(15))}${tag} ${role.description || ""}`,
55
+ );
56
+ logger.log(
57
+ ` ${chalk.gray("permissions:")} ${role.permissions.join(", ")}`,
58
+ );
59
+ }
60
+ }
61
+
62
+ await shutdown();
63
+ } catch (err) {
64
+ logger.error(`Failed: ${err.message}`);
65
+ process.exit(1);
66
+ }
67
+ });
68
+
69
+ // auth create-role
70
+ auth
71
+ .command("create-role")
72
+ .description("Create a custom role")
73
+ .argument("<name>", "Role name")
74
+ .option("-d, --description <desc>", "Role description")
75
+ .option("-p, --permissions <perms>", "Comma-separated permissions")
76
+ .action(async (name, options) => {
77
+ try {
78
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
79
+ if (!ctx.db) {
80
+ logger.error("Database not available");
81
+ process.exit(1);
82
+ }
83
+ const db = ctx.db.getDatabase();
84
+
85
+ const perms = options.permissions
86
+ ? options.permissions.split(",").map((s) => s.trim())
87
+ : [];
88
+ const role = createRole(db, name, options.description, perms);
89
+
90
+ logger.success(`Role created: ${role.name}`);
91
+ if (role.permissions.length > 0) {
92
+ logger.log(
93
+ ` ${chalk.bold("Permissions:")} ${role.permissions.join(", ")}`,
94
+ );
95
+ }
96
+
97
+ await shutdown();
98
+ } catch (err) {
99
+ logger.error(`Failed: ${err.message}`);
100
+ process.exit(1);
101
+ }
102
+ });
103
+
104
+ // auth delete-role
105
+ auth
106
+ .command("delete-role")
107
+ .description("Delete a custom role")
108
+ .argument("<name>", "Role name")
109
+ .option("--force", "Skip confirmation")
110
+ .action(async (name, options) => {
111
+ try {
112
+ if (!options.force) {
113
+ const { confirm } = await import("@inquirer/prompts");
114
+ const ok = await confirm({
115
+ message: `Delete role "${name}"? All grants for this role will be removed.`,
116
+ });
117
+ if (!ok) {
118
+ logger.info("Cancelled");
119
+ return;
120
+ }
121
+ }
122
+
123
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
124
+ if (!ctx.db) {
125
+ logger.error("Database not available");
126
+ process.exit(1);
127
+ }
128
+ const db = ctx.db.getDatabase();
129
+ const ok = deleteRole(db, name);
130
+
131
+ if (ok) {
132
+ logger.success(`Role deleted: ${name}`);
133
+ } else {
134
+ logger.error(`Role not found or is built-in: ${name}`);
135
+ }
136
+
137
+ await shutdown();
138
+ } catch (err) {
139
+ logger.error(`Failed: ${err.message}`);
140
+ process.exit(1);
141
+ }
142
+ });
143
+
144
+ // auth grant
145
+ auth
146
+ .command("grant")
147
+ .description("Grant a role to a user")
148
+ .argument("<user-did>", "User DID")
149
+ .argument("<role>", "Role name")
150
+ .option("--expires <date>", "Expiration date (ISO 8601)")
151
+ .action(async (userDid, role, options) => {
152
+ try {
153
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
154
+ if (!ctx.db) {
155
+ logger.error("Database not available");
156
+ process.exit(1);
157
+ }
158
+ const db = ctx.db.getDatabase();
159
+ const grant = grantRole(db, userDid, role, null, options.expires);
160
+
161
+ logger.success(`Granted role "${role}" to ${userDid}`);
162
+ if (grant.expiresAt) {
163
+ logger.log(` ${chalk.bold("Expires:")} ${grant.expiresAt}`);
164
+ }
165
+
166
+ await shutdown();
167
+ } catch (err) {
168
+ logger.error(`Failed: ${err.message}`);
169
+ process.exit(1);
170
+ }
171
+ });
172
+
173
+ // auth revoke
174
+ auth
175
+ .command("revoke")
176
+ .description("Revoke a role from a user")
177
+ .argument("<user-did>", "User DID")
178
+ .argument("<role>", "Role name")
179
+ .action(async (userDid, role) => {
180
+ try {
181
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
182
+ if (!ctx.db) {
183
+ logger.error("Database not available");
184
+ process.exit(1);
185
+ }
186
+ const db = ctx.db.getDatabase();
187
+ const ok = revokeRole(db, userDid, role);
188
+
189
+ if (ok) {
190
+ logger.success(`Revoked role "${role}" from ${userDid}`);
191
+ } else {
192
+ logger.error("Grant not found");
193
+ }
194
+
195
+ await shutdown();
196
+ } catch (err) {
197
+ logger.error(`Failed: ${err.message}`);
198
+ process.exit(1);
199
+ }
200
+ });
201
+
202
+ // auth grant-permission (direct permission)
203
+ auth
204
+ .command("grant-permission")
205
+ .description("Grant a direct permission to a user")
206
+ .argument("<user-did>", "User DID")
207
+ .argument("<permission>", "Permission scope (e.g., note:read)")
208
+ .action(async (userDid, permission) => {
209
+ try {
210
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
211
+ if (!ctx.db) {
212
+ logger.error("Database not available");
213
+ process.exit(1);
214
+ }
215
+ const db = ctx.db.getDatabase();
216
+ grantPermission(db, userDid, permission);
217
+ logger.success(`Granted permission "${permission}" to ${userDid}`);
218
+
219
+ await shutdown();
220
+ } catch (err) {
221
+ logger.error(`Failed: ${err.message}`);
222
+ process.exit(1);
223
+ }
224
+ });
225
+
226
+ // auth revoke-permission
227
+ auth
228
+ .command("revoke-permission")
229
+ .description("Revoke a direct permission from a user")
230
+ .argument("<user-did>", "User DID")
231
+ .argument("<permission>", "Permission scope")
232
+ .action(async (userDid, permission) => {
233
+ try {
234
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
235
+ if (!ctx.db) {
236
+ logger.error("Database not available");
237
+ process.exit(1);
238
+ }
239
+ const db = ctx.db.getDatabase();
240
+ const ok = revokePermission(db, userDid, permission);
241
+
242
+ if (ok) {
243
+ logger.success(`Revoked permission "${permission}" from ${userDid}`);
244
+ } else {
245
+ logger.error("Permission grant not found");
246
+ }
247
+
248
+ await shutdown();
249
+ } catch (err) {
250
+ logger.error(`Failed: ${err.message}`);
251
+ process.exit(1);
252
+ }
253
+ });
254
+
255
+ // auth check
256
+ auth
257
+ .command("check")
258
+ .description("Check if a user has a specific permission")
259
+ .argument("<user-did>", "User DID")
260
+ .argument("<permission>", "Permission to check")
261
+ .option("--json", "Output as JSON")
262
+ .action(async (userDid, permission, options) => {
263
+ try {
264
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
265
+ if (!ctx.db) {
266
+ logger.error("Database not available");
267
+ process.exit(1);
268
+ }
269
+ const db = ctx.db.getDatabase();
270
+ const allowed = checkPermission(db, userDid, permission);
271
+
272
+ if (options.json) {
273
+ console.log(
274
+ JSON.stringify({ userDid, permission, allowed }, null, 2),
275
+ );
276
+ } else if (allowed) {
277
+ logger.success(
278
+ `${chalk.green("ALLOWED")} — ${userDid} has permission: ${permission}`,
279
+ );
280
+ } else {
281
+ logger.log(
282
+ `${chalk.red("DENIED")} — ${userDid} does not have permission: ${permission}`,
283
+ );
284
+ }
285
+
286
+ await shutdown();
287
+ } catch (err) {
288
+ logger.error(`Failed: ${err.message}`);
289
+ process.exit(1);
290
+ }
291
+ });
292
+
293
+ // auth permissions
294
+ auth
295
+ .command("permissions")
296
+ .description("Show all permissions for a user")
297
+ .argument("<user-did>", "User DID")
298
+ .option("--json", "Output as JSON")
299
+ .action(async (userDid, options) => {
300
+ try {
301
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
302
+ if (!ctx.db) {
303
+ logger.error("Database not available");
304
+ process.exit(1);
305
+ }
306
+ const db = ctx.db.getDatabase();
307
+ const perms = getUserPermissions(db, userDid);
308
+
309
+ if (options.json) {
310
+ console.log(JSON.stringify(perms, null, 2));
311
+ } else {
312
+ logger.log(chalk.bold(`Permissions for ${chalk.cyan(userDid)}:\n`));
313
+ if (perms.isAdmin) {
314
+ logger.log(` ${chalk.green("ADMIN")} — Full access (wildcard *)`);
315
+ }
316
+ if (perms.roles.length > 0) {
317
+ logger.log(` ${chalk.bold("Roles:")} ${perms.roles.join(", ")}`);
318
+ }
319
+ if (perms.directPermissions.length > 0) {
320
+ logger.log(
321
+ ` ${chalk.bold("Direct:")} ${perms.directPermissions.join(", ")}`,
322
+ );
323
+ }
324
+ if (perms.effectivePermissions.length > 0) {
325
+ logger.log(
326
+ ` ${chalk.bold("Effective:")} ${perms.effectivePermissions.join(", ")}`,
327
+ );
328
+ } else {
329
+ logger.log(` ${chalk.gray("No permissions assigned")}`);
330
+ }
331
+ }
332
+
333
+ await shutdown();
334
+ } catch (err) {
335
+ logger.error(`Failed: ${err.message}`);
336
+ process.exit(1);
337
+ }
338
+ });
339
+
340
+ // auth users
341
+ auth
342
+ .command("users")
343
+ .description("List all users with role assignments")
344
+ .option("--json", "Output as JSON")
345
+ .action(async (options) => {
346
+ try {
347
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
348
+ if (!ctx.db) {
349
+ logger.error("Database not available");
350
+ process.exit(1);
351
+ }
352
+ const db = ctx.db.getDatabase();
353
+ const users = listUserRoles(db);
354
+
355
+ if (options.json) {
356
+ console.log(JSON.stringify(users, null, 2));
357
+ } else if (users.length === 0) {
358
+ logger.info("No role assignments yet");
359
+ } else {
360
+ logger.log(chalk.bold(`Users with roles (${users.length}):\n`));
361
+ for (const u of users) {
362
+ logger.log(` ${chalk.cyan(u.userDid)}`);
363
+ logger.log(` ${chalk.gray("roles:")} ${u.roles.join(", ")}`);
364
+ }
365
+ }
366
+
367
+ await shutdown();
368
+ } catch (err) {
369
+ logger.error(`Failed: ${err.message}`);
370
+ process.exit(1);
371
+ }
372
+ });
373
+
374
+ // auth scopes
375
+ auth
376
+ .command("scopes")
377
+ .description("List all available permission scopes")
378
+ .action(async () => {
379
+ logger.log(chalk.bold("Available Permission Scopes:\n"));
380
+ for (const scope of PERMISSION_SCOPES) {
381
+ const [resource, action] = scope.split(":");
382
+ logger.log(
383
+ ` ${chalk.cyan(resource.padEnd(12))}:${chalk.white(action)}`,
384
+ );
385
+ }
386
+ });
387
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * BI commands
3
+ * chainlesschain bi query|dashboard|report|schedule|anomaly|predict|templates
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import { logger } from "../lib/logger.js";
8
+ import { bootstrap, shutdown } from "../runtime/bootstrap.js";
9
+ import {
10
+ ensureBITables,
11
+ nlQuery,
12
+ createDashboard,
13
+ generateReport,
14
+ scheduleReport,
15
+ detectAnomaly,
16
+ predictTrend,
17
+ listTemplates,
18
+ } from "../lib/bi-engine.js";
19
+
20
+ export function registerBiCommand(program) {
21
+ const bi = program
22
+ .command("bi")
23
+ .description(
24
+ "Business intelligence — queries, dashboards, reports, analytics",
25
+ );
26
+
27
+ // bi query
28
+ bi.command("query <question>")
29
+ .description("Natural-language query (NL→SQL)")
30
+ .option("--json", "Output as JSON")
31
+ .action(async (question, options) => {
32
+ try {
33
+ const result = nlQuery(question);
34
+ if (options.json) {
35
+ console.log(JSON.stringify(result, null, 2));
36
+ } else {
37
+ logger.success("Query translated");
38
+ logger.log(
39
+ ` ${chalk.bold("SQL:")} ${chalk.cyan(result.generatedSQL)}`,
40
+ );
41
+ logger.log(` ${chalk.bold("Rows:")} ${result.rowCount}`);
42
+ logger.log(` ${chalk.bold("Visual:")} ${result.visualization.type}`);
43
+ }
44
+ } catch (err) {
45
+ logger.error(`Failed: ${err.message}`);
46
+ process.exit(1);
47
+ }
48
+ });
49
+
50
+ // bi dashboard
51
+ bi.command("dashboard <name>")
52
+ .description("Create a dashboard")
53
+ .option("--widgets <json>", "Widgets as JSON array")
54
+ .option("--layout <json>", "Layout as JSON")
55
+ .option("--json", "Output as JSON")
56
+ .action(async (name, options) => {
57
+ try {
58
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
59
+ if (!ctx.db) {
60
+ logger.error("Database not available");
61
+ process.exit(1);
62
+ }
63
+ const db = ctx.db.getDatabase();
64
+ ensureBITables(db);
65
+
66
+ const widgets = options.widgets ? JSON.parse(options.widgets) : [];
67
+ const layout = options.layout ? JSON.parse(options.layout) : undefined;
68
+ const dashboard = createDashboard(db, name, widgets, layout);
69
+
70
+ if (options.json) {
71
+ console.log(JSON.stringify(dashboard, null, 2));
72
+ } else {
73
+ logger.success(`Dashboard created: ${chalk.cyan(name)}`);
74
+ logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(dashboard.id)}`);
75
+ logger.log(` ${chalk.bold("Widgets:")} ${dashboard.widgets.length}`);
76
+ }
77
+
78
+ await shutdown();
79
+ } catch (err) {
80
+ logger.error(`Failed: ${err.message}`);
81
+ process.exit(1);
82
+ }
83
+ });
84
+
85
+ // bi report
86
+ bi.command("report <name>")
87
+ .description("Generate a report")
88
+ .option("--format <pdf|excel>", "Report format", "pdf")
89
+ .option("--sections <csv>", "Comma-separated section names")
90
+ .option("--json", "Output as JSON")
91
+ .action(async (name, options) => {
92
+ try {
93
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
94
+ if (!ctx.db) {
95
+ logger.error("Database not available");
96
+ process.exit(1);
97
+ }
98
+ const db = ctx.db.getDatabase();
99
+ ensureBITables(db);
100
+
101
+ const sections = options.sections
102
+ ? options.sections.split(",").map((s) => s.trim())
103
+ : undefined;
104
+ const report = generateReport(db, name, {
105
+ format: options.format,
106
+ sections,
107
+ });
108
+
109
+ if (options.json) {
110
+ console.log(JSON.stringify(report, null, 2));
111
+ } else {
112
+ logger.success(`Report generated: ${chalk.cyan(name)}`);
113
+ logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(report.id)}`);
114
+ logger.log(` ${chalk.bold("Format:")} ${report.format}`);
115
+ logger.log(` ${chalk.bold("Sections:")} ${report.sections.length}`);
116
+ }
117
+
118
+ await shutdown();
119
+ } catch (err) {
120
+ logger.error(`Failed: ${err.message}`);
121
+ process.exit(1);
122
+ }
123
+ });
124
+
125
+ // bi schedule
126
+ bi.command("schedule <report-id> <cron>")
127
+ .description("Schedule a report for recurring generation")
128
+ .option("--recipients <csv>", "Comma-separated recipient emails")
129
+ .action(async (reportId, cron, options) => {
130
+ try {
131
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
132
+ if (!ctx.db) {
133
+ logger.error("Database not available");
134
+ process.exit(1);
135
+ }
136
+ const db = ctx.db.getDatabase();
137
+ ensureBITables(db);
138
+
139
+ const recipients = options.recipients
140
+ ? options.recipients.split(",").map((s) => s.trim())
141
+ : [];
142
+ const schedule = scheduleReport(db, reportId, cron, recipients);
143
+ logger.success("Report scheduled");
144
+ logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(schedule.id)}`);
145
+ logger.log(` ${chalk.bold("Cron:")} ${schedule.cron}`);
146
+ logger.log(
147
+ ` ${chalk.bold("Recipients:")} ${schedule.recipients.length}`,
148
+ );
149
+
150
+ await shutdown();
151
+ } catch (err) {
152
+ logger.error(`Failed: ${err.message}`);
153
+ process.exit(1);
154
+ }
155
+ });
156
+
157
+ // bi anomaly
158
+ bi.command("anomaly")
159
+ .description("Detect anomalies in data using Z-score")
160
+ .option("--data <json>", "Data as JSON array of numbers")
161
+ .option("--threshold <n>", "Z-score threshold", "2")
162
+ .option("--json", "Output as JSON")
163
+ .action(async (options) => {
164
+ try {
165
+ const data = options.data ? JSON.parse(options.data) : [];
166
+ const result = detectAnomaly(data, {
167
+ threshold: parseFloat(options.threshold),
168
+ });
169
+
170
+ if (options.json) {
171
+ console.log(JSON.stringify(result, null, 2));
172
+ } else {
173
+ logger.log(` ${chalk.bold("Mean:")} ${result.mean.toFixed(2)}`);
174
+ logger.log(` ${chalk.bold("Std Dev:")} ${result.std.toFixed(2)}`);
175
+ logger.log(` ${chalk.bold("Threshold:")} ${result.threshold}`);
176
+ logger.log(
177
+ ` ${chalk.bold("Anomalies:")} ${result.anomalies.length}`,
178
+ );
179
+ for (const a of result.anomalies) {
180
+ logger.log(
181
+ ` [${a.index}] value=${a.value} z=${a.zScore.toFixed(2)}`,
182
+ );
183
+ }
184
+ }
185
+ } catch (err) {
186
+ logger.error(`Failed: ${err.message}`);
187
+ process.exit(1);
188
+ }
189
+ });
190
+
191
+ // bi predict
192
+ bi.command("predict")
193
+ .description("Predict trend using linear regression")
194
+ .option("--data <json>", "Data as JSON array of numbers")
195
+ .option("--periods <n>", "Number of periods to predict", "3")
196
+ .option("--json", "Output as JSON")
197
+ .action(async (options) => {
198
+ try {
199
+ const data = options.data ? JSON.parse(options.data) : [];
200
+ const result = predictTrend(data, parseInt(options.periods));
201
+
202
+ if (options.json) {
203
+ console.log(JSON.stringify(result, null, 2));
204
+ } else {
205
+ logger.log(
206
+ ` ${chalk.bold("Trend:")} ${chalk.cyan(result.trend)}`,
207
+ );
208
+ logger.log(` ${chalk.bold("Slope:")} ${result.slope}`);
209
+ logger.log(
210
+ ` ${chalk.bold("Predictions:")} ${result.predictions.join(", ")}`,
211
+ );
212
+ }
213
+ } catch (err) {
214
+ logger.error(`Failed: ${err.message}`);
215
+ process.exit(1);
216
+ }
217
+ });
218
+
219
+ // bi templates
220
+ bi.command("templates")
221
+ .description("List available BI templates")
222
+ .option("--json", "Output as JSON")
223
+ .action(async (options) => {
224
+ try {
225
+ const templates = listTemplates();
226
+ if (options.json) {
227
+ console.log(JSON.stringify(templates, null, 2));
228
+ } else {
229
+ for (const t of templates) {
230
+ logger.log(
231
+ ` ${chalk.cyan(t.id)} ${chalk.bold(t.name)} — ${t.description}`,
232
+ );
233
+ }
234
+ }
235
+ } catch (err) {
236
+ logger.error(`Failed: ${err.message}`);
237
+ process.exit(1);
238
+ }
239
+ });
240
+ }