diff --git a/README.md b/README.md index 92c2f66..f7282da 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ If you like this project, please consider supporting me on Patreon, as it helps - **AI-Powered Code Assistance**: Interact with AI to ask questions about your current code file and receive intelligent suggestions for improvement or modification. - **One-Click Application**: Quickly apply the AI's suggested changes to your source code with a single command, streamlining the editing process and saving time. +- **Project-Specific Instruction Files**: Customize AI behavior by adding a markdown file (`avante.md` by default) in the project root. This file is automatically referenced during workspace changes. You can also configure a custom file name for tailored project instructions. ## Installation @@ -84,6 +85,8 @@ For building binary if you wish to build from source, then `cargo` is required. ---@type avante.Config opts = { -- add any opts here + -- this file can containe specific instructions for your project + instructions_file = "avante.md" -- for example provider = "claude", providers = { @@ -656,6 +659,7 @@ For other users just add a custom provider Avante.nvim provides several completion sources that can be integrated with blink.cmp: #### Mentions (`@` trigger) + Mentions allow you to quickly reference specific features or add files to the chat context: - `@codebase` - Enable project context and repository mapping @@ -665,6 +669,7 @@ Mentions allow you to quickly reference specific features or add files to the ch - `@buffers` - Add open buffers to chat context #### Slash Commands (`/` trigger) + Built-in slash commands for common operations: - `/help` - Show help message with available commands @@ -676,6 +681,7 @@ Built-in slash commands for common operations: - `/commit` - Generate commit message for changes #### Shortcuts (`#` trigger) + Shortcuts provide quick access to predefined prompt templates. You can customize these in your config: ```lua @@ -983,6 +989,7 @@ Fast Apply is a feature that enables instant code edits with high accuracy by le Fast Apply addresses the common pain point of slow code application in AI-assisted development. Instead of waiting for a full language model to process and apply changes, Fast Apply uses a specialized "apply model" that can quickly and accurately merge code edits with 96-98% accuracy at speeds of 2500-4500+ tokens per second. Key benefits: + - **Instant application**: Code changes are applied immediately without noticeable delays - **High accuracy**: Specialized models achieve 96-98% accuracy for code edits - **Seamless workflow**: Maintains the natural flow of development without interruptions @@ -993,6 +1000,7 @@ Key benefits: To enable Fast Apply, you need to: 1. **Enable Fast Apply in your configuration**: + ```lua behaviour = { enable_fastapply = true, -- Enable Fast Apply feature @@ -1004,6 +1012,7 @@ To enable Fast Apply, you need to: Go to [morphllm.com](https://morphllm.com/api-keys) and create an account and get the API key. 3. **Set your Morph API key**: + ```bash export MORPH_API_KEY="your-api-key" ``` @@ -1021,11 +1030,11 @@ To enable Fast Apply, you need to: Morph provides different models optimized for different use cases: -| Model | Speed | Accuracy | Context Limit | -|-------|-------|----------|---------------| -| `morph-v3-fast` | 4500+ tok/sec | 96% | 16k tokens | -| `morph-v3-large` | 2500+ tok/sec | 98% | 16k tokens | -| `auto` | 2500-4500 tok/sec | 98% | 16k tokens | +| Model | Speed | Accuracy | Context Limit | +| ---------------- | ----------------- | -------- | ------------- | +| `morph-v3-fast` | 4500+ tok/sec | 96% | 16k tokens | +| `morph-v3-large` | 2500+ tok/sec | 98% | 16k tokens | +| `auto` | 2500-4500 tok/sec | 98% | 16k tokens | ### How It Works @@ -1037,6 +1046,7 @@ When Fast Apply is enabled and a Morph provider is configured, avante.nvim will: 4. Apply the changes directly to your files with high accuracy The process uses a specialized prompt format that includes: + - ``: Clear description of what changes to make - ``: The original code content - ``: The specific changes using truncation markers (`// ... existing code ...`) @@ -1387,6 +1397,7 @@ Avante.nvim can be extended to work with other plugins by using its extension mo ### How to disable agentic mode? Avante.nvim provides two interaction modes: + - **`agentic`** (default): Uses AI tools to automatically generate and apply code changes - **`legacy`**: Uses the traditional planning method without automatic tool execution @@ -1400,10 +1411,12 @@ To disable agentic mode and switch to legacy mode, update your configuration: ``` **What's the difference?** + - **Agentic mode**: AI can automatically execute tools like file operations, bash commands, web searches, etc. to complete complex tasks - **Legacy mode**: AI provides suggestions and plans but requires manual approval for all actions **When should you use legacy mode?** + - If you prefer more control over what actions the AI takes - If you're concerned about security with automatic tool execution - If you want to manually review each step before applying changes diff --git a/lua/avante/config.lua b/lua/avante/config.lua index 199b924..c6836ca 100644 --- a/lua/avante/config.lua +++ b/lua/avante/config.lua @@ -17,6 +17,9 @@ local Utils = require("avante.utils") ---@class avante.CoreConfig: avante.Config local M = {} + +--- Default configuration for project-specific instruction file +M.instructions_file = "avante.md" ---@class avante.Config M._defaults = { debug = false, diff --git a/lua/avante/llm.lua b/lua/avante/llm.lua index ba6dc24..029142a 100644 --- a/lua/avante/llm.lua +++ b/lua/avante/llm.lua @@ -8,6 +8,7 @@ local Utils = require("avante.utils") local Prompts = require("avante.utils.prompts") local Config = require("avante.config") local Path = require("avante.path") +local PPath = require("plenary.path") local Providers = require("avante.providers") local LLMToolHelpers = require("avante.llm_tools.helpers") local LLMTools = require("avante.llm_tools") @@ -255,6 +256,17 @@ end ---@param opts AvanteGeneratePromptsOptions ---@return AvantePromptOptions function M.generate_prompts(opts) + local project_instruction_file = Config.instructions_file or "avante.md" + local project_root = Utils.root.get() + local instruction_file_path = PPath:new(project_root, project_instruction_file) + + if instruction_file_path:exists() then + local lines = Utils.read_file_from_buf_or_disk(instruction_file_path:absolute()) + local instruction_content = lines and table.concat(lines, "\n") or "" + + if instruction_content then opts.instructions = (opts.instructions or "") .. "\n" .. instruction_content end + end + local provider = opts.provider or Providers[Config.provider] local mode = opts.mode or Config.mode @@ -264,7 +276,6 @@ function M.generate_prompts(opts) image_paths = vim.list_extend(image_paths, opts.prompt_opts.image_paths) end - local project_root = Utils.root.get() Path.prompts.initialize(Path.prompts.get_templates_dir(project_root), project_root) local system_info = Utils.get_system_info() diff --git a/tests/llm_spec.lua b/tests/llm_spec.lua new file mode 100644 index 0000000..91e7cc0 --- /dev/null +++ b/tests/llm_spec.lua @@ -0,0 +1,115 @@ +local utils = require("avante.utils") +local PPath = require("plenary.path") + +local llm = require("avante.llm") + +describe("generate_prompts", function() + local project_root = "/tmp/project_root" + + before_each(function() + local mock_dir = PPath:new("tests", project_root) + mock_dir:mkdir({ parents = true }) + + local mock_file = PPath:new("tests", project_root, "avante.md") + mock_file:write("# Mock Instructions\nThis is a mock instruction file.", "w") + + -- Mock the project root + utils.root = {} + utils.root.get = function() return mock_dir end + + -- Mock Config.providers + local Config = require("avante.config") + Config.instructions_file = "avante.md" + Config.provider = "openai" + Config.providers = { + openai = { + endpoint = "https://api.mock.com/v1", + model = "gpt-mock", + timeout = 10000, + context_window = 1000, + extra_request_body = { + temperature = 0.5, + max_tokens = 1000, + }, + }, + } + -- Mock Config.history to prevent nil access error in Path.setup() + Config.history = { + max_tokens = 4096, + carried_entry_count = nil, + storage_path = "/tmp/test_avante_history", + paste = { + extension = "png", + filename = "pasted-%Y-%m-%d-%H-%M-%S", + }, + } + + -- Mock Config.behaviour + Config.behaviour = { + auto_focus_sidebar = true, + auto_suggestions = false, -- Experimental stage + auto_suggestions_respect_ignore = false, + auto_set_highlight_group = true, + auto_set_keymaps = true, + auto_apply_diff_after_generation = false, + jump_result_buffer_on_finish = false, + support_paste_from_clipboard = false, + minimize_diff = true, + enable_token_counting = true, + use_cwd_as_project_root = false, + auto_focus_on_diff_view = false, + auto_approve_tool_permissions = false, -- Default: show permission prompts for all tools + auto_check_diagnostics = true, + enable_fastapply = false, + } + + -- Mock Config.rules to prevent nil access error in get_templates_dir() + Config.rules = { + project_dir = nil, + global_dir = nil, + } + + -- Mock P.available to always return true + local Path = require("avante.path") + Path.available = function() return true end + + -- Mock the Prompt functions directly since _templates_lib is a local variable + -- that we can't easily access from outside the module + Path.prompts.initialize = function(cache_directory, project_directory) + -- Mock initialization - no-op for tests + end + + Path.prompts.render_file = function(path, opts) + -- Mock render - return empty string for tests + return "" + end + + Path.prompts.render_mode = function(mode, opts) + -- Mock render_mode - return empty string for tests + return "" + end + + Path.setup() -- Initialize necessary paths like cache_path + end) + + after_each(function() + -- Clean up created test files and directories + local mock_dir = PPath:new("tests", project_root) + if mock_dir:exists() then mock_dir:rmdir() end + end) + + it("should include instruction file content when the file exists", function() + local opts = {} + llm.generate_prompts(opts) + assert.are.same("\n# Mock Instructions\nThis is a mock instruction file.", opts.instructions) + end) + + it("should not modify instructions if the file does not exist", function() + local mock_file = PPath:new("tests", project_root, "avante.md") + if mock_file:exists() then mock_file:rm() end + + local opts = {} + llm.generate_prompts(opts) + assert.are.same(opts.instructions, nil) + end) +end)