Skip to content

Commit 658a62c

Browse files
authored
docs(spec): add English prompt construction specification (#5)
* docs(spec): add English prompt construction specification - create prompt_construction_en.md with universal tool description pattern - define three-part structure: function description, when to use, best practices - provide detailed requirements, examples, and quality check standards - include applicable formats in markdown, plain text, and JSON * feat(prompts): standardize and enhance system prompts - update generator prompt with usage, best practices, and format - expand git_bot system prompt for comprehensive guidance - rewrite git_edit and git_read prompts with clear usage and best practices - list available operations explicitly in tool prompts * docs: add extension development guide - create EXTENSION_DEVELOPMENT_GUIDE.md with comprehensive instructions - cover extension architecture, structure, feature integration, and examples - include best practices, testing, and API reference for developers * 修复和优化:更新多个 Git 相关扩展文件,提升功能稳定性和代码一致性 * 提交所有修改 * 修复和更新代码:提交所有当前更改 * fix(git): 修正 commit_message 字段描述文本截断问题 * refactor(gitcommit): 优化 slash_commands 相关实现 - 移除未使用的 Job 依赖 - 调整相关函数结构,提升可读性和维护性 * fix(git): 修复 cherry-pick 命令参数拼接方式 * refactor(git): 优化 push 参数处理逻辑 - 简化 force、remote、branch、tags、tag_name 参数插入流程 - 提高代码可读性和维护性 * docs(git): update push function docstring - Remove 'asynchronously' from push function comment - Clarify documentation for push operation * fix(git_edit): 修正无提交信息时的提示文本 - 将提示内容从分析diff生成信息改为引导用户使用 @git_read diff --staged 工具查看变更并生成提交信息 - 优化了用户交互流程 * refactor(git_edit): 移除 rebase 相关代码和 schema 配置 - 删除 rebase 相关的 schema 字段和描述 - 移除 rebase 操作的实现逻辑 - 更新可用操作列表,去除 rebase * docs(readme): add GEMINI.md with project overview and usage - Introduce project features, installation, and usage instructions - Document configuration options and safety features
1 parent c2f0fb2 commit 658a62c

File tree

11 files changed

+1445
-120
lines changed

11 files changed

+1445
-120
lines changed

EXTENSION_DEVELOPMENT_GUIDE.md

Lines changed: 940 additions & 0 deletions
Large diffs are not rendered by default.

GEMINI.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Project Overview: CodeCompanion Git Commit Extension
2+
3+
This project is a CodeCompanion extension designed to generate AI-powered Git commit messages following the Conventional Commits specification. It integrates powerful Git tools for enhanced workflow.
4+
5+
## Key Features:
6+
7+
- **AI Commit Generation**: Generates Conventional Commits compliant messages using CodeCompanion's LLM adapters.
8+
- **Git Tool Integration**: Allows execution of Git operations via `@git_read` (read-only) and `@git_edit` (write) tools within chat.
9+
- **Multi-language Support**: Supports generating commit messages in multiple languages (e.g., English, Chinese).
10+
- **Smart Buffer Integration**: Auto-generates commit messages in gitcommit buffers.
11+
- **File Filtering**: Supports glob patterns to exclude generated files from analysis.
12+
- **Natural Language Interface**: Enables controlling Git workflows through conversational commands.
13+
14+
## Installation:
15+
16+
Add this extension to your CodeCompanion configuration in `init.lua` or similar:
17+
18+
```lua
19+
require("codecompanion").setup({
20+
extensions = {
21+
gitcommit = {
22+
callback = "codecompanion._extensions.gitcommit",
23+
opts = {
24+
adapter = "openai",
25+
model = "gpt-4",
26+
languages = { "English", "Chinese" },
27+
exclude_files = { "*.pb.go", "*.min.js", "package-lock.json", "dist/*", "build/*", "node_modules/*" },
28+
buffer = {
29+
enabled = true,
30+
keymap = "<leader>gc",
31+
auto_generate = true,
32+
auto_generate_delay = 100,
33+
},
34+
add_slash_command = true,
35+
add_git_tool = true,
36+
add_git_commands = true,
37+
}
38+
}
39+
}
40+
})
41+
```
42+
43+
## Usage:
44+
45+
### Commands:
46+
47+
- `:CodeCompanionGitCommit` or `:CCGitCommit`: Generate Git commit message.
48+
- `:CodeCompanionGit` or `:CCGit`: Open Git assistant chat.
49+
50+
### Git Tool Operations in Chat:
51+
52+
- **Read-only (`@git_read`)**: e.g., `@git_read status`, `@git_read log --count 5`
53+
- **Write (`@git_edit`)**: e.g., `@git_edit stage --files ["src/main.lua", "README.md"]`, `@git_edit create_branch --branch_name "feature/new-ui"`
54+
55+
### Workflow Examples:
56+
57+
- **Quick commit**: Check status, stage files, then generate commit message.
58+
- **Branch management**: List branches, create new branch, make changes, stage files, then generate commit message.
59+
60+
## Configuration Options:
61+
62+
Key configurable options include LLM adapter and model, supported languages, file exclusion patterns, buffer integration settings (enable, keymap, auto-generate), and toggles for slash commands, Git tools, and Git commands.
63+
64+
## Safety Features:
65+
66+
- Read-only operations (`@git_read`) require no confirmation.
67+
- Modifying operations (`@git_edit`) require user confirmation.
68+
- Repository validation ensures operations in valid Git repositories.
69+
- Comprehensive error handling.
70+
71+
## License:
72+
73+
MIT License

lua/codecompanion/_extensions/gitcommit/buffer.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ function Buffer._insert_commit_message(bufnr, message)
187187
-- Move cursor to the beginning of the commit message
188188
vim.api.nvim_win_set_cursor(0, { 1, 0 })
189189

190-
vim.notify("Commit message generated and inserted!", vim.log.levels.INFO)
190+
vim.notify("Commit message generated and inserted!", vim.log.levels.INFO)
191191
end
192192

193193
---Get current configuration

lua/codecompanion/_extensions/gitcommit/generator.lua

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ local codecompanion_schema = require("codecompanion.schema")
77
local Generator = {}
88

99
--- @type string?
10-
local _adapater = nil
10+
local _adapter = nil
1111
--- @type string?
1212
local _model = nil
1313

@@ -19,12 +19,12 @@ local CONSTANTS = {
1919
--- @param adapter string? The adapter to use for generation
2020
--- @param model string? The model of the adapter to use for generation
2121
function Generator.setup(adapter, model)
22-
_adapater = adapter or codecompanion_config.strategies.chat.adapter
22+
_adapter = adapter or codecompanion_config.strategies.chat.adapter
2323
_model = model or codecompanion_config.strategies.chat.model
2424

2525
-- Validate adapter
26-
if not codecompanion_adapter.resolve(_adapater) then
27-
error("Invalid adapter specified: " .. tostring(_adapater))
26+
if not codecompanion_adapter.resolve(_adapter) then
27+
error("Invalid adapter specified: " .. tostring(_adapter))
2828
end
2929
end
3030

@@ -35,7 +35,7 @@ end
3535
---@param callback fun(result: string|nil, error: string|nil) Callback function
3636
function Generator.generate_commit_message(diff, lang, callback)
3737
-- Setup adapter
38-
local adapter = codecompanion_adapter.resolve(_adapater)
38+
local adapter = codecompanion_adapter.resolve(_adapter)
3939
if not adapter then
4040
return callback(nil, "Failed to resolve adapter")
4141
end
@@ -73,32 +73,34 @@ end
7373
---@return string prompt The formatted prompt
7474
function Generator._create_prompt(diff, lang)
7575
return string.format(
76-
[[You are an expert at following the Conventional Commit specification.
76+
[[Generate Conventional Commit compliant messages
7777
78-
Please only return a commit message that strictly follows the Conventional Commit specification, without any additional text or explanations. The commit message should include:
78+
When to use:
79+
• When analyzing git diffs for commit messages
80+
• When standardizing commit format across projects
81+
• When ensuring consistent commit message patterns
82+
• When generating structured commit documentation
7983
80-
1. Type (required): lowercase, e.g., feat, fix, docs, style, refactor, perf, test, chore
81-
2. Scope (optional): in parentheses after type, e.g., feat(parser)
82-
3. Description (required): space after colon, start with verb, be concise
83-
4. Body (optional): use bullet points (-) to list specific changes
84+
Best practices:
85+
• Must include required type (feat, fix, docs, style, refactor, perf, test, chore)
86+
• Use lowercase for type, optional scope in parentheses
87+
• Start description with imperative verb, keep under 50 characters
88+
• Add body with bullet points for complex changes
89+
• Ensure language matches specification: %s
8490
85-
Example format:
91+
Format: type(scope): description
8692
87-
feat(scope): add new feature
93+
Example:
94+
feat(auth): add OAuth2 integration
8895
89-
- implement X functionality
90-
- update Y module
91-
- add tests for Z
92-
93-
Note: You need to answer in %s.
94-
95-
Based on the git diff provided below, generate a standardized commit message.
96+
- implement Google OAuth provider
97+
- update user authentication flow
98+
- add integration tests
9699
100+
Generate commit message for this diff:
97101
```diff
98102
%s
99-
```
100-
101-
]],
103+
```]],
102104
lang or "English",
103105
diff
104106
)
@@ -111,10 +113,15 @@ end
111113
---@param callback fun(result: string|nil, error: string|nil) Callback function
112114
function Generator._handle_response(err, data, _adapter, callback)
113115
-- Handle request errors
114-
if err and err.stderr ~= "{}" then
115-
local error_msg = "Error generating commit message: " .. (err.stderr or "Unknown error")
116+
if err then
117+
local error_msg = "Error generating commit message: " .. (err.stderr or err.message or "Unknown error")
116118
return callback(nil, error_msg)
117119
end
120+
121+
-- Check for empty or invalid data
122+
if not data then
123+
return callback(nil, "No response received from LLM")
124+
end
118125

119126
-- Process successful response
120127
if data then

lua/codecompanion/_extensions/gitcommit/git.lua

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---@class CodeCompanion.GitCommit.Git
22
local Git = {}
33

4-
-- 存储配置
4+
-- Store configuration
55
local config = {}
66

77
---Setup Git module with configuration
@@ -201,7 +201,7 @@ function Git.get_contextual_diff()
201201
end
202202
end
203203

204-
-- 3. Fallback: If no staged changes and not amending, check for all local changes (working directory vs. HEAD)
204+
-- Fallback: If no staged changes and not amending, check for all local changes (working directory vs. HEAD)
205205
local all_local_diff = vim.fn.system("git diff --no-ext-diff HEAD")
206206
if vim.v.shell_error == 0 and vim.trim(all_local_diff) ~= "" then
207207
local filtered_diff = Git._filter_diff(all_local_diff)
@@ -240,7 +240,7 @@ function Git.commit_changes(message)
240240
return false
241241
end
242242

243-
-- 直接通过 stdin 传递 commit message,无需临时文件
243+
-- Pass commit message directly through stdin without temporary files
244244
local cmd
245245
if Git.is_amending() then
246246
cmd = "git commit --amend -F -"

lua/codecompanion/_extensions/gitcommit/init.lua

Lines changed: 84 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,19 @@ local function setup_tools(opts)
9292
chat_tools.groups = chat_tools.groups or {}
9393
chat_tools.groups["git_bot"] = {
9494
description = "A Git agent that can perform read and write operations.",
95-
system_prompt = "You are a Git assistant. You have access to the `git_read` and `git_edit` tools to manage the git repository.",
95+
system_prompt = [[Provide Git repository assistance and management
96+
97+
When to use:
98+
• When users need comprehensive Git operations
99+
• When combining read and write Git operations
100+
• When providing guided Git workflow assistance
101+
• When troubleshooting Git repository issues
102+
103+
Best practices:
104+
• Use git_read tool for repository analysis first
105+
• Ensure operations are safe before execution
106+
• Avoid destructive operations without user confirmation
107+
• Provide clear explanations for Git commands]],
96108
tools = {
97109
"git_read",
98110
"git_edit",
@@ -127,30 +139,44 @@ local function setup_slash_commands(opts)
127139
end
128140

129141
local slash_commands = require("codecompanion.config").strategies.chat.slash_commands
130-
local Job = require("plenary.job")
131142

132143
local function get_commit_content(chat, choice)
133-
Job:new({
134-
command = "git",
144+
local stdout = vim.uv.new_pipe(false)
145+
local stderr = vim.uv.new_pipe(false)
146+
local output = ""
147+
local error_output = ""
148+
149+
local handle = vim.uv.spawn("git", {
135150
args = { "show", choice.hash },
136-
on_exit = function(j, rv)
137-
local content = table.concat(j:result(), "\n")
138-
vim.schedule(function()
139-
if rv ~= 0 or not content or content == "" then
140-
chat:add_reference(
141-
{ role = "user", content = "Error: Failed to get commit content." },
142-
"git",
143-
"<git_error>"
144-
)
145-
else
146-
chat:add_reference({
147-
role = "user",
148-
content = string.format("Selected commit (%s) full content:\n```\n%s\n```", choice.hash, content),
149-
}, "git", "<git_commit>")
150-
end
151-
end)
152-
end,
153-
}):start()
151+
stdio = { nil, stdout, stderr },
152+
}, function(code, signal)
153+
vim.schedule(function()
154+
stdout:close()
155+
stderr:close()
156+
if code == 0 then
157+
chat:add_reference({
158+
role = "user",
159+
content = string.format("Selected commit (%s) full content:\n```\n%s\n```", choice.hash, output),
160+
}, "git", "<git_commit>")
161+
else
162+
chat:add_reference(
163+
{ role = "user", content = "Error: Failed to get commit content.\n" .. error_output },
164+
"git",
165+
"<git_error>"
166+
)
167+
end
168+
end)
169+
end)
170+
171+
vim.uv.read_start(stdout, function(err, data)
172+
if err then return end
173+
if data then output = output .. data end
174+
end)
175+
176+
vim.uv.read_start(stderr, function(err, data)
177+
if err then return end
178+
if data then error_output = error_output .. data end
179+
end)
154180
end
155181

156182
local function select_commit(chat, items)
@@ -167,20 +193,22 @@ local function setup_slash_commands(opts)
167193
end
168194

169195
local function get_commit_list(chat, opts)
170-
Job:new({
171-
command = "git",
196+
local stdout = vim.uv.new_pipe(false)
197+
local stderr = vim.uv.new_pipe(false)
198+
local output = ""
199+
local error_output = ""
200+
201+
local handle = vim.uv.spawn("git", {
172202
args = { "log", "--oneline", "-n", tostring(opts.gitcommit_select_count) },
173-
on_exit = function(j, rv)
174-
local output = j:result()
175-
vim.schedule(function()
176-
if rv ~= 0 then
177-
return chat:add_reference({ role = "user", content = "Error: Failed to get git log" }, "git", "<git_error>")
178-
end
179-
if not output or #output == 0 then
180-
return chat:add_reference({ role = "user", content = "No commits found." }, "git", "<git_error>")
181-
end
203+
stdio = { nil, stdout, stderr },
204+
}, function(code, signal)
205+
vim.schedule(function()
206+
stdout:close()
207+
stderr:close()
208+
if code == 0 then
209+
local lines = vim.split(output, "\n")
182210
local items = {}
183-
for _, line in ipairs(output) do
211+
for _, line in ipairs(lines) do
184212
local hash, msg = line:match("^(%w+)%s(.+)$")
185213
if hash and msg then
186214
table.insert(items, { label = hash .. " " .. msg, hash = hash })
@@ -190,9 +218,21 @@ local function setup_slash_commands(opts)
190218
return chat:add_reference({ role = "user", content = "No commits found." }, "git", "<git_error>")
191219
end
192220
select_commit(chat, items)
193-
end)
194-
end,
195-
}):start()
221+
else
222+
chat:add_reference({ role = "user", content = "Error: Failed to get git log\n" .. error_output }, "git", "<git_error>")
223+
end
224+
end)
225+
end)
226+
227+
vim.uv.read_start(stdout, function(err, data)
228+
if err then return end
229+
if data then output = output .. data end
230+
end)
231+
232+
vim.uv.read_start(stderr, function(err, data)
233+
if err then return end
234+
if data then error_output = error_output .. data end
235+
end)
196236
end
197237

198238
slash_commands["gitcommit"] = {
@@ -396,6 +436,13 @@ return {
396436
local GitTool = require("codecompanion._extensions.gitcommit.tools.git").GitTool
397437
return GitTool.search_commits(pattern, count)
398438
end,
439+
440+
---Merge a branch
441+
---@param branch string The branch to merge
442+
merge = function(branch)
443+
local GitTool = require("codecompanion._extensions.gitcommit.tools.git").GitTool
444+
return GitTool.merge(branch)
445+
end,
399446
},
400447
},
401448
}

0 commit comments

Comments
 (0)