Files
nvim/lua/plugins/codecompanion.lua

370 lines
17 KiB
Lua
Raw Normal View History

2025-04-14 15:20:54 +01:00
require('helpers').edit_cf('pa', '/lua/plugins/codecompanion.lua')
2025-04-14 11:38:42 +01:00
2025-04-29 17:22:28 +01:00
vim.cmd [[cab cc CodeCompanion]]
2025-04-13 22:53:08 +01:00
return {
'olimorris/codecompanion.nvim',
2025-04-15 20:48:35 +01:00
opts = function(_, opts)
opts.adapters = {
2025-04-17 08:53:20 +01:00
gpt = function()
2025-04-14 15:20:54 +01:00
return require('codecompanion.adapters').extend('copilot', {
schema = {
model = {
2025-05-05 07:25:00 +01:00
default = 'gpt-4.1',
2025-04-14 15:20:54 +01:00
},
2025-04-17 08:53:20 +01:00
max_tokens = {
default = 1000000,
},
},
})
end,
flash = function()
return require('codecompanion.adapters').extend('copilot', {
schema = {
model = {
2025-05-12 09:04:40 +01:00
default = 'gemini-2.0-flash-001',
2025-04-17 08:53:20 +01:00
},
max_tokens = {
default = 1000000,
},
},
})
end,
gemini = function()
return require('codecompanion.adapters').extend('copilot', {
schema = {
model = {
default = 'gemini-2.5-pro',
},
max_tokens = {
default = 1000000,
},
},
})
end,
sonnet = function()
return require('codecompanion.adapters').extend('copilot', {
schema = {
model = {
default = 'claude-3.7-sonnet',
},
max_tokens = {
default = 1000000,
},
2025-04-14 15:20:54 +01:00
},
})
end,
2025-04-15 20:48:35 +01:00
}
opts.display = {
2025-04-14 15:20:54 +01:00
chat = {
show_settings = true,
2025-05-08 07:59:46 +01:00
start_in_insert_mode = false,
2025-04-14 15:20:54 +01:00
},
2025-04-15 20:48:35 +01:00
}
opts.strategies = {
chat = {
2025-04-29 17:22:28 +01:00
adapter = 'gpt',
2025-04-15 20:48:35 +01:00
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(),
},
2025-05-05 07:25:00 +01:00
['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 = '<C-s>', i = '<C-s>' },
},
close = {
modes = { n = '<C-c>', i = '<C-c>' },
},
2025-04-15 20:48:35 +01:00
},
},
2025-04-17 08:53:20 +01:00
inline = {
adapter = 'flash',
},
2025-04-15 20:48:35 +01:00
}
2025-05-05 07:25:00 +01:00
opts.extensions = {
mcphub = {
callback = 'mcphub.extensions.codecompanion',
opts = {
show_result_in_chat = true,
make_vars = true,
make_slash_commands = true,
},
},
}
opts.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 isnt 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.
2025-05-12 09:04:40 +01:00
- Use full path names when using tools to read and modify files.
2025-05-05 07:25:00 +01:00
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
opts.prompt_library = {
['Code Expert'] = {
strategy = 'chat',
description = 'Get some special adcvice from an LLM.',
opts = {
mapping = '<Leader>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 = {
mapping = '<Leader>cd',
modes = { 'n' },
short_name = 'gm',
auto_submit = false,
stop_context_insertion = false,
user_prompt = false,
is_default = true,
2025-05-05 07:28:57 +01:00
adapter = {
2025-05-05 07:25:00 +01:00
temperature = 1,
},
},
prompts = {
{
role = 'system',
content = function(context)
return [[
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.
]]
end,
},
{
role = 'user',
content = function(context)
return ''
end,
opts = {
contains_code = false,
},
},
},
},
2025-05-12 09:04:40 +01:00
['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<T> creates T|Closure(): T, you can add a second argument to specify the depth so closure<T, 2> creates T|Closure(): T|Closure(): (Closure(): T). This is useful because GraphQL queries can return closures.
- promise<T> creates T|SyncPromise<T>, 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<array{a: int}, 1, 1> will create array{a: int}|SyncPromise<array{a: int}>|array{a: SyncPromise<int>}|SyncPromise<array{a: SyncPromise<int>}>. 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<T> 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<TType, TField> will create a type for the arguments of a field on a type
- GType<T> will create an type for a specific GraphQL type, input, interface, or enum
- GVal<T> will create a type for the return value of a field on one of the root types (Query or Mutation)
- GVal<TType, TField> will create a type for the return value of a field on a type
- pick<T, TKeys> will create an array from T with only the keys in TKeys (e.g. pick<array{a: int, b: int, c: int}, 'a'|'b'> will create array{a: int, b: int})
- except<T, TKeys> works like pick only it excludes the keys in TKeys (e.g. except<array{a: int, b: int, c: int}, 'a'|'b'> will create array{c: int})
- union<T, U> creates a union array of T and U (e.g. union<array{a: int}, array{b: int}> 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.',
},
},
},
},
2025-05-05 07:25:00 +01:00
}
2025-04-15 20:48:35 +01:00
end,
2025-04-13 22:53:08 +01:00
dependencies = {
'nvim-lua/plenary.nvim',
'nvim-treesitter/nvim-treesitter',
2025-04-15 20:48:35 +01:00
'Davidyz/VectorCode',
2025-05-05 07:25:00 +01:00
'ravitemer/mcphub.nvim',
2025-04-13 22:53:08 +01:00
},
}