@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.
- package/LICENSE +21 -0
- package/README.md +767 -0
- package/dist/agent-config-zPvcqu07.js +14 -0
- package/dist/agent-config-zPvcqu07.js.map +1 -0
- package/dist/approvals-DpjxGHFp.js +67 -0
- package/dist/approvals-DpjxGHFp.js.map +1 -0
- package/dist/ask-CID3jnuL.js +52 -0
- package/dist/ask-CID3jnuL.js.map +1 -0
- package/dist/audit-log-DNMY9mUZ.js +49 -0
- package/dist/audit-log-DNMY9mUZ.js.map +1 -0
- package/dist/auth-CyFuu9X_.js +2 -0
- package/dist/auth-DFWwWcYD.js +93 -0
- package/dist/auth-DFWwWcYD.js.map +1 -0
- package/dist/autofill-Di_-SP7t.js +51 -0
- package/dist/autofill-Di_-SP7t.js.map +1 -0
- package/dist/backup-CeMk9z86.js +417 -0
- package/dist/backup-CeMk9z86.js.map +1 -0
- package/dist/backup-f_hC7rBV.js +2 -0
- package/dist/calendly-Bft_wwji.js +52 -0
- package/dist/calendly-Bft_wwji.js.map +1 -0
- package/dist/calendly-D3coO92o.cjs +53 -0
- package/dist/calendly-D3coO92o.cjs.map +1 -0
- package/dist/chunk-DakpK96I.cjs +43 -0
- package/dist/churn-C28IgnAj.js +54 -0
- package/dist/churn-C28IgnAj.js.map +1 -0
- package/dist/cli.js +4396 -0
- package/dist/cli.js.map +1 -0
- package/dist/colors-BG07TZQz.js +11 -0
- package/dist/colors-BG07TZQz.js.map +1 -0
- package/dist/compliance-B1kk5-YS.js +115 -0
- package/dist/compliance-B1kk5-YS.js.map +1 -0
- package/dist/compliance-B91zNvCR.cjs +156 -0
- package/dist/compliance-B91zNvCR.cjs.map +1 -0
- package/dist/compliance-CKSBoQUe.js +118 -0
- package/dist/compliance-CKSBoQUe.js.map +1 -0
- package/dist/compliance-CujOqAKk.js +2 -0
- package/dist/context-builder-BzWAp3Zs.js +96 -0
- package/dist/context-builder-BzWAp3Zs.js.map +1 -0
- package/dist/context-builder-DlrRcqmJ.js +2 -0
- package/dist/conversation-intel-mm7Lhemh.js +72 -0
- package/dist/conversation-intel-mm7Lhemh.js.map +1 -0
- package/dist/custom-fields-CzNeD3_v.js +2 -0
- package/dist/custom-fields-Pl2t9xzp.js +73 -0
- package/dist/custom-fields-Pl2t9xzp.js.map +1 -0
- package/dist/custom-objects-BHgn1GEX.js +78 -0
- package/dist/custom-objects-BHgn1GEX.js.map +1 -0
- package/dist/custom-objects-CIFrmQ2V.js +2 -0
- package/dist/customer-dir-DIylZ8Q6.js +75 -0
- package/dist/customer-dir-DIylZ8Q6.js.map +1 -0
- package/dist/daemon/worker.js +207 -0
- package/dist/daemon/worker.js.map +1 -0
- package/dist/enrichment-3XvgGDfB.js +103 -0
- package/dist/enrichment-3XvgGDfB.js.map +1 -0
- package/dist/file-lock-B_zi7NQl.js +22 -0
- package/dist/file-lock-B_zi7NQl.js.map +1 -0
- package/dist/gmail-auth-BP6cJwfw.js +40 -0
- package/dist/gmail-auth-BP6cJwfw.js.map +1 -0
- package/dist/gmail-auth-DxakCtGm.cjs +44 -0
- package/dist/gmail-auth-DxakCtGm.cjs.map +1 -0
- package/dist/gmail-auth-OComS92L.js +40 -0
- package/dist/gmail-auth-OComS92L.js.map +1 -0
- package/dist/gmail-push-watch-DELQFMPk.js +20 -0
- package/dist/gmail-push-watch-DELQFMPk.js.map +1 -0
- package/dist/gmail-sender-StTpJ9Ub.js +32 -0
- package/dist/gmail-sender-StTpJ9Ub.js.map +1 -0
- package/dist/gmail-sync-DIaxInDT.js +204 -0
- package/dist/gmail-sync-DIaxInDT.js.map +1 -0
- package/dist/gmail-sync-hHm9gaWd.cjs +218 -0
- package/dist/gmail-sync-hHm9gaWd.cjs.map +1 -0
- package/dist/gmail-sync-rQaVqKWd.js +214 -0
- package/dist/gmail-sync-rQaVqKWd.js.map +1 -0
- package/dist/gmail-webhook-handler-DS7OlRPX.js +3 -0
- package/dist/gmail-webhook-handler-e5Od25FX.js +97 -0
- package/dist/gmail-webhook-handler-e5Od25FX.js.map +1 -0
- package/dist/goal-engine-CUZSpERI.js +2 -0
- package/dist/goal-engine-KpBftn4V.js +295 -0
- package/dist/goal-engine-KpBftn4V.js.map +1 -0
- package/dist/google-drive-sync-DEPcqFca.js +105 -0
- package/dist/google-drive-sync-DEPcqFca.js.map +1 -0
- package/dist/hybrid-search-BmHttLrR.js +40 -0
- package/dist/hybrid-search-BmHttLrR.js.map +1 -0
- package/dist/hygiene-DZqfYpFf.js +38 -0
- package/dist/hygiene-DZqfYpFf.js.map +1 -0
- package/dist/identity-CI6olMNm.js +41 -0
- package/dist/identity-CI6olMNm.js.map +1 -0
- package/dist/identity-gyfWdrcX.js +2 -0
- package/dist/import-hubspot-BaK71U_K.js +588 -0
- package/dist/import-hubspot-BaK71U_K.js.map +1 -0
- package/dist/index-V8BFaH-b.d.ts +539 -0
- package/dist/index-V8BFaH-b.d.ts.map +1 -0
- package/dist/index-YqwMd6aQ.d.cts +538 -0
- package/dist/index-YqwMd6aQ.d.cts.map +1 -0
- package/dist/index.cjs +185 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +538 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +539 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +165 -0
- package/dist/index.js.map +1 -0
- package/dist/interactions-writer-CrPStUll.cjs +77 -0
- package/dist/interactions-writer-CrPStUll.cjs.map +1 -0
- package/dist/interactions-writer-DO3KcSR3.js +52 -0
- package/dist/interactions-writer-DO3KcSR3.js.map +1 -0
- package/dist/interactions-writer-SLHnoEeE.js +46 -0
- package/dist/interactions-writer-SLHnoEeE.js.map +1 -0
- package/dist/interactions-writer-dSPy1XfO.js +2 -0
- package/dist/knowledge-base-D0Fh40kc.js +1013 -0
- package/dist/knowledge-base-D0Fh40kc.js.map +1 -0
- package/dist/lancedb-CCBbpulq.js +2 -0
- package/dist/lancedb-rlvWoPwl.js +98 -0
- package/dist/lancedb-rlvWoPwl.js.map +1 -0
- package/dist/lead-model-BCFzyktm.js +109 -0
- package/dist/lead-model-BCFzyktm.js.map +1 -0
- package/dist/llm-DEjWcqmW.js +2 -0
- package/dist/llm-DvzZqva0.js +372 -0
- package/dist/llm-DvzZqva0.js.map +1 -0
- package/dist/llm-Z8RIYkpF.js +174 -0
- package/dist/llm-Z8RIYkpF.js.map +1 -0
- package/dist/llm-iijeXmgq.cjs +198 -0
- package/dist/llm-iijeXmgq.cjs.map +1 -0
- package/dist/mcp-CdTJWTJf.d.cts +12 -0
- package/dist/mcp-CdTJWTJf.d.cts.map +1 -0
- package/dist/mcp-CdTJWTJf.d.ts +12 -0
- package/dist/mcp-CdTJWTJf.d.ts.map +1 -0
- package/dist/mcp.cjs +7464 -0
- package/dist/mcp.cjs.map +1 -0
- package/dist/mcp.d.cts +12 -0
- package/dist/mcp.d.cts.map +1 -0
- package/dist/mcp.d.ts +12 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +7448 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory-Bb6ky3kb.js +58 -0
- package/dist/memory-Bb6ky3kb.js.map +1 -0
- package/dist/memory-Cy6-Tbyl.js +2 -0
- package/dist/metrics-DH8wHvya.js +26 -0
- package/dist/metrics-DH8wHvya.js.map +1 -0
- package/dist/microsoft-auth-B8_S45gh.js +17 -0
- package/dist/microsoft-auth-B8_S45gh.js.map +1 -0
- package/dist/microsoft-calendar-B6MMtUQK.js +67 -0
- package/dist/microsoft-calendar-B6MMtUQK.js.map +1 -0
- package/dist/microsoft-sync-CpZVoSuq.js +68 -0
- package/dist/microsoft-sync-CpZVoSuq.js.map +1 -0
- package/dist/nba-3wanmJ0U.js +48 -0
- package/dist/nba-3wanmJ0U.js.map +1 -0
- package/dist/notification-dispatcher-0vYNngWe.js +97 -0
- package/dist/notification-dispatcher-0vYNngWe.js.map +1 -0
- package/dist/opportunity-score-BTMOQSTV.js +47 -0
- package/dist/opportunity-score-BTMOQSTV.js.map +1 -0
- package/dist/pipedrive-client-CdGKpH9b.js +17 -0
- package/dist/pipedrive-client-CdGKpH9b.js.map +1 -0
- package/dist/pipeline-writer-BqBrYrQc.js +2 -0
- package/dist/pipeline-writer-BvVquKIe.js +96 -0
- package/dist/pipeline-writer-BvVquKIe.js.map +1 -0
- package/dist/pipeline-writer-N2omexxp.cjs +121 -0
- package/dist/pipeline-writer-N2omexxp.cjs.map +1 -0
- package/dist/pipeline-writer-eufx_0o1.js +102 -0
- package/dist/pipeline-writer-eufx_0o1.js.map +1 -0
- package/dist/proactive-agent-BgQXw3ac.js +96 -0
- package/dist/proactive-agent-BgQXw3ac.js.map +1 -0
- package/dist/proactive-worker-BrLHNhjH.js +229 -0
- package/dist/proactive-worker-BrLHNhjH.js.map +1 -0
- package/dist/push-manager-CdqIIkuh.js +108 -0
- package/dist/push-manager-CdqIIkuh.js.map +1 -0
- package/dist/push-manager-CowY-0IK.js +2 -0
- package/dist/quote-generator-BfwENXzg.js +133 -0
- package/dist/quote-generator-BfwENXzg.js.map +1 -0
- package/dist/quote-generator-OhSFsi3x.js +2 -0
- package/dist/rbac-C7c8tcES.js +2 -0
- package/dist/rbac-CTIktZaC.js +91 -0
- package/dist/rbac-CTIktZaC.js.map +1 -0
- package/dist/relationship-health-odxEoQdJ.js +454 -0
- package/dist/relationship-health-odxEoQdJ.js.map +1 -0
- package/dist/revenue-simulation-BJdRTEHc.js +2 -0
- package/dist/revenue-simulation-Bqf2DLVB.js +251 -0
- package/dist/revenue-simulation-Bqf2DLVB.js.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/salesforce-client-rhZFa_p5.js +51 -0
- package/dist/salesforce-client-rhZFa_p5.js.map +1 -0
- package/dist/segments-BqcD5HIl.js +61 -0
- package/dist/segments-BqcD5HIl.js.map +1 -0
- package/dist/sequence-engine-CCTHEBgi.js +2 -0
- package/dist/sequence-engine-J1lTW_in.js +91 -0
- package/dist/sequence-engine-J1lTW_in.js.map +1 -0
- package/dist/sequence-store-DaaWr0Os.js +221 -0
- package/dist/sequence-store-DaaWr0Os.js.map +1 -0
- package/dist/server-Dyva03K8.js +4287 -0
- package/dist/server-Dyva03K8.js.map +1 -0
- package/dist/session-B9AilxOE.js +81 -0
- package/dist/session-B9AilxOE.js.map +1 -0
- package/dist/session-D0qFkBla.cjs +82 -0
- package/dist/session-D0qFkBla.cjs.map +1 -0
- package/dist/session-D9ub6Wl1.js +79 -0
- package/dist/session-D9ub6Wl1.js.map +1 -0
- package/dist/session-mWHA71Lw.js +2 -0
- package/dist/session-store-B0QZE8Bx.cjs +697 -0
- package/dist/session-store-B0QZE8Bx.cjs.map +1 -0
- package/dist/session-store-C8tEvMPw.js +543 -0
- package/dist/session-store-C8tEvMPw.js.map +1 -0
- package/dist/session-store-CEa39Dxs.js +15 -0
- package/dist/session-store-CEa39Dxs.js.map +1 -0
- package/dist/sla-engine-5IhTsBUR.js +2 -0
- package/dist/sla-engine-BqX-7u-7.js +53 -0
- package/dist/sla-engine-BqX-7u-7.js.map +1 -0
- package/dist/sop-DkhVChGy.js +2 -0
- package/dist/sop-Vp0UPWFW.js +70 -0
- package/dist/sop-Vp0UPWFW.js.map +1 -0
- package/dist/survey-engine-C06hcQt3.js +2 -0
- package/dist/survey-engine-DBjCYqCv.js +147 -0
- package/dist/survey-engine-DBjCYqCv.js.map +1 -0
- package/dist/sync-state-ChaLbamC.js +33 -0
- package/dist/sync-state-ChaLbamC.js.map +1 -0
- package/dist/sync-state-CwLSt_1m.js +2 -0
- package/dist/ticket-writer-CjqKeIRD.js +2 -0
- package/dist/ticket-writer-j2oX_Wal.js +134 -0
- package/dist/ticket-writer-j2oX_Wal.js.map +1 -0
- package/dist/tone-Bdm5uaht.js +48 -0
- package/dist/tone-Bdm5uaht.js.map +1 -0
- package/dist/tone-DRKlZgPr.cjs +43 -0
- package/dist/tone-DRKlZgPr.cjs.map +1 -0
- package/dist/tone-vNb2DAAD.js +39 -0
- package/dist/tone-vNb2DAAD.js.map +1 -0
- package/dist/transcript-watcher-CL2QUygI.js +132 -0
- package/dist/transcript-watcher-CL2QUygI.js.map +1 -0
- package/dist/unmatched-transcripts-BsH5bhkU.js +26 -0
- package/dist/unmatched-transcripts-BsH5bhkU.js.map +1 -0
- package/dist/unmatched-transcripts-D0PrJ9iz.js +2 -0
- package/dist/update-deal-BNwPGaTV.js +2 -0
- package/dist/update-deal-DKC79skb.js +91 -0
- package/dist/update-deal-DKC79skb.js.map +1 -0
- package/dist/usage-CClTf5e6.cjs +57 -0
- package/dist/usage-CClTf5e6.cjs.map +1 -0
- package/dist/usage-D0-TYJkw.js +93 -0
- package/dist/usage-D0-TYJkw.js.map +1 -0
- package/dist/usage-D0u9a-lV.js +54 -0
- package/dist/usage-D0u9a-lV.js.map +1 -0
- package/dist/vault-C1D3zScD.js +2 -0
- package/dist/vault-DXCg29W-.js +86 -0
- package/dist/vault-DXCg29W-.js.map +1 -0
- package/dist/webhooks-7EpA05Qr.js +138 -0
- package/dist/webhooks-7EpA05Qr.js.map +1 -0
- package/dist/webhooks-BO2UAnmn.js +94 -0
- package/dist/webhooks-BO2UAnmn.js.map +1 -0
- package/dist/webhooks-Xn6zO6kd.cjs +97 -0
- package/dist/webhooks-Xn6zO6kd.cjs.map +1 -0
- package/dist/write-queue-BDolUxfs.cjs +26 -0
- package/dist/write-queue-BDolUxfs.cjs.map +1 -0
- package/dist/write-queue-IbsAjUnh.js +21 -0
- package/dist/write-queue-IbsAjUnh.js.map +1 -0
- package/package.json +142 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
//#region src/schemas/pipeline.ts
|
|
5
|
+
const PipelineDealSchema = z.object({
|
|
6
|
+
name: z.string().min(1),
|
|
7
|
+
stage: z.enum([
|
|
8
|
+
"lead",
|
|
9
|
+
"qualified",
|
|
10
|
+
"proposal",
|
|
11
|
+
"negotiation",
|
|
12
|
+
"won",
|
|
13
|
+
"lost"
|
|
14
|
+
]),
|
|
15
|
+
value: z.number().optional(),
|
|
16
|
+
currency: z.string().default("EUR"),
|
|
17
|
+
probability: z.number().min(0).max(100).optional(),
|
|
18
|
+
close_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD required").optional(),
|
|
19
|
+
notes: z.string().optional(),
|
|
20
|
+
updated: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD required")
|
|
21
|
+
});
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/fs/pipeline-writer.ts
|
|
24
|
+
const PIPELINE_HEADER = "# Pipeline\n\n";
|
|
25
|
+
const TABLE_HEADER = `| Name | Stage | Value | Currency | Probability | Close Date | Notes | Updated |
|
|
26
|
+
|------|-------|-------|----------|-------------|------------|-------|---------|`;
|
|
27
|
+
function escapeMd(val) {
|
|
28
|
+
if (val === void 0 || val === null) return "";
|
|
29
|
+
return String(val).replace(/\|/g, "\\|");
|
|
30
|
+
}
|
|
31
|
+
function serializeDeal(deal) {
|
|
32
|
+
return `| ${escapeMd(deal.name)} | ${escapeMd(deal.stage)} | ${deal.value !== void 0 ? String(deal.value) : ""} | ${escapeMd(deal.currency)} | ${deal.probability !== void 0 ? String(deal.probability) : ""} | ${escapeMd(deal.close_date)} | ${escapeMd(deal.notes)} | ${escapeMd(deal.updated)} |`;
|
|
33
|
+
}
|
|
34
|
+
function parseDealsFromMarkdown(content) {
|
|
35
|
+
const lines = content.split("\n");
|
|
36
|
+
const deals = [];
|
|
37
|
+
let inTable = false;
|
|
38
|
+
let headerParsed = false;
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
if (!inTable && trimmed.startsWith("| Name |")) {
|
|
42
|
+
inTable = true;
|
|
43
|
+
headerParsed = false;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (inTable && !headerParsed && trimmed.startsWith("|---")) {
|
|
47
|
+
headerParsed = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (inTable && headerParsed && trimmed.startsWith("|")) {
|
|
51
|
+
const [name, stage, valueStr, currency, probabilityStr, close_date, notes, updated] = trimmed.split("|").slice(1, -1).map((c) => c.trim().replace(/\\\|/g, "|"));
|
|
52
|
+
if (!name || !stage || !updated) continue;
|
|
53
|
+
const raw = {
|
|
54
|
+
name,
|
|
55
|
+
stage,
|
|
56
|
+
currency: currency || "EUR",
|
|
57
|
+
updated
|
|
58
|
+
};
|
|
59
|
+
if (valueStr) raw["value"] = parseFloat(valueStr);
|
|
60
|
+
if (probabilityStr) raw["probability"] = parseFloat(probabilityStr);
|
|
61
|
+
if (close_date) raw["close_date"] = close_date;
|
|
62
|
+
if (notes) raw["notes"] = notes;
|
|
63
|
+
const result = PipelineDealSchema.safeParse(raw);
|
|
64
|
+
if (result.success) deals.push(result.data);
|
|
65
|
+
} else if (inTable && !trimmed.startsWith("|")) inTable = false;
|
|
66
|
+
}
|
|
67
|
+
return deals;
|
|
68
|
+
}
|
|
69
|
+
function serializeDeals(deals) {
|
|
70
|
+
if (deals.length === 0) return `${PIPELINE_HEADER}<!-- Deals listed here -->\n`;
|
|
71
|
+
return `${PIPELINE_HEADER}${TABLE_HEADER}\n${deals.map(serializeDeal).join("\n")}\n`;
|
|
72
|
+
}
|
|
73
|
+
function readPipelineSync(dataDir, slug) {
|
|
74
|
+
const filePath = path.join(dataDir, "customers", slug, "pipeline.md");
|
|
75
|
+
if (!fs.existsSync(filePath)) return [];
|
|
76
|
+
return parseDealsFromMarkdown(fs.readFileSync(filePath, "utf-8"));
|
|
77
|
+
}
|
|
78
|
+
async function readPipeline(dataDir, slug) {
|
|
79
|
+
return readPipelineSync(dataDir, slug);
|
|
80
|
+
}
|
|
81
|
+
async function upsertDeal(dataDir, slug, deal) {
|
|
82
|
+
const filePath = path.join(dataDir, "customers", slug, "pipeline.md");
|
|
83
|
+
const existing = await readPipeline(dataDir, slug);
|
|
84
|
+
const idx = existing.findIndex((d) => d.name === deal.name);
|
|
85
|
+
let updated;
|
|
86
|
+
if (idx >= 0) {
|
|
87
|
+
updated = [...existing];
|
|
88
|
+
updated[idx] = deal;
|
|
89
|
+
} else updated = [...existing, deal];
|
|
90
|
+
const content = serializeDeals(updated);
|
|
91
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { readPipelineSync as n, upsertDeal as r, readPipeline as t };
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=pipeline-writer-BvVquKIe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-writer-BvVquKIe.js","names":[],"sources":["../src/schemas/pipeline.ts","../src/fs/pipeline-writer.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const PipelineDealSchema = z.object({\n name: z.string().min(1),\n stage: z.enum([\"lead\", \"qualified\", \"proposal\", \"negotiation\", \"won\", \"lost\"]),\n value: z.number().optional(),\n currency: z.string().default(\"EUR\"),\n probability: z.number().min(0).max(100).optional(),\n close_date: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n .optional(),\n notes: z.string().optional(),\n updated: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\"),\n});\n\nexport type PipelineDeal = z.infer<typeof PipelineDealSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { PipelineDealSchema, type PipelineDeal } from \"../schemas/pipeline.js\";\n\nconst PIPELINE_HEADER = \"# Pipeline\\n\\n\";\nconst TABLE_HEADER = `| Name | Stage | Value | Currency | Probability | Close Date | Notes | Updated |\n|------|-------|-------|----------|-------------|------------|-------|---------|`;\n\nfunction escapeMd(val: string | undefined | null): string {\n if (val === undefined || val === null) return \"\";\n return String(val).replace(/\\|/g, \"\\\\|\");\n}\n\nfunction serializeDeal(deal: PipelineDeal): string {\n return `| ${escapeMd(deal.name)} | ${escapeMd(deal.stage)} | ${deal.value !== undefined ? String(deal.value) : \"\"} | ${escapeMd(deal.currency)} | ${deal.probability !== undefined ? String(deal.probability) : \"\"} | ${escapeMd(deal.close_date)} | ${escapeMd(deal.notes)} | ${escapeMd(deal.updated)} |`;\n}\n\nfunction parseDealsFromMarkdown(content: string): PipelineDeal[] {\n const lines = content.split(\"\\n\");\n const deals: PipelineDeal[] = [];\n\n let inTable = false;\n let headerParsed = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Look for the table header row\n if (!inTable && trimmed.startsWith(\"| Name |\")) {\n inTable = true;\n headerParsed = false;\n continue;\n }\n\n if (inTable && !headerParsed && trimmed.startsWith(\"|---\")) {\n headerParsed = true;\n continue;\n }\n\n if (inTable && headerParsed && trimmed.startsWith(\"|\")) {\n // Parse a data row\n const cells = trimmed\n .split(\"|\")\n .slice(1, -1)\n .map((c) => c.trim().replace(/\\\\\\|/g, \"|\"));\n\n const [name, stage, valueStr, currency, probabilityStr, close_date, notes, updated] =\n cells as [string, string, string, string, string, string, string, string];\n\n if (!name || !stage || !updated) continue;\n\n const raw: Record<string, unknown> = {\n name,\n stage,\n currency: currency || \"EUR\",\n updated,\n };\n\n if (valueStr) raw[\"value\"] = parseFloat(valueStr);\n if (probabilityStr) raw[\"probability\"] = parseFloat(probabilityStr);\n if (close_date) raw[\"close_date\"] = close_date;\n if (notes) raw[\"notes\"] = notes;\n\n const result = PipelineDealSchema.safeParse(raw);\n if (result.success) {\n deals.push(result.data);\n }\n } else if (inTable && !trimmed.startsWith(\"|\")) {\n // End of table\n inTable = false;\n }\n }\n\n return deals;\n}\n\nfunction serializeDeals(deals: PipelineDeal[]): string {\n if (deals.length === 0) {\n return `${PIPELINE_HEADER}<!-- Deals listed here -->\\n`;\n }\n const rows = deals.map(serializeDeal).join(\"\\n\");\n return `${PIPELINE_HEADER}${TABLE_HEADER}\\n${rows}\\n`;\n}\n\nexport function readPipelineSync(dataDir: string, slug: string): PipelineDeal[] {\n const filePath = path.join(dataDir, \"customers\", slug, \"pipeline.md\");\n if (!fs.existsSync(filePath)) {\n return [];\n }\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n return parseDealsFromMarkdown(content);\n}\n\nexport async function readPipeline(dataDir: string, slug: string): Promise<PipelineDeal[]> {\n return readPipelineSync(dataDir, slug);\n}\n\nexport async function upsertDeal(dataDir: string, slug: string, deal: PipelineDeal): Promise<void> {\n const filePath = path.join(dataDir, \"customers\", slug, \"pipeline.md\");\n const existing = await readPipeline(dataDir, slug);\n\n const idx = existing.findIndex((d) => d.name === deal.name);\n let updated: PipelineDeal[];\n if (idx >= 0) {\n updated = [...existing];\n updated[idx] = deal;\n } else {\n updated = [...existing, deal];\n }\n\n const content = serializeDeals(updated);\n fs.writeFileSync(filePath, content, \"utf-8\");\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
+
let path = require("path");
|
|
3
|
+
path = require_chunk.__toESM(path, 1);
|
|
4
|
+
let fs = require("fs");
|
|
5
|
+
fs = require_chunk.__toESM(fs, 1);
|
|
6
|
+
let zod = require("zod");
|
|
7
|
+
//#region src/schemas/pipeline.ts
|
|
8
|
+
const PipelineDealSchema = zod.z.object({
|
|
9
|
+
name: zod.z.string().min(1),
|
|
10
|
+
stage: zod.z.enum([
|
|
11
|
+
"lead",
|
|
12
|
+
"qualified",
|
|
13
|
+
"proposal",
|
|
14
|
+
"negotiation",
|
|
15
|
+
"won",
|
|
16
|
+
"lost"
|
|
17
|
+
]),
|
|
18
|
+
value: zod.z.number().optional(),
|
|
19
|
+
currency: zod.z.string().default("EUR"),
|
|
20
|
+
probability: zod.z.number().min(0).max(100).optional(),
|
|
21
|
+
close_date: zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD required").optional(),
|
|
22
|
+
notes: zod.z.string().optional(),
|
|
23
|
+
updated: zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD required")
|
|
24
|
+
});
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/fs/pipeline-writer.ts
|
|
27
|
+
var pipeline_writer_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
28
|
+
readPipeline: () => readPipeline,
|
|
29
|
+
readPipelineSync: () => readPipelineSync,
|
|
30
|
+
upsertDeal: () => upsertDeal
|
|
31
|
+
});
|
|
32
|
+
const PIPELINE_HEADER = "# Pipeline\n\n";
|
|
33
|
+
const TABLE_HEADER = `| Name | Stage | Value | Currency | Probability | Close Date | Notes | Updated |
|
|
34
|
+
|------|-------|-------|----------|-------------|------------|-------|---------|`;
|
|
35
|
+
function escapeMd(val) {
|
|
36
|
+
if (val === void 0 || val === null) return "";
|
|
37
|
+
return String(val).replace(/\|/g, "\\|");
|
|
38
|
+
}
|
|
39
|
+
function serializeDeal(deal) {
|
|
40
|
+
return `| ${escapeMd(deal.name)} | ${escapeMd(deal.stage)} | ${deal.value !== void 0 ? String(deal.value) : ""} | ${escapeMd(deal.currency)} | ${deal.probability !== void 0 ? String(deal.probability) : ""} | ${escapeMd(deal.close_date)} | ${escapeMd(deal.notes)} | ${escapeMd(deal.updated)} |`;
|
|
41
|
+
}
|
|
42
|
+
function parseDealsFromMarkdown(content) {
|
|
43
|
+
const lines = content.split("\n");
|
|
44
|
+
const deals = [];
|
|
45
|
+
let inTable = false;
|
|
46
|
+
let headerParsed = false;
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
const trimmed = line.trim();
|
|
49
|
+
if (!inTable && trimmed.startsWith("| Name |")) {
|
|
50
|
+
inTable = true;
|
|
51
|
+
headerParsed = false;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (inTable && !headerParsed && trimmed.startsWith("|---")) {
|
|
55
|
+
headerParsed = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (inTable && headerParsed && trimmed.startsWith("|")) {
|
|
59
|
+
const [name, stage, valueStr, currency, probabilityStr, close_date, notes, updated] = trimmed.split("|").slice(1, -1).map((c) => c.trim().replace(/\\\|/g, "|"));
|
|
60
|
+
if (!name || !stage || !updated) continue;
|
|
61
|
+
const raw = {
|
|
62
|
+
name,
|
|
63
|
+
stage,
|
|
64
|
+
currency: currency || "EUR",
|
|
65
|
+
updated
|
|
66
|
+
};
|
|
67
|
+
if (valueStr) raw["value"] = parseFloat(valueStr);
|
|
68
|
+
if (probabilityStr) raw["probability"] = parseFloat(probabilityStr);
|
|
69
|
+
if (close_date) raw["close_date"] = close_date;
|
|
70
|
+
if (notes) raw["notes"] = notes;
|
|
71
|
+
const result = PipelineDealSchema.safeParse(raw);
|
|
72
|
+
if (result.success) deals.push(result.data);
|
|
73
|
+
} else if (inTable && !trimmed.startsWith("|")) inTable = false;
|
|
74
|
+
}
|
|
75
|
+
return deals;
|
|
76
|
+
}
|
|
77
|
+
function serializeDeals(deals) {
|
|
78
|
+
if (deals.length === 0) return `${PIPELINE_HEADER}<!-- Deals listed here -->\n`;
|
|
79
|
+
return `${PIPELINE_HEADER}${TABLE_HEADER}\n${deals.map(serializeDeal).join("\n")}\n`;
|
|
80
|
+
}
|
|
81
|
+
function readPipelineSync(dataDir, slug) {
|
|
82
|
+
const filePath = path.default.join(dataDir, "customers", slug, "pipeline.md");
|
|
83
|
+
if (!fs.default.existsSync(filePath)) return [];
|
|
84
|
+
return parseDealsFromMarkdown(fs.default.readFileSync(filePath, "utf-8"));
|
|
85
|
+
}
|
|
86
|
+
async function readPipeline(dataDir, slug) {
|
|
87
|
+
return readPipelineSync(dataDir, slug);
|
|
88
|
+
}
|
|
89
|
+
async function upsertDeal(dataDir, slug, deal) {
|
|
90
|
+
const filePath = path.default.join(dataDir, "customers", slug, "pipeline.md");
|
|
91
|
+
const existing = await readPipeline(dataDir, slug);
|
|
92
|
+
const idx = existing.findIndex((d) => d.name === deal.name);
|
|
93
|
+
let updated;
|
|
94
|
+
if (idx >= 0) {
|
|
95
|
+
updated = [...existing];
|
|
96
|
+
updated[idx] = deal;
|
|
97
|
+
} else updated = [...existing, deal];
|
|
98
|
+
const content = serializeDeals(updated);
|
|
99
|
+
fs.default.writeFileSync(filePath, content, "utf-8");
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
Object.defineProperty(exports, "pipeline_writer_exports", {
|
|
103
|
+
enumerable: true,
|
|
104
|
+
get: function() {
|
|
105
|
+
return pipeline_writer_exports;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
Object.defineProperty(exports, "readPipeline", {
|
|
109
|
+
enumerable: true,
|
|
110
|
+
get: function() {
|
|
111
|
+
return readPipeline;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
Object.defineProperty(exports, "upsertDeal", {
|
|
115
|
+
enumerable: true,
|
|
116
|
+
get: function() {
|
|
117
|
+
return upsertDeal;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
//# sourceMappingURL=pipeline-writer-N2omexxp.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-writer-N2omexxp.cjs","names":["z"],"sources":["../src/schemas/pipeline.ts","../src/fs/pipeline-writer.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const PipelineDealSchema = z.object({\n name: z.string().min(1),\n stage: z.enum([\"lead\", \"qualified\", \"proposal\", \"negotiation\", \"won\", \"lost\"]),\n value: z.number().optional(),\n currency: z.string().default(\"EUR\"),\n probability: z.number().min(0).max(100).optional(),\n close_date: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n .optional(),\n notes: z.string().optional(),\n updated: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\"),\n});\n\nexport type PipelineDeal = z.infer<typeof PipelineDealSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { PipelineDealSchema, type PipelineDeal } from \"../schemas/pipeline.js\";\n\nconst PIPELINE_HEADER = \"# Pipeline\\n\\n\";\nconst TABLE_HEADER = `| Name | Stage | Value | Currency | Probability | Close Date | Notes | Updated |\n|------|-------|-------|----------|-------------|------------|-------|---------|`;\n\nfunction escapeMd(val: string | undefined | null): string {\n if (val === undefined || val === null) return \"\";\n return String(val).replace(/\\|/g, \"\\\\|\");\n}\n\nfunction serializeDeal(deal: PipelineDeal): string {\n return `| ${escapeMd(deal.name)} | ${escapeMd(deal.stage)} | ${deal.value !== undefined ? String(deal.value) : \"\"} | ${escapeMd(deal.currency)} | ${deal.probability !== undefined ? String(deal.probability) : \"\"} | ${escapeMd(deal.close_date)} | ${escapeMd(deal.notes)} | ${escapeMd(deal.updated)} |`;\n}\n\nfunction parseDealsFromMarkdown(content: string): PipelineDeal[] {\n const lines = content.split(\"\\n\");\n const deals: PipelineDeal[] = [];\n\n let inTable = false;\n let headerParsed = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Look for the table header row\n if (!inTable && trimmed.startsWith(\"| Name |\")) {\n inTable = true;\n headerParsed = false;\n continue;\n }\n\n if (inTable && !headerParsed && trimmed.startsWith(\"|---\")) {\n headerParsed = true;\n continue;\n }\n\n if (inTable && headerParsed && trimmed.startsWith(\"|\")) {\n // Parse a data row\n const cells = trimmed\n .split(\"|\")\n .slice(1, -1)\n .map((c) => c.trim().replace(/\\\\\\|/g, \"|\"));\n\n const [name, stage, valueStr, currency, probabilityStr, close_date, notes, updated] =\n cells as [string, string, string, string, string, string, string, string];\n\n if (!name || !stage || !updated) continue;\n\n const raw: Record<string, unknown> = {\n name,\n stage,\n currency: currency || \"EUR\",\n updated,\n };\n\n if (valueStr) raw[\"value\"] = parseFloat(valueStr);\n if (probabilityStr) raw[\"probability\"] = parseFloat(probabilityStr);\n if (close_date) raw[\"close_date\"] = close_date;\n if (notes) raw[\"notes\"] = notes;\n\n const result = PipelineDealSchema.safeParse(raw);\n if (result.success) {\n deals.push(result.data);\n }\n } else if (inTable && !trimmed.startsWith(\"|\")) {\n // End of table\n inTable = false;\n }\n }\n\n return deals;\n}\n\nfunction serializeDeals(deals: PipelineDeal[]): string {\n if (deals.length === 0) {\n return `${PIPELINE_HEADER}<!-- Deals listed here -->\\n`;\n }\n const rows = deals.map(serializeDeal).join(\"\\n\");\n return `${PIPELINE_HEADER}${TABLE_HEADER}\\n${rows}\\n`;\n}\n\nexport function readPipelineSync(dataDir: string, slug: string): PipelineDeal[] {\n const filePath = path.join(dataDir, \"customers\", slug, \"pipeline.md\");\n if (!fs.existsSync(filePath)) {\n return [];\n }\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n return parseDealsFromMarkdown(content);\n}\n\nexport async function readPipeline(dataDir: string, slug: string): Promise<PipelineDeal[]> {\n return readPipelineSync(dataDir, slug);\n}\n\nexport async function upsertDeal(dataDir: string, slug: string, deal: PipelineDeal): Promise<void> {\n const filePath = path.join(dataDir, \"customers\", slug, \"pipeline.md\");\n const existing = await readPipeline(dataDir, slug);\n\n const idx = existing.findIndex((d) => d.name === deal.name);\n let updated: PipelineDeal[];\n if (idx >= 0) {\n updated = [...existing];\n updated[idx] = deal;\n } else {\n updated = [...existing, deal];\n }\n\n const content = serializeDeals(updated);\n fs.writeFileSync(filePath, content, \"utf-8\");\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region src/schemas/pipeline.ts
|
|
6
|
+
const PipelineDealSchema = z.object({
|
|
7
|
+
name: z.string().min(1),
|
|
8
|
+
stage: z.enum([
|
|
9
|
+
"lead",
|
|
10
|
+
"qualified",
|
|
11
|
+
"proposal",
|
|
12
|
+
"negotiation",
|
|
13
|
+
"won",
|
|
14
|
+
"lost"
|
|
15
|
+
]),
|
|
16
|
+
value: z.number().optional(),
|
|
17
|
+
currency: z.string().default("EUR"),
|
|
18
|
+
probability: z.number().min(0).max(100).optional(),
|
|
19
|
+
close_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD required").optional(),
|
|
20
|
+
notes: z.string().optional(),
|
|
21
|
+
updated: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "YYYY-MM-DD required")
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/fs/pipeline-writer.ts
|
|
25
|
+
var pipeline_writer_exports = /* @__PURE__ */ __exportAll({
|
|
26
|
+
readPipeline: () => readPipeline,
|
|
27
|
+
readPipelineSync: () => readPipelineSync,
|
|
28
|
+
upsertDeal: () => upsertDeal
|
|
29
|
+
});
|
|
30
|
+
const PIPELINE_HEADER = "# Pipeline\n\n";
|
|
31
|
+
const TABLE_HEADER = `| Name | Stage | Value | Currency | Probability | Close Date | Notes | Updated |
|
|
32
|
+
|------|-------|-------|----------|-------------|------------|-------|---------|`;
|
|
33
|
+
function escapeMd(val) {
|
|
34
|
+
if (val === void 0 || val === null) return "";
|
|
35
|
+
return String(val).replace(/\|/g, "\\|");
|
|
36
|
+
}
|
|
37
|
+
function serializeDeal(deal) {
|
|
38
|
+
return `| ${escapeMd(deal.name)} | ${escapeMd(deal.stage)} | ${deal.value !== void 0 ? String(deal.value) : ""} | ${escapeMd(deal.currency)} | ${deal.probability !== void 0 ? String(deal.probability) : ""} | ${escapeMd(deal.close_date)} | ${escapeMd(deal.notes)} | ${escapeMd(deal.updated)} |`;
|
|
39
|
+
}
|
|
40
|
+
function parseDealsFromMarkdown(content) {
|
|
41
|
+
const lines = content.split("\n");
|
|
42
|
+
const deals = [];
|
|
43
|
+
let inTable = false;
|
|
44
|
+
let headerParsed = false;
|
|
45
|
+
for (const line of lines) {
|
|
46
|
+
const trimmed = line.trim();
|
|
47
|
+
if (!inTable && trimmed.startsWith("| Name |")) {
|
|
48
|
+
inTable = true;
|
|
49
|
+
headerParsed = false;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (inTable && !headerParsed && trimmed.startsWith("|---")) {
|
|
53
|
+
headerParsed = true;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (inTable && headerParsed && trimmed.startsWith("|")) {
|
|
57
|
+
const [name, stage, valueStr, currency, probabilityStr, close_date, notes, updated] = trimmed.split("|").slice(1, -1).map((c) => c.trim().replace(/\\\|/g, "|"));
|
|
58
|
+
if (!name || !stage || !updated) continue;
|
|
59
|
+
const raw = {
|
|
60
|
+
name,
|
|
61
|
+
stage,
|
|
62
|
+
currency: currency || "EUR",
|
|
63
|
+
updated
|
|
64
|
+
};
|
|
65
|
+
if (valueStr) raw["value"] = parseFloat(valueStr);
|
|
66
|
+
if (probabilityStr) raw["probability"] = parseFloat(probabilityStr);
|
|
67
|
+
if (close_date) raw["close_date"] = close_date;
|
|
68
|
+
if (notes) raw["notes"] = notes;
|
|
69
|
+
const result = PipelineDealSchema.safeParse(raw);
|
|
70
|
+
if (result.success) deals.push(result.data);
|
|
71
|
+
} else if (inTable && !trimmed.startsWith("|")) inTable = false;
|
|
72
|
+
}
|
|
73
|
+
return deals;
|
|
74
|
+
}
|
|
75
|
+
function serializeDeals(deals) {
|
|
76
|
+
if (deals.length === 0) return `${PIPELINE_HEADER}<!-- Deals listed here -->\n`;
|
|
77
|
+
return `${PIPELINE_HEADER}${TABLE_HEADER}\n${deals.map(serializeDeal).join("\n")}\n`;
|
|
78
|
+
}
|
|
79
|
+
function readPipelineSync(dataDir, slug) {
|
|
80
|
+
const filePath = path.join(dataDir, "customers", slug, "pipeline.md");
|
|
81
|
+
if (!fs.existsSync(filePath)) return [];
|
|
82
|
+
return parseDealsFromMarkdown(fs.readFileSync(filePath, "utf-8"));
|
|
83
|
+
}
|
|
84
|
+
async function readPipeline(dataDir, slug) {
|
|
85
|
+
return readPipelineSync(dataDir, slug);
|
|
86
|
+
}
|
|
87
|
+
async function upsertDeal(dataDir, slug, deal) {
|
|
88
|
+
const filePath = path.join(dataDir, "customers", slug, "pipeline.md");
|
|
89
|
+
const existing = await readPipeline(dataDir, slug);
|
|
90
|
+
const idx = existing.findIndex((d) => d.name === deal.name);
|
|
91
|
+
let updated;
|
|
92
|
+
if (idx >= 0) {
|
|
93
|
+
updated = [...existing];
|
|
94
|
+
updated[idx] = deal;
|
|
95
|
+
} else updated = [...existing, deal];
|
|
96
|
+
const content = serializeDeals(updated);
|
|
97
|
+
fs.writeFileSync(filePath, content, "utf-8");
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { readPipeline as n, upsertDeal as r, pipeline_writer_exports as t };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=pipeline-writer-eufx_0o1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-writer-eufx_0o1.js","names":[],"sources":["../src/schemas/pipeline.ts","../src/fs/pipeline-writer.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const PipelineDealSchema = z.object({\n name: z.string().min(1),\n stage: z.enum([\"lead\", \"qualified\", \"proposal\", \"negotiation\", \"won\", \"lost\"]),\n value: z.number().optional(),\n currency: z.string().default(\"EUR\"),\n probability: z.number().min(0).max(100).optional(),\n close_date: z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\")\n .optional(),\n notes: z.string().optional(),\n updated: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/, \"YYYY-MM-DD required\"),\n});\n\nexport type PipelineDeal = z.infer<typeof PipelineDealSchema>;\n","import fs from \"fs\";\nimport path from \"path\";\nimport { PipelineDealSchema, type PipelineDeal } from \"../schemas/pipeline.js\";\n\nconst PIPELINE_HEADER = \"# Pipeline\\n\\n\";\nconst TABLE_HEADER = `| Name | Stage | Value | Currency | Probability | Close Date | Notes | Updated |\n|------|-------|-------|----------|-------------|------------|-------|---------|`;\n\nfunction escapeMd(val: string | undefined | null): string {\n if (val === undefined || val === null) return \"\";\n return String(val).replace(/\\|/g, \"\\\\|\");\n}\n\nfunction serializeDeal(deal: PipelineDeal): string {\n return `| ${escapeMd(deal.name)} | ${escapeMd(deal.stage)} | ${deal.value !== undefined ? String(deal.value) : \"\"} | ${escapeMd(deal.currency)} | ${deal.probability !== undefined ? String(deal.probability) : \"\"} | ${escapeMd(deal.close_date)} | ${escapeMd(deal.notes)} | ${escapeMd(deal.updated)} |`;\n}\n\nfunction parseDealsFromMarkdown(content: string): PipelineDeal[] {\n const lines = content.split(\"\\n\");\n const deals: PipelineDeal[] = [];\n\n let inTable = false;\n let headerParsed = false;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Look for the table header row\n if (!inTable && trimmed.startsWith(\"| Name |\")) {\n inTable = true;\n headerParsed = false;\n continue;\n }\n\n if (inTable && !headerParsed && trimmed.startsWith(\"|---\")) {\n headerParsed = true;\n continue;\n }\n\n if (inTable && headerParsed && trimmed.startsWith(\"|\")) {\n // Parse a data row\n const cells = trimmed\n .split(\"|\")\n .slice(1, -1)\n .map((c) => c.trim().replace(/\\\\\\|/g, \"|\"));\n\n const [name, stage, valueStr, currency, probabilityStr, close_date, notes, updated] =\n cells as [string, string, string, string, string, string, string, string];\n\n if (!name || !stage || !updated) continue;\n\n const raw: Record<string, unknown> = {\n name,\n stage,\n currency: currency || \"EUR\",\n updated,\n };\n\n if (valueStr) raw[\"value\"] = parseFloat(valueStr);\n if (probabilityStr) raw[\"probability\"] = parseFloat(probabilityStr);\n if (close_date) raw[\"close_date\"] = close_date;\n if (notes) raw[\"notes\"] = notes;\n\n const result = PipelineDealSchema.safeParse(raw);\n if (result.success) {\n deals.push(result.data);\n }\n } else if (inTable && !trimmed.startsWith(\"|\")) {\n // End of table\n inTable = false;\n }\n }\n\n return deals;\n}\n\nfunction serializeDeals(deals: PipelineDeal[]): string {\n if (deals.length === 0) {\n return `${PIPELINE_HEADER}<!-- Deals listed here -->\\n`;\n }\n const rows = deals.map(serializeDeal).join(\"\\n\");\n return `${PIPELINE_HEADER}${TABLE_HEADER}\\n${rows}\\n`;\n}\n\nexport function readPipelineSync(dataDir: string, slug: string): PipelineDeal[] {\n const filePath = path.join(dataDir, \"customers\", slug, \"pipeline.md\");\n if (!fs.existsSync(filePath)) {\n return [];\n }\n const content = fs.readFileSync(filePath, \"utf-8\") as string;\n return parseDealsFromMarkdown(content);\n}\n\nexport async function readPipeline(dataDir: string, slug: string): Promise<PipelineDeal[]> {\n return readPipelineSync(dataDir, slug);\n}\n\nexport async function upsertDeal(dataDir: string, slug: string, deal: PipelineDeal): Promise<void> {\n const filePath = path.join(dataDir, \"customers\", slug, \"pipeline.md\");\n const existing = await readPipeline(dataDir, slug);\n\n const idx = existing.findIndex((d) => d.name === deal.name);\n let updated: PipelineDeal[];\n if (idx >= 0) {\n updated = [...existing];\n updated[idx] = deal;\n } else {\n updated = [...existing, deal];\n }\n\n const content = serializeDeals(updated);\n fs.writeFileSync(filePath, content, \"utf-8\");\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { r as listCustomerSlugs } from "./customer-dir-DIylZ8Q6.js";
|
|
2
|
+
import { t as withJsonFile } from "./file-lock-B_zi7NQl.js";
|
|
3
|
+
import { i as buildSimulationInput, l as runSimulation } from "./revenue-simulation-Bqf2DLVB.js";
|
|
4
|
+
import { t as readPipeline } from "./pipeline-writer-BvVquKIe.js";
|
|
5
|
+
import { n as readHealth, t as computeCustomerHealth } from "./relationship-health-odxEoQdJ.js";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
//#region src/core/proactive-agent.ts
|
|
9
|
+
function queuePath(dataDir) {
|
|
10
|
+
return path.join(dataDir, ".agentic", "agent-queue.json");
|
|
11
|
+
}
|
|
12
|
+
function readQueue(dataDir) {
|
|
13
|
+
const p = queuePath(dataDir);
|
|
14
|
+
if (!fs.existsSync(p)) return [];
|
|
15
|
+
try {
|
|
16
|
+
const raw = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
17
|
+
return Array.isArray(raw) ? raw : [];
|
|
18
|
+
} catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async function enqueueTask(dataDir, task) {
|
|
23
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24
|
+
const newTask = {
|
|
25
|
+
...task,
|
|
26
|
+
id: `task_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`,
|
|
27
|
+
createdAt: now,
|
|
28
|
+
status: "pending"
|
|
29
|
+
};
|
|
30
|
+
await withJsonFile(queuePath(dataDir), (current) => {
|
|
31
|
+
return [...Array.isArray(current) ? current : [], newTask];
|
|
32
|
+
});
|
|
33
|
+
return newTask;
|
|
34
|
+
}
|
|
35
|
+
async function markTaskDone(dataDir, taskId, result) {
|
|
36
|
+
await withJsonFile(queuePath(dataDir), (current) => {
|
|
37
|
+
const tasks = Array.isArray(current) ? [...current] : [];
|
|
38
|
+
const idx = tasks.findIndex((t) => t.id === taskId);
|
|
39
|
+
if (idx >= 0) tasks[idx] = {
|
|
40
|
+
...tasks[idx],
|
|
41
|
+
status: "done",
|
|
42
|
+
...result ? { result } : {}
|
|
43
|
+
};
|
|
44
|
+
return tasks;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async function buildDailyBriefing(dataDir, today) {
|
|
48
|
+
const slugs = listCustomerSlugs(dataDir);
|
|
49
|
+
const urgent = [];
|
|
50
|
+
const opportunities = [];
|
|
51
|
+
const todayDate = /* @__PURE__ */ new Date(`${today}T00:00:00Z`);
|
|
52
|
+
const customerData = await Promise.all(slugs.map(async (slug) => {
|
|
53
|
+
return {
|
|
54
|
+
slug,
|
|
55
|
+
health: readHealth(dataDir, slug) ?? computeCustomerHealth(dataDir, slug, today),
|
|
56
|
+
deals: await readPipeline(dataDir, slug).catch(() => [])
|
|
57
|
+
};
|
|
58
|
+
}));
|
|
59
|
+
for (const { slug, health, deals } of customerData) {
|
|
60
|
+
for (const contact of health.contacts) if (contact.riskFlags.includes("NO_CONTACT_30D")) urgent.push(`${slug}: ${contact.name} has been silent for ${contact.daysSinceContact} days — health ${contact.score}/100`);
|
|
61
|
+
const activeDeals = deals.filter((d) => d.stage !== "won" && d.stage !== "lost");
|
|
62
|
+
if (health.overallHealth >= 65 && activeDeals.length > 0) opportunities.push(`${slug}: relationship health ${health.overallHealth}/100 with ${activeDeals.length} active deal(s) — good time for expansion or upsell.`);
|
|
63
|
+
for (const deal of deals) {
|
|
64
|
+
if (deal.stage === "won" || deal.stage === "lost") continue;
|
|
65
|
+
if (deal.close_date && deal.close_date.trim() !== "") {
|
|
66
|
+
const daysToClose = Math.floor((new Date(deal.close_date).getTime() - todayDate.getTime()) / 864e5);
|
|
67
|
+
if (daysToClose <= 7 && daysToClose >= 0) urgent.push(`${slug}: Deal "${deal.name}" closes in ${daysToClose} day(s) — ${deal.stage}`);
|
|
68
|
+
else if (daysToClose < 0) urgent.push(`${slug}: Deal "${deal.name}" close date passed (${Math.abs(daysToClose)} days overdue)`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
let forecast = "No active pipeline.";
|
|
73
|
+
try {
|
|
74
|
+
const simInput = await buildSimulationInput(dataDir, "quarter", today);
|
|
75
|
+
if (simInput.deals.length > 0) {
|
|
76
|
+
const sim = runSimulation({
|
|
77
|
+
...simInput,
|
|
78
|
+
iterations: 1e3
|
|
79
|
+
});
|
|
80
|
+
forecast = `Q forecast: P50 €${(sim.p50 / 1e3).toFixed(1)}k / P90 €${(sim.p90 / 1e3).toFixed(1)}k — ${simInput.deals.length} deal(s) in pipeline.`;
|
|
81
|
+
}
|
|
82
|
+
} catch {}
|
|
83
|
+
const topAction = urgent.length > 0 ? urgent[0].replace(/^[^:]+: /, "").split("—")[0]?.trim() ?? urgent[0] : "Review your pipeline and schedule next customer check-ins.";
|
|
84
|
+
return {
|
|
85
|
+
date: today,
|
|
86
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
87
|
+
urgent,
|
|
88
|
+
opportunities,
|
|
89
|
+
forecast,
|
|
90
|
+
topAction
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { readQueue as i, enqueueTask as n, markTaskDone as r, buildDailyBriefing as t };
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=proactive-agent-BgQXw3ac.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proactive-agent-BgQXw3ac.js","names":[],"sources":["../src/core/proactive-agent.ts"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport { withJsonFile } from \"./file-lock.js\";\nimport { computeCustomerHealth, readHealth } from \"./relationship-health.js\";\nimport { readPipeline } from \"../fs/pipeline-writer.js\";\nimport { listCustomerSlugs } from \"../fs/customer-dir.js\";\nimport { buildSimulationInput, runSimulation } from \"./revenue-simulation.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type TaskType =\n | \"daily_briefing\"\n | \"relationship_decay_alert\"\n | \"deal_risk_alert\"\n | \"external_signal_alert\"\n | \"follow_up_nudge\"\n | \"goal_progress_update\"\n | \"pipeline_forecast_weekly\"\n | \"playbook_suggestion\";\n\nexport type NotificationChannel = \"telegram\" | \"slack\" | \"email\" | \"mcp_tool_response\";\nexport type TaskPriority = \"urgent\" | \"high\" | \"normal\";\nexport type TaskStatus = \"pending\" | \"processing\" | \"done\" | \"failed\";\n\nexport interface AgentTask {\n id: string;\n type: TaskType;\n slug?: string;\n priority: TaskPriority;\n payload: unknown;\n createdAt: string;\n scheduledFor: string;\n status: TaskStatus;\n result?: string;\n channel: NotificationChannel;\n}\n\nexport interface DailyBriefing {\n date: string;\n generatedAt: string;\n urgent: string[];\n opportunities: string[];\n forecast: string;\n topAction: string;\n}\n\n// ─── Queue path ───────────────────────────────────────────────────────────────\n\nfunction queuePath(dataDir: string): string {\n return path.join(dataDir, \".agentic\", \"agent-queue.json\");\n}\n\n// ─── Queue operations ─────────────────────────────────────────────────────────\n\nexport function readQueue(dataDir: string): AgentTask[] {\n const p = queuePath(dataDir);\n if (!fs.existsSync(p)) return [];\n try {\n const raw = JSON.parse(fs.readFileSync(p, \"utf-8\") as string) as unknown;\n return Array.isArray(raw) ? (raw as AgentTask[]) : [];\n } catch {\n return [];\n }\n}\n\nexport async function enqueueTask(\n dataDir: string,\n task: Omit<AgentTask, \"id\" | \"createdAt\" | \"status\">\n): Promise<AgentTask> {\n const now = new Date().toISOString();\n const newTask: AgentTask = {\n ...task,\n id: `task_${Date.now()}_${Math.random().toString(16).slice(2, 8)}`,\n createdAt: now,\n status: \"pending\",\n };\n\n await withJsonFile<AgentTask[]>(queuePath(dataDir), (current) => {\n const existing = Array.isArray(current) ? current : [];\n return [...existing, newTask];\n });\n\n return newTask;\n}\n\nexport async function markTaskDone(\n dataDir: string,\n taskId: string,\n result?: string\n): Promise<void> {\n await withJsonFile<AgentTask[]>(queuePath(dataDir), (current) => {\n const tasks = Array.isArray(current) ? [...current] : [];\n const idx = tasks.findIndex((t) => t.id === taskId);\n if (idx >= 0) {\n tasks[idx] = { ...tasks[idx]!, status: \"done\", ...(result ? { result } : {}) };\n }\n return tasks;\n });\n}\n\n// ─── Daily briefing ───────────────────────────────────────────────────────────\n\nexport async function buildDailyBriefing(dataDir: string, today: string): Promise<DailyBriefing> {\n const slugs = listCustomerSlugs(dataDir);\n\n const urgent: string[] = [];\n const opportunities: string[] = [];\n const todayDate = new Date(`${today}T00:00:00Z`);\n\n // Parallel I/O across all customers\n const customerData = await Promise.all(\n slugs.map(async (slug) => {\n const cached = readHealth(dataDir, slug);\n const health = cached ?? computeCustomerHealth(dataDir, slug, today);\n const deals = await readPipeline(dataDir, slug).catch(() => []);\n return { slug, health, deals };\n })\n );\n\n for (const { slug, health, deals } of customerData) {\n // Relationship decay alerts\n for (const contact of health.contacts) {\n if (contact.riskFlags.includes(\"NO_CONTACT_30D\")) {\n urgent.push(\n `${slug}: ${contact.name} has been silent for ${contact.daysSinceContact} days — health ${contact.score}/100`\n );\n }\n }\n\n // Opportunities — B-grade+ relationship health with active deals\n const activeDeals = deals.filter((d) => d.stage !== \"won\" && d.stage !== \"lost\");\n if (health.overallHealth >= 65 && activeDeals.length > 0) {\n opportunities.push(\n `${slug}: relationship health ${health.overallHealth}/100 with ${activeDeals.length} active deal(s) — good time for expansion or upsell.`\n );\n }\n\n // Deal risk alerts\n for (const deal of deals) {\n if (deal.stage === \"won\" || deal.stage === \"lost\") continue;\n if (deal.close_date && deal.close_date.trim() !== \"\") {\n const daysToClose = Math.floor(\n (new Date(deal.close_date).getTime() - todayDate.getTime()) / 86_400_000\n );\n if (daysToClose <= 7 && daysToClose >= 0) {\n urgent.push(\n `${slug}: Deal \"${deal.name}\" closes in ${daysToClose} day(s) — ${deal.stage}`\n );\n } else if (daysToClose < 0) {\n urgent.push(\n `${slug}: Deal \"${deal.name}\" close date passed (${Math.abs(daysToClose)} days overdue)`\n );\n }\n }\n }\n }\n\n // Revenue forecast\n let forecast = \"No active pipeline.\";\n try {\n const simInput = await buildSimulationInput(dataDir, \"quarter\", today);\n if (simInput.deals.length > 0) {\n const sim = runSimulation({ ...simInput, iterations: 1000 });\n forecast = `Q forecast: P50 €${(sim.p50 / 1000).toFixed(1)}k / P90 €${(sim.p90 / 1000).toFixed(1)}k — ${simInput.deals.length} deal(s) in pipeline.`;\n }\n } catch {\n // forecast stays as default\n }\n\n // Top action\n const topAction =\n urgent.length > 0\n ? (urgent[0]!\n .replace(/^[^:]+: /, \"\")\n .split(\"—\")[0]\n ?.trim() ?? urgent[0]!)\n : \"Review your pipeline and schedule next customer check-ins.\";\n\n return {\n date: today,\n generatedAt: new Date().toISOString(),\n urgent,\n opportunities,\n forecast,\n topAction,\n };\n}\n"],"mappings":"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"}
|