Skip to content

feat(cli): add an in-app transcript viewer - #2999

Merged
Astro-Han merged 5 commits into
apache:mainfrom
me2seeks:feat/tui-transcript-viewer
Aug 22, 2026
Merged

feat(cli): add an in-app transcript viewer#2999
Astro-Han merged 5 commits into
apache:mainfrom
me2seeks:feat/tui-transcript-viewer

Conversation

@me2seeks

@me2seeks me2seeks commented Aug 14, 2026

Copy link
Copy Markdown
Contributor
English

Summary

Long TUI sessions currently depend on the terminal's native scrollback. Once the terminal discards old rows, the beginning of the session is no longer reachable even though the CLI still has the transcript projection.

This adds a read-only /transcript viewer that:

  • opens at the latest part of the transcript and follows new output while it remains at the end;
  • scrolls by line with Up/Down and by page with Page Up/Page Down;
  • jumps with Home/End;
  • closes with q or Escape.

The normal live surface remains unpaged and keeps using terminal scrollback. The viewer renders through the existing CLI transcript projection with detached geometry and stable clone keys, so browsing cannot replace the memoized lines or viewport coordinates used by live scrollback reconciliation.

Escape intentionally closes this read-only overlay. Editing an earlier turn remains on Maka's existing double-Escape rewind flow, which branches the session and refills the prompt; the viewer does not introduce a second mutation path.

Fixes #2997

Before / after

The captures use the same 40-line TUI integration fixture. Before, the live screen has already scrolled past lines 1-21. After opening /transcript and pressing Home, the retained canonical projection makes the beginning reachable again.

Before — live terminal scrollback only

Before: the live TUI only shows filler lines 22 through 40

After — /transcript at Home

After: the transcript overlay shows the prompt and filler lines starting at 1

Verification

  • npm --workspace packages/cli test — 281/281
  • focused TUI integration capture: browses a long transcript without depending on terminal scrollback
  • git diff --check
  • correctness review and simplify-audit: no P0-P3 findings

Status

Ready for review. The requested visual evidence is included above.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally
  • Before/after captures document the UI behavior

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

AI disclosure: Codex implemented the change under me2seeks's direction and review.

中文

概要

长 TUI 会话目前依赖终端自身的 scrollback。终端丢弃较早的行后,即使 CLI 仍保留 transcript 投影,用户也无法再回到会话开头。

本 PR 新增只读的 /transcript 浏览器:

  • 打开时定位到 transcript 末尾,并在停留末尾时跟随新输出;
  • Up/Down 按行滚动,Page Up/Page Down 按页滚动;
  • Home/End 跳到开头或结尾;
  • q 或 Escape 关闭。

正常的实时界面仍不分页,继续使用终端 scrollback。浏览器复用现有 CLI transcript 投影,并使用分离的 geometry 与稳定的 clone key,因此浏览不会替换实时 scrollback reconciliation 所依赖的 memoized lines 或 viewport 坐标。

Escape 在这里只关闭只读 overlay。编辑较早轮次仍使用 Maka 现有的双击 Escape rewind:它会创建会话分支并回填 prompt;浏览器不会引入第二条修改会话的路径。

关联并关闭 #2997

前后对比

两张截图来自同一个 40 行 TUI 集成夹具。before 中实时界面已滚过第 1–21 行;after 中打开 /transcript 并按 Home 后,仍可从 canonical transcript 投影访问会话开头。

Before — 仅依赖终端 scrollback

Before:实时 TUI 只剩 filler line 22 到 40

After — /transcript 跳到开头

After:transcript overlay 重新显示 prompt 与 filler line 1 起的内容

验证

  • npm --workspace packages/cli test — 281/281
  • focused TUI 集成截图夹具:browses a long transcript without depending on terminal scrollback
  • git diff --check
  • 正确性复核与 simplify-audit:无 P0-P3 finding

状态

已就绪,可重新 review;维护者要求的视觉证据已补在上方。

AI 披露:本变更由 Codex 在 me2seeks 的指导与审核下实现。

\n\n## AI use\n- [ ] No generative tool was used for implementation.\n- [x] Generative tooling was used and the result was reviewed and verified by the author.\n\nTool(s) and scope: OpenAI Codex (Maka) assisted with implementation, tests, review remediation, and PR documentation.\n\nFinal squash trailer: `Generated-by: Maka`

@me2seeks
me2seeks force-pushed the feat/tui-transcript-viewer branch from 2ae2bcc to ff6601e Compare August 17, 2026 16:07
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@me2seeks, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 59 minutes

Limit details: You’ve used all 3 included reviews currently available.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d8dbafb-1eff-407a-95b8-68be306bae03

📥 Commits

Reviewing files that changed from the base of the PR and between 62556ab and 7c952e3.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • packages/cli/src/__tests__/pi-tui-runner.test.ts
  • packages/cli/src/__tests__/pi-tui-transcript-viewer.test.ts
  • packages/cli/src/pi-tui-layout.ts
  • packages/cli/src/pi-tui-runner.ts
  • packages/cli/src/pi-tui-transcript-viewer.ts
  • packages/cli/src/tui-primary-guidance.ts
  • packages/core/src/slash-command-catalog.ts

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

@me2seeks
me2seeks marked this pull request as ready for review August 17, 2026 17:05
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

feat(cli): add in-app transcript viewer for long TUI sessions

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add a read-only /transcript full-screen overlay to browse the entire TUI transcript.
• Keep the live surface unpaged, using detached geometry and stable clones to avoid cache
 corruption.
• Add regression tests and document the new command in the changelog.
Diagram

graph TD
  U["User"] --> R["TUI runner"] --> C["/transcript command"] --> V["Transcript viewer overlay"] --> D["Transcript renderDocument()"] --> F["Transcript renderer"] --> S[("Transcript state/cache")]
  T["Terminal rows"] --> V
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Always-on in-app pager for the live surface
  • ➕ One unified scrolling model; no separate overlay mode
  • ➕ Could eliminate dependence on terminal scrollback entirely
  • ➖ Higher risk: changes core live reconciliation and keybindings
  • ➖ More complex to preserve existing rewind/edit flows
2. Persist and render a separate plain-text transcript buffer
  • ➕ Viewer would not interact with live memoization/caches at all
  • ➕ Simpler rendering (no geometry projection concerns)
  • ➖ Duplicates formatting logic and risks divergence from canonical transcript rendering
  • ➖ Loses structured rendering features provided by the existing projection
3. Delegate to external pager (e.g., less) via export
  • ➕ Minimal TUI complexity; leverages mature navigation features
  • ➕ Works well for extremely long transcripts
  • ➖ Breaks in-app flow; requires spawning processes and handling terminal mode switches
  • ➖ Does not solve 'keep following live output' without additional plumbing

Recommendation: The chosen overlay approach is the best tradeoff: it adds an explicit, read-only browsing mode while keeping the live surface unchanged. Reusing the existing transcript projection plus detached geometry and stable clone keys avoids cache/scrollback reconciliation regressions that an always-on pager would risk.

Files changed (7) +453 / -3

Enhancement (3) +159 / -1
pi-tui-runner.tsWire /transcript slash command to a full-screen overlay viewer +32/-1

Wire /transcript slash command to a full-screen overlay viewer

• Adds a TranscriptViewerOverlay-based overlay that renders the transcript document and sizes to terminal rows. Registers the '/transcript' command with usage validation and updates help text to clarify live transcript scrolling.

packages/cli/src/pi-tui-runner.ts

pi-tui-transcript-viewer.tsImplement TranscriptViewerOverlay with scrolling, jumping, and close controls +126/-0

Implement TranscriptViewerOverlay with scrolling, jumping, and close controls

• Adds a read-only full-screen viewer component that renders a header/body/footer layout, supports Up/Down, Page Up/Down, Home/End, and closes via 'q' or Escape. Includes logic to follow new output only when positioned at the end and to prioritize content in very small terminals.

packages/cli/src/pi-tui-transcript-viewer.ts

slash-command-catalog.tsRegister /transcript in the slash command catalog +1/-0

Register /transcript in the slash command catalog

• Adds the 'transcript' command ID as a TUI-only command requiring an active session so help/catalog ownership remains centralized.

packages/core/src/slash-command-catalog.ts

Bug fix (1) +34 / -0
pi-tui-layout.tsAdd renderDocument() with stable entry clones for viewer rendering +34/-0

Add renderDocument() with stable entry clones for viewer rendering

• Extends MakaTranscriptComponent with a full-document render path that resets geometry and uses per-entry clone keys. Prevents viewer renders from mutating or rekeying the memoized live-scrollback render cache.

packages/cli/src/pi-tui-layout.ts

Tests (2) +255 / -2
pi-tui-runner.test.tsAdd integration coverage for opening/closing /transcript viewer +61/-2

Add integration coverage for opening/closing /transcript viewer

• Updates expectations to assert the live surface remains unpaged by default. Adds an end-to-end test that runs a long transcript, opens '/transcript', navigates to the head via Home, and closes with 'q'.

packages/cli/src/tests/pi-tui-runner.test.ts

pi-tui-transcript-viewer.test.tsIntroduce unit tests for TranscriptViewerOverlay behavior and regressions +194/-0

Introduce unit tests for TranscriptViewerOverlay behavior and regressions

• Adds focused tests for navigation (line/page/home/end), tail-follow behavior, closing behavior, tiny viewport rendering, detached geometry rendering, and ensuring viewer renders do not replace the live scrollback cache.

packages/cli/src/tests/pi-tui-transcript-viewer.test.ts

Documentation (1) +5 / -0
CHANGELOG.mdDocument new /transcript viewer command +5/-0

Document new /transcript viewer command

• Adds an 'Added' changelog entry describing the new '/transcript' in-app transcript browsing capability and navigation behavior.

CHANGELOG.md

@qodo-code-review

qodo-code-review Bot commented Aug 17, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. No-op scroll breaks follow ✓ Resolved 🐞 Bug ≡ Correctness
Description
TranscriptViewerOverlay.scrollBy() sets followsEnd=false for any upward scroll attempt, even when
the viewport cannot move (e.g., maxTop===0 when the transcript fits). After that, newly appended
output will no longer auto-follow the tail and the viewer will remain pinned at the head once the
transcript becomes scrollable.
Code

packages/cli/src/pi-tui-transcript-viewer.ts[R108-110]

+    this.top = clamp(this.top + delta, 0, maxTop);
+    this.followsEnd = delta > 0 && this.top === maxTop;
+    this.input.onChange();
Relevance

●●● Strong

Accepted bug precedents favor deterministic state-preservation fixes; no close rejection precedent
found.

PR-#1587
PR-#3159

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
render() snaps top to maxTop only when followsEnd is true; scrollBy() currently clears
followsEnd on any negative delta even if top is clamped to maxTop (notably when maxTop is
0), so later appends won’t snap to the new end.

packages/cli/src/pi-tui-transcript-viewer.ts[71-83]
packages/cli/src/pi-tui-transcript-viewer.ts[106-111]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`TranscriptViewerOverlay.scrollBy()` updates `followsEnd` based on the scroll *direction* (`delta > 0`) instead of the resulting position. If the transcript currently fits in the viewport (`maxTop() === 0`), pressing Up/PageUp clamps `top` to 0 (no movement) but still flips `followsEnd` to `false`, so subsequent transcript growth will not follow the tail.

## Issue Context
The viewer’s render path uses `followsEnd` to decide whether to snap `top` to `maxTop` on each render. If `followsEnd` is incorrectly cleared while still at the end, the viewer stops following appended output.

## Fix Focus Areas
- packages/cli/src/pi-tui-transcript-viewer.ts[71-83]
- packages/cli/src/pi-tui-transcript-viewer.ts[106-111]
- packages/cli/src/__tests__/pi-tui-transcript-viewer.test.ts[66-103]

## Suggested change
In `scrollBy()`, compute `followsEnd` from the final position, e.g.:
- set `this.followsEnd = this.top === maxTop;`

This keeps follow enabled when the view remains at the end after clamping (including the `maxTop===0` no-op case), while still disabling follow when the user actually scrolls away from the end.

## Test
Add a regression test:
1) Start with a document that fits the viewport (`maxTop===0`).
2) Send Up/PageUp (no movement).
3) Append enough lines so it becomes scrollable (`maxTop>0`).
4) Assert the viewer still renders the tail (i.e., follow remained enabled).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced: This is a user-visible TUI feature spanning runner integration, overlay state/navigation, transcript rendering isolation, and slash-command registration; it has meaningful behavioral and architectural risk, but not enough independent logic density to justify redundant extended passes.

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread packages/cli/src/pi-tui-transcript-viewer.ts

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I reviewed exact head 3b211039fc681ef3c2eef118a88f3e07f4b19120, including the full diff, current CI, merge state, and the resolved automated feedback. I found no reproducible P0–P3 code issue.

The problem definition is correct: the canonical transcript projection remains available in memory even when terminal scrollback has discarded older rendered rows. A read-only /transcript overlay is a minimal first-principles solution. It reuses the existing projection and renderer, does not create a second session-history authority, and isolates viewer geometry/cache keys so browsing cannot mutate live scrollback reconciliation.

The prior no-op upward-scroll finding is fixed and covered. I did not find low-quality tests, removable parallel state, or a useful PR split.

The remaining stop is governance rather than code. Issue #2997 says implementation PRs should remain Draft during the repository feature freeze until maintainers decide whether and when to take the feature. This PR is currently Ready and has no maintainer approval. Please restore Draft status or record the maintainer decision before merging.

Disclosure: Codex performed the read-only source, lifecycle, test, CI, and feedback analysis. The human contributor remains responsible for independently verifying the final diff and deciding whether the feature should merge.

中文

代码层面没有 P0–P3,复用了 canonical transcript projection,没有平行 history authority。当前只因 feature-freeze 治理要求暂缓:应恢复 Draft 或取得 maintainer 明确决定。

@Astro-Han

Copy link
Copy Markdown
Contributor

Before this can merge: this is a UI/UX change (new in-app transcript viewer overlay in the TUI), so per the project's review gate it needs before/after screenshots of the visual change — a terminal capture showing the new /transcript overlay (ideally with the previous behavior: the transcript overflowing lost scrollback). The review itself is PASS (all P3s), but the merge is gated on the screenshots. Please add them to the PR description and re-request review.

AI-assisted review disclosure: this comment was written by a human maintainer following the review-gate rule; no AI contributed to this content.

Astro-Han

This comment was marked as duplicate.

@Astro-Han
Astro-Han dismissed their stale review August 18, 2026 07:36

Review superseded by the main review comment; screenshot request communicated via comment.

Add a full-screen, read-only /transcript overlay with line, page, and boundary navigation. Keep the existing live transcript and rewind interactions unchanged while rendering the viewer through a detached geometry projection.
Give the viewer stable entry clones so its full-document renders cannot replace memoized lines that the live surface has frozen in terminal scrollback. Cover the background-update path with a regression test.
Prefer one transcript row over the navigation footer when only two terminal rows are available, and keep the displayed range valid when no body row fits.
scrollBy derived followsEnd from the scroll direction, so pressing Up on a
transcript that already fit the viewport pinned the viewer at the head once
it grew scrollable. Derive follow from the clamped position instead: the
viewer keeps following whenever it ends at the tail, including the no-op
case.
@me2seeks
me2seeks force-pushed the feat/tui-transcript-viewer branch from 3b21103 to 33fd635 Compare August 18, 2026 15:38
@me2seeks

Copy link
Copy Markdown
Contributor Author

@Astro-Han The requested before/after terminal captures are now in the PR description. They use the same 40-line TUI integration fixture: the live screen has scrolled past lines 1-21, while /transcript + Home recovers the prompt and line 1 from the canonical projection. CLI passes 281/281 on the rebased head. Could you re-review the current head when convenient?

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reusing the live transcript projection while isolating geometry/cache keys is the right architecture; this does not create a second history authority. Three concrete lifecycle/routing issues remain: opening the viewer retains a second rendered transcript cache for the rest of the session, /transcript is sent to the model when a turn is running, and resize clamping can land at the tail without restoring tail-follow behavior.

AI-assisted review: Codex coordinated two independent reviewer passes and an OpenCode Go DeepSeek V4 Flash high-effort adversarial pass. I verified exact head 33fd635d1bd850902aa51ba70e21b83bd894aadf, overlay/input routing, render-cache reachability, resize behavior, and current CI. No local tests were run.

中文审查

复用 live transcript projection、只隔离 geometry/cache key 的架构是正确的,没有创建第二份 history authority。当前还有三个问题:打开 viewer 后会在整个会话中保留第二份渲染缓存;turn 运行时 /transcript 会被发送给模型;resize clamp 到尾部后不会恢复 tail-follow。

本次为 AI 辅助审查,已核验精确 head、overlay/input routing、render-cache 可达性、resize 行为和当前 CI;未运行本地测试。

Comment thread packages/cli/src/pi-tui-layout.ts Outdated
Comment thread packages/cli/src/pi-tui-runner.ts
Comment thread packages/cli/src/pi-tui-transcript-viewer.ts

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The implementation review is clean on this head: /transcript is intercepted locally during a turn, the viewer owns and releases its separate rendered cache, tail-follow/resize/tiny-view behavior is covered, all CI is green, and no unresolved code-review finding remains.

I’m using COMMENT rather than APPROVE only because the public acceptance gate in #2997 still says implementation PRs should remain Draft during the feature freeze until a maintainer decides to receive them, while this PR is currently Ready. No code change is requested here; once that maintainer decision is recorded (or the PR returns to Draft), the code itself is ready for approval.

AI-assisted review disclosure: Codex reviewed exact head 7c952e3, including the relevant TUI paths, focused tests, current CI, and review-thread state.

中文说明

当前代码审查已通过:运行中的 /transcript 会在本地处理,viewer 的独立缓存能够释放,tail-follow、resize 和极小窗口行为都有覆盖,CI 全绿,也没有未解决的代码 finding。这里使用 COMMENT 而不是 APPROVE,只因为 #2997 的公开治理条件仍要求 feature freeze 期间保持 Draft、等待 maintainer 接收决定;代码本身不需要再修改。

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this — the transcript viewer reads well and the scope is tight.

What this solves / how: the terminal scrollback window is bounded, so earlier transcript lines scroll out of reach while the canonical in-memory projection still holds them. This renders that same projection into a full-screen read-only overlay rather than introducing a second history source or touching persistence — the fix sits at the same layer as the problem.

Review notes:

  • Reuses the existing renderMakaPiTranscript projection; no duplicated transcript authority, no pagination state, no second source of truth. The added geometry isolation and entry clone cache are the minimum needed.
  • The live TUI already walks the same full projection, so large transcripts don't take a new asymptotic hit here.
  • Existing review feedback on this PR is addressed at the current head.

Non-blocking note (not a finding): with a turn running, Ctrl+C inside the viewer is inert — the runner's if (tui.hasOverlay()) return undefined short-circuits before the turn-interrupt branch, and TranscriptViewerOverlay.handleInput only handles q/Esc/navigation. Users can still Esc out and then interrupt, so nothing is stuck. Worth flagging only because Ctrl+C behaviour across overlays is currently uneven: most overlays close themselves on it, user-question gets an explicit allowlist ahead of the hasOverlay() check, and this one does neither. That hasOverlay() short-circuit predates this PR, so unifying the convention belongs in its own change rather than here.

LGTM.


AI-assisted review. Findings were independently checked and an attempt was made to falsify each one before posting; a preliminary finding about the Ctrl+C convention was withdrawn during that check.

@Astro-Han
Astro-Han merged commit 766c800 into apache:main Aug 22, 2026
19 checks passed
Astro-Han added a commit that referenced this pull request Aug 22, 2026
#2999 added the /transcript command and the change making midTurn a
required field on MakaSlashCommand landed separately. Each was green on
its own branch; main broke where they met.

'local' rather than 'refuse': showTranscriptViewer only calls
tui.showOverlay, never entering runControl, so it satisfies the local
contract — and mid-turn is exactly when reading back the transcript is
most useful, so refusing there would remove the command's main value.

Generated-by: Claude Opus 5 (Claude Code)
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.

feat(cli): add an in-app transcript viewer for long TUI sessions

2 participants