@datasynx/agentic-crm 1.1.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.
package/dist/mcp.js CHANGED
@@ -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 |
@@ -1421,7 +1422,7 @@ async function buildContext(dataDir, slug) {
1421
1422
  }
1422
1423
  //#endregion
1423
1424
  //#region src/mcp/tools/get-customer-context.ts
1424
- const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1425
+ const DATA_DIR$53 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1425
1426
  function triggerOnQuerySync(dataDir, slug) {
1426
1427
  const auth = getGmailAuth();
1427
1428
  if (!auth) return;
@@ -1442,7 +1443,7 @@ function triggerOnQuerySync(dataDir, slug) {
1442
1443
  }).then(() => updateSlugSyncState(dataDir, slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() })).catch(() => {})).catch(() => {});
1443
1444
  } catch {}
1444
1445
  }
1445
- async function handleGetCustomerContext(input, dataDir = DATA_DIR$52) {
1446
+ async function handleGetCustomerContext(input, dataDir = DATA_DIR$53) {
1446
1447
  const targetSlug = input.slug ?? getSession()?.customerSlug;
1447
1448
  if (!targetSlug) return {
1448
1449
  content: [{
@@ -1578,8 +1579,8 @@ async function searchKnowledge(dataDir, slug, query, limit) {
1578
1579
  }
1579
1580
  //#endregion
1580
1581
  //#region src/mcp/tools/search-customer-knowledge.ts
1581
- const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1582
- 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) {
1583
1584
  const limit = input.limit ?? 5;
1584
1585
  try {
1585
1586
  const results = await searchKnowledge(dataDir, input.slug, input.query, limit);
@@ -1627,14 +1628,14 @@ If no results: returns empty array with a helpful sync suggestion.`,
1627
1628
  }
1628
1629
  //#endregion
1629
1630
  //#region src/mcp/tools/list-customers.ts
1630
- const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1631
+ const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1631
1632
  function extractLastInteractionDate(interactionsPath) {
1632
1633
  if (!fs.existsSync(interactionsPath)) return void 0;
1633
1634
  const content = fs.readFileSync(interactionsPath, "utf-8");
1634
1635
  const match = /^## (\d{4}-\d{2}-\d{2})/m.exec(content);
1635
1636
  return match ? match[1] : void 0;
1636
1637
  }
1637
- async function handleListCustomers(input, dataDir = DATA_DIR$50) {
1638
+ async function handleListCustomers(input, dataDir = DATA_DIR$51) {
1638
1639
  const customersDir = path.join(dataDir, "customers");
1639
1640
  const customers = [];
1640
1641
  if (!fs.existsSync(customersDir)) return { content: [{
@@ -2147,8 +2148,8 @@ async function updateHealthFromInteraction(dataDir, slug) {
2147
2148
  }
2148
2149
  //#endregion
2149
2150
  //#region src/mcp/tools/log-interaction.ts
2150
- const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2151
- 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) {
2152
2153
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2153
2154
  const interactionDate = input.date ?? today;
2154
2155
  const sourceRef = input.source ?? `agent://log/${Date.now()}`;
@@ -2257,8 +2258,8 @@ var update_deal_exports = /* @__PURE__ */ __exportAll({
2257
2258
  handleUpdateDeal: () => handleUpdateDeal,
2258
2259
  registerUpdateDeal: () => registerUpdateDeal
2259
2260
  });
2260
- const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2261
- 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) {
2262
2263
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2263
2264
  const deal = {
2264
2265
  name: input.dealName,
@@ -2341,12 +2342,12 @@ Returns: { success: boolean, deal: object }`,
2341
2342
  }
2342
2343
  //#endregion
2343
2344
  //#region src/mcp/tools/export-customer.ts
2344
- const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2345
+ const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2345
2346
  function countInteractions(content) {
2346
2347
  const matches = content.match(/^## \d{4}-\d{2}-\d{2}/gm);
2347
2348
  return matches ? matches.length : 0;
2348
2349
  }
2349
- async function handleExportCustomer(input, dataDir = DATA_DIR$47) {
2350
+ async function handleExportCustomer(input, dataDir = DATA_DIR$48) {
2350
2351
  enforceRbac(dataDir, "export_customer");
2351
2352
  const customerDir = path.join(dataDir, "customers", input.slug);
2352
2353
  if (!fs.existsSync(customerDir)) return {
@@ -2459,8 +2460,8 @@ Returns:
2459
2460
  }
2460
2461
  //#endregion
2461
2462
  //#region src/mcp/tools/update-customer-facts.ts
2462
- const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2463
- 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) {
2464
2465
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2465
2466
  try {
2466
2467
  enforceRbac(dataDir, "update_customer_facts");
@@ -2638,8 +2639,8 @@ function scoreDealForToday(deal, todayDate) {
2638
2639
  }
2639
2640
  //#endregion
2640
2641
  //#region src/mcp/tools/get-deal-health.ts
2641
- const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2642
- 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) {
2643
2644
  try {
2644
2645
  const deals = await readPipeline(dataDir, input.slug);
2645
2646
  const today = /* @__PURE__ */ new Date();
@@ -2688,8 +2689,8 @@ Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }`,
2688
2689
  }
2689
2690
  //#endregion
2690
2691
  //#region src/mcp/tools/get-pipeline-forecast.ts
2691
- const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2692
- 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) {
2693
2694
  try {
2694
2695
  const slugs = listCustomerSlugs(dataDir).filter((d) => !input.filter || d.includes(input.filter));
2695
2696
  const allDeals = [];
@@ -2750,8 +2751,8 @@ Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count,
2750
2751
  }
2751
2752
  //#endregion
2752
2753
  //#region src/mcp/tools/summarize-meeting.ts
2753
- const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2754
- 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) {
2755
2756
  try {
2756
2757
  let summary = input.transcript.slice(0, 400);
2757
2758
  let nextSteps = [];
@@ -2874,8 +2875,8 @@ function getPipelineStages(dataDir) {
2874
2875
  }
2875
2876
  //#endregion
2876
2877
  //#region src/mcp/tools/get-pipeline-stages.ts
2877
- const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2878
- 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) {
2879
2880
  const stages = getPipelineStages(dataDir);
2880
2881
  return { content: [{
2881
2882
  type: "text",
@@ -2903,8 +2904,8 @@ async function searchAcrossCustomers(dataDir, query, limit = 5, excludeSlug) {
2903
2904
  }
2904
2905
  //#endregion
2905
2906
  //#region src/mcp/tools/get-market-intelligence.ts
2906
- const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2907
- 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) {
2908
2909
  const excludeSlug = input.excludeCurrentCustomer ? input.slug : void 0;
2909
2910
  const all = listCustomerSlugs(dataDir);
2910
2911
  const totalCustomersSearched = excludeSlug ? all.filter((s) => s !== excludeSlug).length : all.length;
@@ -2935,7 +2936,7 @@ function registerGetMarketIntelligence(server) {
2935
2936
  }
2936
2937
  //#endregion
2937
2938
  //#region src/mcp/tools/get-relationship-graph.ts
2938
- const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2939
+ const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2939
2940
  function summarizeNode(n) {
2940
2941
  return {
2941
2942
  id: n.id,
@@ -2943,7 +2944,7 @@ function summarizeNode(n) {
2943
2944
  email: n.properties["email"]
2944
2945
  };
2945
2946
  }
2946
- async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$40) {
2947
+ async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$41) {
2947
2948
  try {
2948
2949
  const graph = readGraph(dataDir, input.slug);
2949
2950
  const stakeholders = getStakeholders(graph);
@@ -3011,9 +3012,9 @@ Returns: {
3011
3012
  }
3012
3013
  //#endregion
3013
3014
  //#region src/mcp/tools/get-relationship-health.ts
3014
- const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3015
+ const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3015
3016
  const MAX_HEALTH_AGE_MS = 3600 * 1e3;
3016
- async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$39) {
3017
+ async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$40) {
3017
3018
  try {
3018
3019
  let health = readHealth(dataDir, input.slug);
3019
3020
  if (health === null || Date.now() - new Date(health.updatedAt).getTime() > MAX_HEALTH_AGE_MS) {
@@ -3682,8 +3683,8 @@ async function runDealAgent(config, dataDir, llmFn = callLlm) {
3682
3683
  }
3683
3684
  //#endregion
3684
3685
  //#region src/mcp/tools/run-deal-agent.ts
3685
- const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3686
- 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) {
3687
3688
  try {
3688
3689
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3689
3690
  const result = await runDealAgent({
@@ -3750,8 +3751,8 @@ Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], tr
3750
3751
  }
3751
3752
  //#endregion
3752
3753
  //#region src/mcp/tools/approve-agent-action.ts
3753
- const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3754
- 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) {
3755
3756
  try {
3756
3757
  const queue = readAgentQueue(dataDir, input.slug);
3757
3758
  const idx = queue.pendingActions.findIndex((a) => a.actionId === input.actionId);
@@ -4011,8 +4012,8 @@ async function buildSimulationInput(dataDir, horizon, today, externalSignals = [
4011
4012
  }
4012
4013
  //#endregion
4013
4014
  //#region src/mcp/tools/simulate-revenue.ts
4014
- const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4015
- 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) {
4016
4017
  try {
4017
4018
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4018
4019
  const horizon = input.horizon ?? "quarter";
@@ -4070,8 +4071,8 @@ Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMo
4070
4071
  }
4071
4072
  //#endregion
4072
4073
  //#region src/mcp/tools/get-playbook.ts
4073
- const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4074
- 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) {
4075
4076
  try {
4076
4077
  const playbooks = listPlaybooks(dataDir, input.slug);
4077
4078
  if (!(input.stage !== void 0 || input.value !== void 0 || input.healthScore !== void 0)) return { content: [{
@@ -4156,12 +4157,12 @@ Returns: { matches: [{ name, score, trigger, successRate, usedCount, content }],
4156
4157
  ...healthScore !== void 0 ? { healthScore } : {},
4157
4158
  ...daysSinceContact !== void 0 ? { daysSinceContact } : {},
4158
4159
  ...championPresent !== void 0 ? { championPresent } : {}
4159
- }, DATA_DIR$35));
4160
+ }, DATA_DIR$36));
4160
4161
  }
4161
4162
  //#endregion
4162
4163
  //#region src/mcp/tools/create-playbook.ts
4163
- const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4164
- 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) {
4165
4166
  try {
4166
4167
  const name = toKebabCase(input.name);
4167
4168
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -4234,12 +4235,12 @@ Returns: { success: true, playbook: { name, trigger, successRate, path } }`,
4234
4235
  trigger,
4235
4236
  content,
4236
4237
  ...successRate !== void 0 ? { successRate } : {}
4237
- }, DATA_DIR$34));
4238
+ }, DATA_DIR$35));
4238
4239
  }
4239
4240
  //#endregion
4240
4241
  //#region src/mcp/tools/list-playbooks.ts
4241
- const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4242
- 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) {
4243
4244
  try {
4244
4245
  const playbooks = listPlaybooks(dataDir, input.slug);
4245
4246
  return { content: [{
@@ -4278,12 +4279,12 @@ Args:
4278
4279
 
4279
4280
  Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }`,
4280
4281
  inputSchema: z.object({ slug: z.string().describe("Customer ID") })
4281
- }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$33));
4282
+ }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$34));
4282
4283
  }
4283
4284
  //#endregion
4284
4285
  //#region src/mcp/tools/distill-playbook.ts
4285
- const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4286
- 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) {
4287
4288
  try {
4288
4289
  const result = await distillPlaybook(dataDir, input.slug, input.dealName, input.outcome, llmFn);
4289
4290
  if (!result.ok) {
@@ -4342,7 +4343,7 @@ Returns: { success: true, playbook: { name, trigger, successRate, path }, reason
4342
4343
  slug,
4343
4344
  dealName,
4344
4345
  outcome
4345
- }, DATA_DIR$32));
4346
+ }, DATA_DIR$33));
4346
4347
  }
4347
4348
  //#endregion
4348
4349
  //#region src/core/goal-engine.ts
@@ -4560,8 +4561,8 @@ function getActiveGoals(dataDir) {
4560
4561
  }
4561
4562
  //#endregion
4562
4563
  //#region src/mcp/tools/pursue-goal.ts
4563
- const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4564
- 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 = {}) {
4565
4566
  try {
4566
4567
  enforceRbac(dataDir, "pursue_goal");
4567
4568
  const goal = await pursueGoal(dataDir, {
@@ -4624,12 +4625,12 @@ Returns: { goalId, description, target, deadline, decomposition: { analysis, cur
4624
4625
  goal,
4625
4626
  deadline,
4626
4627
  ...context !== void 0 ? { context } : {}
4627
- }, DATA_DIR$31));
4628
+ }, DATA_DIR$32));
4628
4629
  }
4629
4630
  //#endregion
4630
4631
  //#region src/mcp/tools/get-goal-status.ts
4631
- const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4632
- 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) {
4633
4634
  try {
4634
4635
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4635
4636
  const allGoals = input.goalId ? readGoals(dataDir).filter((g) => g.id === input.goalId) : getActiveGoals(dataDir);
@@ -4688,17 +4689,17 @@ Args:
4688
4689
 
4689
4690
  Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }`,
4690
4691
  inputSchema: z.object({ goalId: z.string().optional().describe("Specific goal ID (omit for all active goals)") })
4691
- }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$30));
4692
+ }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$31));
4692
4693
  }
4693
4694
  //#endregion
4694
4695
  //#region src/mcp/tools/register-push-subscription.ts
4695
- const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4696
+ const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4696
4697
  const VALID_PROVIDERS = [
4697
4698
  "gmail",
4698
4699
  "microsoft-graph",
4699
4700
  "slack"
4700
4701
  ];
4701
- async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$29) {
4702
+ async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$30) {
4702
4703
  try {
4703
4704
  if (!VALID_PROVIDERS.includes(input.provider)) return { content: [{
4704
4705
  type: "text",
@@ -4784,12 +4785,12 @@ Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning
4784
4785
  ...microsoftResource !== void 0 ? { microsoftResource } : {},
4785
4786
  ...slackTeamId !== void 0 ? { slackTeamId } : {},
4786
4787
  ...slackChannelId !== void 0 ? { slackChannelId } : {}
4787
- }, DATA_DIR$29));
4788
+ }, DATA_DIR$30));
4788
4789
  }
4789
4790
  //#endregion
4790
4791
  //#region src/mcp/tools/get-push-status.ts
4791
- const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4792
- 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) {
4793
4794
  try {
4794
4795
  let subs = await readSubscriptions(dataDir);
4795
4796
  if (input.slug) subs = subs.filter((s) => s.slug === input.slug);
@@ -4861,7 +4862,7 @@ Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHou
4861
4862
  }, async ({ slug, provider }) => handleGetPushStatus({
4862
4863
  ...slug !== void 0 ? { slug } : {},
4863
4864
  ...provider !== void 0 ? { provider } : {}
4864
- }, DATA_DIR$28));
4865
+ }, DATA_DIR$29));
4865
4866
  }
4866
4867
  //#endregion
4867
4868
  //#region src/core/org-intelligence.ts
@@ -4927,8 +4928,8 @@ function deriveRecommendation(people, missingRoles) {
4927
4928
  }
4928
4929
  //#endregion
4929
4930
  //#region src/mcp/tools/get-org-intelligence.ts
4930
- const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4931
- 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) {
4932
4933
  try {
4933
4934
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4934
4935
  const map = buildStakeholderMap(dataDir, input.slug, today, input.dealName);
@@ -5061,8 +5062,8 @@ function buildExecutiveSummary(slug, dealName, stakeholders, overallHealth, sim,
5061
5062
  }
5062
5063
  //#endregion
5063
5064
  //#region src/mcp/tools/open-deal-room.ts
5064
- const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5065
- 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) {
5066
5067
  try {
5067
5068
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5068
5069
  const brief = await buildDealRoom(dataDir, input.slug, input.dealName, today);
@@ -5145,8 +5146,8 @@ async function buildDailyBriefing(dataDir, today) {
5145
5146
  }
5146
5147
  //#endregion
5147
5148
  //#region src/mcp/tools/get-proactive-briefing.ts
5148
- const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5149
- 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) {
5150
5151
  try {
5151
5152
  const briefing = await buildDailyBriefing(dataDir, input.date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
5152
5153
  return { content: [{
@@ -5246,15 +5247,15 @@ function getTemplate(dataDir, id) {
5246
5247
  }
5247
5248
  //#endregion
5248
5249
  //#region src/mcp/tools/list-email-templates.ts
5249
- const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5250
- 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) {
5251
5252
  const summary = listTemplates(dataDir, input.category ? { category: input.category } : {}).map(({ body: _body, ...meta }) => meta);
5252
5253
  return { content: [{
5253
5254
  type: "text",
5254
5255
  text: JSON.stringify(summary, null, 2)
5255
5256
  }] };
5256
5257
  }
5257
- function registerListEmailTemplates(server, dataDir = DATA_DIR$24) {
5258
+ function registerListEmailTemplates(server, dataDir = DATA_DIR$25) {
5258
5259
  server.registerTool("list_email_templates", {
5259
5260
  description: "List available email templates. Optionally filter by category (e.g. 'outreach', 'followup', 'support').",
5260
5261
  inputSchema: z.object({ category: z.string().optional().describe("Filter by category") })
@@ -5288,8 +5289,8 @@ async function buildVariablesFromCustomer(dataDir, slug) {
5288
5289
  }
5289
5290
  //#endregion
5290
5291
  //#region src/mcp/tools/get-email-template.ts
5291
- const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5292
- 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) {
5293
5294
  const tmpl = getTemplate(dataDir, input.id);
5294
5295
  if (!tmpl) return { content: [{
5295
5296
  type: "text",
@@ -5305,7 +5306,7 @@ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$23) {
5305
5306
  }, null, 2)
5306
5307
  }] };
5307
5308
  }
5308
- function registerGetEmailTemplate(server, dataDir = DATA_DIR$23) {
5309
+ function registerGetEmailTemplate(server, dataDir = DATA_DIR$24) {
5309
5310
  server.registerTool("get_email_template", {
5310
5311
  description: "Get a specific email template by ID, including its body and detected variables.",
5311
5312
  inputSchema: z.object({ id: z.string().describe("Template ID (e.g. 'enterprise-intro')") })
@@ -5313,8 +5314,8 @@ function registerGetEmailTemplate(server, dataDir = DATA_DIR$23) {
5313
5314
  }
5314
5315
  //#endregion
5315
5316
  //#region src/mcp/tools/draft-email.ts
5316
- const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5317
- 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) {
5318
5319
  const tmpl = getTemplate(dataDir, input.templateId);
5319
5320
  if (!tmpl) return { content: [{
5320
5321
  type: "text",
@@ -5358,7 +5359,7 @@ async function handleDraftEmail(input, dataDir = DATA_DIR$22) {
5358
5359
  }, null, 2)
5359
5360
  }] };
5360
5361
  }
5361
- function registerDraftEmail(server, dataDir = DATA_DIR$22) {
5362
+ function registerDraftEmail(server, dataDir = DATA_DIR$23) {
5362
5363
  server.registerTool("draft_email", {
5363
5364
  description: `Draft a personalized email for a customer using a stored template.
5364
5365
  Variables are auto-filled from the customer's main_facts.md. Override any variable manually.
@@ -5466,8 +5467,8 @@ async function updateEnrollment(dataDir, id, updates) {
5466
5467
  }
5467
5468
  //#endregion
5468
5469
  //#region src/mcp/tools/enroll-in-sequence.ts
5469
- const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5470
- 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) {
5471
5472
  const sequence = getSequence(dataDir, input.sequenceId);
5472
5473
  if (!sequence) return { content: [{
5473
5474
  type: "text",
@@ -5499,7 +5500,7 @@ async function handleEnrollInSequence(input, dataDir = DATA_DIR$21) {
5499
5500
  })
5500
5501
  }] };
5501
5502
  }
5502
- function registerEnrollInSequence(server, dataDir = DATA_DIR$21) {
5503
+ function registerEnrollInSequence(server, dataDir = DATA_DIR$22) {
5503
5504
  server.registerTool("enroll_in_sequence", {
5504
5505
  description: `Enroll a contact in an email sequence. Validates that the sequence and its first template exist.
5505
5506
  Returns: { enrollmentId, sequenceName, totalSteps }`,
@@ -5516,8 +5517,8 @@ Returns: { enrollmentId, sequenceName, totalSteps }`,
5516
5517
  }
5517
5518
  //#endregion
5518
5519
  //#region src/mcp/tools/list-sequence-enrollments.ts
5519
- const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5520
- 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) {
5521
5522
  let enrollments = readEnrollments(dataDir);
5522
5523
  if (input.slug !== void 0) enrollments = enrollments.filter((e) => e.slug === input.slug);
5523
5524
  if (input.status !== void 0) enrollments = enrollments.filter((e) => e.status === input.status);
@@ -5526,7 +5527,7 @@ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$20) {
5526
5527
  text: JSON.stringify({ enrollments }, null, 2)
5527
5528
  }] };
5528
5529
  }
5529
- function registerListSequenceEnrollments(server, dataDir = DATA_DIR$20) {
5530
+ function registerListSequenceEnrollments(server, dataDir = DATA_DIR$21) {
5530
5531
  server.registerTool("list_sequence_enrollments", {
5531
5532
  description: `List email sequence enrollments. Filter by customer slug or status.
5532
5533
  Returns: { enrollments: SequenceEnrollment[] }`,
@@ -5545,8 +5546,8 @@ Returns: { enrollments: SequenceEnrollment[] }`,
5545
5546
  }
5546
5547
  //#endregion
5547
5548
  //#region src/mcp/tools/unenroll-from-sequence.ts
5548
- const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5549
- 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) {
5550
5551
  if (!await updateEnrollment(dataDir, input.enrollmentId, { status: "paused" })) return { content: [{
5551
5552
  type: "text",
5552
5553
  text: JSON.stringify({
@@ -5559,7 +5560,7 @@ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$19) {
5559
5560
  text: JSON.stringify({ success: true })
5560
5561
  }] };
5561
5562
  }
5562
- function registerUnenrollFromSequence(server, dataDir = DATA_DIR$19) {
5563
+ function registerUnenrollFromSequence(server, dataDir = DATA_DIR$20) {
5563
5564
  server.registerTool("unenroll_from_sequence", {
5564
5565
  description: `Unenroll (pause) a contact from an email sequence. Sets status to "paused" (soft delete).
5565
5566
  Returns: { success: boolean }`,
@@ -5568,8 +5569,8 @@ Returns: { success: boolean }`,
5568
5569
  }
5569
5570
  //#endregion
5570
5571
  //#region src/mcp/tools/list-sequences.ts
5571
- const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5572
- 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) {
5573
5574
  const sequences = listSequences(dataDir);
5574
5575
  const enrollments = readEnrollments(dataDir);
5575
5576
  const result = sequences.map((seq) => ({
@@ -5583,7 +5584,7 @@ async function handleListSequences(_input, dataDir = DATA_DIR$18) {
5583
5584
  text: JSON.stringify({ sequences: result }, null, 2)
5584
5585
  }] };
5585
5586
  }
5586
- function registerListSequences(server, dataDir = DATA_DIR$18) {
5587
+ function registerListSequences(server, dataDir = DATA_DIR$19) {
5587
5588
  server.registerTool("list_sequences", {
5588
5589
  description: `List all email sequences with step count and enrollment count.
5589
5590
  Returns: { sequences: Array<{ id, name, stepCount, enrollmentCount }> }`,
@@ -5718,8 +5719,8 @@ async function generateQuote(dataDir, input) {
5718
5719
  }
5719
5720
  //#endregion
5720
5721
  //#region src/mcp/tools/generate-quote.ts
5721
- const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5722
- 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) {
5723
5724
  try {
5724
5725
  const quote = await generateQuote(dataDir, input);
5725
5726
  return { content: [{
@@ -5743,7 +5744,7 @@ async function handleGenerateQuote(input, dataDir = DATA_DIR$17) {
5743
5744
  }] };
5744
5745
  }
5745
5746
  }
5746
- function registerGenerateQuote(server, dataDir = DATA_DIR$17) {
5747
+ function registerGenerateQuote(server, dataDir = DATA_DIR$18) {
5747
5748
  server.registerTool("generate_quote", {
5748
5749
  description: `Generate a professional HTML quote/offer for a customer deal.
5749
5750
  Calculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.
@@ -5771,8 +5772,8 @@ Returns: { quoteNumber, htmlPath, total, currency, validUntil }`,
5771
5772
  }
5772
5773
  //#endregion
5773
5774
  //#region src/mcp/tools/get-quote-status.ts
5774
- const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5775
- 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) {
5776
5777
  if (input.quoteNumber) {
5777
5778
  const quote = readQuote(dataDir, input.quoteNumber);
5778
5779
  if (!quote) return { content: [{
@@ -5790,7 +5791,7 @@ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$16) {
5790
5791
  text: JSON.stringify({ quotes }, null, 2)
5791
5792
  }] };
5792
5793
  }
5793
- function registerGetQuoteStatus(server, dataDir = DATA_DIR$16) {
5794
+ function registerGetQuoteStatus(server, dataDir = DATA_DIR$17) {
5794
5795
  server.registerTool("get_quote_status", {
5795
5796
  description: `Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for a customer).
5796
5797
  Returns quote with status: draft | sent | viewed | accepted | declined`,
@@ -5805,7 +5806,7 @@ Returns quote with status: draft | sent | viewed | accepted | declined`,
5805
5806
  }
5806
5807
  //#endregion
5807
5808
  //#region src/mcp/tools/get-booking-link.ts
5808
- const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5809
+ const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5809
5810
  function loadCalendlyConfig(dataDir) {
5810
5811
  const p = path.join(dataDir, ".agentic", "integrations", "calendly.yaml");
5811
5812
  if (!fs.existsSync(p)) return {};
@@ -5828,7 +5829,7 @@ function readCustomerFacts(dataDir, slug) {
5828
5829
  ...email ? { email } : {}
5829
5830
  };
5830
5831
  }
5831
- async function handleGetBookingLink(input, dataDir = DATA_DIR$15) {
5832
+ async function handleGetBookingLink(input, dataDir = DATA_DIR$16) {
5832
5833
  const config = loadCalendlyConfig(dataDir);
5833
5834
  const apiKey = config.apiKey ?? process.env["CALENDLY_API_KEY"] ?? "";
5834
5835
  if (!apiKey) return { content: [{
@@ -5856,7 +5857,7 @@ async function handleGetBookingLink(input, dataDir = DATA_DIR$15) {
5856
5857
  }] };
5857
5858
  }
5858
5859
  }
5859
- function registerGetBookingLink(server, dataDir = DATA_DIR$15) {
5860
+ function registerGetBookingLink(server, dataDir = DATA_DIR$16) {
5860
5861
  server.registerTool("get_booking_link", {
5861
5862
  description: `Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.
5862
5863
  Requires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.
@@ -6026,8 +6027,8 @@ function calcSlaDue(createdDate, priority, rules) {
6026
6027
  }
6027
6028
  //#endregion
6028
6029
  //#region src/mcp/tools/create-ticket.ts
6029
- const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6030
- 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) {
6031
6032
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6032
6033
  const rules = loadSlaRules(dataDir);
6033
6034
  const priority = input.priority ?? "normal";
@@ -6049,7 +6050,7 @@ async function handleCreateTicket(input, dataDir = DATA_DIR$14) {
6049
6050
  text: JSON.stringify({ ticket }, null, 2)
6050
6051
  }] };
6051
6052
  }
6052
- function registerCreateTicket(server, dataDir = DATA_DIR$14) {
6053
+ function registerCreateTicket(server, dataDir = DATA_DIR$15) {
6053
6054
  server.registerTool("create_ticket", {
6054
6055
  description: `Create a support ticket for a customer. Auto-calculates SLA due date based on priority.
6055
6056
  Returns: { ticket } with id T-NNN, status=open, slaDue`,
@@ -6075,8 +6076,8 @@ Returns: { ticket } with id T-NNN, status=open, slaDue`,
6075
6076
  }
6076
6077
  //#endregion
6077
6078
  //#region src/mcp/tools/update-ticket.ts
6078
- const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6079
- 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) {
6080
6081
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6081
6082
  if (!ticket) return { content: [{
6082
6083
  type: "text",
@@ -6095,7 +6096,7 @@ async function handleUpdateTicket(input, dataDir = DATA_DIR$13) {
6095
6096
  text: JSON.stringify({ ticket: updated }, null, 2)
6096
6097
  }] };
6097
6098
  }
6098
- function registerUpdateTicket(server, dataDir = DATA_DIR$13) {
6099
+ function registerUpdateTicket(server, dataDir = DATA_DIR$14) {
6099
6100
  server.registerTool("update_ticket", {
6100
6101
  description: `Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.
6101
6102
  Returns: { ticket }`,
@@ -6120,8 +6121,8 @@ Returns: { ticket }`,
6120
6121
  }
6121
6122
  //#endregion
6122
6123
  //#region src/mcp/tools/list-tickets.ts
6123
- const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6124
- 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) {
6125
6126
  const results = await listAllTickets(dataDir, {
6126
6127
  ...input.slug !== void 0 ? { slug: input.slug } : {},
6127
6128
  ...input.status !== void 0 ? { status: input.status } : {},
@@ -6133,7 +6134,7 @@ async function handleListTickets(input, dataDir = DATA_DIR$12) {
6133
6134
  text: JSON.stringify({ tickets: results }, null, 2)
6134
6135
  }] };
6135
6136
  }
6136
- function registerListTickets(server, dataDir = DATA_DIR$12) {
6137
+ function registerListTickets(server, dataDir = DATA_DIR$13) {
6137
6138
  server.registerTool("list_tickets", {
6138
6139
  description: `List support tickets. Filter by customer, status, priority, or assignee. Sorted by priority then date.
6139
6140
  Returns: { tickets: Array<{ slug, ticket }> }`,
@@ -6163,8 +6164,8 @@ Returns: { tickets: Array<{ slug, ticket }> }`,
6163
6164
  }
6164
6165
  //#endregion
6165
6166
  //#region src/mcp/tools/close-ticket.ts
6166
- const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6167
- 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) {
6168
6169
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6169
6170
  if (!ticket) return { content: [{
6170
6171
  type: "text",
@@ -6191,7 +6192,7 @@ async function handleCloseTicket(input, dataDir = DATA_DIR$11) {
6191
6192
  text: JSON.stringify({ ticket: updated }, null, 2)
6192
6193
  }] };
6193
6194
  }
6194
- function registerCloseTicket(server, dataDir = DATA_DIR$11) {
6195
+ function registerCloseTicket(server, dataDir = DATA_DIR$12) {
6195
6196
  server.registerTool("close_ticket", {
6196
6197
  description: `Close a support ticket. Optionally logs the resolution as an interaction.
6197
6198
  Returns: { ticket } with status=closed`,
@@ -6345,8 +6346,8 @@ async function savePendingSurvey(dataDir, surveyId, slug, contactEmail, token) {
6345
6346
  }
6346
6347
  //#endregion
6347
6348
  //#region src/mcp/tools/send-nps-survey.ts
6348
- const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6349
- 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) {
6350
6351
  const survey = getSurvey(dataDir, input.surveyId);
6351
6352
  if (!survey) return { content: [{
6352
6353
  type: "text",
@@ -6367,7 +6368,7 @@ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$10) {
6367
6368
  }, null, 2)
6368
6369
  }] };
6369
6370
  }
6370
- function registerSendNpsSurvey(server, dataDir = DATA_DIR$10) {
6371
+ function registerSendNpsSurvey(server, dataDir = DATA_DIR$11) {
6371
6372
  server.registerTool("send_nps_survey", {
6372
6373
  description: `Generate an NPS/CSAT survey email for a customer contact. Returns subject, HTML body, and a token-based response URL.
6373
6374
  Does NOT send automatically — returns draft for review.
@@ -6387,8 +6388,8 @@ Returns: { token, subject, body, surveyUrl }`,
6387
6388
  }
6388
6389
  //#endregion
6389
6390
  //#region src/mcp/tools/get-survey-results.ts
6390
- const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6391
- 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) {
6392
6393
  const responses = loadSurveyResponses(dataDir, input.surveyId, input.slug);
6393
6394
  const nps = calcNpsScore(responses);
6394
6395
  const promoters = responses.filter((r) => r.score >= 9).length;
@@ -6414,7 +6415,7 @@ async function handleGetSurveyResults(input, dataDir = DATA_DIR$9) {
6414
6415
  }, null, 2)
6415
6416
  }] };
6416
6417
  }
6417
- function registerGetSurveyResults(server, dataDir = DATA_DIR$9) {
6418
+ function registerGetSurveyResults(server, dataDir = DATA_DIR$10) {
6418
6419
  server.registerTool("get_survey_results", {
6419
6420
  description: `Get NPS/CSAT survey results with score breakdown. Calculates Net Promoter Score.
6420
6421
  Returns: { npsScore, totalResponses, promoters, passives, detractors, responses[] }`,
@@ -6515,8 +6516,8 @@ function getKbMetaForExport(article) {
6515
6516
  }
6516
6517
  //#endregion
6517
6518
  //#region src/mcp/tools/search-knowledge-base.ts
6518
- const DATA_DIR$8 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6519
- 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) {
6520
6521
  const results = searchKbSimple(dataDir, input.query, { ...input.publicOnly ? { publicOnly: true } : {} });
6521
6522
  const limited = (input.category ? results.filter((a) => a.category === input.category) : results).slice(0, input.limit ?? 10);
6522
6523
  return { content: [{
@@ -6531,7 +6532,7 @@ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$8) {
6531
6532
  }, null, 2)
6532
6533
  }] };
6533
6534
  }
6534
- function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$8) {
6535
+ function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$9) {
6535
6536
  server.registerTool("search_knowledge_base", {
6536
6537
  description: `Search the knowledge base for articles. Text search on title, body, and tags.
6537
6538
  Returns: { count, articles[] } with excerpts`,
@@ -6550,8 +6551,8 @@ Returns: { count, articles[] } with excerpts`,
6550
6551
  }
6551
6552
  //#endregion
6552
6553
  //#region src/mcp/tools/create-kb-article.ts
6553
- const DATA_DIR$7 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6554
- 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) {
6555
6556
  if (getKbArticle(dataDir, input.id)) return { content: [{
6556
6557
  type: "text",
6557
6558
  text: JSON.stringify({ error: `Article '${input.id}' already exists` })
@@ -6579,7 +6580,7 @@ async function handleCreateKbArticle(input, dataDir = DATA_DIR$7) {
6579
6580
  }, null, 2)
6580
6581
  }] };
6581
6582
  }
6582
- function registerCreateKbArticle(server, dataDir = DATA_DIR$7) {
6583
+ function registerCreateKbArticle(server, dataDir = DATA_DIR$8) {
6583
6584
  server.registerTool("create_kb_article", {
6584
6585
  description: `Create a new knowledge base article. Articles are stored as Markdown files in .agentic/knowledge-base/.
6585
6586
  Returns: { id, title, category, path }`,
@@ -6604,8 +6605,8 @@ Returns: { id, title, category, path }`,
6604
6605
  }
6605
6606
  //#endregion
6606
6607
  //#region src/mcp/tools/backup-now.ts
6607
- const DATA_DIR$6 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6608
- 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) {
6609
6610
  const zipPath = path.join(dataDir, `dxcrm-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.zip`);
6610
6611
  const manifest = await runBackup(zipPath, dataDir, { ...input.remote ? { remote: input.remote } : {} }).catch(() => null);
6611
6612
  if (!manifest) return { content: [{
@@ -6642,8 +6643,8 @@ function registerBackupNow(server) {
6642
6643
  }
6643
6644
  //#endregion
6644
6645
  //#region src/mcp/tools/list-backups.ts
6645
- const DATA_DIR$5 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6646
- 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) {
6647
6648
  const logEntries = readBackupLog(dataDir);
6648
6649
  const fileEntries = listBackupsInDir(dataDir);
6649
6650
  const entries = logEntries.length > 0 ? logEntries : fileEntries;
@@ -6677,8 +6678,8 @@ function registerListBackups(server) {
6677
6678
  }
6678
6679
  //#endregion
6679
6680
  //#region src/mcp/tools/trigger-sync.ts
6680
- const DATA_DIR$4 = process.cwd();
6681
- async function handleTriggerSync(input, dataDir = DATA_DIR$4) {
6681
+ const DATA_DIR$5 = process.cwd();
6682
+ async function handleTriggerSync(input, dataDir = DATA_DIR$5) {
6682
6683
  const auth = getGmailAuth();
6683
6684
  if (!auth) return { content: [{
6684
6685
  type: "text",
@@ -6772,8 +6773,8 @@ Returns: { success: boolean, synced: number, skipped: number, customers: [...],
6772
6773
  }
6773
6774
  //#endregion
6774
6775
  //#region src/mcp/tools/get-audit-log.ts
6775
- const DATA_DIR$3 = process.cwd();
6776
- async function handleGetAuditLog(input, dataDir = DATA_DIR$3) {
6776
+ const DATA_DIR$4 = process.cwd();
6777
+ async function handleGetAuditLog(input, dataDir = DATA_DIR$4) {
6777
6778
  const entries = readAuditLog(dataDir);
6778
6779
  const filterOpts = { limit: input.limit ?? 50 };
6779
6780
  if (input.slug !== void 0) filterOpts.slug = input.slug;
@@ -6815,8 +6816,8 @@ Returns: { total: number, returned: number, entries: [{timestamp, actor, tool, s
6815
6816
  }
6816
6817
  //#endregion
6817
6818
  //#region src/mcp/tools/get-logs.ts
6818
- const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6819
- 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) {
6820
6821
  const query = {
6821
6822
  ...input.level !== void 0 ? { level: input.level } : {},
6822
6823
  ...input.component !== void 0 ? { component: input.component } : {},
@@ -6877,6 +6878,140 @@ Returns (summary): { total, byLevel, byComponent, firstTs, lastTs, recentErrors
6877
6878
  });
6878
6879
  }
6879
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
6880
7015
  //#region src/mcp/prompts.ts
6881
7016
  /**
6882
7017
  * CRM playbook prompts exposed via MCP `prompts/list` + `prompts/get`.
@@ -7293,6 +7428,7 @@ function createMcpServer() {
7293
7428
  registerTriggerSync(server);
7294
7429
  registerGetAuditLog(server);
7295
7430
  registerGetLogs(server);
7431
+ registerGetDiagnostics(server);
7296
7432
  registerCustomObjectTools(server);
7297
7433
  registerPrompts(server);
7298
7434
  registerResources(server);