ethanplusai/harvey

▲ 248 stars today★ 3,343⑂ 673

Autonomous AI sales agent powered by Claude Code or Codex. Finds prospects, writes cold emails, sends campaigns, handles replies, books meetings.

About ethanplusai/harvey

ethanplusai/harvey is an open-source project on GitHub, mainly written in Python. Autonomous AI sales agent powered by Claude Code or Codex. Finds prospects, writes cold emails, sends campaigns, handles replies, books meetings. It currently holds 3,343 stars and 673 forks with 31 open issues, and was last pushed on 2026-10-27 (repository created 2026-03-12).

Project Overview

Git Homed tracks it on the Today's Trending board, currently at rank #35 with 248 new stars today.

GitHub Repository Details

Repository ethanplusai/harvey · default branch main · size 12992 KB · watchers 22 · source: GitHub REST API and repository README

README

Harvey

An autonomous sales agent that runs on your Claude Code or Codex subscription.

Harvey finds businesses worth selling to, learns something specific about each one, writes cold email that references it, sends it, reads the replies, and works the conversation toward a meeting. It runs on your machine, in a loop, on its own.

The unusual part: there is no model API bill. Harvey uses your Claude Pro or Max subscription through Claude Code, or your ChatGPT subscription through Codex. Model calls use the plan you already pay for, without an API key or per-token API billing. Claude is the default; Codex is an opt-in provider on macOS and Linux.

$ harvey run

============================================================ Harvey is online. Always Be Closing. ============================================================ Checking pipeline state... Decision: handle_replies (2 new replies) handler → Elena Park, Harbor Dental Studio interested, answer drafted handler → Priya Nair, Highline Roofing objection, answer drafted Decision: write_campaign (3 new prospects with no drafts) writer → 2-email sequence: Phoenix HVAC hiring a dispatcher outbox → 5 emails waiting for your approval Cycle complete. Sleeping for 15 minutes.

And the dashboard it keeps, at localhost:5555. In this order: the Today screen, a conversation Harvey handed back because a prospect asked for a contract, approving a drafted reply from the outbox, confirming a signal, building a cohort from confirmed signals, searching contacts, and the same screen in dark mode:

Harvey's dashboard: the Today screen, a conversation handed back for a human, approving a reply in the outbox, confirming a signal and building a cohort, searching contacts, then dark mode

The demo data is invented: "Bookwell" selling online booking to roofers, dental practices and HVAC shops, on .test domains.

---

Table of contents

---

Why the subscription thing matters

Every other autonomous sales agent bills you per token. That is the whole reason they cost what they cost: an agent that thinks in a loop, all day, is an agent that burns API credits in a loop, all day. Vendors solve this by thinking less — shorter prompts, cheaper models, fewer passes.

Harvey uses the subscription-backed CLI for the provider you choose: claude for Claude Code, or an isolated codex app-server for Codex. Both return text with model tools disabled. Harvey handles the prospecting and mail operations itself.

Claude calls run with none of your own Claude Code setup: no CLAUDE.md (yours, the project's or any parent folder's), no hooks, skills, plugins, MCP servers or settings files, no tools, in an empty temporary folder, and nothing saved to your Claude transcripts. Your login (subscription or claude setup-token) and any admin-managed policy still apply. This needs a Claude CLI with --safe-mode; with an older one Harvey pauses instead of sending prompts unisolated and says to run claude update. To check this offline against your installed CLI, with no credentials or network: HARVEY_CLAUDE_CLI_TESTS=1 pytest tests/test_claude_cli_isolation.py.

Your selected subscription covers model calls, subject to its usage limits. That lets Harvey use its skills library for personalization and reply handling without a separate model API bill.

What this costs you:

| | | |---|---| | Claude Pro/Max or a ChatGPT plan with Codex access | your existing subscription | | Finding businesses | $0 on the default source, or ~$0.37 per 1,000 on the cheapest paid one | | Reading their websites | $0 — three HTTP requests each, no model call | | Writing and sending | your subscription + a mailbox (~$7/mo Google Workspace) | | Prospecting tools | none. No Apollo, no ZoomInfo, no Clearbit, no Clay. |

For comparison, the tools Harvey replaces start at $250–500/month — and they are weakest precisely where Harvey is strongest: small local businesses with 5–50 people, where single-provider contact coverage caps out around 30% and micro-businesses are frequently absent entirely.

Harvey also budgets itself. It reads the active provider's subscription quota and throttles to leave headroom for your own Claude or Codex work. Set max_daily_claude_percent: 80 for either provider—the setting's name predates Codex support. If quota cannot be read, Harvey reports it as unavailable and falls back to its daily call budget.

Harvey runs locally with your own login. Check the terms and usage limits of the provider you choose.

---

What Harvey actually does

Harvey runs a heartbeat: wake up, check the budget, decide what most needs doing, do it, log it, sleep. Every 15 minutes by default.

                 ┌──────────────────────────────────────┐
                 ↓                                      │
   Wake  →  Quiet hours?  →  Budget OK?  →  Decide  →  Act  →  Log  →  Sleep
                 │               │
              (sleep)         (sleep)

It decides deterministically, not by asking a model what to do — that would burn a call on something derivable from four counts:

handle replies  >  send campaigns  >  write campaigns  >  prospect  >  idle (analyze)

Profiling and outbox draining ride along on every cycle regardless, because they cost nothing and have to happen on schedule.

---

Quick start

Prerequisites

Easiest: let Claude set it up

git clone https://github.com/ethanplusai/harvey.git
cd harvey
claude

Then say: "set up Harvey for me". The repo ships a CLAUDE.md that turns Claude Code into the setup wizard — it checks what state you're in, installs what's missing, asks what it needs, and trains Harvey on your product.

Or do it yourself

# 1. Install — note python3.13, not python3. On macOS bare python3 is the

system 3.9, and a venv built from it can't run Harvey.

python3.13 -m venv .venv && source .venv/bin/activate && pip install -e .

2. Configure — every variable is documented inline with where to get it

cp .env.example .env && $EDITOR .env

3. Learn your product from your website (writes harvey.local.yaml; re-running

it updates the product, targeting and company and keeps your other settings)

harvey train https://your-company.com

4. Choose what makes a good prospect (see below — this one matters)

harvey signals --confirm free

5. Find businesses. Free source, no account needed.

harvey discover

6. Watch it work

harvey dashboard # prints a private link to http://127.0.0.1:5555 harvey run

Or deploy with Docker (Linux)

Docker Compose runs the dashboard; start and stop the heartbeat from its Actions card. Host networking preserves the dashboard's loopback-only bind at 127.0.0.1:5555. This Compose setup supports native Linux; use the pip install above on macOS or Windows. On a remote Linux host, reach the dashboard through an SSH tunnel rather than exposing its port to the network.

git clone https://github.com/ethanplusai/harvey.git
cd harvey

Create the credentials file only if you do not already have one.

test -e .env || cp .env.example .env

Run as your Linux user, never root. Set these again when rebuilding.

export HARVEY_UID=$(id -u) HARVEY_GID=$(id -g)

Sign in to Claude on the host first; ~/.claude must already exist.

The image ships Claude only; Codex is not included.

docker compose build docker compose run --rm harvey setup

docker compose up -d docker compose logs harvey

Open the private unlock link printed in the logs.

Compose mounts the checkout directory at /app. Setup credentials, both YAML config files, training output, knowledge files, and data/ remain in that checkout when containers are recreated. Directory mounting also permits atomic configuration saves; do not replace it with individual config-file mounts. Harvey loads .env directly. Restart the container after editing credentials. The checkout and Claude auth directory must be writable by the selected user.

To train later, use docker compose run --rm harvey train ; harvey.local.yaml is saved directly to the checkout. Back up this file, .env, skills/, and data/ before moving installations. The running code also comes from the checkout: restart after updating it, and rebuild when dependencies or the Dockerfile change. The build context excludes local data and credentials.

The dashboard listens on 5555. If you change the port in the Compose command, change the Dockerfile health-check port too and rebuild. Stop the service with docker compose down; its files remain in the checkout. Use a host install if you need the Codex provider.

---

You confirm what it looks for

This is the part that makes Harvey different from a black box, and it is deliberately not skippable.

Harvey knows how to collect 29 signals about a business. It does not collect any of them until you say so. On first run it proposes the catalog; you confirm, skip, or reject each one in the dashboard or from the terminal:

harvey signals                       # review the catalog
harvey signals --confirm free        # everything that costs nothing (26 of them)
harvey signals --confirm SERP_RANK   # a paid one, opted into explicitly

Every signal shows what it is in plain language, what it costs, and how many companies already carry it.

| | Signal | What it tells you | |---|---|---| | Discovery | SERP_RANK | Where they rank for their own core search, stored with the search phrase. Positions 11–30 are the sweet spot: visible enough to be trying, and losing. | | | IN_LOCAL_PACK / MAPS_RANK | Whether they hold one of the three map slots above the results, and where they sit in Google Maps. | | | NO_WEBSITE | They have a listing and a phone and no site. If you sell websites, the highest-intent signal that exists. | | | UNCLAIMED_LISTING | Nobody has claimed their Google Business Profile. | | | REVIEW_RATING / REVIEW_COUNT | Good but invisible, or visible but struggling. | | Profile | INCUMBENT_AGENCY | Who currently has the account, credited in their own footer. Every detection builds a roster you can target as a whole book of business. | | | RUNNING_GOOGLE_ADS / RUNNING_META_ADS | They are spending on acquisition today. The strongest budget signal available for free. | | | NO_ONLINE_BOOKING | Leads have to phone in, so after-hours demand is lost. | | | NO_SCHEMA_MARKUP | A concrete, checkable SEO gap you can name in an email. | | | BLOG_STALE | Content marketing started and abandoned. | | | SITE_PAGE_COUNT, TECH_STACK, BLOCKS_AI_CRAWLERS | Size, platform, AI-search visibility. | | | HIRING_ROLE / OPEN_ROLES | What they're hiring for, and how many roles are open. When their site links a Greenhouse, Lever, Ashby or Workable board, Harvey reads that board's public feed (one request): roles matching icp.hiring_signals (else icp.titles), with department, location, posting date and the posting's URL; when the board lists none, it records "not hiring" so a closed role stops counting. Otherwise it reads the careers page. Set hiring_signals to the roles that mean they're buying: the titles fallback names who you email, so "CEO" only matches a CEO opening (never "Assistant to the CEO"), which is rarely the signal you want. | | | YC_BACKED, ACCELERATOR, FUNDING_ROUND | A startup, as its own site says: YC or another accelerator (with the batch or cohort), or a raise it announced (round, amount, date). | | People | CONTACT_FOUND, DECISION_MAKER_TITLE, LIKELY_OWNER, REGISTRY_VERIFIED | Who decides, and whether they own the place. | | Contact | EMAIL_PATTERN, EMAIL_STATUS, CONTACT_FORM_URL | Whether you can actually reach them. |

Then a cohort is a query, not a list

Once signals are confirmed, the dashboard's cohort builder turns them into a target set — pick what a good prospect must have, what disqualifies them, and a range for numeric signals (organic rank 11 to 30), and it counts the matches live:

Has a marketing agency  AND  running Google Ads  AND NOT  already has online booking
→ 23 companies

That is a campaign with a reason behind it. You know exactly why each of those 23 is on the list, and so does the email Harvey writes them.

---

The prospecting pipeline

Four stages. Each one runs independently, re-runs safely, and states its cost.

1. DISCOVER — who exists

The only stage that spends money, so it always estimates first and never calls anything until you press go.

harvey discover --providers   # the menu, with real prices
harvey discover --estimate    # projected spend, then exits
harvey discover               # go

| Source | Cost | Free tier | Best for | |---|---|---|---| | OpenStreetMap (default) | free | unlimited, no account | Trying the whole pipeline before paying anyone. Coverage is thin for businesses without a storefront: OSM maps premises, so whole trades are barely present (under 2,000 roofers and under 2,300 plumbers in the entire US). Harvey says so in the UI rather than quietly under-delivering. | | TinyFish | free | 30 searches a minute, 500 an hour, no card | Finding businesses by phrase (including the startup and hiring presets) at no cost. Records no search rank: its ordering isn't Google's and it takes no location, so use Serper or DataForSEO SERP for rank signals. | | DataForSEO Business Listings | $0.372 / 1,000 | $1 credit | Local trades, clinics, contractors. Phone, domain, rating, claimed status — and it can filter for businesses with no website at all. | | DataForSEO SERP | $0.006 / search at depth 30 (Live, $0.002 per 10 results) | $1 credit + free sandbox | Organic rank as the buying signal, plus who holds the local 3-pack, searched from the city centre. | | DataForSEO Google Maps | $0.002 / search (up to 100 results) | $1 credit + free sandbox | Map rank, rating, category and claimed status, including businesses with no website. | | Serper | ~$0.30–1.00 / 1,000 | 2,500 free, no card | The easiest paid one to try. | | Scavio Google Maps | $0.01 / page (up to 20 listings) | 50 credits, no card | Google Maps listings around each city: phone, website, category, rating and reviews, including businesses with no website. | | Scavio Google Search | ~$0.03 / depth-30 search | same 50 credits | Rank as the signal, on the same key as Scavio Google Maps. |

Selling to startups? Harvey has a built-in startup skill, made of three parts:

| Preset | Finds | Signal | |---|---|---| | yc (YC startups) | Companies whose own site says they're backed by Y Combinator | YC_BACKED, with the batch (W24, S23) | | accelerator (Accelerator startups) | Ones backed by Techstars, 500 Global, Antler, Entrepreneur First, South Park Commons, Neo, AI Grant, a16z speedrun or Plug and Play (two searches per industry; the signal also reads Pioneer) | ACCELERATOR, with the program and cohort ("Techstars 2024") | | funding (Recently funded) | Companies announcing their own raise ("we raised", "announces $X Series A") in the last 18 months, news sites and funding databases excluded | FUNDING_ROUND: round, amount and date |

harvey discover --preset accelerator --provider serper, or the Look for menu on the Discover tab.

About page (and for a raise, the page the search found on their own site and their press or news page) for a line where the company says it about itself ("we went through Y Combinator", "Acme (Techstars '24)", "we raised a $4M seed round"), a "Backed by" logo wall, or a link to its own YC page. Customer and audience lines ("trusted by YC-backed startups", "we help founders raise their seed round") don't count. Many startups never say it on their site, so these find the ones that do; a YC or accelerator preset hit counts as a yes too. A raise's date comes from its own clause, or from the publish date of a press, news or blog post. Money that isn't a raise (loans closed, verdicts won, money raised for a charity, an investor's round in another company) doesn't count. skills/selling_to_startups.md, a playbook for writing to founders with a section on companies that just raised, for YC- and accelerator-backed companies and for raises dated within the last 18 months. An older or undated raise alone doesn't bring the playbook, and is never written to as news.

Harvey never reads ycombinator.com, the accelerators' directories, Crunchbase, PitchBook or LinkedIn, whose terms forbid scraping them; everything comes from the companies' own sites and your search provider.

Looking for companies hiring a role? `harvey discover --preset hiring --provider serper` (or Look for: Companies hiring for your roles) searches your industries for companies whose own careers pages list one of your icp.hiring_signals roles (else icp.titles). Hosted boards and job sites are left out of the search, since a board page names a board, not a website to profile. Confirm HIRING_ROLE and profiling reads the board their site links to and records the role; the search hit alone records nothing.

Cities geocode themselves (cached forever, one lookup each). Directories, aggregators and institutions are filtered by pattern, not a blocklist — en.wikipedia.org and biz.yelp.com never make it in.

Depth 20–30, not 100. Google removed 100-results-per-page in September 2025, so depth 100 is now billed as ten pages nearly everywhere. Below rank 30 it is mostly directories anyway.

2. PROFILE — what they are

Free. Three HTTP requests per business. No browser, no model call. This is the highest value-per-effort stage in the whole system, because it produces the facts that make an email specific.

Website requests from Scout, Profile, and the local training crawler are limited to public HTTP(S) destinations. Every DNS answer and redirect is checked; local, private, link-local, and metadata addresses are rejected. Connections use a validated IP while preserving the website's TLS identity. These website clients ignore environment proxies, so proxy routing cannot bypass that boundary.

Homepage, robots.txt, sitemap.xml, plus the team and careers pages when they exist — then regex over HTML already in hand. Out comes the incumbent agency, the ad pixels, the missing schema, the abandoned blog, the open roles, the named humans.

Agency detection is the compounding one. Almost every agency credits itself in the footer, and Harvey scores the wording: "Powered by" / "Website by" → 0.9 confidence; a bare descriptive link → 0.75; an unlabelled link → 0.35 and not recorded. A wrong incumbent in an email is worse than saying nothing.

3. ENRICH — who decides

The owner, from the company's own site. Harvey reads the homepage and follows the site's own menu to the pages where people are named — team, about, our story, meet the dentists, attorneys, contact — six pages at most, skipping links that land back on the homepage. On those pages it reads:

Owners rank first, and at most two people per company become prospects. Headings made of ordinary words ("Team of Experts"), customers quoted in testimonials or tweets ("Founder at Quillnest"), other organisations' officers ("KTR President Vic Hale"), former or late owners ("Past President", "the late Harold Smith"), and taglines are not people. Titles match your ICP by whole word: "Vice President" doesn't count as "President" unless you list it, and Proprietor, Principal, Managing Partner, Founder and CEO all count as an owner. With LIKELY_OWNER confirmed, each owner is recorded as an observation with the evidence that says so.

When a company's site names nobody, or names an owner whose address is only a guess, and Harvey has no contact there yet — common for small trades — it also uses a general address the company prints on its own pages (only general mailboxes such as owner@, office@, info@ or hello@, never a person's own address). Harvey reads whole pages for it, including schema.org and addresses hidden from scrapers, and accepts an address on another domain only when that domain leads to the company's own site. It is never guessed, it is verified like any other address, and those emails greet the company's team instead of a first name.

A site behind a bot challenge (SiteGround's "sgcaptcha", Cloudflare's "Just a moment…") is reported as challenged, not unreachable, and isn't retried.

4. VERIFY — can you reach them

Harvey learns each company's address pattern and verifies one candidate, rather than brute-forcing name variations. Any address the site publishes can teach it: nvale@ next to Nora Vale is {first initial}{last}, and j.smith@ is an initial and surname. A lone dotless address no named person owns (kmorrow@: Kmorrow, or K. Morrow?) is ambiguous and teaches nothing. An address two named people could own (jsmith@ when John and Jane Smith both work there) is format evidence only, and Harvey never guesses or verifies an address another named person could own: a verifier would confirm their mailbox. It decodes the usual hiding tricks (Cloudflare email protection, a mail link's data-user/data-domain attributes, name [at] domain [dot] com) and never invents an address from ordinary text. A person's own published address is used as is. With no evidence at all, it guesses first.last@, or first@ when something besides the website says the company is small (your ICP's company size, or a local

GitHub Stars & Activity

3,343Stars
673Forks
31Open issues
PythonLanguage

GitHub Popularity

GitHub stars3,343
Forks673
Open issues31
Primary languagePython
LicenseMIT
Stars gained today248
Created2026-03-12
Last pushed2026-10-27

Trending History

Daily boardrank #35 · ▲ 248 stars

Related GitHub Projects

1

anthropics / skills

Python★ 184,916⑂ 21,866▲ 287 stars
→
2

MemPalace / mempalace

Python★ 60,123⑂ 7,614▲ 357 stars
→
3

vectorize-io / hindsight

Python★ 56,371⑂ 6,745▲ 1,454 stars
→
4

HKUDS / DeepTutor

Python★ 51,200⑂ 6,284▲ 1,930 stars
→
5

langchain-ai / deepagents

Python★ 30,780⑂ 4,329▲ 60 stars
→
6

openclaw / openclaw

TypeScript★ 393,424⑂ 82,694▲ 149 stars
→
7

obra / superpowers

Shell★ 309,055⑂ 27,483▲ 1,034 stars
→
8

mattpocock / skills

Shell★ 306,969⑂ 25,824▲ 882 stars
→

More Trending Repositories