Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three

GitHub Copilot vs Cursor vs Claude Code vs Codeium: Which AI Coding Tool Wins in 2025
65 views

Most comparisons of AI coding tools rank them on a handful of tasks and a pricing table. Both go stale within months: all three products ship changes constantly, and their plans and model lineups change with them. Earlier versions of this post did exactly that, with April 2025 prices and a five-task “benchmark” that wasn’t reproducible. This version is different. It compares only what each vendor documents (checked on October 10, 2026), shows how to set up one repository so all three behave the same way, and gives three workflows you can run in any of them.

⚡ TL;DR
  • All three now cover the same broad ground: an agent in the editor or terminal, cloud agents that work on a branch and open pull requests, automated PR review, hooks, and MCP tools. The differences are in where you work and how you control them.
  • Pick by workflow: Claude Code if your team lives in the terminal and wants scriptable runs (claude -p); Cursor if you want an AI-first editor; Copilot if you’re standardized on GitHub and want agents in issues and pull requests.
  • Put shared instructions in AGENTS.md. Cursor and Copilot read it, and Claude Code reads it when there’s no CLAUDE.md, or through an @AGENTS.md import.
  • Enforce limits with hooks and CI, not instructions: a hook can deny edits to infra/ whatever the model decides.
  • Check current prices on each vendor’s pricing page; don’t trust a blog table, including old versions of this one.

What each tool offers, from the vendor docs

CapabilityClaude CodeCursorGitHub Copilot
Where you use itTerminal CLI, IDE extensions (VS Code, JetBrains), desktop app, webThe Cursor editor, plus a CLIIDE extensions, GitHub.com, Copilot CLI
Agent that edits and runs commands locallyYes (the core product)Agent in the editorAgent mode in the IDE; Copilot CLI
Cloud agent that works on a branchClaude Code in the cloudCloud Agents (isolated VMs; push a branch for handoff)Copilot cloud agent
Automated PR reviewCode review and CI/CD integrations, including GitHub ActionsBugbotCopilot code review
Repository instructionsCLAUDE.md, .claude/rules/, or AGENTS.md.cursor/rules/*.mdc or AGENTS.md.github/copilot-instructions.md, .github/instructions/*.instructions.md, or AGENTS.md
HooksYes (PreToolUse, PostToolUse and more)YesYes
MCP toolsYesYesYes
Non-interactive / CI runsclaude -p with --allowedToolsCLI headless modeCopilot CLI programmatic mode

Product names change: Copilot’s agent that works from issues is now documented as Copilot cloud agent, and Cursor’s Background Agents became Cloud Agents. Check the links at the end before relying on a specific feature.

Figure 1. One repository setup for three tools. AGENTS.md holds the shared instructions every tool reads; small tool-specific files add only what differs; hooks and CI enforce the limits that instructions can't. Diagram by CheatCoders.
Figure 1. One repository setup for three tools. AGENTS.md holds the shared instructions every tool reads; small tool-specific files add only what differs; hooks and CI enforce the limits that instructions can’t. Diagram by CheatCoders.

Choose by workflow

  • Terminal-first teams and automation: Claude Code. It started as a CLI, runs non-interactively with claude -p, and its hooks and permission rules (allow, ask, deny in settings) give fine control over what it can run.
  • Editor-first teams: Cursor. The agent lives in the editor, rules can be scoped by path, and Cloud Agents and Bugbot extend it to branches and PRs.
  • GitHub-centered teams: Copilot. Agents start from issues and pull requests on GitHub, review comes built in, and administration happens in the organization’s Copilot settings.

Many teams use more than one. That works if the repository, not each tool, holds the rules, which is the setup below.

Step 1: One instructions file for every tool

Each vendor documents AGENTS.md support:

  • Cursor supports AGENTS.md in the project root and subdirectories, as a plain-markdown alternative to project rules.
  • Copilot uses AGENTS.md files anywhere in the repository for agents, with the nearest one in the directory tree taking precedence.
  • Claude Code reads AGENTS.md only when there’s no CLAUDE.md in the working directory or above it. If you need a CLAUDE.md, start it with @AGENTS.md to import the shared file, then add Claude-specific lines.

Keep the file short and concrete. Claude Code’s docs recommend under 200 lines per instruction file and instructions specific enough to verify (“Run npm test before committing”, not “test your changes”). A CI check keeps the setup from drifting:

python
"""CI check: one source of agent instructions (AGENTS.md) that Claude Code, Cursor and Copilot all read."""
import pathlib
import sys

root = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else ".")
errors = []
agents = root / "AGENTS.md"
if not agents.exists():
    errors.append("AGENTS.md missing at the repository root")
claude = root / "CLAUDE.md"
if claude.exists() and not claude.is_symlink() and "@AGENTS.md" not in claude.read_text():
    # Claude Code reads AGENTS.md only when no CLAUDE.md exists; otherwise CLAUDE.md must import it.
    errors.append("CLAUDE.md exists but doesn't import @AGENTS.md, so Claude Code won't read AGENTS.md")
for extra in [root / ".github/copilot-instructions.md", *root.glob(".cursor/rules/*.mdc")]:
    if extra.exists() and agents.exists():
        shared = set(agents.read_text().splitlines()) & set(extra.read_text().splitlines()) - {""}
        if len(shared) > 3:
            errors.append(f"{extra.relative_to(root)} repeats {len(shared)} lines of AGENTS.md; keep tool-specific rules only")
if agents.exists() and len(agents.read_text().splitlines()) > 200:
    errors.append("AGENTS.md is over 200 lines; Claude Code's docs recommend under 200 lines per instruction file")
print("\n".join(errors) or "instructions: OK")
sys.exit(1 if errors else 0)

Step 2: Enforce limits with a hook

An instruction like “don’t edit infrastructure” is a preference. A hook is enforcement. In Claude Code, a PreToolUse hook receives the tool name and input as JSON on stdin; for the Edit, Write and Read tools, tool_input.file_path is always an absolute path. Printing a permissionDecision of deny blocks the call and tells Claude why:

python
#!/usr/bin/env python3
"""Claude Code PreToolUse hook: deny edits to paths that need a human (infra, CI, secrets)."""
import fnmatch
import json
import os
import sys

PROTECTED = ["infra/*", ".github/workflows/*", "*.pem", ".env*", "migrations/*"]

event = json.load(sys.stdin)
path = event.get("tool_input", {}).get("file_path", "").replace("\\", "/")   # absolute for Edit/Write/Read
root = os.environ.get("CLAUDE_PROJECT_DIR", "").replace("\\", "/").rstrip("/") + "/"
rel = path[len(root):] if root != "/" and path.startswith(root) else path
if any(fnmatch.fnmatch(rel, pat) for pat in PROTECTED):
    print(json.dumps({"hookSpecificOutput": {
        "hookEventName": "PreToolUse", "permissionDecision": "deny",
        "permissionDecisionReason": f"{rel} is protected: propose the change in the PR description instead"}}))
sys.exit(0)                                   # no output = no decision; the normal permission flow applies
json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{"type": "command", "command": "python3 \"${CLAUDE_PROJECT_DIR}/.claude/hooks/protect_paths.py\""}]
      }
    ]
  }
}

Save the script as .claude/hooks/protect_paths.py and the configuration in .claude/settings.json. Cursor and Copilot have their own hook systems with different configuration formats, so port the same rule to each; your CI should enforce it too (for example with CODEOWNERS on infra/), because CI applies whichever tool wrote the change. The test feeds the hook sample events and runs the instructions check on a temporary repository:

bash
set -e
T=$(mktemp -d); export CLAUDE_PROJECT_DIR=$T
echo '--- hook'
for p in "$T/src/app.py" "$T/infra/stack.ts" "$T/.env.production" "$T/.github/workflows/deploy.yml"; do
  printf '{"tool_name":"Edit","tool_input":{"file_path":"%s"},"tool_use_id":"t1"}' "$p" | python3 protect_paths.py | sed "s#^#$(basename $p): #" || true
  printf '{"tool_name":"Edit","tool_input":{"file_path":"%s"}}' "$p" | python3 protect_paths.py | grep -q deny || echo "$(basename $p): allowed (no output)"
done
python3 -c "import json;json.load(open('settings.json'))" && echo "settings.json: valid JSON"
echo '--- instructions check'
printf '# Build\nRun make test before every commit.\n' > $T/AGENTS.md
printf '## Claude Code\nUse plan mode under src/billing.\n' > $T/CLAUDE.md
python3 check_instructions.py $T || true
printf '@AGENTS.md\n\n## Claude Code\nUse plan mode under src/billing.\n' > $T/CLAUDE.md
python3 check_instructions.py $T
text
--- hook
app.py: allowed (no output)
stack.ts: {"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "infra/stack.ts is protected: propose the change in the PR description instead"}}
.env.production: {"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": ".env.production is protected: propose the change in the PR description instead"}}
deploy.yml: {"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": ".github/workflows/deploy.yml is protected: propose the change in the PR description instead"}}
settings.json: valid JSON
--- instructions check
CLAUDE.md exists but doesn't import @AGENTS.md, so Claude Code won't read AGENTS.md
instructions: OK

The application file passes through with no decision, the three protected paths are denied with a reason the agent can relay, and the instructions check catches a CLAUDE.md that would hide AGENTS.md from Claude Code.

Step 3: Three workflows that work in any of them

Tests for legacy code

  1. Ask the agent to read the module and list its behaviors as test names first, without writing code. Review the list; it’s your spec.
  2. Have it write characterization tests that pass against the current code, including the odd behavior. Don’t let it “fix” anything yet.
  3. Run the suite and check coverage. Ask for tests for the uncovered branches only.
  4. Only then start refactoring, with the tests as a safety net.

Dependency upgrades

  1. Give the agent the official migration guide or changelog for the version jump, not just the version number; models’ knowledge of recent releases is incomplete.
  2. Ask for a plan: affected files and the breaking change behind each. Approve it before edits.
  3. Upgrade one package per pull request, with the lockfile change in the same PR, and run the full test suite.

Debugging with full context

  1. Paste the exact error, the command that produced it and what changed recently. Ask for hypotheses ranked by likelihood, each with a way to test it.
  2. Let the agent run the cheapest check first (a log query, a failing test), not the most interesting fix.
  3. Once fixed, ask for a regression test that fails without the fix.

In every workflow, the agent proposes and runs checks; a human reviews the diff. That holds whichever tool you use.

Running them in CI

All three can run outside an interactive session. For example, Claude Code’s non-interactive mode is claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash", where the allowed tools list limits what it can do without asking. In CI, run agents with a scoped token, no production credentials, and the same gates you’d apply to a human’s PR; see CI gates for agent PRs.

Troubleshooting

SymptomLikely causeFix
Claude Code ignores AGENTS.mdA CLAUDE.md exists in the directory or aboveAdd @AGENTS.md at the top of CLAUDE.md
Cursor ignores a rule filePlain .md in .cursor/rulesProject rules must use the .mdc extension
Tools behave differently on the same repoRules copied into each tool’s file and driftedShared rules in AGENTS.md only; run the check in CI
Hook never blocks on WindowsBackslash paths compared with forward slashesNormalize separators first, as the script does
Agent follows instructions inconsistentlyLong, vague instruction fileUnder 200 lines, concrete and verifiable rules

FAQ

Which one is best?

The one that fits where your team works and that you can control. Trial each on your own repository for a week with the same instructions and gates, and compare merged PRs and review effort, not demos.

Why no pricing table?

Plans and usage limits change often, and a stale table misleads. Check each vendor’s pricing page when you decide.

Can I use two of them on one repository?

Yes, with shared instructions in AGENTS.md and enforcement in hooks and CI.

Do these tools see my secrets?

They see what’s in the files and commands they can access. Keep secrets out of the repository and exclude sensitive paths; see secret-aware context filters.

Official documentation

Last updated on October 10, 2026

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.