review

package
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 7, 2026 License: MIT Imports: 36 Imported by: 0

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-cli) 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-cli) 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 .entire/settings.json.

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 closest ancestor" — 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 detailView, the pure-function renderer for the alt-screen drill-in view. It renders one agent's live event buffer with header/footer chrome and pads to exactly termHeight lines so every frame has the same line count (avoids ghost rows in the Bubble Tea alt-screen frame 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.

Index

Constants

View Source
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

View Source
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.

View Source
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

func ClearPendingReviewMarker(ctx context.Context) error

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 the closest ancestor.

Empty sections are skipped (no triple-newline gaps). The scope clause is only added when cfg.ScopeBaseRef is non-empty.

func ConfirmFirstRunSetup

func ConfirmFirstRunSetup(ctx context.Context, out io.Writer) bool

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

func DecodeSkills(encoded string) ([]string, error)

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

func EncodeSkills(skills []string) (string, error)

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

func NewCommand(deps Deps) *cobra.Command

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

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

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 .entire/settings.json. 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

func SaveReviewConfig(ctx context.Context, review map[string]settings.ReviewConfig) error

SaveReviewConfig persists the review map into .entire/settings.json while preserving all other settings. A Load error means the file exists but is malformed — we must NOT silently overwrite it with an empty struct, or every unrelated setting the user had configured would be wiped. Return the error so the caller can surface it instead.

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

type AgentChoice struct {
	Name  string
	Label string
}

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

type DumpSink struct {
	W io.Writer
}

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 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) (ScopeStats, error)

ComputeScopeStats gathers the data formatScopeBanner needs. Used by CU6 to build the banner before launching agents.

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
}

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. KeyCtrlC in the dashboard calls this function (gated by a sync.Once in the model). 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) *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()).

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 and waits for the user to press any key to dismiss.

IMPORTANT: RunFinished blocks until the user dismisses (presses any key) so that post-run sinks (e.g. DumpSink) render their narrative AFTER the TUI has exited and the terminal is back in normal mode.

func (*TUISink) Start

func (s *TUISink) Start()

Start spawns the Bubble Tea program in its own goroutine. Must be called before any AgentEvent calls. Subsequent calls are no-ops.

func (*TUISink) Wait

func (s *TUISink) Wait()

Wait blocks until the Bubble Tea program exits. Safe to call after Start. If Start was never called, Wait returns immediately.

Directories

Path Synopsis
Package types defines the per-agent abstraction interfaces for `entire review`.
Package types defines the per-agent abstraction interfaces for `entire review`.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL