@hasna/microservices 0.0.9 → 0.0.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 (100) hide show
  1. package/bin/index.js +236 -36
  2. package/bin/mcp.js +153 -4
  3. package/dist/index.js +120 -3
  4. package/microservices/microservice-analytics/package.json +27 -0
  5. package/microservices/microservice-analytics/src/cli/index.ts +373 -0
  6. package/microservices/microservice-analytics/src/db/analytics.ts +564 -0
  7. package/microservices/microservice-analytics/src/db/database.ts +93 -0
  8. package/microservices/microservice-analytics/src/db/migrations.ts +50 -0
  9. package/microservices/microservice-analytics/src/index.ts +37 -0
  10. package/microservices/microservice-analytics/src/mcp/index.ts +334 -0
  11. package/microservices/microservice-assets/package.json +27 -0
  12. package/microservices/microservice-assets/src/cli/index.ts +375 -0
  13. package/microservices/microservice-assets/src/db/assets.ts +370 -0
  14. package/microservices/microservice-assets/src/db/database.ts +93 -0
  15. package/microservices/microservice-assets/src/db/migrations.ts +51 -0
  16. package/microservices/microservice-assets/src/index.ts +32 -0
  17. package/microservices/microservice-assets/src/mcp/index.ts +346 -0
  18. package/microservices/microservice-compliance/package.json +27 -0
  19. package/microservices/microservice-compliance/src/cli/index.ts +467 -0
  20. package/microservices/microservice-compliance/src/db/compliance.ts +633 -0
  21. package/microservices/microservice-compliance/src/db/database.ts +93 -0
  22. package/microservices/microservice-compliance/src/db/migrations.ts +63 -0
  23. package/microservices/microservice-compliance/src/index.ts +46 -0
  24. package/microservices/microservice-compliance/src/mcp/index.ts +438 -0
  25. package/microservices/microservice-habits/package.json +27 -0
  26. package/microservices/microservice-habits/src/cli/index.ts +315 -0
  27. package/microservices/microservice-habits/src/db/database.ts +93 -0
  28. package/microservices/microservice-habits/src/db/habits.ts +451 -0
  29. package/microservices/microservice-habits/src/db/migrations.ts +46 -0
  30. package/microservices/microservice-habits/src/index.ts +31 -0
  31. package/microservices/microservice-habits/src/mcp/index.ts +313 -0
  32. package/microservices/microservice-health/package.json +27 -0
  33. package/microservices/microservice-health/src/cli/index.ts +484 -0
  34. package/microservices/microservice-health/src/db/database.ts +93 -0
  35. package/microservices/microservice-health/src/db/health.ts +708 -0
  36. package/microservices/microservice-health/src/db/migrations.ts +70 -0
  37. package/microservices/microservice-health/src/index.ts +63 -0
  38. package/microservices/microservice-health/src/mcp/index.ts +437 -0
  39. package/microservices/microservice-leads/package.json +27 -0
  40. package/microservices/microservice-leads/src/cli/index.ts +596 -0
  41. package/microservices/microservice-leads/src/db/database.ts +93 -0
  42. package/microservices/microservice-leads/src/db/leads.ts +520 -0
  43. package/microservices/microservice-leads/src/db/lists.ts +151 -0
  44. package/microservices/microservice-leads/src/db/migrations.ts +93 -0
  45. package/microservices/microservice-leads/src/index.ts +65 -0
  46. package/microservices/microservice-leads/src/lib/enrichment.ts +202 -0
  47. package/microservices/microservice-leads/src/lib/scoring.ts +134 -0
  48. package/microservices/microservice-leads/src/mcp/index.ts +533 -0
  49. package/microservices/microservice-notifications/package.json +27 -0
  50. package/microservices/microservice-notifications/src/cli/index.ts +349 -0
  51. package/microservices/microservice-notifications/src/db/database.ts +93 -0
  52. package/microservices/microservice-notifications/src/db/migrations.ts +62 -0
  53. package/microservices/microservice-notifications/src/db/notifications.ts +509 -0
  54. package/microservices/microservice-notifications/src/index.ts +41 -0
  55. package/microservices/microservice-notifications/src/mcp/index.ts +422 -0
  56. package/microservices/microservice-products/package.json +27 -0
  57. package/microservices/microservice-products/src/cli/index.ts +416 -0
  58. package/microservices/microservice-products/src/db/categories.ts +154 -0
  59. package/microservices/microservice-products/src/db/database.ts +93 -0
  60. package/microservices/microservice-products/src/db/migrations.ts +58 -0
  61. package/microservices/microservice-products/src/db/pricing-tiers.ts +66 -0
  62. package/microservices/microservice-products/src/db/products.ts +452 -0
  63. package/microservices/microservice-products/src/index.ts +53 -0
  64. package/microservices/microservice-products/src/mcp/index.ts +453 -0
  65. package/microservices/microservice-projects/package.json +27 -0
  66. package/microservices/microservice-projects/src/cli/index.ts +480 -0
  67. package/microservices/microservice-projects/src/db/database.ts +93 -0
  68. package/microservices/microservice-projects/src/db/migrations.ts +65 -0
  69. package/microservices/microservice-projects/src/db/projects.ts +715 -0
  70. package/microservices/microservice-projects/src/index.ts +57 -0
  71. package/microservices/microservice-projects/src/mcp/index.ts +501 -0
  72. package/microservices/microservice-proposals/package.json +27 -0
  73. package/microservices/microservice-proposals/src/cli/index.ts +400 -0
  74. package/microservices/microservice-proposals/src/db/database.ts +93 -0
  75. package/microservices/microservice-proposals/src/db/migrations.ts +52 -0
  76. package/microservices/microservice-proposals/src/db/proposals.ts +532 -0
  77. package/microservices/microservice-proposals/src/index.ts +37 -0
  78. package/microservices/microservice-proposals/src/mcp/index.ts +375 -0
  79. package/microservices/microservice-reading/package.json +27 -0
  80. package/microservices/microservice-reading/src/cli/index.ts +464 -0
  81. package/microservices/microservice-reading/src/db/database.ts +93 -0
  82. package/microservices/microservice-reading/src/db/migrations.ts +59 -0
  83. package/microservices/microservice-reading/src/db/reading.ts +524 -0
  84. package/microservices/microservice-reading/src/index.ts +51 -0
  85. package/microservices/microservice-reading/src/mcp/index.ts +368 -0
  86. package/microservices/microservice-travel/package.json +27 -0
  87. package/microservices/microservice-travel/src/cli/index.ts +505 -0
  88. package/microservices/microservice-travel/src/db/database.ts +93 -0
  89. package/microservices/microservice-travel/src/db/migrations.ts +77 -0
  90. package/microservices/microservice-travel/src/db/travel.ts +802 -0
  91. package/microservices/microservice-travel/src/index.ts +60 -0
  92. package/microservices/microservice-travel/src/mcp/index.ts +495 -0
  93. package/microservices/microservice-wiki/package.json +27 -0
  94. package/microservices/microservice-wiki/src/cli/index.ts +345 -0
  95. package/microservices/microservice-wiki/src/db/database.ts +93 -0
  96. package/microservices/microservice-wiki/src/db/migrations.ts +55 -0
  97. package/microservices/microservice-wiki/src/db/wiki.ts +395 -0
  98. package/microservices/microservice-wiki/src/index.ts +32 -0
  99. package/microservices/microservice-wiki/src/mcp/index.ts +344 -0
  100. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,7 +7,8 @@ var CATEGORIES = [
7
7
  "Productivity",
8
8
  "HR",
9
9
  "Analytics",
10
- "Management"
10
+ "Management",
11
+ "Personal"
11
12
  ];
12
13
  var MICROSERVICES = [
13
14
  {
@@ -73,6 +74,20 @@ var MICROSERVICES = [
73
74
  category: "CRM",
74
75
  tags: ["social-media", "posts", "scheduling", "engagement", "analytics", "x", "linkedin", "instagram"]
75
76
  },
77
+ {
78
+ name: "leads",
79
+ displayName: "Leads",
80
+ description: "Lead generation, storage, scoring, and data enrichment with pipeline tracking, bulk import/export, and deduplication",
81
+ category: "CRM",
82
+ tags: ["leads", "lead-generation", "scoring", "enrichment", "pipeline", "dedup", "import", "export"]
83
+ },
84
+ {
85
+ name: "proposals",
86
+ displayName: "Proposals",
87
+ description: "Create, send, track, and convert proposals with templates, expiry tracking, and conversion analytics",
88
+ category: "CRM",
89
+ tags: ["proposals", "quotes", "estimates", "sales", "clients", "conversion"]
90
+ },
76
91
  {
77
92
  name: "inventory",
78
93
  displayName: "Inventory",
@@ -101,6 +116,34 @@ var MICROSERVICES = [
101
116
  category: "Operations",
102
117
  tags: ["domains", "dns", "ssl", "registrar", "nameservers", "whois", "certificates"]
103
118
  },
119
+ {
120
+ name: "products",
121
+ displayName: "Products",
122
+ description: "Product catalog with categories, pricing tiers, variants, bulk import/export, and inventory status tracking",
123
+ category: "Operations",
124
+ tags: ["products", "catalog", "pricing", "categories", "sku", "inventory", "import", "export"]
125
+ },
126
+ {
127
+ name: "notifications",
128
+ displayName: "Notifications",
129
+ description: "Send notifications across channels (email, Slack, SMS, webhook, in-app) with rules, templates, variable substitution, and event processing",
130
+ category: "Operations",
131
+ tags: ["notifications", "alerts", "email", "slack", "sms", "webhook", "in-app", "templates", "rules", "events"]
132
+ },
133
+ {
134
+ name: "projects",
135
+ displayName: "Projects",
136
+ description: "Project management with milestones, deliverables, budget tracking, timelines, and progress reporting",
137
+ category: "Operations",
138
+ tags: ["projects", "milestones", "deliverables", "budget", "timeline", "planning", "tracking"]
139
+ },
140
+ {
141
+ name: "compliance",
142
+ displayName: "Compliance",
143
+ description: "Compliance management with requirements tracking, license management, and audit scheduling across regulatory frameworks (GDPR, SOC2, HIPAA, PCI, ISO 27001)",
144
+ category: "Operations",
145
+ tags: ["compliance", "gdpr", "soc2", "hipaa", "pci", "iso27001", "audit", "licenses", "requirements", "regulatory"]
146
+ },
104
147
  {
105
148
  name: "notes",
106
149
  displayName: "Notes",
@@ -156,6 +199,55 @@ var MICROSERVICES = [
156
199
  description: "Transcribe audio and video from files and URLs (YouTube, Vimeo, Wistia, etc.) using ElevenLabs or OpenAI Whisper",
157
200
  category: "Productivity",
158
201
  tags: ["transcription", "audio", "video", "youtube", "vimeo", "wistia", "elevenlabs", "openai", "whisper", "speech-to-text"]
202
+ },
203
+ {
204
+ name: "wiki",
205
+ displayName: "Wiki",
206
+ description: "Wiki with pages, version history, internal links, and hierarchical page trees",
207
+ category: "Productivity",
208
+ tags: ["wiki", "pages", "knowledge-base", "versioning", "links", "markdown"]
209
+ },
210
+ {
211
+ name: "assets",
212
+ displayName: "Assets",
213
+ description: "Digital asset management with collections, tagging, metadata, and type-based organization",
214
+ category: "Productivity",
215
+ tags: ["assets", "files", "collections", "media", "images", "documents", "digital-assets"]
216
+ },
217
+ {
218
+ name: "habits",
219
+ displayName: "Habits",
220
+ description: "Habit tracking with streaks, completions, and analytics \u2014 daily, weekly, and monthly habits with completion rates and reports",
221
+ category: "Personal",
222
+ tags: ["habits", "streaks", "tracking", "completions", "daily", "weekly", "goals", "wellness"]
223
+ },
224
+ {
225
+ name: "health",
226
+ displayName: "Health",
227
+ description: "Health tracking with metrics, medications, appointments, and fitness logs",
228
+ category: "Personal",
229
+ tags: ["health", "metrics", "medications", "appointments", "fitness", "wellness", "medical"]
230
+ },
231
+ {
232
+ name: "reading",
233
+ displayName: "Reading",
234
+ description: "Reading tracker with books, highlights, reading sessions, pace analytics, and progress tracking",
235
+ category: "Personal",
236
+ tags: ["reading", "books", "highlights", "sessions", "tracking", "pace", "library"]
237
+ },
238
+ {
239
+ name: "travel",
240
+ displayName: "Travel",
241
+ description: "Travel management with trips, bookings, documents, loyalty programs, and budget tracking",
242
+ category: "Personal",
243
+ tags: ["travel", "trips", "bookings", "flights", "hotels", "loyalty", "documents", "budget"]
244
+ },
245
+ {
246
+ name: "analytics",
247
+ displayName: "Analytics",
248
+ description: "Business analytics with KPIs, dashboards, reports, and AI-powered executive summaries",
249
+ category: "Analytics",
250
+ tags: ["analytics", "kpis", "dashboards", "reports", "metrics", "business-intelligence", "executive-summary"]
159
251
  }
160
252
  ];
161
253
  function getMicroservice(name) {
@@ -270,6 +362,28 @@ function resolveSourceDir() {
270
362
  return fromBin;
271
363
  }
272
364
  var SOURCE_DIR = resolveSourceDir();
365
+ var PRESERVED_DB_FILES = new Set(["data.db", "data.db-wal", "data.db-shm"]);
366
+ function getCliCandidates(baseDir) {
367
+ return [
368
+ join2(baseDir, "src", "cli", "index.ts"),
369
+ join2(baseDir, "cli.ts"),
370
+ join2(baseDir, "src", "index.ts")
371
+ ];
372
+ }
373
+ function hasInstalledSource(baseDir) {
374
+ return getCliCandidates(baseDir).some((candidate) => existsSync2(candidate));
375
+ }
376
+ function clearInstalledSource(baseDir) {
377
+ if (!existsSync2(baseDir)) {
378
+ return;
379
+ }
380
+ for (const entry of readdirSync(baseDir)) {
381
+ if (PRESERVED_DB_FILES.has(entry)) {
382
+ continue;
383
+ }
384
+ rmSync(join2(baseDir, entry), { recursive: true, force: true });
385
+ }
386
+ }
273
387
  function getMicroservicePath(name) {
274
388
  const msName = name.startsWith("microservice-") ? name : `microservice-${name}`;
275
389
  return join2(SOURCE_DIR, msName);
@@ -310,6 +424,9 @@ function installMicroservice(name, options = {}) {
310
424
  if (!existsSync2(targetDir)) {
311
425
  mkdirSync2(targetDir, { recursive: true });
312
426
  }
427
+ if (existsSync2(destPath) && overwrite) {
428
+ clearInstalledSource(destPath);
429
+ }
313
430
  cpSync(sourcePath, destPath, { recursive: true });
314
431
  return {
315
432
  microservice: cleanName,
@@ -334,7 +451,7 @@ function getInstalledMicroservices(targetDir) {
334
451
  }
335
452
  return readdirSync(dir).filter((f) => {
336
453
  const fullPath = join2(dir, f);
337
- return f.startsWith("microservice-") && statSync(fullPath).isDirectory();
454
+ return f.startsWith("microservice-") && statSync(fullPath).isDirectory() && hasInstalledSource(fullPath);
338
455
  }).map((f) => f.replace("microservice-", ""));
339
456
  }
340
457
  function removeMicroservice(name, options = {}) {
@@ -363,7 +480,7 @@ function getMicroserviceStatus(name) {
363
480
  const msName = name.startsWith("microservice-") ? name : `microservice-${name}`;
364
481
  const msPath = join2(dir, msName);
365
482
  const dbPath = join2(msPath, "data.db");
366
- const installed = existsSync2(msPath);
483
+ const installed = hasInstalledSource(msPath);
367
484
  const hasDatabase = existsSync2(dbPath);
368
485
  let dbSizeBytes = 0;
369
486
  if (hasDatabase) {
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@hasna/microservice-analytics",
3
+ "version": "0.0.1",
4
+ "description": "Business analytics microservice with KPIs, dashboards, reports, and AI-powered executive summaries",
5
+ "type": "module",
6
+ "bin": {
7
+ "microservice-analytics": "./src/cli/index.ts",
8
+ "microservice-analytics-mcp": "./src/mcp/index.ts"
9
+ },
10
+ "exports": {
11
+ ".": "./src/index.ts"
12
+ },
13
+ "scripts": {
14
+ "dev": "bun run ./src/cli/index.ts",
15
+ "test": "bun test"
16
+ },
17
+ "dependencies": {
18
+ "@modelcontextprotocol/sdk": "^1.26.0",
19
+ "commander": "^12.1.0",
20
+ "zod": "^3.24.0"
21
+ },
22
+ "license": "Apache-2.0",
23
+ "publishConfig": {
24
+ "registry": "https://registry.npmjs.org",
25
+ "access": "public"
26
+ }
27
+ }
@@ -0,0 +1,373 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { Command } from "commander";
4
+ import {
5
+ recordKpi,
6
+ getKpi,
7
+ getKpiTrend,
8
+ listKpis,
9
+ getLatestKpis,
10
+ createDashboard,
11
+ getDashboard,
12
+ listDashboards,
13
+ updateDashboard,
14
+ deleteDashboard,
15
+ generateReport,
16
+ getReport,
17
+ listReports,
18
+ getBusinessHealth,
19
+ generateExecutiveSummary,
20
+ } from "../db/analytics.js";
21
+
22
+ const program = new Command();
23
+
24
+ program
25
+ .name("microservice-analytics")
26
+ .description("Business analytics microservice")
27
+ .version("0.0.1");
28
+
29
+ // --- KPIs ---
30
+
31
+ const kpiCmd = program
32
+ .command("kpi")
33
+ .description("KPI management");
34
+
35
+ kpiCmd
36
+ .command("record")
37
+ .description("Record a KPI value")
38
+ .requiredOption("--name <name>", "KPI name")
39
+ .requiredOption("--value <value>", "KPI value")
40
+ .option("--category <category>", "Category")
41
+ .option("--source <service>", "Source service")
42
+ .option("--period <period>", "Period (e.g. 2024-Q1)")
43
+ .option("--json", "Output as JSON", false)
44
+ .action((opts) => {
45
+ const kpi = recordKpi({
46
+ name: opts.name,
47
+ value: parseFloat(opts.value),
48
+ category: opts.category,
49
+ source_service: opts.source,
50
+ period: opts.period,
51
+ });
52
+
53
+ if (opts.json) {
54
+ console.log(JSON.stringify(kpi, null, 2));
55
+ } else {
56
+ console.log(`Recorded KPI: ${kpi.name} = ${kpi.value} (${kpi.id})`);
57
+ }
58
+ });
59
+
60
+ kpiCmd
61
+ .command("get")
62
+ .description("Get the latest value for a KPI")
63
+ .argument("<name>", "KPI name")
64
+ .option("--period <period>", "Filter by period")
65
+ .option("--json", "Output as JSON", false)
66
+ .action((name, opts) => {
67
+ const kpi = getKpi(name, opts.period);
68
+ if (!kpi) {
69
+ console.error(`KPI '${name}' not found.`);
70
+ process.exit(1);
71
+ }
72
+
73
+ if (opts.json) {
74
+ console.log(JSON.stringify(kpi, null, 2));
75
+ } else {
76
+ console.log(`${kpi.name}: ${kpi.value}`);
77
+ if (kpi.category) console.log(` Category: ${kpi.category}`);
78
+ if (kpi.period) console.log(` Period: ${kpi.period}`);
79
+ if (kpi.source_service) console.log(` Source: ${kpi.source_service}`);
80
+ console.log(` Recorded: ${kpi.recorded_at}`);
81
+ }
82
+ });
83
+
84
+ kpiCmd
85
+ .command("trend")
86
+ .description("Get KPI trend over time")
87
+ .argument("<name>", "KPI name")
88
+ .option("--days <n>", "Number of days to look back", "30")
89
+ .option("--json", "Output as JSON", false)
90
+ .action((name, opts) => {
91
+ const trend = getKpiTrend(name, parseInt(opts.days));
92
+
93
+ if (opts.json) {
94
+ console.log(JSON.stringify(trend, null, 2));
95
+ } else {
96
+ if (trend.length === 0) {
97
+ console.log(`No trend data for '${name}' in the last ${opts.days} days.`);
98
+ return;
99
+ }
100
+ console.log(`Trend for ${name} (last ${opts.days} days):`);
101
+ for (const kpi of trend) {
102
+ console.log(` ${kpi.recorded_at}: ${kpi.value}`);
103
+ }
104
+ }
105
+ });
106
+
107
+ kpiCmd
108
+ .command("list")
109
+ .description("List KPIs")
110
+ .option("--category <category>", "Filter by category")
111
+ .option("--source <service>", "Filter by source service")
112
+ .option("--limit <n>", "Limit results")
113
+ .option("--latest", "Show only latest value per KPI", false)
114
+ .option("--json", "Output as JSON", false)
115
+ .action((opts) => {
116
+ const kpis = opts.latest
117
+ ? getLatestKpis()
118
+ : listKpis({
119
+ category: opts.category,
120
+ source_service: opts.source,
121
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
122
+ });
123
+
124
+ if (opts.json) {
125
+ console.log(JSON.stringify(kpis, null, 2));
126
+ } else {
127
+ if (kpis.length === 0) {
128
+ console.log("No KPIs found.");
129
+ return;
130
+ }
131
+ for (const kpi of kpis) {
132
+ const cat = kpi.category ? ` [${kpi.category}]` : "";
133
+ console.log(` ${kpi.name}: ${kpi.value}${cat}`);
134
+ }
135
+ console.log(`\n${kpis.length} KPI(s)`);
136
+ }
137
+ });
138
+
139
+ // --- Dashboards ---
140
+
141
+ const dashCmd = program
142
+ .command("dashboard")
143
+ .description("Dashboard management");
144
+
145
+ dashCmd
146
+ .command("create")
147
+ .description("Create a dashboard")
148
+ .requiredOption("--name <name>", "Dashboard name")
149
+ .option("--description <desc>", "Description")
150
+ .option("--json", "Output as JSON", false)
151
+ .action((opts) => {
152
+ const dashboard = createDashboard({
153
+ name: opts.name,
154
+ description: opts.description,
155
+ });
156
+
157
+ if (opts.json) {
158
+ console.log(JSON.stringify(dashboard, null, 2));
159
+ } else {
160
+ console.log(`Created dashboard: ${dashboard.name} (${dashboard.id})`);
161
+ }
162
+ });
163
+
164
+ dashCmd
165
+ .command("list")
166
+ .description("List dashboards")
167
+ .option("--json", "Output as JSON", false)
168
+ .action((opts) => {
169
+ const dashboards = listDashboards();
170
+
171
+ if (opts.json) {
172
+ console.log(JSON.stringify(dashboards, null, 2));
173
+ } else {
174
+ if (dashboards.length === 0) {
175
+ console.log("No dashboards found.");
176
+ return;
177
+ }
178
+ for (const d of dashboards) {
179
+ const desc = d.description ? ` — ${d.description}` : "";
180
+ console.log(` ${d.name}${desc} (${d.widgets.length} widgets)`);
181
+ }
182
+ console.log(`\n${dashboards.length} dashboard(s)`);
183
+ }
184
+ });
185
+
186
+ dashCmd
187
+ .command("get")
188
+ .description("Get a dashboard")
189
+ .argument("<id>", "Dashboard ID")
190
+ .option("--json", "Output as JSON", false)
191
+ .action((id, opts) => {
192
+ const dashboard = getDashboard(id);
193
+ if (!dashboard) {
194
+ console.error(`Dashboard '${id}' not found.`);
195
+ process.exit(1);
196
+ }
197
+
198
+ if (opts.json) {
199
+ console.log(JSON.stringify(dashboard, null, 2));
200
+ } else {
201
+ console.log(`${dashboard.name}`);
202
+ if (dashboard.description) console.log(` Description: ${dashboard.description}`);
203
+ console.log(` Widgets: ${dashboard.widgets.length}`);
204
+ console.log(` Created: ${dashboard.created_at}`);
205
+ }
206
+ });
207
+
208
+ dashCmd
209
+ .command("update")
210
+ .description("Update a dashboard")
211
+ .argument("<id>", "Dashboard ID")
212
+ .option("--name <name>", "Name")
213
+ .option("--description <desc>", "Description")
214
+ .option("--json", "Output as JSON", false)
215
+ .action((id, opts) => {
216
+ const input: Record<string, unknown> = {};
217
+ if (opts.name !== undefined) input.name = opts.name;
218
+ if (opts.description !== undefined) input.description = opts.description;
219
+
220
+ const dashboard = updateDashboard(id, input);
221
+ if (!dashboard) {
222
+ console.error(`Dashboard '${id}' not found.`);
223
+ process.exit(1);
224
+ }
225
+
226
+ if (opts.json) {
227
+ console.log(JSON.stringify(dashboard, null, 2));
228
+ } else {
229
+ console.log(`Updated: ${dashboard.name}`);
230
+ }
231
+ });
232
+
233
+ dashCmd
234
+ .command("delete")
235
+ .description("Delete a dashboard")
236
+ .argument("<id>", "Dashboard ID")
237
+ .action((id) => {
238
+ const deleted = deleteDashboard(id);
239
+ if (deleted) {
240
+ console.log(`Deleted dashboard ${id}`);
241
+ } else {
242
+ console.error(`Dashboard '${id}' not found.`);
243
+ process.exit(1);
244
+ }
245
+ });
246
+
247
+ // --- Reports ---
248
+
249
+ const reportCmd = program
250
+ .command("report")
251
+ .description("Report management");
252
+
253
+ reportCmd
254
+ .command("generate")
255
+ .description("Generate a report")
256
+ .requiredOption("--name <name>", "Report name")
257
+ .requiredOption("--type <type>", "Report type (daily|weekly|monthly|quarterly|annual|custom)")
258
+ .option("--period <period>", "Period (e.g. 2024-Q1)")
259
+ .option("--json", "Output as JSON", false)
260
+ .action((opts) => {
261
+ const report = generateReport({
262
+ name: opts.name,
263
+ type: opts.type,
264
+ period: opts.period,
265
+ });
266
+
267
+ if (opts.json) {
268
+ console.log(JSON.stringify(report, null, 2));
269
+ } else {
270
+ console.log(`Generated report: ${report.name} (${report.id})`);
271
+ console.log(`Type: ${report.type}`);
272
+ if (report.period) console.log(`Period: ${report.period}`);
273
+ console.log(`\n${report.content}`);
274
+ }
275
+ });
276
+
277
+ reportCmd
278
+ .command("list")
279
+ .description("List reports")
280
+ .option("--type <type>", "Filter by type")
281
+ .option("--limit <n>", "Limit results")
282
+ .option("--json", "Output as JSON", false)
283
+ .action((opts) => {
284
+ const reports = listReports({
285
+ type: opts.type,
286
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
287
+ });
288
+
289
+ if (opts.json) {
290
+ console.log(JSON.stringify(reports, null, 2));
291
+ } else {
292
+ if (reports.length === 0) {
293
+ console.log("No reports found.");
294
+ return;
295
+ }
296
+ for (const r of reports) {
297
+ const period = r.period ? ` (${r.period})` : "";
298
+ console.log(` [${r.type}] ${r.name}${period} — ${r.generated_at}`);
299
+ }
300
+ console.log(`\n${reports.length} report(s)`);
301
+ }
302
+ });
303
+
304
+ reportCmd
305
+ .command("get")
306
+ .description("Get a report")
307
+ .argument("<id>", "Report ID")
308
+ .option("--json", "Output as JSON", false)
309
+ .action((id, opts) => {
310
+ const report = getReport(id);
311
+ if (!report) {
312
+ console.error(`Report '${id}' not found.`);
313
+ process.exit(1);
314
+ }
315
+
316
+ if (opts.json) {
317
+ console.log(JSON.stringify(report, null, 2));
318
+ } else {
319
+ console.log(`${report.name} [${report.type}]`);
320
+ if (report.period) console.log(`Period: ${report.period}`);
321
+ console.log(`Generated: ${report.generated_at}`);
322
+ console.log(`\n${report.content}`);
323
+ }
324
+ });
325
+
326
+ // --- Health & Summary ---
327
+
328
+ program
329
+ .command("health")
330
+ .description("Get overall business health summary")
331
+ .option("--json", "Output as JSON", false)
332
+ .action((opts) => {
333
+ const health = getBusinessHealth();
334
+
335
+ if (opts.json) {
336
+ console.log(JSON.stringify(health, null, 2));
337
+ } else {
338
+ console.log("=== Business Health ===");
339
+ console.log(` Unique KPIs: ${health.total_kpis}`);
340
+ console.log(` Reports: ${health.report_count}`);
341
+ console.log(` Dashboards: ${health.dashboard_count}`);
342
+
343
+ if (health.categories.length > 0) {
344
+ console.log("\n Categories:");
345
+ for (const cat of health.categories) {
346
+ console.log(` ${cat.category}: ${cat.count} KPI(s), latest value: ${cat.latest_value}`);
347
+ }
348
+ }
349
+
350
+ if (health.latest_kpis.length > 0) {
351
+ console.log("\n Latest KPIs:");
352
+ for (const kpi of health.latest_kpis) {
353
+ console.log(` ${kpi.name}: ${kpi.value}`);
354
+ }
355
+ }
356
+ }
357
+ });
358
+
359
+ program
360
+ .command("summary")
361
+ .description("Generate an AI-powered executive summary")
362
+ .option("--json", "Output as JSON", false)
363
+ .action(async (opts) => {
364
+ const summary = await generateExecutiveSummary();
365
+
366
+ if (opts.json) {
367
+ console.log(JSON.stringify({ summary }, null, 2));
368
+ } else {
369
+ console.log(summary);
370
+ }
371
+ });
372
+
373
+ program.parse(process.argv);