From 1eb8e2fbfb4f582634ed86045530273cf4b3c0f0 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Sat, 2 Aug 2025 11:27:03 +0800 Subject: [PATCH 01/20] feat(nvim): Add VectorCode integration for Neovim help files --- .../_extensions/vectorcode/init.lua | 13 ++++- .../integrations/codecompanion/init.lua | 3 + .../codecompanion/prompts/nvim.lua | 58 +++++++++++++++++++ 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 lua/vectorcode/integrations/codecompanion/prompts/nvim.lua diff --git a/lua/codecompanion/_extensions/vectorcode/init.lua b/lua/codecompanion/_extensions/vectorcode/init.lua index ec25b1ad..1618e988 100644 --- a/lua/codecompanion/_extensions/vectorcode/init.lua +++ b/lua/codecompanion/_extensions/vectorcode/init.lua @@ -52,7 +52,8 @@ local M = { opts.tool_opts = merge_tool_opts(opts.tool_opts) logger.info("Received codecompanion extension opts:\n", opts) local cc_config = require("codecompanion.config").config - local cc_integration = require("vectorcode.integrations").codecompanion.chat + local cc_integration = require("vectorcode.integrations").codecompanion + local cc_chat_integration = cc_integration.chat for _, sub_cmd in pairs(valid_tools) do local tool_name = string.format("vectorcode_%s", sub_cmd) if cc_config.strategies.chat.tools[tool_name] ~= nil then @@ -73,7 +74,7 @@ local M = { else cc_config.strategies.chat.tools[tool_name] = { description = string.format("Run VectorCode %s tool", sub_cmd), - callback = cc_integration.make_tool(sub_cmd, opts.tool_opts[sub_cmd]), + callback = cc_chat_integration.make_tool(sub_cmd, opts.tool_opts[sub_cmd]), opts = { requires_approval = opts.tool_opts[sub_cmd].requires_approval }, } logger.info(string.format("%s tool has been created.", tool_name)) @@ -105,6 +106,14 @@ local M = { tools = included_tools, } end + + for _, prompt in pairs(cc_integration.prompts) do + if type(prompt) == "function" then + ---@diagnostic disable-next-line: cast-local-type + prompt = prompt() + end + cc_config.prompt_library[prompt.name] = prompt.prompts + end end), } diff --git a/lua/vectorcode/integrations/codecompanion/init.lua b/lua/vectorcode/integrations/codecompanion/init.lua index 5358c18b..5fe5cb69 100644 --- a/lua/vectorcode/integrations/codecompanion/init.lua +++ b/lua/vectorcode/integrations/codecompanion/init.lua @@ -16,4 +16,7 @@ return { end end, }, + prompts = { + require("vectorcode.integrations.codecompanion.prompts.nvim"), + }, } diff --git a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua new file mode 100644 index 00000000..74de0a89 --- /dev/null +++ b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua @@ -0,0 +1,58 @@ +---@module "codecompanion" + +return require("vectorcode.config").check_cli_wrap(function() + local constants = require("codecompanion.config").config.constants + local rtp = vim.fs.normalize(vim.env.VIMRUNTIME) + + if not rtp then + return error("Failed to locate the neovim runtime!", vim.log.levels.ERROR) + end + local stat = vim.uv.fs_stat(rtp) + if not stat or stat.type ~= "directory" then + return error( + string.format( + "$VIMRUNTIME is %s, which is not a valid directory to be accessed.", + rtp + ), + vim.log.levels.ERROR + ) + end + return { + name = "Neovim Assistant", + prompts = { + strategy = "chat", + description = "Use VectorCode to index and query from neovim documentation and lua runtime.", + + prompts = { + { + role = constants.SYSTEM_ROLE, + content = string.format( + [[You are an neovim expert. +You will be given tools to index and query from the neovim runtime library. +This will include lua APIs for the neovim runtime and documentation for the neovim build that the user is running. +Use the tools to explain the user's questions related to neovim. +The runtime files are stored in `%s`. +You can ONLY use the vectorcode tools to interact with these files or directory. +DO NOT attempt to read from or write into this directory. +]], + rtp + ), + }, + { + role = constants.USER_ROLE, + content = string.format( + [[ +You are given the @{vectorcode_vectorise} and @{vectorcode_query} tools. +Vectorrise all lua files and `*.txt` files under this directory using the `vectorcode_vectorise` tool. Use wildcards to match all lua and `txt` files. Use absolute paths when supplying paths to the vectorcode_vectorise tool; +Use `%s` as the value of the `project_root` argument; +When you're done, I'll be asking you questions related to these documents. +Use the vectorcode_query tool to query from the project root and answer my question. +]], + rtp, + rtp + ), + }, + }, + }, + } +end) From 0361052f607eed20313297729eebbb6b8d8be7ef Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Tue, 12 Aug 2025 11:40:45 +0800 Subject: [PATCH 02/20] feat(nvim): use pre_hook to prepare the embeddings. --- .../codecompanion/prompts/init.lua | 28 +++++++++++++ .../codecompanion/prompts/nvim.lua | 41 ++++++++++++++++--- .../codecompanion/vectorise_tool.lua | 2 +- lua/vectorcode/jobrunner/init.lua | 4 +- 4 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 lua/vectorcode/integrations/codecompanion/prompts/init.lua diff --git a/lua/vectorcode/integrations/codecompanion/prompts/init.lua b/lua/vectorcode/integrations/codecompanion/prompts/init.lua new file mode 100644 index 00000000..1d150753 --- /dev/null +++ b/lua/vectorcode/integrations/codecompanion/prompts/init.lua @@ -0,0 +1,28 @@ +local M = {} + +local vc_config = require("vectorcode.config") + +---@param path string[]|string path to files or wildcards. +---@param project_root? string +---@param callback? VectorCode.JobRunner.Callback +function M.vectorise_files(path, project_root, callback) + if type(path) == "string" then + path = { path } + end + local jobrunner = + require("vectorcode.integrations.codecompanion.common").initialise_runner( + vc_config.get_user_config().async_backend == "lsp" + ) + + local args = { "vectorise", "--pipe" } + if project_root then + vim.list_extend(args, { "--project_root", project_root }) + end + vim.list_extend(args, path) + jobrunner.run_async(args, function(result, error, code, signal) + if type(callback) == "function" then + callback(result, error, code, signal) + end + end, 0) +end +return M diff --git a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua index 74de0a89..37f40521 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua @@ -1,5 +1,26 @@ ---@module "codecompanion" +local config = require("vectorcode.config") +local prepare = function(rtp) + vim.notify( + "Vectorising neovim runtime files...", + vim.log.levels.INFO, + config.notify_opts + ) + require("vectorcode.integrations.codecompanion.prompts").vectorise_files( + vim.fs.joinpath(rtp, "**/*.lua"), + rtp, + function(result, _, _, _) + if result ~= nil and not vim.tbl_isempty(result) then + vim.schedule_wrap(vim.notify)( + string.format("Added %d files to the database!", result.add or 0), + vim.log.levels.INFO, + config.notify_opts + ) + end + end + ) +end return require("vectorcode.config").check_cli_wrap(function() local constants = require("codecompanion.config").config.constants local rtp = vim.fs.normalize(vim.env.VIMRUNTIME) @@ -17,12 +38,17 @@ return require("vectorcode.config").check_cli_wrap(function() vim.log.levels.ERROR ) end + return { name = "Neovim Assistant", prompts = { strategy = "chat", description = "Use VectorCode to index and query from neovim documentation and lua runtime.", - + opts = { + pre_hook = function() + prepare(rtp) + end, + }, prompts = { { role = constants.SYSTEM_ROLE, @@ -30,10 +56,11 @@ return require("vectorcode.config").check_cli_wrap(function() [[You are an neovim expert. You will be given tools to index and query from the neovim runtime library. This will include lua APIs for the neovim runtime and documentation for the neovim build that the user is running. -Use the tools to explain the user's questions related to neovim. The runtime files are stored in `%s`. You can ONLY use the vectorcode tools to interact with these files or directory. DO NOT attempt to read from or write into this directory. +When the user asked a question that is not part of a previous query tool call, make a new query using new keywords that are directly relevant to the new question. +If the tool returns an error that says the collection doesn't exist, it's because the files are still being indexed, and you should ask the user to wait for it to finish. ]], rtp ), @@ -42,13 +69,15 @@ DO NOT attempt to read from or write into this directory. role = constants.USER_ROLE, content = string.format( [[ -You are given the @{vectorcode_vectorise} and @{vectorcode_query} tools. -Vectorrise all lua files and `*.txt` files under this directory using the `vectorcode_vectorise` tool. Use wildcards to match all lua and `txt` files. Use absolute paths when supplying paths to the vectorcode_vectorise tool; +You are given the @{vectorcode_query} tool. Use `%s` as the value of the `project_root` argument; -When you're done, I'll be asking you questions related to these documents. +I'll be asking you questions related to these documents. Use the vectorcode_query tool to query from the project root and answer my question. + +Here's my question: + +- ]], - rtp, rtp ), }, diff --git a/lua/vectorcode/integrations/codecompanion/vectorise_tool.lua b/lua/vectorcode/integrations/codecompanion/vectorise_tool.lua index d26dde05..cba59190 100644 --- a/lua/vectorcode/integrations/codecompanion/vectorise_tool.lua +++ b/lua/vectorcode/integrations/codecompanion/vectorise_tool.lua @@ -52,7 +52,7 @@ The paths should be accurate (DO NOT ASSUME A PATH EXIST) and case case-sensitiv paths = { type = "array", items = { type = "string" }, - description = "Paths to the files to be vectorised. DO NOT use directories for this parameter.", + description = "Paths to the files to be vectorised. DO NOT use directories for this parameter. You may use wildcard here if the user instructed to do so.", }, project_root = { type = "string", diff --git a/lua/vectorcode/jobrunner/init.lua b/lua/vectorcode/jobrunner/init.lua index 48d77aff..b49754ce 100644 --- a/lua/vectorcode/jobrunner/init.lua +++ b/lua/vectorcode/jobrunner/init.lua @@ -1,5 +1,7 @@ local utils = require("vectorcode.utils") +---@alias VectorCode.JobRunner.Callback fun(result: table, error: table, code:integer, signal: integer?) + --- A class for calling vectorcode commands that aims at providing a unified API for both LSP and command-line backend. --- Implementations exist for both direct command-line execution (`cmd.lua`) and LSP (`lsp.lua`). --- For the format of the `result`, see https://github.com/Davidyz/VectorCode/blob/main/docs/cli.md#for-developers @@ -13,7 +15,7 @@ local utils = require("vectorcode.utils") --- - `signal`: _for cmd runner only_, the shell signal sent to the process. --- The `bufnr` is used for context, potentially to find the project root or attach LSP clients. --- Returns a job handle (e.g., PID or LSP request ID) or nil if the job couldn't be started. ----@field run_async fun(args: string[], callback:fun(result: table, error: table, code:integer, signal: integer?)?, bufnr: integer):(job_handle:integer?) +---@field run_async fun(args: string[], callback:VectorCode.JobRunner.Callback?, bufnr: integer):(job_handle:integer?) --- Runs a vectorcode command synchronously, blocking until completion or timeout. --- Executes the command specified by `args`. Waits for up to `timeout_ms` milliseconds. --- The `bufnr` is used for context, potentially to find the project root or attach LSP clients. From fd0ac2a0dca372b16fd68194858aa92fc9256c75 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 13 Aug 2025 17:41:52 +0800 Subject: [PATCH 03/20] feat(nvim): also vectorise help files --- lua/vectorcode/integrations/codecompanion/prompts/nvim.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua index 37f40521..e236db95 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua @@ -8,7 +8,7 @@ local prepare = function(rtp) config.notify_opts ) require("vectorcode.integrations.codecompanion.prompts").vectorise_files( - vim.fs.joinpath(rtp, "**/*.lua"), + { vim.fs.joinpath(rtp, "lua/**/*.lua"), vim.fs.joinpath(rtp, "doc/**/*.txt") }, rtp, function(result, _, _, _) if result ~= nil and not vim.tbl_isempty(result) then From fdbd538716609dc10db09b336d66d049a30e0965 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 13 Aug 2025 17:42:18 +0800 Subject: [PATCH 04/20] feat(nvim): Add toggleable notifications for vectorising help files --- .../codecompanion/prompts/nvim.lua | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua index e236db95..685bf170 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua @@ -1,17 +1,25 @@ ---@module "codecompanion" local config = require("vectorcode.config") + +---@param rtp string path to the project_root local prepare = function(rtp) - vim.notify( - "Vectorising neovim runtime files...", - vim.log.levels.INFO, - config.notify_opts - ) + if config.get_user_config().notify then + vim.notify( + "Vectorising neovim runtime files...", + vim.log.levels.INFO, + config.notify_opts + ) + end require("vectorcode.integrations.codecompanion.prompts").vectorise_files( { vim.fs.joinpath(rtp, "lua/**/*.lua"), vim.fs.joinpath(rtp, "doc/**/*.txt") }, rtp, function(result, _, _, _) - if result ~= nil and not vim.tbl_isempty(result) then + if + result ~= nil + and not vim.tbl_isempty(result) + and config.get_user_config().notify + then vim.schedule_wrap(vim.notify)( string.format("Added %d files to the database!", result.add or 0), vim.log.levels.INFO, @@ -21,6 +29,7 @@ local prepare = function(rtp) end ) end + return require("vectorcode.config").check_cli_wrap(function() local constants = require("codecompanion.config").config.constants local rtp = vim.fs.normalize(vim.env.VIMRUNTIME) @@ -61,6 +70,7 @@ You can ONLY use the vectorcode tools to interact with these files or directory. DO NOT attempt to read from or write into this directory. When the user asked a question that is not part of a previous query tool call, make a new query using new keywords that are directly relevant to the new question. If the tool returns an error that says the collection doesn't exist, it's because the files are still being indexed, and you should ask the user to wait for it to finish. +Do not cite information that was not part of the provided context or tool output. ]], rtp ), From d6c0db3ffba31e33ba051a21ed4561c7b731329d Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Fri, 15 Aug 2025 16:48:33 +0800 Subject: [PATCH 05/20] fix(nvim): make sure to load the custom system prompt --- lua/vectorcode/integrations/codecompanion/prompts/nvim.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua index 685bf170..73b73eb0 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua @@ -57,6 +57,7 @@ return require("vectorcode.config").check_cli_wrap(function() pre_hook = function() prepare(rtp) end, + ignore_system_prompt = true, }, prompts = { { From e1288c85547a50f68086bd38686d50d8631ef444 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Tue, 2 Sep 2025 11:25:35 +0800 Subject: [PATCH 06/20] feat(nvim): Add customisable prompt library for CodeCompanion --- docs/neovim/README.md | 29 ++++++ .../_extensions/vectorcode/init.lua | 35 ++++++- .../integrations/codecompanion/init.lua | 4 +- .../codecompanion/prompts/init.lua | 93 ++++++++++++++++++ .../codecompanion/prompts/nvim.lua | 98 ------------------- lua/vectorcode/utils.lua | 16 +++ 6 files changed, 169 insertions(+), 106 deletions(-) delete mode 100644 lua/vectorcode/integrations/codecompanion/prompts/nvim.lua diff --git a/docs/neovim/README.md b/docs/neovim/README.md index 2a10b528..183a490c 100644 --- a/docs/neovim/README.md +++ b/docs/neovim/README.md @@ -18,6 +18,8 @@ * [Integrations](#integrations) * [milanglacier/minuet-ai.nvim](#milanglacierminuet-ainvim) * [olimorris/codecompanion.nvim](#olimorriscodecompanionnvim) + * [Tools](#tools) + * [Prompt Library](#prompt-library) * [CopilotC-Nvim/CopilotChat.nvim](#copilotc-nvimcopilotchatnvim) * [Setup](#setup) * [Configuration Options](#configuration-options) @@ -155,6 +157,7 @@ or change the value of `async_opts.n_query` in the `setup` function [![asciicast](https://asciinema.org/a/8WP8QJHNAR9lEllZSSx3poLPD.svg)](https://asciinema.org/a/8WP8QJHNAR9lEllZSSx3poLPD?t=3) +#### Tools The following requires VectorCode 0.7+ and a recent version of CodeCompanion.nvim. The CodeCompanion extension will register the following tools: @@ -277,6 +280,32 @@ The `query` tool contains the following extra config options: query so that when the LLM decide what information to include, it _may_ be able to avoid omitting stuff related to query. +#### Prompt Library + +On VectorCode 0.7.16+ and CodeCompanion.nvim 17.20.0+, VectorCode also provides a +customisable prompt library that helps you RAG local directories. The following is the +a preset that vectorises the lua source code and help files in the neovim runtime +directory. +```lua +require('codecompanion').setup{ + extensions = { + vectorcode = { + ---@type VectorCode.CodeCompanion.ExtensionOpts + opts = { + prompt_library = { + { + ["Neovim Tutor"] = { + project_root = vim.env.VIMRUNTIME, + file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, + }, + }, + } + } + } + } +} +``` + ### [CopilotC-Nvim/CopilotChat.nvim](https://github.com/CopilotC-Nvim/CopilotChat.nvim) [CopilotC-Nvim/CopilotChat.nvim](https://github.com/CopilotC-Nvim/CopilotChat.nvim) diff --git a/lua/codecompanion/_extensions/vectorcode/init.lua b/lua/codecompanion/_extensions/vectorcode/init.lua index 1618e988..4f8a55f8 100644 --- a/lua/codecompanion/_extensions/vectorcode/init.lua +++ b/lua/codecompanion/_extensions/vectorcode/init.lua @@ -8,9 +8,17 @@ ---@field tool_opts table --- Whether to add a tool group that contains all vectorcode tools. ---@field tool_group VectorCode.CodeCompanion.ToolGroupOpts +---Prompt library that automatically creates VectorCode collections on local files +---and set up prompts to let LLM search from certain directories. +--- +---The keys should be the human-readable name of the prompt (as they'd appear in +---the action menu), and values would be `VectorCode.CodeCompanion.PromptFactory.Opts` +---objects. +---@field prompt_library table local vc_config = require("vectorcode.config") local logger = vc_config.logger +local vim_runtime = vim.fs.normalize(vim.env.VIMRUNTIME) ---@type VectorCode.CodeCompanion.ExtensionOpts|{} local default_extension_opts = { @@ -25,6 +33,13 @@ local default_extension_opts = { files_rm = {}, }, tool_group = { enabled = true, collapse = true, extras = {} }, + + prompt_library = { + ["Neovim Tutor"] = { + project_root = vim_runtime, + file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, + }, + }, } ---@type sub_cmd[] @@ -107,12 +122,22 @@ local M = { } end - for _, prompt in pairs(cc_integration.prompts) do - if type(prompt) == "function" then - ---@diagnostic disable-next-line: cast-local-type - prompt = prompt() + for name, prompt_opts in pairs(opts.prompt_library) do + if prompt_opts.name ~= nil and prompt_opts.name ~= name then + vim.notify( + string.format( + "The name of `%s` is inconsistent in the opts (`%s`).\nRenaming to `%s`.", + name, + prompt_opts.name, + name + ), + vim.log.levels.WARN, + vc_config.notify_opts + ) end - cc_config.prompt_library[prompt.name] = prompt.prompts + prompt_opts.name = name + cc_config.prompt_library[name] = + cc_chat_integration.prompts.register_prompt(prompt_opts) end end), } diff --git a/lua/vectorcode/integrations/codecompanion/init.lua b/lua/vectorcode/integrations/codecompanion/init.lua index 5fe5cb69..ea793976 100644 --- a/lua/vectorcode/integrations/codecompanion/init.lua +++ b/lua/vectorcode/integrations/codecompanion/init.lua @@ -15,8 +15,6 @@ return { error("Unsupported version of codecompanion!") end end, - }, - prompts = { - require("vectorcode.integrations.codecompanion.prompts.nvim"), + prompts = require("vectorcode.integrations.codecompanion.prompts"), }, } diff --git a/lua/vectorcode/integrations/codecompanion/prompts/init.lua b/lua/vectorcode/integrations/codecompanion/prompts/init.lua index 1d150753..5c2964c3 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/init.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/init.lua @@ -1,6 +1,8 @@ local M = {} local vc_config = require("vectorcode.config") +local utils = require("vectorcode.utils") +local constants = require("codecompanion.config").config.constants ---@param path string[]|string path to files or wildcards. ---@param project_root? string @@ -25,4 +27,95 @@ function M.vectorise_files(path, project_root, callback) end end, 0) end + +---@class VectorCode.CodeCompanion.PromptFactory.Opts +---@field name string? human-readable name of this prompt +---@field project_root string project_root of the files to be added to the database +---Paths to the files in the local directory to be added to the database. +--- +---These should either be absolute paths, or relative to the project root. +---@field file_patterns string[] +---See https://codecompanion.olimorris.dev/extending/prompts.html#recipe-2-using-context-in-your-prompts +--- +---Note: If a system prompt is set here, your default chat system prompt will be ignored. +---@field system_prompt? string|fun(context:table):string +---This contains some preliminary messages (filled into the chat buffer) that tells the LLM about the task. +---If you're overwriting the default message, make sure to include the tool (`@{vectorcode_query}`). +--- +---See https://codecompanion.olimorris.dev/extending/prompts.html#recipe-2-using-context-in-your-prompts +---@field user_prompt? string|fun(context:table):string + +---@param opts VectorCode.CodeCompanion.PromptFactory.Opts +function M.register_prompt(opts) + assert( + utils.is_directory(opts.project_root), + string.format("`%s` is not a valid directory.", opts.project_root) + ) + assert( + opts.file_patterns ~= nil and (not vim.tbl_isempty(opts.file_patterns)), + "Recieved empty path specs." + ) + + assert(type(opts.name) == "string", "`name` cannot be `nil`.") + + local prompts = {} + if opts.system_prompt then + table.insert( + prompts, + { role = constants.SYSTEM_ROLE, content = opts.system_prompt } + ) + end + table.insert(prompts, #prompts + 1, { + role = constants.USER_ROLE, + content = opts.user_prompt + or string.format( + [[I have some questions about the documents under the `%s` directory. +The files have been added to the database and can be searched by calling the @{vectorcode_query} tool. +When you call the tool, use `%s` as the value for the argument `project_root`. +Use the information returned by the tool to answer my questions, and cite the sources when appropriate. +If you need more information, call the tool with different search keywords or ask for more context and/or tools. + +Here's my question: + +- ]], + opts.project_root, + opts.project_root + ), + }) + return { + name = opts.name, + strategy = "chat", + opts = { + ignore_system_prompt = opts.system_prompt ~= nil, + pre_hook = function() + M.vectorise_files( + vim.iter(opts.file_patterns):map(function(p) + if vim.fn.isabsolutepath(p) == 1 then + return p + else + return vim.fs.joinpath(opts.project_root, p) + end + end), + opts.project_root, + function(result, _, _, _) + if + result ~= nil + and not vim.tbl_isempty(result) + and vc_config.get_user_config().notify + then + vim.schedule_wrap(vim.notify)( + string.format( + "Vectorised %d files at `%s`.", + result.add or 0, + opts.project_root + ) + ) + end + end + ) + end, + }, + prompts = prompts, + } +end return M diff --git a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua b/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua deleted file mode 100644 index 73b73eb0..00000000 --- a/lua/vectorcode/integrations/codecompanion/prompts/nvim.lua +++ /dev/null @@ -1,98 +0,0 @@ ----@module "codecompanion" - -local config = require("vectorcode.config") - ----@param rtp string path to the project_root -local prepare = function(rtp) - if config.get_user_config().notify then - vim.notify( - "Vectorising neovim runtime files...", - vim.log.levels.INFO, - config.notify_opts - ) - end - require("vectorcode.integrations.codecompanion.prompts").vectorise_files( - { vim.fs.joinpath(rtp, "lua/**/*.lua"), vim.fs.joinpath(rtp, "doc/**/*.txt") }, - rtp, - function(result, _, _, _) - if - result ~= nil - and not vim.tbl_isempty(result) - and config.get_user_config().notify - then - vim.schedule_wrap(vim.notify)( - string.format("Added %d files to the database!", result.add or 0), - vim.log.levels.INFO, - config.notify_opts - ) - end - end - ) -end - -return require("vectorcode.config").check_cli_wrap(function() - local constants = require("codecompanion.config").config.constants - local rtp = vim.fs.normalize(vim.env.VIMRUNTIME) - - if not rtp then - return error("Failed to locate the neovim runtime!", vim.log.levels.ERROR) - end - local stat = vim.uv.fs_stat(rtp) - if not stat or stat.type ~= "directory" then - return error( - string.format( - "$VIMRUNTIME is %s, which is not a valid directory to be accessed.", - rtp - ), - vim.log.levels.ERROR - ) - end - - return { - name = "Neovim Assistant", - prompts = { - strategy = "chat", - description = "Use VectorCode to index and query from neovim documentation and lua runtime.", - opts = { - pre_hook = function() - prepare(rtp) - end, - ignore_system_prompt = true, - }, - prompts = { - { - role = constants.SYSTEM_ROLE, - content = string.format( - [[You are an neovim expert. -You will be given tools to index and query from the neovim runtime library. -This will include lua APIs for the neovim runtime and documentation for the neovim build that the user is running. -The runtime files are stored in `%s`. -You can ONLY use the vectorcode tools to interact with these files or directory. -DO NOT attempt to read from or write into this directory. -When the user asked a question that is not part of a previous query tool call, make a new query using new keywords that are directly relevant to the new question. -If the tool returns an error that says the collection doesn't exist, it's because the files are still being indexed, and you should ask the user to wait for it to finish. -Do not cite information that was not part of the provided context or tool output. -]], - rtp - ), - }, - { - role = constants.USER_ROLE, - content = string.format( - [[ -You are given the @{vectorcode_query} tool. -Use `%s` as the value of the `project_root` argument; -I'll be asking you questions related to these documents. -Use the vectorcode_query tool to query from the project root and answer my question. - -Here's my question: - -- -]], - rtp - ), - }, - }, - }, - } -end) diff --git a/lua/vectorcode/utils.lua b/lua/vectorcode/utils.lua index 341c96cc..f9d65bc9 100644 --- a/lua/vectorcode/utils.lua +++ b/lua/vectorcode/utils.lua @@ -153,4 +153,20 @@ function M.make_changes_cb(max_num) end end +---@param f string +---@return boolean +function M.is_file(f) + assert(type(f) == "string", "`f` is not a string") + local stats = vim.uv.fs_stat(f) + return stats and (stats.type == "file") or false +end + +---@param f string +---@return boolean +function M.is_directory(f) + assert(type(f) == "string", "`f` is not a string") + local stats = vim.uv.fs_stat(f) + return stats and (stats.type == "directory") or false +end + return M From a5b5cefd6bb0aee21e67daab03a2dff005bd89ad Mon Sep 17 00:00:00 2001 From: Davidyz <30951234+Davidyz@users.noreply.github.com> Date: Tue, 2 Sep 2025 03:26:10 +0000 Subject: [PATCH 07/20] Auto generate docs --- doc/VectorCode.txt | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/doc/VectorCode.txt b/doc/VectorCode.txt index 14551f58..d05227af 100644 --- a/doc/VectorCode.txt +++ b/doc/VectorCode.txt @@ -29,6 +29,8 @@ Table of Contents *VectorCode-table-of-contents* - |VectorCode-integrations| - |VectorCode-milanglacier/minuet-ai.nvim| - |VectorCode-olimorris/codecompanion.nvim| + - |VectorCode-tools| + - |VectorCode-prompt-library| - |VectorCode-copilotc-nvim/copilotchat.nvim| - |VectorCode-setup| - |VectorCode-configuration-options| @@ -176,6 +178,9 @@ OLIMORRIS/CODECOMPANION.NVIM ~ + +TOOLS + The following requires VectorCode 0.7+ and a recent version of CodeCompanion.nvim. @@ -295,6 +300,34 @@ so that when the LLM decide what information to include, it _may_ be able to avoid omitting stuff related to query. +PROMPT LIBRARY + +On VectorCode 0.7.16+ and CodeCompanion.nvim 17.20.0+, VectorCode also provides +a customisable prompt library that helps you RAG local directories. The +following is the a preset that vectorises the lua source code and help files in +the neovim runtime directory. + +>lua + require('codecompanion').setup{ + extensions = { + vectorcode = { + ---@type VectorCode.CodeCompanion.ExtensionOpts + opts = { + prompt_library = { + { + ["Neovim Tutor"] = { + project_root = vim.env.VIMRUNTIME, + file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, + }, + }, + } + } + } + } + } +< + + COPILOTC-NVIM/COPILOTCHAT.NVIM ~ CopilotC-Nvim/CopilotChat.nvim From da6b51a182ced395b41c11d9c1585fc62d7934d1 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Tue, 2 Sep 2025 11:37:01 +0800 Subject: [PATCH 08/20] feat(nvim): Improve vectorisation feedback --- docs/neovim/README.md | 3 +++ .../codecompanion/prompts/init.lua | 21 +++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/neovim/README.md b/docs/neovim/README.md index 183a490c..aae739c7 100644 --- a/docs/neovim/README.md +++ b/docs/neovim/README.md @@ -306,6 +306,9 @@ require('codecompanion').setup{ } ``` +The first time will take some extra time for computing the embeddings, but the +subsequent runs should be a lot faster. + ### [CopilotC-Nvim/CopilotChat.nvim](https://github.com/CopilotC-Nvim/CopilotChat.nvim) [CopilotC-Nvim/CopilotChat.nvim](https://github.com/CopilotC-Nvim/CopilotChat.nvim) diff --git a/lua/vectorcode/integrations/codecompanion/prompts/init.lua b/lua/vectorcode/integrations/codecompanion/prompts/init.lua index 5c2964c3..66cb9ed0 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/init.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/init.lua @@ -1,6 +1,7 @@ local M = {} local vc_config = require("vectorcode.config") +local cc_common = require("vectorcode.integrations.codecompanion.common") local utils = require("vectorcode.utils") local constants = require("codecompanion.config").config.constants @@ -88,6 +89,9 @@ Here's my question: opts = { ignore_system_prompt = opts.system_prompt ~= nil, pre_hook = function() + vim.notify( + string.format("Add files under `%s` to the database.", opts.project_root) + ) M.vectorise_files( vim.iter(opts.file_patterns):map(function(p) if vim.fn.isabsolutepath(p) == 1 then @@ -97,12 +101,8 @@ Here's my question: end end), opts.project_root, - function(result, _, _, _) - if - result ~= nil - and not vim.tbl_isempty(result) - and vc_config.get_user_config().notify - then + function(result, err, _, _) + if result ~= nil and not vim.tbl_isempty(result) then vim.schedule_wrap(vim.notify)( string.format( "Vectorised %d files at `%s`.", @@ -110,6 +110,15 @@ Here's my question: opts.project_root ) ) + elseif err ~= nil then + err = cc_common.flatten_table_to_string(err) + if err ~= "" then + vim.schedule_wrap(vim.notify)( + err, + vim.log.levels.WARN, + vc_config.notify_opts + ) + end end end ) From 87f45203010b926962f135dedbe075d1131d7a90 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Tue, 2 Sep 2025 11:40:29 +0800 Subject: [PATCH 09/20] fix(nvim): loop require. --- lua/vectorcode/integrations/codecompanion/prompts/init.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/vectorcode/integrations/codecompanion/prompts/init.lua b/lua/vectorcode/integrations/codecompanion/prompts/init.lua index 66cb9ed0..9ab4b92e 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/init.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/init.lua @@ -1,9 +1,8 @@ local M = {} local vc_config = require("vectorcode.config") -local cc_common = require("vectorcode.integrations.codecompanion.common") + local utils = require("vectorcode.utils") -local constants = require("codecompanion.config").config.constants ---@param path string[]|string path to files or wildcards. ---@param project_root? string @@ -59,7 +58,10 @@ function M.register_prompt(opts) assert(type(opts.name) == "string", "`name` cannot be `nil`.") + local cc_common = require("vectorcode.integrations.codecompanion.common") + local constants = require("codecompanion.config").config.constants local prompts = {} + if opts.system_prompt then table.insert( prompts, From 0af447bd20878aab3c1c876a61a0c336e8745797 Mon Sep 17 00:00:00 2001 From: Davidyz <30951234+Davidyz@users.noreply.github.com> Date: Tue, 2 Sep 2025 03:41:03 +0000 Subject: [PATCH 10/20] Auto generate docs --- doc/VectorCode.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/VectorCode.txt b/doc/VectorCode.txt index d05227af..652e7388 100644 --- a/doc/VectorCode.txt +++ b/doc/VectorCode.txt @@ -327,6 +327,9 @@ the neovim runtime directory. } < +The first time will take some extra time for computing the embeddings, but the +subsequent runs should be a lot faster. + COPILOTC-NVIM/COPILOTCHAT.NVIM ~ From a9c0106d6f499c54b98cbc38abc64d34f0b67906 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Tue, 2 Sep 2025 11:57:20 +0800 Subject: [PATCH 11/20] feat(nvim): allow function type for `project_root` and `file_patterns` --- .../codecompanion/prompts/init.lua | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lua/vectorcode/integrations/codecompanion/prompts/init.lua b/lua/vectorcode/integrations/codecompanion/prompts/init.lua index 9ab4b92e..01b87c70 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/init.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/init.lua @@ -30,11 +30,11 @@ end ---@class VectorCode.CodeCompanion.PromptFactory.Opts ---@field name string? human-readable name of this prompt ----@field project_root string project_root of the files to be added to the database +---@field project_root string|(fun():string) project_root of the files to be added to the database ---Paths to the files in the local directory to be added to the database. --- ---These should either be absolute paths, or relative to the project root. ----@field file_patterns string[] +---@field file_patterns string[]|(fun():string[]) ---See https://codecompanion.olimorris.dev/extending/prompts.html#recipe-2-using-context-in-your-prompts --- ---Note: If a system prompt is set here, your default chat system prompt will be ignored. @@ -47,11 +47,23 @@ end ---@param opts VectorCode.CodeCompanion.PromptFactory.Opts function M.register_prompt(opts) + opts = vim.deepcopy(opts) + + if type(opts.project_root) == "function" then + opts.project_root = opts.project_root() + end + + if type(opts.file_patterns) == "function" then + opts.file_patterns = opts.file_patterns() + end + assert( - utils.is_directory(opts.project_root), + ---@diagnostic disable-next-line: param-type-mismatch + type(opts.project_root) == "string" and utils.is_directory(opts.project_root), string.format("`%s` is not a valid directory.", opts.project_root) ) assert( + ---@diagnostic disable-next-line: param-type-mismatch opts.file_patterns ~= nil and (not vim.tbl_isempty(opts.file_patterns)), "Recieved empty path specs." ) From 36cb149b46b38fb3860662125a1559946406ae9a Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Tue, 2 Sep 2025 12:22:05 +0800 Subject: [PATCH 12/20] docs(nvim): Vectorise help files notify message --- docs/neovim/README.md | 5 +++-- lua/vectorcode/integrations/codecompanion/prompts/init.lua | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/neovim/README.md b/docs/neovim/README.md index aae739c7..28818444 100644 --- a/docs/neovim/README.md +++ b/docs/neovim/README.md @@ -179,7 +179,7 @@ option explained below. ```lua ---@module "vectorcode" -opts = { +require('codecompanion').setup({ extensions = { vectorcode = { ---@type VectorCode.CodeCompanion.ExtensionOpts @@ -222,7 +222,7 @@ opts = { }, }, } -} +}) ``` The following are the common options that all tools supports: @@ -292,6 +292,7 @@ require('codecompanion').setup{ vectorcode = { ---@type VectorCode.CodeCompanion.ExtensionOpts opts = { + ---@type table prompt_library = { { ["Neovim Tutor"] = { diff --git a/lua/vectorcode/integrations/codecompanion/prompts/init.lua b/lua/vectorcode/integrations/codecompanion/prompts/init.lua index 01b87c70..94e6f0f2 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/init.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/init.lua @@ -119,7 +119,7 @@ Here's my question: if result ~= nil and not vim.tbl_isempty(result) then vim.schedule_wrap(vim.notify)( string.format( - "Vectorised %d files at `%s`.", + "Vectorised %d new files.", result.add or 0, opts.project_root ) From f5110230ae477b30c0a333219a06220f28e391b7 Mon Sep 17 00:00:00 2001 From: Davidyz <30951234+Davidyz@users.noreply.github.com> Date: Tue, 2 Sep 2025 04:22:55 +0000 Subject: [PATCH 13/20] Auto generate docs --- doc/VectorCode.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/VectorCode.txt b/doc/VectorCode.txt index 652e7388..b54f58af 100644 --- a/doc/VectorCode.txt +++ b/doc/VectorCode.txt @@ -201,7 +201,7 @@ the `include_in_toolbox` option explained below. >lua ---@module "vectorcode" - opts = { + require('codecompanion').setup({ extensions = { vectorcode = { ---@type VectorCode.CodeCompanion.ExtensionOpts @@ -244,7 +244,7 @@ the `include_in_toolbox` option explained below. }, }, } - } + }) < The following are the common options that all tools supports: @@ -313,6 +313,7 @@ the neovim runtime directory. vectorcode = { ---@type VectorCode.CodeCompanion.ExtensionOpts opts = { + ---@type table prompt_library = { { ["Neovim Tutor"] = { From 02f4bc18b621166078ac71e3601da8cd09b76c07 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Tue, 2 Sep 2025 16:49:51 +0800 Subject: [PATCH 14/20] docs(nvim): Document customisable prompt library for Neovim --- docs/neovim/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/neovim/README.md b/docs/neovim/README.md index 28818444..bbb3e0a4 100644 --- a/docs/neovim/README.md +++ b/docs/neovim/README.md @@ -1,4 +1,5 @@ # NeoVim Plugin + > [!NOTE] > This plugin depends on the CLI tool. Please go through > [the CLI documentation](../cli/README.md) and make sure the VectorCode CLI is working @@ -298,6 +299,8 @@ require('codecompanion').setup{ ["Neovim Tutor"] = { project_root = vim.env.VIMRUNTIME, file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, + -- system_prompt = ..., + -- user_prompt = ..., }, }, } @@ -307,6 +310,19 @@ require('codecompanion').setup{ } ``` +The `prompt_library` option is a mapping of prompt name (`string`) to a lua table +(type annotation available) that contains some information used to generate the +embeddings: + +- `project_root`: `string`, the path to the directory (for example, + `/usr/share/nvim/runtime/`); +- `file_patterns`: `string[]`, file name patterns that defines files to be vectorised. + You should either use absolute paths or relative paths from the project root; +- `system_prompt` and `user_prompt`: `string|fun(context:table):string|nil`: + These options allow you to customise the prompts. See + [codecompanion.nvim documentation](https://codecompanion.olimorris.dev/extending/prompts#recipe-2-using-context-in-your-prompts) + if you want to use a function here that build the prompts from the context. + The first time will take some extra time for computing the embeddings, but the subsequent runs should be a lot faster. From 88717cc38c87220e3157c3a74d236978ffac0576 Mon Sep 17 00:00:00 2001 From: Davidyz <30951234+Davidyz@users.noreply.github.com> Date: Tue, 2 Sep 2025 08:50:33 +0000 Subject: [PATCH 15/20] Auto generate docs --- doc/VectorCode.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/VectorCode.txt b/doc/VectorCode.txt index b54f58af..5385080e 100644 --- a/doc/VectorCode.txt +++ b/doc/VectorCode.txt @@ -319,6 +319,8 @@ the neovim runtime directory. ["Neovim Tutor"] = { project_root = vim.env.VIMRUNTIME, file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, + -- system_prompt = ..., + -- user_prompt = ..., }, }, } @@ -328,6 +330,19 @@ the neovim runtime directory. } < +The `prompt_library` option is a mapping of prompt name (`string`) to a lua +table (type annotation available) that contains some information used to +generate the embeddings: + +- `project_root``string`, the path to the directory (for example, + `/usr/share/nvim/runtime/`); +- `file_patterns``string[]`, file name patterns that defines files to be vectorised. + You should either use absolute paths or relative paths from the project root; +- `system_prompt` and `user_prompt``string|fun(context:table):string|nil` + Theseoptions allow you to customise the prompts. See + codecompanion.nvim documentation + if you want to use a function here that build the prompts from the context. + The first time will take some extra time for computing the embeddings, but the subsequent runs should be a lot faster. From 5b81998d8b5a91aa640b99a08c4636b75c77fe5d Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Tue, 2 Sep 2025 17:17:41 +0800 Subject: [PATCH 16/20] refactor(nvim): move prompt library presets to dedicated module --- docs/neovim/README.md | 13 ++++++++++--- lua/codecompanion/_extensions/vectorcode/init.lua | 8 +------- .../integrations/codecompanion/prompts/presets.lua | 9 +++++++++ 3 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 lua/vectorcode/integrations/codecompanion/prompts/presets.lua diff --git a/docs/neovim/README.md b/docs/neovim/README.md index bbb3e0a4..472457ec 100644 --- a/docs/neovim/README.md +++ b/docs/neovim/README.md @@ -284,9 +284,12 @@ The `query` tool contains the following extra config options: #### Prompt Library On VectorCode 0.7.16+ and CodeCompanion.nvim 17.20.0+, VectorCode also provides a -customisable prompt library that helps you RAG local directories. The following is the -a preset that vectorises the lua source code and help files in the neovim runtime -directory. +customisable prompt library that helps you RAG local directories. The presets +provided by VectorCode are available +[here](../../lua/vectorcode/integrations/codecompanion/prompts/presets.lua), which +you can refer to if you wish to build local RAG APPs with CodeCompanion.nvim and +VectorCode. + ```lua require('codecompanion').setup{ extensions = { @@ -297,6 +300,10 @@ require('codecompanion').setup{ prompt_library = { { ["Neovim Tutor"] = { + -- this is for demonstration only. + -- "Neovim Tutor" is shipped with this plugin + -- and you don't need to add it in the config + -- unless you're not happy with the defaults. project_root = vim.env.VIMRUNTIME, file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, -- system_prompt = ..., diff --git a/lua/codecompanion/_extensions/vectorcode/init.lua b/lua/codecompanion/_extensions/vectorcode/init.lua index 4f8a55f8..95406e75 100644 --- a/lua/codecompanion/_extensions/vectorcode/init.lua +++ b/lua/codecompanion/_extensions/vectorcode/init.lua @@ -18,7 +18,6 @@ local vc_config = require("vectorcode.config") local logger = vc_config.logger -local vim_runtime = vim.fs.normalize(vim.env.VIMRUNTIME) ---@type VectorCode.CodeCompanion.ExtensionOpts|{} local default_extension_opts = { @@ -34,12 +33,7 @@ local default_extension_opts = { }, tool_group = { enabled = true, collapse = true, extras = {} }, - prompt_library = { - ["Neovim Tutor"] = { - project_root = vim_runtime, - file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, - }, - }, + prompt_library = require("vectorcode.integrations.codecompanion.prompts.presets"), } ---@type sub_cmd[] diff --git a/lua/vectorcode/integrations/codecompanion/prompts/presets.lua b/lua/vectorcode/integrations/codecompanion/prompts/presets.lua new file mode 100644 index 00000000..1338cb7d --- /dev/null +++ b/lua/vectorcode/integrations/codecompanion/prompts/presets.lua @@ -0,0 +1,9 @@ +---@type table +local M = {} + +M["Neovim Tutor"] = { + project_root = vim.fs.normalize(vim.env.VIMRUNTIME), + file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, +} + +return M From 43a56e2f8e07dc1b065e7d612d8470859b3155c2 Mon Sep 17 00:00:00 2001 From: Davidyz <30951234+Davidyz@users.noreply.github.com> Date: Tue, 2 Sep 2025 09:18:34 +0000 Subject: [PATCH 17/20] Auto generate docs --- doc/VectorCode.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/VectorCode.txt b/doc/VectorCode.txt index 5385080e..1ec15d36 100644 --- a/doc/VectorCode.txt +++ b/doc/VectorCode.txt @@ -303,9 +303,11 @@ avoid omitting stuff related to query. PROMPT LIBRARY On VectorCode 0.7.16+ and CodeCompanion.nvim 17.20.0+, VectorCode also provides -a customisable prompt library that helps you RAG local directories. The -following is the a preset that vectorises the lua source code and help files in -the neovim runtime directory. +a customisable prompt library that helps you RAG local directories. The presets +provided by VectorCode are available here +<../../lua/vectorcode/integrations/codecompanion/prompts/presets.lua>, which +you can refer to if you wish to build local RAG APPs with CodeCompanion.nvim +and VectorCode. >lua require('codecompanion').setup{ @@ -317,6 +319,10 @@ the neovim runtime directory. prompt_library = { { ["Neovim Tutor"] = { + -- this is for demonstration only. + -- "Neovim Tutor" is shipped with this plugin + -- and you don't need to add it in the config + -- unless you're not happy with the defaults. project_root = vim.env.VIMRUNTIME, file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, -- system_prompt = ..., From db9f17e41a7177b61a141b6d98cafdd83b954eb5 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Tue, 2 Sep 2025 21:54:18 +0800 Subject: [PATCH 18/20] feat(nvim): Improve project root and file pattern handling --- .../_extensions/vectorcode/init.lua | 22 +++++++++-- .../codecompanion/prompts/init.lua | 37 +++++++++++-------- lua/vectorcode/utils.lua | 8 +++- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/lua/codecompanion/_extensions/vectorcode/init.lua b/lua/codecompanion/_extensions/vectorcode/init.lua index 95406e75..b5803ec2 100644 --- a/lua/codecompanion/_extensions/vectorcode/init.lua +++ b/lua/codecompanion/_extensions/vectorcode/init.lua @@ -18,6 +18,7 @@ local vc_config = require("vectorcode.config") local logger = vc_config.logger +local utils = require("vectorcode.utils") ---@type VectorCode.CodeCompanion.ExtensionOpts|{} local default_extension_opts = { @@ -129,9 +130,24 @@ local M = { vc_config.notify_opts ) end - prompt_opts.name = name - cc_config.prompt_library[name] = - cc_chat_integration.prompts.register_prompt(prompt_opts) + if type(prompt_opts.project_root) == "function" then + prompt_opts.project_root = prompt_opts.project_root() + end + if not utils.is_directory(prompt_opts.project_root) then + vim.notify( + string.format( + "`%s` is not a valid directory for CodeCompanion prompt library.\nSkipping `%s`.", + prompt_opts.project_root, + name + ), + vim.log.levels.WARN, + vc_config.notify_opts + ) + else + prompt_opts.name = name + cc_config.prompt_library[name] = + cc_chat_integration.prompts.register_prompt(prompt_opts) + end end end), } diff --git a/lua/vectorcode/integrations/codecompanion/prompts/init.lua b/lua/vectorcode/integrations/codecompanion/prompts/init.lua index 94e6f0f2..b06b53f3 100644 --- a/lua/vectorcode/integrations/codecompanion/prompts/init.lua +++ b/lua/vectorcode/integrations/codecompanion/prompts/init.lua @@ -11,6 +11,8 @@ function M.vectorise_files(path, project_root, callback) if type(path) == "string" then path = { path } end + assert(not vim.tbl_isempty(path), "`path` cannot be empty") + local jobrunner = require("vectorcode.integrations.codecompanion.common").initialise_runner( vc_config.get_user_config().async_backend == "lsp" @@ -49,10 +51,6 @@ end function M.register_prompt(opts) opts = vim.deepcopy(opts) - if type(opts.project_root) == "function" then - opts.project_root = opts.project_root() - end - if type(opts.file_patterns) == "function" then opts.file_patterns = opts.file_patterns() end @@ -103,17 +101,24 @@ Here's my question: opts = { ignore_system_prompt = opts.system_prompt ~= nil, pre_hook = function() - vim.notify( - string.format("Add files under `%s` to the database.", opts.project_root) - ) + if vc_config.get_user_config().notify then + vim.notify( + string.format("Adding files under `%s` to the database.", opts.project_root), + vim.log.levels.INFO, + vc_config.notify_opts + ) + end M.vectorise_files( - vim.iter(opts.file_patterns):map(function(p) - if vim.fn.isabsolutepath(p) == 1 then - return p - else - return vim.fs.joinpath(opts.project_root, p) - end - end), + vim + .iter(opts.file_patterns) + :map(function(p) + if vim.fn.isabsolutepath(p) == 1 then + return p + else + return vim.fs.joinpath(opts.project_root, p) + end + end) + :totable(), opts.project_root, function(result, err, _, _) if result ~= nil and not vim.tbl_isempty(result) then @@ -122,7 +127,9 @@ Here's my question: "Vectorised %d new files.", result.add or 0, opts.project_root - ) + ), + vim.log.levels.INFO, + vc_config.notify_opts ) elseif err ~= nil then err = cc_common.flatten_table_to_string(err) diff --git a/lua/vectorcode/utils.lua b/lua/vectorcode/utils.lua index f9d65bc9..605d429b 100644 --- a/lua/vectorcode/utils.lua +++ b/lua/vectorcode/utils.lua @@ -156,7 +156,9 @@ end ---@param f string ---@return boolean function M.is_file(f) - assert(type(f) == "string", "`f` is not a string") + if type(f) ~= "string" then + return false + end local stats = vim.uv.fs_stat(f) return stats and (stats.type == "file") or false end @@ -164,7 +166,9 @@ end ---@param f string ---@return boolean function M.is_directory(f) - assert(type(f) == "string", "`f` is not a string") + if type(f) ~= "string" then + return false + end local stats = vim.uv.fs_stat(f) return stats and (stats.type == "directory") or false end From be3b59279e5fb5f34580f8285f9c090855758c63 Mon Sep 17 00:00:00 2001 From: Zhe Yu Date: Wed, 3 Sep 2025 16:38:32 +0800 Subject: [PATCH 19/20] update documentations. --- docs/neovim/README.md | 62 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/docs/neovim/README.md b/docs/neovim/README.md index 472457ec..685480cc 100644 --- a/docs/neovim/README.md +++ b/docs/neovim/README.md @@ -180,7 +180,7 @@ option explained below. ```lua ---@module "vectorcode" -require('codecompanion').setup({ +require("codecompanion").setup({ extensions = { vectorcode = { ---@type VectorCode.CodeCompanion.ExtensionOpts @@ -215,14 +215,14 @@ require('codecompanion').setup({ enabled = false, adapter = nil, query_augmented = true, - } + }, }, files_ls = {}, - files_rm = {} - } + files_rm = {}, + }, }, }, - } + }, }) ``` @@ -291,7 +291,7 @@ you can refer to if you wish to build local RAG APPs with CodeCompanion.nvim and VectorCode. ```lua -require('codecompanion').setup{ +require("codecompanion").setup({ extensions = { vectorcode = { ---@type VectorCode.CodeCompanion.ExtensionOpts @@ -301,8 +301,8 @@ require('codecompanion').setup{ { ["Neovim Tutor"] = { -- this is for demonstration only. - -- "Neovim Tutor" is shipped with this plugin - -- and you don't need to add it in the config + -- "Neovim Tutor" is shipped with this plugin already, + -- and you don't need to add it in the config -- unless you're not happy with the defaults. project_root = vim.env.VIMRUNTIME, file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, @@ -310,11 +310,11 @@ require('codecompanion').setup{ -- user_prompt = ..., }, }, - } - } - } - } -} + }, + }, + }, + }, +}) ``` The `prompt_library` option is a mapping of prompt name (`string`) to a lua table @@ -346,13 +346,14 @@ contextual information about your codebase to enhance Copilot's responses. Add t to your CopilotChat configuration: ```lua -local vectorcode_ctx = require('vectorcode.integrations.copilotchat').make_context_provider({ - prompt_header = "Here are relevant files from the repository:", -- Customize header text - prompt_footer = "\nConsider this context when answering:", -- Customize footer text - skip_empty = true, -- Skip adding context when no files are retrieved -}) - -require('CopilotChat').setup({ +local vectorcode_ctx = + require("vectorcode.integrations.copilotchat").make_context_provider({ + prompt_header = "Here are relevant files from the repository:", -- Customize header text + prompt_footer = "\nConsider this context when answering:", -- Customize footer text + skip_empty = true, -- Skip adding context when no files are retrieved + }) + +require("CopilotChat").setup({ -- Your other CopilotChat options... contexts = { @@ -364,10 +365,10 @@ require('CopilotChat').setup({ prompts = { Explain = { prompt = "Explain the following code in detail:\n$input", - context = {"selection", "vectorcode"}, -- Add vectorcode to the context + context = { "selection", "vectorcode" }, -- Add vectorcode to the context }, -- Other prompts... - } + }, }) ``` @@ -395,7 +396,7 @@ The integration includes caching to avoid sending duplicate context to the LLM, You can configure VectorCode to be part of your sticky prompts, ensuring every conversation includes relevant codebase context automatically: ```lua -require('CopilotChat').setup({ +require("CopilotChat").setup({ -- Your other CopilotChat options... sticky = { @@ -416,8 +417,8 @@ cached retrieval results. ```lua tabline = { lualine_y = { - require("vectorcode.integrations").lualine(opts) - } + require("vectorcode.integrations").lualine(opts), + }, } ``` `opts` is a table with the following configuration option: @@ -442,7 +443,7 @@ tabline = { end end, }, - } + }, } ``` This will further delay the loading of VectorCode to the moment you (or one of @@ -589,12 +590,9 @@ vim.api.nvim_create_autocmd("LspAttach", { callback = function() local bufnr = vim.api.nvim_get_current_buf() cacher.async_check("config", function() - cacher.register_buffer( - bufnr, - { - n_query = 10, - } - ) + cacher.register_buffer(bufnr, { + n_query = 10, + }) end, nil) end, desc = "Register buffer for VectorCode", From 01e5244afac1c105748de44aec76c56e4793e201 Mon Sep 17 00:00:00 2001 From: Davidyz <30951234+Davidyz@users.noreply.github.com> Date: Wed, 3 Sep 2025 08:39:15 +0000 Subject: [PATCH 20/20] Auto generate docs --- doc/VectorCode.txt | 60 ++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/doc/VectorCode.txt b/doc/VectorCode.txt index 1ec15d36..0824b0b0 100644 --- a/doc/VectorCode.txt +++ b/doc/VectorCode.txt @@ -201,7 +201,7 @@ the `include_in_toolbox` option explained below. >lua ---@module "vectorcode" - require('codecompanion').setup({ + require("codecompanion").setup({ extensions = { vectorcode = { ---@type VectorCode.CodeCompanion.ExtensionOpts @@ -236,14 +236,14 @@ the `include_in_toolbox` option explained below. enabled = false, adapter = nil, query_augmented = true, - } + }, }, files_ls = {}, - files_rm = {} - } + files_rm = {}, + }, }, }, - } + }, }) < @@ -310,7 +310,7 @@ you can refer to if you wish to build local RAG APPs with CodeCompanion.nvim and VectorCode. >lua - require('codecompanion').setup{ + require("codecompanion").setup({ extensions = { vectorcode = { ---@type VectorCode.CodeCompanion.ExtensionOpts @@ -320,8 +320,8 @@ and VectorCode. { ["Neovim Tutor"] = { -- this is for demonstration only. - -- "Neovim Tutor" is shipped with this plugin - -- and you don't need to add it in the config + -- "Neovim Tutor" is shipped with this plugin already, + -- and you don't need to add it in the config -- unless you're not happy with the defaults. project_root = vim.env.VIMRUNTIME, file_patterns = { "lua/**/*.lua", "doc/**/*.txt" }, @@ -329,11 +329,11 @@ and VectorCode. -- user_prompt = ..., }, }, - } - } - } - } - } + }, + }, + }, + }, + }) < The `prompt_library` option is a mapping of prompt name (`string`) to a lua @@ -368,13 +368,14 @@ contextual information about your codebase to enhance Copilot’s responses. Add this to your CopilotChat configuration: >lua - local vectorcode_ctx = require('vectorcode.integrations.copilotchat').make_context_provider({ - prompt_header = "Here are relevant files from the repository:", -- Customize header text - prompt_footer = "\nConsider this context when answering:", -- Customize footer text - skip_empty = true, -- Skip adding context when no files are retrieved - }) + local vectorcode_ctx = + require("vectorcode.integrations.copilotchat").make_context_provider({ + prompt_header = "Here are relevant files from the repository:", -- Customize header text + prompt_footer = "\nConsider this context when answering:", -- Customize footer text + skip_empty = true, -- Skip adding context when no files are retrieved + }) - require('CopilotChat').setup({ + require("CopilotChat").setup({ -- Your other CopilotChat options... contexts = { @@ -386,10 +387,10 @@ Add this to your CopilotChat configuration: prompts = { Explain = { prompt = "Explain the following code in detail:\n$input", - context = {"selection", "vectorcode"}, -- Add vectorcode to the context + context = { "selection", "vectorcode" }, -- Add vectorcode to the context }, -- Other prompts... - } + }, }) < @@ -424,7 +425,7 @@ You can configure VectorCode to be part of your sticky prompts, ensuring every conversation includes relevant codebase context automatically: >lua - require('CopilotChat').setup({ + require("CopilotChat").setup({ -- Your other CopilotChat options... sticky = { @@ -450,8 +451,8 @@ cached retrieval results. >lua tabline = { lualine_y = { - require("vectorcode.integrations").lualine(opts) - } + require("vectorcode.integrations").lualine(opts), + }, } < @@ -477,7 +478,7 @@ when neovim starts). If this bothers you, you can use the following snippet: end end, }, - } + }, } < @@ -633,12 +634,9 @@ in an autocmd: callback = function() local bufnr = vim.api.nvim_get_current_buf() cacher.async_check("config", function() - cacher.register_buffer( - bufnr, - { - n_query = 10, - } - ) + cacher.register_buffer(bufnr, { + n_query = 10, + }) end, nil) end, desc = "Register buffer for VectorCode",