@ghyper9023/pi-dev-workflow 0.4.1 → 0.4.3
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/.pi-dev-output/pi-grill/answers/answer-mpfe77f1-20260521-1913.md +58 -0
- package/.pi-dev-output/pi-grill/answers/answer-mpfh37wu-20260521-2034.md +13 -0
- package/.pi-dev-output/pi-grill/answers/answer-mpfi5q4c-20260521-2104.md +13 -0
- package/.pi-dev-output/pi-grill/answers/answer-mpfizccb-20260521-2127.md +13 -0
- package/.pi-dev-output/pi-grill/answers/answer-mpfjk78k-20260521-2143.md +13 -0
- package/.pi-dev-output/pi-grill/answers/answer-mpfttme1-20260522-0230.md +13 -0
- package/.pi-dev-output/pi-grill/questions/questions-mpfdz1tz-20260521-1907.json +94 -0
- package/.pi-dev-output/pi-plans/20260521-113000-fix-loopcount-timeout.md +215 -0
- package/.pi-dev-output/pi-plans/20260521-1730-grill-input-wrap-back-fix.md +240 -0
- package/.pi-dev-output/pi-plans/20260521-230000-fix-timeout-display-loopcount-gitdiff.md +253 -0
- package/.pi-dev-output/pi-plans/20260521-230500-esc-double-press-confirm-workflow.md +137 -0
- package/.pi-dev-output/pi-plans/20260521-235000-fix-gitdiff-loopcount.md +258 -0
- package/.pi-dev-output/pi-plans/20260522-113000-grill-left-arrow-fix.md +274 -0
- package/.pi-dev-output/pi-review/html/20260521-2305-review-workflow-index.html +196 -0
- package/.pi-dev-output/pi-review/md/review-20260520-100000.md +91 -0
- package/.pi-dev-output/pi-review/md/review-20260521-140000.md +191 -0
- package/.pi-dev-output/pi-review/md/review-20260521-190000.md +189 -0
- package/.pi-dev-output/pi-review/md/review-20260521-204500.md +241 -0
- package/.pi-dev-output/pi-review/md/review-20260521-214500.md +270 -0
- package/.pi-dev-output/pi-review/md/review-20260521-215158.md +214 -0
- package/.pi-dev-output/pi-review/md/review-20260521-234500.md +201 -0
- package/.pi-dev-output/pi-review/md/review-20260521-235500.md +422 -0
- package/.pi-dev-output/pi-review/md/review-20260522-000000.md +212 -0
- package/.pi-dev-output/pi-review/md/review-20260522-003000.md +377 -0
- package/.pi-dev-output/pi-review/md/review-20260522-003500.md +296 -0
- package/.pi-dev-output/pi-review/md/review-20260522-105000.md +166 -0
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-113000-fix-loopcount-timeout.json +402 -0
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-1730-grill-input-wrap-back-fix.json +447 -0
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-230000-fix-timeout-display-loopcount-gitdiff.json +708 -0
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-230500-esc-double-press-confirm-workflow.json +365 -0
- package/.pi-dev-output/pi-workflow/checkpoint-20260521-235000-fix-gitdiff-loopcount.json +395 -0
- package/.pi-dev-output/pi-workflow/checkpoint-20260522-113000-grill-left-arrow-fix.json +473 -0
- package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfhyxc5.json +30 -0
- package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfi2unc.json +49 -0
- package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfi382e.json +59 -0
- package/.pi-dev-output/pi-workflow/checkpoint-archive-mpfi5r22.json +76 -0
- package/.version/RELEASE-v0.4.2.md +31 -0
- package/.version/RELEASE-v0.4.3.md +42 -0
- package/README.md +21 -3
- package/extensions/dev-prompts.ts +16 -8
- package/extensions/grill-me-agent.ts +74 -8
- package/extensions/ui-helpers.ts +59 -7
- package/extensions/workflow-engine.ts +80 -32
- package/package.json +1 -1
- package/tests/test-loopcount-timeout-fix.mjs +336 -0
- package/themes/oh-my-pi-titanium.json +90 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 2,
|
|
3
|
+
"createdAt": "2026-05-21T12:59:04.215Z",
|
|
4
|
+
"updatedAt": "2026-05-21T13:02:24.734Z",
|
|
5
|
+
"prompt": "[fix] 修复 当前目录 中的 问题: 1.commit提交哈希:01413c9edb1dee3af525bfabdb4f55de7f0a3b4b里面的改动, 现在的超时时间显示位置: ``` ▶ ⠏ 🔧 修复代码 → 审查 (52.6s) |__ ⠏ worker · |__ 超时时间60m ``` 2.问题2,`第 0 次循环`只在排队时候显示了,等loop组开始工作连`第 1 次循环`的提示都不见了 2.根据git diff --name-status获取的文件变动信息,是通过正则筛选的,但很明显git diff --name-status给的结果如下: ```git diff --name-statusM .gitignoreM Cargo.lock ``` 非常的规整,正则现在会识别出来一些无关东西,判断不出来是哪里来的,git diff --name-status直接拿到的信息`X 空格 filepath 换行`只需要简单的string处理即可,无需复杂正则,而且正则效果不好。\n\n**背景**:\n- 输入:见代码上下文\n- 预期行为:预期: 1.超时时间显示位置: ``` ▶ ⠏ 🔧 修复代码 → 审查 (52.6s/超时时间60m ) |__ ⠏ worker · ``` 超时时间应该跟在计时的后面(当前计时/超时时间xm) 2.恢复`第 x 次循环`的显示,上次fix的模板是x计数不更新,但是修改后直接导致`第 x 次循环`不见了,恢复显示并修复x计数和更新ui逻辑。 3.将正则匹配改成普通的string处理 3.严格完成以上任务要求,不得破坏原有其他功能,不得偷懒省略,以最小改动实现。\n- 当前错误:请描述当前错误\n**任务**:\n1. 不要仅仅消除报错(Suppress),要解决根本原因。\n2. 先读取相关代码和日志,诊断根因(多步推理,不要先给结论)。\n3. 提供至少一种修复方案,并说明为什么这样做。\n4. 编写测试用例复现该 Bug 并确认修复有效。\n**输出**:提供 diff 和两句话的根因分析。\n**约束**:只修 bug,不做重构;最小化改动;不要假设错误是微不足道的。",
|
|
6
|
+
"mode": "attended",
|
|
7
|
+
"steps": [
|
|
8
|
+
{
|
|
9
|
+
"status": "done",
|
|
10
|
+
"durationMs": 15309
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"status": "pending"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"currentStepIndex": 0,
|
|
17
|
+
"loopCounts": {},
|
|
18
|
+
"planFilePath": ".pi-dev-output/pi-plans/20260521-1730-grill-input-wrap-back-fix.md",
|
|
19
|
+
"taskSummary": "fix - 修复 当前目录 中的 问题: 1.commit提交哈希:01413c9edb1dee3af525bfabdb4f55de7f0a3b4b里面的改动, 现在的超时时间显示位置: ``` ▶ ⠏ 🔧 修复代码 → 审查 (52.6s) |__ ⠏ worker · |__ 超时时间60m ``` 2.问题2,`第 0 次循环`只在排队时候显示了,等loop组开始工作连`第 1 次循环`的提示都不见了 2.根据git diff --name-status获取的文件变动信息,是通过正则筛选的,但很明显git diff --name-status给的结果如下: ```git diff --name-statusM .gitignoreM Cargo.lock ``` 非常的规整,正则现在会识别出来一些无关东西,判断不出来是哪里来的,git diff --name-status直接拿到的信息`X 空格 filepath 换行`只需要简单的string处理即可,无需复杂正则,而且正则效果不好。",
|
|
20
|
+
"workflowType": "自定义",
|
|
21
|
+
"fileChanges": [
|
|
22
|
+
{
|
|
23
|
+
"agent": "planner",
|
|
24
|
+
"stepIndex": 0,
|
|
25
|
+
"type": "new",
|
|
26
|
+
"filePath": ".pi-dev-output/pi-workflow/checkpoint-archive-mpfhyxc5.json",
|
|
27
|
+
"timestamp": "2026-05-21T12:59:04.213Z"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"agent": "planner",
|
|
31
|
+
"stepIndex": 0,
|
|
32
|
+
"type": "new",
|
|
33
|
+
"filePath": ".pi-dev-output/pi-workflow/checkpoint-archive-mpfi382e.json",
|
|
34
|
+
"timestamp": "2026-05-21T13:02:24.733Z"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"subAgentRuns": 2,
|
|
38
|
+
"filesModified": 0,
|
|
39
|
+
"filesCreated": 2,
|
|
40
|
+
"agentRunHistory": [
|
|
41
|
+
{
|
|
42
|
+
"agent": "planner",
|
|
43
|
+
"stepIndex": 0,
|
|
44
|
+
"startedAt": "2026-05-21T12:58:31.215Z",
|
|
45
|
+
"durationMs": 32941,
|
|
46
|
+
"exitCode": 143,
|
|
47
|
+
"toolCount": 0
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"agent": "planner",
|
|
51
|
+
"stepIndex": 0,
|
|
52
|
+
"startedAt": "2026-05-21T13:02:09.425Z",
|
|
53
|
+
"durationMs": 15259,
|
|
54
|
+
"exitCode": 143,
|
|
55
|
+
"toolCount": 0
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"baseline": [
|
|
59
|
+
{
|
|
60
|
+
"path": ".pi-dev-output/pi-grill/answers/answer-mpfhy5x2-20260521-2058.md",
|
|
61
|
+
"hash": "609a8f2ace3d8a0d2ead3b31cb1027223c819336"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"path": ".pi-dev-output/pi-grill/answers/answer-mpfi2th1-20260521-2102.md",
|
|
65
|
+
"hash": "e8cd2855d32632a6486d8c793af320ed6cd74835"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"path": ".pi-dev-output/pi-workflow/checkpoint-archive-mpfhyxc5.json",
|
|
69
|
+
"hash": "9041bd1be486c6141080e623760514026c6b409b"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"path": ".pi-dev-output/pi-workflow/checkpoint-archive-mpfi2unc.json",
|
|
73
|
+
"hash": "72c8520e2d0784be27b4482b03895f82c1239a89"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Release v0.4.2
|
|
2
|
+
|
|
3
|
+
## 🚀 Features
|
|
4
|
+
|
|
5
|
+
- **Esc 双击确认停止机制** — 工作流运行期间,首次按 Esc 提示再次确认,3 秒内双击才终止工作流,避免误触中断
|
|
6
|
+
- **Grill 交互增强**
|
|
7
|
+
- 选项标签超长时自动截断,完整文本显示在 description 中
|
|
8
|
+
- 输入框上方增加实时换行预览区域
|
|
9
|
+
- 左方向键支持返回上一题
|
|
10
|
+
- 优化导航提示文字
|
|
11
|
+
- **独立超时配置** — reviewer 子代理新增 `reviewTimeoutMs` 独立超时,不再与 worker 共用超时时间(trimmer: 20min, worker: 30min, reviewer: 15min)
|
|
12
|
+
- **新主题** — 添加 `oh-my-pi-titanium` 主题
|
|
13
|
+
- **循环计数显示** — loop-group 在工作时正确显示 `第 N 次循环`,时序与执行同步
|
|
14
|
+
|
|
15
|
+
## 🐛 Bug Fixes
|
|
16
|
+
|
|
17
|
+
- **Git diff 解析** — 改用简单字符串分割替代正则匹配,避免解析出 `checkpoint-${planId}.json`、`[],\n\t\t\t\toutputs:` 等垃圾条目
|
|
18
|
+
- **循环计数器时序** — 修复循环计数从 planner 进入 loop 时显示错误(0→1→1→2 的问题),现正确为 0→1→2→3
|
|
19
|
+
- **超时显示位置** — 超时时间从 loop 组迁移到具体的子代理步骤上,显示更准确
|
|
20
|
+
- **工作流状态重置** — 修复 sub-step 状态重置函数名错误
|
|
21
|
+
|
|
22
|
+
## 🔧 Refactor
|
|
23
|
+
|
|
24
|
+
- UI 循环计数显示重构,子步骤时长/超时信息改进
|
|
25
|
+
- 文件变更检测支持 git 标准状态码(M/A/D)
|
|
26
|
+
|
|
27
|
+
## 📦 Files Changed
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
41 files changed, 7351 insertions(+), 62 deletions(-)
|
|
31
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Release v0.4.3
|
|
2
|
+
|
|
3
|
+
## 🐛 Bug Fixes
|
|
4
|
+
|
|
5
|
+
### Grill 选项完整文本不可见(选项不换行)
|
|
6
|
+
选项列表中超长文本会被截断显示 `...`,但完整文本只能通过 `description` 列查看,而该列**单行显示且宽度受限**,导致长文本依旧被截断,用户无法阅读完整选项内容。
|
|
7
|
+
|
|
8
|
+
**修复方案**:移除 `description` 列,改为**选项预览面板**。
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
之前: (a) a[推荐] 最终用户... a[推荐] 最终用户(运维/... ← 灰色列单行截断
|
|
12
|
+
之后: (a) a[推荐] 最终用户(运维/系统管理员)——需要快...
|
|
13
|
+
a[推荐] 最终用户(运维/系统管理员)——需要快速部署
|
|
14
|
+
rksh-server、通过客户端连接远程服务器、使用终端和 ← 预览面板自动换行
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- 选项列表下方新增 `Text` 预览面板,展示当前选中选项的完整文本
|
|
18
|
+
- 使用 `wrapTextWithAnsi` 实现自动换行,灰色 `dim` 样式
|
|
19
|
+
- 跟随 `↑↓` 导航实时更新,初始化时默认显示第一个选项
|
|
20
|
+
- 选中 `✏️ 自定义输入` 或 `← 返回上一题` 时预览自动清空
|
|
21
|
+
|
|
22
|
+
### Grill 左方向键与输入框光标左移冲突
|
|
23
|
+
选项列表中 `←` 键被拦截用于"返回上一题",导致自定义输入框中 `←` 键无法正常移动光标编辑文本。
|
|
24
|
+
|
|
25
|
+
**修复方案**:将返回上一题的快捷键从 `←` 改为 `Ctrl+Shift+←`,输入框中 `←`/`→` 恢复为光标移动。
|
|
26
|
+
|
|
27
|
+
| 场景 | 之前 | 之后 |
|
|
28
|
+
|------|------|------|
|
|
29
|
+
| 选项列表返回 | `←` | `Ctrl+Shift+←` |
|
|
30
|
+
| 输入框返回 | `←`(吃掉键,无法编辑) | `Ctrl+Shift+←` |
|
|
31
|
+
| 输入框光标 | ❌ 左移被拦截 | ✅ `←`/`→` 正常移动 |
|
|
32
|
+
|
|
33
|
+
## 📦 Files Changed
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
2 files changed, 65 insertions(+), 15 deletions(-)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- `extensions/grill-me-agent.ts` — 预览面板 + 动态截断宽度 + 键位修正
|
|
42
|
+
- `extensions/ui-helpers.ts` — 移除 `Key.left` 拦截,恢复输入框光标左移
|
package/README.md
CHANGED
|
@@ -56,7 +56,8 @@ pi-package/
|
|
|
56
56
|
│ ├── git-commands.ts # git-sub-agent 命令
|
|
57
57
|
│ ├── grill-me-agent.ts # Grill + PRD 运行时:设计评审、PRD 生成
|
|
58
58
|
│ ├── sub-agents.ts # 子代理系统:git-sub-agent + review-sub-agent
|
|
59
|
-
│
|
|
59
|
+
│ ├── workflow-engine.ts # 工作流编排引擎(由 dev-prompts.ts 引入)
|
|
60
|
+
│ └── ui-helpers.ts # TUI 组件构建器(Select/Confirm/Input/Widget)
|
|
60
61
|
└── themes/
|
|
61
62
|
└── claude-code-theme.json # Claude Code CLI 风格主题
|
|
62
63
|
```
|
|
@@ -342,11 +343,22 @@ Grill("拷问式评审")是提交方案前由 AI sub-agent 从多个维度
|
|
|
342
343
|
### 交互形式
|
|
343
344
|
|
|
344
345
|
每道问题在 TUI 中以 SelectList 呈现:
|
|
345
|
-
- 选项列表:`(a) ...` `(b) ...` `(c)
|
|
346
|
+
- 选项列表:`(a) ...` `(b) ...` `(c) ...`(完整显示,不截断)
|
|
346
347
|
- 最末选项:`✏️ 自定义输入` — 可输入自己的回答
|
|
347
|
-
- 导航:↑↓ 选择,Enter
|
|
348
|
+
- 导航:↑↓ 选择,Enter 确认
|
|
349
|
+
- 返回:`Ctrl+Shift+←` 返回上一题(输入框中也可用 `Ctrl+Shift+←` 返回上一步)
|
|
350
|
+
- 跳过:`Ctrl+Shift+→` 在输入框中跳过当前输入并继续
|
|
351
|
+
- 取消:Esc 取消全部评审
|
|
348
352
|
- 进度:标题栏显示 `问题 3/18`
|
|
349
353
|
|
|
354
|
+
### 输入框特性
|
|
355
|
+
|
|
356
|
+
自定义输入和 `/dev-*` 向导中的输入框支持:
|
|
357
|
+
- **实时换行预览**:输入超长文本时,输入框上方会显示完整的换行预览(灰色文字),实时跟随输入变化
|
|
358
|
+
- **光标操作**:`←` 和 `→` 键可正常移动光标编辑已有内容(不触发返回)
|
|
359
|
+
- **返回上一题**:`Ctrl+Shift+←` 在输入框中返回上一题
|
|
360
|
+
- **跳过输入**:`Ctrl+Shift+→` 提交当前内容(可为空)并继续
|
|
361
|
+
|
|
350
362
|
## PRD 文档生成
|
|
351
363
|
|
|
352
364
|
仅 `/dev-feat` 命令在执行完成后自动触发 PRD 生成。其余 `/dev-*` 命令不包含此阶段。
|
|
@@ -407,6 +419,12 @@ A: 在 `extensions/grill-me-agent.ts` 中修改对应 AgentDef 的 `systemPrompt
|
|
|
407
419
|
**Q: 评审结果是否影响原提示词?**
|
|
408
420
|
A: 评审问答以「设计评审记录」区块追加到原提示词末尾,原提示词内容不变。主代理执行时会同时参考原需求 + 评审中确认的决策。
|
|
409
421
|
|
|
422
|
+
**Q: Grill 中如何返回上一题?**
|
|
423
|
+
A: 使用 `Ctrl+Shift+←` 返回上一题(在选项列表和自定义输入框中均适用)。裸 `←` 键在选项列表中无效果,在输入框中用于光标左移编辑文本。
|
|
424
|
+
|
|
425
|
+
**Q: 自定义输入框中的键位有哪些?**
|
|
426
|
+
A: `Enter` 确认提交,`Esc` 取消返回选项列表,`Ctrl+Shift+←` 返回上一题,`Ctrl+Shift+→` 跳过输入并继续,方向键 `←`/`→` 用于移动光标编辑已有文本。
|
|
427
|
+
|
|
410
428
|
**Q: `grill-with-docs` skill 和 `/dev-*` 内置的 Grill 有什么区别?**
|
|
411
429
|
A: `grill-with-docs` 是可独立调用的 skill(`/skill:grill-with-docs`),侧重领域术语统一和文档同步(更新 CONTEXT.md、创建 ADR)。`/dev-*` 内置的 Grill 是任务向导的一部分,侧重方案评审,不涉及文档持久化。
|
|
412
430
|
|
|
@@ -381,7 +381,8 @@ const FEAT_WORKFLOW_STEPS: WorkflowStepDef[] = [
|
|
|
381
381
|
loopAgentName: "worker",
|
|
382
382
|
reviewAgentName: "reviewer",
|
|
383
383
|
maxLoops: 3,
|
|
384
|
-
timeoutMs:
|
|
384
|
+
timeoutMs: 1_800_000,
|
|
385
|
+
reviewTimeoutMs: 900_000,
|
|
385
386
|
},
|
|
386
387
|
{
|
|
387
388
|
id: "trimmer-reviewer",
|
|
@@ -390,7 +391,8 @@ const FEAT_WORKFLOW_STEPS: WorkflowStepDef[] = [
|
|
|
390
391
|
loopAgentName: "trimmer",
|
|
391
392
|
reviewAgentName: "reviewer",
|
|
392
393
|
maxLoops: 3,
|
|
393
|
-
timeoutMs:
|
|
394
|
+
timeoutMs: 1_200_000,
|
|
395
|
+
reviewTimeoutMs: 900_000,
|
|
394
396
|
},
|
|
395
397
|
{
|
|
396
398
|
id: "docWriter",
|
|
@@ -416,7 +418,8 @@ const FIX_WORKFLOW_STEPS: WorkflowStepDef[] = [
|
|
|
416
418
|
loopAgentName: "worker",
|
|
417
419
|
reviewAgentName: "reviewer",
|
|
418
420
|
maxLoops: 3,
|
|
419
|
-
timeoutMs:
|
|
421
|
+
timeoutMs: 1_800_000,
|
|
422
|
+
reviewTimeoutMs: 900_000,
|
|
420
423
|
},
|
|
421
424
|
{
|
|
422
425
|
id: "docWriter",
|
|
@@ -442,7 +445,8 @@ const REFACTOR_WORKFLOW_STEPS: WorkflowStepDef[] = [
|
|
|
442
445
|
loopAgentName: "worker",
|
|
443
446
|
reviewAgentName: "reviewer",
|
|
444
447
|
maxLoops: 3,
|
|
445
|
-
timeoutMs:
|
|
448
|
+
timeoutMs: 1_800_000,
|
|
449
|
+
reviewTimeoutMs: 900_000,
|
|
446
450
|
},
|
|
447
451
|
{
|
|
448
452
|
id: "trimmer-reviewer",
|
|
@@ -451,7 +455,8 @@ const REFACTOR_WORKFLOW_STEPS: WorkflowStepDef[] = [
|
|
|
451
455
|
loopAgentName: "trimmer",
|
|
452
456
|
reviewAgentName: "reviewer",
|
|
453
457
|
maxLoops: 3,
|
|
454
|
-
timeoutMs:
|
|
458
|
+
timeoutMs: 1_200_000,
|
|
459
|
+
reviewTimeoutMs: 900_000,
|
|
455
460
|
},
|
|
456
461
|
];
|
|
457
462
|
|
|
@@ -470,7 +475,8 @@ const PERF_WORKFLOW_STEPS: WorkflowStepDef[] = [
|
|
|
470
475
|
loopAgentName: "worker",
|
|
471
476
|
reviewAgentName: "reviewer",
|
|
472
477
|
maxLoops: 3,
|
|
473
|
-
timeoutMs:
|
|
478
|
+
timeoutMs: 1_800_000,
|
|
479
|
+
reviewTimeoutMs: 900_000,
|
|
474
480
|
},
|
|
475
481
|
];
|
|
476
482
|
|
|
@@ -489,7 +495,8 @@ const TEST_WORKFLOW_STEPS: WorkflowStepDef[] = [
|
|
|
489
495
|
loopAgentName: "worker",
|
|
490
496
|
reviewAgentName: "reviewer",
|
|
491
497
|
maxLoops: 3,
|
|
492
|
-
timeoutMs:
|
|
498
|
+
timeoutMs: 1_800_000,
|
|
499
|
+
reviewTimeoutMs: 900_000,
|
|
493
500
|
},
|
|
494
501
|
];
|
|
495
502
|
|
|
@@ -518,7 +525,8 @@ const STYLE_WORKFLOW_STEPS: WorkflowStepDef[] = [
|
|
|
518
525
|
loopAgentName: "trimmer",
|
|
519
526
|
reviewAgentName: "reviewer",
|
|
520
527
|
maxLoops: 2,
|
|
521
|
-
timeoutMs:
|
|
528
|
+
timeoutMs: 1_200_000,
|
|
529
|
+
reviewTimeoutMs: 900_000,
|
|
522
530
|
},
|
|
523
531
|
];
|
|
524
532
|
|
|
@@ -21,6 +21,10 @@ import {
|
|
|
21
21
|
SelectList,
|
|
22
22
|
Text,
|
|
23
23
|
Spacer,
|
|
24
|
+
matchesKey,
|
|
25
|
+
Key,
|
|
26
|
+
truncateToWidth,
|
|
27
|
+
wrapTextWithAnsi,
|
|
24
28
|
type SelectItem,
|
|
25
29
|
} from "@earendil-works/pi-tui";
|
|
26
30
|
import { uiSelect, uiConfirm, uiInput } from "./ui-helpers";
|
|
@@ -589,7 +593,23 @@ export async function runGrillPhase(
|
|
|
589
593
|
};
|
|
590
594
|
}
|
|
591
595
|
|
|
592
|
-
/**
|
|
596
|
+
/**
|
|
597
|
+
* Show a single grill question as TUI SelectList with option items and custom input entry.
|
|
598
|
+
*
|
|
599
|
+
* Navigation:
|
|
600
|
+
* - ↑↓ 选择, Enter 确认, Esc 取消全部评审
|
|
601
|
+
* - Ctrl+Shift+← 返回上一题(仅当 backable=true 且 currentIndex > 1 时生效)
|
|
602
|
+
* - 选择 "✏️ 自定义输入" 进入文本输入模式
|
|
603
|
+
*
|
|
604
|
+
* @param ctx - Extension command context for TUI rendering
|
|
605
|
+
* @param q - The grill question to display
|
|
606
|
+
* @param currentIndex - 1-based index of current question
|
|
607
|
+
* @param totalCount - Total number of questions
|
|
608
|
+
* @param titlePrefix - Prefix for the question title bar
|
|
609
|
+
* @param backable - Whether navigating back to previous question is allowed
|
|
610
|
+
* @param previousAnswer - Previous answer to pre-fill as "上次选择" marker
|
|
611
|
+
* @returns Selected option text, "__BACK__" for back navigation, or null for cancel
|
|
612
|
+
*/
|
|
593
613
|
async function showQuestionTUI(
|
|
594
614
|
ctx: ExtensionCommandContext,
|
|
595
615
|
q: GrillQuestion,
|
|
@@ -599,12 +619,21 @@ async function showQuestionTUI(
|
|
|
599
619
|
backable = false,
|
|
600
620
|
previousAnswer?: string,
|
|
601
621
|
): Promise<string | null> {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
622
|
+
// 根据终端宽度计算截断宽度
|
|
623
|
+
const termWidth = process.stdout.columns || 120;
|
|
624
|
+
const maxOptWidth = Math.min(termWidth - 12, 100);
|
|
625
|
+
const selectItems: SelectItem[] = q.options.map((opt, i) => {
|
|
626
|
+
const prefix = `(${String.fromCharCode(97 + i)}) `;
|
|
627
|
+
const label = opt === previousAnswer
|
|
628
|
+
? `${prefix}${opt} - 上次选择`
|
|
629
|
+
: `${prefix}${opt}`;
|
|
630
|
+
const truncated = truncateToWidth(label, maxOptWidth, "...");
|
|
631
|
+
return {
|
|
632
|
+
value: `opt-${i}`,
|
|
633
|
+
label: truncated,
|
|
634
|
+
// 完整文本由下方的预览面板展示(支持换行),description 列无法换行故移除
|
|
635
|
+
};
|
|
636
|
+
});
|
|
608
637
|
|
|
609
638
|
const customLabel = previousAnswer && !q.options.includes(previousAnswer)
|
|
610
639
|
? `✏️ 自定义输入 - 上次选择`
|
|
@@ -639,14 +668,46 @@ async function showQuestionTUI(
|
|
|
639
668
|
description: (s) => theme.fg("muted", s),
|
|
640
669
|
scrollInfo: (s) => theme.fg("dim", s),
|
|
641
670
|
noMatch: (s) => theme.fg("warning", s),
|
|
671
|
+
}, {
|
|
672
|
+
minPrimaryColumnWidth: 30,
|
|
673
|
+
maxPrimaryColumnWidth: maxOptWidth + 2,
|
|
674
|
+
truncatePrimary: ({ text, maxWidth }) => truncateToWidth(text, maxWidth, "..."),
|
|
642
675
|
});
|
|
643
676
|
selectList.onSelect = (item) => done(item.value);
|
|
644
677
|
selectList.onCancel = () => done(null);
|
|
645
678
|
container.addChild(selectList);
|
|
646
679
|
|
|
680
|
+
// 完整选项预览面板(支持换行,展示当前选中选项的完整文本)
|
|
681
|
+
const previewWidth = Math.max(30, termWidth - 8);
|
|
682
|
+
const previewText = new Text("", 0, 0);
|
|
683
|
+
container.addChild(new Spacer(1));
|
|
684
|
+
container.addChild(previewText);
|
|
685
|
+
|
|
686
|
+
// 初始化预览为第一个选项
|
|
687
|
+
if (q.options.length > 0) {
|
|
688
|
+
const initialWrapped = wrapTextWithAnsi(q.options[0], previewWidth);
|
|
689
|
+
previewText.setText(
|
|
690
|
+
initialWrapped.map(l => theme.fg("dim", ` ${l}`)).join("\n")
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
selectList.onSelectionChange = (item) => {
|
|
695
|
+
if (item.value.startsWith("opt-")) {
|
|
696
|
+
const idx = parseInt(item.value.replace("opt-", ""), 10);
|
|
697
|
+
const fullText = q.options[idx];
|
|
698
|
+
const wrapped = wrapTextWithAnsi(fullText, previewWidth);
|
|
699
|
+
previewText.setText(
|
|
700
|
+
wrapped.map(l => theme.fg("dim", ` ${l}`)).join("\n")
|
|
701
|
+
);
|
|
702
|
+
} else {
|
|
703
|
+
previewText.setText("");
|
|
704
|
+
}
|
|
705
|
+
tui.requestRender();
|
|
706
|
+
};
|
|
707
|
+
|
|
647
708
|
container.addChild(new Spacer(1));
|
|
648
709
|
const hint = backable && currentIndex > 1
|
|
649
|
-
? " ↑↓ 导航 • Enter 选择 •
|
|
710
|
+
? " ↑↓ 导航 • Enter 选择 • Ctrl+Shift+← 返回上一题 • Esc 取消全部评审"
|
|
650
711
|
: " ↑↓ 导航 • Enter 选择 • Esc 取消全部评审";
|
|
651
712
|
container.addChild(
|
|
652
713
|
new Text(theme.fg("dim", hint), 0, 0),
|
|
@@ -657,6 +718,11 @@ async function showQuestionTUI(
|
|
|
657
718
|
render: (w) => container.render(w),
|
|
658
719
|
invalidate: () => container.invalidate(),
|
|
659
720
|
handleInput: (data) => {
|
|
721
|
+
// Ctrl+Shift+← → 返回上一题(SelectList 不处理该键,需自行拦截)
|
|
722
|
+
if (backable && currentIndex > 1 && matchesKey(data, Key.ctrlShift("left"))) {
|
|
723
|
+
done("__BACK__");
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
660
726
|
selectList.handleInput(data);
|
|
661
727
|
tui.requestRender();
|
|
662
728
|
},
|
package/extensions/ui-helpers.ts
CHANGED
|
@@ -205,7 +205,12 @@ export function uiConfirm(
|
|
|
205
205
|
// ── Input (replaces ctx.ui.input) ────────────────────────────
|
|
206
206
|
|
|
207
207
|
/**
|
|
208
|
-
* Show an input dialog with proper wrapping.
|
|
208
|
+
* Show an input dialog with proper wrapping and live preview.
|
|
209
|
+
*
|
|
210
|
+
* Features:
|
|
211
|
+
* - 实时换行预览:输入框上方显示完整的换行预览(跟随输入实时更新)
|
|
212
|
+
* - 方向键 ←/→ 可正常移动光标编辑已有内容
|
|
213
|
+
*
|
|
209
214
|
* Returns the entered string, or BACK_MARKER on back, or undefined on cancel.
|
|
210
215
|
* When backable=true, supports Ctrl+Shift+← for back and Ctrl+Shift+→ for submit+next.
|
|
211
216
|
*/
|
|
@@ -229,6 +234,11 @@ export function uiInput(
|
|
|
229
234
|
}
|
|
230
235
|
container.addChild(new Spacer(1));
|
|
231
236
|
|
|
237
|
+
// 实时换行预览区域(在输入框上方)
|
|
238
|
+
const previewText = new Text("", 0, 0);
|
|
239
|
+
container.addChild(previewText);
|
|
240
|
+
container.addChild(new Spacer(1));
|
|
241
|
+
|
|
232
242
|
const input = new Input(placeholder ?? "", width - 2);
|
|
233
243
|
if (initialValue) {
|
|
234
244
|
input.setValue(initialValue);
|
|
@@ -255,6 +265,7 @@ export function uiInput(
|
|
|
255
265
|
render: (w) => container.render(w),
|
|
256
266
|
invalidate: () => container.invalidate(),
|
|
257
267
|
handleInput: (data) => {
|
|
268
|
+
|
|
258
269
|
// Intercept back/next keys before passing to Input
|
|
259
270
|
if (backable) {
|
|
260
271
|
// Ctrl+Shift+← → go back to previous question
|
|
@@ -269,6 +280,19 @@ export function uiInput(
|
|
|
269
280
|
}
|
|
270
281
|
}
|
|
271
282
|
input.handleInput(data);
|
|
283
|
+
|
|
284
|
+
// 读取更新后的 value,更新预览
|
|
285
|
+
const val = input.getValue();
|
|
286
|
+
if (val.length > 0) {
|
|
287
|
+
const wrapped = wrapTextWithAnsi(val, width - 4);
|
|
288
|
+
const previewContent = wrapped
|
|
289
|
+
.map(l => theme.fg("dim", ` ${l}`))
|
|
290
|
+
.join("\n");
|
|
291
|
+
previewText.setText(previewContent);
|
|
292
|
+
} else {
|
|
293
|
+
previewText.setText("");
|
|
294
|
+
}
|
|
295
|
+
|
|
272
296
|
tui.requestRender();
|
|
273
297
|
},
|
|
274
298
|
};
|
|
@@ -362,7 +386,7 @@ function formatDurationFull(ms: number): string {
|
|
|
362
386
|
return `${m}m${s}s`;
|
|
363
387
|
}
|
|
364
388
|
|
|
365
|
-
function formatTimeout(ms: number): string {
|
|
389
|
+
export function formatTimeout(ms: number): string {
|
|
366
390
|
const m = Math.floor(ms / 60000);
|
|
367
391
|
const s = Math.floor((ms % 60000) / 1000);
|
|
368
392
|
return s > 0 ? `${m}m${s}s` : `${m}m`;
|
|
@@ -462,8 +486,13 @@ function buildWidgetLines(state: WorkflowWidgetState, theme: Theme, expanded: bo
|
|
|
462
486
|
loopStr = dim(theme, ` · 第 ${s.loopCount} 次循环`);
|
|
463
487
|
} else if (s.maxLoops != null) {
|
|
464
488
|
if (isRunning) {
|
|
465
|
-
//
|
|
466
|
-
|
|
489
|
+
// 当 loop-group 开始运行时,loopCount 已经通过 executeLoopGroup 在循环开头设置了,
|
|
490
|
+
// 所以不需要 fallback 显示"第 1 次循环"
|
|
491
|
+
// 直接使用 s.loopCount 的值
|
|
492
|
+
if (s.loopCount == null || s.loopCount === 0) {
|
|
493
|
+
// 安全 fallback(理论上不会走到这里)
|
|
494
|
+
loopStr = dim(theme, ` · 第 1 次循环`);
|
|
495
|
+
}
|
|
467
496
|
} else if (isPending) {
|
|
468
497
|
loopStr = dim(theme, ` · 第 0 次循环`);
|
|
469
498
|
}
|
|
@@ -516,9 +545,32 @@ function buildWidgetLines(state: WorkflowWidgetState, theme: Theme, expanded: bo
|
|
|
516
545
|
? theme.fg("accent", spinnerFrame())
|
|
517
546
|
: dim(theme, "◦");
|
|
518
547
|
|
|
519
|
-
// Agent line: " |__ ✓ worker ·"
|
|
548
|
+
// Agent line: " |__ ✓ worker · (52.6s/超时时间60m)"
|
|
549
|
+
// Build inline duration and timeout info for sub-step
|
|
550
|
+
let subDurStr = "";
|
|
551
|
+
let subTimeoutStr = "";
|
|
552
|
+
let subDurClose = "";
|
|
553
|
+
let elapsedMs: number | undefined;
|
|
554
|
+
if (sub.startedAt) {
|
|
555
|
+
elapsedMs = Date.now() - sub.startedAt;
|
|
556
|
+
} else if (sub.durationMs != null) {
|
|
557
|
+
elapsedMs = sub.durationMs;
|
|
558
|
+
}
|
|
559
|
+
if (elapsedMs != null) {
|
|
560
|
+
subDurStr = dim(theme, ` (${formatDurationFull(elapsedMs)}`);
|
|
561
|
+
} else if (isSubRunning) {
|
|
562
|
+
subDurStr = dim(theme, ` (0s`);
|
|
563
|
+
}
|
|
564
|
+
// Extract timeout info from sub.detail (e.g. "超时时间60m")
|
|
565
|
+
// detail is set in runAgentWithProgress as `超时时间${formatTimeout(timeoutMs)}`
|
|
566
|
+
if (sub.detail && sub.detail.includes("超时时间")) {
|
|
567
|
+
subTimeoutStr = dim(theme, `/${sub.detail}`);
|
|
568
|
+
}
|
|
569
|
+
if (subDurStr) {
|
|
570
|
+
subDurClose = dim(theme, ")");
|
|
571
|
+
}
|
|
520
572
|
const agentConnector = dim(theme, "|__");
|
|
521
|
-
lines.push(`${agentIndent}${agentConnector} ${subIcon} ${sub.agent}
|
|
573
|
+
lines.push(`${agentIndent}${agentConnector} ${subIcon} ${sub.agent} ·${subDurStr}${subTimeoutStr}${subDurClose}`);
|
|
522
574
|
|
|
523
575
|
// ── Children (tools, outputs, or "正在排队") ──
|
|
524
576
|
const childItems: string[] = [];
|
|
@@ -536,7 +588,7 @@ function buildWidgetLines(state: WorkflowWidgetState, theme: Theme, expanded: bo
|
|
|
536
588
|
childItems.push(`output:${o}`);
|
|
537
589
|
}
|
|
538
590
|
}
|
|
539
|
-
if (childItems.length === 0 && sub.detail) {
|
|
591
|
+
if (childItems.length === 0 && sub.detail && !sub.detail.includes("超时时间")) {
|
|
540
592
|
childItems.push(sub.detail);
|
|
541
593
|
}
|
|
542
594
|
}
|