Skip to content

Commit 8c9bfbf

Browse files
authored
Merge pull request #55 from myrk-kale/feat/decisions-window
feat(tui): decisions and flow windows, and a shortcut to ask the agent
2 parents 0707113 + 83c321b commit 8c9bfbf

54 files changed

Lines changed: 8252 additions & 132 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎README.md‎

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ Every key below can be changed in `.klaub.toml` under `[keys]` ([configuration](
268268
| `2` or `F3` | The structure tree on the left: modules, files and symbols |
269269
| `3` | The flat list of changed files on the left |
270270
| `F4` | The code focused (`Enter` on a list gets there too) |
271+
| `4` | The Decisions window, one technical decision to a screen |
272+
| `5` | The Flow window: a form that asks the agent for a diagram, and the diagram |
271273
| `Tab`, `Shift+Tab` | Next and previous mode |
272274
| `Esc` | Go back (from the code, to the list you came from) |
273275

@@ -315,6 +317,18 @@ The screen is a list on the left and the code of the selected unit on the right.
315317
| `M` | Next module |
316318
| `x` | Clear filters |
317319

320+
### Ask the agent about what you are looking at
321+
322+
Inside `klaub wrap`, `t` on any screen puts a description of what is on it into the agent's input and takes you to the agent, so you only type the question after `My question:` and press Enter yourself. `Shift+→` brings you back.
323+
324+
| On the screen | What the agent receives |
325+
| --- | --- |
326+
| The queue, the tree, the file list, the code | The unit (symbol and file), how it changed, its priority and findings, the version and lines on screen, the line your cursor is on, and the `git diff` and `git show` that show the change and the previous file |
327+
| A decision | The whole card (context, what was decided, why, alternatives, consequences, the question, where in the code) and what you have answered so far. On the summary, every decision with its answer |
328+
| The Flow window | The diagram's steps in reading order with the selected one marked, and the relations. In the form, the choices made so far |
329+
330+
The text tells the agent not to change or run anything before your question. It is one line, cut in the middle if it is long so the end (the question and your answer) always arrives. It is the one text Klaub puts in the agent without asking first, because it is meant to be one key; set `[integration] ask_agent_confirms = true` to be asked each time, with the destination and the text.
331+
318332
### Around the agent
319333

320334
These work after `klaub wrap`. Press the prefix (`Ctrl+B` by default), release it, then the key.
@@ -331,6 +345,40 @@ These work after `klaub wrap`. Press the prefix (`Ctrl+B` by default), release i
331345

332346
An optional mouse mode (`[ui] mouse = true`) adds wheel scrolling and clicks on rows and code lines.
333347

348+
### Flow
349+
350+
The Flow window draws how the change runs as a diagram: a box for each step and connectors between them, the steps the change touched outlined, the certainty of each step and relation marked. Without a diagram the window is a form, and what you choose there shapes the request:
351+
352+
| Choice | Options |
353+
| --- | --- |
354+
| Focus | Free text: what the diagram should concentrate on. Empty covers the whole change |
355+
| Type | Code flow, abstract (components and responsibilities), data flow, sequence of interactions |
356+
| Level of detail | High level, medium, low level (functions and the branches that decide the path) |
357+
| Reach | Only what changed, changes and neighbours, end to end (user, frontend, API, storage, outside systems, even other repositories) |
358+
| Size | About 8, 15 or 30 steps at most |
359+
| Starts at | Automatic, a user action, an HTTP request, a job or event |
360+
| Error paths, Outside systems, Mark what changed | Yes or no each |
361+
362+
`↑/↓` move between the choices, `←/→` change one, `Enter` on Focus types the focus. `g` puts the request in the agent's input after you confirm; it carries everything the agent needs (the change, your choices, the shape of the file), so nothing has to be installed, and Enter is never pressed for you. When the agent saves `.klaub/flows/<snapshot-id>.json` the diagram appears by itself. In the diagram the arrows follow the connectors, `Enter` opens the code of a changed step, `e` brings the form back to ask for another diagram and `L` reads the file again. `klaub flow prompt` prints the same request with the choices as flags (`--focus`, `--kind`, `--detail`, `--reach`, `--size`, `--start`, `--errors`, `--no-externals`, `--no-mark-changes`).
363+
364+
### Decisions
365+
366+
The Decisions window asks the agent for the technical decisions of the change, told so that you can decide without knowing the code or what the product does there. If the file does not exist, `g` offers to put the request in the agent's input. The request carries everything the agent needs (the change, what to list, the shape of the file and how to write each field), so nothing has to be installed; you read it there and press Enter yourself. when the agent saves `.klaub/decisions/<snapshot-id>.json`, the window fills in by itself. Klaub does not write the decisions: it validates the file against the snapshot and shows nothing that fails or belongs to another state of the code.
367+
368+
One decision fills the screen, with its question always visible at the bottom.
369+
370+
| Key | What it does |
371+
| --- | --- |
372+
| `→` or `Enter`, `←` or `Backspace` | Next decision, back |
373+
| `a`, `d` | Approve, reject (the same key again takes it back) |
374+
| `c` | Comment, with or without a verdict (`Enter` saves, `Ctrl+J` starts a new line) |
375+
| `j`/`k`, `PgDn`/`PgUp` | Scroll a long card |
376+
| `End` | Jump to the summary |
377+
| `f` | From the summary: hand the rejected and commented decisions back to the agent, after you confirm |
378+
| `g` | Ask the agent for the decisions |
379+
380+
After the last card the summary lists every decision with its answer. Rejected decisions and comments can be put in the agent's input (inside `klaub wrap`) or printed with `klaub decisions feedback` to paste anywhere. Your answers are stored in the review file.
381+
334382
## Command reference
335383

336384
| Command | What it does |
@@ -341,7 +389,8 @@ An optional mouse mode (`[ui] mouse = true`) adds wheel scrolling and clicks on
341389
| `klaub reviews list\|show\|export` | List stored reviews, print one, export as JSON, Markdown or SARIF |
342390
| `klaub feedback ID` | Print the feedback of a finished review |
343391
| `klaub tests list\|run` | List test associations, or run the configured command after confirming |
344-
| `klaub flow status\|context\|validate\|prompt` | Work with flow diagrams |
392+
| `klaub flow status\|context\|validate\|prompt` | Work with flow diagrams. `prompt` takes the same choices as the Flow window as flags |
393+
| `klaub decisions status\|context\|validate\|prompt\|feedback` | Work with the technical decisions: check a file, print the request for the agent, print your answers for the agent |
345394
| `klaub doctor` | Check Git, the terminal, grammars, the cache, directories and SSH |
346395
| `klaub crashes list\|show` | Read crash reports saved on this machine. Nothing is ever sent |
347396
| `klaub completions SHELL` | Print a shell completion script |
@@ -375,15 +424,15 @@ Run `klaub wrap claude` or `klaub wrap codex` in your repository, let the agent
375424

376425
### Why does Klaub not use an LLM?
377426

378-
A reviewer you cannot explain is hard to trust, and a second model is one more thing to check. Klaub parses your code, matches symbols across versions, builds a call graph and applies fixed rules, so the same change gives the same queue and every score has a reason. The trade is honest: it cannot find a bug that needs understanding what the code is for.
427+
A reviewer you cannot explain is hard to trust, and a second model is one more thing to check. Klaub parses your code, matches symbols across versions, builds a call graph and applies fixed rules, so the same change gives the same queue and every score has a reason. The trade is honest: it cannot find a bug that needs understanding what the code is for. The one place a model is involved is the Decisions window, and there it is your agent that writes the file, not Klaub: Klaub only validates it and records what you answer.
379428

380429
### Does Klaub send my code anywhere?
381430

382431
No. Klaub makes no network requests, has no telemetry and needs no account. Comments and reviews are files under `.klaub/` in your repository, and crash reports stay on your machine.
383432

384433
### Will Klaub press Enter or submit anything in my agent?
385434

386-
Never. Putting text in the agent's input always shows you the destination and the exact text first, and it sends one line as a bracketed paste, so it cannot be read as a submission. You press Enter yourself.
435+
Never. Putting a request or your answers in the agent's input shows you the destination and the exact text first, and everything is sent as one line in a bracketed paste, so it cannot be read as a submission. The `t` shortcut that adds what you are looking at as context skips that question unless you turn it on, and it works the same way: one line, no Enter. You press Enter yourself.
387436

388437
### How do I install the Klaub skills?
389438

‎benches/latency.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ use klaub::adapters::git::GitSnapshotProvider;
4040
use klaub::adapters::git::testing::{FixedClock, TempRepo};
4141
use klaub::adapters::parse::TreeSitterParser;
4242
use klaub::adapters::storage::{
43-
FsConfigSource, FsFileReader, FsFlowRepository, JsonReviewRepository, SqliteCache,
43+
FsConfigSource, FsDecisionRepository, FsFileReader, FsFlowRepository, JsonReviewRepository,
44+
SqliteCache,
4445
};
4546
use klaub::app::{OpenOptions, Ports, ReviewSession};
4647
use klaub::domain::config::Config;
@@ -132,6 +133,7 @@ fn ports(root: &Path, config: &Config, cache: Arc<dyn AnalysisCache>) -> Ports {
132133
parser: Arc::new(TreeSitterParser::new()),
133134
reviews: Arc::new(JsonReviewRepository::new(root)),
134135
flows: Arc::new(FsFlowRepository::new(root, &config.flows.directory).unwrap()),
136+
decisions: Arc::new(FsDecisionRepository::new(root, &config.decisions.directory).unwrap()),
135137
cache,
136138
files: Arc::new(FsFileReader),
137139
config: Arc::new(FsConfigSource::new(None)),

‎benches/ui.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ use klaub::adapters::git::GitSnapshotProvider;
2929
use klaub::adapters::git::testing::{FixedClock, TempRepo};
3030
use klaub::adapters::parse::TreeSitterParser;
3131
use klaub::adapters::storage::{
32-
FsConfigSource, FsFileReader, FsFlowRepository, JsonReviewRepository, SqliteCache,
32+
FsConfigSource, FsDecisionRepository, FsFileReader, FsFlowRepository, JsonReviewRepository,
33+
SqliteCache,
3334
};
3435
use klaub::app::{OpenOptions, Ports, ReviewSession};
3536
use klaub::domain::config::Config;
@@ -91,6 +92,9 @@ fn app() -> (TempRepo, App) {
9192
parser: Arc::new(TreeSitterParser::new()),
9293
reviews: Arc::new(JsonReviewRepository::new(repo.path())),
9394
flows: Arc::new(FsFlowRepository::new(repo.path(), &config.flows.directory).unwrap()),
95+
decisions: Arc::new(
96+
FsDecisionRepository::new(repo.path(), &config.decisions.directory).unwrap(),
97+
),
9498
cache: Arc::new(SqliteCache::in_memory().unwrap()),
9599
files: Arc::new(FsFileReader),
96100
config: Arc::new(FsConfigSource::new(None)),

‎docs/architecture.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ Dependencies point inward. An adapter names `domain` for the port it implements
7373
| `services::test_evidence` | Domain service | Test association heuristics, JUnit and Coverage.py report import, staleness. |
7474
| `services::context` | Domain service | Review brief, acceptance criteria, human review coverage. |
7575
| `services::flow` | Domain service | Flow document schema, validation, `current / missing / invalid / stale` status, prompt text. |
76+
| `services::decisions` | Domain service | Decisions document validation (the schema is `domain::decisions`), the tally of answers, the request for the agent and the text that hands the rejected and commented decisions back. |
7677
| `services::review` | Domain service | Comments, anchors and relocation, state machine, follow-up decisions, feedback payload. |
7778
| `app` | Application | Use cases that orchestrate ports and services into an analysis and a review session. |
7879
| `adapters::git` | Driven adapter | `SnapshotProvider` and workspace reading through the Git CLI. |
@@ -93,6 +94,7 @@ Ports live in `domain::ports`. Each has an in-memory fake in tests.
9394
| `SourceParser` | Symbols, calls, imports, entrypoint hints, highlight spans | `adapters::parse` |
9495
| `ReviewRepository` | Persist and load reviews and feedback | `adapters::storage` |
9596
| `FlowRepository` | Read flow documents by snapshot id | `adapters::storage` |
97+
| `DecisionRepository` | Read decisions documents by snapshot id | `adapters::storage` |
9698
| `AnalysisCache` | Derived-data cache keyed by content hash and parser version | `adapters::storage` |
9799
| `ConfigSource` | Load project and user configuration | `adapters::storage` |
98100
| `AgentChannel` | Describe the agent pane and paste text into it | `adapters::tmux` |

‎docs/configuration.md‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Every key has a default, so an empty file (or none) is valid. The schema lives i
3030
| `ui.mouse` | `false` | The wheel scrolls, a click on a tab switches to it, a click on a row selects it and a second click opens it. Off by default because capturing the mouse stops the terminal's own text selection until you hold the modifier it offers (usually `Shift`, or `Option` in iTerm2). The mouse only does what a key already does. |
3131
| `ui.hold_to_peek` | `false` | Hold `B` (the `peek_previous` key) while reading code to see the previous version, and let go to come back. It needs a terminal that reports key releases (the kitty keyboard protocol: kitty, Ghostty, WezTerm, recent iTerm2 and foot). Where that is missing, which includes tmux, the key says so and `v` still toggles. While it is on, the terminal sends keys as escape codes except inside text fields, which keep ordinary input so compose and `AltGr` characters still type. |
3232
| `integration.submit_mode` | `"manual"` | The only mode. Klaub never presses `Enter` in the agent. |
33-
| `integration.paste_requires_confirmation` | `true` | Putting text in the agent's input always asks first. |
33+
| `integration.ask_agent_confirms` | `false` | The `t` shortcut, which puts what is on screen in the agent's input for you to ask about it, does it with one key. `true` asks first, showing the destination and the text. |
3434
| `integration.tmux_prefix` | `"C-b"` | The prefix of the session chords. Moving between the agent and the reviewer needs no prefix: `Shift+←` goes to the agent and `Shift+→` to the reviewer. The rest are `Prefix+R`, `Prefix+A`, `Prefix+d`, `Prefix+Q` and `Prefix+?`. |
3535

3636
## `[keys]`
@@ -45,7 +45,7 @@ explain = "E"
4545

4646
The ids are:
4747

48-
`queue`, `structure`, `files`, `code`, `next_unreviewed`, `previous_unreviewed`, `next_critical`, `toggle_reviewed`, `toggle_bookmark`, `next_bookmark`, `explain`, `toggle_version`, `peek_previous`, `next_change`, `previous_change`, `toggle_code_scope`, `search`, `filter_high`, `filter_unreviewed`, `filter_introduced`, `filter_no_tests`, `filter_new`, `cycle_scope`, `cycle_module`, `clear_filters`, `history_back`, `history_forward`, `deeper_tree`, `shallower_tree`, `refresh`, `help`.
48+
`queue`, `structure`, `files`, `code`, `decisions`, `flow`, `ask_agent`, `next_unreviewed`, `previous_unreviewed`, `next_critical`, `toggle_reviewed`, `toggle_bookmark`, `next_bookmark`, `explain`, `toggle_version`, `peek_previous`, `next_change`, `previous_change`, `toggle_code_scope`, `search`, `filter_high`, `filter_unreviewed`, `filter_introduced`, `filter_no_tests`, `filter_new`, `cycle_scope`, `cycle_module`, `clear_filters`, `history_back`, `history_forward`, `deeper_tree`, `shallower_tree`, `refresh`, `help`.
4949

5050
The keys that only mean something inside some modes move the same way. A moved key answers to its new character in the modes it works in, and its old character stops acting there:
5151

@@ -56,6 +56,13 @@ The keys that only mean something inside some modes move the same way. A moved k
5656
| `open_file` | `o` | Code |
5757
| `inspect_contract` | `i` | Code |
5858
| `toggle_inspector` | `K` | Code |
59+
| `approve`, `reject` | `a`, `d` | Decisions |
60+
| `comment` | `c` | Decisions |
61+
| `send_decisions` | `f` | Decisions |
62+
| `ask_decisions` | `g` | Decisions |
63+
| `ask_flow` | `g` | Flow |
64+
| `edit_flow` | `e` | Flow |
65+
| `reload_flow` | `L` | Flow |
5966

6067
```toml
6168
[keys]
@@ -113,6 +120,14 @@ The weights of the explainable score. Every key is an integer and the review sho
113120

114121
`disabled` is a list of rule ids that are off, the way to silence a rule everywhere.
115122

123+
## `[decisions]`
124+
125+
| Key | Default | Meaning |
126+
| --- | --- | --- |
127+
| `enabled` | `true` | Look for `.klaub/decisions/<snapshot-id>.json`. |
128+
| `directory` | `".klaub/decisions"` | Relative to the worktree. |
129+
| `max_file_bytes` | `1048576` | Larger decisions files are rejected. |
130+
116131
## `[coverage]`, `[flows]`
117132

118133
| Key | Default | Meaning |
@@ -122,4 +137,4 @@ The weights of the explainable score. Every key is an integer and the review sho
122137
| `flows.enabled` | `true` | Look for `.klaub/flows/<snapshot-id>.json`. |
123138
| `flows.directory` | `".klaub/flows"` | Relative to the worktree. |
124139
| `flows.max_file_bytes` | `1048576` | Larger flow files are rejected. |
125-
| `flows.missing_prompt` | `"confirm"` | `confirm`: a missing diagram offers a request for the agent, prepared only after you confirm. `never`: no offer. |
140+
| `flows.missing_prompt` | `"confirm"` | `confirm`: the Flow window can put a request for the agent in its input, only after you confirm. `never`: it cannot. |

‎docs/internationalization.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ language = "pt" # "auto" (the default), "en", "pt" or "es"
2626
| --- | --- |
2727
| Every screen, hint, prompt and status message of the reader, the command palette and the help | The findings, their evidence and the reasons behind a priority (they come from the analysis rules) |
2828
| Messages and prompts of `klaub review`, `reviews`, `session`, `tests`, `crashes`, `version`, `wrap` and `doctor` | Everything machine-readable: JSON output (including the `detail` of `klaub doctor --format json`), review documents, Markdown and SARIF exports |
29-
| `klaub --help` and `klaub <command> --help` | The feedback and flow requests Klaub pastes into the agent, and the text of errors that come from Git, tmux or the file system |
29+
| `klaub --help` and `klaub <command> --help` | The feedback, flow and decisions requests Klaub pastes into the agent, and the text of errors that come from Git, tmux or the file system |
3030
| Yes/no prompts: `s`, `sim` (Portuguese) and `s`, `si`, `sí` (Spanish) are accepted next to `y` and `yes` | clap's own words in the usage line (`[OPTIONS]`, `<COMMAND>`), `[default: ...]` and its parse errors |
3131

3232
Keys do not change with the language. The reader's hints name the same keys in every language.

0 commit comments

Comments
 (0)