add AGENTS.md and update README.md

This commit is contained in:
2026-07-28 23:08:49 +02:00
parent 949935a2aa
commit 07e8bc2f5b
2 changed files with 597 additions and 280 deletions

220
AGENTS.md Normal file
View File

@@ -0,0 +1,220 @@
# AGENTS.md
## Protected README preamble
The notice at the very top of `README.md` stating that the entire repository is
AI-generated, including the adjacent placeholder for the repository owner's
personal comments, is intentional and permanent.
**Do not remove, replace, relocate, soften, or rewrite that preamble or its
owner-comment placeholder.** Only the repository owner may fill in or edit the
placeholder. Changes elsewhere in the README must preserve this section
verbatim and keep it before the project title.
## Repository purpose
`diple` is a keyboard-first terminal UI for people receiving GitHub pull
request reviews. It is a Go 1.24 application built with Bubble Tea and
Lip Gloss. It uses GitHub's GraphQL and REST APIs, authenticates through the
GitHub CLI or token environment variables, and keeps optional cache, draft,
read-state, and local-AI data on disk.
The module path is:
```text
git.pablu.de/Pablu/diple
```
The application is one `package main`. There is no internal package hierarchy,
so keep new code close to the feature it serves and avoid introducing
abstractions without a concrete second use.
## Product boundaries
- The primary user is the PR author or assignee responding to a review, not the
reviewer submitting one.
- Reading must remain useful when some GitHub subsections fail or cached data
is temporarily stale.
- Mutations must be explicit, permission-aware, confirmed where destructive,
and protected against stale PR heads where applicable.
- Local AI findings and discussions are local-only. They must never be
published to GitHub implicitly.
- AI providers must not receive local checkout contents. Current AI context
comes from authenticated GitHub PR data and filtered diffs.
- Never run code, hooks, tools, or repository commands on behalf of an AI
provider. Preserve the hardened, tool-free provider boundary.
- The UI is keyboard-first. Similar actions should use the same configurable
keybinding groups across screens.
- Narrow terminals, wrapped content, Unicode grapheme clusters, no-color mode,
and high-contrast mode are supported behavior, not optional polish.
## Important files
- `main.go`: startup, configuration application, service wiring, persistence,
and Bubble Tea program creation.
- `cli.go`: CLI help and Bash, Zsh, and Fish completion generators.
- `config.go`: TOML schema, defaults, lookup paths, and validation.
- `keybindings.go`: configurable key groups, defaults, help labels, and
contextual conflict validation.
- `github.go`: GitHub queries, pagination, mutations, capabilities, rate-limit
reporting, and service interfaces.
- `cache.go`: immediate cached reads, offline fallback, content-aware writes,
and bounded pruning.
- `tui.go`: main application state, updates, screens, rendering, refresh
coordination, and contextual help.
- `pr_editor.go`, `text_editor.go`, `branch_completion.go`: PR metadata editor,
reusable text editing, Vim-style motions/visual mode, and target-branch
completion.
- `markdown.go`, `markdown_editor_highlight.go`: GitHub-flavored comment
rendering and non-destructive editor highlighting.
- `highlight.go`, `suggestions.go`: syntax-highlighted diff hunks and GitHub
suggestion previews.
- `theme.go`: built-in, custom, high-contrast, and no-color palettes.
- `health.go`: component health, stable status, rate limits, and session event
history.
- `conflicts.go`: read-only conflicting-file discovery in a temporary bare Git
repository; it must not depend on or modify the current Git/Jujutsu checkout.
- `drafts.go`, `persistence.go`: versioned, atomic local state.
- `ai.go`, `ai_diff.go`, `ai_codex.go`, `ai_store.go`, `ai_tui.go`:
experimental local-only AI review, filtering, provider isolation, local
storage, and UI.
- `types.go`: shared domain models and capability flags.
- `TODO.md`: future work; keep completed behavior out of open TODO sections.
Most source files have a corresponding `_test.go`. Add focused regression
tests beside the code being changed.
## Build and validation
Use the package, not an individual source file:
```sh
go run .
go test ./...
go test -race ./...
go vet ./...
go build ./...
```
Run `gofmt` on changed Go files. A normal implementation should at least pass
`go test ./...`; changes involving asynchronous refreshes, provider progress,
or shared persistence should also pass the race detector.
Do not use `go run main.go`: this repository relies on the other files in the
same package.
Do not run Git commands or alter repository history unless the user explicitly
asks. Preserve unrelated working-tree changes.
## Architecture and state flow
`App` is the Bubble Tea model. GitHub operations return typed messages to
`Update`; rendering belongs in `View` and feature-specific view helpers.
Network calls, subprocesses, and disk reads must not block the update loop.
Long operations should expose stable progress and support cancellation where
possible.
GitHub access is expressed through narrow service interfaces. Keep compile-time
interface assertions in `main.go` when implementations change. The cached
service must preserve the capability behavior of the live service rather than
silently bypassing write gates.
Refreshes are intentionally incremental:
- Render cached core data immediately when available.
- Replace it with live core data without blanking the current screen.
- Enrich checks, annotations, and conflict details independently.
- Retain the last complete subsection when one enrichment fails.
- Ignore results from superseded requests.
Do not turn partial failures into an all-or-nothing screen failure.
## GitHub correctness and write safety
- Paginate list-like GitHub data or make any remaining bound visible.
- Treat GraphQL partial data and errors deliberately.
- Use the authenticated endpoint consistently, including GitHub Enterprise
Server URL derivation.
- Respect rate limits, retry windows, and adaptive polling.
- Check the exposed capability gate before showing or executing a mutation.
- Preserve drafts if a mutation or post-mutation refresh fails.
- Require confirmation for merge, auto-merge, and other consequential actions.
- Use expected head OIDs where GitHub supports them; reject stale prepared
actions instead of applying them to a changed PR.
- Sanitize untrusted GitHub and subprocess text before terminal rendering.
- Never interpolate untrusted content into shell commands.
## Local persistence
Cache, state, drafts, and local-AI files are user data:
- Keep formats versioned.
- Write atomically.
- Use restrictive permissions for sensitive or user-authored content.
- Avoid rewriting unchanged files.
- Bound cache growth and preserve corrupt-file diagnostics in Health.
- Do not silently delete recoverable drafts or local review state.
## Text, Markdown, and terminal behavior
- Terminal width is a runtime constraint. Wrap help, errors, paths, Markdown,
editor lines, and modal content without losing structural prefixes.
- Measure display cells, not bytes or rune counts.
- Editing and selections must operate on grapheme boundaries.
- Soft-wrapped editor rows are virtual display rows: navigation may traverse
them, but saved GitHub Markdown must reconstruct the original logical lines.
- Syntax and Markdown highlighting must not insert, remove, or replace editable
characters.
- Keep the active pane visually distinguishable.
- Compact footers show only the first configured key for each action; the help
popup may show all configured alternatives.
- Add new actions to contextual help and keybinding validation.
## Themes
Do not hard-code feature colors outside the theme palette. New UI elements must
remain legible in built-in dark/light themes, custom themes, `high-contrast`,
and `no-color`. Syntax and Markdown highlighting should follow the selected
theme rather than an independent fixed palette.
## Experimental AI rules
The provider abstraction is intentionally broader than the current Codex CLI
implementation. Keep provider-specific parsing in its adapter.
- AI remains disabled by default.
- Every inference run requires an explicit user action and scope confirmation.
- The inference-free status check and quota-consuming provider test must remain
visibly distinct.
- Filter excluded/sensitive files, redact secret-like values, and enforce byte,
file, call, output, and result-count limits.
- Treat paths, code, PR text, and comments as untrusted prompt data.
- Validate findings against changed diff lines and the prepared head.
- Pin one exact model for a run; never silently fall back.
- Reject attempted tool, command, or file-change events.
- Only display provider-exposed reasoning summaries. Never request, infer, log,
or display hidden chain-of-thought.
- Store findings locally with deterministic deduplication and mark them
outdated when the PR head changes.
Any future provider that sends data to a direct API needs explicit privacy and
retention semantics documented before implementation.
## Change discipline
Prefer small, idiomatic changes that preserve current behavior. Correctness
comes before API consistency, and API consistency comes before refactoring.
Do not rewrite working subsystems merely for style.
When behavior changes:
1. Identify the active screen and input context.
2. Update state transitions and cancellation behavior.
3. Update rendering, contextual help, and capability explanations.
4. Add or update regression tests.
5. Update `README.md` for user-visible configuration or workflow changes.
6. Update `TODO.md` only when work is genuinely completed or newly deferred.
If a product decision would materially affect persistence compatibility,
GitHub writes, AI data exposure, destructive behavior, or default keybindings,
ask the repository owner rather than guessing.

657
README.md
View File

@@ -1,21 +1,99 @@
> [!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
A terminal UI for people receiving GitHub pull-request reviews. It `diple` is a keyboard-first terminal interface for reading and responding to
shows open PRs and a scrollable PR dashboard with the description, branches, GitHub pull request reviews. It is designed primarily for the person receiving
review state, merge conflicts and affected files, checks, people, labels, a review: it keeps the PR description, status, changed code, review threads,
milestone, activity, change statistics, thread totals, submitted reviews, and and the actions needed to address feedback in one terminal application.
the PR conversation. Review threads and comments are paginated rather than
silently stopping at the first page. The The project is under active development. GitHub write actions are guarded by
thread viewer includes highlighted diff hunks, comment authors, and read-only the permissions reported for the current user and ask for confirmation where
reaction counts on individual comments. Resolved threads start folded. GitHub suggestion blocks are shown as the result is consequential. The optional AI review feature is experimental,
syntax-highlighted remove/add previews. Comments and PR descriptions render disabled by default, and local-only.
GitHub Flavored Markdown, including quoted replies, inline and fenced code,
lists and tasks, links, tables, emphasis, strikethrough, emoji, and GitHub ## What diple does
alerts. The current PR is refreshed in the background.
### 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, and target branch;
- 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 reviewers, assignees, 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 ## Install and run
Requires Go 1.24+, Git 2.38+, and an authenticated GitHub CLI: From a source checkout:
```sh ```sh
go install . go install .
@@ -23,127 +101,255 @@ gh auth login
diple diple
``` ```
To run directly from a source checkout instead: Run without installing:
```sh ```sh
go run . go run .
``` ```
Use `go run .`, not `go run main.go`: the latter compiles only `main.go` and Use `go run .`, not `go run main.go`. The latter omits the other Go files in
omits the other files in the package. the package.
For automation, `GH_TOKEN` or `GITHUB_TOKEN` can still be provided and takes By default, diple finds open PRs assigned to the authenticated user across all
precedence over the GitHub CLI credential. Enterprise token environment repositories:
variables are also supported.
By default the PR picker searches all repositories for open PRs assigned to the ```sh
authenticated user and groups the results by repository. Use `--repo` to limit diple
the picker to one repository: ```
Limit the picker to one repository:
```sh ```sh
diple --repo owner/repository diple --repo owner/repository
``` ```
With a repository selected, pass `--all` to include every open PR in that Include every open PR in that repository:
repository:
```sh ```sh
diple --repo owner/repository --all --poll 15s diple --repo owner/repository --all
``` ```
GitHub Enterprise Server can be used after authenticating that host: 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 ```sh
gh auth login --hostname github.example.com gh auth login --hostname github.example.com
diple --repo owner/repository \ diple \
--repo owner/repository \
--endpoint https://github.example.com/api/graphql --endpoint https://github.example.com/api/graphql
``` ```
## Shell completion 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.
`diple` generates completion scripts without contacting GitHub or loading the ## Navigation
configuration. Choose the command for your shell:
```sh The defaults are Vim-like and every binding is configurable.
# Bash: current session
source <(diple completion bash)
# Zsh: current session - `j` / `k`: move down / up
source <(diple completion zsh) - `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
# Fish: install for the current user Compact footers show only the first configured key for each action. The
diple completion fish > ~/.config/fish/completions/diple.fish contextual help popup shows all alternatives and is the authoritative in-app
``` reference.
For persistent Bash completion, write the generated output to a directory The PR description editor defaults to Vim-style modal editing, including
loaded by your distribution's `bash-completion` package. For persistent Zsh Normal, Insert, and Visual modes, word/find motions, deletion, system clipboard
completion, write it to a file named `_diple` in a directory on `$fpath`, then yank/paste, and soft-wrap-aware movement. Set `editing.mode = "standard"` for a
run `compinit`. `diple completion --help` lists the supported shells, while non-modal editor. Target-branch completion uses `ctrl+n` and `ctrl+p`.
`diple --help` shows grouped command-line options, defaults, configuration
precedence, and authentication behavior.
## Configuration ## Configuration
The optional TOML configuration is loaded from Configuration is optional TOML. diple checks:
`$DIPLE_CONFIG`, `$XDG_CONFIG_HOME/diple/config.toml`, or the operating
system's user configuration directory at `diple/config.toml`.
On Linux this is normally `~/.config/diple/config.toml`. On macOS,
`~/Library/Application Support/diple/config.toml` is preferred, with
`~/.config/diple/config.toml` automatically used as a fallback when it
exists.
For migration, `GH_THREADS_CONFIG` and existing `gh-threads` configuration or 1. `--config FILE`;
cache directories remain fallback locations when their new `diple` 2. `DIPLE_CONFIG`;
counterparts do not yet exist. 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 ```toml
theme = "dark" # built-in name, "custom", or an accessibility mode theme = "dark"
refresh_interval = "10s" refresh_interval = "10s" # minimum 2s
repository = "" # optional owner/repository default repository = "" # optional "owner/repository"
show_all = false # requires repository show_all = false # requires repository
limit = 50 limit = 50 # 1-1000
endpoint = "https://api.github.com/graphql" endpoint = "https://api.github.com/graphql"
[display] [display]
fold_resolved = true fold_resolved = true
thread_list_width_percent = 33 # 20-60 thread_list_width_percent = 33 # 20-60
dashboard_mode = "hotkey" # "hotkey" or "intermediate" dashboard_mode = "hotkey" # "hotkey" or "intermediate"
compact_reviews = true # aggregate submitted review history compact_reviews = true
[paths] [paths]
scroll = false scroll = false
scroll_interval = "350ms" # minimum 50ms scroll_interval = "350ms" # minimum 50ms
[threads] [threads]
# Each status must occur exactly once. "outdated" means unresolved and outdated; # Each category must occur exactly once. Resolved wins over outdated.
# resolved threads remain in "resolved" even when they are also outdated.
status_order = ["unresolved", "outdated", "resolved"] status_order = ["unresolved", "outdated", "resolved"]
within_status = "file" # "file" or "timestamp" (oldest first) within_status = "file" # "file" or "timestamp"
[cache] [cache]
enabled = true # instant stale view plus offline fallback enabled = true
max_age = "168h" # 7 days; 0 means no age limit max_age = "168h" # 7 days; 0 disables offline expiry
directory = "" # defaults to the OS user cache directory directory = "" # empty uses the OS cache directory
max_entries = 200 # bounded oldest-first pruning; 10-10000 max_entries = 200 # 10-10000
[editing] [editing]
mode = "vim" # "vim" or "standard"; description field only for now mode = "vim" # "vim" or "standard"
[ai] [ai]
# Experimental and local-only. Disabled unless explicitly enabled. Each run
# still requires confirmation after its exact scope and redactions are shown.
enabled = false enabled = false
provider = "codex-cli" # provider abstraction; only Codex CLI is implemented provider = "codex-cli" # currently the only implemented provider
model = "" # empty uses the provider default for the whole run model = "" # empty selects the provider default
command = "codex" command = "codex"
timeout = "3m" timeout = "3m"
max_calls = 8 max_calls = 8
max_request_bytes = 180000 max_request_bytes = 180000
max_run_bytes = 900000 max_run_bytes = 900000
max_file_bytes = 150000 max_file_bytes = 150000
store_directory = "" # defaults beside config.toml, mode 0700/0600 store_directory = ""
exclude = ["*.lock", "go.sum", "package-lock.json", "vendor/", "node_modules/", "dist/", "build/", "generated/", "coverage/", "*.generated.*", "*_generated.*", "*.min.js", "*.map", ".env", ".env.*", "*.pem", "*.key", "*.p12", "*.pfx", "*credentials*"] exclude = [
"*.lock", "go.sum", "package-lock.json", "vendor/", "node_modules/",
"dist/", "build/", "generated/", "coverage/", "*.generated.*",
"*_generated.*", "*.min.js", "*.map", ".env", ".env.*", "*.pem",
"*.key", "*.p12", "*.pfx", "*credentials*",
]
```
`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.
Thread categories are:
- `unresolved`: current unresolved threads;
- `outdated`: unresolved threads attached to outdated code; and
- `resolved`: all resolved threads, including resolved-and-outdated threads.
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] [keybindings.general]
quit = ["q", "ctrl+c"] quit = ["q", "ctrl+c"]
help = ["?", "f1"] help = ["?", "f1"]
@@ -153,7 +359,6 @@ confirm = ["y"]
reject = ["n", "esc"] reject = ["n", "esc"]
[keybindings.navigation] [keybindings.navigation]
# Shared by the picker, dashboard, thread panes, help, and Vim Normal/Visual modes.
down = ["j", "down"] down = ["j", "down"]
up = ["k", "up"] up = ["k", "up"]
left = ["h", "left"] left = ["h", "left"]
@@ -231,235 +436,127 @@ repeat_find = [";"]
repeat_find_reverse = [","] repeat_find_reverse = [","]
``` ```
Themes are compiled into `diple`; they do not require a separate download. Printable bindings do not steal ordinary text while an input field, search, or
Available names are `dark`, `light`, `catppuccin` (`catppuccin-mocha`), Insert mode owns that key.
`catppuccin-latte`, `gruvbox` (`gruvbox-dark`), `gruvbox-light`,
`one-dark-pro`, `github` (`github-dark`), `github-light`, `high-contrast`,
and `no-color`.
Set `theme = "custom"` to inherit a built-in palette and replace only the ## Cache and local data
roles you care about:
```toml The read cache is designed for fast startup and offline fallback:
theme = "custom"
[custom_theme] - core picker and PR snapshots are stored separately;
base = "catppuccin-mocha" # defaults to "dark" - unchanged content is not rewritten on every refresh;
mode = "dark" # "dark" or "light"; controls Markdown rendering - changed files are replaced atomically;
title = "#F5C2E7" - old entries are pruned at `cache.max_entries`; and
active_foreground = "#1E1E2E" - live data automatically replaces the visible cached snapshot.
active_background = "#89B4FA"
selection_background = "#313244"
author_palette = ["#89B4FA", "#CBA6F7", "#94E2D5", "#F9E2AF"]
syntax_theme = "catppuccin-mocha"
```
Every color override uses `#RRGGBB`. The complete set of roles is `title`, Cached data is labelled when first shown. A normal refresh does not repeatedly
`dim`, `text`, `active_foreground`, `active_background`, `success`, `warning`, reintroduce the cached header.
`error`, `editor_foreground`, `editor_background`, `pane_inactive`,
`pane_active`, `quote`, `selection_background`,
`suggestion_remove_background`, `suggestion_add_background`,
`changed_remove_background`, and `changed_add_background`.
`author_palette` accepts one or more colors. `syntax_theme` accepts an installed
Chroma style name; invalid colors, bases, and syntax styles are reported as
configuration errors at startup. The `[custom_theme]` table is ignored unless
`theme = "custom"`.
Every command binding accepts one or more Bubble Tea key names. Omitted Read state and recoverable drafts live beside the configuration file as
settings retain their defaults, while an explicitly configured action replaces `state.json` and `drafts.json`. Experimental AI data defaults to the `ai`
its default keys. Printable keys remain text in Insert mode, reply drafts, and directory beside the configuration. These files are versioned and written
search queries; command bindings apply in the appropriate non-text context. atomically; sensitive user-authored state uses restrictive permissions.
The contextual `?` popup and compact screen footers use the configured keys.
Configuration loading also checks each active context independently. A key may
be reused on unrelated screens, but assigning it to two different actions that
can be active together reports the context and both conflicting actions.
## Experimental local AI review ## Experimental local AI review
Set `ai.enabled = true` to expose the `A` menu on the dashboard and thread Enable the feature explicitly:
screens. The initial provider uses the authenticated Codex CLI, so run
`codex login` first. A full review creates clearly labelled `LOCAL AI · LOCAL
ONLY` threads; it may also attach local-only context to unresolved GitHub
threads. Pressing the normal reply key on a local AI thread starts a discussion
with the same configured model. Resolving or unresolving those threads changes
only the permission-restricted local per-PR state file. For small,
self-contained replacements the model can include a standard GitHub-style
suggestion in its local comment. These use the existing syntax-aware
remove/add preview and remain local; diple does not apply or publish them.
Every model run is manually confirmed. The preview shows the head commit, ```toml
model, files, byte budget, call count, exclusions, and redaction count. Input [ai]
comes exclusively from GitHub's authenticated PR diff and PR metadata: diple enabled = true
does not read the local checkout for AI review. Secret-like values are provider = "codex-cli"
redacted, binary/generated/vendor/lock/oversized files are excluded, and the command = "codex"
provider subprocess receives a small environment allowlist. Codex is launched ```
ephemerally in an empty temporary directory with project instructions ignored,
read-only sandboxing, approvals disabled, and all supported tool surfaces
disabled. Any attempted tool event or malformed/out-of-range structured result
fails the run closed.
The AI menu distinguishes the inference-free provider status refresh from a Authenticate Codex separately before opening diple:
provider test that makes one deliberately small structured model call. The
test requires its own confirmation, consumes provider quota, and sends no PR
content or local files. Preparing and running a review displays animated,
stable progress; multi-chunk reviews report completed model calls. When the
provider exposes a reasoning summary, diple shows a bounded, sanitized summary
beside the progress bar. It never requests or displays hidden chain-of-thought.
PR content is untrusted and is explicitly delimited as data in the model ```sh
prompt. Results are validated against changed paths and lines, deduplicated, codex login
and retained as outdated when the PR head moves. No AI result is published to ```
GitHub. Publishing proposed replies and additional providers remain future
work; a future direct API provider must require no-training and zero-data-
retention guarantees.
When cached data exists, the picker and PR details are rendered immediately The `A` menu can:
from that snapshot while a live GitHub refresh runs in the background. Cached
screens are labelled with their save time and are replaced automatically when
fresh data arrives. Core PR and review data is rendered before check
annotations and conflict-file analysis finish. A failed subsection keeps its
last complete value, is marked partial, and does not discard the rest of a
successful refresh. Check annotations are fetched separately only for failed
checks and are reused by immutable check ID.
The cache uses separate JSON files for the picker and each visited PR. Cache - review the current PR and create local-only review threads;
content is hashed before writing: unchanged responses do not rewrite their - discuss an existing local AI thread with the same selected model;
files. Their modification time is touched at most once per day (or half the - add local-only context to existing unresolved GitHub threads;
configured maximum age, when shorter) so recently validated snapshots remain - produce small GitHub-style suggestion blocks for contained changes;
usable without writing on every poll. Changed files are replaced atomically, - refresh provider status without making an inference call; and
and oldest cache entries are pruned at the configured bound. Read state and - run one explicitly confirmed, minimal provider test that consumes quota but
recoverable reply/metadata drafts use versioned, atomic files beside the sends no PR contents.
configuration.
Polling adapts to GitHub's reported rate-limit budget. It backs off as the Before a review, diple shows the exact head commit, selected model, included and
remaining budget gets low, honors server retry windows, and adds jitter to excluded files, byte count, maximum model-call count, and redaction count.
avoid synchronized clients. Opening another PR or starting another refresh Every run requires confirmation.
cancels the superseded request.
GitHub's public APIs report whether a PR conflicts but do not expose its AI input comes from the authenticated GitHub PR diff and PR metadata, not from
conflicting file paths. For conflicting PRs only, `diple` performs a the local checkout. diple excludes configured sensitive, generated, vendored,
read-only `git merge-tree` analysis in a temporary bare repository. It never lock, binary, and oversized files; redacts secret-like values; chunks bounded
touches or inspects the current checkout, so Git, Jujutsu (`jj`), and directories requests; and validates findings against lines changed in the prepared head.
without a local repository behave identically. The analysis fetches the exact
remote base branch and pull-request head ref using the existing GitHub
credential. Results are memoized by the base and head commit, and failed scans
are retried after one minute.
Command-line flags override the configuration. `GH_REPO` overrides the The Codex process runs ephemerally in an empty temporary directory with:
configured repository when `--repo` is not provided. The corresponding flags
include `--config`, `--theme`, `--poll`, `--fold-resolved`,
`--thread-list-width`, `--dashboard-mode`, `--compact-reviews`,
`--path-scroll`, and `--path-scroll-interval`, plus `--cache`,
`--cache-max-age`, `--cache-dir`, and `--editor-mode`.
Boolean settings can be disabled explicitly, for
example `--compact-reviews=false`.
With the default `dashboard_mode = "hotkey"`, opening a PR goes directly to its - repository instructions ignored;
review threads and `d` opens the dashboard only when requested. Set - a read-only sandbox;
`dashboard_mode = "intermediate"` to follow picker → dashboard → review - approvals disabled;
threads instead. - a restricted environment;
- tools, commands, browser, network, plugins, memories, and multi-agent
features disabled; and
- a strict structured-output schema.
Compact reviews aggregate submission counts by state and author. Reviews with Attempted tool or file-change events fail the run. Provider output is bounded
a written summary retain a compact one-line body, while timestamps and commit and sanitized. Progress may display a provider-exposed reasoning summary, but
SHAs are omitted. Set `compact_reviews = false` to restore the complete review diple neither requests nor displays hidden chain-of-thought.
history and metadata.
## Default keys 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.
| Key | Action | Only the Codex CLI provider is currently implemented. The interface permits
| --- | --- | future providers, but their privacy and retention behavior must be defined
| `h` / `l` | Focus the thread list / thread detail | before they are added.
| `j` / `k` | Move between items or scroll the dashboard/focused detail |
| `?` | Show contextual keybinding help |
| `H` | Open application health and diagnostics |
| `d` | Open the current pull request dashboard |
| `e` | Edit the current PR title, target branch, and description from its dashboard |
| `a` | Enable or disable auto-merge from the dashboard |
| `M` | Merge now when GitHub reports that all represented requirements are satisfied |
| `/` | Fuzzy-search paths and filter with `status:`, `author:`, `updated:true` |
| `F` | Clear active thread filters |
| `n` / `N` | Next / previous thread with a new update |
| `c` | Compose a reply to the selected thread |
| `R` | Resolve or unresolve the selected thread |
| `ctrl-p` / `ctrl-n` | Choose the previous / next fuzzy-search or branch-completion match |
| `g` / `G` | First / last item |
| `enter` / `l` | Open the selected PR dashboard or its review threads |
| `enter` | Toggle the selected review thread |
| `za` | Toggle the selected thread |
| `ctrl-d` / `ctrl-u` | Scroll thread detail or page through lists |
| `tab` | Hide or reveal the thread list |
| `b` / `esc` | Return to the previous screen |
| `r` | Refresh now |
| `q` | Quit |
The Health modal reports the interactive loop, configuration, GitHub API, ## Shell completion
rate-limit budget and reset/retry time, disk cache, unread-state persistence,
draft recovery, core PR data, and secondary enrichment. Session warnings and
errors are retained there with their component and timestamp. Long diagnostics
wrap to the modal width. Refresh activity occupies a stable informational row
so polling does not reorder the report. Press `H` from the picker, dashboard,
or thread view; `b` or `esc` closes it without changing the underlying scroll
position.
The reply composer appears inline beneath the selected thread so its code and Generate completion without contacting GitHub or loading configuration:
comments remain visible while writing. It supports multiple lines: `enter`
inserts a newline, `ctrl-s` opens the rendered confirmation preview, and `esc`
cancels. Replies and resolution changes require an explicit `y` confirmation.
Write keys remain disabled for cached snapshots, during refreshes, and whenever
GitHub does not grant the corresponding capability.
Auto-merge and immediate merge actions are available from the dashboard and ```sh
always require confirmation. The selected method is the repository's first # Bash, current session
available method in `squash`, `merge`, then `rebase` preference order. Both source <(diple completion bash)
mutations include the currently displayed head commit OID, so a force-push or
new commit prevents a stale merge. “Merge now” is gated for drafts, conflicts,
required reviews, required checks, unresolved required conversations, closed
PRs, and branches that require a merge queue; GitHub performs the final
permission and mergeability validation.
The dashboard editor works with raw Markdown so template checklists can be # Zsh, current session
updated directly. The active line is highlighted without inserting a source <(diple completion zsh)
layout-changing block character. It opens with the description focused;
`tab` and `shift-tab` move between the description, title, and target branch.
When the target branch is focused, repository branches are recommended using
the typed text, likely branch names, the current/default branch, and each
branch's latest commit time. The list updates as you type. Use `ctrl-p` and
`ctrl-n` to select the previous or next result, then `tab` or `enter` to complete it;
pressing `tab` again moves to the description.
With the default `editing.mode = "vim"`, the description starts in Normal mode. # Fish, persistent user installation
It supports `hjkl`, `0`, `^`, `$`, `gg`, `G`, `w`/`W`, `b`/`B`, `e`/`E`, diple completion fish > ~/.config/fish/completions/diple.fish
`f`/`F`/`t`/`T` with `;` and `,`, `i`/`a`/`I`/`A`, `o`/`O`, `s`, and ```
`x`/`X`. `s` removes the character under the cursor and enters Insert mode.
Soft-wrapped rows behave as visual editor lines for vertical and line-local
motions, but do not add newlines to the Markdown submitted to GitHub.
`ctrl-d` and `ctrl-u` move the cursor and viewport down or up by half a page,
including while extending a Visual selection.
`v` starts character-wise Visual mode and `V` starts visual-line selection;
`d` or `x` deletes the selection, `y` copies it to the system clipboard, and
`p` pastes from the system clipboard. Normal mode uses a block cursor, while
Insert mode uses the terminal's hardware bar cursor at the boundary between
characters without hiding or shifting either character.
The description retains its raw Markdown while headings, emphasis, inline
code, links, quote markers, and HTML comments receive syntax highlighting.
Highlighting consists only of zero-width terminal styling and cannot alter
wrapping, selection, clipboard contents, cursor offsets, or submitted text.
`esc` returns from Insert to Normal mode; a second `esc` cancels the editor.
Set `editing.mode = "standard"` for direct insertion with arrow,
`home`, and `end` navigation. Title and target branch remain standard inputs
in either mode. `ctrl-s` opens an explicit confirmation. If the title,
description, or target branch changes remotely while the editor is open,
submission is blocked rather than overwriting the newer metadata.
## Current scope 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:
The application can reply to review threads, resolve or unresolve them, update ```sh
the PR title, target branch, and description, enable or disable auto-merge, and mkdir -p ~/.zfunc
merge an eligible PR immediately. Comment reactions remain read-only. Other diple completion zsh > ~/.zfunc/_diple
write operations remain disabled. The dashboard shows the capability gate, fpath=(~/.zfunc $fpath)
including why each action is unavailable. Read state autoload -Uz compinit
persists beside the configuration, and recent PR data is cached for offline compinit
fallback. Check contexts and annotations are paginated. GitHub features which ```
depend on server-side context, such as unfurling issue references or displaying
uploaded images, are represented textually in the terminal. See Run `diple completion --help` for the supported shells.
[`TODO.md`](TODO.md) for remaining read-only work and write-support preparation.
## 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).