> [!IMPORTANT] > **This entire repository is AI-generated.** The source code, tests, and > documentation were produced through AI-assisted development. > > **Repository owner's two cents:** > Like the previous text says this Repo is entirely slop coded. > I guided the ai (gpt5.6-sol) as best as I could and got out the program I desired. > Use this at your own risk, especially the AI integration. > _No AI Agents were harmed during creation of this Program_ # diple `diple` is a keyboard-first terminal interface for reading and responding to GitHub pull request reviews. It is designed primarily for the person receiving a review: it keeps the PR description, status, changed code, review threads, and the actions needed to address feedback in one terminal application. The project is under active development. GitHub write actions are guarded by the permissions reported for the current user and ask for confirmation where the result is consequential. The optional AI review feature is experimental, disabled by default, and local-only. ## What diple does ### Pull request picker - Loads open PRs assigned to the authenticated user across repositories. - Groups the picker by repository. - Can be restricted to one `owner/repository`. - Can show every open PR in a selected repository. - Uses a disk cache to display a recent snapshot immediately while live data loads. ### Dashboard - Shows the title, Markdown description, branches, author, assignees, reviewers, labels, milestone, merge state, review decision, checks, change statistics, submitted reviews, timeline activity, and PR conversation. - Reports whether the PR has conflicts. - Attempts to identify conflicting files with a read-only temporary Git analysis. This does not inspect or modify the current Git or Jujutsu checkout. - Shows check-run annotations independently so a failure in one subsection does not blank the rest of the dashboard. - Provides a Health popup containing API, cache, persistence, conflict-scan, rate-limit, write-capability, and AI-provider diagnostics. ### Review threads - Displays review comments beside the exact review-time diff hunk when GitHub provides it, even when the file has since changed. - Syntax-highlights code using the file path to choose a lexer. - Highlights the reviewed line range and exact changed spans. - Wraps long source lines as continuation rows without inventing line numbers. - Renders GitHub Flavored Markdown, including quoted replies, inline and fenced code, lists, task lists, tables, links, emphasis, strikethrough, emoji, and GitHub alerts. - Renders GitHub suggestion blocks as syntax-highlighted removal/addition previews. - Shows deterministic per-author colors and read-only reaction counts. - Folds resolved threads by default and distinguishes unread or updated local state. - Supports fuzzy path search; whitespace-separated terms may match separate portions of the same path. - Supports configurable status and within-status ordering. ### GitHub write actions When GitHub reports that the authenticated user has permission, diple can: - reply to review threads; - resolve and unresolve review threads; - edit the PR title, Markdown description, target branch, requested reviewers, and assignees; - enable or disable auto-merge; and - merge immediately when the PR is eligible. The UI explains unavailable actions through its write-capability gate. Metadata and reply drafts are persisted locally so cancellation or a restart does not silently discard work. Reactions are currently read-only. Assigning labels or milestones is not implemented yet. ## Requirements - Go 1.24 or newer to build from source. - An authenticated [GitHub CLI](https://cli.github.com/) installation, or a supported GitHub token environment variable. - Git 2.38 or newer for conflicting-file discovery. The rest of the PR remains usable if that optional scan cannot run. - A terminal with reasonable Unicode support. - Optional: an authenticated Codex CLI for experimental local AI review. ## Install and run From a source checkout: ```sh go install . gh auth login diple ``` Run without installing: ```sh go run . ``` Use `go run .`, not `go run main.go`. The latter omits the other Go files in the package. By default, diple finds open PRs assigned to the authenticated user across all repositories: ```sh diple ``` Limit the picker to one repository: ```sh diple --repo owner/repository ``` Include every open PR in that repository: ```sh diple --repo owner/repository --all ``` Adjust polling or inspect all command-line options: ```sh diple --poll 15s diple --help ``` Command-line options override configuration values. `GH_REPO` supplies the default repository only when `--repo` is absent. ## Authentication Credential lookup uses the first available value in this order: 1. `GH_TOKEN` 2. `GITHUB_TOKEN` 3. `GH_ENTERPRISE_TOKEN` 4. `GITHUB_ENTERPRISE_TOKEN` 5. the token returned by `gh auth token` for the endpoint host For normal interactive use: ```sh gh auth login diple ``` For GitHub Enterprise Server, authenticate the host and provide its GraphQL endpoint: ```sh gh auth login --hostname github.example.com diple \ --repo owner/repository \ --endpoint https://github.example.com/api/graphql ``` The token must have sufficient access to read the selected repositories. Write actions additionally depend on the permissions GitHub reports for the particular PR or thread. ## Navigation The defaults are Vim-like and every binding is configurable. - `j` / `k`: move down / up - `h` / `l`: switch panes or move left / right in the active context - `enter`: open or toggle the selected item - `b`: go back outside text editing - `d`: open the dashboard - `tab`: hide or show the thread list - `/`: fuzzy-search thread file paths - `n` / `N`: next / previous unread thread - `c`: reply to the selected thread - `R`: resolve or unresolve the selected thread - `r`: refresh - `H`: open Health - `A`: open the experimental local AI menu - `?`: show all bindings for the current screen - `q`: quit Compact footers show only the first configured key for each action. The contextual help popup shows all alternatives and is the authoritative in-app reference. The PR description editor defaults to Vim-style modal editing, including Normal, Insert, and Visual modes, word/find motions, deletion, system clipboard yank/paste, and soft-wrap-aware movement. Set `editing.mode = "standard"` for a non-modal editor. Target-branch, reviewer, and assignee completion use `ctrl+n` and `ctrl+p`; reviewer and assignee fields accept comma-separated GitHub usernames. Current reviewers and assignees are prefilled and marked in completion results. Reviewer suggestions prioritize recent contributors using the latest 100 commits on the repository's default branch; this bounded window is also shown in the editor. Every change is shown in the existing confirmation screen before GitHub is updated. ## Configuration Configuration is optional TOML. diple checks: 1. `--config FILE`; 2. `DIPLE_CONFIG`; 3. `GH_THREADS_CONFIG` as a migration fallback; 4. `$XDG_CONFIG_HOME/diple/config.toml`; 5. the operating-system configuration directory; and 6. legacy `gh-threads` paths when no diple configuration exists. Common default paths: - Linux: `~/.config/diple/config.toml` - macOS: `~/Library/Application Support/diple/config.toml` - macOS fallback: `~/.config/diple/config.toml` Unknown settings and invalid values are rejected at startup instead of being silently ignored. ### Example configuration All settings below show their normal defaults unless noted otherwise: ```toml theme = "dark" refresh_interval = "10s" # minimum 2s repository = "" # optional "owner/repository" show_all = false # requires repository limit = 50 # 1-1000 endpoint = "https://api.github.com/graphql" mascot = false # show the optional Difflet terminal mascot mascot_expressive = false # allow emotional Difflet expressions mascot_animated = false # allow brief state-driven motion [display] fold_resolved = true thread_list_width_percent = 33 # 20-60 dashboard_mode = "hotkey" # "hotkey" or "intermediate" compact_reviews = true viewer_label = "login" # "login" or "you" [paths] scroll = false scroll_interval = "350ms" # minimum 50ms [threads] # Each category must occur exactly once. Resolved wins over outdated. status_order = ["unresolved", "outdated", "resolved"] within_status = "file" # "file" or "timestamp" [cache] enabled = true max_age = "168h" # 7 days; 0 disables offline expiry directory = "" # empty uses the OS cache directory max_entries = 200 # 10-10000 [editing] mode = "vim" # "vim" or "standard" [ai] enabled = false provider = "codex-cli" # currently the only implemented provider model = "" # empty selects the provider default command = "codex" timeout = "3m" max_calls = 8 max_request_bytes = 180000 max_run_bytes = 900000 max_file_bytes = 150000 max_context_rounds = 2 # automatic file-request rounds for a thread max_context_files = 8 # additional files per thread discussion store_directory = "" exclude = [ "*.lock", "go.sum", "package-lock.json", "vendor/", "node_modules/", "dist/", "build/", "generated/", "coverage/", "*.generated.*", "*_generated.*", "*.min.js", "*.map", ] sensitive_paths = [ ".env", ".env.*", "*.pem", "*.key", "*.p12", "*.pfx", "*credentials*", ] ``` As with other TOML arrays, setting `exclude` or `sensitive_paths` replaces its default list. Copy the defaults you still want before adding project-specific patterns. `dashboard_mode = "hotkey"` opens threads directly from the picker and leaves the dashboard on `d`. `"intermediate"` places the dashboard between the picker and thread viewer. `compact_reviews = true` summarizes the submitted-review history instead of showing every repeated `COMMENTED` event. `viewer_label = "login"` shows your GitHub username like every other author. Set it to `"you"` to replace your username with `@you` throughout the UI. Difflet is disabled by default. Set `mascot = true` to keep it visible to the right next to the active view's naturally sized header, separated by a small gap. On normal terminal widths Difflet is centered horizontally and the header uses the space to its left. When centering would make the header too narrow, Difflet falls back to a small right-edge inset. Header information wraps when the combined header and mascot do not fit. The layout adds only the vertical rows required to display the four-line mascot. `mascot_animated` controls brief loading, blink, success, and error motion independently from `mascot_expressive`, which permits stronger emotional faces. Disabling animation leaves the appropriate final state visible. Disabling the mascot preserves the normal header layout. Thread categories are: - `unresolved`: current unresolved threads; - `outdated`: unresolved threads attached to outdated code; and - `resolved`: all resolved threads, including resolved-and-outdated threads. New threads retain a `NEW THREAD` marker. When an existing thread receives new comments, diple places a `NEW MESSAGES` divider before the first unread comment and emphasizes the unread comment rail. Moving the thread-list cursor does not clear this state. It is cleared after the last unread comment becomes visible while scrolling the focused detail pane, or manually with `keybindings.threads.mark_read` (`m` by default). Within a category, `"file"` keeps paths together and `"timestamp"` sorts by the time the thread was opened. ### Themes Built-in themes: - `dark` - `light` - `catppuccin` / `catppuccin-mocha` - `catppuccin-latte` - `gruvbox` / `gruvbox-dark` - `gruvbox-light` - `one-dark-pro` - `github` / `github-dark` - `github-light` - `high-contrast` - `no-color` The selected palette also controls Markdown and source-code syntax highlighting. For a custom theme, set `theme = "custom"` and override any subset of a built-in base: ```toml theme = "custom" [custom_theme] base = "catppuccin" mode = "dark" title = "#f5c2e7" dim = "#7f849c" text = "#cdd6f4" active_foreground = "#11111b" active_background = "#89b4fa" success = "#a6e3a1" warning = "#f9e2af" error = "#f38ba8" editor_foreground = "#cdd6f4" editor_background = "#313244" pane_inactive = "#585b70" pane_active = "#89b4fa" quote = "#94e2d5" selection_background = "#45475a" suggestion_remove_background = "#3b1f2b" suggestion_add_background = "#193b2a" changed_remove_background = "#4b1f2b" changed_add_background = "#1d4b32" author_palette = ["#89b4fa", "#cba6f7", "#94e2d5", "#f9e2af"] syntax_theme = "catppuccin-mocha" ``` Colors must use `#RRGGBB`. `mode` is `dark` or `light`; `syntax_theme` must be an installed Chroma style. Omitted custom values inherit from `base`. ### Keybindings Each action accepts one or more Bubble Tea key names. Defining an action replaces its default list; omitted actions retain their defaults. Configuration validation rejects conflicting assignments within the same active context. ```toml [keybindings.general] quit = ["q", "ctrl+c"] help = ["?", "f1"] refresh = ["r"] back = ["b", "esc"] confirm = ["y"] reject = ["n", "esc"] [keybindings.navigation] down = ["j", "down"] up = ["k", "up"] left = ["h", "left"] right = ["l", "right"] first = ["g"] last = ["G"] page_down = ["ctrl+d", "pgdown"] page_up = ["ctrl+u", "pgup"] [keybindings.views] open = ["enter", "l"] dashboard = ["d"] health = ["H"] edit = ["e"] auto_merge = ["a"] merge_now = ["M"] toggle_list = ["tab"] ai = ["A"] [keybindings.threads] search = ["/"] clear_filter = ["F"] next_unread = ["n"] previous_unread = ["N"] mark_read = ["m"] reply = ["c"] resolve = ["R"] toggle = ["enter"] fold_prefix = ["z"] fold_toggle = ["a"] [keybindings.input] cancel = ["esc"] submit = ["ctrl+s"] newline = ["enter"] delete_backward = ["backspace"] delete_forward = ["delete"] clear = ["ctrl+u"] next_field = ["tab"] previous_field = ["shift+tab"] next_completion = ["ctrl+n"] previous_completion = ["ctrl+p"] line_start = ["home", "ctrl+a"] line_end = ["end", "ctrl+e"] [keybindings.vim] insert = ["i"] append = ["a"] insert_line_start = ["I"] append_line_end = ["A"] open_below = ["o"] open_above = ["O"] replace_character = ["s"] visual = ["v"] visual_line = ["V"] selection_other_end = ["o"] yank = ["y"] delete = ["d", "x", "delete"] delete_before = ["X", "backspace"] paste = ["p"] line_start = ["0", "home"] first_non_blank = ["^"] line_end = ["$", "end"] word_forward = ["w"] big_word_forward = ["W"] word_backward = ["b"] big_word_backward = ["B"] word_end = ["e"] big_word_end = ["E"] go_prefix = ["g"] find_forward = ["f"] find_backward = ["F"] till_forward = ["t"] till_backward = ["T"] repeat_find = [";"] repeat_find_reverse = [","] ``` Printable bindings do not steal ordinary text while an input field, search, or Insert mode owns that key. ## Cache and local data The read cache is designed for fast startup and offline fallback: - core picker and PR snapshots are stored separately; - unchanged content is not rewritten on every refresh; - changed files are replaced atomically; - old entries are pruned at `cache.max_entries`; and - live data automatically replaces the visible cached snapshot. Cached data is labelled when first shown. A normal refresh does not repeatedly reintroduce the cached header. Read state and recoverable drafts live beside the configuration file as `state.json` and `drafts.json`. Experimental AI data defaults to the `ai` directory beside the configuration. These files are versioned and written atomically; sensitive user-authored state uses restrictive permissions. ## Experimental local AI review Enable the feature explicitly: ```toml [ai] enabled = true provider = "codex-cli" command = "codex" ``` Authenticate Codex separately before opening diple: ```sh codex login ``` The `A` menu can: - review the current PR and create local-only review threads; - discuss an existing local AI thread with the same selected model; - add local-only context to existing unresolved GitHub threads; - let a focused thread discussion request bounded, exact-head repository files; - produce small GitHub-style suggestion blocks for contained changes; - refresh provider status without making an inference call; and - run one explicitly confirmed, minimal provider test that consumes quota but sends no PR contents. Before a review, diple shows the exact head commit, selected model, initial included and excluded files, byte count, maximum model-call count, and redaction count. Every run requires confirmation. A focused thread confirmation also shows its repository-tree summary and the configured automatic file-request limits. Full reviews use the authenticated GitHub PR diff. Focused discussions instead send only the selected thread, its hunk, the complete target file when allowed, minimal PR identifiers, and a bounded tree for the exact PR head. The model can request additional paths from that tree, but diple validates and retrieves their committed blobs through GitHub; the provider never receives local checkout access. `sensitive_paths` are absent from the model-visible tree and can never be requested. `exclude` paths may appear as unavailable tree entries but their contents are not sent. Binary, submodule, oversized, generated, vendored, and lock-file content remains unavailable. All supplied content is bounded, control-sanitized, and checked for secret-like values. Full-review findings remain restricted to visibly changed lines in the prepared head. The Codex process runs ephemerally in an empty temporary directory with: - repository instructions ignored; - a read-only sandbox; - approvals disabled; - a restricted environment; - tools, commands, browser, network, plugins, memories, and multi-agent features disabled; and - a strict structured-output schema. Attempted tool or file-change events fail the run. Provider output is bounded and sanitized. Progress may display a provider-exposed reasoning summary, but diple neither requests nor displays hidden chain-of-thought. Progress and Health report GitHub, filtering, and provider timing without storing prompts or repository contents. AI findings are stored locally, deduplicated deterministically, and marked outdated when the PR head changes. Resolving a local AI thread remains local. diple never publishes an AI finding or discussion to GitHub automatically. Only the Codex CLI provider is currently implemented. The interface permits future providers, but their privacy and retention behavior must be defined before they are added. ## Shell completion Generate completion without contacting GitHub or loading configuration: ```sh # Bash, current session source <(diple completion bash) # Zsh, current session source <(diple completion zsh) # Fish, persistent user installation diple completion fish > ~/.config/fish/completions/diple.fish ``` For persistent Zsh completion, save the output as `_diple` in a directory on `$fpath` and ensure `compinit` runs. The generated Zsh script also initializes completion when sourced directly: ```sh mkdir -p ~/.zfunc diple completion zsh > ~/.zfunc/_diple fpath=(~/.zfunc $fpath) autoload -Uz compinit compinit ``` Run `diple completion --help` for the supported shells. ## Development The module path is `git.pablu.de/Pablu/diple`. The repository is a single Go `package main` built around Bubble Tea, Lip Gloss, Chroma, and Glamour. ```sh gofmt -w path/to/changed.go go test ./... go test -race ./... go vet ./... go build ./... ``` Repository architecture, safety invariants, and contributor guidance are documented in [AGENTS.md](AGENTS.md). Planned work is tracked in [TODO.md](TODO.md).