20 KiB
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 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:
go install .
gh auth login
diple
Run without installing:
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:
diple
Limit the picker to one repository:
diple --repo owner/repository
Include every open PR in that repository:
diple --repo owner/repository --all
Adjust polling or inspect all command-line options:
diple --poll 15s
diple --version
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:
GH_TOKENGITHUB_TOKENGH_ENTERPRISE_TOKENGITHUB_ENTERPRISE_TOKEN- the token returned by
gh auth tokenfor the endpoint host
For normal interactive use:
gh auth login
diple
For GitHub Enterprise Server, authenticate the host and provide its GraphQL endpoint:
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 / uph/l: switch panes or move left / right in the active contextenter: open or toggle the selected itemb: go back outside text editingd: open the dashboardtab: hide or show the thread list/: fuzzy-search thread file pathsn/N: next / previous unread threadc: reply to the selected threadR: resolve or unresolve the selected threadr: refreshH: open HealthA: open the experimental local AI menu?: show all bindings for the current screenq: 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. Pending individual review requests and assignees are
prefilled and marked in completion results. Reviewers who already submitted a
review, and requested teams, appear first as protected subdued tokens in the
reviewer field. Their handles retain a darker version of their deterministic
user color, while their brackets and review state use the theme's dim color.
GitHub only permits changing pending review requests.
Protected reviewers cannot receive cursor focus or be deleted, and are excluded
from reviewer completion. Newly entered names gain a visual @ prefix and
their deterministic user color as soon as they exactly match an eligible
reviewer. At that point the suggestions reset to the remaining eligible users;
pressing Space commits the current reviewer and starts the next entry. Reviewers
already present in the field are excluded from those suggestions. 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:
--config FILE;DIPLE_CONFIG;GH_THREADS_CONFIGas a migration fallback;$XDG_CONFIG_HOME/diple/config.toml;- the operating-system configuration directory; and
- legacy
gh-threadspaths 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:
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 show it on the pull
request picker, dashboard, and thread screens. On the dashboard it is centered
beside the first metadata rows so it does not add whitespace below the pull
request title. Editor and popup views hide it to preserve their full usable
height. On other supported screens Difflet sits to the right of the naturally
sized header. Header information wraps when the combined header and mascot do
not fit.
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; andresolved: 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 when the thread detail pane receives focus, when
the thread is resolved, 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:
darklightcatppuccin/catppuccin-mochacatppuccin-lattegruvbox/gruvbox-darkgruvbox-lightone-dark-progithub/github-darkgithub-lighthigh-contrastno-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:
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.
[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:
[ai]
enabled = true
provider = "codex-cli"
command = "codex"
Authenticate Codex separately before opening diple:
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:
# 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:
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.
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. Planned work is tracked in TODO.md.