@datasynx/agentic-crm 1.0.0 → 1.2.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.
Files changed (89) hide show
  1. package/README.md +8 -1
  2. package/dist/{ask-D8iYqDAr.js → ask-CDysGnRg.js} +2 -2
  3. package/dist/{ask-D8iYqDAr.js.map → ask-CDysGnRg.js.map} +1 -1
  4. package/dist/attachments-CX2GAtsw.cjs +517 -0
  5. package/dist/attachments-CX2GAtsw.cjs.map +1 -0
  6. package/dist/attachments-D207gXfN.js +514 -0
  7. package/dist/attachments-D207gXfN.js.map +1 -0
  8. package/dist/attachments-rLa96rOK.js +514 -0
  9. package/dist/attachments-rLa96rOK.js.map +1 -0
  10. package/dist/chunk-BfDYWZQ8.cjs +32 -0
  11. package/dist/chunk-BfDYWZQ8.cjs.map +1 -0
  12. package/dist/chunk-BhUZmQg5.js +32 -0
  13. package/dist/chunk-BhUZmQg5.js.map +1 -0
  14. package/dist/chunk-ChC83jai.js +2 -0
  15. package/dist/chunk-e_w8qqtP.js +32 -0
  16. package/dist/chunk-e_w8qqtP.js.map +1 -0
  17. package/dist/cli.js +20 -18
  18. package/dist/cli.js.map +1 -1
  19. package/dist/daemon/worker.js +3 -3
  20. package/dist/{doctor-C14-vnJ1.js → doctor-BFeelnq8.js} +2 -2
  21. package/dist/{doctor-C14-vnJ1.js.map → doctor-BFeelnq8.js.map} +1 -1
  22. package/dist/doctor-CYDaNmFn.js +2 -0
  23. package/dist/email-body-BFSRa0AW.cjs +42 -0
  24. package/dist/email-body-BFSRa0AW.cjs.map +1 -0
  25. package/dist/email-body-BOd7U-D2.js +42 -0
  26. package/dist/email-body-BOd7U-D2.js.map +1 -0
  27. package/dist/{gmail-sync-DueE6tl5.js → gmail-sync-B4Iu3AQb.js} +45 -15
  28. package/dist/gmail-sync-B4Iu3AQb.js.map +1 -0
  29. package/dist/{gmail-sync-GEy3oVvw.cjs → gmail-sync-BpSVESSe.cjs} +45 -15
  30. package/dist/gmail-sync-BpSVESSe.cjs.map +1 -0
  31. package/dist/{gmail-sync-C-NmibzS.js → gmail-sync-DIbrPnTK.js} +45 -15
  32. package/dist/gmail-sync-DIbrPnTK.js.map +1 -0
  33. package/dist/{gmail-webhook-handler-kGKpbY9h.js → gmail-webhook-handler-BzOFbvgh.js} +2 -2
  34. package/dist/{gmail-webhook-handler-kGKpbY9h.js.map → gmail-webhook-handler-BzOFbvgh.js.map} +1 -1
  35. package/dist/{gmail-webhook-handler-B26COilD.js → gmail-webhook-handler-CvSDW_Js.js} +1 -1
  36. package/dist/{google-drive-sync-D1n7WKZn.js → google-drive-sync-B_I1d54Y.js} +2 -2
  37. package/dist/{google-drive-sync-D1n7WKZn.js.map → google-drive-sync-B_I1d54Y.js.map} +1 -1
  38. package/dist/html-BaeOCZKE.js +36 -0
  39. package/dist/html-BaeOCZKE.js.map +1 -0
  40. package/dist/html-CmOku6jS.cjs +47 -0
  41. package/dist/html-CmOku6jS.cjs.map +1 -0
  42. package/dist/{import-hubspot-DB4n89jy.js → import-hubspot-CTId9IGV.js} +2 -2
  43. package/dist/{import-hubspot-DB4n89jy.js.map → import-hubspot-CTId9IGV.js.map} +1 -1
  44. package/dist/{index-pY7tYXwH.d.cts → index-CLUKKfGb.d.cts} +12 -8
  45. package/dist/index-CLUKKfGb.d.cts.map +1 -0
  46. package/dist/{index-B0IMMrp_.d.ts → index-D8jJ1VIt.d.ts} +14 -10
  47. package/dist/index-D8jJ1VIt.d.ts.map +1 -0
  48. package/dist/index.d.cts +12 -8
  49. package/dist/index.d.cts.map +1 -1
  50. package/dist/index.d.ts +14 -10
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/{interactions-writer-RJB8SWf2.js → interactions-writer-B2y-73lh.js} +1 -1
  53. package/dist/{interactions-writer-DbSyI2rt.js → interactions-writer-B8XAzdqR.js} +3 -2
  54. package/dist/interactions-writer-B8XAzdqR.js.map +1 -0
  55. package/dist/{interactions-writer-a2yzBd7T.cjs → interactions-writer-BRJNrefF.cjs} +3 -2
  56. package/dist/interactions-writer-BRJNrefF.cjs.map +1 -0
  57. package/dist/{interactions-writer-BZzUIgJd.js → interactions-writer-ZQcpFOh9.js} +3 -2
  58. package/dist/interactions-writer-ZQcpFOh9.js.map +1 -0
  59. package/dist/{knowledge-base-DHNc4hVj.js → knowledge-base-Bx2PKQR2.js} +10 -7
  60. package/dist/knowledge-base-Bx2PKQR2.js.map +1 -0
  61. package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
  62. package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
  63. package/dist/mcp.cjs +308 -150
  64. package/dist/mcp.cjs.map +1 -1
  65. package/dist/mcp.d.cts.map +1 -1
  66. package/dist/mcp.d.ts.map +1 -1
  67. package/dist/mcp.js +308 -150
  68. package/dist/mcp.js.map +1 -1
  69. package/dist/{microsoft-calendar-jIu9K5zX.js → microsoft-calendar-BgVR8GDv.js} +3 -3
  70. package/dist/{microsoft-calendar-jIu9K5zX.js.map → microsoft-calendar-BgVR8GDv.js.map} +1 -1
  71. package/dist/{microsoft-sync-R_r8HL-B.js → microsoft-sync-D30_XksI.js} +3 -3
  72. package/dist/{microsoft-sync-R_r8HL-B.js.map → microsoft-sync-D30_XksI.js.map} +1 -1
  73. package/dist/{nba-mTJ4yEqD.js → nba-DwdfM93s.js} +2 -2
  74. package/dist/{nba-mTJ4yEqD.js.map → nba-DwdfM93s.js.map} +1 -1
  75. package/dist/{server-DqSMYhSA.js → server-BhNLrnAD.js} +201 -145
  76. package/dist/server-BhNLrnAD.js.map +1 -0
  77. package/dist/{transcript-watcher-0mh2ZhmH.js → transcript-watcher-BoClrJAz.js} +2 -2
  78. package/dist/{transcript-watcher-0mh2ZhmH.js.map → transcript-watcher-BoClrJAz.js.map} +1 -1
  79. package/package.json +13 -2
  80. package/dist/gmail-sync-C-NmibzS.js.map +0 -1
  81. package/dist/gmail-sync-DueE6tl5.js.map +0 -1
  82. package/dist/gmail-sync-GEy3oVvw.cjs.map +0 -1
  83. package/dist/index-B0IMMrp_.d.ts.map +0 -1
  84. package/dist/index-pY7tYXwH.d.cts.map +0 -1
  85. package/dist/interactions-writer-BZzUIgJd.js.map +0 -1
  86. package/dist/interactions-writer-DbSyI2rt.js.map +0 -1
  87. package/dist/interactions-writer-a2yzBd7T.cjs.map +0 -1
  88. package/dist/knowledge-base-DHNc4hVj.js.map +0 -1
  89. package/dist/server-DqSMYhSA.js.map +0 -1
package/dist/mcp.js CHANGED
@@ -2,7 +2,7 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { A as writeMainFacts, C as writeJsonArray, D as ensureCustomerDir, E as customerExists, M as isSafePathSegment, O as listCustomerSlugs, S as readJsonFile, T as assertSafeSlug, a as customerVisibility, d as readAuditLog, f as writeAuditEntry, h as runBackup, i as canSeeCustomer, j as assertSafePathSegment, k as readMainFacts, l as filterAuditLog, m as readBackupLog, n as getSession, o as enforceRbac, p as listBackupsInDir, u as getActor, w as writeJsonFile, x as readJsonArray } from "./session-store-DWxJ5Pof.js";
3
3
  import { t as writeFileAtomic } from "./atomic-write-8yjqqLtS.js";
4
4
  import { t as withFileQueue } from "./write-queue-IbsAjUnh.js";
5
- import { n as formatInteractionEntry, t as appendInteraction } from "./interactions-writer-BZzUIgJd.js";
5
+ import { n as formatInteractionEntry, t as appendInteraction } from "./interactions-writer-ZQcpFOh9.js";
6
6
  import { n as queryLogs, r as summarizeLogs, t as logger } from "./logger-UaF5p9d1.js";
7
7
  import { i as upsertDeal, n as readPipeline, r as readPipelineSync } from "./pipeline-writer-rDj-ni6q.js";
8
8
  import { i as guardIsoDate, t as callLlm } from "./llm-BnSUBisu.js";
@@ -398,7 +398,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
398
398
  | log_interaction | Write a new interaction entry (call, email, meeting, note) — immediately searchable | rep+ |
399
399
  | update_deal | Create or update a deal in pipeline.md — upserts by deal name | rep+ |
400
400
  | update_customer_facts | Update fields in customer profile (domain, contact, stage, tags) | admin |
401
- | export_customer | Export all customer data as JSON or Markdown | admin |
401
+ | export_customer | Export all customer data (incl. attachment contents) as JSON or Markdown | admin |
402
402
  | get_deal_health | Score deal health 0–100 (A–F grade) based on activity, velocity, close date, probability | any |
403
403
  | get_pipeline_forecast | Aggregate weighted pipeline revenue across all customers grouped by stage | any |
404
404
  | get_pipeline_stages | List all configured pipeline stages (defaults: lead, qualified, proposal, negotiation, won, lost) | any |
@@ -443,6 +443,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
443
443
  | trigger_sync | Force immediate Gmail sync for one or all customers | rep+ |
444
444
  | get_audit_log | Read audit log — all write operations with actor, tool, customer | admin |
445
445
  | get_logs | Query/aggregate the structured application log (level, component, errors) | admin |
446
+ | get_diagnostics | Self-diagnostic health check (data integrity, temp files, log errors, backups) | admin |
446
447
  | define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |
447
448
  | create_record | Create a record of a custom object (validated against its schema) | rep+ |
448
449
  | list_records | List records of a custom object | any |
@@ -532,12 +533,14 @@ RBAC: admin
532
533
  - Input: slug (required) + any combination of the optional fields
533
534
  - Returns: { success: boolean, facts: object }
534
535
 
535
- ### export_customer({ slug, format? })
536
- Export all customer data (main_facts + interactions count + pipeline + attachments list).
536
+ ### export_customer({ slug, format?, includeAttachmentContent? })
537
+ Export all customer data (main_facts + interactions + pipeline + attachments).
538
+ Set includeAttachmentContent to inline every attachment's converted Markdown —
539
+ a single sendable bundle of all conversations and documents for the customer.
537
540
  RBAC: admin
538
- - Input: { slug: string, format?: "json" | "markdown" (default "json") }
539
- - Returns (JSON): { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments }
540
- - Returns (Markdown): Formatted document with all sections
541
+ - Input: { slug: string, format?: "json" | "markdown" (default "json"), includeAttachmentContent?: boolean (default false) }
542
+ - Returns (JSON): { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments[, attachmentContents] }
543
+ - Returns (Markdown): Formatted document with all sections (and attachment contents when requested)
541
544
 
542
545
  ### get_deal_health({ slug })
543
546
  Score the health of all deals for a customer based on activity recency, stage velocity,
@@ -1419,7 +1422,7 @@ async function buildContext(dataDir, slug) {
1419
1422
  }
1420
1423
  //#endregion
1421
1424
  //#region src/mcp/tools/get-customer-context.ts
1422
- const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1425
+ const DATA_DIR$53 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1423
1426
  function triggerOnQuerySync(dataDir, slug) {
1424
1427
  const auth = getGmailAuth();
1425
1428
  if (!auth) return;
@@ -1432,7 +1435,7 @@ function triggerOnQuerySync(dataDir, slug) {
1432
1435
  const sources = JSON.parse(fs.readFileSync(sourcesPath, "utf-8"));
1433
1436
  if (!sources.gmail?.enabled || !sources.gmail.query) return;
1434
1437
  const query = sources.gmail.query;
1435
- import("./gmail-sync-C-NmibzS.js").then(({ syncGmail }) => syncGmail({
1438
+ import("./gmail-sync-DIbrPnTK.js").then(({ syncGmail }) => syncGmail({
1436
1439
  slug,
1437
1440
  dataDir,
1438
1441
  auth,
@@ -1440,7 +1443,7 @@ function triggerOnQuerySync(dataDir, slug) {
1440
1443
  }).then(() => updateSlugSyncState(dataDir, slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() })).catch(() => {})).catch(() => {});
1441
1444
  } catch {}
1442
1445
  }
1443
- async function handleGetCustomerContext(input, dataDir = DATA_DIR$52) {
1446
+ async function handleGetCustomerContext(input, dataDir = DATA_DIR$53) {
1444
1447
  const targetSlug = input.slug ?? getSession()?.customerSlug;
1445
1448
  if (!targetSlug) return {
1446
1449
  content: [{
@@ -1576,8 +1579,8 @@ async function searchKnowledge(dataDir, slug, query, limit) {
1576
1579
  }
1577
1580
  //#endregion
1578
1581
  //#region src/mcp/tools/search-customer-knowledge.ts
1579
- const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1580
- async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$51) {
1582
+ const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1583
+ async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$52) {
1581
1584
  const limit = input.limit ?? 5;
1582
1585
  try {
1583
1586
  const results = await searchKnowledge(dataDir, input.slug, input.query, limit);
@@ -1625,14 +1628,14 @@ If no results: returns empty array with a helpful sync suggestion.`,
1625
1628
  }
1626
1629
  //#endregion
1627
1630
  //#region src/mcp/tools/list-customers.ts
1628
- const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1631
+ const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1629
1632
  function extractLastInteractionDate(interactionsPath) {
1630
1633
  if (!fs.existsSync(interactionsPath)) return void 0;
1631
1634
  const content = fs.readFileSync(interactionsPath, "utf-8");
1632
1635
  const match = /^## (\d{4}-\d{2}-\d{2})/m.exec(content);
1633
1636
  return match ? match[1] : void 0;
1634
1637
  }
1635
- async function handleListCustomers(input, dataDir = DATA_DIR$50) {
1638
+ async function handleListCustomers(input, dataDir = DATA_DIR$51) {
1636
1639
  const customersDir = path.join(dataDir, "customers");
1637
1640
  const customers = [];
1638
1641
  if (!fs.existsSync(customersDir)) return { content: [{
@@ -2145,8 +2148,8 @@ async function updateHealthFromInteraction(dataDir, slug) {
2145
2148
  }
2146
2149
  //#endregion
2147
2150
  //#region src/mcp/tools/log-interaction.ts
2148
- const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2149
- async function handleLogInteraction(input, dataDir = DATA_DIR$49) {
2151
+ const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2152
+ async function handleLogInteraction(input, dataDir = DATA_DIR$50) {
2150
2153
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2151
2154
  const interactionDate = input.date ?? today;
2152
2155
  const sourceRef = input.source ?? `agent://log/${Date.now()}`;
@@ -2255,8 +2258,8 @@ var update_deal_exports = /* @__PURE__ */ __exportAll({
2255
2258
  handleUpdateDeal: () => handleUpdateDeal,
2256
2259
  registerUpdateDeal: () => registerUpdateDeal
2257
2260
  });
2258
- const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2259
- async function handleUpdateDeal(input, dataDir = DATA_DIR$48) {
2261
+ const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2262
+ async function handleUpdateDeal(input, dataDir = DATA_DIR$49) {
2260
2263
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2261
2264
  const deal = {
2262
2265
  name: input.dealName,
@@ -2339,12 +2342,12 @@ Returns: { success: boolean, deal: object }`,
2339
2342
  }
2340
2343
  //#endregion
2341
2344
  //#region src/mcp/tools/export-customer.ts
2342
- const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2345
+ const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2343
2346
  function countInteractions(content) {
2344
2347
  const matches = content.match(/^## \d{4}-\d{2}-\d{2}/gm);
2345
2348
  return matches ? matches.length : 0;
2346
2349
  }
2347
- async function handleExportCustomer(input, dataDir = DATA_DIR$47) {
2350
+ async function handleExportCustomer(input, dataDir = DATA_DIR$48) {
2348
2351
  enforceRbac(dataDir, "export_customer");
2349
2352
  const customerDir = path.join(dataDir, "customers", input.slug);
2350
2353
  if (!fs.existsSync(customerDir)) return {
@@ -2371,14 +2374,27 @@ async function handleExportCustomer(input, dataDir = DATA_DIR$47) {
2371
2374
  interactionsCount = countInteractions(interactionsContent);
2372
2375
  }
2373
2376
  const pipeline = await readPipeline(dataDir, input.slug);
2377
+ const includeAttachmentContent = input.includeAttachmentContent ?? false;
2374
2378
  const attachmentsDir = path.join(customerDir, "attachments");
2375
2379
  const attachments = [];
2380
+ const attachmentContents = {};
2376
2381
  if (fs.existsSync(attachmentsDir)) try {
2377
2382
  const files = fs.readdirSync(attachmentsDir);
2378
2383
  for (const f of files) try {
2379
- if (fs.statSync(path.join(attachmentsDir, f)).isFile()) attachments.push(f);
2384
+ if (!fs.statSync(path.join(attachmentsDir, f)).isFile()) continue;
2385
+ attachments.push(f);
2386
+ if (includeAttachmentContent && f.endsWith(".md")) attachmentContents[f] = fs.readFileSync(path.join(attachmentsDir, f), "utf-8");
2380
2387
  } catch {}
2381
2388
  } catch {}
2389
+ const attachmentContentSection = () => {
2390
+ const entries = Object.entries(attachmentContents);
2391
+ if (!includeAttachmentContent || entries.length === 0) return [];
2392
+ return [
2393
+ "",
2394
+ `## Attachment Contents (${entries.length})`,
2395
+ ...entries.map(([name, content]) => `\n### ${name}\n\n${content.trim()}`)
2396
+ ];
2397
+ };
2382
2398
  if (format === "markdown") return { content: [{
2383
2399
  type: "text",
2384
2400
  text: [
@@ -2397,7 +2413,8 @@ async function handleExportCustomer(input, dataDir = DATA_DIR$47) {
2397
2413
  pipeline.length > 0 ? pipeline.map((d) => `- **${d.name}** · ${d.stage}${d.value !== void 0 ? ` · €${d.value}` : ""}${d.close_date ? ` · close: ${d.close_date}` : ""}`).join("\n") : "(no deals)",
2398
2414
  "",
2399
2415
  `## Attachments (${attachments.length})`,
2400
- attachments.length > 0 ? attachments.map((f) => `- ${f}`).join("\n") : "(none)"
2416
+ attachments.length > 0 ? attachments.map((f) => `- ${f}`).join("\n") : "(none)",
2417
+ ...attachmentContentSection()
2401
2418
  ].join("\n")
2402
2419
  }] };
2403
2420
  const exported = {
@@ -2406,7 +2423,8 @@ async function handleExportCustomer(input, dataDir = DATA_DIR$47) {
2406
2423
  mainFacts,
2407
2424
  interactionsCount,
2408
2425
  pipeline,
2409
- attachments
2426
+ attachments,
2427
+ ...includeAttachmentContent ? { attachmentContents } : {}
2410
2428
  };
2411
2429
  return { content: [{
2412
2430
  type: "text",
@@ -2416,29 +2434,34 @@ async function handleExportCustomer(input, dataDir = DATA_DIR$47) {
2416
2434
  function registerExportCustomer(server) {
2417
2435
  server.registerTool("export_customer", {
2418
2436
  title: "Export Customer",
2419
- description: `Export all customer data (main_facts + interactions count + pipeline deals).
2420
- Useful for reporting, audits, or creating backups.
2437
+ description: `Export all customer data (main_facts + interactions + pipeline deals + attachments).
2438
+ Useful for reporting, audits, handoffs, or creating a complete sendable bundle
2439
+ of every conversation and document for a customer.
2421
2440
 
2422
2441
  Args:
2423
2442
  slug: Customer ID (e.g. "acme-corp")
2424
2443
  format: Output format — "json" (default) or "markdown"
2444
+ includeAttachmentContent: Inline the converted Markdown of every attachment
2445
+ (default false). Use this to produce a single self-contained bundle.
2425
2446
 
2426
2447
  Returns:
2427
- JSON: { slug, exportedAt, mainFacts, interactionsCount, pipeline }
2428
- Markdown: Formatted document with all sections`,
2448
+ JSON: { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments[, attachmentContents] }
2449
+ Markdown: Formatted document with all sections (and attachment contents when requested)`,
2429
2450
  inputSchema: z.object({
2430
2451
  slug: z.string().describe("Customer slug (e.g. 'acme-corp')"),
2431
- format: z.enum(["json", "markdown"]).optional().describe("Output format: 'json' (default) or 'markdown'")
2452
+ format: z.enum(["json", "markdown"]).optional().describe("Output format: 'json' (default) or 'markdown'"),
2453
+ includeAttachmentContent: z.boolean().optional().describe("Inline converted attachment Markdown into the export (default false)")
2432
2454
  })
2433
- }, async ({ slug, format }) => handleExportCustomer({
2455
+ }, async ({ slug, format, includeAttachmentContent }) => handleExportCustomer({
2434
2456
  slug,
2435
- ...format !== void 0 ? { format } : {}
2457
+ ...format !== void 0 ? { format } : {},
2458
+ ...includeAttachmentContent !== void 0 ? { includeAttachmentContent } : {}
2436
2459
  }));
2437
2460
  }
2438
2461
  //#endregion
2439
2462
  //#region src/mcp/tools/update-customer-facts.ts
2440
- const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2441
- async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$46) {
2463
+ const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2464
+ async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$47) {
2442
2465
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2443
2466
  try {
2444
2467
  enforceRbac(dataDir, "update_customer_facts");
@@ -2616,8 +2639,8 @@ function scoreDealForToday(deal, todayDate) {
2616
2639
  }
2617
2640
  //#endregion
2618
2641
  //#region src/mcp/tools/get-deal-health.ts
2619
- const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2620
- async function handleGetDealHealth(input, dataDir = DATA_DIR$45) {
2642
+ const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2643
+ async function handleGetDealHealth(input, dataDir = DATA_DIR$46) {
2621
2644
  try {
2622
2645
  const deals = await readPipeline(dataDir, input.slug);
2623
2646
  const today = /* @__PURE__ */ new Date();
@@ -2666,8 +2689,8 @@ Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }`,
2666
2689
  }
2667
2690
  //#endregion
2668
2691
  //#region src/mcp/tools/get-pipeline-forecast.ts
2669
- const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2670
- async function handleGetPipelineForecast(input, dataDir = DATA_DIR$44) {
2692
+ const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2693
+ async function handleGetPipelineForecast(input, dataDir = DATA_DIR$45) {
2671
2694
  try {
2672
2695
  const slugs = listCustomerSlugs(dataDir).filter((d) => !input.filter || d.includes(input.filter));
2673
2696
  const allDeals = [];
@@ -2728,8 +2751,8 @@ Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count,
2728
2751
  }
2729
2752
  //#endregion
2730
2753
  //#region src/mcp/tools/summarize-meeting.ts
2731
- const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2732
- async function handleSummarizeMeeting(input, dataDir = DATA_DIR$43) {
2754
+ const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2755
+ async function handleSummarizeMeeting(input, dataDir = DATA_DIR$44) {
2733
2756
  try {
2734
2757
  let summary = input.transcript.slice(0, 400);
2735
2758
  let nextSteps = [];
@@ -2852,8 +2875,8 @@ function getPipelineStages(dataDir) {
2852
2875
  }
2853
2876
  //#endregion
2854
2877
  //#region src/mcp/tools/get-pipeline-stages.ts
2855
- const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2856
- async function handleGetPipelineStages(_input, dataDir = DATA_DIR$42) {
2878
+ const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2879
+ async function handleGetPipelineStages(_input, dataDir = DATA_DIR$43) {
2857
2880
  const stages = getPipelineStages(dataDir);
2858
2881
  return { content: [{
2859
2882
  type: "text",
@@ -2881,8 +2904,8 @@ async function searchAcrossCustomers(dataDir, query, limit = 5, excludeSlug) {
2881
2904
  }
2882
2905
  //#endregion
2883
2906
  //#region src/mcp/tools/get-market-intelligence.ts
2884
- const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2885
- async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$41) {
2907
+ const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2908
+ async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$42) {
2886
2909
  const excludeSlug = input.excludeCurrentCustomer ? input.slug : void 0;
2887
2910
  const all = listCustomerSlugs(dataDir);
2888
2911
  const totalCustomersSearched = excludeSlug ? all.filter((s) => s !== excludeSlug).length : all.length;
@@ -2913,7 +2936,7 @@ function registerGetMarketIntelligence(server) {
2913
2936
  }
2914
2937
  //#endregion
2915
2938
  //#region src/mcp/tools/get-relationship-graph.ts
2916
- const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2939
+ const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2917
2940
  function summarizeNode(n) {
2918
2941
  return {
2919
2942
  id: n.id,
@@ -2921,7 +2944,7 @@ function summarizeNode(n) {
2921
2944
  email: n.properties["email"]
2922
2945
  };
2923
2946
  }
2924
- async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$40) {
2947
+ async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$41) {
2925
2948
  try {
2926
2949
  const graph = readGraph(dataDir, input.slug);
2927
2950
  const stakeholders = getStakeholders(graph);
@@ -2989,9 +3012,9 @@ Returns: {
2989
3012
  }
2990
3013
  //#endregion
2991
3014
  //#region src/mcp/tools/get-relationship-health.ts
2992
- const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3015
+ const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2993
3016
  const MAX_HEALTH_AGE_MS = 3600 * 1e3;
2994
- async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$39) {
3017
+ async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$40) {
2995
3018
  try {
2996
3019
  let health = readHealth(dataDir, input.slug);
2997
3020
  if (health === null || Date.now() - new Date(health.updatedAt).getTime() > MAX_HEALTH_AGE_MS) {
@@ -3541,7 +3564,7 @@ async function executeAction(action, dataDir) {
3541
3564
  if (!slug) return "skipped";
3542
3565
  switch (action.type) {
3543
3566
  case "log_interaction": {
3544
- const { appendInteraction } = await import("./interactions-writer-BZzUIgJd.js").then((n) => n.r);
3567
+ const { appendInteraction } = await import("./interactions-writer-ZQcpFOh9.js").then((n) => n.r);
3545
3568
  await appendInteraction(dataDir, slug, {
3546
3569
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
3547
3570
  type: action.payload["type"] ?? "Note",
@@ -3554,7 +3577,7 @@ async function executeAction(action, dataDir) {
3554
3577
  return "executed";
3555
3578
  }
3556
3579
  case "schedule_meeting": {
3557
- const { appendInteraction } = await import("./interactions-writer-BZzUIgJd.js").then((n) => n.r);
3580
+ const { appendInteraction } = await import("./interactions-writer-ZQcpFOh9.js").then((n) => n.r);
3558
3581
  await appendInteraction(dataDir, slug, {
3559
3582
  date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
3560
3583
  type: "Note",
@@ -3660,8 +3683,8 @@ async function runDealAgent(config, dataDir, llmFn = callLlm) {
3660
3683
  }
3661
3684
  //#endregion
3662
3685
  //#region src/mcp/tools/run-deal-agent.ts
3663
- const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3664
- async function handleRunDealAgent(input, dataDir = DATA_DIR$38) {
3686
+ const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3687
+ async function handleRunDealAgent(input, dataDir = DATA_DIR$39) {
3665
3688
  try {
3666
3689
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3667
3690
  const result = await runDealAgent({
@@ -3728,8 +3751,8 @@ Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], tr
3728
3751
  }
3729
3752
  //#endregion
3730
3753
  //#region src/mcp/tools/approve-agent-action.ts
3731
- const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3732
- async function handleApproveAgentAction(input, dataDir = DATA_DIR$37) {
3754
+ const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3755
+ async function handleApproveAgentAction(input, dataDir = DATA_DIR$38) {
3733
3756
  try {
3734
3757
  const queue = readAgentQueue(dataDir, input.slug);
3735
3758
  const idx = queue.pendingActions.findIndex((a) => a.actionId === input.actionId);
@@ -3989,8 +4012,8 @@ async function buildSimulationInput(dataDir, horizon, today, externalSignals = [
3989
4012
  }
3990
4013
  //#endregion
3991
4014
  //#region src/mcp/tools/simulate-revenue.ts
3992
- const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3993
- async function handleSimulateRevenue(input, dataDir = DATA_DIR$36) {
4015
+ const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4016
+ async function handleSimulateRevenue(input, dataDir = DATA_DIR$37) {
3994
4017
  try {
3995
4018
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3996
4019
  const horizon = input.horizon ?? "quarter";
@@ -4048,8 +4071,8 @@ Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMo
4048
4071
  }
4049
4072
  //#endregion
4050
4073
  //#region src/mcp/tools/get-playbook.ts
4051
- const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4052
- async function handleGetPlaybook(input, dataDir = DATA_DIR$35) {
4074
+ const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4075
+ async function handleGetPlaybook(input, dataDir = DATA_DIR$36) {
4053
4076
  try {
4054
4077
  const playbooks = listPlaybooks(dataDir, input.slug);
4055
4078
  if (!(input.stage !== void 0 || input.value !== void 0 || input.healthScore !== void 0)) return { content: [{
@@ -4134,12 +4157,12 @@ Returns: { matches: [{ name, score, trigger, successRate, usedCount, content }],
4134
4157
  ...healthScore !== void 0 ? { healthScore } : {},
4135
4158
  ...daysSinceContact !== void 0 ? { daysSinceContact } : {},
4136
4159
  ...championPresent !== void 0 ? { championPresent } : {}
4137
- }, DATA_DIR$35));
4160
+ }, DATA_DIR$36));
4138
4161
  }
4139
4162
  //#endregion
4140
4163
  //#region src/mcp/tools/create-playbook.ts
4141
- const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4142
- async function handleCreatePlaybook(input, dataDir = DATA_DIR$34) {
4164
+ const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4165
+ async function handleCreatePlaybook(input, dataDir = DATA_DIR$35) {
4143
4166
  try {
4144
4167
  const name = toKebabCase(input.name);
4145
4168
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -4212,12 +4235,12 @@ Returns: { success: true, playbook: { name, trigger, successRate, path } }`,
4212
4235
  trigger,
4213
4236
  content,
4214
4237
  ...successRate !== void 0 ? { successRate } : {}
4215
- }, DATA_DIR$34));
4238
+ }, DATA_DIR$35));
4216
4239
  }
4217
4240
  //#endregion
4218
4241
  //#region src/mcp/tools/list-playbooks.ts
4219
- const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4220
- async function handleListPlaybooks(input, dataDir = DATA_DIR$33) {
4242
+ const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4243
+ async function handleListPlaybooks(input, dataDir = DATA_DIR$34) {
4221
4244
  try {
4222
4245
  const playbooks = listPlaybooks(dataDir, input.slug);
4223
4246
  return { content: [{
@@ -4256,12 +4279,12 @@ Args:
4256
4279
 
4257
4280
  Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }`,
4258
4281
  inputSchema: z.object({ slug: z.string().describe("Customer ID") })
4259
- }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$33));
4282
+ }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$34));
4260
4283
  }
4261
4284
  //#endregion
4262
4285
  //#region src/mcp/tools/distill-playbook.ts
4263
- const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4264
- async function handleDistillPlaybook(input, dataDir = DATA_DIR$32, llmFn = callLlm) {
4286
+ const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4287
+ async function handleDistillPlaybook(input, dataDir = DATA_DIR$33, llmFn = callLlm) {
4265
4288
  try {
4266
4289
  const result = await distillPlaybook(dataDir, input.slug, input.dealName, input.outcome, llmFn);
4267
4290
  if (!result.ok) {
@@ -4320,7 +4343,7 @@ Returns: { success: true, playbook: { name, trigger, successRate, path }, reason
4320
4343
  slug,
4321
4344
  dealName,
4322
4345
  outcome
4323
- }, DATA_DIR$32));
4346
+ }, DATA_DIR$33));
4324
4347
  }
4325
4348
  //#endregion
4326
4349
  //#region src/core/goal-engine.ts
@@ -4538,8 +4561,8 @@ function getActiveGoals(dataDir) {
4538
4561
  }
4539
4562
  //#endregion
4540
4563
  //#region src/mcp/tools/pursue-goal.ts
4541
- const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4542
- async function handlePursueGoal(input, dataDir = DATA_DIR$31, options = {}) {
4564
+ const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4565
+ async function handlePursueGoal(input, dataDir = DATA_DIR$32, options = {}) {
4543
4566
  try {
4544
4567
  enforceRbac(dataDir, "pursue_goal");
4545
4568
  const goal = await pursueGoal(dataDir, {
@@ -4602,12 +4625,12 @@ Returns: { goalId, description, target, deadline, decomposition: { analysis, cur
4602
4625
  goal,
4603
4626
  deadline,
4604
4627
  ...context !== void 0 ? { context } : {}
4605
- }, DATA_DIR$31));
4628
+ }, DATA_DIR$32));
4606
4629
  }
4607
4630
  //#endregion
4608
4631
  //#region src/mcp/tools/get-goal-status.ts
4609
- const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4610
- async function handleGetGoalStatus(input, dataDir = DATA_DIR$30) {
4632
+ const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4633
+ async function handleGetGoalStatus(input, dataDir = DATA_DIR$31) {
4611
4634
  try {
4612
4635
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4613
4636
  const allGoals = input.goalId ? readGoals(dataDir).filter((g) => g.id === input.goalId) : getActiveGoals(dataDir);
@@ -4666,17 +4689,17 @@ Args:
4666
4689
 
4667
4690
  Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }`,
4668
4691
  inputSchema: z.object({ goalId: z.string().optional().describe("Specific goal ID (omit for all active goals)") })
4669
- }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$30));
4692
+ }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$31));
4670
4693
  }
4671
4694
  //#endregion
4672
4695
  //#region src/mcp/tools/register-push-subscription.ts
4673
- const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4696
+ const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4674
4697
  const VALID_PROVIDERS = [
4675
4698
  "gmail",
4676
4699
  "microsoft-graph",
4677
4700
  "slack"
4678
4701
  ];
4679
- async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$29) {
4702
+ async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$30) {
4680
4703
  try {
4681
4704
  if (!VALID_PROVIDERS.includes(input.provider)) return { content: [{
4682
4705
  type: "text",
@@ -4762,12 +4785,12 @@ Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning
4762
4785
  ...microsoftResource !== void 0 ? { microsoftResource } : {},
4763
4786
  ...slackTeamId !== void 0 ? { slackTeamId } : {},
4764
4787
  ...slackChannelId !== void 0 ? { slackChannelId } : {}
4765
- }, DATA_DIR$29));
4788
+ }, DATA_DIR$30));
4766
4789
  }
4767
4790
  //#endregion
4768
4791
  //#region src/mcp/tools/get-push-status.ts
4769
- const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4770
- async function handleGetPushStatus(input, dataDir = DATA_DIR$28) {
4792
+ const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4793
+ async function handleGetPushStatus(input, dataDir = DATA_DIR$29) {
4771
4794
  try {
4772
4795
  let subs = await readSubscriptions(dataDir);
4773
4796
  if (input.slug) subs = subs.filter((s) => s.slug === input.slug);
@@ -4839,7 +4862,7 @@ Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHou
4839
4862
  }, async ({ slug, provider }) => handleGetPushStatus({
4840
4863
  ...slug !== void 0 ? { slug } : {},
4841
4864
  ...provider !== void 0 ? { provider } : {}
4842
- }, DATA_DIR$28));
4865
+ }, DATA_DIR$29));
4843
4866
  }
4844
4867
  //#endregion
4845
4868
  //#region src/core/org-intelligence.ts
@@ -4905,8 +4928,8 @@ function deriveRecommendation(people, missingRoles) {
4905
4928
  }
4906
4929
  //#endregion
4907
4930
  //#region src/mcp/tools/get-org-intelligence.ts
4908
- const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4909
- async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$27) {
4931
+ const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4932
+ async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$28) {
4910
4933
  try {
4911
4934
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4912
4935
  const map = buildStakeholderMap(dataDir, input.slug, today, input.dealName);
@@ -5039,8 +5062,8 @@ function buildExecutiveSummary(slug, dealName, stakeholders, overallHealth, sim,
5039
5062
  }
5040
5063
  //#endregion
5041
5064
  //#region src/mcp/tools/open-deal-room.ts
5042
- const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5043
- async function handleOpenDealRoom(input, dataDir = DATA_DIR$26) {
5065
+ const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5066
+ async function handleOpenDealRoom(input, dataDir = DATA_DIR$27) {
5044
5067
  try {
5045
5068
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5046
5069
  const brief = await buildDealRoom(dataDir, input.slug, input.dealName, today);
@@ -5123,8 +5146,8 @@ async function buildDailyBriefing(dataDir, today) {
5123
5146
  }
5124
5147
  //#endregion
5125
5148
  //#region src/mcp/tools/get-proactive-briefing.ts
5126
- const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5127
- async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$25) {
5149
+ const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5150
+ async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$26) {
5128
5151
  try {
5129
5152
  const briefing = await buildDailyBriefing(dataDir, input.date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
5130
5153
  return { content: [{
@@ -5224,15 +5247,15 @@ function getTemplate(dataDir, id) {
5224
5247
  }
5225
5248
  //#endregion
5226
5249
  //#region src/mcp/tools/list-email-templates.ts
5227
- const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5228
- async function handleListEmailTemplates(input, dataDir = DATA_DIR$24) {
5250
+ const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5251
+ async function handleListEmailTemplates(input, dataDir = DATA_DIR$25) {
5229
5252
  const summary = listTemplates(dataDir, input.category ? { category: input.category } : {}).map(({ body: _body, ...meta }) => meta);
5230
5253
  return { content: [{
5231
5254
  type: "text",
5232
5255
  text: JSON.stringify(summary, null, 2)
5233
5256
  }] };
5234
5257
  }
5235
- function registerListEmailTemplates(server, dataDir = DATA_DIR$24) {
5258
+ function registerListEmailTemplates(server, dataDir = DATA_DIR$25) {
5236
5259
  server.registerTool("list_email_templates", {
5237
5260
  description: "List available email templates. Optionally filter by category (e.g. 'outreach', 'followup', 'support').",
5238
5261
  inputSchema: z.object({ category: z.string().optional().describe("Filter by category") })
@@ -5266,8 +5289,8 @@ async function buildVariablesFromCustomer(dataDir, slug) {
5266
5289
  }
5267
5290
  //#endregion
5268
5291
  //#region src/mcp/tools/get-email-template.ts
5269
- const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5270
- async function handleGetEmailTemplate(input, dataDir = DATA_DIR$23) {
5292
+ const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5293
+ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$24) {
5271
5294
  const tmpl = getTemplate(dataDir, input.id);
5272
5295
  if (!tmpl) return { content: [{
5273
5296
  type: "text",
@@ -5283,7 +5306,7 @@ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$23) {
5283
5306
  }, null, 2)
5284
5307
  }] };
5285
5308
  }
5286
- function registerGetEmailTemplate(server, dataDir = DATA_DIR$23) {
5309
+ function registerGetEmailTemplate(server, dataDir = DATA_DIR$24) {
5287
5310
  server.registerTool("get_email_template", {
5288
5311
  description: "Get a specific email template by ID, including its body and detected variables.",
5289
5312
  inputSchema: z.object({ id: z.string().describe("Template ID (e.g. 'enterprise-intro')") })
@@ -5291,8 +5314,8 @@ function registerGetEmailTemplate(server, dataDir = DATA_DIR$23) {
5291
5314
  }
5292
5315
  //#endregion
5293
5316
  //#region src/mcp/tools/draft-email.ts
5294
- const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5295
- async function handleDraftEmail(input, dataDir = DATA_DIR$22) {
5317
+ const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5318
+ async function handleDraftEmail(input, dataDir = DATA_DIR$23) {
5296
5319
  const tmpl = getTemplate(dataDir, input.templateId);
5297
5320
  if (!tmpl) return { content: [{
5298
5321
  type: "text",
@@ -5336,7 +5359,7 @@ async function handleDraftEmail(input, dataDir = DATA_DIR$22) {
5336
5359
  }, null, 2)
5337
5360
  }] };
5338
5361
  }
5339
- function registerDraftEmail(server, dataDir = DATA_DIR$22) {
5362
+ function registerDraftEmail(server, dataDir = DATA_DIR$23) {
5340
5363
  server.registerTool("draft_email", {
5341
5364
  description: `Draft a personalized email for a customer using a stored template.
5342
5365
  Variables are auto-filled from the customer's main_facts.md. Override any variable manually.
@@ -5444,8 +5467,8 @@ async function updateEnrollment(dataDir, id, updates) {
5444
5467
  }
5445
5468
  //#endregion
5446
5469
  //#region src/mcp/tools/enroll-in-sequence.ts
5447
- const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5448
- async function handleEnrollInSequence(input, dataDir = DATA_DIR$21) {
5470
+ const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5471
+ async function handleEnrollInSequence(input, dataDir = DATA_DIR$22) {
5449
5472
  const sequence = getSequence(dataDir, input.sequenceId);
5450
5473
  if (!sequence) return { content: [{
5451
5474
  type: "text",
@@ -5477,7 +5500,7 @@ async function handleEnrollInSequence(input, dataDir = DATA_DIR$21) {
5477
5500
  })
5478
5501
  }] };
5479
5502
  }
5480
- function registerEnrollInSequence(server, dataDir = DATA_DIR$21) {
5503
+ function registerEnrollInSequence(server, dataDir = DATA_DIR$22) {
5481
5504
  server.registerTool("enroll_in_sequence", {
5482
5505
  description: `Enroll a contact in an email sequence. Validates that the sequence and its first template exist.
5483
5506
  Returns: { enrollmentId, sequenceName, totalSteps }`,
@@ -5494,8 +5517,8 @@ Returns: { enrollmentId, sequenceName, totalSteps }`,
5494
5517
  }
5495
5518
  //#endregion
5496
5519
  //#region src/mcp/tools/list-sequence-enrollments.ts
5497
- const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5498
- async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$20) {
5520
+ const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5521
+ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$21) {
5499
5522
  let enrollments = readEnrollments(dataDir);
5500
5523
  if (input.slug !== void 0) enrollments = enrollments.filter((e) => e.slug === input.slug);
5501
5524
  if (input.status !== void 0) enrollments = enrollments.filter((e) => e.status === input.status);
@@ -5504,7 +5527,7 @@ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$20) {
5504
5527
  text: JSON.stringify({ enrollments }, null, 2)
5505
5528
  }] };
5506
5529
  }
5507
- function registerListSequenceEnrollments(server, dataDir = DATA_DIR$20) {
5530
+ function registerListSequenceEnrollments(server, dataDir = DATA_DIR$21) {
5508
5531
  server.registerTool("list_sequence_enrollments", {
5509
5532
  description: `List email sequence enrollments. Filter by customer slug or status.
5510
5533
  Returns: { enrollments: SequenceEnrollment[] }`,
@@ -5523,8 +5546,8 @@ Returns: { enrollments: SequenceEnrollment[] }`,
5523
5546
  }
5524
5547
  //#endregion
5525
5548
  //#region src/mcp/tools/unenroll-from-sequence.ts
5526
- const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5527
- async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$19) {
5549
+ const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5550
+ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$20) {
5528
5551
  if (!await updateEnrollment(dataDir, input.enrollmentId, { status: "paused" })) return { content: [{
5529
5552
  type: "text",
5530
5553
  text: JSON.stringify({
@@ -5537,7 +5560,7 @@ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$19) {
5537
5560
  text: JSON.stringify({ success: true })
5538
5561
  }] };
5539
5562
  }
5540
- function registerUnenrollFromSequence(server, dataDir = DATA_DIR$19) {
5563
+ function registerUnenrollFromSequence(server, dataDir = DATA_DIR$20) {
5541
5564
  server.registerTool("unenroll_from_sequence", {
5542
5565
  description: `Unenroll (pause) a contact from an email sequence. Sets status to "paused" (soft delete).
5543
5566
  Returns: { success: boolean }`,
@@ -5546,8 +5569,8 @@ Returns: { success: boolean }`,
5546
5569
  }
5547
5570
  //#endregion
5548
5571
  //#region src/mcp/tools/list-sequences.ts
5549
- const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5550
- async function handleListSequences(_input, dataDir = DATA_DIR$18) {
5572
+ const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5573
+ async function handleListSequences(_input, dataDir = DATA_DIR$19) {
5551
5574
  const sequences = listSequences(dataDir);
5552
5575
  const enrollments = readEnrollments(dataDir);
5553
5576
  const result = sequences.map((seq) => ({
@@ -5561,7 +5584,7 @@ async function handleListSequences(_input, dataDir = DATA_DIR$18) {
5561
5584
  text: JSON.stringify({ sequences: result }, null, 2)
5562
5585
  }] };
5563
5586
  }
5564
- function registerListSequences(server, dataDir = DATA_DIR$18) {
5587
+ function registerListSequences(server, dataDir = DATA_DIR$19) {
5565
5588
  server.registerTool("list_sequences", {
5566
5589
  description: `List all email sequences with step count and enrollment count.
5567
5590
  Returns: { sequences: Array<{ id, name, stepCount, enrollmentCount }> }`,
@@ -5696,8 +5719,8 @@ async function generateQuote(dataDir, input) {
5696
5719
  }
5697
5720
  //#endregion
5698
5721
  //#region src/mcp/tools/generate-quote.ts
5699
- const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5700
- async function handleGenerateQuote(input, dataDir = DATA_DIR$17) {
5722
+ const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5723
+ async function handleGenerateQuote(input, dataDir = DATA_DIR$18) {
5701
5724
  try {
5702
5725
  const quote = await generateQuote(dataDir, input);
5703
5726
  return { content: [{
@@ -5721,7 +5744,7 @@ async function handleGenerateQuote(input, dataDir = DATA_DIR$17) {
5721
5744
  }] };
5722
5745
  }
5723
5746
  }
5724
- function registerGenerateQuote(server, dataDir = DATA_DIR$17) {
5747
+ function registerGenerateQuote(server, dataDir = DATA_DIR$18) {
5725
5748
  server.registerTool("generate_quote", {
5726
5749
  description: `Generate a professional HTML quote/offer for a customer deal.
5727
5750
  Calculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.
@@ -5749,8 +5772,8 @@ Returns: { quoteNumber, htmlPath, total, currency, validUntil }`,
5749
5772
  }
5750
5773
  //#endregion
5751
5774
  //#region src/mcp/tools/get-quote-status.ts
5752
- const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5753
- async function handleGetQuoteStatus(input, dataDir = DATA_DIR$16) {
5775
+ const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5776
+ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$17) {
5754
5777
  if (input.quoteNumber) {
5755
5778
  const quote = readQuote(dataDir, input.quoteNumber);
5756
5779
  if (!quote) return { content: [{
@@ -5768,7 +5791,7 @@ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$16) {
5768
5791
  text: JSON.stringify({ quotes }, null, 2)
5769
5792
  }] };
5770
5793
  }
5771
- function registerGetQuoteStatus(server, dataDir = DATA_DIR$16) {
5794
+ function registerGetQuoteStatus(server, dataDir = DATA_DIR$17) {
5772
5795
  server.registerTool("get_quote_status", {
5773
5796
  description: `Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for a customer).
5774
5797
  Returns quote with status: draft | sent | viewed | accepted | declined`,
@@ -5783,7 +5806,7 @@ Returns quote with status: draft | sent | viewed | accepted | declined`,
5783
5806
  }
5784
5807
  //#endregion
5785
5808
  //#region src/mcp/tools/get-booking-link.ts
5786
- const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5809
+ const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5787
5810
  function loadCalendlyConfig(dataDir) {
5788
5811
  const p = path.join(dataDir, ".agentic", "integrations", "calendly.yaml");
5789
5812
  if (!fs.existsSync(p)) return {};
@@ -5806,7 +5829,7 @@ function readCustomerFacts(dataDir, slug) {
5806
5829
  ...email ? { email } : {}
5807
5830
  };
5808
5831
  }
5809
- async function handleGetBookingLink(input, dataDir = DATA_DIR$15) {
5832
+ async function handleGetBookingLink(input, dataDir = DATA_DIR$16) {
5810
5833
  const config = loadCalendlyConfig(dataDir);
5811
5834
  const apiKey = config.apiKey ?? process.env["CALENDLY_API_KEY"] ?? "";
5812
5835
  if (!apiKey) return { content: [{
@@ -5834,7 +5857,7 @@ async function handleGetBookingLink(input, dataDir = DATA_DIR$15) {
5834
5857
  }] };
5835
5858
  }
5836
5859
  }
5837
- function registerGetBookingLink(server, dataDir = DATA_DIR$15) {
5860
+ function registerGetBookingLink(server, dataDir = DATA_DIR$16) {
5838
5861
  server.registerTool("get_booking_link", {
5839
5862
  description: `Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.
5840
5863
  Requires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.
@@ -6004,8 +6027,8 @@ function calcSlaDue(createdDate, priority, rules) {
6004
6027
  }
6005
6028
  //#endregion
6006
6029
  //#region src/mcp/tools/create-ticket.ts
6007
- const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6008
- async function handleCreateTicket(input, dataDir = DATA_DIR$14) {
6030
+ const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6031
+ async function handleCreateTicket(input, dataDir = DATA_DIR$15) {
6009
6032
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6010
6033
  const rules = loadSlaRules(dataDir);
6011
6034
  const priority = input.priority ?? "normal";
@@ -6027,7 +6050,7 @@ async function handleCreateTicket(input, dataDir = DATA_DIR$14) {
6027
6050
  text: JSON.stringify({ ticket }, null, 2)
6028
6051
  }] };
6029
6052
  }
6030
- function registerCreateTicket(server, dataDir = DATA_DIR$14) {
6053
+ function registerCreateTicket(server, dataDir = DATA_DIR$15) {
6031
6054
  server.registerTool("create_ticket", {
6032
6055
  description: `Create a support ticket for a customer. Auto-calculates SLA due date based on priority.
6033
6056
  Returns: { ticket } with id T-NNN, status=open, slaDue`,
@@ -6053,8 +6076,8 @@ Returns: { ticket } with id T-NNN, status=open, slaDue`,
6053
6076
  }
6054
6077
  //#endregion
6055
6078
  //#region src/mcp/tools/update-ticket.ts
6056
- const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6057
- async function handleUpdateTicket(input, dataDir = DATA_DIR$13) {
6079
+ const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6080
+ async function handleUpdateTicket(input, dataDir = DATA_DIR$14) {
6058
6081
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6059
6082
  if (!ticket) return { content: [{
6060
6083
  type: "text",
@@ -6073,7 +6096,7 @@ async function handleUpdateTicket(input, dataDir = DATA_DIR$13) {
6073
6096
  text: JSON.stringify({ ticket: updated }, null, 2)
6074
6097
  }] };
6075
6098
  }
6076
- function registerUpdateTicket(server, dataDir = DATA_DIR$13) {
6099
+ function registerUpdateTicket(server, dataDir = DATA_DIR$14) {
6077
6100
  server.registerTool("update_ticket", {
6078
6101
  description: `Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.
6079
6102
  Returns: { ticket }`,
@@ -6098,8 +6121,8 @@ Returns: { ticket }`,
6098
6121
  }
6099
6122
  //#endregion
6100
6123
  //#region src/mcp/tools/list-tickets.ts
6101
- const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6102
- async function handleListTickets(input, dataDir = DATA_DIR$12) {
6124
+ const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6125
+ async function handleListTickets(input, dataDir = DATA_DIR$13) {
6103
6126
  const results = await listAllTickets(dataDir, {
6104
6127
  ...input.slug !== void 0 ? { slug: input.slug } : {},
6105
6128
  ...input.status !== void 0 ? { status: input.status } : {},
@@ -6111,7 +6134,7 @@ async function handleListTickets(input, dataDir = DATA_DIR$12) {
6111
6134
  text: JSON.stringify({ tickets: results }, null, 2)
6112
6135
  }] };
6113
6136
  }
6114
- function registerListTickets(server, dataDir = DATA_DIR$12) {
6137
+ function registerListTickets(server, dataDir = DATA_DIR$13) {
6115
6138
  server.registerTool("list_tickets", {
6116
6139
  description: `List support tickets. Filter by customer, status, priority, or assignee. Sorted by priority then date.
6117
6140
  Returns: { tickets: Array<{ slug, ticket }> }`,
@@ -6141,8 +6164,8 @@ Returns: { tickets: Array<{ slug, ticket }> }`,
6141
6164
  }
6142
6165
  //#endregion
6143
6166
  //#region src/mcp/tools/close-ticket.ts
6144
- const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6145
- async function handleCloseTicket(input, dataDir = DATA_DIR$11) {
6167
+ const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6168
+ async function handleCloseTicket(input, dataDir = DATA_DIR$12) {
6146
6169
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6147
6170
  if (!ticket) return { content: [{
6148
6171
  type: "text",
@@ -6169,7 +6192,7 @@ async function handleCloseTicket(input, dataDir = DATA_DIR$11) {
6169
6192
  text: JSON.stringify({ ticket: updated }, null, 2)
6170
6193
  }] };
6171
6194
  }
6172
- function registerCloseTicket(server, dataDir = DATA_DIR$11) {
6195
+ function registerCloseTicket(server, dataDir = DATA_DIR$12) {
6173
6196
  server.registerTool("close_ticket", {
6174
6197
  description: `Close a support ticket. Optionally logs the resolution as an interaction.
6175
6198
  Returns: { ticket } with status=closed`,
@@ -6323,8 +6346,8 @@ async function savePendingSurvey(dataDir, surveyId, slug, contactEmail, token) {
6323
6346
  }
6324
6347
  //#endregion
6325
6348
  //#region src/mcp/tools/send-nps-survey.ts
6326
- const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6327
- async function handleSendNpsSurvey(input, dataDir = DATA_DIR$10) {
6349
+ const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6350
+ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$11) {
6328
6351
  const survey = getSurvey(dataDir, input.surveyId);
6329
6352
  if (!survey) return { content: [{
6330
6353
  type: "text",
@@ -6345,7 +6368,7 @@ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$10) {
6345
6368
  }, null, 2)
6346
6369
  }] };
6347
6370
  }
6348
- function registerSendNpsSurvey(server, dataDir = DATA_DIR$10) {
6371
+ function registerSendNpsSurvey(server, dataDir = DATA_DIR$11) {
6349
6372
  server.registerTool("send_nps_survey", {
6350
6373
  description: `Generate an NPS/CSAT survey email for a customer contact. Returns subject, HTML body, and a token-based response URL.
6351
6374
  Does NOT send automatically — returns draft for review.
@@ -6365,8 +6388,8 @@ Returns: { token, subject, body, surveyUrl }`,
6365
6388
  }
6366
6389
  //#endregion
6367
6390
  //#region src/mcp/tools/get-survey-results.ts
6368
- const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6369
- async function handleGetSurveyResults(input, dataDir = DATA_DIR$9) {
6391
+ const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6392
+ async function handleGetSurveyResults(input, dataDir = DATA_DIR$10) {
6370
6393
  const responses = loadSurveyResponses(dataDir, input.surveyId, input.slug);
6371
6394
  const nps = calcNpsScore(responses);
6372
6395
  const promoters = responses.filter((r) => r.score >= 9).length;
@@ -6392,7 +6415,7 @@ async function handleGetSurveyResults(input, dataDir = DATA_DIR$9) {
6392
6415
  }, null, 2)
6393
6416
  }] };
6394
6417
  }
6395
- function registerGetSurveyResults(server, dataDir = DATA_DIR$9) {
6418
+ function registerGetSurveyResults(server, dataDir = DATA_DIR$10) {
6396
6419
  server.registerTool("get_survey_results", {
6397
6420
  description: `Get NPS/CSAT survey results with score breakdown. Calculates Net Promoter Score.
6398
6421
  Returns: { npsScore, totalResponses, promoters, passives, detractors, responses[] }`,
@@ -6493,8 +6516,8 @@ function getKbMetaForExport(article) {
6493
6516
  }
6494
6517
  //#endregion
6495
6518
  //#region src/mcp/tools/search-knowledge-base.ts
6496
- const DATA_DIR$8 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6497
- async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$8) {
6519
+ const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6520
+ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$9) {
6498
6521
  const results = searchKbSimple(dataDir, input.query, { ...input.publicOnly ? { publicOnly: true } : {} });
6499
6522
  const limited = (input.category ? results.filter((a) => a.category === input.category) : results).slice(0, input.limit ?? 10);
6500
6523
  return { content: [{
@@ -6509,7 +6532,7 @@ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$8) {
6509
6532
  }, null, 2)
6510
6533
  }] };
6511
6534
  }
6512
- function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$8) {
6535
+ function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$9) {
6513
6536
  server.registerTool("search_knowledge_base", {
6514
6537
  description: `Search the knowledge base for articles. Text search on title, body, and tags.
6515
6538
  Returns: { count, articles[] } with excerpts`,
@@ -6528,8 +6551,8 @@ Returns: { count, articles[] } with excerpts`,
6528
6551
  }
6529
6552
  //#endregion
6530
6553
  //#region src/mcp/tools/create-kb-article.ts
6531
- const DATA_DIR$7 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6532
- async function handleCreateKbArticle(input, dataDir = DATA_DIR$7) {
6554
+ const DATA_DIR$8 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6555
+ async function handleCreateKbArticle(input, dataDir = DATA_DIR$8) {
6533
6556
  if (getKbArticle(dataDir, input.id)) return { content: [{
6534
6557
  type: "text",
6535
6558
  text: JSON.stringify({ error: `Article '${input.id}' already exists` })
@@ -6557,7 +6580,7 @@ async function handleCreateKbArticle(input, dataDir = DATA_DIR$7) {
6557
6580
  }, null, 2)
6558
6581
  }] };
6559
6582
  }
6560
- function registerCreateKbArticle(server, dataDir = DATA_DIR$7) {
6583
+ function registerCreateKbArticle(server, dataDir = DATA_DIR$8) {
6561
6584
  server.registerTool("create_kb_article", {
6562
6585
  description: `Create a new knowledge base article. Articles are stored as Markdown files in .agentic/knowledge-base/.
6563
6586
  Returns: { id, title, category, path }`,
@@ -6582,8 +6605,8 @@ Returns: { id, title, category, path }`,
6582
6605
  }
6583
6606
  //#endregion
6584
6607
  //#region src/mcp/tools/backup-now.ts
6585
- const DATA_DIR$6 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6586
- async function handleBackupNow(input, dataDir = DATA_DIR$6) {
6608
+ const DATA_DIR$7 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6609
+ async function handleBackupNow(input, dataDir = DATA_DIR$7) {
6587
6610
  const zipPath = path.join(dataDir, `dxcrm-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.zip`);
6588
6611
  const manifest = await runBackup(zipPath, dataDir, { ...input.remote ? { remote: input.remote } : {} }).catch(() => null);
6589
6612
  if (!manifest) return { content: [{
@@ -6620,8 +6643,8 @@ function registerBackupNow(server) {
6620
6643
  }
6621
6644
  //#endregion
6622
6645
  //#region src/mcp/tools/list-backups.ts
6623
- const DATA_DIR$5 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6624
- async function handleListBackups(input, dataDir = DATA_DIR$5) {
6646
+ const DATA_DIR$6 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6647
+ async function handleListBackups(input, dataDir = DATA_DIR$6) {
6625
6648
  const logEntries = readBackupLog(dataDir);
6626
6649
  const fileEntries = listBackupsInDir(dataDir);
6627
6650
  const entries = logEntries.length > 0 ? logEntries : fileEntries;
@@ -6655,8 +6678,8 @@ function registerListBackups(server) {
6655
6678
  }
6656
6679
  //#endregion
6657
6680
  //#region src/mcp/tools/trigger-sync.ts
6658
- const DATA_DIR$4 = process.cwd();
6659
- async function handleTriggerSync(input, dataDir = DATA_DIR$4) {
6681
+ const DATA_DIR$5 = process.cwd();
6682
+ async function handleTriggerSync(input, dataDir = DATA_DIR$5) {
6660
6683
  const auth = getGmailAuth();
6661
6684
  if (!auth) return { content: [{
6662
6685
  type: "text",
@@ -6691,7 +6714,7 @@ async function handleTriggerSync(input, dataDir = DATA_DIR$4) {
6691
6714
  try {
6692
6715
  const sources = JSON.parse(fs.readFileSync(sourcesPath, "utf-8"));
6693
6716
  if (!sources.gmail?.enabled || !sources.gmail.query) continue;
6694
- const { syncGmail } = await import("./gmail-sync-C-NmibzS.js");
6717
+ const { syncGmail } = await import("./gmail-sync-DIbrPnTK.js");
6695
6718
  const result = await syncGmail({
6696
6719
  slug,
6697
6720
  dataDir,
@@ -6750,8 +6773,8 @@ Returns: { success: boolean, synced: number, skipped: number, customers: [...],
6750
6773
  }
6751
6774
  //#endregion
6752
6775
  //#region src/mcp/tools/get-audit-log.ts
6753
- const DATA_DIR$3 = process.cwd();
6754
- async function handleGetAuditLog(input, dataDir = DATA_DIR$3) {
6776
+ const DATA_DIR$4 = process.cwd();
6777
+ async function handleGetAuditLog(input, dataDir = DATA_DIR$4) {
6755
6778
  const entries = readAuditLog(dataDir);
6756
6779
  const filterOpts = { limit: input.limit ?? 50 };
6757
6780
  if (input.slug !== void 0) filterOpts.slug = input.slug;
@@ -6793,8 +6816,8 @@ Returns: { total: number, returned: number, entries: [{timestamp, actor, tool, s
6793
6816
  }
6794
6817
  //#endregion
6795
6818
  //#region src/mcp/tools/get-logs.ts
6796
- const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6797
- async function handleGetLogs(input, dataDir = DATA_DIR$2) {
6819
+ const DATA_DIR$3 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6820
+ async function handleGetLogs(input, dataDir = DATA_DIR$3) {
6798
6821
  const query = {
6799
6822
  ...input.level !== void 0 ? { level: input.level } : {},
6800
6823
  ...input.component !== void 0 ? { component: input.component } : {},
@@ -6855,6 +6878,140 @@ Returns (summary): { total, byLevel, byComponent, firstTs, lastTs, recentErrors
6855
6878
  });
6856
6879
  }
6857
6880
  //#endregion
6881
+ //#region src/core/doctor.ts
6882
+ var doctor_exports = /* @__PURE__ */ __exportAll({
6883
+ cleanupTempFiles: () => cleanupTempFiles,
6884
+ runDiagnostics: () => runDiagnostics
6885
+ });
6886
+ /** Recursively collect files whose name matches the atomic-write temp pattern. */
6887
+ function findOrphanedTempFiles(dir, depth = 0) {
6888
+ if (depth > 3 || !fs.existsSync(dir)) return [];
6889
+ const out = [];
6890
+ let entries;
6891
+ try {
6892
+ entries = fs.readdirSync(dir);
6893
+ } catch {
6894
+ return [];
6895
+ }
6896
+ for (const entry of entries) {
6897
+ const full = path.join(dir, entry);
6898
+ let isDir = false;
6899
+ try {
6900
+ isDir = fs.statSync(full).isDirectory();
6901
+ } catch {
6902
+ continue;
6903
+ }
6904
+ if (isDir) out.push(...findOrphanedTempFiles(full, depth + 1));
6905
+ else if (/\.\d+\.[0-9a-f]+\.tmp$/.test(entry)) out.push(full);
6906
+ }
6907
+ return out;
6908
+ }
6909
+ /** Delete orphaned atomic-write temp files; returns the paths removed. */
6910
+ function cleanupTempFiles(dataDir) {
6911
+ const temps = [...findOrphanedTempFiles(path.join(dataDir, ".agentic")), ...findOrphanedTempFiles(path.join(dataDir, "customers"))];
6912
+ const removed = [];
6913
+ for (const f of temps) try {
6914
+ fs.rmSync(f, { force: true });
6915
+ removed.push(f);
6916
+ } catch {}
6917
+ return removed;
6918
+ }
6919
+ async function runDiagnostics(dataDir) {
6920
+ const checks = [];
6921
+ const agenticDir = path.join(dataDir, ".agentic");
6922
+ const customersDir = path.join(dataDir, "customers");
6923
+ if (!fs.existsSync(agenticDir) && !fs.existsSync(customersDir)) checks.push({
6924
+ name: "data directory",
6925
+ status: "fail",
6926
+ detail: `Neither .agentic/ nor customers/ found under ${dataDir} — run 'dxcrm init'`
6927
+ });
6928
+ else checks.push({
6929
+ name: "data directory",
6930
+ status: "ok",
6931
+ detail: dataDir
6932
+ });
6933
+ const slugs = listCustomerSlugs(dataDir);
6934
+ const invalid = [];
6935
+ for (const slug of slugs) try {
6936
+ await readMainFacts(dataDir, slug);
6937
+ } catch {
6938
+ invalid.push(slug);
6939
+ }
6940
+ checks.push({
6941
+ name: "customer data",
6942
+ status: invalid.length > 0 ? "fail" : "ok",
6943
+ detail: invalid.length > 0 ? `${invalid.length} of ${slugs.length} invalid: ${invalid.slice(0, 5).join(", ")}` : `${slugs.length} customer(s) valid`
6944
+ });
6945
+ const temps = [...findOrphanedTempFiles(agenticDir), ...findOrphanedTempFiles(customersDir)];
6946
+ checks.push({
6947
+ name: "temp files",
6948
+ status: temps.length > 0 ? "warn" : "ok",
6949
+ detail: temps.length > 0 ? `${temps.length} orphaned temp file(s) from interrupted writes — safe to delete` : "no orphaned temp files"
6950
+ });
6951
+ const summary = summarizeLogs(dataDir);
6952
+ const errorCount = summary.byLevel.error;
6953
+ checks.push({
6954
+ name: "logs",
6955
+ status: errorCount > 0 ? "warn" : "ok",
6956
+ detail: errorCount > 0 ? `${errorCount} error entr${errorCount === 1 ? "y" : "ies"} in the log (dxcrm logs --level error)` : `${summary.total} log entr${summary.total === 1 ? "y" : "ies"}, no errors`
6957
+ });
6958
+ const backupLogPath = path.join(agenticDir, "backup-log.json");
6959
+ if (fs.existsSync(backupLogPath)) try {
6960
+ const entries = JSON.parse(fs.readFileSync(backupLogPath, "utf-8"));
6961
+ const last = entries[entries.length - 1]?.createdAt;
6962
+ const ageDays = last ? Math.floor((Date.now() - new Date(last).getTime()) / 864e5) : Infinity;
6963
+ checks.push({
6964
+ name: "backups",
6965
+ status: ageDays > 7 ? "warn" : "ok",
6966
+ detail: last ? `last backup ${ageDays}d ago` : "no backups recorded"
6967
+ });
6968
+ } catch {
6969
+ checks.push({
6970
+ name: "backups",
6971
+ status: "warn",
6972
+ detail: "backup log unreadable"
6973
+ });
6974
+ }
6975
+ return {
6976
+ ok: !checks.some((c) => c.status === "fail"),
6977
+ checks
6978
+ };
6979
+ }
6980
+ //#endregion
6981
+ //#region src/mcp/tools/get-diagnostics.ts
6982
+ const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6983
+ async function handleGetDiagnostics(input, dataDir = DATA_DIR$2) {
6984
+ let cleaned = 0;
6985
+ if (input.fix) {
6986
+ const { cleanupTempFiles } = await Promise.resolve().then(() => doctor_exports);
6987
+ cleaned = cleanupTempFiles(dataDir).length;
6988
+ }
6989
+ const report = await runDiagnostics(dataDir);
6990
+ return { content: [{
6991
+ type: "text",
6992
+ text: JSON.stringify({
6993
+ ok: report.ok,
6994
+ ...input.fix ? { tempFilesRemoved: cleaned } : {},
6995
+ checks: report.checks
6996
+ }, null, 2)
6997
+ }] };
6998
+ }
6999
+ function registerGetDiagnostics(server) {
7000
+ server.registerTool("get_diagnostics", {
7001
+ title: "Get Diagnostics",
7002
+ description: `Run a self-diagnostic health check of the CRM workspace.
7003
+ Verifies the data directory, validates every customer's profile, detects orphaned
7004
+ atomic-write temp files (a crash signature), surfaces recent log errors, and checks
7005
+ backup freshness. Use to answer "is everything healthy?" before/after bulk operations.
7006
+
7007
+ Args:
7008
+ fix: When true, first remove orphaned temp files (the only safely auto-fixable issue)
7009
+
7010
+ Returns: { ok: boolean, tempFilesRemoved?: number, checks: [{ name, status: "ok"|"warn"|"fail", detail }] }`,
7011
+ inputSchema: z.object({ fix: z.boolean().optional().describe("Clean orphaned temp files before reporting") })
7012
+ }, async ({ fix }) => handleGetDiagnostics(fix !== void 0 ? { fix } : {}));
7013
+ }
7014
+ //#endregion
6858
7015
  //#region src/mcp/prompts.ts
6859
7016
  /**
6860
7017
  * CRM playbook prompts exposed via MCP `prompts/list` + `prompts/get`.
@@ -6949,7 +7106,7 @@ function registerResources(server, dataDir = DATA_DIR$1) {
6949
7106
  description: "Newest-first interaction history for a customer",
6950
7107
  mimeType: "text/markdown"
6951
7108
  }, async (uri, variables) => {
6952
- const { readInteractions } = await import("./interactions-writer-BZzUIgJd.js").then((n) => n.r);
7109
+ const { readInteractions } = await import("./interactions-writer-ZQcpFOh9.js").then((n) => n.r);
6953
7110
  const text = await readInteractions(dataDir, String(variables["slug"]));
6954
7111
  return { contents: [{
6955
7112
  uri: uri.href,
@@ -7271,6 +7428,7 @@ function createMcpServer() {
7271
7428
  registerTriggerSync(server);
7272
7429
  registerGetAuditLog(server);
7273
7430
  registerGetLogs(server);
7431
+ registerGetDiagnostics(server);
7274
7432
  registerCustomObjectTools(server);
7275
7433
  registerPrompts(server);
7276
7434
  registerResources(server);