From a4c14e548016d6368841df1421f7d6be20dba3fd Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Wed, 18 Mar 2026 12:28:36 +0000 Subject: [PATCH 1/3] Add brief ai subcommand for detecting AI-generated contributions Uses chaoss/ai-detection-action as a library to scan git commits for AI tool involvement. Supports commit range filtering, confidence thresholds, and JSON/human/markdown output formats. --- cmd/brief/ai.go | 173 +++++++++++++++++++++++++++++++++ cmd/brief/ai_test.go | 221 +++++++++++++++++++++++++++++++++++++++++++ cmd/brief/main.go | 3 + go.mod | 19 ++++ go.sum | 42 ++++++++ 5 files changed, 458 insertions(+) create mode 100644 cmd/brief/ai.go create mode 100644 cmd/brief/ai_test.go diff --git a/cmd/brief/ai.go b/cmd/brief/ai.go new file mode 100644 index 0000000..474f017 --- /dev/null +++ b/cmd/brief/ai.go @@ -0,0 +1,173 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io" + "os" + + "github.com/chaoss/ai-detection-action/detection" + "github.com/chaoss/ai-detection-action/detection/coauthor" + "github.com/chaoss/ai-detection-action/detection/committer" + "github.com/chaoss/ai-detection-action/detection/message" + "github.com/chaoss/ai-detection-action/detection/toolmention" + "github.com/chaoss/ai-detection-action/scan" +) + +func cmdAI(args []string) { + fs := flag.NewFlagSet("brief ai", flag.ExitOnError) + jsonFlag := fs.Bool("json", false, "Force JSON output") + humanFlag := fs.Bool("human", false, "Force human-readable output") + markdownFlag := fs.Bool("markdown", false, "Force markdown output") + commitRange := fs.String("range", "", "Commit range to scan (e.g. base..head)") + minConfidence := fs.String("min-confidence", "low", "Minimum confidence level: low, medium, or high") + _ = fs.Parse(args) + + path := "." + if fs.NArg() > 0 { + path = fs.Arg(0) + } + + minConf, err := parseConfidence(*minConfidence) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + + detectors := []detection.Detector{ + &committer.Detector{}, + &coauthor.Detector{}, + &message.Detector{}, + &toolmention.Detector{}, + } + + report, err := scan.ScanCommitRange(path, *commitRange, detectors) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + + report = filterByConfidence(report, minConf) + + switch { + case *markdownFlag: + aiMarkdown(os.Stdout, report) + case *jsonFlag || (!*humanFlag && !isTTY()): + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(report); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "error writing JSON: %v\n", err) + os.Exit(1) + } + default: + aiHuman(os.Stdout, report) + } + + if report.Summary.AICommits > 0 { + os.Exit(1) + } +} + +func parseConfidence(s string) (detection.Confidence, error) { + switch s { + case "low": + return detection.ConfidenceLow, nil + case "medium": + return detection.ConfidenceMedium, nil + case "high": + return detection.ConfidenceHigh, nil + default: + return 0, fmt.Errorf("invalid confidence level %q, expected low, medium, or high", s) + } +} + +func filterByConfidence(r scan.Report, minConf detection.Confidence) scan.Report { + var filtered []scan.CommitResult + for _, cr := range r.Commits { + var findings []detection.Finding + for _, f := range cr.Findings { + if f.Confidence >= minConf { + findings = append(findings, f) + } + } + filtered = append(filtered, scan.CommitResult{ + Hash: cr.Hash, + Findings: findings, + }) + } + + // Rebuild summary from filtered results. + summary := scan.Summary{ + TotalCommits: len(filtered), + ToolCounts: map[string]int{}, + ByConfidence: map[string]int{}, + } + for _, cr := range filtered { + if len(cr.Findings) > 0 { + summary.AICommits++ + } + for _, f := range cr.Findings { + summary.ToolCounts[f.Tool]++ + summary.ByConfidence[f.Confidence.String()]++ + } + } + + return scan.Report{ + Commits: filtered, + Summary: summary, + } +} + +const shortHashLen = 12 + +func aiHuman(w io.Writer, r scan.Report) { + _, _ = fmt.Fprintf(w, "%d commits scanned, %d with AI signals\n", r.Summary.TotalCommits, r.Summary.AICommits) + + if r.Summary.AICommits == 0 { + return + } + + _, _ = fmt.Fprintln(w) + for tool, count := range r.Summary.ToolCounts { + _, _ = fmt.Fprintf(w, " %-25s %d commits\n", tool, count) + } + + _, _ = fmt.Fprintln(w) + for _, cr := range r.Commits { + if len(cr.Findings) == 0 { + continue + } + _, _ = fmt.Fprintf(w, "%s\n", cr.Hash[:min(len(cr.Hash), shortHashLen)]) + for _, f := range cr.Findings { + _, _ = fmt.Fprintf(w, " [%s] %s: %s\n", f.Confidence, f.Tool, f.Detail) + } + } +} + +func aiMarkdown(w io.Writer, r scan.Report) { + _, _ = fmt.Fprintf(w, "## AI Detection\n\n") + _, _ = fmt.Fprintf(w, "%d commits scanned, %d with AI signals\n\n", r.Summary.TotalCommits, r.Summary.AICommits) + + if r.Summary.AICommits == 0 { + return + } + + _, _ = fmt.Fprintln(w, "| Tool | Commits |") + _, _ = fmt.Fprintln(w, "|------|---------|") + for tool, count := range r.Summary.ToolCounts { + _, _ = fmt.Fprintf(w, "| %s | %d |\n", tool, count) + } + + _, _ = fmt.Fprintf(w, "\n### Findings\n\n") + for _, cr := range r.Commits { + if len(cr.Findings) == 0 { + continue + } + _, _ = fmt.Fprintf(w, "**%s**\n\n", cr.Hash[:min(len(cr.Hash), shortHashLen)]) + for _, f := range cr.Findings { + _, _ = fmt.Fprintf(w, "- [%s] %s: %s\n", f.Confidence, f.Tool, f.Detail) + } + _, _ = fmt.Fprintln(w) + } +} diff --git a/cmd/brief/ai_test.go b/cmd/brief/ai_test.go new file mode 100644 index 0000000..59d8a64 --- /dev/null +++ b/cmd/brief/ai_test.go @@ -0,0 +1,221 @@ +package main + +import ( + "bytes" + "strings" + "testing" + + "github.com/chaoss/ai-detection-action/detection" + "github.com/chaoss/ai-detection-action/scan" +) + +func TestParseConfidence(t *testing.T) { + tests := []struct { + input string + want detection.Confidence + err bool + }{ + {"low", detection.ConfidenceLow, false}, + {"medium", detection.ConfidenceMedium, false}, + {"high", detection.ConfidenceHigh, false}, + {"invalid", 0, true}, + {"", 0, true}, + } + + for _, tt := range tests { + got, err := parseConfidence(tt.input) + if tt.err && err == nil { + t.Errorf("parseConfidence(%q) expected error", tt.input) + } + if !tt.err && err != nil { + t.Errorf("parseConfidence(%q) unexpected error: %v", tt.input, err) + } + if got != tt.want { + t.Errorf("parseConfidence(%q) = %v, want %v", tt.input, got, tt.want) + } + } +} + +func TestFilterByConfidence(t *testing.T) { + report := scan.Report{ + Commits: []scan.CommitResult{ + { + Hash: "abc123", + Findings: []detection.Finding{ + {Detector: "toolmention", Tool: "Claude", Confidence: detection.ConfidenceLow, Detail: "mentions Claude"}, + {Detector: "coauthor", Tool: "Claude Code", Confidence: detection.ConfidenceHigh, Detail: "co-author trailer"}, + }, + }, + { + Hash: "def456", + Findings: []detection.Finding{ + {Detector: "message", Tool: "Aider", Confidence: detection.ConfidenceMedium, Detail: "aider: prefix"}, + }, + }, + { + Hash: "ghi789", + Findings: nil, + }, + }, + Summary: scan.Summary{ + TotalCommits: 3, + AICommits: 2, + ToolCounts: map[string]int{"Claude": 1, "Claude Code": 1, "Aider": 1}, + ByConfidence: map[string]int{"low": 1, "medium": 1, "high": 1}, + }, + } + + t.Run("filter low keeps all", func(t *testing.T) { + filtered := filterByConfidence(report, detection.ConfidenceLow) + if filtered.Summary.AICommits != 2 { + t.Errorf("expected 2 AI commits, got %d", filtered.Summary.AICommits) + } + if len(filtered.Commits[0].Findings) != 2 { + t.Errorf("expected 2 findings in first commit, got %d", len(filtered.Commits[0].Findings)) + } + }) + + t.Run("filter medium drops low", func(t *testing.T) { + filtered := filterByConfidence(report, detection.ConfidenceMedium) + if filtered.Summary.AICommits != 2 { + t.Errorf("expected 2 AI commits, got %d", filtered.Summary.AICommits) + } + if len(filtered.Commits[0].Findings) != 1 { + t.Errorf("expected 1 finding in first commit, got %d", len(filtered.Commits[0].Findings)) + } + if filtered.Commits[0].Findings[0].Tool != "Claude Code" { + t.Errorf("expected Claude Code finding, got %s", filtered.Commits[0].Findings[0].Tool) + } + }) + + t.Run("filter high drops low and medium", func(t *testing.T) { + filtered := filterByConfidence(report, detection.ConfidenceHigh) + if filtered.Summary.AICommits != 1 { + t.Errorf("expected 1 AI commit, got %d", filtered.Summary.AICommits) + } + if len(filtered.Commits[1].Findings) != 0 { + t.Errorf("expected 0 findings in second commit, got %d", len(filtered.Commits[1].Findings)) + } + }) + + t.Run("total commits preserved", func(t *testing.T) { + filtered := filterByConfidence(report, detection.ConfidenceHigh) + if filtered.Summary.TotalCommits != 3 { + t.Errorf("expected 3 total commits, got %d", filtered.Summary.TotalCommits) + } + }) +} + +func TestAIHuman(t *testing.T) { + r := scan.Report{ + Commits: []scan.CommitResult{ + { + Hash: "abc123def456abc123def456abc123def456abc1", + Findings: []detection.Finding{ + {Detector: "coauthor", Tool: "Claude Code", Confidence: detection.ConfidenceHigh, Detail: "co-author trailer"}, + }, + }, + }, + Summary: scan.Summary{ + TotalCommits: 5, + AICommits: 1, + ToolCounts: map[string]int{"Claude Code": 1}, + ByConfidence: map[string]int{"high": 1}, + }, + } + + var buf bytes.Buffer + aiHuman(&buf, r) + out := buf.String() + + checks := []string{ + "5 commits scanned, 1 with AI signals", + "Claude Code", + "abc123def456", + "[high]", + } + for _, want := range checks { + if !strings.Contains(out, want) { + t.Errorf("output missing %q\ngot:\n%s", want, out) + } + } +} + +func TestAIHumanNoFindings(t *testing.T) { + r := scan.Report{ + Summary: scan.Summary{ + TotalCommits: 3, + AICommits: 0, + ToolCounts: map[string]int{}, + ByConfidence: map[string]int{}, + }, + } + + var buf bytes.Buffer + aiHuman(&buf, r) + out := buf.String() + + if !strings.Contains(out, "0 with AI signals") { + t.Errorf("expected no signals message\ngot:\n%s", out) + } +} + +func TestAIMarkdown(t *testing.T) { + r := scan.Report{ + Commits: []scan.CommitResult{ + { + Hash: "abc123def456abc123def456abc123def456abc1", + Findings: []detection.Finding{ + {Detector: "coauthor", Tool: "Claude Code", Confidence: detection.ConfidenceHigh, Detail: "co-author trailer"}, + }, + }, + }, + Summary: scan.Summary{ + TotalCommits: 5, + AICommits: 1, + ToolCounts: map[string]int{"Claude Code": 1}, + ByConfidence: map[string]int{"high": 1}, + }, + } + + var buf bytes.Buffer + aiMarkdown(&buf, r) + out := buf.String() + + checks := []string{ + "## AI Detection", + "5 commits scanned, 1 with AI signals", + "| Tool | Commits |", + "| Claude Code | 1 |", + "### Findings", + "**abc123def456**", + "- [high] Claude Code: co-author trailer", + } + for _, want := range checks { + if !strings.Contains(out, want) { + t.Errorf("output missing %q\ngot:\n%s", want, out) + } + } +} + +func TestAIMarkdownNoFindings(t *testing.T) { + r := scan.Report{ + Summary: scan.Summary{ + TotalCommits: 3, + AICommits: 0, + ToolCounts: map[string]int{}, + ByConfidence: map[string]int{}, + }, + } + + var buf bytes.Buffer + aiMarkdown(&buf, r) + out := buf.String() + + if !strings.Contains(out, "0 with AI signals") { + t.Errorf("expected no signals message\ngot:\n%s", out) + } + if strings.Contains(out, "### Findings") { + t.Errorf("should not have findings section\ngot:\n%s", out) + } +} diff --git a/cmd/brief/main.go b/cmd/brief/main.go index ddc44db..093effc 100644 --- a/cmd/brief/main.go +++ b/cmd/brief/main.go @@ -45,6 +45,9 @@ func main() { case "missing": cmdMissing(os.Args[2:]) return + case "ai": + cmdAI(os.Args[2:]) + return } } diff --git a/go.mod b/go.mod index 2f9ed12..e23eb0e 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( 4d63.com/gochecknoglobals v0.2.2 // indirect codeberg.org/chavacava/garif v0.2.0 // indirect codeberg.org/polyfloyd/go-errorlint v1.9.0 // indirect + dario.cat/mergo v1.0.0 // indirect dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect dev.gaijin.team/go/golib v0.6.0 // indirect github.com/4meepo/tagalign v1.4.3 // indirect @@ -31,8 +32,10 @@ require ( github.com/Antonboom/testifylint v1.6.4 // indirect github.com/Djarvur/go-err113 v0.1.1 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/MirrexOne/unqueryvet v1.5.4 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/alecthomas/chroma/v2 v2.23.1 // indirect github.com/alecthomas/go-check-sumtype v0.3.1 // indirect github.com/alexkohler/nakedret/v2 v2.0.6 // indirect @@ -57,6 +60,7 @@ require ( github.com/catenacyber/perfsprint v0.10.1 // indirect github.com/ccojocar/zxcvbn-go v1.0.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/chaoss/ai-detection-action v0.0.0-20260217162909-d6accd681af7 // indirect github.com/charithe/durationcheck v0.0.11 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/lipgloss v1.1.0 // indirect @@ -64,13 +68,16 @@ require ( github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/ckaznocha/intrange v0.3.1 // indirect + github.com/cloudflare/circl v1.6.1 // indirect github.com/curioswitch/go-reassign v0.3.0 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/daixiang0/gci v0.13.7 // indirect github.com/dave/dst v0.27.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/ecosyste-ms/ecosystems-go v0.1.1 // indirect + github.com/emirpasic/gods v1.18.1 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fatih/structtag v1.2.0 // indirect @@ -82,6 +89,9 @@ require ( github.com/git-pkgs/vers v0.2.4 // indirect github.com/github/go-spdx/v2 v2.4.0 // indirect github.com/go-critic/go-critic v0.14.3 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-git/v5 v5.16.5 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect github.com/go-toolsmith/astequal v1.2.0 // indirect @@ -94,6 +104,7 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/godoc-lint/godoc-lint v0.11.2 // indirect github.com/gofrs/flock v0.13.0 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golangci/asciicheck v0.5.0 // indirect github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect @@ -119,11 +130,13 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jgautheron/goconst v1.8.2 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jjti/go-spancheck v0.6.5 // indirect github.com/julz/importas v0.2.0 // indirect github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/kisielk/errcheck v1.10.0 // indirect github.com/kkHAIKE/contextcheck v1.1.6 // indirect github.com/kulti/thelper v0.7.1 // indirect @@ -160,6 +173,7 @@ require ( github.com/oapi-codegen/runtime v1.1.2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect @@ -183,6 +197,7 @@ require ( github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/sivchari/containedctx v1.0.3 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect github.com/sonatard/noctx v0.5.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spf13/afero v1.15.0 // indirect @@ -205,6 +220,7 @@ require ( github.com/ultraware/whitespace v0.2.0 // indirect github.com/uudashr/gocognit v1.2.1 // indirect github.com/uudashr/iface v1.4.1 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xen0n/gosmopolitan v1.3.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yagipy/maintidx v1.0.0 // indirect @@ -218,14 +234,17 @@ require ( go.uber.org/multierr v1.10.0 // indirect go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.48.0 // indirect golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect golang.org/x/mod v0.33.0 // indirect + golang.org/x/net v0.51.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/text v0.35.0 // indirect golang.org/x/tools v0.42.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect honnef.co/go/tools v0.7.0 // indirect mvdan.cc/gofumpt v0.9.2 // indirect diff --git a/go.sum b/go.sum index 6e657a9..36bc9d9 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6M codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ= codeberg.org/polyfloyd/go-errorlint v1.9.0 h1:VkdEEmA1VBpH6ecQoMR4LdphVI3fA4RrCh2an7YmodI= codeberg.org/polyfloyd/go-errorlint v1.9.0/go.mod h1:GPRRu2LzVijNn4YkrZYJfatQIdS+TrcK8rL5Xs24qw8= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y= dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI= dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo= @@ -65,10 +67,15 @@ github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/MirrexOne/unqueryvet v1.5.4 h1:38QOxShO7JmMWT+eCdDMbcUgGCOeJphVkzzRgyLJgsQ= github.com/MirrexOne/unqueryvet v1.5.4/go.mod h1:fs9Zq6eh1LRIhsDIsxf9PONVUjYdFHdtkHIgZdJnyPU= github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= @@ -133,6 +140,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chaoss/ai-detection-action v0.0.0-20260217162909-d6accd681af7 h1:Z6m5QArXIsS5avd6FCeRnhoCiw3htlIwkwTj8nnIzKo= +github.com/chaoss/ai-detection-action v0.0.0-20260217162909-d6accd681af7/go.mod h1:iz/PoWmlN6j9wbzvhjtUCAATM18ezUE5iF5/oU0uuzQ= github.com/charithe/durationcheck v0.0.11 h1:g1/EX1eIiKS57NTWsYtHDZ/APfeXKhye1DidBcABctk= github.com/charithe/durationcheck v0.0.11/go.mod h1:x5iZaixRNl8ctbM+3B2RrPG5t856TxRyVQEnbIEM2X4= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= @@ -151,10 +160,14 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs= github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ= github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ= github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY= @@ -170,6 +183,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ecosyste-ms/ecosystems-go v0.1.1 h1:YYiBK9TCCTeE+BtmpN2FssaRFcmF+T0v4LrupIOjehQ= github.com/ecosyste-ms/ecosystems-go v0.1.1/go.mod h1:VczXs1CO9nL8XbL1NwvgmwIaqzMsAxcsXnTpRtwi9gU= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -212,6 +227,12 @@ github.com/github/go-spdx/v2 v2.4.0 h1:+4IwVwJJbm3rzvrQ6P1nI9BDMcy3la4RchRy5uehV github.com/github/go-spdx/v2 v2.4.0/go.mod h1:/5rwgS0txhGtRdUZwc02bTglzg6HK3FfuEbECKlK2Sg= github.com/go-critic/go-critic v0.14.3 h1:5R1qH2iFeo4I/RJU8vTezdqs08Egi4u5p6vOESA0pog= github.com/go-critic/go-critic v0.14.3/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= +github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -262,6 +283,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -373,6 +396,8 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4= github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= @@ -393,6 +418,8 @@ github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY= github.com/karamaru-alpha/copyloopvar v1.2.2 h1:yfNQvP9YaGQR7VaWLYcfZUlRP2eo2vhExWKxD/fP6q0= github.com/karamaru-alpha/copyloopvar v1.2.2/go.mod h1:oY4rGZqZ879JkJMtX3RRkcXRkmUvH0x35ykgaKgsgJY= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.10.0 h1:Lvs/YAHP24YKg08LA8oDw2z9fJVme090RAXd90S+rrw= github.com/kisielk/errcheck v1.10.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -496,6 +523,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -563,10 +592,13 @@ github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOms github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/sonatard/noctx v0.5.0 h1:e/jdaqAsuWVOKQ0P6NWiIdDNHmHT5SwuuSfojFjzwrw= github.com/sonatard/noctx v0.5.0/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= @@ -623,6 +655,8 @@ github.com/uudashr/gocognit v1.2.1 h1:CSJynt5txTnORn/DkhiB4mZjwPuifyASC8/6Q0I/QS github.com/uudashr/gocognit v1.2.1/go.mod h1:acaubQc6xYlXFEMb9nWX2dYBzJ/bIjEkc1zzvyIZg5Q= github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU= github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= @@ -672,8 +706,11 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -752,6 +789,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= @@ -795,6 +833,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -826,6 +865,7 @@ golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1003,6 +1043,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From d5c14c393b8d48442d3d7937fa51423f355a94b8 Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Sun, 5 Apr 2026 20:18:09 +0100 Subject: [PATCH 2/3] Sort tool names in output and exclude empty commits after filtering --- cmd/brief/ai.go | 40 ++++++++++++++++++++++------------------ cmd/brief/ai_test.go | 14 ++++++++++---- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/cmd/brief/ai.go b/cmd/brief/ai.go index 474f017..7bd2edb 100644 --- a/cmd/brief/ai.go +++ b/cmd/brief/ai.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os" + "sort" "github.com/chaoss/ai-detection-action/detection" "github.com/chaoss/ai-detection-action/detection/coauthor" @@ -91,22 +92,22 @@ func filterByConfidence(r scan.Report, minConf detection.Confidence) scan.Report findings = append(findings, f) } } - filtered = append(filtered, scan.CommitResult{ - Hash: cr.Hash, - Findings: findings, - }) + if len(findings) > 0 { + filtered = append(filtered, scan.CommitResult{ + Hash: cr.Hash, + Findings: findings, + }) + } } // Rebuild summary from filtered results. summary := scan.Summary{ - TotalCommits: len(filtered), + TotalCommits: len(r.Commits), + AICommits: len(filtered), ToolCounts: map[string]int{}, ByConfidence: map[string]int{}, } for _, cr := range filtered { - if len(cr.Findings) > 0 { - summary.AICommits++ - } for _, f := range cr.Findings { summary.ToolCounts[f.Tool]++ summary.ByConfidence[f.Confidence.String()]++ @@ -119,6 +120,15 @@ func filterByConfidence(r scan.Report, minConf detection.Confidence) scan.Report } } +func sortedToolNames(counts map[string]int) []string { + names := make([]string, 0, len(counts)) + for name := range counts { + names = append(names, name) + } + sort.Strings(names) + return names +} + const shortHashLen = 12 func aiHuman(w io.Writer, r scan.Report) { @@ -129,15 +139,12 @@ func aiHuman(w io.Writer, r scan.Report) { } _, _ = fmt.Fprintln(w) - for tool, count := range r.Summary.ToolCounts { - _, _ = fmt.Fprintf(w, " %-25s %d commits\n", tool, count) + for _, tool := range sortedToolNames(r.Summary.ToolCounts) { + _, _ = fmt.Fprintf(w, " %-25s %d commits\n", tool, r.Summary.ToolCounts[tool]) } _, _ = fmt.Fprintln(w) for _, cr := range r.Commits { - if len(cr.Findings) == 0 { - continue - } _, _ = fmt.Fprintf(w, "%s\n", cr.Hash[:min(len(cr.Hash), shortHashLen)]) for _, f := range cr.Findings { _, _ = fmt.Fprintf(w, " [%s] %s: %s\n", f.Confidence, f.Tool, f.Detail) @@ -155,15 +162,12 @@ func aiMarkdown(w io.Writer, r scan.Report) { _, _ = fmt.Fprintln(w, "| Tool | Commits |") _, _ = fmt.Fprintln(w, "|------|---------|") - for tool, count := range r.Summary.ToolCounts { - _, _ = fmt.Fprintf(w, "| %s | %d |\n", tool, count) + for _, tool := range sortedToolNames(r.Summary.ToolCounts) { + _, _ = fmt.Fprintf(w, "| %s | %d |\n", tool, r.Summary.ToolCounts[tool]) } _, _ = fmt.Fprintf(w, "\n### Findings\n\n") for _, cr := range r.Commits { - if len(cr.Findings) == 0 { - continue - } _, _ = fmt.Fprintf(w, "**%s**\n\n", cr.Hash[:min(len(cr.Hash), shortHashLen)]) for _, f := range cr.Findings { _, _ = fmt.Fprintf(w, "- [%s] %s: %s\n", f.Confidence, f.Tool, f.Detail) diff --git a/cmd/brief/ai_test.go b/cmd/brief/ai_test.go index 59d8a64..39cea70 100644 --- a/cmd/brief/ai_test.go +++ b/cmd/brief/ai_test.go @@ -65,11 +65,14 @@ func TestFilterByConfidence(t *testing.T) { }, } - t.Run("filter low keeps all", func(t *testing.T) { + t.Run("filter low keeps commits with findings", func(t *testing.T) { filtered := filterByConfidence(report, detection.ConfidenceLow) if filtered.Summary.AICommits != 2 { t.Errorf("expected 2 AI commits, got %d", filtered.Summary.AICommits) } + if len(filtered.Commits) != 2 { + t.Errorf("expected 2 commits (empty-finding commit excluded), got %d", len(filtered.Commits)) + } if len(filtered.Commits[0].Findings) != 2 { t.Errorf("expected 2 findings in first commit, got %d", len(filtered.Commits[0].Findings)) } @@ -88,13 +91,16 @@ func TestFilterByConfidence(t *testing.T) { } }) - t.Run("filter high drops low and medium", func(t *testing.T) { + t.Run("filter high drops commits with no remaining findings", func(t *testing.T) { filtered := filterByConfidence(report, detection.ConfidenceHigh) if filtered.Summary.AICommits != 1 { t.Errorf("expected 1 AI commit, got %d", filtered.Summary.AICommits) } - if len(filtered.Commits[1].Findings) != 0 { - t.Errorf("expected 0 findings in second commit, got %d", len(filtered.Commits[1].Findings)) + if len(filtered.Commits) != 1 { + t.Errorf("expected 1 commit, got %d", len(filtered.Commits)) + } + if filtered.Commits[0].Hash != "abc123" { + t.Errorf("expected abc123, got %s", filtered.Commits[0].Hash) } }) From 5f9c7489777f155fc39d9e70278cfead5bb66584 Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Mon, 6 Apr 2026 10:24:24 +0100 Subject: [PATCH 3/3] Run go mod tidy to fix CI New direct dependencies were listed as indirect, causing go test and golangci-lint to fail. --- go.mod | 4 ++-- go.sum | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index e23eb0e..20fdaf6 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.25.6 require ( github.com/BurntSushi/toml v1.6.0 + github.com/chaoss/ai-detection-action v0.0.0-20260324080339-5f93e9bf0213 github.com/git-pkgs/enrichment v0.2.2 github.com/git-pkgs/forge v0.3.1 github.com/git-pkgs/licensecheck v0.4.0 @@ -60,7 +61,6 @@ require ( github.com/catenacyber/perfsprint v0.10.1 // indirect github.com/ccojocar/zxcvbn-go v1.0.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/chaoss/ai-detection-action v0.0.0-20260217162909-d6accd681af7 // indirect github.com/charithe/durationcheck v0.0.11 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/lipgloss v1.1.0 // indirect @@ -234,7 +234,7 @@ require ( go.uber.org/multierr v1.10.0 // indirect go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.48.0 // indirect + golang.org/x/crypto v0.49.0 // indirect golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.51.0 // indirect diff --git a/go.sum b/go.sum index 36bc9d9..39c73b1 100644 --- a/go.sum +++ b/go.sum @@ -100,8 +100,12 @@ github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQ github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/ashanbrown/forbidigo/v2 v2.3.0 h1:OZZDOchCgsX5gvToVtEBoV2UWbFfI6RKQTir2UZzSxo= github.com/ashanbrown/forbidigo/v2 v2.3.0/go.mod h1:5p6VmsG5/1xx3E785W9fouMxIOkvY2rRV9nMdWadd6c= github.com/ashanbrown/makezero/v2 v2.1.0 h1:snuKYMbqosNokUKm+R6/+vOPs8yVAi46La7Ck6QYSaE= @@ -140,8 +144,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chaoss/ai-detection-action v0.0.0-20260217162909-d6accd681af7 h1:Z6m5QArXIsS5avd6FCeRnhoCiw3htlIwkwTj8nnIzKo= -github.com/chaoss/ai-detection-action v0.0.0-20260217162909-d6accd681af7/go.mod h1:iz/PoWmlN6j9wbzvhjtUCAATM18ezUE5iF5/oU0uuzQ= +github.com/chaoss/ai-detection-action v0.0.0-20260324080339-5f93e9bf0213 h1:9jOGcXYOdLS/XqwEzG5nG9abEvTjdKGVe7ta5wtN+yg= +github.com/chaoss/ai-detection-action v0.0.0-20260324080339-5f93e9bf0213/go.mod h1:iz/PoWmlN6j9wbzvhjtUCAATM18ezUE5iF5/oU0uuzQ= github.com/charithe/durationcheck v0.0.11 h1:g1/EX1eIiKS57NTWsYtHDZ/APfeXKhye1DidBcABctk= github.com/charithe/durationcheck v0.0.11/go.mod h1:x5iZaixRNl8ctbM+3B2RrPG5t856TxRyVQEnbIEM2X4= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= @@ -183,6 +187,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ecosyste-ms/ecosystems-go v0.1.1 h1:YYiBK9TCCTeE+BtmpN2FssaRFcmF+T0v4LrupIOjehQ= github.com/ecosyste-ms/ecosystems-go v0.1.1/go.mod h1:VczXs1CO9nL8XbL1NwvgmwIaqzMsAxcsXnTpRtwi9gU= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -225,12 +231,16 @@ github.com/git-pkgs/vers v0.2.4 h1:Zr3jR/Xf1i/6cvBaJKPxhCwjzqz7uvYHE0Fhid/GPBk= github.com/git-pkgs/vers v0.2.4/go.mod h1:biTbSQK1qdbrsxDEKnqe3Jzclxz8vW6uDcwKjfUGcOo= github.com/github/go-spdx/v2 v2.4.0 h1:+4IwVwJJbm3rzvrQ6P1nI9BDMcy3la4RchRy5uehV/M= github.com/github/go-spdx/v2 v2.4.0/go.mod h1:/5rwgS0txhGtRdUZwc02bTglzg6HK3FfuEbECKlK2Sg= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-critic/go-critic v0.14.3 h1:5R1qH2iFeo4I/RJU8vTezdqs08Egi4u5p6vOESA0pog= github.com/go-critic/go-critic v0.14.3/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -527,6 +537,7 @@ github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -709,8 +720,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=