@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.
@@ -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
- for (const entry of entries) {
249
- if (!entry.name.endsWith(".md")) continue;
250
- if (!entry.isFile() && !entry.isSymbolicLink()) continue;
251
- const filePath = path.join(agentsDir, entry.name);
252
- const agent = loadAgent(filePath);
253
- if (agent) agents.push(agent);
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 count = activeChildren.size;
549
- if (count === 0) {
550
- ctx.ui.notify("i️ 当前没有运行中的 sub-agent", "info");
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
- killAllChildren();
554
- ctx.ui.notify(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, "info");
555
- ctx.ui.notify(`🛑 已终止 ${count} 个 sub-agent 进程`, "warning");
556
- ctx.ui.notify(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`, "info");
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 ctx.ui.select(
693
+ const mode = await uiSelect(
694
+ ctx,
665
695
  "🔍 检测到审查意图",
666
696
  [
667
697
  "1. 后台审查(非阻塞,异步通知)",