salesprompter-cli 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/dist/engine.js ADDED
@@ -0,0 +1,147 @@
1
+ import { z } from "zod";
2
+ import { SAMPLE_COMPANIES, SAMPLE_CONTACTS, SAMPLE_SIGNALS, SAMPLE_TECH } from "./sample-data.js";
3
+ function pickByIndex(items, index) {
4
+ const item = items[index % items.length];
5
+ if (item === undefined) {
6
+ throw new Error("sample data invariant violated");
7
+ }
8
+ return item;
9
+ }
10
+ function normalizeCompanySize(employeeCount) {
11
+ if (employeeCount < 50) {
12
+ return "1-49";
13
+ }
14
+ if (employeeCount < 200) {
15
+ return "50-199";
16
+ }
17
+ if (employeeCount < 500) {
18
+ return "200-499";
19
+ }
20
+ return "500+";
21
+ }
22
+ function deriveCompanyNameFromDomain(domain) {
23
+ const normalizedDomain = domain
24
+ .toLowerCase()
25
+ .replace(/^https?:\/\//, "")
26
+ .replace(/^www\./, "")
27
+ .split("/")[0];
28
+ const hostname = normalizedDomain.split(".")[0] ?? normalizedDomain;
29
+ return hostname
30
+ .split(/[-_]/)
31
+ .filter((part) => part.length > 0)
32
+ .map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`)
33
+ .join(" ");
34
+ }
35
+ function buildTargetCompany(target, fallback, icp) {
36
+ const domain = target.companyDomain?.trim().toLowerCase();
37
+ if (domain === undefined) {
38
+ return fallback;
39
+ }
40
+ return {
41
+ companyName: target.companyName?.trim() || deriveCompanyNameFromDomain(domain),
42
+ domain,
43
+ industry: icp.industries[0] ?? fallback.industry,
44
+ region: icp.regions[0] ?? fallback.region,
45
+ employeeCount: fallback.employeeCount
46
+ };
47
+ }
48
+ export class HeuristicLeadProvider {
49
+ async generateLeads(icp, count, target = {}) {
50
+ return Array.from({ length: count }, (_, index) => {
51
+ const fallbackCompany = pickByIndex(SAMPLE_COMPANIES, index);
52
+ const company = buildTargetCompany(target, fallbackCompany, icp);
53
+ const contact = pickByIndex(SAMPLE_CONTACTS, index + 1);
54
+ const signals = [pickByIndex(SAMPLE_SIGNALS, index), pickByIndex(SAMPLE_SIGNALS, index + 2)];
55
+ const industry = icp.industries[index % Math.max(icp.industries.length, 1)] ?? company.industry;
56
+ const region = icp.regions[index % Math.max(icp.regions.length, 1)] ?? company.region;
57
+ const title = icp.titles[index % Math.max(icp.titles.length, 1)] ?? contact.title;
58
+ return {
59
+ companyName: company.companyName,
60
+ domain: company.domain,
61
+ industry,
62
+ region,
63
+ employeeCount: company.employeeCount,
64
+ contactName: contact.contactName,
65
+ title,
66
+ email: `${contact.contactName.toLowerCase().replaceAll(" ", ".")}@${company.domain}`,
67
+ source: target.companyDomain ? "heuristic-target-account" : "heuristic-seed",
68
+ signals
69
+ };
70
+ });
71
+ }
72
+ }
73
+ export class HeuristicEnrichmentProvider {
74
+ async enrichLeads(leads) {
75
+ return leads.map((lead, index) => ({
76
+ ...lead,
77
+ techStack: [pickByIndex(SAMPLE_TECH, index), pickByIndex(SAMPLE_TECH, index + 3)],
78
+ crmFit: lead.employeeCount > 200 ? "high" : lead.employeeCount > 100 ? "medium" : "low",
79
+ outreachFit: lead.signals.some((signal) => signal.includes("outbound")) ? "high" : "medium",
80
+ buyingStage: lead.signals.some((signal) => signal.includes("funding")) ? "active-evaluation" : "solution-aware",
81
+ notes: [
82
+ `${lead.companyName} matches the ${lead.industry} segment.`,
83
+ `${lead.contactName} is likely close to revenue tooling decisions.`
84
+ ]
85
+ }));
86
+ }
87
+ }
88
+ export class HeuristicScoringProvider {
89
+ async scoreLeads(icp, leads) {
90
+ return leads.map((lead) => {
91
+ const rationale = [];
92
+ let score = 40;
93
+ if (icp.industries.includes(lead.industry)) {
94
+ score += 20;
95
+ rationale.push("Industry matches ICP.");
96
+ }
97
+ if (icp.regions.includes(lead.region)) {
98
+ score += 10;
99
+ rationale.push("Region matches ICP.");
100
+ }
101
+ const normalizedSize = normalizeCompanySize(lead.employeeCount);
102
+ if (icp.companySizes.includes(normalizedSize)) {
103
+ score += 10;
104
+ rationale.push("Company size matches ICP.");
105
+ }
106
+ if (icp.titles.includes(lead.title)) {
107
+ score += 10;
108
+ rationale.push("Contact title matches ICP.");
109
+ }
110
+ const requiredMatches = icp.requiredSignals.filter((signal) => lead.signals.includes(signal));
111
+ score += Math.min(requiredMatches.length * 5, 10);
112
+ if (requiredMatches.length > 0) {
113
+ rationale.push(`Matched ${requiredMatches.length} required buying signals.`);
114
+ }
115
+ const excludedMatches = icp.excludedSignals.filter((signal) => lead.signals.includes(signal));
116
+ score -= excludedMatches.length * 15;
117
+ if (excludedMatches.length > 0) {
118
+ rationale.push(`Matched ${excludedMatches.length} excluded signals.`);
119
+ }
120
+ if (lead.crmFit === "high") {
121
+ score += 5;
122
+ rationale.push("Strong CRM fit.");
123
+ }
124
+ if (lead.outreachFit === "high") {
125
+ score += 5;
126
+ rationale.push("Strong outreach fit.");
127
+ }
128
+ const clampedScore = z.number().int().min(0).max(100).parse(score);
129
+ const grade = clampedScore >= 85 ? "A" : clampedScore >= 70 ? "B" : clampedScore >= 55 ? "C" : "D";
130
+ return {
131
+ ...lead,
132
+ score: clampedScore,
133
+ grade,
134
+ rationale
135
+ };
136
+ });
137
+ }
138
+ }
139
+ export class DryRunSyncProvider {
140
+ async sync(target, leads) {
141
+ return {
142
+ target,
143
+ synced: leads.length,
144
+ dryRun: true
145
+ };
146
+ }
147
+ }
package/dist/io.js ADDED
@@ -0,0 +1,17 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export async function readJsonFile(filePath, schema) {
4
+ const content = await readFile(filePath, "utf8");
5
+ const parsed = JSON.parse(content);
6
+ return schema.parse(parsed);
7
+ }
8
+ export async function writeJsonFile(filePath, value) {
9
+ await mkdir(path.dirname(filePath), { recursive: true });
10
+ await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
11
+ }
12
+ export function splitCsv(value) {
13
+ return value
14
+ .split(",")
15
+ .map((entry) => entry.trim())
16
+ .filter((entry) => entry.length > 0);
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ export const SAMPLE_COMPANIES = [
2
+ { companyName: "Northstar Freight", domain: "northstarfreight.com", industry: "Logistics", region: "North America", employeeCount: 180 },
3
+ { companyName: "Brightpath Health", domain: "brightpathhealth.io", industry: "Healthcare", region: "Europe", employeeCount: 320 },
4
+ { companyName: "ForgeOps Cloud", domain: "forgeopscloud.dev", industry: "Software", region: "North America", employeeCount: 85 },
5
+ { companyName: "Summit Retail Group", domain: "summitretailgroup.com", industry: "Retail", region: "Europe", employeeCount: 540 },
6
+ { companyName: "Atlas Industrial", domain: "atlasindustrial.co", industry: "Manufacturing", region: "North America", employeeCount: 260 },
7
+ { companyName: "Meridian Finance", domain: "meridianfinance.ai", industry: "Financial Services", region: "Europe", employeeCount: 140 }
8
+ ];
9
+ export const SAMPLE_CONTACTS = [
10
+ { contactName: "Avery Chen", title: "VP Sales" },
11
+ { contactName: "Jordan Patel", title: "Head of Revenue Operations" },
12
+ { contactName: "Taylor Morgan", title: "Director of Growth" },
13
+ { contactName: "Morgan Diaz", title: "Chief Revenue Officer" },
14
+ { contactName: "Cameron Lee", title: "Sales Operations Manager" },
15
+ { contactName: "Riley Brooks", title: "Demand Generation Lead" }
16
+ ];
17
+ export const SAMPLE_SIGNALS = [
18
+ "hiring sales reps",
19
+ "recent funding",
20
+ "expanding into new regions",
21
+ "using fragmented sales tooling",
22
+ "growing outbound team",
23
+ "launching new product line"
24
+ ];
25
+ export const SAMPLE_TECH = [
26
+ "HubSpot",
27
+ "Salesforce",
28
+ "Apollo",
29
+ "Instantly",
30
+ "Outreach",
31
+ "Clay",
32
+ "Segment",
33
+ "PostHog"
34
+ ];
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { z } from "zod";
4
+ import { EnrichedLeadSchema, IcpSchema, LeadSchema, ScoredLeadSchema, SyncTargetSchema } from "./domain.js";
5
+ import { DryRunSyncProvider, HeuristicEnrichmentProvider, HeuristicLeadProvider, HeuristicScoringProvider } from "./engine.js";
6
+ import { readJsonFile, splitCsv, writeJsonFile } from "./io.js";
7
+ const program = new Command();
8
+ const leadProvider = new HeuristicLeadProvider();
9
+ const enrichmentProvider = new HeuristicEnrichmentProvider();
10
+ const scoringProvider = new HeuristicScoringProvider();
11
+ const syncProvider = new DryRunSyncProvider();
12
+ function printOutput(value) {
13
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
14
+ }
15
+ program
16
+ .name("salesprompter")
17
+ .description("Sales workflow CLI for ICP definition, lead generation, enrichment, scoring, and sync.")
18
+ .version("0.1.0");
19
+ program
20
+ .command("icp:define")
21
+ .description("Define an ideal customer profile and write it to a JSON file.")
22
+ .requiredOption("--name <name>", "Human-readable ICP name")
23
+ .option("--industries <items>", "Comma-separated industries", "")
24
+ .option("--company-sizes <items>", "Comma-separated buckets like 1-49,50-199,200-499,500+", "")
25
+ .option("--regions <items>", "Comma-separated regions", "")
26
+ .option("--titles <items>", "Comma-separated titles", "")
27
+ .option("--pains <items>", "Comma-separated pain points", "")
28
+ .option("--required-signals <items>", "Comma-separated required signals", "")
29
+ .option("--excluded-signals <items>", "Comma-separated excluded signals", "")
30
+ .requiredOption("--out <path>", "Output file path")
31
+ .action(async (options) => {
32
+ const icp = IcpSchema.parse({
33
+ name: options.name,
34
+ industries: splitCsv(options.industries),
35
+ companySizes: splitCsv(options.companySizes),
36
+ regions: splitCsv(options.regions),
37
+ titles: splitCsv(options.titles),
38
+ pains: splitCsv(options.pains),
39
+ requiredSignals: splitCsv(options.requiredSignals),
40
+ excludedSignals: splitCsv(options.excludedSignals)
41
+ });
42
+ await writeJsonFile(options.out, icp);
43
+ printOutput({ status: "ok", icp });
44
+ });
45
+ program
46
+ .command("leads:generate")
47
+ .description("Generate seed leads against an ICP.")
48
+ .requiredOption("--icp <path>", "Path to ICP JSON")
49
+ .option("--count <number>", "Number of leads to generate", "10")
50
+ .option("--company-domain <domain>", "Target a specific company domain like deel.com")
51
+ .option("--company-name <name>", "Optional company name override for a targeted domain")
52
+ .requiredOption("--out <path>", "Output file path")
53
+ .action(async (options) => {
54
+ const icp = await readJsonFile(options.icp, IcpSchema);
55
+ const count = z.coerce.number().int().min(1).max(1000).parse(options.count);
56
+ const target = {
57
+ companyDomain: options.companyDomain,
58
+ companyName: options.companyName
59
+ };
60
+ const leads = await leadProvider.generateLeads(icp, count, target);
61
+ await writeJsonFile(options.out, leads);
62
+ printOutput({ status: "ok", generated: leads.length, out: options.out, target: target.companyDomain ?? null });
63
+ });
64
+ program
65
+ .command("leads:enrich")
66
+ .description("Enrich leads with fit, buying stage, and tech stack data.")
67
+ .requiredOption("--in <path>", "Path to lead JSON array")
68
+ .requiredOption("--out <path>", "Output file path")
69
+ .action(async (options) => {
70
+ const leads = await readJsonFile(options.in, z.array(LeadSchema));
71
+ const enriched = await enrichmentProvider.enrichLeads(leads);
72
+ await writeJsonFile(options.out, enriched);
73
+ printOutput({ status: "ok", enriched: enriched.length, out: options.out });
74
+ });
75
+ program
76
+ .command("leads:score")
77
+ .description("Score enriched leads against an ICP.")
78
+ .requiredOption("--icp <path>", "Path to ICP JSON")
79
+ .requiredOption("--in <path>", "Path to enriched lead JSON array")
80
+ .requiredOption("--out <path>", "Output file path")
81
+ .action(async (options) => {
82
+ const icp = await readJsonFile(options.icp, IcpSchema);
83
+ const leads = await readJsonFile(options.in, z.array(EnrichedLeadSchema));
84
+ const scored = await scoringProvider.scoreLeads(icp, leads);
85
+ await writeJsonFile(options.out, scored);
86
+ printOutput({ status: "ok", scored: scored.length, out: options.out });
87
+ });
88
+ program
89
+ .command("sync:crm")
90
+ .description("Dry-run sync scored leads into a CRM target.")
91
+ .requiredOption("--target <target>", "hubspot|salesforce|pipedrive")
92
+ .requiredOption("--in <path>", "Path to scored lead JSON array")
93
+ .action(async (options) => {
94
+ const target = SyncTargetSchema.parse(options.target);
95
+ const leads = await readJsonFile(options.in, z.array(ScoredLeadSchema));
96
+ const result = await syncProvider.sync(target, leads);
97
+ printOutput({ status: "ok", ...result });
98
+ });
99
+ program
100
+ .command("sync:outreach")
101
+ .description("Dry-run sync scored leads into an outreach platform.")
102
+ .requiredOption("--target <target>", "apollo|instantly|outreach")
103
+ .requiredOption("--in <path>", "Path to scored lead JSON array")
104
+ .action(async (options) => {
105
+ const target = SyncTargetSchema.parse(options.target);
106
+ const leads = await readJsonFile(options.in, z.array(ScoredLeadSchema));
107
+ const result = await syncProvider.sync(target, leads);
108
+ printOutput({ status: "ok", ...result });
109
+ });
110
+ program.parseAsync(process.argv).catch((error) => {
111
+ const message = error instanceof Error ? error.message : "Unknown error";
112
+ process.stderr.write(`${message}\n`);
113
+ process.exitCode = 1;
114
+ });
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ export const IcpSchema = z.object({
3
+ name: z.string().min(1),
4
+ industries: z.array(z.string().min(1)).default([]),
5
+ companySizes: z.array(z.string().min(1)).default([]),
6
+ regions: z.array(z.string().min(1)).default([]),
7
+ titles: z.array(z.string().min(1)).default([]),
8
+ pains: z.array(z.string().min(1)).default([]),
9
+ requiredSignals: z.array(z.string().min(1)).default([]),
10
+ excludedSignals: z.array(z.string().min(1)).default([]),
11
+ });
12
+ export const LeadSchema = z.object({
13
+ companyName: z.string().min(1),
14
+ domain: z.string().min(1),
15
+ industry: z.string().min(1),
16
+ region: z.string().min(1),
17
+ employeeCount: z.number().int().nonnegative(),
18
+ contactName: z.string().min(1),
19
+ title: z.string().min(1),
20
+ email: z.string().email(),
21
+ source: z.string().min(1),
22
+ signals: z.array(z.string().min(1)).default([]),
23
+ });
24
+ export const EnrichedLeadSchema = LeadSchema.extend({
25
+ techStack: z.array(z.string().min(1)).default([]),
26
+ crmFit: z.enum(["high", "medium", "low"]),
27
+ outreachFit: z.enum(["high", "medium", "low"]),
28
+ buyingStage: z.enum(["problem-aware", "solution-aware", "active-evaluation"]),
29
+ notes: z.array(z.string().min(1)).default([]),
30
+ });
31
+ export const ScoredLeadSchema = EnrichedLeadSchema.extend({
32
+ score: z.number().int().min(0).max(100),
33
+ grade: z.enum(["A", "B", "C", "D"]),
34
+ rationale: z.array(z.string().min(1)).default([]),
35
+ });
36
+ export const SyncTargetSchema = z.enum(["hubspot", "salesforce", "pipedrive", "apollo", "instantly", "outreach"]);
@@ -0,0 +1,147 @@
1
+ import { z } from "zod";
2
+ import { SAMPLE_COMPANIES, SAMPLE_CONTACTS, SAMPLE_SIGNALS, SAMPLE_TECH } from "./sample-data.js";
3
+ function pickByIndex(items, index) {
4
+ const item = items[index % items.length];
5
+ if (item === undefined) {
6
+ throw new Error("sample data invariant violated");
7
+ }
8
+ return item;
9
+ }
10
+ function normalizeCompanySize(employeeCount) {
11
+ if (employeeCount < 50) {
12
+ return "1-49";
13
+ }
14
+ if (employeeCount < 200) {
15
+ return "50-199";
16
+ }
17
+ if (employeeCount < 500) {
18
+ return "200-499";
19
+ }
20
+ return "500+";
21
+ }
22
+ function deriveCompanyNameFromDomain(domain) {
23
+ const normalizedDomain = domain
24
+ .toLowerCase()
25
+ .replace(/^https?:\/\//, "")
26
+ .replace(/^www\./, "")
27
+ .split("/")[0];
28
+ const hostname = normalizedDomain.split(".")[0] ?? normalizedDomain;
29
+ return hostname
30
+ .split(/[-_]/)
31
+ .filter((part) => part.length > 0)
32
+ .map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`)
33
+ .join(" ");
34
+ }
35
+ function buildTargetCompany(target, fallback, icp) {
36
+ const domain = target.companyDomain?.trim().toLowerCase();
37
+ if (domain === undefined) {
38
+ return fallback;
39
+ }
40
+ return {
41
+ companyName: target.companyName?.trim() || deriveCompanyNameFromDomain(domain),
42
+ domain,
43
+ industry: icp.industries[0] ?? fallback.industry,
44
+ region: icp.regions[0] ?? fallback.region,
45
+ employeeCount: fallback.employeeCount
46
+ };
47
+ }
48
+ export class HeuristicLeadProvider {
49
+ async generateLeads(icp, count, target = {}) {
50
+ return Array.from({ length: count }, (_, index) => {
51
+ const fallbackCompany = pickByIndex(SAMPLE_COMPANIES, index);
52
+ const company = buildTargetCompany(target, fallbackCompany, icp);
53
+ const contact = pickByIndex(SAMPLE_CONTACTS, index + 1);
54
+ const signals = [pickByIndex(SAMPLE_SIGNALS, index), pickByIndex(SAMPLE_SIGNALS, index + 2)];
55
+ const industry = icp.industries[index % Math.max(icp.industries.length, 1)] ?? company.industry;
56
+ const region = icp.regions[index % Math.max(icp.regions.length, 1)] ?? company.region;
57
+ const title = icp.titles[index % Math.max(icp.titles.length, 1)] ?? contact.title;
58
+ return {
59
+ companyName: company.companyName,
60
+ domain: company.domain,
61
+ industry,
62
+ region,
63
+ employeeCount: company.employeeCount,
64
+ contactName: contact.contactName,
65
+ title,
66
+ email: `${contact.contactName.toLowerCase().replaceAll(" ", ".")}@${company.domain}`,
67
+ source: target.companyDomain ? "heuristic-target-account" : "heuristic-seed",
68
+ signals
69
+ };
70
+ });
71
+ }
72
+ }
73
+ export class HeuristicEnrichmentProvider {
74
+ async enrichLeads(leads) {
75
+ return leads.map((lead, index) => ({
76
+ ...lead,
77
+ techStack: [pickByIndex(SAMPLE_TECH, index), pickByIndex(SAMPLE_TECH, index + 3)],
78
+ crmFit: lead.employeeCount > 200 ? "high" : lead.employeeCount > 100 ? "medium" : "low",
79
+ outreachFit: lead.signals.some((signal) => signal.includes("outbound")) ? "high" : "medium",
80
+ buyingStage: lead.signals.some((signal) => signal.includes("funding")) ? "active-evaluation" : "solution-aware",
81
+ notes: [
82
+ `${lead.companyName} matches the ${lead.industry} segment.`,
83
+ `${lead.contactName} is likely close to revenue tooling decisions.`
84
+ ]
85
+ }));
86
+ }
87
+ }
88
+ export class HeuristicScoringProvider {
89
+ async scoreLeads(icp, leads) {
90
+ return leads.map((lead) => {
91
+ const rationale = [];
92
+ let score = 40;
93
+ if (icp.industries.includes(lead.industry)) {
94
+ score += 20;
95
+ rationale.push("Industry matches ICP.");
96
+ }
97
+ if (icp.regions.includes(lead.region)) {
98
+ score += 10;
99
+ rationale.push("Region matches ICP.");
100
+ }
101
+ const normalizedSize = normalizeCompanySize(lead.employeeCount);
102
+ if (icp.companySizes.includes(normalizedSize)) {
103
+ score += 10;
104
+ rationale.push("Company size matches ICP.");
105
+ }
106
+ if (icp.titles.includes(lead.title)) {
107
+ score += 10;
108
+ rationale.push("Contact title matches ICP.");
109
+ }
110
+ const requiredMatches = icp.requiredSignals.filter((signal) => lead.signals.includes(signal));
111
+ score += Math.min(requiredMatches.length * 5, 10);
112
+ if (requiredMatches.length > 0) {
113
+ rationale.push(`Matched ${requiredMatches.length} required buying signals.`);
114
+ }
115
+ const excludedMatches = icp.excludedSignals.filter((signal) => lead.signals.includes(signal));
116
+ score -= excludedMatches.length * 15;
117
+ if (excludedMatches.length > 0) {
118
+ rationale.push(`Matched ${excludedMatches.length} excluded signals.`);
119
+ }
120
+ if (lead.crmFit === "high") {
121
+ score += 5;
122
+ rationale.push("Strong CRM fit.");
123
+ }
124
+ if (lead.outreachFit === "high") {
125
+ score += 5;
126
+ rationale.push("Strong outreach fit.");
127
+ }
128
+ const clampedScore = z.number().int().min(0).max(100).parse(score);
129
+ const grade = clampedScore >= 85 ? "A" : clampedScore >= 70 ? "B" : clampedScore >= 55 ? "C" : "D";
130
+ return {
131
+ ...lead,
132
+ score: clampedScore,
133
+ grade,
134
+ rationale
135
+ };
136
+ });
137
+ }
138
+ }
139
+ export class DryRunSyncProvider {
140
+ async sync(target, leads) {
141
+ return {
142
+ target,
143
+ synced: leads.length,
144
+ dryRun: true
145
+ };
146
+ }
147
+ }
@@ -0,0 +1,17 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export async function readJsonFile(filePath, schema) {
4
+ const content = await readFile(filePath, "utf8");
5
+ const parsed = JSON.parse(content);
6
+ return schema.parse(parsed);
7
+ }
8
+ export async function writeJsonFile(filePath, value) {
9
+ await mkdir(path.dirname(filePath), { recursive: true });
10
+ await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
11
+ }
12
+ export function splitCsv(value) {
13
+ return value
14
+ .split(",")
15
+ .map((entry) => entry.trim())
16
+ .filter((entry) => entry.length > 0);
17
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ export const SAMPLE_COMPANIES = [
2
+ { companyName: "Northstar Freight", domain: "northstarfreight.com", industry: "Logistics", region: "North America", employeeCount: 180 },
3
+ { companyName: "Brightpath Health", domain: "brightpathhealth.io", industry: "Healthcare", region: "Europe", employeeCount: 320 },
4
+ { companyName: "ForgeOps Cloud", domain: "forgeopscloud.dev", industry: "Software", region: "North America", employeeCount: 85 },
5
+ { companyName: "Summit Retail Group", domain: "summitretailgroup.com", industry: "Retail", region: "Europe", employeeCount: 540 },
6
+ { companyName: "Atlas Industrial", domain: "atlasindustrial.co", industry: "Manufacturing", region: "North America", employeeCount: 260 },
7
+ { companyName: "Meridian Finance", domain: "meridianfinance.ai", industry: "Financial Services", region: "Europe", employeeCount: 140 }
8
+ ];
9
+ export const SAMPLE_CONTACTS = [
10
+ { contactName: "Avery Chen", title: "VP Sales" },
11
+ { contactName: "Jordan Patel", title: "Head of Revenue Operations" },
12
+ { contactName: "Taylor Morgan", title: "Director of Growth" },
13
+ { contactName: "Morgan Diaz", title: "Chief Revenue Officer" },
14
+ { contactName: "Cameron Lee", title: "Sales Operations Manager" },
15
+ { contactName: "Riley Brooks", title: "Demand Generation Lead" }
16
+ ];
17
+ export const SAMPLE_SIGNALS = [
18
+ "hiring sales reps",
19
+ "recent funding",
20
+ "expanding into new regions",
21
+ "using fragmented sales tooling",
22
+ "growing outbound team",
23
+ "launching new product line"
24
+ ];
25
+ export const SAMPLE_TECH = [
26
+ "HubSpot",
27
+ "Salesforce",
28
+ "Apollo",
29
+ "Instantly",
30
+ "Outreach",
31
+ "Clay",
32
+ "Segment",
33
+ "PostHog"
34
+ ];