{"id":50559137,"url":"https://github.com/chatman-media/sales","last_synced_at":"2026-06-04T10:30:33.504Z","repository":{"id":357921435,"uuid":"1239142641","full_name":"chatman-media/sales","owner":"chatman-media","description":"LLM sales funnel engine — persona composition, funnel routing, ELO A/B testing, self-play evaluation, coach LLM","archived":false,"fork":false,"pushed_at":"2026-05-14T22:17:41.000Z","size":126,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-14T22:37:48.506Z","etag":null,"topics":["ab-testing","bun","cialdini","elo-rating","funnel","llm","sales-bot","self-play","telegram-bot","typescript"],"latest_commit_sha":null,"homepage":"https://github.com/chatman-media/sales","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chatman-media.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-14T20:00:58.000Z","updated_at":"2026-05-14T22:21:26.000Z","dependencies_parsed_at":"2026-05-14T22:03:43.816Z","dependency_job_id":null,"html_url":"https://github.com/chatman-media/sales","commit_stats":null,"previous_names":["chatman-media/sales","chatman-media/chatman_sales"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/chatman-media/sales","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Fsales","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Fsales/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Fsales/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Fsales/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chatman-media","download_url":"https://codeload.github.com/chatman-media/sales/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chatman-media%2Fsales/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33901305,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ab-testing","bun","cialdini","elo-rating","funnel","llm","sales-bot","self-play","telegram-bot","typescript"],"created_at":"2026-06-04T10:30:32.763Z","updated_at":"2026-06-04T10:30:33.488Z","avatar_url":"https://github.com/chatman-media.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @chatman-media/sales\n\n[![npm](https://img.shields.io/npm/v/@chatman-media/sales)](https://www.npmjs.com/package/@chatman-media/sales)\n[![CI](https://github.com/chatman-media/sales/actions/workflows/ci.yml/badge.svg)](https://github.com/chatman-media/sales/actions/workflows/ci.yml)\n[![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178C6?logo=typescript\u0026logoColor=white)](https://www.typescriptlang.org/)\n[![Bun](https://img.shields.io/badge/runtime-Bun-fbf0df?logo=bun\u0026logoColor=black)](https://bun.sh)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n[![powered by @chatman-media/rag](https://img.shields.io/badge/RAG-@chatman--media%2Frag-6366f1)](https://github.com/chatman-media/rag)\n\n**LLM-powered sales funnel engine for conversational bots.** Persona composition, funnel stage routing, A/B testing with ELO ratings, self-play evaluation, and a coach LLM that iterates on failing styles.\n\nBuilt from the production sales layer of [sales-guru](https://github.com/chatman-media/sales-guru) — a Telegram recruitment bot that runs 24/7 qualifying inbound candidates for foreign work contracts.\n\n---\n\n## What's inside\n\n| Module | What it does |\n|--------|-------------|\n| **Types** | `Style`, `FunnelStage`, `Hook`, `StageConfig` — full Zod schema for a sales persona |\n| **Prompt** | `composeSystemPrompt` — builds multi-section system prompts: persona, tone, framework (AIDA/PAS/SPIN/NEPQ/Belfort), hooks, skills, stage guidance, KB context |\n| **Stage router** | `nextStage` — sub-ms Unicode-aware regex router for Cyrillic + English |\n| **Stage classifier** | `classifyStage` — LLM classifier with regex fallback, `{stage, confidence, source}` |\n| **ELO** | `eloUpdate` / `eloUpdatePair` — standard ELO math, K=32, symmetric pairwise |\n| **A/B router** | `pickVariant` — SHA-256 deterministic assignment, same user always gets same variant |\n| **Skills** | 25 persuasion techniques (Cialdini × 7, Voss × 5, NLP × 3, classical sales × 5, custom × 5) |\n| **Built-in styles** | 4 production-tested personas: marina-prime, cold-direct-pas, empathetic-nepq, flirty-belfort |\n| **Self-play** | `runSelfPlayMatch` — full RAG pipeline vs LLM-driven candidate; per-turn skill grading; reflect guard |\n| **Pairwise** | `runPairwiseMatch` — A vs B against same persona; comparative judge; symmetric ELO update |\n| **Coach** | `proposeStyleEdits` — reads losing transcripts, proposes concrete JSON edits to tone/hooks/guidance/few-shot |\n| **Shadow eval** | `runShadowEval` — Wilson 95% LB on B's win rate → keep / rollback / inconclusive |\n| **Skill recommender** | `rankSkillRecommendations` — Wilson LB ranking; draws count as 0.5 wins |\n\n---\n\n## Install\n\n```bash\nbun add @chatman-media/sales     # Bun\nnpm install @chatman-media/sales # npm / pnpm / yarn\n```\n\nPeer dependency: [`@chatman-media/rag`](https://github.com/chatman-media/rag) for `ChatClient`, `EmbeddingClient`, `IKbStore`, and `answerWithRag`.\n\n---\n\n## Quick start — compose a prompt\n\n```typescript\nimport { composeSystemPrompt, getStyleOrThrow } from \"@chatman-media/sales\";\n\nconst style = getStyleOrThrow(\"marina-prime-v1\");\n\nconst prompt = composeSystemPrompt(style, \"qualify\", kbContext, {\n  userFacts: { city: \"Москва\", age: \"24\" },\n  skills: attachedSkills,\n});\n```\n\n## Stage routing\n\n```typescript\nimport { nextStage, classifyStage } from \"@chatman-media/sales\";\n\n// Fast regex (zero cost):\nconst stage = nextStage({ turnNumber: 3, currentStage: \"qualify\", lastUserMessage: msg });\n\n// LLM with regex fallback:\nconst result = await classifyStage({ chat, userMessage: msg, currentStage: stage, turnNumber: 3 });\nconsole.log(result.stage, result.confidence, result.source); // \"pitch\" 0.87 \"llm\"\n```\n\n## A/B testing with ELO\n\n```typescript\nimport { pickVariant, eloUpdate } from \"@chatman-media/sales\";\n\n// Deterministic assignment — same user always gets same style:\nconst styleSlug = pickVariant(\n  { slug: \"summer-2025\", variants: [\n    { styleSlug: \"marina-prime-v1\", weight: 50 },\n    { styleSlug: \"empathetic-nepq-v1\", weight: 50 },\n  ]},\n  userId,\n);\n\n// Update ELO after a match:\nconst newRating = eloUpdate(currentRating, \"won\"); // K=32, baseline=1500\n```\n\n## Self-play evaluation\n\n```typescript\nimport { runSelfPlayMatch, CANDIDATE_PERSONAS } from \"@chatman-media/sales\";\n\nconst result = await runSelfPlayMatch(deps, {\n  style: myStyle,\n  styleId: 42,\n  persona: CANDIDATE_PERSONAS.find(p =\u003e p.slug === \"skeptic-anya\")!,\n  maxTurns: 20,\n});\n\nconsole.log(result.outcome);          // \"won\" | \"lost\" | \"draw\"\nconsole.log(result.transcript);       // full back-and-forth\nconsole.log(result.skillsAttributed); // which skills the bot actually used\nconsole.log(result.fabricationsCaught); // reflect guard catches\n```\n\n## Coach LLM\n\n```typescript\nimport { proposeStyleEdits, applyEditsToStyle } from \"@chatman-media/sales\";\n\nconst proposal = await proposeStyleEdits({ style, matchesRepo, chat });\nconsole.log(proposal.summary);   // \"Bot too formal with price-sensitive personas\"\nconsole.log(proposal.edits);     // { voice_tone: \"...\", hooks_add: [...] }\nconsole.log(proposal.rationale); // per-edit explanation\n\n// Pure merge — returns new Style, nothing persisted:\nconst improved = applyEditsToStyle(style, proposal.edits);\n```\n\n## Storage interfaces\n\nAll DB-heavy modules accept injected interfaces — no ORM dependency:\n\n```typescript\nimport type { ISelfPlayMatchesRepo, ISkillsRepo, IStyleRatingsRepo } from \"@chatman-media/sales\";\n\n// Implement for your DB (Postgres, SQLite, in-memory):\nclass MyMatchesRepo implements ISelfPlayMatchesRepo {\n  async insert(match) { /* ... */ }\n  async byId(id) { /* ... */ }\n  async list(opts) { /* ... */ }\n}\n```\n\n---\n\n## Built-in styles\n\n| Slug | Persona | Framework | Voice |\n|------|---------|-----------|-------|\n| `marina-prime-v1` | Марина, PrimeConnect | NEPQ | Human recruiter, Telegram-native, warm |\n| `cold-direct-pas-v1` | Менеджер | PAS | Direct, no fluff, fast pitch |\n| `empathetic-nepq-v1` | Наталья | NEPQ | Warm, anxiety-aware, unhurried |\n| `flirty-belfort-v1` | Виктория | Straight Line | Assertive, playful, confident |\n\n## Persuasion skills\n\n25 atomic techniques from Cialdini, Voss, NLP, and classical sales — each with a `promptFragment` injected into the system prompt and an `applicableStages` filter:\n\n```typescript\nimport { SKILL_CATALOGUE, SKILL_BY_SLUG } from \"@chatman-media/sales\";\n\nconst mirroring = SKILL_BY_SLUG.get(\"mirroring\");\n// { slug: \"mirroring\", family: \"voss\", promptFragment: \"...\", applicableStages: [\"qualify\", \"objection\"] }\n```\n\n---\n\n## Architecture\n\n```\n@chatman-media/sales\n├── types.ts          Style / FunnelStage / Hook schemas (zod)\n├── prompt.ts         System prompt composition\n├── stage-router.ts   Regex funnel stage router\n├── stage-classifier.ts  LLM stage classifier\n├── elo.ts            ELO rating engine\n├── ab-router.ts      Deterministic A/B picker\n├── skills/\n│   └── catalogue.ts  25 persuasion techniques\n├── styles/\n│   └── *.ts          4 built-in personas\n├── self-play/\n│   ├── personas.ts   8 candidate archetypes\n│   ├── judge.ts      LLM match judge\n│   ├── orchestrator.ts  Full match loop\n│   └── pairwise.ts   Head-to-head comparison\n├── coach.ts          Style iteration from losses\n├── shadow-eval.ts    Wilson-LB A/B shadow runner\n├── skill-recommendations.ts  Wilson LB skill ranker\n└── store.ts          Storage interfaces (IKbStore, ISelfPlayMatchesRepo, …)\n```\n\nDepends on [`@chatman-media/rag`](https://github.com/chatman-media/rag) for `ChatClient`, `EmbeddingClient`, `IKbStore`, `answerWithRag`, and `gradeSkills`.\n\n---\n\n## License\n\n[MIT](LICENSE) — Alexander Kireev / [chatman-media](https://github.com/chatman-media)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchatman-media%2Fsales","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchatman-media%2Fsales","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchatman-media%2Fsales/lists"}