Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/.system/research/NVIM-RESEARCH-KZYYEFY_-pi-and-neovim-capabilities-for-pivi/index.md

Raw
Rendered preview

id: NVIM-RESEARCH-KZYYEFY_ type: research title: Pi and Neovim capabilities for pivi

Pi and Neovim capabilities for pivi

Sources

Installed documentation only; no remembered API behavior.

Source Version Location
Pi docs (rpc.md, extensions.md, usage.md, sessions.md, json.md) 0.85.1 $PI_CONTEXT_DOCS_PATH
Neovim runtime docs (api.txt, lua.txt, undo.txt, editing.txt, options.txt) 0.12.5 $VIMRUNTIME/doc

Pi facts

Transport

  • pi --mode rpc speaks JSON commands on stdin, responses and events on stdout.
  • Strict JSONL, LF only; clients strip a trailing \r; Unicode separators must not split records.
  • Optional id on commands correlates responses; most events carry no id.
  • Startup flags relevant to a client: --provider, --model, --name, --session <path|id>, --session-dir, --no-session, -e <ext>, --tools, --append-system-prompt.

Commands that map to pivi surfaces

pivi need RPC command Notes
Submit prompt prompt During streaming requires streamingBehavior: steer | followUp, else error
Queue while running steer, follow_up, clear_queue Extension commands rejected here
Stop abort, abort_retry, abort_bash abort waits for idle
Status get_state, get_session_stats Model, thinking, streaming, queue, context usage
Transcript get_messages, get_entries, get_tree get_entries since is a durable cursor; leafId detects branch moves
Sessions new_session, switch_session, fork, clone, set_session_name Switch/fork cancellable by extensions
Slash commands get_commands Returns extension, prompt-template, and skill commands; invoked as /name through prompt
Model/thinking set_model, cycle_model, get_available_models, set_thinking_level, get_available_thinking_levels
Compaction compact, set_auto_compaction

Built-in TUI commands such as /settings and /hotkeys are absent from get_commands and do not execute over RPC.

Concurrency semantics

stateDiagram-v2
    [*] --> Idle
    Idle --> Streaming: prompt
    Streaming --> Streaming: steer / follow_up queued
    Streaming --> Streaming: extension command executes immediately
    Streaming --> Idle: agent_settled
    Streaming --> Idle: abort
  • A plain prompt while streaming is rejected without streamingBehavior.
  • An extension command (/name) sent through prompt executes immediately even during streaming and manages its own LLM interaction.
  • agent_end marks one low-level run; agent_settled marks full settlement after retry, compaction, and queued continuations.
  • Therefore a truly ephemeral question that never enters session history needs either an extension command or a separate process (pi -p / pi --mode json / a second --mode rpc --no-session child).

Events

Streamed as JSONL: agent_start, agent_end, agent_settled, turn_start, turn_end, message_start, message_update, message_end, tool_execution_start|update|end, bash_execution_update, queue_update, compaction_start|end, auto_retry_start|end, summarization_retry_*, extension_error.

  • message_update carries only deltas; clients assemble partial state by contentIndex; message_end.message is authoritative.
  • Tool-call streaming exposes toolcall_start (id, toolName), toolcall_delta (argument chunks), toolcall_end (complete call). This is the only source for progressive edit intent, and argument chunks are partial JSON text.
  • tool_execution_update.partialResult is cumulative, not a delta.

Extension UI over RPC

Method RPC behavior
select, confirm, input, editor Request/response sub-protocol with matching id; optional timeout auto-resolves agent-side
notify, setStatus, setWidget, setTitle, set_editor_text Fire-and-forget requests
custom Returns undefined
setWorkingMessage, setWorkingIndicator, setFooter, setHeader, setEditorComponent, setToolsExpanded No-ops
getEditorText, getToolsExpanded, theme getters/setters Degraded or empty

ctx.mode === "rpc" and ctx.hasUI === true; TUI-only capability must be guarded on ctx.mode === "tui".

Extension hooks a companion extension can use

  • pi.registerTool at load or at runtime; new tools are callable without /reload.
  • Built-in tools read, bash, powershell, edit, write, grep, find, ls can be overridden by registering the same name; result and details shapes must match exactly; omitted renderers fall back to built-ins; promptSnippet/promptGuidelines are not inherited.
  • Built-in tools also accept pluggable operations (ReadOperations, WriteOperations, EditOperations, BashOperations, …) via createReadTool and siblings, plus a spawnHook for shell tools. This delegates I/O without reimplementing tool semantics.
  • tool_call can mutate arguments or block; tool_result can post-process.
  • input event sees raw text with source of interactive | rpc | extension and can continue, transform, or handled.
  • before_agent_start exposes and can extend systemPrompt and structured systemPromptOptions.
  • pi.registerCommand supports getArgumentCompletions; pi.getCommands mirrors get_commands.
  • Tool output must be truncated; built-in limits are 50KB and 2000 lines.

Neovim facts

Transport

  • vim.system(cmd, { stdin = true, stdout = fun(err, data), stderr = fun(err, data) }) gives streaming callbacks plus SystemObj:write(data) and obj:write(nil) to close stdin; obj:kill() stops the child.
  • Output callbacks run in fast contexts; API calls must be deferred through vim.schedule (api-fast, vim.in_fast_event()).
  • jobstart/nvim_chan_send remain the alternative channel API.

Buffers and edits

Need Mechanism Caveat
Precise edit nvim_buf_set_text, nvim_buf_set_lines Each call opens a new undo block
One undoable change :undojoin before the next change E790 after undo/redo; must not be used blindly
Change detection nvim_buf_attach on_lines, b:changedtick Callbacks are fast-context
Diffing model output against buffer vim.text.diff with result_type = 'indices' Available in 0.12
Disk coherence after buffer edits buffer write plus :checktime; 'autoread' defaults on Unwritten buffers stay invisible to filesystem tools

Presentation

  • Extmarks provide virt_text with virt_text_pos = "inline" for ghost text, virt_lines (with virt_lines_above, virt_lines_overflow), highlights, and signs.
  • nvim_set_decoration_provider with ephemeral marks renders per redraw without persistent state.
  • vim.diagnostic namespaces keep agent findings separate from LSP and compiler diagnostics.
  • Quickfix and location lists use setqflist/setloclist and normal navigation.

Integration surface

  • nvim_exec_autocmds emits User events with a data payload (event-data), which fits publishing cached pivi state.
  • nvim_create_user_command supplies subcommands, nargs, and custom completion for :Pi help and :Pi command ....
  • <Plug> mappings expose behavior without claiming global keys.
  • vim.ui.select and vim.ui.input can back RPC dialog methods.

Spec claims versus evidence

Spec claim Support Risk
Non-terminal JSONL subprocess channel pi --mode rpc plus vim.system stdin/stdout Low
Semantic status (queue, run, tool, retry, compaction) get_state, get_session_stats, event stream Low
Slash commands via :Pi command ... get_commands plus prompt with /name Low; TUI-only commands unavailable
:Pi help runnable during an active run Extension command executes during streaming, or a separate ephemeral process Medium; not ephemeral if routed through the working session
Loaded buffers authoritative for reads and edits Built-in tool override or injected ReadOperations/EditOperations/WriteOperations Medium; result and details shapes must match exactly
Progressive ghost text from streaming tool arguments toolcall_delta plus inline virt_text Medium; requires tolerant partial-JSON parsing
One coherent undoable change per edit nvim_buf_set_text plus :undojoin Medium; E790 and multi-hunk edits
Edits visible to later shell work without watchers Explicit buffer write plus :checktime Medium; unwritten buffers diverge from disk
Extension dialogs mapped to native surfaces extension_ui_request/extension_ui_response Low
Multiple sessions, resume, switch --session, switch_session, new_session, fork, clone Low

Open questions

  • Does :Pi help use a second short-lived Pi process, or a companion extension command inside the working session that suppresses history?
  • Can two Pi processes safely target one session file, or must ephemeral help always run with --no-session?
  • Is buffer authority better implemented as operations injection into built-in tools than as full tool overrides, given the exact details shape requirement?
  • How should partial toolcall_delta arguments be parsed safely enough to show ghost text without ever mutating a buffer?
  • When must pivi write a modified buffer to disk so that agent shell commands observe agent edits?
---
id: NVIM-RESEARCH-KZYYEFY_
type: research
title: Pi and Neovim capabilities for pivi
---

# Pi and Neovim capabilities for pivi

## Sources

Installed documentation only; no remembered API behavior.

| Source | Version | Location |
| --- | --- | --- |
| Pi docs (`rpc.md`, `extensions.md`, `usage.md`, `sessions.md`, `json.md`) | 0.85.1 | `$PI_CONTEXT_DOCS_PATH` |
| Neovim runtime docs (`api.txt`, `lua.txt`, `undo.txt`, `editing.txt`, `options.txt`) | 0.12.5 | `$VIMRUNTIME/doc` |

## Pi facts

### Transport

- `pi --mode rpc` speaks JSON commands on stdin, responses and events on stdout.
- Strict JSONL, LF only; clients strip a trailing `\r`; Unicode separators must not split records.
- Optional `id` on commands correlates responses; most events carry no `id`.
- Startup flags relevant to a client: `--provider`, `--model`, `--name`, `--session <path|id>`, `--session-dir`, `--no-session`, `-e <ext>`, `--tools`, `--append-system-prompt`.

### Commands that map to pivi surfaces

| pivi need | RPC command | Notes |
| --- | --- | --- |
| Submit prompt | `prompt` | During streaming requires `streamingBehavior: steer \| followUp`, else error |
| Queue while running | `steer`, `follow_up`, `clear_queue` | Extension commands rejected here |
| Stop | `abort`, `abort_retry`, `abort_bash` | `abort` waits for idle |
| Status | `get_state`, `get_session_stats` | Model, thinking, streaming, queue, context usage |
| Transcript | `get_messages`, `get_entries`, `get_tree` | `get_entries since` is a durable cursor; `leafId` detects branch moves |
| Sessions | `new_session`, `switch_session`, `fork`, `clone`, `set_session_name` | Switch/fork cancellable by extensions |
| Slash commands | `get_commands` | Returns extension, prompt-template, and skill commands; invoked as `/name` through `prompt` |
| Model/thinking | `set_model`, `cycle_model`, `get_available_models`, `set_thinking_level`, `get_available_thinking_levels` | |
| Compaction | `compact`, `set_auto_compaction` | |

Built-in TUI commands such as `/settings` and `/hotkeys` are absent from `get_commands` and do not execute over RPC.

### Concurrency semantics

```mermaid
stateDiagram-v2
    [*] --> Idle
    Idle --> Streaming: prompt
    Streaming --> Streaming: steer / follow_up queued
    Streaming --> Streaming: extension command executes immediately
    Streaming --> Idle: agent_settled
    Streaming --> Idle: abort
```

- A plain `prompt` while streaming is rejected without `streamingBehavior`.
- An extension command (`/name`) sent through `prompt` executes immediately even during streaming and manages its own LLM interaction.
- `agent_end` marks one low-level run; `agent_settled` marks full settlement after retry, compaction, and queued continuations.
- Therefore a truly ephemeral question that never enters session history needs either an extension command or a separate process (`pi -p` / `pi --mode json` / a second `--mode rpc --no-session` child).

### Events

Streamed as JSONL: `agent_start`, `agent_end`, `agent_settled`, `turn_start`, `turn_end`, `message_start`, `message_update`, `message_end`, `tool_execution_start|update|end`, `bash_execution_update`, `queue_update`, `compaction_start|end`, `auto_retry_start|end`, `summarization_retry_*`, `extension_error`.

- `message_update` carries only deltas; clients assemble partial state by `contentIndex`; `message_end.message` is authoritative.
- Tool-call streaming exposes `toolcall_start` (`id`, `toolName`), `toolcall_delta` (argument chunks), `toolcall_end` (complete call). This is the only source for progressive edit intent, and argument chunks are partial JSON text.
- `tool_execution_update.partialResult` is cumulative, not a delta.

### Extension UI over RPC

| Method | RPC behavior |
| --- | --- |
| `select`, `confirm`, `input`, `editor` | Request/response sub-protocol with matching `id`; optional `timeout` auto-resolves agent-side |
| `notify`, `setStatus`, `setWidget`, `setTitle`, `set_editor_text` | Fire-and-forget requests |
| `custom` | Returns `undefined` |
| `setWorkingMessage`, `setWorkingIndicator`, `setFooter`, `setHeader`, `setEditorComponent`, `setToolsExpanded` | No-ops |
| `getEditorText`, `getToolsExpanded`, theme getters/setters | Degraded or empty |

`ctx.mode === "rpc"` and `ctx.hasUI === true`; TUI-only capability must be guarded on `ctx.mode === "tui"`.

### Extension hooks a companion extension can use

- `pi.registerTool` at load or at runtime; new tools are callable without `/reload`.
- Built-in tools `read`, `bash`, `powershell`, `edit`, `write`, `grep`, `find`, `ls` can be overridden by registering the same name; result and `details` shapes must match exactly; omitted renderers fall back to built-ins; `promptSnippet`/`promptGuidelines` are not inherited.
- Built-in tools also accept pluggable operations (`ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, …) via `createReadTool` and siblings, plus a `spawnHook` for shell tools. This delegates I/O without reimplementing tool semantics.
- `tool_call` can mutate arguments or block; `tool_result` can post-process.
- `input` event sees raw text with `source` of `interactive | rpc | extension` and can `continue`, `transform`, or `handled`.
- `before_agent_start` exposes and can extend `systemPrompt` and structured `systemPromptOptions`.
- `pi.registerCommand` supports `getArgumentCompletions`; `pi.getCommands` mirrors `get_commands`.
- Tool output must be truncated; built-in limits are 50KB and 2000 lines.

## Neovim facts

### Transport

- `vim.system(cmd, { stdin = true, stdout = fun(err, data), stderr = fun(err, data) })` gives streaming callbacks plus `SystemObj:write(data)` and `obj:write(nil)` to close stdin; `obj:kill()` stops the child.
- Output callbacks run in fast contexts; API calls must be deferred through `vim.schedule` (`api-fast`, `vim.in_fast_event()`).
- `jobstart`/`nvim_chan_send` remain the alternative channel API.

### Buffers and edits

| Need | Mechanism | Caveat |
| --- | --- | --- |
| Precise edit | `nvim_buf_set_text`, `nvim_buf_set_lines` | Each call opens a new undo block |
| One undoable change | `:undojoin` before the next change | `E790` after undo/redo; must not be used blindly |
| Change detection | `nvim_buf_attach` `on_lines`, `b:changedtick` | Callbacks are fast-context |
| Diffing model output against buffer | `vim.text.diff` with `result_type = 'indices'` | Available in 0.12 |
| Disk coherence after buffer edits | buffer write plus `:checktime`; `'autoread'` defaults on | Unwritten buffers stay invisible to filesystem tools |

### Presentation

- Extmarks provide `virt_text` with `virt_text_pos = "inline"` for ghost text, `virt_lines` (with `virt_lines_above`, `virt_lines_overflow`), highlights, and signs.
- `nvim_set_decoration_provider` with `ephemeral` marks renders per redraw without persistent state.
- `vim.diagnostic` namespaces keep agent findings separate from LSP and compiler diagnostics.
- Quickfix and location lists use `setqflist`/`setloclist` and normal navigation.

### Integration surface

- `nvim_exec_autocmds` emits `User` events with a `data` payload (`event-data`), which fits publishing cached pivi state.
- `nvim_create_user_command` supplies subcommands, `nargs`, and custom completion for `:Pi help` and `:Pi command ...`.
- `<Plug>` mappings expose behavior without claiming global keys.
- `vim.ui.select` and `vim.ui.input` can back RPC dialog methods.

## Spec claims versus evidence

| Spec claim | Support | Risk |
| --- | --- | --- |
| Non-terminal JSONL subprocess channel | `pi --mode rpc` plus `vim.system` stdin/stdout | Low |
| Semantic status (queue, run, tool, retry, compaction) | `get_state`, `get_session_stats`, event stream | Low |
| Slash commands via `:Pi command ...` | `get_commands` plus `prompt` with `/name` | Low; TUI-only commands unavailable |
| `:Pi help` runnable during an active run | Extension command executes during streaming, or a separate ephemeral process | Medium; not ephemeral if routed through the working session |
| Loaded buffers authoritative for reads and edits | Built-in tool override or injected `ReadOperations`/`EditOperations`/`WriteOperations` | Medium; result and `details` shapes must match exactly |
| Progressive ghost text from streaming tool arguments | `toolcall_delta` plus inline `virt_text` | Medium; requires tolerant partial-JSON parsing |
| One coherent undoable change per edit | `nvim_buf_set_text` plus `:undojoin` | Medium; `E790` and multi-hunk edits |
| Edits visible to later shell work without watchers | Explicit buffer write plus `:checktime` | Medium; unwritten buffers diverge from disk |
| Extension dialogs mapped to native surfaces | `extension_ui_request`/`extension_ui_response` | Low |
| Multiple sessions, resume, switch | `--session`, `switch_session`, `new_session`, `fork`, `clone` | Low |

## Open questions

- Does `:Pi help` use a second short-lived Pi process, or a companion extension command inside the working session that suppresses history?
- Can two Pi processes safely target one session file, or must ephemeral help always run with `--no-session`?
- Is buffer authority better implemented as operations injection into built-in tools than as full tool overrides, given the exact `details` shape requirement?
- How should partial `toolcall_delta` arguments be parsed safely enough to show ghost text without ever mutating a buffer?
- When must pivi write a modified buffer to disk so that agent shell commands observe agent edits?