mirror of
https://github.com/alibaba/open-code-review.git
synced 2026-07-09 17:28:58 +00:00
feat(review): add --preview flag to show files before running LLM review
This commit is contained in:
parent
834a4e7e3f
commit
e71fa597d8
5 changed files with 226 additions and 40 deletions
|
|
@ -105,6 +105,7 @@ type reviewOptions struct {
|
|||
background string // --background: optional requirement context
|
||||
concurrency int
|
||||
perFileTimeout int
|
||||
preview bool
|
||||
showHelp bool
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +125,7 @@ func parseReviewFlags(args []string) (reviewOptions, error) {
|
|||
a.IntVar(&opts.perFileTimeout, "timeout", 10, "concurrent task timeout in minutes")
|
||||
a.StringVar(&opts.audience, "audience", "human", "output audience: human (show progress) or agent (summary only)")
|
||||
a.StringVarP(&opts.background, "background", "b", "", "optional requirement/business context for the review")
|
||||
a.BoolVarP(&opts.preview, "preview", "p", false, "preview which files will be reviewed without running the LLM")
|
||||
|
||||
if err := a.Parse(args); err != nil {
|
||||
return opts, fmt.Errorf("parse flags: %w", err)
|
||||
|
|
@ -183,6 +185,10 @@ Examples:
|
|||
# Agent mode (summary only, no progress lines)
|
||||
ocr review --audience agent
|
||||
|
||||
# Preview which files will be reviewed
|
||||
ocr review --preview
|
||||
ocr review -c abc123 -p
|
||||
|
||||
Flags:
|
||||
--audience string output audience: human (show progress) or agent (summary only) (default "human")
|
||||
-b, --background string optional requirement/business context for the review
|
||||
|
|
@ -190,6 +196,7 @@ Flags:
|
|||
-f, --format string output format: text or json (default "text")
|
||||
--concurrency int max concurrent file reviews (default 8)
|
||||
--from string source ref to start diff from (e.g., 'main')
|
||||
-p, --preview preview which files will be reviewed without running the LLM
|
||||
--repo string root directory of the git repository (default: current dir)
|
||||
--rule string path to JSON file with system review rules
|
||||
--timeout int concurrent task timeout in minutes (default 10)
|
||||
|
|
|
|||
|
|
@ -162,9 +162,9 @@ func buildDiffLines(comment model.LlmComment) []suggestdiff.DiffLine {
|
|||
}
|
||||
|
||||
type jsonOutput struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Comments []model.LlmComment `json:"comments"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Comments []model.LlmComment `json:"comments"`
|
||||
Warnings []agent.AgentWarning `json:"warnings,omitempty"`
|
||||
}
|
||||
|
||||
|
|
@ -216,3 +216,65 @@ func outputJSONNoFiles() error {
|
|||
enc.SetIndent("", " ")
|
||||
return enc.Encode(out)
|
||||
}
|
||||
|
||||
func outputPreviewText(p *agent.DiffPreview) {
|
||||
if p.TotalFiles == 0 {
|
||||
fmt.Println("No files changed.")
|
||||
return
|
||||
}
|
||||
|
||||
maxPathLen := 0
|
||||
for _, e := range p.Entries {
|
||||
if len(e.Path) > maxPathLen {
|
||||
maxPathLen = len(e.Path)
|
||||
}
|
||||
}
|
||||
if maxPathLen < 20 {
|
||||
maxPathLen = 20
|
||||
}
|
||||
pathFmt := fmt.Sprintf("%%-%ds", maxPathLen)
|
||||
|
||||
fmt.Printf("\nPreview: %d file(s) changed | \033[32m+%d\033[0m \033[31m-%d\033[0m\n",
|
||||
p.TotalFiles, p.TotalInsertions, p.TotalDeletions)
|
||||
|
||||
if p.ReviewableCount > 0 {
|
||||
fmt.Printf("\n\033[1mWill review (%d):\033[0m\n", p.ReviewableCount)
|
||||
for _, e := range p.Entries {
|
||||
if !e.WillReview {
|
||||
continue
|
||||
}
|
||||
fmt.Printf(" %s "+pathFmt+" \033[32m+%-4d\033[0m \033[31m-%-4d\033[0m\n",
|
||||
statusBadge(e.Status), e.Path, e.Insertions, e.Deletions)
|
||||
}
|
||||
}
|
||||
|
||||
if p.ExcludedCount > 0 {
|
||||
fmt.Printf("\n\033[1mExcluded from review (%d):\033[0m\n", p.ExcludedCount)
|
||||
for _, e := range p.Entries {
|
||||
if e.WillReview {
|
||||
continue
|
||||
}
|
||||
fmt.Printf(" %s "+pathFmt+" \033[2m(%s)\033[0m\n",
|
||||
statusBadge(e.Status), e.Path, e.ExcludeReason)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func statusBadge(status string) string {
|
||||
switch status {
|
||||
case "added":
|
||||
return "\033[32m[A]\033[0m"
|
||||
case "modified":
|
||||
return "\033[33m[M]\033[0m"
|
||||
case "deleted":
|
||||
return "\033[31m[D]\033[0m"
|
||||
case "renamed":
|
||||
return "\033[36m[R]\033[0m"
|
||||
case "binary":
|
||||
return "\033[35m[B]\033[0m"
|
||||
default:
|
||||
return "[?]"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ func runReview(args []string) error {
|
|||
return fmt.Errorf("load rules: %w", err)
|
||||
}
|
||||
|
||||
if opts.preview {
|
||||
return runPreview(repoDir, opts, fileFilter)
|
||||
}
|
||||
|
||||
toolEntries, err := toolsconfig.Load(opts.toolConfigPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load tools: %w", err)
|
||||
|
|
@ -198,6 +202,24 @@ func requireGitRepo(dir string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func runPreview(repoDir string, opts reviewOptions, fileFilter *rules.FileFilter) error {
|
||||
ag := agent.New(agent.Args{
|
||||
RepoDir: repoDir,
|
||||
From: opts.from,
|
||||
To: opts.to,
|
||||
Commit: opts.commit,
|
||||
FileFilter: fileFilter,
|
||||
})
|
||||
|
||||
preview, err := ag.Preview()
|
||||
if err != nil {
|
||||
return fmt.Errorf("preview failed: %w", err)
|
||||
}
|
||||
|
||||
outputPreviewText(preview)
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildToolRegistry(collector *tool.CommentCollector, fr *tool.FileReader, diffMap map[string]string) tool.Registry {
|
||||
reg := tool.NewRegistry()
|
||||
reg.Register(tool.NewFileRead(fr))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue