Skip to content
Open
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
18 changes: 6 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ jobs:
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Clone hawk for shared/types
run: git clone --depth=1 https://github.com/GrayCodeAI/hawk.git ../hawk
- name: Boundary guard
run: bash ./scripts/check-ecosystem-boundaries.sh
- name: gofumpt diff
run: |
go install mvdan.cc/gofumpt@v0.10.0
Expand All @@ -77,8 +77,8 @@ jobs:
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Clone hawk for shared/types
run: git clone --depth=1 https://github.com/GrayCodeAI/hawk.git ../hawk
- name: Boundary guard
run: bash ./scripts/check-ecosystem-boundaries.sh
- uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v7.0.0
with:
version: v2.1.0
Expand All @@ -98,8 +98,8 @@ jobs:
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Clone hawk for shared/types
run: git clone --depth=1 https://github.com/GrayCodeAI/hawk.git ../hawk
- name: Boundary guard
run: bash ./scripts/check-ecosystem-boundaries.sh
- name: Tidy check
run: |
go mod tidy
Expand Down Expand Up @@ -139,8 +139,6 @@ jobs:
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Clone hawk for shared/types
run: git clone --depth=1 https://github.com/GrayCodeAI/hawk.git ../hawk
- name: govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@v1.1.4
Expand All @@ -162,8 +160,6 @@ jobs:
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Clone hawk for shared/types
run: git clone --depth=1 https://github.com/GrayCodeAI/hawk.git ../hawk
- name: deadcode
run: |
go install golang.org/x/tools/cmd/deadcode@latest
Expand Down Expand Up @@ -207,8 +203,6 @@ jobs:
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Clone hawk for shared/types
run: git clone --depth=1 https://github.com/GrayCodeAI/hawk.git ../hawk
- name: Build
env:
GOOS: ${{ matrix.goos }}
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
# Local state
.claude/
.codegraph/
.gocache/
coverage.out

# Go workspace (local dev only — each developer creates their own)
go.work
go.work.sum

# macOS
.DS_Store
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ gofumpt -w . # Format
- Conventional Commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:`
- No `Co-authored-by:` trailers (auto-stripped by githook)
- `gofumpt` formatting enforced in CI
- Import `shared/types` from hawk for cross-repo types
- Import `hawk-core-contracts/types` for cross-repo types

## Common Pitfalls

Expand All @@ -49,7 +49,7 @@ gofumpt -w . # Format
- **Option functions use `With` prefix**: `WithProvider()`, `WithMaxTokens()`, `WithParallel()` — never bare `Provider()`
- **Preset options are bare vars**: `Quick`, `Thorough`, `SecurityFocus`, `CI` — exported `var Option` values
- **Internal types mirror public ones**: public `Finding` maps to internal `review.Finding` via `toPublicFindings()`
- **Severity is a type alias**: `type Severity = types.Severity` from `hawk/shared/types` — never define your own
- **Severity is a type alias**: `type Severity = types.Severity` from `hawk-core-contracts/types` — never define your own
- **Error sentinel naming**: `ErrNoProvider`, `ErrEmptyDiff`, `ErrContextCancelled` — always `Err` prefix, package-scoped
- **Mock types in tests**: `mockProvider` (unexported), implements `Provider` with `response string` and `err error` fields

Expand Down Expand Up @@ -85,7 +85,7 @@ gofumpt -w . # Format
- **Safe to refactor**: `toPublicFindings()`, `toPublicComments()` — mapping functions, add fields freely
- **Do not touch**: `Provider` interface signature — breaking change for all consumers (hawk, eyrie integration)
- **Do not touch**: `Finding`, `Result`, `Stats` struct field names/tags — used in JSON serialization by consumers
- **Do not touch**: `Severity` type alias — it re-exports from `hawk/shared/types`; changing it breaks cross-repo compatibility
- **Do not touch**: `Severity` type alias — it re-exports from `hawk-core-contracts/types`; changing it breaks cross-repo compatibility
- **Safe to extend**: add new `Option` functions, new presets, new `StaticRule` entries, new taint source/sink patterns
- **When adding concerns**: add to `defaultConfig().concerns` list and create corresponding `review.Concern` in `internal/review/`

Expand All @@ -97,7 +97,7 @@ gofumpt -w . # Format
| Reviewer implementation | `reviewer.go` (orchestration, parallel concerns, reflection) |
| Configuration & presets | `options.go` (`config` struct, `With*` functions, presets) |
| LLM provider interface | `provider.go` (`Provider`, `Message`, `ChatOpts`, `Response`) |
| Severity type alias | `severity.go` (re-exports from `hawk/shared/types`) |
| Severity type alias | `severity.go` (re-exports from `hawk-core-contracts/types`) |
| Static analysis rules | `static_rules.go` (`StaticRule`, `StaticAnalyzer`, 30+ rules) |
| Taint analysis | `taint_analysis.go` (`TaintAnalyzer`, source/sink/sanitizer patterns) |
| Diff parsing internals | `internal/diff/` |
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ GOVULNCHECK := $(GOBIN_DIR)/govulncheck
# ---------------------------------------------------------------------------
# Phony declarations (alphabetical).
# ---------------------------------------------------------------------------
.PHONY: all bench build ci clean cover fmt help lint lint-fix \
.PHONY: all bench boundaries build ci clean cover fmt help lint lint-fix \
security test test-10x test-race tidy version vet

boundaries: ## Enforce support-repo import boundaries.
bash ./scripts/check-ecosystem-boundaries.sh

# ---------------------------------------------------------------------------
# Default target.
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -93,7 +96,7 @@ tidy: ## Tidy go.mod / go.sum.
# ---------------------------------------------------------------------------
# Composite gate used by CI and pre-push.
# ---------------------------------------------------------------------------
ci: tidy fmt vet lint test-race security ## Run everything CI runs.
ci: tidy fmt vet lint boundaries test-race security ## Run everything CI runs.
@echo "All CI checks passed."

# ---------------------------------------------------------------------------
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@

Sight provides intelligent code review capabilities by analyzing diffs with AI. It understands context, identifies issues, and suggests improvements.

## Ecosystem Boundaries

Sight is a Hawk support engine. Keep the dependency edge one-way:

- use `hawk-core-contracts` for any cross-repo shared contracts
- do not import `hawk/internal/*`
- do not import removed legacy path `hawk/shared/types`; use `hawk-core-contracts/types`

## Features

- **Diff-aware analysis** - Reviews only changed code with full context
Expand Down
113 changes: 113 additions & 0 deletions contracts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package sight

import reviewcontracts "github.com/GrayCodeAI/hawk-core-contracts/review"

// ToContractFinding converts a sight finding into the shared review contract.
func ToContractFinding(f Finding) reviewcontracts.Finding {
return reviewcontracts.Finding{
Concern: f.Concern,
Severity: f.Severity,
File: f.File,
Line: f.Line,
EndLine: f.EndLine,
Message: f.Message,
Fix: f.Fix,
Reasoning: f.Reasoning,
CWE: f.CWE,
Confidence: f.Confidence,
SASTSource: f.SASTSource,
}
}

// ToContractFindings converts sight findings into shared review contracts.
func ToContractFindings(findings []Finding) []reviewcontracts.Finding {
if len(findings) == 0 {
return nil
}
out := make([]reviewcontracts.Finding, len(findings))
for i, f := range findings {
out[i] = ToContractFinding(f)
}
return out
}

// FromContractFinding converts a shared review contract into a sight finding.
func FromContractFinding(f reviewcontracts.Finding) Finding {
return Finding{
Concern: f.Concern,
Severity: f.Severity,
File: f.File,
Line: f.Line,
EndLine: f.EndLine,
Message: f.Message,
Fix: f.Fix,
Reasoning: f.Reasoning,
CWE: f.CWE,
Confidence: f.Confidence,
SASTSource: f.SASTSource,
}
}

// ToContractInlineComment converts a sight inline comment into the shared review contract.
func ToContractInlineComment(c InlineComment) reviewcontracts.InlineComment {
return reviewcontracts.InlineComment{
Path: c.Path,
StartLine: c.StartLine,
EndLine: c.EndLine,
Body: c.Body,
Suggestion: c.Suggestion,
}
}

// ToContractInlineComments converts sight inline comments into shared review contracts.
func ToContractInlineComments(comments []InlineComment) []reviewcontracts.InlineComment {
if len(comments) == 0 {
return nil
}
out := make([]reviewcontracts.InlineComment, len(comments))
for i, c := range comments {
out[i] = ToContractInlineComment(c)
}
return out
}

func toContractConfidenceBreakdown(b *ConfidenceBreakdown) *reviewcontracts.ConfidenceBreakdown {
if b == nil {
return nil
}
return &reviewcontracts.ConfidenceBreakdown{
High: ToContractFindings(b.High),
Medium: ToContractFindings(b.Medium),
Low: ToContractFindings(b.Low),
}
}

func toContractStats(s Stats) reviewcontracts.Stats {
return reviewcontracts.Stats{
FilesReviewed: s.FilesReviewed,
HunksAnalyzed: s.HunksAnalyzed,
FindingsTotal: s.FindingsTotal,
BySeverity: s.BySeverity,
ByConcern: s.ByConcern,
TokensUsed: s.TokensUsed,
DurationPerConcern: s.DurationPerConcern,
AverageConfidence: s.AverageConfidence,
HighConfidenceCount: s.HighConfidenceCount,
LowConfidenceCount: s.LowConfidenceCount,
}
}

// ToContractResult converts a sight result into the shared review contract.
func ToContractResult(r *Result) *reviewcontracts.Result {
if r == nil {
return nil
}
return &reviewcontracts.Result{
Findings: ToContractFindings(r.Findings),
Comments: ToContractInlineComments(r.Comments),
Stats: toContractStats(r.Stats),
Report: r.Report,
FailOn: r.FailOn,
ConfidenceBreakdown: toContractConfidenceBreakdown(r.ConfidenceBreakdown),
}
}
51 changes: 51 additions & 0 deletions contracts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package sight

import "testing"

func TestToContractResult(t *testing.T) {
t.Parallel()

result := &Result{
Findings: []Finding{
{
Concern: "security",
Severity: SeverityHigh,
File: "main.go",
Line: 12,
Message: "issue",
Fix: "fix",
Confidence: 0.9,
},
},
Comments: []InlineComment{{Path: "main.go", StartLine: 12, Body: "comment"}},
Stats: Stats{
FilesReviewed: 1,
FindingsTotal: 1,
BySeverity: map[Severity]int{SeverityHigh: 1},
ByConcern: map[string]int{"security": 1},
TokensUsed: 42,
},
Report: "report",
FailOn: SeverityMedium,
ConfidenceBreakdown: &ConfidenceBreakdown{
High: []Finding{{Concern: "security", Severity: SeverityHigh, File: "main.go", Line: 12, Message: "issue", Confidence: 0.9}},
},
}

got := ToContractResult(result)
if got == nil {
t.Fatal("expected non-nil contract result")
}
if got.Report != "report" {
t.Fatalf("Report = %q, want report", got.Report)
}
if len(got.Findings) != 1 || got.Findings[0].Severity != SeverityHigh {
t.Fatalf("unexpected findings conversion: %+v", got.Findings)
}
if got.Stats.TokensUsed != 42 {
t.Fatalf("TokensUsed = %d, want 42", got.Stats.TokensUsed)
}
if got.ConfidenceBreakdown == nil || len(got.ConfidenceBreakdown.High) != 1 {
t.Fatal("expected confidence breakdown to convert")
}
}
2 changes: 1 addition & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ sight/
├── reviewer.go 🔄 Reviewer: parallel concern orchestration
├── options.go ⚙️ config, With* functions, presets
├── provider.go 🔌 Provider interface (consumers implement)
├── severity.go 📊 Re-exports from hawk/shared/types
├── severity.go 📊 Re-exports from hawk-core-contracts/types
├── static_rules.go 🛡️ 30+ static analysis rules
├── taint_analysis.go 🔗 SSA-based taint tracking
├── sast_integration.go 🔒 SAST-LLM fusion
Expand Down
2 changes: 1 addition & 1 deletion go.mod

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 0 additions & 7 deletions go.work

This file was deleted.

2 changes: 1 addition & 1 deletion internal/review/concerns.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Package review implements the multi-concern LLM review pipeline.
package review

import "github.com/GrayCodeAI/hawk/shared/types"
import "github.com/GrayCodeAI/hawk-core-contracts/types"

// Severity mirrors the public type for internal use.
type Severity = types.Severity
Expand Down
15 changes: 15 additions & 0 deletions lefthook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,18 @@ commit-msg:
echo " full guide: https://www.conventionalcommits.org/"
exit 1
fi

strip-co-authored-by:
run: |
# Strip Co-authored-by: trailers that AI tools (Claude, Cursor, etc.) add.
# This enforces the rule that commits list only the human author.
sed '/^[Cc]o-[Aa]uthored-[Bb]y:/d' "{1}" > "{1}.tmp" && mv "{1}.tmp" "{1}"

# ---------------------------------------------------------------------------
# prepare-commit-msg — strip AI co-author trailers after tools inject them.
# ---------------------------------------------------------------------------
prepare-commit-msg:
commands:
strip-co-authored-by:
run: |
sed '/^[Cc]o-[Aa]uthored-[Bb]y:/d' "{1}" > "{1}.tmp" && mv "{1}.tmp" "{1}"
21 changes: 21 additions & 0 deletions scripts/check-ecosystem-boundaries.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

violations="$(
rg -n 'github\.com/GrayCodeAI/hawk/(internal/|shared/types)' \
--glob '*.go' \
. || true
)"

if [[ -n "${violations}" ]]; then
echo "forbidden Hawk imports found:"
echo "${violations}"
echo
echo "support repos must use hawk-core-contracts or local contracts, not hawk/internal or removed hawk/shared/types"
exit 1
fi

echo "ecosystem boundary guard passed"
Loading
Loading