salesprompter-cli 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +150 -49
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -175,17 +175,28 @@ function deriveCompanyNameFromDomain(domain) {
|
|
|
175
175
|
function writeWizardLine(message = "") {
|
|
176
176
|
process.stdout.write(`${message}\n`);
|
|
177
177
|
}
|
|
178
|
+
function isOpaqueOrgId(value) {
|
|
179
|
+
return /^org_[A-Za-z0-9]+$/.test(value);
|
|
180
|
+
}
|
|
178
181
|
function getOrgLabel(session) {
|
|
179
|
-
|
|
182
|
+
const label = session.user.orgName ?? session.user.orgSlug ?? session.user.orgId ?? null;
|
|
183
|
+
if (label && isOpaqueOrgId(label)) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
return label;
|
|
180
187
|
}
|
|
181
188
|
function writeSessionSummary(session) {
|
|
189
|
+
const identity = session.user.name?.trim()
|
|
190
|
+
? `${session.user.name} (${session.user.email})`
|
|
191
|
+
: session.user.email;
|
|
192
|
+
writeWizardLine(`Signed in as ${identity}.`);
|
|
182
193
|
const orgLabel = getOrgLabel(session);
|
|
183
194
|
if (orgLabel) {
|
|
184
|
-
writeWizardLine(`
|
|
185
|
-
return;
|
|
195
|
+
writeWizardLine(`Workspace: ${orgLabel}`);
|
|
186
196
|
}
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
}
|
|
198
|
+
function normalizeChoiceText(value) {
|
|
199
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
189
200
|
}
|
|
190
201
|
async function promptChoice(rl, prompt, options, defaultValue) {
|
|
191
202
|
const defaultIndex = options.findIndex((option) => option.value === defaultValue);
|
|
@@ -198,7 +209,7 @@ async function promptChoice(rl, prompt, options, defaultValue) {
|
|
|
198
209
|
const description = option.description ? ` - ${option.description}` : "";
|
|
199
210
|
writeWizardLine(` ${index + 1}. ${option.label}${description}`);
|
|
200
211
|
}
|
|
201
|
-
const answer = (await rl.question(`
|
|
212
|
+
const answer = (await rl.question(`Select an option (press Enter for ${defaultIndex + 1}): `)).trim();
|
|
202
213
|
if (answer.length === 0) {
|
|
203
214
|
return defaultValue;
|
|
204
215
|
}
|
|
@@ -210,11 +221,25 @@ async function promptChoice(rl, prompt, options, defaultValue) {
|
|
|
210
221
|
}
|
|
211
222
|
return selected.value;
|
|
212
223
|
}
|
|
213
|
-
const
|
|
224
|
+
const normalizedAnswer = normalizeChoiceText(answer);
|
|
225
|
+
const matched = options.find((option) => {
|
|
226
|
+
if (normalizeChoiceText(option.value) === normalizedAnswer) {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
if (normalizeChoiceText(option.label) === normalizedAnswer) {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
return (option.aliases ?? []).some((alias) => normalizeChoiceText(alias) === normalizedAnswer);
|
|
233
|
+
});
|
|
214
234
|
if (matched) {
|
|
215
235
|
return matched.value;
|
|
216
236
|
}
|
|
217
|
-
|
|
237
|
+
if (/^(npx|salesprompter)\b/i.test(answer)) {
|
|
238
|
+
writeWizardLine("You are already in the wizard. Pick an option here, or press Ctrl-C to exit.");
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
writeWizardLine("Please choose one of the options above.");
|
|
242
|
+
}
|
|
218
243
|
writeWizardLine();
|
|
219
244
|
}
|
|
220
245
|
}
|
|
@@ -265,7 +290,7 @@ async function ensureWizardSession(options) {
|
|
|
265
290
|
throw error;
|
|
266
291
|
}
|
|
267
292
|
}
|
|
268
|
-
writeWizardLine("
|
|
293
|
+
writeWizardLine("First, sign in to Salesprompter.");
|
|
269
294
|
writeWizardLine();
|
|
270
295
|
const result = await performLogin({
|
|
271
296
|
apiUrl: options?.apiUrl,
|
|
@@ -276,7 +301,92 @@ async function ensureWizardSession(options) {
|
|
|
276
301
|
return result.session;
|
|
277
302
|
}
|
|
278
303
|
async function runVendorIcpWizard(rl) {
|
|
279
|
-
const
|
|
304
|
+
const startPoint = await promptChoice(rl, "How do you want to start?", [
|
|
305
|
+
{
|
|
306
|
+
value: "custom",
|
|
307
|
+
label: "Create a custom profile",
|
|
308
|
+
description: "Answer a few questions about who you want to sell to",
|
|
309
|
+
aliases: ["custom", "from scratch", "new profile"]
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
value: "template",
|
|
313
|
+
label: "Use the Deel template",
|
|
314
|
+
description: "Quick start from the current built-in template",
|
|
315
|
+
aliases: ["template", "deel", "use template"]
|
|
316
|
+
}
|
|
317
|
+
], "custom");
|
|
318
|
+
writeWizardLine();
|
|
319
|
+
if (startPoint === "custom") {
|
|
320
|
+
const productName = await promptText(rl, "What are you selling?", { required: true });
|
|
321
|
+
const description = await promptText(rl, "Who is this for? (optional)");
|
|
322
|
+
const industries = await promptText(rl, "Target industries (optional, comma-separated)");
|
|
323
|
+
const companySizes = await promptText(rl, "Target company sizes (optional, comma-separated)");
|
|
324
|
+
const regions = await promptText(rl, "Target regions (optional, comma-separated)");
|
|
325
|
+
const countries = await promptText(rl, "Target countries (optional, comma-separated)");
|
|
326
|
+
const titles = await promptText(rl, "Target job titles (optional, comma-separated)");
|
|
327
|
+
const keywords = await promptText(rl, "Keywords or buying signals (optional, comma-separated)");
|
|
328
|
+
writeWizardLine();
|
|
329
|
+
const slug = slugify(productName) || "icp";
|
|
330
|
+
const outPath = await promptText(rl, "Where should I save the ICP JSON?", {
|
|
331
|
+
defaultValue: `./data/${slug}-icp.json`,
|
|
332
|
+
required: true
|
|
333
|
+
});
|
|
334
|
+
writeWizardLine();
|
|
335
|
+
const icp = IcpSchema.parse({
|
|
336
|
+
name: `${productName} ICP`,
|
|
337
|
+
description,
|
|
338
|
+
industries: splitCsv(industries),
|
|
339
|
+
companySizes: splitCsv(companySizes),
|
|
340
|
+
regions: splitCsv(regions),
|
|
341
|
+
countries: splitCsv(countries),
|
|
342
|
+
titles: splitCsv(titles),
|
|
343
|
+
keywords: splitCsv(keywords)
|
|
344
|
+
});
|
|
345
|
+
await writeJsonFile(outPath, icp);
|
|
346
|
+
writeWizardLine(`Created ${icp.name}.`);
|
|
347
|
+
writeWizardLine(`Saved ICP to ${outPath}.`);
|
|
348
|
+
writeWizardLine();
|
|
349
|
+
writeWizardLine("Equivalent raw command:");
|
|
350
|
+
const defineArgs = ["salesprompter", "icp:define", "--name", icp.name];
|
|
351
|
+
if (description.trim().length > 0) {
|
|
352
|
+
defineArgs.push("--description", description);
|
|
353
|
+
}
|
|
354
|
+
if (industries.trim().length > 0) {
|
|
355
|
+
defineArgs.push("--industries", industries);
|
|
356
|
+
}
|
|
357
|
+
if (companySizes.trim().length > 0) {
|
|
358
|
+
defineArgs.push("--company-sizes", companySizes);
|
|
359
|
+
}
|
|
360
|
+
if (regions.trim().length > 0) {
|
|
361
|
+
defineArgs.push("--regions", regions);
|
|
362
|
+
}
|
|
363
|
+
if (countries.trim().length > 0) {
|
|
364
|
+
defineArgs.push("--countries", countries);
|
|
365
|
+
}
|
|
366
|
+
if (titles.trim().length > 0) {
|
|
367
|
+
defineArgs.push("--titles", titles);
|
|
368
|
+
}
|
|
369
|
+
if (keywords.trim().length > 0) {
|
|
370
|
+
defineArgs.push("--keywords", keywords);
|
|
371
|
+
}
|
|
372
|
+
defineArgs.push("--out", outPath);
|
|
373
|
+
writeWizardLine(` ${buildCommandLine(defineArgs)}`);
|
|
374
|
+
writeWizardLine();
|
|
375
|
+
writeWizardLine("Next suggested command:");
|
|
376
|
+
writeWizardLine(` ${buildCommandLine([
|
|
377
|
+
"salesprompter",
|
|
378
|
+
"leads:generate",
|
|
379
|
+
"--icp",
|
|
380
|
+
outPath,
|
|
381
|
+
"--count",
|
|
382
|
+
"5",
|
|
383
|
+
"--out",
|
|
384
|
+
`./data/${slug}-leads.json`
|
|
385
|
+
])}`);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const vendor = "deel";
|
|
389
|
+
writeWizardLine("Using the built-in Deel ICP template.");
|
|
280
390
|
writeWizardLine();
|
|
281
391
|
const market = await promptChoice(rl, "Which market do you want to target?", [
|
|
282
392
|
{ value: "dach", label: "DACH", description: "Germany, Austria, Switzerland" },
|
|
@@ -373,13 +483,15 @@ async function runLeadGenerationWizard(rl) {
|
|
|
373
483
|
const source = await promptChoice(rl, "How do you want to generate leads?", [
|
|
374
484
|
{
|
|
375
485
|
value: "target-account",
|
|
376
|
-
label: "At
|
|
377
|
-
description: "Example: find people at
|
|
486
|
+
label: "At one company",
|
|
487
|
+
description: "Example: find people at acme.com",
|
|
488
|
+
aliases: ["company", "one company", "specific company", "account"]
|
|
378
489
|
},
|
|
379
490
|
{
|
|
380
491
|
value: "vendor-lookup",
|
|
381
|
-
label: "From BigQuery",
|
|
382
|
-
description: "Use
|
|
492
|
+
label: "From my BigQuery data",
|
|
493
|
+
description: "Use a saved profile to search the lead data you already have",
|
|
494
|
+
aliases: ["bigquery", "warehouse", "my data", "from bigquery"]
|
|
383
495
|
}
|
|
384
496
|
], "target-account");
|
|
385
497
|
writeWizardLine();
|
|
@@ -390,40 +502,32 @@ async function runLeadGenerationWizard(rl) {
|
|
|
390
502
|
await runVendorLookupWizard(rl);
|
|
391
503
|
}
|
|
392
504
|
async function runVendorLookupWizard(rl) {
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
const market = await promptChoice(rl, "Which market should the BigQuery lookup target?", [
|
|
396
|
-
{ value: "dach", label: "DACH", description: "Germany, Austria, Switzerland" },
|
|
397
|
-
{ value: "europe", label: "Europe" },
|
|
398
|
-
{ value: "global", label: "Global" }
|
|
399
|
-
], "dach");
|
|
400
|
-
const limit = z.coerce.number().int().min(1).max(5000).parse(await promptText(rl, "How many rows should the lookup return?", { defaultValue: "100", required: true }));
|
|
401
|
-
const execute = await promptYesNo(rl, "Execute the BigQuery lookup now?", false);
|
|
402
|
-
writeWizardLine();
|
|
403
|
-
const slug = slugify(vendor);
|
|
404
|
-
const icpPath = await promptText(rl, "Where should I save the ICP JSON?", {
|
|
405
|
-
defaultValue: `./data/${slug}-icp-${market}.json`,
|
|
505
|
+
const icpPath = await promptText(rl, "Where is your ICP JSON file?", {
|
|
506
|
+
defaultValue: "./data/icp.json",
|
|
406
507
|
required: true
|
|
407
508
|
});
|
|
509
|
+
const limit = z.coerce.number().int().min(1).max(5000).parse(await promptText(rl, "How many leads should I look up?", { defaultValue: "100", required: true }));
|
|
510
|
+
const execute = await promptYesNo(rl, "Execute the BigQuery lookup now?", false);
|
|
511
|
+
writeWizardLine();
|
|
512
|
+
const icp = await readJsonFile(icpPath, IcpSchema);
|
|
513
|
+
const slug = slugify(icp.name) || "icp";
|
|
408
514
|
const sqlPath = await promptText(rl, "Where should I save the generated SQL?", {
|
|
409
|
-
defaultValue: `./data/${slug}-lookup
|
|
515
|
+
defaultValue: `./data/${slug}-lookup.sql`,
|
|
410
516
|
required: true
|
|
411
517
|
});
|
|
412
518
|
const rawPath = execute
|
|
413
519
|
? await promptText(rl, "Where should I save raw BigQuery rows?", {
|
|
414
|
-
defaultValue: `./data/${slug}-leads-raw
|
|
520
|
+
defaultValue: `./data/${slug}-leads-raw.json`,
|
|
415
521
|
required: true
|
|
416
522
|
})
|
|
417
523
|
: "";
|
|
418
524
|
const leadPath = execute
|
|
419
525
|
? await promptText(rl, "Where should I save normalized leads?", {
|
|
420
|
-
defaultValue: `./data/${slug}-leads
|
|
526
|
+
defaultValue: `./data/${slug}-leads.json`,
|
|
421
527
|
required: true
|
|
422
528
|
})
|
|
423
529
|
: "";
|
|
424
530
|
writeWizardLine();
|
|
425
|
-
const icp = buildVendorIcp(vendor, market);
|
|
426
|
-
await writeJsonFile(icpPath, icp);
|
|
427
531
|
const sql = buildBigQueryLeadLookupSql(icp, {
|
|
428
532
|
table: "icpidentifier.SalesGPT.leadPool_new",
|
|
429
533
|
companyField: "companyName",
|
|
@@ -451,15 +555,14 @@ async function runVendorLookupWizard(rl) {
|
|
|
451
555
|
await writeJsonFile(leadPath, normalizedLeads);
|
|
452
556
|
executedRowCount = parsedRows.length;
|
|
453
557
|
}
|
|
454
|
-
writeWizardLine(`
|
|
558
|
+
writeWizardLine(`Using ICP from ${icpPath}.`);
|
|
455
559
|
writeWizardLine(`Saved lookup SQL to ${sqlPath}.`);
|
|
456
560
|
if (execute) {
|
|
457
561
|
writeWizardLine(`Saved ${executedRowCount ?? 0} raw rows to ${rawPath}.`);
|
|
458
562
|
writeWizardLine(`Saved normalized leads to ${leadPath}.`);
|
|
459
563
|
}
|
|
460
564
|
writeWizardLine();
|
|
461
|
-
writeWizardLine("Equivalent raw
|
|
462
|
-
writeWizardLine(` ${buildCommandLine(["salesprompter", "icp:vendor", "--vendor", vendor, "--market", market, "--out", icpPath])}`);
|
|
565
|
+
writeWizardLine("Equivalent raw command:");
|
|
463
566
|
const lookupArgs = ["salesprompter", "leads:lookup:bq", "--icp", icpPath, "--limit", String(limit), "--sql-out", sqlPath];
|
|
464
567
|
if (execute) {
|
|
465
568
|
lookupArgs.push("--execute", "--out", rawPath, "--lead-out", leadPath);
|
|
@@ -467,13 +570,8 @@ async function runVendorLookupWizard(rl) {
|
|
|
467
570
|
writeWizardLine(` ${buildCommandLine(lookupArgs)}`);
|
|
468
571
|
}
|
|
469
572
|
async function runOutreachSyncWizard(rl) {
|
|
470
|
-
const target =
|
|
471
|
-
|
|
472
|
-
value: "instantly",
|
|
473
|
-
label: "Instantly",
|
|
474
|
-
description: "Create campaign leads from a scored leads JSON file"
|
|
475
|
-
}
|
|
476
|
-
], "instantly");
|
|
573
|
+
const target = "instantly";
|
|
574
|
+
writeWizardLine("Using Instantly.");
|
|
477
575
|
writeWizardLine();
|
|
478
576
|
if (!process.env.INSTANTLY_API_KEY || process.env.INSTANTLY_API_KEY.trim().length === 0) {
|
|
479
577
|
throw new Error("INSTANTLY_API_KEY is required for the Instantly sync flow.");
|
|
@@ -528,8 +626,8 @@ async function runWizard(options) {
|
|
|
528
626
|
if (runtimeOutputOptions.json || runtimeOutputOptions.quiet) {
|
|
529
627
|
throw new Error("wizard does not support --json or --quiet.");
|
|
530
628
|
}
|
|
531
|
-
writeWizardLine("Salesprompter
|
|
532
|
-
writeWizardLine("
|
|
629
|
+
writeWizardLine("Salesprompter");
|
|
630
|
+
writeWizardLine("Tell me what you want to do, and I will guide you through it.");
|
|
533
631
|
writeWizardLine();
|
|
534
632
|
await ensureWizardSession(options);
|
|
535
633
|
const rl = createInterface({
|
|
@@ -540,18 +638,21 @@ async function runWizard(options) {
|
|
|
540
638
|
const flow = await promptChoice(rl, "What do you want help with?", [
|
|
541
639
|
{
|
|
542
640
|
value: "vendor-icp",
|
|
543
|
-
label: "
|
|
544
|
-
description: "
|
|
641
|
+
label: "Figure out who to target",
|
|
642
|
+
description: "Create an ideal customer profile for your product",
|
|
643
|
+
aliases: ["icp", "ideal customer", "who to target", "targeting", "profile"]
|
|
545
644
|
},
|
|
546
645
|
{
|
|
547
646
|
value: "lead-generation",
|
|
548
647
|
label: "Generate leads",
|
|
549
|
-
description: "Find people at
|
|
648
|
+
description: "Find people at one company or from your BigQuery data",
|
|
649
|
+
aliases: ["leads", "find leads", "lead generation", "find people"]
|
|
550
650
|
},
|
|
551
651
|
{
|
|
552
652
|
value: "outreach-sync",
|
|
553
|
-
label: "
|
|
554
|
-
description: "
|
|
653
|
+
label: "Add leads to Instantly",
|
|
654
|
+
description: "Send a scored leads file to an Instantly campaign",
|
|
655
|
+
aliases: ["instantly", "outreach", "send leads", "campaign"]
|
|
555
656
|
}
|
|
556
657
|
], "vendor-icp");
|
|
557
658
|
writeWizardLine();
|
package/package.json
CHANGED