@ghyper9023/pi-dev-workflow 0.7.0 → 0.8.0

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.
@@ -34,7 +34,7 @@ async function runGitCommand(
34
34
  pi: ExtensionAPI,
35
35
  ctx: {
36
36
  cwd: string;
37
- ui: { notify: (msg: string, type: string) => void };
37
+ ui: { notify: (msg: string, type: "info" | "warning" | "error") => void };
38
38
  },
39
39
  args: string[],
40
40
  action: string,
@@ -46,7 +46,7 @@ async function runGitCommand(
46
46
  ctx.ui.notify(`❌ ${action} 失败 (exit ${result.code}): ${detail}`, "error");
47
47
  return false;
48
48
  }
49
- ctx.ui.notify(`✅ ${action} 完成`, "success");
49
+ ctx.ui.notify(`✅ ${action} 完成`, "info");
50
50
  return true;
51
51
  } catch (err) {
52
52
  ctx.ui.notify(`❌ ${action} 异常: ${err instanceof Error ? err.message : String(err)}`, "error");
@@ -1,9 +1,13 @@
1
1
  /**
2
- * grill-me-agent.ts — 设计 (Grill) 和 PRD 生成的管理器(运行在当前代理中)
2
+ * grill-me-agent.ts — 设计追问(Grill)与 PRD 生成的运行时(运行在当前代理中)
3
3
  *
4
4
  * 职责:
5
- * 1. runGrillPhase() — 由当前代理生成评审问题,TUI 逐题呈现(选项 + 自定义输入)
6
- * 2. runPRDPhase() — 由当前代理生成 PRD,保存到 .pi-dev-output/pi-prd/
5
+ * 1. 注册 /grill 命令 — 对方案做追问式打磨,问答附到方案后交给当前代理
6
+ * 2. 注册 /prd 命令 — 按需求描述生成 PRD,保存到 .pi-dev-output/pi-prd/
7
+ * 3. runGrillPhase() — 由当前代理生成评审问题,TUI 逐题呈现(选项 + 自定义输入)
8
+ * 4. runPRDPhase() — 由当前代理生成 PRD,并询问是否立即开始开发
9
+ *
10
+ * 两个命令各自独立,不依附于 /dev-* 命令。
7
11
  *
8
12
  * 与旧版的区别:不再创建隔离的子代理进程,而是把追问/PRD 任务交给当前代理执行。
9
13
  * 当前模型普遍具备 >=1M 上下文窗口,无需通过子代理隔离上下文。
@@ -28,7 +32,7 @@ import {
28
32
  wrapTextWithAnsi,
29
33
  type SelectItem,
30
34
  } from "@earendil-works/pi-tui";
31
- import { uiSelect, uiConfirm, uiInput } from "./ui-helpers";
35
+ import { uiSelect, uiConfirm, uiInput, uiTaskArg } from "./ui-helpers";
32
36
  import { pollFor, getLastAssistantTextAfter } from "./session-utils";
33
37
 
34
38
  // ── Types ────────────────────────────────────────────────────
@@ -103,25 +107,6 @@ export function saveAnswerFile(cwd: string, content: string): string {
103
107
  return path.join(DEV_OUTPUT_DIR, GRILL_DIRNAME, GRILL_ANSWERS_DIRNAME, filename);
104
108
  }
105
109
 
106
- /**
107
- * Find the most recent answer backup file and read its content.
108
- * Returns undefined if no backup exists or read fails.
109
- */
110
- export function recoverFromBackup(cwd: string): string | undefined {
111
- const dir = path.join(cwd, DEV_OUTPUT_DIR, GRILL_DIRNAME, GRILL_ANSWERS_DIRNAME);
112
- try {
113
- if (!fs.existsSync(dir)) return undefined;
114
- const files = fs.readdirSync(dir)
115
- .filter(f => f.startsWith("answer-") && f.endsWith(".md"))
116
- .map(f => ({ name: f, mtime: fs.statSync(path.join(dir, f)).mtimeMs }))
117
- .sort((a, b) => b.mtime - a.mtime);
118
- if (files.length === 0) return undefined;
119
- return fs.readFileSync(path.join(dir, files[0].name), "utf-8");
120
- } catch {
121
- return undefined;
122
- }
123
- }
124
-
125
110
  /** Generate a safe PRD filename. */
126
111
  function generatePrdFilename(moduleSuggestion: string): string {
127
112
  const safe = moduleSuggestion
@@ -830,12 +815,62 @@ async function askDevelopmentStart(
830
815
  }
831
816
  }
832
817
 
833
- // ── Extension factory (required by pi extension loader) ─────
834
- //
835
- // This file lives in extensions/ so pi will attempt to load it.
836
- // The default export satisfies the loader; the real functionality
837
- // is consumed by dev-prompts.ts via named imports.
838
- //
839
- export default function (_pi: ExtensionAPI) {
840
- // grill-me-agent is a helper module, not a standalone extension.
818
+ // ── Extension factory ────────────────────────────────────
819
+
820
+ export default function (pi: ExtensionAPI) {
821
+ pi.registerCommand("grill", {
822
+ description: "(grill) 提交前追问式打磨方案 — AI 逐题追问,问答附到方案后交给当前代理执行",
823
+ handler: async (args, ctx) => {
824
+ if (!ctx.hasUI) {
825
+ ctx.ui.notify("/grill 需要交互式界面", "error");
826
+ return;
827
+ }
828
+
829
+ const plan = await uiTaskArg(
830
+ ctx,
831
+ args,
832
+ "待打磨的方案 / 任务描述(必填)",
833
+ "如 实现邮箱密码登录:注册、登录、会话保持",
834
+ );
835
+ if (!plan) return;
836
+
837
+ const result = await runGrillPhase(plan, ctx, pi, {
838
+ title: "方案追问完善",
839
+ description: "AI 会针对方案逐题追问:术语精确化、边界条件、失败路径、验证方式。",
840
+ questionTitle: "方案追问完善",
841
+ });
842
+ if (result.cancelled) {
843
+ ctx.ui.notify("已取消追问,未发送任何内容", "info");
844
+ return;
845
+ }
846
+ if (result.pairs.length === 0) {
847
+ ctx.ui.notify("未进入追问,未发送任何内容", "info");
848
+ return;
849
+ }
850
+
851
+ saveAnswerFile(ctx.cwd, result.enhancedPrompt);
852
+ pi.sendUserMessage(result.enhancedPrompt, { deliverAs: "followUp" });
853
+ ctx.ui.notify("追问完成,方案已发送给当前代理", "info");
854
+ },
855
+ });
856
+
857
+ pi.registerCommand("prd", {
858
+ description: "(prd) 按需求描述生成 PRD 文档,并可选直接开始开发",
859
+ handler: async (args, ctx) => {
860
+ if (!ctx.hasUI) {
861
+ ctx.ui.notify("/prd 需要交互式界面", "error");
862
+ return;
863
+ }
864
+
865
+ const requirement = await uiTaskArg(
866
+ ctx,
867
+ args,
868
+ "PRD 需求描述(必填)",
869
+ "如 支持邮箱密码注册登录,含密码重置",
870
+ );
871
+ if (!requirement) return;
872
+
873
+ await runPRDPhase(requirement, requirement.split("\n")[0] || "feature", pi, ctx);
874
+ },
875
+ });
841
876
  }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Pre-Check Extension
3
+ *
4
+ * 提供 `/dev-pre-check` 命令:在真正执行任务前插入一道「意图校验」闸门。
5
+ *
6
+ * 流程:
7
+ * 1. 取用户原始 prompt(命令参数,缺省则弹输入框)
8
+ * 2. 组合提示词 =「用户原始 prompt」+ 固定的「用自己的话重述目标与问题」指令,
9
+ * 并把本轮任务意图改成「只做意图理解,不产生任何实质改动」
10
+ * 3. 等待代理给出复述(代理空闲 + 本轮新增的 assistant 文本)
11
+ * 4. 用户确认意图:正确 → 开始真正的工作;不正确 → 补充说明后重新复述;取消 → 结束
12
+ * 5. 确认正确后,把「原始 prompt + 已确认的意图复述」投递给代理开始执行
13
+ *
14
+ * confirmIntent() 是上述 2-4 步的通用实现,/dev-feat 等 dev 命令同样复用:
15
+ * 它们把已确认的意图接进自己组装的提示词,而不是另造一套意图识别。
16
+ */
17
+
18
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
19
+ import { uiInput, uiSelect } from "./ui-helpers";
20
+ import { pollFor, getLastAssistantTextAfter } from "./session-utils";
21
+
22
+ // ── 提示词组装 ────────────────────────────────────────────────
23
+
24
+ /** 固定的「意图复述」指令,与用户原始 prompt 组合成意图校验提示词。 */
25
+ const RESTATE_INSTRUCTION = [
26
+ "## 你的任务",
27
+ "用自己的话重述你认为用户的目标是什么,以及用户试图解决的问题是什么。",
28
+ "",
29
+ "## 硬性约束(最高优先级)",
30
+ "- 本轮只做意图理解与复述,不执行任何实质工作。",
31
+ "- 禁止修改、创建、删除任何文件;禁止运行任何有副作用的命令。",
32
+ "- 禁止调用写入类工具(如 write、edit,以及 bash 中的写操作);只允许只读探查(如 read、grep)。",
33
+ "- 禁止输出代码、补丁、diff、实施计划、解决方案或改进建议。",
34
+ "- 不要反问用户、不要请求确认、不要给出下一步行动。",
35
+ "- 若原始 prompt 存在歧义或信息缺口,在「不确定之处」中明确指出,但不要替用户做决定。",
36
+ "",
37
+ "## 输出格式(严格遵循)",
38
+ "### 1. 用户的目标",
39
+ "(用自己的话,2-4 句)",
40
+ "",
41
+ "### 2. 用户试图解决的问题",
42
+ "(用自己的话,2-4 句)",
43
+ "",
44
+ "### 3. 不确定之处",
45
+ "(逐条列出,无则写「无」)",
46
+ "",
47
+ "### 4. 一句话概括",
48
+ "(一句话说清「用户想让我做什么」)",
49
+ "",
50
+ "复述完成后立即停止,等待用户确认意图。",
51
+ ].join("\n");
52
+
53
+ /** 组装意图校验提示词:原始 prompt + 固定复述指令(+ 历史修正说明)。 */
54
+ export function buildPreCheckPrompt(originalPrompt: string, corrections: string[]): string {
55
+ const lines: string[] = [
56
+ "[pre-check] 任务意图校验:只复述,不执行",
57
+ "",
58
+ "## 用户原始 prompt(原文)",
59
+ originalPrompt,
60
+ "",
61
+ RESTATE_INSTRUCTION,
62
+ ];
63
+
64
+ if (corrections.length > 0) {
65
+ lines.push(
66
+ "",
67
+ "## 用户对上一次复述的修正(必须吸收)",
68
+ ...corrections.map((c, i) => `${i + 1}. ${c}`),
69
+ );
70
+ }
71
+
72
+ return lines.join("\n");
73
+ }
74
+
75
+ /** 组装确认后的执行提示词:原始 prompt + 已确认的意图复述。 */
76
+ export function buildExecutionPrompt(originalPrompt: string, intent: string): string {
77
+ return [
78
+ "[execute] 意图已确认,开始执行",
79
+ "",
80
+ "## 用户原始 prompt(原文,权威需求)",
81
+ originalPrompt,
82
+ "",
83
+ "## 已由用户确认的任务意图",
84
+ intent,
85
+ "",
86
+ "## 要求",
87
+ "- 现在开始执行用户原始 prompt 描述的工作。",
88
+ "- 以上已确认意图是本任务的权威前提;与你的理解冲突时,以该意图为准。",
89
+ "- 执行前若仍需澄清,先向我提问,不要臆测。",
90
+ ].join("\n");
91
+ }
92
+
93
+ // ── 意图确认循环 ─────────────────────────────────────────────
94
+
95
+ const PRE_CHECK_TIMEOUT_MS = 5 * 60_000;
96
+
97
+ /**
98
+ * 复述 → 确认 →(必要时)修正 的意图确认循环。
99
+ * 返回已确认的意图复述;用户取消或始终未取到复述时返回 undefined。
100
+ */
101
+ export async function confirmIntent(
102
+ task: string,
103
+ ctx: ExtensionCommandContext,
104
+ pi: ExtensionAPI,
105
+ ): Promise<string | undefined> {
106
+ const corrections: string[] = [];
107
+
108
+ for (;;) {
109
+ ctx.ui.notify("正在让代理复述任务意图(只分析,不执行)...", "info");
110
+ const sentAt = Date.now();
111
+ pi.sendUserMessage(buildPreCheckPrompt(task, corrections), { deliverAs: "followUp" });
112
+
113
+ // 完成标志:代理空闲且本轮产生了新的 assistant 文本。
114
+ // 不依赖 waitForIdle —— 它可能在 followUp 触发的新 turn 开始前就返回。
115
+ const intent = await pollFor(
116
+ () => (ctx.isIdle() ? getLastAssistantTextAfter(ctx, sentAt) || undefined : undefined),
117
+ PRE_CHECK_TIMEOUT_MS,
118
+ );
119
+
120
+ if (!intent) {
121
+ const retry = await uiSelect(ctx, "未获取到代理的意图复述", ["重试", "取消"]);
122
+ if (retry === "重试") continue;
123
+ return undefined;
124
+ }
125
+
126
+ const decision = await uiSelect(ctx, "意图确认:以上复述是否准确反映你的目标?", [
127
+ "是 — 意图正确,开始执行",
128
+ "否 — 意图不正确,我要补充说明后重新分析",
129
+ "取消",
130
+ ]);
131
+
132
+ if (!decision || decision === "取消") return undefined;
133
+ if (decision.startsWith("是")) return intent;
134
+
135
+ const fix = await uiInput(
136
+ ctx,
137
+ "补充/修正说明",
138
+ "说明哪里理解错了、遗漏了什么",
139
+ false,
140
+ );
141
+ if (fix === undefined) return undefined;
142
+ corrections.push(fix.trim() || "(用户未提供具体说明,请重新检查复述)");
143
+ }
144
+ }
145
+
146
+ // ── 流程 ─────────────────────────────────────────────────────
147
+
148
+ async function runPreCheck(
149
+ pi: ExtensionAPI,
150
+ ctx: ExtensionCommandContext,
151
+ args: string,
152
+ ): Promise<void> {
153
+ if (!ctx.hasUI) {
154
+ ctx.ui.notify("/dev-pre-check 需要交互式界面才能确认意图", "error");
155
+ return;
156
+ }
157
+
158
+ let originalPrompt = args.trim();
159
+ if (!originalPrompt) {
160
+ const input = await uiInput(
161
+ ctx,
162
+ "待校验的需求(原始 prompt)",
163
+ "输入你希望 AI 先复述确认的需求原文",
164
+ true,
165
+ );
166
+ if (input === undefined) return;
167
+ originalPrompt = input.trim();
168
+ if (!originalPrompt) return;
169
+ }
170
+
171
+ const intent = await confirmIntent(originalPrompt, ctx, pi);
172
+ if (!intent) {
173
+ ctx.ui.notify("已取消,未执行任何改动", "info");
174
+ return;
175
+ }
176
+
177
+ pi.sendUserMessage(buildExecutionPrompt(originalPrompt, intent), { deliverAs: "followUp" });
178
+ ctx.ui.notify("意图已确认,开始执行", "info");
179
+ }
180
+
181
+ // ── Extension ────────────────────────────────────────────────
182
+
183
+ export default function (pi: ExtensionAPI) {
184
+ pi.registerCommand("dev-pre-check", {
185
+ description: "(pre-check) 先让 AI 复述任务意图并确认,确认正确后再开始真正的工作",
186
+ handler: async (args, ctx) => {
187
+ await runPreCheck(pi, ctx, args);
188
+ },
189
+ });
190
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Review Detection Extension
3
+ *
4
+ * 拦截带审查意图的输入,交给 review-html skill 在当前代理中执行:
5
+ * - 直接输入 `/skill:review-html` → 立即审查
6
+ * - 输入同时命中「审查意图」与「代码对象」关键词 → 弹选单让用户确认
7
+ *
8
+ * 审查报告由 review-html skill 写入 .pi-dev-output/pi-review/html/,
9
+ * 这里以报告文件出现作为完成标志(不使用 waitForIdle:它可能在 followUp
10
+ * 触发的新 turn 开始前就返回,导致误报完成)。
11
+ */
12
+
13
+ import * as fs from "node:fs";
14
+ import * as path from "node:path";
15
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
16
+ import { uiSelect } from "./ui-helpers";
17
+
18
+ /** Find the newest HTML review file generated after `afterMs`(忽略先前遗留的旧报告)。 */
19
+ function findNewestReviewHtml(cwd: string, afterMs: number): string {
20
+ const candidates = [
21
+ path.join(cwd, ".pi-dev-output", "pi-review", "html"),
22
+ path.join(cwd, "pi-review"),
23
+ path.join(cwd, ".pi-dev-output", "pi-review"),
24
+ ];
25
+
26
+ for (const reviewDir of candidates) {
27
+ try {
28
+ if (fs.existsSync(reviewDir)) {
29
+ const files = fs.readdirSync(reviewDir)
30
+ .filter(f => f.endsWith(".html"))
31
+ .map(f => ({
32
+ name: f,
33
+ mtime: fs.statSync(path.join(reviewDir, f)).mtimeMs,
34
+ }))
35
+ .filter(f => f.mtime > afterMs)
36
+ .sort((a, b) => b.mtime - a.mtime);
37
+ if (files.length > 0) {
38
+ const rel = path.relative(cwd, reviewDir);
39
+ return rel + "/" + files[0].name;
40
+ }
41
+ }
42
+ } catch {
43
+ // ignore fs errors
44
+ }
45
+ }
46
+
47
+ return "";
48
+ }
49
+
50
+ /** Run a code review in the current agent and report the result. */
51
+ async function runReview(task: string, ctx: ExtensionContext, pi: ExtensionAPI): Promise<string | undefined> {
52
+ const startTime = Date.now();
53
+ ctx.ui.notify("🤖 正在运行代码审查,请稍候...", "info");
54
+
55
+ pi.sendUserMessage(`/skill:review-html\n\n${task}`, {
56
+ deliverAs: "followUp",
57
+ expandPromptTemplates: true,
58
+ });
59
+
60
+ // 以“报告文件生成为完成标志”进行轮询;不使用 waitForIdle,
61
+ // 避免其在 sendUserMessage 触发的新 turn 开始前立即返回,导致误报“已完成”。
62
+ const deadline = Date.now() + 10 * 60_000;
63
+ let filePath = "";
64
+ while (Date.now() < deadline) {
65
+ filePath = findNewestReviewHtml(ctx.cwd, startTime);
66
+ if (filePath) break;
67
+ await new Promise((r) => setTimeout(r, 2_000));
68
+ }
69
+
70
+ const dur = ((Date.now() - startTime) / 1000).toFixed(1);
71
+ if (filePath) {
72
+ ctx.ui.notify(`📄 审查报告已生成: ${filePath} (${dur}s)`, "info");
73
+ } else {
74
+ ctx.ui.notify(`⚠️ 审查未生成报告 (${dur}s),请查看当前代理的回复或稍后重试`, "warning");
75
+ }
76
+ return filePath;
77
+ }
78
+
79
+ export default function (pi: ExtensionAPI) {
80
+ pi.on("input", async (event, ctx) => {
81
+ if (!ctx.hasUI) return { action: "continue" };
82
+ // 扩展注入的消息(sendUserMessage,source 为 "extension")不重入本处理器,避免无限递归
83
+ if (event.source === "extension") return { action: "continue" };
84
+
85
+ const text = event.text.trim().toLowerCase();
86
+
87
+ // Detect review-html skill invocation or explicit review request.
88
+ const isReviewSkill = text.startsWith("/skill:review-html");
89
+ const hasReviewIntent = text.includes("review") ||
90
+ text.includes("审查") || text.includes("审阅") || text.includes("review-html");
91
+ const hasCodeTarget = text.includes("code") || text.includes("代码") ||
92
+ text.includes("diff") || text.includes("commit") ||
93
+ text.includes("html") || text.includes("report") || text.includes("报告") ||
94
+ text.includes("本次改动") || text.includes("这次改动");
95
+ const isReviewRequest = !isReviewSkill && hasReviewIntent && hasCodeTarget;
96
+
97
+ if (!isReviewSkill && !isReviewRequest) return { action: "continue" };
98
+
99
+ // ── Skill invocation: run review directly ─────────────────
100
+ if (isReviewSkill) {
101
+ await runReview(event.text, ctx, pi);
102
+ return { action: "handled" };
103
+ }
104
+
105
+ // ── Review intent: let the user choose ────────────────────
106
+ const mode = await uiSelect(
107
+ ctx,
108
+ "🔍 检测到审查意图",
109
+ [
110
+ "1. 开始审查(阻塞,等待结果)",
111
+ "2. 不是审查(放行给主代理)",
112
+ ],
113
+ );
114
+
115
+ if (!mode || mode.startsWith("2")) {
116
+ return { action: "continue" };
117
+ }
118
+
119
+ await runReview(event.text, ctx, pi);
120
+
121
+ return { action: "handled" };
122
+ });
123
+ }
@@ -98,16 +98,15 @@ export function detectProjectDefaults(cwd: string): ProjectDefaults {
98
98
  return d;
99
99
  }
100
100
 
101
- /** 根据项目探测结果生成常规验收标准(测试 + lint + pre-commit + CI)。 */
102
- export function defaultAcceptance(d: ProjectDefaults): string {
103
- const parts: string[] = [];
104
- if (d.testCmd) parts.push(`运行 ${d.testCmd} 确认全部测试通过、无回归`);
105
- else parts.push(`按 ${d.language || "项目"} 的常规测试方式编写并运行测试,确认核心逻辑正确、无回归`);
106
- if (d.lintCmd) parts.push(`运行 ${d.lintCmd} 符合代码规范`);
107
- if (d.hasPreCommit) parts.push("通过本地 pre-commit 钩子检查");
108
- if (d.hasCI) parts.push("通过 CI 检查");
109
- if (parts.length === 0) parts.push("核心逻辑正确、边界情况处理完善、无行为回归");
110
- return parts.join(";");
101
+ /** 根据项目探测结果生成常规验收标准条目(测试 + lint + pre-commit + CI)。 */
102
+ export function defaultAcceptanceItems(d: ProjectDefaults): string[] {
103
+ const items: string[] = [];
104
+ if (d.testCmd) items.push(`运行 ${d.testCmd} 确认全部测试通过、无回归`);
105
+ else items.push(`按 ${d.language || "项目"} 的常规测试方式编写并运行测试,确认核心逻辑正确、无回归`);
106
+ if (d.lintCmd) items.push(`运行 ${d.lintCmd} 符合代码规范`);
107
+ if (d.hasPreCommit) items.push("通过本地 pre-commit 钩子检查");
108
+ if (d.hasCI) items.push("通过 CI 检查");
109
+ return items;
111
110
  }
112
111
 
113
112
  /**
@@ -8,9 +8,10 @@
8
8
  * - uiSelect() — replaces ctx.ui.select() with wrapping
9
9
  * - uiConfirm() — replaces ctx.ui.confirm() with wrapping
10
10
  * - uiInput() — replaces ctx.ui.input() with wrapping
11
+ * - uiTaskArg() — 命令参数或输入框二选一取任务文本
11
12
  */
12
13
 
13
- import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
14
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
14
15
  import {
15
16
  Box,
16
17
  Container,
@@ -28,7 +29,7 @@ import { Key, matchesKey } from "@earendil-works/pi-tui";
28
29
 
29
30
  // ── Types ────────────────────────────────────────────────────
30
31
 
31
- type Theme = ExtensionCommandContext["ui"]["theme"];
32
+ type Theme = ExtensionContext["ui"]["theme"];
32
33
 
33
34
  // ── Constants ─────────────────────────────────────────────────
34
35
 
@@ -79,7 +80,7 @@ function dim(theme: Theme, text: string): string {
79
80
  * caller should check for it via choice === BACK_OPTION_TEXT.
80
81
  */
81
82
  export function uiSelect(
82
- ctx: ExtensionCommandContext,
83
+ ctx: ExtensionContext,
83
84
  title: string,
84
85
  items: string[],
85
86
  backable = false,
@@ -136,7 +137,7 @@ export function uiSelect(
136
137
  * Returns true for Yes, false for No, "back" for back, undefined on cancel.
137
138
  */
138
139
  export function uiConfirm(
139
- ctx: ExtensionCommandContext,
140
+ ctx: ExtensionContext,
140
141
  title: string,
141
142
  message?: string,
142
143
  backable = false,
@@ -210,7 +211,7 @@ export function uiConfirm(
210
211
  * When backable=true, supports Ctrl+Shift+← for back and Ctrl+Shift+→ for submit+next.
211
212
  */
212
213
  export function uiInput(
213
- ctx: ExtensionCommandContext,
214
+ ctx: ExtensionContext,
214
215
  label: string,
215
216
  placeholder?: string,
216
217
  required = false,
@@ -294,6 +295,25 @@ export function uiInput(
294
295
  });
295
296
  }
296
297
 
298
+ // ── Task argument helper ─────────────────────────────────────
299
+
300
+ /**
301
+ * 取任务文本:优先使用命令参数,缺省时弹一次必填输入框。
302
+ * 参数为空、输入为空或用户取消时返回 undefined。
303
+ */
304
+ export async function uiTaskArg(
305
+ ctx: ExtensionContext,
306
+ args: string,
307
+ label: string,
308
+ placeholder?: string,
309
+ ): Promise<string | undefined> {
310
+ const fromArgs = args.trim();
311
+ if (fromArgs) return fromArgs;
312
+ const input = await uiInput(ctx, label, placeholder, true);
313
+ const text = input?.trim();
314
+ return text ? text : undefined;
315
+ }
316
+
297
317
  // ═══════════════════════════════════════════════════════════════
298
318
  // Extension factory (no-op — ui-helpers is a helper module)
299
319
  // ═══════════════════════════════════════════════════════════════
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghyper9023/pi-dev-workflow",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "keywords": [
5
5
  "pi-package"
6
6
  ],
@@ -1,10 +1,19 @@
1
1
  ## Global Agent Rules
2
2
 
3
3
  ### Language
4
- 默认用简体中文回复;用户明确指定时再切换语言。
4
+ 默认用简体中文回复;用户明确指定其他语种时再切换语言。
5
5
  默认不使用 emoji,除非用户明确要求。
6
6
  回答应简短精炼且足够说明情况。
7
7
 
8
+ ### 思考过程
9
+ **严格遵循:**审题 → 思考 → 汇总 → 验证 → 讨教 → 最终:
10
+ 1.审题:独立且清晰的确认用户意图,用自己的话重述你认为用户的目标是什么,以及用户试图解决的问题是什么。
11
+ 2.思考:独立的探索真实数据,分析事情(真实代码/数据/文档/现状)的实际价值。
12
+ 3.汇总:整合分析结果,区分事实、推断、假设;聚焦真正影响结果的关键因素。
13
+ 4.验证:假设当前正是用户目标完成时候的状态,但用户表示不满意,深入分析哪里没有做到位,把方案补全。
14
+ 5.讨教:遇到不确定、专业、时效性或陌生问题时,主动检索网络、官方文档、知识库、成熟案例,进行外部校验。
15
+ 6.最终:完成以上流程后再作答;**先结论,后依据**,优先给出最小闭环、可执行方案。明确不确定性,禁止编造。
16
+
8
17
  ### Working Style
9
18
  像高绩效资深工程师一样工作:直接、克制、执行优先。
10
19
  技术准确优先于迎合用户信念。专注事实和解决问题,提供直接、客观的技术信息,避免不必要的夸张、赞美或情感认同。
@@ -50,6 +59,7 @@ git commit 操作保持分批提交,避免一次性提交过多代码或多个
50
59
  ### Validation
51
60
  改完先做最相关的验证:优先针对性测试,再类型 /lint/build,最后最小冒烟。
52
61
  能跑就跑;不能跑就明确说明原因,不把静态阅读包装成运行时验证。
62
+ 查看项目中的CI,`.pre-commit-check.yaml`等内容(如有),完成相关验证。
53
63
 
54
64
  ### Stop Rule
55
65
  每完成一个关键步骤就判断一次:是否已经能基于足够证据回答用户的核心请求。
@@ -58,6 +68,6 @@ git commit 操作保持分批提交,避免一次性提交过多代码或多个
58
68
  ### 核心理念
59
69
  高效达成请求结果,不过度工程。无范围蔓延。只实现直接要求的内容。无假想设计,无必要抽象,默认无注释。不为不可能条件加校验。无时间预估。无 emoji。无过度认同。准确、客观、简洁。始终牢记自己的身份和职责。
60
70
 
61
- **除用户明确要求执行特定操作外,默认严格遵循以上规范**
62
- **除用户明确要求执行特定操作外,默认严格遵循以上规范**
63
- **除用户明确要求执行特定操作外,默认严格遵循以上规范**
71
+ **除用户明确要求执行特定操作外,默认严格遵循以上规范,在最终返回前确认已经遵守了[思考过程]的完整流程:审题->思考->汇总->验证->讨教->最终**
72
+ **除用户明确要求执行特定操作外,默认严格遵循以上规范,在最终返回前确认已经遵守了[思考过程]的完整流程:审题->思考->汇总->验证->讨教->最终**
73
+ **除用户明确要求执行特定操作外,默认严格遵循以上规范,在最终返回前确认已经遵守了[思考过程]的完整流程:审题->思考->汇总->验证->讨教->最终**