Search by

coderbuds / ai-detector

ejntaylor

Multi-strategy AI code detection for pull requests

Package info

github.com/coderbuds/ai-detector

Homepage

Language:Python

pkg:composer/coderbuds/ai-detector

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 4

Open Issues: 0

v1.0.1 2026-09-11 16:12 UTC

README

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.

Validate rules License Stars

๐Ÿ“– Why We Open-Sourced This

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 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
  • โœ… Framework-agnostic - Use with any language (PHP, Python, Node.js, Ruby, Go)
  • โœ… No vendor lock-in - Own the detection logic, integrate however you want

๐Ÿ“ Read the full story: Why We Open-Sourced Our AI Detection Rules

๐Ÿš€ Quick Start

All detection rules are in the rules/ directory as YAML files:

git clone https://github.com/coderbuds/ai-detector
cd ai-detector

Example Rule:

# rules/claude-code.yml
tool:
  id: claude-code
  name: Claude Code
  provider: Anthropic

explicit_markers:
  commit_footers:
    - pattern: '\[Claude Code\]\(https://claude\.com/claude-code\)'
      regex: true
      confidence: 100
      description: "Official Claude Code footer in PR description"

  co_author_attributions:
    - pattern: 'Co-Authored-By: Claude Sonnet'
      regex: false
      confidence: 100
      description: "Claude co-author attribution"

๐Ÿค– Supported AI Tools

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 or open an issue.

๐Ÿ“ฆ Usage Examples

PHP (Laravel/Symfony)

use Symfony\Component\Yaml\Yaml;

// Load all rule files
$rulesPath = __DIR__ . '/vendor/coderbuds/ai-detector/rules';
$rules = [];

foreach (glob($rulesPath . '/*.yml') as $file) {
    $data = Yaml::parseFile($file);
    $rules[$data['tool']['id']] = $data;
}

// Check PR for AI markers
function detectAI(string $prDescription, array $commits): ?array
{
    global $rules;

    foreach ($rules as $toolId => $rule) {
        // Check commit footers
        foreach ($rule['explicit_markers']['commit_footers'] ?? [] as $marker) {
            $pattern = $marker['regex']
                ? '#' . $marker['pattern'] . '#i'
                : '/' . preg_quote($marker['pattern'], '/') . '/i';

            if (preg_match($pattern, $prDescription)) {
                return [
                    'tool' => $rule['tool']['name'],
                    'confidence' => $marker['confidence'],
                    'indicator' => $marker['description'],
                ];
            }
        }

        // Check bot authors in commits
        foreach ($rule['explicit_markers']['bot_authors'] ?? [] as $bot) {
            foreach ($commits as $commit) {
                if (isset($bot['email']) && $commit['author']['email'] === $bot['email']) {
                    return [
                        'tool' => $rule['tool']['name'],
                        'confidence' => $bot['confidence'],
                        'indicator' => $bot['description'],
                    ];
                }
            }
        }
    }

    return null; // No AI detected
}

Python

import yaml
import re
from pathlib import Path

# Load all rules
rules = {}
rules_dir = Path('vendor/coderbuds/ai-detector/rules')

for rule_file in rules_dir.glob('*.yml'):
    with open(rule_file) as f:
        data = yaml.safe_load(f)
        rules[data['tool']['id']] = data

def detect_ai(pr_description, commits):
    """Detect AI tool usage in pull request."""
    for tool_id, rule in rules.items():
        # Check commit footers
        for marker in rule.get('explicit_markers', {}).get('commit_footers', []):
            pattern = marker['pattern'] if marker.get('regex') else re.escape(marker['pattern'])
            if re.search(pattern, pr_description, re.IGNORECASE):
                return {
                    'tool': rule['tool']['name'],
                    'confidence': marker['confidence'],
                    'indicator': marker['description']
                }

        # Check bot authors
        for bot in rule.get('explicit_markers', {}).get('bot_authors', []):
            for commit in commits:
                if bot.get('email') and commit['author']['email'] == bot['email']:
                    return {
                        'tool': rule['tool']['name'],
                        'confidence': bot['confidence'],
                        'indicator': bot['description']
                    }

    return None  # No AI detected

Node.js / TypeScript

const yaml = require('js-yaml');
const fs = require('fs');
const path = require('path');

// Load all rules
const rulesDir = path.join(__dirname, 'node_modules/@coderbuds/ai-detector/rules');
const rules = {};

fs.readdirSync(rulesDir)
  .filter(file => file.endsWith('.yml'))
  .forEach(file => {
    const data = yaml.load(fs.readFileSync(path.join(rulesDir, file), 'utf8'));
    rules[data.tool.id] = data;
  });

function detectAI(prDescription, commits) {
  for (const [toolId, rule] of Object.entries(rules)) {
    // Check commit footers
    for (const marker of rule.explicit_markers?.commit_footers || []) {
      const pattern = new RegExp(marker.pattern, 'i');
      if (pattern.test(prDescription)) {
        return {
          tool: rule.tool.name,
          confidence: marker.confidence,
          indicator: marker.description
        };
      }
    }

    // Check bot authors
    for (const bot of rule.explicit_markers?.bot_authors || []) {
      for (const commit of commits) {
        if (bot.email && commit.author.email === bot.email) {
          return {
            tool: rule.tool.name,
            confidence: bot.confidence,
            indicator: bot.description
          };
        }
      }
    }
  }

  return null; // No AI detected
}

๐Ÿ“‹ Detection Categories

The YAML rules check for these marker types:

Category Description Example
commit_footers Signatures in PR descriptions "๐Ÿค– Generated with Claude Code"
co_author_attributions Co-author tags in commits Co-Authored-By: GitHub Copilot
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, claude/fix-login
text_patterns Title or description text (location: title or description) [codex] Fix publishing
mcp_clients The MCP client that worked on the change, by client_name or user_agent (regex) codex-mcp-client

mcp_clients is the one category that is not on the pull request. Coding agents name themselves when they connect to an MCP server (initialize's clientInfo, and the HTTP User-Agent), so a consumer that runs an MCP server and saw a session act on a change โ€” Coderbuds links the size check an agent runs just before opening a pull request โ€” can pass that client in. It is how local Codex CLI, which signs nothing, becomes visible.

๐ŸŽฏ What This Detects (And Doesn't)

โœ… Detects (Explicit Attribution)

  • Pull requests with AI tool footers
  • Commits authored by AI bots
  • Co-author attributions to AI tools
  • Branch names following AI tool patterns
  • PR labels indicating AI usage

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 on the pull request, and neither does any agent whose footer a person deletes. The one trace Codex does leave is the name it connects to MCP servers with โ€” see mcp_clients

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.

๐Ÿ†š Free vs Paid

This package is 100% free and open source (MIT License). Use it for:

  • โœ… Individual PR detection
  • โœ… CI/CD pipeline checks
  • โœ… Local development workflows
  • โœ… Custom integrations

Coderbuds Platform (paid service) adds:

  • ๐Ÿ“Š Team-level analytics over time
  • ๐Ÿ“ˆ AI adoption trends and insights
  • ๐ŸŽฏ Correlation with DORA metrics
  • ๐Ÿ” Behavioral AI detection (no explicit markers needed)
  • ๐Ÿข 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).

๐Ÿค Contributing

We welcome contributions! Help us:

  • ๐Ÿ†• Add new AI tool signatures
  • ๐Ÿ› Fix detection edge cases
  • ๐Ÿ“– Improve documentation
  • ๐Ÿงช Add test cases

How to Contribute

  1. Fork the repository
  2. Create a new rule file rules/your-tool.yml
  3. Follow the schema:
tool:
  id: your-tool-slug
  name: Your Tool Name
  provider: Company Name
  website: https://tool-website.com

explicit_markers:
  commit_footers:
    - pattern: 'Generated with Your Tool'
      regex: false
      confidence: 100
      description: "Tool footer in PR description"

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

bot_authors entries match on username, email, email_pattern or name_pattern; mcp_clients entries on client_name or user_agent; labels entries on name. Any other key is silently ignored by the matcher, so the validator rejects it.

  1. Add fixture cases to fixtures/cases.yml โ€” a pull request your rule must catch, and one it must not
  2. Run the validator: pip install pyyaml && python3 scripts/validate_rules.py
  3. 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

๐Ÿ“š Documentation

๐Ÿ“œ License

MIT License - Use freely in commercial and open-source projects.

See LICENSE.md for details.

๐Ÿ™ Credits

Created by Coderbuds - AI adoption analytics for engineering teams.

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

Star this repo โญ if you find it useful!

๐Ÿ”— Links

Have questions? Open a GitHub Discussion or tweet at us.

Want team insights? Start tracking with Coderbuds (30-day free trial, no credit card required).