@datasynx/agentic-crm 0.1.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.
Files changed (251) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +767 -0
  3. package/dist/agent-config-zPvcqu07.js +14 -0
  4. package/dist/agent-config-zPvcqu07.js.map +1 -0
  5. package/dist/approvals-DpjxGHFp.js +67 -0
  6. package/dist/approvals-DpjxGHFp.js.map +1 -0
  7. package/dist/ask-CID3jnuL.js +52 -0
  8. package/dist/ask-CID3jnuL.js.map +1 -0
  9. package/dist/audit-log-DNMY9mUZ.js +49 -0
  10. package/dist/audit-log-DNMY9mUZ.js.map +1 -0
  11. package/dist/auth-CyFuu9X_.js +2 -0
  12. package/dist/auth-DFWwWcYD.js +93 -0
  13. package/dist/auth-DFWwWcYD.js.map +1 -0
  14. package/dist/autofill-Di_-SP7t.js +51 -0
  15. package/dist/autofill-Di_-SP7t.js.map +1 -0
  16. package/dist/backup-CeMk9z86.js +417 -0
  17. package/dist/backup-CeMk9z86.js.map +1 -0
  18. package/dist/backup-f_hC7rBV.js +2 -0
  19. package/dist/calendly-Bft_wwji.js +52 -0
  20. package/dist/calendly-Bft_wwji.js.map +1 -0
  21. package/dist/calendly-D3coO92o.cjs +53 -0
  22. package/dist/calendly-D3coO92o.cjs.map +1 -0
  23. package/dist/chunk-DakpK96I.cjs +43 -0
  24. package/dist/churn-C28IgnAj.js +54 -0
  25. package/dist/churn-C28IgnAj.js.map +1 -0
  26. package/dist/cli.js +4396 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/colors-BG07TZQz.js +11 -0
  29. package/dist/colors-BG07TZQz.js.map +1 -0
  30. package/dist/compliance-B1kk5-YS.js +115 -0
  31. package/dist/compliance-B1kk5-YS.js.map +1 -0
  32. package/dist/compliance-B91zNvCR.cjs +156 -0
  33. package/dist/compliance-B91zNvCR.cjs.map +1 -0
  34. package/dist/compliance-CKSBoQUe.js +118 -0
  35. package/dist/compliance-CKSBoQUe.js.map +1 -0
  36. package/dist/compliance-CujOqAKk.js +2 -0
  37. package/dist/context-builder-BzWAp3Zs.js +96 -0
  38. package/dist/context-builder-BzWAp3Zs.js.map +1 -0
  39. package/dist/context-builder-DlrRcqmJ.js +2 -0
  40. package/dist/conversation-intel-mm7Lhemh.js +72 -0
  41. package/dist/conversation-intel-mm7Lhemh.js.map +1 -0
  42. package/dist/custom-fields-CzNeD3_v.js +2 -0
  43. package/dist/custom-fields-Pl2t9xzp.js +73 -0
  44. package/dist/custom-fields-Pl2t9xzp.js.map +1 -0
  45. package/dist/custom-objects-BHgn1GEX.js +78 -0
  46. package/dist/custom-objects-BHgn1GEX.js.map +1 -0
  47. package/dist/custom-objects-CIFrmQ2V.js +2 -0
  48. package/dist/customer-dir-DIylZ8Q6.js +75 -0
  49. package/dist/customer-dir-DIylZ8Q6.js.map +1 -0
  50. package/dist/daemon/worker.js +207 -0
  51. package/dist/daemon/worker.js.map +1 -0
  52. package/dist/enrichment-3XvgGDfB.js +103 -0
  53. package/dist/enrichment-3XvgGDfB.js.map +1 -0
  54. package/dist/file-lock-B_zi7NQl.js +22 -0
  55. package/dist/file-lock-B_zi7NQl.js.map +1 -0
  56. package/dist/gmail-auth-BP6cJwfw.js +40 -0
  57. package/dist/gmail-auth-BP6cJwfw.js.map +1 -0
  58. package/dist/gmail-auth-DxakCtGm.cjs +44 -0
  59. package/dist/gmail-auth-DxakCtGm.cjs.map +1 -0
  60. package/dist/gmail-auth-OComS92L.js +40 -0
  61. package/dist/gmail-auth-OComS92L.js.map +1 -0
  62. package/dist/gmail-push-watch-DELQFMPk.js +20 -0
  63. package/dist/gmail-push-watch-DELQFMPk.js.map +1 -0
  64. package/dist/gmail-sender-StTpJ9Ub.js +32 -0
  65. package/dist/gmail-sender-StTpJ9Ub.js.map +1 -0
  66. package/dist/gmail-sync-DIaxInDT.js +204 -0
  67. package/dist/gmail-sync-DIaxInDT.js.map +1 -0
  68. package/dist/gmail-sync-hHm9gaWd.cjs +218 -0
  69. package/dist/gmail-sync-hHm9gaWd.cjs.map +1 -0
  70. package/dist/gmail-sync-rQaVqKWd.js +214 -0
  71. package/dist/gmail-sync-rQaVqKWd.js.map +1 -0
  72. package/dist/gmail-webhook-handler-DS7OlRPX.js +3 -0
  73. package/dist/gmail-webhook-handler-e5Od25FX.js +97 -0
  74. package/dist/gmail-webhook-handler-e5Od25FX.js.map +1 -0
  75. package/dist/goal-engine-CUZSpERI.js +2 -0
  76. package/dist/goal-engine-KpBftn4V.js +295 -0
  77. package/dist/goal-engine-KpBftn4V.js.map +1 -0
  78. package/dist/google-drive-sync-DEPcqFca.js +105 -0
  79. package/dist/google-drive-sync-DEPcqFca.js.map +1 -0
  80. package/dist/hybrid-search-BmHttLrR.js +40 -0
  81. package/dist/hybrid-search-BmHttLrR.js.map +1 -0
  82. package/dist/hygiene-DZqfYpFf.js +38 -0
  83. package/dist/hygiene-DZqfYpFf.js.map +1 -0
  84. package/dist/identity-CI6olMNm.js +41 -0
  85. package/dist/identity-CI6olMNm.js.map +1 -0
  86. package/dist/identity-gyfWdrcX.js +2 -0
  87. package/dist/import-hubspot-BaK71U_K.js +588 -0
  88. package/dist/import-hubspot-BaK71U_K.js.map +1 -0
  89. package/dist/index-V8BFaH-b.d.ts +539 -0
  90. package/dist/index-V8BFaH-b.d.ts.map +1 -0
  91. package/dist/index-YqwMd6aQ.d.cts +538 -0
  92. package/dist/index-YqwMd6aQ.d.cts.map +1 -0
  93. package/dist/index.cjs +185 -0
  94. package/dist/index.cjs.map +1 -0
  95. package/dist/index.d.cts +538 -0
  96. package/dist/index.d.cts.map +1 -0
  97. package/dist/index.d.ts +539 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +165 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/interactions-writer-CrPStUll.cjs +77 -0
  102. package/dist/interactions-writer-CrPStUll.cjs.map +1 -0
  103. package/dist/interactions-writer-DO3KcSR3.js +52 -0
  104. package/dist/interactions-writer-DO3KcSR3.js.map +1 -0
  105. package/dist/interactions-writer-SLHnoEeE.js +46 -0
  106. package/dist/interactions-writer-SLHnoEeE.js.map +1 -0
  107. package/dist/interactions-writer-dSPy1XfO.js +2 -0
  108. package/dist/knowledge-base-D0Fh40kc.js +1013 -0
  109. package/dist/knowledge-base-D0Fh40kc.js.map +1 -0
  110. package/dist/lancedb-CCBbpulq.js +2 -0
  111. package/dist/lancedb-rlvWoPwl.js +98 -0
  112. package/dist/lancedb-rlvWoPwl.js.map +1 -0
  113. package/dist/lead-model-BCFzyktm.js +109 -0
  114. package/dist/lead-model-BCFzyktm.js.map +1 -0
  115. package/dist/llm-DEjWcqmW.js +2 -0
  116. package/dist/llm-DvzZqva0.js +372 -0
  117. package/dist/llm-DvzZqva0.js.map +1 -0
  118. package/dist/llm-Z8RIYkpF.js +174 -0
  119. package/dist/llm-Z8RIYkpF.js.map +1 -0
  120. package/dist/llm-iijeXmgq.cjs +198 -0
  121. package/dist/llm-iijeXmgq.cjs.map +1 -0
  122. package/dist/mcp-CdTJWTJf.d.cts +12 -0
  123. package/dist/mcp-CdTJWTJf.d.cts.map +1 -0
  124. package/dist/mcp-CdTJWTJf.d.ts +12 -0
  125. package/dist/mcp-CdTJWTJf.d.ts.map +1 -0
  126. package/dist/mcp.cjs +7464 -0
  127. package/dist/mcp.cjs.map +1 -0
  128. package/dist/mcp.d.cts +12 -0
  129. package/dist/mcp.d.cts.map +1 -0
  130. package/dist/mcp.d.ts +12 -0
  131. package/dist/mcp.d.ts.map +1 -0
  132. package/dist/mcp.js +7448 -0
  133. package/dist/mcp.js.map +1 -0
  134. package/dist/memory-Bb6ky3kb.js +58 -0
  135. package/dist/memory-Bb6ky3kb.js.map +1 -0
  136. package/dist/memory-Cy6-Tbyl.js +2 -0
  137. package/dist/metrics-DH8wHvya.js +26 -0
  138. package/dist/metrics-DH8wHvya.js.map +1 -0
  139. package/dist/microsoft-auth-B8_S45gh.js +17 -0
  140. package/dist/microsoft-auth-B8_S45gh.js.map +1 -0
  141. package/dist/microsoft-calendar-B6MMtUQK.js +67 -0
  142. package/dist/microsoft-calendar-B6MMtUQK.js.map +1 -0
  143. package/dist/microsoft-sync-CpZVoSuq.js +68 -0
  144. package/dist/microsoft-sync-CpZVoSuq.js.map +1 -0
  145. package/dist/nba-3wanmJ0U.js +48 -0
  146. package/dist/nba-3wanmJ0U.js.map +1 -0
  147. package/dist/notification-dispatcher-0vYNngWe.js +97 -0
  148. package/dist/notification-dispatcher-0vYNngWe.js.map +1 -0
  149. package/dist/opportunity-score-BTMOQSTV.js +47 -0
  150. package/dist/opportunity-score-BTMOQSTV.js.map +1 -0
  151. package/dist/pipedrive-client-CdGKpH9b.js +17 -0
  152. package/dist/pipedrive-client-CdGKpH9b.js.map +1 -0
  153. package/dist/pipeline-writer-BqBrYrQc.js +2 -0
  154. package/dist/pipeline-writer-BvVquKIe.js +96 -0
  155. package/dist/pipeline-writer-BvVquKIe.js.map +1 -0
  156. package/dist/pipeline-writer-N2omexxp.cjs +121 -0
  157. package/dist/pipeline-writer-N2omexxp.cjs.map +1 -0
  158. package/dist/pipeline-writer-eufx_0o1.js +102 -0
  159. package/dist/pipeline-writer-eufx_0o1.js.map +1 -0
  160. package/dist/proactive-agent-BgQXw3ac.js +96 -0
  161. package/dist/proactive-agent-BgQXw3ac.js.map +1 -0
  162. package/dist/proactive-worker-BrLHNhjH.js +229 -0
  163. package/dist/proactive-worker-BrLHNhjH.js.map +1 -0
  164. package/dist/push-manager-CdqIIkuh.js +108 -0
  165. package/dist/push-manager-CdqIIkuh.js.map +1 -0
  166. package/dist/push-manager-CowY-0IK.js +2 -0
  167. package/dist/quote-generator-BfwENXzg.js +133 -0
  168. package/dist/quote-generator-BfwENXzg.js.map +1 -0
  169. package/dist/quote-generator-OhSFsi3x.js +2 -0
  170. package/dist/rbac-C7c8tcES.js +2 -0
  171. package/dist/rbac-CTIktZaC.js +91 -0
  172. package/dist/rbac-CTIktZaC.js.map +1 -0
  173. package/dist/relationship-health-odxEoQdJ.js +454 -0
  174. package/dist/relationship-health-odxEoQdJ.js.map +1 -0
  175. package/dist/revenue-simulation-BJdRTEHc.js +2 -0
  176. package/dist/revenue-simulation-Bqf2DLVB.js +251 -0
  177. package/dist/revenue-simulation-Bqf2DLVB.js.map +1 -0
  178. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  179. package/dist/salesforce-client-rhZFa_p5.js +51 -0
  180. package/dist/salesforce-client-rhZFa_p5.js.map +1 -0
  181. package/dist/segments-BqcD5HIl.js +61 -0
  182. package/dist/segments-BqcD5HIl.js.map +1 -0
  183. package/dist/sequence-engine-CCTHEBgi.js +2 -0
  184. package/dist/sequence-engine-J1lTW_in.js +91 -0
  185. package/dist/sequence-engine-J1lTW_in.js.map +1 -0
  186. package/dist/sequence-store-DaaWr0Os.js +221 -0
  187. package/dist/sequence-store-DaaWr0Os.js.map +1 -0
  188. package/dist/server-Dyva03K8.js +4287 -0
  189. package/dist/server-Dyva03K8.js.map +1 -0
  190. package/dist/session-B9AilxOE.js +81 -0
  191. package/dist/session-B9AilxOE.js.map +1 -0
  192. package/dist/session-D0qFkBla.cjs +82 -0
  193. package/dist/session-D0qFkBla.cjs.map +1 -0
  194. package/dist/session-D9ub6Wl1.js +79 -0
  195. package/dist/session-D9ub6Wl1.js.map +1 -0
  196. package/dist/session-mWHA71Lw.js +2 -0
  197. package/dist/session-store-B0QZE8Bx.cjs +697 -0
  198. package/dist/session-store-B0QZE8Bx.cjs.map +1 -0
  199. package/dist/session-store-C8tEvMPw.js +543 -0
  200. package/dist/session-store-C8tEvMPw.js.map +1 -0
  201. package/dist/session-store-CEa39Dxs.js +15 -0
  202. package/dist/session-store-CEa39Dxs.js.map +1 -0
  203. package/dist/sla-engine-5IhTsBUR.js +2 -0
  204. package/dist/sla-engine-BqX-7u-7.js +53 -0
  205. package/dist/sla-engine-BqX-7u-7.js.map +1 -0
  206. package/dist/sop-DkhVChGy.js +2 -0
  207. package/dist/sop-Vp0UPWFW.js +70 -0
  208. package/dist/sop-Vp0UPWFW.js.map +1 -0
  209. package/dist/survey-engine-C06hcQt3.js +2 -0
  210. package/dist/survey-engine-DBjCYqCv.js +147 -0
  211. package/dist/survey-engine-DBjCYqCv.js.map +1 -0
  212. package/dist/sync-state-ChaLbamC.js +33 -0
  213. package/dist/sync-state-ChaLbamC.js.map +1 -0
  214. package/dist/sync-state-CwLSt_1m.js +2 -0
  215. package/dist/ticket-writer-CjqKeIRD.js +2 -0
  216. package/dist/ticket-writer-j2oX_Wal.js +134 -0
  217. package/dist/ticket-writer-j2oX_Wal.js.map +1 -0
  218. package/dist/tone-Bdm5uaht.js +48 -0
  219. package/dist/tone-Bdm5uaht.js.map +1 -0
  220. package/dist/tone-DRKlZgPr.cjs +43 -0
  221. package/dist/tone-DRKlZgPr.cjs.map +1 -0
  222. package/dist/tone-vNb2DAAD.js +39 -0
  223. package/dist/tone-vNb2DAAD.js.map +1 -0
  224. package/dist/transcript-watcher-CL2QUygI.js +132 -0
  225. package/dist/transcript-watcher-CL2QUygI.js.map +1 -0
  226. package/dist/unmatched-transcripts-BsH5bhkU.js +26 -0
  227. package/dist/unmatched-transcripts-BsH5bhkU.js.map +1 -0
  228. package/dist/unmatched-transcripts-D0PrJ9iz.js +2 -0
  229. package/dist/update-deal-BNwPGaTV.js +2 -0
  230. package/dist/update-deal-DKC79skb.js +91 -0
  231. package/dist/update-deal-DKC79skb.js.map +1 -0
  232. package/dist/usage-CClTf5e6.cjs +57 -0
  233. package/dist/usage-CClTf5e6.cjs.map +1 -0
  234. package/dist/usage-D0-TYJkw.js +93 -0
  235. package/dist/usage-D0-TYJkw.js.map +1 -0
  236. package/dist/usage-D0u9a-lV.js +54 -0
  237. package/dist/usage-D0u9a-lV.js.map +1 -0
  238. package/dist/vault-C1D3zScD.js +2 -0
  239. package/dist/vault-DXCg29W-.js +86 -0
  240. package/dist/vault-DXCg29W-.js.map +1 -0
  241. package/dist/webhooks-7EpA05Qr.js +138 -0
  242. package/dist/webhooks-7EpA05Qr.js.map +1 -0
  243. package/dist/webhooks-BO2UAnmn.js +94 -0
  244. package/dist/webhooks-BO2UAnmn.js.map +1 -0
  245. package/dist/webhooks-Xn6zO6kd.cjs +97 -0
  246. package/dist/webhooks-Xn6zO6kd.cjs.map +1 -0
  247. package/dist/write-queue-BDolUxfs.cjs +26 -0
  248. package/dist/write-queue-BDolUxfs.cjs.map +1 -0
  249. package/dist/write-queue-IbsAjUnh.js +21 -0
  250. package/dist/write-queue-IbsAjUnh.js.map +1 -0
  251. package/package.json +142 -0
@@ -0,0 +1,1013 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import matter from "gray-matter";
4
+ import { z } from "zod";
5
+ //#region src/mcp/capabilities.ts
6
+ const CAPABILITIES_TEXT = `
7
+ # DatasynxOpenCRM — Agent Guide
8
+
9
+ ## Product
10
+ DatasynxOpenCRM is a local-first, MCP-native CRM. All customer data lives in markdown
11
+ files on your machine. No cloud, no HubSpot, no per-seat pricing.
12
+
13
+ ## Agent Wake (Telegram Notifications)
14
+ \`dxcrm agent spawn\` enables a wake-triggered agent for a customer. When new emails arrive,
15
+ the agent sends a Telegram notification so you never miss an inbound message.
16
+ \`\`\`
17
+ dxcrm agent spawn acme-corp --channel telegram
18
+ \`\`\`
19
+ Requires: \`TELEGRAM_BOT_TOKEN\` + \`TELEGRAM_CHAT_ID\` env vars.
20
+
21
+ ## Golden Path — Agent Session Workflow
22
+
23
+ The recommended sequence for a productive agent session:
24
+
25
+ 1. \`get_capabilities()\` — understand available tools (this guide)
26
+ 2. \`get_active_session()\` — check for an active customer session
27
+ 3. \`get_customer_context({ slug })\` — load full briefing for the customer
28
+ 4. \`search_customer_knowledge({ slug, query })\` — find specific historical information
29
+ 5. \`log_interaction()\` / \`update_deal()\` — write back what happened
30
+
31
+ ## RBAC — Role-Based Access Control
32
+
33
+ Tools enforce the \`DXCRM_ACTOR\` environment variable for identity. Configure roles with \`dxcrm rbac set\`.
34
+
35
+ | Role | Permissions |
36
+ |---|---|
37
+ | admin | All tools, all customers |
38
+ | manager | log_interaction, update_deal, pursue_goal + all read tools |
39
+ | rep | log_interaction, update_deal (own customers only) + all read tools |
40
+
41
+ Default role: rep (when DXCRM_ACTOR is not set or has no assigned role).
42
+
43
+ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
44
+
45
+ ## Available Tools
46
+
47
+ | Tool | Purpose | RBAC |
48
+ |---|---|---|
49
+ | get_capabilities | Returns this guide — understand what the CRM can do | any |
50
+ | get_active_session | Check which customer session is currently active | any |
51
+ | get_customer_context | Full LLM-ready briefing for a customer (last 10 interactions, pipeline, contacts) | any (rep: own only) |
52
+ | search_customer_knowledge | Hybrid vector + full-text search across emails and transcripts for a customer | any |
53
+ | list_customers | List all customers with stage, last interaction date, and deal value | any (rep: own only) |
54
+ | log_interaction | Write a new interaction entry (call, email, meeting, note) — immediately searchable | rep+ |
55
+ | update_deal | Create or update a deal in pipeline.md — upserts by deal name | rep+ |
56
+ | update_customer_facts | Update fields in customer profile (domain, contact, stage, tags) | admin |
57
+ | export_customer | Export all customer data as JSON or Markdown | admin |
58
+ | get_deal_health | Score deal health 0–100 (A–F grade) based on activity, velocity, close date, probability | any |
59
+ | get_pipeline_forecast | Aggregate weighted pipeline revenue across all customers grouped by stage | any |
60
+ | get_pipeline_stages | List all configured pipeline stages (defaults: lead, qualified, proposal, negotiation, won, lost) | any |
61
+ | summarize_meeting | LLM-summarize a transcript and log it as a Meeting interaction | rep+ |
62
+ | get_market_intelligence | Semantic search across all customers for patterns and common topics | any |
63
+ | get_relationship_graph | Stakeholder map: champions, blockers, economic buyers, warm intro paths | any |
64
+ | get_relationship_health | Health scores (0–100, A–F, trend) per contact with decay detection and risk flags | any |
65
+ | run_deal_agent | Analyze deal and generate prioritized action plan (observe/suggest/act autonomy levels) | rep+ |
66
+ | approve_agent_action | Approve or reject a pending action from the deal agent queue | rep+ |
67
+ | simulate_revenue | Monte Carlo pipeline forecast — P10/P50/P90, sensitivity map, at-risk revenue | any |
68
+ | get_playbook | Retrieve playbooks matching current deal situation (trigger-matched, sorted by success rate) | any |
69
+ | create_playbook | Create or update a playbook with trigger DSL encoding proven tactics | rep+ |
70
+ | list_playbooks | List all playbooks for a customer (metadata only, no body) | any |
71
+ | distill_playbook | LLM-extract a reusable playbook from a won or lost deal's interaction history | rep+ |
72
+ | pursue_goal | Set a revenue/pipeline goal and get an AI-decomposed action plan with sub-goals | manager+ |
73
+ | get_goal_status | Get all active goals or a specific goal with progress, days remaining, sub-goals | any |
74
+ | register_push_subscription | Register real-time push subscription (Gmail Pub/Sub, MS Graph, Slack Events) | admin |
75
+ | get_push_status | Show all push subscriptions: expiry, events processed, renewal needs | any |
76
+ | get_org_intelligence | Stakeholder map with champions, buyers, blockers, health scores, risk flags, recommendation | any |
77
+ | open_deal_room | Multi-agent deal brief: graph + health + deal health + simulation + playbook in one call | any |
78
+ | get_proactive_briefing | Daily briefing: urgent alerts, opportunities, P50/P90 forecast, top action | any |
79
+ | list_email_templates | List all saved email templates with id, name, category, subject | any |
80
+ | get_email_template | Retrieve a single email template with full body and detected variables | any |
81
+ | draft_email | Draft a personalized email from a template with auto-filled customer variables | rep+ |
82
+ | enroll_in_sequence | Enroll a contact in a multi-step email sequence | rep+ |
83
+ | list_sequence_enrollments | List active sequence enrollments filtered by customer or status | any |
84
+ | unenroll_from_sequence | Pause (soft-unenroll) a contact from an active sequence | rep+ |
85
+ | list_sequences | List all defined email sequences with step count and enrollment count | any |
86
+ | generate_quote | Generate a professional HTML quote with line items, VAT, subtotal, total | rep+ |
87
+ | get_quote_status | Retrieve a generated quote by number or list all quotes for a customer | any |
88
+ | get_booking_link | Get a Calendly booking link for a customer — optionally pre-fills name/email | rep+ |
89
+ | create_ticket | Create a support ticket with auto-calculated SLA due date based on priority | rep+ |
90
+ | update_ticket | Update ticket status or assignee (resolved auto-sets resolution date) | rep+ |
91
+ | list_tickets | List tickets filtered by customer, status, priority, or assignee | any |
92
+ | close_ticket | Close a ticket and optionally log resolution as an interaction | rep+ |
93
+ | send_nps_survey | Generate NPS/CSAT survey token + HTML email draft (does not send automatically) | rep+ |
94
+ | get_survey_results | NPS score, promoter/passive/detractor breakdown, all responses for a survey | any |
95
+ | search_knowledge_base | Full-text search across KB articles (title, body, tags) with category and public filters | any |
96
+ | create_kb_article | Create a new knowledge base article stored as Markdown in .agentic/knowledge-base/ | rep+ |
97
+ | backup_now | Trigger immediate backup of customers/ + .agentic/ with SHA-256 integrity check | admin |
98
+ | list_backups | List available backups with date, size, verification status, and customer count | any |
99
+ | trigger_sync | Force immediate Gmail sync for one or all customers | rep+ |
100
+ | get_audit_log | Read audit log — all write operations with actor, tool, customer | admin |
101
+ | define_custom_object | Define a runtime custom object type with typed fields (no migration) | admin |
102
+ | create_record | Create a record of a custom object (validated against its schema) | rep+ |
103
+ | list_records | List records of a custom object | any |
104
+ | list_custom_objects | List all defined custom objects and their schemas | any |
105
+
106
+ ## MCP Resources (read-only)
107
+
108
+ Besides Tools, the server exposes read-only Resources you can fetch via resources/read:
109
+ - crm://customers — list of all customer slugs (JSON)
110
+ - crm://customer/{slug} — LLM-ready briefing (main facts, recent interactions, pipeline)
111
+ - crm://pipeline/{slug} — deals for a customer (JSON)
112
+ - crm://timeline/{slug} — newest-first interaction history (Markdown)
113
+
114
+ ## MCP Prompts (playbooks)
115
+
116
+ Reusable playbook prompts via prompts/get (argument: slug):
117
+ - deal_risk_review — assess deal health and risk, recommend next steps
118
+ - draft_follow_up — draft a personalized follow-up email
119
+ - account_brief — concise executive account brief
120
+ - pipeline_summary — pipeline + forecast summary
121
+
122
+ ## Tool Reference
123
+
124
+ ### get_capabilities()
125
+ Returns all available MCP tools, their inputs, and the CRM workflow guide.
126
+ - Input: none
127
+ - Returns: This guide text
128
+
129
+ ### get_active_session()
130
+ Check which customer is currently active in the session store.
131
+ - Input: none
132
+ - Returns: { hasSession: boolean, customerSlug?, customerName?, startedAt?, owner? }
133
+
134
+ ### get_customer_context({ slug? })
135
+ Load complete briefing for a customer. Reads main_facts.md, last 10 interactions,
136
+ and pipeline deals. Returns a structured markdown context block.
137
+ Automatically triggers a background Gmail sync if last sync was >30 minutes ago.
138
+ - Input: { slug?: string } — Customer ID (e.g. "acme-corp"). Leave empty for active session.
139
+ - Returns: Formatted markdown with Quick Reference, Contacts, Critical Context,
140
+ Recent Activity, Pipeline, and Open Questions
141
+ - Performance: <3 seconds. Token budget: <3000 tokens.
142
+
143
+ ### search_customer_knowledge({ slug, query, limit? })
144
+ Hybrid vector + full-text search across all emails and transcripts for a customer.
145
+ Searches the LanceDB docs table for the given customer.
146
+ - Input: { slug: string, query: string, limit?: number (default 5, max 50) }
147
+ - Returns: { results: Array<{ content, score, source }> }
148
+
149
+ ### list_customers({ filter? })
150
+ List all customers with their stage, last interaction date, and deal value.
151
+ RBAC: rep role only sees owned customers.
152
+ - Input: { filter?: string } — Optional substring filter on name or slug (case-insensitive)
153
+ - Returns: Array of { slug, name, stage, lastInteraction?, dealValue? }
154
+
155
+ ### log_interaction({ slug, type, summary, with, nextSteps?, direction?, source?, date? })
156
+ Write a new interaction entry to interactions.md. Immediately searchable.
157
+ Also auto-updates the relationship graph and health scores (fire-and-forget).
158
+ Use after every call, meeting, or email.
159
+ RBAC: rep+
160
+ - Input:
161
+ slug: Customer ID
162
+ type: "Email" | "Call" | "Meeting" | "Note" | "Demo" | "Proposal" | "Contract" | "Other"
163
+ summary: 2-5 sentences describing what happened
164
+ with: Who was involved (name or email)
165
+ nextSteps?: Array of action items
166
+ direction?: "inbound" | "outbound"
167
+ source?: Source reference string (auto-generated if omitted)
168
+ date?: Interaction date YYYY-MM-DD (defaults to today)
169
+ - Returns: { success: boolean, path: string, entry: string }
170
+
171
+ ### update_deal({ slug, dealName, stage?, value?, probability?, closeDate?, notes? })
172
+ Update or create a deal in pipeline.md. Upserts by deal name.
173
+ RBAC: rep+
174
+ - Input:
175
+ slug: Customer ID
176
+ dealName: Deal name (used as unique key)
177
+ stage?: "lead" | "qualified" | "proposal" | "negotiation" | "won" | "lost"
178
+ value?: Deal value in euros
179
+ probability?: Win probability (0-100)
180
+ closeDate?: Expected close date (YYYY-MM-DD)
181
+ notes?: Free-text notes
182
+ - Returns: { success: boolean, deal: object }
183
+
184
+ ### update_customer_facts({ slug, name?, domain?, email?, phone?, industry?, relationshipStage?, dealValue?, primaryContact?, timezone?, tags? })
185
+ Update fields in a customer's main_facts.md profile. Merges patch into existing data. Sets updated = today.
186
+ RBAC: admin
187
+ - Input: slug (required) + any combination of the optional fields
188
+ - Returns: { success: boolean, facts: object }
189
+
190
+ ### export_customer({ slug, format? })
191
+ Export all customer data (main_facts + interactions count + pipeline + attachments list).
192
+ RBAC: admin
193
+ - Input: { slug: string, format?: "json" | "markdown" (default "json") }
194
+ - Returns (JSON): { slug, exportedAt, mainFacts, interactionsCount, pipeline, attachments }
195
+ - Returns (Markdown): Formatted document with all sections
196
+
197
+ ### get_deal_health({ slug })
198
+ Score the health of all deals for a customer based on activity recency, stage velocity,
199
+ close date proximity, and probability.
200
+ - Input: { slug: string }
201
+ - Returns: { slug, deals: [{ deal, stage, score, grade, signals, warnings }] }
202
+
203
+ ### get_pipeline_forecast({ filter? })
204
+ Aggregate weighted pipeline revenue across all customers. Groups open deals by stage,
205
+ computes probability-weighted expected revenue. Excludes won/lost deals.
206
+ - Input: { filter?: string } — Optional filter by customer slug substring
207
+ - Returns: { deals: [...], totalWeightedValue: number, byStage: { stage: { count, weightedValue } } }
208
+
209
+ ### get_pipeline_stages()
210
+ Returns all configured pipeline stages. Falls back to default stages if no custom stages configured.
211
+ - Input: none
212
+ - Returns: { stages: [{ id, label, order, probability, color, final }] }
213
+
214
+ ### summarize_meeting({ slug, transcript, with?, date? })
215
+ LLM-summarize a meeting transcript and log it as a Meeting interaction.
216
+ Falls back to raw text slice if LLM unavailable.
217
+ RBAC: rep+
218
+ - Input:
219
+ slug: Customer ID
220
+ transcript: Full meeting transcript text
221
+ with?: Participant names
222
+ date?: Meeting date YYYY-MM-DD (defaults to today)
223
+ - Returns: { success, summary, nextSteps, sourceRef }
224
+
225
+ ### get_market_intelligence({ query, excludeCurrentCustomer?, slug? })
226
+ Search across all customers to find patterns, common topics, or similar issues.
227
+ Uses semantic search (LanceDB) across all customer knowledge bases.
228
+ Results use slug (not real names) for privacy.
229
+ - Input: { query: string, excludeCurrentCustomer?: boolean, slug?: string }
230
+ - Returns: { query, results: CrossCustomerResult[], totalCustomersSearched }
231
+
232
+ ### get_relationship_graph({ slug })
233
+ Returns the knowledge graph for a customer: contacts, companies, and their relationships.
234
+ Auto-populated from every log_interaction call. Shows stakeholder map with champions, blockers,
235
+ economic buyers, and warm intro paths.
236
+ - Input: { slug: string }
237
+ - Returns: { nodeCount, edgeCount, updatedAt, stakeholders: { champions[], blockers[], economicBuyers[], allContacts[], missingRoles[] }, warmIntroPaths[], nodes[], edges[] }
238
+
239
+ ### get_relationship_health({ slug })
240
+ Returns health scores (0-100, A-F grade) for all contacts. Scores decay when cadence breaks.
241
+ Risk flags: NO_CONTACT_14D, NO_CONTACT_30D, CHAMPION_SILENT.
242
+ Recomputes automatically if stale (>1h) or missing.
243
+ - Input: { slug: string }
244
+ - Returns: { overallHealth, updatedAt, atRiskContacts[], coldContacts[], contacts: ContactHealth[] }
245
+
246
+ ### run_deal_agent({ slug, dealName, autonomyLevel?, instruction?, valueThreshold? })
247
+ Analyzes deal situation (health, relationships, stakeholder gaps) via LLM (rule-based fallback).
248
+ Returns prioritized action plan with confidence scores and full reasoning trace.
249
+ autonomyLevel: "observe" (read-only) | "suggest" (queue for review, default) | "act" (auto-execute if confidence ≥ 0.7 and value < valueThreshold)
250
+ RBAC: rep+
251
+ - Input: { slug, dealName, autonomyLevel?: "observe"|"suggest"|"act", instruction?, valueThreshold?: number (default 50000) }
252
+ - Returns: { assessment, riskLevel, plan[], actionsQueued[], actionsExecuted[], trace }
253
+
254
+ ### approve_agent_action({ slug, actionId, approved })
255
+ Execute (approved=true) or reject (approved=false) a pending deal agent action.
256
+ Find actionId in run_deal_agent response.actionsQueued[].actionId
257
+ RBAC: rep+
258
+ - Input: { slug, actionId, approved: boolean }
259
+ - Returns: { success, actionId, status }
260
+
261
+ ### simulate_revenue({ horizon?, iterations? })
262
+ Monte Carlo simulation over all active deals. Adjusts probabilities via health score (D12) and
263
+ champion presence (D11). Returns P10/P50/P90 confidence interval + sensitivity map.
264
+ horizon: "quarter" (default) | "year"
265
+ - Returns: { forecast: { p10, p50, p90, expected, stdDev, atRiskRevenue, byCloseMonth, topRisks, sensitivityMap }, confidence, dealCount, horizon }
266
+
267
+ ### get_playbook({ slug, stage?, value?, healthScore?, daysSinceContact?, championPresent? })
268
+ Returns playbooks matching the current deal situation. Without deal context, returns all playbooks.
269
+ Playbooks are sorted by success rate (highest first). run_deal_agent uses playbooks automatically.
270
+ - Input: slug (required) + optional deal context fields for trigger matching
271
+ - Returns: { matches: [{ name, score, matchedConditions, trigger, successRate, usedCount, content }], totalPlaybooks, slug }
272
+
273
+ ### create_playbook({ slug, name, trigger, content, successRate? })
274
+ Create or update a playbook encoding proven tactics for a specific deal situation.
275
+ Trigger DSL uses AND-only conditions: deal_stage_<s> | value > N | value < N | days_stalled > N | health < N | health > N | no_champion | has_champion
276
+ RBAC: rep+
277
+ - Input: slug, name, trigger (DSL string), content (markdown), successRate? (0–1, default 0.5)
278
+ - Returns: { success: true, playbook: { name, trigger, successRate, usedCount, lastUpdated, path } }
279
+
280
+ ### list_playbooks({ slug })
281
+ List all playbooks for a customer (metadata only — no body content for performance).
282
+ - Input: { slug: string }
283
+ - Returns: { playbooks: [{ name, trigger, successRate, usedCount, lastUpdated }], count, slug }
284
+
285
+ ### distill_playbook({ slug, dealName, outcome })
286
+ LLM analyzes a deal's interaction history and extracts a reusable playbook.
287
+ Run after every won or lost deal to build procedural memory.
288
+ RBAC: rep+
289
+ outcome: "won" | "lost"
290
+ - Returns: { success: true, playbook: { name, trigger, successRate, path }, reasoning }
291
+
292
+ ### pursue_goal({ goal, deadline, context? })
293
+ Set a revenue or pipeline goal and get an AI-decomposed action plan.
294
+ Analyzes current pipeline (P50 forecast) and decomposes the gap into prioritized sub-goals per deal.
295
+ Persists goal to .agentic/goals.json for tracking.
296
+ RBAC: manager+
297
+ - Input: { goal: string, deadline: "YYYY-MM-DD", context?: string }
298
+ - Returns: { goalId, description, target, deadline, decomposition: { analysis, currentPipeline, gap, subGoals, probabilisticOutcome } }
299
+
300
+ ### get_goal_status({ goalId? })
301
+ Get the status of active goals. Without goalId, returns all active goals.
302
+ - Input: { goalId?: string } — omit for all active goals
303
+ - Returns: { goals: [{ id, description, target, progress, status, deadline, daysRemaining, subGoals }], activeCount, completedCount }
304
+
305
+ ### register_push_subscription({ provider, slug, webhookUrl, ... })
306
+ Register a real-time push subscription so providers send events in real-time (no polling).
307
+ RBAC: admin only
308
+ - Input:
309
+ provider: "gmail" | "microsoft-graph" | "slack"
310
+ slug: Customer slug to receive events for
311
+ webhookUrl: Public HTTPS URL for provider callbacks
312
+ gmailTopicName?: (Gmail) Cloud Pub/Sub topic name
313
+ microsoftClientState?: (MS Graph) Secret for HMAC verification
314
+ microsoftResource?: (MS Graph) Resource path
315
+ slackTeamId?: (Slack) Workspace team ID
316
+ slackChannelId?: (Slack) Optional specific channel
317
+ - Returns: { subscriptionId, provider, slug, status, expiresAt, createdAt, warning? }
318
+
319
+ ### get_push_status({ slug?, provider? })
320
+ Show all push subscriptions with expiry and event counts.
321
+ - Input: { slug?: string, provider?: "gmail" | "microsoft-graph" | "slack" }
322
+ - Returns: { subscriptions: [{ id, provider, slug, status, expiresAt, expiresInHours, needsRenewal, lastEventAt, eventsProcessed }], summary: { total, active, expiringSoon, expired } }
323
+
324
+ ### get_org_intelligence({ slug, dealName? })
325
+ Build a stakeholder map for a customer: champions, economic buyers, blockers, health scores, risk flags, and a prioritised recommendation.
326
+ - Input: { slug: string, dealName?: string }
327
+ - Returns: { slug, updatedAt, people: [{ name, email, role, healthScore, daysSinceContact, contactStrength, riskFlags }], missingRoles, riskAssessment, recommendation }
328
+
329
+ ### open_deal_room({ slug, dealName })
330
+ Multi-agent deal brief: orchestrates relationship graph, health scores, deal health, Monte Carlo simulation, and playbook matching into a unified brief with executive summary, top priorities, and risk score (0–100).
331
+ - Input: { slug: string, dealName: string }
332
+ - Returns: { slug, dealName, generatedAt, stakeholders, relationshipHealth, dealHealth, revenueSimulation, recommendedPlaybook, executiveSummary, topPriorities, riskScore }
333
+
334
+ ### get_proactive_briefing({ date? })
335
+ Generate a proactive daily briefing: urgent alerts (relationship decay, deal risk, overdue close dates),
336
+ pipeline forecast (P50/P90), and a single top-action recommendation.
337
+ - Input: { date?: "YYYY-MM-DD" } — defaults to today
338
+ - Returns: { date, generatedAt, urgent: string[], opportunities: string[], forecast: string, topAction: string }
339
+
340
+ ### list_email_templates({ category? })
341
+ List available email templates. Optionally filter by category.
342
+ - Input: { category?: string } — e.g. "outreach", "followup", "support"
343
+ - Returns: Array of { id, name, category, subject } (body excluded for performance)
344
+
345
+ ### get_email_template({ id })
346
+ Get a specific email template with full body and detected template variables.
347
+ - Input: { id: string } — Template ID (e.g. "enterprise-intro")
348
+ - Returns: { id, name, category, subject, body, detectedVariables: string[] }
349
+
350
+ ### draft_email({ slug, templateId, overrides?, tone? })
351
+ Draft a personalized email for a customer using a stored template.
352
+ Variables are auto-filled from the customer's main_facts.md.
353
+ Optional tone (e.g. "formal", "friendly", "concise") LLM-polishes the body;
354
+ falls back to plain template-fill without an ANTHROPIC_API_KEY.
355
+ Does NOT send automatically — returns the draft for review.
356
+ RBAC: rep+
357
+ - Input: { slug, templateId, overrides?: Record<string, string> }
358
+ - Returns: { subject, body, to, slug, templateId, resolvedVariables }
359
+
360
+ ### enroll_in_sequence({ slug, contactEmail, sequenceId })
361
+ Enroll a contact in an email sequence. Validates that the sequence and first template exist.
362
+ RBAC: rep+
363
+ - Input: { slug: string, contactEmail: string, sequenceId: string }
364
+ - Returns: { enrollmentId, sequenceName, totalSteps }
365
+
366
+ ### list_sequence_enrollments({ slug?, status? })
367
+ List email sequence enrollments. Filter by customer slug or status.
368
+ - Input: { slug?: string, status?: "active" | "paused" | "completed" }
369
+ - Returns: { enrollments: SequenceEnrollment[] }
370
+
371
+ ### unenroll_from_sequence({ enrollmentId })
372
+ Pause (soft-unenroll) a contact from an email sequence. Sets status to "paused".
373
+ RBAC: rep+
374
+ - Input: { enrollmentId: string }
375
+ - Returns: { success: boolean }
376
+
377
+ ### list_sequences()
378
+ List all defined email sequences with step count and current enrollment count.
379
+ - Input: none
380
+ - Returns: { sequences: [{ id, name, stepCount, enrollmentCount }] }
381
+
382
+ ### generate_quote({ slug, dealName, lineItems, vatPercent?, validUntilDays?, currency? })
383
+ Generate a professional HTML quote for a customer deal.
384
+ Calculates subtotal, VAT, and total. Saves JSON + HTML to .agentic/quotes/.
385
+ RBAC: rep+
386
+ - Input:
387
+ slug: Customer slug
388
+ dealName: Deal name this quote is for
389
+ lineItems: Array<{ description, quantity, unitPrice }>
390
+ vatPercent?: VAT percentage (default 19)
391
+ validUntilDays?: Quote validity in days (default 30)
392
+ currency?: Currency code (default EUR)
393
+ - Returns: { quoteNumber, htmlPath, total, subtotal, vat, vatPercent, currency, validUntil, status }
394
+
395
+ ### get_quote_status({ quoteNumber?, slug? })
396
+ Get quote status and details. Filter by quoteNumber (single quote) or slug (all quotes for customer).
397
+ - Input: { quoteNumber?: string, slug?: string }
398
+ - Returns (single): Full quote object with status: draft | sent | viewed | accepted | declined
399
+ - Returns (list): { quotes: [...] }
400
+
401
+ ### get_booking_link({ slug, eventType?, prefillName? })
402
+ Get a Calendly booking link for a customer. Optionally pre-fills the customer's name/email.
403
+ Requires CALENDLY_API_KEY env var or .agentic/integrations/calendly.yaml config.
404
+ RBAC: rep+
405
+ - Input: { slug, eventType?: string, prefillName?: boolean }
406
+ - Returns: { bookingUrl, eventType, duration }
407
+
408
+ ### create_ticket({ slug, title, description?, priority?, assignee? })
409
+ Create a support ticket. Auto-calculates SLA due date based on priority.
410
+ SLA defaults: urgent=4h, high=24h, normal=72h, low=168h.
411
+ RBAC: rep+
412
+ - Input:
413
+ slug: Customer slug
414
+ title: Ticket title
415
+ description?: Detailed description
416
+ priority?: "urgent" | "high" | "normal" | "low" (default: normal)
417
+ assignee?: Assignee name or email
418
+ - Returns: { ticket } with id T-NNN, status=open, slaDue
419
+
420
+ ### update_ticket({ slug, ticketId, status?, assignee? })
421
+ Update a ticket's status or assignee. Setting status=resolved auto-sets resolved date.
422
+ RBAC: rep+
423
+ - Input: { slug, ticketId, status?: "open"|"in-progress"|"waiting"|"resolved"|"closed", assignee?: string }
424
+ - Returns: { ticket }
425
+
426
+ ### list_tickets({ slug?, status?, priority?, assignee? })
427
+ List support tickets sorted by priority then date. Filter by any combination of fields.
428
+ - Input: { slug?, status?: "open"|"in-progress"|"waiting"|"resolved"|"closed", priority?: "urgent"|"high"|"normal"|"low", assignee? }
429
+ - Returns: { tickets: Array<{ slug, ticket }> }
430
+
431
+ ### close_ticket({ slug, ticketId, resolution? })
432
+ Close a ticket and optionally log the resolution as an interaction in interactions.md.
433
+ RBAC: rep+
434
+ - Input: { slug, ticketId, resolution?: string }
435
+ - Returns: { ticket } with status=closed
436
+
437
+ ### send_nps_survey({ slug, contactEmail, surveyId, serverUrl? })
438
+ Generate an NPS/CSAT survey email draft. Returns subject, HTML body, and a token-based response URL.
439
+ Does NOT send automatically — returns draft for review.
440
+ Requires survey definition in .agentic/surveys/.
441
+ RBAC: rep+
442
+ - Input: { slug, contactEmail, surveyId, serverUrl?: string }
443
+ - Returns: { token, subject, body, surveyUrl, note }
444
+
445
+ ### get_survey_results({ surveyId, slug? })
446
+ Calculate NPS score and breakdown for a survey. Optionally filter to a single customer.
447
+ - Input: { surveyId: string, slug?: string }
448
+ - Returns: { surveyId, totalResponses, npsScore (-100 to 100), promoters, passives, detractors, responses: [{ slug, email, score, comment?, respondedAt }] }
449
+
450
+ ### search_knowledge_base({ query, category?, publicOnly?, limit? })
451
+ Full-text search across all KB articles (title, body, tags).
452
+ - Input: { query, category?: string, publicOnly?: boolean, limit?: number (default 10) }
453
+ - Returns: { query, count, articles: [{ id, title, category, excerpt, public, tags }] }
454
+
455
+ ### create_kb_article({ id, title, body, category?, tags?, public?, sourceTicketId? })
456
+ Create a new knowledge base article. Articles are stored as Markdown in .agentic/knowledge-base/.
457
+ Returns an error if an article with the same ID already exists.
458
+ RBAC: rep+
459
+ - Input:
460
+ id: Article slug (e.g. "troubleshoot-api-timeout")
461
+ title: Article title
462
+ body: Article body in Markdown
463
+ category?: Category (default: "general")
464
+ tags?: Array of tags for search
465
+ public?: Make publicly accessible (default: false)
466
+ sourceTicketId?: Ticket this article was created from
467
+ - Returns: { id, title, category, path }
468
+
469
+ ### backup_now({ remote?, note? })
470
+ Trigger an immediate backup of all CRM data (customers/ + .agentic/).
471
+ Creates a timestamped ZIP with SHA-256 manifest. Optionally uploads to S3/rsync/local.
472
+ RBAC: admin
473
+ - Input: { remote?: string, note?: string }
474
+ - Returns: { path, createdAt, customerCount, fileCount, sizeMb, directories, verified, uploadedTo? }
475
+
476
+ ### list_backups({ limit? })
477
+ List available CRM backups with metadata. Shows log-tracked backups first, falls back to directory scan.
478
+ - Input: { limit?: number (default 10, max 50) }
479
+ - Returns: { count, totalAvailable, backups: [{ filename, createdAt, sizeMb, verified, encrypted, customerCount, fileCount }] }
480
+
481
+ ## Data Structure
482
+
483
+ Customer data lives in \`customers/<slug>/\`:
484
+ - \`main_facts.md\` — YAML frontmatter + free-text sections
485
+ - \`interactions.md\` — Chronological log (newest first)
486
+ - \`pipeline.md\` — Deal table in Markdown
487
+ - \`sources.json\` — Gmail/transcript sync config per customer
488
+
489
+ Agentic data lives in \`.agentic/\`:
490
+ - \`goals.json\` — Active goals and decompositions
491
+ - \`push-subscriptions.json\` — Real-time push registrations
492
+ - \`backup-log.json\` — Backup history
493
+ - \`rbac.json\` — Role assignments
494
+ - \`audit.log\` — Full audit trail
495
+ - \`knowledge-base/\` — KB articles
496
+ - \`quotes/\` — Generated quote files
497
+
498
+ ## Response Format
499
+
500
+ Always cite sources (gmail://thread/... or file://...) when available.
501
+
502
+ ## Framework Integration
503
+
504
+ | Framework | Tier | Config |
505
+ |---|---|---|
506
+ | Claude Code | 1 | CLAUDE.md + ~/.claude.json + .claude/settings.json |
507
+ | Codex CLI | 1 | AGENTS.md + ~/.codex/config.toml |
508
+ | Grok Build (xAI) | 1 | AGENTS.md + ~/.grok/user-settings.json + .grok/settings.json |
509
+ | OpenClaw | 1 | SOUL.md + AGENTS.md + TOOLS.md |
510
+ | Hermes Agent | 1 | SOUL.md + Skill |
511
+ | Antigravity CLI | 1 | GEMINI.md + AGENTS.md + SKILL.md |
512
+ | Cursor | 2 | .cursor/rules/datasynx-crm.mdc |
513
+ | Windsurf | 2 | MCP config only |
514
+ | Cline | 2 | MCP config only |
515
+ | Claude Desktop | 2 | MCP config only |
516
+
517
+ ### Manual Grok Build configuration
518
+ \`\`\`json
519
+ // ~/.grok/user-settings.json (mcpServers is an ARRAY in Grok, not a map)
520
+ {
521
+ "mcpServers": [
522
+ {
523
+ "name": "datasynx-opencrm",
524
+ "transport": {
525
+ "type": "stdio",
526
+ "command": "node",
527
+ "args": ["/path/to/node_modules/datasynx-opencrm/dist/mcp.js"],
528
+ "env": { "DXCRM_DATA_DIR": "~/.dxcrm" }
529
+ }
530
+ }
531
+ ]
532
+ }
533
+ \`\`\`
534
+ Run \`grok inspect\` to verify the server is discovered.
535
+
536
+ ## CLI Reference (Phase 2)
537
+
538
+ ### dxcrm status
539
+ Show daemon status, customer count, sync ages, and unmatched transcript queue.
540
+ \`\`\`
541
+ dxcrm status
542
+ dxcrm status --unmatched # list full unmatched transcript queue
543
+ \`\`\`
544
+
545
+ ### dxcrm agent spawn <slug>
546
+ Spawn a wake-triggered agent for a customer. Sends Telegram notifications on new email.
547
+ \`\`\`
548
+ dxcrm agent spawn acme-corp --channel telegram
549
+ dxcrm agent spawn acme-corp --channel telegram --chat-id 12345
550
+ dxcrm agent status
551
+ dxcrm agent remove acme-corp
552
+ \`\`\`
553
+ Requires: \`TELEGRAM_BOT_TOKEN\` + \`TELEGRAM_CHAT_ID\` env vars.
554
+
555
+ ### dxcrm import <path>
556
+ Import customers and interactions from HubSpot or generic CSV export.
557
+ \`\`\`
558
+ dxcrm import contacts.csv --from csv
559
+ dxcrm import hubspot-export.csv --from hubspot
560
+ dxcrm import hubspot-export.csv --from hubspot --dry-run
561
+ \`\`\`
562
+ - Two-pass: creates customers first, then imports activities
563
+ - Idempotent: re-running skips already-imported rows
564
+ - sourceRef format: \`hubspot://activity/<id>\` or \`csv://row/<hash>\`
565
+
566
+ ## CLI Reference (Phase 3 — Team)
567
+
568
+ ### dxcrm server start
569
+ Start a shared HTTP MCP server. Multiple team members connect via URL.
570
+ \`\`\`
571
+ dxcrm server start --data /mnt/crm-data --port 3847
572
+ dxcrm server status
573
+ \`\`\`
574
+ Set actor identity: \`export DXCRM_ACTOR=alice\`
575
+
576
+ ### dxcrm audit
577
+ Show who changed what and when. Audit trail at \`.agentic/audit.log\`.
578
+ \`\`\`
579
+ dxcrm audit # Last 20 entries
580
+ dxcrm audit --slug acme-corp # Filter by customer
581
+ dxcrm audit --actor alice # Filter by actor
582
+ \`\`\`
583
+ Log format: \`2026-06-01T09:14:00Z | alice | log_interaction | acme-corp | summary\`
584
+
585
+ ### session ownership
586
+ \`\`\`
587
+ dxcrm session open acme-corp --owner alice
588
+ # or: DXCRM_ACTOR=alice dxcrm session open acme-corp
589
+ \`\`\`
590
+ \`get_active_session()\` returns \`{ owner: "alice", ... }\` when owner is set.
591
+
592
+ ## CLI Reference (Phase 5 — Migration)
593
+
594
+ ### dxcrm import — Pipedrive API
595
+ \`\`\`
596
+ dxcrm import --from pipedrive --mode api --token <tok> --url https://myco.pipedrive.com
597
+ \`\`\`
598
+ Two-pass: persons → customers, activities → interactions.
599
+ sourceRef: \`pipedrive://activity/<id>\`
600
+
601
+ ### CSV LLM Field Mapping
602
+ Generic CSV imports now use LLM-assisted column detection (fallback to heuristics when ANTHROPIC_API_KEY is unset).
603
+
604
+ ## CLI Reference (Enterprise — Sprints R1–R5)
605
+
606
+ ### dxcrm stages
607
+ Manage custom pipeline stages.
608
+ \`\`\`
609
+ dxcrm stages list # List all stages
610
+ dxcrm stages set <id> <label> [--order N] [--probability N] [--color #hex] [--final]
611
+ dxcrm stages delete <id> # Remove a stage
612
+ dxcrm stages reset # Reset to defaults
613
+ \`\`\`
614
+ Default stages: lead → qualified → proposal → negotiation → won → lost
615
+
616
+ ### dxcrm rbac
617
+ Role-based access control. Roles: admin > manager > rep.
618
+ \`\`\`
619
+ dxcrm rbac set alice admin # Assign role
620
+ dxcrm rbac show # List all roles
621
+ dxcrm rbac check alice update_deal # Permission check
622
+ \`\`\`
623
+ Config: \`.agentic/rbac.json\` | Actor: \`DXCRM_ACTOR\` env var
624
+ Enforcement: per MCP tool call | Default role: rep
625
+
626
+ ### dxcrm gdpr
627
+ GDPR erasure with dry-run safety and audit trail.
628
+ \`\`\`
629
+ dxcrm gdpr erase acme-corp # Dry-run (shows plan)
630
+ dxcrm gdpr erase acme-corp --confirm # Permanent deletion
631
+ dxcrm gdpr list-erasures # Erasure history
632
+ \`\`\`
633
+ On confirm: deletes customers/<slug>/, writes audit.log, appends gdpr-erasures.json
634
+
635
+ ### dxcrm security-report
636
+ Generate Markdown security questionnaire for procurement/SOC2 review.
637
+ \`\`\`
638
+ dxcrm security-report
639
+ dxcrm security-report --output sec-report.md
640
+ \`\`\`
641
+
642
+ ### Microsoft Outlook Sync
643
+ \`\`\`
644
+ dxcrm sync --provider microsoft
645
+ \`\`\`
646
+ Prerequisites: write \`.agentic/microsoft-token.json\` with \`{ "accessToken": "..." }\`
647
+ sourceRef: \`microsoft://message/<id>\` | API: Microsoft Graph v1.0
648
+
649
+ ### Salesforce Import
650
+ \`\`\`
651
+ dxcrm import --from salesforce --mode api --token <tok> --url https://myco.salesforce.com
652
+ \`\`\`
653
+ Two-pass: contacts → customers, tasks → interactions (WhoId attribution)
654
+ sourceRef: \`salesforce://task/<id>\` | API: Salesforce REST v58.0
655
+
656
+ ## CLI Reference (D16 — Goal-Based Orchestration)
657
+
658
+ ### dxcrm goal set
659
+ Set a goal and get a decomposed action plan based on current pipeline state.
660
+ \`\`\`
661
+ dxcrm goal set "Close €500k ARR this quarter" --deadline 2026-09-30
662
+ \`\`\`
663
+
664
+ ### dxcrm goal status
665
+ Show all active goals with progress bars and days remaining.
666
+ \`\`\`
667
+ dxcrm goal status
668
+ \`\`\`
669
+
670
+ ### dxcrm goal update
671
+ Manually update goal progress (0–100%).
672
+ \`\`\`
673
+ dxcrm goal update goal_abc123 --progress 45
674
+ \`\`\`
675
+
676
+ ### dxcrm goal cancel
677
+ Cancel an active goal.
678
+ \`\`\`
679
+ dxcrm goal cancel goal_abc123
680
+ \`\`\`
681
+
682
+ ## CLI Reference (D17 — Real-Time Push Ingestion)
683
+
684
+ ### dxcrm push register
685
+ Register a push subscription so providers send events in real-time (no polling).
686
+ \`\`\`
687
+ dxcrm push register acme-corp --provider gmail --webhook-url https://myserver.com/webhooks/gmail --topic-name projects/x/topics/gmail-push
688
+ dxcrm push register acme-corp --provider microsoft-graph --webhook-url https://myserver.com/webhooks/microsoft --client-state <secret>
689
+ dxcrm push register acme-corp --provider slack --webhook-url https://myserver.com/webhooks/slack --team-id T12345
690
+ \`\`\`
691
+
692
+ ### dxcrm push status
693
+ Show all push subscriptions, expiry and events processed.
694
+ \`\`\`
695
+ dxcrm push status
696
+ dxcrm push status --slug acme-corp
697
+ dxcrm push status --provider gmail
698
+ \`\`\`
699
+
700
+ ### dxcrm push revoke
701
+ Revoke a push subscription by ID.
702
+ \`\`\`
703
+ dxcrm push revoke psub_1716892800_a1b2c3
704
+ \`\`\`
705
+
706
+ ### dxcrm push renew
707
+ Renew expiring push subscriptions (also runs automatically daily at 06:00).
708
+ \`\`\`
709
+ dxcrm push renew --all
710
+ \`\`\`
711
+
712
+ ### register_push_subscription (MCP)
713
+ Register a real-time push subscription. Admin only.
714
+ \`\`\`
715
+ register_push_subscription({ provider: "gmail", slug: "acme-corp", webhookUrl: "https://myserver.com/webhooks/gmail", gmailTopicName: "projects/x/topics/y" })
716
+ \`\`\`
717
+ Returns: { subscriptionId, provider, slug, status, expiresAt, warning? }
718
+
719
+ ### get_push_status (MCP)
720
+ Show all push subscriptions with expiry and event counts.
721
+ \`\`\`
722
+ get_push_status() // all subscriptions
723
+ get_push_status({ slug: "acme-corp" }) // filter by customer
724
+ get_push_status({ provider: "gmail" }) // filter by provider
725
+ \`\`\`
726
+ Returns: { subscriptions: [...], summary: { total, active, expiringSoon, expired } }
727
+
728
+ ### get_org_intelligence (MCP)
729
+ Build a stakeholder map for a customer: champions, economic buyers, blockers, health scores, risk flags, and a prioritised recommendation.
730
+ \`\`\`
731
+ get_org_intelligence({ slug: "acme-corp" })
732
+ get_org_intelligence({ slug: "acme-corp", dealName: "Enterprise License" })
733
+ \`\`\`
734
+ Returns: { slug, updatedAt, people: [{ name, email, role, healthScore, daysSinceContact, contactStrength, riskFlags }], missingRoles, riskAssessment, recommendation }
735
+
736
+ ### open_deal_room (MCP)
737
+ Multi-agent deal brief: orchestrates stakeholder map, relationship health, deal health, Monte Carlo simulation, and playbook matching into a single structured brief.
738
+ \`\`\`
739
+ open_deal_room({ slug: "acme-corp", dealName: "Enterprise License 2026" })
740
+ \`\`\`
741
+ Returns: { slug, dealName, generatedAt, stakeholders, relationshipHealth, dealHealth, revenueSimulation, recommendedPlaybook, executiveSummary, topPriorities, riskScore }
742
+
743
+ ### get_proactive_briefing (MCP)
744
+ Generate a proactive daily briefing: urgent alerts (relationship decay, imminent close dates), opportunities (high-health customers with active pipeline), P50/P90 forecast, and a single top-action recommendation.
745
+ \`\`\`
746
+ get_proactive_briefing() // today
747
+ get_proactive_briefing({ date: "2026-05-28" }) // specific date
748
+ \`\`\`
749
+ Returns: { date, generatedAt, urgent: string[], opportunities: string[], forecast: string, topAction: string }
750
+
751
+ ## H2 — Email Templates
752
+
753
+ ### list_email_templates (MCP)
754
+ List all saved email templates. Returns id, name, category, subject, and body preview.
755
+ \`\`\`
756
+ list_email_templates()
757
+ list_email_templates({ category: "follow-up" })
758
+ \`\`\`
759
+ Returns: { templates: [{ id, name, category, subject, bodyPreview }] }
760
+
761
+ ### get_email_template (MCP)
762
+ Retrieve a single email template with full body and all variables.
763
+ \`\`\`
764
+ get_email_template({ id: "proposal-follow-up" })
765
+ \`\`\`
766
+ Returns: { id, name, category, subject, body, variables: string[] }
767
+
768
+ ### draft_email (MCP)
769
+ Draft a personalized email from a template, substituting variables from customer context.
770
+ \`\`\`
771
+ draft_email({ slug: "acme-corp", templateId: "proposal-follow-up", overrides: { subject: "Following up on your proposal" } })
772
+ \`\`\`
773
+ Returns: { subject, body, suggestedTo, suggestedCc?, variables }
774
+
775
+ ## H1 — Email Sequences
776
+
777
+ ### enroll_in_sequence (MCP)
778
+ Enroll a customer contact in a multi-step email sequence. Steps are sent automatically.
779
+ \`\`\`
780
+ enroll_in_sequence({ slug: "acme-corp", sequenceId: "onboarding-7day", contactEmail: "alice@acme.com" })
781
+ \`\`\`
782
+ Returns: { enrollmentId, slug, sequenceId, contactEmail, enrolledAt, nextStepDue, totalSteps }
783
+
784
+ ### list_sequence_enrollments (MCP)
785
+ List active (and optionally completed) sequence enrollments.
786
+ \`\`\`
787
+ list_sequence_enrollments()
788
+ list_sequence_enrollments({ slug: "acme-corp", status: "active" })
789
+ \`\`\`
790
+ Returns: { enrollments: [{ enrollmentId, slug, sequenceId, contactEmail, currentStep, nextStepDue, status }] }
791
+
792
+ ### unenroll_from_sequence (MCP)
793
+ Remove a contact from an active sequence (marks as cancelled).
794
+ \`\`\`
795
+ unenroll_from_sequence({ enrollmentId: "enr_abc123" })
796
+ \`\`\`
797
+ Returns: { success: boolean, enrollmentId }
798
+
799
+ ### list_sequences (MCP)
800
+ List all defined email sequences with step count and description.
801
+ \`\`\`
802
+ list_sequences()
803
+ \`\`\`
804
+ Returns: { sequences: [{ id, name, description, steps: number, triggerOn? }] }
805
+
806
+ ## H4 — Quotes
807
+
808
+ ### generate_quote (MCP)
809
+ Generate a structured quote document for a customer deal.
810
+ \`\`\`
811
+ generate_quote({ slug: "acme-corp", dealName: "Enterprise License", lineItems: [{ description: "Platform (12 mo)", quantity: 1, unitPrice: 24000 }], validDays: 30 })
812
+ \`\`\`
813
+ Returns: { quoteId, slug, dealName, total, currency, validUntil, markdownTable, fullText }
814
+
815
+ ### get_quote_status (MCP)
816
+ Retrieve a generated quote with full line items and total.
817
+ \`\`\`
818
+ get_quote_status({ quoteId: "Q-2026-001" })
819
+ \`\`\`
820
+ Returns: { quoteId, slug, dealName, lineItems, subtotal, total, validUntil, status }
821
+
822
+ ## H3 — Meeting Scheduler
823
+
824
+ ### get_booking_link (MCP)
825
+ Get a scheduling link for a meeting with a customer. Configure via DXCRM_CALENDLY_URL or per-customer sources.json.
826
+ \`\`\`
827
+ get_booking_link({ slug: "acme-corp", meetingType: "demo" })
828
+ \`\`\`
829
+ Returns: { url, meetingType, calendarProvider, prefillEmail?, note? }
830
+
831
+ ## H6 — Ticket Management
832
+
833
+ ### create_ticket (MCP)
834
+ Create a support ticket. Auto-sets SLA due date: critical=4h, high=24h, medium=72h, low=168h.
835
+ \`\`\`
836
+ create_ticket({ slug: "acme-corp", title: "Login broken", priority: "high", description: "Cannot login since yesterday", assignee: "alice" })
837
+ \`\`\`
838
+ Returns: { ticketId, slug, title, priority, status, slaDue, assignee?, createdAt }
839
+
840
+ ### update_ticket (MCP)
841
+ Update ticket status or assignee.
842
+ \`\`\`
843
+ update_ticket({ slug: "acme-corp", ticketId: "T-001", status: "in-progress", assignee: "bob" })
844
+ \`\`\`
845
+ Returns: { ticketId, status, updatedAt }
846
+
847
+ ### list_tickets (MCP)
848
+ List tickets sorted by priority. Filter by customer, status, priority, or assignee.
849
+ \`\`\`
850
+ list_tickets()
851
+ list_tickets({ slug: "acme-corp", status: "open" })
852
+ list_tickets({ priority: "high", assignee: "alice" })
853
+ \`\`\`
854
+ Returns: { tickets: [{ ticketId, slug, title, priority, status, slaDue, assignee?, createdAt }] }
855
+
856
+ ### close_ticket (MCP)
857
+ Close a ticket and optionally log a resolution note to interactions.md.
858
+ \`\`\`
859
+ close_ticket({ slug: "acme-corp", ticketId: "T-001", resolution: "Fixed by updating oauth token" })
860
+ \`\`\`
861
+ Returns: { ticketId, status: "closed", closedAt, resolution? }
862
+
863
+ ## H7 — NPS/CSAT Survey Engine
864
+
865
+ ### send_nps_survey (MCP)
866
+ Generate a survey token and HTML email body. Customers click a score button (0–10) which
867
+ posts to your server's /survey/respond endpoint. Set DXCRM_SERVER_URL or pass serverUrl.
868
+ \`\`\`
869
+ send_nps_survey({ slug: "acme-corp", contactEmail: "alice@acme.com", surveyId: "q1-nps" })
870
+ send_nps_survey({ slug: "acme-corp", contactEmail: "alice@acme.com", surveyId: "q1-nps", serverUrl: "https://crm.myco.com" })
871
+ \`\`\`
872
+ Returns: { token, emailSubject, emailBody (HTML), surveyId, expiresAt }
873
+
874
+ ### get_survey_results (MCP)
875
+ Calculate NPS score and breakdown by promoter/passive/detractor.
876
+ \`\`\`
877
+ get_survey_results({ surveyId: "q1-nps" })
878
+ get_survey_results({ surveyId: "q1-nps", slug: "acme-corp" })
879
+ \`\`\`
880
+ Returns: { surveyId, npsScore (-100 to 100), responseCount, promoters, passives, detractors, responses: [{ slug, contactEmail, score, comment?, respondedAt }] }
881
+
882
+ ## H8 — Knowledge Base
883
+
884
+ ### search_knowledge_base (MCP)
885
+ Full-text search across all KB articles (title, body, tags).
886
+ \`\`\`
887
+ search_knowledge_base({ query: "password reset" })
888
+ search_knowledge_base({ query: "billing", publicOnly: true })
889
+ \`\`\`
890
+ Returns: { results: [{ id, title, category, excerpt, public, tags }] }
891
+
892
+ ### create_kb_article (MCP)
893
+ Create or update a knowledge base article (upserts by ID).
894
+ \`\`\`
895
+ create_kb_article({ id: "password-reset", title: "How to reset your password", body: "## Steps\\n1. Go to login...", category: "account", tags: ["password", "auth"], public: true })
896
+ \`\`\`
897
+ Returns: { id, title, createdAt, updatedAt, public }
898
+
899
+ ## Enterprise Backup
900
+
901
+ ### backup_now (MCP)
902
+ Trigger an immediate backup of customers/ + .agentic/. Creates a timestamped ZIP with
903
+ SHA-256 manifest. Optionally encrypts (AES-256-GCM) and uploads to S3/rsync/local.
904
+ \`\`\`
905
+ backup_now({})
906
+ backup_now({ remote: "s3://my-bucket/crm-backups/", note: "Pre-migration backup" })
907
+ \`\`\`
908
+ Returns: { path, createdAt, customerCount, fileCount, sizeMb, directories, verified, uploadedTo? }
909
+
910
+ ### list_backups (MCP)
911
+ List available CRM backups with metadata from .agentic/backup-log.json.
912
+ Falls back to directory scan if log unavailable.
913
+ \`\`\`
914
+ list_backups({ limit: 10 })
915
+ \`\`\`
916
+ Returns: { count, totalAvailable, backups: [{ filename, createdAt, sizeMb, verified, encrypted, customerCount, fileCount }] }
917
+
918
+ ### trigger_sync (MCP)
919
+ Force an immediate Gmail sync without waiting for the 30-minute daemon cycle.
920
+ \`\`\`
921
+ trigger_sync({ slug: "acme-corp" }) // sync one customer
922
+ trigger_sync({}) // sync all customers
923
+ trigger_sync({ since: "2026-06-01" }) // sync from specific date
924
+ \`\`\`
925
+ Returns: { success, synced, skipped, customers: [...], errors: [...] }
926
+
927
+ ### get_audit_log (MCP)
928
+ Read the append-only CRM audit log of all write operations.
929
+ \`\`\`
930
+ get_audit_log({}) // last 50 entries
931
+ get_audit_log({ slug: "acme-corp" }) // filtered by customer
932
+ get_audit_log({ actor: "alice", limit: 20 }) // filtered by actor
933
+ \`\`\`
934
+ Returns: { total, returned, entries: [{ timestamp, actor, tool, slug, summary }] }
935
+ `.trim();
936
+ //#endregion
937
+ //#region src/schemas/kb-article.ts
938
+ const KbArticleSchema = z.object({
939
+ id: z.string().min(1),
940
+ title: z.string().min(1),
941
+ category: z.string().default("general"),
942
+ tags: z.array(z.string()).default([]),
943
+ public: z.boolean().default(false),
944
+ createdAt: z.string(),
945
+ updatedAt: z.string(),
946
+ sourceTicketId: z.string().optional()
947
+ });
948
+ //#endregion
949
+ //#region src/fs/knowledge-base.ts
950
+ function kbDir(dataDir) {
951
+ return path.join(dataDir, ".agentic", "knowledge-base");
952
+ }
953
+ function listKbArticles(dataDir, opts) {
954
+ const dir = kbDir(dataDir);
955
+ if (!fs.existsSync(dir)) return [];
956
+ const results = [];
957
+ const categories = fs.readdirSync(dir).filter((f) => {
958
+ try {
959
+ return fs.statSync(path.join(dir, f)).isDirectory();
960
+ } catch {
961
+ return false;
962
+ }
963
+ });
964
+ for (const cat of categories) {
965
+ const catDir = path.join(dir, cat);
966
+ const files = fs.readdirSync(catDir).filter((f) => f.endsWith(".md"));
967
+ for (const file of files) try {
968
+ const parsed = matter(fs.readFileSync(path.join(catDir, file), "utf-8"));
969
+ const meta = KbArticleSchema.safeParse(parsed.data);
970
+ if (!meta.success) continue;
971
+ if (opts?.category && meta.data.category !== opts.category) continue;
972
+ if (opts?.publicOnly && !meta.data.public) continue;
973
+ results.push({
974
+ ...meta.data,
975
+ body: parsed.content.trim()
976
+ });
977
+ } catch {
978
+ continue;
979
+ }
980
+ }
981
+ return results;
982
+ }
983
+ function getKbArticle(dataDir, id) {
984
+ return listKbArticles(dataDir).find((a) => a.id === id) ?? null;
985
+ }
986
+ function writeKbArticle(dataDir, article) {
987
+ const dir = path.join(kbDir(dataDir), article.category);
988
+ fs.mkdirSync(dir, { recursive: true });
989
+ const { body, ...meta } = article;
990
+ const content = matter.stringify(body, meta);
991
+ fs.writeFileSync(path.join(dir, `${article.id}.md`), content, "utf-8");
992
+ }
993
+ function deleteKbArticle(dataDir, id) {
994
+ const article = listKbArticles(dataDir).find((a) => a.id === id);
995
+ if (!article) return false;
996
+ const p = path.join(kbDir(dataDir), article.category, `${id}.md`);
997
+ if (!fs.existsSync(p)) return false;
998
+ fs.unlinkSync(p);
999
+ return true;
1000
+ }
1001
+ function searchKbSimple(dataDir, query, opts) {
1002
+ const all = listKbArticles(dataDir, opts?.publicOnly ? { publicOnly: true } : {});
1003
+ const lower = query.toLowerCase();
1004
+ return all.filter((a) => a.title.toLowerCase().includes(lower) || a.body.toLowerCase().includes(lower) || a.tags.some((t) => t.toLowerCase().includes(lower)));
1005
+ }
1006
+ function getKbMetaForExport(article) {
1007
+ const { body: _body, ...meta } = article;
1008
+ return meta;
1009
+ }
1010
+ //#endregion
1011
+ export { searchKbSimple as a, listKbArticles as i, getKbArticle as n, writeKbArticle as o, getKbMetaForExport as r, CAPABILITIES_TEXT as s, deleteKbArticle as t };
1012
+
1013
+ //# sourceMappingURL=knowledge-base-D0Fh40kc.js.map