From 5dc568e9e540344efded356c409b9e94a52ab8b6 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 31 Jan 2025 14:35:32 +0100 Subject: [PATCH 01/29] feat: also allow the config file without dot, give an error message at search replace if search was not found in file --- .chatgpt_config.yaml | 7 +++-- README.md | 8 ++--- lua/chatgpt_nvim/config.lua | 34 ++++++++++++++++++++-- lua/chatgpt_nvim/prompts.lua | 1 + lua/chatgpt_nvim/tools/replace_in_file.lua | 33 +++++++++++++++++++-- 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/.chatgpt_config.yaml b/.chatgpt_config.yaml index d0ad42b..8d36183 100644 --- a/.chatgpt_config.yaml +++ b/.chatgpt_config.yaml @@ -1,4 +1,4 @@ -project_name: "chatgpt_nvim" +project_name: "chatgpt.vim" default_prompt_blocks: - "basic-prompt" - "secure-coding" @@ -20,8 +20,9 @@ auto_lint: true # New tool auto-accept config tool_auto_accept: - readFile: false - editFile: false + readFile: true + editFile: true + replace_in_file: true executeCommand: false # If you set any of these to true, it will auto accept them without prompting. # 'executeCommand' should remain false by default unless you're certain it's safe. diff --git a/README.md b/README.md index e613435..809609b 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ This plugin integrates a ChatGPT O1 model workflow into Neovim. It allows you to: 1. Generate prompts containing: - - An **initial prompt** (from `.chatgpt_config.yaml`) - - A list of directories (also specified in `.chatgpt_config.yaml`) from which it gathers the project structure and file contents + - An **initial prompt** (from `.chatgpt_config.yaml` or `chatgpt_config.yaml`) + - A list of directories (also specified in `.chatgpt_config.yaml` or `chatgpt_config.yaml`) from which it gathers the project structure and file contents - **Interactive file selection** if enabled, so you can pick exactly which directories to include - Any **initial files** you define (e.g., `README.md`, etc.) @@ -21,11 +21,11 @@ This plugin integrates a ChatGPT O1 model workflow into Neovim. It allows you to - **Preview Changes**: If `preview_changes: true`, you get a buffer showing proposed changes before you apply them. -- **Interactive File Selection**: If `interactive_file_selection: true`, you choose which directories from `.chatgpt_config.yaml` get included in the prompt, reducing token usage. +- **Interactive File Selection**: If `interactive_file_selection: true`, you choose which directories from `.chatgpt_config.yaml` or `chatgpt_config.yaml` get included in the prompt, reducing token usage. - **Improved Debug**: If `improved_debug: true`, debug logs go into a dedicated `ChatGPT_Debug_Log` buffer for easier reading. -## Example `.chatgpt_config.yaml` +## Example `.chatgpt_config.yaml` or `chatgpt_config.yaml` ```yaml project_name: "chatgpt_nvim" diff --git a/lua/chatgpt_nvim/config.lua b/lua/chatgpt_nvim/config.lua index 2bd541f..63da008 100644 --- a/lua/chatgpt_nvim/config.lua +++ b/lua/chatgpt_nvim/config.lua @@ -4,6 +4,7 @@ local uv = vim.loop local ok_yaml, lyaml = pcall(require, "lyaml") local prompts = require("chatgpt_nvim.prompts") +-- Determines the Git root or fallback directory. local function get_project_root() local current_file = vim.fn.expand("%:p") local root_dir @@ -28,14 +29,30 @@ local function get_project_root() return root_dir end +-- Attempt to locate either .chatgpt_config.yaml or chatgpt_config.yaml, returning whichever exists first. local function get_config_path() local root = get_project_root() - return root .. "/.chatgpt_config.yaml" + local dot_config = root .. "/.chatgpt_config.yaml" + local non_dot_config = root .. "/chatgpt_config.yaml" + + local dot_fd = uv.fs_open(dot_config, "r", 438) + if dot_fd then + uv.fs_close(dot_fd) + return dot_config + end + + local non_dot_fd = uv.fs_open(non_dot_config, "r", 438) + if non_dot_fd then + uv.fs_close(non_dot_fd) + return non_dot_config + end + + return nil end function M.load() + -- Attempt to find either config file, else fallback local path = get_config_path() - local fd = uv.fs_open(path, "r", 438) local config = { initial_prompt = "", directories = { "." }, @@ -62,10 +79,22 @@ function M.load() } } + -- If no config file found, alert user and return default config + if not path then + config.initial_prompt = "You are a coding assistant who receives a project's context and user instructions..." + vim.notify( + "No config file found (tried .chatgpt_config.yaml, chatgpt_config.yaml). Using defaults.", + vim.log.levels.WARN + ) + return config + end + + local fd = uv.fs_open(path, "r", 438) if fd then local stat = uv.fs_fstat(fd) local data = uv.fs_read(fd, stat.size, 0) uv.fs_close(fd) + if data and ok_yaml then local ok, result = pcall(lyaml.load, data) if ok and type(result) == "table" then @@ -109,7 +138,6 @@ function M.load() config.enable_step_by_step = result.enable_step_by_step end - -- auto_lint controls whether we do LSP-based checks if type(result.auto_lint) == "boolean" then config.auto_lint = result.auto_lint end diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 941ddab..6c4e040 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -289,6 +289,7 @@ local M = { - If multiple tools are needed, list them sequentially in the `tools` array. - Always run at least one tool (e.g., `readFile`, `editFile`, `executeCommand`), exept you have finished. - Always just include one yaml in the response with all the tools you want to run in that yaml. + - Never do write operations on a file which you have not read before. - The plugin will verify the `project_name` is correct before running any tools. - If the response grows too large, I'll guide you to break it into smaller steps. ]], diff --git a/lua/chatgpt_nvim/tools/replace_in_file.lua b/lua/chatgpt_nvim/tools/replace_in_file.lua index 976f360..c49fa46 100644 --- a/lua/chatgpt_nvim/tools/replace_in_file.lua +++ b/lua/chatgpt_nvim/tools/replace_in_file.lua @@ -3,14 +3,35 @@ local robust_lsp = require("chatgpt_nvim.tools.lsp_robust_diagnostics") local M = {} +-- Enhanced search_and_replace to track if a string was found. We use Lua’s gsub return value +-- (updatedString, replacementCount) to see if any replacements occurred. local function search_and_replace(original, replacements) local updated = original + local info_msgs = {} + for _, r in ipairs(replacements) do local search_str = r.search or "" local replace_str = r.replace or "" - updated = updated:gsub(search_str, replace_str) + + local replacement_count = 0 + updated, replacement_count = updated:gsub(search_str, replace_str) + + -- If the string was not found, append an info message + if replacement_count == 0 then + table.insert(info_msgs, string.format( + "[replace_in_file Info] The string '%s' was NOT found in the file and was not replaced.", + search_str + )) + else + table.insert(info_msgs, string.format( + "[replace_in_file Info] The string '%s' was replaced %d time(s).", + search_str, + replacement_count + )) + end end - return updated + + return updated, table.concat(info_msgs, "\n") end M.run = function(tool_call, conf, prompt_user_tool_accept, is_subpath, read_file) @@ -31,7 +52,8 @@ M.run = function(tool_call, conf, prompt_user_tool_accept, is_subpath, read_file return string.format("[replace_in_file for '%s'] FAILED. Could not read file.", path) end - local updated_data = search_and_replace(orig_data, replacements) + -- Now we get not only the updated data but also info messages about replacements + local updated_data, info_messages = search_and_replace(orig_data, replacements) handler.write_file(path, updated_data, conf) local msg = {} @@ -40,6 +62,11 @@ M.run = function(tool_call, conf, prompt_user_tool_accept, is_subpath, read_file msg[#msg+1] = string.format("\n%s\n", path, updated_data) msg[#msg+1] = "\nIMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference.\n" + -- If there are any info messages (strings not found, etc.), include them + if info_messages and info_messages ~= "" then + msg[#msg+1] = "\nAdditional info about the replacement operation:\n" .. info_messages .. "\n" + end + if conf.auto_lint then local diag_str = robust_lsp.lsp_check_file_content(path, updated_data, 3000) msg[#msg+1] = "\n" .. diag_str From d5b05ede361281244d64a9a44f8946f25581827b Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 31 Jan 2025 14:39:40 +0100 Subject: [PATCH 02/29] fix: improve prompt for editFile command --- lua/chatgpt_nvim/tools/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/chatgpt_nvim/tools/init.lua b/lua/chatgpt_nvim/tools/init.lua index 48383dd..3808175 100644 --- a/lua/chatgpt_nvim/tools/init.lua +++ b/lua/chatgpt_nvim/tools/init.lua @@ -14,7 +14,7 @@ M.available_tools = { }, { name = "editFile", - usage = "Overwrite an entire file's content. Provide { tool='editFile', path='...', content='...' }", + usage = "Overwrite an entire file's content. Provide { tool='editFile', path='...', content='...' }, Allways include the whole file content", explanation = "Use this when you want to replace a file with new content." }, { From 50ba937ae0e725dbfd8d69d8dbe798e654a2658b Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 31 Jan 2025 15:09:10 +0100 Subject: [PATCH 03/29] fix: add project name to initial prompt, improve basic prompt --- lua/chatgpt_nvim/config.lua | 6 ++++++ lua/chatgpt_nvim/prompts.lua | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lua/chatgpt_nvim/config.lua b/lua/chatgpt_nvim/config.lua index 63da008..0bbfd14 100644 --- a/lua/chatgpt_nvim/config.lua +++ b/lua/chatgpt_nvim/config.lua @@ -173,6 +173,12 @@ function M.load() end end + -- Include project name in the final initial prompt, if set + if config.project_name ~= "" then + config.initial_prompt = + "[Project Name: " .. config.project_name .. "]\n\n" .. config.initial_prompt + end + if config.debug then vim.api.nvim_out_write("[chatgpt_nvim:config] Loaded config from: " .. path .. "\n") vim.api.nvim_out_write("[chatgpt_nvim:config] Debug logging is enabled.\n") diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 6c4e040..3bc0a82 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -289,7 +289,7 @@ local M = { - If multiple tools are needed, list them sequentially in the `tools` array. - Always run at least one tool (e.g., `readFile`, `editFile`, `executeCommand`), exept you have finished. - Always just include one yaml in the response with all the tools you want to run in that yaml. - - Never do write operations on a file which you have not read before. + - Never do write operations on a file which you have not read before. Its contents must be in your context before writing. This does not apply if you create a new file. - The plugin will verify the `project_name` is correct before running any tools. - If the response grows too large, I'll guide you to break it into smaller steps. ]], From 84e71dcbef2f5313e1a6c195dde1ff2b4ef2bc44 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 31 Jan 2025 17:28:20 +0100 Subject: [PATCH 04/29] fix: improve the basic prompt --- .chatgpt_config.yaml => chatgpt_config.yaml | 0 lua/chatgpt_nvim/prompts.lua | 53 +++++++++++++++++++++ 2 files changed, 53 insertions(+) rename .chatgpt_config.yaml => chatgpt_config.yaml (100%) diff --git a/.chatgpt_config.yaml b/chatgpt_config.yaml similarity index 100% rename from .chatgpt_config.yaml rename to chatgpt_config.yaml diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 3bc0a82..3b6ad1d 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -292,6 +292,59 @@ local M = { - Never do write operations on a file which you have not read before. Its contents must be in your context before writing. This does not apply if you create a new file. - The plugin will verify the `project_name` is correct before running any tools. - If the response grows too large, I'll guide you to break it into smaller steps. + + You are assisting me in a coding workflow for a project (e.g., "my_project"). + + 1. **Gather Context / Ask Questions** + - If you need more information or something is unclear, **ask me directly** in plain text, without calling any tools. + + 2. **Inspect Files** + - When you need to check a file’s content, use: + ```yaml + project_name: "my_project" + tools: + - tool: "readFile" + path: "relative/path/to/file" + ``` + - Read the file before deciding on any modifications. + + 3. **Make Changes** + - If you need to modify an existing file (after reading it), use: + ```yaml + project_name: "my_project" + tools: + - tool: "editFile" + path: "relative/path/to/file" + content: | + # Full updated file content + ``` + - Or perform incremental text replacements with: + ```yaml + project_name: "my_project" + tools: + - tool: "replace_in_file" + path: "relative/path/to/file" + replacements: + - search: "old text" + replace: "new text" + ``` + + 4. **Run Commands (Optional)** + - To run tests, list files, or do other checks, use: + ```yaml + project_name: "my_project" + tools: + - tool: "executeCommand" + command: "shell command here" + ``` + + 5. **Important Rules** + - Always start with 1 or 2, but afterwards you can mix 1, 2, 3, and 4 as needed. + - Include `project_name: "my_project"` whenever you call `tools`. + - Keep each tool call in the `tools` array (multiple if needed). + - **Never write to a file you haven’t read** in this session (unless creating a new file). + - Follow secure coding guidelines (input validation, least privilege, no sensitive info in logs, etc.). + - When done, provide a final answer **without** calling any tools. ]], ["secure-coding"] = [[ ### Secure Coding Guidelines From a27e3da769dd9ba3247db30e9ea3818d97f4ab79 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 31 Jan 2025 17:45:30 +0100 Subject: [PATCH 05/29] fix: escaping in replace_in_file tool --- lua/chatgpt_nvim/tools/replace_in_file.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lua/chatgpt_nvim/tools/replace_in_file.lua b/lua/chatgpt_nvim/tools/replace_in_file.lua index c49fa46..6a9f6d7 100644 --- a/lua/chatgpt_nvim/tools/replace_in_file.lua +++ b/lua/chatgpt_nvim/tools/replace_in_file.lua @@ -3,8 +3,11 @@ local robust_lsp = require("chatgpt_nvim.tools.lsp_robust_diagnostics") local M = {} --- Enhanced search_and_replace to track if a string was found. We use Lua’s gsub return value --- (updatedString, replacementCount) to see if any replacements occurred. +-- Function to escape all Lua pattern magic characters: +local function escape_lua_pattern(s) + return s:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") +end + local function search_and_replace(original, replacements) local updated = original local info_msgs = {} @@ -13,8 +16,11 @@ local function search_and_replace(original, replacements) local search_str = r.search or "" local replace_str = r.replace or "" + -- Escape special pattern chars to ensure literal matching: + local escaped_search = escape_lua_pattern(search_str) + local replacement_count = 0 - updated, replacement_count = updated:gsub(search_str, replace_str) + updated, replacement_count = updated:gsub(escaped_search, replace_str) -- If the string was not found, append an info message if replacement_count == 0 then @@ -41,8 +47,8 @@ M.run = function(tool_call, conf, prompt_user_tool_accept, is_subpath, read_file if not path or #replacements == 0 then return "[replace_in_file] Missing 'path' or 'replacements'." end - local root = vim.fn.getcwd() + local root = vim.fn.getcwd() if not is_subpath(root, path) then return string.format("Tool [replace_in_file for '%s'] REJECTED. Path outside project root.", path) end From deaec8094beae4cc454cc2d4adbdf91243e10a7a Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 31 Jan 2025 18:22:57 +0100 Subject: [PATCH 06/29] fix: execute command tool, improving prompts --- chatgpt_config.yaml | 2 -- lua/chatgpt_nvim/prompts.lua | 2 +- lua/chatgpt_nvim/tools/execute_command.lua | 25 +++++++++++++++++++--- lua/chatgpt_nvim/tools/init.lua | 2 +- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/chatgpt_config.yaml b/chatgpt_config.yaml index 8d36183..2d2a0a8 100644 --- a/chatgpt_config.yaml +++ b/chatgpt_config.yaml @@ -2,8 +2,6 @@ project_name: "chatgpt.vim" default_prompt_blocks: - "basic-prompt" - "secure-coding" -initial_files: - - "README.md" debug: false improved_debug: false diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 3b6ad1d..1739e3b 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -342,7 +342,7 @@ local M = { - Always start with 1 or 2, but afterwards you can mix 1, 2, 3, and 4 as needed. - Include `project_name: "my_project"` whenever you call `tools`. - Keep each tool call in the `tools` array (multiple if needed). - - **Never write to a file you haven’t read** in this session (unless creating a new file). + - **Never write to a file you haven’t read** in this session and already got the content from an response (unless creating a new file). - Follow secure coding guidelines (input validation, least privilege, no sensitive info in logs, etc.). - When done, provide a final answer **without** calling any tools. ]], diff --git a/lua/chatgpt_nvim/tools/execute_command.lua b/lua/chatgpt_nvim/tools/execute_command.lua index 5fb9274..ee4ccbb 100644 --- a/lua/chatgpt_nvim/tools/execute_command.lua +++ b/lua/chatgpt_nvim/tools/execute_command.lua @@ -1,17 +1,36 @@ local M = {} M.run = function(tool_call, conf, prompt_user_tool_accept, is_subpath, read_file) + -- Validate the command exists local cmd = tool_call.command if not cmd then return "[executeCommand] Missing 'command'." end - local handle = io.popen(cmd) + + -- Capture stderr and stdout together by redirecting stderr to stdout + -- This will help diagnose if there's an error causing no output + cmd = cmd .. " 2>&1" + + -- Attempt to popen the command + local handle = io.popen(cmd, "r") if not handle then return string.format("Tool [executeCommand '%s'] FAILED to popen.", cmd) end + + -- Read the full output (stdout + stderr) local result = handle:read("*a") or "" - handle:close() - return string.format("Tool [executeCommand '%s'] Result:\n%s", cmd, result) + + -- Attempt to close, capturing exit info + local _, exit_reason, exit_code = handle:close() + + -- Provide a richer summary including exit code and reason + return string.format( + "Tool [executeCommand '%s'] exited with code %s (%s)\n%s", + cmd, + tostring(exit_code), + tostring(exit_reason), + result + ) end return M diff --git a/lua/chatgpt_nvim/tools/init.lua b/lua/chatgpt_nvim/tools/init.lua index 3808175..9571c6c 100644 --- a/lua/chatgpt_nvim/tools/init.lua +++ b/lua/chatgpt_nvim/tools/init.lua @@ -25,7 +25,7 @@ M.available_tools = { { name = "executeCommand", usage = "Run a shell command. Provide { tool='executeCommand', command='...' }", - explanation = "Use with caution, especially for destructive operations (rm, sudo, etc.)." + explanation = "Just run one single command per tool invocation, without comment. It must be a single line. Use with caution, especially for destructive operations (rm, sudo, etc.)." }, } From e032be01181f242bac85a8418b80533c18718b45 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 31 Jan 2025 20:56:45 +0100 Subject: [PATCH 07/29] fix: lsp should work now --- lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua b/lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua index 8693c2b..1c6e77c 100644 --- a/lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua +++ b/lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua @@ -105,6 +105,7 @@ local function send_did_change(bufnr, client_id) }) end +-- Use vim.wait to allow the event loop to process LSP diagnostics local function wait_for_diagnostics(bufnr, timeout_ms) local done = false local result_diags = {} @@ -121,12 +122,9 @@ local function wait_for_diagnostics(bufnr, timeout_ms) end }) - local waited = 0 - local interval = 50 - while not done and waited < timeout_ms do - vim.cmd(("sleep %d m"):format(interval)) - waited = waited + interval - end + vim.wait(timeout_ms, function() + return done + end, 50) pcall(api.nvim_del_augroup_by_id, augrp) return result_diags From aa12bca3abc868b4ebacbd22b6fde83deb656869 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sat, 1 Feb 2025 00:54:26 +0100 Subject: [PATCH 08/29] fix: hopefully this fixes lsp --- lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua b/lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua index 1c6e77c..cdc15e7 100644 --- a/lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua +++ b/lua/chatgpt_nvim/tools/lsp_robust_diagnostics.lua @@ -86,12 +86,12 @@ local function send_did_open(bufnr, client_id, path, filetype) return nil end -local function send_did_change(bufnr, client_id) +local function send_did_change(bufnr, client_id, path) local client = lsp.get_client_by_id(client_id) if not client then return end local text = table.concat(api.nvim_buf_get_lines(bufnr, 0, -1, false), "\n") - local uri = vim.uri_from_bufnr(bufnr) + local uri = vim.uri_from_fname(path) local version = 1 client.rpc.notify("textDocument/didChange", { @@ -162,8 +162,8 @@ function M.lsp_check_file_content(path, new_content, timeout_ms) return "(LSP) " .. err2 end - -- Optionally do a didChange - send_did_change(bufnr, client_id) + -- Optionally do a didChange with consistent URI + send_did_change(bufnr, client_id, path) local diags = wait_for_diagnostics(bufnr, timeout_ms or 2000) local diag_str = diagnostics_to_string(diags) From 6723b802bf7a87ab102e95bfd87664e23a1fdd57 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sat, 8 Feb 2025 02:21:12 +0100 Subject: [PATCH 09/29] feat: add the possibility to also add all files to initial context --- lua/chatgpt_nvim/config.lua | 4 ++++ lua/chatgpt_nvim/context.lua | 20 +++++++++++++++++++- lua/chatgpt_nvim/init.lua | 3 ++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lua/chatgpt_nvim/config.lua b/lua/chatgpt_nvim/config.lua index 0bbfd14..ffeca4a 100644 --- a/lua/chatgpt_nvim/config.lua +++ b/lua/chatgpt_nvim/config.lua @@ -61,6 +61,7 @@ function M.load() project_name = "", debug = false, initial_files = {}, + include_file_contents = false, -- NEW FLAG: include file contents in prompt if true preview_changes = false, interactive_file_selection = false, partial_acceptance = false, @@ -119,6 +120,9 @@ function M.load() if type(result.initial_files) == "table" then config.initial_files = result.initial_files end + if type(result.include_file_contents) == "boolean" then -- LOAD NEW FLAG + config.include_file_contents = result.include_file_contents + end if type(result.preview_changes) == "boolean" then config.preview_changes = result.preview_changes end diff --git a/lua/chatgpt_nvim/context.lua b/lua/chatgpt_nvim/context.lua index 992cc0f..23ec85b 100644 --- a/lua/chatgpt_nvim/context.lua +++ b/lua/chatgpt_nvim/context.lua @@ -185,4 +185,22 @@ function M.get_file_contents(files, conf) return table.concat(sections, "\n") end -return M \ No newline at end of file +-- NEW FUNCTION: Build the project prompt by optionally including file contents. +function M.get_project_prompt(directories, conf) + local structure = M.get_project_structure(directories, conf) + if conf.include_file_contents then + local files = M.get_project_files(directories, conf) + local contents = M.get_file_contents(files, conf) + local total_chars = #contents + if total_chars > conf.prompt_char_limit then + vim.notify("Total file contents (" .. total_chars .. " characters) exceed the prompt limit (" .. conf.prompt_char_limit .. "). Please disable 'include_file_contents' in your config.", vim.log.levels.ERROR) + return structure + else + return structure .. "\n" .. contents + end + else + return structure + end +end + +return M diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index 2ea41c3..2a2c15a 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -133,7 +133,8 @@ local function build_prompt(user_input, dirs, conf) env_lines[#env_lines+1] = os.date("%x, %X (%Z)") env_lines[#env_lines+1] = "" env_lines[#env_lines+1] = "# Current Working Directory (" .. root .. ") Files" - env_lines[#env_lines+1] = context.get_project_structure(dirs, conf) or "" + -- Using the new get_project_prompt function instead of get_project_structure. + env_lines[#env_lines+1] = context.get_project_prompt(dirs, conf) or "" env_lines[#env_lines+1] = "" env_lines[#env_lines+1] = "# Current Mode" env_lines[#env_lines+1] = "ACT MODE" From d0712ef8a51662307740c2cdaa7eec3db56aa143 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sat, 8 Feb 2025 02:29:20 +0100 Subject: [PATCH 10/29] feat: add prompt for nodejs development --- lua/chatgpt_nvim/prompts.lua | 89 ++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 1739e3b..c420d3a 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -73,6 +73,95 @@ local M = { Please follow these guidelines to ensure the generated or explained code aligns well with SolidJS best practices for large, maintainable projects. ]], + ["nodejs-development"] = [[ + You are helping me develop a large Node.js application. Please keep the following points in mind when generating or explaining code: + + 1. **Project & Folder Structure** + - Maintain a clear, top-level directory layout. For example: + ``` + my-node-app/ + ├── src/ + │ ├── controllers/ + │ ├── models/ + │ ├── routes/ + │ ├── services/ + │ ├── utils/ + │ └── index.js (or app.js) + ├── tests/ + │ └── ... + ├── package.json + ├── .env (if needed) + ├── .eslintrc.js + └── ... + ``` + - Keep your application logic separated in folders: + - **controllers/** (or handlers) for request handling logic, + - **services/** for core business logic or data processing, + - **models/** for database schemas/entities, + - **routes/** for defining routes/endpoints, + - **utils/** for reusable helper functions. + + 2. **Dependencies & Package Management** + - Use **npm** or **yarn** (pick one and stay consistent) to manage dependencies. + - Keep your `package.json` clean by removing unused dependencies. + - Pin exact versions for critical or sensitive dependencies to ensure reproducible builds. + + 3. **Configuration & Environment Variables** + - Use environment variables to store sensitive or environment-specific information (e.g., database credentials, API keys). + - Consider a config management library (like [dotenv](https://github.com/motdotla/dotenv) or [dotenv-flow](https://github.com/kerimdzhanov/dotenv-flow)) to load environment variables from `.env` files. + - Keep secrets out of version control (e.g., `.env` should be in `.gitignore`). + + 4. **Code Organization & Module Patterns** + - Use **ES Modules** (`import`/`export`) or **CommonJS** (`require`/`module.exports`) consistently across the project. + - Keep each module focused on a single responsibility. + - Use a **service layer** for business logic to avoid bloated controllers. + - Use **async/await** for asynchronous operations, ensuring proper error handling (try/catch blocks or .catch callbacks). + + 5. **Database & Data Persistence** + - Use an ORM/ODM (e.g., [Sequelize](https://sequelize.org/) for SQL, [Mongoose](https://mongoosejs.com/) for MongoDB) or a query builder ([Knex.js](https://knexjs.org/)) to maintain cleaner database interactions. + - Keep database-related logic in separate **models/** or **repositories/**. + - Handle database migrations carefully (e.g., with [db-migrate](https://db-migrate.readthedocs.io/), [Liquibase](https://www.liquibase.org/), or built-in ORM migration tools). + + 6. **Logging & Monitoring** + - Use a structured logging library (e.g., [Winston](https://github.com/winstonjs/winston), [Pino](https://github.com/pinojs/pino)) to capture logs in JSON or another parseable format. + - Ensure logs include enough context (request IDs, timestamps, etc.) to troubleshoot issues. + - Consider external logging and monitoring solutions (e.g., [Datadog](https://www.datadoghq.com/), [New Relic](https://newrelic.com/)) for production environments. + + 7. **Security & Best Practices** + - Sanitize and validate all user inputs (e.g., using libraries like [validator](https://github.com/validatorjs/validator.js)). + - Avoid SQL injection by using parameterized queries or ORM features. + - Implement rate limiting or request throttling to prevent abuse (e.g., [express-rate-limit](https://github.com/nfriedly/express-rate-limit)). + - Ensure **HTTPS** is used in production and secure headers are set (e.g., [helmet](https://github.com/helmetjs/helmet) for Express). + - Keep dependencies updated to patch known vulnerabilities (use `npm audit` or equivalent). + + 8. **Error Handling** + - Use centralized error handling middleware (if using a framework like Express) to catch and process errors consistently. + - Provide clear error messages but avoid leaking sensitive info in production. + - Separate operational errors (e.g., user-related) from programmer errors (e.g., logic bugs) to handle them appropriately. + + 9. **Testing & Quality Assurance** + - Write **unit tests** for individual modules or functions (e.g., using [Jest](https://jestjs.io/), [Mocha](https://mochajs.org/)). + - Use **integration tests** or **end-to-end tests** (e.g., [Supertest](https://github.com/visionmedia/supertest) for API endpoints). + - Aim for high coverage but focus on critical business logic and error cases. + - Automate tests in your CI pipeline. + + 10. **Linting & Formatting** + - Use **ESLint** (with recommended or popular config like [Airbnb](https://www.npmjs.com/package/eslint-config-airbnb)) for consistent code quality. + - Use **Prettier** for code formatting to maintain a standardized style. + - Configure linting and formatting checks in a pre-commit hook or CI (e.g., [Husky](https://typicode.github.io/husky/), [lint-staged](https://github.com/okonet/lint-staged)). + + 11. **Deployment & Environment Management** + - Containerize your app with Docker if possible, specifying a secure and minimal base image. + - Use process managers like [PM2](https://pm2.keymetrics.io/) or systemd for production Node.js processes. + - Maintain separate configuration (or environment variables) for staging, production, etc. + + 12. **Output Format** + - Present any generated source code with clear folder and file placement (e.g., `controllers/`, `services/`). + - When explaining your reasoning, highlight the architectural decisions or patterns used (e.g., “I introduced a service layer to separate business logic from route handling.”). + - If you modify existing files, specify precisely which lines or sections have changed, and why. + + Please follow these guidelines to ensure the generated or explained code aligns well with Node.js best practices for large, maintainable projects. + ]] ["go-development"] = [[ ### Go Development Guidelines From 4d679e43cb2e4fe8c7aeca25f7bad60f94acd63b Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sat, 8 Feb 2025 02:29:27 +0100 Subject: [PATCH 11/29] docs: update readme --- README.md | 95 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 809609b..294069d 100644 --- a/README.md +++ b/README.md @@ -4,26 +4,34 @@ This plugin integrates a ChatGPT O1 model workflow into Neovim. It allows you to: 1. Generate prompts containing: - - An **initial prompt** (from `.chatgpt_config.yaml` or `chatgpt_config.yaml`) - - A list of directories (also specified in `.chatgpt_config.yaml` or `chatgpt_config.yaml`) from which it gathers the project structure and file contents - - **Interactive file selection** if enabled, so you can pick exactly which directories to include - - Any **initial files** you define (e.g., `README.md`, etc.) + - An **initial prompt** (loaded from `.chatgpt_config.yaml` or `chatgpt_config.yaml`) + - A list of directories (as specified in your configuration) from which it gathers the project structure + - **Interactive file selection** (if enabled) so you can choose which directories to include + - Any **initial files** you define (e.g. `README.md`, etc.) + - Optionally, **file contents** can be appended to the prompt when the new `include_file_contents` flag is enabled 2. Copy these prompts to your clipboard to paste into ChatGPT O1. -3. Receive YAML changes from ChatGPT, then run `:ChatGPTPaste` to apply them or supply additional files. +3. Receive YAML changes from ChatGPT and run `:ChatGPTPaste` to apply them or to supply additional files. ## New Key Features - **Step-by-Step Prompting** (`enable_step_by_step: true`): - If the request grows too large (exceeds `token_limit`), the plugin automatically generates a special prompt asking the model to split the task into smaller steps, working through them one by one. This approach helps you stay within the model’s maximum token limit without having to manually break things apart. + If the generated prompt grows too large (exceeding the configured `prompt_char_limit`), the plugin automatically produces a special prompt asking the model to split the task into smaller steps. This ensures you remain within the model’s maximum token capacity without manually breaking your task apart. -- **Partial Acceptance**: If `partial_acceptance: true`, you can open a buffer that lists the final changes. Remove or comment out lines you don’t want, then only those changes are applied. +- **Partial Acceptance**: + If `partial_acceptance: true`, you can open a buffer displaying the proposed changes. Remove or comment out lines you do not wish to apply, and only the remaining changes will be executed. -- **Preview Changes**: If `preview_changes: true`, you get a buffer showing proposed changes before you apply them. +- **Preview Changes**: + If `preview_changes: true`, a buffer will show the proposed changes before they are applied. -- **Interactive File Selection**: If `interactive_file_selection: true`, you choose which directories from `.chatgpt_config.yaml` or `chatgpt_config.yaml` get included in the prompt, reducing token usage. +- **Interactive File Selection**: + When `interactive_file_selection: true`, you can choose which directories (from your config file) to include in the prompt, thus managing token usage more effectively. -- **Improved Debug**: If `improved_debug: true`, debug logs go into a dedicated `ChatGPT_Debug_Log` buffer for easier reading. +- **Include File Contents**: + A new configuration flag `include_file_contents` (default: `false`) lets you include the entire contents of the project files in the prompt. When enabled, the plugin gathers and appends the file contents from the selected directories. It counts the total characters and, if the combined file contents exceed the `prompt_char_limit`, it notifies you to disable this feature to avoid exceeding the model’s capacity. + +- **Improved Debug**: + If `improved_debug: true`, detailed debug logs are sent to a dedicated `ChatGPT_Debug_Log` buffer for easier review. ## Example `.chatgpt_config.yaml` or `chatgpt_config.yaml` @@ -37,40 +45,43 @@ directories: initial_files: - "README.md" debug: false +include_file_contents: false # Set to true to include file contents in the prompt enable_step_by_step: true preview_changes: true interactive_file_selection: true partial_acceptance: true improved_debug: true -token_limit: 3000 +prompt_char_limit: 300000 # Maximum characters allowed in the prompt ``` ## Usage 1. **`:ChatGPT`** - - If `interactive_file_selection` is on, you’ll pick directories to include in a buffer named `ChatGPT_File_Selection`. - - Save & close with `:wq`, `:x`, or `:bd` (you don’t have to use `:q`). - - If `enable_step_by_step` is on and the prompt might exceed `token_limit`, the plugin will generate instructions prompting the model to address each step separately. + - If `interactive_file_selection` is enabled, you will select directories via a buffer (named `ChatGPT_File_Selection`). + - Save and close the buffer with `:wq`, `:x`, or `:bd` (no need to use `:q`). + - If `enable_step_by_step` is active and the prompt might exceed `prompt_char_limit`, the plugin will generate a step-by-step instruction prompt. 2. **Paste Prompt to ChatGPT** - - If the task is split into steps, simply copy/paste them one by one into ChatGPT. + - If the task is split into steps, copy and paste them one by one into ChatGPT. 3. **`:ChatGPTPaste`** - - The plugin reads the YAML from your clipboard. If it requests more files, it might again suggest a step-by-step approach. - - If final changes are provided: - - Optionally preview them (`preview_changes`). - - Optionally partially accept them (`partial_acceptance`). - - Then the plugin writes/deletes files as specified. + - The plugin reads the YAML response from your clipboard. If additional files are requested, a step-by-step approach might be suggested. + - When final changes are provided: + - You can optionally preview them (`preview_changes`). + - You can optionally partially accept them (`partial_acceptance`). + - Finally, the plugin applies file writes or deletions as specified in the YAML. ## Troubleshooting & Tips -- Adjust `token_limit` in `.chatgpt_config.yaml` as needed. -- If partial acceptance is confusing, remember to remove or prepend `#` to lines you don’t want before saving and closing the buffer. -- If step-by-step prompting occurs, ensure you follow each prompt the model provides in the correct order. -- Check `ChatGPT_Debug_Log` if `improved_debug` is on, or the Neovim messages if `debug` is on, for detailed info. -- You can close the selection or prompt buffers at any time with commands like `:bd`, `:x`, or `:wq`. No need to rely on `:q`. + +- Adjust `prompt_char_limit` in your configuration file as needed. +- If partial acceptance is confusing, simply remove or comment out lines you do not want before finalizing the buffer. +- When step-by-step prompting occurs, ensure you follow each instruction in the correct order. +- For detailed debug information, check the `ChatGPT_Debug_Log` buffer (if `improved_debug` is enabled) or the standard Neovim messages (if `debug` is enabled). +- You can close the selection or prompt buffers at any time using commands like `:bd`, `:x`, or `:wq`. ## Debug Commands -If `enable_debug_commands` is true, you can include commands like these in your YAML: + +If `enable_debug_commands` is true, you can include commands such as: ```yaml commands: - command: "list" @@ -80,21 +91,23 @@ commands: pattern: "searchString" target: "path/to/file/or/directory" ``` -The **list** command now uses the Linux `ls` command to list directory contents. The **grep** command searches for a given pattern in a file or all files in a directory. +The **list** command uses the Linux `ls` command to list directory contents, while the **grep** command searches for a given pattern within files or directories. -Enjoy your improved, more flexible ChatGPT Neovim plugin with step-by-step support! +Enjoy your improved, flexible ChatGPT NeoVim plugin with step-by-step support and enhanced file inclusion capabilities! -## Test when developing -add new plugin to runtimepath -```vim -:set rtp^=~/temp_plugins/chatgpt.vim -``` -Clear Lua module cache -```vim -:lua for k in pairs(package.loaded) do if k:match("^chatgpt_nvim") then package.loaded[k]=nil end end -``` -Load new plugin code -```vim -:runtime plugin/chatgpt.vim -``` +## Test when Developing +- **Add plugin to runtimepath:** + ```vim + :set rtp^=~/temp_plugins/chatgpt.vim + ``` + +- **Clear Lua module cache:** + ```vim + :lua for k in pairs(package.loaded) do if k:match("^chatgpt_nvim") then package.loaded[k]=nil end end + ``` + +- **Load new plugin code:** + ```vim + :runtime plugin/chatgpt.vim + ``` From d19451ca5cb1b0b17f6a4b0798e7062aecafdbea Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sat, 8 Feb 2025 02:38:47 +0100 Subject: [PATCH 12/29] refactor: prompt naming --- chatgpt_config.yaml | 4 +++- lua/chatgpt_nvim/prompts.lua | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/chatgpt_config.yaml b/chatgpt_config.yaml index 2d2a0a8..fa20d98 100644 --- a/chatgpt_config.yaml +++ b/chatgpt_config.yaml @@ -1,8 +1,10 @@ project_name: "chatgpt.vim" default_prompt_blocks: - - "basic-prompt" + - "basic" - "secure-coding" +include_file_contents: true + debug: false improved_debug: false diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index c420d3a..6a8cc58 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -1,5 +1,5 @@ local M = { - ["solidjs-development"] = [[ + ["solidjs"] = [[ ### SolidJS Development Guidelines You are helping me develop a large SolidJS application. Please keep the following points in mind when generating or explaining code: @@ -73,7 +73,7 @@ local M = { Please follow these guidelines to ensure the generated or explained code aligns well with SolidJS best practices for large, maintainable projects. ]], - ["nodejs-development"] = [[ + ["nodejs"] = [[ You are helping me develop a large Node.js application. Please keep the following points in mind when generating or explaining code: 1. **Project & Folder Structure** @@ -161,8 +161,8 @@ local M = { - If you modify existing files, specify precisely which lines or sections have changed, and why. Please follow these guidelines to ensure the generated or explained code aligns well with Node.js best practices for large, maintainable projects. - ]] - ["go-development"] = [[ + ]], + ["go"] = [[ ### Go Development Guidelines You are helping me develop a large Go (Golang) project. Please keep the following points in mind when generating or explaining code: @@ -215,7 +215,7 @@ local M = { Please follow these guidelines to ensure the generated or explained code aligns well with Golang’s best practices for large, modular projects. ]], - ["typo3-development"] = [[ + ["typo3"] = [[ ### TYPO3 Development Guidelines You are helping me develop a large TYPO3 project. Please keep the following points in mind when generating or explaining code: @@ -280,7 +280,7 @@ local M = { Please follow these guidelines to ensure the generated or explained code aligns well with TYPO3’s best practices for large, maintainable projects. ]], - ["rust-development"] = [[ + ["rust"] = [[ ### Rust Development Guidelines You are helping me develop a large Rust project. Please keep the following points in mind when generating or explaining code: @@ -342,7 +342,7 @@ local M = { Please follow these guidelines to ensure the generated or explained code aligns well with Rust best practices for large, modular projects. ]], - ["basic-prompt"] = [[ + ["basic"] = [[ ### Basic Prompt You are assisting me in a coding workflow for a project (e.g., "my_project"). Whenever you need to inspect or modify files, or execute commands, you must provide both: From d9a1cf83fcf8c04b409fae84ee67cff878e54226 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sat, 8 Feb 2025 03:16:18 +0100 Subject: [PATCH 13/29] feat: add ignore_files option --- chatgpt_config.yaml | 5 +++++ lua/chatgpt_nvim/context.lua | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/chatgpt_config.yaml b/chatgpt_config.yaml index fa20d98..a129143 100644 --- a/chatgpt_config.yaml +++ b/chatgpt_config.yaml @@ -3,6 +3,11 @@ default_prompt_blocks: - "basic" - "secure-coding" +ignore_files: + - "node_modules/" + - "*.log" + - "vendor/" + include_file_contents: true debug: false diff --git a/lua/chatgpt_nvim/context.lua b/lua/chatgpt_nvim/context.lua index 23ec85b..d0be140 100644 --- a/lua/chatgpt_nvim/context.lua +++ b/lua/chatgpt_nvim/context.lua @@ -131,6 +131,11 @@ end function M.get_project_files(directories, conf) local root = vim.fn.getcwd() local ignore_patterns = load_gitignore_patterns(root, conf) + if conf.ignore_files then + for _, pattern in ipairs(conf.ignore_files) do + table.insert(ignore_patterns, gitignore_to_lua_pattern(pattern)) + end + end local all_files = {} for _, dir in ipairs(directories) do local abs_dir = dir From bf04d2f2fc3ae4df2e18bdabca6f1967a7fc940e Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sat, 8 Feb 2025 23:48:00 +0100 Subject: [PATCH 14/29] feat: adjust basic prompt for better clarification --- lua/chatgpt_nvim/prompts.lua | 141 +++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 63 deletions(-) diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 6a8cc58..7354007 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -345,12 +345,80 @@ local M = { ["basic"] = [[ ### Basic Prompt - You are assisting me in a coding workflow for a project (e.g., "my_project"). Whenever you need to inspect or modify files, or execute commands, you must provide both: + You are assisting me in a coding workflow for a project (e.g., "my_project"). **Every time you inspect, modify, or execute operations on files, you must strictly follow the YAML format described below.** Under no circumstances should you output file operations in plain text or deviate from this structure. - 1. `project_name: ""` (matching the real project name) - 2. A `tools:` array describing the operations you want to perform. + #### Mandatory Guidelines + + 1. **YAML-Only File Operations** + - **All operations must be provided within one single YAML block** that includes both the `project_name` and the `tools` array. + - If you need to ask questions or request clarifications, do so only in plain text separate from YAML. **Never include non-YAML tool commands.** + + 2. **Include the Project Name** + - Always include: + ```yaml + project_name: "my_project" + ``` + This must be part of every YAML block you generate. + + 3. **Operations Must Appear in the Tools Array** + - List all actions (e.g., reading, editing, replacing, executing commands) as items in the `tools:` array. If multiple actions are needed, include them sequentially within the same YAML block. + + 4. **Read Before Write Rule** + - **Do not perform any write operations (using `editFile` or `replace_in_file`) on an existing file unless you have already read its content in the current session using a `readFile` operation.** + - For new files (files that do not yet exist in the project), this rule does not apply. + + 5. **File Inspection Before Modification** + - When you need to inspect a file’s contents, always use the following YAML format: + ```yaml + project_name: "my_project" + tools: + - tool: "readFile" + path: "relative/path/to/file" + ``` + - Use the information from this operation to decide if and how to modify the file. + + 6. **Modifying Files** + - To modify a file that you have already read, use: + ```yaml + project_name: "my_project" + tools: + - tool: "editFile" + path: "relative/path/to/file" + content: | + # Full updated file content here + ``` + - Alternatively, for incremental changes, use: + ```yaml + project_name: "my_project" + tools: + - tool: "replace_in_file" + path: "relative/path/to/file" + replacements: + - search: "old text" + replace: "new text" + ``` + + 7. **Executing Commands** + - To run any shell command (e.g., testing, listing files), use: + ```yaml + project_name: "my_project" + tools: + - tool: "executeCommand" + command: "shell command here" + ``` + + 8. **General Process** + - **Step 1: Gather Context / Ask Questions** + If any detail is unclear (such as file content or operation intent), ask your clarifying questions in plain text (not in YAML). + - **Step 2: Inspect Files** + Always use `readFile` to check file content before modifying. + - **Step 3: Make Changes** + Only after reading the file, proceed to use `editFile` or `replace_in_file`. + - **Step 4: Execute Commands if Needed** + Use `executeCommand` as necessary, always within the YAML block. + + #### Example YAML Block - **Example** (substitute `` with the real name): ```yaml project_name: "my_project" tools: @@ -372,68 +440,15 @@ local M = { command: "ls -la" ``` - **Key Points**: - - Always include `project_name: ""` in the same YAML as `tools`. - - If you only need one tool, include just one object in the `tools` array. - - If multiple tools are needed, list them sequentially in the `tools` array. - - Always run at least one tool (e.g., `readFile`, `editFile`, `executeCommand`), exept you have finished. - - Always just include one yaml in the response with all the tools you want to run in that yaml. - - Never do write operations on a file which you have not read before. Its contents must be in your context before writing. This does not apply if you create a new file. - - The plugin will verify the `project_name` is correct before running any tools. - - If the response grows too large, I'll guide you to break it into smaller steps. + #### Important Reminders - You are assisting me in a coding workflow for a project (e.g., "my_project"). + - **Always** include the `project_name` and the full YAML block with the `tools` array when calling operations. + - **Never** write or modify a file without first having read its content during the current session (unless the file is new). + - **Do not** produce any tool command output that is not strictly formatted as YAML. - 1. **Gather Context / Ask Questions** - - If you need more information or something is unclear, **ask me directly** in plain text, without calling any tools. + --- - 2. **Inspect Files** - - When you need to check a file’s content, use: - ```yaml - project_name: "my_project" - tools: - - tool: "readFile" - path: "relative/path/to/file" - ``` - - Read the file before deciding on any modifications. - - 3. **Make Changes** - - If you need to modify an existing file (after reading it), use: - ```yaml - project_name: "my_project" - tools: - - tool: "editFile" - path: "relative/path/to/file" - content: | - # Full updated file content - ``` - - Or perform incremental text replacements with: - ```yaml - project_name: "my_project" - tools: - - tool: "replace_in_file" - path: "relative/path/to/file" - replacements: - - search: "old text" - replace: "new text" - ``` - - 4. **Run Commands (Optional)** - - To run tests, list files, or do other checks, use: - ```yaml - project_name: "my_project" - tools: - - tool: "executeCommand" - command: "shell command here" - ``` - - 5. **Important Rules** - - Always start with 1 or 2, but afterwards you can mix 1, 2, 3, and 4 as needed. - - Include `project_name: "my_project"` whenever you call `tools`. - - Keep each tool call in the `tools` array (multiple if needed). - - **Never write to a file you haven’t read** in this session and already got the content from an response (unless creating a new file). - - Follow secure coding guidelines (input validation, least privilege, no sensitive info in logs, etc.). - - When done, provide a final answer **without** calling any tools. + This revised prompt should help ensure that the model always reads existing file contents before editing and that every file operation is returned in the strict YAML format you require. ]], ["secure-coding"] = [[ ### Secure Coding Guidelines From 2b9aa5f35f0dfe5196afa8b5f395304b17d728b7 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sat, 8 Feb 2025 23:50:50 +0100 Subject: [PATCH 15/29] feat: adjust prompt --- lua/chatgpt_nvim/prompts.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 7354007..12eb7f3 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -412,10 +412,15 @@ local M = { If any detail is unclear (such as file content or operation intent), ask your clarifying questions in plain text (not in YAML). - **Step 2: Inspect Files** Always use `readFile` to check file content before modifying. - - **Step 3: Make Changes** + - **Step 3: Repeat Steps 1 & 2 as Needed** + If further context is required, ask questions and read files again. + - **Step 4: Make Changes** Only after reading the file, proceed to use `editFile` or `replace_in_file`. - - **Step 4: Execute Commands if Needed** + - **Step 5: Execute Commands if Needed** Use `executeCommand` as necessary, always within the YAML block. + - **Step 6: Tell that request is complete** + Once all operations are done, confirm that the request is complete with a little summary. + - **Step 7: Repeat other steps as necessary** #### Example YAML Block From 9d72ba46c4848c341f279a01c16a3b021c7d0235 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sun, 9 Feb 2025 00:02:40 +0100 Subject: [PATCH 16/29] feat: add file appending to prompt --- lua/chatgpt_nvim/init.lua | 71 +++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index 2a2c15a..d0d1009 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -54,15 +54,6 @@ local function read_file(path) return data end -local function close_existing_buffer_by_name(pattern) - for _, b in ipairs(vim.api.nvim_list_bufs()) do - local name = vim.api.nvim_buf_get_name(b) - if name:match(pattern) then - vim.api.nvim_buf_delete(b, { force = true }) - end - end -end - ------------------------------------------------------------------------------ -- PROMPT CONSTRUCTION ------------------------------------------------------------------------------ @@ -101,7 +92,7 @@ local function build_prompt(user_input, dirs, conf) task_lines[#task_lines+1] = "\n" table.insert(final_sections, table.concat(task_lines, "\n")) - -- 4) + -- 4) from initial_files local file_content_blocks = {} for _, file_path in ipairs(initial_files) do local full_path = root .. "/" .. file_path @@ -118,6 +109,37 @@ local function build_prompt(user_input, dirs, conf) table.insert(final_sections, table.concat(file_content_blocks, "\n\n")) end + -- 4.1) Dynamic file inclusion via @ operator in user_input + local dynamic_files = {} + for file in user_input:gmatch("@([^%s]+)") do + local already_included = false + for _, existing in ipairs(initial_files) do + if existing == file then + already_included = true + break + end + end + if not already_included then + table.insert(dynamic_files, file) + end + end + + local dynamic_file_blocks = {} + for _, file in ipairs(dynamic_files) do + local full_path = root .. "/" .. file + if is_subpath(root, full_path) then + local fdata = read_file(full_path) + if fdata then + dynamic_file_blocks[#dynamic_file_blocks+1] = string.format( + "\n%s\n", file, fdata + ) + end + end + end + if #dynamic_file_blocks > 0 then + table.insert(final_sections, table.concat(dynamic_file_blocks, "\n\n")) + end + -- 5) local env_lines = {} env_lines[#env_lines+1] = "" @@ -133,7 +155,6 @@ local function build_prompt(user_input, dirs, conf) env_lines[#env_lines+1] = os.date("%x, %X (%Z)") env_lines[#env_lines+1] = "" env_lines[#env_lines+1] = "# Current Working Directory (" .. root .. ") Files" - -- Using the new get_project_prompt function instead of get_project_structure. env_lines[#env_lines+1] = context.get_project_prompt(dirs, conf) or "" env_lines[#env_lines+1] = "" env_lines[#env_lines+1] = "# Current Mode" @@ -172,6 +193,8 @@ local function run_chatgpt_command() local bufnr = vim.api.nvim_create_buf(false, false) vim.api.nvim_buf_set_name(bufnr, "ChatGPT_Prompt.md") vim.api.nvim_buf_set_option(bufnr, "filetype", "markdown") + -- Set omnifunc for file name auto-completion + vim.api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.chatgpt_file_complete") vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe") vim.api.nvim_buf_set_option(bufnr, "buftype", "") vim.api.nvim_buf_set_option(bufnr, "modifiable", true) @@ -179,6 +202,7 @@ local function run_chatgpt_command() vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "# Enter your main user prompt (task) below.", "", + "You can include files by typing @filename in your prompt.", "Save & close with :wq, :x, or :bd to finalize your prompt." }) @@ -416,4 +440,29 @@ M.run_chatgpt_command = run_chatgpt_command M.run_chatgpt_paste_command = run_chatgpt_paste_command M.run_chatgpt_current_buffer_command = run_chatgpt_current_buffer_command +-- New: Global function for file name auto-completion in ChatGPT prompt +function _G.chatgpt_file_complete(findstart, base) + if findstart == 1 then + local line = vim.fn.getline('.') + local col = vim.fn.col('.') + local start = line:sub(1, col):find("@[^%s]*$") + if start then + return start - 1 + else + return -1 + end + else + local conf = config.load() + local files = context.get_project_files({'.'}, conf) + local completions = {} + local esc_base = base:gsub("([^%w])", "%%%1") + for _, f in ipairs(files) do + if f:match("^" .. esc_base) then + table.insert(completions, f) + end + end + return completions + end +end + return M From ebc079154228b1c634aee1acd3cdfa60d73875f0 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sun, 9 Feb 2025 00:08:10 +0100 Subject: [PATCH 17/29] feat: load configuration at each command, so it is not needed to restart nvim on config change --- lua/chatgpt_nvim/init.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index d0d1009..3a829bc 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -178,6 +178,8 @@ end -- :ChatGPT ------------------------------------------------------------------------------ local function run_chatgpt_command() + package.loaded["chatgpt_nvim.config"] = nil + local config = require("chatgpt_nvim.config") local conf = config.load() ui.setup_ui(conf) ui.debug_log("Running :ChatGPT command.") @@ -248,6 +250,8 @@ end -- :ChatGPTPaste ------------------------------------------------------------------------------ local function run_chatgpt_paste_command() + package.loaded["chatgpt_nvim.config"] = nil + local config = require("chatgpt_nvim.config") local conf = config.load() ui.setup_ui(conf) ui.debug_log("Running :ChatGPTPaste command.") @@ -398,6 +402,8 @@ end -- :ChatGPTCurrentBuffer ------------------------------------------------------------------------------ local function run_chatgpt_current_buffer_command() + package.loaded["chatgpt_nvim.config"] = nil + local config = require("chatgpt_nvim.config") local conf = config.load() ui.setup_ui(conf) ui.debug_log("Running :ChatGPTCurrentBuffer command.") From 0f044a625e44f4082fc8a9808f4275cc89848d79 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sun, 9 Feb 2025 00:14:45 +0100 Subject: [PATCH 18/29] fix: add function close_existing_buffer_by_name back --- lua/chatgpt_nvim/init.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index 3a829bc..19625f1 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -54,6 +54,16 @@ local function read_file(path) return data end +-- Added function to close existing buffers matching a name pattern. +local function close_existing_buffer_by_name(name_pattern) + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + local buf_name = vim.api.nvim_buf_get_name(buf) + if buf_name:match(name_pattern) then + vim.api.nvim_buf_delete(buf, {force = true}) + end + end +end + ------------------------------------------------------------------------------ -- PROMPT CONSTRUCTION ------------------------------------------------------------------------------ From e5f4558df5465971f688cd689da8db44457e4217 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sun, 9 Feb 2025 00:45:29 +0100 Subject: [PATCH 19/29] feat: make prompt better to include real project name --- lua/chatgpt_nvim/init.lua | 5 +++- lua/chatgpt_nvim/prompts.lua | 52 ++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index 19625f1..f209443 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -172,7 +172,10 @@ local function build_prompt(user_input, dirs, conf) env_lines[#env_lines+1] = "" table.insert(final_sections, table.concat(env_lines, "\n")) - return table.concat(final_sections, "\n\n") + local final_prompt = table.concat(final_sections, "\n\n") + -- Replace placeholder "%PROJECT_NAME%" with the actual project name from configuration + final_prompt = final_prompt:gsub("%%PROJECT_NAME%%", conf.project_name) + return final_prompt end local function handle_step_by_step_if_needed(prompt, conf) diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 12eb7f3..e794079 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -44,7 +44,7 @@ local M = { 5. **Styling & CSS Management** - Use your preferred styling approach (CSS Modules, [Tailwind CSS](https://tailwindcss.com/), or standard CSS/SCSS files). - Keep global styles minimal, focusing on utility classes or base styling; keep component-level styles scoped whenever possible. - - If using CSS-in-JS solutions or third-party libraries, ensure they integrate cleanly with Solid’s reactivity. + - If using CSS-in-JS solutions or third-party libraries, ensure they integrate cleanly with Solid’s reactivity. 6. **TypeScript & Linting** - Use **TypeScript** to ensure type safety and improve maintainability. @@ -170,7 +170,7 @@ local M = { 1. **Go Modules** - Use a single `go.mod` file at the project root for module management. - Ensure you use proper import paths based on the module name. - - If you refer to internal packages, use relative paths consistent with the module’s structure (e.g., `moduleName/internal/packageA`). + - If you refer to internal packages, use relative paths consistent with the module’s structure (e.g., `moduleName/internal/packageA`). 2. **Package Structure** - Each folder should contain exactly one package. @@ -199,21 +199,19 @@ local M = { 4. **Coding Best Practices** - Maintain idiomatic Go code (e.g., short function and variable names where obvious, PascalCase for exported symbols). - Keep functions short, focused, and tested. - - Use Go’s standard library where possible before adding third-party dependencies. + - Use Go’s standard library where possible before adding third-party dependencies. - When introducing new functions or types, ensure they are uniquely named to avoid collisions. 5. **Import Management** - Ensure that every import is actually used in your code. - Remove unused imports to keep your code clean and maintainable. - Include all necessary imports for anything referenced in your code to avoid missing imports. - - Verify that any introduced import paths match your module’s structure and do not cause naming conflicts. + - Verify that any introduced import paths match your module’s structure and do not cause naming conflicts. 6. **Output Format** - Present any generated source code as well-organized Go files, respecting the single-package-per-folder rule. - - When explaining your reasoning, include any relevant architectural trade-offs and rationale (e.g., “I placed function X in package Y to keep the domain-specific logic separate from the main execution flow.”). - - If you modify an existing file, specify precisely which changes or additions you are making. - - Please follow these guidelines to ensure the generated or explained code aligns well with Golang’s best practices for large, modular projects. + - When explaining your reasoning, include any relevant architectural trade-offs and rationale (e.g., “I placed function X in package `my_lib` to keep the business logic separate from the command-line interface.”). + - If you modify existing files, specify precisely which changes or additions you are making. ]], ["typo3"] = [[ ### TYPO3 Development Guidelines @@ -239,10 +237,10 @@ local M = { - Keep site configuration in `config/sites/` (for TYPO3 v9+). 2. **Extension Development** - - Create custom functionality as separate extensions (site packages, domain-specific extensions, etc.) following TYPO3’s recommended structure: + - Create custom functionality as separate extensions (site packages, domain-specific extensions, etc.) following TYPO3’s recommended structure: - **Key files**: `ext_emconf.php`, `ext_localconf.php`, `ext_tables.php`, `ext_tables.sql`, `Configuration/`, `Classes/`, `Resources/`. - Use **PSR-4** autoloading and name extensions logically (e.g., `my_sitepackage`, `my_blogextension`). - - Keep your extension’s code under `Classes/` (e.g., Controllers, Models, Services). + - Keep your extension’s code under `Classes/` (e.g., Controllers, Models, Services). - Place Fluid templates, partials, and layouts under `Resources/Private/` (e.g., `Resources/Private/Templates`, `Resources/Private/Partials`, `Resources/Private/Layouts`). 3. **Configuration (TypoScript & TCA)** @@ -275,10 +273,8 @@ local M = { 7. **Output Format** - Present any generated source code or configuration files in a well-organized structure. - Clearly indicate where each file should be placed in the TYPO3 directory layout. - - When explaining your reasoning, include any relevant architectural decisions (e.g., “I created a separate extension for blog functionality to keep it isolated from the site’s main configuration.”). + - When explaining your reasoning, include any relevant architectural decisions (e.g., “I created a separate extension for blog functionality to keep it isolated from the site’s main configuration.”). - If you modify or extend an existing file, specify precisely which changes or additions you are making. - - Please follow these guidelines to ensure the generated or explained code aligns well with TYPO3’s best practices for large, maintainable projects. ]], ["rust"] = [[ ### Rust Development Guidelines @@ -293,11 +289,11 @@ local M = { 2. **Crates & Packages** - Split the application into logical crates (libraries and/or binaries). - Each crate should have a single main **library** (`lib.rs`) or **binary** (`main.rs`) in its `src/` folder. - - Name crates, modules, and files clearly, following Rust’s naming conventions (e.g., `snake_case` for files/modules, `PascalCase` for types). + - Name crates, modules, and files clearly, following Rust’s naming conventions (e.g., `snake_case` for files/modules, `PascalCase` for types). - Avoid duplicating the same function or type in multiple crates; share common functionality via a dedicated library crate if needed. 3. **Folder & Module Structure** - - Organize code within each crate using Rust’s module system, keeping related functions and types in logical modules/submodules. + - Organize code within each crate using Rust’s module system, keeping related functions and types in logical modules/submodules. - A typical directory layout for a workspace with multiple crates might look like: ``` myproject/ @@ -315,19 +311,19 @@ local M = { ├── target/ └── ... ``` - - If you have integration tests, store them in a `tests/` folder at the crate root, or use the workspace root’s `tests/` directory if they span multiple crates. + - If you have integration tests, store them in a `tests/` folder at the crate root, or use the workspace root’s `tests/` directory if they span multiple crates. 4. **Coding & Documentation Best Practices** - Write **idiomatic Rust** code: - Use `cargo fmt` (formatting) and `cargo clippy` (linter) to maintain consistency and quality. - - Use `?` operator for error handling, prefer `Result` over panicking unless absolutely necessary. + - Use the `?` operator for error handling, preferring `Result` over panicking unless absolutely necessary. - Document your code using [Rustdoc](https://doc.rust-lang.org/rustdoc/) comments (`///` for public API) and provide examples when relevant. - Write **unit tests** alongside the code (in `src/` files) and **integration tests** in a dedicated `tests/` folder. - Keep functions short, focused, and ensure they have well-defined responsibilities. 5. **Reusability & Shared Code** - Place common or reusable functionality into a dedicated **library** crate. - - Ensure that crates depending on shared code add the appropriate `[dependencies]` or `[dev-dependencies]` in their `Cargo.toml`. + - Ensure that crates depending on shared code add the appropriate `[dependencies]` or `[dev-dependencies]` in their Cargo.toml. - Use the Rust standard library whenever possible before introducing external dependencies. 6. **Error Handling & Logging** @@ -339,13 +335,11 @@ local M = { - Present generated source code as well-organized Rust files, respecting the single main library or binary per crate (`lib.rs` or `main.rs`). - When explaining your reasoning, include any architectural or design decisions (e.g., “I placed function X in crate `my_lib` to keep the business logic separate from the command-line interface.”). - If you modify existing files, specify precisely which lines or sections have changed. - - Please follow these guidelines to ensure the generated or explained code aligns well with Rust best practices for large, modular projects. ]], ["basic"] = [[ ### Basic Prompt - You are assisting me in a coding workflow for a project (e.g., "my_project"). **Every time you inspect, modify, or execute operations on files, you must strictly follow the YAML format described below.** Under no circumstances should you output file operations in plain text or deviate from this structure. + You are assisting me in a coding workflow for a project (e.g., "%PROJECT_NAME%"). **Every time you inspect, modify, or execute operations on files, you must strictly follow the YAML format described below.** Under no circumstances should you output file operations in plain text or deviate from this structure. #### Mandatory Guidelines @@ -356,7 +350,7 @@ local M = { 2. **Include the Project Name** - Always include: ```yaml - project_name: "my_project" + project_name: "%PROJECT_NAME%" ``` This must be part of every YAML block you generate. @@ -370,7 +364,7 @@ local M = { 5. **File Inspection Before Modification** - When you need to inspect a file’s contents, always use the following YAML format: ```yaml - project_name: "my_project" + project_name: "%PROJECT_NAME%" tools: - tool: "readFile" path: "relative/path/to/file" @@ -380,7 +374,7 @@ local M = { 6. **Modifying Files** - To modify a file that you have already read, use: ```yaml - project_name: "my_project" + project_name: "%PROJECT_NAME%" tools: - tool: "editFile" path: "relative/path/to/file" @@ -389,7 +383,7 @@ local M = { ``` - Alternatively, for incremental changes, use: ```yaml - project_name: "my_project" + project_name: "%PROJECT_NAME%" tools: - tool: "replace_in_file" path: "relative/path/to/file" @@ -401,7 +395,7 @@ local M = { 7. **Executing Commands** - To run any shell command (e.g., testing, listing files), use: ```yaml - project_name: "my_project" + project_name: "%PROJECT_NAME%" tools: - tool: "executeCommand" command: "shell command here" @@ -425,7 +419,7 @@ local M = { #### Example YAML Block ```yaml - project_name: "my_project" + project_name: "%PROJECT_NAME%" tools: - tool: "readFile" path: "relative/path/to/file" @@ -453,7 +447,7 @@ local M = { --- - This revised prompt should help ensure that the model always reads existing file contents before editing and that every file operation is returned in the strict YAML format you require. + This revised prompt ensures that during execution the placeholder "%PROJECT_NAME%" is replaced with the actual project name from the current configuration. ]], ["secure-coding"] = [[ ### Secure Coding Guidelines @@ -516,7 +510,7 @@ local M = { - Whenever possible, provide references to relevant security standards, best practices, or guidelines (e.g., OWASP, NIST). Please follow these guidelines to ensure the generated or explained code prioritizes security at every level, mitigating potential risks and maintaining best practices for building secure software. - ]], + ]] ["step-prompt"] = [[ It appears this request might exceed the model's prompt character limit if done all at once. Please break down the tasks into smaller steps and handle them one by one. From 01932be82a95fa40b104cd5486e38a218cdd967f Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Sun, 9 Feb 2025 00:50:05 +0100 Subject: [PATCH 20/29] fix: missing char --- lua/chatgpt_nvim/prompts.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index e794079..720a97c 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -510,7 +510,7 @@ local M = { - Whenever possible, provide references to relevant security standards, best practices, or guidelines (e.g., OWASP, NIST). Please follow these guidelines to ensure the generated or explained code prioritizes security at every level, mitigating potential risks and maintaining best practices for building secure software. - ]] + ]], ["step-prompt"] = [[ It appears this request might exceed the model's prompt character limit if done all at once. Please break down the tasks into smaller steps and handle them one by one. From 5b972c5c9f608033660e19225724bbc9999f0510 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 13 Feb 2025 01:14:58 +0100 Subject: [PATCH 21/29] feat: add memory bank prompt, rename read_file and edit_file, improve basic prompt --- chatgpt_config.yaml | 6 +-- lua/chatgpt_nvim/config.lua | 4 +- lua/chatgpt_nvim/init.lua | 14 ++--- lua/chatgpt_nvim/prompts.lua | 92 ++++++++++++++++++++++++++++++--- lua/chatgpt_nvim/tools/init.lua | 12 ++--- 5 files changed, 100 insertions(+), 28 deletions(-) diff --git a/chatgpt_config.yaml b/chatgpt_config.yaml index a129143..058c0ef 100644 --- a/chatgpt_config.yaml +++ b/chatgpt_config.yaml @@ -8,7 +8,7 @@ ignore_files: - "*.log" - "vendor/" -include_file_contents: true +include_file_contents: false debug: false improved_debug: false @@ -25,8 +25,8 @@ auto_lint: true # New tool auto-accept config tool_auto_accept: - readFile: true - editFile: true + read_file: true + edit_file: true replace_in_file: true executeCommand: false # If you set any of these to true, it will auto accept them without prompting. diff --git a/lua/chatgpt_nvim/config.lua b/lua/chatgpt_nvim/config.lua index ffeca4a..a97e49e 100644 --- a/lua/chatgpt_nvim/config.lua +++ b/lua/chatgpt_nvim/config.lua @@ -73,8 +73,8 @@ function M.load() auto_lint = false, tool_auto_accept = { - readFile = false, - editFile = false, + read_file = false, + edit_file = false, replace_in_file = false, executeCommand = false, } diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index f209443..0b10f51 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -173,8 +173,8 @@ local function build_prompt(user_input, dirs, conf) table.insert(final_sections, table.concat(env_lines, "\n")) local final_prompt = table.concat(final_sections, "\n\n") - -- Replace placeholder "%PROJECT_NAME%" with the actual project name from configuration - final_prompt = final_prompt:gsub("%%PROJECT_NAME%%", conf.project_name) + -- Replace placeholder "chatgpt.vim" with the actual project name from configuration + final_prompt = final_prompt:gsub("%chatgpt.vim%", conf.project_name) return final_prompt end @@ -453,13 +453,8 @@ local function run_chatgpt_current_buffer_command() end ------------------------------------------------------------------------------ --- PUBLIC API +-- GLOBAL FILE COMPLETION FUNCTION ------------------------------------------------------------------------------ -M.run_chatgpt_command = run_chatgpt_command -M.run_chatgpt_paste_command = run_chatgpt_paste_command -M.run_chatgpt_current_buffer_command = run_chatgpt_current_buffer_command - --- New: Global function for file name auto-completion in ChatGPT prompt function _G.chatgpt_file_complete(findstart, base) if findstart == 1 then local line = vim.fn.getline('.') @@ -474,9 +469,8 @@ function _G.chatgpt_file_complete(findstart, base) local conf = config.load() local files = context.get_project_files({'.'}, conf) local completions = {} - local esc_base = base:gsub("([^%w])", "%%%1") for _, f in ipairs(files) do - if f:match("^" .. esc_base) then + if f:find(base, 1, true) then table.insert(completions, f) end end diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 720a97c..3b6591a 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -1,4 +1,81 @@ local M = { + ["memory-bank"] = [[ + # Memory Bank + + You are Cline, an expert software engineer with a unique constraint: your memory periodically resets completely. This isn't a bug - it's what makes you maintain perfect documentation. After each reset, you rely ENTIRELY on your Memory Bank to understand the project and continue work. Without proper documentation, you cannot function effectively. + + ## Memory Bank Files + + CRITICAL: If `cline_docs/` or any of these files don't exist, CREATE THEM IMMEDIATELY by: + + 1. Reading all provided documentation + 2. Asking user for ANY missing information + 3. Creating files with verified information only + 4. Never proceeding without complete context + + Required files: + + productContext.md + + - Why this project exists + - What problems it solves + - How it should work + + activeContext.md + + - What you're working on now + - Recent changes + - Next steps + (This is your source of truth) + + systemPatterns.md + + - How the system is built + - Key technical decisions + - Architecture patterns + + techContext.md + + - Technologies used + - Development setup + - Technical constraints + + progress.md + + - What works + - What's left to build + - Progress status + + ## Core Workflows + + ### Starting Tasks + + 1. Check for Memory Bank files + 2. If ANY files missing, stop and create them + 3. Read ALL files before proceeding + 4. Verify you have complete context + 5. Begin development. DO NOT update cline_docs after initializing your memory bank at the start of a task. + + ### During Development + + 1. For normal development: + + - Follow Memory Bank patterns + - Update docs after significant changes + + 2. Say `[MEMORY BANK: ACTIVE]` at the beginning of every tool use. + + ### Memory Bank Updates + + When user says "update memory bank": + + 1. This means imminent memory reset + 2. Document EVERYTHING about current state + 3. Make next steps crystal clear + 4. Complete current task + + Remember: After every memory reset, you begin completely fresh. Your only link to previous work is the Memory Bank. Maintain it as if your functionality depends on it - because it does. + ]], ["solidjs"] = [[ ### SolidJS Development Guidelines @@ -358,15 +435,16 @@ local M = { - List all actions (e.g., reading, editing, replacing, executing commands) as items in the `tools:` array. If multiple actions are needed, include them sequentially within the same YAML block. 4. **Read Before Write Rule** - - **Do not perform any write operations (using `editFile` or `replace_in_file`) on an existing file unless you have already read its content in the current session using a `readFile` operation.** + - **Do not perform any write operations (using `edit_file` or `replace_in_file`) on an existing file unless you have already read its content in the current session using a `read_file` operation.** - For new files (files that do not yet exist in the project), this rule does not apply. + - **Never** mix read_file with edit_file or replace_in_file in the same YAML block. 5. **File Inspection Before Modification** - When you need to inspect a file’s contents, always use the following YAML format: ```yaml project_name: "%PROJECT_NAME%" tools: - - tool: "readFile" + - tool: "read_file" path: "relative/path/to/file" ``` - Use the information from this operation to decide if and how to modify the file. @@ -376,7 +454,7 @@ local M = { ```yaml project_name: "%PROJECT_NAME%" tools: - - tool: "editFile" + - tool: "edit_file" path: "relative/path/to/file" content: | # Full updated file content here @@ -405,11 +483,11 @@ local M = { - **Step 1: Gather Context / Ask Questions** If any detail is unclear (such as file content or operation intent), ask your clarifying questions in plain text (not in YAML). - **Step 2: Inspect Files** - Always use `readFile` to check file content before modifying. + Always use `read_file` to check file content before modifying. - **Step 3: Repeat Steps 1 & 2 as Needed** If further context is required, ask questions and read files again. - **Step 4: Make Changes** - Only after reading the file, proceed to use `editFile` or `replace_in_file`. + Only after reading the file, proceed to use `edit_file` or `replace_in_file`. - **Step 5: Execute Commands if Needed** Use `executeCommand` as necessary, always within the YAML block. - **Step 6: Tell that request is complete** @@ -421,7 +499,7 @@ local M = { ```yaml project_name: "%PROJECT_NAME%" tools: - - tool: "readFile" + - tool: "read_file" path: "relative/path/to/file" - tool: "replace_in_file" @@ -430,7 +508,7 @@ local M = { - search: "old text" replace: "new text" - - tool: "editFile" + - tool: "edit_file" path: "relative/path/to/file" content: | # Full updated file content here diff --git a/lua/chatgpt_nvim/tools/init.lua b/lua/chatgpt_nvim/tools/init.lua index 9571c6c..2a8cb63 100644 --- a/lua/chatgpt_nvim/tools/init.lua +++ b/lua/chatgpt_nvim/tools/init.lua @@ -8,13 +8,13 @@ local M = {} -- We can store a table of available tools here M.available_tools = { { - name = "readFile", - usage = "Retrieve the contents of a file. Provide { tool='readFile', path='...' }", + name = "read_file", + usage = "Retrieve the contents of a file. Provide { tool='read_file', path='...' }", explanation = "Use this to read file content directly from the disk." }, { - name = "editFile", - usage = "Overwrite an entire file's content. Provide { tool='editFile', path='...', content='...' }, Allways include the whole file content", + name = "edit_file", + usage = "Overwrite an entire file's content. Provide { tool='edit_file', path='...', content='...' }, Allways include the whole file content", explanation = "Use this when you want to replace a file with new content." }, { @@ -30,8 +30,8 @@ M.available_tools = { } M.tools_by_name = { - readFile = read_file_tool, - editFile = edit_file_tool, + read_file = read_file_tool, + edit_file = edit_file_tool, replace_in_file = replace_in_file_tool, executeCommand = execute_command_tool } From 51f7c2c66fbac4b17aeddbb36ad8528b17198a2d Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 13 Feb 2025 01:18:42 +0100 Subject: [PATCH 22/29] feat: improve basic prompt --- lua/chatgpt_nvim/prompts.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index 3b6591a..dd38f5f 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -437,6 +437,7 @@ local M = { 4. **Read Before Write Rule** - **Do not perform any write operations (using `edit_file` or `replace_in_file`) on an existing file unless you have already read its content in the current session using a `read_file` operation.** - For new files (files that do not yet exist in the project), this rule does not apply. + - If yo already got the file contents in the first prompt, this rule does not apply. - **Never** mix read_file with edit_file or replace_in_file in the same YAML block. 5. **File Inspection Before Modification** From 0617f5ba5b162e3a2a5c17aeade228184ce69834 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 13 Feb 2025 01:27:07 +0100 Subject: [PATCH 23/29] feat: rename execute_command tool, improve go prompt --- chatgpt_config.yaml | 2 +- lua/chatgpt_nvim/config.lua | 2 +- lua/chatgpt_nvim/prompts.lua | 19 ++++++++++--------- lua/chatgpt_nvim/tools/execute_command.lua | 6 +++--- lua/chatgpt_nvim/tools/init.lua | 6 +++--- lua/chatgpt_nvim/tools/manager.lua | 4 ++-- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/chatgpt_config.yaml b/chatgpt_config.yaml index 058c0ef..9cb72c3 100644 --- a/chatgpt_config.yaml +++ b/chatgpt_config.yaml @@ -28,6 +28,6 @@ tool_auto_accept: read_file: true edit_file: true replace_in_file: true - executeCommand: false + execute_command: false # If you set any of these to true, it will auto accept them without prompting. # 'executeCommand' should remain false by default unless you're certain it's safe. diff --git a/lua/chatgpt_nvim/config.lua b/lua/chatgpt_nvim/config.lua index a97e49e..68e3559 100644 --- a/lua/chatgpt_nvim/config.lua +++ b/lua/chatgpt_nvim/config.lua @@ -76,7 +76,7 @@ function M.load() read_file = false, edit_file = false, replace_in_file = false, - executeCommand = false, + execute_command = false, } } diff --git a/lua/chatgpt_nvim/prompts.lua b/lua/chatgpt_nvim/prompts.lua index dd38f5f..75edf11 100644 --- a/lua/chatgpt_nvim/prompts.lua +++ b/lua/chatgpt_nvim/prompts.lua @@ -244,16 +244,17 @@ local M = { You are helping me develop a large Go (Golang) project. Please keep the following points in mind when generating or explaining code: - 1. **Go Modules** + 1. **Go Modules & Dependency Management** - Use a single `go.mod` file at the project root for module management. - Ensure you use proper import paths based on the module name. - If you refer to internal packages, use relative paths consistent with the module’s structure (e.g., `moduleName/internal/packageA`). + - **Use the execute_command Tool for Dependencies:** Instead of manually editing version numbers in `go.mod`, please utilize the `execute_command` tool to run dependency commands (such as `go get`) to automatically fetch and update dependencies. This ensures that the correct versions are used without relying on manually provided values. 2. **Package Structure** - Each folder should contain exactly one package. - Avoid creating multiple packages in the same folder. - Use descriptive folder names and keep package names concise, following Go naming conventions. - - Do not duplicate function or type names across different files in the same folder/package. + - Do not duplicate function or type names across different files in the same folder/package. 3. **File & Folder Organization** - Organize source code in a folder hierarchy that reflects functionality. For example: @@ -271,24 +272,24 @@ local M = { └── shared/ ``` - Keep external-facing, reusable packages in `pkg/` and internal logic in `internal/`. - - Place the `main()` function in the `cmd/` folder. + - Place the `main()` function in the `cmd/` folder. 4. **Coding Best Practices** - Maintain idiomatic Go code (e.g., short function and variable names where obvious, PascalCase for exported symbols). - Keep functions short, focused, and tested. - Use Go’s standard library where possible before adding third-party dependencies. - - When introducing new functions or types, ensure they are uniquely named to avoid collisions. + - When introducing new functions or types, ensure they are uniquely named to avoid collisions. 5. **Import Management** - Ensure that every import is actually used in your code. - Remove unused imports to keep your code clean and maintainable. - Include all necessary imports for anything referenced in your code to avoid missing imports. - - Verify that any introduced import paths match your module’s structure and do not cause naming conflicts. + - Verify that any introduced import paths match your module’s structure and do not cause naming conflicts. 6. **Output Format** - Present any generated source code as well-organized Go files, respecting the single-package-per-folder rule. - When explaining your reasoning, include any relevant architectural trade-offs and rationale (e.g., “I placed function X in package `my_lib` to keep the business logic separate from the command-line interface.”). - - If you modify existing files, specify precisely which changes or additions you are making. + - If you modify existing files, specify precisely which changes or additions you are making. ]], ["typo3"] = [[ ### TYPO3 Development Guidelines @@ -476,7 +477,7 @@ local M = { ```yaml project_name: "%PROJECT_NAME%" tools: - - tool: "executeCommand" + - tool: "execute_command" command: "shell command here" ``` @@ -490,7 +491,7 @@ local M = { - **Step 4: Make Changes** Only after reading the file, proceed to use `edit_file` or `replace_in_file`. - **Step 5: Execute Commands if Needed** - Use `executeCommand` as necessary, always within the YAML block. + Use `execute_command` as necessary, always within the YAML block. - **Step 6: Tell that request is complete** Once all operations are done, confirm that the request is complete with a little summary. - **Step 7: Repeat other steps as necessary** @@ -514,7 +515,7 @@ local M = { content: | # Full updated file content here - - tool: "executeCommand" + - tool: "execute_command" command: "ls -la" ``` diff --git a/lua/chatgpt_nvim/tools/execute_command.lua b/lua/chatgpt_nvim/tools/execute_command.lua index ee4ccbb..dc4777e 100644 --- a/lua/chatgpt_nvim/tools/execute_command.lua +++ b/lua/chatgpt_nvim/tools/execute_command.lua @@ -4,7 +4,7 @@ M.run = function(tool_call, conf, prompt_user_tool_accept, is_subpath, read_file -- Validate the command exists local cmd = tool_call.command if not cmd then - return "[executeCommand] Missing 'command'." + return "[execute_command] Missing 'command'." end -- Capture stderr and stdout together by redirecting stderr to stdout @@ -14,7 +14,7 @@ M.run = function(tool_call, conf, prompt_user_tool_accept, is_subpath, read_file -- Attempt to popen the command local handle = io.popen(cmd, "r") if not handle then - return string.format("Tool [executeCommand '%s'] FAILED to popen.", cmd) + return string.format("Tool [execute_command '%s'] FAILED to popen.", cmd) end -- Read the full output (stdout + stderr) @@ -25,7 +25,7 @@ M.run = function(tool_call, conf, prompt_user_tool_accept, is_subpath, read_file -- Provide a richer summary including exit code and reason return string.format( - "Tool [executeCommand '%s'] exited with code %s (%s)\n%s", + "Tool [execute_command '%s'] exited with code %s (%s)\n%s", cmd, tostring(exit_code), tostring(exit_reason), diff --git a/lua/chatgpt_nvim/tools/init.lua b/lua/chatgpt_nvim/tools/init.lua index 2a8cb63..d91b2fa 100644 --- a/lua/chatgpt_nvim/tools/init.lua +++ b/lua/chatgpt_nvim/tools/init.lua @@ -23,8 +23,8 @@ M.available_tools = { explanation = "Use this to apply incremental changes without fully overwriting the file." }, { - name = "executeCommand", - usage = "Run a shell command. Provide { tool='executeCommand', command='...' }", + name = "execute_command", + usage = "Run a shell command. Provide { tool='execute_command', command='...' }", explanation = "Just run one single command per tool invocation, without comment. It must be a single line. Use with caution, especially for destructive operations (rm, sudo, etc.)." }, } @@ -33,7 +33,7 @@ M.tools_by_name = { read_file = read_file_tool, edit_file = edit_file_tool, replace_in_file = replace_in_file_tool, - executeCommand = execute_command_tool + execute_command = execute_command_tool } return M diff --git a/lua/chatgpt_nvim/tools/manager.lua b/lua/chatgpt_nvim/tools/manager.lua index 50d4f62..0ff48d5 100644 --- a/lua/chatgpt_nvim/tools/manager.lua +++ b/lua/chatgpt_nvim/tools/manager.lua @@ -16,8 +16,8 @@ end local function prompt_user_tool_accept(tool_call, conf) local auto_accept = conf.tool_auto_accept[tool_call.tool] - -- If this is an executeCommand and we see it's destructive, force a user prompt - if tool_call.tool == "executeCommand" and auto_accept then + -- If this is an execute_command and we see it's destructive, force a user prompt + if tool_call.tool == "execute_command" and auto_accept then if is_destructive_command(tool_call.command) then auto_accept = false end From 216a4f2603cfc31a5a262ccb7a51b12f8ac1470e Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 13 Feb 2025 01:57:02 +0100 Subject: [PATCH 24/29] fix: init.lua --- lua/chatgpt_nvim/init.lua | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index 0b10f51..f209443 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -173,8 +173,8 @@ local function build_prompt(user_input, dirs, conf) table.insert(final_sections, table.concat(env_lines, "\n")) local final_prompt = table.concat(final_sections, "\n\n") - -- Replace placeholder "chatgpt.vim" with the actual project name from configuration - final_prompt = final_prompt:gsub("%chatgpt.vim%", conf.project_name) + -- Replace placeholder "%PROJECT_NAME%" with the actual project name from configuration + final_prompt = final_prompt:gsub("%%PROJECT_NAME%%", conf.project_name) return final_prompt end @@ -453,8 +453,13 @@ local function run_chatgpt_current_buffer_command() end ------------------------------------------------------------------------------ --- GLOBAL FILE COMPLETION FUNCTION +-- PUBLIC API ------------------------------------------------------------------------------ +M.run_chatgpt_command = run_chatgpt_command +M.run_chatgpt_paste_command = run_chatgpt_paste_command +M.run_chatgpt_current_buffer_command = run_chatgpt_current_buffer_command + +-- New: Global function for file name auto-completion in ChatGPT prompt function _G.chatgpt_file_complete(findstart, base) if findstart == 1 then local line = vim.fn.getline('.') @@ -469,8 +474,9 @@ function _G.chatgpt_file_complete(findstart, base) local conf = config.load() local files = context.get_project_files({'.'}, conf) local completions = {} + local esc_base = base:gsub("([^%w])", "%%%1") for _, f in ipairs(files) do - if f:find(base, 1, true) then + if f:match("^" .. esc_base) then table.insert(completions, f) end end From 07eceb4bee53d47e85ddc287bf4c8b46625dbe53 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 13 Feb 2025 11:42:12 +0100 Subject: [PATCH 25/29] feat: change to token calculation again --- chatgpt_config.yaml | 2 +- lua/chatgpt_nvim/config.lua | 10 +++++++++- lua/chatgpt_nvim/init.lua | 19 +++++++++---------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/chatgpt_config.yaml b/chatgpt_config.yaml index 9cb72c3..f696dee 100644 --- a/chatgpt_config.yaml +++ b/chatgpt_config.yaml @@ -8,7 +8,7 @@ ignore_files: - "*.log" - "vendor/" -include_file_contents: false +include_file_contents: true debug: false improved_debug: false diff --git a/lua/chatgpt_nvim/config.lua b/lua/chatgpt_nvim/config.lua index 68e3559..8b9875a 100644 --- a/lua/chatgpt_nvim/config.lua +++ b/lua/chatgpt_nvim/config.lua @@ -87,6 +87,7 @@ function M.load() "No config file found (tried .chatgpt_config.yaml, chatgpt_config.yaml). Using defaults.", vim.log.levels.WARN ) + config.max_token = 2048 return config end @@ -120,7 +121,7 @@ function M.load() if type(result.initial_files) == "table" then config.initial_files = result.initial_files end - if type(result.include_file_contents) == "boolean" then -- LOAD NEW FLAG + if type(result.include_file_contents) == "boolean" then config.include_file_contents = result.include_file_contents end if type(result.preview_changes) == "boolean" then @@ -153,10 +154,17 @@ function M.load() end end end + + if type(result.max_token) == "number" then + config.max_token = result.max_token + else + config.max_token = 2048 + end end end else config.initial_prompt = "You are a coding assistant who receives a project's context and user instructions..." + config.max_token = 2048 end -- Merge default prompt blocks diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index f209443..a8a3a08 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -173,15 +173,20 @@ local function build_prompt(user_input, dirs, conf) table.insert(final_sections, table.concat(env_lines, "\n")) local final_prompt = table.concat(final_sections, "\n\n") - -- Replace placeholder "%PROJECT_NAME%" with the actual project name from configuration final_prompt = final_prompt:gsub("%%PROJECT_NAME%%", conf.project_name) return final_prompt end +-- New token estimation function. +local function estimate_token_count(text) + -- Use a simple heuristic: assume an average of 4 characters per token. + return math.floor(#text / 4) +end + local function handle_step_by_step_if_needed(prompt, conf) - local length = #prompt - local limit = conf.prompt_char_limit or 8000 - if (not conf.enable_step_by_step) or (length <= limit) then + local token_count = estimate_token_count(prompt) + local limit = conf.max_token or 2048 + if (not conf.enable_step_by_step) or (token_count <= limit) then return { prompt } end return { prompts["step-prompt"] } @@ -208,7 +213,6 @@ local function run_chatgpt_command() local bufnr = vim.api.nvim_create_buf(false, false) vim.api.nvim_buf_set_name(bufnr, "ChatGPT_Prompt.md") vim.api.nvim_buf_set_option(bufnr, "filetype", "markdown") - -- Set omnifunc for file name auto-completion vim.api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.chatgpt_file_complete") vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe") vim.api.nvim_buf_set_option(bufnr, "buftype", "") @@ -280,9 +284,7 @@ local function run_chatgpt_paste_command() return end - -- Check if we have tools if data.tools then - -- Must also verify project name if not data.project_name or data.project_name ~= conf.project_name then vim.api.nvim_err_writeln("Project name mismatch or missing. Aborting tool usage.") return @@ -294,7 +296,6 @@ local function run_chatgpt_paste_command() return end - -- If we see project_name & files => older YAML style. We handle it but it's discouraged now. if data.project_name and data.files then if data.project_name ~= conf.project_name then vim.api.nvim_err_writeln("Project name mismatch. Aborting.") @@ -361,7 +362,6 @@ local function run_chatgpt_paste_command() end end else - -- Not final => user is requesting more files local requested_paths = {} local root = vim.fn.getcwd() for _, fileinfo in ipairs(data.files) do @@ -459,7 +459,6 @@ M.run_chatgpt_command = run_chatgpt_command M.run_chatgpt_paste_command = run_chatgpt_paste_command M.run_chatgpt_current_buffer_command = run_chatgpt_current_buffer_command --- New: Global function for file name auto-completion in ChatGPT prompt function _G.chatgpt_file_complete(findstart, base) if findstart == 1 then local line = vim.fn.getline('.') From 1deae056b73a0f089f11d47c0aeed607ff85d974 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 13 Feb 2025 11:49:07 +0100 Subject: [PATCH 26/29] feat: change token calculation --- lua/chatgpt_nvim/init.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index a8a3a08..fcc3dfd 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -179,8 +179,16 @@ end -- New token estimation function. local function estimate_token_count(text) - -- Use a simple heuristic: assume an average of 4 characters per token. - return math.floor(#text / 4) + -- Instead of simply dividing the character count by 4, + -- split the text into non-whitespace chunks and further split each chunk + -- into sequences of alphanumeric characters and punctuation. + local token_count = 0 + for chunk in text:gmatch("%S+") do + for token in chunk:gmatch("(%w+|%p+)") do + token_count = token_count + 1 + end + end + return token_count end local function handle_step_by_step_if_needed(prompt, conf) From a5bad60d8eb9b64995dd908d5cad8a453a1bcd04 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 13 Feb 2025 11:54:27 +0100 Subject: [PATCH 27/29] feat: dont include chatgpt_config.yaml in file list --- lua/chatgpt_nvim/context.lua | 4 +++- lua/chatgpt_nvim/init.lua | 20 +++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lua/chatgpt_nvim/context.lua b/lua/chatgpt_nvim/context.lua index d0be140..edef33d 100644 --- a/lua/chatgpt_nvim/context.lua +++ b/lua/chatgpt_nvim/context.lua @@ -148,7 +148,9 @@ function M.get_project_files(directories, conf) local rel_files = {} for _, f in ipairs(all_files) do local rel = vim.fn.fnamemodify(f, ":.") - table.insert(rel_files, rel) + if not rel:match("^%.?chatgpt_config%.yaml$") then + table.insert(rel_files, rel) + end end if conf.debug then diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index fcc3dfd..81d7d63 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -122,15 +122,17 @@ local function build_prompt(user_input, dirs, conf) -- 4.1) Dynamic file inclusion via @ operator in user_input local dynamic_files = {} for file in user_input:gmatch("@([^%s]+)") do - local already_included = false - for _, existing in ipairs(initial_files) do - if existing == file then - already_included = true - break + if file ~= "chatgpt_config.yaml" and file ~= ".chatgpt_config.yaml" then + local already_included = false + for _, existing in ipairs(initial_files) do + if existing == file then + already_included = true + break + end + end + if not already_included then + table.insert(dynamic_files, file) end - end - if not already_included then - table.insert(dynamic_files, file) end end @@ -173,7 +175,7 @@ local function build_prompt(user_input, dirs, conf) table.insert(final_sections, table.concat(env_lines, "\n")) local final_prompt = table.concat(final_sections, "\n\n") - final_prompt = final_prompt:gsub("%%PROJECT_NAME%%", conf.project_name) + final_prompt = final_prompt:gsub("%chatgpt.vim%", conf.project_name) return final_prompt end From 32fcc2622fa20268190ea89e36b918e30fba2864 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 14 Feb 2025 15:26:28 +0100 Subject: [PATCH 28/29] feat: remove old prompt_chat_limit --- chatgpt_config.yaml | 4 ---- lua/chatgpt_nvim/config.lua | 6 ++---- lua/chatgpt_nvim/context.lua | 8 +------- lua/chatgpt_nvim/init.lua | 26 -------------------------- 4 files changed, 3 insertions(+), 41 deletions(-) diff --git a/chatgpt_config.yaml b/chatgpt_config.yaml index f696dee..02a6832 100644 --- a/chatgpt_config.yaml +++ b/chatgpt_config.yaml @@ -18,16 +18,12 @@ interactive_file_selection: false partial_acceptance: false enable_debug_commands: true -prompt_char_limit: 300000 enable_chunking: false enable_step_by_step: true auto_lint: true -# New tool auto-accept config tool_auto_accept: read_file: true edit_file: true replace_in_file: true execute_command: false - # If you set any of these to true, it will auto accept them without prompting. - # 'executeCommand' should remain false by default unless you're certain it's safe. diff --git a/lua/chatgpt_nvim/config.lua b/lua/chatgpt_nvim/config.lua index 8b9875a..2634478 100644 --- a/lua/chatgpt_nvim/config.lua +++ b/lua/chatgpt_nvim/config.lua @@ -57,7 +57,7 @@ function M.load() initial_prompt = "", directories = { "." }, default_prompt_blocks = {}, - prompt_char_limit = 300000, + -- Removed prompt_char_limit project_name = "", debug = false, initial_files = {}, @@ -109,9 +109,7 @@ function M.load() if type(result.default_prompt_blocks) == "table" then config.default_prompt_blocks = result.default_prompt_blocks end - if type(result.prompt_char_limit) == "number" then - config.prompt_char_limit = result.prompt_char_limit - end + -- Removed prompt_char_limit handling if type(result.project_name) == "string" then config.project_name = result.project_name end diff --git a/lua/chatgpt_nvim/context.lua b/lua/chatgpt_nvim/context.lua index edef33d..63ea354 100644 --- a/lua/chatgpt_nvim/context.lua +++ b/lua/chatgpt_nvim/context.lua @@ -198,13 +198,7 @@ function M.get_project_prompt(directories, conf) if conf.include_file_contents then local files = M.get_project_files(directories, conf) local contents = M.get_file_contents(files, conf) - local total_chars = #contents - if total_chars > conf.prompt_char_limit then - vim.notify("Total file contents (" .. total_chars .. " characters) exceed the prompt limit (" .. conf.prompt_char_limit .. "). Please disable 'include_file_contents' in your config.", vim.log.levels.ERROR) - return structure - else - return structure .. "\n" .. contents - end + return structure .. "\n" .. contents else return structure end diff --git a/lua/chatgpt_nvim/init.lua b/lua/chatgpt_nvim/init.lua index 81d7d63..7ec656f 100644 --- a/lua/chatgpt_nvim/init.lua +++ b/lua/chatgpt_nvim/init.lua @@ -179,11 +179,7 @@ local function build_prompt(user_input, dirs, conf) return final_prompt end --- New token estimation function. local function estimate_token_count(text) - -- Instead of simply dividing the character count by 4, - -- split the text into non-whitespace chunks and further split each chunk - -- into sequences of alphanumeric characters and punctuation. local token_count = 0 for chunk in text:gmatch("%S+") do for token in chunk:gmatch("(%w+|%p+)") do @@ -202,9 +198,6 @@ local function handle_step_by_step_if_needed(prompt, conf) return { prompts["step-prompt"] } end ------------------------------------------------------------------------------- --- :ChatGPT ------------------------------------------------------------------------------- local function run_chatgpt_command() package.loaded["chatgpt_nvim.config"] = nil local config = require("chatgpt_nvim.config") @@ -273,9 +266,6 @@ local function run_chatgpt_command() vim.cmd("buffer " .. bufnr) end ------------------------------------------------------------------------------- --- :ChatGPTPaste ------------------------------------------------------------------------------- local function run_chatgpt_paste_command() package.loaded["chatgpt_nvim.config"] = nil local config = require("chatgpt_nvim.config") @@ -403,16 +393,6 @@ local function run_chatgpt_paste_command() local length = #prompt ui.debug_log("Returning requested files. Character count: " .. length) - if length > (conf.prompt_char_limit or 8000) and conf.enable_step_by_step then - local large_step = prompts["step-prompt"] - copy_to_clipboard(large_step) - print("Step-by-step guidance copied to clipboard!") - return - elseif length > (conf.prompt_char_limit or 8000) then - vim.api.nvim_err_writeln("Requested files exceed prompt character limit. No step-by-step support enabled.") - return - end - copy_to_clipboard(prompt) print("Prompt (with requested files) copied to clipboard! Paste it into ChatGPT.") end @@ -421,9 +401,6 @@ local function run_chatgpt_paste_command() end end ------------------------------------------------------------------------------- --- :ChatGPTCurrentBuffer ------------------------------------------------------------------------------- local function run_chatgpt_current_buffer_command() package.loaded["chatgpt_nvim.config"] = nil local config = require("chatgpt_nvim.config") @@ -462,9 +439,6 @@ local function run_chatgpt_current_buffer_command() end end ------------------------------------------------------------------------------- --- PUBLIC API ------------------------------------------------------------------------------- M.run_chatgpt_command = run_chatgpt_command M.run_chatgpt_paste_command = run_chatgpt_paste_command M.run_chatgpt_current_buffer_command = run_chatgpt_current_buffer_command From 7fb8671840181ca5166fcf7b8a8d5f5e10a5efa7 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Fri, 14 Feb 2025 19:18:51 +0100 Subject: [PATCH 29/29] fix: add ignore_files to config --- lua/chatgpt_nvim/config.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lua/chatgpt_nvim/config.lua b/lua/chatgpt_nvim/config.lua index 2634478..7941bad 100644 --- a/lua/chatgpt_nvim/config.lua +++ b/lua/chatgpt_nvim/config.lua @@ -109,7 +109,6 @@ function M.load() if type(result.default_prompt_blocks) == "table" then config.default_prompt_blocks = result.default_prompt_blocks end - -- Removed prompt_char_limit handling if type(result.project_name) == "string" then config.project_name = result.project_name end @@ -122,6 +121,9 @@ function M.load() if type(result.include_file_contents) == "boolean" then config.include_file_contents = result.include_file_contents end + if type(result.ignore_files) == "table" then + config.ignore_files = result.ignore_files + end if type(result.preview_changes) == "boolean" then config.preview_changes = result.preview_changes end