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