Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Validate rules

on:
push:
branches: [main]
pull_request:

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install pyyaml
- run: python3 scripts/validate_rules.py
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 CoderBuds
Copyright (c) 2025 Coderbuds

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
66 changes: 37 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Portable, maintainable AI code detection rules for identifying AI-generated pull requests and commits. Detect Claude Code, GitHub Copilot, Cursor, and other AI coding assistants.

[![Accuracy](https://img.shields.io/badge/accuracy-98%25-brightgreen)](https://github.com/coderbuds/ai-detector)
[![Validate rules](https://github.com/coderbuds/ai-detector/actions/workflows/validate.yml/badge.svg)](https://github.com/coderbuds/ai-detector/actions/workflows/validate.yml)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE.md)
[![Stars](https://img.shields.io/github/stars/coderbuds/ai-detector?style=social)](https://github.com/coderbuds/ai-detector/stargazers)

Expand All @@ -12,7 +12,7 @@

Over 46% of code on GitHub is now AI-assisted (GitHub Octoverse 2024). Engineering teams need transparency into which pull requests use AI coding tools.

We built these detection rules for [CoderBuds](https://coderbuds.com) and decided to **open-source them** because:
We built these detection rules for [Coderbuds](https://coderbuds.com) and decided to **open-source them** because:

- ✅ **Transparency builds trust** - Developers deserve to know how AI detection works
- ✅ **Community contributions** - Help us keep rules updated as AI tools evolve
Expand Down Expand Up @@ -58,17 +58,18 @@ explicit_markers:

## 🤖 Supported AI Tools

| Tool | Provider | Detection Method | Accuracy |
|------|----------|------------------|----------|
| **Claude Code** | Anthropic | Footer, co-author, bot email | 100% |
| **GitHub Copilot** | Microsoft | Bot commits, co-author | 100% |
| **Cursor** | Anysphere | Footer, link, markers | 96% |
| **Devin** | Cognition AI | Bot author, footer | 100% |
| **WindSurf** | Codeium | Footer, attribution | 100% |
| **OpenAI Codex** | OpenAI | Branch patterns, markers | 100% |
| **Aider** | Open Source | Commit patterns | 90% |
| **v0.dev** | Vercel | Markers, comments | 95% |
| **Replit AI** | Replit | Bot author, markers | 100% |
| Tool | Provider | What it leaves behind |
|------|----------|------------------------|
| **Claude Code** | Anthropic | Description footer, `Co-Authored-By: Claude` trailer, `claude/` branches |
| **GitHub Copilot** | Microsoft | Bot commit authors, co-author trailer, footer |
| **Cursor** | Anysphere | Footer and links, `CURSOR_SUMMARY` comment, `cursor/` branches |
| **Jules** | Google | "PR created automatically by Jules" signature, task link, `jules-<id>` branches |
| **Devin** | Cognition AI | Bot commit authors, footer, links |
| **OpenAI Codex** | OpenAI | `codex/` branches, `[codex]` titles, Codex task links, `codex` label |
| **WindSurf** | Codeium | Footer link, HTML comment, `windsurf/` branches |
| **Aider** | Open Source | `aider: ` commit prefix, aider.chat links, `aider/` branches |
| **v0.dev** | Vercel | Footer and HTML comments |
| **Replit AI** | Replit | Bot commit author, footer link, HTML comment, `replit-ai` label |

**Missing a tool?** [Submit a PR](https://github.com/coderbuds/ai-detector/pulls) or [open an issue](https://github.com/coderbuds/ai-detector/issues).

Expand Down Expand Up @@ -114,7 +115,7 @@ function detectAI(string $prDescription, array $commits): ?array
// Check bot authors in commits
foreach ($rule['explicit_markers']['bot_authors'] ?? [] as $bot) {
foreach ($commits as $commit) {
if (str_contains($commit['author']['email'], $bot['pattern'])) {
if (isset($bot['email']) && $commit['author']['email'] === $bot['email']) {
return [
'tool' => $rule['tool']['name'],
'confidence' => $bot['confidence'],
Expand Down Expand Up @@ -161,7 +162,7 @@ def detect_ai(pr_description, commits):
# Check bot authors
for bot in rule.get('explicit_markers', {}).get('bot_authors', []):
for commit in commits:
if bot['pattern'] in commit['author']['email']:
if bot.get('email') and commit['author']['email'] == bot['email']:
return {
'tool': rule['tool']['name'],
'confidence': bot['confidence'],
Expand Down Expand Up @@ -206,7 +207,7 @@ function detectAI(prDescription, commits) {
// Check bot authors
for (const bot of rule.explicit_markers?.bot_authors || []) {
for (const commit of commits) {
if (commit.author.email.includes(bot.pattern)) {
if (bot.email && commit.author.email === bot.email) {
return {
tool: rule.tool.name,
confidence: bot.confidence,
Expand Down Expand Up @@ -234,7 +235,8 @@ The YAML rules check for these marker types:
| `bot_authors` | Bot emails and usernames | `github-copilot[bot]`, `noreply@anthropic.com` |
| `html_comments` | Special HTML comments | `<!-- Generated by AI -->` |
| `labels` | PR labels | `codex`, `ai-generated` |
| `branch_patterns` | Branch naming conventions | `codex/feature`, `cursor-refactor` |
| `branch_patterns` | Branch naming conventions | `codex/feature`, `claude/fix-login` |
| `text_patterns` | Title or description text (`location: title` or `description`) | `[codex] Fix publishing` |

---

Expand All @@ -248,16 +250,19 @@ The YAML rules check for these marker types:
- Branch names following AI tool patterns
- PR labels indicating AI usage

**Accuracy: 98-100%** - When explicit markers exist, detection is certain.
A marker at confidence 100 is the tool signing its own work. Lower confidences — branch names, title prefixes — are strong conventions, not signatures: a person can name a branch `claude/…` too.

### ❌ **Doesn't Detect (Without Additional Analysis)**

- Subtle AI usage without markers
- ChatGPT code copied manually
- AI-assisted refactoring without attribution
- Code quality or "AI-like" patterns
- Agents that sign nothing. **Codex CLI run locally leaves no marker at all**, and neither does any agent whose footer a person deletes

**For behavioral analysis** (analyzing code patterns), see [CoderBuds Platform](https://coderbuds.com).
**No match does not mean a person wrote it.** On Coderbuds' own production data, pull requests with no marker were routinely agent-written — including every one Codex CLI produced. Treat "no marker found" as unknown, never as human. The only reliable source for those is the agent saying so itself; Coderbuds asks coding agents to report their authorship over MCP for exactly this reason.

**For behavioral analysis** (analyzing code patterns), see [Coderbuds Platform](https://coderbuds.com).

---

Expand All @@ -270,7 +275,7 @@ This package is **100% free and open source** (MIT License). Use it for:
- ✅ Local development workflows
- ✅ Custom integrations

**[CoderBuds Platform](https://coderbuds.com)** (paid service) adds:
**[Coderbuds Platform](https://coderbuds.com)** (paid service) adds:

- 📊 Team-level analytics over time
- 📈 AI adoption trends and insights
Expand All @@ -279,7 +284,7 @@ This package is **100% free and open source** (MIT License). Use it for:
- 🏢 Enterprise features (SSO, audit logs)
- 📝 Custom reporting and exports

**Analogy:** This package is like Sentry's SDK (free). CoderBuds is like Sentry's hosted platform (paid).
**Analogy:** This package is like Sentry's SDK (free). Coderbuds is like Sentry's hosted platform (paid).

---

Expand Down Expand Up @@ -313,18 +318,21 @@ explicit_markers:
description: "Tool footer in PR description"

bot_authors:
- pattern: 'your-tool[bot]'
location: commit_author
- username: 'your-tool[bot]'
confidence: 100
description: "Your Tool bot author"
```

4. **Test against real PRs** - Verify accuracy
5. **Submit a PR** with test results
`bot_authors` entries match on `username`, `email`, `email_pattern` or `name_pattern`; `labels` entries on `name`. Any other key is silently ignored by the matcher, so the validator rejects it.

4. **Add fixture cases** to `fixtures/cases.yml` — a pull request your rule must catch, and one it must not
5. **Run the validator:** `pip install pyyaml && python3 scripts/validate_rules.py`
6. **Submit a PR** — CI runs the same check

### Contribution Guidelines

- Include at least 3 example PRs showing the pattern
- Match signatures, not mentions: a rule that fires on a tool's *name* also fires on every changelog and dependency bump that mentions it
- Document confidence levels (100 = definitive, 80+ = high, 60+ = medium)
- Add test cases if possible
- Update this README's tool table
Expand All @@ -334,7 +342,7 @@ explicit_markers:
## 📚 Documentation

- **[Blog Post: Why We Open-Sourced This](https://coderbuds.com/blog/open-source-ai-code-detection-yaml-rules)**
- **[CoderBuds Platform](https://coderbuds.com)** - Team analytics
- **[Coderbuds Platform](https://coderbuds.com)** - Team analytics
- **[GitHub Discussions](https://github.com/coderbuds/ai-detector/discussions)** - Ask questions
- **[Issues](https://github.com/coderbuds/ai-detector/issues)** - Report bugs

Expand All @@ -350,7 +358,7 @@ See [LICENSE.md](LICENSE.md) for details.

## 🙏 Credits

Created by **[CoderBuds](https://coderbuds.com)** - AI adoption analytics for engineering teams.
Created by **[Coderbuds](https://coderbuds.com)** - AI adoption analytics for engineering teams.

**Built with transparency in mind.** Developers deserve to know how AI detection works.

Expand All @@ -362,7 +370,7 @@ Created by **[CoderBuds](https://coderbuds.com)** - AI adoption analytics for en

- **[Try the Live Detector](https://coderbuds.com/blog/open-source-ai-code-detection-yaml-rules#try-it)** - Paste any GitHub PR URL
- **[Full Blog Post](https://coderbuds.com/blog/open-source-ai-code-detection-yaml-rules)** - Why we open-sourced this
- **[CoderBuds Platform](https://coderbuds.com)** - Team AI adoption analytics
- **[Coderbuds Platform](https://coderbuds.com)** - Team AI adoption analytics
- **[GitHub](https://github.com/coderbuds/ai-detector)** - Source code
- **[Issues](https://github.com/coderbuds/ai-detector/issues)** - Bug reports
- **[Discussions](https://github.com/coderbuds/ai-detector/discussions)** - Community
Expand All @@ -371,4 +379,4 @@ Created by **[CoderBuds](https://coderbuds.com)** - AI adoption analytics for en

**Have questions?** Open a [GitHub Discussion](https://github.com/coderbuds/ai-detector/discussions) or [tweet at us](https://twitter.com/coderbuds).

**Want team insights?** [Start tracking with CoderBuds](https://coderbuds.com/register) (30-day free trial, no credit card required).
**Want team insights?** [Start tracking with Coderbuds](https://coderbuds.com/register) (30-day free trial, no credit card required).
124 changes: 124 additions & 0 deletions fixtures/cases.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Pull request shapes seen in production, and who they must be attributed to.
# `expect: null` means no tool — the rules must stay quiet. Titles, branches
# and descriptions are real shapes with identifying details removed.

- name: Dependabot bump of @openai/codex is not a Codex pull request
expect: null
pr:
title: "build(deps): bump @openai/codex from 0.117.0 to 0.118.0"
branch: dependabot/npm_and_yarn/openai/codex-0.118.0
description: "Bumps [@openai/codex](https://github.com/openai/codex/tree/HEAD/codex-cli) from 0.117.0 to 0.118.0."

- name: Mentioning a .codex/ config path is not a Codex pull request
expect: null
pr:
title: Configure root MCP servers
branch: feature/mcp-config
description: "Adds `.codex/config.toml` alongside `.cursor/mcp.json` so every agent loads the same servers."

- name: Codex cloud branch
expect: openai-codex
pr:
title: Validate generated PR actor
branch: codex/validate-generated-pr-actor
description: "## Summary\n- validate the actor"

- name: Codex cloud title prefix with the branch unknown
expect: openai-codex
pr:
title: "[codex] Fix trusted publishing npm upgrade step"
description: ""

- name: Codex cloud task link
expect: openai-codex
pr:
title: "fix(client): use getJWTToken() for dashboard requests"
description: "Uses the SDK helper.\n\n------\n[Codex Task](https://chatgpt.com/codex/cloud/tasks/task_e_0123456789abcdef0123456789abcdef)"

- name: A pull request about Codex setup is not a Codex pull request
expect: null
pr:
title: "docs: add Codex MCP setup"
branch: docs/codex-mcp
description: "The setup page did not include a Codex-specific path."

- name: Jules signature
expect: jules
pr:
title: "Palette: Add Escape key dismissal to modals"
branch: palette/escape-dismissal-1498727143055922862
description: "Adds keyboard dismissal.\n\n---\n*PR created automatically by Jules for task [1498727143055922862](https://jules.google.com/task/1498727143055922862) started by @someone*"

- name: Jules task branch alone
expect: jules
pr:
title: "Sentinel: Path parameter UUID validation"
branch: jules-4486629921808545700-9ef0dd4e
description: "Validates UUID path parameters."

- name: Claude Code web branch
expect: claude-code
pr:
title: "fix(PLT-1400): set dedicated ip_pool on sends"
branch: claude/sparkpost-ip-pool-update-ab4c44
description: "Sets the pool."

- name: Claude co-author trailer in lower case
expect: claude-code
pr:
title: Add bulk delete script
branch: bulk-delete
description: ""
commits:
- message: "Add bulk delete script\n\nCo-authored-by: Claude <noreply@anthropic.com>"

- name: Claude Code footer
expect: claude-code
pr:
title: Show agent action stages
branch: agent-stages
description: "## Summary\n...\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)"

- name: Cursor background agent branch
expect: cursor
pr:
title: "fix(CLP-2112): prevent crash on loops with no end date"
branch: cursor/fix-loop-no-end-date-crash-1edb
description: "Guards the loop."

- name: The word raider is not Aider
expect: null
pr:
title: "Fix raider: boss spawn timing"
branch: fix/raider-spawn
description: "The raider: boss spawned twice. Braided timers fixed."

- name: Aider commit prefix
expect: aider
pr:
title: Add tests for parser
branch: parser-tests
description: ""
commits:
- message: "aider: Added tests for the parser"

- name: Aider branch
expect: aider
pr:
title: Add parser tests
branch: aider/parser-tests
description: ""

- name: A changelog naming WindSurf is not a WindSurf pull request
expect: null
pr:
title: "build(deps): bump laravel/boost from 2.1 to 2.2"
branch: dependabot/composer/laravel/boost-2.2
description: "Add WindSurf and Kiro guideline files by @contributor"

- name: A plain human pull request
expect: null
pr:
title: Fix pagination on the repositories page
branch: fix/repositories-pagination
description: "## Summary\n- off-by-one in the page count\n\n## Testing\n- added a feature test"
12 changes: 3 additions & 9 deletions rules/aider.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ tool:

explicit_markers:
commit_footers:
- pattern: 'aider:'
regex: false
- pattern: '(?m)^aider: '
regex: true
confidence: 90
description: "Aider commit message prefix"

Expand All @@ -16,15 +16,9 @@ explicit_markers:
confidence: 100
description: "Aider website reference"

text_patterns:
- pattern: 'aider'
location: description
case_insensitive: true
confidence: 60
description: "Description mentions aider"

branch_patterns:
- pattern: '^aider[-/]'
regex: true
case_insensitive: true
confidence: 80
description: "Branch name starts with aider-"
Expand Down
Loading
Loading