feat: AI 修复链路扩展 + 修复预览两步确认 + 自定义规则/AI 审查条目支持 AI 修复
- AI 修复:无原生 fix 的 linter 条目走 AI 修复(aiFixEngine/fixPrompt,AI 重检收敛),自定义规则与 AI 审查条目新增 AI 修复按钮(customFixEngine),自定义规则条目展开显示 AI 建议(suggestion 字段)
- 修复预览:面板触发修复先用内置 diff 预览,面板内「应用/取消」两步确认后才写入(fixPreview/fixPending,单条与分 tab 批量均支持)
- 修复面板交互:linter/custom/ai 分 tab「全部修复」、已修复+撤销、pending 按钮状态同步
- bug 修复:修复按钮失败后卡 ⏳ 不恢复;custom/ai 修复不稳定(空修复重试、AI 重检收敛判定放宽、降级接受最后一次有效修复)
- 移除 AI codeDiff 展示块
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
# 修复前 Diff 预览确认功能设计书
|
||||
|
||||
> 日期:2026-08-19
|
||||
> 状态:已批准(Stage ④ 通过);2026-08-19 修订确认交互为「面板内两步确认」
|
||||
> 流程:① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
当前自动修复(`src/fix/`)在用户点击「修复 / AI 修复 / 全部修复」后**立即写盘**,用户无法在写入前确认改动内容,误修后只能依赖撤销功能回退。
|
||||
|
||||
本次功能目标:在面板触发的修复**应用前**,用 VSCode 内置 diff 编辑器展示「修改前 → 修改后」的代码对比,用户确认后才真正写入。
|
||||
|
||||
## 2. 需求澄清结论(Stage ② 共识)
|
||||
|
||||
| 决策项 | 结论 |
|
||||
|--------|------|
|
||||
| 触发时机 | 修复**前**预览确认,确认后才写入 |
|
||||
| 展示方式 | VSCode 内置 diff 编辑器(`vscode.diff`,左旧右新) |
|
||||
| 预览范围 | 单个修复 + 全部修复都走预览 |
|
||||
| 全部修复 | 合并成一个 diff,确认后一次性应用 |
|
||||
| AI 修复 | 原生 fix 与 AI fix 两条路径都预览 |
|
||||
| 入口范围 | **仅面板按钮**预览;hover 快速修复保持直接应用(零行为变化) |
|
||||
| 确认交互 | **面板内两步确认**:点「修复」→ 打开 diff + 面板按钮变「应用/取消」(常驻不消失);回面板点「应用」才写入。「全部修复」同理(「全部应用/取消」) |
|
||||
|
||||
> 修订说明:初版设计为 diff + `showInformationMessage` 确认对话框,实测发现非模态通知几秒后自动消失(返回值 undefined 被当取消并关闭 diff)。改为面板内两步确认——按钮翻转用 webview 轻量消息完成,不全量重建面板,滚动/标签页不丢失。
|
||||
|
||||
## 3. 现状关键点(已核实)
|
||||
|
||||
- `fixEngine.ts:123-129` 与 `aiFixEngine.ts:164-170`:多轮循环在**内存字符串**上计算最终 `currentText`,最后一步才用整文档 `WorkspaceEdit` 写入。
|
||||
- 计算与写入天然可分,只需让引擎支持"只算不写"(`dryRun`)即可在写入前弹 diff。
|
||||
- 引擎内部已用 `mockDocument` 对内存文本执行 `adapter.check`,支持对累积文本反复计算,无需改核心算法。
|
||||
|
||||
## 4. 架构概览
|
||||
|
||||
```
|
||||
面板 fix/fixAll 消息 → codeReviewer.fixIssue / codeReviewer.fixAll
|
||||
│ (origin === 'panel')
|
||||
▼
|
||||
FixEngine/AiFixEngine (dryRun=true) → 返回最终 newText(不写盘)
|
||||
│
|
||||
▼
|
||||
fixPreview.openPreviewDiff() → vscode.diff 内置 diff 编辑器(左原文本 / 右新文本)
|
||||
│
|
||||
▼
|
||||
FixPendingStore.setSingle/setBatch + ReviewPanel.postMessage('pending'/'batchPending')
|
||||
│ 面板条目按钮翻转:「修复」→「应用 / 取消」
|
||||
▼
|
||||
codeReviewer.applyFixPreview / applyAllPreview ← 面板「应用」按钮
|
||||
│
|
||||
▼
|
||||
fixPreview.applyNewText() → 整文档 WorkspaceEdit 一次性写入 + recordFixes + save + refresh
|
||||
codeReviewer.cancelFixPreview / cancelAllPreview → 关闭 diff + 还原按钮(不写入)
|
||||
```
|
||||
|
||||
**数据流**:
|
||||
|
||||
1. `fixIssue`(panel)/ `fixAll` 以 `dryRun=true` 调用修复引擎,拿到最终 `newText`,**不写盘**。
|
||||
2. `openPreviewDiff` 用 `TextDocumentContentProvider`(scheme `codeReviewerPreview`)提供新文本,`vscode.diff` 打开内置 diff 编辑器。
|
||||
3. 结果存入 `FixPendingStore`,向面板 `postMessage` 翻转按钮(`pending` 单条 / `batchPending` 全部)——「修复」隐藏、「应用/取消」显示,**常驻不消失**。
|
||||
4. 用户回面板点「应用」→ `applyFixPreview`/`applyAllPreview` 整文档 `WorkspaceEdit` 写入、记录撤销会话、保存、刷新;点「取消」→ `cancelFixPreview`/`cancelAllPreview` 关闭 diff、还原按钮、不写入。
|
||||
|
||||
## 5. 文件变更清单
|
||||
|
||||
### 新建
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/fix/fixPreview.ts` | diff 内容提供者、`openPreviewDiff`、`closePreviewEditor`、`applyNewText` |
|
||||
| `src/fix/fixPending.ts` | 待确认修复存储(PendingFix / PendingBatch / FixPendingStore) |
|
||||
| `docs/superpowers/specs/2026-08-19-fix-preview-design.md` | 本设计书 |
|
||||
|
||||
### 修改
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `src/fix/fixEngine.ts` | `FixResult` 增 `newText?: string`;`fixDiagnostic` 增 `dryRun` 参数,为真时跳过写盘 |
|
||||
| `src/fix/aiFixEngine.ts` | `aiFixDiagnostic` 增 `dryRun` 参数,为真时跳过写盘并返回 `newText` |
|
||||
| `src/activation/commands.ts` | `fixIssue` panel 路径两步确认;`fixAll` 内存累积 + 合并 diff + 存 pending;新增 `applyFixPreview`/`cancelFixPreview`/`applyAllPreview`/`cancelAllPreview` 命令 |
|
||||
| `src/panel/webview.ts` | 消息类型增 `applyFix`/`cancelFix`/`applyAll`/`cancelAll`;条目与顶部渲染隐藏的应用/取消按钮;`postMessage` 公开方法 |
|
||||
| `src/views/reviewPanel.js` | `window.addEventListener('message')` 翻转按钮显隐 |
|
||||
| `src/extension.ts` | 创建/传递 `FixPendingStore`;文档关闭时清 pending |
|
||||
| `src/i18n/messages.ts` | 新增 `report.fixAllApply`;复用 `fix.apply` / `fix.cancel` / `fix.previewTitle` |
|
||||
|
||||
## 6. 关键接口定义
|
||||
|
||||
### 6.1 `FixResult`(fixEngine.ts)
|
||||
|
||||
```typescript
|
||||
export interface FixResult {
|
||||
success: boolean;
|
||||
attempts: number;
|
||||
message?: string;
|
||||
appliedFixes: AppliedFix[];
|
||||
newText?: string; // 新增:最终整文档文本(dryRun 时返回)
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 引擎 dryRun
|
||||
|
||||
```typescript
|
||||
export async function fixDiagnostic(
|
||||
document: vscode.TextDocument,
|
||||
workingDir: string,
|
||||
adapter: LinterAdapter,
|
||||
diag: LinterDiagnostic,
|
||||
maxIterations: number,
|
||||
dryRun?: boolean
|
||||
): Promise<FixResult>;
|
||||
|
||||
export async function aiFixDiagnostic(
|
||||
document, workingDir, adapter, diag, maxIterations,
|
||||
provider, options,
|
||||
dryRun?: boolean
|
||||
): Promise<FixResult>;
|
||||
```
|
||||
|
||||
- `dryRun=true`:跳过末尾 `WorkspaceEdit` 与 `applyEdit`,返回 `newText: currentText`。
|
||||
- `dryRun=false`(默认):保持现有写盘行为,**不返回** `newText`(或一并返回,调用方忽略)。
|
||||
- hover 入口仍走默认路径,行为零变化。
|
||||
|
||||
### 6.3 `src/fix/fixPreview.ts`
|
||||
|
||||
```typescript
|
||||
export class FixPreviewContentProvider implements vscode.TextDocumentContentProvider {
|
||||
provideTextDocumentContent(uri: vscode.Uri): string;
|
||||
set(uri: vscode.Uri, text: string): void;
|
||||
clear(uri: vscode.Uri): void;
|
||||
}
|
||||
|
||||
export interface PreviewRequest {
|
||||
originalText: string;
|
||||
newText: string;
|
||||
title: string;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export async function openPreviewDiff(req: PreviewRequest): Promise<vscode.Uri | undefined>;
|
||||
// 打开内置 diff 编辑器,返回右侧 newUri(供后续关闭)
|
||||
|
||||
export async function closePreviewEditor(uri: vscode.Uri): Promise<void>;
|
||||
// 关闭对应 diff 编辑器标签并清理 provider 内容
|
||||
|
||||
export async function applyNewText(document: vscode.TextDocument, newText: string): Promise<boolean>;
|
||||
// 整文档 WorkspaceEdit 一次性写入(recordFixes 由调用方完成)
|
||||
```
|
||||
|
||||
### 6.4 `src/fix/fixPending.ts`
|
||||
|
||||
```typescript
|
||||
export interface PendingFix {
|
||||
key: string; // `${ruleId}@${line}`
|
||||
ruleId: string;
|
||||
line: number;
|
||||
filePath: string;
|
||||
originalText: string;
|
||||
newText: string;
|
||||
appliedFixes: AppliedFix[];
|
||||
diffUri?: vscode.Uri;
|
||||
}
|
||||
|
||||
export interface PendingBatch {
|
||||
filePath: string;
|
||||
originalText: string;
|
||||
newText: string;
|
||||
results: { ruleId: string; line: number; appliedFixes: AppliedFix[] }[];
|
||||
diffUri?: vscode.Uri;
|
||||
}
|
||||
|
||||
export class FixPendingStore {
|
||||
setSingle(fix: PendingFix): void;
|
||||
getSingle(filePath: string, key: string): PendingFix | undefined;
|
||||
deleteSingle(filePath: string, key: string): void;
|
||||
setBatch(batch: PendingBatch): void;
|
||||
getBatch(filePath: string): PendingBatch | undefined;
|
||||
deleteBatch(filePath: string): void;
|
||||
clear(filePath: string): void;
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5 面板消息协议
|
||||
|
||||
- 新增消息:`applyFix`/`cancelFix`(带 line/ruleId)→ `codeReviewer.applyFixPreview`/`cancelFixPreview`;`applyAll`/`cancelAll` → `applyAllPreview`/`cancelAllPreview`。
|
||||
- 扩展 → 面板:`{ type: 'pending', key, on: boolean }`(翻转单条按钮)、`{ type: 'batchPending', on: boolean }`(翻转顶部按钮)。
|
||||
|
||||
## 7. 核心逻辑
|
||||
|
||||
### 7.1 单个修复(fixIssue,panel origin)
|
||||
|
||||
```
|
||||
document = resolveFixDocument(...)
|
||||
diag = 命中诊断
|
||||
result = resolveFix(..., dryRun=true) // 计算,不写盘
|
||||
if !result.success → 提示失败,return
|
||||
key = `${ruleId}@${line}`
|
||||
existing = pendingStore.getSingle(filePath, key) // 重复预览先关旧 diff
|
||||
diffUri = openPreviewDiff({ originalText, newText, title, fileName })
|
||||
pendingStore.setSingle({ key, ruleId, line, filePath, originalText, newText, appliedFixes, diffUri })
|
||||
ReviewPanel.postMessage({ type:'pending', key, on:true }) // 面板按钮 → 「应用 / 取消」
|
||||
return // 不写盘
|
||||
```
|
||||
|
||||
用户回面板:
|
||||
- 点「应用」→ `applyFixPreview`:`applyNewText(document, newText)` → `recordFixes` → 关 diff → 删 pending → `save` → `refreshAfterFix` → 提示成功。
|
||||
- 点「取消」→ `cancelFixPreview`:关 diff → 删 pending → postMessage 还原按钮。
|
||||
|
||||
### 7.2 全部修复(fixAll)
|
||||
|
||||
```
|
||||
fixables = 可修复诊断列表(含 AI)
|
||||
let currentText = document.getText()
|
||||
results: { ruleId, line, appliedFixes }[] = []
|
||||
for each fixable:
|
||||
fresh = 对 currentText 构造 mock doc 重跑 check 得 freshDiag
|
||||
result = fixDiagnostic / aiFixDiagnostic(dryRun=true)
|
||||
if result.success: results.push(...); currentText = result.newText!
|
||||
diffUri = openPreviewDiff({ originalText, newText: currentText, title, fileName })
|
||||
pendingStore.setBatch({ filePath, originalText, newText, results, diffUri })
|
||||
ReviewPanel.postMessage({ type:'batchPending', on:true }) // 顶部按钮 → 「全部应用 / 取消」
|
||||
return // 不写盘
|
||||
```
|
||||
|
||||
- 「全部应用」→ `applyAllPreview`:`applyNewText(document, currentText)` → 逐条 `recordFixes` → 关 diff → 删 batch → `save` → `refreshAfterFix` → 提示批量完成。
|
||||
- 「取消全部」→ `cancelAllPreview`:关 diff → 删 batch → 还原按钮。
|
||||
|
||||
> 说明:多轮引擎已在内部对**传入文本**反复 `adapter.check`,因此 fixAll 只需把上一个 fix 的 `newText` 作为下一个的输入(用 mockDocument 包装为 TextDocument 再传引擎),即可得到合并后的最终文本;位置漂移由引擎内 `findClosestFixable` 按行号就近匹配处理。
|
||||
|
||||
### 7.3 diff 提供者
|
||||
|
||||
- 注册 `vscode.workspace.registerTextDocumentContentProvider('codeReviewerPreview', provider)`(extension.ts 调 `registerFixPreviewProvider` 一次)。
|
||||
- 新文本 URI:`codeReviewerPreview://new/<fileName>-<timestamp>`,内容即 `newText`;原文本 URI 同样走 provider(避免脏文档时左屏显示磁盘旧内容与引擎计算不一致)。
|
||||
|
||||
### 7.4 面板按钮翻转(reviewPanel.js)
|
||||
|
||||
```javascript
|
||||
window.addEventListener('message', e => {
|
||||
const msg = e.data;
|
||||
if (msg.type === 'pending') {
|
||||
// 按 data-fix-key 找到「修复 / 应用 / 取消」三个按钮,按 msg.on 显隐
|
||||
} else if (msg.type === 'batchPending') {
|
||||
// 按 data-fix-all-btn 找到「全部修复 / 全部应用 / 取消」三个按钮,按 msg.on 显隐
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
- 应用/取消按钮在 buildHtml 时默认 `display:none`,收到消息后显隐切换,**不全量重建面板**,滚动位置与标签页保持。
|
||||
|
||||
### 7.5 hover 入口保持不变
|
||||
|
||||
- `codeActionProvider.ts` 触发的 `fixIssue` 传 `origin: 'hover'`,命令内直接 `dryRun=false` 执行,不弹 diff、不弹确认框。
|
||||
|
||||
## 8. i18n 新增 key
|
||||
|
||||
| key | zh-CN | en | ja |
|
||||
|-----|-------|----|----|
|
||||
| `fix.previewTitle` | 修复预览 | Fix preview | 修正プレビュー |
|
||||
| `fix.apply` | 应用 | Apply | 適用 |
|
||||
| `fix.cancel` | 取消 | Cancel | キャンセル |
|
||||
| `report.fixAllApply` | 全部应用 | Apply All | すべて適用 |
|
||||
|
||||
## 9. 配置变更
|
||||
|
||||
无新增配置。
|
||||
|
||||
## 10. 测试计划
|
||||
|
||||
- 现有 `npm test`(106 tests)回归通过。
|
||||
- 引擎 dryRun 路径回归:原有 fixEngine/aiFixEngine 测试走默认(写盘)路径不受影响。
|
||||
|
||||
## 11. 验证顺序
|
||||
|
||||
`lint → compile → test`(`npm run lint` / `npm run compile` / `npm test`)
|
||||
@@ -0,0 +1,139 @@
|
||||
# 修复功能扩展到自定义规则与 AI 审查设计书
|
||||
|
||||
> 日期:2026-08-20
|
||||
> 状态:已批准(Stage ④ 通过)
|
||||
> 流程:① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
当前自动修复只支持静态分析(linter)条目:`fixIssue`/`fixAll` 从 `orchestrator.getAnalysisResult()` 取 linter 诊断,走 native fix 或 `aiFixDiagnostic`(依赖 LinterAdapter 重 lint 验证收敛)。
|
||||
|
||||
用户要求将修复能力扩展到**自定义规则(custom tab)**与 **AI 审查(ai tab)** 条目。
|
||||
|
||||
## 2. 需求澄清结论(Stage ② 共识)
|
||||
|
||||
| 决策项 | 结论 |
|
||||
|--------|------|
|
||||
| 修复范围 | 面板 custom + ai 条目标题均支持 AI 修复 |
|
||||
| 验证方式 | custom/ai 无对应 linter 适配器,无法重 lint 验证 → **AI 重检收敛**(新文本提交 AI 确认问题消除,未消除带反馈重试 ≤maxIterations) |
|
||||
| 入口交互 | **面板按钮 + 两步确认**(与 linter 一致:打开 diff → 应用/取消) |
|
||||
| 全部修复 | **分 tab 批量**:linter/custom/ai 各自有「全部修复」按钮 |
|
||||
| 修复引擎 | **新建 `customFixEngine.ts`**,复用 AI 修复 prompt,无 adapter 依赖 |
|
||||
| 修复后展示 | 各 tab 顶部显示「✅ 已修复 + 撤销」区块 |
|
||||
| ai codeDiff | **删除**该字段(schema/prompt/报告展示全部移除) |
|
||||
|
||||
## 3. 现状关键点(已核实)
|
||||
|
||||
- `fixIssue`(commands.ts:360)从 `orchestrator.getAnalysisResult()` 取 linter 诊断,`resolveFix` 依赖 adapter。
|
||||
- custom 诊断在 `currentReport.customRuleDiagnostics`(`LinterDiagnostic[]`);ai 诊断在 `currentReport.aiFindings`(`AIFinding[]`,`f.line` 已转 0-based)。
|
||||
- `aiFixEngine.requestFix`(依赖 provider/options/diag/context)可复用;其收敛验证 `adapter.check` 对 custom/ai 不可用。
|
||||
- 两步确认基础设施已完备:`fixPending.ts`、`openPreviewDiff`/`closePreviewEditor`/`applyNewText`、4 个 preview 命令、`reviewPanel.js` 按钮翻转、`pendingStore`。
|
||||
- `codeDiff` 字段定义于 schema,唯一用途是 report.ts 导出 Markdown 时展示 ` ```diff ` 块,从未用于真实修复。
|
||||
|
||||
## 4. 架构概览
|
||||
|
||||
```
|
||||
面板 custom/ai 条目标题「🤖 AI 修复」 / 各 tab「全部修复」
|
||||
│ send('fix', line, ruleId, 'custom'|'ai') / send('fixAll', ..., 'custom'|'ai')
|
||||
▼
|
||||
codeReviewer.fixIssue / fixAll (payload.source 分流)
|
||||
│ source === 'custom'|'ai'
|
||||
▼
|
||||
findCustomIssue / findAIIssue 从 currentReport 定位 ReviewIssueInput
|
||||
│
|
||||
▼
|
||||
customFixEngine.aiFixReviewIssue(document, diag, maxIterations, provider, options, dryRun)
|
||||
每轮:buildFixContext → requestFix(AI 生成 {originalText,newText}) → indexOf 匹配替换
|
||||
→ verifyFixed(AI 重检 {fixed,reason}) → 未消除带反馈重试 ≤maxIterations
|
||||
│ dryRun=true 返回 newText(不写盘)
|
||||
▼
|
||||
两步确认:openPreviewDiff + pendingStore + postMessage 翻转按钮
|
||||
▼ 面板「应用」→ applyFixPreview/applyAllPreview → applyNewText + recordFixes(source) + save + refresh
|
||||
```
|
||||
|
||||
## 5. 文件变更清单
|
||||
|
||||
### 新建
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/fix/fixPrompt.ts` | 共享修复/重检 prompt builder(三语):`buildFixSystemPrompt`/`buildFixUserPrompt`/`buildFixContext`/`buildVerifySystemPrompt`/`buildVerifyUserPrompt`,`ReviewIssueInput` 接口 |
|
||||
| `src/fix/customFixEngine.ts` | `aiFixReviewIssue`:AI 生成修复 + AI 重检收敛,无 adapter 依赖,支持 dryRun |
|
||||
| `src/test/customFixEngine.test.ts` | 4 用例(收敛/匹配失败/重试收敛/最大轮次失败) |
|
||||
|
||||
### 修改
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `src/fix/aiFixEngine.ts` | 移除内联 prompt builder,改用共享 fixPrompt(行为不变) |
|
||||
| `src/fix/fixSession.ts` | `FixedEntry.source: 'linter'|'custom'|'ai'`;`recordFixes` 加 source 参数(默认 linter) |
|
||||
| `src/fix/fixPending.ts` | `PendingFix`/`PendingBatch` 加 `source` 字段 |
|
||||
| `src/activation/commands.ts` | `resolveReviewIssueFix`/`findCustomIssue`/`findAIIssue`;`fixIssue` 按 source 分流(custom/ai 走 AI 重检);`fixAll` 分 tab 批量;`applyFixPreview`/`applyAllPreview` recordFixes 传 source;`refreshAfterFix` 保留 custom suggestion |
|
||||
| `src/panel/webview.ts` | custom/ai 列表启用 AI 修复按钮 + 各自「全部修复」按钮 + 已修复区块(按 source 过滤/徽章);`handleMessage.fixAll` 传 source;`FixedEntryView` 接口 |
|
||||
| `src/ai/schema.ts` | 删 `TranslatedDiagnostic.codeDiff`、`AIFinding.codeDiff` |
|
||||
| `src/ai/engine.ts` | 删完整审查 + 方法审查 prompt 中的 codeDiff 要求 |
|
||||
| `src/utils/report.ts` | 删 Markdown 导出的 ` ```diff ` codeDiff 块 |
|
||||
|
||||
## 6. 关键接口
|
||||
|
||||
### 6.1 `src/fix/fixPrompt.ts`
|
||||
|
||||
```typescript
|
||||
export interface ReviewIssueInput {
|
||||
ruleId: string;
|
||||
line: number; // 0-based
|
||||
message: string;
|
||||
suggestion?: string;
|
||||
}
|
||||
export function buildFixSystemPrompt(): string;
|
||||
export function buildFixUserPrompt(diag: ReviewIssueInput, context: string): string;
|
||||
export function buildFixContext(code: string, line: number): string;
|
||||
export function buildVerifySystemPrompt(): string;
|
||||
export function buildVerifyUserPrompt(diag: ReviewIssueInput, code: string): string;
|
||||
```
|
||||
|
||||
### 6.2 `src/fix/customFixEngine.ts`
|
||||
|
||||
```typescript
|
||||
export async function aiFixReviewIssue(
|
||||
document: vscode.TextDocument,
|
||||
diag: ReviewIssueInput,
|
||||
maxIterations: number,
|
||||
provider: AIProvider,
|
||||
options: ChatOptions,
|
||||
dryRun?: boolean
|
||||
): Promise<FixResult>; // 复用 FixResult/AppliedFix,无 adapter
|
||||
```
|
||||
|
||||
流程:每轮 `requestFix`(AI 生成 `{originalText,newText}`)→ `indexOf` 匹配 → 替换 → `verifyFixed`(AI 重检 `{fixed,reason}`)→ 未消除带 reason 反馈重试。
|
||||
|
||||
### 6.3 命令协议
|
||||
|
||||
- `fixIssue` payload.source:`'linter'|'custom'|'ai'`(webview 已传),命令分流。
|
||||
- `fixAll` payload:`{ source?: 'linter'|'custom'|'ai' }`,分 tab 批量。
|
||||
- 面板消息 `fixAll` 透传 source:`send('fixAll', undefined, undefined, 'custom')`。
|
||||
|
||||
### 6.4 面板
|
||||
|
||||
- custom/ai 条目:`buildIssueItem(..., aiFixable=true)` → 显示「🤖 AI 修复」+ 隐藏的应用/取消按钮(复用现有两步确认)。
|
||||
- 各 tab 顶部「全部修复」→ `send('fixAll', ..., source)`。
|
||||
- 已修复区块:`fixedEntries` 按 `source` 过滤 + `buildFixedItem` 按 source 渲染徽章;custom/ai 列表用 fixedKeys 过滤已修条目。
|
||||
|
||||
## 7. 删除 codeDiff
|
||||
|
||||
- schema.ts:`TranslatedDiagnostic.codeDiff`、`AIFinding.codeDiff` 移除。
|
||||
- engine.ts:完整审查(zh/en/ja)与方法审查(En/Zh/Ja)prompt 中 `"codeDiff"` 要求行全部移除。
|
||||
- report.ts:`reportToMarkdown` 中 ` ```diff ` 代码块展示移除。
|
||||
|
||||
## 8. i18n
|
||||
|
||||
无需新增 key(复用 `fix.aiRunning`/`fix.aiFailed`/`fix.noAI`/`report.fixAll`/`report.fixAILabel`/`report.fixedIssues`/`report.undoFix`)。
|
||||
|
||||
## 9. 测试计划
|
||||
|
||||
- 新增 `customFixEngine.test.ts` 4 用例:收敛、匹配失败、重检后重试收敛、最大轮次失败。
|
||||
- 现有 106 用例回归通过(共 110 passing)。
|
||||
|
||||
## 10. 验证顺序
|
||||
|
||||
`lint → compile → test`(`npm run lint` / `npm run compile` / `npm test`)。
|
||||
@@ -0,0 +1,50 @@
|
||||
# 自定义规则条目展开显示 AI 修复建议功能设计书
|
||||
|
||||
> 日期:2026-08-20
|
||||
> 状态:已批准(Stage ④ 通过)
|
||||
> 流程:① 用户提出 → ② 需求澄清 → ③ 方案设计 → ④ 人类审批
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
审查面板「自定义规则」(custom tab)条目当前**不可展开**:点击无反应、无 AI 修复建议详情。用户期望自定义规则条目与静态分析(linter)条目一致,展开后显示 AI 给出的修复建议(💡 建议文本)。
|
||||
|
||||
## 2. 需求澄清结论(Stage ② 共识)
|
||||
|
||||
| 决策项 | 结论 |
|
||||
|--------|------|
|
||||
| 实现程度 | 展开显示 AI 修复建议**文本**(💡),不提供 AI 修复改代码按钮 |
|
||||
| 展示方式 | 复用现有 `buildIssueItem` 的 `detail-suggestion` 展开详情 |
|
||||
| 入口 | 完整审查 + 方法审查两条路径的自定义规则条目 |
|
||||
|
||||
## 3. 现状关键点(已核实)
|
||||
|
||||
- `CustomRuleResult`(schema.ts:8-13)仅含 `ruleId/line/severity/message`,无 `suggestion` 字段。
|
||||
- 自定义规则 AI prompt(engine.ts `buildCustomRuleSystemPrompt` 完整审查、`buildMethodSystemPrompt*` 方法审查)输出 JSON 格式均无 `suggestion`。
|
||||
- `merger.ts:92-97` 映射 custom 诊断时未传 `suggestion` → `LinterDiagnostic.suggestion` 为空。
|
||||
- `webview.ts:365` `buildIssueItem(..., d.suggestion, false, false)` 因 `suggestion` 为空,展开条件 `suggestion && suggestion !== message` 不满足 → 不可展开。
|
||||
|
||||
## 4. 方案
|
||||
|
||||
### 4.1 `src/ai/schema.ts`
|
||||
`CustomRuleResult` 增加可选字段 `suggestion?: string`。
|
||||
|
||||
### 4.2 `src/ai/engine.ts`
|
||||
- `buildCustomRuleSystemPrompt`(完整审查,zh/en/ja 三段):输出 JSON 格式加 `"suggestion"` 字段,并要求每条违规必须给出可执行的修复建议。
|
||||
- `buildMethodSystemPromptEn/Zh/Ja`(方法审查):`customRuleResults` 输出段加 `"suggestion"` 字段。
|
||||
- `runAIReview` / `runMethodReview` 已用 `...r` 展开映射,suggestion 自动透传,无需改动。
|
||||
|
||||
### 4.3 `src/merger/merger.ts`
|
||||
custom 诊断映射补 `suggestion: r.suggestion`。
|
||||
|
||||
### 4.4 `src/panel/webview.ts`
|
||||
无需改动——`buildCustomList` 已传 `d.suggestion`,suggestion 非空时 `buildIssueItem` 展开条件即满足,复用现有 `detail-suggestion` 渲染。
|
||||
|
||||
## 5. 影响面与风险
|
||||
|
||||
- **兼容性**:`suggestion` 为可选字段,AI 未返回时条目不可展开(与现状一致),零回归。
|
||||
- **AI 一致性**:仅提示词要求,AI 不保证 100% 返回——有则展开,无则不展开。
|
||||
- 不新增 i18n;hover 快速修复、AI 修复按钮、两步确认逻辑均不受影响。
|
||||
|
||||
## 6. 验证
|
||||
|
||||
`lint → compile → test`(`npm run lint` / `npm run compile` / `npm test`)。
|
||||
Reference in New Issue
Block a user