@ghyper9023/pi-dev-workflow 0.4.3 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.doc/AGENT-FRONTMATTER-REFERENCE.md +198 -0
- package/.version/RELEASE-v0.5.0.md +82 -0
- package/README.md +2 -0
- package/agents/git-agent.md +7 -0
- package/agents/grill/dev-doc-grill-agent.md +7 -0
- package/agents/grill/dev-fix-grill-agent.md +7 -0
- package/agents/grill/dev-grill-agent.md +7 -0
- package/agents/grill/dev-perf-grill-agent.md +7 -0
- package/agents/grill/dev-prd-agent.md +7 -0
- package/agents/grill/dev-refactor-grill-agent.md +7 -0
- package/agents/grill/dev-test-grill-agent.md +7 -0
- package/agents/review-agent.md +7 -0
- package/agents/workflow/docWriter-agent.md +13 -2
- package/agents/workflow/planner-agent.md +16 -3
- package/agents/workflow/reviewer-agent.md +21 -4
- package/agents/workflow/trimmer-agent.md +12 -1
- package/agents/workflow/worker-agent.md +17 -2
- package/extensions/sub-agents.ts +192 -30
- package/extensions/ui-helpers.ts +20 -20
- package/extensions/workflow-engine.ts +347 -16
- package/package.json +1 -1
- package/tests/test-workflow-engine-bugs.mjs +317 -0
- package/.pi-dev-output/pi-grill/answers/answer-mpds3by7-20260520-1606.md +0 -14
- package/.pi-dev-output/pi-grill/answers/answer-mpfe77f1-20260521-1913.md +0 -58
- package/.pi-dev-output/pi-grill/answers/answer-mpfh37wu-20260521-2034.md +0 -13
- package/.pi-dev-output/pi-grill/answers/answer-mpfi5q4c-20260521-2104.md +0 -13
- package/.pi-dev-output/pi-grill/answers/answer-mpfizccb-20260521-2127.md +0 -13
- package/.pi-dev-output/pi-grill/answers/answer-mpfjk78k-20260521-2143.md +0 -13
- package/.pi-dev-output/pi-grill/answers/answer-mpfttme1-20260522-0230.md +0 -13
- package/.pi-dev-output/pi-grill/questions/questions-mpfdz1tz-20260521-1907.json +0 -94
- package/.pi-dev-output/pi-plans/20260520-153000-fix-workflow-engine-bugs.md +0 -150
- package/.pi-dev-output/pi-plans/20260521-113000-fix-loopcount-timeout.md +0 -215
- package/.pi-dev-output/pi-plans/20260521-1730-grill-input-wrap-back-fix.md +0 -240
- package/.pi-dev-output/pi-plans/20260521-230000-fix-timeout-display-loopcount-gitdiff.md +0 -253
- package/.pi-dev-output/pi-plans/20260521-230500-esc-double-press-confirm-workflow.md +0 -137
- package/.pi-dev-output/pi-plans/20260521-235000-fix-gitdiff-loopcount.md +0 -258
- package/.pi-dev-output/pi-plans/20260522-113000-grill-left-arrow-fix.md +0 -274
- package/.pi-dev-output/pi-review/html/20260521-2305-review-workflow-index.html +0 -196
- package/.pi-dev-output/pi-review/md/review-20260520-100000.md +0 -91
- package/.pi-dev-output/pi-review/md/review-20260521-140000.md +0 -191
- package/.pi-dev-output/pi-review/md/review-20260521-190000.md +0 -189
- package/.pi-dev-output/pi-review/md/review-20260521-204500.md +0 -241
- package/.pi-dev-output/pi-review/md/review-20260521-214500.md +0 -270
- package/.pi-dev-output/pi-review/md/review-20260521-215158.md +0 -214
- package/.pi-dev-output/pi-review/md/review-20260521-234500.md +0 -201
- package/.pi-dev-output/pi-review/md/review-20260521-235500.md +0 -422
- package/.pi-dev-output/pi-review/md/review-20260522-000000.md +0 -212
- package/.pi-dev-output/pi-review/md/review-20260522-003000.md +0 -377
- package/.pi-dev-output/pi-review/md/review-20260522-003500.md +0 -296
- package/.pi-dev-output/pi-review/md/review-20260522-105000.md +0 -166
- package/.pi-dev-output/pi-workflow/checkpoint-20260520-153000-fix-workflow-engine-bugs.json +0 -108
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-113000-fix-loopcount-timeout.json +0 -402
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-1730-grill-input-wrap-back-fix.json +0 -447
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-230000-fix-timeout-display-loopcount-gitdiff.json +0 -708
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-230500-esc-double-press-confirm-workflow.json +0 -365
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-235000-fix-gitdiff-loopcount.json +0 -395
- package/.pi-dev-output/pi-workflow/checkpoint-20260522-113000-grill-left-arrow-fix.json +0 -473
- package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfhyxc5.json +0 -30
- package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfi2unc.json +0 -49
- package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfi382e.json +0 -59
- package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfi5r22.json +0 -76
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
# Grill 左方向键冲突与选项描述显示修复 — 实施计划
|
|
2
|
-
|
|
3
|
-
## 概述
|
|
4
|
-
|
|
5
|
-
修复 `dc0d3fa9` 提交引入的两个回归问题:
|
|
6
|
-
|
|
7
|
-
1. **左方向键破坏 Input 光标左移**:`uiInput` 中将裸 `←` 拦截为"返回",导致所有 `backable=true` 的输入场景中,左方向键的"光标左移"功能完全失效。
|
|
8
|
-
2. **Grill 选项截断+description 显示混乱**:选项 label 被截断后,完整文本放入 `description` 字段,但 SelectList 的 description 列不换行,导致用户看到"左边简短被截断 + 右边灰色不换行"的混乱显示。
|
|
9
|
-
|
|
10
|
-
**用户明确要求**:grill 提问环节的向左返回统一改为 `Ctrl+Shift+←`(与输入式一致),输入式的左方向键恢复为光标左移。
|
|
11
|
-
|
|
12
|
-
## 根因分析
|
|
13
|
-
|
|
14
|
-
### Bug 1:左方向键冲突
|
|
15
|
-
|
|
16
|
-
**问题位置**:`extensions/ui-helpers.ts` 第 263-266 行
|
|
17
|
-
|
|
18
|
-
`dc0d3fa9` 提交在 `uiInput.handleInput` 中添加了:
|
|
19
|
-
|
|
20
|
-
```typescript
|
|
21
|
-
// 左方向键 → 返回(优先于 Input 的光标左移)
|
|
22
|
-
if (backable && matchesKey(data, Key.left)) {
|
|
23
|
-
done(BACK_MARKER);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
这段拦截在 `input.handleInput(data)` 之前执行,Input 组件永远不会收到左方向键事件,导致:
|
|
29
|
-
- Input 内部的 `tui.editor.cursorLeft` 绑定(左方向键)永远不会被触发
|
|
30
|
-
- 用户无法在输入框中左移光标
|
|
31
|
-
- 影响范围:所有 `backable=true` 的 uiInput 调用(grill 自定义输入、dev-* wizard 输入等)
|
|
32
|
-
|
|
33
|
-
同时,`extensions/grill-me-agent.ts` 第 671-674 行的 `showQuestionTUI.handleInput` 也拦截了左方向键:
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
// 左方向键 → 返回上一题
|
|
37
|
-
if (backable && currentIndex > 1 && matchesKey(data, Key.left)) {
|
|
38
|
-
done("__BACK__");
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
两处拦截叠加,用户按 `←` 时:
|
|
44
|
-
- 在 SelectList 中 → 返回上一题(正确,因为 SelectList 不处理 left 键)
|
|
45
|
-
- 在 Input 中 → 返回上一题(错误,期望光标左移)
|
|
46
|
-
|
|
47
|
-
### Bug 2:选项截断+description 显示混乱
|
|
48
|
-
|
|
49
|
-
**问题位置**:`extensions/grill-me-agent.ts` 第 602-618 行
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
const MAX_OPTION_LABEL = 50;
|
|
53
|
-
const truncated = truncateToWidth(label, MAX_OPTION_LABEL, "...");
|
|
54
|
-
return {
|
|
55
|
-
value: `opt-${i}`,
|
|
56
|
-
label: truncated,
|
|
57
|
-
description: truncated !== label ? opt : undefined,
|
|
58
|
-
};
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
SelectList 的渲染流程:
|
|
62
|
-
1. `renderItem` 对 `label` 使用 `truncateToWidth` 到主列宽度(截断为 50 字符 + "...")
|
|
63
|
-
2. 如果 `description` 存在,使用两列布局,description 列也被 `truncateToWidth` 到剩余宽度
|
|
64
|
-
|
|
65
|
-
当终端宽度不足以显示完整的 description 文本时,description 列也会被截断且**不换行**,产生用户看到的"左边截断 + 右边灰色不换行"效果。
|
|
66
|
-
|
|
67
|
-
## 修复方案
|
|
68
|
-
|
|
69
|
-
### 方案(根据用户建议)
|
|
70
|
-
|
|
71
|
-
**核心思路**:统一使用 `Ctrl+Shift+←` 作为返回键,恢复裸 `←` 的光标左移功能。
|
|
72
|
-
|
|
73
|
-
**详细改动**:
|
|
74
|
-
|
|
75
|
-
| 位置 | 当前行为(`dc0d3fa9`) | 修复后行为 |
|
|
76
|
-
|------|----------------------|-----------|
|
|
77
|
-
| `grill-me-agent.ts` handleInput | 裸 `←` 拦截为返回 | `Ctrl+Shift+←` 拦截为返回 |
|
|
78
|
-
| `ui-helpers.ts` uiInput handleInput | 裸 `←` 拦截为返回(破坏光标左移) | 移除裸 `←` 拦截 |
|
|
79
|
-
| `ui-helpers.ts` uiInput handleInput | `Ctrl+Shift+←` 返回(保留) | 保留 `Ctrl+Shift+←` |
|
|
80
|
-
| 选项列表(SelectList) | 用户按 `←` → 返回上一题 | 用户按 `Ctrl+Shift+←` → 返回上一题 |
|
|
81
|
-
| 输入框(Input) | 用户按 `←` → 返回上一题 ❌ | 用户按 `←` → 光标左移 ✅ |
|
|
82
|
-
| 输入框(Input) | 用户按 `Ctrl+Shift+←` → 返回上一题 | 保留 |
|
|
83
|
-
|
|
84
|
-
**Bug 2 修复**:直接移除截断+description 方案,恢复完整 label 显示。
|
|
85
|
-
|
|
86
|
-
## 文件清单
|
|
87
|
-
|
|
88
|
-
### 修改文件
|
|
89
|
-
|
|
90
|
-
| 文件路径 | 改动描述 | 风险等级 |
|
|
91
|
-
|---------|---------|---------|
|
|
92
|
-
| `extensions/ui-helpers.ts` | `uiInput.handleInput` 中移除裸 `←` 拦截(第 263-266 行);更新 JSDoc 注释 | 低 |
|
|
93
|
-
| `extensions/grill-me-agent.ts` | 1. `showQuestionTUI.handleInput` 中 `Key.left` → `Key.ctrlShift("left")` 2. 更新 hint 文案 3. 移除选项截断+description 方案,恢复完整 label | 低 |
|
|
94
|
-
|
|
95
|
-
### 无新增/删除文件
|
|
96
|
-
|
|
97
|
-
## 实施步骤
|
|
98
|
-
|
|
99
|
-
### 步骤 1:修复 `ui-helpers.ts` — 移除 uiInput 中的裸 ← 拦截
|
|
100
|
-
|
|
101
|
-
- **前置条件**:无
|
|
102
|
-
- **改动文件**:`extensions/ui-helpers.ts`
|
|
103
|
-
- **改动内容**:
|
|
104
|
-
|
|
105
|
-
**a) 删除裸 ← 拦截(约第 263-266 行)**
|
|
106
|
-
|
|
107
|
-
删除以下代码块:
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
// 左方向键 → 返回(优先于 Input 的光标左移)
|
|
111
|
-
if (backable && matchesKey(data, Key.left)) {
|
|
112
|
-
done(BACK_MARKER);
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
保留 `Ctrl+Shift+←` 和 `Ctrl+Shift+→` 拦截不受影响。
|
|
118
|
-
|
|
119
|
-
**b) 更新 JSDoc 注释(约第 220 行)**
|
|
120
|
-
|
|
121
|
-
将:
|
|
122
|
-
|
|
123
|
-
```
|
|
124
|
-
* When backable=true, supports ← for back, Ctrl+Shift+← for back, and Ctrl+Shift+→ for submit+next.
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
改为:
|
|
128
|
-
|
|
129
|
-
```
|
|
130
|
-
* When backable=true, supports Ctrl+Shift+← for back and Ctrl+Shift+→ for submit+next.
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
- **验证方式**:
|
|
134
|
-
- 运行 `/dev-feat` → 进入"核心功能描述"输入框 → 按 `←` → 确认光标左移
|
|
135
|
-
- 按 `Ctrl+Shift+←` → 确认返回上一题
|
|
136
|
-
- 按 `Ctrl+Shift+→` → 确认跳过并继续
|
|
137
|
-
|
|
138
|
-
### 步骤 2:修复 `grill-me-agent.ts` — 选项列表 ← 改为 Ctrl+Shift+←
|
|
139
|
-
|
|
140
|
-
- **前置条件**:步骤 1 完成
|
|
141
|
-
- **改动文件**:`extensions/grill-me-agent.ts`
|
|
142
|
-
- **改动内容**:
|
|
143
|
-
|
|
144
|
-
**a) 修改 handleInput 中的左方向键拦截(约第 671-674 行)**
|
|
145
|
-
|
|
146
|
-
当前:
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
// 左方向键 → 返回上一题(SelectList 不处理 left 键,需自行拦截)
|
|
150
|
-
if (backable && currentIndex > 1 && matchesKey(data, Key.left)) {
|
|
151
|
-
done("__BACK__");
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
改为:
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
// Ctrl+Shift+← → 返回上一题(SelectList 不处理该键,需自行拦截)
|
|
160
|
-
if (backable && currentIndex > 1 && matchesKey(data, Key.ctrlShift("left"))) {
|
|
161
|
-
done("__BACK__");
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**b) 修改 hint 提示文字(约第 660 行)**
|
|
167
|
-
|
|
168
|
-
当前:
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
const hint = backable && currentIndex > 1
|
|
172
|
-
? " ↑↓ 导航 • Enter 选择 • ← 返回上一题 • Esc 取消全部评审"
|
|
173
|
-
: " ↑↓ 导航 • Enter 选择 • Esc 取消全部评审";
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
改为:
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
const hint = backable && currentIndex > 1
|
|
180
|
-
? " ↑↓ 导航 • Enter 选择 • Ctrl+Shift+← 返回上一题 • Esc 取消全部评审"
|
|
181
|
-
: " ↑↓ 导航 • Enter 选择 • Esc 取消全部评审";
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
- **验证方式**:
|
|
185
|
-
- 运行 `/dev-fix` → 进入 grill 评审
|
|
186
|
-
- 在第二个问题按 `←` → 确认**无反应**(正确,SelectList 不处理 left 键)
|
|
187
|
-
- 按 `Ctrl+Shift+←` → 确认返回到第一个问题
|
|
188
|
-
- 确认第一个问题选择的答案被保留
|
|
189
|
-
|
|
190
|
-
### 步骤 3:修复 `grill-me-agent.ts` — 移除选项截断+description 方案
|
|
191
|
-
|
|
192
|
-
- **前置条件**:步骤 2 完成
|
|
193
|
-
- **改动文件**:`extensions/grill-me-agent.ts`
|
|
194
|
-
- **改动内容**:
|
|
195
|
-
|
|
196
|
-
**a) 修改选项构建逻辑(约第 602-618 行)**
|
|
197
|
-
|
|
198
|
-
当前代码(`dc0d3fa9` 引入):
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
const MAX_OPTION_LABEL = 50;
|
|
202
|
-
const selectItems: SelectItem[] = q.options.map((opt, i) => {
|
|
203
|
-
const prefix = `(${String.fromCharCode(97 + i)}) `;
|
|
204
|
-
const label = opt === previousAnswer
|
|
205
|
-
? `${prefix}${opt} - 上次选择`
|
|
206
|
-
: `${prefix}${opt}`;
|
|
207
|
-
const truncated = truncateToWidth(label, MAX_OPTION_LABEL, "...");
|
|
208
|
-
return {
|
|
209
|
-
value: `opt-${i}`,
|
|
210
|
-
label: truncated,
|
|
211
|
-
description: truncated !== label ? opt : undefined,
|
|
212
|
-
};
|
|
213
|
-
});
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
恢复为(还原到 `dc0d3fa9` 之前的状态):
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
const selectItems: SelectItem[] = q.options.map((opt, i) => ({
|
|
220
|
-
value: `opt-${i}`,
|
|
221
|
-
label: opt === previousAnswer
|
|
222
|
-
? `(${String.fromCharCode(97 + i)}) ${opt} - 上次选择`
|
|
223
|
-
: `(${String.fromCharCode(97 + i)}) ${opt}`,
|
|
224
|
-
}));
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
**b) 清理不再需要的 import(文件顶部约第 21-27 行)**
|
|
228
|
-
|
|
229
|
-
如果 `truncateToWidth` 在文件其他地方没有使用(经 grep 检查仅在步骤 3a 处使用),则删除 import 中的 `truncateToWidth`:
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
// 删除: truncateToWidth,
|
|
233
|
-
import {
|
|
234
|
-
Container,
|
|
235
|
-
SelectList,
|
|
236
|
-
Text,
|
|
237
|
-
Spacer,
|
|
238
|
-
matchesKey,
|
|
239
|
-
Key,
|
|
240
|
-
type SelectItem,
|
|
241
|
-
} from "@earendil-works/pi-tui";
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
- **验证方式**:
|
|
245
|
-
- 查看 grill 问题的选项 → 确认显示完整标签文本(而不是截断+description 两列显示)
|
|
246
|
-
- 当选项文本超宽时,SelectList 会自动截断到可用宽度(这是 SelectList 的内部行为,不需要手动干预)
|
|
247
|
-
|
|
248
|
-
## 依赖关系
|
|
249
|
-
|
|
250
|
-
- 步骤 1 和步骤 2、3 无直接依赖,但建议按顺序 1→2→3 执行,以保证逻辑一致性
|
|
251
|
-
- 步骤 3 中需先确认 `truncateToWidth` 是否在其他地方使用,再决定是否清理 import
|
|
252
|
-
|
|
253
|
-
## 测试策略
|
|
254
|
-
|
|
255
|
-
手动测试清单:
|
|
256
|
-
|
|
257
|
-
| 测试场景 | 操作 | 预期结果 |
|
|
258
|
-
|---------|------|---------|
|
|
259
|
-
| 输入框左方向键 | 在 dev-* 输入框中按 `←` | 光标左移 ✅(修复点) |
|
|
260
|
-
| 输入框 Ctrl+Shift+← | 在 dev-* 输入框中按 `Ctrl+Shift+←` | 返回上一题 ✅ |
|
|
261
|
-
| 输入框 Ctrl+Shift+→ | 在 dev-* 输入框中按 `Ctrl+Shift+→` | 跳过并继续 ✅ |
|
|
262
|
-
| Grill 选项列表 ← | 在 grill 第二题按 `←` | 无反应(SelectList 不处理 left 键)✅ |
|
|
263
|
-
| Grill 选项列表 Ctrl+Shift+← | 在 grill 第二题按 `Ctrl+Shift+←` | 返回上一题 ✅(修复点) |
|
|
264
|
-
| Grill 自定义输入 ← | 进入自定义输入框按 `←` | 光标左移 ✅(修复点) |
|
|
265
|
-
| Grill 自定义输入 Ctrl+Shift+← | 在自定义输入框按 `Ctrl+Shift+←` | 返回选项列表 ✅ |
|
|
266
|
-
| Grill 选项完整显示 | 查看长选项文本 | 完整 label 显示 ✅(修复点) |
|
|
267
|
-
| 原有功能 | ↑↓ Enter Esc | 正常导航/选择/取消 ✅ |
|
|
268
|
-
|
|
269
|
-
## 注意事项
|
|
270
|
-
|
|
271
|
-
- **最小改动**:仅修改 2 个文件中涉及的 4 个代码位置,总计约 10-15 行
|
|
272
|
-
- **不修改 pi-tui 库**:所有改动在 extensions/ 目录内完成
|
|
273
|
-
- **兼容性**:原有的 `Ctrl+Shift+←` 和 `Ctrl+Shift+→` 功能完全保留,新增的选项列表 `Ctrl+Shift+←` 与输入框保持一致的按键语义
|
|
274
|
-
- **SelectList 的 `← 返回上一题` 选项**:注意 `showQuestionTUI` 中还有一个 `"← 返回上一题"` 的 `SelectItem`(第 637 行),这是通过选择选项来返回,而非通过按键。此功能应保留,因为用户也可以通过 ↑↓ 导航到该选项后按 Enter 返回。但需要确认它与 `Key.left` 拦截的分离——按键拦截在 handleInput 中,选项选择在 SelectList 的 onSelect 回调中,两者互不干扰。保留该选项作为额外导航方式。
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="zh-CN">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Code Review Report</title>
|
|
7
|
-
<style>
|
|
8
|
-
*{margin:0;padding:0;box-sizing:border-box}
|
|
9
|
-
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f5f5f5;color:#333;line-height:1.6;padding:20px}
|
|
10
|
-
.container{max-width:960px;margin:0 auto}
|
|
11
|
-
h1{font-size:24px;margin-bottom:8px;color:#1a1a1a}
|
|
12
|
-
.summary{background:#fff;border-radius:8px;padding:20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.1)}
|
|
13
|
-
.summary-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}
|
|
14
|
-
.summary-item{background:#fafafa;padding:12px;border-radius:6px}
|
|
15
|
-
.summary-item .label{font-size:12px;color:#666;text-transform:uppercase}
|
|
16
|
-
.summary-item .value{font-size:20px;font-weight:700;margin-top:4px}
|
|
17
|
-
.bug-count .value{color:#d32f2f}
|
|
18
|
-
.warn-count .value{color:#f57c00}
|
|
19
|
-
.info-count .value{color:#1976d2}
|
|
20
|
-
.score{font-size:36px;font-weight:700;text-align:center;padding:16px;background:#fafafa;border-radius:6px;margin-top:12px}
|
|
21
|
-
.score.good{color:#2e7d32}
|
|
22
|
-
.score.fair{color:#f57c00}
|
|
23
|
-
.commit-msg{background:#e8f5e9;padding:12px 16px;border-radius:6px;margin-top:12px;font-family:"SF Mono","Consolas",monospace;font-size:13px;color:#2e7d32}
|
|
24
|
-
.file-group{background:#fff;border-radius:8px;margin-bottom:12px;box-shadow:0 1px 3px rgba(0,0,0,.1);overflow:hidden}
|
|
25
|
-
.file-header{display:flex;justify-content:space-between;align-items:center;padding:14px 20px;cursor:pointer;transition:background .15s}
|
|
26
|
-
.file-header:hover{background:#f0f0f0}
|
|
27
|
-
.file-header .path{font-family:"SF Mono","Consolas",monospace;font-size:14px;font-weight:600;color:#1a1a1a}
|
|
28
|
-
.file-header .status{display:flex;gap:8px;align-items:center}
|
|
29
|
-
.badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600}
|
|
30
|
-
.badge.bug{background:#ffebee;color:#c62828}
|
|
31
|
-
.badge.warn{background:#fff3e0;color:#e65100}
|
|
32
|
-
.badge.info{background:#e3f2fd;color:#1565c0}
|
|
33
|
-
.file-body{display:none;padding:0 20px 20px;border-top:1px solid #eee}
|
|
34
|
-
.file-body.open{display:block}
|
|
35
|
-
.issue{margin:12px 0;padding:12px 16px;border-left:4px solid #ccc;background:#fafafa;border-radius:0 6px 6px 0}
|
|
36
|
-
.issue.bug{border-color:#d32f2f}
|
|
37
|
-
.issue.warn{border-color:#f57c00}
|
|
38
|
-
.issue.info{border-color:#1976d2}
|
|
39
|
-
.issue .tag{font-size:11px;font-weight:700;text-transform:uppercase;margin-bottom:4px}
|
|
40
|
-
.issue.bug .tag{color:#d32f2f}
|
|
41
|
-
.issue.warn .tag{color:#f57c00}
|
|
42
|
-
.issue.info .tag{color:#1976d2}
|
|
43
|
-
.issue .desc{font-size:14px;margin:4px 0}
|
|
44
|
-
.issue .code{background:#272822;color:#f8f8f2;padding:8px 12px;border-radius:4px;font-size:13px;font-family:"SF Mono","Consolas",monospace;margin:6px 0;overflow-x:auto;white-space:pre}
|
|
45
|
-
.diff-stats{font-size:12px;color:#666;margin-left:12px}
|
|
46
|
-
.add{color:#2e7d32}
|
|
47
|
-
.del{color:#c62828}
|
|
48
|
-
.toggle-icon{color:#999;font-size:12px}
|
|
49
|
-
</style>
|
|
50
|
-
</head>
|
|
51
|
-
<body>
|
|
52
|
-
<div class="container">
|
|
53
|
-
<h1>🔍 Code Review Report</h1>
|
|
54
|
-
|
|
55
|
-
<div class="summary">
|
|
56
|
-
<p><strong>审查范围:</strong>git diff HEAD(4 files changed, +56, -30)</p>
|
|
57
|
-
|
|
58
|
-
<div class="summary-grid">
|
|
59
|
-
<div class="summary-item bug-count"><div class="label">BUG</div><div class="value">2</div></div>
|
|
60
|
-
<div class="summary-item warn-count"><div class="label">敏感信息</div><div class="value">1</div></div>
|
|
61
|
-
<div class="summary-item info-count"><div class="label">可维护性</div><div class="value">1</div></div>
|
|
62
|
-
<div class="summary-item info-count"><div class="label">规范</div><div class="value">1</div></div>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<div class="score fair">评分:55/100</div>
|
|
66
|
-
<p style="margin-top:8px;font-size:14px;color:#666;text-align:center">存在 2 个 BUG(运行时错误),1 个敏感信息变更,扣分严重</p>
|
|
67
|
-
|
|
68
|
-
<div class="commit-msg">
|
|
69
|
-
<strong>💡 commit message 建议:</strong><br>
|
|
70
|
-
<code>feat: 增加 reviewTimeoutMs 独立超时配置,修复 workflow sub-step 状态重置函数名错误</code>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
|
|
74
|
-
<!-- workflow-engine.ts -->
|
|
75
|
-
<div class="file-group">
|
|
76
|
-
<div class="file-header" onclick="this.nextElementSibling.classList.toggle('open')">
|
|
77
|
-
<span>
|
|
78
|
-
<span class="path">extensions/workflow-engine.ts</span>
|
|
79
|
-
<span class="diff-stats"><span class="add">+27</span> <span class="del">-11</span></span>
|
|
80
|
-
</span>
|
|
81
|
-
<span class="status">
|
|
82
|
-
<span class="badge bug">2 BUG</span>
|
|
83
|
-
<span class="badge warn">1 敏感</span>
|
|
84
|
-
<span class="toggle-icon">▶</span>
|
|
85
|
-
</span>
|
|
86
|
-
</div>
|
|
87
|
-
<div class="file-body">
|
|
88
|
-
<div class="issue bug">
|
|
89
|
-
<div class="tag">🛑 BUG · 最高优先级</div>
|
|
90
|
-
<div class="desc"><strong>调用已重命名的函数导致 ReferenceError</strong></div>
|
|
91
|
-
<div class="desc">第 1193-1194 行调用 <code>setWidgetSubStepStatus()</code>,但该函数在第 783 行被重命名为 <code>resetWidgetSubStep()</code>(参数从 (status) 变为固定 "pending" 并增加重置 detail/tools/outputs 逻辑)。旧函数名已被删除,运行时将抛出 <code>ReferenceError: setWidgetSubStepStatus is not defined</code>。</div>
|
|
92
|
-
<div class="code">// 第 1193-1194 行(executeLoopGroup)
|
|
93
|
-
setWidgetSubStepStatus(stepIndex, step.loopAgentName!, "pending");
|
|
94
|
-
setWidgetSubStepStatus(stepIndex, step.reviewAgentName!, "pending");</div>
|
|
95
|
-
<div class="desc"><strong>修复:</strong>改为调用 <code>resetWidgetSubStep(stepIndex, step.loopAgentName!)</code> 等。</div>
|
|
96
|
-
</div>
|
|
97
|
-
|
|
98
|
-
<div class="issue bug">
|
|
99
|
-
<div class="tag">🛑 BUG · 最高优先级</div>
|
|
100
|
-
<div class="desc"><strong>runAgentWithProgress 中仍调用已删除的函数</strong></div>
|
|
101
|
-
<div class="desc">第 1114 行仍调用 <code>setWidgetSubStepStatus(stepIndex, agentName, subStatus)</code>,该函数已被删除。执行单步 agent 完成后也会抛出 ReferenceError。</div>
|
|
102
|
-
<div class="code">// 第 1114 行(runAgentWithProgress 末尾)
|
|
103
|
-
setWidgetSubStepStatus(stepIndex, agentName, subStatus);</div>
|
|
104
|
-
<div class="desc"><strong>修复:</strong>将旧函数逻辑恢复,或改写为直接修改 sub-step 状态。</div>
|
|
105
|
-
</div>
|
|
106
|
-
|
|
107
|
-
<div class="issue warn">
|
|
108
|
-
<div class="tag">⚠️ 敏感信息 · 高优先级</div>
|
|
109
|
-
<div class="desc"><strong>runAgentWithProgress 中 sub-step detail 硬编码中文提醒信息</strong></div>
|
|
110
|
-
<div class="desc">新增的 <code>detail: "超时时间${formatTimeout(timeoutMs)}"</code> 是 UI 展示文本,在 engine 层硬编码中文。当工具函数输出被序列化或用于日志管道时,中文文本可能引起编码问题或混乱。建议使用英文 key(如 <code>timeoutText</code> 或分离 data/ui 层)。</div>
|
|
111
|
-
</div>
|
|
112
|
-
|
|
113
|
-
<div class="issue info">
|
|
114
|
-
<div class="tag">🔧 可维护性 · 中优先级</div>
|
|
115
|
-
<div class="desc"><strong>loop-group 未展示 timeoutMs 但 reviewer 仍有独立超时</strong></div>
|
|
116
|
-
<div class="desc"><code>executeWorkflowBackground</code> 中 loop-group 步骤的 <code>updateWidgetStep</code> 调用传入 <code>timeoutMs: undefined</code>,这合理(loop-group 的 timeout 逻辑在内部)。但 reviewTimeoutMs 并未在 widget 中展示,建议在 sub-step detail 中体现 reviewer 的独立超时。</div>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
|
|
121
|
-
<!-- dev-prompts.ts -->
|
|
122
|
-
<div class="file-group">
|
|
123
|
-
<div class="file-header" onclick="this.nextElementSibling.classList.toggle('open')">
|
|
124
|
-
<span>
|
|
125
|
-
<span class="path">extensions/dev-prompts.ts</span>
|
|
126
|
-
<span class="diff-stats"><span class="add">+16</span> <span class="del">-8</span></span>
|
|
127
|
-
</span>
|
|
128
|
-
<span class="status">
|
|
129
|
-
<span class="badge info">1 规范</span>
|
|
130
|
-
<span class="toggle-icon">▶</span>
|
|
131
|
-
</span>
|
|
132
|
-
</div>
|
|
133
|
-
<div class="file-body">
|
|
134
|
-
<div class="issue info">
|
|
135
|
-
<div class="tag">📐 规范 · 低优先级</div>
|
|
136
|
-
<div class="desc"><strong>所有 loop-group 配置的高 timeoutMs 应与 reviewTimeoutMs 有明确注释</strong></div>
|
|
137
|
-
<div class="desc">将 <code>timeoutMs</code> 从 900_000(15min)提升到 1_800_000(30min),并新增 <code>reviewTimeoutMs: 900_000</code>。改动意图明确,配置值合理。但所有步骤的 <code>timeoutMs</code> 和 <code>reviewTimeoutMs</code> 高度重复,建议提取为常量以提高可维护性。</div>
|
|
138
|
-
<div class="code">// 建议提取为命名常量
|
|
139
|
-
const LOOP_TIMEOUT_MS = 1_800_000; // 30min
|
|
140
|
-
const REVIEW_TIMEOUT_MS = 900_000; // 15min
|
|
141
|
-
const TRIM_TIMEOUT_MS = 1_200_000; // 20min</div>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
|
|
146
|
-
<!-- ui-helpers.ts -->
|
|
147
|
-
<div class="file-group">
|
|
148
|
-
<div class="file-header" onclick="this.nextElementSibling.classList.toggle('open')">
|
|
149
|
-
<span>
|
|
150
|
-
<span class="path">extensions/ui-helpers.ts</span>
|
|
151
|
-
<span class="diff-stats"><span class="add">+1</span> <span class="del">-4</span></span>
|
|
152
|
-
</span>
|
|
153
|
-
<span class="status">
|
|
154
|
-
<span class="badge info">1 可维护性</span>
|
|
155
|
-
<span class="toggle-icon">▶</span>
|
|
156
|
-
</span>
|
|
157
|
-
</div>
|
|
158
|
-
<div class="file-body">
|
|
159
|
-
<div class="issue info">
|
|
160
|
-
<div class="tag">🔧 可维护性 · 中优先级</div>
|
|
161
|
-
<div class="desc"><strong>formatTimeout 改为 export,移除 running 状态下强制显示「第 1 次循环」的特判</strong></div>
|
|
162
|
-
<div class="desc"><code>formatTimeout</code> 被导出供 workflow-engine 使用,改动合理。移除 isRunning 分支的「第 1 次循环」硬编码,改为由 engine 层通过 <code>updateWidgetStep</code> 及时更新 <code>loopCount</code>,设计更清晰。pending 状态仍显示「第 0 次循环」,语义明确。</div>
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
</div>
|
|
166
|
-
|
|
167
|
-
<!-- .gitignore -->
|
|
168
|
-
<div class="file-group">
|
|
169
|
-
<div class="file-header" onclick="this.nextElementSibling.classList.toggle('open')">
|
|
170
|
-
<span>
|
|
171
|
-
<span class="path">.gitignore</span>
|
|
172
|
-
<span class="diff-stats"><span class="del">-4</span></span>
|
|
173
|
-
</span>
|
|
174
|
-
<span class="status">
|
|
175
|
-
<span class="badge warn">1 敏感</span>
|
|
176
|
-
<span class="toggle-icon">▶</span>
|
|
177
|
-
</span>
|
|
178
|
-
</div>
|
|
179
|
-
<div class="file-body">
|
|
180
|
-
<div class="issue warn">
|
|
181
|
-
<div class="tag">⚠️ 敏感信息 · 高优先级</div>
|
|
182
|
-
<div class="desc"><strong>移除 pi-review/ 和 pi-dev-output/ 的 gitignore 规则</strong></div>
|
|
183
|
-
<div class="desc">提交移除了 <code>pi-review/</code> 和 <code>pi-dev-output/</code> 的 ignore 规则。这两个目录是 pi 框架的产物输出目录,包含审查报告、构建产物等。如果属于不应提交到版本控制的生成产物,应保留 ignore 规则。请确认这是有意的行为(例如要将产物纳入版本管理),或是误删。</div>
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
</div>
|
|
189
|
-
<script>
|
|
190
|
-
document.querySelectorAll('.file-body.open').forEach(el=>el.classList.remove('open'));
|
|
191
|
-
document.querySelectorAll('.file-header').forEach(h=>{
|
|
192
|
-
h.addEventListener('click',function(){const b=this.querySelector('.toggle-icon');b.textContent=b.textContent==='▶'?'▼':'▶';});
|
|
193
|
-
});
|
|
194
|
-
</script>
|
|
195
|
-
</body>
|
|
196
|
-
</html>
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# 代码审查报告 — workflow-engine 双 Bug 修复
|
|
2
|
-
|
|
3
|
-
**审查时间**: 2026-05-20
|
|
4
|
-
**审查范围**: `extensions/workflow-engine.ts` + `tests/test-workflow-engine-bugs.mjs`
|
|
5
|
-
**审查背景**: 修复 executeLoopGroup 缺少 exitCode 检查 (Bug A) 和 setTimeout cleanupWidget 竞态条件 (Bug B)
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 严重等级汇总
|
|
10
|
-
|
|
11
|
-
| 等级 | 数量 |
|
|
12
|
-
|------|------|
|
|
13
|
-
| 🔴 critical | 0 |
|
|
14
|
-
| 🟡 medium | 2 |
|
|
15
|
-
| 🟢 low | 2 |
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 🟡 中等问题
|
|
20
|
-
|
|
21
|
-
### M1. `tests/test-workflow-engine.mjs` 中 deleteCheckpointFile 导出测试失败(pre-existing)
|
|
22
|
-
|
|
23
|
-
**文件**: `tests/test-workflow-engine.mjs` 第 222 行
|
|
24
|
-
**问题**: 测试断言 `source.includes("export function deleteCheckpointFile")` 但 `workflow-engine.ts` 中并没有 `deleteCheckpointFile` 函数。这不是本次修复引入的问题,而是已有测试的回归。
|
|
25
|
-
|
|
26
|
-
**建议**:
|
|
27
|
-
- 要么在 workflow-engine.ts 中添加 `deleteCheckpointFile` 的导出(如果实际需要)
|
|
28
|
-
- 要么从测试中移除这个断言(如果该函数已被移除或从未存在)
|
|
29
|
-
- 由于本次任务是"只修 bug,不做重构",建议作为独立 issue 处理
|
|
30
|
-
|
|
31
|
-
### M2. `executeLoopGroup` 中 retry 后未递归检查 agentResult
|
|
32
|
-
|
|
33
|
-
**文件**: `extensions/workflow-engine.ts` 第 1204 行
|
|
34
|
-
**问题**: 在非 full-auto 模式下,exitCode 检查分支中 retry 逻辑是:
|
|
35
|
-
```typescript
|
|
36
|
-
agentResult = await runAgentWithProgress(loopAgent, `[RETRY]\n\n${loopTask}`, ...);
|
|
37
|
-
```
|
|
38
|
-
retry 后**没有**再次检查 `agentResult.exitCode`。如果 retry 的 agent 仍然异常退出(例如退出码 1),流程会无检查地进入 review 阶段。这与超时处理的模式不一致——超时处理在 retry 后会检查 `isTimeoutResult(agentResult)` 并 fallback。
|
|
39
|
-
|
|
40
|
-
**严重程度**: 中等 — 边界情况(连续两次退出码非零),但可能导致错误结果被静默接受。
|
|
41
|
-
|
|
42
|
-
**建议**: retry 后添加 exitCode 检查:
|
|
43
|
-
```typescript
|
|
44
|
-
agentResult = await runAgentWithProgress(loopAgent, `[RETRY]\n\n${loopTask}`, ...);
|
|
45
|
-
// retry 后再次检查
|
|
46
|
-
if (agentResult.exitCode !== 0 && !isTimeoutResult(agentResult)) {
|
|
47
|
-
// fallback: 跳过此步骤
|
|
48
|
-
state.status = "skipped";
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## 🟢 低优先级问题
|
|
56
|
-
|
|
57
|
-
### L1. `cleanupWidget` 中的 `_workflowRunning = false` 与 `executeWorkflowBackground` 的 `_workflowRunning = false` 重复
|
|
58
|
-
|
|
59
|
-
**文件**: `extensions/workflow-engine.ts` 第 803 行 和第 1450 行
|
|
60
|
-
**问题**: `executeWorkflowBackground` 在步骤循环完成后设置 `_workflowRunning = false`(第 1450 行),然后新建的定时器在 5 秒后调用 `cleanupWidget()` 再次设置 `_workflowRunning = false`(第 803 行)。虽然不会影响正确性(重复赋值相同值),但逻辑上冗余。
|
|
61
|
-
|
|
62
|
-
**建议**: 可以移除 `executeWorkflowBackground` 中的 `_workflowRunning = false`,让 `cleanupWidget` 统一管理该变量,避免逻辑分散。
|
|
63
|
-
|
|
64
|
-
### L2. 测试中 `simulateBugAFix` 的 retry 返回语义不精确
|
|
65
|
-
|
|
66
|
-
**文件**: `tests/test-workflow-engine-bugs.mjs` 第 147-174 行
|
|
67
|
-
**问题**: `simulateBugAFix` 函数在非 full-auto 模式下返回 `"retry"`,但这个返回值没有区分"用户选择了跳过"、"用户选择了重试"、"用户选择了取消"三种情况。虽然作为模拟测试够用,但语义不够精确。
|
|
68
|
-
|
|
69
|
-
**建议**: 可以用更精确的返回值(如 `"retry"`、`"skip"`、`"cancel"`),或使用对象形式返回。
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## 维护建议
|
|
74
|
-
|
|
75
|
-
1. 修复测试 `test-workflow-engine.mjs` 中 deleteCheckpointFile 的断言——这会导致 CI 失败。
|
|
76
|
-
2. 考虑在 retry 后添加二次 exitCode 检查(M2),以增强鲁棒性。
|
|
77
|
-
3. 整体修复方案符合"最小化改动"要求,代码阅读性和可维护性好。
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
## 测试结果
|
|
82
|
-
|
|
83
|
-
- ✅ `node tests/test-workflow-engine-bugs.mjs` — **50/50 通过**(新测试全部通过)
|
|
84
|
-
- ⚠️ `node tests/test-workflow-engine.mjs` — **73/74 通过**(1 个 pre-existing 失败,非本次改动导致)
|
|
85
|
-
- ✅ `git diff HEAD` 确认变更仅限于计划中指定的范围
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## 审查结论
|
|
90
|
-
|
|
91
|
-
本次修复正确地解决了两个 Bug。代码实现与实施计划一致,没有引入回归。存在 2 个中等问题和 2 个低优先级问题,但没有严重问题。
|