Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions docs/designs/2025-12-20-slash-commit-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Slash Commit Command

**Date:** 2025-12-20

## Context

用户期望在 `src/slash-commands/builtin/` 下新增一个 `/commit` 内置命令,复用 `src/nodeBridge.ts` 中的 commit message 生成提示词。核心需求是将该提示词抽取为独立函数,支持传入 `language` 参数,使其可复用。

## Discussion

### 命令类型选择
- **prompt 类型**: 类似 `/review`,生成提示词让 AI 执行 commit 流程 ✅ 用户选择
- **local-jsx 类型**: 复用现有 CommitUI 组件,在聊天中渲染交互式界面
- **local 类型**: 直接调用 nodeBridge 生成 commit message 并返回文本结果

用户选择 prompt 类型,因为它更轻量且符合 slash command 的交互模式。

### 提示词函数位置
- **新建 prompts 目录**: 抽取函数到独立目录如 `src/prompts/generateCommit.ts`
- **utils 目录**: 放在 `src/utils/` 下作为工具函数 ✅ 用户选择

### 用户交互
用户期望 AI 使用 `AskUserQuestion` 工具提供多个操作选项,类似 `neo commit` CLI 命令的交互体验:
- Commit - 提交更改
- Commit & Push - 提交并推送
- Create Branch & Commit - 创建新分支并提交

## Approach

采用简洁函数式抽取方案:
1. 将 `createGenerateCommitSystemPrompt` 抽取到 `src/utils/commitPrompt.ts`
2. 函数接受 `language: string` 参数
3. `nodeBridge.ts` 改用新函数,保持向后兼容
4. 新建 `/commit` 命令作为 prompt 类型,引导 AI 使用 `AskUserQuestion` 提供操作选项

## Architecture

### 文件结构
```
src/utils/commitPrompt.ts # 新建 - 抽取的 prompt 生成函数
src/slash-commands/builtin/commit.ts # 新建 - /commit 命令
src/slash-commands/builtin/index.ts # 修改 - 注册命令
src/nodeBridge.ts # 修改 - 改用新函数
```

### 核心接口
```typescript
// src/utils/commitPrompt.ts
export function createGenerateCommitSystemPrompt(language: string): string
```

### /commit 命令流程
1. 检查是否有 staged changes
2. 获取 staged diff(排除 lock 文件)
3. 使用 `createGenerateCommitSystemPrompt(language)` 生成系统提示词
4. AI 分析 diff 并生成 commit 信息
5. 展示 commitMessage、branchName、summary、isBreakingChange
6. 使用 `AskUserQuestion` 让用户选择操作
7. 执行对应的 git 命令
49 changes: 1 addition & 48 deletions src/nodeBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { query } from './query';
import { SessionConfigManager } from './session';
import { SlashCommandManager } from './slashCommand';
import type { ApprovalCategory, ToolUse } from './tool';
import { createGenerateCommitSystemPrompt } from './utils/commitPrompt';
import { getFiles } from './utils/files';
import { listDirectory } from './utils/list';
import { randomUUID } from './utils/randomUUID';
Expand Down Expand Up @@ -2405,51 +2406,3 @@ function normalizeProviders(providers: ProvidersMap, context: Context) {
},
);
}

function createGenerateCommitSystemPrompt(language: string) {
const { isEnglish } = require('./utils/language');
const useEnglish = isEnglish(language);
const descriptionLang = useEnglish
? ''
: `\n - Use ${language} for the description`;
const summaryLang = useEnglish ? '' : `\n - Use ${language}`;
return `
You are an expert software engineer that generates Git commit information based on provided diffs.

Review the provided context and diffs which are about to be committed to a git repo.
Analyze the changes carefully and generate a JSON response with the following fields:

1. **commitMessage**: A one-line commit message following conventional commit format
- Format: <type>: <description>
- Types: fix, feat, build, chore, ci, docs, style, refactor, perf, test${descriptionLang}
- Use imperative mood (e.g., "add feature" not "added feature")
- Do not exceed 72 characters
- Do not capitalize the first letter
- Do not end with a period

2. **branchName**: A suggested Git branch name
- Format: <type>/<description> for conventional commits, or <description> for regular changes
- Use only lowercase letters, numbers, and hyphens
- Maximum 50 characters
- No leading or trailing hyphens

3. **isBreakingChange**: Boolean indicating if this is a breaking change
- Set to true if the changes break backward compatibility
- Look for removed public APIs, changed function signatures, etc.

4. **summary**: A brief 1-2 sentence summary of the changes${summaryLang}
- Describe what was changed and why

## Response Format

Respond with valid JSON only, no additional text or markdown formatting.

Example response:
{
"commitMessage": "feat: add user authentication system",
"branchName": "feat/add-user-authentication",
"isBreakingChange": false,
"summary": "Added JWT-based authentication with login and logout endpoints."
}
`.trim();
}
71 changes: 71 additions & 0 deletions src/slash-commands/builtin/commit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { createGenerateCommitSystemPrompt } from '../../utils/commitPrompt';
import { TOOL_NAMES } from '../../constants';
import { isEnglish } from '../../utils/language';
import type { PromptCommand } from '../types';

export function createCommitCommand(language: string): PromptCommand {
const useEnglish = isEnglish(language);
const lang = useEnglish ? '' : ` Communicate in ${language}.`;

return {
type: 'prompt',
name: 'commit',
description: 'Generate commit message for staged changes',
progressMessage: 'Generating commit message...',
async getPromptForCommand(_args?: string) {
const systemPrompt = createGenerateCommitSystemPrompt(language);
const lockFiles = [
'pnpm-lock.yaml',
'package-lock.json',
'yarn.lock',
'bun.lockb',
'Gemfile.lock',
'Cargo.lock',
];
const lockFilesPattern = lockFiles.map((file) => `':!${file}'`).join(' ');

return [
{
role: 'user',
content: `You are a Git commit assistant.${lang}

## System Prompt for Commit Generation
${systemPrompt}

## Instructions

Follow these steps:

1. First, check if there are staged changes using bash("git diff --cached --stat")
- If no staged changes, inform the user and ask if they want to stage all changes using bash("git add -A")

2. Get the staged diff using bash("git --no-pager diff --cached -- . ${lockFilesPattern}")

3. Analyze the diff and generate commit information (commitMessage, branchName, isBreakingChange, summary)

4. Present the generated information to the user in a clear format:
- Commit Message: <the generated message>
- Branch Name: <suggested branch name>
- Summary: <brief summary>
- Breaking Change: Yes/No

5. Use ${TOOL_NAMES.ASK_USER_QUESTION} tool to let the user choose an action with these options:
- "Commit" - Commit with the generated message
- "Commit & Push" - Commit and push to remote
- "Create Branch & Commit" - Create new branch with suggested name, then commit

6. Based on user's choice, execute the corresponding git commands:
- Commit: bash("git commit -m '<commitMessage>'")
- Push: bash("git push origin '<branchName>'")
- Create Branch: bash("git checkout -b '<branchName>'")

7. If the user wants to modify the commit message or branch name before executing, allow them to provide a new value.

8. Report the result of each operation to the user.

Note: If any git command fails, explain the error and suggest possible solutions.`,
},
];
},
};
}
2 changes: 2 additions & 0 deletions src/slash-commands/builtin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { SlashCommand } from '../types';
import { createAddDirCommand } from './add-dir';
import { createBugCommand } from './bug';
import { clearCommand } from './clear';
import { createCommitCommand } from './commit';
import { compactCommand } from './compact';
import { contextCommand } from './context';
import { exitCommand } from './exit';
Expand Down Expand Up @@ -40,6 +41,7 @@ export function createBuiltinCommands(opts: {
createOutputStyleCommand(),
createResumeCommand(),
createReviewCommand(opts.language),
createCommitCommand(opts.language),
createTerminalSetupCommand(),
createBugCommand(),
compactCommand,
Expand Down
49 changes: 49 additions & 0 deletions src/utils/commitPrompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { isEnglish } from './language';

export function createGenerateCommitSystemPrompt(language: string): string {
const useEnglish = isEnglish(language);
const descriptionLang = useEnglish
? ''
: `\n - Use ${language} for the description`;
const summaryLang = useEnglish ? '' : `\n - Use ${language}`;

return `
You are an expert software engineer that generates Git commit information based on provided diffs.

Review the provided context and diffs which are about to be committed to a git repo.
Analyze the changes carefully and generate a JSON response with the following fields:

1. **commitMessage**: A one-line commit message following conventional commit format
- Format: <type>: <description>
- Types: fix, feat, build, chore, ci, docs, style, refactor, perf, test${descriptionLang}
- Use imperative mood (e.g., "add feature" not "added feature")
- Do not exceed 72 characters
- Do not capitalize the first letter
- Do not end with a period

2. **branchName**: A suggested Git branch name
- Format: <type>/<description> for conventional commits, or <description> for regular changes
- Use only lowercase letters, numbers, and hyphens
- Maximum 50 characters
- No leading or trailing hyphens

3. **isBreakingChange**: Boolean indicating if this is a breaking change
- Set to true if the changes break backward compatibility
- Look for removed public APIs, changed function signatures, etc.

4. **summary**: A brief 1-2 sentence summary of the changes${summaryLang}
- Describe what was changed and why

## Response Format

Respond with valid JSON only, no additional text or markdown formatting.

Example response:
{
"commitMessage": "feat: add user authentication system",
"branchName": "feat/add-user-authentication",
"isBreakingChange": false,
"summary": "Added JWT-based authentication with login and logout endpoints."
}
`.trim();
}
Loading