Documentation
¶
Overview ¶
Package review — see env.go for package-level rationale.
cmd.go provides NewCommand(), the cobra entry point for `entire review`. It routes through the new AgentReviewer / Sink / Run architecture for launchable agents (claude-code, codex, gemini) and falls back to RunMarkerFallback for non-launchable agents (cursor, opencode, factoryai-droid, copilot-cli).
Package review — see env.go for package-level rationale.
dump.go provides DumpSink, a Sink implementation that writes a per-agent narrative dump to an io.Writer after the run completes. AgentEvent is a no-op; events are read from RunSummary.AgentRuns[].Buffer in RunFinished.
Output format: each agent's block is composed as markdown (`# claude-code review`, with failure context in blockquotes/bold) and rendered through mdrender for terminal writers. Non-TTY writers receive raw markdown so pipelines can grep / pipe / save without ANSI escape codes.
Package review contains the env-var contract between `entire review` (which spawns the agent process) and the lifecycle hook (which adopts the session). These names are stable API; renaming any constant is a breaking change.
Design rationale: each spawned agent inherits its own copy of the process environment, so multi-tenant correctness (multiple worktrees, multi-agent runs) holds by construction — one agent's env vars cannot bleed into another agent's session. The lifecycle UserPromptSubmit hook reads these env vars to tag the in-flight session as a review session (Kind = "agent_review") and records which skills were run.
Package review — see env.go for package-level rationale.
marker_fallback.go provides the PendingReviewMarker type and its write/read/clear helpers, plus RunMarkerFallback which handles review for non-launchable agents (cursor, opencode, factoryai-droid, copilot-cli) — agents that don't (yet) implement AgentReviewer.
For launchable agents (claude-code, codex, gemini) the new architecture uses env-var handshake (env.go) + AgentReviewer.Start, and the lifecycle hook reads ENTIRE_REVIEW_* env vars off the spawned process — there is no marker-file adoption code path.
For non-launchable agents the marker is purely a record of what the user was asked to do: RunMarkerFallback writes it before printing manual-start guidance, and `entire attach --review <session-id>` (and its discovery shortcut `entire review attach`) reads the marker to tag a manual session after the fact. ReadPendingReviewMarker / ClearPendingReviewMarker are exported for that attach flow; nothing else reads the marker.
Package review — see env.go for package-level rationale.
multipicker.go provides spawn-time agent multi-selection and per-run prompt collection for multi-agent review runs. When 2+ launchable agents are configured AND the user has not passed --agent, the dispatch logic in cmd.go calls PickAgents to let the user choose a subset and optionally add a one-off prompt without editing settings.
Package review — see env.go for package-level rationale.
picker.go implements the interactive review skills picker and agent selection helpers. pickConfig presents a huh multi-select per installed agent and saves the selection to clone-local review preferences.
Package review — see env.go for package-level rationale.
prompt.go implements the shared prompt composer used by all per-agent reviewers. The scope clause pins agents to "commits unique to this branch vs the mainline base ref, plus uncommitted working-tree changes" — preventing the divergent-default problem where codex defaulted to origin/main...HEAD and claude defaulted to working-tree-only on the same invocation (regression class from #1018 commit b9ed9c074; enforced structurally here).
Package review — see env.go for package-level rationale.
run.go implements the single-agent orchestrator. CU8 generalizes to N agents with a sibling RunMulti that re-uses the same AgentRun classification and Sink fan-out.
Package review — see env.go for package-level rationale.
run_multi.go implements RunMulti, the N-agent orchestrator. It runs each AgentReviewer in its own goroutine under a shared context, fans their event streams into a single dispatch loop that calls each Sink serially (preserving the serial-dispatch contract from CU4), and aggregates the per-agent results into a single RunSummary.
Cancellation: when ctx is cancelled (typically by the cobra command's SIGINT handling), all per-agent reviewers see ctx.Err() via their individual Process implementations and exit. RunMulti waits for every goroutine to drain before returning, ensuring no AgentEvent calls fire after RunFinished.
Goroutine accounting for N agents:
- N forwarding goroutines: one per agent that started successfully, reads proc.Events() and sends tagged events to the fan-in channel.
- 1 channel-close goroutine: waits for all N forwarders via WaitGroup then closes the fan-in channel so the dispatch loop terminates.
- 1 dispatch goroutine (the caller's): ranges over fan-in, updates per-agent state, calls sinks serially. RunMulti does NOT spawn a separate dispatch goroutine — the fan-in loop runs on the RunMulti call stack, keeping the goroutine count low.
Package review — see env.go for package-level rationale.
scope.go implements scope detection for `entire review`. The scope is the git ref the review is bounded by: "commits unique to this branch vs <baseRef>". Pinning the scope at launch time prevents the divergent-default problem where different agents default to different comparison points (e.g. codex used origin/main...HEAD, claude used working-tree-only) on the same invocation.
Package review — see env.go for package-level rationale.
synthesis_prompt.go builds the LLM prompt that asks a configured summary provider to synthesize a unified verdict across N per-agent review reports. It is a pure-function composer with no I/O; all logic is testable without network calls or TTY state.
Package review — see env.go for package-level rationale.
synthesis_sink.go provides SynthesisSink, an opt-in Sink that prompts the user (y/N, default N) after all agents finish, then asks a configured summary provider to synthesize a unified verdict across the per-agent narratives. Skipped silently in non-TTY mode, on cancellation, or when fewer than 2 agents produced usable output.
Composition: appended AFTER DumpSink in TTY-mode sink slices, so the y/N prompt appears below the per-agent narrative dump.
Package review — see env.go for package-level rationale.
tui_detail.go provides the alt-screen drill-in renderer. The body content is produced by [eventLines] (one or more wrapped lines per event) and fed into a bubbles/v2/viewport on [reviewTUIModel]; this file's [detailFrame] is the pure-function chrome (header + body + footer) that wraps the viewport's pre-rendered output and pads to exactly termHeight lines so every frame has the same line count (avoids ghost rows in Bubble Tea's alt-screen diff).
Package review — see env.go for package-level rationale.
tui_model.go provides reviewTUIModel, the Bubble Tea Model for the review dashboard. The model renders a per-agent status table during the run and supports Ctrl+O drill-in mode for inspecting one agent's live event buffer on the alt screen.
Package review — see env.go for package-level rationale.
tui_sink.go provides TUISink, a Sink implementation that renders a Bubble Tea status dashboard during a review run and supports Ctrl+O drill-in mode for inspecting one agent's live event buffer. Used in interactive (TTY) runs; non-TTY runs use DumpSink instead.
Package review — see env.go for package-level rationale.
Index ¶
- Constants
- Variables
- func AppendReviewEnv(base []string, agentName string, cfg reviewtypes.RunConfig, prompt string) []string
- func BuildReviewPickerFields(agentName string, builtins []skilldiscovery.CuratedSkill, ...) []huh.Field
- func ClearPendingReviewMarker(ctx context.Context) error
- func ComposeReviewPrompt(cfg reviewtypes.RunConfig) string
- func ConfirmFirstRunSetup(ctx context.Context, out io.Writer) bool
- func DecodeSkills(encoded string) ([]string, error)
- func EncodeSkills(skills []string) (string, error)
- func MergePickerResults(existing map[string]settings.ReviewConfig, offered map[string]struct{}, ...) map[string]settings.ReviewConfig
- func NewCommand(deps Deps) *cobra.Command
- func PromptForAgent(ctx context.Context, eligible []AgentChoice) (string, error)
- func Run(ctx context.Context, reviewer reviewtypes.AgentReviewer, ...) (reviewtypes.RunSummary, error)
- func RunMarkerFallback(ctx context.Context, agentName string, cfg reviewtypes.RunConfig, ...) error
- func RunMulti(ctx context.Context, reviewers []reviewtypes.AgentReviewer, ...) (reviewtypes.RunSummary, error)
- func RunReviewConfigPicker(ctx context.Context, out io.Writer, ...) (map[string]settings.ReviewConfig, error)
- func SaveReviewConfig(ctx context.Context, review map[string]settings.ReviewConfig) error
- func SaveReviewFixAgent(ctx context.Context, agentName string) error
- func SelectReviewAgent(review map[string]settings.ReviewConfig, override string) (string, settings.ReviewConfig, error)
- func SplitSavedPicks(saved []string, builtins []skilldiscovery.CuratedSkill, ...) ([]string, []string)
- func VerifyConfiguredSkillsInstalled(ctx context.Context, ag agent.Agent, cfg settings.ReviewConfig) error
- func WritePendingReviewMarker(ctx context.Context, m PendingReviewMarker) error
- type AgentChoice
- type Deps
- type DumpSink
- type LocalReviewManifest
- type ManifestSource
- type PendingReviewMarker
- type PickedAgents
- type ScopeStats
- type SynthesisProvider
- type SynthesisSink
- type TUISink
Constants ¶
const ( // EnvSession is the review-session indicator. `entire review` sets this // to "1" on the spawned agent process; the lifecycle hook treats any // other value (including unset) as a normal coding session. Kept as a // sentinel string rather than a bool so future versions can carry // additional metadata in the value without breaking the contract. EnvSession = "ENTIRE_REVIEW_SESSION" // EnvAgent is the name of the agent spawned for the review (e.g. // "claude-code"). The lifecycle hook requires this to match the hook's // agent before tagging the session, preventing stale exported review env // from tagging sessions for a different agent. EnvAgent = "ENTIRE_REVIEW_AGENT" // EnvSkills is a JSON-encoded []string of skill invocations passed to the // agent verbatim (e.g. `["/pr-review-toolkit:review-pr","/test-auditor"]`). // Use EncodeSkills / DecodeSkills to round-trip the value safely. EnvSkills = "ENTIRE_REVIEW_SKILLS" // EnvPrompt is the full prompt text sent to the agent at review start. The // lifecycle hook stores this so the checkpoint records what the agent was // asked to review. EnvPrompt = "ENTIRE_REVIEW_PROMPT" // EnvStartingSHA is the git commit SHA that was HEAD when `entire review` // was invoked. The lifecycle hook requires this to match the session's // initial base_commit before tagging the session, so stale env from an old // HEAD does not mark a later normal session as a review. EnvStartingSHA = "ENTIRE_REVIEW_STARTING_SHA" )
Variables ¶
var ErrNoAgentsSelected = errors.New("no agents selected for review")
ErrNoAgentsSelected is returned when the user unchecks all agents. Caller should surface a clear error rather than running with zero agents.
var ErrPickerCancelled = errors.New("agent picker cancelled")
ErrPickerCancelled is returned when the user aborts the multi-select.
Functions ¶
func AppendReviewEnv ¶
func AppendReviewEnv(base []string, agentName string, cfg reviewtypes.RunConfig, prompt string) []string
AppendReviewEnv adds the ENTIRE_REVIEW_* env vars to base, returning the new slice. Used by per-agent reviewers in their AgentReviewer.Start implementations to propagate the review-session contract to spawned agent processes.
agentName must be the agent's stable registry key (e.g. "claude-code"). cfg carries skills and the starting SHA. prompt is the full composed prompt text (result of ComposeReviewPrompt).
Any pre-existing ENTIRE_REVIEW_* entries in base are stripped before the new values are appended. This handles nested invocations (an `entire review` run spawning another agent that calls `entire review`) and stale inheritance from a parent shell — the most-recent values must win, with no chance of duplicate keys whose precedence is implementation-defined.
func BuildReviewPickerFields ¶
func BuildReviewPickerFields( agentName string, builtins []skilldiscovery.CuratedSkill, discovered []agent.DiscoveredSkill, activeHints []skilldiscovery.InstallHint, previousPrompt string, builtinPicksOut, discoveredPicksOut *[]string, promptOut *string, ) []huh.Field
BuildReviewPickerFields composes the per-agent group fields for the review picker. Returns a slice of huh.Field in render order:
0: built-in commands (multiselect) OR note 1: installed plugin skills (multiselect) OR note 2: install hints (note with all active hint messages) — OMITTED if empty 3: additional instructions (text) — always present
func ClearPendingReviewMarker ¶
ClearPendingReviewMarker removes the marker. Missing file is not an error.
func ComposeReviewPrompt ¶
func ComposeReviewPrompt(cfg reviewtypes.RunConfig) string
ComposeReviewPrompt assembles the prompt sent to the agent. It joins the configured skill invocations, the always-prompt, the per-run prompt, and a scope clause that pins the agent to commits unique to the current branch vs cfg.ScopeBaseRef plus any uncommitted changes.
Empty sections are skipped (no triple-newline gaps). The scope clause is only added when cfg.ScopeBaseRef is non-empty.
func ConfirmFirstRunSetup ¶
ConfirmFirstRunSetup prints a banner framing the picker as first-run setup (rather than the review itself) and waits for the user to confirm. Returns false if the user cancels; caller should bail gracefully.
Signposting matters here because `entire review` with no config silently drops into the picker — users running the command to start a review can mistake the picker for the review. The banner + confirmation makes the setup phase explicit, and the trailing "running review now" line in the caller closes the loop on what comes next.
func DecodeSkills ¶
DecodeSkills deserialises a value previously produced by EncodeSkills. An empty string decodes to a nil slice (no skills configured). Any other value that is not valid JSON returns an error.
func EncodeSkills ¶
EncodeSkills serialises a slice of skill invocation strings to a JSON value suitable for storing in the ENTIRE_REVIEW_SKILLS environment variable. An empty or nil slice encodes to the literal string "[]".
func MergePickerResults ¶
func MergePickerResults(existing map[string]settings.ReviewConfig, offered map[string]struct{}, selected map[string]settings.ReviewConfig) map[string]settings.ReviewConfig
MergePickerResults combines the picker's output with existing review config entries that the picker did not surface. Agents in `offered` are fully controlled by the picker: if they appear in `selected` with a non-zero config the entry is set, otherwise the entry is removed. Agents not in `offered` keep their existing config untouched.
Exported so tests can drive it directly — the picker itself can't run headless.
func NewCommand ¶
NewCommand returns the `entire review` cobra command wired with the provided deps. Callers in the cli package pass a fully-populated Deps; tests pass a Deps with stub fields.
func PromptForAgent ¶
func PromptForAgent(ctx context.Context, eligible []AgentChoice) (string, error)
PromptForAgent renders the single-select agent picker shown when more than one eligible agent is configured. Returns the chosen agent name. Respects accessibility mode via newAccessibleForm.
func Run ¶
func Run( ctx context.Context, reviewer reviewtypes.AgentReviewer, cfg reviewtypes.RunConfig, sinks []reviewtypes.Sink, ) (reviewtypes.RunSummary, error)
Run executes a single-agent review. Events from the agent are forwarded to all sinks via AgentEvent as they arrive; on completion, RunFinished is called on each sink with the populated RunSummary.
Returns the summary plus the agent's terminal error (nil on clean exit, ctx.Err() on cancellation, an error wrapping *exec.ExitError on non-zero exit, or an agent-level error when the event stream reports failure). Callers can inspect both: the summary is always populated even on error.
func RunMarkerFallback ¶
func RunMarkerFallback(ctx context.Context, agentName string, cfg reviewtypes.RunConfig, worktreePath string, out io.Writer) error
RunMarkerFallback handles review for non-launchable agents (cursor, opencode, factoryai-droid, copilot-cli) by writing the pending-review marker file and printing manual-start guidance. The user is told to open the agent themselves and run the configured skills.
The marker is NOT auto-adopted by anything — the lifecycle hook reads ENTIRE_REVIEW_* env vars on the spawned process, not the marker file. For non-launchable agents the user starts the agent manually, so no env inheritance happens. The marker exists purely so that `entire attach --review <session-id>` (and its `entire review attach` shortcut) has a record of what the user was asked to review when tagging the session after the fact.
agentName must be the agent's registry key (e.g. "cursor"). cfg carries skills and the starting SHA. worktreePath scopes the marker so sessions in other worktrees don't claim it. out is the destination for user-facing guidance.
func RunMulti ¶
func RunMulti( ctx context.Context, reviewers []reviewtypes.AgentReviewer, cfg reviewtypes.RunConfig, sinks []reviewtypes.Sink, ) (reviewtypes.RunSummary, error)
RunMulti executes a multi-agent review. Each reviewer runs concurrently; events from N agents are funneled into a single dispatch loop that calls every sink's AgentEvent in arrival order from a single goroutine (serial-dispatch guarantee).
Returns the aggregated RunSummary plus the FIRST non-cancellation per-agent error encountered. Callers that want individual errors should inspect summary.AgentRuns[i].Err. The returned summary is always populated, even on error; sinks are always notified via RunFinished.
Cancellation propagates through ctx; each reviewer's Process honors it. When ctx is cancelled all per-agent goroutines will eventually drain and the channel will close; RunMulti waits for all before calling RunFinished.
func RunReviewConfigPicker ¶
func RunReviewConfigPicker(ctx context.Context, out io.Writer, getInstalled func(context.Context) []types.AgentName) (map[string]settings.ReviewConfig, error)
RunReviewConfigPicker presents a huh multi-select for each installed agent that has curated review skills, and saves the selection to clone-local review preferences. Previously-saved skills are pre-checked via huh.Option.Selected(true), mirroring how `entire enable` preserves prior selections in its own agent picker.
getInstalled is injected to avoid an import cycle with the cli package.
func SaveReviewConfig ¶
SaveReviewConfig persists the review map into clone-local preferences while preserving other review preferences. A load error means the preferences file exists but is malformed — we must NOT silently overwrite it with an empty struct, or every unrelated review preference would be wiped. Return the error so the caller can surface it instead.
func SaveReviewFixAgent ¶ added in v0.6.2
func SelectReviewAgent ¶
func SelectReviewAgent(review map[string]settings.ReviewConfig, override string) (string, settings.ReviewConfig, error)
SelectReviewAgent picks an agent from the configured review map.
If override is non-empty, returns the config for that agent or an error listing the configured alternatives. Otherwise returns the alphabetically first configured agent — deterministic but user-overridable via --agent.
func SplitSavedPicks ¶
func SplitSavedPicks(saved []string, builtins []skilldiscovery.CuratedSkill, discovered []agent.DiscoveredSkill) ([]string, []string)
SplitSavedPicks partitions a flat saved-skills list into the subset that matches built-in curated commands and the subset that matches discovered plugin skills. Skill names that match neither are dropped from both — they're preserved on the settings side via MergePickerResults when they belong to a picker-unaware agent entry.
func VerifyConfiguredSkillsInstalled ¶
func VerifyConfiguredSkillsInstalled(ctx context.Context, ag agent.Agent, cfg settings.ReviewConfig) error
VerifyConfiguredSkillsInstalled is the spawn-time backstop for the silent-failure vector. For each skill in cfg.Skills, check it's either a curated built-in or returned by the agent's SkillDiscoverer; fail with a user-facing error if any skill is missing. Empty Skills (prompt-only config) short-circuits to nil — a freeform prompt has no skill list to validate against.
func WritePendingReviewMarker ¶
func WritePendingReviewMarker(ctx context.Context, m PendingReviewMarker) error
WritePendingReviewMarker persists the marker. Overwrites any existing marker.
Types ¶
type AgentChoice ¶
AgentChoice is one row in the spawn-time picker. Name is the agent registry key (used for marker/override); Label is the picker-visible string ("<name> (N skills configured)" or "<name> (prompt-only)").
func ComputeEligibleConfigured ¶
func ComputeEligibleConfigured(s *settings.EntireSettings, installed []types.AgentName) []AgentChoice
ComputeEligibleConfigured returns the sorted list of agents that are both configured (non-zero ReviewConfig entry) AND have hooks installed. Only eligible agents are valid picker targets — spawning a review for an agent without hooks would silently drop the review metadata.
type Deps ¶
type Deps struct {
// GetAgentsWithHooksInstalled returns the registry names of all agents
// whose lifecycle hooks are installed in the current repo.
GetAgentsWithHooksInstalled func(ctx context.Context) []types.AgentName
// NewSilentError wraps an error so the cobra root does not double-print it.
NewSilentError func(err error) error
// PromptForAgentFn overrides the interactive agent picker. Nil means
// PromptForAgent is used (the real huh form). Tests inject a stub.
PromptForAgentFn func(ctx context.Context, eligible []AgentChoice) (string, error)
// MultiPickerFn overrides PickAgents for the multi-agent picker. Nil
// means PickAgents is used (the real huh form). Tests inject a stub.
MultiPickerFn func(ctx context.Context, eligible []AgentChoice) (PickedAgents, error)
// HeadHasReviewCheckpoint checks whether HEAD's checkpoint metadata
// includes a review session. Returns (true, infoString) if HasReview is set.
// Injected to avoid an import cycle: review → checkpoint → codex → review.
HeadHasReviewCheckpoint func(ctx context.Context) (bool, string)
// ReviewCheckpointContext returns best-effort checkpoint context for the
// branch review scope. Injected from the cli package because checkpoint
// readers cannot be imported here without cycling through agent reviewers.
ReviewCheckpointContext func(ctx context.Context, worktreeRoot string, scopeBaseRef string) string
// ReviewerFor maps an agent registry name to its AgentReviewer
// implementation. Returns nil for non-launchable agents (cursor, opencode,
// factoryai-droid, copilot-cli). Injected to break the import cycle:
// per-agent reviewer packages import review (for ComposeReviewPrompt /
// AppendReviewEnv), so review/cmd.go cannot import them back.
ReviewerFor func(agentName string) reviewtypes.AgentReviewer
// AttachCmd, when non-nil, is registered as the `review attach`
// subcommand. Callers in the cli package pass newReviewAttachCmd() here;
// tests pass nil to skip the subcommand.
AttachCmd *cobra.Command
// SynthesisProvider, when non-nil, enables the synthesis sink in TTY mode.
// Production wiring resolves the same provider entire explain uses.
// When nil, the synthesis sink is not appended and synthesis is unavailable.
SynthesisProvider SynthesisProvider
// PromptYN overrides the y/N confirmation form used by SynthesisSink.
// Nil means the real huh form is used (realPromptYN in synthesis_sink.go).
// Tests inject a stub to avoid TTY interactions.
PromptYN func(ctx context.Context, question string, def bool) (bool, error)
}
Deps collects the runtime-injectable hooks NewCommand needs from the parent cli package. Tests stub fields to drive branches that would otherwise require a real TTY or enabled repo. Production wiring is provided by buildReviewDeps in cmd/entire/cli/review_bridge.go and passed to NewCommand from root.go.
type DumpSink ¶
DumpSink writes per-agent narrative blocks to W after the run completes.
func (DumpSink) AgentEvent ¶
func (DumpSink) AgentEvent(_ string, _ reviewtypes.Event)
AgentEvent is intentionally a no-op. DumpSink renders post-run from the AgentRun.Buffer slices in RunFinished.
func (DumpSink) RunFinished ¶
func (s DumpSink) RunFinished(summary reviewtypes.RunSummary)
RunFinished writes a narrative block per agent, then a counts line.
type LocalReviewManifest ¶ added in v0.6.2
type LocalReviewManifest struct {
Version int `json:"version"`
WorktreePath string `json:"worktree_path"`
CreatedAt time.Time `json:"created_at"`
StartingSHA string `json:"starting_sha,omitempty"`
Sources []ManifestSource `json:"sources"`
AggregateOutput string `json:"aggregate_output,omitempty"`
}
LocalReviewManifest records one local `entire review` invocation. It lets `entire review --fix <session-id>` use a single session id as the lookup handle while still loading sibling agent outputs from the same review run.
type ManifestSource ¶ added in v0.6.2
type PendingReviewMarker ¶
type PendingReviewMarker struct {
AgentName string `json:"agent_name"`
Skills []string `json:"skills"`
// Prompt is the composed review prompt the agent will receive.
// Stored on the marker (rather than recomputed on adoption) so session
// metadata records exactly what the agent was asked to do.
Prompt string `json:"prompt,omitempty"`
StartingSHA string `json:"starting_sha"`
StartedAt time.Time `json:"started_at"`
WorktreePath string `json:"worktree_path,omitempty"`
}
PendingReviewMarker is written by `entire review` before instructing the user to open a non-launchable agent. The marker records which agent and skills should run so that `entire review attach` can tag the resulting session after the fact.
WorktreePath scopes the marker to the worktree `entire review` was invoked from: multiple worktrees in one repo share .git/entire-sessions/, so without this field any session in any worktree could race to claim the marker. A blank WorktreePath (pre-fix markers) falls back to the legacy unscoped behavior — any session can adopt.
func ReadPendingReviewMarker ¶
func ReadPendingReviewMarker(ctx context.Context) (PendingReviewMarker, bool, error)
ReadPendingReviewMarker returns the marker if one exists. ok=false with err=nil indicates "no pending review."
type PickedAgents ¶
type PickedAgents struct {
// Names contains the agent registry keys selected by the user,
// e.g. ["claude-code", "codex"]. Sorted alphabetically.
Names []string
// PerRun is optional textarea content; "" when the user skipped or cleared it.
PerRun string
}
PickedAgents is the result of PickAgents: the agents the user selected for this run, plus an optional per-run prompt to append to the composed review prompt for each agent.
func PickAgents ¶
func PickAgents(ctx context.Context, eligible []AgentChoice) (PickedAgents, error)
PickAgents shows a multi-select form populated from eligible (the agents that are both configured AND have an AgentReviewer), pre-checks all of them, and returns the user's selection plus an optional per-run prompt.
Returns ErrPickerCancelled if the user aborts. An empty selection (user unchecked all boxes) returns ErrNoAgentsSelected.
Requires len(eligible) >= 2; returns an error if the caller passes fewer than 2 choices — this function is for multi-agent flows only.
type ScopeStats ¶
type ScopeStats struct {
BaseRef string
CurrentBranch string // empty for detached HEAD
Commits int // commits unique to current branch vs BaseRef
FilesChanged int // files changed across those commits
Uncommitted int // uncommitted file changes in the working tree
}
ScopeStats summarises the scope of a review for the banner.
func ComputeScopeStats ¶
func ComputeScopeStats(ctx context.Context, repo *git.Repository, baseOverride string) (ScopeStats, error)
ComputeScopeStats gathers the data formatScopeBanner needs. Used to build the banner before launching agents.
baseOverride, if non-empty, bypasses the mainline auto-detection and is used as the scope base directly. This is the entry point for the `--base <ref>` command-line flag. The override is verified via `repo.ResolveRevision(plumbing.Revision(<ref>))` (matching the codebase pattern in explain.go:156) before being used; an unknown ref produces an error before any agents are spawned, so users learn about the typo immediately instead of after a 10-minute review run scoped against a default the flag was supposed to override.
type SynthesisProvider ¶
type SynthesisProvider interface {
// Synthesize takes the composed synthesis prompt and returns the
// verdict text. Errors are surfaced to the caller; SynthesisSink
// degrades gracefully on error rather than failing the run.
Synthesize(ctx context.Context, prompt string) (string, error)
}
SynthesisProvider abstracts the LLM call that produces the cross-agent verdict. Injected via Deps so tests can stub the provider call without a real API roundtrip. Production wiring (in review_bridge.go) calls into the same provider entire explain uses.
type SynthesisSink ¶
type SynthesisSink struct {
Provider SynthesisProvider
Writer io.Writer
InputTTY bool // true if stdin can prompt the user
PromptYN func(ctx context.Context, question string, def bool) (bool, error)
PerRunPrompt string // if non-empty, included in the synthesis prompt for context
RunContext context.Context // optional; nil falls back to context.Background()
ProviderTimeout time.Duration // optional; zero uses defaultSynthesisProviderTimeout
OnResult func(result string)
}
SynthesisSink composes a multi-agent verdict by calling a configured summary provider after the run finishes. AgentEvent is a no-op; all work happens in RunFinished.
func (SynthesisSink) AgentEvent ¶
func (SynthesisSink) AgentEvent(_ string, _ reviewtypes.Event)
AgentEvent is a no-op; SynthesisSink only acts in RunFinished.
func (SynthesisSink) RunFinished ¶
func (s SynthesisSink) RunFinished(summary reviewtypes.RunSummary)
RunFinished optionally synthesizes a cross-agent verdict.
Skip silently when:
- stdin isn't a TTY (s.InputTTY == false)
- the run was cancelled (summary.Cancelled)
- fewer than 2 agents produced usable output (status Succeeded or Failed with non-empty narrative buffer)
Otherwise prompt y/N (default N). On y: compose prompt, call provider, print response. On provider failure: print "synthesis unavailable: <err>" with the underlying error; user can still commit.
type TUISink ¶
type TUISink struct {
// contains filtered or unexported fields
}
TUISink is a Sink that renders a Bubble Tea dashboard. The orchestrator calls AgentEvent/RunFinished from a single goroutine (CU4 serial-dispatch contract); the sink translates each event into a tea.Msg and sends it via Program.Send. Bubble Tea's Send is thread-safe, but we never rely on that property — the serial-dispatch promise means Send is only called from the orchestrator's dispatch goroutine.
Cancellation: cancel is the same context.CancelFunc that controls the orchestrator's run context. The first KeyCtrlC in the dashboard fires this function (guarded by a sync.Once in the model) and switches the dashboard to a "Cancelling agents..." indicator while agents drain; a second KeyCtrlC force-quits without waiting. Out-of-TUI SIGINT routes through the cobra root's context, which cancels the same function — no parallel signal.Notify goroutine is needed here.
func NewTUISink ¶
func NewTUISink(agents []string, cancel context.CancelFunc, output io.Writer, input io.Reader) *TUISink
NewTUISink creates a TUISink wired to cancel for Ctrl+C handling. agents is the ordered list of agent names that will run; the dashboard pre-renders one row per agent so the user sees the full run shape from the first frame. output is the writer the Bubble Tea program renders into (typically cmd.OutOrStdout()). input is the reader Bubble Tea reads keypresses from (typically os.Stdin in production); tests must pass a non-TTY reader (e.g. bytes.NewReader(nil)) so Bubble Tea does not put the inherited terminal into raw mode and corrupt sibling test output.
tea.WithoutSignalHandler keeps SIGINT routing on the cobra root's existing handler (which cancels the run context), so the TUI's KeyCtrlC path and the OS signal path share a single cancel function with no race.
func (*TUISink) AgentEvent ¶
func (s *TUISink) AgentEvent(agent string, ev reviewtypes.Event)
AgentEvent (Sink interface): translate ev into a tea.Msg and Send it to the Bubble Tea program. Implements the serial-dispatch contract: the orchestrator calls this from a single goroutine.
Note: Send is safe to call from goroutines other than the TUI's update loop; Bubble Tea's implementation queues the message internally.
func (*TUISink) RunFinished ¶
func (s *TUISink) RunFinished(summary reviewtypes.RunSummary)
RunFinished (Sink interface): mark the run complete and send the final summary message. The TUI shows the dashboard one more frame with the terminal statuses, then waits for the user to dismiss it. After a run completes, dismissal requires an explicit exit key (q/Esc/Enter/Ctrl+C); Ctrl+O still drills into agent buffers for post-mortem inspection. Other keys are no-ops so users can navigate the completed run without accidentally dismissing.
IMPORTANT: RunFinished blocks until the user dismisses so that post-run sinks (e.g. DumpSink) render their narrative AFTER the TUI has exited and the terminal is back in normal mode.