@datasynx/agentic-crm 1.7.0 → 1.8.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.cjs CHANGED
@@ -452,6 +452,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
452
452
  | get_logs | Query/aggregate the structured application log (level, component, errors) | admin |
453
453
  | get_diagnostics | Self-diagnostic health check (data integrity, temp files, log errors, backups) | admin |
454
454
  | get_pipeline_changes | Pipeline time-travel: what changed (won/lost/moved/value) since a date | any |
455
+ | get_pipeline_velocity | Stage dwell times, sales cycle, and stalled deals from snapshot history | any |
455
456
  | define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |
456
457
  | create_record | Create a record of a custom object (validated against its schema) | rep+ |
457
458
  | list_records | List records of a custom object | any |
@@ -1430,7 +1431,7 @@ async function buildContext(dataDir, slug) {
1430
1431
  }
1431
1432
  //#endregion
1432
1433
  //#region src/mcp/tools/get-customer-context.ts
1433
- const DATA_DIR$54 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1434
+ const DATA_DIR$55 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1434
1435
  function triggerOnQuerySync(dataDir, slug) {
1435
1436
  const auth = getGmailAuth();
1436
1437
  if (!auth) return;
@@ -1451,7 +1452,7 @@ function triggerOnQuerySync(dataDir, slug) {
1451
1452
  }).then(() => updateSlugSyncState(dataDir, slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() })).catch(() => {})).catch(() => {});
1452
1453
  } catch {}
1453
1454
  }
1454
- async function handleGetCustomerContext(input, dataDir = DATA_DIR$54) {
1455
+ async function handleGetCustomerContext(input, dataDir = DATA_DIR$55) {
1455
1456
  const targetSlug = input.slug ?? require_session_store.getSession()?.customerSlug;
1456
1457
  if (!targetSlug) return {
1457
1458
  content: [{
@@ -1587,8 +1588,8 @@ async function searchKnowledge(dataDir, slug, query, limit) {
1587
1588
  }
1588
1589
  //#endregion
1589
1590
  //#region src/mcp/tools/search-customer-knowledge.ts
1590
- const DATA_DIR$53 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1591
- async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$53) {
1591
+ const DATA_DIR$54 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1592
+ async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$54) {
1592
1593
  const limit = input.limit ?? 5;
1593
1594
  try {
1594
1595
  const results = await searchKnowledge(dataDir, input.slug, input.query, limit);
@@ -1636,14 +1637,14 @@ If no results: returns empty array with a helpful sync suggestion.`,
1636
1637
  }
1637
1638
  //#endregion
1638
1639
  //#region src/mcp/tools/list-customers.ts
1639
- const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1640
+ const DATA_DIR$53 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
1640
1641
  function extractLastInteractionDate(interactionsPath) {
1641
1642
  if (!fs.default.existsSync(interactionsPath)) return void 0;
1642
1643
  const content = fs.default.readFileSync(interactionsPath, "utf-8");
1643
1644
  const match = /^## (\d{4}-\d{2}-\d{2})/m.exec(content);
1644
1645
  return match ? match[1] : void 0;
1645
1646
  }
1646
- async function handleListCustomers(input, dataDir = DATA_DIR$52) {
1647
+ async function handleListCustomers(input, dataDir = DATA_DIR$53) {
1647
1648
  const customersDir = path.default.join(dataDir, "customers");
1648
1649
  const customers = [];
1649
1650
  if (!fs.default.existsSync(customersDir)) return { content: [{
@@ -2156,8 +2157,8 @@ async function updateHealthFromInteraction(dataDir, slug) {
2156
2157
  }
2157
2158
  //#endregion
2158
2159
  //#region src/mcp/tools/log-interaction.ts
2159
- const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2160
- async function handleLogInteraction(input, dataDir = DATA_DIR$51) {
2160
+ const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2161
+ async function handleLogInteraction(input, dataDir = DATA_DIR$52) {
2161
2162
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2162
2163
  const interactionDate = input.date ?? today;
2163
2164
  const sourceRef = input.source ?? `agent://log/${Date.now()}`;
@@ -2266,8 +2267,8 @@ var update_deal_exports = /* @__PURE__ */ require_chunk.__exportAll({
2266
2267
  handleUpdateDeal: () => handleUpdateDeal,
2267
2268
  registerUpdateDeal: () => registerUpdateDeal
2268
2269
  });
2269
- const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2270
- async function handleUpdateDeal(input, dataDir = DATA_DIR$50) {
2270
+ const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2271
+ async function handleUpdateDeal(input, dataDir = DATA_DIR$51) {
2271
2272
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2272
2273
  const deal = {
2273
2274
  name: input.dealName,
@@ -2350,12 +2351,12 @@ Returns: { success: boolean, deal: object }`,
2350
2351
  }
2351
2352
  //#endregion
2352
2353
  //#region src/mcp/tools/export-customer.ts
2353
- const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2354
+ const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2354
2355
  function countInteractions(content) {
2355
2356
  const matches = content.match(/^## \d{4}-\d{2}-\d{2}/gm);
2356
2357
  return matches ? matches.length : 0;
2357
2358
  }
2358
- async function handleExportCustomer(input, dataDir = DATA_DIR$49) {
2359
+ async function handleExportCustomer(input, dataDir = DATA_DIR$50) {
2359
2360
  require_session_store.enforceRbac(dataDir, "export_customer");
2360
2361
  const customerDir = path.default.join(dataDir, "customers", input.slug);
2361
2362
  if (!fs.default.existsSync(customerDir)) return {
@@ -2468,8 +2469,8 @@ Returns:
2468
2469
  }
2469
2470
  //#endregion
2470
2471
  //#region src/mcp/tools/update-customer-facts.ts
2471
- const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2472
- async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$48) {
2472
+ const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2473
+ async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$49) {
2473
2474
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2474
2475
  try {
2475
2476
  require_session_store.enforceRbac(dataDir, "update_customer_facts");
@@ -2647,8 +2648,8 @@ function scoreDealForToday(deal, todayDate) {
2647
2648
  }
2648
2649
  //#endregion
2649
2650
  //#region src/mcp/tools/get-deal-health.ts
2650
- const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2651
- async function handleGetDealHealth(input, dataDir = DATA_DIR$47) {
2651
+ const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2652
+ async function handleGetDealHealth(input, dataDir = DATA_DIR$48) {
2652
2653
  try {
2653
2654
  const deals = await require_pipeline_writer.readPipeline(dataDir, input.slug);
2654
2655
  const today = /* @__PURE__ */ new Date();
@@ -2697,8 +2698,8 @@ Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }`,
2697
2698
  }
2698
2699
  //#endregion
2699
2700
  //#region src/mcp/tools/get-pipeline-forecast.ts
2700
- const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2701
- async function handleGetPipelineForecast(input, dataDir = DATA_DIR$46) {
2701
+ const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2702
+ async function handleGetPipelineForecast(input, dataDir = DATA_DIR$47) {
2702
2703
  try {
2703
2704
  const slugs = require_session_store.listCustomerSlugs(dataDir).filter((d) => !input.filter || d.includes(input.filter));
2704
2705
  const allDeals = [];
@@ -2759,8 +2760,8 @@ Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count,
2759
2760
  }
2760
2761
  //#endregion
2761
2762
  //#region src/mcp/tools/summarize-meeting.ts
2762
- const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2763
- async function handleSummarizeMeeting(input, dataDir = DATA_DIR$45) {
2763
+ const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2764
+ async function handleSummarizeMeeting(input, dataDir = DATA_DIR$46) {
2764
2765
  try {
2765
2766
  let summary = input.transcript.slice(0, 400);
2766
2767
  let nextSteps = [];
@@ -2883,8 +2884,8 @@ function getPipelineStages(dataDir) {
2883
2884
  }
2884
2885
  //#endregion
2885
2886
  //#region src/mcp/tools/get-pipeline-stages.ts
2886
- const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2887
- async function handleGetPipelineStages(_input, dataDir = DATA_DIR$44) {
2887
+ const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2888
+ async function handleGetPipelineStages(_input, dataDir = DATA_DIR$45) {
2888
2889
  const stages = getPipelineStages(dataDir);
2889
2890
  return { content: [{
2890
2891
  type: "text",
@@ -2912,8 +2913,8 @@ async function searchAcrossCustomers(dataDir, query, limit = 5, excludeSlug) {
2912
2913
  }
2913
2914
  //#endregion
2914
2915
  //#region src/mcp/tools/get-market-intelligence.ts
2915
- const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2916
- async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$43) {
2916
+ const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2917
+ async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$44) {
2917
2918
  const excludeSlug = input.excludeCurrentCustomer ? input.slug : void 0;
2918
2919
  const all = require_session_store.listCustomerSlugs(dataDir);
2919
2920
  const totalCustomersSearched = excludeSlug ? all.filter((s) => s !== excludeSlug).length : all.length;
@@ -2944,7 +2945,7 @@ function registerGetMarketIntelligence(server) {
2944
2945
  }
2945
2946
  //#endregion
2946
2947
  //#region src/mcp/tools/get-relationship-graph.ts
2947
- const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2948
+ const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
2948
2949
  function summarizeNode(n) {
2949
2950
  return {
2950
2951
  id: n.id,
@@ -2952,7 +2953,7 @@ function summarizeNode(n) {
2952
2953
  email: n.properties["email"]
2953
2954
  };
2954
2955
  }
2955
- async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$42) {
2956
+ async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$43) {
2956
2957
  try {
2957
2958
  const graph = readGraph(dataDir, input.slug);
2958
2959
  const stakeholders = getStakeholders(graph);
@@ -3020,9 +3021,9 @@ Returns: {
3020
3021
  }
3021
3022
  //#endregion
3022
3023
  //#region src/mcp/tools/get-relationship-health.ts
3023
- const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3024
+ const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3024
3025
  const MAX_HEALTH_AGE_MS = 3600 * 1e3;
3025
- async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$41) {
3026
+ async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$42) {
3026
3027
  try {
3027
3028
  let health = readHealth(dataDir, input.slug);
3028
3029
  if (health === null || Date.now() - new Date(health.updatedAt).getTime() > MAX_HEALTH_AGE_MS) {
@@ -3691,8 +3692,8 @@ async function runDealAgent(config, dataDir, llmFn = require_llm.callLlm) {
3691
3692
  }
3692
3693
  //#endregion
3693
3694
  //#region src/mcp/tools/run-deal-agent.ts
3694
- const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3695
- async function handleRunDealAgent(input, dataDir = DATA_DIR$40) {
3695
+ const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3696
+ async function handleRunDealAgent(input, dataDir = DATA_DIR$41) {
3696
3697
  try {
3697
3698
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3698
3699
  const result = await runDealAgent({
@@ -3759,8 +3760,8 @@ Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], tr
3759
3760
  }
3760
3761
  //#endregion
3761
3762
  //#region src/mcp/tools/approve-agent-action.ts
3762
- const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3763
- async function handleApproveAgentAction(input, dataDir = DATA_DIR$39) {
3763
+ const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
3764
+ async function handleApproveAgentAction(input, dataDir = DATA_DIR$40) {
3764
3765
  try {
3765
3766
  const queue = readAgentQueue(dataDir, input.slug);
3766
3767
  const idx = queue.pendingActions.findIndex((a) => a.actionId === input.actionId);
@@ -4020,8 +4021,8 @@ async function buildSimulationInput(dataDir, horizon, today, externalSignals = [
4020
4021
  }
4021
4022
  //#endregion
4022
4023
  //#region src/mcp/tools/simulate-revenue.ts
4023
- const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4024
- async function handleSimulateRevenue(input, dataDir = DATA_DIR$38) {
4024
+ const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4025
+ async function handleSimulateRevenue(input, dataDir = DATA_DIR$39) {
4025
4026
  try {
4026
4027
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4027
4028
  const horizon = input.horizon ?? "quarter";
@@ -4079,8 +4080,8 @@ Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMo
4079
4080
  }
4080
4081
  //#endregion
4081
4082
  //#region src/mcp/tools/get-playbook.ts
4082
- const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4083
- async function handleGetPlaybook(input, dataDir = DATA_DIR$37) {
4083
+ const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4084
+ async function handleGetPlaybook(input, dataDir = DATA_DIR$38) {
4084
4085
  try {
4085
4086
  const playbooks = listPlaybooks(dataDir, input.slug);
4086
4087
  if (!(input.stage !== void 0 || input.value !== void 0 || input.healthScore !== void 0)) return { content: [{
@@ -4165,12 +4166,12 @@ Returns: { matches: [{ name, score, trigger, successRate, usedCount, content }],
4165
4166
  ...healthScore !== void 0 ? { healthScore } : {},
4166
4167
  ...daysSinceContact !== void 0 ? { daysSinceContact } : {},
4167
4168
  ...championPresent !== void 0 ? { championPresent } : {}
4168
- }, DATA_DIR$37));
4169
+ }, DATA_DIR$38));
4169
4170
  }
4170
4171
  //#endregion
4171
4172
  //#region src/mcp/tools/create-playbook.ts
4172
- const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4173
- async function handleCreatePlaybook(input, dataDir = DATA_DIR$36) {
4173
+ const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4174
+ async function handleCreatePlaybook(input, dataDir = DATA_DIR$37) {
4174
4175
  try {
4175
4176
  const name = toKebabCase(input.name);
4176
4177
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -4243,12 +4244,12 @@ Returns: { success: true, playbook: { name, trigger, successRate, path } }`,
4243
4244
  trigger,
4244
4245
  content,
4245
4246
  ...successRate !== void 0 ? { successRate } : {}
4246
- }, DATA_DIR$36));
4247
+ }, DATA_DIR$37));
4247
4248
  }
4248
4249
  //#endregion
4249
4250
  //#region src/mcp/tools/list-playbooks.ts
4250
- const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4251
- async function handleListPlaybooks(input, dataDir = DATA_DIR$35) {
4251
+ const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4252
+ async function handleListPlaybooks(input, dataDir = DATA_DIR$36) {
4252
4253
  try {
4253
4254
  const playbooks = listPlaybooks(dataDir, input.slug);
4254
4255
  return { content: [{
@@ -4287,12 +4288,12 @@ Args:
4287
4288
 
4288
4289
  Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }`,
4289
4290
  inputSchema: zod.z.object({ slug: zod.z.string().describe("Customer ID") })
4290
- }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$35));
4291
+ }, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$36));
4291
4292
  }
4292
4293
  //#endregion
4293
4294
  //#region src/mcp/tools/distill-playbook.ts
4294
- const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4295
- async function handleDistillPlaybook(input, dataDir = DATA_DIR$34, llmFn = require_llm.callLlm) {
4295
+ const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4296
+ async function handleDistillPlaybook(input, dataDir = DATA_DIR$35, llmFn = require_llm.callLlm) {
4296
4297
  try {
4297
4298
  const result = await distillPlaybook(dataDir, input.slug, input.dealName, input.outcome, llmFn);
4298
4299
  if (!result.ok) {
@@ -4351,7 +4352,7 @@ Returns: { success: true, playbook: { name, trigger, successRate, path }, reason
4351
4352
  slug,
4352
4353
  dealName,
4353
4354
  outcome
4354
- }, DATA_DIR$34));
4355
+ }, DATA_DIR$35));
4355
4356
  }
4356
4357
  //#endregion
4357
4358
  //#region src/core/goal-engine.ts
@@ -4569,8 +4570,8 @@ function getActiveGoals(dataDir) {
4569
4570
  }
4570
4571
  //#endregion
4571
4572
  //#region src/mcp/tools/pursue-goal.ts
4572
- const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4573
- async function handlePursueGoal(input, dataDir = DATA_DIR$33, options = {}) {
4573
+ const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4574
+ async function handlePursueGoal(input, dataDir = DATA_DIR$34, options = {}) {
4574
4575
  try {
4575
4576
  require_session_store.enforceRbac(dataDir, "pursue_goal");
4576
4577
  const goal = await pursueGoal(dataDir, {
@@ -4633,12 +4634,12 @@ Returns: { goalId, description, target, deadline, decomposition: { analysis, cur
4633
4634
  goal,
4634
4635
  deadline,
4635
4636
  ...context !== void 0 ? { context } : {}
4636
- }, DATA_DIR$33));
4637
+ }, DATA_DIR$34));
4637
4638
  }
4638
4639
  //#endregion
4639
4640
  //#region src/mcp/tools/get-goal-status.ts
4640
- const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4641
- async function handleGetGoalStatus(input, dataDir = DATA_DIR$32) {
4641
+ const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4642
+ async function handleGetGoalStatus(input, dataDir = DATA_DIR$33) {
4642
4643
  try {
4643
4644
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4644
4645
  const allGoals = input.goalId ? readGoals(dataDir).filter((g) => g.id === input.goalId) : getActiveGoals(dataDir);
@@ -4697,17 +4698,17 @@ Args:
4697
4698
 
4698
4699
  Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }`,
4699
4700
  inputSchema: zod.z.object({ goalId: zod.z.string().optional().describe("Specific goal ID (omit for all active goals)") })
4700
- }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$32));
4701
+ }, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$33));
4701
4702
  }
4702
4703
  //#endregion
4703
4704
  //#region src/mcp/tools/register-push-subscription.ts
4704
- const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4705
+ const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4705
4706
  const VALID_PROVIDERS = [
4706
4707
  "gmail",
4707
4708
  "microsoft-graph",
4708
4709
  "slack"
4709
4710
  ];
4710
- async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$31) {
4711
+ async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$32) {
4711
4712
  try {
4712
4713
  if (!VALID_PROVIDERS.includes(input.provider)) return { content: [{
4713
4714
  type: "text",
@@ -4793,12 +4794,12 @@ Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning
4793
4794
  ...microsoftResource !== void 0 ? { microsoftResource } : {},
4794
4795
  ...slackTeamId !== void 0 ? { slackTeamId } : {},
4795
4796
  ...slackChannelId !== void 0 ? { slackChannelId } : {}
4796
- }, DATA_DIR$31));
4797
+ }, DATA_DIR$32));
4797
4798
  }
4798
4799
  //#endregion
4799
4800
  //#region src/mcp/tools/get-push-status.ts
4800
- const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4801
- async function handleGetPushStatus(input, dataDir = DATA_DIR$30) {
4801
+ const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4802
+ async function handleGetPushStatus(input, dataDir = DATA_DIR$31) {
4802
4803
  try {
4803
4804
  let subs = await readSubscriptions(dataDir);
4804
4805
  if (input.slug) subs = subs.filter((s) => s.slug === input.slug);
@@ -4870,7 +4871,7 @@ Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHou
4870
4871
  }, async ({ slug, provider }) => handleGetPushStatus({
4871
4872
  ...slug !== void 0 ? { slug } : {},
4872
4873
  ...provider !== void 0 ? { provider } : {}
4873
- }, DATA_DIR$30));
4874
+ }, DATA_DIR$31));
4874
4875
  }
4875
4876
  //#endregion
4876
4877
  //#region src/core/org-intelligence.ts
@@ -4936,8 +4937,8 @@ function deriveRecommendation(people, missingRoles) {
4936
4937
  }
4937
4938
  //#endregion
4938
4939
  //#region src/mcp/tools/get-org-intelligence.ts
4939
- const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4940
- async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$29) {
4940
+ const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
4941
+ async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$30) {
4941
4942
  try {
4942
4943
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4943
4944
  const map = buildStakeholderMap(dataDir, input.slug, today, input.dealName);
@@ -5070,8 +5071,8 @@ function buildExecutiveSummary(slug, dealName, stakeholders, overallHealth, sim,
5070
5071
  }
5071
5072
  //#endregion
5072
5073
  //#region src/mcp/tools/open-deal-room.ts
5073
- const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5074
- async function handleOpenDealRoom(input, dataDir = DATA_DIR$28) {
5074
+ const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5075
+ async function handleOpenDealRoom(input, dataDir = DATA_DIR$29) {
5075
5076
  try {
5076
5077
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5077
5078
  const brief = await buildDealRoom(dataDir, input.slug, input.dealName, today);
@@ -5154,8 +5155,8 @@ async function buildDailyBriefing(dataDir, today) {
5154
5155
  }
5155
5156
  //#endregion
5156
5157
  //#region src/mcp/tools/get-proactive-briefing.ts
5157
- const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5158
- async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$27) {
5158
+ const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5159
+ async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$28) {
5159
5160
  try {
5160
5161
  const briefing = await buildDailyBriefing(dataDir, input.date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
5161
5162
  return { content: [{
@@ -5255,15 +5256,15 @@ function getTemplate(dataDir, id) {
5255
5256
  }
5256
5257
  //#endregion
5257
5258
  //#region src/mcp/tools/list-email-templates.ts
5258
- const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5259
- async function handleListEmailTemplates(input, dataDir = DATA_DIR$26) {
5259
+ const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5260
+ async function handleListEmailTemplates(input, dataDir = DATA_DIR$27) {
5260
5261
  const summary = listTemplates(dataDir, input.category ? { category: input.category } : {}).map(({ body: _body, ...meta }) => meta);
5261
5262
  return { content: [{
5262
5263
  type: "text",
5263
5264
  text: JSON.stringify(summary, null, 2)
5264
5265
  }] };
5265
5266
  }
5266
- function registerListEmailTemplates(server, dataDir = DATA_DIR$26) {
5267
+ function registerListEmailTemplates(server, dataDir = DATA_DIR$27) {
5267
5268
  server.registerTool("list_email_templates", {
5268
5269
  description: "List available email templates. Optionally filter by category (e.g. 'outreach', 'followup', 'support').",
5269
5270
  inputSchema: zod.z.object({ category: zod.z.string().optional().describe("Filter by category") })
@@ -5297,8 +5298,8 @@ async function buildVariablesFromCustomer(dataDir, slug) {
5297
5298
  }
5298
5299
  //#endregion
5299
5300
  //#region src/mcp/tools/get-email-template.ts
5300
- const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5301
- async function handleGetEmailTemplate(input, dataDir = DATA_DIR$25) {
5301
+ const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5302
+ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$26) {
5302
5303
  const tmpl = getTemplate(dataDir, input.id);
5303
5304
  if (!tmpl) return { content: [{
5304
5305
  type: "text",
@@ -5314,7 +5315,7 @@ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$25) {
5314
5315
  }, null, 2)
5315
5316
  }] };
5316
5317
  }
5317
- function registerGetEmailTemplate(server, dataDir = DATA_DIR$25) {
5318
+ function registerGetEmailTemplate(server, dataDir = DATA_DIR$26) {
5318
5319
  server.registerTool("get_email_template", {
5319
5320
  description: "Get a specific email template by ID, including its body and detected variables.",
5320
5321
  inputSchema: zod.z.object({ id: zod.z.string().describe("Template ID (e.g. 'enterprise-intro')") })
@@ -5322,8 +5323,8 @@ function registerGetEmailTemplate(server, dataDir = DATA_DIR$25) {
5322
5323
  }
5323
5324
  //#endregion
5324
5325
  //#region src/mcp/tools/draft-email.ts
5325
- const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5326
- async function handleDraftEmail(input, dataDir = DATA_DIR$24) {
5326
+ const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5327
+ async function handleDraftEmail(input, dataDir = DATA_DIR$25) {
5327
5328
  const tmpl = getTemplate(dataDir, input.templateId);
5328
5329
  if (!tmpl) return { content: [{
5329
5330
  type: "text",
@@ -5367,7 +5368,7 @@ async function handleDraftEmail(input, dataDir = DATA_DIR$24) {
5367
5368
  }, null, 2)
5368
5369
  }] };
5369
5370
  }
5370
- function registerDraftEmail(server, dataDir = DATA_DIR$24) {
5371
+ function registerDraftEmail(server, dataDir = DATA_DIR$25) {
5371
5372
  server.registerTool("draft_email", {
5372
5373
  description: `Draft a personalized email for a customer using a stored template.
5373
5374
  Variables are auto-filled from the customer's main_facts.md. Override any variable manually.
@@ -5475,8 +5476,8 @@ async function updateEnrollment(dataDir, id, updates) {
5475
5476
  }
5476
5477
  //#endregion
5477
5478
  //#region src/mcp/tools/enroll-in-sequence.ts
5478
- const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5479
- async function handleEnrollInSequence(input, dataDir = DATA_DIR$23) {
5479
+ const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5480
+ async function handleEnrollInSequence(input, dataDir = DATA_DIR$24) {
5480
5481
  const sequence = getSequence(dataDir, input.sequenceId);
5481
5482
  if (!sequence) return { content: [{
5482
5483
  type: "text",
@@ -5508,7 +5509,7 @@ async function handleEnrollInSequence(input, dataDir = DATA_DIR$23) {
5508
5509
  })
5509
5510
  }] };
5510
5511
  }
5511
- function registerEnrollInSequence(server, dataDir = DATA_DIR$23) {
5512
+ function registerEnrollInSequence(server, dataDir = DATA_DIR$24) {
5512
5513
  server.registerTool("enroll_in_sequence", {
5513
5514
  description: `Enroll a contact in an email sequence. Validates that the sequence and its first template exist.
5514
5515
  Returns: { enrollmentId, sequenceName, totalSteps }`,
@@ -5525,8 +5526,8 @@ Returns: { enrollmentId, sequenceName, totalSteps }`,
5525
5526
  }
5526
5527
  //#endregion
5527
5528
  //#region src/mcp/tools/list-sequence-enrollments.ts
5528
- const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5529
- async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$22) {
5529
+ const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5530
+ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$23) {
5530
5531
  let enrollments = readEnrollments(dataDir);
5531
5532
  if (input.slug !== void 0) enrollments = enrollments.filter((e) => e.slug === input.slug);
5532
5533
  if (input.status !== void 0) enrollments = enrollments.filter((e) => e.status === input.status);
@@ -5535,7 +5536,7 @@ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$22) {
5535
5536
  text: JSON.stringify({ enrollments }, null, 2)
5536
5537
  }] };
5537
5538
  }
5538
- function registerListSequenceEnrollments(server, dataDir = DATA_DIR$22) {
5539
+ function registerListSequenceEnrollments(server, dataDir = DATA_DIR$23) {
5539
5540
  server.registerTool("list_sequence_enrollments", {
5540
5541
  description: `List email sequence enrollments. Filter by customer slug or status.
5541
5542
  Returns: { enrollments: SequenceEnrollment[] }`,
@@ -5554,8 +5555,8 @@ Returns: { enrollments: SequenceEnrollment[] }`,
5554
5555
  }
5555
5556
  //#endregion
5556
5557
  //#region src/mcp/tools/unenroll-from-sequence.ts
5557
- const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5558
- async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$21) {
5558
+ const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5559
+ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$22) {
5559
5560
  if (!await updateEnrollment(dataDir, input.enrollmentId, { status: "paused" })) return { content: [{
5560
5561
  type: "text",
5561
5562
  text: JSON.stringify({
@@ -5568,7 +5569,7 @@ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$21) {
5568
5569
  text: JSON.stringify({ success: true })
5569
5570
  }] };
5570
5571
  }
5571
- function registerUnenrollFromSequence(server, dataDir = DATA_DIR$21) {
5572
+ function registerUnenrollFromSequence(server, dataDir = DATA_DIR$22) {
5572
5573
  server.registerTool("unenroll_from_sequence", {
5573
5574
  description: `Unenroll (pause) a contact from an email sequence. Sets status to "paused" (soft delete).
5574
5575
  Returns: { success: boolean }`,
@@ -5577,8 +5578,8 @@ Returns: { success: boolean }`,
5577
5578
  }
5578
5579
  //#endregion
5579
5580
  //#region src/mcp/tools/list-sequences.ts
5580
- const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5581
- async function handleListSequences(_input, dataDir = DATA_DIR$20) {
5581
+ const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5582
+ async function handleListSequences(_input, dataDir = DATA_DIR$21) {
5582
5583
  const sequences = listSequences(dataDir);
5583
5584
  const enrollments = readEnrollments(dataDir);
5584
5585
  const result = sequences.map((seq) => ({
@@ -5592,7 +5593,7 @@ async function handleListSequences(_input, dataDir = DATA_DIR$20) {
5592
5593
  text: JSON.stringify({ sequences: result }, null, 2)
5593
5594
  }] };
5594
5595
  }
5595
- function registerListSequences(server, dataDir = DATA_DIR$20) {
5596
+ function registerListSequences(server, dataDir = DATA_DIR$21) {
5596
5597
  server.registerTool("list_sequences", {
5597
5598
  description: `List all email sequences with step count and enrollment count.
5598
5599
  Returns: { sequences: Array<{ id, name, stepCount, enrollmentCount }> }`,
@@ -5727,8 +5728,8 @@ async function generateQuote(dataDir, input) {
5727
5728
  }
5728
5729
  //#endregion
5729
5730
  //#region src/mcp/tools/generate-quote.ts
5730
- const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5731
- async function handleGenerateQuote(input, dataDir = DATA_DIR$19) {
5731
+ const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5732
+ async function handleGenerateQuote(input, dataDir = DATA_DIR$20) {
5732
5733
  try {
5733
5734
  const quote = await generateQuote(dataDir, input);
5734
5735
  return { content: [{
@@ -5752,7 +5753,7 @@ async function handleGenerateQuote(input, dataDir = DATA_DIR$19) {
5752
5753
  }] };
5753
5754
  }
5754
5755
  }
5755
- function registerGenerateQuote(server, dataDir = DATA_DIR$19) {
5756
+ function registerGenerateQuote(server, dataDir = DATA_DIR$20) {
5756
5757
  server.registerTool("generate_quote", {
5757
5758
  description: `Generate a professional HTML quote/offer for a customer deal.
5758
5759
  Calculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.
@@ -5780,8 +5781,8 @@ Returns: { quoteNumber, htmlPath, total, currency, validUntil }`,
5780
5781
  }
5781
5782
  //#endregion
5782
5783
  //#region src/mcp/tools/get-quote-status.ts
5783
- const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5784
- async function handleGetQuoteStatus(input, dataDir = DATA_DIR$18) {
5784
+ const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5785
+ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$19) {
5785
5786
  if (input.quoteNumber) {
5786
5787
  const quote = readQuote(dataDir, input.quoteNumber);
5787
5788
  if (!quote) return { content: [{
@@ -5799,7 +5800,7 @@ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$18) {
5799
5800
  text: JSON.stringify({ quotes }, null, 2)
5800
5801
  }] };
5801
5802
  }
5802
- function registerGetQuoteStatus(server, dataDir = DATA_DIR$18) {
5803
+ function registerGetQuoteStatus(server, dataDir = DATA_DIR$19) {
5803
5804
  server.registerTool("get_quote_status", {
5804
5805
  description: `Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for a customer).
5805
5806
  Returns quote with status: draft | sent | viewed | accepted | declined`,
@@ -5814,7 +5815,7 @@ Returns quote with status: draft | sent | viewed | accepted | declined`,
5814
5815
  }
5815
5816
  //#endregion
5816
5817
  //#region src/mcp/tools/get-booking-link.ts
5817
- const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5818
+ const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
5818
5819
  function loadCalendlyConfig(dataDir) {
5819
5820
  const p = path.default.join(dataDir, ".agentic", "integrations", "calendly.yaml");
5820
5821
  if (!fs.default.existsSync(p)) return {};
@@ -5837,7 +5838,7 @@ function readCustomerFacts(dataDir, slug) {
5837
5838
  ...email ? { email } : {}
5838
5839
  };
5839
5840
  }
5840
- async function handleGetBookingLink(input, dataDir = DATA_DIR$17) {
5841
+ async function handleGetBookingLink(input, dataDir = DATA_DIR$18) {
5841
5842
  const config = loadCalendlyConfig(dataDir);
5842
5843
  const apiKey = config.apiKey ?? process.env["CALENDLY_API_KEY"] ?? "";
5843
5844
  if (!apiKey) return { content: [{
@@ -5865,7 +5866,7 @@ async function handleGetBookingLink(input, dataDir = DATA_DIR$17) {
5865
5866
  }] };
5866
5867
  }
5867
5868
  }
5868
- function registerGetBookingLink(server, dataDir = DATA_DIR$17) {
5869
+ function registerGetBookingLink(server, dataDir = DATA_DIR$18) {
5869
5870
  server.registerTool("get_booking_link", {
5870
5871
  description: `Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.
5871
5872
  Requires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.
@@ -6035,8 +6036,8 @@ function calcSlaDue(createdDate, priority, rules) {
6035
6036
  }
6036
6037
  //#endregion
6037
6038
  //#region src/mcp/tools/create-ticket.ts
6038
- const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6039
- async function handleCreateTicket(input, dataDir = DATA_DIR$16) {
6039
+ const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6040
+ async function handleCreateTicket(input, dataDir = DATA_DIR$17) {
6040
6041
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6041
6042
  const rules = loadSlaRules(dataDir);
6042
6043
  const priority = input.priority ?? "normal";
@@ -6058,7 +6059,7 @@ async function handleCreateTicket(input, dataDir = DATA_DIR$16) {
6058
6059
  text: JSON.stringify({ ticket }, null, 2)
6059
6060
  }] };
6060
6061
  }
6061
- function registerCreateTicket(server, dataDir = DATA_DIR$16) {
6062
+ function registerCreateTicket(server, dataDir = DATA_DIR$17) {
6062
6063
  server.registerTool("create_ticket", {
6063
6064
  description: `Create a support ticket for a customer. Auto-calculates SLA due date based on priority.
6064
6065
  Returns: { ticket } with id T-NNN, status=open, slaDue`,
@@ -6084,8 +6085,8 @@ Returns: { ticket } with id T-NNN, status=open, slaDue`,
6084
6085
  }
6085
6086
  //#endregion
6086
6087
  //#region src/mcp/tools/update-ticket.ts
6087
- const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6088
- async function handleUpdateTicket(input, dataDir = DATA_DIR$15) {
6088
+ const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6089
+ async function handleUpdateTicket(input, dataDir = DATA_DIR$16) {
6089
6090
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6090
6091
  if (!ticket) return { content: [{
6091
6092
  type: "text",
@@ -6104,7 +6105,7 @@ async function handleUpdateTicket(input, dataDir = DATA_DIR$15) {
6104
6105
  text: JSON.stringify({ ticket: updated }, null, 2)
6105
6106
  }] };
6106
6107
  }
6107
- function registerUpdateTicket(server, dataDir = DATA_DIR$15) {
6108
+ function registerUpdateTicket(server, dataDir = DATA_DIR$16) {
6108
6109
  server.registerTool("update_ticket", {
6109
6110
  description: `Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.
6110
6111
  Returns: { ticket }`,
@@ -6129,8 +6130,8 @@ Returns: { ticket }`,
6129
6130
  }
6130
6131
  //#endregion
6131
6132
  //#region src/mcp/tools/list-tickets.ts
6132
- const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6133
- async function handleListTickets(input, dataDir = DATA_DIR$14) {
6133
+ const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6134
+ async function handleListTickets(input, dataDir = DATA_DIR$15) {
6134
6135
  const results = await listAllTickets(dataDir, {
6135
6136
  ...input.slug !== void 0 ? { slug: input.slug } : {},
6136
6137
  ...input.status !== void 0 ? { status: input.status } : {},
@@ -6142,7 +6143,7 @@ async function handleListTickets(input, dataDir = DATA_DIR$14) {
6142
6143
  text: JSON.stringify({ tickets: results }, null, 2)
6143
6144
  }] };
6144
6145
  }
6145
- function registerListTickets(server, dataDir = DATA_DIR$14) {
6146
+ function registerListTickets(server, dataDir = DATA_DIR$15) {
6146
6147
  server.registerTool("list_tickets", {
6147
6148
  description: `List support tickets. Filter by customer, status, priority, or assignee. Sorted by priority then date.
6148
6149
  Returns: { tickets: Array<{ slug, ticket }> }`,
@@ -6172,8 +6173,8 @@ Returns: { tickets: Array<{ slug, ticket }> }`,
6172
6173
  }
6173
6174
  //#endregion
6174
6175
  //#region src/mcp/tools/close-ticket.ts
6175
- const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6176
- async function handleCloseTicket(input, dataDir = DATA_DIR$13) {
6176
+ const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6177
+ async function handleCloseTicket(input, dataDir = DATA_DIR$14) {
6177
6178
  const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
6178
6179
  if (!ticket) return { content: [{
6179
6180
  type: "text",
@@ -6200,7 +6201,7 @@ async function handleCloseTicket(input, dataDir = DATA_DIR$13) {
6200
6201
  text: JSON.stringify({ ticket: updated }, null, 2)
6201
6202
  }] };
6202
6203
  }
6203
- function registerCloseTicket(server, dataDir = DATA_DIR$13) {
6204
+ function registerCloseTicket(server, dataDir = DATA_DIR$14) {
6204
6205
  server.registerTool("close_ticket", {
6205
6206
  description: `Close a support ticket. Optionally logs the resolution as an interaction.
6206
6207
  Returns: { ticket } with status=closed`,
@@ -6354,8 +6355,8 @@ async function savePendingSurvey(dataDir, surveyId, slug, contactEmail, token) {
6354
6355
  }
6355
6356
  //#endregion
6356
6357
  //#region src/mcp/tools/send-nps-survey.ts
6357
- const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6358
- async function handleSendNpsSurvey(input, dataDir = DATA_DIR$12) {
6358
+ const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6359
+ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$13) {
6359
6360
  const survey = getSurvey(dataDir, input.surveyId);
6360
6361
  if (!survey) return { content: [{
6361
6362
  type: "text",
@@ -6376,7 +6377,7 @@ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$12) {
6376
6377
  }, null, 2)
6377
6378
  }] };
6378
6379
  }
6379
- function registerSendNpsSurvey(server, dataDir = DATA_DIR$12) {
6380
+ function registerSendNpsSurvey(server, dataDir = DATA_DIR$13) {
6380
6381
  server.registerTool("send_nps_survey", {
6381
6382
  description: `Generate an NPS/CSAT survey email for a customer contact. Returns subject, HTML body, and a token-based response URL.
6382
6383
  Does NOT send automatically — returns draft for review.
@@ -6396,8 +6397,8 @@ Returns: { token, subject, body, surveyUrl }`,
6396
6397
  }
6397
6398
  //#endregion
6398
6399
  //#region src/mcp/tools/get-survey-results.ts
6399
- const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6400
- async function handleGetSurveyResults(input, dataDir = DATA_DIR$11) {
6400
+ const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6401
+ async function handleGetSurveyResults(input, dataDir = DATA_DIR$12) {
6401
6402
  const responses = loadSurveyResponses(dataDir, input.surveyId, input.slug);
6402
6403
  const nps = calcNpsScore(responses);
6403
6404
  const promoters = responses.filter((r) => r.score >= 9).length;
@@ -6423,7 +6424,7 @@ async function handleGetSurveyResults(input, dataDir = DATA_DIR$11) {
6423
6424
  }, null, 2)
6424
6425
  }] };
6425
6426
  }
6426
- function registerGetSurveyResults(server, dataDir = DATA_DIR$11) {
6427
+ function registerGetSurveyResults(server, dataDir = DATA_DIR$12) {
6427
6428
  server.registerTool("get_survey_results", {
6428
6429
  description: `Get NPS/CSAT survey results with score breakdown. Calculates Net Promoter Score.
6429
6430
  Returns: { npsScore, totalResponses, promoters, passives, detractors, responses[] }`,
@@ -6524,8 +6525,8 @@ function getKbMetaForExport(article) {
6524
6525
  }
6525
6526
  //#endregion
6526
6527
  //#region src/mcp/tools/search-knowledge-base.ts
6527
- const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6528
- async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$10) {
6528
+ const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6529
+ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$11) {
6529
6530
  const results = searchKbSimple(dataDir, input.query, { ...input.publicOnly ? { publicOnly: true } : {} });
6530
6531
  const limited = (input.category ? results.filter((a) => a.category === input.category) : results).slice(0, input.limit ?? 10);
6531
6532
  return { content: [{
@@ -6540,7 +6541,7 @@ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$10) {
6540
6541
  }, null, 2)
6541
6542
  }] };
6542
6543
  }
6543
- function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$10) {
6544
+ function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$11) {
6544
6545
  server.registerTool("search_knowledge_base", {
6545
6546
  description: `Search the knowledge base for articles. Text search on title, body, and tags.
6546
6547
  Returns: { count, articles[] } with excerpts`,
@@ -6559,8 +6560,8 @@ Returns: { count, articles[] } with excerpts`,
6559
6560
  }
6560
6561
  //#endregion
6561
6562
  //#region src/mcp/tools/create-kb-article.ts
6562
- const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6563
- async function handleCreateKbArticle(input, dataDir = DATA_DIR$9) {
6563
+ const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6564
+ async function handleCreateKbArticle(input, dataDir = DATA_DIR$10) {
6564
6565
  if (getKbArticle(dataDir, input.id)) return { content: [{
6565
6566
  type: "text",
6566
6567
  text: JSON.stringify({ error: `Article '${input.id}' already exists` })
@@ -6588,7 +6589,7 @@ async function handleCreateKbArticle(input, dataDir = DATA_DIR$9) {
6588
6589
  }, null, 2)
6589
6590
  }] };
6590
6591
  }
6591
- function registerCreateKbArticle(server, dataDir = DATA_DIR$9) {
6592
+ function registerCreateKbArticle(server, dataDir = DATA_DIR$10) {
6592
6593
  server.registerTool("create_kb_article", {
6593
6594
  description: `Create a new knowledge base article. Articles are stored as Markdown files in .agentic/knowledge-base/.
6594
6595
  Returns: { id, title, category, path }`,
@@ -6613,8 +6614,8 @@ Returns: { id, title, category, path }`,
6613
6614
  }
6614
6615
  //#endregion
6615
6616
  //#region src/mcp/tools/backup-now.ts
6616
- const DATA_DIR$8 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6617
- async function handleBackupNow(input, dataDir = DATA_DIR$8) {
6617
+ const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6618
+ async function handleBackupNow(input, dataDir = DATA_DIR$9) {
6618
6619
  const zipPath = path.default.join(dataDir, `dxcrm-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.zip`);
6619
6620
  const manifest = await require_session_store.runBackup(zipPath, dataDir, { ...input.remote ? { remote: input.remote } : {} }).catch(() => null);
6620
6621
  if (!manifest) return { content: [{
@@ -6651,8 +6652,8 @@ function registerBackupNow(server) {
6651
6652
  }
6652
6653
  //#endregion
6653
6654
  //#region src/mcp/tools/list-backups.ts
6654
- const DATA_DIR$7 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6655
- async function handleListBackups(input, dataDir = DATA_DIR$7) {
6655
+ const DATA_DIR$8 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6656
+ async function handleListBackups(input, dataDir = DATA_DIR$8) {
6656
6657
  const logEntries = require_session_store.readBackupLog(dataDir);
6657
6658
  const fileEntries = require_session_store.listBackupsInDir(dataDir);
6658
6659
  const entries = logEntries.length > 0 ? logEntries : fileEntries;
@@ -6686,8 +6687,8 @@ function registerListBackups(server) {
6686
6687
  }
6687
6688
  //#endregion
6688
6689
  //#region src/mcp/tools/trigger-sync.ts
6689
- const DATA_DIR$6 = process.cwd();
6690
- async function handleTriggerSync(input, dataDir = DATA_DIR$6) {
6690
+ const DATA_DIR$7 = process.cwd();
6691
+ async function handleTriggerSync(input, dataDir = DATA_DIR$7) {
6691
6692
  const auth = getGmailAuth();
6692
6693
  if (!auth) return { content: [{
6693
6694
  type: "text",
@@ -6781,8 +6782,8 @@ Returns: { success: boolean, synced: number, skipped: number, customers: [...],
6781
6782
  }
6782
6783
  //#endregion
6783
6784
  //#region src/mcp/tools/get-audit-log.ts
6784
- const DATA_DIR$5 = process.cwd();
6785
- async function handleGetAuditLog(input, dataDir = DATA_DIR$5) {
6785
+ const DATA_DIR$6 = process.cwd();
6786
+ async function handleGetAuditLog(input, dataDir = DATA_DIR$6) {
6786
6787
  const entries = require_session_store.readAuditLog(dataDir);
6787
6788
  const filterOpts = { limit: input.limit ?? 50 };
6788
6789
  if (input.slug !== void 0) filterOpts.slug = input.slug;
@@ -6824,8 +6825,8 @@ Returns: { total: number, returned: number, entries: [{timestamp, actor, tool, s
6824
6825
  }
6825
6826
  //#endregion
6826
6827
  //#region src/mcp/tools/get-logs.ts
6827
- const DATA_DIR$4 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6828
- async function handleGetLogs(input, dataDir = DATA_DIR$4) {
6828
+ const DATA_DIR$5 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6829
+ async function handleGetLogs(input, dataDir = DATA_DIR$5) {
6829
6830
  const query = {
6830
6831
  ...input.level !== void 0 ? { level: input.level } : {},
6831
6832
  ...input.component !== void 0 ? { component: input.component } : {},
@@ -6987,8 +6988,8 @@ async function runDiagnostics(dataDir) {
6987
6988
  }
6988
6989
  //#endregion
6989
6990
  //#region src/mcp/tools/get-diagnostics.ts
6990
- const DATA_DIR$3 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6991
- async function handleGetDiagnostics(input, dataDir = DATA_DIR$3) {
6991
+ const DATA_DIR$4 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
6992
+ async function handleGetDiagnostics(input, dataDir = DATA_DIR$4) {
6992
6993
  let cleaned = 0;
6993
6994
  if (input.fix) {
6994
6995
  const { cleanupTempFiles } = await Promise.resolve().then(() => doctor_exports);
@@ -7027,14 +7028,14 @@ function snapshotsDir(dataDir) {
7027
7028
  function snapshotPath(dataDir, id) {
7028
7029
  return path.default.join(snapshotsDir(dataDir), `${id}.json`);
7029
7030
  }
7030
- function dealKey(d) {
7031
+ function dealKey$1(d) {
7031
7032
  return `${d.slug}::${d.name}`;
7032
7033
  }
7033
- function isOpen(stage) {
7034
+ function isOpen$1(stage) {
7034
7035
  return stage !== "won" && stage !== "lost";
7035
7036
  }
7036
7037
  function openValue(deals) {
7037
- return deals.filter((d) => isOpen(d.stage)).reduce((sum, d) => sum + d.value, 0);
7038
+ return deals.filter((d) => isOpen$1(d.stage)).reduce((sum, d) => sum + d.value, 0);
7038
7039
  }
7039
7040
  /** Build a live snapshot of the current pipeline across all customers. */
7040
7041
  function collectDeals(dataDir) {
@@ -7056,6 +7057,18 @@ function snapshotIds(dataDir) {
7056
7057
  function loadSnapshot(dataDir, id) {
7057
7058
  return require_session_store.readJsonFile(snapshotPath(dataDir, id), null);
7058
7059
  }
7060
+ function listSnapshots(dataDir) {
7061
+ return snapshotIds(dataDir).flatMap((id) => {
7062
+ const snap = loadSnapshot(dataDir, id);
7063
+ if (!snap) return [];
7064
+ return [{
7065
+ id,
7066
+ takenAt: snap.takenAt,
7067
+ dealCount: snap.deals.length,
7068
+ openValue: openValue(snap.deals)
7069
+ }];
7070
+ });
7071
+ }
7059
7072
  /** The most recent snapshot whose id is at or before `iso` (YYYY-MM-DD). */
7060
7073
  function latestSnapshotAtOrBefore(dataDir, iso) {
7061
7074
  const id = snapshotIds(dataDir).filter((s) => s <= iso).pop();
@@ -7063,8 +7076,8 @@ function latestSnapshotAtOrBefore(dataDir, iso) {
7063
7076
  }
7064
7077
  /** Compute what changed between two snapshots (before → after). */
7065
7078
  function diffSnapshots(before, after) {
7066
- const beforeByKey = new Map(before.deals.map((d) => [dealKey(d), d]));
7067
- const afterByKey = new Map(after.deals.map((d) => [dealKey(d), d]));
7079
+ const beforeByKey = new Map(before.deals.map((d) => [dealKey$1(d), d]));
7080
+ const afterByKey = new Map(after.deals.map((d) => [dealKey$1(d), d]));
7068
7081
  const added = [];
7069
7082
  const removed = [];
7070
7083
  const advanced = [];
@@ -7138,11 +7151,11 @@ function diffAgainstNow(dataDir, since, today = (/* @__PURE__ */ new Date()).toI
7138
7151
  }
7139
7152
  //#endregion
7140
7153
  //#region src/mcp/tools/get-pipeline-changes.ts
7141
- const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
7154
+ const DATA_DIR$3 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
7142
7155
  function daysAgoIso(days) {
7143
7156
  return (/* @__PURE__ */ new Date(Date.now() - days * 864e5)).toISOString().slice(0, 10);
7144
7157
  }
7145
- async function handleGetPipelineChanges(input, dataDir = DATA_DIR$2) {
7158
+ async function handleGetPipelineChanges(input, dataDir = DATA_DIR$3) {
7146
7159
  const since = input.since ?? daysAgoIso(input.days ?? 7);
7147
7160
  const diff = diffAgainstNow(dataDir, since);
7148
7161
  const payload = diff ? diff : { error: `No pipeline snapshot at or before ${since}. Snapshots accrue daily via the daemon.` };
@@ -7177,6 +7190,158 @@ or { error } when no baseline snapshot exists yet.`,
7177
7190
  });
7178
7191
  }
7179
7192
  //#endregion
7193
+ //#region src/core/velocity.ts
7194
+ const DEFAULT_STALLED_DAYS = 14;
7195
+ const OPEN_STAGES = [
7196
+ "lead",
7197
+ "qualified",
7198
+ "proposal",
7199
+ "negotiation"
7200
+ ];
7201
+ function dealKey(d) {
7202
+ return `${d.slug}::${d.name}`;
7203
+ }
7204
+ function isOpen(stage) {
7205
+ return stage !== "won" && stage !== "lost";
7206
+ }
7207
+ /** Whole days between two YYYY-MM-DD ids (b - a). */
7208
+ function daysBetween(a, b) {
7209
+ const ms = Date.parse(`${b}T00:00:00Z`) - Date.parse(`${a}T00:00:00Z`);
7210
+ return Math.round(ms / 864e5);
7211
+ }
7212
+ function stalledThreshold(opts) {
7213
+ if (opts?.stalledDays !== void 0) return opts.stalledDays;
7214
+ const n = parseInt(process.env["DXCRM_STALLED_DAYS"] ?? "", 10);
7215
+ return Number.isFinite(n) && n > 0 ? n : DEFAULT_STALLED_DAYS;
7216
+ }
7217
+ /** Per-deal chronological list of (date, stage) observations. */
7218
+ function buildTimelines(snaps) {
7219
+ const timelines = /* @__PURE__ */ new Map();
7220
+ for (const snap of snaps) for (const deal of snap.deals) {
7221
+ const key = dealKey(deal);
7222
+ const points = timelines.get(key) ?? [];
7223
+ points.push({
7224
+ date: snap.id,
7225
+ stage: deal.stage,
7226
+ deal
7227
+ });
7228
+ timelines.set(key, points);
7229
+ }
7230
+ return timelines;
7231
+ }
7232
+ function analyzeVelocity(dataDir, opts) {
7233
+ const threshold = stalledThreshold(opts);
7234
+ const metas = listSnapshots(dataDir);
7235
+ if (metas.length === 0) return {
7236
+ fromId: null,
7237
+ toId: null,
7238
+ snapshotCount: 0,
7239
+ stageDurations: [],
7240
+ avgSalesCycleDays: null,
7241
+ wonCount: 0,
7242
+ stalledDeals: [],
7243
+ stalledThresholdDays: threshold
7244
+ };
7245
+ const snaps = metas.flatMap((m) => {
7246
+ const s = loadSnapshot(dataDir, m.id);
7247
+ return s ? [s] : [];
7248
+ });
7249
+ const latestId = snaps[snaps.length - 1].id;
7250
+ const timelines = buildTimelines(snaps);
7251
+ const dwellTotals = /* @__PURE__ */ new Map();
7252
+ const cycleDurations = [];
7253
+ let wonCount = 0;
7254
+ const stalledDeals = [];
7255
+ for (const points of timelines.values()) {
7256
+ const firstSeen = points[0].date;
7257
+ let stageEnteredAt = points[0].date;
7258
+ let currentStage = points[0].stage;
7259
+ for (let i = 1; i < points.length; i++) {
7260
+ const p = points[i];
7261
+ if (p.stage !== currentStage) {
7262
+ const acc = dwellTotals.get(currentStage) ?? {
7263
+ total: 0,
7264
+ samples: 0
7265
+ };
7266
+ acc.total += daysBetween(stageEnteredAt, p.date);
7267
+ acc.samples += 1;
7268
+ dwellTotals.set(currentStage, acc);
7269
+ if (p.stage === "won") {
7270
+ wonCount += 1;
7271
+ cycleDurations.push(daysBetween(firstSeen, p.date));
7272
+ }
7273
+ currentStage = p.stage;
7274
+ stageEnteredAt = p.date;
7275
+ }
7276
+ }
7277
+ const last = points[points.length - 1];
7278
+ if (last.date === latestId && isOpen(last.stage)) {
7279
+ const daysInStage = daysBetween(stageEnteredAt, latestId);
7280
+ if (daysInStage > threshold) stalledDeals.push({
7281
+ slug: last.deal.slug,
7282
+ name: last.deal.name,
7283
+ stage: last.stage,
7284
+ daysInStage,
7285
+ value: last.deal.value
7286
+ });
7287
+ }
7288
+ }
7289
+ const stageDurations = OPEN_STAGES.flatMap((stage) => {
7290
+ const acc = dwellTotals.get(stage);
7291
+ if (!acc || acc.samples === 0) return [];
7292
+ return [{
7293
+ stage,
7294
+ avgDays: Math.round(acc.total / acc.samples),
7295
+ samples: acc.samples
7296
+ }];
7297
+ });
7298
+ const avgSalesCycleDays = cycleDurations.length > 0 ? Math.round(cycleDurations.reduce((a, b) => a + b, 0) / cycleDurations.length) : null;
7299
+ stalledDeals.sort((a, b) => b.daysInStage - a.daysInStage);
7300
+ return {
7301
+ fromId: snaps[0].id,
7302
+ toId: latestId,
7303
+ snapshotCount: snaps.length,
7304
+ stageDurations,
7305
+ avgSalesCycleDays,
7306
+ wonCount,
7307
+ stalledDeals,
7308
+ stalledThresholdDays: threshold
7309
+ };
7310
+ }
7311
+ //#endregion
7312
+ //#region src/mcp/tools/get-pipeline-velocity.ts
7313
+ const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
7314
+ async function handleGetPipelineVelocity(input, dataDir = DATA_DIR$2) {
7315
+ const opts = {};
7316
+ if (input.stalledDays !== void 0) opts.stalledDays = input.stalledDays;
7317
+ const report = analyzeVelocity(dataDir, opts);
7318
+ return { content: [{
7319
+ type: "text",
7320
+ text: JSON.stringify(report, null, 2)
7321
+ }] };
7322
+ }
7323
+ function registerGetPipelineVelocity(server) {
7324
+ server.registerTool("get_pipeline_velocity", {
7325
+ title: "Get Pipeline Velocity",
7326
+ description: `Pipeline velocity analytics from the daily snapshot history.
7327
+ Reconstructs each deal's stage journey to answer "where do deals get stuck?"
7328
+ and "which deals are rotting?".
7329
+
7330
+ Args:
7331
+ stalledDays: A deal open in the same stage longer than this is "stalled"
7332
+ (default 14).
7333
+
7334
+ Returns: { fromId, toId, snapshotCount, stageDurations[{stage,avgDays,samples}],
7335
+ avgSalesCycleDays, wonCount, stalledDeals[{slug,name,stage,daysInStage,value}],
7336
+ stalledThresholdDays }. snapshotCount is 0 until the daemon has taken snapshots.`,
7337
+ inputSchema: zod.z.object({ stalledDays: zod.z.number().int().min(1).max(365).optional().describe("Days in one stage before a deal counts as stalled (default 14)") })
7338
+ }, async ({ stalledDays }) => {
7339
+ const input = {};
7340
+ if (stalledDays !== void 0) input.stalledDays = stalledDays;
7341
+ return handleGetPipelineVelocity(input);
7342
+ });
7343
+ }
7344
+ //#endregion
7180
7345
  //#region src/mcp/prompts.ts
7181
7346
  /**
7182
7347
  * CRM playbook prompts exposed via MCP `prompts/list` + `prompts/get`.
@@ -7595,6 +7760,7 @@ function createMcpServer() {
7595
7760
  registerGetLogs(server);
7596
7761
  registerGetDiagnostics(server);
7597
7762
  registerGetPipelineChanges(server);
7763
+ registerGetPipelineVelocity(server);
7598
7764
  registerCustomObjectTools(server);
7599
7765
  registerPrompts(server);
7600
7766
  registerResources(server);