From 07e8bc2f5baa82d6e8965b2af128b4f7b1b1be33 Mon Sep 17 00:00:00 2001 From: Pablu Date: Tue, 28 Jul 2026 23:08:49 +0200 Subject: [PATCH] add AGENTS.md and update README.md --- AGENTS.md | 220 ++++++++++++++++++ README.md | 657 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 597 insertions(+), 280 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..43955ed --- /dev/null +++ b/AGENTS.md @@ -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. diff --git a/README.md b/README.md index e216d02..e8d5aaf 100644 --- a/README.md +++ b/README.md @@ -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 -A terminal UI for people receiving GitHub pull-request reviews. It -shows open PRs and a scrollable PR dashboard with the description, branches, -review state, merge conflicts and affected files, checks, people, labels, -milestone, activity, change statistics, thread totals, submitted reviews, and -the PR conversation. Review threads and comments are paginated rather than -silently stopping at the first page. The -thread viewer includes highlighted diff hunks, comment authors, and read-only -reaction counts on individual comments. Resolved threads start folded. GitHub suggestion blocks are shown as -syntax-highlighted remove/add previews. Comments and PR descriptions render -GitHub Flavored Markdown, including quoted replies, inline and fenced code, -lists and tasks, links, tables, emphasis, strikethrough, emoji, and GitHub -alerts. The current PR is refreshed in the background. +`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, 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 -Requires Go 1.24+, Git 2.38+, and an authenticated GitHub CLI: +From a source checkout: ```sh go install . @@ -23,127 +101,255 @@ gh auth login diple ``` -To run directly from a source checkout instead: +Run without installing: ```sh go run . ``` -Use `go run .`, not `go run main.go`: the latter compiles only `main.go` and -omits the other files in the package. +Use `go run .`, not `go run main.go`. The latter omits the other Go files in +the package. -For automation, `GH_TOKEN` or `GITHUB_TOKEN` can still be provided and takes -precedence over the GitHub CLI credential. Enterprise token environment -variables are also supported. +By default, diple finds open PRs assigned to the authenticated user across all +repositories: -By default the PR picker searches all repositories for open PRs assigned to the -authenticated user and groups the results by repository. Use `--repo` to limit -the picker to one repository: +```sh +diple +``` + +Limit the picker to one repository: ```sh diple --repo owner/repository ``` -With a repository selected, pass `--all` to include every open PR in that -repository: +Include every open PR in that repository: ```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 gh auth login --hostname github.example.com -diple --repo owner/repository \ +diple \ + --repo owner/repository \ --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 -configuration. Choose the command for your shell: +## Navigation -```sh -# Bash: current session -source <(diple completion bash) +The defaults are Vim-like and every binding is configurable. -# Zsh: current session -source <(diple completion zsh) +- `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 -# Fish: install for the current user -diple completion fish > ~/.config/fish/completions/diple.fish -``` +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. -For persistent Bash completion, write the generated output to a directory -loaded by your distribution's `bash-completion` package. For persistent Zsh -completion, write it to a file named `_diple` in a directory on `$fpath`, then -run `compinit`. `diple completion --help` lists the supported shells, while -`diple --help` shows grouped command-line options, defaults, configuration -precedence, and authentication behavior. +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 completion uses `ctrl+n` and `ctrl+p`. ## Configuration -The optional TOML configuration is loaded from -`$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. +Configuration is optional TOML. diple checks: -For migration, `GH_THREADS_CONFIG` and existing `gh-threads` configuration or -cache directories remain fallback locations when their new `diple` -counterparts do not yet exist. +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" # built-in name, "custom", or an accessibility mode -refresh_interval = "10s" -repository = "" # optional owner/repository default -show_all = false # requires repository -limit = 50 +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" [display] fold_resolved = true thread_list_width_percent = 33 # 20-60 dashboard_mode = "hotkey" # "hotkey" or "intermediate" -compact_reviews = true # aggregate submitted review history +compact_reviews = true [paths] scroll = false scroll_interval = "350ms" # minimum 50ms [threads] -# Each status must occur exactly once. "outdated" means unresolved and outdated; -# resolved threads remain in "resolved" even when they are also outdated. +# Each category must occur exactly once. Resolved wins over outdated. status_order = ["unresolved", "outdated", "resolved"] -within_status = "file" # "file" or "timestamp" (oldest first) +within_status = "file" # "file" or "timestamp" [cache] -enabled = true # instant stale view plus offline fallback -max_age = "168h" # 7 days; 0 means no age limit -directory = "" # defaults to the OS user cache directory -max_entries = 200 # bounded oldest-first pruning; 10-10000 +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"; description field only for now +mode = "vim" # "vim" or "standard" [ai] -# Experimental and local-only. Disabled unless explicitly enabled. Each run -# still requires confirmation after its exact scope and redactions are shown. enabled = false -provider = "codex-cli" # provider abstraction; only Codex CLI is implemented -model = "" # empty uses the provider default for the whole run +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 -store_directory = "" # defaults beside config.toml, mode 0700/0600 -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*"] +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*", +] +``` +`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] quit = ["q", "ctrl+c"] help = ["?", "f1"] @@ -153,7 +359,6 @@ confirm = ["y"] reject = ["n", "esc"] [keybindings.navigation] -# Shared by the picker, dashboard, thread panes, help, and Vim Normal/Visual modes. down = ["j", "down"] up = ["k", "up"] left = ["h", "left"] @@ -231,235 +436,127 @@ repeat_find = [";"] repeat_find_reverse = [","] ``` -Themes are compiled into `diple`; they do not require a separate download. -Available names are `dark`, `light`, `catppuccin` (`catppuccin-mocha`), -`catppuccin-latte`, `gruvbox` (`gruvbox-dark`), `gruvbox-light`, -`one-dark-pro`, `github` (`github-dark`), `github-light`, `high-contrast`, -and `no-color`. +Printable bindings do not steal ordinary text while an input field, search, or +Insert mode owns that key. -Set `theme = "custom"` to inherit a built-in palette and replace only the -roles you care about: +## Cache and local data -```toml -theme = "custom" +The read cache is designed for fast startup and offline fallback: -[custom_theme] -base = "catppuccin-mocha" # defaults to "dark" -mode = "dark" # "dark" or "light"; controls Markdown rendering -title = "#F5C2E7" -active_foreground = "#1E1E2E" -active_background = "#89B4FA" -selection_background = "#313244" -author_palette = ["#89B4FA", "#CBA6F7", "#94E2D5", "#F9E2AF"] -syntax_theme = "catppuccin-mocha" -``` +- 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. -Every color override uses `#RRGGBB`. The complete set of roles is `title`, -`dim`, `text`, `active_foreground`, `active_background`, `success`, `warning`, -`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"`. +Cached data is labelled when first shown. A normal refresh does not repeatedly +reintroduce the cached header. -Every command binding accepts one or more Bubble Tea key names. Omitted -settings retain their defaults, while an explicitly configured action replaces -its default keys. Printable keys remain text in Insert mode, reply drafts, and -search queries; command bindings apply in the appropriate non-text context. -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. +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 -Set `ai.enabled = true` to expose the `A` menu on the dashboard and thread -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. +Enable the feature explicitly: -Every model run is manually confirmed. The preview shows the head commit, -model, files, byte budget, call count, exclusions, and redaction count. Input -comes exclusively from GitHub's authenticated PR diff and PR metadata: diple -does not read the local checkout for AI review. Secret-like values are -redacted, binary/generated/vendor/lock/oversized files are excluded, and the -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. +```toml +[ai] +enabled = true +provider = "codex-cli" +command = "codex" +``` -The AI menu distinguishes the inference-free provider status refresh from a -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. +Authenticate Codex separately before opening diple: -PR content is untrusted and is explicitly delimited as data in the model -prompt. Results are validated against changed paths and lines, deduplicated, -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. +```sh +codex login +``` -When cached data exists, the picker and PR details are rendered immediately -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 `A` menu can: -The cache uses separate JSON files for the picker and each visited PR. Cache -content is hashed before writing: unchanged responses do not rewrite their -files. Their modification time is touched at most once per day (or half the -configured maximum age, when shorter) so recently validated snapshots remain -usable without writing on every poll. Changed files are replaced atomically, -and oldest cache entries are pruned at the configured bound. Read state and -recoverable reply/metadata drafts use versioned, atomic files beside the -configuration. +- 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; +- 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. -Polling adapts to GitHub's reported rate-limit budget. It backs off as the -remaining budget gets low, honors server retry windows, and adds jitter to -avoid synchronized clients. Opening another PR or starting another refresh -cancels the superseded request. +Before a review, diple shows the exact head commit, selected model, included and +excluded files, byte count, maximum model-call count, and redaction count. +Every run requires confirmation. -GitHub's public APIs report whether a PR conflicts but do not expose its -conflicting file paths. For conflicting PRs only, `diple` performs a -read-only `git merge-tree` analysis in a temporary bare repository. It never -touches or inspects the current checkout, so Git, Jujutsu (`jj`), and directories -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. +AI input comes from the authenticated GitHub PR diff and PR metadata, not from +the local checkout. diple excludes configured sensitive, generated, vendored, +lock, binary, and oversized files; redacts secret-like values; chunks bounded +requests; and validates findings against lines changed in the prepared head. -Command-line flags override the configuration. `GH_REPO` overrides the -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`. +The Codex process runs ephemerally in an empty temporary directory with: -With the default `dashboard_mode = "hotkey"`, opening a PR goes directly to its -review threads and `d` opens the dashboard only when requested. Set -`dashboard_mode = "intermediate"` to follow picker → dashboard → review -threads instead. +- 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. -Compact reviews aggregate submission counts by state and author. Reviews with -a written summary retain a compact one-line body, while timestamps and commit -SHAs are omitted. Set `compact_reviews = false` to restore the complete review -history and metadata. +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. -## 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 | -| --- | --- | -| `h` / `l` | Focus the thread list / thread detail | -| `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 | +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. -The Health modal reports the interactive loop, configuration, GitHub API, -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. +## Shell completion -The reply composer appears inline beneath the selected thread so its code and -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. +Generate completion without contacting GitHub or loading configuration: -Auto-merge and immediate merge actions are available from the dashboard and -always require confirmation. The selected method is the repository's first -available method in `squash`, `merge`, then `rebase` preference order. Both -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. +```sh +# Bash, current session +source <(diple completion bash) -The dashboard editor works with raw Markdown so template checklists can be -updated directly. The active line is highlighted without inserting a -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. +# Zsh, current session +source <(diple completion zsh) -With the default `editing.mode = "vim"`, the description starts in Normal mode. -It supports `hjkl`, `0`, `^`, `$`, `gg`, `G`, `w`/`W`, `b`/`B`, `e`/`E`, -`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. +# Fish, persistent user installation +diple completion fish > ~/.config/fish/completions/diple.fish +``` -## 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 -the PR title, target branch, and description, enable or disable auto-merge, and -merge an eligible PR immediately. Comment reactions remain read-only. Other -write operations remain disabled. The dashboard shows the capability gate, -including why each action is unavailable. Read state -persists beside the configuration, and recent PR data is cached for offline -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 -[`TODO.md`](TODO.md) for remaining read-only work and write-support preparation. +```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).