{
  "markdown": "<p align=\"center\">\n  <img src=\"assets/hero.svg\" alt=\"sf-intelligence — Salesforce Org Intelligence for AI agents: a read-only, offline MCP server and CLI\" width=\"100%\">\n</p>\n\n<p align=\"center\">\n  <a href=\"./LICENSE\"><img src=\"https://img.shields.io/badge/License-MIT%20%2B%20Commons%20Clause-blue.svg\" alt=\"License: MIT + Commons Clause\"></a>\n  <a href=\"https://www.npmjs.com/package/sf-intelligence\"><img src=\"https://img.shields.io/npm/v/sf-intelligence.svg?color=cb3837&label=npm\" alt=\"npm version\"></a>\n  <a href=\"https://github.com/PranavNagrecha/Salesforce-Intelligence/actions/workflows/ci.yml\"><img src=\"https://github.com/PranavNagrecha/Salesforce-Intelligence/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"></a>\n  <img src=\"https://img.shields.io/badge/node-%E2%89%A520-339933.svg\" alt=\"Node.js >= 20\">\n  <img src=\"https://img.shields.io/badge/status-read--only%20%26%20offline-2ea44f.svg\" alt=\"Read-only and offline-first\">\n</p>\n\n# sf-intelligence\n\n**A grounded, fail-closed backend for AI assistants working in one Salesforce\norg.** Your AI host (Claude, or any MCP client) asks it questions in plain\nlanguage; it answers from the org's **real metadata** — never a guess — and\nlabels every claim with how much to lean on it.\n\n`sf-intelligence` is an **offline, read-only, MCP-first knowledge base** for a\nsingle Salesforce org. You run one retrieve (`sf project retrieve`); it builds a\nlocal Markdown vault and a DuckDB dependency graph. From then on every answer is\nserved locally by default — **offline-first, no network egress for vault\nanswers.** An **opt-in live read-only plane** (`sfi.live_*`) can answer record\ncounts and samples when enabled. Private by design. MIT + Commons Clause.\n\nIt is **not a standalone chatbot** — it has no chat surface. It is the\nevidence layer an AI host consumes: a router that **advises** (ranked tool\ncandidates plus explicit disclosures), a host LLM that **decides** (picks and\nruns the tools), and an engine that grounds every answer in the vault and\n**fails closed** — refusing, disclosing a gap, or asking a clarifying\nquestion — when it cannot.\n\n## Ask it like a person\n\nYou don't need to know the exact API name of anything. The front door is a\n**typo-tolerant resolver** that turns messy phrasing into the right component —\nand when it isn't sure, it **asks you a clarifying question** instead of\nguessing:\n\n> **You:** where's the emale field?\n>\n> **It:** I found several matches for \"email\" — which did you mean?\n> · `Email__c` on `Account`\n> · `Email__c` on `Contact`\n> · `Alternate_Email__c` on `Lead`\n\n> **You:** what about the paymnet object?\n>\n> **It:** `Payment__c` — one confident match.\n\n> **You:** is there a \"warranty fee\" field?\n>\n> **It:** Nothing matched that confidently. Your vault was last refreshed on\n> 2026-05-20 — want to pull fresh metadata from the org (`/sfi-refresh`) in case\n> it's new, or rephrase?\n\nThat is the headline behavior for **resolution and routing**: **they never\nsilently commit to a guess.** Resolution is always heuristic and labeled as\nsuch. When several components are plausible it hands you a clarifying question;\nwhen nothing matches it offers to refresh from the org or stop. A high match\nscore is string similarity, not proof. **Source grep tools** (`sfi.search_apex_source`,\n`sfi.search_flow_metadata`) walk the vaulted `source/` tree from your last\nrefresh — run `/sfi-refresh` before trusting an empty grep result.\n\n## Advisory routing — the funnel advises, the host decides\n\nUnder the hood a semantic **router** (`sfi.route_question`) reads each question\nand returns a **meaning-ranked shortlist** (`toolCandidates`) of the `sfi.*`\ntools that can answer it — so your AI host picks and runs them without you ever\ntyping a tool name. It runs fully **offline** (a small TF-IDF model over the\ntool catalog — no network, no embeddings service) and it **advises rather than\ndictates**: the candidates are the primary output; the deterministic `route`\nthat rides along is a non-authoritative hint. The host LLM decides which tools\nto run, in what order.\n\nConfidence semantics are explicit. When no deterministic intent matches but the\nsemantic funnel's top candidate scores above a fixed floor, the router returns a\n**`funnel-advisory` route** — the top funnel tools, confidence **`low` by\nconstruction**, reason flagged `FUNNEL-DERIVED` — an advisory pick for the host\nto verify (resolve the named component, then ground), never a command. Each\ncandidate row also carries `cosine`, its raw semantic score, so a host can tell\nreal semantic support from a regex-rule assertion.\n\nThe router also tags each question with the plane that answers it —\nthe **offline vault** (metadata, dependencies, permissions), the **live org**\n(counts, samples, limits, inactive users — read-only, opt-in), or a **hybrid** of\nboth (e.g. \"is this field *actually* populated?\"). Every answer is stamped with\nits provenance (`offline_snapshot`, `live_org`, or `hybrid`) and freshness.\nClarifying questions are a **last resort**: a qualifier already in the question\n(\"the X *object*\", an object word next to a same-named field, a literal API\nname) auto-resolves instead of blocking, and offered options are hygienic —\nfuzzy lookalike junk never appears as a choice. But when two genuinely competing\ncomponents remain, or the best-fitting tools diverge on something consequential\n(one destructive-simulation, one read-only), the router **stops and asks which\nyou meant** instead of letting the host silently commit. When nothing fits, it\n**says so** rather than guessing (and can log the gap locally — opt-in via\n`logGap: true`). (A deterministic, no-LLM routing mode is available via\n`SFI_ROUTER_MODE=offline` for CI / air-gapped hosts.)\n\nAn **experimental, opt-in RRF hybrid embeddings layer** is available for early\nadopters (`SFI_EMBEDDINGS=1` + `npm i @huggingface/transformers`). It fuses the\nTF-IDF candidates with a locally cached neural model (`~23 MB`) via Reciprocal\nRank Fusion. The model is **not bundled** with the npm package and isn't\nfetched automatically — it requires the separate peer-dependency install\nabove, and the download-on-first-use path is still being hardened, so treat it\nas a manual opt-in step, not something that happens for you. **Off by\ndefault** either way — the lexical path is byte-identical when unset, and if\nthe model isn't installed or cached the funnel silently falls back to\nlexical-only. The honesty/refusal decision and the deterministic `route.tools`\nplan are not affected. See\n[`docs/configuration.md`](./docs/configuration.md) for details.\n\n## Refusal behavior — fail closed, offer the read\n\nSome questions should never route to an executable tool, no matter how well\nthey score. Score-independent **refusal gates** run on the raw question before\nany intent matching, and a refusal is non-executable *by shape* (`tools: []`\nplus a structured `route.refusal` disclosure):\n\n- **Write imperatives** (\"delete the X field for me\", \"go ahead and merge these\n  profiles\") → `refused-write`, with a **read-only alternative** offered instead\n  (`safe_to_delete_field`, `what_if_merge_profiles`, `get_impact`, … by verb\n  family) — the product has no write path; the refusal names the simulation\n  that answers the underlying question safely.\n- **Prompt injection / record-value exfiltration** (\"ignore your previous\n  instructions…\", \"dump all SSN values\") → `refused-injection`, with candidates\n  and guidance suppressed entirely.\n- **Runtime telemetry no tool models** → `honest-gap-runtime`, naming the\n  nearest real reads. **Non-Salesforce asks** → `out-of-scope`.\n\nLegitimate reads are explicit excluders — \"am I allowed to edit…\", \"who can\ndelete…\", \"is it safe to…\" are permission *questions* and route normally. On a\n2,000-question real-org evaluation, the gates cut genuine over-confident routes\nfrom 69 to 11 with **zero** answerable questions falsely refused.\n\n## Conversation context — follow-ups without server-side memory\n\nThe product stores **no conversation state**. Instead, the host may pass an\noptional `context.previous` on each `route_question` call describing what the\nprior turn was about, and terse follow-ups (\"does it fire on delete too?\",\n\"what about on Contact?\", \"the second one\") resolve against it — pronoun\nsubstitution is an **exact-id** lookup (never fuzzy), an inherited tool is an\nadvisory continuation **capped at `medium` confidence**, and a clarification\npick re-dispatches through the normal clarification contract (out-of-range\nordinals re-ask, stale ids are rejected). A self-contained question ignores\ncontext entirely, and refusal gates run before any context logic — context\nnever bypasses them. Host-side, after routing \"who can edit the SSN field?\"\nand running the tools:\n\n```jsonc\n// next turn: \"can Support Agents specifically edit it?\"\n{\n  \"question\": \"can Support Agents specifically edit it?\",\n  \"context\": {\n    \"previous\": {\n      \"question\": \"who can edit the SSN field?\",\n      \"tool\": \"sfi.field_access_audit\",\n      \"componentId\": \"CustomField:Contact.SSN__c\"\n    }\n  }\n}\n```\n\nWhen (and only when) context changes the route, the response discloses it in\n`route.contextApplied`. See [docs/routing.md](./docs/routing.md) for the full\nhost contract.\n\n## Honesty guarantees\n\nThe design rule across the surface is **fail closed, disclose first**:\n\n- **Grounded or refused.** Every answer path ends in real tool output against\n  the vault (or the consented live org); when no tool covers the ask, the\n  router returns an honest gap — never a lookalike tool, never general\n  Salesforce knowledge dressed up as org fact.\n- **Premise checks.** A question naming a component the resolver cannot find\n  still routes (the tools fail closed on the unknown id), but confidence is\n  downgraded and a `PREMISE CHECK` disclosure warns that no such component\n  exists in the vault — and a funnel-advisory route is never granted on a\n  failed premise.\n- **Disclosure-first.** Coverage caveats, staleness warnings, live-consent\n  notices, refusals, and context application are structured fields the host\n  renders *before* the answer — not fine print after it.\n\n## How it works\n\nOne read-only **refresh** turns your org into a local vault; from then on every\nquestion is answered **offline** — your AI host asks the router for a shortlist,\npicks the tools, runs them against the vault, and grounds the answer. The host\ndecides; the router only advises.\n\n```mermaid\nflowchart TB\n    subgraph REFRESH[\"Refresh — once, and whenever the org changes\"]\n        direction LR\n        ORG[(\"Salesforce org\")] -->|\"sf project retrieve (read-only)\"| SRC[\"source/ raw metadata\"]\n        SRC -->|\"extract + parse Apex / Flow / XML\"| VAULT[(\"Graph A · Local vault<br/>your org, grounded<br/>Markdown + DuckDB graph\")]\n    end\n\n    subgraph CONCEPT[\"Concept Model — ships with the package · no org data, ever\"]\n        CM[[\"Graph B · Concept Model<br/>143 org-independent concepts / 195 rules<br/>save-order · relationships · sharing · code-shape\"]]\n    end\n\n    subgraph ASK[\"Ask — every question, offline on your machine\"]\n        direction TB\n        Q[\"Your question<br/>(plain language)\"] --> HOST[\"Host LLM<br/>(Claude, etc.)\"]\n        HOST -->|\"1 · route_question\"| FUNNEL[\"Semantic funnel<br/>offline TF-IDF — no network\"]\n        FUNNEL -->|\"toolCandidates + guidance<br/>(ranked shortlist — advises)\"| HOST\n        HOST -->|\"2a · retrieve: picks and runs\"| TOOLS[\"sfi.* retrieval tools\"]\n        TOOLS -->|\"read\"| VAULT\n        TOOLS -.->|\"opt-in, read-only, capped\"| LIVE[(\"Live org\")]\n        HOST -->|\"2b · reason: resolve\"| RESOLVE[\"sfi.resolve<br/>fix the component\"]\n        RESOLVE -->|\"interpret\"| INTERPRET[\"sfi.interpret · deterministic OFFLINE JOIN<br/>Concept Model × grounded vault slice<br/>no LLM · no live org read\"]\n        INTERPRET -->|\"cited, confidence-tiered<br/>structural-implication claims\"| SYNTH[\"3 · synthesize_answer\"]\n        TOOLS --> SYNTH\n        SYNTH --> ANS[\"Grounded answer<br/>+ provenance + freshness<br/>+ cited canonical ids\"]\n    end\n\n    VAULT ==>|\"grounded slice · Graph A\"| INTERPRET\n    CM ==>|\"concept rules · Graph B\"| INTERPRET\n```\n\nEvery box on the **Ask** path runs on your machine; the dotted edge to the live\norg is the only one that can touch Salesforce, and only after you opt in.\nRetrieval (`route_question → sfi.* → synthesize_answer`) reports *what's in the\norg*; the reasoning path (`resolve → interpret → synthesize_answer`) reports\n*what its shape implies*. `sfi.interpret` is a deterministic, offline **join** of\nthe org-independent **Concept Model** (Graph B — 143 concepts / 195 rules, which\nnever touches your org) against a grounded slice of the vault (Graph A); it runs\nwith no LLM and no live read, and every claim it feeds `synthesize_answer` is\ncited and confidence-tiered. The deterministic `SFI_ROUTER_MODE=offline` mode\ncollapses step 1 to a single routed plan for hosts with no LLM in the loop.\n\n## Reasoning, not just retrieval\n\nRetrieval tells you *what's in the org*. It doesn't tell you what a structure\n**implies** — that a master-detail parent delete cascade-deletes its children,\nthat two active before-save flows on one object run in an **undefined order**, or\nthat an `@AuraEnabled` method is an entry point where Apex does not auto-enforce\nfield-level security. SfIntelligence answers those with a small second graph.\n\nAlongside the org's grounded vault, the product ships a **Concept Model**: **143**\norg-independent, curated concepts and **195** rules that encode general Salesforce\ntruth — save-order phases, relationship semantics, sharing posture, code-shape\nsignals. No org data lives in it; the org enters reasoning **only** through the\ngrounded slice at query time. The `sfi.interpret` tool **joins** the two: it\nassembles a minimal graph slice around one component and fires the applicable\nrules to produce **cited, confidence-tiered structural-implication claims**. It\nis **deterministic and offline** — no LLM, no live org read — and its claims are\nalso folded into `sfi.synthesize_answer` so they reach a normal answer, hedged\nand attributed, on any MCP host.\n\nThe concept families are curated structural patterns, not vulnerability\ndetection. A sample of what fires today:\n\n- **Relationships** — master-detail cascade-delete + read-only roll-up summary;\n  junction (two-master) many-to-many; formula/roll-up fields are read-only, so a\n  write to one is write-hostile.\n- **Automation** — multiple active record-triggered flows in one trigger context\n  (undefined execution order); a firing condition that tests a field another\n  automation writes in the same save (cross-phase coupling); before/after trigger\n  phases; a flow fault-path rollback gap.\n- **Sharing & security surface** — organization-wide default (OWD) posture;\n  `without sharing` system-context Apex; an external API surface\n  (`@RestResource` / `@AuraEnabled` / `@InvocableMethod`) where FLS/CRUD are not\n  auto-enforced; async boundaries; connected-app OAuth scope.\n- **Code shape** — Apex SOQL-injection / bulkification / CRUD-FLS / swallowed-\n  exception / hardcoded-id gaps; a test with no assertions.\n\nEvery claim is **grounded or it does not exist**. Each interpretation carries a\n`groundedIn` list of the exact component ids it matched — **no citation ⇒ no\nclaim** — and its confidence is **computed, never asserted**: the *weakest* of\nthe rule's ceiling and the grounding edges it matched (see the two-axis note\nbelow). An empty result means **\"no concept rule fired,\"** never \"nothing depends\non it.\" And the governor/security concepts reason about **static code shape** —\nthey name a surface or a coupling, not a live limit breach or a proven runtime\nvulnerability. For a worked resolve → interpret → synthesize example, see\n[`docs/guides/asking-questions.md`](./docs/guides/asking-questions.md) §2b; for\nthe design rationale, [ADR-008](./docs/decisions/ADR-008-deterministic-concept-model-reasoning.md).\n\n## What you can ask\n\nEight capability areas, each answerable in natural language (ask\n`sfi.capabilities`, or just \"what can you do?\", for the live map):\n\n| Area | Example questions |\n| --- | --- |\n| **Find & identify** | \"where is the email field?\" · \"what's the payment object called?\" |\n| **Understand** | \"what does this validation rule do?\" · \"what happens when an Account is saved?\" (automation on standard objects works even when the object file was not retrieved; `objectModeled: false` is surfaced) |\n| **Impact & dependencies** | \"what breaks if I delete this field?\" · \"is it safe to deactivate this flow?\" |\n| **Permissions & sharing** | \"why can't this user see this record?\" · \"who can edit the Salary field?\" · \"who holds the Sales Manager permission set?\" (live) · \"what does user Jane hold?\" (live) · \"who's in the Support queue?\" (live) · \"which active users have zero permission-set assignments?\" (live) |\n| **Automation & code** | \"what runs on Case create?\" · \"which Apex methods have no real test coverage?\" · \"decode this Apex debug log / governor-limit exception to the class that ran\" |\n| **Decision-support (before you build/change)** | \"before I add automation to this object, what already runs there?\" · \"building Apex here — what should I watch out for?\" · \"before I change/require this field, what breaks?\" |\n| **Architect & developer** | \"are there circular Apex dependencies?\" · \"which classes have no test reference?\" · \"does the vault still match the live org?\" |\n| **Integrations** | \"what external systems does this org talk to?\" · \"list every outbound endpoint\" |\n| **Documentation** | \"give me a tour of this org\" · \"generate a data dictionary\" · \"which reports / objects / permission sets have no description?\" (`list_components` with `missingDescription: true`) |\n| **Health & audit** | \"is my vault fresh?\" · \"where is PII stored?\" · \"how has the org changed across refreshes?\" |\n\nThese are **advisory, read-only briefings** — e.g. `automation_build_advisor`,\n`apex_build_advisor`, `field_change_advisor` synthesize what the org already\nshows so you make a better build decision; `find_dependency_cycles`,\n`apex_test_coverage`, `live_drift_check`, and `org_history` serve architects and\ndevelopers. None of them write to the org.\n\nEvery org artifact the product names is backed by a tool call and cited with its\ncanonical ID (`CustomObject:Account`, `CustomField:Account.Industry__c`), and\nevery relationship is cited with its confidence (`declared`, `parsed`, or\n`heuristic`).\n\n## Trust & confidence glossary\n\nEvery answer is tagged so you know how much to lean on it. These tags match the\nruntime values verbatim (the `trust` block on analysis tools, the per-edge\n`confidence`, and `synthesize_answer`'s `provenance.stamp`).\n\n**Edge confidence** — how a *relationship* was derived. This is the per-edge tier\nyou see on dependency answers:\n\n- `declared` — Salesforce metadata states it directly (a layout assignment, a\n  field's `referenceTo`, a permission grant). Highest trust.\n- `parsed` — produced by AST/XML parsing of source (the **parser-grade Apex\n  pass that runs on every refresh by default** — resolved field reads/writes,\n  cross-class calls, field-level SOQL — plus the formula tokenizer, Flow\n  elements, a profile's `<layoutAssignments>`). High trust.\n- `heuristic` — produced by regex / token / dynamic-string analysis (the Apex\n  recall scanner that supplements the parsed pass, name-pattern detection). May\n  have false positives — spot-check before acting.\n\n**Claim confidence** — a *different* axis: how well a **reasoning claim** from\n`sfi.interpret` is grounded. It uses the same `declared | parsed | heuristic`\nwords, but it is **computed, not read off one edge**: a claim's confidence is the\n*weakest* of the concept rule's ceiling and the grounding edges the claim matched.\nAn absence-shaped claim under non-complete coverage reads `unknown`. Do not\nconflate the two — edge confidence describes a single relationship; claim\nconfidence describes an interpretation that *rests on* one or more such edges and\ncan never exceed the weakest of them.\n\n**Provenance** — where the answer came from:\n\n- `offline_snapshot` — the last `/sfi-refresh` vault. The default for every vault\n  tool.\n- `live_org` — an opt-in, capped, read-only `sfi.live_*` SOQL read.\n- `hybrid` — fuses vault + live and discloses both provenances.\n\n**Completeness** — how much of what the answer depends on was actually retrieved:\n\n- `complete` — the refresh modeled every metadata family the answer needs.\n- `partial` — a family the answer depends on was not retrieved; absence means\n  \"not checked\", never \"none\" (a `coverageCaveat` names the gap).\n- `unknown` — coverage could not be determined.\n\nTwo tools make this concrete. `sfi.coverage_report` lists what the retrieve\nmanifest *requested and returned*; `sfi.retrieve_blindspot_report` lists what the\ngraph *references but never retrieved* — automation/code/config that depends on a\ncomponent the vault never pulled — so an \"X is unused / nothing references X\"\nanswer carries a known-coverage caveat instead of a silent blind spot.\n\n## How much it models\n\n| | |\n| --- | --- |\n| **MCP roster** | Every `sfi.*` tool is registered in code; run `sfi.capabilities` (see `productManifest`) for the live registered/advertised counts — never a handwritten number |\n| **Graph model** | A broad `ComponentType` union across **103** component types (objects, fields, Flows, Apex, layouts, permissions, sharing, UI, legacy automation, integrations, CPQ, OmniStudio, reports, FlexiPages, and more) connected by **23** typed edge types — see `sfi.capabilities` for how tools group those families. Counts are pinned by `eval/product-manifest.json`. |\n| **Concept Model** | Concept Model (143 concepts / 195 rules) — org-independent, curated — JOIN against the grounded vault to produce cited structural-implication claims via `sfi.interpret`. No org data lives in the model. Same figures in `eval/product-manifest.json`. |\n| **26** | skills + **5** slash commands + **4** subagents (Claude Code plugin layer) that auto-activate in a session |\n\n## What it does NOT do\n\nBoundaries are explicit, and the product tells you plainly when it hits one\nrather than papering over the gap with general Salesforce knowledge:\n\n- **Offline by default.** Vault tools never call Salesforce mid-conversation.\n  Run `/sfi-refresh` to update metadata. **Opt-in live tools** (`sfi.live_*`)\n  run read-only SOQL with strict caps and label answers `provenance: live_org`.\n  The live plane is **off until you grant once per org** with\n  `sfi.live_consent { grant: true }` (binds OrgId+principal, scopes+expiry;\n  persists locally) or set `SFI_LIVE_PLANE_ENABLED=1`. Per-call\n  `liveEnabled: true` is intent only — not a consent substitute. Step up\n  `sample` / `users` scopes for row samples and user-identity tools.\n  **Hybrid** answers fuse vault + live and disclose both provenances plus the\n  active grant. Live never backfills stale vault claims, and the product never\n  auto-picks which org to query.\n- **No record-level data.** The vault stores schema and source, not rows. \"How\n  many Opportunities closed last quarter\" is a question for your org directly.\n- **Static analysis, not runtime.** Dependency edges are derived from metadata\n  and source. Dynamic SOQL, reflective Apex, and runtime metadata lookups are\n  invisible to static analysis — a \"no references found\" result means \"no static\n  evidence\", not \"definitely unused\".\n- **Reasoning is deterministic rule-match, not an LLM and not a live check.**\n  `sfi.interpret` fires curated concept rules against the offline grounded slice.\n  Its claims are structural implications of the *code and metadata shape* — it\n  names a pattern (a cascade-delete, an undefined flow order, an unenforced entry\n  point), and cites the ids it matched. A governor or security concept is a\n  **static-shape signal, not a proven runtime limit breach or a proven\n  vulnerability**; a claim with no citation is never made, and an empty result\n  means \"no rule fired,\" not \"nothing depends on it.\"\n- **Read-only.** The product never writes to your org. It has no write path.\n- **Tested scale (CI budgets).** Three separate ceilings are gated in CI:\n  - **Graph import:** 10,000 nodes in &lt;90s (`packages/graph/test/scale-import.test.ts`, `SCALE_IMPORT_BUDGET_MS`).\n  - **Full refresh:** 1,000 `CustomObject`+`CustomField` files by default in &lt;10m (`packages/cli/test/scale-refresh.test.ts`, `SCALE_REFRESH_FIELD_COUNT`, `SCALE_REFRESH_BUDGET_MS`).\n  - **Resolve:** p95 under 2s on the CI vault (`pnpm eval:scale`, `SCALE_BUDGET_MS`).\n  Very large production orgs may still need narrowed retrieves or multiple vaults.\n\n## Review changes before you deploy (PR gate)\n\n`sfi.review_change` is the pre-deploy gate: hand it the components a PR /\n`package.xml` / `git diff` touches and it returns a per-component risk verdict\n(`blocking` / `risky` / `review` / `safe`), each one's direct dependents, and the\ntests to run — most-dangerous first, entirely offline against the target org's\nlast vault refresh. A deleted component with any dependent fails **closed**\n(`blocking`); a modified component with firm dependents is `risky`; `overallVerdict`\nis the worst across the set. Point it at another vault (`againstVault`) to answer\n\"will this changeset break anything in **prod**?\" against that org's graph.\n\nThe same analysis ships as a GitHub Action — the composite Action at\n[`.github/actions/review-change`](./.github/actions/review-change/README.md)\nemits SARIF 2.1.0 (findings show up inline on the PR's \"Files changed\" tab and in\nthe Security tab) plus a markdown PR comment, with a 0/1/2 exit-code gate. Copy\n[`docs/ci/review-change-pr-gate.example.yml`](./docs/ci/review-change-pr-gate.example.yml)\ninto your org repo to wire it up. It never runs `sfi refresh` and never calls the\n`sf` CLI — it only reads an already-built vault.\n\n## Try it now — no Salesforce org needed\n\nWant to see it work before pointing it at your own org? One command serves a\nbuilt-in **synthetic demo org** (\"Verdant Energy,\" a fictional solar installer)\nover MCP — fully offline, no auth, no `sf` CLI:\n\n```bash\n# Register the demo server with Claude Code (or any MCP client):\nclaude mcp add --transport stdio --scope user sf-intelligence-demo -- npx -y sf-intelligence demo\n```\n\nThen ask it things like:\n\n> - *What happens when I save a Project?*\n> - *What breaks if I delete `Invoice__c.Amount__c`?*\n> - *Why can't an Installer see an Invoice?*\n> - *Which Apex has governor-limit risk?*\n\nThe first run builds the demo vault in a few seconds (cached under\n`~/.sf-intelligence/demo`); every run after is instant. Nothing leaves your\nmachine. When you're ready for your real org, follow **Install** below.\n\n## Install\n\n`sf-intelligence` is distributed on npm as `sf-intelligence` — an MCP\nserver plus the `sfi` command-line tool. Register the server with your MCP\nclient once, then drive everything through `sfi` (or, inside Claude Code, the\n`/sfi-*` slash commands that wrap it).\n\n**Requirements:** [Node.js 20+](https://nodejs.org) and an authenticated\n[Salesforce CLI](https://developer.salesforce.com/tools/salesforcecli) (`sf`)\npointed at the org you want to vault. `npx` fetches everything else.\n\n### Register the MCP server\n\nEvery host launches the same command; each reads a **different config file in a\ndifferent format**. Pick yours — the full per-host, per-platform detail\n(exact paths for macOS and Windows, where the logs are, what to do when it\ndoesn't connect) is in **[docs/guides/mcp-hosts.md](./docs/guides/mcp-hosts.md)**.\n\n| Host | Config file | Top-level key |\n|---|---|---|\n| Claude Code | `.mcp.json` (project) or `~/.claude.json` | `mcpServers` |\n| Claude Desktop | `claude_desktop_config.json` | `mcpServers` |\n| Codex | `~/.codex/config.toml` | `[mcp_servers.*]` (TOML) |\n| VS Code + GitHub Copilot | `.vscode/mcp.json` | **`servers`** |\n\n**Claude Code** — from your Salesforce DX repo, add it project-scoped (writes a\n`.mcp.json` at the repo root that your team can commit):\n\n```sh\nclaude mcp add --scope project sf-intelligence -- \\\n  npx -y sf-intelligence mcp --vault \"$PWD/org-kb\"\n```\n\n**Claude Desktop** — add this block to the client's MCP config\n(`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS,\n`%APPDATA%\\Claude\\claude_desktop_config.json` on Windows), then quit and reopen\nthe app:\n\n```json\n{\n  \"mcpServers\": {\n    \"sf-intelligence\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"sf-intelligence\", \"mcp\", \"--vault\", \"/abs/path/to/org-kb\"]\n    }\n  }\n}\n```\n\n**VS Code + GitHub Copilot** — create `.vscode/mcp.json` in your project. Note\nthe top-level key is `servers`, **not** `mcpServers`; pasting the Claude block\nhere parses fine and registers nothing:\n\n```json\n{\n  \"servers\": {\n    \"sf-intelligence\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"sf-intelligence\", \"mcp\"],\n      \"cwd\": \"${workspaceFolder}\",\n      \"env\": { \"SFI_VAULT\": \"${workspaceFolder}/org-kb\" }\n    }\n  }\n}\n```\n\n**Codex** — `codex mcp add sf-intelligence -- npx -y sf-intelligence mcp --vault /abs/path/to/org-kb`,\nor the TOML equivalent in `~/.codex/config.toml`.\n\nRestart the client. The `sfi.*` tools are now available — ask `sfi.capabilities`\nfor the live tool map.\n\n> **Always pass an absolute `--vault` path.** The server looks for `./org-kb`\n> relative to *its own* working directory, and most hosts pick that directory\n> for you — Claude Desktop and a user-scope VS Code server never run inside your\n> Salesforce project. `--vault` is what makes the config work anywhere.\n>\n> **Before your first `sfi refresh`** the server starts in **setup mode**: it\n> connects and offers one tool, `sfi.setup_status`, which tells your chat exactly\n> what to run next. That is expected — just ask your assistant to set it up.\n\n> **Tip:** `npm install -g sf-intelligence` puts an `sfi` command on your\n> PATH, so first-run setup is `sfi init` / `sfi refresh` instead of the longer\n> `npx -y sf-intelligence …` form.\n\n### Optional: full roster vs compact core\n\n**Default is the 25-tool core roster** (AUDIT-F6; includes `sfi.live_consent`)\nso MCP hosts don't pay a ~250 KB `tools/list` tax. Non-core tools stay\nreachable via `sfi.run_analysis` (byte-identical) and are not directly\ninvokable under core. Set `SFI_TOOL_PROFILE=full` (or add\n`\"env\": { \"SFI_TOOL_PROFILE\": \"full\" }` in the config block above) to advertise\nand directly invoke the entire roster. See\n[docs/configuration.md](./docs/configuration.md) for the full reference.\n\n### Install as a Claude Code plugin\n\nThe MCP registration above gets you the `sfi.*` tools. Installing\n`sf-intelligence` as a **Claude Code plugin** additionally gets you **26\nskills** that auto-activate on Salesforce vocabulary (no need to remember tool\nnames) and **5 slash commands** (`/sfi-onboard`, `/sfi-init`, `/sfi-refresh`,\n`/sfi-status`, `/sfi-field-audit`) that wrap the `sfi` CLI. Install it from this repo's\nmarketplace:\n\n```sh\n/plugin marketplace add PranavNagrecha/Salesforce-Intelligence\n/plugin install sf-intelligence@sf-intelligence\n```\n\nRun `/reload-plugins` (or restart Claude Code) to pick it up in the current\nsession; `/plugin list` confirms it's installed and enabled. The plugin\nmanifest registers the same MCP server shown above, so once it's installed\nyou don't need a separate `claude mcp add` step.\n\n## First run\n\nWork from **your per-org repository** — the Salesforce DX project you want to\nvault (the directory with `sfdx-project.json`). The first refresh is read-only:\nit retrieves metadata and builds a local knowledge base; it never deploys or\nmutates Salesforce data.\n\n```sh\n# 0. New here? The guided path walks install → auth → init → refresh → ask,\n#    with real starter questions at the end.\nsfi quickstart\n\n# 1. Create the local org-kb/ vault layout and record which sf org alias to use.\nsfi init\n\n# 2. Retrieve metadata and build the vault.\nsfi refresh --target-org my-org-alias\n\n# 3. Confirm vault freshness, source-tree hash, and component counts any time.\nsfi status\n\n# sfi doctor checks the sf CLI, the vault, target-org auth, freshness, and the\n# graph file, and prints an actionable fix for each problem.\nsfi doctor\n```\n\n**How long does a refresh take?** A small sandbox builds in a few minutes; a\nproduction-scale org is typically **~10–12 minutes** under the defaults — the\nretrieve dominates, and the default build also runs the parser-grade Apex pass\n(seconds per few hundred classes) and pulls the **top 500 reports/dashboards by\nactual usage** (a minute or two on report-heavy orgs; `--no-reports` skips it).\nIn a hurry on a big org? `sfi refresh --staged` serves a skeleton vault in\nseconds and the ten priority metadata families within minutes, then finishes\nthe full build behind the scenes — see the\n[first-refresh guide](./docs/guides/first-refresh.md). Re-refreshes re-extract\nand rebuild the graph in full by default (results always match a cold build);\n`--incremental` reuses the per-file parse cache for the same result, faster,\nand `--incremental-graph` additionally re-imports only the changed nodes/edges\ninto the graph instead of rebuilding it — combine both for the largest win.\n`--types <list>` scopes either flag to specific metadata types (e.g.\n`sfi refresh --types Flow --incremental-graph`); a scoped refresh only ever\ntouches the requested type(s) — every other type in the vault is left as-is.\n\nNo global install? Prefix each with `npx -y sf-intelligence`, e.g.\n`npx -y sf-intelligence init`.\n\nThen ask anything the vault can answer, in whatever MCP client you registered:\n\n> What fields does the Account object have?\n>\n> What breaks if I delete `CustomField:Account.Industry__c`?\n>\n> Why can't the Standard User profile see Opportunities?\n>\n> Give me a tour of this org.\n\n**Running sf-intelligence as a Claude Code plugin?** The same operations are\navailable as slash commands — `/sfi-onboard` (guided first run), `/sfi-init`,\n`/sfi-refresh`, `/sfi-status` — and the coaching skills auto-activate when\nSalesforce vocabulary appears. See [Install as a Claude Code\nplugin](#install-as-a-claude-code-plugin) above if you haven't installed it\nthat way yet.\n\n### Serve it over HTTP (read-only)\n\n`sfi mcp` is the stdio server your MCP client launches. To share one vault with\nother machines or clients, the same server speaks streamable HTTP:\n\n```sh\n# From the vault directory: prints a bearer token once, listens on 127.0.0.1:8787.\nsfi serve --http --generate-token\n\n# Team path: JSON token→identity map (annotation writes attribute to the caller).\n# sfi serve --http --tokens-file ./tokens.json\n```\n\nThe remote posture is deliberately strict: a bearer token is required on every\nrequest (solo `--token` / `--generate-token`, or `--tokens-file` for per-caller\nidentity), the bind is loopback unless you pass `--host` (a non-loopback host\nwarns and refuses to run tokenless), and the **live plane is hard-disabled over\nHTTP** — a remote caller can never spend your Salesforce API budget, even if\nthe host has standing live consent. A refresh underneath a running server is\nsafe: readers keep answering from the old graph until the new one swaps in.\n\n### Give the vault a memory\n\n```sh\n# One-time, from the vault directory: inits a git repo INSIDE org-kb/\n# (rebuildable surfaces like graph/ and snapshots/ are gitignored).\nsfi vault git enable\n```\n\nFrom then on every refresh commits the vault's source and rendered Markdown, so\n\"**when did this component change?**\" (`sfi.component_history` — one timeline\nentry per source-changing refresh) and \"**what did it look like before?**\"\n(`sfi.component_as_of`) become answerable from the vault's own history. A vault\nwithout git answers those honestly (`available: false` plus this enable hint) —\nnever an error.\n\n## Privacy\n\nEverything stays on your machine. The vault (`org-kb/`) is local; the MCP server\ndefaults to `SFI_NETWORK_MODE=off` and makes no network calls while answering\nvault questions. Optional egress is explicit: npm update-check only when\n`SFI_UPDATE_CHECK=1` (or `updates-only` mode); Salesforce retrieve/live only\nwhen refresh or an authorized live tool elevates to `salesforce-read`. This\npublic repository ships **zero org data** — a release privacy guard scans the\nshipping set on every release and fails the build if a real org identifier\nleaks. What you vault is yours.\n\n## Roadmap\n\nThe product is read-only today by design; that is the major axis of future work.\n\n- **Deeper code resolution.** Parsed Apex call edges carry the called method\n  names today; the remaining composite tools still reason at class granularity —\n  method-level reachability across them is the next step.\n- **A careful write side.** A small, opt-in set of *proposal* tools (e.g. draft a\n  permission-set diff or a validation-rule edit for human review) once the read\n  side is mature. The default will always be read-only.\n\n(Earlier roadmap entries shipped: Tooling-API-backed stale-vault detection is\nnow the `sfi watch` daemon + drift badges; remote read-only serving is\n`sfi serve --http`.)\n\n## Feedback\n\nA weak or wrong answer, or a question it couldn't route? That's the most useful\nthing you can send back. It's captured **locally** — nothing phones home:\n\n```sh\nsfi feedback mark \"where is the SSN field used\" --wrong   # or --weak\nsfi feedback export                                       # → sfi-feedback.json (scrubbed)\n```\n\n`sfi feedback export` bundles the local route-gap log plus your ratings into one\nfile with org PII (emails, URLs, record ids) stripped — component/api names are\nkept because they're the signal. Exports are **scoped to the current vault by\ndefault**: the log file is machine-global (`~/.sf-intelligence/`), but each gap\nis stamped with the vault it was asked against, and only the current vault's\ngaps are exported (the file reports how many were excluded). `--all` exports\nthe whole machine-global log — review it before sharing if you work across\nmultiple orgs. Share it (or just describe the gap) at\n<https://github.com/PranavNagrecha/Salesforce-Intelligence/issues>.\n\n## License\n\n`sf-intelligence` is licensed under the **MIT License with the [Commons Clause](./LICENSE)** (see also [`NOTICE`](./NOTICE)). In plain English:\n\n- ✅ **Free to use for any purpose — including at work, inside a company.** Evaluate, study, modify, self-host for your own use, fork, redistribute, and contribute — all free.\n- 🚫 **You may not _Sell_ it without a commercial license** — i.e. provide it to third parties, for a fee, as a product or service whose value derives substantially from the Software. That includes offering it (or a hosted / SaaS / derivative version) as a paid product or service, reselling it or paid access to it, or charging for hosting/support whose value derives substantially from it.\n\nFor a commercial (\"Sell\") license, contact **pranav.sfintelligence@gmail.com**. _(Plain-English summary; the [LICENSE](./LICENSE) controls. Not legal advice.)_\n\n## Documentation\n\n- [Documentation index](./docs/README.md) — guides, architecture, configuration\n- [Contributing](./CONTRIBUTING.md) · [Security](./SECURITY.md)\n\nThe build harness (a sibling repo, separate from this product) vendors Addy\nOsmani's [`agent-skills`](https://github.com/addyosmani/agent-skills) under MIT.\nCopyright (c) Addy Osmani; full MIT license text travels with the harness. None\nof that content is redistributed in this product directory; the attribution is\nrecorded here for completeness.\n",
  "bytes": 39414,
  "sha": "4db55c29a183e20263510ed4c4795fdfd51d608d109dc3e454d9daacdcc26168",
  "repo_slug": "pranavnagrecha/salesforce-intelligence",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_pranavnagrecha_sf_intelligence_49519a63/readme"
}