|
| str | _redact (str text) |
| dict|None | _parse_args (str|None arguments) |
| str | _norm_path (object value) |
| bool | _paths_agree (object arg_path, Iterable[object] result_paths) |
| str | _fmt_bytes (int n) |
| str | _classify (str line) |
| str | _reanchor_hunk (str line, int offset) |
| tuple[tuple[DiffLine,...], bool, int] | _truncate (list[DiffLine] body, int max_lines) |
| DiffRender|None | _build_edit_diff (dict args, dict result, int max_lines) |
| DiffRender|None | _build_write_diff (dict args, dict result, int max_lines) |
| DiffRender|None | build_mutation_diff (str tool, str|None arguments, Any result, *, int max_lines=DEFAULT_MAX_DIFF_LINES) |
| str | _elide (str value) |
| str|None | summarize_mutation_call (str tool, str|None arguments) |
| str | diff_header_line (DiffRender diff) |
| str|None | diff_footer_line (DiffRender diff) |
| list[str] | render_diff_plain (DiffRender|None diff, *, bool color=True) |
Diff-first rendering model for the mutation tools (plan 188 T1).
Purpose
-------
Every comparator in the field renders an edit as a **diff**; OC Chat
rendered ``write_file`` / ``edit_file`` results as a truncated JSON
blob. This module owns the one shared, framework-free model that turns
a mutation tool's *call arguments* plus its *result payload* into a
renderable unified diff, so all three terminal presenters
(:class:`~oc_ui.terminal.presenter.ConsolePresenter`,
:class:`~oc_ui.terminal.rich_presenter.RichPresenter`, and the
:class:`~oc_ui.terminal.enhanced_presenter.EnhancedPresenter` that
subclasses it) show the same thing and degrade the same way.
Nothing here changes the tool contract. The diff is derived at RENDER
time from data the render lane already carries: the presenter sees the
arguments in :meth:`~oc_ui.presenter_base.Presenter.show_tool_call`
before the tool runs and the result in
:meth:`~oc_ui.presenter_base.Presenter.show_tool_result` after — this
module just pairs them (:class:`MutationArgsLedger`) and folds them
into a diff.
**What the render lane can and cannot know.** ``edit_file`` carries
both sides of the change (``old_string`` / ``new_string``) plus the
real ``match_line``, so its diff is a true, file-anchored unified diff.
``write_file`` carries only the NEW content — the previous contents are
gone by the time the result arrives — so its render is the full written
content as additions, and the header says exactly that rather than
implying a comparison that was never made.
Responsibilities
----------------
- :class:`MutationArgsLedger` — FIFO pairing of ``show_tool_call``
arguments with the matching ``show_tool_result``.
- :class:`DiffLine` / :class:`DiffRender` — the pure, presenter-neutral
diff model.
- :func:`build_mutation_diff` — the honesty-gated constructor:
returns ``None`` (caller falls back to the legacy JSON line) for a
non-mutation tool, an error / gate / confirmation result, unparseable
or absent arguments, or a call/result pair whose paths disagree.
- :func:`diff_header_line` — the one-line summary above the body.
- :func:`render_diff_plain` — ANSI (or plain) line renderer used by
the console presenter; Rich builds its own renderable from the same
:class:`DiffRender`.
Diagnostics
-----------
Domain: OC_UI.TERMINAL
Levels:
L2 — lifecycle events
L3 — semantic details (guard rejections)
L4 — deep tracing
Contracts
---------
- Framework-free: no ``rich`` / ``prompt_toolkit`` import here, exactly
like :mod:`oc_ui.terminal.statusline` — the plain console must render
a diff with no optional dependency installed.
- :func:`build_mutation_diff` NEVER guesses. Any missing or
contradictory input returns ``None`` so the caller degrades to the
previous rendering rather than showing a diff that might be wrong.
- Rendered body text is passed through the same secret redaction the
read path already applies (:func:`oc_chat.tools._sandbox.redact_secrets`),
so a diff is not a secret side-channel. Redaction is best-effort: an
import failure degrades to unredacted text rather than losing the
render (the content is already on the operator's own disk).
- Truncation is explicit: :attr:`DiffRender.truncated` is set, the
hidden line count is carried, and
:attr:`DiffRender.full_content_hint` names the on-disk file where the
complete content lives.
Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)
- oc_chat.tools._sandbox (lazy; redact_secrets for rendered diff body text)
| str | None oc_ui.terminal.diffview.summarize_mutation_call |
( |
str | tool, |
|
|
str | None | arguments ) |
Compact ``show_tool_call`` argument rendering for a mutation tool.
``show_tool_call`` fires BEFORE dispatch, so a large ``write_file``
used to dump its entire ``content`` into scrollback and *then* render
the diff underneath — the announce line defeated the diff. For
:data:`MUTATION_TOOLS` the bulky fields (:data:`_ELIDED_ARG_FIELDS`)
become size markers; every other field renders normally, so the path,
the expected count and any future flag stay visible.
Returns ``None`` for a non-mutation tool or unparseable arguments —
the caller then renders the arguments exactly as before.
Definition at line 399 of file diffview.py.