return { 'olimorris/codecompanion.nvim', config = function() require('codecompanion').setup { adapters = { http = { default = function() if (require('helpers').has_copilot()) then return require('codecompanion.adapters').extend('copilot', { schema = { model = { default = vim.env.DEFAULT_AI_MODEL, }, max_tokens = { default = 1000000, }, } }) end return require('codecompanion.adapters').extend('openai_compatible', { env = { url = vim.env.DEFAULT_OPENAI_API_BASE, api_key = vim.env.DEFAULT_OPENAI_API_KEY, chat_url = '/v1/chat/completions', models_endpoint = '/v1/models', }, schema = { model = { default = vim.env.DEFAULT_AI_MODEL, }, max_tokens = { default = 1000000, }, }, }) end, }, }, display = { chat = { show_settings = true, start_in_insert_mode = false, }, }, strategies = { chat = { adapter = 'default', slash_commands = { -- codebase = require('vectorcode.integrations').codecompanion.chat.make_slash_command(), }, tools = { -- vectorcode = { -- description = 'Run VectorCode to retrieve the project context.', -- callback = require('vectorcode.integrations').codecompanion.chat.make_tool(), -- }, ['cmd_runner'] = { opts = { requires_approval = false, }, }, }, roles = { ---@type string|fun(adapter: CodeCompanion.Adapter): string llm = function(adapter) return 'CodeCompanion (' .. adapter.formatted_name .. ': ' .. adapter.parameters.model .. ')' end, }, keymaps = { send = { modes = { n = '', i = '' }, }, close = { modes = { n = '', i = '' }, }, }, }, inline = { adapter = { name = 'default', model = vim.env.FAST_MODEL, }, }, cmd = { adapter = { name = 'default', model = vim.env.FAST_MODEL, }, }, }, extensions = { mcphub = { callback = 'mcphub.extensions.codecompanion', opts = { show_result_in_chat = true, make_vars = true, make_slash_commands = true, }, }, }, system_prompt = function(opts) local language = opts.language or 'English' return string.format( [[You are an AI programming assistant named "CodeCompanion". You are currently plugged into the Neovim text editor on a user's machine. Your core tasks include: - Answering general programming questions. - Explaining how the code in a Neovim buffer works. - Reviewing the selected code from a Neovim buffer. - Generating unit tests for the selected code. - Proposing fixes for problems in the selected code. - Scaffolding code for a new workspace. - Finding relevant code to the user's query. - Proposing fixes for test failures. - Answering questions about Neovim. - Running tools. You must: - Follow the user's requirements carefully and to the letter. - Keep your answers short and impersonal, especially if the user's context is outside your core tasks. - Minimize additional prose unless clarification is needed. - Use Markdown formatting in your answers. - Include the programming language name at the start of each Markdown code block. - Avoid including line numbers in code blocks. - Avoid wrapping the whole response in triple backticks. - Only return code that's directly relevant to the task at hand. You may omit code that isn’t necessary for the solution. - Avoid using H1, H2 or H3 headers in your responses as these are reserved for the user. - Use actual line breaks in your responses; only use "\n" when you want a literal backslash followed by 'n'. - All non-code text responses must be written in the %s language indicated. - Multiple, different tools can be called as part of the same response. - Use full path names when using tools to read and modify files. When given a task: 1. Think step-by-step and, unless the user requests otherwise or the task is very simple, describe your plan in detailed pseudocode. 2. Output the final code in a single code block, ensuring that only relevant code is included. 3. End your response with a short suggestion for the next user turn that directly supports continuing the conversation. 4. Provide exactly one complete reply per conversation turn. 5. If necessary, execute multiple tools in a single turn.]], language ) end, prompt_library = { ['Code Expert'] = { strategy = 'chat', description = 'Get some special advice from an LLM.', opts = { mapping = 'ce', modes = { 'v' }, short_name = 'expert', auto_submit = true, stop_context_insertion = true, user_prompt = true, }, prompts = { { role = 'system', content = function(context) return 'I want you to act as a senior' .. context.filetype .. 'developer I will ask you specific questions and I want you to return concise explanations and codeblock examples.' end, }, { role = 'user', content = function(context) local text = require('codecompanion.helpers.actions').get_code(context.start_line, context.end_line) return 'I have the following code:\n\n```' .. context.filetype .. '\n' .. text .. '\n```\n\n' end, opts = { contains_code = true, }, }, }, }, ['Games Master'] = { strategy = 'chat', description = 'A personal Games Master Assistant.', opts = { user_prompt = false, }, prompts = { { role = 'system', content = [[ You are a personal Games Master Assistant. You are currently plugged in to the Neovim text editor on a user's machine. Your core tasks include: - Crafting engaging role playing sessions - Building immersive and authentic worlds - Creating compelling characters for the players to interact with and drive the story - Reviewing session summaries and building on them - Providing advice on how to bring sessions to life - Create exciting encounters to challenge the players - Encounters can be combat, social, or exploration based - There should always be a story reason for the encounter - Combine player character backgrounds and motivations into the world - Build tension and drama into the game - Ensure each session provides opportunities for the players to engage with and drive the story You must: - Follow the user's requirements carefully and to the letter. - Keep answers focused on the task at hand. - Provide original ideas and suggestions. - Use Markdown formatting in your answers. - Use actual line breaks instead of '\n' in your response to begin new lines. - Use '\n' only when you want a literal backslash followed by a character 'n'. - All responses must be written in English. - Multiple, different tools can be called as part of the same response. - Help sessions be fast paced and fun. - Ensure there are multiple soltuions to each problem. - Avoid railroading the players. When given a task: 1. Consider the existing world and characters. Use tools to gather information that may be relevant. 2. Provide exactly one complete reply per conversation turn. 3. If necessary, execute multiple tools in a single turn. ]], }, { role = 'user', content = '', }, }, }, ['PHPStan Fixer'] = { strategy = 'workflow', description = 'Use a workflow to fix PHPStan errors until there are none left.', opts = { short_name = 'phpstan', }, prompts = { { { name = 'Run PHPStan', role = 'user', opts = { auto_submit = false }, content = function() -- Enable turbo mode!!! vim.g.codecompanion_auto_tool_mode = true return [[PHPStan is a static analysis tool for PHP. It is currently reporting errors in the code. Your task is to fix these errors and run PHPStan again until there are no errors left. First of all use the @cmd_runner tool to run the `composer type-check` command. This will run PHPStan and output type errors in the code.]] end, }, }, { { name = 'Fetch files', role = 'user', opts = { auto_submit = false }, content = function() -- Enable turbo mode!!! vim.g.codecompanion_auto_tool_mode = true return 'PHPStan has reported errors. Look at the output and see where the files are reported. Use the @mcp tool to read all the offending files so you can implement the fixes.' end, }, }, { { name = 'Fix errors and run', role = 'user', opts = { auto_submit = false }, content = function() -- Enable turbo mode!!! vim.g.codecompanion_auto_tool_mode = true return [[### Instructions Now you have the errors and the appropriate context you can fix the errors. ### Steps to Follow You are required to analyse the PHPStan errors and write code to fix them. Reason through the errors and write out the possible causes and fixes for each. 1. Then use the @mcp tool to update the files with the fixes. When editing files use the full path name including the project name /Users/chris/Code/Sites/hylark/ 2. After editing the files use the @cmd_runner tool again to run the `composer type-check` command to see if any errors remain. We'll repeat this cycle until there are no errors. Ensure no deviations from these steps. ### Tips for fixing PHPStan errors - Do not ignore the errors - Use Webmozart Assert library to narrow types - Most errors are due to missing docblocks or calling methods/parameters on types that PHPStan cannot infer - Use fully qualified namespaces for classes in doc blocks - The code is working so fixes should not change the behaviour of the code - There are some custom types in this project that help define types - closure creates T|Closure(): T, you can add a second argument to specify the depth so closure creates T|Closure(): T|Closure(): (Closure(): T). This is useful because GraphQL queries can return closures. - promise creates T|SyncPromise, as with closure, you can add a second argument for depth. You can also add a third argument if T is an array to allow adding promises to each value, so promise will create array{a: int}|SyncPromise|array{a: SyncPromise}|SyncPromise}>. This is useful because GraphQL queries can return promises and arrays of promises. - GraphQL schema types (these should only be used in the Query classes) - GArgs will look at the GraphQL schema and create an array type for the arguments of a field on one of the root types (Query or Mutation) - GArgs will create a type for the arguments of a field on a type - GType will create an type for a specific GraphQL type, input, interface, or enum - GVal will create a type for the return value of a field on one of the root types (Query or Mutation) - GVal will create a type for the return value of a field on a type - pick will create an array from T with only the keys in TKeys (e.g. pick will create array{a: int, b: int}) - except works like pick only it excludes the keys in TKeys (e.g. except will create array{c: int}) - union creates a union array of T and U (e.g. union will create array{a: int, b: int})]] end, }, }, { { name = 'Repeat On Failure', role = 'user', opts = { auto_submit = false }, -- Scope this prompt to the cmd_runner tool condition = function() return _G.codecompanion_current_tool == 'cmd_runner' end, -- Repeat until the tests pass, as indicated by the testing flag -- which the cmd_runner tool sets on the chat buffer repeat_until = function(chat) -- Check if the last message in the chat buffer contains "[ERROR] found" local messages = chat.messages local last_message = messages[#messages] if last_message and last_message.role == 'assistant' then local content = last_message.content return not content:find '[ERROR] found' end return true end, content = 'PHPStan is still reporting errors. Edit the code to fix the errors and run PHPStan again.', }, }, }, }, }, } end, dependencies = { 'nvim-lua/plenary.nvim', 'nvim-treesitter/nvim-treesitter', -- 'Davidyz/VectorCode', 'ravitemer/mcphub.nvim', }, }