Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions doc/codecompanion.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3951,6 +3951,27 @@ Also, multiple files can be selected and added to the chat buffer:
Please note that these mappings may be different depending on your provider.


/DOCUMENT ~


[!NOTE] Currently only available with the Anthropic adapter
The `document` slash command allows you to add document files (PDF, DOCX, RTF,
CSV, XLSX) to the chat buffer for analysis and discussion with the LLM. The
command provides three different sources for documents:

- **File**: Browse and select document files from your file system using native, `Telescope`, `mini.pick`, `fzf.lua` or `snacks.nvim` providers
- **URL**: Provide a URL to a publicly accessible document (the URL must end with a supported file extension)
- **Files API**: Reference a document previously uploaded via Anthropic’s Files API by providing the `file_id`

Documents are automatically validated for: - Size limit (32MB maximum per
Anthropic API requirements) - Supported file types (pdf, rtf, docx, csv, xlsx)

In the config for the slash command, you can specify directories (`opts.dirs`)
and filetypes (`opts.filetypes`) to customize the document picker’s search
scope. By default, it searches the current working directory for all supported
document types.


/HELP ~

The `help` slash command allows you to add content from a vim help file
Expand Down
17 changes: 17 additions & 0 deletions doc/usage/chat-buffer/slash-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ The _file_ slash command allows you to add the contents of a file in the current

Please note that these mappings may be different depending on your provider.

## /document

> [!NOTE]
> Currently only available with the Anthropic adapter

The _document_ slash command allows you to add document files (PDF, DOCX, RTF, CSV, XLSX) to the chat buffer for analysis and discussion with the LLM. The command provides different sources for documents depending on adapter capabilities:

- **File**: Browse and select document files from your file system using native, _Telescope_, _mini.pick_, _fzf.lua_ or _snacks.nvim_ providers
- **URL**: Provide a URL to a publicly accessible document (the URL must end with a supported file extension)
- **Files API**: Reference a document previously uploaded via Anthropic's Files API by providing the `file_id` (only available if the adapter supports the Files API)

Documents are automatically validated for:
- Size limit (32MB maximum per Anthropic API requirements)
- Supported file types (pdf, rtf, docx, csv, xlsx)

In the config for the slash command, you can specify directories (`opts.dirs`) and filetypes (`opts.filetypes`) to customize the document picker's search scope. By default, it searches the current working directory for all supported document types.

## /help

The _help_ slash command allows you to add content from a vim help file (`:h helpfile`), to the chat buffer, by searching for help tags. Currently this is only available for _Telescope_, _mini.pick_, _fzf_lua_ and _snacks.nvim_ providers. By default, the slash command will prompt you to trim a help file that is over 1,000 lines in length.
Expand Down
2 changes: 1 addition & 1 deletion lua/codecompanion/adapters/acp/auggie_cli.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ return {

---@param self CodeCompanion.ACPAdapter
---@param messages table
---@param capabilities table
---@param capabilities ACP.agentCapabilities
---@return table
form_messages = function(self, messages, capabilities)
return helpers.form_messages(self, messages, capabilities)
Expand Down
2 changes: 1 addition & 1 deletion lua/codecompanion/adapters/acp/cagent.lua
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ return {

---@param self CodeCompanion.ACPAdapter
---@param messages table
---@param capabilities table
---@param capabilities ACP.agentCapabilities
---@return table
form_messages = function(self, messages, capabilities)
return helpers.form_messages(self, messages, capabilities)
Expand Down
2 changes: 1 addition & 1 deletion lua/codecompanion/adapters/acp/claude_code.lua
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ return {

---@param self CodeCompanion.ACPAdapter
---@param messages table
---@param capabilities table
---@param capabilities ACP.agentCapabilities
---@return table
form_messages = function(self, messages, capabilities)
return helpers.form_messages(self, messages, capabilities)
Expand Down
2 changes: 1 addition & 1 deletion lua/codecompanion/adapters/acp/codex.lua
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ return {

---@param self CodeCompanion.ACPAdapter
---@param messages table
---@param capabilities table
---@param capabilities ACP.agentCapabilities
---@return table
form_messages = function(self, messages, capabilities)
return helpers.form_messages(self, messages, capabilities)
Expand Down
3 changes: 2 additions & 1 deletion lua/codecompanion/adapters/acp/gemini_cli.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ return {
},
opts = {
vision = true,
doc_upload = true,
},
commands = {
default = {
Expand Down Expand Up @@ -50,7 +51,7 @@ return {

---@param self CodeCompanion.ACPAdapter
---@param messages table
---@param capabilities table
---@param capabilities ACP.agentCapabilities
---@return table
form_messages = function(self, messages, capabilities)
return helpers.form_messages(self, messages, capabilities)
Expand Down
2 changes: 1 addition & 1 deletion lua/codecompanion/adapters/acp/goose.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ return {

---@param self CodeCompanion.ACPAdapter
---@param messages table
---@param capabilities table
---@param capabilities ACP.agentCapabilities
---@return table
form_messages = function(self, messages, capabilities)
return helpers.form_messages(self, messages, capabilities)
Expand Down
37 changes: 37 additions & 0 deletions lua/codecompanion/adapters/acp/helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,43 @@ M.form_messages = function(self, messages, capabilities)
}
end
end

if msg._meta and msg._meta.tag == "document" and msg.context and msg.context.mimetype then
if has.embeddedContext then
if msg.context.source == "url" then
-- URL-based document
return {
type = "resource_link",
resource = {
uri = "file://" .. msg.context.url,
name = "",
mimeType = msg.context.mimetype,
},
}
elseif msg.context.source == "file" then
-- Files API reference - requires file_api capability
log:warn("The %s agent does not support Files API", self.formatted_name)
-- Remove the message if file_api is not supported
return nil
else
-- Base64-encoded document
return {
type = "resource",
resource = {
uri = "file://" .. msg.context.path,
name = vim.fn.fnamemodify(msg.context.path, ":t"),
mimeType = msg.context.mimetype,
blob = msg.content,
},
}
end
else
log:warn("The %s agent does not support receiving documents", self.formatted_name)
-- Remove the message if document upload support is not enabled
return nil
end
end

if msg.content and msg.content ~= "" then
if msg._meta and msg._meta.tag == "file" then
-- If we can't send the file as a resource, send as text
Expand Down
2 changes: 1 addition & 1 deletion lua/codecompanion/adapters/acp/kimi_cli.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ return {

---@param self CodeCompanion.ACPAdapter
---@param messages table
---@param capabilities table
---@param capabilities ACP.agentCapabilities
---@return table
form_messages = function(self, messages, capabilities)
return helpers.form_messages(self, messages, capabilities)
Expand Down
2 changes: 1 addition & 1 deletion lua/codecompanion/adapters/acp/opencode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ return {

---@param self CodeCompanion.ACPAdapter
---@param messages table
---@param capabilities table
---@param capabilities ACP.agentCapabilities
---@return table
form_messages = function(self, messages, capabilities)
return helpers.form_messages(self, messages, capabilities)
Expand Down
82 changes: 73 additions & 9 deletions lua/codecompanion/adapters/http/anthropic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ return {
stream = true,
tools = true,
vision = true,
doc_upload = true,
file_api = true,
},
url = "https://api.anthropic.com/v1/messages",
env = {
Expand Down Expand Up @@ -111,6 +113,12 @@ return {
if not model_opts.opts.has_vision then
self.opts.vision = false
end
if not model_opts.opts.has_doc_upload then
self.opts.doc_upload = false
end
if not model_opts.opts.has_file_api then
self.opts.file_api = false
end
end

-- Add the extended output header if enabled
Expand Down Expand Up @@ -198,6 +206,56 @@ return {
end
end

-- 3a. Account for any documents (PDFs)
if m._meta and m._meta.tag == "document" and m.context then
if self.opts and self.opts.doc_upload then
if m.context.source == "url" then
-- URL-based document
m.content = {
{
type = "document",
source = {
type = "url",
url = m.context.url,
},
},
}
elseif m.context.source == "file" then
-- Files API reference - requires file_api capability
if self.opts.file_api then
m.content = {
{
type = "document",
source = {
type = "file",
file_id = m.context.file_id,
},
},
}
else
-- Remove the message if file_api is not supported
return nil
end
else
-- Base64-encoded document
local content_data = m.content
m.content = {
{
type = "document",
source = {
type = "base64",
media_type = m.context.mimetype or "application/pdf",
data = content_data,
},
},
}
end
else
-- Remove the message if document upload support is not enabled
return nil
end
end

-- 4. Remove disallowed keys
m = adapter_utils.filter_out_messages({
message = m,
Expand Down Expand Up @@ -379,7 +437,7 @@ return {
---@return table|nil
form_tools = function(self, tools)
if not self.opts.tools or not tools then
return
return nil
end

local transformed = {}
Expand Down Expand Up @@ -611,35 +669,41 @@ return {
choices = {
["claude-haiku-4-5"] = {
formatted_name = "Claude Haiku 4.5",
opts = { can_reason = true, has_vision = true },
opts = { can_reason = true, has_vision = true, has_doc_upload = true, has_file_api = true },
},
["claude-opus-4-5"] = {
formatted_name = "Claude Opus 4.5",
opts = { can_reason = true, has_vision = true },
opts = { can_reason = true, has_vision = true, has_doc_upload = true, has_file_api = true },
},
["claude-sonnet-4-5"] = {
formatted_name = "Claude Sonnet 4.5",
opts = { can_reason = true, has_vision = true },
opts = { can_reason = true, has_vision = true, has_doc_upload = true, has_file_api = true },
},
["claude-opus-4-1"] = {
formatted_name = "Claude Opus 4.1",
opts = { can_reason = true, has_vision = true },
opts = { can_reason = true, has_vision = true, has_doc_upload = true, has_file_api = true },
},
["claude-opus-4-0"] = {
formatted_name = "Claude Opus 4",
opts = { can_reason = true, has_vision = true },
opts = { can_reason = true, has_vision = true, has_doc_upload = true, has_file_api = true },
},
["claude-sonnet-4-0"] = {
formatted_name = "Claude Sonnet 4",
opts = { can_reason = true, has_vision = true },
opts = { can_reason = true, has_vision = true, has_doc_upload = true, has_file_api = true },
},
["claude-3-7-sonnet-latest"] = {
formatted_name = "Claude Sonnet 3.7",
opts = { can_reason = true, has_vision = true, has_token_efficient_tools = true },
opts = {
can_reason = true,
has_vision = true,
has_token_efficient_tools = true,
has_doc_upload = true,
has_file_api = true,
},
},
["claude-3-5-haiku-latest"] = {
formatted_name = "Claude Haiku 3.5",
opts = { has_vision = true },
opts = { has_vision = true, has_doc_upload = true, has_file_api = true },
},
},
},
Expand Down
17 changes: 17 additions & 0 deletions lua/codecompanion/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,23 @@ If you are providing code changes, use the insert_edit_into_file tool (if availa
provider = providers.images, -- telescope|snacks|default
},
},
["document"] = {
callback = "interactions.chat.slash_commands.builtin.document",
description = "Insert a document (PDF)",
---@param opts { adapter: CodeCompanion.HTTPAdapter|CodeCompanion.ACPAdapter }
---@return boolean
enabled = function(opts)
if opts.adapter and opts.adapter.opts then
return opts.adapter.opts.doc_upload == true
end
return false
end,
opts = {
dirs = {}, -- Directories to search for documents
filetypes = { "pdf", "rtf", "docx", "csv", "xslx" }, -- Filetypes to search for
provider = providers.pickers, -- telescope|fzf_lua|mini_pick|snacks|default
},
},
["rules"] = {
callback = "interactions.chat.slash_commands.builtin.rules",
description = "Insert rules into the chat buffer",
Expand Down
37 changes: 37 additions & 0 deletions lua/codecompanion/interactions/chat/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,43 @@ function Chat:add_image_message(image, opts)
})
end

---Add a document to the chat buffer
---@param document CodeCompanion.Document The document object containing the path and other metadata
---@param opts? {role?: "user"|string, source?: string, bufnr?: integer} Options for adding the document
---@return nil
function Chat:add_document_message(document, opts)
opts = vim.tbl_deep_extend("force", {
role = config.constants.USER_ROLE,
source = "codecompanion.interactions.chat.slash_commands.document",
bufnr = document.bufnr,
}, opts or {})

local id = "<document>" .. (document.id or document.path) .. "</document>"

self:add_message({
role = opts.role,
content = document.base64 or "",
}, {
context = {
id = id,
mimetype = document.mimetype,
path = document.path or document.id,
source = document.source,
url = document.url,
file_id = document.file_id,
},
_meta = { tag = "document" },
visible = false,
})

self.context:add({
bufnr = opts.bufnr,
id = id,
path = document.path or document.url or document.file_id,
source = opts.source,
})
end

---Apply any tools or variables that a user has tagged in their message
---@param message table
---@return nil
Expand Down
Loading