salesprompter-cli 0.1.22 → 0.1.23

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.
@@ -0,0 +1,368 @@
1
+ const COMPANY_SUFFIX_PATTERNS = [
2
+ /\s+GmbH\s*&\s*Co\.?\s+KG\.?$/i,
3
+ /\s+GmbH\.?$/i,
4
+ /\s+AG\.?$/i,
5
+ /\s+SE\.?$/i,
6
+ /\s+KG\.?$/i,
7
+ /\s+OHG\.?$/i,
8
+ /\s+GbR\.?$/i,
9
+ /\s+S\.A\.?$/i,
10
+ /\s+SA\.?$/i,
11
+ /\s+SARL\.?$/i,
12
+ /\s+LLC\.?$/i,
13
+ /\s+Inc\.?$/i,
14
+ /\s+Corp\.?$/i,
15
+ /\s+Ltd\.?$/i,
16
+ /\s+Group$/i
17
+ ];
18
+ const DACH_SIGNAL_MATCHERS = [
19
+ { label: "Germany", pattern: /(^|[^a-z])(germany|deutschland|bundesrepublik)([^a-z]|$)/i },
20
+ { label: "Austria", pattern: /(^|[^a-z])(austria|österreich|oesterreich)([^a-z]|$)/i },
21
+ { label: "Switzerland", pattern: /(^|[^a-z])(switzerland|schweiz|suisse|svizzera)([^a-z]|$)/i },
22
+ { label: "Berlin", pattern: /(^|[^a-z])berlin([^a-z]|$)/i },
23
+ { label: "Hamburg", pattern: /(^|[^a-z])hamburg([^a-z]|$)/i },
24
+ { label: "Munich", pattern: /(^|[^a-z])(munich|münchen|muenchen)([^a-z]|$)/i },
25
+ { label: "Frankfurt", pattern: /(^|[^a-z])frankfurt([^a-z]|$)/i },
26
+ { label: "Cologne", pattern: /(^|[^a-z])(cologne|köln|koeln)([^a-z]|$)/i },
27
+ { label: "Stuttgart", pattern: /(^|[^a-z])stuttgart([^a-z]|$)/i },
28
+ { label: "Düsseldorf", pattern: /(^|[^a-z])(düsseldorf|duesseldorf)([^a-z]|$)/i },
29
+ { label: "Vienna", pattern: /(^|[^a-z])(vienna|wien)([^a-z]|$)/i },
30
+ { label: "Graz", pattern: /(^|[^a-z])graz([^a-z]|$)/i },
31
+ { label: "Linz", pattern: /(^|[^a-z])linz([^a-z]|$)/i },
32
+ { label: "Salzburg", pattern: /(^|[^a-z])salzburg([^a-z]|$)/i },
33
+ { label: "Zurich", pattern: /(^|[^a-z])(zurich|zürich|zuerich)([^a-z]|$)/i },
34
+ { label: "Geneva", pattern: /(^|[^a-z])(geneva|genf)([^a-z]|$)/i },
35
+ { label: "Basel", pattern: /(^|[^a-z])basel([^a-z]|$)/i },
36
+ { label: "Bern", pattern: /(^|[^a-z])bern([^a-z]|$)/i },
37
+ { label: "Lausanne", pattern: /(^|[^a-z])lausanne([^a-z]|$)/i },
38
+ { label: "Zug", pattern: /(^|[^a-z])zug([^a-z]|$)/i }
39
+ ];
40
+ const DEEL_RELEVANT_TITLE_MATCHERS = [
41
+ /\bhead of (hr|human resources|people|people operations|people ops|people services|payroll|total rewards)\b/i,
42
+ /\b(head of compensation|head of benefits|head of people and culture)\b/i,
43
+ /\b(chief people officer|chro)\b/i,
44
+ /\b(vp|vice president)\b.*\b(hr|human resources|people|payroll|total rewards|compensation|benefits)\b/i,
45
+ /\bdirector\b.*\b(hr|human resources|people|payroll|total rewards|compensation|benefits|global mobility)\b/i,
46
+ /\b(hr|human resources|people|payroll)\b.*\bdirector\b/i,
47
+ /\b(hr business partner|hrbp)\b/i,
48
+ /\b(people operations manager|people ops manager|hr operations manager|payroll manager|benefits manager|compensation manager|global mobility manager)\b/i,
49
+ /\b(global payroll|global mobility|compensation and benefits)\b/i,
50
+ /\b(leiter personal|leiter hr|leiter human resources|personalleitung|personalleitung|bereichsleiter personal)\b/i
51
+ ];
52
+ const DEEL_TITLE_EXCLUSION_MATCHERS = [
53
+ /\bassistant\b/i,
54
+ /\bassistent/i,
55
+ /\brecruit/i,
56
+ /\btalent acquisition\b/i,
57
+ /\bhead chef\b/i,
58
+ /\bceo\b/i,
59
+ /\bchief executive officer\b/i,
60
+ /\bfounder\b/i,
61
+ /\bco-founder\b/i,
62
+ /\bpresident\b/i,
63
+ /\bhead of sales\b/i,
64
+ /\bmarketing\b/i
65
+ ];
66
+ function normalizeNullableString(value) {
67
+ const trimmed = value?.trim() ?? "";
68
+ return trimmed.length > 0 ? trimmed : null;
69
+ }
70
+ export function cleanDeelCompanyName(value) {
71
+ let next = normalizeNullableString(value);
72
+ if (!next) {
73
+ return null;
74
+ }
75
+ for (const pattern of COMPANY_SUFFIX_PATTERNS) {
76
+ next = next.replace(pattern, "").trim();
77
+ }
78
+ return next.length > 0 ? next : normalizeNullableString(value);
79
+ }
80
+ export function isDeelRelevantSalesNavTitle(value) {
81
+ const normalized = normalizeNullableString(value);
82
+ if (!normalized) {
83
+ return false;
84
+ }
85
+ if (DEEL_TITLE_EXCLUSION_MATCHERS.some((pattern) => pattern.test(normalized))) {
86
+ return false;
87
+ }
88
+ return DEEL_RELEVANT_TITLE_MATCHERS.some((pattern) => pattern.test(normalized));
89
+ }
90
+ export function extractLinkedInCompanyHandleFromUrl(value) {
91
+ const normalized = normalizeNullableString(value);
92
+ if (!normalized) {
93
+ return null;
94
+ }
95
+ try {
96
+ const url = new URL(normalized);
97
+ if (!/(^|\.)linkedin\.com$/i.test(url.hostname)) {
98
+ return null;
99
+ }
100
+ const segments = url.pathname.split("/").filter((segment) => segment.length > 0);
101
+ const companyIndex = segments.findIndex((segment) => segment.toLowerCase() === "company");
102
+ const handle = companyIndex >= 0 ? segments[companyIndex + 1] ?? "" : "";
103
+ return handle.trim().length > 0 ? handle.trim().toLowerCase() : null;
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ function preferredProfileUrl(row) {
110
+ return (normalizeNullableString(row.linkedin_profile_url) ??
111
+ normalizeNullableString(row.default_profile_url) ??
112
+ row.sales_nav_profile_url);
113
+ }
114
+ function collectDachSignals(value) {
115
+ if (!value) {
116
+ return [];
117
+ }
118
+ const matches = [];
119
+ for (const matcher of DACH_SIGNAL_MATCHERS) {
120
+ if (matcher.pattern.test(value)) {
121
+ matches.push(matcher.label);
122
+ }
123
+ }
124
+ return matches;
125
+ }
126
+ export function classifyDeelSalesNavLanguage(row) {
127
+ const signalSources = [
128
+ { field: "location", value: normalizeNullableString(row.location) },
129
+ { field: "companyLocation", value: normalizeNullableString(row.company_location) },
130
+ { field: "searchQuery", value: normalizeNullableString(row.search_query) }
131
+ ];
132
+ const languageSignals = [];
133
+ const signalFields = [];
134
+ for (const source of signalSources) {
135
+ const sourceSignals = collectDachSignals(source.value);
136
+ if (sourceSignals.length > 0) {
137
+ languageSignals.push(...sourceSignals);
138
+ signalFields.push(source.field);
139
+ }
140
+ }
141
+ const uniqueSignals = [...new Set(languageSignals)];
142
+ const uniqueFields = [...new Set(signalFields)];
143
+ if (uniqueSignals.length > 0) {
144
+ return {
145
+ language: "de",
146
+ marketSegment: "dach",
147
+ languageReason: `clear_dach_signal:${uniqueSignals.join("|")}`,
148
+ languageSignals: uniqueSignals,
149
+ signalFields: uniqueFields
150
+ };
151
+ }
152
+ return {
153
+ language: "en",
154
+ marketSegment: "non-dach",
155
+ languageReason: "fallback_non_dach",
156
+ languageSignals: [],
157
+ signalFields: []
158
+ };
159
+ }
160
+ export function normalizeDeelSalesNavRow(row) {
161
+ const classification = classifyDeelSalesNavLanguage(row);
162
+ return {
163
+ id: row.id,
164
+ orgId: row.org_id,
165
+ runId: normalizeNullableString(row.run_id),
166
+ language: classification.language,
167
+ marketSegment: classification.marketSegment,
168
+ languageReason: classification.languageReason,
169
+ languageSignals: classification.languageSignals,
170
+ signalFields: classification.signalFields,
171
+ fullName: normalizeNullableString(row.full_name),
172
+ firstName: normalizeNullableString(row.first_name),
173
+ lastName: normalizeNullableString(row.last_name),
174
+ title: normalizeNullableString(row.title),
175
+ industry: normalizeNullableString(row.industry),
176
+ companyName: normalizeNullableString(row.company_name),
177
+ companyNameCleaned: cleanDeelCompanyName(row.company_name),
178
+ location: normalizeNullableString(row.location),
179
+ companyLocation: normalizeNullableString(row.company_location),
180
+ preferredProfileUrl: preferredProfileUrl(row),
181
+ linkedinProfileUrl: normalizeNullableString(row.linkedin_profile_url),
182
+ defaultProfileUrl: normalizeNullableString(row.default_profile_url),
183
+ salesNavProfileUrl: row.sales_nav_profile_url,
184
+ companyUrl: normalizeNullableString(row.company_url),
185
+ regularCompanyUrl: normalizeNullableString(row.regular_company_url),
186
+ companyLinkedInHandle: extractLinkedInCompanyHandleFromUrl(row.company_url) ??
187
+ extractLinkedInCompanyHandleFromUrl(row.regular_company_url),
188
+ searchQuery: normalizeNullableString(row.search_query),
189
+ scrapedAt: normalizeNullableString(row.scraped_at)
190
+ };
191
+ }
192
+ function percentage(count, total) {
193
+ if (total <= 0) {
194
+ return 0;
195
+ }
196
+ return Number(((count / total) * 100).toFixed(2));
197
+ }
198
+ export function buildDeelSalesNavPack(orgId, rows) {
199
+ const locales = { de: [], en: [] };
200
+ const titleCounts = new Map();
201
+ const signalFieldCounts = {
202
+ location: 0,
203
+ companyLocation: 0,
204
+ searchQuery: 0,
205
+ none: 0
206
+ };
207
+ const fieldCounts = {
208
+ firstName: 0,
209
+ lastName: 0,
210
+ fullName: 0,
211
+ companyName: 0,
212
+ companyNameCleaned: 0,
213
+ preferredProfileUrl: 0,
214
+ linkedinProfileUrl: 0,
215
+ companyLinkedInHandle: 0,
216
+ location: 0,
217
+ companyLocation: 0,
218
+ searchQuery: 0
219
+ };
220
+ for (const row of rows) {
221
+ const prepared = normalizeDeelSalesNavRow(row);
222
+ locales[prepared.language].push(prepared);
223
+ if (prepared.title) {
224
+ titleCounts.set(prepared.title, (titleCounts.get(prepared.title) ?? 0) + 1);
225
+ }
226
+ if (prepared.signalFields.length === 0) {
227
+ signalFieldCounts.none += 1;
228
+ }
229
+ else {
230
+ for (const field of prepared.signalFields) {
231
+ if (field === "location") {
232
+ signalFieldCounts.location += 1;
233
+ }
234
+ else if (field === "companyLocation") {
235
+ signalFieldCounts.companyLocation += 1;
236
+ }
237
+ else if (field === "searchQuery") {
238
+ signalFieldCounts.searchQuery += 1;
239
+ }
240
+ }
241
+ }
242
+ if (prepared.firstName)
243
+ fieldCounts.firstName += 1;
244
+ if (prepared.lastName)
245
+ fieldCounts.lastName += 1;
246
+ if (prepared.fullName)
247
+ fieldCounts.fullName += 1;
248
+ if (prepared.companyName)
249
+ fieldCounts.companyName += 1;
250
+ if (prepared.companyNameCleaned)
251
+ fieldCounts.companyNameCleaned += 1;
252
+ if (prepared.preferredProfileUrl)
253
+ fieldCounts.preferredProfileUrl += 1;
254
+ if (prepared.linkedinProfileUrl)
255
+ fieldCounts.linkedinProfileUrl += 1;
256
+ if (prepared.companyLinkedInHandle)
257
+ fieldCounts.companyLinkedInHandle += 1;
258
+ if (prepared.location)
259
+ fieldCounts.location += 1;
260
+ if (prepared.companyLocation)
261
+ fieldCounts.companyLocation += 1;
262
+ if (prepared.searchQuery)
263
+ fieldCounts.searchQuery += 1;
264
+ }
265
+ const total = rows.length;
266
+ const localeCounts = {
267
+ de: locales.de.length,
268
+ en: locales.en.length
269
+ };
270
+ const fieldCoverage = {
271
+ firstName: { count: fieldCounts.firstName, percentage: percentage(fieldCounts.firstName, total) },
272
+ lastName: { count: fieldCounts.lastName, percentage: percentage(fieldCounts.lastName, total) },
273
+ fullName: { count: fieldCounts.fullName, percentage: percentage(fieldCounts.fullName, total) },
274
+ companyName: { count: fieldCounts.companyName, percentage: percentage(fieldCounts.companyName, total) },
275
+ companyNameCleaned: { count: fieldCounts.companyNameCleaned, percentage: percentage(fieldCounts.companyNameCleaned, total) },
276
+ preferredProfileUrl: { count: fieldCounts.preferredProfileUrl, percentage: percentage(fieldCounts.preferredProfileUrl, total) },
277
+ linkedinProfileUrl: { count: fieldCounts.linkedinProfileUrl, percentage: percentage(fieldCounts.linkedinProfileUrl, total) },
278
+ companyLinkedInHandle: { count: fieldCounts.companyLinkedInHandle, percentage: percentage(fieldCounts.companyLinkedInHandle, total) },
279
+ location: { count: fieldCounts.location, percentage: percentage(fieldCounts.location, total) },
280
+ companyLocation: { count: fieldCounts.companyLocation, percentage: percentage(fieldCounts.companyLocation, total) },
281
+ searchQuery: { count: fieldCounts.searchQuery, percentage: percentage(fieldCounts.searchQuery, total) }
282
+ };
283
+ return {
284
+ vendor: "deel",
285
+ source: "salesnav-supabase",
286
+ orgId,
287
+ total,
288
+ locales,
289
+ summary: {
290
+ localeCounts,
291
+ localePercentages: {
292
+ de: percentage(localeCounts.de, total),
293
+ en: percentage(localeCounts.en, total)
294
+ },
295
+ fieldCoverage,
296
+ signalFieldCounts,
297
+ topTitles: [...titleCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 20)
298
+ }
299
+ };
300
+ }
301
+ function csvEscape(value) {
302
+ const normalized = value ?? "";
303
+ return `"${normalized.replaceAll('"', '""')}"`;
304
+ }
305
+ export function buildDeelSalesNavCsvHeader() {
306
+ return [
307
+ "id",
308
+ "orgId",
309
+ "runId",
310
+ "language",
311
+ "marketSegment",
312
+ "languageReason",
313
+ "languageSignals",
314
+ "signalFields",
315
+ "fullName",
316
+ "firstName",
317
+ "lastName",
318
+ "title",
319
+ "industry",
320
+ "companyName",
321
+ "companyNameCleaned",
322
+ "location",
323
+ "companyLocation",
324
+ "preferredProfileUrl",
325
+ "linkedinProfileUrl",
326
+ "defaultProfileUrl",
327
+ "salesNavProfileUrl",
328
+ "companyUrl",
329
+ "regularCompanyUrl",
330
+ "companyLinkedInHandle",
331
+ "searchQuery",
332
+ "scrapedAt"
333
+ ].join(",");
334
+ }
335
+ export function buildDeelSalesNavCsvLines(rows) {
336
+ return rows
337
+ .map((row) => [
338
+ row.id,
339
+ row.orgId,
340
+ row.runId ?? "",
341
+ row.language,
342
+ row.marketSegment,
343
+ row.languageReason,
344
+ row.languageSignals.join("|"),
345
+ row.signalFields.join("|"),
346
+ row.fullName ?? "",
347
+ row.firstName ?? "",
348
+ row.lastName ?? "",
349
+ row.title ?? "",
350
+ row.industry ?? "",
351
+ row.companyName ?? "",
352
+ row.companyNameCleaned ?? "",
353
+ row.location ?? "",
354
+ row.companyLocation ?? "",
355
+ row.preferredProfileUrl,
356
+ row.linkedinProfileUrl ?? "",
357
+ row.defaultProfileUrl ?? "",
358
+ row.salesNavProfileUrl,
359
+ row.companyUrl ?? "",
360
+ row.regularCompanyUrl ?? "",
361
+ row.companyLinkedInHandle ?? "",
362
+ row.searchQuery ?? "",
363
+ row.scrapedAt ?? ""
364
+ ]
365
+ .map(csvEscape)
366
+ .join(","))
367
+ .join("\n");
368
+ }
@@ -6,7 +6,7 @@ function marketCountries(market) {
6
6
  if (market === "europe") {
7
7
  return ["DE", "AT", "CH", "NL", "GB", "FR", "SE", "DK"];
8
8
  }
9
- return ["DE", "AT", "CH", "NL", "GB", "FR", "SE", "DK", "US"];
9
+ return [];
10
10
  }
11
11
  function sqlStringList(values) {
12
12
  return values.map((value) => `'${value.replaceAll("'", "\\'")}'`).join(", ");
@@ -54,7 +54,8 @@ export function buildDirectPathLeadExportSql(vendor, market, limit) {
54
54
  if (vendor !== "deel") {
55
55
  throw new Error(`Unsupported direct-path vendor: ${vendor}`);
56
56
  }
57
- const countries = sqlStringList(marketCountries(market));
57
+ const countries = marketCountries(market);
58
+ const countryClause = countries.length > 0 ? `co.countryCode IN (${sqlStringList(countries)})` : "TRUE";
58
59
  const broadTitlePredicate = broadTitlePredicateSql();
59
60
  const titleExclusions = titleExclusionPredicateSql();
60
61
  return `WITH hr_leadlists AS (
@@ -129,7 +130,7 @@ SELECT
129
130
  FROM contacts c
130
131
  JOIN hr_leadlists l ON c.leadListId = l.leadListId
131
132
  LEFT JOIN companies co ON c.companyId = co.companyId
132
- WHERE co.countryCode IN (${countries})
133
+ WHERE ${countryClause}
133
134
  AND co.domain IS NOT NULL
134
135
  AND co.companySize IN ('200-499', '501-1000', '1001-5000', '5001-10.000', '10.000+')
135
136
  AND (
@@ -140,7 +141,7 @@ QUALIFY ROW_NUMBER() OVER (PARTITION BY c.contactId ORDER BY co.company_ts DESC)
140
141
  ORDER BY co.company_ts DESC
141
142
  LIMIT ${limit}`;
142
143
  }
143
- function classifySegment(title) {
144
+ export function classifyDirectPathSegment(title) {
144
145
  const normalized = title.toLowerCase();
145
146
  if (normalized.includes("payroll") ||
146
147
  normalized.includes("people services") ||
@@ -275,7 +276,7 @@ export function segmentDirectPathRows(vendor, market, rows) {
275
276
  };
276
277
  for (const row of rows) {
277
278
  const title = row.jobTitle ?? "";
278
- const { segment, reason } = classifySegment(title);
279
+ const { segment, reason } = classifyDirectPathSegment(title);
279
280
  segments[segment].push({
280
281
  ...row,
281
282
  segment,
package/dist/domain.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { z } from "zod";
2
+ export const LeadCustomVariableValueSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
3
+ export const LeadCustomVariablesSchema = z.record(z.string(), LeadCustomVariableValueSchema);
2
4
  export const IcpSchema = z.object({
3
5
  name: z.string().min(1),
4
6
  description: z.string().default(""),
@@ -33,6 +35,7 @@ export const LeadSchema = z.object({
33
35
  email: z.string().email(),
34
36
  source: z.string().min(1),
35
37
  signals: z.array(z.string().min(1)).default([]),
38
+ customVariables: LeadCustomVariablesSchema.optional(),
36
39
  });
37
40
  export const EnrichedLeadSchema = LeadSchema.extend({
38
41
  techStack: z.array(z.string().min(1)).default([]),
package/dist/instantly.js CHANGED
@@ -43,7 +43,8 @@ function buildLeadPayload(lead, campaignId) {
43
43
  salesprompter_outreach_fit: lead.outreachFit,
44
44
  salesprompter_signals: lead.signals.join(", "),
45
45
  salesprompter_tech_stack: lead.techStack.join(", "),
46
- salesprompter_rationale: lead.rationale.join(" | ")
46
+ salesprompter_rationale: lead.rationale.join(" | "),
47
+ ...(lead.customVariables ?? {})
47
48
  }
48
49
  };
49
50
  }
@@ -1,11 +1,18 @@
1
- function marketCountrySql(market) {
1
+ function marketCountries(market) {
2
2
  if (market === "dach") {
3
- return '"DE", "AT", "CH"';
3
+ return ["DE", "AT", "CH"];
4
4
  }
5
5
  if (market === "europe") {
6
- return '"DE", "AT", "CH", "NL", "GB", "FR", "SE", "DK"';
6
+ return ["DE", "AT", "CH", "NL", "GB", "FR", "SE", "DK"];
7
+ }
8
+ return [];
9
+ }
10
+ function marketCountrySql(market) {
11
+ const countries = marketCountries(market);
12
+ if (countries.length === 0) {
13
+ return null;
7
14
  }
8
- return '"DE", "AT", "CH", "NL", "GB", "FR", "SE", "DK", "US"';
15
+ return countries.map((country) => `"${country}"`).join(", ");
9
16
  }
10
17
  function marketQueryTerms(market) {
11
18
  if (market === "dach") {
@@ -30,6 +37,16 @@ export function buildLeadlistsFunnelQueries(vendor, market) {
30
37
  const countries = marketCountrySql(market);
31
38
  const queryTerms = marketQueryTerms(market);
32
39
  const marketLikeClause = encodedLikeClause(queryTerms);
40
+ const countryInClause = countries === null ? "TRUE" : `c.countryCode IN (${countries})`;
41
+ const countryWithDomainClause = countries === null ? "c.domain IS NOT NULL" : `c.countryCode IN (${countries}) AND c.domain IS NOT NULL`;
42
+ const companyMarketClause = countries === null ? "TRUE" : `e.countryCode IN (${countries})`;
43
+ const companyUsableDomainClause = countries === null
44
+ ? "e.domain IS NOT NULL AND COALESCE(e.domainBlacklisted, FALSE) = FALSE"
45
+ : `e.countryCode IN (${countries}) AND e.domain IS NOT NULL AND COALESCE(e.domainBlacklisted, FALSE) = FALSE`;
46
+ const companyDomainNotFoundClause = countries === null
47
+ ? "COALESCE(e.company_emailDomainNotFound, FALSE) = TRUE"
48
+ : `e.countryCode IN (${countries}) AND COALESCE(e.company_emailDomainNotFound, FALSE) = TRUE`;
49
+ const downstreamCountryClause = countries === null ? "TRUE" : `UPPER(CAST(company_countryCode AS STRING)) IN (${countries})`;
33
50
  return {
34
51
  queryTerms,
35
52
  stages: [
@@ -73,10 +90,10 @@ SELECT
73
90
  COUNT(*) AS people_with_company_url,
74
91
  COUNTIF(companyIdFromUrl IS NOT NULL) AS people_with_extractable_company_id,
75
92
  COUNTIF(c.companyId IS NOT NULL) AS people_joined_to_company,
76
- COUNTIF(c.countryCode IN (${countries})) AS people_in_market_company,
77
- COUNTIF(c.countryCode IN (${countries}) AND c.domain IS NOT NULL) AS people_in_market_company_with_domain,
78
- COUNTIF(c.countryCode IN (${countries}) AND p.firstName IS NOT NULL) AS people_in_market_with_first_name,
79
- COUNTIF(c.countryCode IN (${countries}) AND p.lastName IS NOT NULL) AS people_in_market_with_last_name
93
+ COUNTIF(${countryInClause}) AS people_in_market_company,
94
+ COUNTIF(${countryWithDomainClause}) AS people_in_market_company_with_domain,
95
+ COUNTIF(${countryInClause} AND p.firstName IS NOT NULL) AS people_in_market_with_first_name,
96
+ COUNTIF(${countryInClause} AND p.lastName IS NOT NULL) AS people_in_market_with_last_name
80
97
  FROM people p
81
98
  LEFT JOIN companies c ON p.companyIdFromUrl = c.companyId`
82
99
  },
@@ -97,10 +114,10 @@ SELECT
97
114
  COUNT(DISTINCT CONCAT(CAST(h.companyId AS STRING),"|",CAST(h.leadListId AS STRING))) AS distinct_company_search_rows,
98
115
  COUNTIF(h.companyUrl IS NOT NULL OR h.regularCompanyUrl IS NOT NULL) AS with_linkedin_company_profile,
99
116
  COUNTIF(e.handle IS NOT NULL) AS with_handle,
100
- COUNTIF(e.countryCode IN (${countries})) AS in_market,
101
- COUNTIF(e.countryCode IN (${countries}) AND e.domain IS NOT NULL) AS with_domain,
102
- COUNTIF(e.countryCode IN (${countries}) AND e.domain IS NOT NULL AND COALESCE(e.domainBlacklisted, FALSE) = FALSE) AS with_usable_domain,
103
- COUNTIF(e.countryCode IN (${countries}) AND COALESCE(e.company_emailDomainNotFound, FALSE) = TRUE) AS domain_not_found
117
+ COUNTIF(${companyMarketClause}) AS in_market,
118
+ COUNTIF(${countries === null ? "e.domain IS NOT NULL" : `e.countryCode IN (${countries}) AND e.domain IS NOT NULL`}) AS with_domain,
119
+ COUNTIF(${companyUsableDomainClause}) AS with_usable_domain,
120
+ COUNTIF(${companyDomainNotFoundClause}) AS domain_not_found
104
121
  FROM hr_company_search h
105
122
  LEFT JOIN enriched e
106
123
  ON CAST(h.companyId AS STRING) = CAST(e.companyId AS STRING)
@@ -118,7 +135,7 @@ LEFT JOIN enriched e
118
135
  COUNTIF(email IS NOT NULL AND COALESCE(email_invalid, FALSE) = FALSE) AS with_valid_email
119
136
  FROM \`icpidentifier.SalesGPT.leadPool_new\`
120
137
  WHERE CAST(functionId AS STRING) = "12"
121
- AND UPPER(CAST(company_countryCode AS STRING)) IN (${countries})`
138
+ AND ${downstreamCountryClause}`
122
139
  }
123
140
  ],
124
141
  notes: [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "salesprompter-cli",
3
- "version": "0.1.22",
4
- "description": "JSON-first sales prospecting CLI for ICP definition, lead generation, enrichment, scoring, and CRM/outreach sync.",
3
+ "version": "0.1.23",
4
+ "description": "JSON-first workflow automation CLI for account discovery, data processing, and system sync.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "salesprompter": "dist/cli.js"
@@ -29,23 +29,16 @@
29
29
  "access": "public"
30
30
  },
31
31
  "keywords": [
32
- "sales",
33
32
  "salesprompter",
34
33
  "cli",
35
- "icp",
36
- "ideal-customer-profile",
37
- "prospecting",
38
- "lead-generation",
39
- "lead-enrichment",
40
- "lead-scoring",
41
- "sales-intelligence",
42
- "crm",
43
- "outreach",
44
- "instantly",
45
- "hubspot",
46
- "llm",
47
- "ai-agent",
48
- "codex"
34
+ "automation",
35
+ "workflow",
36
+ "orchestration",
37
+ "data",
38
+ "integration",
39
+ "sync",
40
+ "json",
41
+ "tooling"
49
42
  ],
50
43
  "homepage": "https://salesprompter-cli.vercel.app",
51
44
  "repository": {