| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| """AI tool signature detection logic. | ||
|
|
||
| This module provides the public API for detecting AI tool signatures in commit | ||
| messages. The signature data (tool definitions and patterns) lives in | ||
| :mod:`commit_check.ai_signatures_data`. | ||
|
|
||
| Typical usage:: | ||
|
|
||
| from commit_check.ai_signatures import detect_ai_signatures | ||
|
|
||
| result = detect_ai_signatures( | ||
| "feat: init\\n\\nCo-authored-by: Claude <noreply@anthropic.com>" | ||
| ) | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import re | ||
|
|
||
| from commit_check.ai_signatures_data import ALL_KNOWN_TOOLS as _ALL_KNOWN_TOOLS | ||
|
|
||
| # Re-export for convenience — consumers can import everything from | ||
| # commit_check.ai_signatures without knowing about the data/logic split. | ||
| ALL_KNOWN_TOOLS = _ALL_KNOWN_TOOLS | ||
|
|
||
|
|
||
| #: Flat list of all compiled patterns for bulk scanning. | ||
| #: Each tuple is ``(regex, tool_name, description, kind)``. | ||
| ALL_PATTERNS: list[tuple[re.Pattern[str], str, str, str]] = [ | ||
| (p.regex, tool.name, p.description, p.kind) | ||
| for tool in ALL_KNOWN_TOOLS | ||
| for p in tool.patterns | ||
| ] | ||
|
|
||
|
|
||
| def detect_ai_signatures(message: str) -> list[dict[str, str]]: | ||
| """Scan *message* for known AI tool signatures. | ||
|
|
||
| :param message: The full commit message (subject + body) to scan. | ||
| :returns: A list of dicts, one per matched signature, each with keys | ||
| ``"tool"``, ``"kind"``, ``"description"``, and ``"matched_text"``. | ||
| Returns an empty list when no signatures are found. | ||
|
|
||
| Example:: | ||
|
|
||
| >>> detect_ai_signatures( | ||
| ... "feat: init\\n\\nCo-authored-by: Claude <noreply@anthropic.com>" | ||
| ... ) | ||
| [{'tool': 'Claude Code', 'kind': 'trailer', ...}] | ||
| """ | ||
| results: list[dict[str, str]] = [] | ||
| seen: set[str] = set() | ||
|
|
||
| for regex, tool_name, desc, kind in ALL_PATTERNS: | ||
| for match in regex.finditer(message): | ||
| matched = match.group(0).strip() | ||
| if matched not in seen: | ||
| seen.add(matched) | ||
| results.append( | ||
| { | ||
| "tool": tool_name, | ||
| "kind": kind, | ||
| "description": desc, | ||
| "matched_text": matched, | ||
| } | ||
| ) | ||
|
|
||
| return results | ||
|
|
||
|
|
||
| def has_ai_signature(message: str) -> bool: | ||
| """Return ``True`` if *message* contains any known AI signature.""" | ||
| for regex, _tool_name, _desc, _kind in ALL_PATTERNS: | ||
| if regex.search(message): | ||
| return True | ||
| return False |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,257 @@ | ||
| """Known AI tool signatures — pure data, no detection logic. | ||
|
|
||
| This module defines the data structures and the curated registry of known AI | ||
| coding tool signatures. To add a new tool, define a ``KnownAiTool`` entry | ||
| with its patterns and add it to ``ALL_KNOWN_TOOLS``. | ||
|
|
||
| The detection logic lives in :mod:`commit_check.ai_signatures`. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import re | ||
| from dataclasses import dataclass, field | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class AiSignaturePattern: | ||
| """A single pattern that identifies AI tool usage in a commit message. | ||
|
|
||
| :param regex: A compiled regex that, if matched anywhere in the commit | ||
| message body, indicates the corresponding tool was involved. | ||
| :param kind: ``"trailer"`` for structured ``Key: value`` footer lines | ||
| (matched case-insensitively), ``"body_marker"`` for any other text | ||
| marker. | ||
| :param description: Human-readable description of what is matched. | ||
| """ | ||
|
|
||
| regex: re.Pattern[str] | ||
| kind: str # "trailer" | "body_marker" | ||
| description: str = "" | ||
|
|
||
|
|
||
| @dataclass(frozen=True) | ||
| class KnownAiTool: | ||
| """A known AI coding tool and its commit-message signatures. | ||
|
|
||
| :param name: Short display name (e.g. ``"Claude Code"``, ``"GitHub Copilot"``). | ||
| :param patterns: One or more signature patterns that indicate this tool. | ||
| """ | ||
|
|
||
| name: str | ||
| patterns: list[AiSignaturePattern] = field(default_factory=list) | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Pattern helpers | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
|
|
||
| def _trailer( | ||
| key: str, value_pattern: str = r".*", description: str = "" | ||
| ) -> AiSignaturePattern: | ||
| """Build a trailer pattern for a structured ``Key: value`` line. | ||
|
|
||
| The match is case-insensitive and anchors the key at the start of a line. | ||
| """ | ||
| raw = rf"^{re.escape(key)}:\s*{value_pattern}\s*$" | ||
| return AiSignaturePattern( | ||
| regex=re.compile(raw, re.IGNORECASE | re.MULTILINE), | ||
| kind="trailer", | ||
| description=description or f"``{key}:`` trailer", | ||
| ) | ||
|
|
||
|
|
||
| def _body_marker(pattern: str, description: str = "") -> AiSignaturePattern: | ||
| """Build a free-text body marker pattern.""" | ||
| return AiSignaturePattern( | ||
| regex=re.compile(pattern, re.MULTILINE), | ||
| kind="body_marker", | ||
| description=description, | ||
| ) | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Known tool signatures | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| # --- Anthropic Claude Code / Claude CLI --- | ||
| CLAUDE_CODE = KnownAiTool( | ||
| name="Claude Code", | ||
| patterns=[ | ||
| # Standard Co-authored-by trailer added by Claude Code. | ||
| # When an email is present, anchor to known AI noreply addresses | ||
| # to avoid false positives with human co-authors named Claude. | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"Claude(?: Code)?" | ||
| r"(?:\s*<(?:noreply@anthropic\.com" | ||
| r"|\d+\+Claude@users\.noreply\.github\.com)>)?", | ||
| "``Co-authored-by: Claude`` trailer", | ||
| ), | ||
| # Assisted-by trailer (Linux kernel style, with optional tool list) | ||
| _trailer( | ||
| "Assisted-by", | ||
| r"Claude:\S+(?:\s+\S+)*", | ||
| "``Assisted-by: Claude:<model> [tools]`` trailer", | ||
| ), | ||
| # Body marker: generated-with notice | ||
| _body_marker( | ||
| r"🤖\s*Generated\s+(?:with|by)\s+\[?Claude", | ||
| "``🤖 Generated with Claude`` body marker", | ||
| ), | ||
| # Session ID trailer (Claude Code sometimes adds this) | ||
| _trailer("Claude-Session", r"\S+", "``Claude-Session:`` trailer"), | ||
| # Workflow ID trailer | ||
| _trailer("Claude-Workflow", r"\S+", "``Claude-Workflow:`` trailer"), | ||
| ], | ||
| ) | ||
|
|
||
| # --- GitHub Copilot --- | ||
| COPILOT = KnownAiTool( | ||
| name="GitHub Copilot", | ||
| patterns=[ | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"Copilot" | ||
| r"(?:\s*<\d+\+Copilot@users\.noreply\.github\.com>)?", | ||
| "``Co-authored-by: Copilot`` trailer", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # --- OpenAI Codex --- | ||
| CODEX = KnownAiTool( | ||
| name="OpenAI Codex", | ||
| patterns=[ | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"Codex\s*(?:<[^>]*>)?", | ||
| "``Co-authored-by: Codex`` trailer", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # --- Gemini (Google) --- | ||
| GEMINI = KnownAiTool( | ||
| name="Gemini", | ||
| patterns=[ | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"Gemini\s*(?:<[^>]*>)?", | ||
| "``Co-authored-by: Gemini`` trailer", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # --- Cursor --- | ||
| CURSOR = KnownAiTool( | ||
| name="Cursor", | ||
| patterns=[ | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"Cursor\s*(?:<[^>]*>)?", | ||
| "``Co-authored-by: Cursor`` trailer", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # --- Devin --- | ||
| DEVIN = KnownAiTool( | ||
| name="Devin", | ||
| patterns=[ | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"Devin\s*(?:<[^>]*>)?", | ||
| "``Co-authored-by: Devin`` trailer", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # --- Aider --- | ||
| AIDER = KnownAiTool( | ||
| name="Aider", | ||
| patterns=[ | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"Aider\s*(?:<[^>]*>)?", | ||
| "``Co-authored-by: Aider`` trailer", | ||
| ), | ||
| # aider appends "(aider)" to the author name | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"[^<]+\(aider\)\s*(?:<[^>]*>)?", | ||
| "``Co-authored-by: ... (aider)`` trailer", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # --- Windsurf (Codeium) --- | ||
| WINDSURF = KnownAiTool( | ||
| name="Windsurf", | ||
| patterns=[ | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"Windsurf\s*(?:<[^>]*>)?", | ||
| "``Co-authored-by: Windsurf`` trailer", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # --- Tabby --- | ||
| TABBY = KnownAiTool( | ||
| name="Tabby", | ||
| patterns=[ | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"Tabby\s*(?:<[^>]*>)?", | ||
| "``Co-authored-by: Tabby`` trailer", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # --- Generic / catch-all AI patterns --- | ||
| GENERIC_AI = KnownAiTool( | ||
| name="Generic AI", | ||
| patterns=[ | ||
| # Catch AI agent model identifiers in Co-authored-by | ||
| # (e.g. claude-sonnet-4, gpt-4-turbo, gemini-1.5-pro). | ||
| # A hyphenated model suffix is required so bare human first names | ||
| # ("Claude", "Gemini") are NOT flagged, regardless of the email. | ||
| _trailer( | ||
| "Co-authored-by", | ||
| r"(?:claude|gpt|gemini)[\w.]*-[\w.-]+(?:\s*<[^>]*>)?", | ||
| "``Co-authored-by`` with AI model name", | ||
| ), | ||
| # Catch Assisted-by trailer (Linux kernel style) regardless of agent, | ||
| # with optional trailing tool list. | ||
| _trailer( | ||
| "Assisted-by", | ||
| r"\S+:\S+(?:\s+\S+)*", | ||
| "``Assisted-by: <tool>:<model> [tools]`` trailer (kernel style)", | ||
| ), | ||
| # Catch common body markers | ||
| _body_marker( | ||
| r"^Generated\s+(?:by|with)\s+(?:AI|artificial intelligence)", | ||
| "``Generated by AI`` body marker", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Master registry — ordered by specificity (most specific first) | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| #: All known AI tools, ordered so that more specific patterns are checked first. | ||
| ALL_KNOWN_TOOLS: list[KnownAiTool] = [ | ||
| CLAUDE_CODE, | ||
| COPILOT, | ||
| CODEX, | ||
| GEMINI, | ||
| CURSOR, | ||
| DEVIN, | ||
| AIDER, | ||
| WINDSURF, | ||
| TABBY, | ||
| GENERIC_AI, | ||
| ] | ||
| Back | FazBrowse Home | New Git URL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain🌐 Web query:
Devin AI Cognition commit co-authored-by bot email address
💡 Result:
Devin AI, developed by Cognition, uses a Co-authored-by trailer in its git commit messages to provide attribution for changes made by the AI agent [1][2]. Regarding the email address associated with this trailer, official documentation for Devin notes a correction in its changelog: the system was updated to use the correct GitHub App bot email address instead of the previously used noreply@cognition.ai address [3]. The standard format used for the trailer is: Co-authored-by: Devin devin@cognition.ai While earlier or alternative configurations may have utilized other patterns, current documentation emphasizes the transition to official GitHub App bot integrations for improved attribution and compatibility [3]. Depending on the specific integration setup (such as the Devin GitHub App), the trailer may reference the official GitHub App bot identity (e.g., devin-ai-integration[bot]) to ensure clear provenance within repository histories [4]. Users are encouraged to check their specific repository's Devin integration documentation for the most accurate, environment-specific attribution string [5][3].
Citations:
Restrict DEVIN to a bot identity
Devin\s*(?:<[^>]*>)? still matches any human co-author named Devin with any email, so Co-authored-by: Devin <devin@company.com> is classified as AI and rejected in forbid mode. Anchor this to Devin’s bot email/identity instead of a bare name.
🤖 Prompt for AI AgentsSorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.