# Cursor vs Copilot Market Share: 12 Steps, 70 Min [2026]

> Source: <https://shattered.io/cursor-vs-github-copilot-market-share-audit-12-steps-2026/>
> Published: 2026-11-15 02:16:08+00:00

Every few weeks a new chart claims Cursor is overtaking GitHub Copilot, or that Copilot still dwarfs every rival. Both stories can be true on the same day, because the numbers rest on different denominators. A developer survey counts people who say they used a tool. A vendor page counts enterprises, seats or Fortune lists. A billing export counts licences. The [Stack Overflow 2025 Developer Survey press release](https://stackoverflow.co/company/press/archive/stack-overflow-2025-developer-survey/) is a widely cited example of the first kind, and none of the three answers the question an engineering lead actually has: which assistant do my developers use, and how sure am I?

You can settle the cursor vs github copilot market share question for your own team in about 70 minutes. The kit below is roughly 270 lines of standard-library Python, three data sources and one confidence interval per number. It reads a public survey CSV, a Copilot seat export and a three-question pulse survey, then prints a table per layer with its denominator spelled out. If you are still choosing a tool rather than measuring one, run the [team pilot for the best AI coding assistants](https://shattered.io/best-ai-coding-assistants-team-pilot-12-steps-2026/) first and come back once people have habits worth counting.

The ground shifted in the last six weeks, which is why a fresh audit beats a quoted chart. GitHub’s changelog says Copilot became available to licensed GitHub Enterprise users at no extra seat cost on October 28, 2026, and Gemini 4 Hydrogen reached general availability in Copilot on November 13, 2026. Cursor moved new plans to a bundled subscription on October 12, 2026, and its own announcement reportedly places it inside SpaceX since August 14, 2026. Every one of those changes redefines who counts as a user, so the audit records definitions next to every number.

Don't miss new tech stories on Google

Add Shattered once in the Google app and our stories show up in your news suggestions.

[Add now](https://www.google.com/preferences/source?hl=en&q=https%3A%2F%2Fshattered.io%2F)

## What Market Share Means for Cursor and GitHub Copilot

Nobody publishes a clean cursor vs github copilot market share figure. The vendor material and survey pages checked on November 15, 2026 held no reliable, dated dataset that measures the two products against each other. Vendors publish reach claims instead. As fetched on November 15, 2026, Cursor’s enterprise page says more than 50,000 enterprises and 64% of the Fortune 500 use it, and GitHub’s Copilot page says more than 90% of the Fortune 100 use Copilot. Both are company statements with no disclosed method, so neither belongs in a share calculation.

The useful move is to split the phrase into measures that each have a numerator and a denominator. Seven of them show up in almost every argument about the two products, and they rarely agree with each other.

| Measure | What it counts | Typical denominator | Main trap | 
|---|---|---|---|
| Ever used | Respondents who tried the tool once | All survey respondents | Free trials inflate it | 
| Used in the last 30 days | Recent activity | Respondents who answered the question | Window length differs between waves | 
| Primary assistant | The one tool a developer opens first | Respondents forced to pick one | Hides people who use both | 
| Paid seats | Licences bought | Headcount or eligible users | Shelfware counts as share | 
| Enabled or assigned seats | Licences handed out | Organization members | Bundles such as Copilot inside GitHub Enterprise inflate it | 
| Weekly active users | Distinct users with a logged event | Assigned seats | Telemetry and version gaps undercount | 
| AI-assisted code share | Accepted suggestions or generated lines | Lines committed | Each vendor defines it differently | 

Survey design adds a second layer of confusion. In the Stack Overflow results pages, the two tools can sit in different question groups, one for AI assistants and one for AI-enabled editors, so a side-by-side percentage compares answers to different questions. Check the question text before you quote a pair of numbers. For a price-and-limits view of the same rivalry, the [Claude Code vs Cursor vs Copilot pricing breakdown](https://shattered.io/claude-code-vs-cursor-vs-github-copilot-pricing-limits-2026/) covers what each plan includes.

Think of the evidence as three families. Survey data is cheap and broad but self-reported, and people overstate tools they have heard about. Billing and seat data is precise about money and silent about behavior. Telemetry is precise about behavior but lives inside each vendor’s console, and no vendor hands its raw events to a rival. An honest audit stacks all three, states what each can see, and refuses to let one family stand in for another.

The audit measures four of the seven directly: used in the last 30 days, primary assistant, assigned seats and active seats. It adds an install count from device management as a cross-check. The other three (paid seats, weekly active users and AI-assisted code share) need vendor consoles, so the kit leaves slots for them in the vendor ledger in step 12.

## Prerequisites and Versions

Plan for 70 minutes and a laptop with a terminal. Nothing here needs a database, a notebook server or a paid analytics product. The kit uses only the Python standard library, so there is no virtual environment to break.

- **Python 3.12 or newer.** The code uses built-in generic types such as`tuple[float, float]` and nothing outside the standard library.
- **jq** to merge paginated JSON from the GitHub API.
- **GitHub CLI, latest version,** authenticated with`gh auth login` as a user who can read Copilot seat data for your organization.
- **A public survey CSV.** Stack Overflow publishes downloadable results for its annual Developer Survey, and the 2025 edition is the one the public charts usually cite.
- **A way to send a three-question form** to your engineers: any form tool that exports CSV is enough.
- **A device inventory export** from your MDM (Jamf and Intune both export installed-app lists) with one row per device and app.

Two access notes save time. Copilot seat data is billing-adjacent, so a normal developer token returns a 403. Ask an organization owner for read access before you start. Also check with your security team on the inventory export, because it lists every installed app per device. If your company restricts which assistants engineers may run, the [open-source AI coding assistant audit](https://shattered.io/open-source-ai-coding-assistant-audit-egress-12-steps-2026/) shows how to inventory telemetry and network egress for tools the vendor consoles never see.

Gather the inputs before you write any code. You need the survey CSV in `data/survey.csv`, the seat export in `data/copilot_seats.json`, the pulse results in `data/pulse.csv` and the inventory in `data/inventory.csv`. The first run of the kit works on generated demo data (step 11), so you can finish the code before the real exports arrive.

## Steps 1 to 3: Project Skeleton, Survey Data and Config

### Step 1: Create the project skeleton

Make one folder with a package for the logic, a data folder for raw inputs and a tests folder. Keeping raw exports out of the package matters later, because you will add them to `.gitignore`.

```
mkdir -p share-audit/audit share-audit/data share-audit/tests
cd share-audit
touch audit/__init__.py tests/__init__.py
printf 'data/\nreport.md\n__pycache__/\n' > .gitignore
python3 --version
jq --version
```

Expected output on the test machine: `Python 3.12.3` and `jq-1.7`. Any 3.12 build is fine.

### Step 2: Download the survey and find the tool column

Download the survey results zip from the Stack Overflow Developer Survey site and copy the main results CSV to `data/survey.csv`. Column names change between survey years, so never trust a name from an old tutorial. Print the header and search it for AI-related columns instead.

```
head -1 data/survey.csv | tr ',' '\n' | grep -n -i 'ai'
head -1 data/survey.csv | tr ',' '\n' | grep -n -i 'branch\|employment'
```

Pick the column whose values list tools such as Copilot and Cursor, and the column that tells professional developers from learners. Multi-select answers in these files have historically been joined with a semicolon, but open a few rows and confirm that in your download. In the demo data the tool column is `AITools` and the filter column is `MainBranch`, which are placeholders, not the real names.

### Step 3: Write config.json

Put every choice that affects a number into one config file: the as-of date, headcount, the survey column, the filter and the product labels. A reviewer should be able to read this file and know exactly what was counted.

```
{
  "as_of": "2026-11-15",
  "headcount": 140,
  "survey": {
    "file": "data/survey.csv",
    "tool_column": "AITools",
    "separator": ";",
    "filters": {"MainBranch": "I am a developer by profession"},
    "products": {
      "GitHub Copilot": ["GitHub Copilot"],
      "Cursor": ["Cursor"]
    }
  },
  "team": {
    "copilot_seats": "data/copilot_seats.json",
    "pulse": "data/pulse.csv",
    "inventory": "data/inventory.csv"
  }
}
```

The `products` map lets one product match several labels. If the survey spells it both `GitHub Copilot` and `Copilot`, list both under one key. The `as_of` date makes seat activity reproducible, so a rerun next month does not silently change last month’s answer.

## Steps 4 and 5: Count Products and Add Confidence Intervals

### Step 4: Count mentions with an explicit denominator

The denominator is the respondents who answered the tool question, not everyone in the file. Blank cells and `NA` mean the person skipped it, and counting them as non-users drags both shares down by the same factor and hides the real gap. The loader applies your filters, and the counter reports `k` (mentions) and `n` (answered).

``` python
import csv
from dataclasses import dataclass

from .stats import wilson

@dataclass
class Measure:
    product: str
    k: int
    n: int

    @property
    def p(self) -> float:
        return self.k / self.n if self.n else 0.0

    @property
    def ci(self) -> tuple[float, float]:
        return wilson(self.k, self.n)

def load_rows(path: str, filters: dict[str, str]) -> list[dict[str, str]]:
    with open(path, newline="", encoding="utf-8") as fh:
        rows = list(csv.DictReader(fh))
    for col, wanted in filters.items():
        if rows and col not in rows[0]:
            raise KeyError(f"filter column {col!r} not in header")
        rows = [r for r in rows if r.get(col, "") == wanted]
    return rows

def count_products(rows, column, sep, products) -> list[Measure]:
    if rows and column not in rows[0]:
        raise KeyError(f"tool column {column!r} not in header: {list(rows[0])[:8]}...")
    answered = [r for r in rows if r[column].strip() and r[column].strip().upper() != "NA"]
    n = len(answered)
    out = []
    for name, labels in products.items():
        wanted = {x.casefold() for x in labels}
        k = sum(
            1
            for r in answered
            if wanted & {t.strip().casefold() for t in r[column].split(sep)}
        )
        out.append(Measure(name, k, n))
    return out
```

Two details deserve attention. First, matching is case-insensitive and exact per label after splitting, so a respondent who picked both tools counts once for each. Shares therefore will not sum to 100%. Second, a missing column raises `KeyError` with the first few header names, which turns a silent zero into a loud message.

### Step 5: Add Wilson intervals

A point estimate without an interval invites false precision. The Wilson score interval behaves well at small samples and near 0% or 100%, which is exactly where a pulse survey of 100 engineers lives. The normal approximation can dip below zero there, and Wilson cannot.

``` php
from math import sqrt

def wilson(k: int, n: int, z: float = 1.96) -> tuple[float, float]:
    if n == 0:
        return (0.0, 0.0)
    p = k / n
    denom = 1 + z * z / n
    centre = (p + z * z / (2 * n)) / denom
    half = z * sqrt(p * (1 - p) / n + z * z / (4 * n * n)) / denom
    return (max(0.0, centre - half), min(1.0, centre + half))

def pct(x: float) -> str:
    return f"{x * 100:.1f}%"
```

At the 95% level, 50 of 100 gives roughly 40.4% to 59.6%. That width is the honest message of a 100-person survey: a ten-point gap between two tools can still be noise when the intervals touch. The report in step 10 turns that rule into a verdict, so nobody has to eyeball it.

Work through the demo survey to see the effect. Copilot appears in far more answers than Cursor, and the two intervals do not overlap, so the ordering is not in doubt, although the size of the gap still depends on who answered. Rerun with a different filter, such as learners only, and expect both shares to move. Write down which filter produced each number.

## Steps 6 and 7: Export Copilot Seats and Measure Activity

### Step 6: Export seat data with gh and jq

GitHub exposes seat assignments through an organization billing endpoint. The response lists each seat with the assignee and a `last_activity_at` timestamp. GitHub changed the Copilot billing model on June 1, 2026, so check the current REST documentation for field names before you rely on this export in a report. The call below pages through every seat and merges the pages into one file.

```
ORG=your-org
gh api -H "Accept: application/vnd.github+json" \
  "/orgs/$ORG/copilot/billing/seats?per_page=100" --paginate \
  | jq -s '{total_seats: ([.[].total_seats] | max), seats: [.[].seats[]]}' \
  > data/copilot_seats.json

jq '.seats | length' data/copilot_seats.json
```

The `--paginate` flag prints one JSON document per page, back to back. A plain `json.load` chokes on that, so the `jq -s` slurp step is not optional. With the demo file, the last command prints `96`.

Keep one caveat in view. Since October 28, 2026, enterprise accounts can receive Copilot through their GitHub Enterprise licence, and users covered by an enterprise account without that licence can buy Copilot Standalone at $10 per user per month. A seat count taken after that change counts people who may never have opened an editor. That is why the audit separates assigned seats from active ones. For how Copilot’s seat model compares with an agent-first rival, see the [GitHub Copilot vs Claude Code seat comparison](https://shattered.io/github-copilot-vs-claude-code-2026/).

### Step 7: Count assigned and active seats

Active means any recorded activity inside the last 30 days before the as-of date. The function below returns both numbers, and the pulse and inventory readers sit next to it in the same module. Write the whole file now, since step 8 and step 9 use its other functions.

``` python
import csv
import json
from datetime import date, datetime, timedelta

from .survey import Measure

def copilot_seats(path: str, as_of: date, window_days: int = 30) -> dict[str, int]:
    with open(path, encoding="utf-8") as fh:
        data = json.load(fh)
    seats = data["seats"]
    cutoff = as_of - timedelta(days=window_days)
    assigned = len(seats)
    active = 0
    for s in seats:
        ts = s.get("last_activity_at")
        if ts and datetime.fromisoformat(ts.replace("Z", "+00:00")).date() >= cutoff:
            active += 1
    return {"assigned": assigned, "active": active}

def pulse(path: str) -> dict[str, int]:
    with open(path, newline="", encoding="utf-8") as fh:
        rows = list(csv.DictReader(fh))
    counts = {"respondents": len(rows), "primary_copilot": 0, "primary_cursor": 0,
              "primary_other": 0, "uses_copilot": 0, "uses_cursor": 0, "uses_both": 0}
    for r in rows:
        primary = r["primary"].strip().lower()
        used = {t.strip().lower() for t in r["used_30d"].split(";") if t.strip()}
        counts["primary_copilot" if primary == "copilot" else
               "primary_cursor" if primary == "cursor" else "primary_other"] += 1
        counts["uses_copilot"] += "copilot" in used
        counts["uses_cursor"] += "cursor" in used
        counts["uses_both"] += {"copilot", "cursor"} <= used
    return counts

def inventory(path: str) -> dict[str, int]:
    with open(path, newline="", encoding="utf-8") as fh:
        rows = list(csv.DictReader(fh))
    devices = {r["device_id"] for r in rows}
    has_cursor = {r["device_id"] for r in rows if r["app"].lower() == "cursor"}
    has_vscode = {r["device_id"] for r in rows if r["app"].lower() == "vscode"}
    return {"devices": len(devices), "cursor_installed": len(has_cursor),
            "vscode_installed": len(has_vscode)}

def to_measures(seats, pulse_counts, inv, headcount: int) -> list[Measure]:
    n = pulse_counts["respondents"]
    return [
        Measure("copilot seats assigned / headcount", seats["assigned"], headcount),
        Measure("copilot seats active 30d / assigned", seats["active"], seats["assigned"]),
        Measure("cursor installed / managed devices", inv["cursor_installed"], inv["devices"]),
        Measure("copilot used 30d / pulse respondents", pulse_counts["uses_copilot"], n),
        Measure("cursor used 30d / pulse respondents", pulse_counts["uses_cursor"], n),
        Measure("copilot primary / pulse respondents", pulse_counts["primary_copilot"], n),
        Measure("cursor primary / pulse respondents", pulse_counts["primary_cursor"], n),
    ]
```

A seat with a null `last_activity_at` counts as inactive, which is correct for a seat that was assigned and never used. The ratio of active to assigned seats is the closest thing to a utilization rate you will get without a vendor dashboard.

## Steps 8 and 9: Cursor Inventory and the Pulse Survey

### Step 8: Build the Cursor install inventory

The vendor material checked on November 15, 2026 documents no Cursor seat export that mirrors the Copilot endpoint, so the first Cursor signal is indirect: how many managed devices have it installed. Ask your device-management admin for a CSV with two columns, `device_id` and `app`, filtered to the apps `cursor` and `vscode`. Lowercase the app names in the export so the reader matches them.

```
device_id,app
mac-000,vscode
mac-001,vscode
mac-001,cursor
```

Install count is a ceiling, not a usage number. People install Cursor to try it and never come back, and some developers use it on personal laptops outside your inventory. Treat it as the cheapest cross-check on the pulse survey: if Cursor is installed on a large share of devices but only a small share of respondents say they used it last month, one of the two layers is wrong.

### Step 9: Run a three-question pulse survey

Behavior beats licences, so ask people directly. Send three questions and keep the form anonymous, because named responses skew toward the tool management prefers. The audit needs two columns in the export.

1. **Primary assistant.** Single choice: Copilot, Cursor, other, none. Use the product names exactly as the vendors do, and do not offer an umbrella label such as Copilot without saying whether you mean the GitHub product.
2. **Used in the last 30 days.** Multi-select with the same options. Export it joined with a semicolon.
3. **Who pays.** Company, self or free tier. The kit does not read this column yet, but it is cheap to collect and explains a lot when the results look odd.

```
respondent,primary,used_30d
0,cursor,cursor
1,copilot,copilot
2,copilot,copilot;cursor
```

Aim for a response rate above two thirds of the engineers you care about. Below that, the people who answered are probably the people who care most about tools, and your Cursor share will read high. Keep the survey short, send it twice, and publish the response rate next to the result. Responses that tie to a person raise real data-handling questions, and the [guide to AI assistant security and your data](https://shattered.io/ai-assistant-security-copilots-and-your-data/) is a good checklist before you collect names.

## Step 10: Render the Report and Read the Output

The report module turns measures into markdown tables and compares two measures with a simple rule: if the 95% intervals overlap, the verdict is a tie. That rule is conservative on purpose. It will sometimes call a real difference a tie, but it will not crown a leader on noise.

``` php
from .stats import pct

def table(measures) -> str:
    lines = ["| Measure | k | n | Share | 95% interval |", "|---|---:|---:|---:|---|"]
    for m in measures:
        lo, hi = m.ci
        lines.append(f"| {m.product} | {m.k} | {m.n} | {pct(m.p)} | {pct(lo)} to {pct(hi)} |")
    return "\n".join(lines)

def overlap(a, b) -> bool:
    return not (a.ci[1] < b.ci[0] or b.ci[1] < a.ci[0])

def verdict(a, b, label: str, name_a: str = "Copilot", name_b: str = "Cursor") -> str:
    gap = (a.p - b.p) * 100
    if overlap(a, b):
        return f"{label}: {name_a} minus {name_b} is {gap:+.1f} points, intervals overlap, call it a tie."
    lead = name_a if a.p > b.p else name_b
    return f"{label}: {name_a} minus {name_b} is {gap:+.1f} points, intervals separate, {lead} leads."

def render(survey_ms, team_ms, notes) -> str:
    parts = ["# Cursor vs GitHub Copilot share audit", "", "## Survey layer", "", table(survey_ms), ""]
    if len(survey_ms) >= 2:
        parts += [verdict(survey_ms[0], survey_ms[1], "Survey"), ""]
    parts += ["## Team layer", "", table(team_ms), ""]
    by = {m.product: m for m in team_ms}
    parts += [verdict(by["copilot used 30d / pulse respondents"], by["cursor used 30d / pulse respondents"], "Used in last 30 days"), ""]
    parts += [verdict(by["copilot primary / pulse respondents"], by["cursor primary / pulse respondents"], "Primary assistant"), ""]
    parts += ["## Comparability notes", ""] + [f"- {n}" for n in notes]
    return "\n".join(parts) + "\n"
```

The runner loads the config, calls each reader and writes `report.md`. It also prints the denominators as plain notes, so the report cannot travel without them.

``` python
import json
import sys
from datetime import date

from audit import report, survey, team

def main(path: str = "config.json") -> None:
    cfg = json.load(open(path, encoding="utf-8"))
    as_of = date.fromisoformat(cfg["as_of"])
    s = cfg["survey"]
    rows = survey.load_rows(s["file"], s["filters"])
    survey_ms = survey.count_products(rows, s["tool_column"], s["separator"], s["products"])

    t = cfg["team"]
    seats = team.copilot_seats(t["copilot_seats"], as_of)
    pulse = team.pulse(t["pulse"])
    inv = team.inventory(t["inventory"])
    team_ms = team.to_measures(seats, pulse, inv, cfg["headcount"])

    notes = [
        f"Survey denominator: {survey_ms[0].n} filtered respondents who answered the tool question.",
        f"Team denominator: {pulse['respondents']} pulse respondents out of {cfg['headcount']} engineers.",
        "Seat counts measure licences, the pulse measures behaviour, the inventory measures installs.",
        "Do not subtract or divide numbers across layers without matching the denominator.",
    ]
    out = report.render(survey_ms, team_ms, notes)
    open("report.md", "w", encoding="utf-8").write(out)
    print(out)

if __name__ == "__main__":
    main(*sys.argv[1:])
```

Run it from the project root with `python3 run_audit.py`. On the generated demo data (not real market data), the output looks like this.

```
# Cursor vs GitHub Copilot share audit

## Survey layer

| Measure | k | n | Share | 95% interval |
|---|---:|---:|---:|---|
| GitHub Copilot | 461 | 729 | 63.2% | 59.7% to 66.7% |
| Cursor | 155 | 729 | 21.3% | 18.4% to 24.4% |

Survey: Copilot minus Cursor is +42.0 points, intervals separate, Copilot leads.

## Team layer

| Measure | k | n | Share | 95% interval |
|---|---:|---:|---:|---|
| copilot seats assigned / headcount | 96 | 140 | 68.6% | 60.5% to 75.7% |
| copilot seats active 30d / assigned | 71 | 96 | 74.0% | 64.4% to 81.7% |
| cursor installed / managed devices | 40 | 118 | 33.9% | 26.0% to 42.8% |
| copilot used 30d / pulse respondents | 72 | 104 | 69.2% | 59.8% to 77.3% |
| cursor used 30d / pulse respondents | 45 | 104 | 43.3% | 34.2% to 52.9% |
| copilot primary / pulse respondents | 61 | 104 | 58.7% | 49.0% to 67.6% |
| cursor primary / pulse respondents | 24 | 104 | 23.1% | 16.0% to 32.0% |

Used in last 30 days: Copilot minus Cursor is +26.0 points, intervals separate, Copilot leads.

Primary assistant: Copilot minus Cursor is +35.6 points, intervals separate, Copilot leads.

## Comparability notes

- Survey denominator: 729 filtered respondents who answered the tool question.
- Team denominator: 104 pulse respondents out of 140 engineers.
- Seat counts measure licences, the pulse measures behaviour, the inventory measures installs.
- Do not subtract or divide numbers across layers without matching the denominator.
```

Read the output from the top. The survey layer has many usable answers, so each survey interval is fairly narrow. The team layer is noisier: 104 answers give intervals of roughly nine points on each side for the pulse rows. The table below translates the demo numbers into the claims you can and cannot make.

| Layer | Demo value | Claim it supports | Claim it does not support | 
|---|---|---|---|
| Survey, Copilot vs Cursor | Copilot named far more often than Cursor, intervals separate | Among surveyed professional developers who answered, Copilot was named far more often | That your team looks the same | 
| Copilot seats assigned / headcount | 96 of 140 | Licences cover about two thirds of engineers | That two thirds of engineers use Copilot | 
| Copilot seats active in 30 days | 71 of 96 (74.0%) | A quarter of assigned seats sat idle | Anything about Cursor | 
| Cursor installed / managed devices | 40 of 118 | Cursor is installed on about a third of devices | That a third of developers use it | 
| Used in 30 days, pulse | Copilot ahead of Cursor, intervals separate | Copilot reach is larger, and many people use both | That Cursor is a niche tool | 
| Primary assistant, pulse | Copilot well ahead of Cursor | Copilot is the default for most respondents | Anything about people who skipped the pulse | 

Notice that the gap between the two 30-day shares is smaller than the gap between the primary shares. Many respondents use both tools, so the first number measures reach and the second measures habit. A single headline figure would have buried that difference.

Quote the interval and the sample size every time. A gap of 26.0 points means the pulse found that difference in this sample, not that the true gap equals 26. A slide showing 69% against 43% with 104 respondents printed underneath survives questions. The same slide without the sample size invites the first skeptic in the room to ask how many people you asked, and the discussion shifts from tools to method.

## Steps 11 and 12: Tests, Demo Data and the Vendor Ledger

### Step 11: Add a test and generate demo data

One statistical function carries every interval in the report, so pin its behavior with a test. The known value for 50 of 100 comes from the Wilson formula, and the zero cases guard against a divide-by-zero crash on an empty column.

``` python
import unittest

from audit.stats import wilson

class WilsonTest(unittest.TestCase):
    def test_zero_n(self):
        self.assertEqual(wilson(0, 0), (0.0, 0.0))

    def test_known_interval(self):
        lo, hi = wilson(50, 100)
        self.assertAlmostEqual(lo, 0.4038, places=3)
        self.assertAlmostEqual(hi, 0.5962, places=3)

    def test_bounds(self):
        lo, hi = wilson(0, 30)
        self.assertEqual(lo, 0.0)
        self.assertGreater(hi, 0.0)

if __name__ == "__main__":
    unittest.main()
python3 -m unittest discover -s tests -t . -v

test_bounds (tests.test_stats.WilsonTest.test_bounds) ... ok
test_known_interval (tests.test_stats.WilsonTest.test_known_interval) ... ok
test_zero_n (tests.test_stats.WilsonTest.test_zero_n) ... ok

Ran 3 tests in 0.000s

OK
```

The demo generator writes a 1,200-row survey, 96 Copilot seats, 118 device rows with random Cursor installs and a 104-person pulse. It seeds the random generator, so every run produces the same files and the output above is reproducible. Run it with `python3 make_demo.py` before the first audit.

``` python
import csv
import json
import random
from datetime import date, timedelta

random.seed(2026)
AS_OF = date(2026, 11, 15)

# survey: 1,200 respondents, 900 answered the tool question
tools_pool = [("GitHub Copilot", 0.62), ("Cursor", 0.19), ("ChatGPT", 0.75), ("Claude Code", 0.12)]
with open("data/survey.csv", "w", newline="", encoding="utf-8") as fh:
    w = csv.writer(fh)
    w.writerow(["ResponseId", "MainBranch", "AITools"])
    for i in range(1200):
        branch = "I am a developer by profession" if random.random() < 0.8 else "I am learning to code"
        if random.random() < 0.75:
            picked = [t for t, p in tools_pool if random.random() < p]
            cell = ";".join(picked) if picked else "None"
        else:
            cell = "NA"
        w.writerow([i, branch, cell])

# team: 140 engineers, 96 Copilot seats, 118 managed devices
seats = []
for i in range(96):
    last = AS_OF - timedelta(days=random.choice([1, 2, 3, 5, 9, 14, 21, 28, 35, 50, 70]))
    seats.append({"assignee": {"login": f"dev{i:03d}"}, "last_activity_at": f"{last.isoformat()}T09:30:00Z"})
json.dump({"total_seats": len(seats), "seats": seats}, open("data/copilot_seats.json", "w"))

with open("data/inventory.csv", "w", newline="") as fh:
    w = csv.writer(fh)
    w.writerow(["device_id", "app"])
    for d in range(118):
        w.writerow([f"mac-{d:03d}", "vscode"])
        if random.random() < 0.31:
            w.writerow([f"mac-{d:03d}", "cursor"])

with open("data/pulse.csv", "w", newline="") as fh:
    w = csv.writer(fh)
    w.writerow(["respondent", "primary", "used_30d"])
    for i in range(104):
        r = random.random()
        primary = "copilot" if r < 0.52 else "cursor" if r < 0.80 else "other"
        used = {primary} if primary != "other" else set()
        if random.random() < 0.28:
            used |= {"copilot", "cursor"}
        w.writerow([i, primary, ";".join(sorted(used))])
print("demo data written")
```

### Step 12: Keep a vendor claim ledger

Vendor numbers are useful context as long as they never enter the share calculation. Add a small markdown or CSV ledger with the claim, the exact source page, the fetch date and the measure it represents. The rows below are the ones current as fetched on November 15, 2026.

| Claim | Source | What it measures | Safe for share math? | 
|---|---|---|---|
| More than 50,000 enterprises and 64% of the Fortune 500 use Cursor | Cursor enterprise page | Company-level reach, vendor-reported | No, method undisclosed | 
| More than 90% of the Fortune 100 use Copilot | GitHub Copilot page | Company-level reach, vendor-reported | No, method undisclosed | 
| Copilot Free includes 2,000 completions per month | GitHub plans page | Entitlement, not usage | No | 
| GitHub Enterprise now includes Copilot for licensed users | GitHub changelog, October 28, 2026 | Packaging change | No, it inflates enabled seats | 
| Usage metrics gained code-review detail, chat and VS Code agent usage in the Copilot impact dashboard | GitHub enterprise roundup, November 5 to 6, 2026 | Metric definition change | Only with a break marker in the series | 

The ledger also records plan structure, because plan changes move denominators. A tracker at [BenchLM’s dated list of AI coding plan changes](https://benchlm.ai/coding-plans/changes) logs these shifts, and its Cursor pricing page reports that since October 12, 2026 Cursor sells Free, Plus, Premium, Super and Ultra tiers. Treat that as a secondary source and confirm on the vendor page. The Copilot ladder comes from GitHub’s own pages.

| Product | Plan | Price | Counting note | 
|---|---|---|---|
| GitHub Copilot | Free | $0 | 2,000 completions per month cap | 
| GitHub Copilot | Pro | $10 per month | Individual seat | 
| GitHub Copilot | Pro+ | $39 per month | Individual seat | 
| GitHub Copilot | Max | $100 per month | Individual seat | 
| GitHub Copilot | Business | $19 per granted seat per month | Organization seat | 
| GitHub Copilot | Enterprise | $39 per granted seat per month | May come bundled through GitHub Enterprise | 
| Cursor (BenchLM tracker) | Plus | $8 per month | Plan family introduced October 12, 2026 | 
| Cursor (BenchLM tracker) | Premium | $30 per month | Third-party models billed separately at API list prices | 
| Cursor (BenchLM tracker) | Super | $100 per month | Same plan family | 
| Cursor (BenchLM tracker) | Ultra | $200 per month | Top tier listed | 

Read the ladder as a map of where denominators leak. Copilot Free, Pro, Pro+ and Max are individual plans, Business and Enterprise are organization seats, and the enterprise bundle adds a path where a licence arrives without a purchase decision. Cursor’s tiers climb from $8 to $200 per month, so the same share of users can stand for very different revenue. Share of users and share of spend are separate questions, and this audit answers only the first.

Finally, schedule the run. A monthly snapshot appended to a history CSV beats any single audit, because trends survive definition noise better than levels do.

```
# crontab -e  (first day of each month, 07:00)
0 7 1 * * cd /srv/share-audit && python3 run_audit.py > /dev/null \
  && cp report.md "history/report-$(date +\%Y-\%m).md"
```

## Common Pitfalls That Skew Cursor vs Copilot Share

Most wrong share numbers come from process mistakes rather than math mistakes. These seven recur in team audits and in public charts alike.

1. **Mixing denominators.** A survey of all respondents, one of professionals and one of enterprise buyers answer three different questions. Fix the filter in config.json and print`n` next to every share.
2. **Treating seats as users.** Bundling Copilot into GitHub Enterprise since October 28, 2026 means a seat can exist without a single completion. Always report active seats beside assigned ones.
3. **Reading installs as usage.** An install is a ceiling. When installs and pulse answers disagree by more than the intervals allow, suspect the sampling, not the arithmetic.
4. **Forgetting that people use both.** In a multi-select question, shares overlap and exceed 100% in total. Report the primary assistant too, and quote the share who use both.
5. **Label drift.** Microsoft renamed the GitHub Copilot harness in Copilot Studio to the Microsoft Copilot harness on October 28, 2026. The[Microsoft Copilot Studio post on the harness rename](https://www.microsoft.com/en-us/copilot/blog/copilot-studio/the-github-copilot-harness-in-copilot-studio-is-now-the-microsoft-copilot-harness/) shows how fast names move. If your pulse offers an umbrella label, respondents may mean a different product than you do.
6. **Comparing across plan changes.** A Cursor user who moved to the new plan family after October 12, 2026 may have changed behavior because of billing, not preference. Mark transitions as events in your history file.
7. **Ignoring non-response.** A 40% response rate with enthusiasts overrepresented produces a clean-looking interval around a biased number. The interval measures sampling noise only.

Two further traps are subtler. Surveys also miss tools that engineers run locally or through a terminal, such as [Goose with a local Ollama model](https://shattered.io/goose-ai-coding-assistant-local-ollama-12-steps-2026/) or the [Aider terminal assistant](https://shattered.io/aider-local-ai-coding-assistant-ollama-git-2026/), so keep an other bucket in the pulse and look at it. And a share that looks stable can hide a swap: half the team may have moved from Copilot to Cursor while another half moved back, which is why the monthly history matters more than any one snapshot.

Timing matters too. A pulse sent the week after a model launch or a pricing change measures excitement rather than habit. Send it in a quiet week, or on a fixed day each month, so the noise averages out across runs.

## Troubleshooting: 10 Failures and Their Fixes

- **KeyError: tool column not in header.** The survey year changed the column names. Rerun the header search from step 2 and update`tool_column` . The error message prints the first eight header names to help.
- **Every product shows k=0.** The label strings do not match. Print the distinct values from the column, for example with`python3 -c` and`collections.Counter` over the split cells, then copy the exact text into the products map. Case is ignored, but spelling and spaces are not.
- **Survey filter leaves zero rows.** The filter value must equal the cell text exactly, including punctuation. Copy it from the data, not from memory.
- **gh api returns a permission or not-found error for seats.** Your token lacks the billing or Copilot management permission, or the organization is not on a plan with seat management. Ask an owner to confirm access, then run`gh auth status` .
- **json.JSONDecodeError on the seat file.** You saved the raw paginated output. Rerun step 6 with the`jq -s` merge so the file holds one document.
- **Active seats read zero.** Either`as_of` in the config is stale or`last_activity_at` is null for the whole org. Check the date first, then open one seat record with`jq '.seats[0]'` .
- **UnicodeDecodeError or a stray BOM in the first header.** Spreadsheet exports often add a byte-order mark. Open the file with`encoding="utf-8-sig"` in the reader that fails.
- **Pulse rows all land in primary_other.** Respondents typed or selected values such as GitHub Copilot rather than copilot. Normalize the export to the lowercase keys the reader expects.
- **Inventory shows zero Cursor installs on Windows.** The MDM reports an executable name such as Cursor.exe. Map app names to cursor and vscode before writing the CSV.
- **The verdict says tie but the gap looks big.** The sample is small. Intervals that touch mean the data cannot separate the tools. Raise the response rate or pool two months of pulse data before you argue the point.

If the report runs but a number looks wrong, trace it by hand once. Pick one product, count the matching rows with a spreadsheet filter and compare the result with `k`. A single reconciled number gives you more trust in the whole table than another round of reading code. When the pulse shows engineers moving from Copilot to Cursor, the [Cursor rules migration guide from Copilot](https://shattered.io/cursor-rules-migrate-github-copilot-2026/) covers the configuration work that follows.

## Advanced Tips for Sharper Adoption Numbers

- **Weight the survey.** If one region or seniority band is overrepresented, add a weight column and compute weighted proportions. Report the effective sample size, since weights shrink it, and widen the interval accordingly.
- **Slice by team before you average.** A company-wide tie can hide one group that is 80% Cursor and another that is 80% Copilot. Compute the table per team and look for splits before you accept the blended number.
- **Annotate the series.** GitHub added chat and VS Code agent usage to its Copilot impact dashboard on November 5 to 6, 2026, and its usage API gained support for custom models on November 3. A trend line that crosses those dates needs a break marker.
- **Join usage to outcomes.** Share says who uses a tool, not whether it helps. Pair the audit with a task-level test such as the[Cursor vs GitHub Copilot benchmark harness](https://shattered.io/benchmark-cursor-vs-github-copilot-2026/) to compare cost and quality per accepted change.
- **Track model availability as a covariate.** New models draw trial usage. Gemini 4 Carbon reached general availability in Copilot on November 10, 2026, Gemini 4 Hydrogen followed on November 13, and Cursor’s release notes add new models every few weeks. Log those dates in the history file and expect short spikes.
- **Bootstrap the gap.** For the difference between two shares drawn from one respondent pool, resample respondents with replacement a few thousand times. This respects the overlap from people who use both tools, which the independent-intervals rule ignores.
- **Pseudonymize early.** Hash respondent IDs on import and keep the key outside the repository. The audit never needs a name.

The overlap rule in step 10 is a screen, not a hypothesis test. For a decision with money attached, such as dropping a vendor, run the bootstrap on the paired pulse data and write the interval for the gap itself. That takes about 20 lines and turns a hunch into a number with an error bar.

## Reading Your Result Against the November 2026 News Cycle

Your cursor vs github copilot market share audit will land in a noisy news week. On the Copilot side, GitHub reported Copilot Fusion as generally available on October 28, 2026, with Fusion Standard, High and Max modes, and brought Copilot into Xcode on November 4. The Gemini 4 Hydrogen and Gemini 4 Carbon additions arrived on November 13 and November 10. On the Cursor side, the company’s changelog shows an Agent SDK on October 20, 2026, and reports of voice chat with agents on November 6 and multiplayer Projects around November 10.

None of those launches measures adoption. They change what people try, which moves short-term usage, and they change what the vendor will tell you about usage. The ownership story adds noise too. A secondary dossier on [who owns Cursor](https://getleadwave.io/dossier/who-owns-cursor) describes the SpaceX deal, and the company’s own post is the better primary source. Ownership affects pricing and model access, and it can change how Cursor reports numbers, but it says nothing about how many of your developers open the editor on Tuesday.

Use the news as a checklist of questions for your next pulse rather than as evidence. If a new model shipped, ask whether anyone switched tools because of it. If a plan changed, ask who pays now. The [coding and developer tools coverage](https://shattered.io/category/software/) tracks these launches, and the audit gives you the local numbers to weigh them against.

Billing events deserve their own line in the history file. GitHub began billing cloud sandbox usage in November 2026, and on Cursor’s newer plans third-party models bill at API list prices from the Premium tier up. Teams that see metered spend often change their habits, so if an active-seat rate drops after a billing date, check the calendar before you conclude that people prefer the rival.

One more habit pays off. Write down your prediction before each run, for example that Copilot’s active-seat rate stays above 70%, and compare it with the result. Teams that do this learn quickly where their intuition about tool adoption is off, and they stop arguing from anecdotes in planning meetings.

## The Complete Project, Layout and One-Command Run

The finished kit has this layout. Copy the blocks in order and the audit runs without any repository.

```
share-audit/
  config.json
  run_audit.py
  make_demo.py
  audit/
    __init__.py
    stats.py
    survey.py
    team.py
    report.py
  data/
    survey.csv
    copilot_seats.json
    pulse.csv
    inventory.csv
  tests/
    __init__.py
    test_stats.py
```

Run the full sequence on demo data first, then swap in real exports and rerun. If the demo run reproduces the numbers shown in step 10, your environment is correct and any later surprise comes from the data.

```
python3 make_demo.py
python3 -m unittest discover -s tests -t .
python3 run_audit.py
cat report.md
```

When you replace the demo files, change only three things: the paths and column names in `config.json`, the headcount and the as-of date. Do not edit the modules to make a number look right. If a real export needs a new rule, add it to the config or a new function and write a test for it first. Commit the config and the report to a private repository, and keep raw data out with the `.gitignore` from step 1.

Handle the report like any survey result. Publish the tables and denominators, limit the raw CSVs to a small group and delete pulse files after a retention period your legal team accepts. Engineers answer more honestly when they know management cannot trace a response to a name.

## FAQ: Cursor vs GitHub Copilot Market Share

### Is there an official Cursor vs GitHub Copilot market share figure?

No. As of November 15, 2026, the sources checked held no dated, comparable dataset that measures the two products against each other. Cursor and GitHub publish reach claims on their own pages, and survey publishers such as Stack Overflow report what respondents say they use. Each uses a different denominator.

### Which survey should I trust most?

Trust the one whose question wording, sampling frame and date you can read. A large public survey gives you the scale of a population, and your own pulse survey gives you the behavior of the people you manage. Use the first for context and the second for decisions, and never mix their denominators.

### How many responses do I need for the pulse survey?

For a 95% interval about nine points wide on each side near the middle of the range, you need roughly 100 answers, as the demo shows. Doubling the sample trims that width by roughly 30%, not by half. If your team has 40 engineers, survey everyone and report the response rate instead of an interval.

### Does a Copilot seat count as a user?

Only when it shows activity. GitHub began including Copilot with eligible GitHub Enterprise licences on October 28, 2026, so assigned seats now overstate use more easily than before. Active seats in the last 30 days are a better stand-in, and the pulse survey is better still.

### Can I audit Cursor usage from the vendor side?

Look in your Cursor admin console first, since plan features vary and change often. The vendor material checked here documents no seat export comparable to the Copilot endpoint, so the install inventory plus the pulse survey is the reliable fallback and still gives a usable estimate.

### What does it cost to run this audit?

The code costs nothing, and the data comes from tools you already pay for. The main cost is about 70 minutes of setup plus a few minutes per month to rerun. The pulse survey costs each engineer under two minutes.

### How often should I rerun it?

Monthly is a good default, with an extra run after any pricing change, model launch or tool rollout. A monthly history file shows whether a change is a trend or a spike, and it timestamps the plan and model events you logged in step 12.

### Why not just quote the Stack Overflow percentages?

Quote them as context, with the survey year and the question text beside each number. They describe respondents to one annual survey, which skews toward people who answer developer surveys, and they predate the October 2026 plan and packaging changes at both vendors. They tell you what a surveyed population did, not what your engineers do.

### Should I include other assistants in the survey?

Yes. Add Claude Code, Windsurf, local assistants and an other option, because the share of people who use neither Cursor nor Copilot is itself a finding. Adding products to the config takes one line per label, and the report code compares any two measures you pass to the verdict function.
