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
Binary file removed .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@
.claude/
.codegraph/
coverage.out

# macOS
.DS_Store
45 changes: 44 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,53 @@ version: "2"
linters:
default: none
enable:
- errcheck
- govet
- ineffassign
- nilerr
- staticcheck
- unused
- misspell
- gocritic
- unconvert
- whitespace
- bodyclose
- noctx
- nilerr
settings:
errcheck:
check-type-assertions: true
check-blank: false
gocritic:
enabled-tags:
- diagnostic
- performance
disabled-checks:
- hugeParam
- rangeValCopy
- appendAssign
# appendCombine hurts readability when each appended item is a
# documented struct literal (see registerBuiltinChecks).
- appendCombine
staticcheck:
# QF1001 (De Morgan's law) is disabled: !(a || b) reads more clearly
# than the rewritten !a && !b in this codebase.
checks: ["all", "-SA1019", "-ST1003", "-QF1001"]
exclusions:
presets:
- comments
- std-error-handling
rules:
- path: _test\.go
linters:
- errcheck
- gocritic
- noctx
- bodyclose
# git.go shells out to short-lived local `git` commands; threading a
# context through these helpers adds no practical cancellation value.
- path: internal/context/git\.go
linters:
- noctx

issues:
max-issues-per-linter: 0
Expand Down
2 changes: 0 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
linguist hints (collapse `go.sum` in PR diffs).
- `.editorconfig` — UTF-8, LF, final newline, trim trailing whitespace,
tabs for Go, 2-space indent for YAML/JSON/TOML.
- `.github/dependabot.yml` — weekly `gomod` and `github-actions`
updates.
- `.github/PULL_REQUEST_TEMPLATE.md` — Summary / Changes / Review-
quality impact (eval-set numbers) / Testing / Checklist.
- `.github/ISSUE_TEMPLATE/bug_report.yml` — structured bug report
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<strong>AI-powered code review for diffs</strong>
</p>
<p align="center">
<a href="https://golang.org/"><img src="https://img.shields.io/badge/Go-1.23+-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go"></a>
<a href="https://golang.org/"><img src="https://img.shields.io/badge/Go-1.26+-00ADD8?style=flat-square&logo=go&logoColor=white" alt="Go"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="License"></a>
<a href="https://github.com/GrayCodeAI/sight/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/GrayCodeAI/sight/ci.yml?style=flat-square&label=tests" alt="CI"></a>
</p>
Expand Down Expand Up @@ -61,7 +61,7 @@ type Provider interface {
go get github.com/GrayCodeAI/sight@latest
```

Requires Go 1.23+.
Requires Go 1.26+.

## Contributing

Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ We follow [coordinated vulnerability disclosure](https://en.wikipedia.org/wiki/C

## Security practices in this repo

- **Dependency monitoring:** automated via Dependabot (see
`.github/dependabot.yml`).
- **Dependency monitoring:** vulnerable dependencies are detected by
`govulncheck`, which runs on every CI build (see "Vulnerability scanning").
- **Static analysis:** `golangci-lint` / `ruff` / `mypy` enforced in CI.
- **Vulnerability scanning:** `govulncheck` (Go) / `pip-audit` (Python) run
on every CI build.
Expand Down
4 changes: 2 additions & 2 deletions autofix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func TestExtractRelevantDiff_LineWindowExtraction(t *testing.T) {
b.WriteString("diff --git a/big.go b/big.go\n--- a/big.go\n+++ b/big.go\n")
b.WriteString("@@ -1,20 +1,20 @@\n")
for i := 1; i <= 20; i++ {
b.WriteString(fmt.Sprintf(" line %d context\n", i))
fmt.Fprintf(&b, " line %d context\n", i)
}
b.WriteString("+added line\n")

Expand Down Expand Up @@ -189,7 +189,7 @@ func TestExtractRelevantDiff_Truncation(t *testing.T) {
b.WriteString("diff --git a/big.go b/big.go\n--- a/big.go\n+++ b/big.go\n")
b.WriteString("@@ -1,50 +1,50 @@\n")
for i := 1; i <= 50; i++ {
b.WriteString(fmt.Sprintf(" this is a long context line number %d with extra padding text to make it longer\n", i))
fmt.Fprintf(&b, " this is a long context line number %d with extra padding text to make it longer\n", i)
}

result := extractRelevantDiff(b.String(), "big.go", 25)
Expand Down
2 changes: 1 addition & 1 deletion checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
// check prompt.
//
// Returns an empty slice (not an error) if the directory does not exist.
func LoadChecks(dir string) ([]CustomCheck, error) {

Check failure on line 41 in checks.go

View workflow job for this annotation

GitHub Actions / deadcode

unreachable func: LoadChecks
entries, err := os.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
Expand Down Expand Up @@ -72,14 +72,14 @@

// LoadChecksFromRepo is a convenience that looks for .sight/checks/ relative
// to the given repository root directory.
func LoadChecksFromRepo(repoDir string) ([]CustomCheck, error) {

Check failure on line 75 in checks.go

View workflow job for this annotation

GitHub Actions / deadcode

unreachable func: LoadChecksFromRepo
return LoadChecks(filepath.Join(repoDir, ".sight", "checks"))
}

// CustomChecksToConcerns converts loaded custom checks into internal Concern
// values suitable for the review pipeline. Only enabled checks are included.
// If languages is non-empty, the concern prompt notes which languages apply.
func CustomChecksToConcerns(checks []CustomCheck) []review.Concern {

Check failure on line 82 in checks.go

View workflow job for this annotation

GitHub Actions / deadcode

unreachable func: CustomChecksToConcerns
var concerns []review.Concern
for _, c := range checks {
if !c.Enabled {
Expand All @@ -105,7 +105,7 @@
// additional concerns to the review. This is the primary integration point:
//
// sight.Review(ctx, diff, sight.WithCustomChecks(".sight/checks"))
func WithCustomChecks(dir string) Option {

Check failure on line 108 in checks.go

View workflow job for this annotation

GitHub Actions / deadcode

unreachable func: WithCustomChecks
return optFunc(func(c *config) {
checks, err := LoadChecks(dir)
if err != nil || len(checks) == 0 {
Expand All @@ -120,7 +120,7 @@
}

// WithCustomChecksFromRepo loads checks from .sight/checks/ within the repo root.
func WithCustomChecksFromRepo(repoDir string) Option {

Check failure on line 123 in checks.go

View workflow job for this annotation

GitHub Actions / deadcode

unreachable func: WithCustomChecksFromRepo
return WithCustomChecks(filepath.Join(repoDir, ".sight", "checks"))
}

Expand Down Expand Up @@ -182,7 +182,7 @@
}
}
case "enabled":
check.Enabled = strings.ToLower(value) != "false"
check.Enabled = !strings.EqualFold(value, "false")
}
}
}
6 changes: 1 addition & 5 deletions dedup.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,7 @@ func buildReason(rep Finding, dupes []Finding, config DedupConfig) string {
parts = append(parts, "same file")
}

if len(dupes) == 1 {
parts = append(parts, "similar concern/message text")
} else {
parts = append(parts, "similar concern/message text")
}
parts = append(parts, "similar concern/message text")

if rep.File != "" && len(dupes) > 0 && dupes[0].File == rep.File {
parts = append(parts, "within line distance threshold")
Expand Down
7 changes: 4 additions & 3 deletions describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,12 @@ func buildDescribePrompt(files []diff.File, maxTokens int) string {

for _, f := range files {
prefix := ""
if f.Added {
switch {
case f.Added:
prefix = " (new)"
} else if f.Deleted {
case f.Deleted:
prefix = " (deleted)"
} else if f.Renamed {
case f.Renamed:
prefix = " (renamed from " + f.OldPath + ")"
}
b.WriteString("- " + f.Path + prefix + "\n")
Expand Down
5 changes: 1 addition & 4 deletions filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,7 @@ REASONING: brief explanation`, f.Concern, f.Severity, f.File, f.Line, f.Message,
func parseFilterResponse(f Finding, response string) FilterResult {
lower := strings.ToLower(response)

confirmed := true
if strings.Contains(lower, "confirmed: no") || strings.Contains(lower, "false positive") {
confirmed = false
}
confirmed := !(strings.Contains(lower, "confirmed: no") || strings.Contains(lower, "false positive"))

confidence := 0.7
if idx := strings.Index(lower, "confidence:"); idx >= 0 {
Expand Down
4 changes: 2 additions & 2 deletions incremental.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,9 @@ func enrichDiffWithContext(ctx context.Context, diffText string, contextLines in
// Add surrounding context before the hunk
contextBefore := loadFileLines(ctx, currentFile, startLine-contextLines, startLine-1)
if len(contextBefore) > 0 {
enriched.WriteString(fmt.Sprintf("\n--- Context before (lines %d-%d) ---\n", startLine-contextLines, startLine-1))
fmt.Fprintf(&enriched, "\n--- Context before (lines %d-%d) ---\n", startLine-contextLines, startLine-1)
for i, cl := range contextBefore {
enriched.WriteString(fmt.Sprintf(" %d | %s\n", startLine-contextLines+i, cl))
fmt.Fprintf(&enriched, " %d | %s\n", startLine-contextLines+i, cl)
}
}

Expand Down
4 changes: 2 additions & 2 deletions internal/comment/comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,10 @@ func buildComment(f FindingInput) Inline {
sev = sevLabels[f.Severity]
}

body.WriteString(fmt.Sprintf("**[%s]** %s\n", sev, f.Message))
fmt.Fprintf(&body, "**[%s]** %s\n", sev, f.Message)

if f.Reasoning != "" {
body.WriteString(fmt.Sprintf("\n> %s\n", f.Reasoning))
fmt.Fprintf(&body, "\n> %s\n", f.Reasoning)
}

c := Inline{
Expand Down
6 changes: 3 additions & 3 deletions internal/context/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ func FormatContext(contexts []FileContext) string {
if len(fc.RecentCommits) == 0 {
continue
}
b.WriteString(fmt.Sprintf("### %s — Recent changes:\n", fc.Path))
fmt.Fprintf(&b, "### %s — Recent changes:\n", fc.Path)
for _, c := range fc.RecentCommits {
b.WriteString(fmt.Sprintf(" - %s\n", c))
fmt.Fprintf(&b, " - %s\n", c)
}
if fc.BlameSnippet != "" {
b.WriteString(fmt.Sprintf(" Blame: %s\n", fc.BlameSnippet))
fmt.Fprintf(&b, " Blame: %s\n", fc.BlameSnippet)
}
b.WriteString("\n")
}
Expand Down
26 changes: 13 additions & 13 deletions internal/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ func FormatTerminal(findings []Finding, stats Stats) string {
b.WriteString("\n")
b.WriteString(bold + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + reset + "\n")
b.WriteString(bold + " SIGHT CODE REVIEW" + reset + "\n")
b.WriteString(fmt.Sprintf(" %d files, %d hunks analyzed", stats.FilesReviewed, stats.HunksAnalyzed))
fmt.Fprintf(&b, " %d files, %d hunks analyzed", stats.FilesReviewed, stats.HunksAnalyzed)
if stats.TokensUsed > 0 {
b.WriteString(fmt.Sprintf(" (%d tokens used)", stats.TokensUsed))
fmt.Fprintf(&b, " (%d tokens used)", stats.TokensUsed)
}
b.WriteString("\n")
b.WriteString(bold + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + reset + "\n\n")
Expand Down Expand Up @@ -82,7 +82,7 @@ func FormatTerminal(findings []Finding, stats Stats) string {
name = severityNames[sev]
}

b.WriteString(fmt.Sprintf(" %s%s%s (%d)\n\n", color+bold, name, reset, len(items)))
fmt.Fprintf(&b, " %s%s%s (%d)\n\n", color+bold, name, reset, len(items))

for _, f := range items {
loc := f.File
Expand All @@ -93,25 +93,25 @@ func FormatTerminal(findings []Finding, stats Stats) string {
}
}

b.WriteString(fmt.Sprintf(" %s%s%s %s\n", color, "●", reset, f.Message))
b.WriteString(fmt.Sprintf(" %s%s%s", dim, loc, reset))
fmt.Fprintf(&b, " %s%s%s %s\n", color, "●", reset, f.Message)
fmt.Fprintf(&b, " %s%s%s", dim, loc, reset)
if f.Concern != "" {
b.WriteString(fmt.Sprintf(" %s[%s]%s", dim, f.Concern, reset))
fmt.Fprintf(&b, " %s[%s]%s", dim, f.Concern, reset)
}
b.WriteString("\n")

if f.Reasoning != "" {
b.WriteString(fmt.Sprintf(" %s▸ %s%s\n", dim, f.Reasoning, reset))
fmt.Fprintf(&b, " %s▸ %s%s\n", dim, f.Reasoning, reset)
}
if f.Fix != "" {
b.WriteString(fmt.Sprintf(" %s⚡ Fix: %s%s\n", "\033[32m", f.Fix, reset))
fmt.Fprintf(&b, " %s⚡ Fix: %s%s\n", "\033[32m", f.Fix, reset)
}
b.WriteString("\n")
}
}

b.WriteString(bold + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + reset + "\n")
b.WriteString(fmt.Sprintf(" SUMMARY: %d findings", len(findings)))
fmt.Fprintf(&b, " SUMMARY: %d findings", len(findings))

parts := []string{}
for _, sev := range order {
Expand All @@ -131,7 +131,7 @@ func FormatTerminal(findings []Finding, stats Stats) string {
for name, d := range stats.DurationPerConcern {
dParts = append(dParts, fmt.Sprintf("%s:%s", name, d.Round(time.Millisecond)))
}
b.WriteString(fmt.Sprintf(" %sTiming: %s%s\n", dim, strings.Join(dParts, " | "), reset))
fmt.Fprintf(&b, " %sTiming: %s%s\n", dim, strings.Join(dParts, " | "), reset)
}
b.WriteString(bold + "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + reset + "\n")

Expand Down Expand Up @@ -309,7 +309,7 @@ func FormatGitHubReview(findings []Finding) string {
}

var b strings.Builder
b.WriteString(fmt.Sprintf("## Sight Review — %d findings\n\n", len(findings)))
fmt.Fprintf(&b, "## Sight Review — %d findings\n\n", len(findings))

for _, f := range findings {
sev := "INFO"
Expand All @@ -320,9 +320,9 @@ func FormatGitHubReview(findings []Finding) string {
if f.Line > 0 {
loc = fmt.Sprintf("%s:%d", f.File, f.Line)
}
b.WriteString(fmt.Sprintf("- **[%s]** `%s` — %s\n", sev, loc, f.Message))
fmt.Fprintf(&b, "- **[%s]** `%s` — %s\n", sev, loc, f.Message)
if f.Fix != "" {
b.WriteString(fmt.Sprintf(" - Fix: %s\n", f.Fix))
fmt.Fprintf(&b, " - Fix: %s\n", f.Fix)
}
}

Expand Down
Loading
Loading