gtm-now 0.10.0 → 0.10.1

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/index.js CHANGED
@@ -10713,8 +10713,8 @@ var init_prospect = __esm({
10713
10713
  },
10714
10714
  output: {
10715
10715
  type: "string",
10716
- enum: ["file", "inline"],
10717
- description: 'Output mode. "file" writes results to JSONL file (auto when >20 results). "inline" returns in response.'
10716
+ enum: ["file", "inline", "csv"],
10717
+ description: 'Output mode. "file" writes JSONL (auto when >20 results). "csv" writes a CSV file for spreadsheet review. "inline" returns in response.'
10718
10718
  },
10719
10719
  detail: {
10720
10720
  type: "string",
@@ -10825,8 +10825,8 @@ var init_prospect = __esm({
10825
10825
  },
10826
10826
  output: {
10827
10827
  type: "string",
10828
- enum: ["file", "inline"],
10829
- description: 'Output mode. "file" writes results to JSONL file (auto when >20 results). "inline" returns in response.'
10828
+ enum: ["file", "inline", "csv"],
10829
+ description: 'Output mode. "file" writes JSONL (auto when >20 results). "csv" writes a CSV file for spreadsheet review. "inline" returns in response.'
10830
10830
  },
10831
10831
  detail: {
10832
10832
  type: "string",
@@ -10849,7 +10849,7 @@ var init_enrich = __esm({
10849
10849
  enrichTools = [
10850
10850
  {
10851
10851
  name: "gtm_enrich",
10852
- description: "Email/phone enrichment via waterfall \u2014 tries providers in order, stops on first match. Always dry_run first for batches > 5.",
10852
+ description: 'Email enrichment via waterfall \u2014 tries providers in order, stops on first match. Always dry_run first for batches > 5. Defaults to email only. Phone costs extra \u2014 only add find: ["email", "phone"] when the user explicitly requests phone numbers.',
10853
10853
  inputSchema: {
10854
10854
  type: "object",
10855
10855
  properties: {
@@ -10876,7 +10876,7 @@ var init_enrich = __esm({
10876
10876
  enum: ["email", "phone", "title", "company", "funding", "tech_stack"]
10877
10877
  },
10878
10878
  default: ["email"],
10879
- description: "Fields to find. Currently supported: email, phone. Additional fields (title, company, funding, tech_stack) are accepted but return an informative error until Phase 2."
10879
+ description: 'Fields to find. Defaults to ["email"] only. Add "phone" ONLY when explicitly requested \u2014 phone lookups cost extra. Additional fields (title, company, funding, tech_stack) return an informative error until Phase 2.'
10880
10880
  },
10881
10881
  waterfall: {
10882
10882
  type: "array",
@@ -11347,7 +11347,9 @@ After finding people, use:
11347
11347
 
11348
11348
  Known limitations:
11349
11349
  - department + seniority filters combined may return 0 results (Disco API uses strict AND). Use one or the other.`,
11350
- gtm_enrich: `Email and phone enrichment via waterfall \u2014 tries multiple providers in order, stops on first match. Supports 1-100 contacts per call.
11350
+ gtm_enrich: `Email enrichment via waterfall \u2014 tries multiple providers in order, stops on first match. Supports 1-100 contacts per call.
11351
+
11352
+ IMPORTANT: Defaults to email only. Phone enrichment costs extra per lookup \u2014 only include when the user explicitly asks for phone numbers.
11351
11353
 
11352
11354
  Always do a dry_run first for batches > 5 to preview cost.
11353
11355
 
@@ -11444,6 +11446,7 @@ var init_tools = __esm({
11444
11446
  "cold_outreach",
11445
11447
  "linkedin_outreach",
11446
11448
  "crm_sync",
11449
+ "export_to_webhook",
11447
11450
  "list_tasks"
11448
11451
  ],
11449
11452
  description: "The workflow to get guidance for"
@@ -11536,6 +11539,12 @@ Start by identifying what kind of companies you're targeting:
11536
11539
  3. gtm_enrich \u2014 get emails
11537
11540
  4. gtm_campaign \u2014 personalized outreach referencing their engagement
11538
11541
 
11542
+ ## Review before enriching
11543
+ After finding companies, review the list before spending enrichment credits:
11544
+ - Use output: 'csv' to write results to a spreadsheet for visual review
11545
+ - Or export to Clay/webhook via gtm_fetch POST for team review (see export_to_webhook guide)
11546
+ - Use gtm_ai with action: 'classify' to auto-filter competitors or bad fits
11547
+
11539
11548
  ## Key principle
11540
11549
  Always dry_run enrichment first. Credits are spent per successful find \u2014 previewing cost avoids surprises.`,
11541
11550
  enrich_list: `# Enrich a Contact List \u2014 Workflow
@@ -11611,6 +11620,26 @@ LinkedIn outreach requires LinkedIn profile URLs \u2014 not emails. Extract prof
11611
11620
 
11612
11621
  ## Key principle
11613
11622
  Always search before creating to avoid duplicates. Most CRMs have built-in email dedup but better to check first.`,
11623
+ export_to_webhook: `# Export Data to Webhook \u2014 Workflow
11624
+
11625
+ Push GTM data to Clay, Make, Zapier, or any webhook endpoint for visual review or further processing.
11626
+
11627
+ ## Steps
11628
+ 1. FIND \u2014 Get your companies or contacts
11629
+ \u2192 gtm_find_companies({ icp_text: "...", limit: 50 })
11630
+ \u2192 gtm_find_people({ company_domain: "...", job_title: "CEO" })
11631
+
11632
+ 2. EXPORT \u2014 Push to webhook via gtm_fetch
11633
+ \u2192 gtm_fetch({ url: "https://your-webhook-url", method: "POST", body: { results: [...] } })
11634
+
11635
+ ## Clay Integration
11636
+ Clay table webhooks accept JSON directly:
11637
+ \u2192 gtm_fetch({ url: "https://api.clay.com/v3/sources/webhook/...", method: "POST", body: { first_name: "...", last_name: "...", company: "...", ... } })
11638
+
11639
+ Send one record per request, or batch them depending on the webhook's format.
11640
+
11641
+ ## Key principle
11642
+ Review data visually before spending enrichment credits. Export \u2192 review \u2192 enrich only the good leads.`,
11614
11643
  list_tasks: `# Available GTM Workflows
11615
11644
 
11616
11645
  Call gtm_guide with one of these tasks:
@@ -11620,6 +11649,7 @@ Call gtm_guide with one of these tasks:
11620
11649
  - cold_outreach \u2014 Enrich + personalize + push to cold email campaign
11621
11650
  - linkedin_outreach \u2014 Extract profiles + personalize + push to LinkedIn campaign
11622
11651
  - crm_sync \u2014 Find/create/update people and deals in CRM
11652
+ - export_to_webhook \u2014 Push data to Clay, Make, Zapier, or any webhook for review
11623
11653
 
11624
11654
  New tools available:
11625
11655
  - gtm_find_companies (provider: storeleads) \u2014 Search 1.2M+ e-commerce stores by category, revenue, platform
@@ -11703,6 +11733,7 @@ import * as os from "os";
11703
11733
  function shouldWriteToFile(count, output) {
11704
11734
  if (output === "file") return true;
11705
11735
  if (output === "inline") return false;
11736
+ if (output === "csv") return false;
11706
11737
  return count > FILE_OUTPUT_THRESHOLD;
11707
11738
  }
11708
11739
  function writeResultsFile(tool, results, meta, resultsDir = DEFAULT_RESULTS_DIR) {
@@ -11720,6 +11751,40 @@ function writeResultsFile(tool, results, meta, resultsDir = DEFAULT_RESULTS_DIR)
11720
11751
  }
11721
11752
  return filePath;
11722
11753
  }
11754
+ function writeResultsCsv(tool, results, resultsDir = DEFAULT_RESULTS_DIR) {
11755
+ fs.mkdirSync(resultsDir, { recursive: true });
11756
+ const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
11757
+ const filename = `${tool}-${timestamp}.csv`;
11758
+ const filePath = path.join(resultsDir, filename);
11759
+ if (results.length === 0) {
11760
+ fs.writeFileSync(filePath, "", "utf-8");
11761
+ return filePath;
11762
+ }
11763
+ const headers = Object.keys(results[0]);
11764
+ const headerLine = headers.map(escapeCsvField).join(",");
11765
+ const dataLines = results.map(
11766
+ (row) => headers.map((h) => escapeCsvField(formatCsvValue(row[h]))).join(",")
11767
+ );
11768
+ const content = [headerLine, ...dataLines].join("\n") + "\n";
11769
+ fs.writeFileSync(filePath, content, "utf-8");
11770
+ try {
11771
+ cleanOldResults(7, resultsDir);
11772
+ } catch {
11773
+ }
11774
+ return filePath;
11775
+ }
11776
+ function formatCsvValue(value) {
11777
+ if (value === null || value === void 0) return "";
11778
+ if (Array.isArray(value)) return value.join(";");
11779
+ if (typeof value === "object") return JSON.stringify(value);
11780
+ return String(value);
11781
+ }
11782
+ function escapeCsvField(value) {
11783
+ if (value.includes(",") || value.includes('"') || value.includes("\n")) {
11784
+ return `"${value.replace(/"/g, '""')}"`;
11785
+ }
11786
+ return value;
11787
+ }
11723
11788
  function cleanOldResults(maxAgeDays, resultsDir = DEFAULT_RESULTS_DIR) {
11724
11789
  let entries;
11725
11790
  try {
@@ -11843,6 +11908,19 @@ async function findCompanies(args, ctx) {
11843
11908
  if (query.max_revenue != null) applied_filters.max_revenue = query.max_revenue;
11844
11909
  const providerUsed = provider.name;
11845
11910
  const output = args.output;
11911
+ if (output === "csv") {
11912
+ const filePath = writeResultsCsv("find_companies", slimmedResults);
11913
+ return {
11914
+ provider: providerUsed,
11915
+ count: results.length,
11916
+ has_more,
11917
+ output: "csv",
11918
+ file: filePath,
11919
+ preview: slimmedResults.slice(0, 3),
11920
+ columns: slimmedResults.length > 0 ? Object.keys(slimmedResults[0]) : [],
11921
+ ...warnings?.length ? { warnings } : {}
11922
+ };
11923
+ }
11846
11924
  if (shouldWriteToFile(results.length, output)) {
11847
11925
  const meta = {
11848
11926
  query: applied_filters,
@@ -11936,6 +12014,19 @@ async function findPeople(args, ctx) {
11936
12014
  if (query.revenue_range) applied_filters.revenue_range = query.revenue_range;
11937
12015
  const providerUsed = provider.name;
11938
12016
  const output = args.output;
12017
+ if (output === "csv") {
12018
+ const filePath = writeResultsCsv("find_people", slimmedResults);
12019
+ return {
12020
+ provider: providerUsed,
12021
+ count: dedupedResults.length,
12022
+ has_more,
12023
+ output: "csv",
12024
+ file: filePath,
12025
+ preview: slimmedResults.slice(0, 3),
12026
+ columns: slimmedResults.length > 0 ? Object.keys(slimmedResults[0]) : [],
12027
+ ...warnings?.length ? { warnings } : {}
12028
+ };
12029
+ }
11939
12030
  if (shouldWriteToFile(dedupedResults.length, output)) {
11940
12031
  const meta = {
11941
12032
  query: applied_filters,
@@ -13344,7 +13435,7 @@ var init_validation = __esm({
13344
13435
  platform: z2.string().optional(),
13345
13436
  min_revenue: z2.number().optional(),
13346
13437
  max_revenue: z2.number().optional(),
13347
- output: z2.enum(["file", "inline"]).optional(),
13438
+ output: z2.enum(["file", "inline", "csv"]).optional(),
13348
13439
  detail: detailSchema
13349
13440
  }),
13350
13441
  // 2. People/contact search
@@ -13385,7 +13476,7 @@ var init_validation = __esm({
13385
13476
  max_employees: z2.number().int().min(1).optional(),
13386
13477
  revenue_range: z2.string().optional(),
13387
13478
  fields: z2.union([z2.literal("all"), z2.array(z2.string())]).optional(),
13388
- output: z2.enum(["file", "inline"]).optional(),
13479
+ output: z2.enum(["file", "inline", "csv"]).optional(),
13389
13480
  detail: detailSchema
13390
13481
  }).refine(
13391
13482
  (data) => !!(data.job_title || data.company_name || data.company_domain || data.company_domains && data.company_domains.length > 0 || data.seniority && data.seniority.length > 0 || data.filter_industry && data.filter_industry.length > 0 || data.skills && data.skills.length > 0 || data.location),
@@ -13520,6 +13611,7 @@ var init_validation = __esm({
13520
13611
  "cold_outreach",
13521
13612
  "linkedin_outreach",
13522
13613
  "crm_sync",
13614
+ "export_to_webhook",
13523
13615
  "list_tasks"
13524
13616
  ])
13525
13617
  }),