|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "database/sql" |
| 6 | + "errors" |
| 7 | + "flag" |
| 8 | + "fmt" |
| 9 | + "os" |
| 10 | + |
| 11 | + "github.com/bit8bytes/gogantic/agents" |
| 12 | + "github.com/bit8bytes/gogantic/llms/ollama" |
| 13 | + "github.com/bit8bytes/gogantic/runner" |
| 14 | + "github.com/bit8bytes/gogantic/stores/moderncsqlite" |
| 15 | + "github.com/bit8bytes/gogantic/tools" |
| 16 | +) |
| 17 | + |
| 18 | +func analyze(ctx context.Context, db *sql.DB, wd string) error { |
| 19 | + fs := flag.NewFlagSet("analyze", flag.ExitOnError) |
| 20 | + model := fs.String("model", "gemma3:12b", "Ollama model to use") |
| 21 | + numCtx := fs.Int("ctx", 16384, "Context window size") |
| 22 | + verbose := fs.Bool("v", false, "Print thought messages as the agent reasons") |
| 23 | + fs.Parse(os.Args[2:]) |
| 24 | + |
| 25 | + targetPath := fs.Arg(0) |
| 26 | + if targetPath == "" { |
| 27 | + return fmt.Errorf("usage: beago analyze [flags] <path>") |
| 28 | + } |
| 29 | + |
| 30 | + storage, err := moderncsqlite.New(ctx, db) |
| 31 | + if err != nil { |
| 32 | + return fmt.Errorf("storage init: %w", err) |
| 33 | + } |
| 34 | + defer storage.Close() |
| 35 | + |
| 36 | + llm := ollama.New(ollama.Model{ |
| 37 | + Model: *model, |
| 38 | + Options: ollama.Options{NumCtx: *numCtx}, |
| 39 | + Stream: false, |
| 40 | + Format: ollama.JSON, |
| 41 | + }) |
| 42 | + |
| 43 | + // These goTools are specifically designed for Golang. |
| 44 | + goTools := []agents.Tool{ |
| 45 | + tools.GitDiff{}, |
| 46 | + tools.ListDeclarations{}, |
| 47 | + tools.FindCalls{}, |
| 48 | + tools.FindUsages{}, |
| 49 | + tools.GetFunctionSignature{}, |
| 50 | + tools.GetStructFields{}, |
| 51 | + tools.RunGoBuild{}, |
| 52 | + tools.RunGoVet{}, |
| 53 | + } |
| 54 | + |
| 55 | + agent, err := agents.NewReAct(ctx, llm, goTools, storage) |
| 56 | + if err != nil { |
| 57 | + return fmt.Errorf("agent init: %w", err) |
| 58 | + } |
| 59 | + |
| 60 | + task := fmt.Sprintf(`You are a Go code reviewer. Your job is to check if newly written code follows the same style and best practices as the rest of this codebase. |
| 61 | +
|
| 62 | +Project root: %s |
| 63 | +Target file: %s |
| 64 | +
|
| 65 | +Follow these steps: |
| 66 | +1. Call GitDiff with the target file path to get the exact changes. |
| 67 | +2. Call ListDeclarations with the project root to find comparable existing code. |
| 68 | +3. Call GetFunctionSignature on functions from the diff and on similar functions found in step 2. |
| 69 | +4. Compare them. Report ONLY concrete deviations in: error handling style, naming conventions, doc comments, receiver naming, function signature shape. |
| 70 | +
|
| 71 | +Format your final answer exactly like this: |
| 72 | +
|
| 73 | +REVIEW <target file> |
| 74 | +
|
| 75 | +No issues found. |
| 76 | +
|
| 77 | +Or if there are deviations: |
| 78 | +
|
| 79 | +REVIEW <target file> |
| 80 | +
|
| 81 | +ISSUE: <short title> |
| 82 | + File: <file>:<line> |
| 83 | + Expected: <what the codebase does> |
| 84 | + Found: <what the new code does> |
| 85 | +
|
| 86 | +Report only concrete deviations. No prose outside this format.`, wd, targetPath) |
| 87 | + |
| 88 | + if err := agent.Task(ctx, task); err != nil { |
| 89 | + return fmt.Errorf("task: %w", err) |
| 90 | + } |
| 91 | + |
| 92 | + r := runner.New(agent, *verbose) |
| 93 | + if err := r.Run(ctx); err != nil { |
| 94 | + return fmt.Errorf("run: %w", err) |
| 95 | + } |
| 96 | + |
| 97 | + finalAnswer, err := agent.Answer() |
| 98 | + if errors.Is(err, agents.ErrNoFinalAnswer) { |
| 99 | + fmt.Println("No final answer found") |
| 100 | + return nil |
| 101 | + } |
| 102 | + if err != nil { |
| 103 | + return fmt.Errorf("answer: %w", err) |
| 104 | + } |
| 105 | + |
| 106 | + fmt.Println(finalAnswer) |
| 107 | + return nil |
| 108 | +} |
0 commit comments