@datasynx/agentic-crm 1.8.0 → 1.9.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/cli.js +71 -8
- package/dist/cli.js.map +1 -1
- package/dist/daemon/worker.js +3 -3
- package/dist/funnel-B2mwpZE1.js +89 -0
- package/dist/funnel-B2mwpZE1.js.map +1 -0
- package/dist/funnel-CJ7fy7hG.js +2 -0
- package/dist/{index-Ewy4f1XW.d.cts → index-BAutNcAT.d.cts} +17 -17
- package/dist/index-BAutNcAT.d.cts.map +1 -0
- package/dist/{index-DoYT-azq.d.ts → index-BBAlKZg6.d.ts} +8 -8
- package/dist/index-BBAlKZg6.d.ts.map +1 -0
- package/dist/index.d.cts +17 -17
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/{knowledge-base-Byo0zwM5.js → knowledge-base-yo-BLUcB.js} +2 -1
- package/dist/knowledge-base-yo-BLUcB.js.map +1 -0
- package/dist/{login-yt9OOQQk.js → login-dc_Hqosw.js} +2 -2
- package/dist/{login-yt9OOQQk.js.map → login-dc_Hqosw.js.map} +1 -1
- package/dist/mailbox-config-BU3vib2T.js +2 -0
- package/dist/{mailbox-config-Dn2xTn9N.js → mailbox-config-trjLPHPG.js} +2 -2
- package/dist/{mailbox-config-Dn2xTn9N.js.map → mailbox-config-trjLPHPG.js.map} +1 -1
- package/dist/{mailbox-poll-B8dvFAXT.js → mailbox-poll-Ban7C3X0.js} +3 -3
- package/dist/{mailbox-poll-B8dvFAXT.js.map → mailbox-poll-Ban7C3X0.js.map} +1 -1
- package/dist/mcp-CdTJWTJf.d.cts.map +1 -1
- package/dist/mcp-CdTJWTJf.d.ts.map +1 -1
- package/dist/mcp.cjs +246 -134
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.d.cts.map +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +246 -134
- package/dist/mcp.js.map +1 -1
- package/dist/{server-C0XkJQBo.js → server-DAcwmRPE.js} +162 -135
- package/dist/server-DAcwmRPE.js.map +1 -0
- package/dist/{token-resolver-D98qPOOf.js → token-resolver-CL8-CSgZ.js} +2 -2
- package/dist/{token-resolver-D98qPOOf.js.map → token-resolver-CL8-CSgZ.js.map} +1 -1
- package/dist/{token-store-B0h0USqe.js → token-store-BPDwePNT.js} +13 -2
- package/dist/token-store-BPDwePNT.js.map +1 -0
- package/dist/token-store-D3HCeXmE.js +2 -0
- package/package.json +1 -1
- package/dist/index-DoYT-azq.d.ts.map +0 -1
- package/dist/index-Ewy4f1XW.d.cts.map +0 -1
- package/dist/knowledge-base-Byo0zwM5.js.map +0 -1
- package/dist/mailbox-config-Bu-J1O4I.js +0 -2
- package/dist/server-C0XkJQBo.js.map +0 -1
- package/dist/token-store-B0h0USqe.js.map +0 -1
- package/dist/token-store-CEmz8d-0.js +0 -2
package/dist/mcp.js
CHANGED
|
@@ -446,6 +446,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
|
|
|
446
446
|
| get_diagnostics | Self-diagnostic health check (data integrity, temp files, log errors, backups) | admin |
|
|
447
447
|
| get_pipeline_changes | Pipeline time-travel: what changed (won/lost/moved/value) since a date | any |
|
|
448
448
|
| get_pipeline_velocity | Stage dwell times, sales cycle, and stalled deals from snapshot history | any |
|
|
449
|
+
| get_pipeline_funnel | Conversion funnel & win rate: where deals leak out of the pipeline | any |
|
|
449
450
|
| define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |
|
|
450
451
|
| create_record | Create a record of a custom object (validated against its schema) | rep+ |
|
|
451
452
|
| list_records | List records of a custom object | any |
|
|
@@ -1424,7 +1425,7 @@ async function buildContext(dataDir, slug) {
|
|
|
1424
1425
|
}
|
|
1425
1426
|
//#endregion
|
|
1426
1427
|
//#region src/mcp/tools/get-customer-context.ts
|
|
1427
|
-
const DATA_DIR$
|
|
1428
|
+
const DATA_DIR$56 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
1428
1429
|
function triggerOnQuerySync(dataDir, slug) {
|
|
1429
1430
|
const auth = getGmailAuth();
|
|
1430
1431
|
if (!auth) return;
|
|
@@ -1445,7 +1446,7 @@ function triggerOnQuerySync(dataDir, slug) {
|
|
|
1445
1446
|
}).then(() => updateSlugSyncState(dataDir, slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() })).catch(() => {})).catch(() => {});
|
|
1446
1447
|
} catch {}
|
|
1447
1448
|
}
|
|
1448
|
-
async function handleGetCustomerContext(input, dataDir = DATA_DIR$
|
|
1449
|
+
async function handleGetCustomerContext(input, dataDir = DATA_DIR$56) {
|
|
1449
1450
|
const targetSlug = input.slug ?? getSession()?.customerSlug;
|
|
1450
1451
|
if (!targetSlug) return {
|
|
1451
1452
|
content: [{
|
|
@@ -1581,8 +1582,8 @@ async function searchKnowledge(dataDir, slug, query, limit) {
|
|
|
1581
1582
|
}
|
|
1582
1583
|
//#endregion
|
|
1583
1584
|
//#region src/mcp/tools/search-customer-knowledge.ts
|
|
1584
|
-
const DATA_DIR$
|
|
1585
|
-
async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$
|
|
1585
|
+
const DATA_DIR$55 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
1586
|
+
async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$55) {
|
|
1586
1587
|
const limit = input.limit ?? 5;
|
|
1587
1588
|
try {
|
|
1588
1589
|
const results = await searchKnowledge(dataDir, input.slug, input.query, limit);
|
|
@@ -1630,14 +1631,14 @@ If no results: returns empty array with a helpful sync suggestion.`,
|
|
|
1630
1631
|
}
|
|
1631
1632
|
//#endregion
|
|
1632
1633
|
//#region src/mcp/tools/list-customers.ts
|
|
1633
|
-
const DATA_DIR$
|
|
1634
|
+
const DATA_DIR$54 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
1634
1635
|
function extractLastInteractionDate(interactionsPath) {
|
|
1635
1636
|
if (!fs.existsSync(interactionsPath)) return void 0;
|
|
1636
1637
|
const content = fs.readFileSync(interactionsPath, "utf-8");
|
|
1637
1638
|
const match = /^## (\d{4}-\d{2}-\d{2})/m.exec(content);
|
|
1638
1639
|
return match ? match[1] : void 0;
|
|
1639
1640
|
}
|
|
1640
|
-
async function handleListCustomers(input, dataDir = DATA_DIR$
|
|
1641
|
+
async function handleListCustomers(input, dataDir = DATA_DIR$54) {
|
|
1641
1642
|
const customersDir = path.join(dataDir, "customers");
|
|
1642
1643
|
const customers = [];
|
|
1643
1644
|
if (!fs.existsSync(customersDir)) return { content: [{
|
|
@@ -2150,8 +2151,8 @@ async function updateHealthFromInteraction(dataDir, slug) {
|
|
|
2150
2151
|
}
|
|
2151
2152
|
//#endregion
|
|
2152
2153
|
//#region src/mcp/tools/log-interaction.ts
|
|
2153
|
-
const DATA_DIR$
|
|
2154
|
-
async function handleLogInteraction(input, dataDir = DATA_DIR$
|
|
2154
|
+
const DATA_DIR$53 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2155
|
+
async function handleLogInteraction(input, dataDir = DATA_DIR$53) {
|
|
2155
2156
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2156
2157
|
const interactionDate = input.date ?? today;
|
|
2157
2158
|
const sourceRef = input.source ?? `agent://log/${Date.now()}`;
|
|
@@ -2260,8 +2261,8 @@ var update_deal_exports = /* @__PURE__ */ __exportAll({
|
|
|
2260
2261
|
handleUpdateDeal: () => handleUpdateDeal,
|
|
2261
2262
|
registerUpdateDeal: () => registerUpdateDeal
|
|
2262
2263
|
});
|
|
2263
|
-
const DATA_DIR$
|
|
2264
|
-
async function handleUpdateDeal(input, dataDir = DATA_DIR$
|
|
2264
|
+
const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2265
|
+
async function handleUpdateDeal(input, dataDir = DATA_DIR$52) {
|
|
2265
2266
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2266
2267
|
const deal = {
|
|
2267
2268
|
name: input.dealName,
|
|
@@ -2344,12 +2345,12 @@ Returns: { success: boolean, deal: object }`,
|
|
|
2344
2345
|
}
|
|
2345
2346
|
//#endregion
|
|
2346
2347
|
//#region src/mcp/tools/export-customer.ts
|
|
2347
|
-
const DATA_DIR$
|
|
2348
|
+
const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2348
2349
|
function countInteractions(content) {
|
|
2349
2350
|
const matches = content.match(/^## \d{4}-\d{2}-\d{2}/gm);
|
|
2350
2351
|
return matches ? matches.length : 0;
|
|
2351
2352
|
}
|
|
2352
|
-
async function handleExportCustomer(input, dataDir = DATA_DIR$
|
|
2353
|
+
async function handleExportCustomer(input, dataDir = DATA_DIR$51) {
|
|
2353
2354
|
enforceRbac(dataDir, "export_customer");
|
|
2354
2355
|
const customerDir = path.join(dataDir, "customers", input.slug);
|
|
2355
2356
|
if (!fs.existsSync(customerDir)) return {
|
|
@@ -2462,8 +2463,8 @@ Returns:
|
|
|
2462
2463
|
}
|
|
2463
2464
|
//#endregion
|
|
2464
2465
|
//#region src/mcp/tools/update-customer-facts.ts
|
|
2465
|
-
const DATA_DIR$
|
|
2466
|
-
async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$
|
|
2466
|
+
const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2467
|
+
async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$50) {
|
|
2467
2468
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2468
2469
|
try {
|
|
2469
2470
|
enforceRbac(dataDir, "update_customer_facts");
|
|
@@ -2641,8 +2642,8 @@ function scoreDealForToday(deal, todayDate) {
|
|
|
2641
2642
|
}
|
|
2642
2643
|
//#endregion
|
|
2643
2644
|
//#region src/mcp/tools/get-deal-health.ts
|
|
2644
|
-
const DATA_DIR$
|
|
2645
|
-
async function handleGetDealHealth(input, dataDir = DATA_DIR$
|
|
2645
|
+
const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2646
|
+
async function handleGetDealHealth(input, dataDir = DATA_DIR$49) {
|
|
2646
2647
|
try {
|
|
2647
2648
|
const deals = await readPipeline(dataDir, input.slug);
|
|
2648
2649
|
const today = /* @__PURE__ */ new Date();
|
|
@@ -2691,8 +2692,8 @@ Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }`,
|
|
|
2691
2692
|
}
|
|
2692
2693
|
//#endregion
|
|
2693
2694
|
//#region src/mcp/tools/get-pipeline-forecast.ts
|
|
2694
|
-
const DATA_DIR$
|
|
2695
|
-
async function handleGetPipelineForecast(input, dataDir = DATA_DIR$
|
|
2695
|
+
const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2696
|
+
async function handleGetPipelineForecast(input, dataDir = DATA_DIR$48) {
|
|
2696
2697
|
try {
|
|
2697
2698
|
const slugs = listCustomerSlugs(dataDir).filter((d) => !input.filter || d.includes(input.filter));
|
|
2698
2699
|
const allDeals = [];
|
|
@@ -2753,8 +2754,8 @@ Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count,
|
|
|
2753
2754
|
}
|
|
2754
2755
|
//#endregion
|
|
2755
2756
|
//#region src/mcp/tools/summarize-meeting.ts
|
|
2756
|
-
const DATA_DIR$
|
|
2757
|
-
async function handleSummarizeMeeting(input, dataDir = DATA_DIR$
|
|
2757
|
+
const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2758
|
+
async function handleSummarizeMeeting(input, dataDir = DATA_DIR$47) {
|
|
2758
2759
|
try {
|
|
2759
2760
|
let summary = input.transcript.slice(0, 400);
|
|
2760
2761
|
let nextSteps = [];
|
|
@@ -2877,8 +2878,8 @@ function getPipelineStages(dataDir) {
|
|
|
2877
2878
|
}
|
|
2878
2879
|
//#endregion
|
|
2879
2880
|
//#region src/mcp/tools/get-pipeline-stages.ts
|
|
2880
|
-
const DATA_DIR$
|
|
2881
|
-
async function handleGetPipelineStages(_input, dataDir = DATA_DIR$
|
|
2881
|
+
const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2882
|
+
async function handleGetPipelineStages(_input, dataDir = DATA_DIR$46) {
|
|
2882
2883
|
const stages = getPipelineStages(dataDir);
|
|
2883
2884
|
return { content: [{
|
|
2884
2885
|
type: "text",
|
|
@@ -2906,8 +2907,8 @@ async function searchAcrossCustomers(dataDir, query, limit = 5, excludeSlug) {
|
|
|
2906
2907
|
}
|
|
2907
2908
|
//#endregion
|
|
2908
2909
|
//#region src/mcp/tools/get-market-intelligence.ts
|
|
2909
|
-
const DATA_DIR$
|
|
2910
|
-
async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$
|
|
2910
|
+
const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2911
|
+
async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$45) {
|
|
2911
2912
|
const excludeSlug = input.excludeCurrentCustomer ? input.slug : void 0;
|
|
2912
2913
|
const all = listCustomerSlugs(dataDir);
|
|
2913
2914
|
const totalCustomersSearched = excludeSlug ? all.filter((s) => s !== excludeSlug).length : all.length;
|
|
@@ -2938,7 +2939,7 @@ function registerGetMarketIntelligence(server) {
|
|
|
2938
2939
|
}
|
|
2939
2940
|
//#endregion
|
|
2940
2941
|
//#region src/mcp/tools/get-relationship-graph.ts
|
|
2941
|
-
const DATA_DIR$
|
|
2942
|
+
const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2942
2943
|
function summarizeNode(n) {
|
|
2943
2944
|
return {
|
|
2944
2945
|
id: n.id,
|
|
@@ -2946,7 +2947,7 @@ function summarizeNode(n) {
|
|
|
2946
2947
|
email: n.properties["email"]
|
|
2947
2948
|
};
|
|
2948
2949
|
}
|
|
2949
|
-
async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$
|
|
2950
|
+
async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$44) {
|
|
2950
2951
|
try {
|
|
2951
2952
|
const graph = readGraph(dataDir, input.slug);
|
|
2952
2953
|
const stakeholders = getStakeholders(graph);
|
|
@@ -3014,9 +3015,9 @@ Returns: {
|
|
|
3014
3015
|
}
|
|
3015
3016
|
//#endregion
|
|
3016
3017
|
//#region src/mcp/tools/get-relationship-health.ts
|
|
3017
|
-
const DATA_DIR$
|
|
3018
|
+
const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
3018
3019
|
const MAX_HEALTH_AGE_MS = 3600 * 1e3;
|
|
3019
|
-
async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$
|
|
3020
|
+
async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$43) {
|
|
3020
3021
|
try {
|
|
3021
3022
|
let health = readHealth(dataDir, input.slug);
|
|
3022
3023
|
if (health === null || Date.now() - new Date(health.updatedAt).getTime() > MAX_HEALTH_AGE_MS) {
|
|
@@ -3685,8 +3686,8 @@ async function runDealAgent(config, dataDir, llmFn = callLlm) {
|
|
|
3685
3686
|
}
|
|
3686
3687
|
//#endregion
|
|
3687
3688
|
//#region src/mcp/tools/run-deal-agent.ts
|
|
3688
|
-
const DATA_DIR$
|
|
3689
|
-
async function handleRunDealAgent(input, dataDir = DATA_DIR$
|
|
3689
|
+
const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
3690
|
+
async function handleRunDealAgent(input, dataDir = DATA_DIR$42) {
|
|
3690
3691
|
try {
|
|
3691
3692
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3692
3693
|
const result = await runDealAgent({
|
|
@@ -3753,8 +3754,8 @@ Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], tr
|
|
|
3753
3754
|
}
|
|
3754
3755
|
//#endregion
|
|
3755
3756
|
//#region src/mcp/tools/approve-agent-action.ts
|
|
3756
|
-
const DATA_DIR$
|
|
3757
|
-
async function handleApproveAgentAction(input, dataDir = DATA_DIR$
|
|
3757
|
+
const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
3758
|
+
async function handleApproveAgentAction(input, dataDir = DATA_DIR$41) {
|
|
3758
3759
|
try {
|
|
3759
3760
|
const queue = readAgentQueue(dataDir, input.slug);
|
|
3760
3761
|
const idx = queue.pendingActions.findIndex((a) => a.actionId === input.actionId);
|
|
@@ -4014,8 +4015,8 @@ async function buildSimulationInput(dataDir, horizon, today, externalSignals = [
|
|
|
4014
4015
|
}
|
|
4015
4016
|
//#endregion
|
|
4016
4017
|
//#region src/mcp/tools/simulate-revenue.ts
|
|
4017
|
-
const DATA_DIR$
|
|
4018
|
-
async function handleSimulateRevenue(input, dataDir = DATA_DIR$
|
|
4018
|
+
const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4019
|
+
async function handleSimulateRevenue(input, dataDir = DATA_DIR$40) {
|
|
4019
4020
|
try {
|
|
4020
4021
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4021
4022
|
const horizon = input.horizon ?? "quarter";
|
|
@@ -4073,8 +4074,8 @@ Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMo
|
|
|
4073
4074
|
}
|
|
4074
4075
|
//#endregion
|
|
4075
4076
|
//#region src/mcp/tools/get-playbook.ts
|
|
4076
|
-
const DATA_DIR$
|
|
4077
|
-
async function handleGetPlaybook(input, dataDir = DATA_DIR$
|
|
4077
|
+
const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4078
|
+
async function handleGetPlaybook(input, dataDir = DATA_DIR$39) {
|
|
4078
4079
|
try {
|
|
4079
4080
|
const playbooks = listPlaybooks(dataDir, input.slug);
|
|
4080
4081
|
if (!(input.stage !== void 0 || input.value !== void 0 || input.healthScore !== void 0)) return { content: [{
|
|
@@ -4159,12 +4160,12 @@ Returns: { matches: [{ name, score, trigger, successRate, usedCount, content }],
|
|
|
4159
4160
|
...healthScore !== void 0 ? { healthScore } : {},
|
|
4160
4161
|
...daysSinceContact !== void 0 ? { daysSinceContact } : {},
|
|
4161
4162
|
...championPresent !== void 0 ? { championPresent } : {}
|
|
4162
|
-
}, DATA_DIR$
|
|
4163
|
+
}, DATA_DIR$39));
|
|
4163
4164
|
}
|
|
4164
4165
|
//#endregion
|
|
4165
4166
|
//#region src/mcp/tools/create-playbook.ts
|
|
4166
|
-
const DATA_DIR$
|
|
4167
|
-
async function handleCreatePlaybook(input, dataDir = DATA_DIR$
|
|
4167
|
+
const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4168
|
+
async function handleCreatePlaybook(input, dataDir = DATA_DIR$38) {
|
|
4168
4169
|
try {
|
|
4169
4170
|
const name = toKebabCase(input.name);
|
|
4170
4171
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -4237,12 +4238,12 @@ Returns: { success: true, playbook: { name, trigger, successRate, path } }`,
|
|
|
4237
4238
|
trigger,
|
|
4238
4239
|
content,
|
|
4239
4240
|
...successRate !== void 0 ? { successRate } : {}
|
|
4240
|
-
}, DATA_DIR$
|
|
4241
|
+
}, DATA_DIR$38));
|
|
4241
4242
|
}
|
|
4242
4243
|
//#endregion
|
|
4243
4244
|
//#region src/mcp/tools/list-playbooks.ts
|
|
4244
|
-
const DATA_DIR$
|
|
4245
|
-
async function handleListPlaybooks(input, dataDir = DATA_DIR$
|
|
4245
|
+
const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4246
|
+
async function handleListPlaybooks(input, dataDir = DATA_DIR$37) {
|
|
4246
4247
|
try {
|
|
4247
4248
|
const playbooks = listPlaybooks(dataDir, input.slug);
|
|
4248
4249
|
return { content: [{
|
|
@@ -4281,12 +4282,12 @@ Args:
|
|
|
4281
4282
|
|
|
4282
4283
|
Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }`,
|
|
4283
4284
|
inputSchema: z.object({ slug: z.string().describe("Customer ID") })
|
|
4284
|
-
}, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$
|
|
4285
|
+
}, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$37));
|
|
4285
4286
|
}
|
|
4286
4287
|
//#endregion
|
|
4287
4288
|
//#region src/mcp/tools/distill-playbook.ts
|
|
4288
|
-
const DATA_DIR$
|
|
4289
|
-
async function handleDistillPlaybook(input, dataDir = DATA_DIR$
|
|
4289
|
+
const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4290
|
+
async function handleDistillPlaybook(input, dataDir = DATA_DIR$36, llmFn = callLlm) {
|
|
4290
4291
|
try {
|
|
4291
4292
|
const result = await distillPlaybook(dataDir, input.slug, input.dealName, input.outcome, llmFn);
|
|
4292
4293
|
if (!result.ok) {
|
|
@@ -4345,7 +4346,7 @@ Returns: { success: true, playbook: { name, trigger, successRate, path }, reason
|
|
|
4345
4346
|
slug,
|
|
4346
4347
|
dealName,
|
|
4347
4348
|
outcome
|
|
4348
|
-
}, DATA_DIR$
|
|
4349
|
+
}, DATA_DIR$36));
|
|
4349
4350
|
}
|
|
4350
4351
|
//#endregion
|
|
4351
4352
|
//#region src/core/goal-engine.ts
|
|
@@ -4563,8 +4564,8 @@ function getActiveGoals(dataDir) {
|
|
|
4563
4564
|
}
|
|
4564
4565
|
//#endregion
|
|
4565
4566
|
//#region src/mcp/tools/pursue-goal.ts
|
|
4566
|
-
const DATA_DIR$
|
|
4567
|
-
async function handlePursueGoal(input, dataDir = DATA_DIR$
|
|
4567
|
+
const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4568
|
+
async function handlePursueGoal(input, dataDir = DATA_DIR$35, options = {}) {
|
|
4568
4569
|
try {
|
|
4569
4570
|
enforceRbac(dataDir, "pursue_goal");
|
|
4570
4571
|
const goal = await pursueGoal(dataDir, {
|
|
@@ -4627,12 +4628,12 @@ Returns: { goalId, description, target, deadline, decomposition: { analysis, cur
|
|
|
4627
4628
|
goal,
|
|
4628
4629
|
deadline,
|
|
4629
4630
|
...context !== void 0 ? { context } : {}
|
|
4630
|
-
}, DATA_DIR$
|
|
4631
|
+
}, DATA_DIR$35));
|
|
4631
4632
|
}
|
|
4632
4633
|
//#endregion
|
|
4633
4634
|
//#region src/mcp/tools/get-goal-status.ts
|
|
4634
|
-
const DATA_DIR$
|
|
4635
|
-
async function handleGetGoalStatus(input, dataDir = DATA_DIR$
|
|
4635
|
+
const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4636
|
+
async function handleGetGoalStatus(input, dataDir = DATA_DIR$34) {
|
|
4636
4637
|
try {
|
|
4637
4638
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4638
4639
|
const allGoals = input.goalId ? readGoals(dataDir).filter((g) => g.id === input.goalId) : getActiveGoals(dataDir);
|
|
@@ -4691,17 +4692,17 @@ Args:
|
|
|
4691
4692
|
|
|
4692
4693
|
Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }`,
|
|
4693
4694
|
inputSchema: z.object({ goalId: z.string().optional().describe("Specific goal ID (omit for all active goals)") })
|
|
4694
|
-
}, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$
|
|
4695
|
+
}, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$34));
|
|
4695
4696
|
}
|
|
4696
4697
|
//#endregion
|
|
4697
4698
|
//#region src/mcp/tools/register-push-subscription.ts
|
|
4698
|
-
const DATA_DIR$
|
|
4699
|
+
const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4699
4700
|
const VALID_PROVIDERS = [
|
|
4700
4701
|
"gmail",
|
|
4701
4702
|
"microsoft-graph",
|
|
4702
4703
|
"slack"
|
|
4703
4704
|
];
|
|
4704
|
-
async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$
|
|
4705
|
+
async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$33) {
|
|
4705
4706
|
try {
|
|
4706
4707
|
if (!VALID_PROVIDERS.includes(input.provider)) return { content: [{
|
|
4707
4708
|
type: "text",
|
|
@@ -4787,12 +4788,12 @@ Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning
|
|
|
4787
4788
|
...microsoftResource !== void 0 ? { microsoftResource } : {},
|
|
4788
4789
|
...slackTeamId !== void 0 ? { slackTeamId } : {},
|
|
4789
4790
|
...slackChannelId !== void 0 ? { slackChannelId } : {}
|
|
4790
|
-
}, DATA_DIR$
|
|
4791
|
+
}, DATA_DIR$33));
|
|
4791
4792
|
}
|
|
4792
4793
|
//#endregion
|
|
4793
4794
|
//#region src/mcp/tools/get-push-status.ts
|
|
4794
|
-
const DATA_DIR$
|
|
4795
|
-
async function handleGetPushStatus(input, dataDir = DATA_DIR$
|
|
4795
|
+
const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4796
|
+
async function handleGetPushStatus(input, dataDir = DATA_DIR$32) {
|
|
4796
4797
|
try {
|
|
4797
4798
|
let subs = await readSubscriptions(dataDir);
|
|
4798
4799
|
if (input.slug) subs = subs.filter((s) => s.slug === input.slug);
|
|
@@ -4864,7 +4865,7 @@ Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHou
|
|
|
4864
4865
|
}, async ({ slug, provider }) => handleGetPushStatus({
|
|
4865
4866
|
...slug !== void 0 ? { slug } : {},
|
|
4866
4867
|
...provider !== void 0 ? { provider } : {}
|
|
4867
|
-
}, DATA_DIR$
|
|
4868
|
+
}, DATA_DIR$32));
|
|
4868
4869
|
}
|
|
4869
4870
|
//#endregion
|
|
4870
4871
|
//#region src/core/org-intelligence.ts
|
|
@@ -4930,8 +4931,8 @@ function deriveRecommendation(people, missingRoles) {
|
|
|
4930
4931
|
}
|
|
4931
4932
|
//#endregion
|
|
4932
4933
|
//#region src/mcp/tools/get-org-intelligence.ts
|
|
4933
|
-
const DATA_DIR$
|
|
4934
|
-
async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$
|
|
4934
|
+
const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4935
|
+
async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$31) {
|
|
4935
4936
|
try {
|
|
4936
4937
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4937
4938
|
const map = buildStakeholderMap(dataDir, input.slug, today, input.dealName);
|
|
@@ -5064,8 +5065,8 @@ function buildExecutiveSummary(slug, dealName, stakeholders, overallHealth, sim,
|
|
|
5064
5065
|
}
|
|
5065
5066
|
//#endregion
|
|
5066
5067
|
//#region src/mcp/tools/open-deal-room.ts
|
|
5067
|
-
const DATA_DIR$
|
|
5068
|
-
async function handleOpenDealRoom(input, dataDir = DATA_DIR$
|
|
5068
|
+
const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5069
|
+
async function handleOpenDealRoom(input, dataDir = DATA_DIR$30) {
|
|
5069
5070
|
try {
|
|
5070
5071
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5071
5072
|
const brief = await buildDealRoom(dataDir, input.slug, input.dealName, today);
|
|
@@ -5148,8 +5149,8 @@ async function buildDailyBriefing(dataDir, today) {
|
|
|
5148
5149
|
}
|
|
5149
5150
|
//#endregion
|
|
5150
5151
|
//#region src/mcp/tools/get-proactive-briefing.ts
|
|
5151
|
-
const DATA_DIR$
|
|
5152
|
-
async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$
|
|
5152
|
+
const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5153
|
+
async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$29) {
|
|
5153
5154
|
try {
|
|
5154
5155
|
const briefing = await buildDailyBriefing(dataDir, input.date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
5155
5156
|
return { content: [{
|
|
@@ -5249,15 +5250,15 @@ function getTemplate(dataDir, id) {
|
|
|
5249
5250
|
}
|
|
5250
5251
|
//#endregion
|
|
5251
5252
|
//#region src/mcp/tools/list-email-templates.ts
|
|
5252
|
-
const DATA_DIR$
|
|
5253
|
-
async function handleListEmailTemplates(input, dataDir = DATA_DIR$
|
|
5253
|
+
const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5254
|
+
async function handleListEmailTemplates(input, dataDir = DATA_DIR$28) {
|
|
5254
5255
|
const summary = listTemplates(dataDir, input.category ? { category: input.category } : {}).map(({ body: _body, ...meta }) => meta);
|
|
5255
5256
|
return { content: [{
|
|
5256
5257
|
type: "text",
|
|
5257
5258
|
text: JSON.stringify(summary, null, 2)
|
|
5258
5259
|
}] };
|
|
5259
5260
|
}
|
|
5260
|
-
function registerListEmailTemplates(server, dataDir = DATA_DIR$
|
|
5261
|
+
function registerListEmailTemplates(server, dataDir = DATA_DIR$28) {
|
|
5261
5262
|
server.registerTool("list_email_templates", {
|
|
5262
5263
|
description: "List available email templates. Optionally filter by category (e.g. 'outreach', 'followup', 'support').",
|
|
5263
5264
|
inputSchema: z.object({ category: z.string().optional().describe("Filter by category") })
|
|
@@ -5291,8 +5292,8 @@ async function buildVariablesFromCustomer(dataDir, slug) {
|
|
|
5291
5292
|
}
|
|
5292
5293
|
//#endregion
|
|
5293
5294
|
//#region src/mcp/tools/get-email-template.ts
|
|
5294
|
-
const DATA_DIR$
|
|
5295
|
-
async function handleGetEmailTemplate(input, dataDir = DATA_DIR$
|
|
5295
|
+
const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5296
|
+
async function handleGetEmailTemplate(input, dataDir = DATA_DIR$27) {
|
|
5296
5297
|
const tmpl = getTemplate(dataDir, input.id);
|
|
5297
5298
|
if (!tmpl) return { content: [{
|
|
5298
5299
|
type: "text",
|
|
@@ -5308,7 +5309,7 @@ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$26) {
|
|
|
5308
5309
|
}, null, 2)
|
|
5309
5310
|
}] };
|
|
5310
5311
|
}
|
|
5311
|
-
function registerGetEmailTemplate(server, dataDir = DATA_DIR$
|
|
5312
|
+
function registerGetEmailTemplate(server, dataDir = DATA_DIR$27) {
|
|
5312
5313
|
server.registerTool("get_email_template", {
|
|
5313
5314
|
description: "Get a specific email template by ID, including its body and detected variables.",
|
|
5314
5315
|
inputSchema: z.object({ id: z.string().describe("Template ID (e.g. 'enterprise-intro')") })
|
|
@@ -5316,8 +5317,8 @@ function registerGetEmailTemplate(server, dataDir = DATA_DIR$26) {
|
|
|
5316
5317
|
}
|
|
5317
5318
|
//#endregion
|
|
5318
5319
|
//#region src/mcp/tools/draft-email.ts
|
|
5319
|
-
const DATA_DIR$
|
|
5320
|
-
async function handleDraftEmail(input, dataDir = DATA_DIR$
|
|
5320
|
+
const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5321
|
+
async function handleDraftEmail(input, dataDir = DATA_DIR$26) {
|
|
5321
5322
|
const tmpl = getTemplate(dataDir, input.templateId);
|
|
5322
5323
|
if (!tmpl) return { content: [{
|
|
5323
5324
|
type: "text",
|
|
@@ -5361,7 +5362,7 @@ async function handleDraftEmail(input, dataDir = DATA_DIR$25) {
|
|
|
5361
5362
|
}, null, 2)
|
|
5362
5363
|
}] };
|
|
5363
5364
|
}
|
|
5364
|
-
function registerDraftEmail(server, dataDir = DATA_DIR$
|
|
5365
|
+
function registerDraftEmail(server, dataDir = DATA_DIR$26) {
|
|
5365
5366
|
server.registerTool("draft_email", {
|
|
5366
5367
|
description: `Draft a personalized email for a customer using a stored template.
|
|
5367
5368
|
Variables are auto-filled from the customer's main_facts.md. Override any variable manually.
|
|
@@ -5469,8 +5470,8 @@ async function updateEnrollment(dataDir, id, updates) {
|
|
|
5469
5470
|
}
|
|
5470
5471
|
//#endregion
|
|
5471
5472
|
//#region src/mcp/tools/enroll-in-sequence.ts
|
|
5472
|
-
const DATA_DIR$
|
|
5473
|
-
async function handleEnrollInSequence(input, dataDir = DATA_DIR$
|
|
5473
|
+
const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5474
|
+
async function handleEnrollInSequence(input, dataDir = DATA_DIR$25) {
|
|
5474
5475
|
const sequence = getSequence(dataDir, input.sequenceId);
|
|
5475
5476
|
if (!sequence) return { content: [{
|
|
5476
5477
|
type: "text",
|
|
@@ -5502,7 +5503,7 @@ async function handleEnrollInSequence(input, dataDir = DATA_DIR$24) {
|
|
|
5502
5503
|
})
|
|
5503
5504
|
}] };
|
|
5504
5505
|
}
|
|
5505
|
-
function registerEnrollInSequence(server, dataDir = DATA_DIR$
|
|
5506
|
+
function registerEnrollInSequence(server, dataDir = DATA_DIR$25) {
|
|
5506
5507
|
server.registerTool("enroll_in_sequence", {
|
|
5507
5508
|
description: `Enroll a contact in an email sequence. Validates that the sequence and its first template exist.
|
|
5508
5509
|
Returns: { enrollmentId, sequenceName, totalSteps }`,
|
|
@@ -5519,8 +5520,8 @@ Returns: { enrollmentId, sequenceName, totalSteps }`,
|
|
|
5519
5520
|
}
|
|
5520
5521
|
//#endregion
|
|
5521
5522
|
//#region src/mcp/tools/list-sequence-enrollments.ts
|
|
5522
|
-
const DATA_DIR$
|
|
5523
|
-
async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$
|
|
5523
|
+
const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5524
|
+
async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$24) {
|
|
5524
5525
|
let enrollments = readEnrollments(dataDir);
|
|
5525
5526
|
if (input.slug !== void 0) enrollments = enrollments.filter((e) => e.slug === input.slug);
|
|
5526
5527
|
if (input.status !== void 0) enrollments = enrollments.filter((e) => e.status === input.status);
|
|
@@ -5529,7 +5530,7 @@ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$23) {
|
|
|
5529
5530
|
text: JSON.stringify({ enrollments }, null, 2)
|
|
5530
5531
|
}] };
|
|
5531
5532
|
}
|
|
5532
|
-
function registerListSequenceEnrollments(server, dataDir = DATA_DIR$
|
|
5533
|
+
function registerListSequenceEnrollments(server, dataDir = DATA_DIR$24) {
|
|
5533
5534
|
server.registerTool("list_sequence_enrollments", {
|
|
5534
5535
|
description: `List email sequence enrollments. Filter by customer slug or status.
|
|
5535
5536
|
Returns: { enrollments: SequenceEnrollment[] }`,
|
|
@@ -5548,8 +5549,8 @@ Returns: { enrollments: SequenceEnrollment[] }`,
|
|
|
5548
5549
|
}
|
|
5549
5550
|
//#endregion
|
|
5550
5551
|
//#region src/mcp/tools/unenroll-from-sequence.ts
|
|
5551
|
-
const DATA_DIR$
|
|
5552
|
-
async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$
|
|
5552
|
+
const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5553
|
+
async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$23) {
|
|
5553
5554
|
if (!await updateEnrollment(dataDir, input.enrollmentId, { status: "paused" })) return { content: [{
|
|
5554
5555
|
type: "text",
|
|
5555
5556
|
text: JSON.stringify({
|
|
@@ -5562,7 +5563,7 @@ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$22) {
|
|
|
5562
5563
|
text: JSON.stringify({ success: true })
|
|
5563
5564
|
}] };
|
|
5564
5565
|
}
|
|
5565
|
-
function registerUnenrollFromSequence(server, dataDir = DATA_DIR$
|
|
5566
|
+
function registerUnenrollFromSequence(server, dataDir = DATA_DIR$23) {
|
|
5566
5567
|
server.registerTool("unenroll_from_sequence", {
|
|
5567
5568
|
description: `Unenroll (pause) a contact from an email sequence. Sets status to "paused" (soft delete).
|
|
5568
5569
|
Returns: { success: boolean }`,
|
|
@@ -5571,8 +5572,8 @@ Returns: { success: boolean }`,
|
|
|
5571
5572
|
}
|
|
5572
5573
|
//#endregion
|
|
5573
5574
|
//#region src/mcp/tools/list-sequences.ts
|
|
5574
|
-
const DATA_DIR$
|
|
5575
|
-
async function handleListSequences(_input, dataDir = DATA_DIR$
|
|
5575
|
+
const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5576
|
+
async function handleListSequences(_input, dataDir = DATA_DIR$22) {
|
|
5576
5577
|
const sequences = listSequences(dataDir);
|
|
5577
5578
|
const enrollments = readEnrollments(dataDir);
|
|
5578
5579
|
const result = sequences.map((seq) => ({
|
|
@@ -5586,7 +5587,7 @@ async function handleListSequences(_input, dataDir = DATA_DIR$21) {
|
|
|
5586
5587
|
text: JSON.stringify({ sequences: result }, null, 2)
|
|
5587
5588
|
}] };
|
|
5588
5589
|
}
|
|
5589
|
-
function registerListSequences(server, dataDir = DATA_DIR$
|
|
5590
|
+
function registerListSequences(server, dataDir = DATA_DIR$22) {
|
|
5590
5591
|
server.registerTool("list_sequences", {
|
|
5591
5592
|
description: `List all email sequences with step count and enrollment count.
|
|
5592
5593
|
Returns: { sequences: Array<{ id, name, stepCount, enrollmentCount }> }`,
|
|
@@ -5721,8 +5722,8 @@ async function generateQuote(dataDir, input) {
|
|
|
5721
5722
|
}
|
|
5722
5723
|
//#endregion
|
|
5723
5724
|
//#region src/mcp/tools/generate-quote.ts
|
|
5724
|
-
const DATA_DIR$
|
|
5725
|
-
async function handleGenerateQuote(input, dataDir = DATA_DIR$
|
|
5725
|
+
const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5726
|
+
async function handleGenerateQuote(input, dataDir = DATA_DIR$21) {
|
|
5726
5727
|
try {
|
|
5727
5728
|
const quote = await generateQuote(dataDir, input);
|
|
5728
5729
|
return { content: [{
|
|
@@ -5746,7 +5747,7 @@ async function handleGenerateQuote(input, dataDir = DATA_DIR$20) {
|
|
|
5746
5747
|
}] };
|
|
5747
5748
|
}
|
|
5748
5749
|
}
|
|
5749
|
-
function registerGenerateQuote(server, dataDir = DATA_DIR$
|
|
5750
|
+
function registerGenerateQuote(server, dataDir = DATA_DIR$21) {
|
|
5750
5751
|
server.registerTool("generate_quote", {
|
|
5751
5752
|
description: `Generate a professional HTML quote/offer for a customer deal.
|
|
5752
5753
|
Calculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.
|
|
@@ -5774,8 +5775,8 @@ Returns: { quoteNumber, htmlPath, total, currency, validUntil }`,
|
|
|
5774
5775
|
}
|
|
5775
5776
|
//#endregion
|
|
5776
5777
|
//#region src/mcp/tools/get-quote-status.ts
|
|
5777
|
-
const DATA_DIR$
|
|
5778
|
-
async function handleGetQuoteStatus(input, dataDir = DATA_DIR$
|
|
5778
|
+
const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5779
|
+
async function handleGetQuoteStatus(input, dataDir = DATA_DIR$20) {
|
|
5779
5780
|
if (input.quoteNumber) {
|
|
5780
5781
|
const quote = readQuote(dataDir, input.quoteNumber);
|
|
5781
5782
|
if (!quote) return { content: [{
|
|
@@ -5793,7 +5794,7 @@ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$19) {
|
|
|
5793
5794
|
text: JSON.stringify({ quotes }, null, 2)
|
|
5794
5795
|
}] };
|
|
5795
5796
|
}
|
|
5796
|
-
function registerGetQuoteStatus(server, dataDir = DATA_DIR$
|
|
5797
|
+
function registerGetQuoteStatus(server, dataDir = DATA_DIR$20) {
|
|
5797
5798
|
server.registerTool("get_quote_status", {
|
|
5798
5799
|
description: `Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for a customer).
|
|
5799
5800
|
Returns quote with status: draft | sent | viewed | accepted | declined`,
|
|
@@ -5808,7 +5809,7 @@ Returns quote with status: draft | sent | viewed | accepted | declined`,
|
|
|
5808
5809
|
}
|
|
5809
5810
|
//#endregion
|
|
5810
5811
|
//#region src/mcp/tools/get-booking-link.ts
|
|
5811
|
-
const DATA_DIR$
|
|
5812
|
+
const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5812
5813
|
function loadCalendlyConfig(dataDir) {
|
|
5813
5814
|
const p = path.join(dataDir, ".agentic", "integrations", "calendly.yaml");
|
|
5814
5815
|
if (!fs.existsSync(p)) return {};
|
|
@@ -5831,7 +5832,7 @@ function readCustomerFacts(dataDir, slug) {
|
|
|
5831
5832
|
...email ? { email } : {}
|
|
5832
5833
|
};
|
|
5833
5834
|
}
|
|
5834
|
-
async function handleGetBookingLink(input, dataDir = DATA_DIR$
|
|
5835
|
+
async function handleGetBookingLink(input, dataDir = DATA_DIR$19) {
|
|
5835
5836
|
const config = loadCalendlyConfig(dataDir);
|
|
5836
5837
|
const apiKey = config.apiKey ?? process.env["CALENDLY_API_KEY"] ?? "";
|
|
5837
5838
|
if (!apiKey) return { content: [{
|
|
@@ -5859,7 +5860,7 @@ async function handleGetBookingLink(input, dataDir = DATA_DIR$18) {
|
|
|
5859
5860
|
}] };
|
|
5860
5861
|
}
|
|
5861
5862
|
}
|
|
5862
|
-
function registerGetBookingLink(server, dataDir = DATA_DIR$
|
|
5863
|
+
function registerGetBookingLink(server, dataDir = DATA_DIR$19) {
|
|
5863
5864
|
server.registerTool("get_booking_link", {
|
|
5864
5865
|
description: `Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.
|
|
5865
5866
|
Requires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.
|
|
@@ -6029,8 +6030,8 @@ function calcSlaDue(createdDate, priority, rules) {
|
|
|
6029
6030
|
}
|
|
6030
6031
|
//#endregion
|
|
6031
6032
|
//#region src/mcp/tools/create-ticket.ts
|
|
6032
|
-
const DATA_DIR$
|
|
6033
|
-
async function handleCreateTicket(input, dataDir = DATA_DIR$
|
|
6033
|
+
const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6034
|
+
async function handleCreateTicket(input, dataDir = DATA_DIR$18) {
|
|
6034
6035
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6035
6036
|
const rules = loadSlaRules(dataDir);
|
|
6036
6037
|
const priority = input.priority ?? "normal";
|
|
@@ -6052,7 +6053,7 @@ async function handleCreateTicket(input, dataDir = DATA_DIR$17) {
|
|
|
6052
6053
|
text: JSON.stringify({ ticket }, null, 2)
|
|
6053
6054
|
}] };
|
|
6054
6055
|
}
|
|
6055
|
-
function registerCreateTicket(server, dataDir = DATA_DIR$
|
|
6056
|
+
function registerCreateTicket(server, dataDir = DATA_DIR$18) {
|
|
6056
6057
|
server.registerTool("create_ticket", {
|
|
6057
6058
|
description: `Create a support ticket for a customer. Auto-calculates SLA due date based on priority.
|
|
6058
6059
|
Returns: { ticket } with id T-NNN, status=open, slaDue`,
|
|
@@ -6078,8 +6079,8 @@ Returns: { ticket } with id T-NNN, status=open, slaDue`,
|
|
|
6078
6079
|
}
|
|
6079
6080
|
//#endregion
|
|
6080
6081
|
//#region src/mcp/tools/update-ticket.ts
|
|
6081
|
-
const DATA_DIR$
|
|
6082
|
-
async function handleUpdateTicket(input, dataDir = DATA_DIR$
|
|
6082
|
+
const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6083
|
+
async function handleUpdateTicket(input, dataDir = DATA_DIR$17) {
|
|
6083
6084
|
const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
|
|
6084
6085
|
if (!ticket) return { content: [{
|
|
6085
6086
|
type: "text",
|
|
@@ -6098,7 +6099,7 @@ async function handleUpdateTicket(input, dataDir = DATA_DIR$16) {
|
|
|
6098
6099
|
text: JSON.stringify({ ticket: updated }, null, 2)
|
|
6099
6100
|
}] };
|
|
6100
6101
|
}
|
|
6101
|
-
function registerUpdateTicket(server, dataDir = DATA_DIR$
|
|
6102
|
+
function registerUpdateTicket(server, dataDir = DATA_DIR$17) {
|
|
6102
6103
|
server.registerTool("update_ticket", {
|
|
6103
6104
|
description: `Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.
|
|
6104
6105
|
Returns: { ticket }`,
|
|
@@ -6123,8 +6124,8 @@ Returns: { ticket }`,
|
|
|
6123
6124
|
}
|
|
6124
6125
|
//#endregion
|
|
6125
6126
|
//#region src/mcp/tools/list-tickets.ts
|
|
6126
|
-
const DATA_DIR$
|
|
6127
|
-
async function handleListTickets(input, dataDir = DATA_DIR$
|
|
6127
|
+
const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6128
|
+
async function handleListTickets(input, dataDir = DATA_DIR$16) {
|
|
6128
6129
|
const results = await listAllTickets(dataDir, {
|
|
6129
6130
|
...input.slug !== void 0 ? { slug: input.slug } : {},
|
|
6130
6131
|
...input.status !== void 0 ? { status: input.status } : {},
|
|
@@ -6136,7 +6137,7 @@ async function handleListTickets(input, dataDir = DATA_DIR$15) {
|
|
|
6136
6137
|
text: JSON.stringify({ tickets: results }, null, 2)
|
|
6137
6138
|
}] };
|
|
6138
6139
|
}
|
|
6139
|
-
function registerListTickets(server, dataDir = DATA_DIR$
|
|
6140
|
+
function registerListTickets(server, dataDir = DATA_DIR$16) {
|
|
6140
6141
|
server.registerTool("list_tickets", {
|
|
6141
6142
|
description: `List support tickets. Filter by customer, status, priority, or assignee. Sorted by priority then date.
|
|
6142
6143
|
Returns: { tickets: Array<{ slug, ticket }> }`,
|
|
@@ -6166,8 +6167,8 @@ Returns: { tickets: Array<{ slug, ticket }> }`,
|
|
|
6166
6167
|
}
|
|
6167
6168
|
//#endregion
|
|
6168
6169
|
//#region src/mcp/tools/close-ticket.ts
|
|
6169
|
-
const DATA_DIR$
|
|
6170
|
-
async function handleCloseTicket(input, dataDir = DATA_DIR$
|
|
6170
|
+
const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6171
|
+
async function handleCloseTicket(input, dataDir = DATA_DIR$15) {
|
|
6171
6172
|
const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
|
|
6172
6173
|
if (!ticket) return { content: [{
|
|
6173
6174
|
type: "text",
|
|
@@ -6194,7 +6195,7 @@ async function handleCloseTicket(input, dataDir = DATA_DIR$14) {
|
|
|
6194
6195
|
text: JSON.stringify({ ticket: updated }, null, 2)
|
|
6195
6196
|
}] };
|
|
6196
6197
|
}
|
|
6197
|
-
function registerCloseTicket(server, dataDir = DATA_DIR$
|
|
6198
|
+
function registerCloseTicket(server, dataDir = DATA_DIR$15) {
|
|
6198
6199
|
server.registerTool("close_ticket", {
|
|
6199
6200
|
description: `Close a support ticket. Optionally logs the resolution as an interaction.
|
|
6200
6201
|
Returns: { ticket } with status=closed`,
|
|
@@ -6348,8 +6349,8 @@ async function savePendingSurvey(dataDir, surveyId, slug, contactEmail, token) {
|
|
|
6348
6349
|
}
|
|
6349
6350
|
//#endregion
|
|
6350
6351
|
//#region src/mcp/tools/send-nps-survey.ts
|
|
6351
|
-
const DATA_DIR$
|
|
6352
|
-
async function handleSendNpsSurvey(input, dataDir = DATA_DIR$
|
|
6352
|
+
const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6353
|
+
async function handleSendNpsSurvey(input, dataDir = DATA_DIR$14) {
|
|
6353
6354
|
const survey = getSurvey(dataDir, input.surveyId);
|
|
6354
6355
|
if (!survey) return { content: [{
|
|
6355
6356
|
type: "text",
|
|
@@ -6370,7 +6371,7 @@ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$13) {
|
|
|
6370
6371
|
}, null, 2)
|
|
6371
6372
|
}] };
|
|
6372
6373
|
}
|
|
6373
|
-
function registerSendNpsSurvey(server, dataDir = DATA_DIR$
|
|
6374
|
+
function registerSendNpsSurvey(server, dataDir = DATA_DIR$14) {
|
|
6374
6375
|
server.registerTool("send_nps_survey", {
|
|
6375
6376
|
description: `Generate an NPS/CSAT survey email for a customer contact. Returns subject, HTML body, and a token-based response URL.
|
|
6376
6377
|
Does NOT send automatically — returns draft for review.
|
|
@@ -6390,8 +6391,8 @@ Returns: { token, subject, body, surveyUrl }`,
|
|
|
6390
6391
|
}
|
|
6391
6392
|
//#endregion
|
|
6392
6393
|
//#region src/mcp/tools/get-survey-results.ts
|
|
6393
|
-
const DATA_DIR$
|
|
6394
|
-
async function handleGetSurveyResults(input, dataDir = DATA_DIR$
|
|
6394
|
+
const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6395
|
+
async function handleGetSurveyResults(input, dataDir = DATA_DIR$13) {
|
|
6395
6396
|
const responses = loadSurveyResponses(dataDir, input.surveyId, input.slug);
|
|
6396
6397
|
const nps = calcNpsScore(responses);
|
|
6397
6398
|
const promoters = responses.filter((r) => r.score >= 9).length;
|
|
@@ -6417,7 +6418,7 @@ async function handleGetSurveyResults(input, dataDir = DATA_DIR$12) {
|
|
|
6417
6418
|
}, null, 2)
|
|
6418
6419
|
}] };
|
|
6419
6420
|
}
|
|
6420
|
-
function registerGetSurveyResults(server, dataDir = DATA_DIR$
|
|
6421
|
+
function registerGetSurveyResults(server, dataDir = DATA_DIR$13) {
|
|
6421
6422
|
server.registerTool("get_survey_results", {
|
|
6422
6423
|
description: `Get NPS/CSAT survey results with score breakdown. Calculates Net Promoter Score.
|
|
6423
6424
|
Returns: { npsScore, totalResponses, promoters, passives, detractors, responses[] }`,
|
|
@@ -6518,8 +6519,8 @@ function getKbMetaForExport(article) {
|
|
|
6518
6519
|
}
|
|
6519
6520
|
//#endregion
|
|
6520
6521
|
//#region src/mcp/tools/search-knowledge-base.ts
|
|
6521
|
-
const DATA_DIR$
|
|
6522
|
-
async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$
|
|
6522
|
+
const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6523
|
+
async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$12) {
|
|
6523
6524
|
const results = searchKbSimple(dataDir, input.query, { ...input.publicOnly ? { publicOnly: true } : {} });
|
|
6524
6525
|
const limited = (input.category ? results.filter((a) => a.category === input.category) : results).slice(0, input.limit ?? 10);
|
|
6525
6526
|
return { content: [{
|
|
@@ -6534,7 +6535,7 @@ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$11) {
|
|
|
6534
6535
|
}, null, 2)
|
|
6535
6536
|
}] };
|
|
6536
6537
|
}
|
|
6537
|
-
function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$
|
|
6538
|
+
function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$12) {
|
|
6538
6539
|
server.registerTool("search_knowledge_base", {
|
|
6539
6540
|
description: `Search the knowledge base for articles. Text search on title, body, and tags.
|
|
6540
6541
|
Returns: { count, articles[] } with excerpts`,
|
|
@@ -6553,8 +6554,8 @@ Returns: { count, articles[] } with excerpts`,
|
|
|
6553
6554
|
}
|
|
6554
6555
|
//#endregion
|
|
6555
6556
|
//#region src/mcp/tools/create-kb-article.ts
|
|
6556
|
-
const DATA_DIR$
|
|
6557
|
-
async function handleCreateKbArticle(input, dataDir = DATA_DIR$
|
|
6557
|
+
const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6558
|
+
async function handleCreateKbArticle(input, dataDir = DATA_DIR$11) {
|
|
6558
6559
|
if (getKbArticle(dataDir, input.id)) return { content: [{
|
|
6559
6560
|
type: "text",
|
|
6560
6561
|
text: JSON.stringify({ error: `Article '${input.id}' already exists` })
|
|
@@ -6582,7 +6583,7 @@ async function handleCreateKbArticle(input, dataDir = DATA_DIR$10) {
|
|
|
6582
6583
|
}, null, 2)
|
|
6583
6584
|
}] };
|
|
6584
6585
|
}
|
|
6585
|
-
function registerCreateKbArticle(server, dataDir = DATA_DIR$
|
|
6586
|
+
function registerCreateKbArticle(server, dataDir = DATA_DIR$11) {
|
|
6586
6587
|
server.registerTool("create_kb_article", {
|
|
6587
6588
|
description: `Create a new knowledge base article. Articles are stored as Markdown files in .agentic/knowledge-base/.
|
|
6588
6589
|
Returns: { id, title, category, path }`,
|
|
@@ -6607,8 +6608,8 @@ Returns: { id, title, category, path }`,
|
|
|
6607
6608
|
}
|
|
6608
6609
|
//#endregion
|
|
6609
6610
|
//#region src/mcp/tools/backup-now.ts
|
|
6610
|
-
const DATA_DIR$
|
|
6611
|
-
async function handleBackupNow(input, dataDir = DATA_DIR$
|
|
6611
|
+
const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6612
|
+
async function handleBackupNow(input, dataDir = DATA_DIR$10) {
|
|
6612
6613
|
const zipPath = path.join(dataDir, `dxcrm-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.zip`);
|
|
6613
6614
|
const manifest = await runBackup(zipPath, dataDir, { ...input.remote ? { remote: input.remote } : {} }).catch(() => null);
|
|
6614
6615
|
if (!manifest) return { content: [{
|
|
@@ -6645,8 +6646,8 @@ function registerBackupNow(server) {
|
|
|
6645
6646
|
}
|
|
6646
6647
|
//#endregion
|
|
6647
6648
|
//#region src/mcp/tools/list-backups.ts
|
|
6648
|
-
const DATA_DIR$
|
|
6649
|
-
async function handleListBackups(input, dataDir = DATA_DIR$
|
|
6649
|
+
const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6650
|
+
async function handleListBackups(input, dataDir = DATA_DIR$9) {
|
|
6650
6651
|
const logEntries = readBackupLog(dataDir);
|
|
6651
6652
|
const fileEntries = listBackupsInDir(dataDir);
|
|
6652
6653
|
const entries = logEntries.length > 0 ? logEntries : fileEntries;
|
|
@@ -6680,8 +6681,8 @@ function registerListBackups(server) {
|
|
|
6680
6681
|
}
|
|
6681
6682
|
//#endregion
|
|
6682
6683
|
//#region src/mcp/tools/trigger-sync.ts
|
|
6683
|
-
const DATA_DIR$
|
|
6684
|
-
async function handleTriggerSync(input, dataDir = DATA_DIR$
|
|
6684
|
+
const DATA_DIR$8 = process.cwd();
|
|
6685
|
+
async function handleTriggerSync(input, dataDir = DATA_DIR$8) {
|
|
6685
6686
|
const auth = getGmailAuth();
|
|
6686
6687
|
if (!auth) return { content: [{
|
|
6687
6688
|
type: "text",
|
|
@@ -6775,8 +6776,8 @@ Returns: { success: boolean, synced: number, skipped: number, customers: [...],
|
|
|
6775
6776
|
}
|
|
6776
6777
|
//#endregion
|
|
6777
6778
|
//#region src/mcp/tools/get-audit-log.ts
|
|
6778
|
-
const DATA_DIR$
|
|
6779
|
-
async function handleGetAuditLog(input, dataDir = DATA_DIR$
|
|
6779
|
+
const DATA_DIR$7 = process.cwd();
|
|
6780
|
+
async function handleGetAuditLog(input, dataDir = DATA_DIR$7) {
|
|
6780
6781
|
const entries = readAuditLog(dataDir);
|
|
6781
6782
|
const filterOpts = { limit: input.limit ?? 50 };
|
|
6782
6783
|
if (input.slug !== void 0) filterOpts.slug = input.slug;
|
|
@@ -6818,8 +6819,8 @@ Returns: { total: number, returned: number, entries: [{timestamp, actor, tool, s
|
|
|
6818
6819
|
}
|
|
6819
6820
|
//#endregion
|
|
6820
6821
|
//#region src/mcp/tools/get-logs.ts
|
|
6821
|
-
const DATA_DIR$
|
|
6822
|
-
async function handleGetLogs(input, dataDir = DATA_DIR$
|
|
6822
|
+
const DATA_DIR$6 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6823
|
+
async function handleGetLogs(input, dataDir = DATA_DIR$6) {
|
|
6823
6824
|
const query = {
|
|
6824
6825
|
...input.level !== void 0 ? { level: input.level } : {},
|
|
6825
6826
|
...input.component !== void 0 ? { component: input.component } : {},
|
|
@@ -6981,8 +6982,8 @@ async function runDiagnostics(dataDir) {
|
|
|
6981
6982
|
}
|
|
6982
6983
|
//#endregion
|
|
6983
6984
|
//#region src/mcp/tools/get-diagnostics.ts
|
|
6984
|
-
const DATA_DIR$
|
|
6985
|
-
async function handleGetDiagnostics(input, dataDir = DATA_DIR$
|
|
6985
|
+
const DATA_DIR$5 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6986
|
+
async function handleGetDiagnostics(input, dataDir = DATA_DIR$5) {
|
|
6986
6987
|
let cleaned = 0;
|
|
6987
6988
|
if (input.fix) {
|
|
6988
6989
|
const { cleanupTempFiles } = await Promise.resolve().then(() => doctor_exports);
|
|
@@ -7144,11 +7145,11 @@ function diffAgainstNow(dataDir, since, today = (/* @__PURE__ */ new Date()).toI
|
|
|
7144
7145
|
}
|
|
7145
7146
|
//#endregion
|
|
7146
7147
|
//#region src/mcp/tools/get-pipeline-changes.ts
|
|
7147
|
-
const DATA_DIR$
|
|
7148
|
+
const DATA_DIR$4 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
7148
7149
|
function daysAgoIso(days) {
|
|
7149
7150
|
return (/* @__PURE__ */ new Date(Date.now() - days * 864e5)).toISOString().slice(0, 10);
|
|
7150
7151
|
}
|
|
7151
|
-
async function handleGetPipelineChanges(input, dataDir = DATA_DIR$
|
|
7152
|
+
async function handleGetPipelineChanges(input, dataDir = DATA_DIR$4) {
|
|
7152
7153
|
const since = input.since ?? daysAgoIso(input.days ?? 7);
|
|
7153
7154
|
const diff = diffAgainstNow(dataDir, since);
|
|
7154
7155
|
const payload = diff ? diff : { error: `No pipeline snapshot at or before ${since}. Snapshots accrue daily via the daemon.` };
|
|
@@ -7303,8 +7304,8 @@ function analyzeVelocity(dataDir, opts) {
|
|
|
7303
7304
|
}
|
|
7304
7305
|
//#endregion
|
|
7305
7306
|
//#region src/mcp/tools/get-pipeline-velocity.ts
|
|
7306
|
-
const DATA_DIR$
|
|
7307
|
-
async function handleGetPipelineVelocity(input, dataDir = DATA_DIR$
|
|
7307
|
+
const DATA_DIR$3 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
7308
|
+
async function handleGetPipelineVelocity(input, dataDir = DATA_DIR$3) {
|
|
7308
7309
|
const opts = {};
|
|
7309
7310
|
if (input.stalledDays !== void 0) opts.stalledDays = input.stalledDays;
|
|
7310
7311
|
const report = analyzeVelocity(dataDir, opts);
|
|
@@ -7335,6 +7336,116 @@ stalledThresholdDays }. snapshotCount is 0 until the daemon has taken snapshots.
|
|
|
7335
7336
|
});
|
|
7336
7337
|
}
|
|
7337
7338
|
//#endregion
|
|
7339
|
+
//#region src/core/funnel.ts
|
|
7340
|
+
const FUNNEL_STAGES = [
|
|
7341
|
+
"lead",
|
|
7342
|
+
"qualified",
|
|
7343
|
+
"proposal",
|
|
7344
|
+
"negotiation",
|
|
7345
|
+
"won"
|
|
7346
|
+
];
|
|
7347
|
+
const STAGE_INDEX = Object.fromEntries(FUNNEL_STAGES.map((s, i) => [s, i]));
|
|
7348
|
+
function emptyReport(snapshotCount) {
|
|
7349
|
+
return {
|
|
7350
|
+
fromId: null,
|
|
7351
|
+
toId: null,
|
|
7352
|
+
snapshotCount,
|
|
7353
|
+
stages: [],
|
|
7354
|
+
wonCount: 0,
|
|
7355
|
+
lostCount: 0,
|
|
7356
|
+
winRatePct: null,
|
|
7357
|
+
biggestLeak: null
|
|
7358
|
+
};
|
|
7359
|
+
}
|
|
7360
|
+
function analyzeFunnel(dataDir) {
|
|
7361
|
+
const metas = listSnapshots(dataDir);
|
|
7362
|
+
if (metas.length === 0) return emptyReport(0);
|
|
7363
|
+
const snaps = metas.flatMap((m) => {
|
|
7364
|
+
const s = loadSnapshot(dataDir, m.id);
|
|
7365
|
+
return s ? [s] : [];
|
|
7366
|
+
});
|
|
7367
|
+
const progress = /* @__PURE__ */ new Map();
|
|
7368
|
+
for (const snap of snaps) for (const deal of snap.deals) {
|
|
7369
|
+
const key = `${deal.slug}::${deal.name}`;
|
|
7370
|
+
const p = progress.get(key) ?? {
|
|
7371
|
+
maxIndex: -1,
|
|
7372
|
+
won: false,
|
|
7373
|
+
lost: false
|
|
7374
|
+
};
|
|
7375
|
+
if (deal.stage === "lost") p.lost = true;
|
|
7376
|
+
else {
|
|
7377
|
+
const idx = STAGE_INDEX[deal.stage];
|
|
7378
|
+
if (idx !== void 0 && idx > p.maxIndex) p.maxIndex = idx;
|
|
7379
|
+
if (deal.stage === "won") p.won = true;
|
|
7380
|
+
}
|
|
7381
|
+
progress.set(key, p);
|
|
7382
|
+
}
|
|
7383
|
+
const reached = new Array(FUNNEL_STAGES.length).fill(0);
|
|
7384
|
+
let wonCount = 0;
|
|
7385
|
+
let lostCount = 0;
|
|
7386
|
+
for (const p of progress.values()) {
|
|
7387
|
+
for (let i = 0; i <= p.maxIndex; i++) reached[i] = (reached[i] ?? 0) + 1;
|
|
7388
|
+
if (p.won) wonCount += 1;
|
|
7389
|
+
else if (p.lost) lostCount += 1;
|
|
7390
|
+
}
|
|
7391
|
+
const stages = FUNNEL_STAGES.map((stage, i) => {
|
|
7392
|
+
const here = reached[i] ?? 0;
|
|
7393
|
+
const next = reached[i + 1];
|
|
7394
|
+
return {
|
|
7395
|
+
stage,
|
|
7396
|
+
reached: here,
|
|
7397
|
+
conversionPctToNext: next === void 0 || here === 0 ? null : Math.round(next / here * 100)
|
|
7398
|
+
};
|
|
7399
|
+
});
|
|
7400
|
+
let biggestLeak = null;
|
|
7401
|
+
for (let i = 0; i < FUNNEL_STAGES.length - 1; i++) {
|
|
7402
|
+
const conv = stages[i].conversionPctToNext;
|
|
7403
|
+
if (conv === null) continue;
|
|
7404
|
+
if (biggestLeak === null || conv < biggestLeak.conversionPct) biggestLeak = {
|
|
7405
|
+
from: FUNNEL_STAGES[i],
|
|
7406
|
+
to: FUNNEL_STAGES[i + 1],
|
|
7407
|
+
conversionPct: conv
|
|
7408
|
+
};
|
|
7409
|
+
}
|
|
7410
|
+
const closed = wonCount + lostCount;
|
|
7411
|
+
const winRatePct = closed > 0 ? Math.round(wonCount / closed * 100) : null;
|
|
7412
|
+
return {
|
|
7413
|
+
fromId: snaps[0].id,
|
|
7414
|
+
toId: snaps[snaps.length - 1].id,
|
|
7415
|
+
snapshotCount: snaps.length,
|
|
7416
|
+
stages,
|
|
7417
|
+
wonCount,
|
|
7418
|
+
lostCount,
|
|
7419
|
+
winRatePct,
|
|
7420
|
+
biggestLeak
|
|
7421
|
+
};
|
|
7422
|
+
}
|
|
7423
|
+
//#endregion
|
|
7424
|
+
//#region src/mcp/tools/get-pipeline-funnel.ts
|
|
7425
|
+
const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
7426
|
+
async function handleGetPipelineFunnel(_input, dataDir = DATA_DIR$2) {
|
|
7427
|
+
const report = analyzeFunnel(dataDir);
|
|
7428
|
+
return { content: [{
|
|
7429
|
+
type: "text",
|
|
7430
|
+
text: JSON.stringify(report, null, 2)
|
|
7431
|
+
}] };
|
|
7432
|
+
}
|
|
7433
|
+
function registerGetPipelineFunnel(server) {
|
|
7434
|
+
server.registerTool("get_pipeline_funnel", {
|
|
7435
|
+
title: "Get Pipeline Funnel",
|
|
7436
|
+
description: `Pipeline conversion funnel & win-rate from the daily snapshot history.
|
|
7437
|
+
For each deal it tracks the furthest stage reached and the win/lost outcome,
|
|
7438
|
+
then builds a cumulative funnel. Answers "where do deals leak out of my
|
|
7439
|
+
pipeline?" and "what's my win rate?".
|
|
7440
|
+
|
|
7441
|
+
Returns: { fromId, toId, snapshotCount,
|
|
7442
|
+
stages[{stage, reached, conversionPctToNext}], wonCount, lostCount, winRatePct,
|
|
7443
|
+
biggestLeak{from,to,conversionPct} }. snapshotCount is 0 until the daemon has
|
|
7444
|
+
taken snapshots.`,
|
|
7445
|
+
inputSchema: z.object({})
|
|
7446
|
+
}, async () => handleGetPipelineFunnel({}));
|
|
7447
|
+
}
|
|
7448
|
+
//#endregion
|
|
7338
7449
|
//#region src/mcp/prompts.ts
|
|
7339
7450
|
/**
|
|
7340
7451
|
* CRM playbook prompts exposed via MCP `prompts/list` + `prompts/get`.
|
|
@@ -7754,6 +7865,7 @@ function createMcpServer() {
|
|
|
7754
7865
|
registerGetDiagnostics(server);
|
|
7755
7866
|
registerGetPipelineChanges(server);
|
|
7756
7867
|
registerGetPipelineVelocity(server);
|
|
7868
|
+
registerGetPipelineFunnel(server);
|
|
7757
7869
|
registerCustomObjectTools(server);
|
|
7758
7870
|
registerPrompts(server);
|
|
7759
7871
|
registerResources(server);
|