@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.cjs
CHANGED
|
@@ -453,6 +453,7 @@ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
|
|
|
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
455
|
| get_pipeline_velocity | Stage dwell times, sales cycle, and stalled deals from snapshot history | any |
|
|
456
|
+
| get_pipeline_funnel | Conversion funnel & win rate: where deals leak out of the pipeline | any |
|
|
456
457
|
| define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |
|
|
457
458
|
| create_record | Create a record of a custom object (validated against its schema) | rep+ |
|
|
458
459
|
| list_records | List records of a custom object | any |
|
|
@@ -1431,7 +1432,7 @@ async function buildContext(dataDir, slug) {
|
|
|
1431
1432
|
}
|
|
1432
1433
|
//#endregion
|
|
1433
1434
|
//#region src/mcp/tools/get-customer-context.ts
|
|
1434
|
-
const DATA_DIR$
|
|
1435
|
+
const DATA_DIR$56 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
1435
1436
|
function triggerOnQuerySync(dataDir, slug) {
|
|
1436
1437
|
const auth = getGmailAuth();
|
|
1437
1438
|
if (!auth) return;
|
|
@@ -1452,7 +1453,7 @@ function triggerOnQuerySync(dataDir, slug) {
|
|
|
1452
1453
|
}).then(() => updateSlugSyncState(dataDir, slug, { lastGmailSync: (/* @__PURE__ */ new Date()).toISOString() })).catch(() => {})).catch(() => {});
|
|
1453
1454
|
} catch {}
|
|
1454
1455
|
}
|
|
1455
|
-
async function handleGetCustomerContext(input, dataDir = DATA_DIR$
|
|
1456
|
+
async function handleGetCustomerContext(input, dataDir = DATA_DIR$56) {
|
|
1456
1457
|
const targetSlug = input.slug ?? require_session_store.getSession()?.customerSlug;
|
|
1457
1458
|
if (!targetSlug) return {
|
|
1458
1459
|
content: [{
|
|
@@ -1588,8 +1589,8 @@ async function searchKnowledge(dataDir, slug, query, limit) {
|
|
|
1588
1589
|
}
|
|
1589
1590
|
//#endregion
|
|
1590
1591
|
//#region src/mcp/tools/search-customer-knowledge.ts
|
|
1591
|
-
const DATA_DIR$
|
|
1592
|
-
async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$
|
|
1592
|
+
const DATA_DIR$55 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
1593
|
+
async function handleSearchCustomerKnowledge(input, dataDir = DATA_DIR$55) {
|
|
1593
1594
|
const limit = input.limit ?? 5;
|
|
1594
1595
|
try {
|
|
1595
1596
|
const results = await searchKnowledge(dataDir, input.slug, input.query, limit);
|
|
@@ -1637,14 +1638,14 @@ If no results: returns empty array with a helpful sync suggestion.`,
|
|
|
1637
1638
|
}
|
|
1638
1639
|
//#endregion
|
|
1639
1640
|
//#region src/mcp/tools/list-customers.ts
|
|
1640
|
-
const DATA_DIR$
|
|
1641
|
+
const DATA_DIR$54 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
1641
1642
|
function extractLastInteractionDate(interactionsPath) {
|
|
1642
1643
|
if (!fs.default.existsSync(interactionsPath)) return void 0;
|
|
1643
1644
|
const content = fs.default.readFileSync(interactionsPath, "utf-8");
|
|
1644
1645
|
const match = /^## (\d{4}-\d{2}-\d{2})/m.exec(content);
|
|
1645
1646
|
return match ? match[1] : void 0;
|
|
1646
1647
|
}
|
|
1647
|
-
async function handleListCustomers(input, dataDir = DATA_DIR$
|
|
1648
|
+
async function handleListCustomers(input, dataDir = DATA_DIR$54) {
|
|
1648
1649
|
const customersDir = path.default.join(dataDir, "customers");
|
|
1649
1650
|
const customers = [];
|
|
1650
1651
|
if (!fs.default.existsSync(customersDir)) return { content: [{
|
|
@@ -2157,8 +2158,8 @@ async function updateHealthFromInteraction(dataDir, slug) {
|
|
|
2157
2158
|
}
|
|
2158
2159
|
//#endregion
|
|
2159
2160
|
//#region src/mcp/tools/log-interaction.ts
|
|
2160
|
-
const DATA_DIR$
|
|
2161
|
-
async function handleLogInteraction(input, dataDir = DATA_DIR$
|
|
2161
|
+
const DATA_DIR$53 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2162
|
+
async function handleLogInteraction(input, dataDir = DATA_DIR$53) {
|
|
2162
2163
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2163
2164
|
const interactionDate = input.date ?? today;
|
|
2164
2165
|
const sourceRef = input.source ?? `agent://log/${Date.now()}`;
|
|
@@ -2267,8 +2268,8 @@ var update_deal_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
|
2267
2268
|
handleUpdateDeal: () => handleUpdateDeal,
|
|
2268
2269
|
registerUpdateDeal: () => registerUpdateDeal
|
|
2269
2270
|
});
|
|
2270
|
-
const DATA_DIR$
|
|
2271
|
-
async function handleUpdateDeal(input, dataDir = DATA_DIR$
|
|
2271
|
+
const DATA_DIR$52 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2272
|
+
async function handleUpdateDeal(input, dataDir = DATA_DIR$52) {
|
|
2272
2273
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2273
2274
|
const deal = {
|
|
2274
2275
|
name: input.dealName,
|
|
@@ -2351,12 +2352,12 @@ Returns: { success: boolean, deal: object }`,
|
|
|
2351
2352
|
}
|
|
2352
2353
|
//#endregion
|
|
2353
2354
|
//#region src/mcp/tools/export-customer.ts
|
|
2354
|
-
const DATA_DIR$
|
|
2355
|
+
const DATA_DIR$51 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2355
2356
|
function countInteractions(content) {
|
|
2356
2357
|
const matches = content.match(/^## \d{4}-\d{2}-\d{2}/gm);
|
|
2357
2358
|
return matches ? matches.length : 0;
|
|
2358
2359
|
}
|
|
2359
|
-
async function handleExportCustomer(input, dataDir = DATA_DIR$
|
|
2360
|
+
async function handleExportCustomer(input, dataDir = DATA_DIR$51) {
|
|
2360
2361
|
require_session_store.enforceRbac(dataDir, "export_customer");
|
|
2361
2362
|
const customerDir = path.default.join(dataDir, "customers", input.slug);
|
|
2362
2363
|
if (!fs.default.existsSync(customerDir)) return {
|
|
@@ -2469,8 +2470,8 @@ Returns:
|
|
|
2469
2470
|
}
|
|
2470
2471
|
//#endregion
|
|
2471
2472
|
//#region src/mcp/tools/update-customer-facts.ts
|
|
2472
|
-
const DATA_DIR$
|
|
2473
|
-
async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$
|
|
2473
|
+
const DATA_DIR$50 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2474
|
+
async function handleUpdateCustomerFacts(input, dataDir = DATA_DIR$50) {
|
|
2474
2475
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2475
2476
|
try {
|
|
2476
2477
|
require_session_store.enforceRbac(dataDir, "update_customer_facts");
|
|
@@ -2648,8 +2649,8 @@ function scoreDealForToday(deal, todayDate) {
|
|
|
2648
2649
|
}
|
|
2649
2650
|
//#endregion
|
|
2650
2651
|
//#region src/mcp/tools/get-deal-health.ts
|
|
2651
|
-
const DATA_DIR$
|
|
2652
|
-
async function handleGetDealHealth(input, dataDir = DATA_DIR$
|
|
2652
|
+
const DATA_DIR$49 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2653
|
+
async function handleGetDealHealth(input, dataDir = DATA_DIR$49) {
|
|
2653
2654
|
try {
|
|
2654
2655
|
const deals = await require_pipeline_writer.readPipeline(dataDir, input.slug);
|
|
2655
2656
|
const today = /* @__PURE__ */ new Date();
|
|
@@ -2698,8 +2699,8 @@ Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }`,
|
|
|
2698
2699
|
}
|
|
2699
2700
|
//#endregion
|
|
2700
2701
|
//#region src/mcp/tools/get-pipeline-forecast.ts
|
|
2701
|
-
const DATA_DIR$
|
|
2702
|
-
async function handleGetPipelineForecast(input, dataDir = DATA_DIR$
|
|
2702
|
+
const DATA_DIR$48 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2703
|
+
async function handleGetPipelineForecast(input, dataDir = DATA_DIR$48) {
|
|
2703
2704
|
try {
|
|
2704
2705
|
const slugs = require_session_store.listCustomerSlugs(dataDir).filter((d) => !input.filter || d.includes(input.filter));
|
|
2705
2706
|
const allDeals = [];
|
|
@@ -2760,8 +2761,8 @@ Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count,
|
|
|
2760
2761
|
}
|
|
2761
2762
|
//#endregion
|
|
2762
2763
|
//#region src/mcp/tools/summarize-meeting.ts
|
|
2763
|
-
const DATA_DIR$
|
|
2764
|
-
async function handleSummarizeMeeting(input, dataDir = DATA_DIR$
|
|
2764
|
+
const DATA_DIR$47 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2765
|
+
async function handleSummarizeMeeting(input, dataDir = DATA_DIR$47) {
|
|
2765
2766
|
try {
|
|
2766
2767
|
let summary = input.transcript.slice(0, 400);
|
|
2767
2768
|
let nextSteps = [];
|
|
@@ -2884,8 +2885,8 @@ function getPipelineStages(dataDir) {
|
|
|
2884
2885
|
}
|
|
2885
2886
|
//#endregion
|
|
2886
2887
|
//#region src/mcp/tools/get-pipeline-stages.ts
|
|
2887
|
-
const DATA_DIR$
|
|
2888
|
-
async function handleGetPipelineStages(_input, dataDir = DATA_DIR$
|
|
2888
|
+
const DATA_DIR$46 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2889
|
+
async function handleGetPipelineStages(_input, dataDir = DATA_DIR$46) {
|
|
2889
2890
|
const stages = getPipelineStages(dataDir);
|
|
2890
2891
|
return { content: [{
|
|
2891
2892
|
type: "text",
|
|
@@ -2913,8 +2914,8 @@ async function searchAcrossCustomers(dataDir, query, limit = 5, excludeSlug) {
|
|
|
2913
2914
|
}
|
|
2914
2915
|
//#endregion
|
|
2915
2916
|
//#region src/mcp/tools/get-market-intelligence.ts
|
|
2916
|
-
const DATA_DIR$
|
|
2917
|
-
async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$
|
|
2917
|
+
const DATA_DIR$45 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2918
|
+
async function handleGetMarketIntelligence(input, dataDir = DATA_DIR$45) {
|
|
2918
2919
|
const excludeSlug = input.excludeCurrentCustomer ? input.slug : void 0;
|
|
2919
2920
|
const all = require_session_store.listCustomerSlugs(dataDir);
|
|
2920
2921
|
const totalCustomersSearched = excludeSlug ? all.filter((s) => s !== excludeSlug).length : all.length;
|
|
@@ -2945,7 +2946,7 @@ function registerGetMarketIntelligence(server) {
|
|
|
2945
2946
|
}
|
|
2946
2947
|
//#endregion
|
|
2947
2948
|
//#region src/mcp/tools/get-relationship-graph.ts
|
|
2948
|
-
const DATA_DIR$
|
|
2949
|
+
const DATA_DIR$44 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
2949
2950
|
function summarizeNode(n) {
|
|
2950
2951
|
return {
|
|
2951
2952
|
id: n.id,
|
|
@@ -2953,7 +2954,7 @@ function summarizeNode(n) {
|
|
|
2953
2954
|
email: n.properties["email"]
|
|
2954
2955
|
};
|
|
2955
2956
|
}
|
|
2956
|
-
async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$
|
|
2957
|
+
async function handleGetRelationshipGraph(input, dataDir = DATA_DIR$44) {
|
|
2957
2958
|
try {
|
|
2958
2959
|
const graph = readGraph(dataDir, input.slug);
|
|
2959
2960
|
const stakeholders = getStakeholders(graph);
|
|
@@ -3021,9 +3022,9 @@ Returns: {
|
|
|
3021
3022
|
}
|
|
3022
3023
|
//#endregion
|
|
3023
3024
|
//#region src/mcp/tools/get-relationship-health.ts
|
|
3024
|
-
const DATA_DIR$
|
|
3025
|
+
const DATA_DIR$43 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
3025
3026
|
const MAX_HEALTH_AGE_MS = 3600 * 1e3;
|
|
3026
|
-
async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$
|
|
3027
|
+
async function handleGetRelationshipHealth(input, dataDir = DATA_DIR$43) {
|
|
3027
3028
|
try {
|
|
3028
3029
|
let health = readHealth(dataDir, input.slug);
|
|
3029
3030
|
if (health === null || Date.now() - new Date(health.updatedAt).getTime() > MAX_HEALTH_AGE_MS) {
|
|
@@ -3692,8 +3693,8 @@ async function runDealAgent(config, dataDir, llmFn = require_llm.callLlm) {
|
|
|
3692
3693
|
}
|
|
3693
3694
|
//#endregion
|
|
3694
3695
|
//#region src/mcp/tools/run-deal-agent.ts
|
|
3695
|
-
const DATA_DIR$
|
|
3696
|
-
async function handleRunDealAgent(input, dataDir = DATA_DIR$
|
|
3696
|
+
const DATA_DIR$42 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
3697
|
+
async function handleRunDealAgent(input, dataDir = DATA_DIR$42) {
|
|
3697
3698
|
try {
|
|
3698
3699
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3699
3700
|
const result = await runDealAgent({
|
|
@@ -3760,8 +3761,8 @@ Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], tr
|
|
|
3760
3761
|
}
|
|
3761
3762
|
//#endregion
|
|
3762
3763
|
//#region src/mcp/tools/approve-agent-action.ts
|
|
3763
|
-
const DATA_DIR$
|
|
3764
|
-
async function handleApproveAgentAction(input, dataDir = DATA_DIR$
|
|
3764
|
+
const DATA_DIR$41 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
3765
|
+
async function handleApproveAgentAction(input, dataDir = DATA_DIR$41) {
|
|
3765
3766
|
try {
|
|
3766
3767
|
const queue = readAgentQueue(dataDir, input.slug);
|
|
3767
3768
|
const idx = queue.pendingActions.findIndex((a) => a.actionId === input.actionId);
|
|
@@ -4021,8 +4022,8 @@ async function buildSimulationInput(dataDir, horizon, today, externalSignals = [
|
|
|
4021
4022
|
}
|
|
4022
4023
|
//#endregion
|
|
4023
4024
|
//#region src/mcp/tools/simulate-revenue.ts
|
|
4024
|
-
const DATA_DIR$
|
|
4025
|
-
async function handleSimulateRevenue(input, dataDir = DATA_DIR$
|
|
4025
|
+
const DATA_DIR$40 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4026
|
+
async function handleSimulateRevenue(input, dataDir = DATA_DIR$40) {
|
|
4026
4027
|
try {
|
|
4027
4028
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4028
4029
|
const horizon = input.horizon ?? "quarter";
|
|
@@ -4080,8 +4081,8 @@ Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMo
|
|
|
4080
4081
|
}
|
|
4081
4082
|
//#endregion
|
|
4082
4083
|
//#region src/mcp/tools/get-playbook.ts
|
|
4083
|
-
const DATA_DIR$
|
|
4084
|
-
async function handleGetPlaybook(input, dataDir = DATA_DIR$
|
|
4084
|
+
const DATA_DIR$39 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4085
|
+
async function handleGetPlaybook(input, dataDir = DATA_DIR$39) {
|
|
4085
4086
|
try {
|
|
4086
4087
|
const playbooks = listPlaybooks(dataDir, input.slug);
|
|
4087
4088
|
if (!(input.stage !== void 0 || input.value !== void 0 || input.healthScore !== void 0)) return { content: [{
|
|
@@ -4166,12 +4167,12 @@ Returns: { matches: [{ name, score, trigger, successRate, usedCount, content }],
|
|
|
4166
4167
|
...healthScore !== void 0 ? { healthScore } : {},
|
|
4167
4168
|
...daysSinceContact !== void 0 ? { daysSinceContact } : {},
|
|
4168
4169
|
...championPresent !== void 0 ? { championPresent } : {}
|
|
4169
|
-
}, DATA_DIR$
|
|
4170
|
+
}, DATA_DIR$39));
|
|
4170
4171
|
}
|
|
4171
4172
|
//#endregion
|
|
4172
4173
|
//#region src/mcp/tools/create-playbook.ts
|
|
4173
|
-
const DATA_DIR$
|
|
4174
|
-
async function handleCreatePlaybook(input, dataDir = DATA_DIR$
|
|
4174
|
+
const DATA_DIR$38 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4175
|
+
async function handleCreatePlaybook(input, dataDir = DATA_DIR$38) {
|
|
4175
4176
|
try {
|
|
4176
4177
|
const name = toKebabCase(input.name);
|
|
4177
4178
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -4244,12 +4245,12 @@ Returns: { success: true, playbook: { name, trigger, successRate, path } }`,
|
|
|
4244
4245
|
trigger,
|
|
4245
4246
|
content,
|
|
4246
4247
|
...successRate !== void 0 ? { successRate } : {}
|
|
4247
|
-
}, DATA_DIR$
|
|
4248
|
+
}, DATA_DIR$38));
|
|
4248
4249
|
}
|
|
4249
4250
|
//#endregion
|
|
4250
4251
|
//#region src/mcp/tools/list-playbooks.ts
|
|
4251
|
-
const DATA_DIR$
|
|
4252
|
-
async function handleListPlaybooks(input, dataDir = DATA_DIR$
|
|
4252
|
+
const DATA_DIR$37 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4253
|
+
async function handleListPlaybooks(input, dataDir = DATA_DIR$37) {
|
|
4253
4254
|
try {
|
|
4254
4255
|
const playbooks = listPlaybooks(dataDir, input.slug);
|
|
4255
4256
|
return { content: [{
|
|
@@ -4288,12 +4289,12 @@ Args:
|
|
|
4288
4289
|
|
|
4289
4290
|
Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }`,
|
|
4290
4291
|
inputSchema: zod.z.object({ slug: zod.z.string().describe("Customer ID") })
|
|
4291
|
-
}, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$
|
|
4292
|
+
}, async ({ slug }) => handleListPlaybooks({ slug }, DATA_DIR$37));
|
|
4292
4293
|
}
|
|
4293
4294
|
//#endregion
|
|
4294
4295
|
//#region src/mcp/tools/distill-playbook.ts
|
|
4295
|
-
const DATA_DIR$
|
|
4296
|
-
async function handleDistillPlaybook(input, dataDir = DATA_DIR$
|
|
4296
|
+
const DATA_DIR$36 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4297
|
+
async function handleDistillPlaybook(input, dataDir = DATA_DIR$36, llmFn = require_llm.callLlm) {
|
|
4297
4298
|
try {
|
|
4298
4299
|
const result = await distillPlaybook(dataDir, input.slug, input.dealName, input.outcome, llmFn);
|
|
4299
4300
|
if (!result.ok) {
|
|
@@ -4352,7 +4353,7 @@ Returns: { success: true, playbook: { name, trigger, successRate, path }, reason
|
|
|
4352
4353
|
slug,
|
|
4353
4354
|
dealName,
|
|
4354
4355
|
outcome
|
|
4355
|
-
}, DATA_DIR$
|
|
4356
|
+
}, DATA_DIR$36));
|
|
4356
4357
|
}
|
|
4357
4358
|
//#endregion
|
|
4358
4359
|
//#region src/core/goal-engine.ts
|
|
@@ -4570,8 +4571,8 @@ function getActiveGoals(dataDir) {
|
|
|
4570
4571
|
}
|
|
4571
4572
|
//#endregion
|
|
4572
4573
|
//#region src/mcp/tools/pursue-goal.ts
|
|
4573
|
-
const DATA_DIR$
|
|
4574
|
-
async function handlePursueGoal(input, dataDir = DATA_DIR$
|
|
4574
|
+
const DATA_DIR$35 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4575
|
+
async function handlePursueGoal(input, dataDir = DATA_DIR$35, options = {}) {
|
|
4575
4576
|
try {
|
|
4576
4577
|
require_session_store.enforceRbac(dataDir, "pursue_goal");
|
|
4577
4578
|
const goal = await pursueGoal(dataDir, {
|
|
@@ -4634,12 +4635,12 @@ Returns: { goalId, description, target, deadline, decomposition: { analysis, cur
|
|
|
4634
4635
|
goal,
|
|
4635
4636
|
deadline,
|
|
4636
4637
|
...context !== void 0 ? { context } : {}
|
|
4637
|
-
}, DATA_DIR$
|
|
4638
|
+
}, DATA_DIR$35));
|
|
4638
4639
|
}
|
|
4639
4640
|
//#endregion
|
|
4640
4641
|
//#region src/mcp/tools/get-goal-status.ts
|
|
4641
|
-
const DATA_DIR$
|
|
4642
|
-
async function handleGetGoalStatus(input, dataDir = DATA_DIR$
|
|
4642
|
+
const DATA_DIR$34 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4643
|
+
async function handleGetGoalStatus(input, dataDir = DATA_DIR$34) {
|
|
4643
4644
|
try {
|
|
4644
4645
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4645
4646
|
const allGoals = input.goalId ? readGoals(dataDir).filter((g) => g.id === input.goalId) : getActiveGoals(dataDir);
|
|
@@ -4698,17 +4699,17 @@ Args:
|
|
|
4698
4699
|
|
|
4699
4700
|
Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }`,
|
|
4700
4701
|
inputSchema: zod.z.object({ goalId: zod.z.string().optional().describe("Specific goal ID (omit for all active goals)") })
|
|
4701
|
-
}, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$
|
|
4702
|
+
}, async ({ goalId }) => handleGetGoalStatus({ ...goalId !== void 0 ? { goalId } : {} }, DATA_DIR$34));
|
|
4702
4703
|
}
|
|
4703
4704
|
//#endregion
|
|
4704
4705
|
//#region src/mcp/tools/register-push-subscription.ts
|
|
4705
|
-
const DATA_DIR$
|
|
4706
|
+
const DATA_DIR$33 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4706
4707
|
const VALID_PROVIDERS = [
|
|
4707
4708
|
"gmail",
|
|
4708
4709
|
"microsoft-graph",
|
|
4709
4710
|
"slack"
|
|
4710
4711
|
];
|
|
4711
|
-
async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$
|
|
4712
|
+
async function handleRegisterPushSubscription(input, dataDir = DATA_DIR$33) {
|
|
4712
4713
|
try {
|
|
4713
4714
|
if (!VALID_PROVIDERS.includes(input.provider)) return { content: [{
|
|
4714
4715
|
type: "text",
|
|
@@ -4794,12 +4795,12 @@ Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning
|
|
|
4794
4795
|
...microsoftResource !== void 0 ? { microsoftResource } : {},
|
|
4795
4796
|
...slackTeamId !== void 0 ? { slackTeamId } : {},
|
|
4796
4797
|
...slackChannelId !== void 0 ? { slackChannelId } : {}
|
|
4797
|
-
}, DATA_DIR$
|
|
4798
|
+
}, DATA_DIR$33));
|
|
4798
4799
|
}
|
|
4799
4800
|
//#endregion
|
|
4800
4801
|
//#region src/mcp/tools/get-push-status.ts
|
|
4801
|
-
const DATA_DIR$
|
|
4802
|
-
async function handleGetPushStatus(input, dataDir = DATA_DIR$
|
|
4802
|
+
const DATA_DIR$32 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4803
|
+
async function handleGetPushStatus(input, dataDir = DATA_DIR$32) {
|
|
4803
4804
|
try {
|
|
4804
4805
|
let subs = await readSubscriptions(dataDir);
|
|
4805
4806
|
if (input.slug) subs = subs.filter((s) => s.slug === input.slug);
|
|
@@ -4871,7 +4872,7 @@ Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHou
|
|
|
4871
4872
|
}, async ({ slug, provider }) => handleGetPushStatus({
|
|
4872
4873
|
...slug !== void 0 ? { slug } : {},
|
|
4873
4874
|
...provider !== void 0 ? { provider } : {}
|
|
4874
|
-
}, DATA_DIR$
|
|
4875
|
+
}, DATA_DIR$32));
|
|
4875
4876
|
}
|
|
4876
4877
|
//#endregion
|
|
4877
4878
|
//#region src/core/org-intelligence.ts
|
|
@@ -4937,8 +4938,8 @@ function deriveRecommendation(people, missingRoles) {
|
|
|
4937
4938
|
}
|
|
4938
4939
|
//#endregion
|
|
4939
4940
|
//#region src/mcp/tools/get-org-intelligence.ts
|
|
4940
|
-
const DATA_DIR$
|
|
4941
|
-
async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$
|
|
4941
|
+
const DATA_DIR$31 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
4942
|
+
async function handleGetOrgIntelligence(input, dataDir = DATA_DIR$31) {
|
|
4942
4943
|
try {
|
|
4943
4944
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4944
4945
|
const map = buildStakeholderMap(dataDir, input.slug, today, input.dealName);
|
|
@@ -5071,8 +5072,8 @@ function buildExecutiveSummary(slug, dealName, stakeholders, overallHealth, sim,
|
|
|
5071
5072
|
}
|
|
5072
5073
|
//#endregion
|
|
5073
5074
|
//#region src/mcp/tools/open-deal-room.ts
|
|
5074
|
-
const DATA_DIR$
|
|
5075
|
-
async function handleOpenDealRoom(input, dataDir = DATA_DIR$
|
|
5075
|
+
const DATA_DIR$30 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5076
|
+
async function handleOpenDealRoom(input, dataDir = DATA_DIR$30) {
|
|
5076
5077
|
try {
|
|
5077
5078
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5078
5079
|
const brief = await buildDealRoom(dataDir, input.slug, input.dealName, today);
|
|
@@ -5155,8 +5156,8 @@ async function buildDailyBriefing(dataDir, today) {
|
|
|
5155
5156
|
}
|
|
5156
5157
|
//#endregion
|
|
5157
5158
|
//#region src/mcp/tools/get-proactive-briefing.ts
|
|
5158
|
-
const DATA_DIR$
|
|
5159
|
-
async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$
|
|
5159
|
+
const DATA_DIR$29 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5160
|
+
async function handleGetProactiveBriefing(input, dataDir = DATA_DIR$29) {
|
|
5160
5161
|
try {
|
|
5161
5162
|
const briefing = await buildDailyBriefing(dataDir, input.date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
|
|
5162
5163
|
return { content: [{
|
|
@@ -5256,15 +5257,15 @@ function getTemplate(dataDir, id) {
|
|
|
5256
5257
|
}
|
|
5257
5258
|
//#endregion
|
|
5258
5259
|
//#region src/mcp/tools/list-email-templates.ts
|
|
5259
|
-
const DATA_DIR$
|
|
5260
|
-
async function handleListEmailTemplates(input, dataDir = DATA_DIR$
|
|
5260
|
+
const DATA_DIR$28 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5261
|
+
async function handleListEmailTemplates(input, dataDir = DATA_DIR$28) {
|
|
5261
5262
|
const summary = listTemplates(dataDir, input.category ? { category: input.category } : {}).map(({ body: _body, ...meta }) => meta);
|
|
5262
5263
|
return { content: [{
|
|
5263
5264
|
type: "text",
|
|
5264
5265
|
text: JSON.stringify(summary, null, 2)
|
|
5265
5266
|
}] };
|
|
5266
5267
|
}
|
|
5267
|
-
function registerListEmailTemplates(server, dataDir = DATA_DIR$
|
|
5268
|
+
function registerListEmailTemplates(server, dataDir = DATA_DIR$28) {
|
|
5268
5269
|
server.registerTool("list_email_templates", {
|
|
5269
5270
|
description: "List available email templates. Optionally filter by category (e.g. 'outreach', 'followup', 'support').",
|
|
5270
5271
|
inputSchema: zod.z.object({ category: zod.z.string().optional().describe("Filter by category") })
|
|
@@ -5298,8 +5299,8 @@ async function buildVariablesFromCustomer(dataDir, slug) {
|
|
|
5298
5299
|
}
|
|
5299
5300
|
//#endregion
|
|
5300
5301
|
//#region src/mcp/tools/get-email-template.ts
|
|
5301
|
-
const DATA_DIR$
|
|
5302
|
-
async function handleGetEmailTemplate(input, dataDir = DATA_DIR$
|
|
5302
|
+
const DATA_DIR$27 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5303
|
+
async function handleGetEmailTemplate(input, dataDir = DATA_DIR$27) {
|
|
5303
5304
|
const tmpl = getTemplate(dataDir, input.id);
|
|
5304
5305
|
if (!tmpl) return { content: [{
|
|
5305
5306
|
type: "text",
|
|
@@ -5315,7 +5316,7 @@ async function handleGetEmailTemplate(input, dataDir = DATA_DIR$26) {
|
|
|
5315
5316
|
}, null, 2)
|
|
5316
5317
|
}] };
|
|
5317
5318
|
}
|
|
5318
|
-
function registerGetEmailTemplate(server, dataDir = DATA_DIR$
|
|
5319
|
+
function registerGetEmailTemplate(server, dataDir = DATA_DIR$27) {
|
|
5319
5320
|
server.registerTool("get_email_template", {
|
|
5320
5321
|
description: "Get a specific email template by ID, including its body and detected variables.",
|
|
5321
5322
|
inputSchema: zod.z.object({ id: zod.z.string().describe("Template ID (e.g. 'enterprise-intro')") })
|
|
@@ -5323,8 +5324,8 @@ function registerGetEmailTemplate(server, dataDir = DATA_DIR$26) {
|
|
|
5323
5324
|
}
|
|
5324
5325
|
//#endregion
|
|
5325
5326
|
//#region src/mcp/tools/draft-email.ts
|
|
5326
|
-
const DATA_DIR$
|
|
5327
|
-
async function handleDraftEmail(input, dataDir = DATA_DIR$
|
|
5327
|
+
const DATA_DIR$26 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5328
|
+
async function handleDraftEmail(input, dataDir = DATA_DIR$26) {
|
|
5328
5329
|
const tmpl = getTemplate(dataDir, input.templateId);
|
|
5329
5330
|
if (!tmpl) return { content: [{
|
|
5330
5331
|
type: "text",
|
|
@@ -5368,7 +5369,7 @@ async function handleDraftEmail(input, dataDir = DATA_DIR$25) {
|
|
|
5368
5369
|
}, null, 2)
|
|
5369
5370
|
}] };
|
|
5370
5371
|
}
|
|
5371
|
-
function registerDraftEmail(server, dataDir = DATA_DIR$
|
|
5372
|
+
function registerDraftEmail(server, dataDir = DATA_DIR$26) {
|
|
5372
5373
|
server.registerTool("draft_email", {
|
|
5373
5374
|
description: `Draft a personalized email for a customer using a stored template.
|
|
5374
5375
|
Variables are auto-filled from the customer's main_facts.md. Override any variable manually.
|
|
@@ -5476,8 +5477,8 @@ async function updateEnrollment(dataDir, id, updates) {
|
|
|
5476
5477
|
}
|
|
5477
5478
|
//#endregion
|
|
5478
5479
|
//#region src/mcp/tools/enroll-in-sequence.ts
|
|
5479
|
-
const DATA_DIR$
|
|
5480
|
-
async function handleEnrollInSequence(input, dataDir = DATA_DIR$
|
|
5480
|
+
const DATA_DIR$25 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5481
|
+
async function handleEnrollInSequence(input, dataDir = DATA_DIR$25) {
|
|
5481
5482
|
const sequence = getSequence(dataDir, input.sequenceId);
|
|
5482
5483
|
if (!sequence) return { content: [{
|
|
5483
5484
|
type: "text",
|
|
@@ -5509,7 +5510,7 @@ async function handleEnrollInSequence(input, dataDir = DATA_DIR$24) {
|
|
|
5509
5510
|
})
|
|
5510
5511
|
}] };
|
|
5511
5512
|
}
|
|
5512
|
-
function registerEnrollInSequence(server, dataDir = DATA_DIR$
|
|
5513
|
+
function registerEnrollInSequence(server, dataDir = DATA_DIR$25) {
|
|
5513
5514
|
server.registerTool("enroll_in_sequence", {
|
|
5514
5515
|
description: `Enroll a contact in an email sequence. Validates that the sequence and its first template exist.
|
|
5515
5516
|
Returns: { enrollmentId, sequenceName, totalSteps }`,
|
|
@@ -5526,8 +5527,8 @@ Returns: { enrollmentId, sequenceName, totalSteps }`,
|
|
|
5526
5527
|
}
|
|
5527
5528
|
//#endregion
|
|
5528
5529
|
//#region src/mcp/tools/list-sequence-enrollments.ts
|
|
5529
|
-
const DATA_DIR$
|
|
5530
|
-
async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$
|
|
5530
|
+
const DATA_DIR$24 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5531
|
+
async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$24) {
|
|
5531
5532
|
let enrollments = readEnrollments(dataDir);
|
|
5532
5533
|
if (input.slug !== void 0) enrollments = enrollments.filter((e) => e.slug === input.slug);
|
|
5533
5534
|
if (input.status !== void 0) enrollments = enrollments.filter((e) => e.status === input.status);
|
|
@@ -5536,7 +5537,7 @@ async function handleListSequenceEnrollments(input, dataDir = DATA_DIR$23) {
|
|
|
5536
5537
|
text: JSON.stringify({ enrollments }, null, 2)
|
|
5537
5538
|
}] };
|
|
5538
5539
|
}
|
|
5539
|
-
function registerListSequenceEnrollments(server, dataDir = DATA_DIR$
|
|
5540
|
+
function registerListSequenceEnrollments(server, dataDir = DATA_DIR$24) {
|
|
5540
5541
|
server.registerTool("list_sequence_enrollments", {
|
|
5541
5542
|
description: `List email sequence enrollments. Filter by customer slug or status.
|
|
5542
5543
|
Returns: { enrollments: SequenceEnrollment[] }`,
|
|
@@ -5555,8 +5556,8 @@ Returns: { enrollments: SequenceEnrollment[] }`,
|
|
|
5555
5556
|
}
|
|
5556
5557
|
//#endregion
|
|
5557
5558
|
//#region src/mcp/tools/unenroll-from-sequence.ts
|
|
5558
|
-
const DATA_DIR$
|
|
5559
|
-
async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$
|
|
5559
|
+
const DATA_DIR$23 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5560
|
+
async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$23) {
|
|
5560
5561
|
if (!await updateEnrollment(dataDir, input.enrollmentId, { status: "paused" })) return { content: [{
|
|
5561
5562
|
type: "text",
|
|
5562
5563
|
text: JSON.stringify({
|
|
@@ -5569,7 +5570,7 @@ async function handleUnenrollFromSequence(input, dataDir = DATA_DIR$22) {
|
|
|
5569
5570
|
text: JSON.stringify({ success: true })
|
|
5570
5571
|
}] };
|
|
5571
5572
|
}
|
|
5572
|
-
function registerUnenrollFromSequence(server, dataDir = DATA_DIR$
|
|
5573
|
+
function registerUnenrollFromSequence(server, dataDir = DATA_DIR$23) {
|
|
5573
5574
|
server.registerTool("unenroll_from_sequence", {
|
|
5574
5575
|
description: `Unenroll (pause) a contact from an email sequence. Sets status to "paused" (soft delete).
|
|
5575
5576
|
Returns: { success: boolean }`,
|
|
@@ -5578,8 +5579,8 @@ Returns: { success: boolean }`,
|
|
|
5578
5579
|
}
|
|
5579
5580
|
//#endregion
|
|
5580
5581
|
//#region src/mcp/tools/list-sequences.ts
|
|
5581
|
-
const DATA_DIR$
|
|
5582
|
-
async function handleListSequences(_input, dataDir = DATA_DIR$
|
|
5582
|
+
const DATA_DIR$22 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5583
|
+
async function handleListSequences(_input, dataDir = DATA_DIR$22) {
|
|
5583
5584
|
const sequences = listSequences(dataDir);
|
|
5584
5585
|
const enrollments = readEnrollments(dataDir);
|
|
5585
5586
|
const result = sequences.map((seq) => ({
|
|
@@ -5593,7 +5594,7 @@ async function handleListSequences(_input, dataDir = DATA_DIR$21) {
|
|
|
5593
5594
|
text: JSON.stringify({ sequences: result }, null, 2)
|
|
5594
5595
|
}] };
|
|
5595
5596
|
}
|
|
5596
|
-
function registerListSequences(server, dataDir = DATA_DIR$
|
|
5597
|
+
function registerListSequences(server, dataDir = DATA_DIR$22) {
|
|
5597
5598
|
server.registerTool("list_sequences", {
|
|
5598
5599
|
description: `List all email sequences with step count and enrollment count.
|
|
5599
5600
|
Returns: { sequences: Array<{ id, name, stepCount, enrollmentCount }> }`,
|
|
@@ -5728,8 +5729,8 @@ async function generateQuote(dataDir, input) {
|
|
|
5728
5729
|
}
|
|
5729
5730
|
//#endregion
|
|
5730
5731
|
//#region src/mcp/tools/generate-quote.ts
|
|
5731
|
-
const DATA_DIR$
|
|
5732
|
-
async function handleGenerateQuote(input, dataDir = DATA_DIR$
|
|
5732
|
+
const DATA_DIR$21 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5733
|
+
async function handleGenerateQuote(input, dataDir = DATA_DIR$21) {
|
|
5733
5734
|
try {
|
|
5734
5735
|
const quote = await generateQuote(dataDir, input);
|
|
5735
5736
|
return { content: [{
|
|
@@ -5753,7 +5754,7 @@ async function handleGenerateQuote(input, dataDir = DATA_DIR$20) {
|
|
|
5753
5754
|
}] };
|
|
5754
5755
|
}
|
|
5755
5756
|
}
|
|
5756
|
-
function registerGenerateQuote(server, dataDir = DATA_DIR$
|
|
5757
|
+
function registerGenerateQuote(server, dataDir = DATA_DIR$21) {
|
|
5757
5758
|
server.registerTool("generate_quote", {
|
|
5758
5759
|
description: `Generate a professional HTML quote/offer for a customer deal.
|
|
5759
5760
|
Calculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.
|
|
@@ -5781,8 +5782,8 @@ Returns: { quoteNumber, htmlPath, total, currency, validUntil }`,
|
|
|
5781
5782
|
}
|
|
5782
5783
|
//#endregion
|
|
5783
5784
|
//#region src/mcp/tools/get-quote-status.ts
|
|
5784
|
-
const DATA_DIR$
|
|
5785
|
-
async function handleGetQuoteStatus(input, dataDir = DATA_DIR$
|
|
5785
|
+
const DATA_DIR$20 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5786
|
+
async function handleGetQuoteStatus(input, dataDir = DATA_DIR$20) {
|
|
5786
5787
|
if (input.quoteNumber) {
|
|
5787
5788
|
const quote = readQuote(dataDir, input.quoteNumber);
|
|
5788
5789
|
if (!quote) return { content: [{
|
|
@@ -5800,7 +5801,7 @@ async function handleGetQuoteStatus(input, dataDir = DATA_DIR$19) {
|
|
|
5800
5801
|
text: JSON.stringify({ quotes }, null, 2)
|
|
5801
5802
|
}] };
|
|
5802
5803
|
}
|
|
5803
|
-
function registerGetQuoteStatus(server, dataDir = DATA_DIR$
|
|
5804
|
+
function registerGetQuoteStatus(server, dataDir = DATA_DIR$20) {
|
|
5804
5805
|
server.registerTool("get_quote_status", {
|
|
5805
5806
|
description: `Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for a customer).
|
|
5806
5807
|
Returns quote with status: draft | sent | viewed | accepted | declined`,
|
|
@@ -5815,7 +5816,7 @@ Returns quote with status: draft | sent | viewed | accepted | declined`,
|
|
|
5815
5816
|
}
|
|
5816
5817
|
//#endregion
|
|
5817
5818
|
//#region src/mcp/tools/get-booking-link.ts
|
|
5818
|
-
const DATA_DIR$
|
|
5819
|
+
const DATA_DIR$19 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
5819
5820
|
function loadCalendlyConfig(dataDir) {
|
|
5820
5821
|
const p = path.default.join(dataDir, ".agentic", "integrations", "calendly.yaml");
|
|
5821
5822
|
if (!fs.default.existsSync(p)) return {};
|
|
@@ -5838,7 +5839,7 @@ function readCustomerFacts(dataDir, slug) {
|
|
|
5838
5839
|
...email ? { email } : {}
|
|
5839
5840
|
};
|
|
5840
5841
|
}
|
|
5841
|
-
async function handleGetBookingLink(input, dataDir = DATA_DIR$
|
|
5842
|
+
async function handleGetBookingLink(input, dataDir = DATA_DIR$19) {
|
|
5842
5843
|
const config = loadCalendlyConfig(dataDir);
|
|
5843
5844
|
const apiKey = config.apiKey ?? process.env["CALENDLY_API_KEY"] ?? "";
|
|
5844
5845
|
if (!apiKey) return { content: [{
|
|
@@ -5866,7 +5867,7 @@ async function handleGetBookingLink(input, dataDir = DATA_DIR$18) {
|
|
|
5866
5867
|
}] };
|
|
5867
5868
|
}
|
|
5868
5869
|
}
|
|
5869
|
-
function registerGetBookingLink(server, dataDir = DATA_DIR$
|
|
5870
|
+
function registerGetBookingLink(server, dataDir = DATA_DIR$19) {
|
|
5870
5871
|
server.registerTool("get_booking_link", {
|
|
5871
5872
|
description: `Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.
|
|
5872
5873
|
Requires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.
|
|
@@ -6036,8 +6037,8 @@ function calcSlaDue(createdDate, priority, rules) {
|
|
|
6036
6037
|
}
|
|
6037
6038
|
//#endregion
|
|
6038
6039
|
//#region src/mcp/tools/create-ticket.ts
|
|
6039
|
-
const DATA_DIR$
|
|
6040
|
-
async function handleCreateTicket(input, dataDir = DATA_DIR$
|
|
6040
|
+
const DATA_DIR$18 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6041
|
+
async function handleCreateTicket(input, dataDir = DATA_DIR$18) {
|
|
6041
6042
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6042
6043
|
const rules = loadSlaRules(dataDir);
|
|
6043
6044
|
const priority = input.priority ?? "normal";
|
|
@@ -6059,7 +6060,7 @@ async function handleCreateTicket(input, dataDir = DATA_DIR$17) {
|
|
|
6059
6060
|
text: JSON.stringify({ ticket }, null, 2)
|
|
6060
6061
|
}] };
|
|
6061
6062
|
}
|
|
6062
|
-
function registerCreateTicket(server, dataDir = DATA_DIR$
|
|
6063
|
+
function registerCreateTicket(server, dataDir = DATA_DIR$18) {
|
|
6063
6064
|
server.registerTool("create_ticket", {
|
|
6064
6065
|
description: `Create a support ticket for a customer. Auto-calculates SLA due date based on priority.
|
|
6065
6066
|
Returns: { ticket } with id T-NNN, status=open, slaDue`,
|
|
@@ -6085,8 +6086,8 @@ Returns: { ticket } with id T-NNN, status=open, slaDue`,
|
|
|
6085
6086
|
}
|
|
6086
6087
|
//#endregion
|
|
6087
6088
|
//#region src/mcp/tools/update-ticket.ts
|
|
6088
|
-
const DATA_DIR$
|
|
6089
|
-
async function handleUpdateTicket(input, dataDir = DATA_DIR$
|
|
6089
|
+
const DATA_DIR$17 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6090
|
+
async function handleUpdateTicket(input, dataDir = DATA_DIR$17) {
|
|
6090
6091
|
const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
|
|
6091
6092
|
if (!ticket) return { content: [{
|
|
6092
6093
|
type: "text",
|
|
@@ -6105,7 +6106,7 @@ async function handleUpdateTicket(input, dataDir = DATA_DIR$16) {
|
|
|
6105
6106
|
text: JSON.stringify({ ticket: updated }, null, 2)
|
|
6106
6107
|
}] };
|
|
6107
6108
|
}
|
|
6108
|
-
function registerUpdateTicket(server, dataDir = DATA_DIR$
|
|
6109
|
+
function registerUpdateTicket(server, dataDir = DATA_DIR$17) {
|
|
6109
6110
|
server.registerTool("update_ticket", {
|
|
6110
6111
|
description: `Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.
|
|
6111
6112
|
Returns: { ticket }`,
|
|
@@ -6130,8 +6131,8 @@ Returns: { ticket }`,
|
|
|
6130
6131
|
}
|
|
6131
6132
|
//#endregion
|
|
6132
6133
|
//#region src/mcp/tools/list-tickets.ts
|
|
6133
|
-
const DATA_DIR$
|
|
6134
|
-
async function handleListTickets(input, dataDir = DATA_DIR$
|
|
6134
|
+
const DATA_DIR$16 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6135
|
+
async function handleListTickets(input, dataDir = DATA_DIR$16) {
|
|
6135
6136
|
const results = await listAllTickets(dataDir, {
|
|
6136
6137
|
...input.slug !== void 0 ? { slug: input.slug } : {},
|
|
6137
6138
|
...input.status !== void 0 ? { status: input.status } : {},
|
|
@@ -6143,7 +6144,7 @@ async function handleListTickets(input, dataDir = DATA_DIR$15) {
|
|
|
6143
6144
|
text: JSON.stringify({ tickets: results }, null, 2)
|
|
6144
6145
|
}] };
|
|
6145
6146
|
}
|
|
6146
|
-
function registerListTickets(server, dataDir = DATA_DIR$
|
|
6147
|
+
function registerListTickets(server, dataDir = DATA_DIR$16) {
|
|
6147
6148
|
server.registerTool("list_tickets", {
|
|
6148
6149
|
description: `List support tickets. Filter by customer, status, priority, or assignee. Sorted by priority then date.
|
|
6149
6150
|
Returns: { tickets: Array<{ slug, ticket }> }`,
|
|
@@ -6173,8 +6174,8 @@ Returns: { tickets: Array<{ slug, ticket }> }`,
|
|
|
6173
6174
|
}
|
|
6174
6175
|
//#endregion
|
|
6175
6176
|
//#region src/mcp/tools/close-ticket.ts
|
|
6176
|
-
const DATA_DIR$
|
|
6177
|
-
async function handleCloseTicket(input, dataDir = DATA_DIR$
|
|
6177
|
+
const DATA_DIR$15 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6178
|
+
async function handleCloseTicket(input, dataDir = DATA_DIR$15) {
|
|
6178
6179
|
const ticket = (await readTickets(dataDir, input.slug)).find((t) => t.id === input.ticketId);
|
|
6179
6180
|
if (!ticket) return { content: [{
|
|
6180
6181
|
type: "text",
|
|
@@ -6201,7 +6202,7 @@ async function handleCloseTicket(input, dataDir = DATA_DIR$14) {
|
|
|
6201
6202
|
text: JSON.stringify({ ticket: updated }, null, 2)
|
|
6202
6203
|
}] };
|
|
6203
6204
|
}
|
|
6204
|
-
function registerCloseTicket(server, dataDir = DATA_DIR$
|
|
6205
|
+
function registerCloseTicket(server, dataDir = DATA_DIR$15) {
|
|
6205
6206
|
server.registerTool("close_ticket", {
|
|
6206
6207
|
description: `Close a support ticket. Optionally logs the resolution as an interaction.
|
|
6207
6208
|
Returns: { ticket } with status=closed`,
|
|
@@ -6355,8 +6356,8 @@ async function savePendingSurvey(dataDir, surveyId, slug, contactEmail, token) {
|
|
|
6355
6356
|
}
|
|
6356
6357
|
//#endregion
|
|
6357
6358
|
//#region src/mcp/tools/send-nps-survey.ts
|
|
6358
|
-
const DATA_DIR$
|
|
6359
|
-
async function handleSendNpsSurvey(input, dataDir = DATA_DIR$
|
|
6359
|
+
const DATA_DIR$14 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6360
|
+
async function handleSendNpsSurvey(input, dataDir = DATA_DIR$14) {
|
|
6360
6361
|
const survey = getSurvey(dataDir, input.surveyId);
|
|
6361
6362
|
if (!survey) return { content: [{
|
|
6362
6363
|
type: "text",
|
|
@@ -6377,7 +6378,7 @@ async function handleSendNpsSurvey(input, dataDir = DATA_DIR$13) {
|
|
|
6377
6378
|
}, null, 2)
|
|
6378
6379
|
}] };
|
|
6379
6380
|
}
|
|
6380
|
-
function registerSendNpsSurvey(server, dataDir = DATA_DIR$
|
|
6381
|
+
function registerSendNpsSurvey(server, dataDir = DATA_DIR$14) {
|
|
6381
6382
|
server.registerTool("send_nps_survey", {
|
|
6382
6383
|
description: `Generate an NPS/CSAT survey email for a customer contact. Returns subject, HTML body, and a token-based response URL.
|
|
6383
6384
|
Does NOT send automatically — returns draft for review.
|
|
@@ -6397,8 +6398,8 @@ Returns: { token, subject, body, surveyUrl }`,
|
|
|
6397
6398
|
}
|
|
6398
6399
|
//#endregion
|
|
6399
6400
|
//#region src/mcp/tools/get-survey-results.ts
|
|
6400
|
-
const DATA_DIR$
|
|
6401
|
-
async function handleGetSurveyResults(input, dataDir = DATA_DIR$
|
|
6401
|
+
const DATA_DIR$13 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6402
|
+
async function handleGetSurveyResults(input, dataDir = DATA_DIR$13) {
|
|
6402
6403
|
const responses = loadSurveyResponses(dataDir, input.surveyId, input.slug);
|
|
6403
6404
|
const nps = calcNpsScore(responses);
|
|
6404
6405
|
const promoters = responses.filter((r) => r.score >= 9).length;
|
|
@@ -6424,7 +6425,7 @@ async function handleGetSurveyResults(input, dataDir = DATA_DIR$12) {
|
|
|
6424
6425
|
}, null, 2)
|
|
6425
6426
|
}] };
|
|
6426
6427
|
}
|
|
6427
|
-
function registerGetSurveyResults(server, dataDir = DATA_DIR$
|
|
6428
|
+
function registerGetSurveyResults(server, dataDir = DATA_DIR$13) {
|
|
6428
6429
|
server.registerTool("get_survey_results", {
|
|
6429
6430
|
description: `Get NPS/CSAT survey results with score breakdown. Calculates Net Promoter Score.
|
|
6430
6431
|
Returns: { npsScore, totalResponses, promoters, passives, detractors, responses[] }`,
|
|
@@ -6525,8 +6526,8 @@ function getKbMetaForExport(article) {
|
|
|
6525
6526
|
}
|
|
6526
6527
|
//#endregion
|
|
6527
6528
|
//#region src/mcp/tools/search-knowledge-base.ts
|
|
6528
|
-
const DATA_DIR$
|
|
6529
|
-
async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$
|
|
6529
|
+
const DATA_DIR$12 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6530
|
+
async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$12) {
|
|
6530
6531
|
const results = searchKbSimple(dataDir, input.query, { ...input.publicOnly ? { publicOnly: true } : {} });
|
|
6531
6532
|
const limited = (input.category ? results.filter((a) => a.category === input.category) : results).slice(0, input.limit ?? 10);
|
|
6532
6533
|
return { content: [{
|
|
@@ -6541,7 +6542,7 @@ async function handleSearchKnowledgeBase(input, dataDir = DATA_DIR$11) {
|
|
|
6541
6542
|
}, null, 2)
|
|
6542
6543
|
}] };
|
|
6543
6544
|
}
|
|
6544
|
-
function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$
|
|
6545
|
+
function registerSearchKnowledgeBase(server, dataDir = DATA_DIR$12) {
|
|
6545
6546
|
server.registerTool("search_knowledge_base", {
|
|
6546
6547
|
description: `Search the knowledge base for articles. Text search on title, body, and tags.
|
|
6547
6548
|
Returns: { count, articles[] } with excerpts`,
|
|
@@ -6560,8 +6561,8 @@ Returns: { count, articles[] } with excerpts`,
|
|
|
6560
6561
|
}
|
|
6561
6562
|
//#endregion
|
|
6562
6563
|
//#region src/mcp/tools/create-kb-article.ts
|
|
6563
|
-
const DATA_DIR$
|
|
6564
|
-
async function handleCreateKbArticle(input, dataDir = DATA_DIR$
|
|
6564
|
+
const DATA_DIR$11 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6565
|
+
async function handleCreateKbArticle(input, dataDir = DATA_DIR$11) {
|
|
6565
6566
|
if (getKbArticle(dataDir, input.id)) return { content: [{
|
|
6566
6567
|
type: "text",
|
|
6567
6568
|
text: JSON.stringify({ error: `Article '${input.id}' already exists` })
|
|
@@ -6589,7 +6590,7 @@ async function handleCreateKbArticle(input, dataDir = DATA_DIR$10) {
|
|
|
6589
6590
|
}, null, 2)
|
|
6590
6591
|
}] };
|
|
6591
6592
|
}
|
|
6592
|
-
function registerCreateKbArticle(server, dataDir = DATA_DIR$
|
|
6593
|
+
function registerCreateKbArticle(server, dataDir = DATA_DIR$11) {
|
|
6593
6594
|
server.registerTool("create_kb_article", {
|
|
6594
6595
|
description: `Create a new knowledge base article. Articles are stored as Markdown files in .agentic/knowledge-base/.
|
|
6595
6596
|
Returns: { id, title, category, path }`,
|
|
@@ -6614,8 +6615,8 @@ Returns: { id, title, category, path }`,
|
|
|
6614
6615
|
}
|
|
6615
6616
|
//#endregion
|
|
6616
6617
|
//#region src/mcp/tools/backup-now.ts
|
|
6617
|
-
const DATA_DIR$
|
|
6618
|
-
async function handleBackupNow(input, dataDir = DATA_DIR$
|
|
6618
|
+
const DATA_DIR$10 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6619
|
+
async function handleBackupNow(input, dataDir = DATA_DIR$10) {
|
|
6619
6620
|
const zipPath = path.default.join(dataDir, `dxcrm-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.zip`);
|
|
6620
6621
|
const manifest = await require_session_store.runBackup(zipPath, dataDir, { ...input.remote ? { remote: input.remote } : {} }).catch(() => null);
|
|
6621
6622
|
if (!manifest) return { content: [{
|
|
@@ -6652,8 +6653,8 @@ function registerBackupNow(server) {
|
|
|
6652
6653
|
}
|
|
6653
6654
|
//#endregion
|
|
6654
6655
|
//#region src/mcp/tools/list-backups.ts
|
|
6655
|
-
const DATA_DIR$
|
|
6656
|
-
async function handleListBackups(input, dataDir = DATA_DIR$
|
|
6656
|
+
const DATA_DIR$9 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6657
|
+
async function handleListBackups(input, dataDir = DATA_DIR$9) {
|
|
6657
6658
|
const logEntries = require_session_store.readBackupLog(dataDir);
|
|
6658
6659
|
const fileEntries = require_session_store.listBackupsInDir(dataDir);
|
|
6659
6660
|
const entries = logEntries.length > 0 ? logEntries : fileEntries;
|
|
@@ -6687,8 +6688,8 @@ function registerListBackups(server) {
|
|
|
6687
6688
|
}
|
|
6688
6689
|
//#endregion
|
|
6689
6690
|
//#region src/mcp/tools/trigger-sync.ts
|
|
6690
|
-
const DATA_DIR$
|
|
6691
|
-
async function handleTriggerSync(input, dataDir = DATA_DIR$
|
|
6691
|
+
const DATA_DIR$8 = process.cwd();
|
|
6692
|
+
async function handleTriggerSync(input, dataDir = DATA_DIR$8) {
|
|
6692
6693
|
const auth = getGmailAuth();
|
|
6693
6694
|
if (!auth) return { content: [{
|
|
6694
6695
|
type: "text",
|
|
@@ -6782,8 +6783,8 @@ Returns: { success: boolean, synced: number, skipped: number, customers: [...],
|
|
|
6782
6783
|
}
|
|
6783
6784
|
//#endregion
|
|
6784
6785
|
//#region src/mcp/tools/get-audit-log.ts
|
|
6785
|
-
const DATA_DIR$
|
|
6786
|
-
async function handleGetAuditLog(input, dataDir = DATA_DIR$
|
|
6786
|
+
const DATA_DIR$7 = process.cwd();
|
|
6787
|
+
async function handleGetAuditLog(input, dataDir = DATA_DIR$7) {
|
|
6787
6788
|
const entries = require_session_store.readAuditLog(dataDir);
|
|
6788
6789
|
const filterOpts = { limit: input.limit ?? 50 };
|
|
6789
6790
|
if (input.slug !== void 0) filterOpts.slug = input.slug;
|
|
@@ -6825,8 +6826,8 @@ Returns: { total: number, returned: number, entries: [{timestamp, actor, tool, s
|
|
|
6825
6826
|
}
|
|
6826
6827
|
//#endregion
|
|
6827
6828
|
//#region src/mcp/tools/get-logs.ts
|
|
6828
|
-
const DATA_DIR$
|
|
6829
|
-
async function handleGetLogs(input, dataDir = DATA_DIR$
|
|
6829
|
+
const DATA_DIR$6 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6830
|
+
async function handleGetLogs(input, dataDir = DATA_DIR$6) {
|
|
6830
6831
|
const query = {
|
|
6831
6832
|
...input.level !== void 0 ? { level: input.level } : {},
|
|
6832
6833
|
...input.component !== void 0 ? { component: input.component } : {},
|
|
@@ -6988,8 +6989,8 @@ async function runDiagnostics(dataDir) {
|
|
|
6988
6989
|
}
|
|
6989
6990
|
//#endregion
|
|
6990
6991
|
//#region src/mcp/tools/get-diagnostics.ts
|
|
6991
|
-
const DATA_DIR$
|
|
6992
|
-
async function handleGetDiagnostics(input, dataDir = DATA_DIR$
|
|
6992
|
+
const DATA_DIR$5 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
6993
|
+
async function handleGetDiagnostics(input, dataDir = DATA_DIR$5) {
|
|
6993
6994
|
let cleaned = 0;
|
|
6994
6995
|
if (input.fix) {
|
|
6995
6996
|
const { cleanupTempFiles } = await Promise.resolve().then(() => doctor_exports);
|
|
@@ -7151,11 +7152,11 @@ function diffAgainstNow(dataDir, since, today = (/* @__PURE__ */ new Date()).toI
|
|
|
7151
7152
|
}
|
|
7152
7153
|
//#endregion
|
|
7153
7154
|
//#region src/mcp/tools/get-pipeline-changes.ts
|
|
7154
|
-
const DATA_DIR$
|
|
7155
|
+
const DATA_DIR$4 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
7155
7156
|
function daysAgoIso(days) {
|
|
7156
7157
|
return (/* @__PURE__ */ new Date(Date.now() - days * 864e5)).toISOString().slice(0, 10);
|
|
7157
7158
|
}
|
|
7158
|
-
async function handleGetPipelineChanges(input, dataDir = DATA_DIR$
|
|
7159
|
+
async function handleGetPipelineChanges(input, dataDir = DATA_DIR$4) {
|
|
7159
7160
|
const since = input.since ?? daysAgoIso(input.days ?? 7);
|
|
7160
7161
|
const diff = diffAgainstNow(dataDir, since);
|
|
7161
7162
|
const payload = diff ? diff : { error: `No pipeline snapshot at or before ${since}. Snapshots accrue daily via the daemon.` };
|
|
@@ -7310,8 +7311,8 @@ function analyzeVelocity(dataDir, opts) {
|
|
|
7310
7311
|
}
|
|
7311
7312
|
//#endregion
|
|
7312
7313
|
//#region src/mcp/tools/get-pipeline-velocity.ts
|
|
7313
|
-
const DATA_DIR$
|
|
7314
|
-
async function handleGetPipelineVelocity(input, dataDir = DATA_DIR$
|
|
7314
|
+
const DATA_DIR$3 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
7315
|
+
async function handleGetPipelineVelocity(input, dataDir = DATA_DIR$3) {
|
|
7315
7316
|
const opts = {};
|
|
7316
7317
|
if (input.stalledDays !== void 0) opts.stalledDays = input.stalledDays;
|
|
7317
7318
|
const report = analyzeVelocity(dataDir, opts);
|
|
@@ -7342,6 +7343,116 @@ stalledThresholdDays }. snapshotCount is 0 until the daemon has taken snapshots.
|
|
|
7342
7343
|
});
|
|
7343
7344
|
}
|
|
7344
7345
|
//#endregion
|
|
7346
|
+
//#region src/core/funnel.ts
|
|
7347
|
+
const FUNNEL_STAGES = [
|
|
7348
|
+
"lead",
|
|
7349
|
+
"qualified",
|
|
7350
|
+
"proposal",
|
|
7351
|
+
"negotiation",
|
|
7352
|
+
"won"
|
|
7353
|
+
];
|
|
7354
|
+
const STAGE_INDEX = Object.fromEntries(FUNNEL_STAGES.map((s, i) => [s, i]));
|
|
7355
|
+
function emptyReport(snapshotCount) {
|
|
7356
|
+
return {
|
|
7357
|
+
fromId: null,
|
|
7358
|
+
toId: null,
|
|
7359
|
+
snapshotCount,
|
|
7360
|
+
stages: [],
|
|
7361
|
+
wonCount: 0,
|
|
7362
|
+
lostCount: 0,
|
|
7363
|
+
winRatePct: null,
|
|
7364
|
+
biggestLeak: null
|
|
7365
|
+
};
|
|
7366
|
+
}
|
|
7367
|
+
function analyzeFunnel(dataDir) {
|
|
7368
|
+
const metas = listSnapshots(dataDir);
|
|
7369
|
+
if (metas.length === 0) return emptyReport(0);
|
|
7370
|
+
const snaps = metas.flatMap((m) => {
|
|
7371
|
+
const s = loadSnapshot(dataDir, m.id);
|
|
7372
|
+
return s ? [s] : [];
|
|
7373
|
+
});
|
|
7374
|
+
const progress = /* @__PURE__ */ new Map();
|
|
7375
|
+
for (const snap of snaps) for (const deal of snap.deals) {
|
|
7376
|
+
const key = `${deal.slug}::${deal.name}`;
|
|
7377
|
+
const p = progress.get(key) ?? {
|
|
7378
|
+
maxIndex: -1,
|
|
7379
|
+
won: false,
|
|
7380
|
+
lost: false
|
|
7381
|
+
};
|
|
7382
|
+
if (deal.stage === "lost") p.lost = true;
|
|
7383
|
+
else {
|
|
7384
|
+
const idx = STAGE_INDEX[deal.stage];
|
|
7385
|
+
if (idx !== void 0 && idx > p.maxIndex) p.maxIndex = idx;
|
|
7386
|
+
if (deal.stage === "won") p.won = true;
|
|
7387
|
+
}
|
|
7388
|
+
progress.set(key, p);
|
|
7389
|
+
}
|
|
7390
|
+
const reached = new Array(FUNNEL_STAGES.length).fill(0);
|
|
7391
|
+
let wonCount = 0;
|
|
7392
|
+
let lostCount = 0;
|
|
7393
|
+
for (const p of progress.values()) {
|
|
7394
|
+
for (let i = 0; i <= p.maxIndex; i++) reached[i] = (reached[i] ?? 0) + 1;
|
|
7395
|
+
if (p.won) wonCount += 1;
|
|
7396
|
+
else if (p.lost) lostCount += 1;
|
|
7397
|
+
}
|
|
7398
|
+
const stages = FUNNEL_STAGES.map((stage, i) => {
|
|
7399
|
+
const here = reached[i] ?? 0;
|
|
7400
|
+
const next = reached[i + 1];
|
|
7401
|
+
return {
|
|
7402
|
+
stage,
|
|
7403
|
+
reached: here,
|
|
7404
|
+
conversionPctToNext: next === void 0 || here === 0 ? null : Math.round(next / here * 100)
|
|
7405
|
+
};
|
|
7406
|
+
});
|
|
7407
|
+
let biggestLeak = null;
|
|
7408
|
+
for (let i = 0; i < FUNNEL_STAGES.length - 1; i++) {
|
|
7409
|
+
const conv = stages[i].conversionPctToNext;
|
|
7410
|
+
if (conv === null) continue;
|
|
7411
|
+
if (biggestLeak === null || conv < biggestLeak.conversionPct) biggestLeak = {
|
|
7412
|
+
from: FUNNEL_STAGES[i],
|
|
7413
|
+
to: FUNNEL_STAGES[i + 1],
|
|
7414
|
+
conversionPct: conv
|
|
7415
|
+
};
|
|
7416
|
+
}
|
|
7417
|
+
const closed = wonCount + lostCount;
|
|
7418
|
+
const winRatePct = closed > 0 ? Math.round(wonCount / closed * 100) : null;
|
|
7419
|
+
return {
|
|
7420
|
+
fromId: snaps[0].id,
|
|
7421
|
+
toId: snaps[snaps.length - 1].id,
|
|
7422
|
+
snapshotCount: snaps.length,
|
|
7423
|
+
stages,
|
|
7424
|
+
wonCount,
|
|
7425
|
+
lostCount,
|
|
7426
|
+
winRatePct,
|
|
7427
|
+
biggestLeak
|
|
7428
|
+
};
|
|
7429
|
+
}
|
|
7430
|
+
//#endregion
|
|
7431
|
+
//#region src/mcp/tools/get-pipeline-funnel.ts
|
|
7432
|
+
const DATA_DIR$2 = process.env["DXCRM_DATA_DIR"] ?? process.cwd();
|
|
7433
|
+
async function handleGetPipelineFunnel(_input, dataDir = DATA_DIR$2) {
|
|
7434
|
+
const report = analyzeFunnel(dataDir);
|
|
7435
|
+
return { content: [{
|
|
7436
|
+
type: "text",
|
|
7437
|
+
text: JSON.stringify(report, null, 2)
|
|
7438
|
+
}] };
|
|
7439
|
+
}
|
|
7440
|
+
function registerGetPipelineFunnel(server) {
|
|
7441
|
+
server.registerTool("get_pipeline_funnel", {
|
|
7442
|
+
title: "Get Pipeline Funnel",
|
|
7443
|
+
description: `Pipeline conversion funnel & win-rate from the daily snapshot history.
|
|
7444
|
+
For each deal it tracks the furthest stage reached and the win/lost outcome,
|
|
7445
|
+
then builds a cumulative funnel. Answers "where do deals leak out of my
|
|
7446
|
+
pipeline?" and "what's my win rate?".
|
|
7447
|
+
|
|
7448
|
+
Returns: { fromId, toId, snapshotCount,
|
|
7449
|
+
stages[{stage, reached, conversionPctToNext}], wonCount, lostCount, winRatePct,
|
|
7450
|
+
biggestLeak{from,to,conversionPct} }. snapshotCount is 0 until the daemon has
|
|
7451
|
+
taken snapshots.`,
|
|
7452
|
+
inputSchema: zod.z.object({})
|
|
7453
|
+
}, async () => handleGetPipelineFunnel({}));
|
|
7454
|
+
}
|
|
7455
|
+
//#endregion
|
|
7345
7456
|
//#region src/mcp/prompts.ts
|
|
7346
7457
|
/**
|
|
7347
7458
|
* CRM playbook prompts exposed via MCP `prompts/list` + `prompts/get`.
|
|
@@ -7761,6 +7872,7 @@ function createMcpServer() {
|
|
|
7761
7872
|
registerGetDiagnostics(server);
|
|
7762
7873
|
registerGetPipelineChanges(server);
|
|
7763
7874
|
registerGetPipelineVelocity(server);
|
|
7875
|
+
registerGetPipelineFunnel(server);
|
|
7764
7876
|
registerCustomObjectTools(server);
|
|
7765
7877
|
registerPrompts(server);
|
|
7766
7878
|
registerResources(server);
|