Skip to content

ACP multi-file permission requests only display the first file's diff #2951

@alexghergh

Description

@alexghergh

Pre-submission checklist

  • I have read the documentation
  • I have updated the plugin and all dependencies to the latest versions
  • I have searched for existing issues and discussions
  • My issue is not a minor or cosmetic quirk (e.g., formatting, spacing, or other non-functional details)

Neovim version (nvim -v)

NVIM v0.11.5

Operating system/version

Fedora Linux Asahi Remix 42

Adapter and model

Codex, any model

Describe the bug

When an ACP agent sends a single permission request containing diffs for multiple files, CodeCompanion currently only displays the first file's edits in the approval UI.

This is a display bug as well as implementation bug. The underlying agent edit still applies changes to all files after approval, but the user only gets to review the first one (even the in chat Proposed edits section only lists the first file only, so all the other file changes are shadow-allowed, meaning the user may never learn about them unless manually inspecting them).

I can think of a couple of ways to fix this:

  • In chat, render one Proposed edits for ... section per file, chained one after another, and only issue the Accept / Reject buttons on the last file, or separately after all files have been shown.
  • In chat, render a single Proposed edits for ... section for all files, but once the diff shows, the user has the option to go back and forth between file edits (not sure how Accept / Reject would work with this).
  • In chat, render a single Proposed edits for ... section for all files, but once the diff shows, the user can only see the first files edits, then upon closing the diff, the second file's edits open the second diff, then upon closing that the third file's edits will show up in a diff etc., chained together. The last file's diff only shows the g2 Accept or reject buttons. Alternatively, the g2 button in a diff that's not the last diff simply says Move to next diff.
  • Re-build the diff window to support multi-file inspection + global accept / reject.
  • Add buttons for Accept all / Reject all, for multi-file edits. (still, how does the user see all files?)

Because ACP permission is atomic per request, the UI cannot have per-file accept / reject. But it should let the user review all files before making the single global decision.

Note: Titles / summaries for the ACP tool should show Edit 2 files or multiple files if these changes are accepted.

Steps to reproduce

Use an ACP adapter and send a request that proposes edits to two files in one permission request.

For example, ask the agent to edit 2 files in the local directory, at the same time.

E.g. an interaction with Codex:

# User

Can you edit both minimal.lua and README.md files at the same time? Add 'test' at the top of the files.

# Codex

<reasoning>

Edit: minimal.lua

---
**Proposed edits for `minimal.lua`:**

diff
@@ -1,3 +1,4 @@
+-- test
 ---@diagnostic disable: missing-fields
 
 --[[


- `gv` - View
- `g2` - Accept
- `g3` - Reject
- `g4` - Cancel
---

<reasoning>

Added `test` at the top of both files, using syntax-safe comments so `minimal.lua` remains valid.

Expected behavior

The user gets to review both (or multiple) files before deciding on an accept / reject globally.

Screenshots or recordings (optional)

No response

minimal.lua file

---@diagnostic disable: missing-fields

--[[
NOTE: Set the config path to enable the copilot adapter to work.
It will search the following paths for a token:
  - "$CODECOMPANION_TOKEN_PATH/github-copilot/hosts.json"
  - "$CODECOMPANION_TOKEN_PATH/github-copilot/apps.json"
--]]
vim.env["CODECOMPANION_TOKEN_PATH"] = vim.fn.expand("~/.config")

vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()

-- Your CodeCompanion setup
local plugins = {
  {
    "olimorris/codecompanion.nvim",
    -- Test with local version (delete if not required)
    -- dir = "~/Code/Neovim/codecompanion.nvim",
    dependencies = {
      { "nvim-lua/plenary.nvim" },
      {
        "nvim-treesitter/nvim-treesitter",
        lazy = false,
        build = ":TSUpdate",
      },

      -- Test with blink.cmp
      {
        "saghen/blink.cmp",
        version = "1.*",
        opts = {
          keymap = {
            preset = "enter",
            ["<S-Tab>"] = { "select_prev", "fallback" },
            ["<Tab>"] = { "select_next", "fallback" },
          },
          cmdline = { sources = { "cmdline" } },
          sources = {
            default = { "lsp", "path", "buffer", "codecompanion" },
          },
        },
      },

      -- Or, test with nvim-cmp
      -- { "hrsh7th/nvim-cmp" },
    },
    opts = {
      --Refer to: https://github.com/olimorris/codecompanion.nvim/blob/main/lua/codecompanion/config.lua
      adapters = {
        acp = {
          codex = function()
            return require("codecompanion.adapters").extend("codex", {
              defaults = {
                auth_method = "chatgpt",
              },
            })
          end,
        },
      },
      interactions = {
        --NOTE: Change the adapter as required
        chat = { adapter = "codex" },
        inline = { adapter = "copilot" },
      },
      opts = {
        log_level = "DEBUG",
      },
    },
  },
}

require("lazy.minit").repro({ spec = plugins })

-- CONFIGURE PLUGINS HERE -----------------------------------------------------

-- Setup Tree-sitter
-- NOTE: Please restart Neovim to ensure parsers are loaded correctly
require("nvim-treesitter")
  .install({
    "lua",
    "markdown",
    "markdown_inline",
    "yaml",
  }, { summary = true, max_jobs = 10 })
  :wait(1800000)

-- Setup nvim-cmp
-- local cmp_status, cmp = pcall(require, "cmp")
-- if cmp_status then
--   cmp.setup({
--     mapping = cmp.mapping.preset.insert({
--       ["<C-b>"] = cmp.mapping.scroll_docs(-4),
--       ["<C-f>"] = cmp.mapping.scroll_docs(4),
--       ["<C-Space>"] = cmp.mapping.complete(),
--       ["<C-e>"] = cmp.mapping.abort(),
--       ["<CR>"] = cmp.mapping.confirm({ select = true }),
--       -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
--     }),
--   })
-- end

Log output (optional)

No response

Minimal reproduction confirmation

  • Yes, I have tested and provided a minimal.lua file that reproduces the issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestreviewed-by-AIThe CodeCompanion agent reviewed this PR

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions