salesprompter-cli
v0.1.17
Published
JSON-first sales prospecting CLI for ICP definition, lead generation, enrichment, scoring, and CRM/outreach sync.
Maintainers
Readme
salesprompter-cli
salesprompter-cli is a JSON-first command line interface for running a practical sales workflow:
- Authenticate via Salesprompter app backend
- Define an ideal customer profile
- Resolve a target account from a domain
- Generate seed leads
- Enrich leads
- Score leads
- Sync leads into CRM and outreach systems
- Analyze upstream lead-list and domain-enrichment bottlenecks
- Replace opaque Pipedream logic with deterministic CLI workflows
It is built for two users at the same time:
- humans working in a terminal
- coding agents such as Codex, Claude Code, and other LLM-driven shell workflows
Start Here
If someone discovers Salesprompter from a vague prompt, give them the shortest working path for their context.
## Human-friendly guided path
npx -y salesprompter-cli@latest
## Explicit guided path
npx -y salesprompter-cli@latest wizard
## Raw command surface for agents and scripts
npx salesprompter-cli@latest --helpOr install it globally:
npm install -g salesprompter-cli
salesprompter
salesprompter wizard
salesprompter --helpBare salesprompter now opens a guided wizard in an interactive terminal. Keep using explicit subcommands for agents, CI, and copy-paste docs.
If your Salesprompter user belongs to multiple organizations, browser login asks which organization the CLI session should use.
Prompt To Command
If the user says something like "I need to determine the ICP of deel.com", there are two different meanings.
1. They want leads at Deel itself
This means Deel is the target account.
salesprompter account:resolve --domain deel.com --company-name Deel --out ./data/deel-account.json
salesprompter leads:generate --icp ./data/icp.json --count 5 --domain deel.com --company-name Deel --out ./data/deel-leads.json2. They sell for Deel and need Deel's ideal customer profile
This means Deel is the vendor, not the target account.
salesprompter icp:vendor --vendor deel --market dach --out ./data/deel-icp.json
salesprompter leads:lookup:bq --icp ./data/deel-icp.json --limit 100 --execute --out ./data/deel-leads-raw.json --lead-out ./data/deel-leads.json
salesprompter leads:enrich --in ./data/deel-leads.json --out ./data/deel-enriched.json
salesprompter leads:score --icp ./data/deel-icp.json --in ./data/deel-enriched.json --out ./data/deel-scored.json3. They want the LLM to call the CLI directly
Use the same commands, but prefer machine-readable output:
salesprompter --json icp:vendor --vendor deel --market dach
salesprompter --json icp:vendor --vendor deel --market dach --out ./data/deel-icp.json
salesprompter --json leads:lookup:bq --icp ./data/deel-icp.json --limit 100 --lead-out ./data/deel-leads.jsonDocumentation
This repository now includes the public Salesprompter docs site for the wider Salesprompter universe, including the app contract, CLI surface, Chrome extension contract, and the main warehouse-backed workflows.
Live docs:
https://salesprompter-cli.vercel.appDocs home:
./index.mdxQuickstart:
./quickstart.mdxArchitecture:
./architecture.mdxApp:
./platform/app.mdxCLI:
./platform/cli.mdxChrome extension:
./platform/chrome-extension.mdxDomain finder:
./workflows/domain-finder.mdxCommand reference:
./reference/cli.mdxEnvironment variables:
./reference/environment-variables.mdxTroubleshooting:
./operations/troubleshooting.mdx
Run the docs locally with:
npm run docs:devBuild the deployable static docs site with:
npm run build:docs:siteIntegration Contract
This CLI is not a standalone toy. It is a production integration surface for the Salesprompter app.
- The Domain Finder flow in this repository is a real end-to-end test case for Salesprompter app integration.
- CLI behavior should be treated as an app contract: auth, BigQuery execution, artifacts, and writeback semantics must remain stable.
- Changes to domain selection, writeback, or audit logic should always be validated with:
- CLI tests (
npm test) - BigQuery-backed runs (
domainfinder:run:bq,domainfinder:audit-existing:bq) - before/after delta checks (
domainfinder:audit-delta)
- CLI tests (
The current version is account-first. It resolves a company from a domain, then generates contacts for that account through provider interfaces. The default providers are still fallback heuristics, but the JSON contracts are now stable for real company and people-data integrations.
When the output mode is fallback, the leads are modeled contacts for workflow testing, not verified real contacts. A real provider path should return mode: "real" using the same JSON shape.
All non-auth commands require a logged-in CLI session. This gives you one identity model across Salesprompter app, CLI, and Chrome extension.
Global output flags:
--json: compact machine-readable JSON (optimized for agent/LLM parsers)--quiet: suppress successful stdout payloads (errors still surface)
Auth and Session
The CLI stores a local session file at ~/.config/salesprompter/auth-session.json (or SALESPROMPTER_CONFIG_DIR).
# Preferred path: browser/device login
salesprompter auth:login
# Fallback path: generate a short-lived CLI token in the Salesprompter app
salesprompter auth:login --token "$SALESPROMPTER_TOKEN" --api-url "https://salesprompter.ai"
# Verify active identity with backend
salesprompter auth:whoami --verify
# Clear local session
salesprompter auth:logoutIf your user belongs to multiple organizations, the browser flow asks you to choose the organization for that CLI session before returning to the terminal.
Environment variables:
SALESPROMPTER_API_BASE_URL: override backend URL (defaulthttps://salesprompter.ai)SALESPROMPTER_CONFIG_DIR: override local config dirSALESPROMPTER_SKIP_AUTH=1: bypass auth guard (tests/dev only)INSTANTLY_API_KEY: required for realsync:outreach --target instantlyINSTANTLY_CAMPAIGN_ID: default campaign id for Instantly syncSALESPROMPTER_INSTANTLY_BASE_URL: override Instantly API base URL (tests/local proxies)
App compatibility:
- Salesprompter app should expose
/api/cli/auth/device/start,/api/cli/auth/device/poll, and/api/cli/auth/me. salesprompter auth:loginuses browser/device login and prints the verification URL plus code before polling.POST /api/cli/auth/tokenremains the fallback path when browser/device login is disabled or unavailable.
Fallback command:
salesprompter auth:login --token "<token-from-app>" --api-url "https://salesprompter.ai"Why this shape works for humans and LLMs
- Every command reads and writes plain JSON.
- Output is machine-readable and composable (
--jsonfor compact transport). - The top-level use cases map ambiguous prompts like "determine the ICP of deel.com" into explicit command paths.
- Domain contracts are explicit and validated with
zod. - External integrations are behind narrow provider interfaces.
- Lead generation reports which provider and mode produced the result.
- Workflow bottlenecks become inspectable artifacts instead of hidden Pipedream state.
- Real prospect lookup can be normalized straight into the CLI lead schema with
--lead-out.
Commands
salesprompter icp:define --name "EU SaaS RevOps" \
--description "RevOps and sales leaders in European growth-stage software companies" \
--industries "Software,Financial Services" \
--company-sizes "50-199,200-499" \
--regions "Europe" \
--countries "DE,NL,GB" \
--titles "Head of Revenue Operations,VP Sales" \
--required-signals "recent funding,growing outbound team" \
--keywords "revenue operations,outbound,sales tooling" \
--out ./data/icp.json
salesprompter auth:login
salesprompter auth:whoami --verify
salesprompter icp:vendor --vendor deel --market dach --out ./data/deel-icp.json
salesprompter leads:lookup:bq --icp ./data/deel-icp.json --limit 100 --execute --out ./data/deel-leads-raw.json --lead-out ./data/deel-leads.json
salesprompter leads:enrich --in ./data/deel-leads.json --out ./data/deel-enriched.json
salesprompter leads:score --icp ./data/deel-icp.json --in ./data/deel-enriched.json --out ./data/deel-scored.json
salesprompter sync:outreach --target instantly --in ./data/deel-scored.json --campaign-id "$INSTANTLY_CAMPAIGN_ID"
salesprompter sync:outreach --target instantly --in ./data/deel-scored.json --campaign-id "$INSTANTLY_CAMPAIGN_ID" --apply
salesprompter icp:from-historical-queries:bq --vendor deel --market dach --out ./data/deel-icp-historical.json --report-out ./data/deel-historical-report.json
salesprompter leadlists:funnel:bq --vendor deel --market dach --out ./data/deel-leadlists-funnel.json
salesprompter domainfinder:backlog:bq --market dach --out ./data/deel-domainfinder-backlog.json
salesprompter domainfinder:candidates:bq --market dach --limit 500 --out ./data/domain-candidates.json --sql-out ./data/domain-candidates.sql
salesprompter domainfinder:input-sql --market dach --out ./data/domainFinder_input_v2.sql
salesprompter domainfinder:select --in ./data/domain-candidates.json --out ./data/domain-decisions.json
salesprompter domainfinder:audit --in ./data/domain-decisions.json --out ./data/domain-audit.json
salesprompter domainfinder:compare-pipedream --in ./data/domain-candidates.json --out ./data/domain-comparison.json
salesprompter domainfinder:audit-existing:bq --market dach --out ./data/domain-existing-audit.json
salesprompter domainfinder:audit-delta --before ./data/domain-existing-audit-before.json --after ./data/domain-existing-audit-after.json --out ./data/domain-existing-audit-delta.json
salesprompter domainfinder:repair-existing:bq --market dach --mode conservative --limit 5000 --out ./data/domain-repair.sql --trace-id salesprompter-cli-repair-dach
salesprompter domainfinder:writeback-sql --in ./data/domain-decisions.json --out ./data/domain-writeback.sql --trace-id salesprompter-cli-dach-20260308
salesprompter domainfinder:writeback:bq --in ./data/domain-decisions.json --out ./data/domain-writeback.sql --trace-id salesprompter-cli-dach-20260308
salesprompter domainfinder:run:bq --market dach --limit 500 --out-dir ./data/domainfinder-run --trace-id salesprompter-cli-dach-20260308
salesprompter account:resolve --domain deel.com --company-name Deel --out ./data/deel-account.json
salesprompter leads:generate --icp ./data/icp.json --count 5 --out ./data/leads.json
salesprompter leads:generate --icp ./data/icp.json --count 5 --domain deel.com --company-name Deel --out ./data/deel-leads.json
salesprompter leads:enrich --in ./data/leads.json --out ./data/enriched.json
salesprompter leads:score --icp ./data/icp.json --in ./data/enriched.json --out ./data/scored.json
salesprompter leads:lookup:bq --icp ./data/deel-icp.json --limit 100
salesprompter queries:analyze:bq --search-kind sales-people --include-function "Human Resources" --out ./data/hr-query-report.json
salesprompter sync:crm --target hubspot --in ./data/scored.json
salesprompter sync:outreach --target instantly --in ./data/scored.jsonDomain Finder Migration
The original Pipedream domainFinder workflow was doing three things:
- fetch a small input set from BigQuery
- ask OpenAI / Hunter for candidate domains
- pick a domain and write it back
The CLI now models that logic directly and improves it:
domainfinder:backlog:bqmeasures whether the backlog is being starved before enrichmentdomainfinder:candidates:bqfetches real candidate rows from BigQuery for backlog companiesdomainfinder:input-sqlgenerates a replacement input view driven bylinkedin_companies, notleadPool_newdomainfinder:selectapplies deterministic domain selection rulesdomainfinder:auditturns decisions into a review queue and writeback summarydomainfinder:compare-pipedreamquantifies how often the old selector would disagree with the improved selectordomainfinder:audit-existing:bqmeasures current warehouse-visible mismatches and bad chosen domainsdomainfinder:audit-deltacompares two audit snapshots and reports metric deltasdomainfinder:repair-existing:bqgenerates (and optionally executes) targeted repair writes with selectable mode:conservative: only missing/blacklisted chosen domainsaggressive: missing/blacklisted plus all mismatchesmismatch-only: only mismatched chosen domains
domainfinder:writeback-sqlemits conservative SQL fordomainFinder_outputdomainfinder:writeback:bqcan execute that writeback in BigQuery when explicitly askeddomainfinder:run:bqruns the full candidate -> decision -> audit -> writeback pipeline and stores all artifacts
Improved selection policy:
- prefer
domain_linkedinwhen present and not blacklisted - otherwise prefer
website_linkedinroot domain when present and not blacklisted - otherwise choose the non-blacklisted candidate with the highest Hunter email count
- otherwise fall back to the first non-null candidate
This removes the earlier failure mode where OpenAI or Hunter could override a good LinkedIn domain purely because of a higher Hunter count.
Writeback policy:
- write to
SalesPrompter.domainFinder_output, which is the source feedinglinkedin_companies.domain - exclude
no-domaindecisions - exclude blacklisted domains from generated writeback SQL
- preserve batch provenance through
trace_id
Development
npm install
npm run build
node ./dist/cli.js --help
npm testBigQuery project selection:
- The CLI runs
bq querywith--project_idfromBQ_PROJECT_ID. - Fallback order:
BQ_PROJECT_ID->GOOGLE_CLOUD_PROJECT->GCLOUD_PROJECT->icpidentifier.
Real Deel Flow
For Deel as the vendor you sell for, do not use --domain deel.com. That path targets contacts at Deel itself.
Use this path instead:
salesprompter icp:vendor --vendor deel --market dach --out ./data/deel-icp.jsonsalesprompter leads:lookup:bq --icp ./data/deel-icp.json --limit 100 --execute --out ./data/deel-leads-raw.json --lead-out ./data/deel-leads.jsonsalesprompter leads:enrich --in ./data/deel-leads.json --out ./data/deel-enriched.jsonsalesprompter leads:score --icp ./data/deel-icp.json --in ./data/deel-enriched.json --out ./data/deel-scored.jsonsalesprompter sync:outreach --target instantly --in ./data/deel-scored.json --campaign-id "$INSTANTLY_CAMPAIGN_ID"- Add
--applyonly when the dry-run output looks correct.
Next integrations
- Replace
HeuristicCompanyProviderwith a real account lookup provider. - Replace
HeuristicPeopleSearchProviderwith Apollo, Clay, LinkedIn, or custom people-data providers. - Replace
HeuristicEnrichmentProviderwith enrichment APIs such as Clearbit, FullEnrich, or custom LLM workflows. - Replace
DryRunSyncProviderwith real HubSpot, Salesforce, Pipedrive, Instantly, Apollo, or Outreach clients. - Add provider selection and credentials for a first real
--domain deel.comworkflow. - Replace configurable
bqfield mapping with a typed adapter per warehouse schema. - Add a real candidate-fetch command that reads domain candidates from BigQuery and feeds them into
domainfinder:select.
