@dench.com/cli 3.5.8-staging.350 → 3.6.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/leads.ts CHANGED
@@ -651,14 +651,16 @@ export function listsHelp(): void {
651
651
  Usage:
652
652
  dench lists [--search "September"] [--limit 25] [--cursor <cursor>] [--json]
653
653
  dench lists show <id> [--json]
654
- dench lists save --title "Shortlist" --people-file <people.json> [--filters-json '{...}'] [--json]
654
+ dench lists save --title "Shortlist" --people-file <people.json> [--filters-json '{...}'] [--saved] [--json]
655
+ dench lists save --title "Target accounts" --companies-file <companies.json> [--json]
656
+ dench lists save --title "Open roles" --jobs-file <jobs.json> [--json]
655
657
  dench lists rows <id> [--enrichment all|enriched|in_progress|no_contacts|failed|not_enriched] [--limit 100] [--cursor <cursor>] [--json]
656
658
  dench lists rename <id> "New name" [--json]
657
659
  dench lists update <id> [--title "New name"] [--filters-json '{"titles":["CTO"]}'] [--json]
658
660
  dench lists delete <id> [--json]
659
661
 
660
662
  Lists are saved searches from Find (people / companies / job postings).
661
- People lists are not Leads. Select profiles in Find and confirm enrichment;
663
+ People lists are not CRM Leads. Searching and saving do not reveal new contact details;
662
664
  there is no mandatory per-profile ICP decision. Import only on explicit request, with the
663
665
  requested configured CRM status. Follow pagination until isDone, even across
664
666
  empty filtered pages. --rows uses original zero-based indices from lists rows.
@@ -668,9 +670,13 @@ empty filtered pages. --rows uses original zero-based indices from lists rows.
668
670
  Assessments files contain 1–100 {row, suggestion: {fit, score, reason}} items.
669
671
  Scores are AI relevance to the saved ICP, not conversion probabilities or CRM qualification.
670
672
  Create one with \`dench leads|companies|jobs find --save-as "Name"\`.
671
- Consolidate selected search profiles with \`dench lists save\`: --people-file
672
- is a JSON array of person rows, not a CSV or a whole tool response. It saves
673
+ Consolidate selected search results with \`dench lists save\`: choose --people-file,
674
+ --companies-file or --jobs-file. The JSON can be an array or a search response
675
+ containing the matching people, companies or jobs array. It saves
673
676
  a new list without searching, enriching, changing source lists, or importing CRM rows.
677
+ Pass --saved to show it in Find; otherwise it stays an unlisted draft accessible by id.
678
+ Add a saved people list to a campaign with \`dench campaign add <campaign> --list <id>\`.
679
+ Use \`--preview\` first to review the selection without importing or enrolling anyone.
674
680
  Delete removes the list, its exported CSV/JSONL files, and any linked watch.
675
681
  CRM lists (saved views on Leads/Companies/Job Postings) are managed with
676
682
  \`dench crm views list|get|create|rename|delete\`.
@@ -716,38 +722,67 @@ export async function runListsCommand(opts: {
716
722
  args.shift();
717
723
  if (verb === "save") {
718
724
  const title = getFlag(args, "--title")?.trim();
719
- const peopleFile = getFlag(args, "--people-file");
725
+ const sources = [
726
+ {
727
+ kind: "people",
728
+ file: getFlag(args, "--people-file"),
729
+ label: "person",
730
+ names: ["fullName", "name"],
731
+ },
732
+ {
733
+ kind: "companies",
734
+ file: getFlag(args, "--companies-file"),
735
+ label: "company",
736
+ names: ["name"],
737
+ },
738
+ {
739
+ kind: "jobs",
740
+ file: getFlag(args, "--jobs-file"),
741
+ label: "job",
742
+ names: ["title"],
743
+ },
744
+ ].filter((source) => source.file);
720
745
  const filtersJson = getFlag(args, "--filters-json");
746
+ const saved = hasFlag(args, "--saved");
721
747
  assertNoUnknownFlags(args, "lists save");
722
- if (!title || !peopleFile) {
748
+ if (!title || sources.length !== 1) {
723
749
  throw new FindCliError(
724
- "dench lists save requires --title and --people-file",
750
+ "dench lists save requires --title and exactly one of --people-file, --companies-file or --jobs-file",
725
751
  );
726
752
  }
727
- let people: unknown;
753
+ const source = sources[0];
754
+ const flag = `--${source.kind}-file`;
755
+ let rows: unknown;
728
756
  let filters: unknown;
729
757
  try {
730
- people = JSON.parse(await readFile(peopleFile, "utf-8"));
758
+ const content: unknown = JSON.parse(
759
+ await readFile(source.file!, "utf-8"),
760
+ );
761
+ rows = Array.isArray(content)
762
+ ? content
763
+ : content && typeof content === "object"
764
+ ? (content as Record<string, unknown>)[source.kind]
765
+ : content;
731
766
  } catch {
732
767
  throw new FindCliError(
733
- "--people-file must be a readable JSON file containing an array of person rows",
768
+ `${flag} must be a readable JSON file containing an array of ${source.label} rows`,
734
769
  );
735
770
  }
736
771
  if (
737
- !Array.isArray(people) ||
738
- people.length === 0 ||
739
- people.some(
740
- (person) =>
741
- !person ||
742
- typeof person !== "object" ||
743
- Array.isArray(person) ||
744
- ![person.fullName, person.name].some(
745
- (name) => typeof name === "string" && name.trim(),
772
+ !Array.isArray(rows) ||
773
+ rows.length === 0 ||
774
+ rows.some(
775
+ (row) =>
776
+ !row ||
777
+ typeof row !== "object" ||
778
+ Array.isArray(row) ||
779
+ !source.names.some(
780
+ (name) => typeof row[name] === "string" && row[name].trim(),
746
781
  ),
747
782
  )
748
783
  ) {
749
784
  throw new FindCliError(
750
- "--people-file must contain a non-empty array of person rows, each with name or fullName",
785
+ `${flag} must contain a non-empty array of ${source.label} rows, each with ${source.names.join(" or ")}`,
751
786
  );
752
787
  }
753
788
  if (filtersJson !== undefined) {
@@ -763,9 +798,10 @@ export async function runListsCommand(opts: {
763
798
  out(
764
799
  ctx,
765
800
  await callAction(ctx, api.saveFind, {
766
- createdFrom: "find_people",
801
+ createdFrom: `find_${source.kind}`,
767
802
  title,
768
- people,
803
+ [source.kind]: rows,
804
+ ...(saved ? { saved: true } : {}),
769
805
  ...(filters !== undefined ? { filters } : {}),
770
806
  }),
771
807
  );
@@ -1096,6 +1132,7 @@ export async function runLeadsCommand(opts: {
1096
1132
  out(ctx, {
1097
1133
  ...(artifact as Record<string, unknown>),
1098
1134
  metadata: result.metadata,
1135
+ billing: result.billing ?? null,
1099
1136
  ...(companyList ? { companyList: companyListInfo(companyList) } : {}),
1100
1137
  });
1101
1138
  } catch (error) {
@@ -1345,6 +1382,7 @@ export async function runCompaniesCommand(opts: {
1345
1382
  out(ctx, {
1346
1383
  ...(artifact as Record<string, unknown>),
1347
1384
  metadata: result.metadata,
1385
+ billing: result.billing ?? null,
1348
1386
  });
1349
1387
  } catch (error) {
1350
1388
  throw new FindCliError(