revdiff

repository·master·Indexed 20 days ago

https://github.com/umputun/revdiff

A TUI-based diff and document reviewer designed to bridge the gap between manual code review and AI-assisted coding. It allows users to annotate changes and pipe those annotations to stdout for consumption by AI agents or scripts. revdiff includes integrations for Codex CLI, OpenCode, pi, and Claude Code, enabling interactive diff reviews and rolling plan reviews.

Tokens
91.7K
Snippets
212
Records
384
Agent score
70%

What's inside revdiff

  1. Overview of revdiff

    master

    revdiff is a Terminal User Interface (TUI) designed for reviewing diffs, files, and documents with inline annotations. It is specifically optimized for terminal-based AI coding sessions (like Claude Code), allowing users to navigate changes, annotate specific lines, and pipe the structured annotation results to stdout upon quitting. This makes it easy to integrate with AI agents, scripts, or other developer tools.

    Key capabilities include:

    • Structured Output: Pipe annotations directly to other tools via stdout.
    • VCS Support: Native support for git, mercurial (hg), and jujutsu (jj), including automatic translation of git-style refs.
    • Advanced Diff Views: Features include intra-line word-diff, collapsed diff mode, and rename-aware diffs.
    • Annotation System: Annotate any line (added, removed, or context) or add file-level notes.
    • Flexible Input: Review files from a VCS, specific files via --only, or arbitrary text via --stdin.
  2. Understand the Structural Refactor Plan

    master

    The structural refactor is an incremental plan to reduce complexity in revdiff by cleaning up startup wiring, tightening theme boundaries, and consolidating UI state.

    Key Objectives:

    • Decompose app/main.go: Split the large monolith into smaller files organized by concern (config, stdin, renderer setup, themes, history).
    • Clean up Theme Boundaries: Move theme discovery and persistence logic out of app/ui and into app/theme and package main.
    • Consolidate UI State: Replace the flat, sprawling ui.Model field list with explicit, grouped sub-state structs (e.g., config, layout, mode, search, annotation).
    • Refactor File State: Replace parallel arrays used for loaded files with a single, explicit loadedFileState object.

    Important Constraints:

    • This is a structural refactor, not a behavioral redesign. All existing features and keyboard behaviors must remain unchanged.
    • The single Bubble Tea Model (ui.Model) remains the core of the application.
  3. Use the `style` package for ANSI coloring and terminal styling

    master

    The app/ui/style package provides three primary types for managing terminal appearance: Resolver for color and style lookups, Renderer for complex UI widget strings, and SGR for ANSI state processing.

    Instead of a single aggregate service, these types are independent and should be instantiated separately and wired into your application configuration.

    import (
        "github.com/umputun/revdiff/app/ui/style"
    )
    
    // Example wiring in main.go
    var res style.Resolver
    if opts.NoColors {
        res = style.PlainResolver()
    } else {
        res = style.NewResolver(style.Colors{ /* ... hex fields ... */ })
    }
    
    cfg := ui.ModelConfig{
        Resolver: res,
        Renderer: style.NewRenderer(res),
        SGR:      style.SGR{},
        // ...
    }
  4. Overview of revdiff architecture

    master

    revdiff is a Terminal User Interface (TUI) designed for reviewing diffs, files, and documents with inline annotations. It is built using the bubbletea framework. The system is organized into several functional layers:

    • Composition Root (app/): Handles the main entry point, configuration parsing, stdin validation, VCS detection, and theme wiring.
    • TUI Layer (app/ui/): Manages the user interface using a single Model struct, including popups (overlay), navigation (sidepane), styling, and an intra-line diff engine (worddiff).
    • Core Logic Packages:
      • app/diff/: Handles VCS detection and diff parsing.
      • app/highlight/: Provides syntax coloring via chroma.
      • app/annotation/: Manages the in-memory store for annotations.
      • app/editor/: Handles invoking the user's $EDITOR.
      • app/theme/: Implements a catalog-centric theme system.
      • app/history/: Manages auto-saving review sessions.
    ┌─────────────────────────────────────────────────────┐
    │  app/ — composition root (package main)             │
    │    main.go          — main(), early-exit flow       │
    │    config.go        — options, parseArgs, config IO │
    │    stdin.go         — stdin validation, /dev/tty    │
    │    renderer_setup.go — VCS detection, renderer pick │
    │    themes.go        — theme CLI commands, wiring    │
    │    history_save.go  — history-save policy           │
    ├─────────────────────────────────────────────────────┤
    │  app/ui/ — bubbletea TUI (single Model struct)      │
    │    ├── overlay/   — popup layers (help, annots,     │
    │    │                theme selector)                 │
    │    ├── sidepane/  — file tree + markdown TOC        │
    │    ├── style/     — color/ANSI resolution           │
    │    └── worddiff/  — intra-line diff engine          │
    ├─────────────────────────────────────────────────────┤
    │  app/diff/        — VCS detection + diff parsing    │
    │  app/highlight/   — chroma syntax coloring          │
    │  app/annotation/  — in-memory annotation store      │
    │  app/editor/      — external $EDITOR invocation     │
    │  app/handoff/     — post-flush command preparation  │
    │  app/keymap/      — configurable keybindings        │
    │  app/theme/       — Catalog-centric theme system   │
    │  app/history/     — review session auto-save         │
    │  app/fsutil/      — filesystem utilities            │
    └─────────────────────────────────────────────────────┘
  5. Manage Annotation Visual-Row Cache and Invalidation

    master

    The revdiff UI model implements a caching mechanism for annotation visual rows to optimize performance. The cache is keyed by a combination of the annotation's body, prefix, and the current pane width.

    Important Invalidation Rule: The visual rows are baked with AnnotationInline resolver styling. Because styling depends on the active theme, any runtime change to the theme (e.g., via applyTheme) or loading a new file (e.g., via handleFileLoaded) must invalidate the cache to prevent visual inconsistencies.

    If you are implementing new features that modify the UI theme or the file state, ensure you call invalidateAnnotationRows() to clear the rowCache.

  6. Understand the revdiff review loop workflow

    master

    The revdiff integration follows a specific agent-driven loop designed to classify and resolve code annotations. The workflow follows these steps:

    1. Initiation: Run /revdiff [args]. The skill resolves arguments and calls revdiff_review.
    2. Annotation Capture: revdiff_review returns captured annotations to the agent as a tool result.
    3. Classification: The agent must classify annotations into:
      • Explanation requests: Answer these first.
      • Code-change directives: Apply these after explanations are handled.
    4. Reviewing Explanations: If explanation notes require user refinement, write them to a temporary markdown file and review them using revdiff_review --only <tempfile>.
    5. Planning: List planned file/code changes before performing edits.
    6. Rerunning: After applying changes, rerun revdiff_review with the same original arguments. Continue this loop until no more annotations are captured.

    Tip: When rerunning, include the --untracked flag if you want the agent to review files that were newly created during the process.

    # Example of reviewing a specific explanation file
    revdiff_review --only path/to/explanation.md
    
    # Example of rerunning with untracked files
    revdiff_review [original_args] --untracked
  7. How Single-File Mode works

    master

    Single-File Mode is an automatic UI optimization in revdiff. When a diff contains exactly one file, the application automatically hides the file tree pane and expands the diff view to occupy the full terminal width. This eliminates the need for pane-switching and provides more space for reviewing the diff.

    Key behaviors in Single-File Mode:

    • Automatic Detection: There is no CLI flag to enable this; it is triggered automatically when the file list contains exactly one file.
    • Layout: The tree pane is not rendered, and the diff pane uses m.width - 2 (accounting for borders).
    • Focus: The application focus is automatically set to the diff pane (paneDiff).
    • Compatibility: Multi-file mode remains unchanged.
  8. Design principles for the app/ui/style sub-package

    master

    The app/ui/style package is designed to be the single source of truth for all color and style resolution in the TUI. It replaces the previous 'god-package' pattern where color knowledge leaked into every renderer.

    Key design principles for this package (and future sub-package extractions) include:

    1. Domain Ownership: Sub-packages must own their domain completely.
    2. Named Types: Use named types for domain values rather than primitive types.
    3. Parameterized Accessors: Prefer parameterized accessors over an explosion of per-role methods.
    4. Methods over Functions: Use methods on types for all helpers, including those that are not part of the public API.
    5. Consumer-side Interfaces: Define interfaces on the consumer side, not the provider side.
    6. Cohesion: Avoid splitting related data and behavior into separate files for the sake of 'cleanness'; keep related logic together.
  9. Handle Overlay Outcomes

    master

    When calling HandleKey on an overlay manager, you must handle the returned Outcome to react to user actions. Common outcomes include:

    • OutcomeClosed: The overlay has been dismissed (e.g., via Esc).
    • OutcomeAnnotationChosen: An annotation was selected from the list. You should use the provided AnnotationTarget to jump to the file and line.
    • OutcomeThemeConfirmed: A theme was selected and confirmed. You should apply the ThemeChoice.
    • OutcomeThemePreview: A theme is being previewed as the user navigates the list.
    • OutcomeThemeCanceled: The theme selection was canceled.
  10. How overlays and sidepanes interact with the Model

    master

    revdiff uses specific patterns to keep UI components decoupled from the main application logic:

    • Overlay Outcome pattern: Overlays (popups) do not directly modify the Model state. Instead, they return Outcome values. This makes side effects explicit and keeps the overlay package independent of the main Model.
    • Factory closures for sidepane components: Components like NewFileTree and ParseTOC are implemented as factory closures rather than direct constructors. This allows them to receive runtime parameters from main.go without requiring the Model to possess knowledge of those specific parameters.
  11. How the Theme API works with Catalog and Theme

    master

    The app/theme package has been refactored to follow a strict object-oriented pattern where all logic is encapsulated within Theme or Catalog structs. There are no standalone exported functions.

    • Theme struct: Represents an individual theme. It owns serialization and validation logic.
    • Catalog struct: Manages theme discovery, loading, installation, and gallery access. It is the primary entry point for theme-related operations.

    To use themes, you should interact with a Catalog instance created via NewCatalog(themesDir).

    // Example of the intended usage pattern
    // Note: Actual implementation details depend on the specific package exports
    
    catalog := theme.NewCatalog(themesDir)
    entries := catalog.Entries()
    // ... use entries to resolve or load themes