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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,26 @@ Any command that needs the agent starts one, so nothing has to be started by han

`--json` prints the agent's own answer, for anything that wants to read it rather than look at it.

For a remote review, `--dir` (`-d`) selects the local checkout and
`--repository` (`-r`) supplies its `owner/name` identity. `--host` (`-H`)
selects the reviewer server; the CLI supplies the API path. Set
`SOURCEANT_REVIEW_TOKEN` for authentication and optionally
`SOURCEANT_REVIEW_HOST` as the default server.

```bash
sourceant review \
--dir /workspace/repo \
--repository acme/example \
--base "$(git -C /workspace/repo rev-parse HEAD~1)" \
--head "$(git -C /workspace/repo rev-parse HEAD)" \
--host https://review.example.com \
--option discovery-passes=3 \
--option evaluation-passes=2 \
--format json
```

`--option` (`-o`) can be repeated for different reviewer settings.

## Building

```bash
Expand Down
2 changes: 2 additions & 0 deletions internal/agent/reviews.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
type Ask struct {
Repository string `json:"repository"`
Against string `json:"against"`
Head string `json:"head,omitempty"`
Title string `json:"title"`
Description string `json:"description"`
Skills []string `json:"skills"`
Expand Down Expand Up @@ -106,6 +107,7 @@ type Read struct {
Summary Summary `json:"summary"`
Suggestions []Suggestion `json:"suggestions"`
Notes map[string]string `json:"notes"`
Execution json.RawMessage `json:"execution,omitempty"`
}

// Review is whether a checkout's work is ready to be proposed to anyone.
Expand Down
71 changes: 64 additions & 7 deletions internal/command/review.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ var (

func reviewCommand(opts *options) *cobra.Command {
var (
against string
title string
skills []string
noWait bool
noModel bool
against string
title string
skills []string
noWait bool
noModel bool
folderPath string
base string
head string
descriptionFile string
remote snapshotOptions
format string
)
command := &cobra.Command{
Use: "review [path]",
Expand All @@ -36,10 +42,49 @@ func reviewCommand(opts *options) *cobra.Command {
"it is committed or not, and say whether it is ready to propose.",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if format != "" && format != "json" && format != "text" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--format text sets opts.asJSON = false, silently flipping output to text even when the global --json flag was passed; this mutates shared options state for the rest of the process. Only override when the user explicitly asked for json, or reject the combination outright.

Suggested change
if format != "" && format != "json" && format != "text" {
if format != "" && format != "json" && format != "text" {
return fmt.Errorf("format must be json or text")
}
if format == "json" {
opts.asJSON = true
}

return fmt.Errorf("format must be json or text")
}
if format != "" {
opts.asJSON = format == "json"
}
folder := "."
if len(args) == 1 {
folder = args[0]
}
if folderPath != "" {
if len(args) != 0 {
return fmt.Errorf("use either --dir or a positional path")
}
folder = folderPath
}
if remote.endpoint != "" {
if against != "" || noWait || noModel || len(skills) != 0 || descriptionFile != "" {
return fmt.Errorf("remote snapshots require committed input and --pr-metadata for context")
}
remote.base, remote.head, remote.title = base, head, title
return remoteReview(cmd, opts, folder, remote)
}
if remote.repository != "" || remote.diffFile != "" || remote.metadataFile != "" || len(remote.configuration) != 0 {
return fmt.Errorf("snapshot options require --host or SOURCEANT_REVIEW_HOST")
}
if base != "" {
if against != "" {
return fmt.Errorf("use either --base or --against")
}
against = base
}
if head != "" && base == "" {
return fmt.Errorf("--head requires --base")
}
description := ""
if descriptionFile != "" {
content, err := os.ReadFile(descriptionFile)
if err != nil {
return fmt.Errorf("the description file could not be read")
}
description = string(content)
}
folder, err := filepath.Abs(folder)
if err != nil {
return err
Expand All @@ -50,8 +95,10 @@ func reviewCommand(opts *options) *cobra.Command {
return err
}
started, err := client.Review(cmd.Context(), agent.Ask{
Repository: repository,
Against: against,
Repository: repository,
Against: against,
Head: head,
Description: description,
// Named, so a list of reviews says where each came from.
Title: or(title, "From the terminal"),
Skills: skills,
Expand Down Expand Up @@ -99,6 +146,16 @@ func reviewCommand(opts *options) *cobra.Command {
command.Flags().StringArrayVar(&skills, "skill", nil, "Read it against this skill, repeatable")
command.Flags().BoolVar(&noWait, "no-wait", false, "Print the link and leave it running")
command.Flags().BoolVar(&noModel, "no-model", false, "Say what changed without judging it")
command.Flags().StringVarP(&folderPath, "dir", "d", "", "The local checkout directory to review")
command.Flags().StringVar(&base, "base", "", "Compare against this commit")
command.Flags().StringVar(&head, "head", "", "Require a clean checkout at this full commit SHA")
command.Flags().StringVar(&descriptionFile, "description-file", "", "Read the change description from this file")
command.Flags().StringVarP(&remote.endpoint, "host", "H", os.Getenv("SOURCEANT_REVIEW_HOST"), "The reviewer server URL, including scheme and optional port")
command.Flags().StringVarP(&remote.repository, "repository", "r", "", "Repository identity as owner/name")
command.Flags().StringVar(&remote.diffFile, "diff-file", "", "Use a patch matching the committed comparison")
command.Flags().StringVar(&remote.metadataFile, "pr-metadata", "", "Read title and body from pull request JSON")
command.Flags().StringArrayVarP(&remote.configuration, "option", "o", nil, "Remote review option as label=value, repeatable")
command.Flags().StringVar(&format, "format", "", "Output format: json or text")
return command
}

Expand Down
Loading
Loading