diff --git a/detection/branchname/branchname_test.go b/detection/branchname/branchname_test.go index 5febd4d..a9f7083 100644 --- a/detection/branchname/branchname_test.go +++ b/detection/branchname/branchname_test.go @@ -50,7 +50,12 @@ func TestDetectNoMatch(t *testing.T) { cases := []string{ "main", "feature/add-login", - "fix-codex-typo", // "codex" appears, but not as a branch prefix + "fix-codex-typo", // "codex" appears, but not as a branch prefix + "rooster/fix-bug", // starts with "roo", but not "roo/" + "windsurfer/refactor", // starts with "windsurf", but not "windsurf/" + "openhands-discussion", // missing trailing slash "openhands/" + "swe-agent-docs", // missing trailing slash "swe-agent/" + "feature/roo-code", // prefix appears in body, not at start "", } diff --git a/detection/constants.go b/detection/constants.go index e4bde75..e48c891 100644 --- a/detection/constants.go +++ b/detection/constants.go @@ -140,14 +140,19 @@ var SupportedToolsInMentions = []string{ // KnownAgentBranchPrefixes maps branch name prefixes used by AI coding CLIs/agents // when they create their own branches (e.g. "codex/fix-bug") to tool names. var KnownAgentBranchPrefixes = map[string]string{ - "codex/": "OpenAI Codex", - "claude/": "Claude", - "copilot/": "GitHub Copilot", - "cursor/": "Cursor", - "devin/": "Devin", - "cline/": "Cline", - "aider/": "Aider", - "gemini/": "Gemini", + "codex/": "OpenAI Codex", + "claude/": "Claude", + "copilot/": "GitHub Copilot", + "cursor/": "Cursor", + "devin/": "Devin", + "cline/": "Cline", + "aider/": "Aider", + "gemini/": "Gemini", + "roo/": "Roo Code", + "roo-code/": "Roo Code", + "windsurf/": "Windsurf", + "openhands/": "OpenHands", + "swe-agent/": "SWE-Agent", } // KnownCoAuthorEmails Known emails present with Co-Authored-By trailers @@ -156,6 +161,9 @@ var KnownCoAuthorEmails = map[string]string{ "cursoragent@cursor.com": "Cursor", "noreply@aider.chat": "Aider", "copilot@github.com": "Copilot", + "noreply@continue.dev": "Continue.dev", + "noreply@windsurf.com": "Windsurf", + "openhands@all-hands.dev": "OpenHands", } // CoAuthorPattern Regex to look for Co-Authored-By trailer with name and email diff --git a/detection/trailer/trailer_test.go b/detection/trailer/trailer_test.go index c8bbcda..2452be4 100644 --- a/detection/trailer/trailer_test.go +++ b/detection/trailer/trailer_test.go @@ -57,6 +57,46 @@ func TestDetect(t *testing.T) { wantScore: []float64{85}, wantConfidence: []detection.Confidence{detection.ConfidenceHigh}, }, + { + name: "coauthor: Continue.dev trailer", + message: "feat: add feature\n\nCo-Authored-By: Continue ", + wantTools: []string{"Continue.dev"}, + wantModels: []string{""}, + wantScore: []float64{75}, + wantConfidence: []detection.Confidence{detection.ConfidenceHigh}, + }, + { + name: "coauthor: Windsurf trailer", + message: "feat: add feature\n\nCo-Authored-By: Windsurf ", + wantTools: []string{"Windsurf"}, + wantModels: []string{""}, + wantScore: []float64{75}, + wantConfidence: []detection.Confidence{detection.ConfidenceHigh}, + }, + { + name: "coauthor: OpenHands trailer", + message: "feat: add feature\n\nCo-Authored-By: OpenHands ", + wantTools: []string{"OpenHands"}, + wantModels: []string{""}, + wantScore: []float64{75}, + wantConfidence: []detection.Confidence{detection.ConfidenceHigh}, + }, + { + name: "coauthor: negative - unknown email domain for Continue", + message: "feat: add feature\n\nCo-Authored-By: Continue ", + wantTools: []string{}, + wantModels: []string{}, + wantScore: []float64{}, + wantConfidence: []detection.Confidence{}, + }, + { + name: "coauthor: negative - human coauthor with AI tool name in username", + message: "feat: add feature\n\nCo-Authored-By: Windsurfer ", + wantTools: []string{}, + wantModels: []string{}, + wantScore: []float64{}, + wantConfidence: []detection.Confidence{}, + }, { name: "coauthor: Cursor trailer with parenthesized model", message: "refactor: extract method\n\nCo-Authored-By: Cursor (composer 2.5) ",