diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ee75bcb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +.git +.github +.gitignore +*.md +.env +.env.* +Dockerfile +.dockerignore +coverage.out +docs/ +deploy/ +api/ +go.work +go.work.sum diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c15da9a --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +# sight environment variables — copy to .env and fill in +# LLM API key for code review (consumers provide their own via the Provider interface) +# sight itself doesn't read this — configure your LLM client before passing it to sight.NewReviewer() +SIGHT_PROVIDER= +SIGHT_API_KEY= diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..7b40db5 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,63 @@ +name: Docker + +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + branches: [main] + paths: + - "Dockerfile" + - "**.go" + - "go.mod" + - "go.sum" + +permissions: + contents: read + packages: write + +env: + REGISTRY: ghcr.io + IMAGE_NAME: graycodeai/sight + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix=sha- + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + VERSION=${{ github.ref_name }} + COMMIT=${{ github.sha }} + BUILD_DATE=${{ github.event.head_commit.timestamp }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8dd91f6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +FROM golang:1.26.3-alpine AS builder + +RUN apk add --no-cache git ca-certificates tzdata + +WORKDIR /build +COPY go.mod go.sum ./ +RUN go mod download && go mod verify + +COPY . . +ARG VERSION=dev +ARG COMMIT=none +ARG BUILD_DATE=unknown +RUN CGO_ENABLED=0 GOOS=linux go build -trimpath \ + -ldflags="-s -w \ + -X main.Version=${VERSION} \ + -X main.Commit=${COMMIT} \ + -X main.BuildDate=${BUILD_DATE}" \ + -o sight ./cmd/sight + +FROM alpine:3.21 +RUN apk add --no-cache ca-certificates tini && \ + adduser -D -u 1000 sight + +COPY --from=builder /build/sight /usr/local/bin/sight +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo + +USER sight +ENTRYPOINT ["tini", "--", "sight"] +CMD ["mcp"] diff --git a/api/openapi.yaml b/api/openapi.yaml new file mode 100644 index 0000000..8507b5c --- /dev/null +++ b/api/openapi.yaml @@ -0,0 +1,95 @@ +openapi: "3.1.0" +info: + title: sight — Code Review Tool Reference + description: | + sight is an AI-powered code review library for diffs. + It parses unified diffs, enriches with code context and git history, + and runs parallel multi-concern reviews through an LLM provider. + + sight operates as a Go library and MCP server (stdio or HTTP). + This document describes the MCP tool surface as a machine-readable reference. + version: "0.1.0" + license: + name: MIT + url: https://github.com/GrayCodeAI/sight/blob/main/LICENSE + contact: + url: https://github.com/GrayCodeAI/sight + +# No HTTP server by default — MCP uses stdio transport. +# HTTP transport is available via: sight mcp --transport http --addr 127.0.0.1:8080 + +tags: + - name: review + description: Code review tools + - name: taint + description: Security taint analysis + +x-mcp-server: + transport: stdio + binary: sight + start_command: ["sight", "mcp"] + http_transport_command: ["sight", "mcp", "--transport", "http", "--addr", "127.0.0.1:8080"] + +x-mcp-tools: + sight_review: + description: AI code review on a unified diff — security, correctness, style, performance + inputSchema: + type: object + required: [diff] + properties: + diff: + type: string + description: Unified diff text (output of `git diff`) + preset: + type: string + enum: [Quick, Standard, Thorough, SecurityFocus, CI] + default: Standard + description: Review thoroughness preset + exclude_files: + type: array + items: + type: string + description: File patterns to exclude from review (glob) + max_tokens: + type: integer + description: Maximum tokens for the LLM review + + sight_describe: + description: Generate a pull request description from a unified diff + inputSchema: + type: object + required: [diff] + properties: + diff: + type: string + description: Unified diff text + + sight_improve: + description: Suggest specific code improvements for a diff + inputSchema: + type: object + required: [diff] + properties: + diff: + type: string + focus: + type: string + description: Focus area for improvements (e.g. "performance", "readability") + + sight_taint: + description: SSA-based cross-function taint analysis for security vulnerabilities + inputSchema: + type: object + required: [path] + properties: + path: + type: string + description: Absolute path to the Go package or module root + patterns: + type: string + default: "./..." + description: Go package patterns to analyze + json: + type: boolean + default: false + description: Output results as JSON diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml new file mode 100644 index 0000000..271dd99 --- /dev/null +++ b/deploy/docker/docker-compose.yml @@ -0,0 +1,12 @@ +name: sight + +services: + sight: + build: + context: ../../ + dockerfile: Dockerfile + image: ghcr.io/graycodeai/sight:dev + env_file: + - path: ../../.env.example + required: false + command: ["mcp"] diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..d6ca4d3 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,124 @@ +
+ +# 👁️ sight Architecture + +**AI-Powered Code Review on Diffs** + +[![Go](https://img.shields.io/badge/Go-1.26+-00ADD8?logo=go)](https://go.dev/) +[![Protocol](https://img.shields.io/badge/Protocol-MCP-purple)]() + +
+ +--- + +## 🎯 Overview + +sight is an AI-powered code review library for Go. It parses unified diffs, enriches with **code context** and **git history**, and runs **parallel multi-concern reviews** through an LLM provider. + +> 💡 No LLM client bundled — consumers inject their own via the `Provider` interface. + +--- + +## 🧱 Components + +``` +sight/ +├── api/openapi.yaml 📜 MCP tool surface reference +├── cmd/sight/main.go 🖥️ CLI entry (mcp, taint subcommands) +├── sight.go 📤 Public API: Review(), Finding, Result, Stats +├── 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 +├── static_rules.go 🛡️ 30+ static analysis rules +├── taint_analysis.go 🔗 SSA-based taint tracking +├── sast_integration.go 🔒 SAST-LLM fusion +├── autofix.go 🔧 Fix suggestion pipeline +├── eval.go 📊 Evaluation harness +├── mcp/ 🔌 MCP server (stdio + HTTP) +└── internal/ + ├── diff/ 📄 Unified diff parser + ├── review/ 🧠 Concerns, prompts, response parsing + ├── comment/ 💬 Inline comment formatting + ├── context/ 📖 Code context + git blame + └── output/ 📊 SARIF and terminal formatters +``` + +--- + +## 📤 Public API + +```go +// 🚀 One-shot review +result, err := sight.Review(ctx, diffText, + sight.WithProvider(myLLMProvider), + sight.Thorough, +) + +// 🔄 Reusable reviewer +reviewer := sight.NewReviewer(sight.WithProvider(myLLMProvider)) +result, err := reviewer.Review(ctx, diffText) + +// ❌ Check if any findings are above threshold +if result.Failed() { + fmt.Printf("Review failed: %d findings\n", len(result.Findings)) +} +``` + +--- + +## 🔌 Provider Interface + +```go +type Provider interface { + Chat(ctx context.Context, messages []Message, opts ChatOpts) (*Response, error) +} +``` + +> Consumers implement this with their LLM client (e.g., using eyrie). hawk wires eyrie as the provider via `internal/bridge/sight/bridge.go`. + +--- + +## ⚡ Presets + +| Preset | Concerns | Speed | +|--------|----------|:-----:| +| 🏃 `Quick` | security, correctness | Fast | +| 📊 `Standard` | security, correctness, style, docs | Medium | +| 🔬 `Thorough` | all concerns | Slow | +| 🔒 `SecurityFocus` | security, taint only | Fast | +| 🤖 `CI` | Standard, fail on High+ | Medium | + +--- + +## 🔌 MCP Server + +```bash +sight mcp # 📡 stdio transport +sight mcp --transport http --addr :8080 # 🌐 HTTP transport +``` + +**Tools:** `sight_review` · `sight_describe` · `sight_improve` · `sight_taint` + +--- + +## 🔎 Findings + +| Field | Description | +|-------|-------------| +| `Concern` | Review category (security, style, etc.) | +| `Severity` | 🟢 Info · 🟡 Low · 🟠 Medium · 🔴 High · 🟥 Critical | +| `File` / `Line` | Location in the diff | +| `Message` | What was found and why | +| `Fix` | Suggested fix | +| `CWE` | CWE reference (security findings) | +| `Confidence` | 0.0–1.0 score | +| `InlineComment` | PR-ready inline comment | + +--- + +## 🛡️ Static Rules + Taint Analysis + +**30+ built-in rules** run without LLM overhead — hardcoded secret patterns, SQL injection sinks, unsafe deserialization, etc. Fused with LLM results. + +**Taint analysis** (`sight taint --path .`) uses SSA-based cross-function tracking to detect source→sink data flows. Sources, sinks, and sanitizers are configurable. diff --git a/examples/basic/main.go b/examples/basic/main.go new file mode 100644 index 0000000..a2ea31c --- /dev/null +++ b/examples/basic/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "context" + "fmt" + "log" + + "github.com/GrayCodeAI/sight" +) + +type mockProvider struct{} + +func (m *mockProvider) Complete(ctx context.Context, messages []sight.Message) (string, error) { + return "Code looks good. Consider adding error handling for edge cases.", nil +} + +func main() { + diff := `diff --git a/main.go b/main.go +--- a/main.go ++++ b/main.go +@@ -10,6 +10,10 @@ func main() { + if err != nil { + log.Fatal(err) + } ++ ++ // Process the data ++ processData(result) ++ + fmt.Println("Done") + } +` + + reviewer := sight.NewReviewer( + sight.WithProvider(&mockProvider{}), + sight.Thorough, + ) + + result, err := reviewer.Review(context.Background(), diff) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Found %d findings:\n", len(result.Findings)) + for _, f := range result.Findings { + fmt.Printf("[%s] %s:%d - %s\n", f.Severity, f.File, f.Line, f.Message) + } +}