Skip to content

TUI shell: ask_user modal, pickers, autocomplete, zeroline skin - #126

Merged
gnanam1990 merged 2 commits into
agent-runtimefrom
tui-shell
Jun 7, 2026
Merged

TUI shell: ask_user modal, pickers, autocomplete, zeroline skin#126
gnanam1990 merged 2 commits into
agent-runtimefrom
tui-shell

Conversation

@gnanam1990

@gnanam1990 gnanam1990 commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

TUI shell — ask_user modal, pickers, autocomplete, zeroline skin

Ports the interactive TUI shell from the reskin branch. Stacked on #125 (agent runtime) — it needs agent.AskUserRequest/AskUserResponse/AskUserQuestion + Options.OnAskUser/ContextWindow from that PR, so this targets the agent-runtime branch; I'll retarget it to main once #125 lands.

Built as a per-file 3-way merge of internal/tui (preserving main's #113 session-wiring drift — SessionID/Model/ReasoningEffort/Cwd), then a full zenline→zeroline rename (imports, identifiers, the Skin string, and zenline_view.gozeroline_view.go) since the rendering package merged earlier as internal/zeroline.

What's in it

  • ask_user — interactive questionnaire modal wired to agent.OnAskUser via an answer channel; transcript rows + session payloads; degrades gracefully with no interactive surface.
  • Compaction sizingmodelContextWindow() sets the agent's ContextWindow from the active model's registry entry; unknown/custom models → 0 (compaction off).
  • Pickers (model / theme / effort / mode) + slash-command autocomplete overlay.
  • zeroline skin rendering (boot / home / chat) behind Options.Skin.
  • Checkpoint recording — before each mutating tool, the run batches an EventSessionCheckpoint in order and flushes it at end-of-run and on cancel.

One cross-module change (called out)

The checkpoint flow needs the event batched in order with the run's other session events (recording it out-of-band reorders it, and breaks multi-call ordering). That requires the snapshot-only entry point, so I exposed sessions.SnapshotForCheckpoint with an explicit orphan-blob safe-usage contract: most callers should use the atomic CaptureToolCheckpoint; this one is for the batch-in-order pattern where the caller (the TUI) guarantees prompt recording incl. on cancel.

Testing

115 tui tests pass. go build ./..., go vet, go test ./..., go test -race ./internal/tui/ ./internal/sessions/, GOOS=windows go build ./... all green. No new deps.

Unblocks the cli wiring PR. Part of decomposing #101.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Slash-command autocomplete with keyboard navigation, alias matching, and completion; suppressed while modals/questionnaires are active
    • Expanded interactive pickers (model, effort, mode, theme) and questionnaire (ask-user) flows
    • /mode and /rewind commands enhanced; /rewind reports restored/deleted/skipped files
    • New "zeroline" UI skin with theme/dark-mode controls and tailored rendering
  • Bug Fixes

    • Session checkpoint events and related state are reliably flushed/persisted when runs are cancelled; in-memory session state refreshes after rewind

… zeroline skin

Ports the interactive TUI shell from the reskin branch onto the agent-runtime branch (stacked: needs agent.AskUserRequest/AskUserResponse/AskUserQuestion + Options.OnAskUser/ContextWindow from that PR). Built as a per-file 3-way merge of internal/tui (preserving main's #113 session-wiring drift), then renamed zenline->zeroline everywhere (imports, identifiers, skin string, and zenline_view.go -> zeroline_view.go) since the rendering package merged as internal/zeroline.

- ask_user: interactive questionnaire modal wired to agent.OnAskUser (answer channel), with transcript rows + session payloads; degrades gracefully when no interactive surface.

- compaction: model.go sizes agent ContextWindow from the active model's registry context window (modelContextWindow); unknown models -> 0 (compaction off).

- pickers (model/theme/effort/mode) + slash-command autocomplete overlay; zeroline skin rendering (boot/home/chat) behind Options.Skin.

- checkpoint recording: before each mutating tool the run batches an EventSessionCheckpoint (in order) and flushes it at end-of-run and on cancel. This required exposing sessions.SnapshotForCheckpoint (the snapshot-only entry point) with an explicit orphan-blob safe-usage contract — most callers should use the atomic CaptureToolCheckpoint, but the TUI needs the event batched IN ORDER with the run's other session events.

build/vet/-race/full-suite + GOOS=windows build green; 115 tui tests pass; no new deps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Zero automated PR review

Verdict: No blockers found

Blockers

  • None found.

Validation

  • [pass] Diff hygiene: git diff --check
  • [pass] Tests: go test ./...
  • [pass] Build: go run ./cmd/zero-release build
  • [pass] Smoke build: go run ./cmd/zero-release smoke

Scope

Head: 066ee5c7de9c
Changed files (22): internal/sessions/checkpoint.go, internal/tui/ask_user_test.go, internal/tui/autocomplete.go, internal/tui/autocomplete_test.go, internal/tui/command_center.go, internal/tui/commands.go, internal/tui/model.go, internal/tui/model_catalog.go, internal/tui/model_test.go, internal/tui/options.go, internal/tui/picker.go, internal/tui/picker_test.go, and 10 more

This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality.

@gnanam1990
gnanam1990 requested a review from Vasanthdev2004 June 7, 2026 16:35

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the TUI shell work. I ran go test ./..., go run ./cmd/zero-release build, and git diff --check at 952d161; all passed locally. I found one blocker in the newly wired /rewind path, so I am requesting changes before this stacks cleanly.

The PR is also stacked on agent-runtime, so it should be merged after that base branch is accepted/merged.

Comment thread internal/tui/session_controls.go
@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a38ebc60-2cf8-4afe-b4c9-07aa9440ddc5

📥 Commits

Reviewing files that changed from the base of the PR and between 952d161 and 066ee5c.

📒 Files selected for processing (2)
  • internal/tui/session_controls.go
  • internal/tui/session_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/tui/session_controls.go

Walkthrough

Adds interactive ask-user questionnaires, slash-command autocomplete, reusable pickers (model/mode/effort/theme), a "zeroline" UI skin and rendering, exported Store.SnapshotForCheckpoint with an orphan-vulnerability contract, ordered mutation checkpointing before tool execution, and cancellation-safe flushing of session events; includes extensive TUI and unit tests.

Changes

Interactive TUI with Questionnaire, Autocomplete, and Pickers

Layer / File(s) Summary
Checkpoint export and orphan-vulnerability contract
internal/sessions/checkpoint.go
CaptureToolCheckpoint now uses the exported Store.SnapshotForCheckpoint; the API documents returned payloads are ORPHAN-VULNERABLE until EventSessionCheckpoint is appended.
Ask-user questionnaire state and messages
internal/tui/model.go
Adds askUserRequestMsg, pendingAskUserPrompt, TUI state, key handling, and lifecycle helpers for questionnaire collection and resolution.
Ask-user transcript and prompt rendering
internal/tui/transcript.go, internal/tui/rendering.go
Adds rowAskUser, ask-user row builders/payload mapping, and renderFocusedAskUserPrompt bordered prompt rendering with options/hint.
Ask-user unit tests
internal/tui/ask_user_test.go
Tests synchronous prompt setup, ordered answer collection, Esc-cancellation delivery, and Enter interception while pending.
Slash-command autocomplete
internal/tui/autocomplete.go
Implements slash-token detection, case-insensitive prefix matching against command names/aliases, canonical-name suggestions, navigation (Tab/Up/Down) with wraparound, completion (replace input + space), and dismissal.
Autocomplete tests
internal/tui/autocomplete_test.go
Covers activation, alias-to-canonical listing, navigation, completion, Esc behavior, modal suppression, and rendering in both skins.
Single-select picker abstraction
internal/tui/picker.go
Adds commandPicker and pickerKind, navigation helpers, and constructors for model/mode/effort/theme pickers.
Picker tests & integration
internal/tui/picker_test.go
Validates opening, cancellation, selection applying handlers, busy-blocking when runs are pending, skin-specific theme picker availability, and rendering.
TUI model runtime & overlays
internal/tui/model.go
Extends model with flushRunIDs, overlay state (suggestions, picker), zeroline skin fields, cancel/defer-quit behavior, agent OnText/OnAskUser integrations, and ordered mutation checkpoint snapshotting before tool calls.
Command kinds and behavior
internal/tui/commands.go, internal/tui/command_center.go
Adds commandMode and commandRewind definitions; updates /model and /mode to use ResolveWithFallback, include notices, reset unsupported effort, and apply mode MaxTurns; assembles multiline success/notice output.
Rewind command and target resolution
internal/tui/session_controls.go
Implements /rewind handling: argument normalization, resolveRewindTarget scanning for latest checkpoint or parsing numeric sequence, ApplyRewind call, and post-reload of in-memory session state.
Session event filtering for cancellation flush
internal/tui/session.go
Adds flushableSessionEvents to drop trailing EventError when flushing canceled runs while preserving checkpoint/tool/permission events.
Cancellation and interrupt tests
internal/tui/session_test.go
Adds tests asserting cancelled runs flush checkpoint and tool-call events, record a single cancellation error, and defer quit until flush completes.
Zeroline UI skin and mapping
internal/tui/zeroline_view.go, internal/tui/zeroline_view_test.go
Adds zeroline renderer, timed tick, theme key controls (Ctrl+L/Ctrl+T/digits), mapping helpers for ask-user/suggestions/picker/permission/transcript, and tests for views and theme keys.
Picker/suggestion overlay rendering
internal/tui/view.go, internal/tui/rendering.go
Adds suggestionOverlay, pickerOverlay, nextPermissionMode, and pickerBusyText plus aligned/highlighted overlay rendering and prompt formatting.
Misc: program options & context window
internal/tui/run.go, internal/tui/model_catalog.go, internal/tui/options.go
Refactors Run() program option passing, adds modelContextWindow helper for compaction, and exposes Options fields Skin, ThemeVariant, and ThemeDark.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Gitlawb/zero#112: Overlaps checkpoint snapshot/export refactor and SnapshotForCheckpoint changes.
  • Gitlawb/zero#66: Related edits to model-switch effort-reset messaging in command_center.
  • Gitlawb/zero#57: Related TUI command definitions/refactor used by autocomplete/picker flows.

Suggested reviewers

  • Vasanthdev2004
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main features added: ask_user modal, pickers, autocomplete, and zeroline skin—all matching the substantial changes throughout the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tui-shell

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
internal/tui/zeroline_view.go (1)

189-224: 💤 Low value

Consider applying De Morgan's law for clarity.

Line 209's negated conjunction could be simplified to an equivalent disjunction: r.id == "" || !resultIDs[r.id], which reads more naturally.

♻️ Optional simplification
-			Running: !(r.id != "" && resultIDs[r.id]),
+			Running: r.id == "" || !resultIDs[r.id],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/zeroline_view.go` around lines 189 - 224, In zerolineRows(),
simplify the Running expression by applying De Morgan: replace the negated
conjunction used when building the row for rowToolCall (currently using !(r.id
!= "" && resultIDs[r.id])) with the equivalent disjunction r.id == "" ||
!resultIDs[r.id]; update the Running field in the zeroline.Row construction for
the rowToolCall case to use that clearer expression (refer to function
zerolineRows, the loop variable r, and the resultIDs map).
internal/tui/ask_user_test.go (1)

124-127: ⚡ Quick win

Remove the empty if block.

The linter (SA9003) flags this empty branch. The comment explains the behavior but doesn't assert anything. Remove the if statement and keep the comment standalone, or remove both if the behavior is sufficiently documented elsewhere.

♻️ Suggested cleanup
-	if next.pending {
-		// cancelRun is the normal Esc path; here we only cancel the prompt, the
-		// run continues with the degraded answers.
-	}
+	// cancelRun is the normal Esc path; here we only cancel the prompt, the
+	// run continues with the degraded answers (pending stays true).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/ask_user_test.go` around lines 124 - 127, Remove the empty
conditional that checks next.pending in internal/tui/ask_user_test.go: delete
the "if next.pending { ... }" block flagged by SA9003 and either leave the
explanatory comment as a standalone comment above the surrounding code or remove
the comment entirely; ensure any mention of cancelRun remains in nearby
comments/tests if still relevant so behavior is still documented.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/tui/ask_user_test.go`:
- Around line 124-127: Remove the empty conditional that checks next.pending in
internal/tui/ask_user_test.go: delete the "if next.pending { ... }" block
flagged by SA9003 and either leave the explanatory comment as a standalone
comment above the surrounding code or remove the comment entirely; ensure any
mention of cancelRun remains in nearby comments/tests if still relevant so
behavior is still documented.

In `@internal/tui/zeroline_view.go`:
- Around line 189-224: In zerolineRows(), simplify the Running expression by
applying De Morgan: replace the negated conjunction used when building the row
for rowToolCall (currently using !(r.id != "" && resultIDs[r.id])) with the
equivalent disjunction r.id == "" || !resultIDs[r.id]; update the Running field
in the zeroline.Row construction for the rowToolCall case to use that clearer
expression (refer to function zerolineRows, the loop variable r, and the
resultIDs map).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ab6d5627-bd1c-45ee-a8df-59bc79ea16d7

📥 Commits

Reviewing files that changed from the base of the PR and between cbaca4a and 952d161.

📒 Files selected for processing (22)
  • internal/sessions/checkpoint.go
  • internal/tui/ask_user_test.go
  • internal/tui/autocomplete.go
  • internal/tui/autocomplete_test.go
  • internal/tui/command_center.go
  • internal/tui/commands.go
  • internal/tui/model.go
  • internal/tui/model_catalog.go
  • internal/tui/model_test.go
  • internal/tui/options.go
  • internal/tui/picker.go
  • internal/tui/picker_test.go
  • internal/tui/rendering.go
  • internal/tui/run.go
  • internal/tui/session.go
  • internal/tui/session_controls.go
  • internal/tui/session_controls_test.go
  • internal/tui/session_test.go
  • internal/tui/transcript.go
  • internal/tui/view.go
  • internal/tui/zeroline_view.go
  • internal/tui/zeroline_view_test.go

…r rewind

handleRewindCommand applied the rewind to the store but returned m unchanged, so m.sessionEvents, m.activeSession, and the transcript still held the dropped events. The next prompt's sessionPrompt re-sent m.sessionEvents as ContextEvents, so a user could /rewind and still have the rewound-away tool calls/messages included in the next turn.

After a successful ApplyRewind, reload the session metadata (store.Get) + events (ReadEvents) and rebuild the transcript from the truncated log — so the dropped events neither reach the agent nor stay on screen. Regression: TestRewindRefreshesInMemorySessionState (resume → rewind latest → assert sessionEvents shrink, transcript + next prompt no longer contain the post-checkpoint content).

build/vet/-race/full-suite + GOOS=windows build green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gnanam1990

Copy link
Copy Markdown
Collaborator Author

@Vasanthdev2004 fixed the P1 — after ApplyRewind the handler now reloads session metadata (store.Get) + events (ReadEvents) and rebuilds the transcript from the truncated log, so the rewound-away events no longer linger in the transcript or get re-sent as ContextEvents on the next prompt. Added TestRewindRefreshesInMemorySessionState (resume → /rewind latest → submit; asserts the dropped content is gone from sessionEvents, transcript, and the next prompt). build/vet/-race/full-suite + windows green. @coderabbitai review

@gnanam1990
gnanam1990 requested a review from Vasanthdev2004 June 7, 2026 16:57
@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

@gnanam1990 Got it — triggering a review now. I'll look at the /rewind in-memory state refresh fix, the transcript rebuild logic, and the new TestRewindRefreshesInMemorySessionState test coverage.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed current head 066ee5c.

The previous /rewind blocker is fixed: after ApplyRewind, the TUI reloads session metadata/events and rebuilds the transcript, and the new regression test verifies rewound-away content is not re-sent in the next prompt.

Validation passed locally:

  • go test ./...
  • go run ./cmd/zero-release build
  • go run ./cmd/zero-release smoke
  • git diff --check origin/agent-runtime...origin/pr-126

CI/CodeRabbit are green. Approved.

@gnanam1990
gnanam1990 merged commit 56d4397 into agent-runtime Jun 7, 2026
6 checks passed
@Vasanthdev2004
Vasanthdev2004 deleted the tui-shell branch June 28, 2026 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants