Skip to content

fix: show change preview before approving file-mutating tools in interactive CLI#3181

Open
praisonai-triage-agent[bot] wants to merge 2 commits into
mainfrom
claude/issue-3179-20260719-0852
Open

fix: show change preview before approving file-mutating tools in interactive CLI#3181
praisonai-triage-agent[bot] wants to merge 2 commits into
mainfrom
claude/issue-3179-20260719-0852

Conversation

@praisonai-triage-agent

@praisonai-triage-agent praisonai-triage-agent Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Fixes #3179

Summary

The interactive Rich and Textual frontends approved a file-mutating tool call from a bare summary (tool name, action type, description) with no preview of the actual change. This makes the interactive frontends display the concrete change — a unified diff when available, otherwise the truncated new content — before prompting, matching the behaviour the console approval_backend._render_preview already offers.

Changes

  • interactive/events.py: added a shared render_change_preview(tool_name, parameters) helper and ApprovalRequest.change_preview() that returns a unified diff for edit/apply_patch (from diff/patch) or truncated content for write; None for non-mutating tools.
  • frontends/rich_frontend.py: renders the preview in a syntax-highlighted Panel (and plain-text fallback) above the approval options.
  • frontends/textual_frontend.py: renders the preview in a scrollable VerticalScroll(Static(...)) in the ApprovalDialog.

Notes

  • Wrapper-layer only (interactive CLI presentation). No core/Agent surface changes, no new params, no new dependencies.
  • Backward-compatible: preview is derived from existing parameters, so event payloads are unchanged.

Validation

  • Unit-level checks confirm preview output for edit/apply_patch/write, None for non-mutating tools, and truncation for large content.

Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added change previews for file-modifying approval requests, including unified diffs and formatted new file content.
    • Approval prompts now display previews before users choose whether to proceed.
    • Previews are available in both Rich and Textual interfaces, with scrolling support for longer changes.
    • Added a plain-text preview format when Rich rendering is unavailable.
  • Improvements
    • Approval dialogs continue to show the same actions when no preview is available.

@MervinPraison

Copy link
Copy Markdown
Owner

@coderabbitai review

@MervinPraison

Copy link
Copy Markdown
Owner

/review

@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@MervinPraison MervinPraison added pipeline/blocked:ci Blocked: CI not green on HEAD pipeline/blocked:no-final Blocked: no FINAL @claude trigger yet pipeline/final-claude-pending Reviews done; waiting for FINAL @claude labels Jul 19, 2026
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor
✅ 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.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ed569876-6204-4ded-9361-f34d77438734

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Interactive approval requests now generate previews for diffs, patches, and write content. Rich and Textual frontends display available previews before presenting approval choices.

Changes

Interactive approval previews

Layer / File(s) Summary
Preview rendering and request delegation
src/praisonai-code/praisonai_code/cli/interactive/events.py
Adds shared rendering for edit/apply-patch diffs and truncated write content, then delegates ApprovalRequest.change_preview() to it.
Frontend approval display
src/praisonai-code/praisonai_code/cli/interactive/frontends/rich_frontend.py, src/praisonai-code/praisonai_code/cli/interactive/frontends/textual_frontend.py
Displays available previews in Rich panels or plain text, and in a scrollable Textual section before approval controls.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant InteractiveFrontend
  participant ApprovalRequest
  participant render_change_preview
  User->>InteractiveFrontend: initiate approval
  InteractiveFrontend->>ApprovalRequest: request.change_preview()
  ApprovalRequest->>render_change_preview: pass tool and parameters
  render_change_preview-->>ApprovalRequest: return concrete preview or None
  ApprovalRequest-->>InteractiveFrontend: return preview
  InteractiveFrontend-->>User: display preview and approval choices
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: showing change previews in interactive CLI approvals for mutating tools.
Linked Issues check ✅ Passed The changes add concrete diff/content previews in Rich and Textual approval flows and leave non-mutating tools unchanged.
Out of Scope Changes check ✅ Passed The modified files stay within the interactive CLI wrapper and preview-rendering path, with no unrelated feature work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/issue-3179-20260719-0852

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps

greptile-apps Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds change previews to the interactive CLI approval dialogs for file-mutating tool calls (edit, write, apply_patch), so users see a concrete diff or file content before deciding to approve. Both previous issues flagged in review threads — Rich markup injection via unescaped brackets and the edit-without-precomputed-diff silent null — are addressed here.

  • events.py: New render_change_preview() helper returns a unified diff (precomputed or synthesized via difflib.unified_diff from old_string/new_string) or truncated file content depending on the tool; ApprovalRequest.change_preview() delegates to it.
  • rich_frontend.py: Renders the preview in a Panel with rich.markup.escape() applied before display, with a plain-text fallback.
  • textual_frontend.py: Renders the preview in a scrollable VerticalScroll(Static(...)) with the same escape() guard, refactored to build the widget list dynamically before yielding.

Confidence Score: 5/5

Safe to merge — the change is wrapper-layer only, touches no core agent logic, and both previously flagged issues (Rich markup injection and edit-without-diff silent null) are correctly addressed.

The logic in render_change_preview correctly handles all three mutating tools, the markup escaping is applied consistently in both frontends, and the difflib synthesis for edit-without-precomputed-diff fills the previously identified gap. No regressions to the approval flow or event payloads.

No files require special attention.

Important Files Changed

Filename Overview
src/praisonai-code/praisonai_code/cli/interactive/events.py Adds render_change_preview() and ApprovalRequest.change_preview(); correctly handles edit (precomputed diff → synthesized difflib diff), write (truncated content), and apply_patch (patch/diff key). Logic is sound and difflib.unified_diff synthesis addresses the previously noted silent-None bug.
src/praisonai-code/praisonai_code/cli/interactive/frontends/rich_frontend.py Renders change preview in a cyan Panel using rich.markup.escape() before passing to Panel, addressing the previously flagged MarkupError risk. Plain-text fallback path also includes the preview. Change is minimal and correct.
src/praisonai-code/praisonai_code/cli/interactive/frontends/textual_frontend.py Refactors compose() to build widget list dynamically, inserting a scrollable VerticalScroll(Static(...)) with the escaped preview when available. rich.markup.escape() is applied correctly; since Textual depends on Rich this import is always safe within the Textual try block.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Agent
    participant ApprovalRequest
    participant render_change_preview
    participant RichFrontend
    participant TextualFrontend

    Agent->>ApprovalRequest: create(tool_name, parameters)
    RichFrontend->>ApprovalRequest: change_preview()
    ApprovalRequest->>render_change_preview: (tool_name, parameters)

    alt tool not in (edit, write, apply_patch)
        render_change_preview-->>ApprovalRequest: None
    else diff/patch key present
        render_change_preview-->>ApprovalRequest: precomputed diff string
    else "tool == write"
        render_change_preview-->>ApprovalRequest: truncated content
    else "tool == edit with old/new strings"
        render_change_preview->>render_change_preview: difflib.unified_diff(old, new)
        render_change_preview-->>ApprovalRequest: synthesized diff
    end

    ApprovalRequest-->>RichFrontend: "preview (str | None)"

    alt preview not None
        RichFrontend->>RichFrontend: escape(preview)
        RichFrontend->>RichFrontend: console.print(Panel(escaped_preview))
    end

    RichFrontend->>RichFrontend: prompt user [1-5]
    RichFrontend-->>Agent: ApprovalResponse

    TextualFrontend->>ApprovalRequest: change_preview()
    ApprovalRequest-->>TextualFrontend: "preview (str | None)"
    alt preview not None
        TextualFrontend->>TextualFrontend: VerticalScroll(Static(escape(preview)))
    end
    TextualFrontend->>TextualFrontend: "yield Vertical(*children)"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Agent
    participant ApprovalRequest
    participant render_change_preview
    participant RichFrontend
    participant TextualFrontend

    Agent->>ApprovalRequest: create(tool_name, parameters)
    RichFrontend->>ApprovalRequest: change_preview()
    ApprovalRequest->>render_change_preview: (tool_name, parameters)

    alt tool not in (edit, write, apply_patch)
        render_change_preview-->>ApprovalRequest: None
    else diff/patch key present
        render_change_preview-->>ApprovalRequest: precomputed diff string
    else "tool == write"
        render_change_preview-->>ApprovalRequest: truncated content
    else "tool == edit with old/new strings"
        render_change_preview->>render_change_preview: difflib.unified_diff(old, new)
        render_change_preview-->>ApprovalRequest: synthesized diff
    end

    ApprovalRequest-->>RichFrontend: "preview (str | None)"

    alt preview not None
        RichFrontend->>RichFrontend: escape(preview)
        RichFrontend->>RichFrontend: console.print(Panel(escaped_preview))
    end

    RichFrontend->>RichFrontend: prompt user [1-5]
    RichFrontend-->>Agent: ApprovalResponse

    TextualFrontend->>ApprovalRequest: change_preview()
    ApprovalRequest-->>TextualFrontend: "preview (str | None)"
    alt preview not None
        TextualFrontend->>TextualFrontend: VerticalScroll(Static(escape(preview)))
    end
    TextualFrontend->>TextualFrontend: "yield Vertical(*children)"
Loading

Reviews (3): Last reviewed commit: "fix: escape rich markup in change previe..." | Re-trigger Greptile

Comment thread src/praisonai-code/praisonai_code/cli/interactive/events.py
@MervinPraison

Copy link
Copy Markdown
Owner

@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.

Phase 1: Review per AGENTS.md

  1. Protocol-driven: check heavy implementations vs core SDK
  2. Backward compatible: ensure zero feature regressions
  3. Performance: no hot-path regressions
  4. SDK value: review in depth whether the change genuinely adds value to the SDK — never add features for the sake of adding them. It must strengthen the SDK (simpler, more user-friendly, robust, world-class, secure). If it does not clearly add value, request changes or recommend rejecting/closing rather than merging scope creep
  5. Do not bloat the Agent class with additional params — only if absolutely required; we already support many params.
  6. Repo routing: agent-callable tools → PraisonAI-Tools; lifecycle plugins → PraisonAI-Plugins; optional sandbox backends → PraisonAI-Plugins (praisonai.sandbox entry point) — request changes if wrongly added to praisonaiagents/

Phase 2: FIX Valid Issues
7. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix
8. Also independently identify and fix any gaps or issues you find in the changed code — do not rely only on prior reviewer feedback
9. Push all code fixes directly to THIS branch (do NOT create a new PR)
10. Comment a summary of exact files modified and what you skipped

Phase 3: Final Verdict
11. If all issues are resolved, approve the PR / close the Issue
12. If blocking issues remain, request changes / leave clear action items

@MervinPraison MervinPraison added pipeline/awaiting-merge-gate FINAL done; waiting for merge gate / CI pipeline/blocked:cooldown Blocked: post-push or @claude cooldown and removed pipeline/final-claude-pending Reviews done; waiting for FINAL @claude pipeline/blocked:no-final Blocked: no FINAL @claude trigger yet labels Jul 19, 2026
@praisonai-triage-agent

praisonai-triage-agent Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Claude finished @MervinPraison's task —— View job


Final Architecture Review — PR #3181

  • Read AGENTS.md + changed files
  • Review scope/value (wrapper-only, backward-compatible)
  • Fix P1: escape Rich markup in preview Panel
  • Fix P2: synthesize diff for edit without precomputed diff
  • Test
  • Push + summary

Phase 1 — Architecture assessment ✅

  • Routing: Correct. All changes are in the interactive CLI wrapper (praisonai-code/.../cli/interactive/) — presentation only. No praisonaiagents/ core, no Agent params, no new deps.
  • Backward compatible: preview is derived from existing parameters; event payloads unchanged. Non-mutating tools still return None → dialog behaviour identical when there's nothing to show.
  • Performance: Off hot path (only runs on approval prompt). difflib is stdlib and lazily imported.
  • SDK value: Genuine — approving the concrete diff/content instead of a bare tool label is a real safety/UX win and mirrors the console approval_backend._render_preview. Not scope creep.

Phase 2 — Fixes pushed (commit 4d544d4)

File Fix
frontends/rich_frontend.py P1 (crash, valid): Panel received the raw preview; any diff with [/] (ubiquitous in Python) raised rich.errors.MarkupError, uncaught → crashed the approval dialog. Now wrapped in rich.markup.escape().
frontends/textual_frontend.py P1 (same bug): Textual Static also interprets Rich markup by default — same bracket crash. Escaped the preview via rich.markup.escape(). Greptile only flagged the Rich frontend; I found and fixed the Textual one independently.
interactive/events.py P2 (gap, valid): an edit call carrying only old_string/new_string (no precomputed diff) silently returned None → no preview. Now synthesises a unified diff via difflib.unified_diff; identical old/new → None (no noise).

Verification

  • render_change_preview checked for: edit+diff, edit synth (old/new), write, non-mutating (None), empty edit (None), identical old/new (None) — all correct.
  • Confirmed escape() renders a bracket-containing diff without MarkupError.
  • py_compile clean; targeted tests 6 passed, 5 skipped. The 12 collection errors are pre-existing praisonaiagents import failures unrelated to this PR.

Skipped / notes

  • Greptile's claim that the console backend "already" synthesises a diff from old/new is inaccurate — approval_backend._render_preview also returns None in that case. I still implemented the synthesis in the interactive layer since it clearly adds value; a follow-up could align the console backend, but that's out of scope for this PR.

Phase 3 — Verdict ✅

Both valid reviewer issues (P1 crash, P2 gap) resolved, plus the parallel Textual crash. Change stays lightweight, wrapper-only, and backward-compatible. Approve — ready to merge once CI is green.
· branch claude/issue-3179-20260719-0852

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In
`@src/praisonai-code/praisonai_code/cli/interactive/frontends/rich_frontend.py`:
- Around line 182-187: Prevent Rich markup parsing of arbitrary preview content
in both frontend sites: in
src/praisonai-code/praisonai_code/cli/interactive/frontends/rich_frontend.py
lines 182-187, update the preview passed to Panel to use rich.syntax.Syntax for
literal syntax-highlighted rendering; in
src/praisonai-code/praisonai_code/cli/interactive/frontends/textual_frontend.py
lines 121-126, wrap preview with rich.text.Text before passing it to Static.
Preserve the existing panel and widget display behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d72e74df-e911-4632-bd3d-31047f7652c9

📥 Commits

Reviewing files that changed from the base of the PR and between 882143d and 467ccd1.

📒 Files selected for processing (3)
  • src/praisonai-code/praisonai_code/cli/interactive/events.py
  • src/praisonai-code/praisonai_code/cli/interactive/frontends/rich_frontend.py
  • src/praisonai-code/praisonai_code/cli/interactive/frontends/textual_frontend.py

Comment on lines +182 to +187
if preview:
console.print(Panel(
preview,
title="[cyan]Change Preview[/cyan]",
border_style="cyan",
))

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.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Prevent Rich markup injection crashes from arbitrary code.

Both frontends currently pass the raw preview string directly to Panel and Static. Because Rich parses markup tags by default, any file content or diff containing brackets (e.g., def foo(x: list[int]): or arr[i]) will be evaluated as Rich tags, crashing the interactive loop with a StyleSyntaxError or MarkupError.

  • src/praisonai-code/praisonai_code/cli/interactive/frontends/rich_frontend.py#L182-L187: Wrap the preview in rich.syntax.Syntax to treat the string as literal code. This prevents the crash and fulfills the PR objective to display a "syntax-highlighted panel".
  • src/praisonai-code/praisonai_code/cli/interactive/frontends/textual_frontend.py#L121-L126: Wrap the preview using rich.text.Text(preview) to safely pass it as a literal renderable to Textual's Static widget.
🛡️ Proposed fixes

For rich_frontend.py:

-            if preview:
-                console.print(Panel(
-                    preview,
-                    title="[cyan]Change Preview[/cyan]",
-                    border_style="cyan",
-                ))
+            if preview:
+                from rich.syntax import Syntax
+                lexer = "diff" if request.tool_name in ("edit", "apply_patch") else "text"
+                console.print(Panel(
+                    Syntax(preview, lexer, word_wrap=True),
+                    title="[cyan]Change Preview[/cyan]",
+                    border_style="cyan",
+                ))

For textual_frontend.py:

-            if preview:
-                children.append(
-                    VerticalScroll(
-                        Static(preview, id="approval-preview"),
-                        id="approval-preview-scroll",
-                    )
-                )
+            if preview:
+                from rich.text import Text
+                children.append(
+                    VerticalScroll(
+                        Static(Text(preview), id="approval-preview"),
+                        id="approval-preview-scroll",
+                    )
+                )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if preview:
console.print(Panel(
preview,
title="[cyan]Change Preview[/cyan]",
border_style="cyan",
))
if preview:
from rich.syntax import Syntax
lexer = "diff" if request.tool_name in ("edit", "apply_patch") else "text"
console.print(Panel(
Syntax(preview, lexer, word_wrap=True),
title="[cyan]Change Preview[/cyan]",
border_style="cyan",
))
Suggested change
if preview:
console.print(Panel(
preview,
title="[cyan]Change Preview[/cyan]",
border_style="cyan",
))
if preview:
from rich.text import Text
children.append(
VerticalScroll(
Static(Text(preview), id="approval-preview"),
id="approval-preview-scroll",
)
)
📍 Affects 2 files
  • src/praisonai-code/praisonai_code/cli/interactive/frontends/rich_frontend.py#L182-L187 (this comment)
  • src/praisonai-code/praisonai_code/cli/interactive/frontends/textual_frontend.py#L121-L126
🤖 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 `@src/praisonai-code/praisonai_code/cli/interactive/frontends/rich_frontend.py`
around lines 182 - 187, Prevent Rich markup parsing of arbitrary preview content
in both frontend sites: in
src/praisonai-code/praisonai_code/cli/interactive/frontends/rich_frontend.py
lines 182-187, update the preview passed to Panel to use rich.syntax.Syntax for
literal syntax-highlighted rendering; in
src/praisonai-code/praisonai_code/cli/interactive/frontends/textual_frontend.py
lines 121-126, wrap preview with rich.text.Text before passing it to Static.
Preserve the existing panel and widget display behavior.

@MervinPraison MervinPraison added pipeline/blocked:stale-final Blocked: FINAL stale after new commits and removed pipeline/blocked:stale-final Blocked: FINAL stale after new commits labels Jul 19, 2026
@praisonai-triage-agent praisonai-triage-agent Bot removed the pipeline/blocked:cooldown Blocked: post-push or @claude cooldown label Jul 19, 2026
@MervinPraison

Copy link
Copy Markdown
Owner

@claude this PR has merge conflicts with main. Please rebase onto latest main, resolve conflicts (keep this PR's intent, merge in newer main logic), run targeted tests, and force-push with --force-with-lease. Comment which files you resolved. Do not bloat the Agent class with additional params — only if absolutely required; we already support many params. Keep the resolution minimal — the aim is a lightweight and powerful package; do not introduce scope creep while resolving conflicts.

@praisonai-triage-agent praisonai-triage-agent Bot added pipeline/blocked:conflict Blocked: merge conflict or rebase pending pipeline/blocked:cooldown Blocked: post-push or @claude cooldown labels Jul 19, 2026
@praisonai-triage-agent

praisonai-triage-agent Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Claude finished @MervinPraison's task —— View job


Merge conflicts resolved — PR #3181

  • Setup git identity & fetch latest main
  • Rebase onto origin/main, resolve conflicts (keep PR intent)
  • Run targeted tests on changed paths
  • Force-push with --force-with-lease
  • Comment files resolved

Files resolved (2)

Both conflicts were in the interactive approval _prompt_approval / ApprovalDialog.compose methods, where main (#3178 — narrow/blanket permission patterns, merged via #3182) overlapped with this PR's change-preview additions. Resolution kept both features:

File Resolution
cli/interactive/frontends/rich_frontend.py Kept main's narrow_pattern/blanket_pattern derivation and this PR's preview = request.change_preview() + preview Panel. The escape(preview) markup-safety fix (2nd PR commit) applied cleanly on top.
cli/interactive/frontends/textual_frontend.py Merged main's narrow_pattern/blanket_pattern locals into this PR's children list builder so the preview VerticalScroll and the pattern-labelled buttons coexist. escape(preview) fix preserved.

events.py auto-merged (no conflict).

Validation

  • py_compile clean on all 3 changed files.
  • Preview logic verified: edit synth-diff ✅, write truncation ✅, non-mutating → None ✅.
  • Targeted tests: 12 passed, 6 skipped (interactive/approval/preview/event) — including main's test_interactive_approval_pattern.py suite, confirming both features coexist.
  • The 12 collection errors are pre-existing environment issues (missing typer / praisonaiagents modules), unrelated to this PR.

Notes

Branch is clean and up to date with main. Ready to merge once CI is green.
· branch claude/issue-3179-20260719-0852

praisonai-triage-agent Bot and others added 2 commits July 19, 2026 10:29
…ractive CLI (fixes #3179)

Render a unified diff (edit/apply_patch) or truncated new content (write)
before prompting for approval in the interactive Rich and Textual frontends,
so users approve the concrete change rather than just a tool label. Adds a
shared render_change_preview helper and ApprovalRequest.change_preview() in
the interactive events module, mirroring approval_backend._render_preview.

Co-authored-by: MervinPraison <MervinPraison@users.noreply.github.com>
- rich_frontend/textual_frontend: escape preview via rich.markup.escape so
  diffs/content containing bracket chars no longer raise MarkupError and crash
  the approval dialog (Greptile P1)
- events.render_change_preview: synthesise a unified diff from old_string/
  new_string for edit calls lacking a precomputed diff (Greptile P2)

Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
@MervinPraison MervinPraison removed the pipeline/blocked:conflict Blocked: merge conflict or rebase pending label Jul 19, 2026
@praisonai-triage-agent praisonai-triage-agent Bot removed the pipeline/blocked:cooldown Blocked: post-push or @claude cooldown label Jul 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pipeline/awaiting-merge-gate FINAL done; waiting for merge gate / CI pipeline/blocked:ci Blocked: CI not green on HEAD

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Interactive CLI approval: show the concrete change (diff/content) before approving a file-mutating tool

1 participant