[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-82805":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":12,"openIssues":13,"contributorsCount":13,"subscribersCount":13,"size":13,"stars1d":14,"stars7d":15,"stars30d":16,"stars90d":13,"forks30d":13,"starsTrendScore":17,"compositeScore":18,"rankGlobal":8,"rankLanguage":8,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":8,"pushedAt":8,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":13,"starSnapshotCount":13,"syncStatus":12,"lastSyncTime":27,"discoverSource":28},82805,"Sales-AI","xup6jammy\u002FSales-AI","xup6jammy",null,"Java",77,1,2,0,18,19,25,54,0.9,"MIT License",false,"main",true,[],"2026-06-12 02:04:28","\u003Cp align=\"center\">\n  \u003Cimg src=\"docs\u002Fassets\u002Fhero.png\" alt=\"Sales AI — a salesperson collaborating with an AI agent that reads customer email and queries the customer database\" width=\"820\" \u002F>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cstrong>English\u003C\u002Fstrong> &nbsp;·&nbsp;\n  \u003Ca href=\"README.zh-TW.md\">繁體中文\u003C\u002Fa> &nbsp;·&nbsp;\n  \u003Ca href=\"README.ja.md\">日本語\u003C\u002Fa> &nbsp;·&nbsp;\n  \u003Ca href=\"README.ko.md\">한국어\u003C\u002Fa>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cimg alt=\"Java 21\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FJava-21-007396?style=flat-square&logo=openjdk&logoColor=white\" \u002F>\n  \u003Cimg alt=\"License: MIT\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-22c55e?style=flat-square\" \u002F>\n  \u003Cimg alt=\"Status: MVP\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FStatus-MVP-3b82f6?style=flat-square\" \u002F>\n  \u003Cimg alt=\"No dependencies\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FDependencies-None-94a3b8?style=flat-square\" \u002F>\n  \u003Cimg alt=\"MCP-ready\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FMCP-ready-f59e0b?style=flat-square\" \u002F>\n  \u003Cimg alt=\"Claude Code skill\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FClaude%20Code-skill-8b5cf6?style=flat-square\" \u002F>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\u003Ci>An AI sales copilot that reads your customer's email like a senior account manager would &mdash; context first, draft last, never hits send.\u003C\u002Fi>\u003C\u002Fp>\n\n---\n\n## TL;DR\n\n- A Java 21 copilot for B2B account managers. It loads the customer's profile and commercial history first, reads only the relevant thread, classifies intent and tone, evaluates risk against an explicit policy, and produces two reply drafts plus follow-up actions.\n- It is not a chatbot. It is context-grounded by design. Refunds, legal language, contract concessions, exceptional discounts, cancellation talk, and churn signals on VIP accounts force a hard manager-approval gate that **blocks the drafts**.\n- Run it in 60 seconds with the stock JDK, no dependencies, no credentials, no network &mdash; see [Run it](#run-it-in-60-seconds).\n\n## Vision\n\nThe goal of Sales AI is to ship a **24\u002F7 automated AI agent focused on the sales function** &mdash; a sales engine that never clocks out.\n\nIt doesn't lock you into one industry. **Plug in your existing customer database, meeting notes, contract archive, and CRM system**, and this agent will respond to customer email like a senior account manager around the clock &mdash; the daytime inquiry, the midnight refund request, the weekend renewal question, all caught in the first window. Manufacturing, finance, SaaS, cross-border e-commerce, B2B services &mdash; **wherever your customers write to you, this agent fits**.\n\n### What's coming next\n\n- **Phase 4 &mdash; Proactive outreach.** Not just inbound replies. Overdue proposals, contracts about to expire, accounts showing churn signals &mdash; the agent drafts the message, orders the priority, schedules the follow-up.\n- **Phase 5 &mdash; Cross-channel messaging.** Beyond email: LinkedIn, WhatsApp, LINE OA, Slack, the website chat widget, social-media DMs &mdash; same customer context, consistent voice across channels.\n- **Phase 6 &mdash; Autonomous closing.** Within manager-defined price bands, contract templates, and discount authorities, the agent runs the full loop &mdash; quote, negotiate, sign, log to CRM &mdash; without a human in the routine path.\n\n### What this is worth to a company\n\n- **Sales output stops being capped by headcount.** A 5,000-customer business that used to need 30 reps to keep coverage can run with 5 reps + AI and hit higher contact frequency.\n- **Speed becomes a weapon.** Industry average reply time on a sales inquiry is 4&ndash;12 hours; this agent replies in 30 seconds. **Speed of reply is conversion.**\n- **Institutional memory doesn't walk out the door.** When a rep leaves, they take customer history, talking points, and account context with them &mdash; every B2B company's biggest unforced loss. Sales AI lives in the database and shows up to work every day.\n- **Managers spend their time where it matters.** Refunds, contract concessions, VIP risk &mdash; those need a signature. The other 80% of routine email no longer eats their calendar.\n- **Auditable by default.** Regulated industries (banking, insurance, healthcare) cannot run a black-box agent. Every Sales AI step writes one audit line; the regulator, the board, and the external auditor can all read what happened and **why**.\n\nToday we ship a context-first MVP with a hard manager-approval gate. As phases progress, the gate narrows and the autonomous portion grows &mdash; but **the gate never disappears**. That is our safety promise.\n\n## Why Java\n\nNot because Java is trendy. Because this project's destination *is* Java's home turf.\n\n| Reason | How it shows up in this codebase |\n|---|---|\n| **B2B enterprise IT runs on Java** | Banks, insurers, manufacturing, ERP\u002FCRM backends &mdash; ~90% Java\u002FSpring. Embedding this agent next to existing services in the same process, sharing the same audit log, sharing the same DI container &mdash; far less friction than a Python sidecar. |\n| **JDK 21 + zero dependencies = 60-second reproduce** | No `pip install` resolving conflicts, no venv, no `node_modules` black hole. `git clone && javac && java` &mdash; three steps, done. Python and Node can't ship that cleanly. |\n| **Records + sealed types fit the domain model** | The 13 domain records are smaller than Python `@dataclass` equivalents and safer &mdash; compile-time null checks, exhaustive enum coverage, switch expressions forcing every case. |\n| **Auditability is regulatory, not nice-to-have** | A red line like refund\u002Flegal\u002Fcontract gets compile-time guaranteed coverage in [`RiskRules.java`](src\u002Fmain\u002Fjava\u002Fcom\u002Fexample\u002Fsalesai\u002Frisk\u002FRiskRules.java). A missing `if\u002Felif` branch in Python passes silently; Java's compiler refuses. |\n| **Complementary to the MCP ecosystem** | Most MCP servers are TS\u002FPython &mdash; fine, the tool layer is language-agnostic. `SKILL.md` is the agent; the engine's language doesn't matter to the user. Choosing Java is a strategic bet on \"easy to embed inside enterprise Java backends.\" |\n\n\"AI agent in Java\" is a scarce category on GitHub (~95% of public AI agent code is Python). For enterprise IT teams already on a Java stack, an agent that drops into their existing backend is a meaningful differentiator, not a liability.\n\n## What makes this different\n\n| | What it brings | Why it matters |\n|---|---|---|\n| **Skill is the agent** | The user-facing agent lives in [`SKILL.md`](skills\u002Fsales-ai\u002FSKILL.md), not in code. The Java MVP is just the engine. | Replace the engine (Java CLI &rarr; Gmail MCP &rarr; CRM MCP) phase by phase without changing how Claude Code calls it. |\n| **Hard safety gate** | Refund \u002F legal \u002F contract \u002F discount \u002F churn signals force `REQUIRES_MANAGER_APPROVAL` and **block the drafts**. | Other agent demos rely on the model \"behaving\"; this one literally has no SMTP code in the build. It cannot send mail even by accident. |\n| **Context-first, not prompt-first** | Customer profile, contract, payments, tickets, AM notes load **before** the model sees the email. | A senior AM does this in their head. LLMs need it written down. |\n| **Auditable by construction** | Every port call writes one audit line; the CLI prints them at the bottom of every report. | If a decision looks wrong, read backwards from the report to the inputs. |\n| **Bilingual classification** | Keyword scorer handles English + 繁體中文 in the same pass. Designed for the next two languages to drop in. | Built for Asia-Pacific B2B mail, not US-only fixtures. |\n| **Zero dependencies, reproducible in 60 seconds** | Stock JDK 21, no Maven, no Gradle, no LLM key, no network. | `git clone && javac && java` and you have the demo. No supply chain. No surprises. |\n\n## Architecture: the Skill is the agent\n\n> **The Skill is the agent. The Java MVP is the engine, replaceable per phase.**\n\n```mermaid\nflowchart TD\n    user[\"Sales user\"] -->|\"&quot;Help me with this customer&quot;\"| cc[\"LLM\"]\n    cc -->|reads| skill[\"\u003Cb>SKILL.md\u003C\u002Fb>\u003Cbr\u002F>11-step workflow\u003Cbr\u002F>safety rules\u003Cbr\u002F>output format\"]\n    skill -->|orchestrates| tools([\"Tool layer\u003Cbr\u002F>\u003Ci>replaceable per phase\u003C\u002Fi>\"])\n\n    tools ==> mvp[\"\u003Cb>Engine — this repo\u003C\u002Fb>\u003Cbr\u002F>Java 21 CLI\u003Cbr\u002F>JSON or JDBC source\u003Cbr\u002F>console audit\"]\n    tools ==> mcp[\"\u003Cb>SQL MCP server — this repo\u003C\u002Fb>\u003Cbr\u002F>4 whitelisted tools\u003Cbr\u002F>SQLite \u002F MySQL \u002F Postgres\u003Cbr\u002F>JSON-RPC over stdio\"]\n    tools -.->|Phase 2| email[\"Gmail MCP\u003Cbr\u002F>Outlook MCP\"]\n    tools -.->|Phase 3b| crm[\"CRM MCP\u003Cbr\u002F>Salesforce \u002F HubSpot\u003Cbr\u002F>Text2SQL\"]\n    tools -.->|Phase 4| llm[\"Agents-Flex Skill\u003Cbr\u002F>Claude \u002F Bedrock \u002F local LLM\"]\n    tools -.->|Phase 5| approve[\"Slack approval bot\"]\n    tools -.->|Phase 6| rag[\"RAG knowledge base\"]\n```\n\nThe same `SKILL.md` runs every phase. The bold lines are what ships in this repo today: the Java engine and the SQL MCP server with four whitelisted query tools. The dotted lines are future replacements — Gmail \u002F Outlook MCP, a real CRM, an LLM behind the draft port. **The 11-step workflow and the safety rules do not change between phases** &mdash; only the implementations behind the ports move. That is the whole point.\n\nThis is what makes the project useful as a study piece, not just as a demo: the engine you read today is exactly the engine that an MCP-backed production deployment will eventually replace, port by port. See [`docs\u002Fintegration-plan.md`](docs\u002Fintegration-plan.md) for the full migration map.\n\n## The 11-step workflow\n\n```mermaid\nsequenceDiagram\n    autonumber\n    actor U as Sales user\n    participant S as SKILL.md\n    participant CC as CustomerContextPort\n    participant ET as EmailThreadPort\n    participant CL as Classifiers\n    participant RP as RiskPolicyPort\n    participant RD as ReplyDraftPort\n    participant AP as ApprovalPort\n    participant AL as AuditLogPort\n\n    U->>S: Help me with customer X\n    S->>CC: lookup by email\n    CC-->>S: profile + commercial history\n    S->>ET: load relevant thread (one customer only)\n    ET-->>S: thread (n messages)\n    S->>CL: classify intent + tone\n    CL-->>S: INTENT, TONE\n    S->>RP: evaluate(profile, thread, intent, tone)\n    RP-->>S: RiskAssessment + reasons\n    Note over S,RP: refund \u002F legal \u002F contract \u002F discount \u002F churn\u003Cbr\u002F>=> REQUIRES_MANAGER_APPROVAL\n    S->>S: derive ReplyStrategy\n    S->>RD: generate two drafts\n    RD-->>S: Safe\u002FFormal + Warm\u002FRelationship\n    S->>AP: isApproved?\n    AP-->>S: false (no --approve)\n    S-->>U: report with drafts BLOCKED + audit summary\n    S->>AL: audit every step above\n```\n\nIn plain language, the same eleven steps the diagram traces:\n\n1. Identify the customer (by id or by email, from CLI args).\n2. Load the customer's commercial profile: tier, contract status, payment status, recent orders, open tickets, account-manager notes.\n3. Load the relevant email thread for that customer. One thread, never the inbox.\n4. Summarise the thread factually: who said what, when, what is being asked, what has already been promised.\n5. Classify business intent into one of `INQUIRY`, `QUOTATION`, `COMPLAINT`, `RENEWAL`, `PAYMENT_ISSUE`, `DELIVERY_DELAY`, `TECHNICAL_SUPPORT`, `NEGOTIATION`, `CHURN_RISK`, `UNKNOWN`.\n6. Classify emotional tone: neutral, frustrated, escalating, conciliatory, urgent, or formal.\n7. Evaluate risk against the explicit policy. Refund \u002F legal \u002F contract \u002F exceptional discount \u002F cancellation \u002F churn-on-VIP all force `REQUIRES_MANAGER_APPROVAL`.\n8. Decide a reply strategy in one or two sentences: acknowledge, commit, defer, escalate, ask for time.\n9. Generate two reply drafts: Option A is safe and formal, Option B is warm and relationship-focused. Both respect the customer's preferred language.\n10. Surface the approval gate. If the risk decision blocks the drafts, the report shows them as `[BLOCKED — manager approval required]` with the proposed wording quoted for review.\n11. Render the report and an audit summary listing every port call. Optionally record the interaction back to CRM.\n\n## Risk decision flow\n\n```mermaid\nflowchart LR\n    inbox[\"Inbound email\"] --> kw{\"Contains any of...\u003Cbr\u002F>refund \u002F 退款\u003Cbr\u002F>legal \u002F 法務\u003Cbr\u002F>contract change\u003Cbr\u002F>cancel \u002F 解約\u003Cbr\u002F>alternative vendor\u003Cbr\u002F>exceptional discount\"}\n    kw -- \"yes\" --> hard[\"\u003Cb>REQUIRES_MANAGER_APPROVAL\u003C\u002Fb>\"]\n    kw -- \"no\" --> ctx{\"Customer context\"}\n    ctx -- \"VIP + overdue\u003Cbr\u002F>+ open HIGH ticket\" --> med[\"MEDIUM\u003Cbr\u002F>(+1 severity bump)\"]\n    ctx -- \"delivery delay only\" --> low[\"LOW\"]\n    ctx -- \"default\" --> ok[\"LOW\"]\n    hard --> block[\"Drafts BLOCKED\u003Cbr\u002F>banner shown\u003Cbr\u002F>audit: APPROVAL_DENIED\"]\n    med --> ready[\"Drafts produced\u003Cbr\u002F>audit: APPROVAL_GRANTED\"]\n    low --> ready\n    ok --> ready\n```\n\nRefund \u002F legal \u002F contract \u002F cancellation language is a **hard stop**, regardless of customer tier or tone. The model never gets to override this gate &mdash; the rule lives in [`risk\u002FRiskRules.java`](src\u002Fmain\u002Fjava\u002Fcom\u002Fexample\u002Fsalesai\u002Frisk\u002FRiskRules.java) and is the first thing a reviewer reads. The full policy lives in [`docs\u002Fsafety-rules.md`](docs\u002Fsafety-rules.md).\n\n## Sample output\n\nThe bundled demo ships a representative case: Wei-Ming Chen, the procurement lead at Lumora Robotics Co., Ltd. (a VIP customer with overdue payment), is asking for a partial refund and credit on a delayed order, with the August renewal now in question.\n\nThe block below is the **actual stdout** of `java -cp out com.example.salesai.SalesAiCli` against the bundled samples &mdash; not a screenshot, not a hand-edited mock. Everything you see is produced by the deterministic workflow in [`app\u002FAdvisorWorkflow.java`](src\u002Fmain\u002Fjava\u002Fcom\u002Fexample\u002Fsalesai\u002Fapp\u002FAdvisorWorkflow.java) and rendered by [`app\u002FAdvisorReportRenderer.java`](src\u002Fmain\u002Fjava\u002Fcom\u002Fexample\u002Fsalesai\u002Fapp\u002FAdvisorReportRenderer.java). The full transcript also lives at [`samples\u002Fadvisor-output.md`](samples\u002Fadvisor-output.md).\n\n```\n=== Sales AI — Report ===\n!! DRAFTS ARE BLOCKED — manager approval required before this reply can leave the building !!\n\nCustomer Context\n- Name: Wei-Ming Chen\n- Company: Lumora Robotics Co., Ltd.\n- Tier: VIP\n- Contract status: ACTIVE (renews 2026-08-31)\n- Payment status: OVERDUE_30D\n- Recent orders:\n    * SO-2026-0188 — 2026-04-12 — $42000 — DELIVERED (On-time delivery, signed acceptance)\n    * SO-2026-0231 — 2026-04-29 — $18500 — DELAYED (Logistics partner missed ETA by 9 days)\n- Recent support state:\n    * SUP-7781 [HIGH] since 2026-04-25: Vision module misalignment after firmware 4.2 rollout\n\nEmail Summary\n- Subject: Order SO-2026-0231 delay + firmware issue — refund expected\n- Current intent: TECHNICAL_SUPPORT\n- Emotional tone: URGENT\n- Key customer ask: Kelly, four days, no plan. Our CTO is now in the loop and is asking about the renewal in August. Please confirm by tomorrow: (1) revised delivery date, (2) refund or credit amount, (3) firmware fix ETA. Otherwise we will pause the renewal d...\n\nRisk Assessment\n- Risk level: REQUIRES_MANAGER_APPROVAL\n- Reasons:\n    * Customer signalled churn risk (mentioned alternative vendors \u002F pause renewal \u002F cancel).\n    * Customer asked for refund or credit.\n    * VIP customer has an overdue payment (OVERDUE_30D).\n    * Customer has an open HIGH-priority support ticket.\n- Requires manager approval: YES\n\nRecommended Reply Strategy\n- Tone: formal, careful, no commitments (the customer is signalling urgency — acknowledge time pressure explicitly)\n- Position: acknowledge, no commitments yet, escalate\n- Avoid saying:\n    * Promising any contractual concession without manager approval\n    * Confirming a refund or credit amount in the reply\n- Allowed commitments:\n    * Acknowledge receipt and the urgency of the situation today\n    * Pull together logistics, engineering, and account management within 24 hours\n    * Provide a written status update with concrete dates by end of next business day\n- Next best action: Hand off to Kelly Wu with full context; do not reply until approved\n\nDraft Option A: Safe \u002F Formal\nSubject: Re: Order SO-2026-0231 delay + firmware issue — refund expected — recovery plan\nBody:\nDear Wei-Ming Chen,\n\nThank you for the directness of your message. I take the points you raised seriously, and I want to address them in order.\nI understand the impact this has had on your operations and on your team's confidence in us.\n\nHere is what I can confirm today:\n  - Acknowledge receipt and the urgency of the situation today\n  - Pull together logistics, engineering, and account management within 24 hours\n  - Provide a written status update with concrete dates by end of next business day\n\nBecause some of the items you raised — in particular any commercial concession — fall outside what I can confirm in writing today, I am bringing them to Kelly Wu's attention so we can come back to you with a single, signed-off response.\n\nPlease consider this message a status update rather than a final commercial response.\n\nNext step from our side: Hand off to Kelly Wu with full context; do not reply until approved\n\nBest regards,\nKelly Wu\n\nDraft Option B: Warm \u002F Relationship-Focused\nSubject: Re: Order SO-2026-0231 delay + firmware issue — refund expected — recovery plan\nBody:\nHi Wei-Ming Chen,\n\nThanks for being so direct with me — I'd much rather hear it this way than find out later. Let me address each point.\nI know this hasn't been the experience you expected from us, and I'm not going to pretend otherwise.\n\nHere is what I can lock in for you right now:\n  - Acknowledge receipt and the urgency of the situation today\n  - Pull together logistics, engineering, and account management within 24 hours\n  - Provide a written status update with concrete dates by end of next business day\n\nOn the commercial side (anything that looks like a refund, credit, or change to the contract), I want to be honest: I won't commit to a number in this email until Kelly Wu has signed it off — that's how we keep our promises clean.\n\nTreat this as me keeping you in the loop, not as the final word on the commercial side.\n\nWhat I'm doing next: Hand off to Kelly Wu with full context; do not reply until approved\n\nTalk soon,\nKelly Wu\n\nFollow-Up Actions\n- Brief the manager — owner: Kelly Wu, due: today\n    Walk the manager through the inbound message, the risk reasons, and the proposed reply before any draft leaves the building.\n- Engineering update on open ticket — owner: Engineering lead, due: within 48 hours\n    Confirm a firmware fix ETA for the open HIGH-priority ticket and write it up in customer-friendly language.\n- Schedule executive check-in — owner: Kelly Wu, due: this week\n    VIP customer — set up a short call with our account exec to keep the relationship anchored.\n\nAudit Summary\n- [...] LOOKUP_CUSTOMER: email=wm.chen@lumora-robotics.example\n- [...] LOAD_THREAD: customerEmail=wm.chen@lumora-robotics.example\n- [...] CLASSIFY_INTENT: thread=THR-90188\n- [...] INTENT_CLASSIFIED: TECHNICAL_SUPPORT\n- [...] CLASSIFY_TONE: thread=THR-90188\n- [...] TONE_CLASSIFIED: URGENT\n- [...] EVALUATE_RISK: intent=TECHNICAL_SUPPORT tone=URGENT\n- [...] RISK_LEVEL: REQUIRES_MANAGER_APPROVAL requiresManagerApproval=true\n- [...] DECIDE_STRATEGY: level=REQUIRES_MANAGER_APPROVAL\n- [...] GENERATE_DRAFTS: tone=formal, careful, no commitments\n- [...] RECOMMEND_FOLLOWUPS: intent=TECHNICAL_SUPPORT\n- [...] EVALUATE_APPROVAL: requiresManagerApproval=true\n- [...] APPROVAL_DENIED: manager flag=false; reasons=[...]\n- [...] CRM_RECORD: customerId=CUST-1042 summary=...; drafts BLOCKED\n\n=== End of Report ===\n```\n\nRe-running with `--approve` does NOT send mail. It writes one extra audit line recording the approval (`APPROVAL_GRANTED`), drops the BLOCKED banner, and changes the closing CRM record to `drafts READY`. A human still has to copy the wording, paste it into the mail client, read it once more, and press send. That is intentional friction. See [`docs\u002Fsafety-rules.md`](docs\u002Fsafety-rules.md).\n\n> **Drafts are in English** in the MVP because the template adapter is deterministic. Generating drafts in the customer's preferred language (here `zh-TW`) is part of Phase 4 &mdash; see [`docs\u002Fintegration-plan.md`](docs\u002Fintegration-plan.md) &mdash; when `TemplateReplyDraftAdapter` is replaced by an LLM-backed Agents-Flex Skill. The strategy and risk decisions stay language-agnostic so they remain auditable.\n\n## Run it in 60 seconds\n\nYou need a stock JDK 21 and nothing else. No build tool. No network. No credentials.\n\n**PowerShell (Windows):**\n\n```powershell\njavac -d out (Get-ChildItem -Recurse src\u002Fmain\u002Fjava\u002F*.java | %{$_.FullName})\njava -Dstdout.encoding=UTF-8 -cp out com.example.salesai.SalesAiCli\n```\n\n> On Windows, `-Dstdout.encoding=UTF-8` makes em-dashes and 中文 render correctly even when the console code page is not 65001. Drop the flag if you have already run `chcp 65001`.\n\n**bash (macOS \u002F Linux \u002F WSL \u002F Git Bash):**\n\n```bash\nfind src\u002Fmain\u002Fjava -name '*.java' | xargs javac -d out\njava -cp out com.example.salesai.SalesAiCli\n```\n\nThe CLI takes a small set of flags. All are optional; the defaults point at the bundled samples.\n\n| Flag | Meaning |\n|------|---------|\n| `--customer-profile \u003Cpath>` | Path to the customer profile JSON. Defaults to `samples\u002Fcustomer-profile.json`. |\n| `--email-thread \u003Cpath>` | Path to the email thread JSON. Defaults to `samples\u002Femail-thread.json`. |\n| `--approve` | Marks the report as manager-approved. Writes an extra audit line; unblocks display of drafts. Does NOT send mail. |\n| `--db \u003Cjdbc-url>` | Load the customer profile from a JDBC database instead of JSON. Pairs with `--db-user` \u002F `--db-password`. Requires `--email`. |\n\n## Phase 2 preview: SQL MCP Server\n\nThe repo also ships an opt-in MCP server that exposes the same customer data as **whitelisted SQL-backed tools** for Claude Code (or any MCP client) to call directly. JSON-RPC 2.0 over stdin\u002Fstdout, four tools, no generic SQL surface.\n\n```\n┌──────────────┐  stdio JSON-RPC  ┌─────────────────────┐  JDBC  ┌──────────┐\n│ LLM          │ ───────────────▶ │ SalesMcpServer      │ ─────▶ │ SQLite \u002F │\n│ (MCP client) │ ◀─────────────── │  4 whitelisted tools│        │ MySQL \u002F  │\n└──────────────┘                  └─────────────────────┘        │ Postgres │\n                                                                  └──────────┘\n```\n\n| MCP tool | What it does | Backed by |\n|---|---|---|\n| `customer.findByEmail` | One customer by primary email (exact, case-insensitive) | one prepared `SELECT` |\n| `customer.findById` | One customer by id, e.g. `CUST-1042` | one prepared `SELECT` |\n| `customer.listOrders` | Recent orders for one customer, capped at 50 | one prepared `SELECT` |\n| `customer.listOpenTickets` | Open support tickets for one customer | one prepared `SELECT` |\n\n**There is no `runSql(query)` tool, and there will not be one.** The whitelist is the safety boundary that honours the \"scoped reads\" promise in [`SKILL.md`](skills\u002Fsales-ai\u002FSKILL.md). Prompt injection arriving in customer email cannot widen the agent's data access — adding a tool requires a code change.\n\n### 90-second demo (SQLite, zero infrastructure)\n\n```powershell\n# 1. Download the SQLite JDBC driver into mcp-server\u002Flib\u002F\nInvoke-WebRequest `\n  -Uri 'https:\u002F\u002Frepo1.maven.org\u002Fmaven2\u002Forg\u002Fxerial\u002Fsqlite-jdbc\u002F3.42.0.1\u002Fsqlite-jdbc-3.42.0.1.jar' `\n  -OutFile 'mcp-server\u002Flib\u002Fsqlite-jdbc-3.42.0.1.jar'\n\n# 2. Compile the MCP server\n$src = Get-ChildItem -Recurse mcp-server\u002Fsrc\u002Fmain\u002Fjava -Filter *.java | %{ $_.FullName }\njavac -d mcp-server\u002Fout -Xlint:all $src\n\n# 3. Seed a demo SQLite DB with the same Lumora Robotics scenario\njava -cp 'mcp-server\u002Flib\u002Fsqlite-jdbc-3.42.0.1.jar;mcp-server\u002Fout' `\n     com.example.salesai.mcp.SeedData\n\n# 4. Re-run the engine, this time pointed at the SQLite DB\njava -Dstdout.encoding=UTF-8 `\n     -cp 'out;mcp-server\u002Flib\u002Fsqlite-jdbc-3.42.0.1.jar' `\n     com.example.salesai.SalesAiCli `\n     --db jdbc:sqlite:mcp-server\u002Fdemo.db `\n     --email wm.chen@lumora-robotics.example\n```\n\nSame report, same risk decision, same blocked drafts &mdash; this time sourced from real `SELECT` statements. To wire the MCP server into Claude Code itself (so the LLM can call the tools, not just the engine), see [`mcp-server\u002FREADME.md`](mcp-server\u002FREADME.md). For the design rationale &mdash; why whitelisting, why stdio, why ship our own server when generic DB MCPs exist &mdash; see [`docs\u002Fmcp-server.md`](docs\u002Fmcp-server.md).\n\n## Why this is not a chatbot\n\nA chatbot is prompt-first: the user types something, the model reads it, the model replies. Context is whatever scrolls back in the conversation, possibly augmented with retrieved snippets. The model's job is to respond to the message in front of it.\n\nA sales copilot is **context-first**. Before the model sees the customer's email at all, the agent loads the customer's tier, their contract status, their payment status, their recent orders, their open support tickets, and the account manager's notes. The email is read against that backdrop. The risk evaluation is performed against that backdrop. The drafts are composed against a redacted projection of that backdrop. The order matters: a chatbot reads the email and then asks \"do I happen to know who this is?\". A copilot answers \"who this is\" before it reads anything.\n\nThere is also a **hard safety boundary**. A refund request, a legal mention, a contract concession, an exceptional discount, a cancellation, or a churn signal on a VIP account forces the manager-approval gate. The drafts are produced, but they are blocked. The audit trail explains exactly why. A chatbot has no such gate; this agent has one as its first-class output.\n\nThe audit trail itself is the third thing chatbots typically do not have. Every port call writes one line. The CLI prints the audit summary at the bottom of every report. If a decision in the report looks wrong, you can read backwards from the conclusion through the steps that produced it. **The model is legible.**\n\n## Port &rarr; MCP migration\n\nThe full architecture lives in [`docs\u002Farchitecture.md`](docs\u002Farchitecture.md). The short version: hexagonal layout, no DI framework, Java 21 records for the domain, a hand-rolled JSON reader instead of Jackson or Gson, and a clean port &rarr; MCP mapping that drives the entire roadmap.\n\n| Port | Future replacement |\n|------|--------------------|\n| `CustomerContextPort` | CRM MCP server, Text2SQL on customer DB |\n| `EmailThreadPort` | Gmail MCP \u002F Outlook MCP \u002F IMAP MCP |\n| `RiskPolicyPort` | Policy engine, eventually LLM with structured output |\n| `ReplyDraftPort` | LLM via Agents-Flex Skill |\n| `CrmPort` | CRM MCP write operations |\n| `ApprovalPort` | Slack approval bot, ticketing system |\n| `AuditLogPort` | OpenTelemetry, Splunk, internal audit DB |\n\nEach phase of [`docs\u002Fintegration-plan.md`](docs\u002Fintegration-plan.md) replaces one or two of these ports. The other packages do not move.\n\n## Roadmap\n\n- [x] **Phase 1 &mdash; MVP.** Mock adapters, deterministic classifiers, console audit. This repo.\n- [ ] **Phase 2 &mdash; Real email.** Replace `MockEmailThreadAdapter` with Gmail MCP \u002F Outlook MCP. Reads remain customer-scoped.\n- [x] **Phase 3a &mdash; Real database.** `JdbcCustomerContextAdapter` + a SQL MCP server with four whitelisted query tools. SQLite for the demo, MySQL \u002F Postgres schemas committed. See [`mcp-server\u002F`](mcp-server\u002F) and [`docs\u002Fmcp-server.md`](docs\u002Fmcp-server.md).\n- [ ] **Phase 3b &mdash; Production CRM.** Point the same JDBC adapter and the same MCP tools at a real CRM (Salesforce, HubSpot) once Text2SQL is in scope.\n- [ ] **Phase 4 &mdash; Real LLM drafts.** Replace `TemplateReplyDraftAdapter` with an Agents-Flex Skill calling Claude \u002F Bedrock \u002F a local LLM, with prompt caching on the stable preamble.\n- [ ] **Phase 5 &mdash; Approval routing.** Replace `ManualApprovalAdapter` with a Slack approval bot or a ticketing-system integration.\n- [ ] **Phase 6 &mdash; Knowledge base \u002F RAG.** Plug a vector store of past won \u002F lost playbooks behind a new port.\n- [ ] **Phase 7 &mdash; Spring Boot service.** Wrap the workflow in Spring Boot, expose it as an MCP server other Claude Code skills can call.\n\nThe detailed shape of each phase, the new safety considerations it introduces, and the OAuth scopes we deliberately do NOT request are in [`docs\u002Fintegration-plan.md`](docs\u002Fintegration-plan.md).\n\n## Borrowed patterns, not borrowed code\n\nThe project owes a clear conceptual debt to several open-source efforts. **None of their source code lives in this repo.**\n\n- [Agents-Flex](https:\u002F\u002Fgithub.com\u002Fagents-flex\u002Fagents-flex) &mdash; Java agent framework. We adopted the Skill-as-spec philosophy and a port \u002F adapter shape that mirrors how an Agents-Flex Skill will plug in at Phase 4. We did not copy any source file, package layout, or class name.\n- [marlinjai\u002Femail-mcp](https:\u002F\u002Fgithub.com\u002Fmarlinjai\u002Femail-mcp) &mdash; unified email MCP across providers. We adopted the single-port-across-providers shape for `EmailThreadPort`. We do not ship an MCP server; we plan to consume one.\n- Public Gmail MCP server examples &mdash; read thread, read message, create draft, send-after-approval. We adopted thread-scoped reads, drafts-before-sends, and the approval-before-write posture.\n- Public CRM MCP server examples &mdash; get customer, recordInteraction-style writes. We adopted the read-mostly write surface and the structured-payload write shape.\n\nWe did not vendor, fork, or copy source code from any of these projects. The full per-project breakdown of what was adopted and what was explicitly not is in [`docs\u002Fborrowed-patterns.md`](docs\u002Fborrowed-patterns.md).\n\n## Use it as a Claude Code skill\n\nThe agent definition lives in [`skills\u002Fsales-ai\u002FSKILL.md`](skills\u002Fsales-ai\u002FSKILL.md). Drop the `skills\u002Fsales-ai\u002F` folder into your Claude Code skills directory (or use the project-local one), then ask Claude things like:\n\n- \"幫我看一下王經理那封信怎麼回。\"\n- \"Take a look at the Lumora thread &mdash; Wei-Ming is asking for a refund.\"\n- \"Customer CUST-1042 just escalated. Walk me through it.\"\n\nClaude will follow the eleven-step workflow in the Skill, call the bundled Java CLI as the tool layer, and surface the report. When the risk decision is `REQUIRES_MANAGER_APPROVAL`, Claude will tell you the drafts are blocked and stop until you grant approval explicitly.\n\n## Documentation\n\n| Doc | What's in it |\n|-----|--------------|\n| [`docs\u002Farchitecture.md`](docs\u002Farchitecture.md) | Hexagonal layout, package boundaries, why no DI, why a hand-rolled JSON reader, extension points. |\n| [`docs\u002Fsafety-rules.md`](docs\u002Fsafety-rules.md) | Every red line, with the *why* and the *how it's enforced* for each. |\n| [`docs\u002Fintegration-plan.md`](docs\u002Fintegration-plan.md) | Phase-by-phase migration toward MCP \u002F Agents-Flex \u002F Spring Boot. OAuth scopes we will and will not request. |\n| [`docs\u002Fborrowed-patterns.md`](docs\u002Fborrowed-patterns.md) | Per-reference breakdown of patterns adopted and source code explicitly not copied. |\n| [`docs\u002Fmcp-server.md`](docs\u002Fmcp-server.md) | Design rationale for the SQL MCP server: why whitelist tools, why stdio, why ship our own. |\n| [`mcp-server\u002FREADME.md`](mcp-server\u002FREADME.md) | How to compile, seed, and wire the MCP server into Claude Code. |\n| [`samples\u002Fadvisor-output.md`](samples\u002Fadvisor-output.md) | Both runs (default + `--approve`) verbatim. |\n| [`skills\u002Fsales-ai\u002FSKILL.md`](skills\u002Fsales-ai\u002FSKILL.md) | The agent definition. The actual product. |\n\n## Build this with us\n\nIf you want to help shape Sales AI from a GitHub demo into a real product &mdash; not just star and move on &mdash; get in touch.\n\nI'm looking for:\n\n- **B2B salespeople \u002F account managers** who feel the daily pain of email + CRM + escalations and want to test-drive the copilot on their own workflows.\n- **Java engineers** interested in productionising AI agents for enterprise environments (banking, insurance, manufacturing, ERP &mdash; the places where Java is already the lingua franca).\n- **Investors and founding partners** who believe sales automation is the next wave and want to back the Java-first, audit-by-default approach.\n- **Design partners** willing to pilot the agent inside their own sales team and shape the roadmap with real customer data.\n\n📩 **a0925281767s@gmail.com**\n\nTell me which bucket you're in, what kind of customers you sell to, and what would make this useful for you. The fastest path from \"interesting demo\" to \"shipping product\" is to build it with the people who will actually use it.\n\n## Contributing\n\nIssues, suggestions, and counter-examples are welcome &mdash; particularly counter-examples. If you can find a request that this agent handles badly (a thread it misclassifies, an approval gate it should have triggered but did not, a draft that leaks an internal-only field), please open an issue with the input that produced the bad output. The safety rules in [`docs\u002Fsafety-rules.md`](docs\u002Fsafety-rules.md) are the product, and protecting them is the most useful contribution you can make.\n\n## License\n\nMIT. See [`LICENSE`](LICENSE).\n\n## Suggested GitHub topics\n\n`java` · `java21` · `ai-agent` · `email-copilot` · `sales-automation` · `mcp` · `agents-flex` · `claude-code` · `hexagonal-architecture` · `llm-tools` · `account-management` · `b2b`\n","Sales-AI 是一个为B2B客户经理设计的AI助手，能够像资深客户经理一样阅读并处理客户的电子邮件。其核心功能包括加载客户资料和商业历史、分析邮件内容的相关性、识别意图与语气、评估风险，并根据公司政策生成两份回复草稿及后续行动建议。该工具不依赖任何外部库或网络连接，使用Java 21开发，可在标准JDK环境下快速运行。适用于需要全天候响应客户需求的企业场景，特别是制造业、金融、SaaS、跨境电商以及B2B服务等领域，帮助企业提高客户沟通效率和服务质量。","2026-06-11 04:09:19","CREATED_QUERY"]