LLMSkillHub
mirroredCopied from carlos-algms/agentic.nvim under MIT. carlos-algms has not claimed this page, and is not affiliated with LLM SkillHub.

carlos-algms / agentic-ui-message-writer

Rules for chat-buffer content and tool-call rendering. Use before editing MessageWriter, PermissionManager, tool-call block rendering, sender headers, thinking blocks, auto-scroll, folds, status rows, permission buttons, or chat- buffer tool-call tests.

0
SKILL.md
6.1 KB

Agentic UI Message Writer

This skill covers chat-buffer content state. For widget windows, layout,
fallback windows, hidden floats, and buffer redirection, see
lua/agentic/ui/AGENTS.md and skill agentic-ui-widget-lifecycle.

Hard rules

  • wrap stays on.
  • Cursor positioning is G0zb, not G$zb.
  • Cursor sits on the trailing "" line below the last block, never inside a
    tool-call block.

  • scrolloff = 4 on chat keeps room for spinner virt_lines above the cursor.
  • Auto-scroll captures before mutation and applies after mutation in the same
    tick. No vim.schedule between them.

  • The _capture_scroll / _apply_scroll pair is needed only when a mutation
    changes the buffer's line count. A rewrite that keeps the line count
    (repaint_status_row) skips it.

  • Tool-call body updates replace only the body between stable anchor pads.
  • Manual folds only. Never foldexpr; read ADR 0001 before proposing foldexpr
    workarounds.

  • tracker.rendered_button_count is render-path-only state.

Tool-call layout

row 0          header         rewritten on every update, NOT folded
row 1          "" top_pad     fold start anchor
row 2..M-1     body           replaced on every update
row M          "" bottom_pad  fold end anchor
row M+1..M+K   permission     K rows: N button rows + N empty spacer rows
row M+K+1      status row     real text, outside the fold

Pinned by, in lua/agentic/ui/message_writer.test.lua:
::"emits anchor pad lines around the body in every block" (pads) and
::"writes pending status word as real text at row N" (status row).

  • K = 2 * N for N permission options.
  • Permission rows are outside the fold.
  • MessageWriter:_render_permission_section owns all permission rows plus the
    status row.

  • Pads are unconditional.
  • Header is rewritten because providers send placeholder titles before final
    titles.

Permission rows

  • Buttons live one-per-row between bottom_pad and the status row.
  • Empty spacer rows sit between buttons, with one trailing spacer above the
    status row.

  • Digit keymaps are bound only while a block is focused.
  • Cycle keys and <CR> are row-gated to button rows or the focused block's
    status row.

  • Spacer rows fall through to default motion.
  • Focus transition repaints old and new status rows.

Regression anchors:

  • permission_manager.test.lua::digit keymap lifecycle::"rebinds digit keymaps with new mapping after focus transition"
  • permission_manager.test.lua::bracket cycle::"focus transition triggers exactly 2 status-row repaints"

Special write paths

Use only the normal write path outside these cases.

MethodWhen to use
write_structural_messageWelcome banner on session open; banner before restore
write_restoring_messagePer-message replay during session restore
replay_history_messagesProvider switch only; bulk repaint from in-memory history
  • Outside restore/provider-switch, use write_message_chunk or
    write_tool_call_block.

  • replay_history_messages does not re-issue ACP send_prompt.
  • write_restoring_message calls write_message(), not
    write_message_chunk(). Each replayed user_message_chunk becomes its own
    message; chunks are not reassembled. Pinned by, in
    lua/agentic/session_manager.test.lua:
    ::"renders replay chunks as formatted messages".

  • Adding a bulk-write path requires a new row here and a test.

Sender classification

MessageWriter:_maybe_write_sender_header maps update.sessionUpdate to
sender. New ACP update types must be classified here or they get no header.

user_message_chunk     -> user
agent_message_chunk    -> agent
agent_thought_chunk    -> agent
tool_call              -> agent
plan                   -> no header

Pinned by, in lua/agentic/ui/message_writer.test.lua:
::"writes user header on first user_message_chunk",
::"writes agent header on first agent_message_chunk",
::"writes agent header before tool call block",
::"skips header for plan updates". No test pins agent_thought_chunk; add one
before changing it.

Thinking blocks reuse one extmark in NS_THINKING. Any non-thought write must
clear thinking state first or the next thought extends the wrong extmark.

TodoList

  • TodoList owns ChatWidget.buf_nrs.todos.
  • It opens after diagnostics in WidgetLayout.
  • It is gated by Config.windows.todos.display.
  • It stays hidden until the first Plan event.
  • It auto-closes when all tasks complete.
  • It has no keymaps.

Traps

  • vim.schedule between mutation and G0zb: redraw can run with stale topline.
  • Replacing the whole tool-call range: manual fold dies.
  • Re-rendering tool-call body after tracker.diff exists: preview consistency
    breaks. Once a diff exists, refresh only header/status.

  • Overwriting status/button rows while permission is pending: buttons disappear
    until the next focus repaint. Use _render_permission_section.

Test invariants

Each invariant has an existing regression test. Deleting one is a behavior
change.

  • Fold survives close and reopen.
  • Fold creation is gated by screen-row count.
  • Fold counts wrapped rows, not buffer lines.
  • Status row and permission rows are real text rendered per state.
  • Block range extmark grows by K on permission render.
  • Focus transition triggers exactly two status-row repaints.
  • Digit keymap dispatches the focused block option.
  • Bracket cycle wraps and no-ops when pending is empty.
  • Concurrent permission map preserves insertion order and supports out-of-order
    resolve.

  • Sender headers deduplicate consecutive same-sender writes.
  • Auto-scroll threshold preserves reading position and permission-row cursor.
  • Thinking state clears on non-thought writes.