@ghyper9023/pi-dev-workflow 0.6.2 → 0.7.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.
Files changed (37) hide show
  1. package/.github/workflows/release.yml +56 -0
  2. package/.pre-commit-config.yaml +75 -0
  3. package/.version/RELEASE-v0.7.0.md +85 -0
  4. package/README.md +80 -228
  5. package/extensions/dev-prompts.ts +351 -450
  6. package/extensions/git-commands.ts +62 -171
  7. package/extensions/grill-me-agent.ts +98 -137
  8. package/extensions/session-utils.ts +169 -0
  9. package/extensions/ui-helpers.ts +2 -780
  10. package/package.json +2 -2
  11. package/prompts/APPEND_SYSTEM.md +63 -59
  12. package/skills/review-html/SKILL.md +1 -1
  13. package/tests/test-no-subagents.mjs +146 -0
  14. package/.doc/AGENT-FRONTMATTER-REFERENCE.md +0 -198
  15. package/agents/git-agent.md +0 -44
  16. package/agents/grill/dev-doc-grill-agent.md +0 -42
  17. package/agents/grill/dev-fix-grill-agent.md +0 -44
  18. package/agents/grill/dev-grill-agent.md +0 -40
  19. package/agents/grill/dev-perf-grill-agent.md +0 -45
  20. package/agents/grill/dev-prd-agent.md +0 -60
  21. package/agents/grill/dev-refactor-grill-agent.md +0 -46
  22. package/agents/grill/dev-test-grill-agent.md +0 -45
  23. package/agents/review-agent.md +0 -53
  24. package/agents/workflow/docWriter-agent.md +0 -53
  25. package/agents/workflow/planner-agent.md +0 -131
  26. package/agents/workflow/reviewer-agent.md +0 -128
  27. package/agents/workflow/trimmer-agent.md +0 -78
  28. package/agents/workflow/worker-agent.md +0 -70
  29. package/extensions/sub-agents.ts +0 -954
  30. package/extensions/workflow-engine.ts +0 -2005
  31. package/tests/test-grill-json-fix.mjs +0 -243
  32. package/tests/test-loopcount-timeout-fix.mjs +0 -336
  33. package/tests/test-output-directory-structure.mjs +0 -177
  34. package/tests/test-save-answer-file-workflow.mjs +0 -187
  35. package/tests/test-workflow-config.mjs +0 -244
  36. package/tests/test-workflow-engine-bugs.mjs +0 -908
  37. package/tests/test-workflow-engine.mjs +0 -518
@@ -1,224 +1,115 @@
1
1
  /**
2
2
  * Git Commands Extension
3
3
  *
4
- * Registers three commands that delegate to git-sub-agent:
5
- * /git-commit [message] - Stage all changes and commit
4
+ * Registers three commands:
5
+ * /git-commit [message] - Commit changes
6
6
  * /git-push - Push commits to remote
7
- * /git-commit-push [message] - Stage, commit, and push in one go
7
+ * /git-commit-push [message] - Commit and push in one go
8
8
  *
9
- * Associated extension: sub-agents.ts (provides spawnSubagent infrastructure)
10
- *
11
- * Place in .pi/extensions/ or ~/.pi/agent/extensions/ for auto-discovery.
9
+ * 传入消息时快速执行:暂存全部 → 提交/推送。
10
+ * 不传消息时把任务交给当前代理:由其查看 diff、识别变更并按
11
+ * Conventional Commits 规范分批提交(提交信息用中文)。
12
12
  */
13
13
 
14
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
15
- import { discoverAgents, spawnSubagent, extractFinalOutput, type AgentDef } from "./sub-agents";
14
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
16
15
  import { uiInput } from "./ui-helpers";
17
16
 
18
- // ── Helpers ──────────────────────────────────────────────────
19
-
20
- function getAgent(
21
- ctx: { ui: { notify: (msg: string, type: string) => void } },
22
- agent: AgentDef | undefined,
23
- name: string,
24
- ): AgentDef | null {
25
- if (!agent) {
26
- ctx.ui.notify(`❌ ${name} not found (check agents/${name}.md)`, "error");
27
- return null;
28
- }
29
- return agent;
30
- }
17
+ // ── 委托给当前代理的任务描述 ─────────────────────────────────
31
18
 
32
- /** Parse git-agent structured output into a clean summary. */
33
- interface GitSummary {
34
- status: "success" | "fail" | "unknown";
35
- summary: string;
36
- details: string[];
37
- }
38
-
39
- function parseGitOutput(output: string): GitSummary {
40
- const result: GitSummary = { status: "unknown", summary: "", details: [] };
41
-
42
- // Parse <status>...</status>
43
- const statusMatch = output.match(/<status>([^<]*)<\/status>/);
44
- if (statusMatch) {
45
- const s = statusMatch[1].trim();
46
- if (s.includes("✅") || s.includes("✔")) result.status = "success";
47
- else if (s.includes("❌") || s.includes("✖")) result.status = "fail";
48
- }
19
+ const COMMIT_BATCH_TASK = [
20
+ "请帮我完成这次 git 提交:",
21
+ "1. 执行 git status 与 git diff 查看当前变更,识别变更内容,必要时先用 git add 暂存相关文件。",
22
+ "2. 按 Conventional Commits 规范分批提交:把逻辑相关的改动拆成独立 commit,避免无关改动混入同一 commit;提交信息描述使用中文。",
23
+ "3. 直接执行提交,不需要向我确认。",
24
+ "4. 严谨修改任何代码,只做 git commit 工作。",
25
+ "5. 你的任务:识别 diff -> 分批提交;请迅速完成,不要进行额外的无关任务思考。"
26
+ ].join("\n");
49
27
 
50
- // Parse <summary>...</summary>
51
- // Anchor to start of line (^ with m flag) to avoid matching a nested
52
- // <summary> tag inside <details> content before the real top-level summary.
53
- const summaryMatch = output.match(/^<summary>([\s\S]*?)<\/summary>/m);
54
- if (summaryMatch) result.summary = summaryMatch[1].trim();
28
+ const COMMIT_PUSH_TASK = COMMIT_BATCH_TASK + "\n6. 全部提交完成后执行 git push 推送。";
55
29
 
56
- // Parse <details>...</details> → extract each line
57
- // Anchor to start of line to avoid matching nested <details> inside content.
58
- const detailsMatch = output.match(/^<details>([\s\S]*?)<\/details>/m);
59
- if (detailsMatch) {
60
- const lines = detailsMatch[1]
61
- .split("\n")
62
- .map((l) => l.replace(/^[-*]\s*/, "").trim())
63
- .filter(Boolean);
64
- result.details = lines;
65
- }
66
-
67
- // Fallback: if no structured format, use raw lines
68
- if (!statusMatch && !summaryMatch) {
69
- const lines = output.split("\n").filter((l) => l.trim());
70
- result.summary = lines[0] || "";
71
- result.details = lines.slice(1).map((l) => l.replace(/^[-*]\s*/, "").trim()).filter(Boolean);
72
- // Infer status from content
73
- if (output.includes("✅") || output.includes("success") || output.includes("完成")) {
74
- result.status = "success";
75
- } else if (output.includes("❌") || output.includes("fail") || output.includes("失败")) {
76
- result.status = "fail";
77
- }
78
- }
79
-
80
- return result;
81
- }
82
-
83
- /** Enrich a detail line with relevant emoji icons. */
84
- function iconify(line: string): string {
85
- const lc = line.toLowerCase();
86
- if (lc.startsWith("commit")) return `📝 ${line}`;
87
- if (lc.startsWith("push")) return `📤 ${line}`;
88
- if (lc.includes("file") || lc.includes("文件")) return `📁 ${line}`;
89
- if (lc.startsWith("branch") || lc.includes("branch")) return `🌿 ${line}`;
90
- if (lc.startsWith("tag") || lc.includes("tag")) return `🏷️ ${line}`;
91
- return ` ${line}`; // indent others
92
- }
30
+ // ── Helpers ──────────────────────────────────────────────────
93
31
 
94
- async function runSubAgent(
95
- agent: AgentDef,
96
- task: string,
32
+ /** Run a git command through pi's executor and report the outcome. */
33
+ async function runGitCommand(
34
+ pi: ExtensionAPI,
97
35
  ctx: {
98
36
  cwd: string;
99
- signal?: AbortSignal;
100
- ui: {
101
- setStatus: (key: string, status: string | undefined) => void;
102
- notify: (msg: string, type: string) => void;
103
- };
37
+ ui: { notify: (msg: string, type: string) => void };
104
38
  },
105
- ): Promise<void> {
106
- const startTime = Date.now();
107
- ctx.ui.setStatus("subagent", "🤖 git-sub-agent working...");
108
-
39
+ args: string[],
40
+ action: string,
41
+ ): Promise<boolean> {
109
42
  try {
110
- const result = await spawnSubagent(
111
- agent,
112
- task,
113
- ctx.cwd,
114
- ctx.signal,
115
- undefined, // use agent's default timeout
116
- (progress) => {
117
- ctx.ui.setStatus("subagent", progress.slice(0, 50));
118
- },
119
- );
120
- const dur = ((Date.now() - startTime) / 1000).toFixed(1);
121
-
122
- // Extract output with fallback to raw stdout
123
- let output = extractFinalOutput(result.output);
124
- if (!output && result.output.trim()) {
125
- const lines = result.output.split("\n").filter((l) => {
126
- const t = l.trim();
127
- return t && !t.startsWith("{") && !t.startsWith("[");
128
- });
129
- if (lines.length > 0) {
130
- output = lines.join("\n").trim();
131
- }
43
+ const result = await pi.exec("git", args, { cwd: ctx.cwd, timeout: 120_000 });
44
+ if (result.code !== 0) {
45
+ const detail = result.stderr?.trim() || result.stdout?.trim() || "未知错误";
46
+ ctx.ui.notify(`❌ ${action} 失败 (exit ${result.code}): ${detail}`, "error");
47
+ return false;
132
48
  }
133
-
134
- ctx.ui.setStatus("subagent", undefined);
135
-
136
- if (output) {
137
- const parsed = parseGitOutput(output);
138
- const statusIcon = parsed.status === "success" ? "✅" : parsed.status === "fail" ? "❌" : "ℹ️";
139
- const detailText = parsed.details.length > 0 ? ` | ${parsed.details.join(" | ")}` : "";
140
- const msg = `${statusIcon} ${parsed.summary || "done"} (${dur}s)${detailText}`;
141
- const notifyType = parsed.status === "fail" ? "error" : "success";
142
- ctx.ui.notify(msg, notifyType);
143
- } else if (result.exitCode !== 0) {
144
- const errMsg = result.stderr || result.output.slice(0, 300) || "未知错误";
145
- ctx.ui.notify(`❌ git-sub-agent 失败 (${dur}s): ${errMsg}`, "error");
146
- } else {
147
- ctx.ui.notify(`✅ git-sub-agent 完成 (${dur}s)`, "success");
148
- }
149
- } finally {
150
- ctx.ui.setStatus("subagent", undefined);
49
+ ctx.ui.notify(`✅ ${action} 完成`, "success");
50
+ return true;
51
+ } catch (err) {
52
+ ctx.ui.notify(`❌ ${action} 异常: ${err instanceof Error ? err.message : String(err)}`, "error");
53
+ return false;
151
54
  }
152
55
  }
153
56
 
154
57
  // ── Extension ────────────────────────────────────────────────
155
58
 
156
59
  export default function (pi: ExtensionAPI) {
157
- const agents = discoverAgents();
158
- const gitAgent = agents.find((a) => a.name === "git-agent");
159
-
160
60
  // ── /git-commit ────────────────────────────────────────────
161
61
  pi.registerCommand("git-commit", {
162
- description: "(sub-agent) Stage all changes and create a commit via git-sub-agent",
62
+ description: "Commit changes (leave message empty to let the agent review the diff and commit in batches)",
163
63
  handler: async (args, ctx) => {
164
- const agent = getAgent(ctx, gitAgent, "git-agent");
165
- if (!agent) return;
166
-
167
64
  let message = args.trim();
168
65
  if (!message) {
169
- const input = await uiInput(ctx, "Commit message", "直接回车让 AI 自动生成,或输入信息后提交...");
170
- if (input === undefined) {
171
- return;
172
- }
66
+ const input = await uiInput(ctx, "Commit message", "直接回车将交由主代理识别变更并分批提交,或输入消息直接提交...");
67
+ if (input === undefined) return;
173
68
  message = input.trim();
174
69
  }
175
70
 
71
+ if (message) {
72
+ const addOk = await runGitCommand(pi, ctx, ["add", "-A"], "暂存所有变更");
73
+ if (!addOk) return;
74
+ await runGitCommand(pi, ctx, ["commit", "-m", message], `提交 (${message})`);
75
+ return;
76
+ }
176
77
 
177
- const task = message
178
- ? `Stage all changes with git add -A, then commit with message: "${message}". Do NOT ask for confirmation.`
179
- : `Stage all changes with git add -A, check the diff, write a Conventional Commits message, and commit. Do NOT ask for confirmation.`;
180
-
181
- await runSubAgent(agent, task, ctx);
78
+ ctx.ui.notify("🔄 已交由当前代理识别变更并分批提交...", "info");
79
+ pi.sendUserMessage(COMMIT_BATCH_TASK, { deliverAs: "followUp" });
182
80
  },
183
81
  });
184
82
 
185
83
  // ── /git-push ─────────────────────────────────────────────
186
84
  pi.registerCommand("git-push", {
187
- description: "(sub-agent) Push commits to remote via git-sub-agent",
85
+ description: "Push commits to the remote repository",
188
86
  handler: async (_args, ctx) => {
189
- const agent = getAgent(ctx, gitAgent, "git-agent");
190
- if (!agent) return;
191
-
192
- await runSubAgent(
193
- agent,
194
- "Push commits to remote with git push. Do NOT ask for confirmation.",
195
- ctx,
196
- );
87
+ await runGitCommand(pi, ctx, ["push"], "推送到远程");
197
88
  },
198
89
  });
199
90
 
200
91
  // ── /git-commit-push ──────────────────────────────────────
201
92
  pi.registerCommand("git-commit-push", {
202
- description: "(sub-agent) Stage, commit, and push via git-sub-agent",
93
+ description: "Commit and push in one go (leave message empty to let the agent review the diff and commit in batches)",
203
94
  handler: async (args, ctx) => {
204
- const agent = getAgent(ctx, gitAgent, "git-agent");
205
- if (!agent) return;
206
-
207
95
  let message = args.trim();
208
96
  if (!message) {
209
- const input = await uiInput(ctx, "Commit message", "直接回车让 AI 自动生成,或输入信息后提交并推送...");
210
- if (input === undefined) {
211
- return;
212
- }
97
+ const input = await uiInput(ctx, "Commit message", "直接回车将交由主代理识别变更、分批提交并推送,或输入消息直接提交并推送...");
98
+ if (input === undefined) return;
213
99
  message = input.trim();
214
100
  }
215
101
 
102
+ if (message) {
103
+ const addOk = await runGitCommand(pi, ctx, ["add", "-A"], "暂存所有变更");
104
+ if (!addOk) return;
105
+ const commitOk = await runGitCommand(pi, ctx, ["commit", "-m", message], `提交 (${message})`);
106
+ if (!commitOk) return;
107
+ await runGitCommand(pi, ctx, ["push"], "推送到远程");
108
+ return;
109
+ }
216
110
 
217
- const task = message
218
- ? `Stage all changes with git add -A, commit with message: "${message}", then push. Do NOT ask for confirmation.`
219
- : `Stage all changes with git add -A, check the diff, write a Conventional Commits message, commit, then push. Do NOT ask for confirmation.`;
220
-
221
- await runSubAgent(agent, task, ctx);
111
+ ctx.ui.notify("🔄 已交由当前代理识别变更、分批提交并推送...", "info");
112
+ pi.sendUserMessage(COMMIT_PUSH_TASK, { deliverAs: "followUp" });
222
113
  },
223
114
  });
224
- }
115
+ }