Competitor gap analysis
CompetitorAnalyzer, added in 0.8.0, runs seven evidence-backed gap-reason detectors against a MeasurementReport and emits only the reasons the data actually supports — sorted by impact, each with concrete evidence and an action item. Nothing is fabricated when the data doesn't support a reason.
The seven gap reasons
| Reason | What it detects | Typical action item |
|---|---|---|
citation-gap | Cited by more independent sources | Build presence on review sites and comparison platforms |
prompt-coverage-gap | Appears in more prompt clusters than you | Create content targeting the clusters where you have no presence |
recommendation-gap | Recommended more often | Strengthen unique value proposition signals |
engine-specific-gap | Invisible on one engine while visible on another | Engine-specific — e.g. strengthen citable, well-structured content for retrieval-based engines |
position-gap | Listed first when both are mentioned | Improve brand authority and E-E-A-T signals |
comparison-content-gap | Benefits from comparison content you don't have | Create a comparison page: "Brand vs Competitor" |
review-social-proof-gap | Cited from review/forum sources you aren't | Build review profiles on G2, Capterra; engage on Reddit |
Each detector only fires when the underlying data actually shows the competitor ahead — position-gap, for example, requires at least 3 co-mentions in the same responses before it'll report a listing-order pattern at all, and only fires if the competitor is listed first more than half the time. A competitor beating you on paper doesn't automatically produce seven reasons; most real reports return two or three.
Impact classification
Every GapReason carries a GapImpact of "high" | "medium" | "low", from two exported classifier functions:
import { classifyImpactByRatio, classifyImpactByPercentGap } from "ai-visibility/competitor";
classifyImpactByRatio(yours: number, theirs: number): GapImpact // e.g. citation-gap, recommendation-gap
classifyImpactByPercentGap(gap: number): GapImpact // e.g. prompt-coverage-gap, comparison-content-gapBoth are exported directly so a consumer building its own reporting on top of MeasurementReport data can reuse the same thresholds instead of re-deriving them.
CLI: compare
npx ai-visibility compare --from report.jsonPrints "Why they're winning": each competitor's visibility gap (theirs - yours) and its ranked reasons grouped by impact (high/medium/low), each with its action item. Like citations, --from <file> loads a previously saved measure --json report instead of re-querying engines — without --from, --competitors defaults to every competitor already present in the loaded report.
Programmatically:
import { CompetitorAnalyzer } from "ai-visibility/competitor";
const report = CompetitorAnalyzer.analyze(measurementReport, "Acme CRM", ["HubSpot", "Pipedrive"]);
report.competitors // CompetitorGap[], sorted by visibility.gap descending
report.overallGap.averageGap
report.overallGap.biggestGap // competitor name, or nullUses the same MeasurementReport as citation analysis— run both against one saved report to see where competitors are cited from and why they're winning, without querying engines twice. Or run everything at once with the full report pipeline.