Clawpatch Ignore Configuration Review

Codex·GPT-5.5·peyton-alt·4mo ago·592hr 24min·1 Checkpoint·2 file changes·+4·83.5K tokens

You are reviewing one semantic feature for clawpatch.

Return strict JSON only. No markdown fences.

Project: { "name": "cli", "detected": { "languages": [ "go" ], "frameworks": [], "packageManagers": [], "commands": { "typecheck": "go test ./...", "lint": null, "format": null, "test": "go test ./..." } } }

Feature: { "schemaVersion": 1, "featureId": "feat_cli-command_d6bd094fc3", "title": "Go command entire", "summary": "Go command package cmd/entire with 1 source file(s).", "kind": "cli-command", "source": "go-cmd", "confidence": "medium", "entrypoints": [ { "path": "cmd/entire/main.go", "symbol": "main", "route": null, "command": "entire" } ], "ownedFiles": [ { "path": "cmd/entire/main.go", "reason": "go package source" } ], "contextFiles": [ { "path": "cmd/entire/cli/activity_cmd.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/activity_render.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/activity_tui.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/activity_types.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/agent_group.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/aliascmd.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/api_client.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/attach.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/attach_transcript.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/auth.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/checkpoint_group.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/checkpoint_reader.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/clean.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/commit_message.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/config.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/constants.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/dispatch.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/dispatch_tui.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/dispatch_wizard.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/doctor.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/doctor_bundle.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/doctor_logs.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/errors.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" }, { "path": "cmd/entire/cli/explain.go", "reason": "imported package github.com/entireio/cli/cmd/entire/cli" } ], "tests": [], "tags": [ "go", "cli" ], "trustBoundaries": [ "user-input", "filesystem", "process-exec", "network" ], "status": "claimed", "lock": { "lockedByRunId": "20260519T175303-e13c5e", "lockedAt": "2026-05-19T17:53:03.057Z", "hostname": "Peytons-MacBook-Pro.local", "pid": 43571 }, "findingIds": [], "patchAttemptIds": [], "analysisHistory": [], "createdAt": "2026-05-19T17:52:59.936Z", "updatedAt": "2026-05-19T17:53:03.058Z" }

Review categories:

  • correctness bugs
  • security issues
  • race/concurrency bugs
  • data loss/corruption
  • resource leaks
  • bad error handling
  • permission/auth gaps
  • API contract mismatches
  • missing/weak tests
  • release/build hazards
  • maintainability risks with concrete impact

Inspect owned files, context files, and linked tests. Treat included tests as first-class evidence of intended behavior. If tests contradict a suspected bug, either skip it or downgrade confidence and explain the uncertainty. Avoid reporting behavior as a bug solely because a helper name implies a broader contract. Deduplicate sibling/root-cause issues: when the same bug pattern appears in multiple owned files, emit one finding with multiple evidence refs instead of separate one-off findings.

Avoid speculative low-evidence findings. Evidence must point at included files.

JSON shape: { "findings": [ { "title": "string", "category": "bug|security|performance|concurrency|api-contract|data-loss|test-gap|docs-gap|build-release|maintainability", "severity": "critical|high|medium|low", "confidence": "high|medium|low", "evidence": [{"path":"string","startLine":1,"endLine":1,"symbol":null,"quote":null}], "reasoning": "string", "reproduction": null, "recommendation": "string", "whyTestsDoNotAlreadyCoverThis": "string", "suggestedRegressionTest": "string or null", "minimumFixScope": "string" } ], "inspected": {"files":["string"],"symbols":["string"],"notes":["string"]} }

Files: --- cmd/entire/main.go package main

import ( "context" "errors" "fmt" "os" "os/signal" "runtime" "strings" "syscall"

)

func main() { // Create context that cancels on interrupt ctx, cancel := context.WithCancel(context.Background())

}

func showSuggestion(cmd *cobra.Command, err error) { // Print usage first (brew style) fmt.Fprint(cmd.OutOrStderr(), cmd.UsageString()) fmt.Fprintf(cmd.OutOrStderr(), "\nError: Invalid usage: %v\n", err) }

--- cmd/entire/cli/activity_cmd.go package cli

import ( "context" "fmt" "io" "net/url" "os" "sort" "strconv" "strings" "time"

)

const ( agentUnknown = "unknown" dateUnknown = "unknown" activityTimeframe = "last-month" activityLimit = 1000 )

// knownAgents maps normalized agent strings from the API to display IDs. // Used for the commit list, where per-checkpoint agent strings are free-form. // The /me/activity endpoint returns already-normalized canonical IDs. var knownAgents = map[string]string{ "claude": "claude", "claudecode": "claude", "gemini": "gemini", "geminicli": "gemini", "amp": "amp", "codex": "codex", "opencode": "opencode", "copilot": "copilot", "copilotcli": "copilot", "pi": "pi", "cursor": "cursor", "droid": "droid", "kiro": "kiro", }

func newActivityCmd() *cobra.Command { cmd := &cobra.Command{ Use: "activity", Short: "Show your activity overview", Long: "Display your activity overview, repository breakdown, and recent commits from entire.io", RunE: func(cmd *cobra.Command, _ []string) error { return runActivity(cmd.Context(), cmd.OutOrStdout(), cmd.ErrOrStderr()) }, } return cmd }

func runActivity(ctx context.Context, w, errW io.Writer) error { client, err := NewAuthenticatedAPIClient(false) if err != nil { fmt.Fprintln(errW, "Not logged in. Run 'entire login' to authenticate.") return NewSilentError(err) }

}

func runActivityStatic(ctx context.Context, w io.Writer, client *api.Client) error { activity, commits, err := fetchActivityData(ctx, client) if err != nil { return err }

}

// fetchActivityData fetches aggregated activity and commits concurrently. func fetchActivityData(ctx context.Context, client *api.Client) (*userActivityResponse, []userCommit, error) { var activity *userActivityResponse var commits []userCommit

}

func fetchActivity(ctx context.Context, client *api.Client) (*userActivityResponse, error) { q := url.Values{} q.Set("timezone", detectTimezone()) q.Set("timeframe", activityTimeframe) q.Set("limit", strconv.Itoa(activityLimit)) path := "/api/v1/me/activity?" + q.Encode()

}

func fetchCommits(ctx context.Context, client *api.Client) ([]userCommit, error) { path := fmt.Sprintf("/api/v1/me/commits?timeframe=%s&limit=%d", activityTimeframe, activityLimit) resp, err := client.Get(ctx, path) if err != nil { return nil, fmt.Errorf("GET commits: %w", err) } defer resp.Body.Close()

}

// detectTimezone returns a best-effort timezone name for the current host. // Order: $TZ → /etc/localtime symlink → time.Local → "UTC" as last resort. // A candidate that fails normalization is skipped (not forwarded, not coerced // to UTC), so a bogus $TZ on a correctly-configured box still yields the // system timezone from /etc/localtime. The server is the canonical authority // for what counts as a valid zone and falls back to UTC for anything it // doesn't recognize, so we only do enough validation to avoid sending // obvious garbage (paths, POSIX forms Go can't load, the "Local" sentinel). func detectTimezone() string { if tz := normalizeTimezone(os.Getenv("TZ")); tz != "" { return tz } if link, err := os.Readlink("/etc/localtime"); err == nil { if tz := normalizeTimezone(link); tz != "" { return tz } } if tz := normalizeTimezone(time.Local.String()); tz != "" { return tz } return "UTC" }

// normalizeTimezone returns a name Go can load as a time zone, or "" if the // input can't be resolved. It strips the POSIX ":" prefix and zoneinfo path // prefix, then requires time.LoadLocation to succeed. // // This is not strict IANA-only validation: Go's LoadLocation accepts legacy // aliases like EST5EDT, GMT0, and PST8PDT in addition to Area/Location // names. Those may or may not be canonically understood by the server — if // the server doesn't recognize one, it falls back to UTC on its end. We // accept that mild mis-bucketing risk as the price of a simple check that // catches the common failure modes (paths, unknown POSIX forms like UTC0, // typos, the "Local" sentinel). func normalizeTimezone(raw string) string { name := strings.TrimPrefix(raw, ":") const marker = "/zoneinfo/" if idx := strings.LastIndex(name, marker); idx >= 0 { name = name[idx+len(marker):] } if name == "" || name == "Local" { return "" } if _, err := time.LoadLocation(name); err != nil { return "" } return name }

func groupCommitsByDay(commits []userCommit) []commitDay { byDate := make(map[string][]userCommit) var dateOrder []string

}

func normalizeAgentString(s string) string { if s == "" { return agentUnknown }

}

// parseFlexibleTime tries RFC3339, then RFC3339Nano. func parseFlexibleTime(s string) (time.Time, error) { t, err := time.Parse(time.RFC3339, s) if err != nil { t, err = time.Parse(time.RFC3339Nano, s) if err != nil { return time.Time{}, fmt.Errorf("parse time %q: %w", s, err) } } return t, nil }

--- cmd/entire/cli/activity_render.go package cli

import ( "fmt" "io" "math" "os" "sort" "strconv" "strings" "time"

)

type activityStyles struct { colorEnabled bool width int

}

// getFullTerminalWidth returns the terminal width without the 80-char cap // used by other commands. Activity benefits from wide output for bar charts. func getFullTerminalWidth(w io.Writer) int { if f, ok := w.(*os.File); ok { if width, _, err := term.GetSize(int(f.Fd())); err == nil && width > 0 { //nolint:gosec // G115: uintptr->int is safe for fd return width } } for _, f := range []*os.File{os.Stdout, os.Stderr} { if f == nil { continue } if width, _, err := term.GetSize(int(f.Fd())); err == nil && width > 0 { //nolint:gosec // G115: uintptr->int is safe for fd return width } } return 80 }

func newActivityStyles(w io.Writer) activityStyles { useColor := shouldUseColor(w) width := getFullTerminalWidth(w)

}

func (s activityStyles) render(style lipgloss.Style, text string) string { if !s.colorEnabled { return text } return style.Render(text) }

func (s activityStyles) renderAgent(agentID, text string) string { if !s.colorEnabled { return text } display := agentDisplayMap[agentID] return lipgloss.NewStyle().Foreground(lipgloss.Color(display.Color)).Render(text) }

type agentDisplay struct { Label string Color string // ANSI 256 color code Char rune // block character for bar charts }

// Agent colors match the dark-mode CSS variables from entire.io (Tailwind 400-level). // Lipgloss resolves hex to the best representation for the terminal's color profile. var agentDisplayMap = map[string]agentDisplay{ "claude": {Label: "Claude Code", Color: "#fb923c", Char: '▓'}, // orange-400 "gemini": {Label: "Gemini", Color: "#60a5fa", Char: '▓'}, // blue-400 "amp": {Label: "Amp", Color: "#f87171", Char: '▓'}, // red-400 "codex": {Label: "Codex", Color: "#818cf8", Char: '▓'}, // indigo-400 "opencode": {Label: "OpenCode", Color: "#22d3ee", Char: '▓'}, // cyan-400 "copilot": {Label: "Copilot", Color: "#a78bfa", Char: '▓'}, // violet-400 "pi": {Label: "Pi", Color: "#fbbf24", Char: '▓'}, // amber-400 "cursor": {Label: "Cursor", Color: "#38bdf8", Char: '▓'}, // sky-400 "droid": {Label: "Droid", Color: "#f472b6", Char: '▓'}, // pink-400 "kiro": {Label: "Kiro", Color: "#c084fc", Char: '▓'}, // purple-400 "unknown": {Label: "Unknown", Color: "245", Char: '░'}, }

var agentOrder = []string{ "claude", "codex", "gemini", "amp", "opencode", "copilot", "pi", "cursor", "droid", "kiro", "unknown", }

func renderActivity(w io.Writer, sty activityStyles, stats contributionStats, repos []repoContribution, hourly []hourlyPoint, days []commitDay) { fmt.Fprintln(w) renderStatCards(w, sty, stats) fmt.Fprintln(w) renderContributionChart(w, sty, hourly, repos) fmt.Fprintln(w) renderRepoChart(w, sty, repos) fmt.Fprintln(w) renderCommitList(w, sty, days) }

func renderStatCards(w io.Writer, sty activityStyles, stats contributionStats) { cards := []struct { label string value string unit string desc string }{ {"THROUGHPUT", fmt.Sprintf("%.1f", stats.Throughput), "k", "Avg. tokens/checkpoint"}, {"ITERATION", fmt.Sprintf("%.1f", stats.Iteration), "x", "Avg sessions/checkpoint"}, {"CONTINUITY", fmt.Sprintf("%.1f", stats.ContinuityH), "h", "Peak session length"}, {"STREAK", strconv.Itoa(stats.Streak), " day", fmt.Sprintf("%d current", stats.CurrentStreak)}, }

}

func renderContributionChart(w io.Writer, sty activityStyles, hourly []hourlyPoint, repos []repoContribution) { renderDotChart(w, sty, hourly, repos) }

func renderDotChart(w io.Writer, sty activityStyles, hourly []hourlyPoint, repos []repoContribution) { agentTotals := make(map[string]int) total := 0 for _, r := range repos { total += r.Total for agent, count := range r.Agents { agentTotals[agent] += count } }

}

// renderBrailleChart is an alternative contribution chart using Unicode braille // characters for higher resolution. Swap renderDotChart → renderBrailleChart in // renderContributionChart to enable it. var _ = renderBrailleChart // keep compiled while inactive

func renderBrailleChart(w io.Writer, sty activityStyles, hourly []hourlyPoint, repos []repoContribution) { // Agent breakdown header + total agentTotals := make(map[string]int) total := 0 for _, r := range repos { total += r.Total for agent, count := range r.Agents { agentTotals[agent] += count } }

}

func renderRepoChart(w io.Writer, sty activityStyles, repos []repoContribution) { if len(repos) == 0 { return }

}

func renderAgentBar(sty activityStyles, agents map[string]int, maxCount, barWidth int) string { if maxCount == 0 { return strings.Repeat(" ", barWidth) }

}

func renderCommitList(w io.Writer, sty activityStyles, days []commitDay) { renderCommitListN(w, sty, days, 3) }

func renderCommitListN(w io.Writer, sty activityStyles, days []commitDay, maxDays int) { if len(days) == 0 { return }

}

func uniqueCommitAgents(c userCommit) []string { seen := make(map[string]struct{}) var result []string for _, cp := range c.Checkpoints { agents := cp.Agents // Fall back to singular Agent field when Agents slice is empty if len(agents) == 0 && cp.Agent != "" { agents = []string{cp.Agent} } for _, a := range agents { id := normalizeAgentString(a) if _, ok := seen[id]; !ok { seen[id] = struct{}{} result = append(result, id) } } } sort.Strings(result) return result }

func formatCommitDate(dateStr string) string { t, err := time.ParseInLocation("2006-01-02", dateStr, time.Local) if err != nil { return dateStr } now := time.Now().Local() today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) days := int(today.Sub(t).Hours() / 24)

}

func padOrTruncate(s string, width int) string { runes := []rune(s) if len(runes) > width { return string(runes[:width-1]) + "…" } return s + strings.Repeat(" ", width-len(runes)) }

func truncateDisplayWidth(s string, width int, tail string) string { if lipgloss.Width(s) <= width { return s } if width <= 0 { return "" } if lipgloss.Width(tail) >= width { return tail[:width] }

}

--- cmd/entire/cli/activity_tui.go package cli

import ( "bytes" "context" "fmt" "os" "strings"

)

// activityDataMsg is sent when API data has been fetched. type activityDataMsg struct { stats contributionStats repos []repoContribution hourly []hourlyPoint days []commitDay }

// activityErrMsg is sent when fetching fails. type activityErrMsg struct{ err error }

type activityModel struct { // Data (nil until loaded) stats *contributionStats repos []repoContribution hourly []hourlyPoint days []commitDay

}

func runActivityTUI(ctx context.Context, client *api.Client) error { sp := spinner.New() sp.Spinner = spinner.Dot sp.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))

}

func (m activityModel) fetchData() tea.Msg { //nolint:ireturn // bubbletea Cmd signature requires tea.Msg return activity, commits, err := fetchActivityData(m.ctx, m.client) if err != nil { return activityErrMsg{err: err} }

}

func (m activityModel) Init() tea.Cmd { return tea.Batch(m.spinner.Tick, m.fetchData) }

func (m activityModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case activityDataMsg: m.loading = false m.stats = &msg.stats m.repos = msg.repos m.hourly = msg.hourly m.days = msg.days if m.width > 0 { m = m.withViewport() } return m, nil

}

func (m activityModel) withViewport() activityModel { headerHeight := m.headerLineCount() vpHeight := m.height - headerHeight - 1 if vpHeight < 1 { vpHeight = 1 }

}

func (m activityModel) View() tea.View { v := tea.View{AltScreen: true} if m.loadErr != nil { v.SetContent(fmt.Sprintf("\n Failed to load activity: %s\n\n Press q to quit.\n", m.loadErr)) return v }

}

func (m activityModel) renderHeader() string { if m.stats == nil { return "" } var buf bytes.Buffer buf.WriteString("\n") renderStatCards(&buf, m.sty, *m.stats) buf.WriteString("\n") renderContributionChart(&buf, m.sty, m.hourly, m.repos) buf.WriteString("\n") renderRepoChart(&buf, m.sty, m.repos) buf.WriteString("\n") return buf.String() }

func (m activityModel) headerLineCount() int { return strings.Count(m.renderHeader(), "\n") }

func (m activityModel) renderCommits() string { var buf bytes.Buffer renderCommitListN(&buf, m.sty, m.days, -1) return buf.String() }

func (m activityModel) renderFooter() string { if !m.sty.colorEnabled { return "" } helpStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("241")) keyStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("245")).Bold(true) sep := helpStyle.Render(" · ")

}

func newActivityStylesWithWidth(width int, useColor bool) activityStyles { return activityStyles{ colorEnabled: useColor, width: width, bold: lipgloss.NewStyle().Bold(true), dim: lipgloss.NewStyle().Faint(true), label: lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Bold(true), value: lipgloss.NewStyle().Bold(true), unit: lipgloss.NewStyle().Foreground(lipgloss.Color("8")), desc: lipgloss.NewStyle().Foreground(lipgloss.Color("8")), repoNm: lipgloss.NewStyle().Foreground(lipgloss.Color("7")), commitH: lipgloss.NewStyle().Foreground(lipgloss.Color("8")), commitM: lipgloss.NewStyle().Bold(true), add: lipgloss.NewStyle().Foreground(lipgloss.Color("2")), del: lipgloss.NewStyle().Foreground(lipgloss.Color("1")), muted: lipgloss.NewStyle().Foreground(lipgloss.Color("8")), } }

func padLeft(n int) string { s := strings.Builder{} if n < 10 { s.WriteString(" ") } else if n < 100 { s.WriteString(" ") } fmt.Fprintf(&s, "%d", n) return s.String() }

--- cmd/entire/cli/activity_types.go package cli

// API response types for the /api/v1/me/* endpoints used by entire activity.

// activityAgentCounts maps the 11 canonical agent IDs to counts. // The API always populates every key (zero for absent agents). type activityAgentCounts map[string]int

// userActivityResponse is the API response for GET /api/v1/me/activity. type userActivityResponse struct { Stats activityStatsResponse json:"stats" HourlyContributions []hourlyPoint json:"hourly_contributions" Repos []repoContribution json:"repos" // DailyContributions is returned but unused by the CLI. }

type activityStatsResponse struct { Tasks int json:"tasks" Orchestration int json:"orchestration" // 0-100, percentage Iteration float64 json:"iteration" Throughput float64 json:"throughput" ContinuityHours float64 json:"continuity_hours" Streak int json:"streak" // scoped to timeframe CurrentStreak int json:"current_streak" // scoped to timeframe LifetimeStreak int json:"lifetime_streak" // last 365 days LifetimeCurrentStreak int json:"lifetime_current_streak" // last 365 days }

// userCommitCheckpoint is checkpoint info nested inside a commit. type userCommitCheckpoint struct { CheckpointID string json:"checkpoint_id" Prompt *string json:"prompt" Agent string json:"agent" Agents []string json:"agents" SessionCount int json:"session_count" TotalSteps int json:"total_steps" }

// userCommit represents a single commit returned by the commits API. type userCommit struct { CommitSHA string json:"commit_sha" CommitMsg *string json:"commit_message" CommitAuthorUsername *string json:"commit_author_username" CommitDate *string json:"commit_date" Additions int json:"additions" Deletions int json:"deletions" FilesChanged int json:"files_changed" Checkpoints []userCommitCheckpoint json:"checkpoints" RepoFullName string json:"repo_full_name" IsPrivate bool json:"is_private" CheckpointRepoFullName *string json:"checkpoint_repo_full_name" }

// userCommitsResponse is the API response for GET /api/v1/me/commits. type userCommitsResponse struct { Commits []userCommit json:"commits" Timeframe string json:"timeframe" UpdatedAt string json:"updated_at" }

// Computed types used for rendering.

type contributionStats struct { Tasks int Throughput float64 // avg tokens/checkpoint in thousands Iteration float64 // avg session_count per checkpoint ContinuityH float64 // peak session length in hours (max(steps)*2/60) Streak int // longest consecutive days (last 365) CurrentStreak int // current streak ending today (last 365) }

// repoContribution matches the API's repos[] shape. Agents is keyed by the // canonical agent ID (claude, gemini, …, unknown) with all 11 keys populated. type repoContribution struct { Repo string json:"repo" Total int json:"total" Agents activityAgentCounts json:"agents" }

// hourlyPoint matches the API's hourly_contributions[] shape. AgentID is a // canonical ID (no client-side normalization needed). type hourlyPoint struct { Date string json:"date" // "2006-01-02", in the caller's timezone Hour int json:"hour" AgentID string json:"agent" Value int json:"value" }

// commitDay groups commits by date for display. type commitDay struct { Date string Commits []userCommit }

--- cmd/entire/cli/agent_group.go package cli

import ( "context" "errors" "fmt" "io"

)

// newAgentGroupCmd builds entire agent. Replaces entire configure. func newAgentGroupCmd() *cobra.Command { cmd := &cobra.Command{ Use: "agent", Short: "Manage agent integrations (add, remove, list)", Long: `Manage agent integrations in this repository.

Commands: list Show installed and available agents add Install hooks for an agent remove Uninstall hooks for an agent

Examples: entire agent entire agent list entire agent add claude-code entire agent remove claude-code`, PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { if _, err := paths.WorktreeRoot(cmd.Context()); err != nil { return errors.New("not a git repository") } return nil }, RunE: func(cmd *cobra.Command, _ []string) error { return runAgentMenu(cmd.Context(), cmd.OutOrStdout()) }, }

}

func runAgentMenu(ctx context.Context, w io.Writer) error { opts := EnableOptions{Telemetry: true} if settings.IsSetUpAny(ctx) { return runManageAgents(ctx, w, opts, nil) } return runSetupFlow(ctx, w, opts) }

func newAgentListCmd() *cobra.Command { return &cobra.Command{ Use: "list", Short: "List installed and available agents", RunE: func(cmd *cobra.Command, _ []string) error { return runAgentList(cmd.Context(), cmd.OutOrStdout()) }, } }

func runAgentList(ctx context.Context, w io.Writer) error { installed := GetAgentsWithHooksInstalled(ctx) installedSet := make(map[types.AgentName]struct{}, len(installed)) for _, name := range installed { installedSet[name] = struct{}{} }

}

func newAgentAddCmd() *cobra.Command { var localDev bool var forceHooks bool

Examples: entire agent add claude-code entire agent add gemini`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { name := args[0] ag, err := agent.Get(types.AgentName(name)) if err != nil { printWrongAgentError(cmd.OutOrStdout(), name) return NewSilentError(errors.New("wrong agent name")) } opts := EnableOptions{ LocalDev: localDev, ForceHooks: forceHooks, Telemetry: true, } return setupAgentHooksNonInteractive(cmd.Context(), cmd.OutOrStdout(), ag, opts) }, }

}

func newAgentRemoveCmd() *cobra.Command { return &cobra.Command{ Use: "remove <agent-name>", Short: "Uninstall hooks for an agent", Long: `Uninstall hooks for the specified agent in this repository.

Examples: entire agent remove claude-code`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return runRemoveAgent(cmd.Context(), cmd.OutOrStdout(), args[0]) }, } }

--- cmd/entire/cli/aliascmd.go package cli

import "github.com/spf13/cobra"

// hideAsAlias marks cmd as a hidden top-level shortcut that prints a one-line // hint pointing at the canonical command. Cobra's Deprecated field renders the // hint to stderr on every invocation while keeping the command functional. func hideAsAlias(cmd *cobra.Command, canonical string) *cobra.Command { cmd.Hidden = true cmd.Deprecated = "use '" + canonical + "' instead" return cmd }

--- cmd/entire/cli/api_client.go package cli

import ( "errors" "fmt"

)

// NewAuthenticatedAPIClient creates an API client using the bearer token // from the CLI login flow. Returns an error if the user is not logged in. // Pass insecureHTTP=true to allow plain HTTP base URLs (for local development). func NewAuthenticatedAPIClient(insecureHTTP bool) (*api.Client, error) { token, err := auth.LookupCurrentToken() if err != nil { return nil, fmt.Errorf("lookup auth token: %w", err) } if token == "" { return nil, errors.New("not logged in (run 'entire login' first)") }

}

--- cmd/entire/cli/attach.go package cli

import ( "context" "errors" "fmt" "io" "log/slog" "os" "os/exec" "strings" "time"

)

// attachOptions carries optional flags for runAttach. Force is the original // flag; Review opts the attach into recording the session as an // agent_review in the checkpoint metadata. type attachOptions struct { Force bool // Review, when true, tags the attached session as a review. Skills are // resolved inside runAttach after the real agent is known (via session // state or transcript auto-detection), not at the cobra layer — the // --agent flag's default points at claude-code, which would otherwise // make a Gemini session incorrectly look up review.claude-code config. Review bool // ReviewSkillsOverride, when non-empty, declares which review skills were // run. Empty is valid: the session is still tagged as a review, with no // structured skills list. Ignored when Review=false. ReviewSkillsOverride []string // ReviewPromptOverride, when non-empty, is recorded instead of the // transcript's first user prompt. Used by entire review attach when a // pending-review marker has the exact prompt the user was asked to run. ReviewPromptOverride string }

func newAttachCmd() *cobra.Command { var ( force bool agentFlag string reviewFlag bool skillsFlag []string ) cmd := &cobra.Command{ Use: "attach <session-id>", Short: "Attach an existing agent session", Long: `Attach an existing agent session that wasn't captured by hooks.

This creates a checkpoint from the session's transcript and links it to the last commit. Use this when hooks failed to fire or weren't installed when the session started, or to attach a research session.

If the last commit already has a checkpoint, the session is added to it. Otherwise a new checkpoint is created.

Use --review to tag the attached session as an agent review. The first user prompt in the transcript is recorded as the review prompt. Pass --skills to declare which skills were actually run; omit to attach a review without a declared skills list.

Works with any registered agent, including external agents enabled via external_agents in settings. Run 'entire agent list' to see the full list.`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) != 1 { return cmd.Help() } if checkDisabledGuard(cmd.Context(), cmd.OutOrStdout()) { return nil } // Discover external agents so --agent <external-name> is recognized // and so auto-detection can find transcripts from external agents. external.DiscoverAndRegister(cmd.Context()) agentName := types.AgentName(agentFlag) opts := attachOptions{ Force: force, Review: reviewFlag, ReviewSkillsOverride: skillsFlag, } return runAttachSurfaceReviewErrors(cmd, args[0], agentName, opts) }, } cmd.Flags().BoolVarP(&force, "force", "f", false, "Skip confirmation and amend the last commit with the checkpoint trailer") cmd.Flags().StringVarP(&agentFlag, "agent", "a", string(agent.DefaultAgentName), "Agent that created the session (see 'entire agent list' for registered agents, including external)") cmd.Flags().BoolVar(&reviewFlag, "review", false, "Tag the attached session as an agent review") cmd.Flags().StringSliceVar(&skillsFlag, "skills", nil, "Optional: declare which review skills were run in this session. Only used with --review") return cmd }

// resolveReviewSkills returns the skills list to record on an // attach-as-review. Only the user's --skills flag counts: configured // settings.Review[agent] is the spawn-path default ("what I'd run if I // used 'entire review'"), not a claim about what actually happened in a // given manual session. Silently attaching configured skills would // misrepresent the session as having run skills it may not have. // // Empty is a valid result — the attach still tags the session as a // review via Kind + ReviewPrompt (the session's first user prompt). The // skills list is a queryable convenience, not the source of truth. func resolveReviewSkills(flagSkills []string) []string { if len(flagSkills) == 0 { return nil } return flagSkills }

// runAttachSurfaceReviewErrors wraps runAttach so review-mode errors reach // the user as clear stderr messages rather than generic cobra error output. // The non-review path preserves the existing runAttach return-err behavior. func runAttachSurfaceReviewErrors(cmd *cobra.Command, sessionID string, agentName types.AgentName, opts attachOptions) error { err := runAttach(cmd.Context(), cmd.OutOrStdout(), sessionID, agentName, opts) if err != nil && opts.Review { cmd.SilenceUsage = true fmt.Fprintln(cmd.ErrOrStderr(), err.Error()) return NewSilentError(err) } return err }

func runAttach(ctx context.Context, w io.Writer, sessionID string, agentName types.AgentName, opts attachOptions) error { // Initialize structured logger so logging.Warn/Info write to .entire/logs/ not stderr. if err := logging.Init(ctx, sessionID); err != nil { // Init failed — logging will use stderr fallback, non-fatal. _ = err } // Flush the 8KB buffered log writer on exit. Without this, any // Warn/Info calls during attach (including the overwrite tripwire) // get silently dropped when the process exits, matching the pattern // already used by resume/clean/reset/rewind/migrate/explain. defer logging.Close()

}

// writeAttachCheckpointV2 writes attach-created checkpoints into the v2 refs. func writeAttachCheckpointV2(ctx context.Context, repo *git.Repository, opts cpkg.WriteCommittedOptions) error { v2URL, err := remote.FetchURL(ctx) if err != nil { logging.Debug(ctx, "attach: using origin for v2 store fetch remote", slog.String("error", err.Error()), ) } v2Store := cpkg.NewV2GitStore(repo, v2URL) if err := v2Store.WriteCommitted(ctx, opts); err != nil { return fmt.Errorf("v2 write committed: %w", err) } return nil }

// getHeadCommit returns the HEAD commit object. func getHeadCommit(repo *git.Repository) (*object.Commit, error) { headRef, err := repo.Head() if err != nil { return nil, fmt.Errorf("failed to get HEAD: %w", err) } commit, err := repo.CommitObject(headRef.Hash()) if err != nil { return nil, fmt.Errorf("failed to get HEAD commit: %w", err) } return commit, nil }

// ensureCheckpointAvailable makes sure the checkpoint referenced by HEAD is // present locally before the attach writes to it. Without this guard, attach // would create a fresh session 0 under the same ID and overwrite the original // session data on push. // // Only the local branch counts — remote-tracking presence is not enough. // If only the remote-tracking ref exists, a subsequent WriteCommitted creates // a brand-new orphan local branch with an empty tree, which would clobber // the remote on push. // // Fast path: check local refs directly — no network. If missing, trigger the // metadata fetch fallback chain used by entire resume (which advances the // local ref on success) and re-check. Returns a possibly-freshly-opened repo // handle so go-git sees any newly fetched packfiles. func ensureCheckpointAvailable(ctx, logCtx context.Context, repo *git.Repository, checkpointID id.CheckpointID, isExistingCheckpoint bool) (*git.Repository, error) { if !isExistingCheckpoint { return repo, nil }

}

// refreshCheckpointRefs runs the resume-equivalent fetch chain for the storage // version we're about to write to. Returns a freshly-opened repo so go-git // sees any newly-fetched packfiles and ref updates. func refreshCheckpointRefs(ctx context.Context, v2Only bool) (*git.Repository, error) { if v2Only { _, repo, err := getV2MetadataTree(ctx) return repo, err } _, repo, err := getMetadataTree(ctx) return repo, err }

// checkpointPresentLocally reports whether the checkpoint already exists on // the local ref we would write to. For v1 / dual-write, that's the local // entire/checkpoints/v1 branch (remote-tracking alone is not enough — see // ensureCheckpointAvailable). For v2-only mode, it's the v2 /main ref, which // has no remote-tracking analog and is therefore already local-only by // construction. func checkpointPresentLocally(ctx context.Context, repo *git.Repository, checkpointID id.CheckpointID, v2Only bool) (bool, error) { if v2Only { v2URL, urlErr := remote.FetchURL(ctx) if urlErr != nil { logging.Debug(ctx, "attach: using origin for v2 store fetch remote", slog.String("error", urlErr.Error()), ) } summary, err := cpkg.NewV2GitStore(repo, v2URL).ReadCommitted(ctx, checkpointID) if err != nil { return false, err //nolint:wrapcheck // Caller wraps with checkpoint ID context } return summary != nil, nil }

}

// suggestCheckpointFetchCommand returns a git fetch command the user can // paste to pull the missing metadata ref. v2 refs live under refs/entire/ // (not refs/heads/), so they need an explicit fully-qualified refspec; // v1 lives on a regular branch and its short name is enough. func suggestCheckpointFetchCommand(ctx context.Context, v2Only bool) string { ref := "entire/checkpoints/v1:entire/checkpoints/v1" if v2Only { ref = paths.V2MainRefName + ":" + paths.V2MainRefName } if remote.Configured(ctx) { if url, err := remote.FetchURL(ctx); err == nil && url != "" { return fmt.Sprintf("git fetch %s %s", url, ref) } } return "git fetch origin " + ref }

func resolveCheckpointID(headCommit *object.Commit) (id.CheckpointID, bool) { existing := trailers.ParseAllCheckpoints(headCommit.Message) if len(existing) > 0 { return existing[len(existing)-1], true }

}

// saveAttachSessionState creates or updates the session state file for the attached session. // If existingState is non-nil, it is updated in place (avoids a redundant disk load). // reviewSkills is the resolved skills list when opts.Review is true; ignored otherwise. func saveAttachSessionState(ctx context.Context, repo *git.Repository, existingState *session.State, sessionID string, agentType types.AgentType, transcriptPath string, checkpointID id.CheckpointID, meta transcriptMetadata, tokenUsage *agent.TokenUsage, opts attachOptions, reviewSkills []string) error { stateStore, err := session.NewStateStore(ctx) if err != nil { return fmt.Errorf("failed to open session store: %w", err) }

}

func reviewPromptForAttach(meta transcriptMetadata, opts attachOptions) string { if opts.ReviewPromptOverride != "" { return opts.ReviewPromptOverride } return meta.FirstPrompt }

// validateAttachPreconditions checks session ID format and git repo state. // Returns the existing session state if the session is already tracked (nil if new). func validateAttachPreconditions(ctx context.Context, repo *git.Repository, sessionID string) (*session.State, error) { if err := validation.ValidateSessionID(sessionID); err != nil { return nil, fmt.Errorf("invalid session ID: %w", err) }

}

// resolveAgentAndTranscript resolves the agent and transcript path. // For existing sessions, resolves the agent from session state's AgentType. // For new sessions, uses the --agent flag with auto-detection fallback. func resolveAgentAndTranscript(ctx context.Context, w io.Writer, sessionID string, agentName types.AgentName, existingState *session.State) (agent.Agent, string, error) { ag, err := resolveAgent(existingState, agentName) if err != nil { return nil, "", err }

}

// resolveAgent resolves the agent to use ...[truncated]

--- cmd/entire/cli/attach_transcript.go package cli

import ( "encoding/json"

)

// transcriptMetadata holds metadata extracted from a single transcript parse pass. type transcriptMetadata struct { FirstPrompt string TurnCount int Model string }

// extractTranscriptMetadata parses transcript bytes once and extracts the first user prompt, // user turn count, and model name. Supports both JSONL (Claude Code, Cursor, OpenCode) and // Gemini JSON format. func extractTranscriptMetadata(data []byte) transcriptMetadata { var meta transcriptMetadata

}

--- cmd/entire/cli/auth.go package cli

import ( "context" "encoding/json" "errors" "fmt" "io" "net/http" "sort" "strings" "time"

)

// authTokenLister lists API tokens for the authenticated user. type authTokenLister func(ctx context.Context, token string) ([]api.Token, error)

// authTokenRevoker revokes a single API token by id. type authTokenRevoker func(ctx context.Context, callerToken, id string) error

// User-visible placeholder strings. Promoted to constants so tests and // production share a single source of truth. const ( placeholderDash = "-" lastUsedNever = "never" lastUsedJustNow = "just now" )

// requireSecureBaseURL enforces TLS unless insecureHTTPAuth is set. Every // command that sends a bearer token over the network (login, logout, // auth status/list/revoke) must call this so credentials don't leak over // plaintext HTTP without explicit opt-in. func requireSecureBaseURL(insecureHTTPAuth bool) error { if insecureHTTPAuth { return nil } if err := api.RequireSecureURL(api.BaseURL()); err != nil { return fmt.Errorf("base URL check: %w", err) } return nil }

// addInsecureHTTPAuthFlag attaches the hidden --insecure-http-auth flag used // by every authenticated command for local development. func addInsecureHTTPAuthFlag(cmd *cobra.Command, target *bool) { cmd.Flags().BoolVar(target, "insecure-http-auth", false, "Allow authentication over plain HTTP (insecure, for local development only)") if err := cmd.Flags().MarkHidden("insecure-http-auth"); err != nil { panic(fmt.Sprintf("hide insecure-http-auth flag: %v", err)) } }

func newAuthCmd() *cobra.Command { cmd := &cobra.Command{ Use: "auth", Short: "Manage authentication and API tokens", Long: "Authentication subcommands. Includes login, logout, status, listing tokens, and revoking tokens.", RunE: func(cmd *cobra.Command, _ []string) error { return cmd.Help() }, }

}

// --- status -----------------------------------------------------------------

func newAuthStatusCmd() *cobra.Command { var insecureHTTPAuth bool cmd := &cobra.Command{ Use: "status", Short: "Show authentication status", RunE: func(cmd *cobra.Command, _ []string) error { if err := requireSecureBaseURL(insecureHTTPAuth); err != nil { return err } return runAuthStatus(cmd.Context(), cmd.OutOrStdout(), auth.NewStore(), defaultListTokens, api.BaseURL()) }, } addInsecureHTTPAuthFlag(cmd, &insecureHTTPAuth) return cmd }

func defaultListTokens(ctx context.Context, token string) ([]api.Token, error) { return api.NewClient(token).ListTokens(ctx) //nolint:wrapcheck // ListTokens already wraps with action context }

func runAuthStatus(ctx context.Context, w io.Writer, store tokenStore, list authTokenLister, baseURL string) error { token, err := store.GetToken(baseURL) if err != nil { return fmt.Errorf("read keychain: %w", err) } if token == "" { fmt.Fprintf(w, "Not logged in to %s\n", baseURL) fmt.Fprintln(w, "Run 'entire login' to authenticate.") return nil }

}

// --- list -------------------------------------------------------------------

func newAuthListCmd() *cobra.Command { var jsonOut bool var insecureHTTPAuth bool cmd := &cobra.Command{ Use: "list", Short: "List active API tokens for the authenticated user", RunE: func(cmd *cobra.Command, _ []string) error { if err := requireSecureBaseURL(insecureHTTPAuth); err != nil { return err } return runAuthList(cmd.Context(), cmd.OutOrStdout(), auth.NewStore(), defaultListTokens, api.BaseURL(), jsonOut) }, } cmd.Flags().BoolVar(&jsonOut, "json", false, "Print tokens as JSON") addInsecureHTTPAuthFlag(cmd, &insecureHTTPAuth) return cmd }

func runAuthList(ctx context.Context, w io.Writer, store tokenStore, list authTokenLister, baseURL string, jsonOut bool) error { token, err := store.GetToken(baseURL) if err != nil { return fmt.Errorf("read keychain: %w", err) } if token == "" { return fmt.Errorf("not logged in to %s; run 'entire login' first", baseURL) }

}

// authListStyles holds the lipgloss styles for entire auth list. Mirrors the // approach in activity_render.go: keep style construction tied to color // detection, and render plain text when color is disabled. type authListStyles struct { colorEnabled bool

}

func newAuthListStyles(w io.Writer) authListStyles { useColor := shouldUseColor(w) s := authListStyles{colorEnabled: useColor} if !useColor { return s } s.header = lipgloss.NewStyle().Foreground(lipgloss.Color("8")).Bold(true) s.id = lipgloss.NewStyle().Foreground(lipgloss.Color("3")) // yellow s.name = lipgloss.NewStyle().Bold(true) s.value = lipgloss.NewStyle() // default fg s.dim = lipgloss.NewStyle().Faint(true) s.warning = lipgloss.NewStyle().Foreground(lipgloss.Color("3")) // yellow s.expired = lipgloss.NewStyle().Foreground(lipgloss.Color("1")) // red return s }

func (s authListStyles) render(style lipgloss.Style, text string) string { if !s.colorEnabled { return text } return style.Render(text) }

// renderAuthListTable prints a styled, column-aligned table of tokens. Column // padding is computed via lipgloss.Width — it strips ANSI escapes, so a styled // cell's visible width matches its plain text. tabwriter can't be used here // once cells contain ANSI codes. func renderAuthListTable(w io.Writer, sty authListStyles, tokens []api.Token, now time.Time) { headerCells := []string{"ID", "NAME", "SCOPE", "CREATED", "LAST USED", "EXPIRES"} header := make([]string, len(headerCells)) for i, h := range headerCells { header[i] = sty.render(sty.header, h) }

}

func writeRow(w io.Writer, cells []string, widths []int) { for i, c := range cells { fmt.Fprint(w, c) if i < len(cells)-1 { fmt.Fprint(w, strings.Repeat(" ", widths[i]-lipgloss.Width(c)+2)) } } fmt.Fprintln(w) }

func styleName(sty authListStyles, name string) string { if name == "" { return sty.render(sty.dim, placeholderDash) } return sty.render(sty.name, name) }

func styleLastUsed(sty authListStyles, lastUsed *string, now time.Time) string { if lastUsed == nil { return sty.render(sty.dim, lastUsedNever) } return sty.render(sty.value, formatAuthLastUsed(lastUsed, now)) }

func styleExpires(sty authListStyles, expiresAt string, now time.Time) string { formatted := formatAuthDate(expiresAt) switch classifyExpiresAt(expiresAt, now) { case expiresExpired: return sty.render(sty.expired, formatted) case expiresSoon: return sty.render(sty.warning, formatted) case expiresNormal: return sty.render(sty.value, formatted) } return sty.render(sty.value, formatted) }

func lastUsedSortKey(t api.Token) string { if t.LastUsedAt == nil { return "" } return *t.LastUsedAt }

// formatAuthDate renders an RFC3339 timestamp as YYYY-MM-DD in local time. func formatAuthDate(s string) string { if s == "" { return placeholderDash } if ts, err := time.Parse(time.RFC3339, s); err == nil { return ts.Local().Format("2006-01-02") } return s }

// formatAuthLastUsed renders a relative "last used" timestamp, with "yesterday" // and absolute-date branches that the shared formatRelativeDuration helper // doesn't cover. func formatAuthLastUsed(s string, now time.Time) string { if s == nil || s == "" { return lastUsedNever } ts, err := time.Parse(time.RFC3339, s) if err != nil { return s } delta := now.Sub(ts) switch { case delta < 0, delta >= 3024time.Hour: return ts.Local().Format("2006-01-02") case delta >= 24time.Hour && delta < 48time.Hour: return "yesterday" default: return formatRelativeDuration(delta) } }

type expiresState int

const ( expiresNormal expiresState = iota expiresSoon expiresExpired )

// classifyExpiresAt classifies an RFC3339 expires-at relative to now. Used to // color the EXPIRES column so tokens worth rotating stand out. func classifyExpiresAt(s string, now time.Time) expiresState { if s == "" { return expiresNormal } ts, err := time.Parse(time.RFC3339, s) if err != nil { return expiresNormal } delta := ts.Sub(now) switch { case delta <= 0: return expiresExpired case delta < 724time.Hour: return expiresSoon default: return expiresNormal } }

func fallback(s, alt string) string { if strings.TrimSpace(s) == "" { return alt } return s }

// --- revoke -----------------------------------------------------------------

func newAuthRevokeCmd() *cobra.Command { var revokeCurrent bool var insecureHTTPAuth bool cmd := &cobra.Command{ Use: "revoke [id]", Short: "Revoke an API token by id", Long: "Revoke a specific API token. Use --current to revoke the token used by this CLI (equivalent to 'entire logout').", Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { id := "" if len(args) == 1 { id = args[0] } if id == "" && !revokeCurrent { return cmd.Help() } if id != "" && revokeCurrent { return errors.New("cannot use both <id> and --current") } if err := requireSecureBaseURL(insecureHTTPAuth); err != nil { return err } return runAuthRevoke(cmd.Context(), cmd.OutOrStdout(), cmd.ErrOrStderr(), auth.NewStore(), defaultListTokens, defaultRevokeTokenByID, defaultRevokeCurrentToken, api.BaseURL(), id, revokeCurrent) }, } cmd.Flags().BoolVar(&revokeCurrent, "current", false, "Revoke the token used by this CLI and remove the local copy") addInsecureHTTPAuthFlag(cmd, &insecureHTTPAuth) return cmd }

func defaultRevokeTokenByID(ctx context.Context, callerToken, id string) error { return api.NewClient(callerToken).RevokeToken(ctx, id) //nolint:wrapcheck // RevokeToken already wraps with action context }

func runAuthRevoke( ctx context.Context, outW, errW io.Writer, store tokenStore, list authTokenLister, revokeByID authTokenRevoker, revokeCurrent revokeCurrentFunc, baseURL, id string, current bool, ) error { token, err := store.GetToken(baseURL) if err != nil { return fmt.Errorf("read keychain: %w", err) } if token == "" { return fmt.Errorf("not logged in to %s; run 'entire login' first", baseURL) }

}

--- cmd/entire/cli/checkpoint_group.go package cli

import ( "errors"

)

// newCheckpointGroupCmd builds the entire checkpoint parent command and // registers list/explain/rewind/search as children. func newCheckpointGroupCmd() *cobra.Command { cmd := &cobra.Command{ Use: "checkpoint", Aliases: []string{"cp", "checkpoints"}, Short: "Inspect, rewind, and search checkpoints", Long: `Operations on checkpoints — the persistent records of agent work tied to commits.

Commands: list List checkpoints on the current branch explain Explain a checkpoint, commit, or session rewind Browse and rewind to a checkpoint search Search checkpoints (semantic + keyword)

Examples: entire checkpoint list entire checkpoint explain <id|sha> entire checkpoint rewind --to <id> entire checkpoint search "fix login"`, PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { if _, err := paths.WorktreeRoot(cmd.Context()); err != nil { return errors.New("not a git repository") } return nil }, }

}

func newCheckpointSearchCmd() *cobra.Command { cmd := newSearchCmd() cmd.Hidden = false return cmd }

// newCheckpointListCmd wraps the existing branch-default list view. func newCheckpointListCmd() *cobra.Command { var sessionFlag string var noPagerFlag bool

Optionally filter by session ID with --session.`, RunE: func(cmd *cobra.Command, _ []string) error { if checkDisabledGuard(cmd.Context(), cmd.OutOrStdout()) { return nil } return runExplainBranchWithFilter(cmd.Context(), cmd.OutOrStdout(), noPagerFlag, sessionFlag) }, }

}

--- cmd/entire/cli/checkpoint_reader.go package cli

import ( "context" "log/slog"

)

type committedCheckpointReaderStores struct { v1Store *checkpoint.GitStore v2Store *checkpoint.V2GitStore reader checkpoint.CommittedListReader readMode checkpoint.CommittedReadMode }

type committedCheckpointReaderOptions struct { blobFetcher checkpoint.BlobFetchFunc fetchRemoteLog string }

func committedCheckpointReadMode(ctx context.Context) checkpoint.CommittedReadMode { return checkpoint.CommittedReadModeForOptions( settings.IsCheckpointsV2Enabled(ctx), settings.CheckpointsVersion(ctx), ) }

func newCommittedCheckpointReader(ctx context.Context, repo *git.Repository, opts committedCheckpointReaderOptions) (*committedCheckpointReaderStores, error) { v1Store := checkpoint.NewGitStore(repo) if opts.blobFetcher != nil { v1Store.SetBlobFetcher(opts.blobFetcher) }

}

func committedCheckpointReadUsesV2(mode checkpoint.CommittedReadMode) bool { return mode != checkpoint.CommittedReadV1 }

--- cmd/entire/cli/clean.go package cli

import ( "context" "errors" "fmt" "io" "io/fs" "os" "strings"

)

func cleanLongDescription(ctx context.Context) string { description := `Clean up Entire session data for the current HEAD commit.

By default, cleans session state and shadow branches for the current HEAD:

  • Session state files (.git/entire-sessions/<session-id>.json)
  • Shadow branch (entire/<commit-hash>-<worktree-hash>)

Use --all to clean all Entire session data across the repository:

  • All session state files (.git/entire-sessions/)

  • All shadow branches

  • Temporary files (.entire/tmp/)`

    s, err := settings.Load(ctx) if err == nil && s.IsCheckpointsV2Enabled() { description += fmt.Sprintf(`

  • Archived v2 full transcripts older than the configured %d-day retention window`, s.GetFullTranscriptGenerationRetentionDays()) }

    description += `

Use --session <id> to clean a specific session only.

Without --force, prompts for confirmation before deleting. Use --dry-run to preview what would be deleted without prompting.`

}

func newCleanCmd() *cobra.Command { var forceFlag bool var allFlag bool var dryRunFlag bool var sessionFlag string

}

// runCleanCurrentHead cleans session data for the current HEAD commit. func runCleanCurrentHead(ctx context.Context, cmd *cobra.Command, force, dryRun bool) error { strat := GetStrategy(ctx) w := cmd.OutOrStdout()

}

// previewCurrentHead shows what would be cleaned for the current HEAD. func previewCurrentHead(ctx context.Context, w io.Writer) error { repo, err := openRepository(ctx) if err != nil { return err }

}

// runCleanSession handles the --session flag: clean/reset a single session. // actionVerb is the capitalized verb (e.g., "Clean" or "Reset") and pastVerb // is the past tense (e.g., "cleaned" or "reset") used in user-facing messages. func runCleanSession(ctx context.Context, cmd *cobra.Command, strat *strategy.ManualCommitStrategy, sessionID string, force, dryRun bool, actionVerb, pastVerb string) error { // Verify the session exists state, err := strategy.LoadSessionState(ctx, sessionID) if err != nil { return fmt.Errorf("failed to load session: %w", err) } if state == nil { return fmt.Errorf("session not found: %s", sessionID) }

}

// runCleanAll cleans all session data across the repository. func runCleanAll(ctx context.Context, cmd *cobra.Command, force, dryRun bool) error { s, err := settings.Load(ctx) if err != nil { fmt.Fprintf(cmd.ErrOrStderr(), "Warning: failed to load settings: %v\n", err) s = &settings.EntireSettings{} }

}

// printSection prints a titled list of items if the slice is non-empty. func printSection(w io.Writer, title string, items []string) { if len(items) == 0 { return } fmt.Fprintf(w, "%s (%d):\n", title, len(items)) for _, item := range items { fmt.Fprintf(w, " %s\n", item) } fmt.Fprintln(w) }

// printResultSection prints a titled list with a leading newline, for post-deletion output. func printResultSection(w io.Writer, title string, items []string) { if len(items) == 0 { return } fmt.Fprintf(w, "\n%s (%d):\n", title, len(items)) for _, item := range items { fmt.Fprintf(w, " %s\n", item) } }

// runCleanAllWithItems is the core logic for cleaning all items. // Separated for testability — tests pass a cmd without a TTY and use force or dryRun to avoid prompts. func runCleanAllWithItems(ctx context.Context, cmd *cobra.Command, force, dryRun bool, items []strategy.CleanupItem, tempFiles []string) error { w := cmd.OutOrStdout() errW := cmd.ErrOrStderr() // Handle no items case if len(items) == 0 && len(tempFiles) == 0 { fmt.Fprintln(w, "No items to clean up.") return nil }

}

// cleanupItemIDs extracts IDs from a slice of CleanupItems. func cleanupItemIDs(items []strategy.CleanupItem) []string { ids := make([]string, len(items)) for i, item := range items { ids[i] = item.ID } return ids }

// listAllTempFiles returns all files in .entire/tmp/ without filtering. // Used by --all since those sessions are being deleted anyway. func listAllTempFiles(ctx context.Context) ([]string, error) { absDir, err := paths.AbsPath(ctx, paths.EntireTmpDir) if err != nil { return nil, fmt.Errorf("failed to resolve temp dir: %w", err) } root, err := os.OpenRoot(absDir) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, fmt.Errorf("failed to open root: %w", err) } defer root.Close()

}

// TempFileDeleteError contains a file name and the error that occurred during deletion. type TempFileDeleteError struct { File string Err error }

// deleteTempFiles removes all files in .entire/tmp/. // Uses os.Root to ensure deletions are confined to the temp directory. // Returns successfully deleted files and any failures with their error reasons. func deleteTempFiles(ctx context.Context, files []string) (deleted []string, failed []TempFileDeleteError) { absDir, err := paths.AbsPath(ctx, paths.EntireTmpDir) if err != nil { for _, file := range files { failed = append(failed, TempFileDeleteError{File: file, Err: err}) } return nil, failed } root, err := os.OpenRoot(absDir) if err != nil { for _, file := range files { failed = append(failed, TempFileDeleteError{File: file, Err: err}) } return nil, failed } defer root.Close()

}

// activeSessionsOnCurrentHead returns sessions on the current HEAD // that are in an active phase (ACTIVE). func activeSessionsOnCurrentHead(ctx context.Context) ([]*session.State, error) { repo, err := openRepository(ctx) if err != nil { return nil, err }

}

// itemWord returns "item" or "items" based on count. func itemWord(n int) string { if n == 1 { return "item" } return "items" }

--- cmd/entire/cli/commit_message.go package cli

import ( "fmt" "strings"

)

// generateCommitMessage creates a commit message from the user's original prompt. // If the prompt is empty or cleans to empty, falls back to "<agentType> session updates". func generateCommitMessage(originalPrompt string, agentType types.AgentType) string { if originalPrompt != "" { cleaned := cleanPromptForCommit(originalPrompt) if cleaned != "" { return cleaned } }

}

// cleanPromptForCommit cleans up a user prompt to make it suitable as a commit message // Uses a loop to remove all matching prefixes until none remain func cleanPromptForCommit(prompt string) string { cleaned := prompt

}

--- cmd/entire/cli/config.go package cli

import ( "context" "fmt" "strings"

)

// Package-level aliases to avoid shadowing the settings package with local variables named "settings". const ( EntireSettingsFile = settings.EntireSettingsFile EntireSettingsLocalFile = settings.EntireSettingsLocalFile )

// EntireSettings is an alias for settings.EntireSettings. type EntireSettings = settings.EntireSettings

// LoadEntireSettings loads the Entire settings from .entire/settings.json, // then applies any overrides from .entire/settings.local.json if it exists. // Returns default settings if neither file exists. // Works correctly from any subdirectory within the repository. func LoadEntireSettings(ctx context.Context) (*settings.EntireSettings, error) { s, err := settings.Load(ctx) if err != nil { return nil, fmt.Errorf("loading settings: %w", err) } return s, nil }

// SaveEntireSettings saves the Entire settings to .entire/settings.json. func SaveEntireSettings(ctx context.Context, s *settings.EntireSettings) error { if err := settings.Save(ctx, s); err != nil { return fmt.Errorf("saving settings: %w", err) } return nil }

// SaveEntireSettingsLocal saves the Entire settings to .entire/settings.local.json. func SaveEntireSettingsLocal(ctx context.Context, s *settings.EntireSettings) error { if err := settings.SaveLocal(ctx, s); err != nil { return fmt.Errorf("saving local settings: %w", err) } return nil }

// IsEnabled returns whether Entire is currently enabled. // Returns true by default if settings cannot be loaded. func IsEnabled(ctx context.Context) (bool, error) { s, err := settings.Load(ctx) if err != nil { return true, err //nolint:wrapcheck // already present in codebase } return s.Enabled, nil }

// GetStrategy returns the manual-commit strategy instance with blob fetching // enabled so that checkpoint reads work after treeless fetches. func GetStrategy(_ context.Context) *strategy.ManualCommitStrategy { s := strategy.NewManualCommitStrategy() s.SetBlobFetcher(FetchBlobsByHash) return s }

// GetLogLevel returns the configured log level from settings. // Returns empty string if not configured (caller should use default). // Note: ENTIRE_LOG_LEVEL env var takes precedence; check it first. func GetLogLevel() string { s, err := settings.Load(context.TODO()) //nolint:contextcheck // Called as a callback via SetLogLevelGetter, no ctx available if err != nil { return "" } return s.LogLevel }

// GetAgentsWithHooksInstalled returns names of agents that have hooks installed. func GetAgentsWithHooksInstalled(ctx context.Context) []types.AgentName { var installed []types.AgentName for _, name := range agent.List() { ag, err := agent.Get(name) if err != nil { continue } if hs, ok := agent.AsHookSupport(ag); ok && hs.AreHooksInstalled(ctx) { installed = append(installed, name) } } return installed }

// InstalledAgentDisplayNames returns user-facing display names for agents with hooks installed. func InstalledAgentDisplayNames(ctx context.Context) []string { installedNames := GetAgentsWithHooksInstalled(ctx) displayNames := make([]string, 0, len(installedNames)) for _, name := range installedNames { if ag, err := agent.Get(name); err == nil { displayNames = append(displayNames, string(ag.Type())) } } return displayNames }

// JoinAgentNames joins agent names into a comma-separated string. func JoinAgentNames(names []types.AgentName) string { strs := make([]string, len(names)) for i, n := range names { strs[i] = string(n) } return strings.Join(strs, ",") }

--- cmd/entire/cli/constants.go package cli

import "github.com/entireio/cli/cmd/entire/cli/paths"

// Note: Tool name constants (ToolWrite, ToolEdit, etc.) and FileModificationTools // have been moved to the agent/claudecode package.

// Directory paths - re-exported from paths package for convenience const ( EntireDir = paths.EntireDir EntireTmpDir = paths.EntireTmpDir EntireMetadataDir = paths.EntireMetadataDir )

--- cmd/entire/cli/dispatch.go package cli

import ( "context" "errors" "fmt" "io" "os"

)

var runDispatch = dispatchpkg.Run var renderDispatchMarkdown = dispatchpkg.RenderMarkdown var dispatchTerminalMode = interactive.IsTerminalWriter var runInteractiveDispatch = defaultRunInteractiveDispatch var renderTerminalMarkdown = defaultRenderTerminalMarkdown

func newDispatchCmd() *cobra.Command { var ( flagLocal bool flagSince string flagUntil string flagAllBranches bool flagRepos []string flagVoice string flagInsecureHTTPAuth bool )

Examples: entire dispatch entire dispatch --local --all-branches entire dispatch --repos entireio/cli entire dispatch --voice neutral`, RunE: func(cmd *cobra.Command, _ []string) error { var ( opts dispatchpkg.Options err error )

}

func runDispatchCommand(ctx context.Context, outW io.Writer, opts dispatchpkg.Options) error { if dispatchTerminalMode(outW) && !IsAccessibleMode() { markdown, err := runInteractiveDispatch(ctx, outW, opts) if err != nil { return err } rendered, err := renderTerminalMarkdown(outW, markdown) if err != nil { return err } if _, err := fmt.Fprint(outW, rendered); err != nil { return fmt.Errorf("write dispatch output: %w", err) } return nil }

}

func isTerminalStdin(file *os.File) bool { return term.IsTerminal(int(file.Fd())) //nolint:gosec // G115: uintptr->int is safe for fd }

func shouldRunDispatchWizard(flagCount int, stdinIsTerminal bool, stdoutIsTerminal bool) bool { return flagCount == 0 && stdinIsTerminal && stdoutIsTerminal }

func parseDispatchFlags( cmd *cobra.Command, flagLocal bool, flagSince string, flagUntil string, flagAllBranches bool, flagRepos []string, flagVoice string, flagInsecureHTTPAuth bool, ) (dispatchpkg.Options, error) { return resolveDispatchOptions( flagLocal, flagSince, flagUntil, flagAllBranches, flagRepos, flagVoice, flagInsecureHTTPAuth, func() (string, error) { return GetCurrentBranch(cmd.Context()) }, ) }

//nolint:wrapcheck // passthrough glue to keep CLI error text unchanged while option logic lives in dispatch package func resolveDispatchOptions( flagLocal bool, flagSince string, flagUntil string, flagAllBranches bool, flagRepos []string, flagVoice string, flagInsecureHTTPAuth bool, currentBranch func() (string, error), ) (dispatchpkg.Options, error) { return dispatchpkg.ResolveOptions( flagLocal, flagSince, flagUntil, flagAllBranches, flagRepos, flagVoice, flagInsecureHTTPAuth, currentBranch, ) }

--- cmd/entire/cli/dispatch_tui.go package cli

import ( "context" "errors" "fmt" "io" "strings"

)

type dispatchRenderResult struct { markdown string err error }

type dispatchStatusModel struct { ctx context.Context cancel context.CancelFunc spinner spinner.Model styles dispatchStatusStyles title string subtitle string details []string footer string width int height int run func(context.Context) (string, error) result dispatchRenderResult }

type dispatchStatusStyles struct { card lipgloss.Style title lipgloss.Style subtitle lipgloss.Style detail lipgloss.Style footer lipgloss.Style spinner lipgloss.Style }

type dispatchProgram interface { Run() (tea.Model, error) }

// newDispatchProgram is overridden by tests via assignment. Tests that mutate // it cannot use t.Parallel() — they would race each other's factory. // altScreen is unused in v2 (set on tea.View instead) but retained for backward // compatibility with existing test fakes. var newDispatchProgram = func(model tea.Model, outW io.Writer, _ bool) dispatchProgram { return tea.NewProgram(model, tea.WithOutput(outW)) }

func defaultRunInteractiveDispatch(ctx context.Context, outW io.Writer, opts dispatchpkg.Options) (string, error) { runCtx, cancel := context.WithCancel(ctx) defer cancel()

}

// defaultRenderTerminalMarkdown renders dispatch's LLM markdown output via // the shared mdrender palette. Always renders (no TTY check) — dispatch's // existing behavior is to emit ANSI codes even when redirected so that // entire dispatch | less -R still shows colors. func defaultRenderTerminalMarkdown(w io.Writer, markdown string) (string, error) { return mdrender.Render(markdown, getTerminalWidth(w), termenv.HasDarkBackground()) //nolint:wrapcheck // mdrender already wraps glamour's errors with package context }

func newDispatchStatusModel( w io.Writer, opts dispatchpkg.Options, run func(context.Context) (string, error), ) dispatchStatusModel { ss := newStatusStyles(w) styles := newDispatchStatusStyles(ss) sp := spinner.New(spinner.WithSpinner(spinner.MiniDot)) if ss.colorEnabled { sp.Style = styles.spinner }

}

func newDispatchStatusStyles(ss statusStyles) dispatchStatusStyles { styles := dispatchStatusStyles{ card: lipgloss.NewStyle(), title: lipgloss.NewStyle().Bold(true), subtitle: lipgloss.NewStyle(), detail: lipgloss.NewStyle(), footer: lipgloss.NewStyle(), spinner: lipgloss.NewStyle().Bold(true), } if !ss.colorEnabled { return styles }

}

func dispatchStatusDetails(opts dispatchpkg.Options) []string { scope := "Scope: current repo" if len(opts.RepoPaths) > 0 { scope = "Scope: " + strings.Join(opts.RepoPaths, ", ") }

}

func (m dispatchStatusModel) Init() tea.Cmd { return tea.Batch(m.spinner.Tick, m.runDispatch()) }

func (m dispatchStatusModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height return m, nil case spinner.TickMsg: var cmd tea.Cmd m.spinner, cmd = m.spinner.Update(msg) return m, cmd case dispatchRenderResult: m.result = msg return m, tea.Quit case tea.KeyPressMsg: if key.Matches(msg, keys.Quit) || key.Matches(msg, keys.Back) { if m.cancel != nil { m.cancel() } m.result.err = errDispatchCancelled return m, tea.Quit } } return m, nil }

func (m dispatchStatusModel) View() tea.View { cardWidth := min(max(m.width-8, 44), 76)

}

func (m dispatchStatusModel) runDispatch() tea.Cmd { return func() tea.Msg { markdown, err := m.run(m.ctx) return dispatchRenderResult{markdown: markdown, err: err} } }

func clearDispatchInlineView(w io.Writer, view string) { lineCount := renderedLineCount(view) for range lineCount { _, _ = io.WriteString(w, "\x1b[1A\x1b[2K\r") //nolint:errcheck // terminal escape sequence, ignore write errors } }

func renderedLineCount(view string) int { if view == "" { return 0 } return strings.Count(view, "\n") + 1 }

--- cmd/entire/cli/dispatch_wizard.go package cli

import ( "context" "errors" "fmt" "os" "os/exec" "path/filepath" "sort" "strings" "sync"

)

var errDispatchCancelled = errors.New("dispatch cancelled") var listDispatchWizardRepos = discoverAuthenticatedDispatchWizardRepos var listDispatchWizardRepoResources = defaultListDispatchWizardRepoResources var resolveDispatchWizardTopLevel = resolveGitTopLevel var getDispatchWizardCurrentBranch = GetCurrentBranch var runDispatchWizardForm = func(form *huh.Form) error { return form.Run() }

func defaultListDispatchWizardRepoResources(ctx context.Context) ([]api.Repository, error) { client, err := NewAuthenticatedAPIClient(false) if err != nil { return nil, err } repos, err := client.ListRepositories(ctx, api.RepositorySortRecent) if err != nil { return nil, fmt.Errorf("list dispatch repos: %w", err) } return repos, nil }

const ( dispatchWizardRepoDiscoveryConcurrencyLimit = 8

)

type dispatchWizardState struct { modeChoice string timeWindowPreset string localBranchMode string currentBranch string currentBranchErr error selectedRepos []string voicePreset string voiceCustom string confirmRun bool }

func newDispatchWizardState() dispatchWizardState { return dispatchWizardState{ modeChoice: dispatchWizardModeLocal, timeWindowPreset: "7d", localBranchMode: dispatchWizardBranchCurrent, voicePreset: "neutral", confirmRun: true, } }

func (s dispatchWizardState) isLocal() bool { return s.modeChoice != dispatchWizardModeServer }

func (s dispatchWizardState) voiceValue() string { switch strings.TrimSpace(s.voicePreset) { case "marvin": return "marvin" case dispatchWizardVoiceCustom: if value := strings.TrimSpace(s.voiceCustom); value != "" { return value } } return "neutral" }

func (s dispatchWizardState) showCustomVoiceInput() bool { return strings.TrimSpace(s.voicePreset) == dispatchWizardVoiceCustom }

func (s dispatchWizardState) selectedReposList() []string { return normalizeDispatchWizardSelections(s.selectedRepos) }

func (s dispatchWizardState) resolveCloudRepos() []string { if s.isLocal() { return nil } return s.selectedReposList() }

func (s dispatchWizardState) showRepoPicker() bool { return !s.isLocal() }

func (s dispatchWizardState) showLocalBranchMode() bool { return s.isLocal() }

func (s dispatchWizardState) resolve() (dispatchpkg.Options, error) { allBranches := s.isLocal() && s.localBranchMode == dispatchWizardBranchAll if s.isLocal() && !allBranches && s.currentBranchErr != nil { return dispatchpkg.Options{}, fmt.Errorf("resolve current branch for local dispatch: %w", s.currentBranchErr) } opts, err := resolveDispatchOptions( s.isLocal(), s.timeWindowPreset, "", allBranches, s.resolveCloudRepos(), s.voiceValue(), false, func() (string, error) { return s.currentBranch, nil }, ) if err != nil { return dispatchpkg.Options{}, err } return opts, nil }

func (s dispatchWizardState) localBranchModeOptions() []huh.Option[string] { return []huh.Option[string]{ huh.NewOption("Current branch", dispatchWizardBranchCurrent), huh.NewOption("All branches", dispatchWizardBranchAll), } }

func buildDispatchWizardSummary(opts dispatchpkg.Options, scope string) string { if strings.TrimSpace(scope) == "" { scope = resolvedDispatchScope(opts) }

}

func resolvedDispatchScope(opts dispatchpkg.Options) string { if len(opts.RepoPaths) > 0 { return "repos:" + strings.Join(opts.RepoPaths, ", ") } return "current repo" }

func (s dispatchWizardState) previewScope(opts dispatchpkg.Options) string { if s.isLocal() { return resolvedDispatchScope(opts) } selectedRepos := s.selectedReposList() if len(selectedRepos) > 0 { return "repos:" + strings.Join(selectedRepos, ", ") } return resolvedDispatchScope(opts) }

func buildDispatchCommand(opts dispatchpkg.Options) string { return strings.Join(compactStrings([]string{ "entire dispatch", mapBoolToFlag(opts.Mode == dispatchpkg.ModeLocal, "--local"), renderStringFlag("--since", strings.TrimSpace(opts.Since)), mapBoolToFlag(opts.AllBranches, "--all-branches"), renderStringFlag("--repos", strings.Join(opts.RepoPaths, ",")), renderStringFlag("--voice", strings.TrimSpace(opts.Voice)), }), " ") }

func compactStrings(values []string) []string { result := make([]string, 0, len(values)) for _, value := range values { if value != "" { result = append(result, value) } } return result }

func mapBoolToFlag(enabled bool, flag string) string { if enabled { return flag } return "" }

func renderStringFlag(name string, value string) string { if value == "" { return "" } return name + " " + quoteShellValue(value) }

func quoteShellValue(value string) string { if value == "" { return "" } if strings.ContainsAny(value, " ,:\t") { return fmt.Sprintf("%q", value) } return value }

func runDispatchWizard(cmd *cobra.Command) (dispatchpkg.Options, error) { ctx := cmd.Context()

}

// newLazyOptions returns a func that runs loader once (under sync.Once) and // returns the cached result on subsequent calls. Safe for concurrent use. func newLazyOptions(loader func() []huh.Option[string]) func() []huh.Option[string] { var ( once sync.Once options []huh.Option[string] ) return func() []huh.Option[string] { once.Do(func() { options = loader() }) return options } }

// buildDispatchRepoOptions dedupes but preserves the caller's order so each // source can pick its own order: the API path surfaces recent-first, and the // local-discovery fallback surfaces the current repo first. func buildDispatchRepoOptions(slugs []string) []huh.Option[string] { options := make([]huh.Option[string], 0, len(slugs)) seen := make(map[string]struct{}, len(slugs)) for _, slug := range slugs { if slug == "" { continue } if _, ok := seen[slug]; ok { continue } seen[slug] = struct{}{} options = append(options, huh.NewOption(slug, slug)) } return options }

func normalizeDispatchWizardSelections(values []string) []string { normalized := make([]string, 0, len(values)) seen := make(map[string]struct{}, len(values)) for _, value := range values { value = strings.TrimSpace(value) if value == "" { continue } if _, ok := seen[value]; ok { continue } seen[value] = struct{}{} normalized = append(normalized, value) } return normalized }

func discoverLocalRepoRoots(ctx context.Context, currentRepo string) []string { rootSet := map[string]struct{}{currentRepo: {}} parent := filepath.Dir(currentRepo)

}

func discoverLocalRepoSlugs(ctx context.Context, currentRepo string) []string { repoRoots := discoverLocalRepoRoots(ctx, currentRepo) repoSlugs := make([]string, 0, len(repoRoots)) seenRepoSlugs := make(map[string]struct{}, len(repoRoots)) for _, repoRoot := range repoRoots { repoSlug := discoverRepoSlug(repoRoot) if repoSlug == "" { continue } if _, ok := seenRepoSlugs[repoSlug]; ok { continue } seenRepoSlugs[repoSlug] = struct{}{} repoSlugs = append(repoSlugs, repoSlug) } return repoSlugs }

func resolveGitTopLevel(ctx context.Context, path string) (string, error) { cmd := exec.CommandContext(ctx, "git", "-C", path, "rev-parse", "--show-toplevel") output, err := cmd.Output() if err != nil { return "", fmt.Errorf("git rev-parse --show-toplevel: %w", err) } return strings.TrimSpace(string(output)), nil }

// discoverAuthenticatedDispatchWizardRepos drops repos with zero checkpoints — // dispatching them would produce nothing. Server order (recent-first) is // preserved. func discoverAuthenticatedDispatchWizardRepos(ctx context.Context) ([]string, error) { repos, err := listDispatchWizardRepoResources(ctx) if err != nil { logging.Warn(ctx, "dispatch wizard repo list failed", "error", err) return nil, err }

}

func discoverRepoSlug(repoRoot string) string { repo, err := git.PlainOpenWithOptions(repoRoot, &git.PlainOpenOptions{DetectDotGit: true}) if err != nil { return "" } remote, err := repo.Remote("origin") if err != nil || len(remote.Config().URLs) == 0 { return "" } owner, repoName, err := searchpkg.ParseGitHubRemote(remote.Config().URLs[0]) if err != nil { return "" } return owner + "/" + repoName }

--- cmd/entire/cli/doctor.go package cli

import ( "context" "errors" "fmt" "io" "os" "path/filepath" "strconv" "time"

)

func newDoctorCmd() *cobra.Command { var forceFlag bool

Checks performed:

  1. Disconnected metadata branches: detects when local and remote entire/checkpoints/v1 branches share no common ancestor (caused by a previous bug). Fixes by cherry-picking local checkpoints onto remote tip.

When checkpoints_v2 is enabled: 2. Disconnected v2 /main ref: same detection for v2 refs under refs/entire/. 3. v2 ref existence: verifies /main and /full/current refs exist consistently. 4. v2 checkpoint counts: verifies /main and /full/current checkpoint counts are consistent. 5. v2 generation health: checks archived generations for valid metadata.

When Codex hooks are installed: 6. Codex hook trust: warn when hooks declared in .codex/hooks.json lack a trusted_hash entry in the user's Codex config (i.e. /hooks review hasn't run yet on this machine, or a newer entire release added a hook the user hasn't approved yet).

  1. Stuck sessions: sessions stuck in ACTIVE or ENDED phase that need cleanup.

A session is considered stuck if:

  • It is in ACTIVE phase with no interaction for over 1 hour
  • It is in ENDED phase with uncondensed checkpoint data on a shadow branch

For each stuck session, you can choose to:

  • Condense: Save session data to permanent storage
  • Discard: Remove the session state and shadow branch data
  • Skip: Leave the session as-is

Use --force to condense all fixable sessions without prompting. Sessions that can't be condensed will be discarded.`, PreRun: func(_ *cobra.Command, _ []string) { strategy.EnsureRedactionConfigured() }, RunE: func(cmd *cobra.Command, _ []string) error { return runSessionsFix(cmd, forceFlag) }, }

}

// stuckSession holds a session state along with diagnostic info. type stuckSession struct { State *strategy.SessionState Reason string ShadowBranch string HasShadowBranch bool CheckpointCount int FilesTouchedCount int }

func runSessionsFix(cmd *cobra.Command, force bool) error { var finalErr error

}

// classifySession determines if a session is stuck and returns diagnostic info. // Returns nil if the session is healthy. func classifySession(state *strategy.SessionState, repo *git.Repository, now time.Time) *stuckSession { // Determine shadow branch info shadowBranch := checkpoint.ShadowBranchNameForCommit(state.BaseCommit, state.WorktreeID) refName := plumbing.NewBranchReferenceName(shadowBranch) _, refErr := repo.Reference(refName, true) hasShadowBranch := refErr == nil

}

// displayStuckSession prints diagnostic info for a stuck session. func displayStuckSession(cmd *cobra.Command, ss stuckSession) { w := cmd.OutOrStdout()

}

// promptSessionAction asks the user what to do with a stuck session. func promptSessionAction(ss stuckSession) (string, error) { var action string

}

// discardSession removes session state and cleans up the shadow branch. func discardSession(ctx context.Context, ss stuckSession, _ *git.Repository, errW io.Writer) error { // Clear session state file if err := strategy.ClearSessionState(ctx, ss.State.SessionID); err != nil { return fmt.Errorf("failed to clear session state: %w", err) }

}

// checkDisconnectedMetadata detects and optionally repairs disconnected // local/remote metadata branches (the "empty-orphan bug"). func checkDisconnectedMetadata(cmd *cobra.Command, force bool) error { repo, err := openRepository(cmd.Context()) if err != nil { return fmt.Errorf("failed to open repository: %w", err) }

}

// checkDisconnectedV2Main detects and optionally repairs disconnected // local/remote v2 /main refs. func checkDisconnectedV2Main(cmd *cobra.Command, force bool) error { repo, err := openRepository(cmd.Context()) if err != nil { return fmt.Errorf("failed to open repository: %w", err) }

}

// checkV2GenerationHealth verifies that archived /full/* generations are well-formed. // Checks: generation.json exists and is valid, timestamps are sane, generation has checkpoints, // and generation sequence numbers are contiguous. func checkV2GenerationHealth(cmd *cobra.Command, repo *git.Repository) error { ctx := cmd.Context() w := cmd.OutOrStdout()

}

// checkV2CheckpointCounts verifies checkpoint count consistency between /main and /full/current. // /main is permanent (accumulates all checkpoints), /full/current holds only the current generation. // So main count >= full/current count. If full/current exceeds main, a dual-write partially failed. // Skips silently if either ref doesn't exist (already covered by checkV2RefExistence). func checkV2CheckpointCounts(cmd *cobra.Command, repo *git.Repository) error { ctx := cmd.Context() w := cmd.OutOrStdout()

}

// checkV2RefExistence verifies that v2 refs exist (or both are absent for a fresh repo). // One ref without the other suggests a partial initialization. func checkV2RefExistence(cmd *cobra.Command, repo *git.Repository) error { w := cmd.OutOrStdout()

}

// checkCodexHookTrust warns about two kinds of drift in the Codex hook // setup: // // 1. .codex/hooks.json is stale relative to what the CLI installs // today (e.g. a release added PostToolUse after the user enabled // Codex). Fix: re-run entire enable. // // 2. A declared hook lacks a trusted_hash entry in the user's Codex // config — either a fresh clone or a newer hook on the file the // user hasn't approved yet. Fix: open /hooks in Codex. // // Both checks are structural (file/key presence). Stays silent when // this repo doesn't have codex hooks installed or when we can't // resolve the worktree root. Warn-only. func checkCodexHookTrust(cmd *cobra.Command) { repoRoot, err := paths.WorktreeRoot(cmd.Context()) if err != nil { return } if _, statErr := os.Stat(filepath.Join(repoRoot, ".codex", "hooks.json")); statErr != nil { return }

...[truncated]

--- cmd/entire/cli/doctor_bundle.go package cli

import ( "archive/zip" "context" "errors" "fmt" "io" "os" "os/exec" "path" "path/filepath" "runtime" "strings" "time"

)

func newDoctorBundleCmd() *cobra.Command { var outFlag string var rawFlag bool

for attaching to bug reports.

The archive includes:

  • logs/ (operational logs from .entire/logs/)
  • settings/settings.json and settings/settings.local.json (if present)
  • git-status.txt, git-log.txt, git-remote.txt
  • version.txt with CLI version, Go version, OS/Arch

Redaction: By default the bundle redacts known secrets (API keys, credentialed URIs, database connection strings, bounded KEY=value credentials) from log files, settings JSON, and git command output before zipping. Pass --raw to skip redaction; use it only when support has explicitly requested an unredacted bundle.

By default the archive is written to a path inside the OS temp directory and that path is printed to stdout. Use --out to choose a specific path.`, RunE: func(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() repoRoot, err := paths.WorktreeRoot(ctx) if err != nil { cmd.SilenceUsage = true return errors.New("not a git repository") }

}

func writeDoctorBundle(ctx context.Context, repoRoot, outPath string, raw bool) error { out, err := os.OpenFile(outPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) //nolint:gosec // user-provided output path is intentional if err != nil { return fmt.Errorf("create bundle: %w", err) } if err := out.Chmod(0o600); err != nil { _ = out.Close() return fmt.Errorf("set bundle permissions: %w", err) } fileClosed := false defer func() { if !fileClosed { _ = out.Close() } }()

}

func versionInfoString() string { var sb strings.Builder fmt.Fprintf(&sb, "Entire CLI %s (%s)\n", versioninfo.Version, versioninfo.Commit) fmt.Fprintf(&sb, "Go: %s\n", runtime.Version()) fmt.Fprintf(&sb, "OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH) return sb.String() }

func addDirToZip(zw *zip.Writer, srcDir, archivePrefix string, raw bool) error { info, err := os.Stat(srcDir) if err != nil { if errors.Is(err, os.ErrNotExist) { return nil } return fmt.Errorf("stat %s: %w", srcDir, err) } if !info.IsDir() { return nil } walkErr := filepath.Walk(srcDir, func(path string, fi os.FileInfo, werr error) error { if werr != nil { return werr } if fi.IsDir() { return nil } rel, err := filepath.Rel(srcDir, path) if err != nil { return fmt.Errorf("rel: %w", err) } return addFileToZip(zw, path, zipEntryName(archivePrefix, rel), raw) }) if walkErr != nil { return fmt.Errorf("walk %s: %w", srcDir, walkErr) } return nil }

func zipEntryName(parts ...string) string { cleanParts := make([]string, 0, len(parts)) for _, part := range parts { if part == "" { continue } cleanParts = append(cleanParts, filepath.ToSlash(part)) } return path.Join(cleanParts...) }

func addFileToZip(zw *zip.Writer, src, archivePath string, raw bool) error { f, err := os.Open(src) //nolint:gosec // path comes from repo-internal walk if err != nil { if errors.Is(err, os.ErrNotExist) { return nil } return fmt.Errorf("open %s: %w", src, err) } defer f.Close()

}

func addStringToZip(zw *zip.Writer, archivePath, contents string, raw bool) error { entryName := zipEntryName(archivePath) w, err := zw.Create(entryName) if err != nil { return fmt.Errorf("zip create %s: %w", entryName, err) } body := contents if !raw { body = string(redactBundleEntry(entryName, []byte(contents))) } if _, err := io.WriteString(w, body); err != nil { return fmt.Errorf("zip write %s: %w", entryName, err) } return nil }

func addCommandOutput(ctx context.Context, zw *zip.Writer, archivePath, dir string, raw bool, name string, args ...string) error { cmd := exec.CommandContext(ctx, name, args...) cmd.Dir = dir out, err := cmd.CombinedOutput() if err != nil { out = append(out, []byte(fmt.Sprintf("\n[error: %v]\n", err))...) } // addStringToZip applies redaction when raw=false; pass through verbatim otherwise. return addStringToZip(zw, archivePath, string(out), raw) }

// redactBundleEntry chooses a redaction strategy per file shape. JSON / JSONL // entries get the field-aware redactor (preserves structure, skips ID fields); // everything else uses the byte-level scrubber. func redactBundleEntry(entryName string, contents []byte) []byte { ext := strings.ToLower(path.Ext(entryName)) if ext == ".json" || ext == ".jsonl" { out, err := redact.JSONLContent(string(contents)) if err == nil { return []byte(out) } // Fall through to plain redaction if the JSON redactor refuses (malformed input, etc.) } return redact.Bytes(contents) }

--- cmd/entire/cli/doctor_logs.go package cli

import ( "bufio" "context" "errors" "fmt" "io" "os" "path/filepath" "time"

)

func newDoctorLogsCmd() *cobra.Command { var tail int var follow bool

By default, prints the last 100 lines. Use --tail N to change. Use --follow to stream new lines as they are written (Ctrl+C to exit).`, RunE: func(cmd *cobra.Command, _ []string) error { repoRoot, err := paths.WorktreeRoot(cmd.Context()) if err != nil { cmd.SilenceUsage = true return errors.New("not a git repository") } logFile := filepath.Join(repoRoot, logging.LogsDir, "entire.log") if _, err := os.Stat(logFile); errors.Is(err, os.ErrNotExist) { fmt.Fprintf(cmd.OutOrStdout(), "No log file at %s yet.\n", logFile) return nil } if err := printTail(cmd.OutOrStdout(), logFile, tail); err != nil { return err } if !follow { return nil } return followFile(cmd.Context(), cmd.OutOrStdout(), logFile) }, }

}

func printTail(w io.Writer, path string, n int) error { f, err := os.Open(path) //nolint:gosec // path is .entire/logs/entire.log under repo root if err != nil { return fmt.Errorf("open log: %w", err) } defer f.Close()

}

func endsWithNewline(s string) bool { return len(s) > 0 && s[len(s)-1] == '\n' }

// readLastNLines reads the file as a stream and returns up to n trailing lines. // For typical log sizes this is fast enough; large files would benefit from a // reverse-seek implementation but the current logger rotates so files stay small. func readLastNLines(r io.Reader, n int) ([]string, error) { scanner := bufio.NewScanner(r) scanner.Buffer(make([]byte, 102464), 10241024) ring := make([]string, 0, n) for scanner.Scan() { line := scanner.Text() + "\n" if len(ring) < n { ring = append(ring, line) } else { ring = append(ring[1:], line) } } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("scan log: %w", err) } return ring, nil }

// followFile polls the log file for appended bytes. It exits cleanly when the // command's context is cancelled (Ctrl+C in a TTY). func followFile(ctx context.Context, w io.Writer, path string) error { f, err := os.Open(path) //nolint:gosec // path is .entire/logs/entire.log under repo root if err != nil { return fmt.Errorf("open log: %w", err) } defer f.Close()

}

--- cmd/entire/cli/errors.go package cli

// SilentError wraps an error to signal that the error message has already been // printed to the user. main.go checks for this type to avoid duplicate output. type SilentError struct { Err error }

func (e *SilentError) Error() string { return e.Err.Error() }

func (e *SilentError) Unwrap() error { return e.Err }

// NewSilentError creates a SilentError wrapping the given error. // Use this when you've already printed a user-friendly error message // and don't want main.go to print the error again. func NewSilentError(err error) *SilentError { return &SilentError{Err: err} }

--- cmd/entire/cli/explain.go package cli

import ( "context" "encoding/hex" "errors" "fmt" "io" "log/slog" "os" "os/exec" "runtime" "sort" "strconv" "strings" "time"

)

const defaultCheckpointSummaryTimeout = 5 * time.Minute

const ( pagerEnvVar = "PAGER" lessEnvVar = "LESS" lessPagerName = "less" lessRawControlEnv = "LESS=-R" windowsGOOS = "windows" )

var checkpointSummaryTimeout = defaultCheckpointSummaryTimeout

var generateTranscriptSummary = summarize.GenerateFromTranscript

// resolveSummaryTimeout picks the effective deadline for explain --generate // using the precedence: per-run flag > settings.summary_timeout_seconds > // package default. Zero or negative values at any layer mean "unset; consult // the next layer down" — matching SummaryTimeoutValue() semantics. // // Settings load failures are logged at debug and fall through to the default; // a parsing hiccup must not break summary generation. func resolveSummaryTimeout(ctx context.Context, flagSeconds int) time.Duration { if flagSeconds > 0 { return time.Duration(flagSeconds) * time.Second } s, err := settings.Load(ctx) if err != nil { logging.Debug(ctx, "summary timeout: settings load failed, using default", slog.String("error", err.Error())) return checkpointSummaryTimeout } if v := s.SummaryTimeoutValue(); v > 0 { return v } return checkpointSummaryTimeout }

// errCannotGenerateTemporaryCheckpoint is returned by runExplainCheckpoint when // --generate is requested for a target that does not match any committed // checkpoint. runExplainAuto uses errors.Is to detect this case and fall back // to resolving the target as a git commit ref. var errCannotGenerateTemporaryCheckpoint = errors.New("cannot generate summary for temporary checkpoint")

type explainCheckpointLookup struct { repo *git.Repository v1Store *checkpoint.GitStore v2Store *checkpoint.V2GitStore reader checkpoint.CommittedListReader readMode checkpoint.CommittedReadMode committed []checkpoint.CommittedInfo }

// generateOrRawLabel returns the user-facing verb for the action the user // requested, used in error messages when a commit target has no trailer. func generateOrRawLabel(generate bool) string { if generate { return "generate summary" } return "show raw transcript" }

// printNoTrailerMessage renders the friendly message shown when a resolved // commit has no Entire-Checkpoint trailer in read-only modes. Takes the // repo so the hash can be abbreviated to the minimum unique length for // this repo's object set (matching git's --abbrev behavior). func printNoTrailerMessage(w io.Writer, repo *git.Repository, hash plumbing.Hash) { styles := newStatusStyles(w) rows := []explainRow{ {Label: "commit", Value: abbreviateCommitHash(repo, hash)}, {Label: "reason", Value: "no Entire-Checkpoint trailer"}, {Label: "hint", Value: "this commit was not created during an Entire session,"}, {Label: "", Value: "or the trailer was removed"}, } fmt.Fprint(w, styles.renderFailure("No associated Entire checkpoint", rows)) }

// errAmbiguousCommitPrefix is returned by resolveCommitUnambiguous when a // hex prefix matches more than one commit. Callers use errors.Is to detect // this case and surface the full wrapped message verbatim. var errAmbiguousCommitPrefix = errors.New("ambiguous commit prefix")

// commitHashesWithPrefix enumerates all commit hashes in the repo whose // SHA starts with the given hex prefix. Returns nil when the storer is not // a *filesystem.Storage or the prefix isn't decodable as hex. // // Per PR review (discussion_r3113804961): the reviewer specifically // suggested repo.Storer.(*filesystem.Storage).HashesWithPrefix followed by // commit filtering. Using this primitive both in resolution (detect // ambiguous user input) and in display (dynamically abbreviate shown // hashes to the minimum unique length). func commitHashesWithPrefix(repo *git.Repository, prefix string) []plumbing.Hash { s, ok := repo.Storer.(*filesystem.Storage) if !ok { return nil } // Truncate to even length for byte-aligned hex decoding. evenHex := prefix[:len(prefix)&^1] decoded, err := hex.DecodeString(evenHex) if err != nil || len(decoded) == 0 { return nil } candidates, err := s.HashesWithPrefix(decoded) if err != nil { return nil } var commits []plumbing.Hash for _, h := range candidates { // HashesWithPrefix matches on even byte boundaries; filter the // dangling nybble for odd-length prefixes. if len(evenHex) != len(prefix) && !strings.HasPrefix(h.String(), prefix) { continue } if _, err := repo.CommitObject(h); err != nil { continue } commits = append(commits, h) } return commits }

// resolveCommitUnambiguous resolves a ref to a commit hash, returning // errAmbiguousCommitPrefix (and the matching hashes) when a hex-prefix input // matches more than one commit. go-git v6's ResolveRevision silently picks // the first candidate in ambiguous cases (its source explicitly says "for // speed purposes don't bother to detect the ambiguity"), which could pick // the wrong commit. Non-hex refs (HEAD, branch names, HEAD~1) bypass the // ambiguity check via commitHashesWithPrefix returning nil. // // The structured ambiguous return lets callers render a styled failure // block (with each match's timestamp/session) without re-resolving the // matches themselves. func resolveCommitUnambiguous(repo *git.Repository, ref string) (plumbing.Hash, []plumbing.Hash, error) { hash, err := repo.ResolveRevision(plumbing.Revision(ref)) if err != nil { return plumbing.ZeroHash, nil, err //nolint:wrapcheck // caller contextualizes } matches := commitHashesWithPrefix(repo, ref) if len(matches) <= 1 { return *hash, nil, nil } return plumbing.ZeroHash, matches, errAmbiguousCommitPrefix }

// abbreviateCommitHash returns the shortest prefix of hash unique among // commit objects in the repo, matching git's --abbrev-commit auto-growth // so displayed short SHAs stay unambiguous as the repo grows. Falls back // to a fixed 12-char prefix if the storer doesn't support fast prefix // lookup, or to the full hash if somehow never unique. func abbreviateCommitHash(repo *git.Repository, hash plumbing.Hash) string { full := hash.String() for length := 7; length < len(full); length++ { matches := commitHashesWithPrefix(repo, full[:length]) if matches == nil { return full[:12] } if len(matches) <= 1 { return full[:length] } } return full }

// interaction holds a single prompt and its responses for display. type interaction struct { Prompt string Responses []string // Multiple responses can occur between tool calls Files []string }

// associatedCommit holds information about a git commit associated with a checkpoint. type associatedCommit struct { SHA string ShortSHA string Message string Author string Email string Date time.Time }

// checkpointDetail holds detailed information about a checkpoint for display. type checkpointDetail struct { Index int ShortID string Timestamp time.Time IsTaskCheckpoint bool Message string // Interactions contains all prompt/response pairs in this checkpoint. // Most strategies have one, but shadow condensations may have multiple. Interactions []interaction // Files is the aggregate list of all files modified (for backwards compat) Files []string }

func newExplainCmd() *cobra.Command { var sessionFlag string var commitFlag string var checkpointFlag string var noPagerFlag bool var shortFlag bool var fullFlag bool var rawTranscriptFlag bool var generateFlag bool var forceFlag bool var searchAllFlag bool var jsonFlag bool var transcriptFlag bool var summaryTimeoutSecondsFlag int sessionIndex := -1 listLimit := 0 // 0 means "use default (branchCheckpointsLimit)"

Use this command to understand what happened during agent-driven development, either for self-review or to understand a teammate's work.

By default, shows checkpoints on the current branch. Pass a checkpoint ID or commit SHA as a positional argument to explain a specific item, or use flags.

Viewing specific items: entire explain <id-or-sha> Auto-detects checkpoint ID or commit SHA entire explain --checkpoint <id> Force interpretation as checkpoint ID entire explain --commit <ref> Force interpretation as commit ref

Filtering the list view: --session Filter checkpoints by session ID (or prefix)

Output verbosity levels (when explaining a specific item): Default: Detailed view with scoped prompts (ID, session, tokens, intent, prompts, files) --short Summary only (ID, session, timestamp, tokens, intent) --full Parsed full transcript (all prompts/responses from entire session) --raw-transcript Raw transcript file (JSONL format)

Machine-readable export modes (additive surface for external consumers): --json Metadata-only JSON. Lists checkpoints when no target is given; emits a single checkpoint envelope when a target is supplied. Transcript bytes are NEVER embedded in the JSON envelope. --transcript Stream the normalized compact transcript bytes (JSONL on /main) to stdout for the selected session. Pair with --raw-transcript for the per-agent raw transcript instead. --session-index Pick a session within a multi-session checkpoint (0-based). Defaults to the latest session. Only meaningful with --transcript or --raw-transcript. --limit Cap the number of checkpoints returned by the list view. Defaults to 100. When the cap is hit, a stderr note says how many were skipped. Only meaningful with --json.

Summary generation: --generate Generate an AI summary for the checkpoint --force Regenerate even if a summary already exists (requires --generate)

Performance options: --search-all Remove branch/depth limits when searching for commits (may be slow)

Checkpoint detail view shows:

  • Author of the checkpoint
  • Associated git commits that reference the checkpoint
  • Prompts and responses from the session

Note: --session filters the list view; the positional arg, --commit, and --checkpoint are mutually exclusive.`, Args: func(_ *cobra.Command, args []string) error { if len(args) > 1 { return fmt.Errorf("accepts at most 1 argument (checkpoint ID or commit SHA), received %d\nHint: use --session to filter the list view, or pass a single checkpoint ID / commit SHA", len(args)) } return nil }, RunE: func(cmd *cobra.Command, args []string) error { // Check if Entire is disabled if checkDisabledGuard(cmd.Context(), cmd.OutOrStdout()) { return nil }

}

// runExplain routes to the appropriate explain function based on flags and the // optional positional target. func runExplain(ctx context.Context, w, errW io.Writer, sessionID, commitRef, checkpointID, target string, noPager, verbose, full, rawTranscript, generate, force, searchAll bool, summaryTimeoutSeconds int) error { // Count mutually exclusive flags (--commit and --checkpoint are mutually exclusive) // --session is now a filter for the list view, not a separate mode flagCount := 0 if commitRef != "" { flagCount++ } if checkpointID != "" { flagCount++ } // If --session is combined with --commit or --checkpoint, that's still an error if sessionID != "" && flagCount > 0 { return errors.New("cannot specify multiple of --session, --commit, --checkpoint") } if flagCount > 1 { return errors.New("cannot specify multiple of --session, --commit, --checkpoint") }

}

// runExplainAuto resolves a positional target as either a checkpoint ID // (or prefix) or a git commit ref. Ordering: checkpoint path first (which // also handles shadow-branch temp checkpoints), falling back to commit // resolution only on checkpoint.ErrCheckpointNotFound. --generate runs // an ambiguity pre-check to avoid writing a summary to the wrong // checkpoint on short-prefix collisions. func runExplainAuto(ctx context.Context, w, errW io.Writer, target string, noPager, verbose, full, rawTranscript, generate, force, searchAll bool, summaryTimeoutSeconds int) error { stop := startSpinner(errW, "Loading checkpoints") lookup, lookupErr := newExplainCheckpointLookup(ctx) stop(false) if generate { if err := runExplainAutoAmbiguityGuard(ctx, target, lookup, lookupErr); err != nil { return err } } checkpointErr := runExplainCheckpointWithLookup(ctx, w, errW, target, noPager, verbose, full, rawTranscript, generate, force, searchAll, lookup, lookupErr, summaryTimeoutSeconds) if checkpointErr == nil { return nil } // Fall back to commit resolution ONLY when nothing (committed or temp) // matched the target. errCannotGenerateTemporaryCheckpoint signals that // we DID match a temp checkpoint but --generate is unsupported for it; // falling back to commit in that case would produce a misleading // "no trailer" error for the shadow-branch commit. if !errors.Is(checkpointErr, checkpoint.ErrCheckpointNotFound) { return checkpointErr } logging.Debug(ctx, "explain auto: checkpoint lookup failed, trying commit fallback", slog.String("target", target), slog.String("checkpoint_error", checkpointErr.Error()))

}

// runExplainAutoAmbiguityGuard refuses --generate when the positional // target resolves as both a git revision and a committed-checkpoint prefix. // Writing a summary to the wrong checkpoint is destructive; read-only flows // tolerate the same ambiguity by preferring the checkpoint path. // // Best-effort: on repo/list failures we return nil so the main flow // surfaces the real error instead of double-reporting. func runExplainAutoAmbiguityGuard(ctx context.Context, target string, lookup *explainCheckpointLookup, lookupErr error) error { // Targets longer than a checkpoint ID can't prefix-match one. // This is coupled to checkpoint IDs being fixed-width; longer t ...[truncated]

4mo ago·10.4s

{"findings":[{"title":"Interrupt handling swallows follow-up termination signals","category":"bug","severity":"medium","confidence":"high","evidence":[{"path":"cmd/entire/main.go","startLine":27,"endLine":31,"symbol":"main","quote":"signal.Notify(sigChan, signals...)"}],"reasoning":"signal.Notify disables the default SIGINT/SIGTERM behavior while registered. The handler reads exactly one signal, cancels the context, and then exits without calling signal.Stop or otherwise restoring default behavior. If any built-in command, prompt, network call, or subprocess path does not promptly observe cmd.Context(), later Ctrl-C/SIGTERM signals are still captured by the registered channel instead of terminating the process, leaving users with SIGKILL as the practical escape hatch.","reproduction":"Run an entire command path that blocks without observing cmd.Context(), send SIGINT once, then send SIGINT or SIGTERM again. The first signal only cancels the context; subsequent handled signals do not take the OS default termination action because Notify remains active.","recommendation":"After the first signal, restore default handling or implement explicit escalation. For example, call signal.Stop(sigChan) after canceling so a second SIGINT/SIGTERM terminates normally, or keep listening and os.Exit with the conventional signal exit code on a second signal.","whyTestsDoNotAlreadyCoverThis":"The feature lists no linked tests. Existing signal coverage around external plugins validates first-signal forwarding, but does not cover second-signal escalation or a built-in command that ignores context cancellation.","suggestedRegressionTest":"Add an integration test with a test-only blocking command that ignores context, send SIGINT twice, and assert the process exits within a short timeout. Add a Unix SIGTERM variant if SIGTERM remains registered.","minimumFixScope":"cmd/entire/main.go signal setup plus a focused integration test."}],"inspected":{"files":["cmd/entire/main.go","cmd/entire/cli/activity_cmd.go","cmd/entire/cli/activity_render.go","cmd/entire/cli/activity_tui.go","cmd/entire/cli/activity_types.go","cmd/entire/cli/agent_group.go","cmd/entire/cli/aliascmd.go","cmd/entire/cli/api_client.go","cmd/entire/cli/attach.go","cmd/entire/cli/attach_transcript.go","cmd/entire/cli/auth.go","cmd/entire/cli/checkpoint_group.go","cmd/entire/cli/checkpoint_reader.go","cmd/entire/cli/clean.go","cmd/entire/cli/commit_message.go","cmd/entire/cli/config.go","cmd/entire/cli/constants.go","cmd/entire/cli/dispatch.go","cmd/entire/cli/dispatch_tui.go","cmd/entire/cli/dispatch_wizard.go","cmd/entire/cli/doctor.go","cmd/entire/cli/doctor_bundle.go","cmd/entire/cli/doctor_logs.go","cmd/entire/cli/errors.go","cmd/entire/cli/explain.go","cmd/entire/cli/root.go","cmd/entire/cli/plugin.go","cmd/entire/cli/integration_test/external_command_signal_unix_test.go"],"symbols":["main","showSuggestion","cli.NewRootCmd","cli.MaybeRunPlugin","cli.PrependPluginBinDirToPATH","runPlugin","NewRootCmd"],"notes":["Used code-reviewer workflow; final output kept strict JSON per request.","No linked tests were declared in the feature metadata.","Evidence for the emitted finding points at the included owned file. Additional root/plugin files were inspected only to understand the entrypoint context."]}}