@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.
- package/bin/index.js +236 -36
- package/bin/mcp.js +153 -4
- package/dist/index.js +120 -3
- package/microservices/microservice-analytics/package.json +27 -0
- package/microservices/microservice-analytics/src/cli/index.ts +373 -0
- package/microservices/microservice-analytics/src/db/analytics.ts +564 -0
- package/microservices/microservice-analytics/src/db/database.ts +93 -0
- package/microservices/microservice-analytics/src/db/migrations.ts +50 -0
- package/microservices/microservice-analytics/src/index.ts +37 -0
- package/microservices/microservice-analytics/src/mcp/index.ts +334 -0
- package/microservices/microservice-assets/package.json +27 -0
- package/microservices/microservice-assets/src/cli/index.ts +375 -0
- package/microservices/microservice-assets/src/db/assets.ts +370 -0
- package/microservices/microservice-assets/src/db/database.ts +93 -0
- package/microservices/microservice-assets/src/db/migrations.ts +51 -0
- package/microservices/microservice-assets/src/index.ts +32 -0
- package/microservices/microservice-assets/src/mcp/index.ts +346 -0
- package/microservices/microservice-compliance/package.json +27 -0
- package/microservices/microservice-compliance/src/cli/index.ts +467 -0
- package/microservices/microservice-compliance/src/db/compliance.ts +633 -0
- package/microservices/microservice-compliance/src/db/database.ts +93 -0
- package/microservices/microservice-compliance/src/db/migrations.ts +63 -0
- package/microservices/microservice-compliance/src/index.ts +46 -0
- package/microservices/microservice-compliance/src/mcp/index.ts +438 -0
- package/microservices/microservice-habits/package.json +27 -0
- package/microservices/microservice-habits/src/cli/index.ts +315 -0
- package/microservices/microservice-habits/src/db/database.ts +93 -0
- package/microservices/microservice-habits/src/db/habits.ts +451 -0
- package/microservices/microservice-habits/src/db/migrations.ts +46 -0
- package/microservices/microservice-habits/src/index.ts +31 -0
- package/microservices/microservice-habits/src/mcp/index.ts +313 -0
- package/microservices/microservice-health/package.json +27 -0
- package/microservices/microservice-health/src/cli/index.ts +484 -0
- package/microservices/microservice-health/src/db/database.ts +93 -0
- package/microservices/microservice-health/src/db/health.ts +708 -0
- package/microservices/microservice-health/src/db/migrations.ts +70 -0
- package/microservices/microservice-health/src/index.ts +63 -0
- package/microservices/microservice-health/src/mcp/index.ts +437 -0
- package/microservices/microservice-leads/package.json +27 -0
- package/microservices/microservice-leads/src/cli/index.ts +596 -0
- package/microservices/microservice-leads/src/db/database.ts +93 -0
- package/microservices/microservice-leads/src/db/leads.ts +520 -0
- package/microservices/microservice-leads/src/db/lists.ts +151 -0
- package/microservices/microservice-leads/src/db/migrations.ts +93 -0
- package/microservices/microservice-leads/src/index.ts +65 -0
- package/microservices/microservice-leads/src/lib/enrichment.ts +202 -0
- package/microservices/microservice-leads/src/lib/scoring.ts +134 -0
- package/microservices/microservice-leads/src/mcp/index.ts +533 -0
- package/microservices/microservice-notifications/package.json +27 -0
- package/microservices/microservice-notifications/src/cli/index.ts +349 -0
- package/microservices/microservice-notifications/src/db/database.ts +93 -0
- package/microservices/microservice-notifications/src/db/migrations.ts +62 -0
- package/microservices/microservice-notifications/src/db/notifications.ts +509 -0
- package/microservices/microservice-notifications/src/index.ts +41 -0
- package/microservices/microservice-notifications/src/mcp/index.ts +422 -0
- package/microservices/microservice-products/package.json +27 -0
- package/microservices/microservice-products/src/cli/index.ts +416 -0
- package/microservices/microservice-products/src/db/categories.ts +154 -0
- package/microservices/microservice-products/src/db/database.ts +93 -0
- package/microservices/microservice-products/src/db/migrations.ts +58 -0
- package/microservices/microservice-products/src/db/pricing-tiers.ts +66 -0
- package/microservices/microservice-products/src/db/products.ts +452 -0
- package/microservices/microservice-products/src/index.ts +53 -0
- package/microservices/microservice-products/src/mcp/index.ts +453 -0
- package/microservices/microservice-projects/package.json +27 -0
- package/microservices/microservice-projects/src/cli/index.ts +480 -0
- package/microservices/microservice-projects/src/db/database.ts +93 -0
- package/microservices/microservice-projects/src/db/migrations.ts +65 -0
- package/microservices/microservice-projects/src/db/projects.ts +715 -0
- package/microservices/microservice-projects/src/index.ts +57 -0
- package/microservices/microservice-projects/src/mcp/index.ts +501 -0
- package/microservices/microservice-proposals/package.json +27 -0
- package/microservices/microservice-proposals/src/cli/index.ts +400 -0
- package/microservices/microservice-proposals/src/db/database.ts +93 -0
- package/microservices/microservice-proposals/src/db/migrations.ts +52 -0
- package/microservices/microservice-proposals/src/db/proposals.ts +532 -0
- package/microservices/microservice-proposals/src/index.ts +37 -0
- package/microservices/microservice-proposals/src/mcp/index.ts +375 -0
- package/microservices/microservice-reading/package.json +27 -0
- package/microservices/microservice-reading/src/cli/index.ts +464 -0
- package/microservices/microservice-reading/src/db/database.ts +93 -0
- package/microservices/microservice-reading/src/db/migrations.ts +59 -0
- package/microservices/microservice-reading/src/db/reading.ts +524 -0
- package/microservices/microservice-reading/src/index.ts +51 -0
- package/microservices/microservice-reading/src/mcp/index.ts +368 -0
- package/microservices/microservice-travel/package.json +27 -0
- package/microservices/microservice-travel/src/cli/index.ts +505 -0
- package/microservices/microservice-travel/src/db/database.ts +93 -0
- package/microservices/microservice-travel/src/db/migrations.ts +77 -0
- package/microservices/microservice-travel/src/db/travel.ts +802 -0
- package/microservices/microservice-travel/src/index.ts +60 -0
- package/microservices/microservice-travel/src/mcp/index.ts +495 -0
- package/microservices/microservice-wiki/package.json +27 -0
- package/microservices/microservice-wiki/src/cli/index.ts +345 -0
- package/microservices/microservice-wiki/src/db/database.ts +93 -0
- package/microservices/microservice-wiki/src/db/migrations.ts +55 -0
- package/microservices/microservice-wiki/src/db/wiki.ts +395 -0
- package/microservices/microservice-wiki/src/index.ts +32 -0
- package/microservices/microservice-wiki/src/mcp/index.ts +344 -0
- 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 =
|
|
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);
|