Metadata-Version: 2.4
Name: teft-leads
Version: 0.1.0
Summary: Local-first LinkedIn intent-signal lead-finder with a local web app — real signals, not stored databases.
Project-URL: Repository, https://github.com/michaelwilhelmsen/linkedin-lead-finder
Author-email: Michael Wilhelmsen <michael@screenpartner.no>
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
Classifier: Environment :: Console
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.12
Requires-Python: <3.13,>=3.12
Requires-Dist: anthropic>=0.40
Requires-Dist: fastapi>=0.115
Requires-Dist: httpx>=0.27
Requires-Dist: openai>=2.0
Requires-Dist: patchright>=1.40
Requires-Dist: pydantic>=2.7
Requires-Dist: typer>=0.12
Requires-Dist: uvicorn>=0.30
Description-Content-Type: text/markdown

# teft

> **teft** *(Norwegian: the instinctive nose for a lead — a scent for who's in-market right now).*
> Repo slug is still `linkedin-lead-finder`; the tool is named `teft`.

A personal, local-first tool that turns a freeform brief into a ranked, explained
shortlist of high-intent LinkedIn prospects — "real signals, not stored databases."

## For colleagues — try it in 2 minutes

Nothing to clone. It runs entirely on your own machine (the web app is `127.0.0.1`-only).

1. **Install uv:** `curl -LsSf https://astral.sh/uv/install.sh | sh` (Windows: see [docs.astral.sh/uv](https://docs.astral.sh/uv))
2. **Run:** `uvx teft-leads serve` — your browser opens the app. (PyPI name is `teft-leads`; the command you type once installed is still `teft`.)
3. **In the app:** Settings → paste your keys → Search → run it.

That's it. The rest of this page is for people building on `teft`.

## About / planning

Planned with the **wayfinder** workflow; the plan lives as **GitHub Issues**. Two maps:

- [**Planning map (#1)**](../../issues/1) — *complete.* Destination reached: the locked v0 definition.
- [**Build plan (#6)**](../../issues/6) — stack, LLM, architecture, name, milestones.
- [**Implementation map (#7)**](../../issues/7) — **active.** v0 as five `wayfinder:task` milestones (M0→M4);
  the **frontier** (open, unblocked, unassigned) is the work takeable now — currently
  [**M0 (#8)**](../../issues/8).

See [`CONCEPT.md`](CONCEPT.md) for the original seed brief.

## v0 at a glance

Python CLI on your Mac, over your *own* logged-in LinkedIn session (Patchright, `li_at` cookie reuse).

**Guided:** `teft run` — one command walks you through config (prompts + saves any missing key/cookie),
then *product → brief → buyer-intent phrases* (or reuse the last campaign), scrapes, ranks, and opens the
results UI. Defaults to headless/fast.

**Or step by step:**
- **`teft scrape -p "phrase" …`** — content-search → per-candidate activity/profile → `candidates.json`.
- **`teft rank --brief brief.txt`** — each candidate → the "why now" prompt, SKIP filter → ranked
  `shortlist-<date>.{md,json}`.
- **`teft view`** — a local, zero-dependency results UI: lead cards, expandable post, Open profile / Open
  post / copy email, and contacted + notes saved to `~/.teft/actions.json`.

No Fit %, no monitoring, no outreach, no external services. `.env` is auto-loaded (no manual `source`).
Speed/stealth knobs: `TEFT_HEADLESS=1`, `TEFT_MIN_DELAY` / `TEFT_MAX_DELAY`, `TEFT_MAX_CANDIDATES` (cap per query, default 25).

## LLM provider

`rank` is provider-pluggable. Default is `claude-opus-4-8` (the tier the "why now" prompt's SKIP-rule
calibration was validated on). To use GPT-5.6 instead, set `TEFT_LLM=openai` (defaults to `gpt-5.6-terra`;
override with `TEFT_MODEL`, e.g. `gpt-5.6-sol` / `gpt-5.6-luna`) and `OPENAI_API_KEY`. **The SKIP calibration
is only proven on Claude — re-run the fixtures on a new provider before trusting it** (see [VALIDATE.md](VALIDATE.md)).
