@ghyper9023/pi-dev-workflow 0.3.1 → 0.4.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.
- package/README.md +171 -29
- package/agents/grill/dev-doc-grill-agent.md +34 -0
- package/agents/grill/dev-fix-grill-agent.md +35 -0
- package/agents/grill/dev-grill-agent.md +33 -0
- package/agents/grill/dev-perf-grill-agent.md +36 -0
- package/agents/grill/dev-prd-agent.md +53 -0
- package/agents/grill/dev-refactor-grill-agent.md +36 -0
- package/agents/grill/dev-test-grill-agent.md +35 -0
- package/agents/review-agent.md +5 -5
- package/agents/workflow/docWriter-agent.md +29 -0
- package/agents/workflow/planner-agent.md +80 -0
- package/agents/workflow/reviewer-agent.md +44 -0
- package/agents/workflow/trimmer-agent.md +34 -0
- package/agents/workflow/worker-agent.md +29 -0
- package/extensions/dev-prompts.ts +408 -222
- package/extensions/git-commands.ts +3 -13
- package/extensions/grill-me-agent.ts +277 -150
- package/extensions/sub-agents.ts +53 -23
- package/extensions/ui-helpers.ts +1030 -0
- package/extensions/workflow-engine.ts +1715 -0
- package/package.json +1 -1
- package/skills/review-html/SKILL.md +2 -2
- package/skills/to-prd/SKILL.md +1 -1
- package/tests/test-grill-json-fix.mjs +243 -0
- package/tests/test-output-directory-structure.mjs +177 -0
- package/tests/test-save-answer-file-workflow.mjs +187 -0
- package/tests/test-workflow-config.mjs +244 -0
- package/tests/test-workflow-engine.mjs +518 -0
package/extensions/sub-agents.ts
CHANGED
|
@@ -17,6 +17,7 @@ import * as fs from "node:fs";
|
|
|
17
17
|
import * as path from "node:path";
|
|
18
18
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
19
19
|
import { Type } from "typebox";
|
|
20
|
+
import { uiSelect } from "./ui-helpers";
|
|
20
21
|
|
|
21
22
|
// ── Configuration ────────────────────────────────────────────
|
|
22
23
|
|
|
@@ -70,11 +71,12 @@ function loadAppendSystem(cwd: string): string | null {
|
|
|
70
71
|
return null;
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
/** Find the newest HTML review file in pi-review/ or pi-dev-output/pi-review/ directory. */
|
|
74
|
+
/** Find the newest HTML review file in .pi-dev-output/pi-review/html/, pi-review/, or .pi-dev-output/pi-review/ directory. */
|
|
74
75
|
function findNewestReviewHtml(cwd: string): string {
|
|
75
76
|
const candidates = [
|
|
77
|
+
path.join(cwd, ".pi-dev-output", "pi-review", "html"),
|
|
76
78
|
path.join(cwd, "pi-review"),
|
|
77
|
-
path.join(cwd, "pi-dev-output", "pi-review"),
|
|
79
|
+
path.join(cwd, ".pi-dev-output", "pi-review"),
|
|
78
80
|
];
|
|
79
81
|
|
|
80
82
|
for (const reviewDir of candidates) {
|
|
@@ -184,6 +186,7 @@ function inferTimeout(name: string): number {
|
|
|
184
186
|
const lc = name.toLowerCase();
|
|
185
187
|
if (lc.includes("review") || lc.includes("审查")) return REVIEW_TIMEOUT_MS;
|
|
186
188
|
if (lc.includes("git")) return GIT_TIMEOUT_MS;
|
|
189
|
+
if (lc.includes("prd")) return REVIEW_TIMEOUT_MS;
|
|
187
190
|
return DEFAULT_TIMEOUT_MS;
|
|
188
191
|
}
|
|
189
192
|
|
|
@@ -238,20 +241,28 @@ export function discoverAgents(): AgentDef[] {
|
|
|
238
241
|
if (!agentsDir) return (_discoveredAgents = []);
|
|
239
242
|
|
|
240
243
|
const agents: AgentDef[] = [];
|
|
241
|
-
let entries: fs.Dirent[];
|
|
242
|
-
try {
|
|
243
|
-
entries = fs.readdirSync(agentsDir, { withFileTypes: true });
|
|
244
|
-
} catch {
|
|
245
|
-
return (_discoveredAgents = agents);
|
|
246
|
-
}
|
|
247
244
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
245
|
+
/** Recursively walk directory to collect all .md agent definitions. */
|
|
246
|
+
function scanAgentsDir(dir: string): void {
|
|
247
|
+
let entries: fs.Dirent[];
|
|
248
|
+
try {
|
|
249
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
250
|
+
} catch {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
for (const entry of entries) {
|
|
254
|
+
if (entry.name.startsWith(".")) continue; // skip hidden files/dirs
|
|
255
|
+
const fullPath = path.join(dir, entry.name);
|
|
256
|
+
if (entry.isDirectory()) {
|
|
257
|
+
scanAgentsDir(fullPath);
|
|
258
|
+
} else if ((entry.isFile() || entry.isSymbolicLink()) && entry.name.endsWith(".md")) {
|
|
259
|
+
const agent = loadAgent(fullPath);
|
|
260
|
+
if (agent) agents.push(agent);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
254
263
|
}
|
|
264
|
+
|
|
265
|
+
scanAgentsDir(agentsDir);
|
|
255
266
|
return (_discoveredAgents = agents);
|
|
256
267
|
}
|
|
257
268
|
|
|
@@ -543,17 +554,35 @@ export default function (pi: ExtensionAPI) {
|
|
|
543
554
|
|
|
544
555
|
// ── /subagent-stop - 主动终止所有正在运行的 sub-agent ──────
|
|
545
556
|
pi.registerCommand("subagent-stop", {
|
|
546
|
-
description: "Terminate all running sub-agents immediately",
|
|
557
|
+
description: "Terminate all running sub-agents immediately. Also cancels any active workflow.",
|
|
547
558
|
handler: async (_args, ctx) => {
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
559
|
+
const childCount = activeChildren.size;
|
|
560
|
+
|
|
561
|
+
// Also try to cancel any active workflow
|
|
562
|
+
let workflowCancelled = false;
|
|
563
|
+
try {
|
|
564
|
+
const { cancelActiveWorkflow, isWorkflowRunning } = await import("./workflow-engine");
|
|
565
|
+
if (isWorkflowRunning()) {
|
|
566
|
+
cancelActiveWorkflow();
|
|
567
|
+
workflowCancelled = true;
|
|
568
|
+
}
|
|
569
|
+
} catch { /* workflow-engine not available, ignore */ }
|
|
570
|
+
|
|
571
|
+
if (childCount === 0 && !workflowCancelled) {
|
|
572
|
+
ctx.ui.notify("i️ 当前没有运行中的 sub-agent 或工作流", "info");
|
|
551
573
|
return;
|
|
552
574
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
575
|
+
|
|
576
|
+
if (childCount > 0) {
|
|
577
|
+
killAllChildren();
|
|
578
|
+
ctx.ui.notify(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, "info");
|
|
579
|
+
ctx.ui.notify(`🛑 已终止 ${childCount} 个 sub-agent 进程`, "warning");
|
|
580
|
+
ctx.ui.notify(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, "info");
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (workflowCancelled) {
|
|
584
|
+
ctx.ui.notify(`🛑 已取消运行中的工作流`, "warning");
|
|
585
|
+
}
|
|
557
586
|
},
|
|
558
587
|
});
|
|
559
588
|
|
|
@@ -661,7 +690,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
661
690
|
|
|
662
691
|
// 对于普通关键词触发的审查请求,询问用户选择模式
|
|
663
692
|
// ctx.ui.select 接受 string[],返回选中的字符串
|
|
664
|
-
const mode = await
|
|
693
|
+
const mode = await uiSelect(
|
|
694
|
+
ctx,
|
|
665
695
|
"🔍 检测到审查意图",
|
|
666
696
|
[
|
|
667
697
|
"1. 后台审查(非阻塞,异步通知)",
|