Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
966a994
refactor: introduce application runtime crate
zatevakhin Jul 20, 2026
6e7bc93
refactor: split runtime lifecycle modules
zatevakhin Jul 20, 2026
63803ad
test: colocate runtime module coverage
zatevakhin Jul 20, 2026
d6c8abe
refactor: route legacy tools through shared parser
zatevakhin Jul 20, 2026
1f4741f
refactor: centralize tool detail behavior
zatevakhin Jul 22, 2026
8051291
fix: keep tool detail parsing production-aligned
zatevakhin Jul 22, 2026
316e06c
test: retain UTF-8 replay coverage
zatevakhin Jul 22, 2026
3a7f012
refactor: move legacy state helpers to app
zatevakhin Jul 22, 2026
7e4831e
test: cover ACP replay edge cases
zatevakhin Jul 22, 2026
35d5f4d
fix: make ACP replay cards idempotent
zatevakhin Jul 22, 2026
5bdebdd
fix: dedupe terminal ACP thinking replays
zatevakhin Jul 22, 2026
d0f1c2f
test: migrate phase 2 slice 4 response coverage
zatevakhin Jul 22, 2026
8d6f1e7
test(ui): use native ACP fixtures
zatevakhin Jul 22, 2026
b5ea8ae
refactor: remove legacy server message reducer
zatevakhin Jul 22, 2026
37be7d6
test: restore native session state coverage
zatevakhin Jul 23, 2026
ed3a222
fix: discover nested session parents on load
zatevakhin Jul 23, 2026
08268ac
refactor: extract phase 3 domain types
zatevakhin Jul 23, 2026
3aab622
refactor: extract session domain types
zatevakhin Aug 8, 2026
054cf61
refactor: extract semantic tool details
zatevakhin Aug 8, 2026
1f31a2f
test: cover semantic tool detail regeneration
zatevakhin Aug 8, 2026
4154a76
refactor: complete semantic tool previews
zatevakhin Aug 8, 2026
224369d
fix: keep tool detail parsing filesystem-free
zatevakhin Aug 8, 2026
eb4118d
refactor: extract chat domain entries
zatevakhin Aug 8, 2026
3ed1e99
refactor: extract model and profile domain types
zatevakhin Aug 8, 2026
a07451d
refactor: remove model profile protocol bridges
zatevakhin Aug 8, 2026
baf243f
refactor: remove domain compatibility bridges
zatevakhin Aug 9, 2026
0076f35
refactor: extract auth domain core
zatevakhin Aug 9, 2026
6382b7a
test: cover oauth auth-method command serialization
zatevakhin Aug 9, 2026
6621ffa
refactor: separate session page wire types
zatevakhin Aug 9, 2026
dc5fd2b
test: strengthen session page adapter coverage
zatevakhin Aug 9, 2026
cb5054e
test: align session adapters with active ingress
zatevakhin Aug 9, 2026
7e836ca
refactor: separate session mutation results
zatevakhin Aug 9, 2026
5d53987
fix: preserve rejected undo frontier
zatevakhin Aug 9, 2026
12c39fa
refactor: separate oauth domain state
zatevakhin Aug 9, 2026
2c3543f
fix: separate auth ui feedback
zatevakhin Aug 9, 2026
c26c483
test: tighten auth ui state coverage
zatevakhin Aug 9, 2026
59fc0f7
test: cover auth notice transitions
zatevakhin Aug 9, 2026
12a73f6
test: cover auth ui notice resets
zatevakhin Aug 9, 2026
f18afc1
refactor: introduce internal command seam
zatevakhin Aug 9, 2026
f366145
fix: centralize session command mapping
zatevakhin Aug 9, 2026
8944891
refactor: complete internal command bus migration
zatevakhin Aug 9, 2026
00c5989
fix: remove orphan pending command queue
zatevakhin Aug 9, 2026
23a6747
refactor: remove legacy protocol scaffolding
zatevakhin Aug 9, 2026
d54fea7
fix: narrow legacy protocol cleanup
zatevakhin Aug 18, 2026
4c4469e
refactor: separate mesh domain values
zatevakhin Aug 18, 2026
03c5d9e
fix: align remote session contracts
zatevakhin Aug 19, 2026
90140bd
fix: honor ACP session list contract
zatevakhin Aug 19, 2026
0b47230
refactor: separate delegation notification values
zatevakhin Aug 19, 2026
f03e742
refactor: split protocol contracts
zatevakhin Aug 19, 2026
f40489b
refactor: complete phase 3 acceptance cleanup
zatevakhin Aug 20, 2026
5e67fd2
refactor: extract diagnostics state
zatevakhin Aug 20, 2026
cc2fd54
refactor: extract auth state
zatevakhin Aug 20, 2026
e37eb68
refactor: extract profiles state
zatevakhin Aug 20, 2026
de20012
refactor: extract mesh state
zatevakhin Aug 20, 2026
a3b2973
refactor: extract navigation state
zatevakhin Aug 20, 2026
2834d5f
refactor: extract models state
zatevakhin Aug 20, 2026
b524aab
refactor: extract sessions state
zatevakhin Aug 21, 2026
1590c36
refactor: extract connection state
zatevakhin Aug 21, 2026
77da7b8
refactor: extract composer state
zatevakhin Aug 21, 2026
834654f
refactor: extract delegates state
zatevakhin Aug 21, 2026
46a275b
refactor: extract chat state owner
zatevakhin Aug 21, 2026
baae47c
refactor: extract render state
zatevakhin Aug 21, 2026
9202042
refactor: centralize application effects
zatevakhin Aug 22, 2026
779980a
fix: address application effect review
zatevakhin Aug 22, 2026
29e8660
fix: preserve phase 5 effect ordering
zatevakhin Aug 22, 2026
e6788e4
fix: make effect scheduling fair
zatevakhin Aug 22, 2026
38c0517
fix: remove terminal event drain starvation
zatevakhin Aug 22, 2026
0884dbb
refactor: split the ACP subsystem (#95)
zatevakhin Aug 27, 2026
64a5cfc
refactor: extract feature reducers (#96)
zatevakhin Aug 27, 2026
12ce6da
refactor: separate domain and rendering (#97)
zatevakhin Aug 28, 2026
1688ab2
refactor: split input and UI by feature (#99)
zatevakhin Aug 29, 2026
10e5e8c
test: harden persistence isolation (#100)
zatevakhin Aug 29, 2026
f3c9157
test: colocate start-page session input tests (#101)
zatevakhin Aug 30, 2026
bc94a2e
test: colocate session popup input tests (#102)
zatevakhin Aug 30, 2026
c60fc22
test: strengthen new session input contracts
zatevakhin Aug 30, 2026
46d902c
test: colocate delegate input tests
zatevakhin Aug 30, 2026
738be83
test: colocate delegate renderer tests
zatevakhin Aug 30, 2026
57330c1
test: colocate elicitation and state tests (#106)
zatevakhin Aug 30, 2026
e89f305
test: colocate chat renderer tests (#107)
zatevakhin Aug 30, 2026
86273fe
test: close ACP contract coverage (#108)
zatevakhin Aug 30, 2026
3ffbbfd
refactor: complete architecture cleanup (#109)
zatevakhin Aug 30, 2026
335aa17
fix(tui): batch terminal events to reduce input lag
zatevakhin Aug 31, 2026
4060689
fix: address CodeRabbit security and correctness findings
zatevakhin Sep 1, 2026
d712eba
chore: flake.lock update
zatevakhin Sep 1, 2026
68fd8cc
fix(acp): keep long-running prompts correlated until completion
zatevakhin Sep 1, 2026
219f8fc
test(ui): add deterministic ratatui snapshot coverage
zatevakhin Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
uses: swatinem/rust-cache@v2

- name: Run Clippy
run: cargo clippy --all-targets -- -D warnings
run: cargo clippy --all-targets --all-features -- -D warnings

build-and-test:
name: Build & Test
Expand All @@ -57,7 +57,7 @@ jobs:
uses: swatinem/rust-cache@v2

- name: Build
run: cargo build --all-targets
run: cargo build --all-targets --all-features

- name: Run tests
run: cargo test --all-targets
run: cargo test --all-targets --all-features
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.direnv/
result
target/
*.snap.new
44 changes: 44 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ similar = "2"
fuzzy-matcher = "0.3"
syntect = { version = "5", default-features = false, features = ["default-syntaxes", "default-themes", "regex-onig"] }
unicode-width = "0.2"
chrono = { version = "0.4.45", default-features = false, features = ["std"] }
toml = "1"
dirs = "6"
fs2 = "0.4"
tokio-tungstenite = "0.29"

[dev-dependencies]
insta = "1.48.0"
serial_test = "3"
132 changes: 131 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,131 @@
# qmtui
# qmtui

`qmtui` is the Rust terminal interface for QueryMT agents. It connects to an
Agent Client Protocol (ACP) endpoint, translates terminal and ACP activity into
application events, updates feature-owned state, executes explicit effects, and
renders the result with Ratatui.

## Architecture

The binary entry point is intentionally small: `src/main.rs` installs Tokio and
calls `qmtui::runtime::run()`. The library is organized around these layers:

| Area | Responsibility |
| --- | --- |
| `src/domain/` | Semantic data shared by state and features; no terminal, UI, transport, channel, filesystem, or process dependencies. |
| `src/*_state.rs` | Feature-owned semantic state and reducer operations. `src/render_state.rs` separately owns layout metrics, render caches, and Ratatui primitives. |
| `src/features/*/input/` | Feature input interpretation. These modules may consume Crossterm key types but do not perform I/O or know about rendering and ACP transport. |
| `src/features/*/view/` | Feature rendering with Ratatui. Views receive state or narrow render inputs and do not know about ACP transport. |
| `src/application.rs` | The `AppEvent`, `Effect`, and `update` boundary for application transitions. |
| `src/handlers.rs` | Root input routing and cross-feature composition over the feature input modules. |
| `src/acp/` | ACP connections, transport, protocol translation, replay, and command dispatch. This subsystem emits application-facing ACP events and has no UI dependency. |
| `src/runtime/` | Process composition: endpoint selection, channels, terminal lifecycle, event scheduling, persistence, clipboard/editor work, and effect execution. |
| `src/ui/mod.rs` | Root screen and popup composition through `ui::draw`; detailed rendering remains in feature view modules. |
| `src/app.rs` | Crate-local composition root joining the twelve feature/state owners plus `should_quit`. |

`App` fields are `pub(crate)` deliberately. Routing, persistence, rendering,
and runtime composition need to combine multiple owners, while public getters
for every field would broaden rather than improve the internal boundary.
Reducers are pure with respect to external side effects: they update in-memory
state and return `Effect` values for the runtime to execute.

## Event And Effect Flow

The application follows one production event path:

```text
Crossterm key/mouse ACP connection runtime/connection/supervisor
| | |
+-----------------------+---------------------------+
v
runtime::event_loop -> AppEvent
|
v
application::update
/ \
handlers + feature ACP/state reducers
input modules and coordination
\ /
state changes
+ Vec<Effect>
|
v
runtime::EffectExecutor
command channel / persistence / clipboard / editor / terminal / quit
|
RuntimeEvent feedback
|
application::update
|
v
ui::draw -> feature views -> RenderState
```

`runtime::event_loop::run_loop` schedules terminal, ACP, connection, supervisor,
and tick events without letting a permanently ready source starve the others.
It draws the current state, sends each event through `application::update`, and
passes returned effects to `runtime::EffectExecutor::execute`. Runtime work that
needs application feedback is converted to `RuntimeEvent` and re-enters the
same update path rather than mutating state from a background adapter.

ACP input follows the same boundary. `acp::inbound` decodes WebSocket or shared
connection input, `acp::notification::translate` converts SDK notifications to
application-facing updates, and `acp::notification::apply` handles replay and
stream ordering before emitting `AcpAppEvent`. The runtime event loop wraps that
as `AppEvent::Acp`; ACP code never renders or imports `crate::ui`.

## Boundary Rules

The architectural guidelines are:

- Domain modules stay free of UI/terminal crates, Tokio synchronization,
filesystem/process access, ACP transport, and protocol DTO imports.
- Top-level semantic `*_state.rs` modules stay free of Ratatui, Crossterm,
channels, I/O, process spawning, and ACP transport.
- Feature input may use Crossterm but not Ratatui, Tokio synchronization, I/O,
process spawning, or ACP transport. Feature views and root UI may use Ratatui
but not ACP transport.
- ACP may use Tokio, JSON-RPC, SDK, and transport types, but not `crate::ui`.
- Removed migration surfaces stay removed: legacy server-message symbols,
diagnostic forwarding methods on `App`, root UI temporary exports, broad
`use crate::handlers::*`, and crate-wide dead-code suppression.
- `src/main.rs` remains a thin call to `qmtui::runtime::run()`, and the twelve
`App` owners plus `should_quit` remain `pub(crate)`.

Intentional exceptions are narrow and documented:

- `src/render_state.rs`, feature views, and `src/ui/` may use Ratatui because
they own render caches, layout primitives, or rendering.
- Feature input may use Crossterm key types. Test modules may use `use super::*`.
- ACP owns its Tokio, JSON-RPC, SDK, and transport dependencies. Runtime,
configuration, session, and persistence adapters may own channels, files,
subprocesses, terminal operations, and other side effects.
- `src/themes_gen.rs` retains its generated crate-level `allow(dead_code)`;
targeted item-level allowances remain valid for retained decoded fields.
- The `cfg(test)` App-to-render adapters in `src/features/chat/view/mod.rs` are
retained for root cache/session integration tests.
- `TuiConfig::delegate_models` remains a read-only migration field, and hidden
`--acp-websocket` remains a compatibility alias for `--ws`.

## Testing

Tests live with the code that owns the contract. Domain and state modules test
semantic transitions; feature input modules test key-to-intent behavior; feature
view modules use Ratatui test backends for layout and rendering; ACP modules test
translation, command, replay, JSON-RPC, and transport contracts; and application,
handler, runtime, and root UI tests cover composition and event/effect ordering.

Run the same primary gates used by CI:

```sh
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo build --all-targets --all-features
cargo test --all-targets --all-features
```

Deterministic tests do not replace environmental validation. Live ACP/backend
behavior, an external compatible WebSocket service, subprocess stdio framing,
real terminal interaction, system clipboard integration, and complete
interactive UI smoke require suitable external services or a manual environment
and are not claimed by the automated suite.
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading