pi-maestro-flow 0.2.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 (179) hide show
  1. package/README.md +124 -0
  2. package/agents/aggregator.md +27 -0
  3. package/agents/cli-explore-agent.md +188 -0
  4. package/agents/cross-role-reviewer.md +171 -0
  5. package/agents/delegate.md +19 -0
  6. package/agents/explorer.md +23 -0
  7. package/agents/impeccable-agent.md +99 -0
  8. package/agents/ralph-executor.md +81 -0
  9. package/agents/reference.md +24 -0
  10. package/agents/role-design-author.md +220 -0
  11. package/agents/team-supervisor.md +143 -0
  12. package/agents/team-worker.md +237 -0
  13. package/agents/ui-design-agent.md +270 -0
  14. package/agents/workflow-analyzer.md +116 -0
  15. package/agents/workflow-codebase-mapper.md +77 -0
  16. package/agents/workflow-collab-planner.md +147 -0
  17. package/agents/workflow-debugger.md +104 -0
  18. package/agents/workflow-executor.md +133 -0
  19. package/agents/workflow-external-researcher.md +86 -0
  20. package/agents/workflow-integration-checker.md +83 -0
  21. package/agents/workflow-nyquist-auditor.md +85 -0
  22. package/agents/workflow-phase-researcher.md +85 -0
  23. package/agents/workflow-plan-checker.md +100 -0
  24. package/agents/workflow-planner.md +200 -0
  25. package/agents/workflow-project-researcher.md +74 -0
  26. package/agents/workflow-research-synthesizer.md +70 -0
  27. package/agents/workflow-reviewer.md +82 -0
  28. package/agents/workflow-roadmapper.md +81 -0
  29. package/agents/workflow-verifier.md +120 -0
  30. package/package.json +58 -0
  31. package/skills/codify-to-knowhow/SKILL.md +167 -0
  32. package/skills/delegation-check/SKILL.md +297 -0
  33. package/skills/domain-add/SKILL.md +75 -0
  34. package/skills/insight-challenge/SKILL.md +226 -0
  35. package/skills/learn-decompose/SKILL.md +124 -0
  36. package/skills/learn-follow/SKILL.md +163 -0
  37. package/skills/learn-investigate/SKILL.md +160 -0
  38. package/skills/learn-second-opinion/SKILL.md +128 -0
  39. package/skills/maestro/SKILL.md +239 -0
  40. package/skills/maestro-amend/SKILL.md +169 -0
  41. package/skills/maestro-analyze/SKILL.md +173 -0
  42. package/skills/maestro-blueprint/SKILL.md +159 -0
  43. package/skills/maestro-brainstorm/SKILL.md +193 -0
  44. package/skills/maestro-collab/SKILL.md +181 -0
  45. package/skills/maestro-companion/SKILL.md +536 -0
  46. package/skills/maestro-composer/SKILL.md +192 -0
  47. package/skills/maestro-execute/SKILL.md +193 -0
  48. package/skills/maestro-fork/SKILL.md +104 -0
  49. package/skills/maestro-grill/SKILL.md +151 -0
  50. package/skills/maestro-guard/SKILL.md +124 -0
  51. package/skills/maestro-help/SKILL.md +328 -0
  52. package/skills/maestro-impeccable/SKILL.md +302 -0
  53. package/skills/maestro-init/SKILL.md +151 -0
  54. package/skills/maestro-merge/SKILL.md +90 -0
  55. package/skills/maestro-milestone-audit/SKILL.md +117 -0
  56. package/skills/maestro-milestone-complete/SKILL.md +133 -0
  57. package/skills/maestro-milestone-release/SKILL.md +131 -0
  58. package/skills/maestro-next/SKILL.md +269 -0
  59. package/skills/maestro-overlay/SKILL.md +203 -0
  60. package/skills/maestro-plan/SKILL.md +194 -0
  61. package/skills/maestro-player/SKILL.md +184 -0
  62. package/skills/maestro-quick/SKILL.md +117 -0
  63. package/skills/maestro-ralph/SKILL.md +905 -0
  64. package/skills/maestro-ralph-cli/SKILL.md +976 -0
  65. package/skills/maestro-ralph-cli-execute/SKILL.md +229 -0
  66. package/skills/maestro-ralph-execute/SKILL.md +409 -0
  67. package/skills/maestro-ralph-v2/SKILL.md +1198 -0
  68. package/skills/maestro-roadmap/SKILL.md +157 -0
  69. package/skills/maestro-swarm-workflow/SKILL.md +272 -0
  70. package/skills/maestro-tools-execute/SKILL.md +143 -0
  71. package/skills/maestro-tools-register/SKILL.md +192 -0
  72. package/skills/maestro-ui-codify/SKILL.md +127 -0
  73. package/skills/maestro-universal-workflow/SKILL.md +622 -0
  74. package/skills/maestro-update/SKILL.md +156 -0
  75. package/skills/manage-codebase-rebuild/SKILL.md +111 -0
  76. package/skills/manage-drift-realign/SKILL.md +159 -0
  77. package/skills/manage-harvest/SKILL.md +121 -0
  78. package/skills/manage-issue/SKILL.md +88 -0
  79. package/skills/manage-issue-discover/SKILL.md +104 -0
  80. package/skills/manage-kg-extractors/SKILL.md +147 -0
  81. package/skills/manage-knowhow/SKILL.md +102 -0
  82. package/skills/manage-knowhow-capture/SKILL.md +109 -0
  83. package/skills/manage-knowledge-audit/SKILL.md +147 -0
  84. package/skills/manage-status/SKILL.md +80 -0
  85. package/skills/manage-wiki/SKILL.md +104 -0
  86. package/skills/odyssey-debug/SKILL.md +344 -0
  87. package/skills/odyssey-improve/SKILL.md +423 -0
  88. package/skills/odyssey-planex/SKILL.md +542 -0
  89. package/skills/odyssey-review-test-fix/SKILL.md +392 -0
  90. package/skills/odyssey-ui/SKILL.md +388 -0
  91. package/skills/prompt-generator/SKILL.md +470 -0
  92. package/skills/quality-auto-test/SKILL.md +168 -0
  93. package/skills/quality-debug/SKILL.md +176 -0
  94. package/skills/quality-refactor/SKILL.md +108 -0
  95. package/skills/quality-retrospective/SKILL.md +134 -0
  96. package/skills/quality-review/SKILL.md +169 -0
  97. package/skills/quality-sync/SKILL.md +90 -0
  98. package/skills/quality-test/SKILL.md +183 -0
  99. package/skills/scholar-anti-ai-writing/SKILL.md +173 -0
  100. package/skills/scholar-anti-ai-writing/references/patterns-chinese.md +242 -0
  101. package/skills/scholar-anti-ai-writing/references/patterns-english.md +242 -0
  102. package/skills/scholar-citation-verify/SKILL.md +166 -0
  103. package/skills/scholar-citation-verify/references/api-usage.md +372 -0
  104. package/skills/scholar-citation-verify/references/common-errors.md +384 -0
  105. package/skills/scholar-citation-verify/references/verification-rules.md +399 -0
  106. package/skills/scholar-experiment/SKILL.md +321 -0
  107. package/skills/scholar-ideation/SKILL.md +288 -0
  108. package/skills/scholar-latex-organizer/SKILL.md +186 -0
  109. package/skills/scholar-publish/SKILL.md +220 -0
  110. package/skills/scholar-rebuttal-pro/README.md +313 -0
  111. package/skills/scholar-rebuttal-pro/SKILL.md +511 -0
  112. package/skills/scholar-review/SKILL.md +227 -0
  113. package/skills/scholar-thesis-docx/README.md +111 -0
  114. package/skills/scholar-thesis-docx/README_EN.md +108 -0
  115. package/skills/scholar-thesis-docx/SKILL.md +212 -0
  116. package/skills/scholar-thesis-docx/references/failure-patterns-and-quality-gates.md +178 -0
  117. package/skills/scholar-thesis-docx/references/figure-and-code-rules.md +33 -0
  118. package/skills/scholar-thesis-docx/references/paper-format-workflow.md +68 -0
  119. package/skills/scholar-thesis-docx/references/script-usage.md +157 -0
  120. package/skills/scholar-thesis-docx/scripts/audit_docx_ooxml.py +429 -0
  121. package/skills/scholar-thesis-docx/scripts/check_word_com.ps1 +50 -0
  122. package/skills/scholar-thesis-docx/scripts/export_word_pdf.ps1 +62 -0
  123. package/skills/scholar-thesis-docx/scripts/normalize_word_styles.ps1 +498 -0
  124. package/skills/scholar-thesis-docx/scripts/render_mermaid_figure.ps1 +148 -0
  125. package/skills/scholar-writing/SKILL.md +296 -0
  126. package/skills/security-audit/SKILL.md +244 -0
  127. package/skills/skill-generator/SKILL.md +472 -0
  128. package/skills/skill-iter-tune/SKILL.md +382 -0
  129. package/skills/skill-simplify/SKILL.md +63 -0
  130. package/skills/skill-tuning/SKILL.md +174 -0
  131. package/skills/spec-add/SKILL.md +105 -0
  132. package/skills/spec-load/SKILL.md +98 -0
  133. package/skills/spec-remove/SKILL.md +74 -0
  134. package/skills/spec-setup/SKILL.md +75 -0
  135. package/skills/team-adversarial-swarm/SKILL.md +233 -0
  136. package/skills/team-adversarial-swarm/scripts/__pycache__/pheromone.cpython-313.pyc +0 -0
  137. package/skills/team-adversarial-swarm/scripts/__pycache__/scoring.cpython-313.pyc +0 -0
  138. package/skills/team-adversarial-swarm/scripts/aco.py +473 -0
  139. package/skills/team-adversarial-swarm/scripts/pheromone.py +144 -0
  140. package/skills/team-adversarial-swarm/scripts/scoring.py +92 -0
  141. package/skills/team-adversarial-swarm/scripts/test_aco.py +475 -0
  142. package/skills/team-arch-opt/SKILL.md +158 -0
  143. package/skills/team-brainstorm/SKILL.md +171 -0
  144. package/skills/team-coordinate/SKILL.md +266 -0
  145. package/skills/team-designer/SKILL.md +160 -0
  146. package/skills/team-executor/SKILL.md +189 -0
  147. package/skills/team-frontend/SKILL.md +136 -0
  148. package/skills/team-frontend-debug/SKILL.md +198 -0
  149. package/skills/team-interactive-craft/SKILL.md +141 -0
  150. package/skills/team-issue/SKILL.md +171 -0
  151. package/skills/team-lifecycle-v4/SKILL.md +209 -0
  152. package/skills/team-motion-design/SKILL.md +142 -0
  153. package/skills/team-perf-opt/SKILL.md +175 -0
  154. package/skills/team-planex/SKILL.md +137 -0
  155. package/skills/team-quality-assurance/SKILL.md +147 -0
  156. package/skills/team-review/SKILL.md +147 -0
  157. package/skills/team-roadmap-dev/SKILL.md +169 -0
  158. package/skills/team-swarm/SKILL.md +178 -0
  159. package/skills/team-swarm/scripts/aco.py +473 -0
  160. package/skills/team-swarm/scripts/pheromone.py +144 -0
  161. package/skills/team-swarm/scripts/scoring.py +92 -0
  162. package/skills/team-swarm/scripts/test_aco.py +475 -0
  163. package/skills/team-tech-debt/SKILL.md +128 -0
  164. package/skills/team-testing/SKILL.md +143 -0
  165. package/skills/team-ui-polish/SKILL.md +141 -0
  166. package/skills/team-uidesign/SKILL.md +144 -0
  167. package/skills/team-ultra-analyze/SKILL.md +173 -0
  168. package/skills/team-ux-improve/SKILL.md +151 -0
  169. package/skills/team-visual-a11y/SKILL.md +156 -0
  170. package/skills/workflow-skill-designer/SKILL.md +496 -0
  171. package/src/extension/index.ts +222 -0
  172. package/src/extension/schemas.ts +131 -0
  173. package/src/providers/cli-tools-loader.ts +74 -0
  174. package/src/providers/provider-registry.ts +130 -0
  175. package/src/tools/delegate.ts +85 -0
  176. package/src/tools/explore.ts +134 -0
  177. package/src/tools/moa.ts +213 -0
  178. package/src/tools/status.ts +99 -0
  179. package/src/tools/wait.ts +166 -0
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Explore action — parallel code search via teammate sub-agents.
3
+ *
4
+ * For each prompt, spawns a teammate agent (explorer profile) that
5
+ * searches the codebase using read/grep/find/ls tools.
6
+ * Results are collected and merged into a unified exploration output.
7
+ */
8
+
9
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
10
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
11
+ import { runTeammate } from "@pi-maestro/teammate/src/runs/execution.ts";
12
+ import type { SingleResult } from "@pi-maestro/teammate/src/shared/types.ts";
13
+
14
+ export interface ExploreParams {
15
+ prompts?: string[];
16
+ endpoint?: string;
17
+ all?: boolean;
18
+ maxTurns?: number;
19
+ concurrency?: number;
20
+ model?: string;
21
+ cwd?: string;
22
+ }
23
+
24
+ export interface ExploreResult {
25
+ prompt: string;
26
+ agent: string;
27
+ model: string;
28
+ content: string;
29
+ exitCode: number;
30
+ durationMs: number;
31
+ }
32
+
33
+ /**
34
+ * Execute explore action: spawn parallel teammate agents for code search.
35
+ */
36
+ export async function executeExplore(
37
+ params: ExploreParams,
38
+ signal: AbortSignal,
39
+ ctx: ExtensionContext,
40
+ ): Promise<AgentToolResult> {
41
+ const prompts = params.prompts ?? [];
42
+
43
+ if (prompts.length === 0) {
44
+ return {
45
+ content: [{ type: "text", text: "No prompts provided for explore action." }],
46
+ isError: true,
47
+ };
48
+ }
49
+
50
+ const concurrency = params.concurrency ?? 4;
51
+ const results: ExploreResult[] = [];
52
+ const errors: string[] = [];
53
+
54
+ // Process prompts with concurrency limit
55
+ const queue = [...prompts];
56
+ const running: Promise<void>[] = [];
57
+
58
+ const processPrompt = async (prompt: string): Promise<void> => {
59
+ if (signal.aborted) return;
60
+
61
+ try {
62
+ const result = await runTeammate(
63
+ {
64
+ agent: "explorer",
65
+ task: prompt,
66
+ model: params.model ?? params.endpoint,
67
+ cwd: params.cwd,
68
+ reply_to: "caller",
69
+ lifecycle: "ephemeral",
70
+ },
71
+ {
72
+ baseCwd: ctx.cwd,
73
+ signal,
74
+ },
75
+ );
76
+
77
+ const lastMessage =
78
+ result.messages[result.messages.length - 1]?.content ?? "(no output)";
79
+
80
+ results.push({
81
+ prompt,
82
+ agent: result.agent,
83
+ model: result.model,
84
+ content: lastMessage,
85
+ exitCode: result.exitCode,
86
+ durationMs: result.durationMs,
87
+ });
88
+ } catch (error) {
89
+ errors.push(
90
+ `Prompt "${prompt.slice(0, 50)}": ${error instanceof Error ? error.message : String(error)}`,
91
+ );
92
+ }
93
+ };
94
+
95
+ // Dispatch with concurrency control
96
+ for (const prompt of queue) {
97
+ if (signal.aborted) break;
98
+
99
+ const task = processPrompt(prompt);
100
+ running.push(task);
101
+
102
+ if (running.length >= concurrency) {
103
+ await Promise.race(running);
104
+ // Remove completed tasks
105
+ for (let i = running.length - 1; i >= 0; i--) {
106
+ const settled = await Promise.race([
107
+ running[i].then(() => true),
108
+ Promise.resolve(false),
109
+ ]);
110
+ if (settled) running.splice(i, 1);
111
+ }
112
+ }
113
+ }
114
+
115
+ // Wait for remaining
116
+ await Promise.allSettled(running);
117
+
118
+ // Build output
119
+ const outputParts: string[] = [];
120
+ for (const result of results) {
121
+ outputParts.push(
122
+ `## Prompt: ${result.prompt.slice(0, 80)}\n**Model**: ${result.model} | **Duration**: ${result.durationMs}ms\n\n${result.content}\n`,
123
+ );
124
+ }
125
+
126
+ if (errors.length > 0) {
127
+ outputParts.push(`## Errors\n${errors.map((e) => `- ${e}`).join("\n")}`);
128
+ }
129
+
130
+ return {
131
+ content: [{ type: "text", text: outputParts.join("\n---\n\n") }],
132
+ isError: results.length === 0 && errors.length > 0,
133
+ };
134
+ }
@@ -0,0 +1,213 @@
1
+ /**
2
+ * MOA (Mixture-of-Agents) action — multi-model synthesis.
3
+ *
4
+ * Phase 1: Spawn parallel teammate agents (reference profile) across
5
+ * different models/endpoints for independent analysis.
6
+ * Phase 2: Spawn aggregator teammate with all reference outputs as
7
+ * context to synthesize a unified result.
8
+ */
9
+
10
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
11
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
12
+ import { runTeammate } from "@pi-maestro/teammate/src/runs/execution.ts";
13
+ import type { SingleResult } from "@pi-maestro/teammate/src/shared/types.ts";
14
+
15
+ export interface MoaParams {
16
+ prompts?: string[];
17
+ preset?: string;
18
+ maxTurns?: number;
19
+ model?: string;
20
+ cwd?: string;
21
+ }
22
+
23
+ interface ReferenceOutput {
24
+ model: string;
25
+ content: string;
26
+ exitCode: number;
27
+ durationMs: number;
28
+ }
29
+
30
+ /**
31
+ * Get available models from registered providers.
32
+ * Falls back to a default set if provider registry is not available.
33
+ */
34
+ function getAvailableModels(preset?: string): string[] {
35
+ // Default models for MOA — the provider registry will override these
36
+ // when fully connected. For now, use a reasonable default set.
37
+ return [
38
+ "anthropic/claude-sonnet-4",
39
+ "google/gemini-2.5-pro",
40
+ "openai/gpt-4.1",
41
+ ];
42
+ }
43
+
44
+ /**
45
+ * Execute MOA action: parallel reference analysis + aggregator synthesis.
46
+ */
47
+ export async function executeMoa(
48
+ params: MoaParams,
49
+ signal: AbortSignal,
50
+ ctx: ExtensionContext,
51
+ ): Promise<AgentToolResult> {
52
+ const prompts = params.prompts ?? [];
53
+ const primaryPrompt = prompts[0];
54
+
55
+ if (!primaryPrompt) {
56
+ return {
57
+ content: [
58
+ { type: "text", text: "No prompts provided for MOA action." },
59
+ ],
60
+ isError: true,
61
+ };
62
+ }
63
+
64
+ const models = getAvailableModels(params.preset);
65
+
66
+ // Phase 1: Spawn parallel reference agents across different models
67
+ const referenceOutputs: ReferenceOutput[] = [];
68
+ const referencePromises: Promise<void>[] = [];
69
+
70
+ for (const model of models) {
71
+ if (signal.aborted) break;
72
+
73
+ const promise = (async () => {
74
+ try {
75
+ const result = await runTeammate(
76
+ {
77
+ agent: "reference",
78
+ task: primaryPrompt,
79
+ model,
80
+ cwd: params.cwd,
81
+ reply_to: "caller",
82
+ lifecycle: "ephemeral",
83
+ },
84
+ {
85
+ baseCwd: ctx.cwd,
86
+ signal,
87
+ },
88
+ );
89
+
90
+ const lastMessage =
91
+ result.messages[result.messages.length - 1]?.content ?? "(no output)";
92
+
93
+ referenceOutputs.push({
94
+ model: result.model,
95
+ content: lastMessage,
96
+ exitCode: result.exitCode,
97
+ durationMs: result.durationMs,
98
+ });
99
+ } catch (error) {
100
+ referenceOutputs.push({
101
+ model,
102
+ content: `Error: ${error instanceof Error ? error.message : String(error)}`,
103
+ exitCode: 1,
104
+ durationMs: 0,
105
+ });
106
+ }
107
+ })();
108
+
109
+ referencePromises.push(promise);
110
+ }
111
+
112
+ // Wait for all reference agents
113
+ await Promise.allSettled(referencePromises);
114
+
115
+ if (signal.aborted) {
116
+ return {
117
+ content: [{ type: "text", text: "MOA operation was aborted." }],
118
+ isError: true,
119
+ };
120
+ }
121
+
122
+ const successfulOutputs = referenceOutputs.filter((r) => r.exitCode === 0);
123
+
124
+ if (successfulOutputs.length === 0) {
125
+ return {
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: `All reference agents failed.\n\n${referenceOutputs.map((r) => `${r.model}: ${r.content}`).join("\n\n")}`,
130
+ },
131
+ ],
132
+ isError: true,
133
+ };
134
+ }
135
+
136
+ // Phase 2: Aggregator synthesizes all reference outputs
137
+ const aggregationPrompt = buildAggregationPrompt(
138
+ primaryPrompt,
139
+ successfulOutputs,
140
+ );
141
+
142
+ try {
143
+ const aggregatorResult = await runTeammate(
144
+ {
145
+ agent: "aggregator",
146
+ task: aggregationPrompt,
147
+ model: params.model,
148
+ cwd: params.cwd,
149
+ reply_to: "caller",
150
+ lifecycle: "ephemeral",
151
+ },
152
+ {
153
+ baseCwd: ctx.cwd,
154
+ signal,
155
+ },
156
+ );
157
+
158
+ const lastMessage =
159
+ aggregatorResult.messages[aggregatorResult.messages.length - 1]
160
+ ?.content ?? "(no output)";
161
+
162
+ return {
163
+ content: [{ type: "text", text: lastMessage }],
164
+ isError: aggregatorResult.exitCode !== 0,
165
+ };
166
+ } catch (error) {
167
+ // Fallback: return raw reference outputs if aggregation fails
168
+ const fallbackOutput = successfulOutputs
169
+ .map((r) => `## ${r.model}\n\n${r.content}`)
170
+ .join("\n\n---\n\n");
171
+
172
+ return {
173
+ content: [
174
+ {
175
+ type: "text",
176
+ text: `Aggregation failed, returning raw reference outputs:\n\n${fallbackOutput}`,
177
+ },
178
+ ],
179
+ isError: false,
180
+ };
181
+ }
182
+ }
183
+
184
+ function buildAggregationPrompt(
185
+ originalPrompt: string,
186
+ referenceOutputs: ReferenceOutput[],
187
+ ): string {
188
+ const referenceSections = referenceOutputs
189
+ .map(
190
+ (r, i) =>
191
+ `### Reference ${i + 1} (${r.model})\n\n${r.content}`,
192
+ )
193
+ .join("\n\n---\n\n");
194
+
195
+ return `You are synthesizing multiple independent analyses into a unified, high-quality response.
196
+
197
+ ## Original Question/Task
198
+
199
+ ${originalPrompt}
200
+
201
+ ## Independent Reference Analyses
202
+
203
+ ${referenceSections}
204
+
205
+ ## Your Task
206
+
207
+ Synthesize these reference analyses into a single, comprehensive response:
208
+ 1. Identify areas of consensus across references
209
+ 2. Note any conflicts or divergent perspectives
210
+ 3. Produce a unified analysis that incorporates the strongest points from each reference
211
+ 4. Where references disagree, evaluate the evidence and make a reasoned judgment
212
+ 5. Ensure the final output is coherent, well-structured, and directly addresses the original question`;
213
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * maestro-status auxiliary tool.
3
+ *
4
+ * Inspects active and completed maestro runs.
5
+ * Supports fleet view (overview) and transcript view (tail logs).
6
+ */
7
+
8
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
9
+
10
+ interface MaestroState {
11
+ activeRuns: Map<
12
+ string,
13
+ {
14
+ action: string;
15
+ startedAt: number;
16
+ correlationId: string;
17
+ }
18
+ >;
19
+ }
20
+
21
+ interface StatusParams {
22
+ id?: string;
23
+ view?: "fleet" | "transcript";
24
+ }
25
+
26
+ function formatDuration(ms: number): string {
27
+ if (ms < 1000) return `${ms}ms`;
28
+ const seconds = Math.floor(ms / 1000);
29
+ if (seconds < 60) return `${seconds}s`;
30
+ const minutes = Math.floor(seconds / 60);
31
+ const remainingSeconds = seconds % 60;
32
+ return `${minutes}m${remainingSeconds}s`;
33
+ }
34
+
35
+ /**
36
+ * Get status of maestro runs.
37
+ */
38
+ export async function executeMaestroStatus(
39
+ params: StatusParams,
40
+ state: MaestroState,
41
+ ): Promise<AgentToolResult> {
42
+ const now = Date.now();
43
+
44
+ // Specific run by ID
45
+ if (params.id) {
46
+ const run = state.activeRuns.get(params.id);
47
+ if (!run) {
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text: `Run "${params.id}" not found. It may have completed.`,
53
+ },
54
+ ],
55
+ };
56
+ }
57
+
58
+ const elapsed = formatDuration(now - run.startedAt);
59
+ return {
60
+ content: [
61
+ {
62
+ type: "text",
63
+ text: [
64
+ `## Run: ${params.id}`,
65
+ `- **Action**: ${run.action}`,
66
+ `- **Status**: running`,
67
+ `- **Elapsed**: ${elapsed}`,
68
+ `- **Correlation ID**: ${run.correlationId}`,
69
+ ].join("\n"),
70
+ },
71
+ ],
72
+ };
73
+ }
74
+
75
+ // Fleet view (default)
76
+ if (state.activeRuns.size === 0) {
77
+ return {
78
+ content: [
79
+ {
80
+ type: "text",
81
+ text: "No active maestro runs.",
82
+ },
83
+ ],
84
+ };
85
+ }
86
+
87
+ const lines: string[] = ["## Maestro Fleet Status", ""];
88
+
89
+ for (const [id, run] of state.activeRuns) {
90
+ const elapsed = formatDuration(now - run.startedAt);
91
+ lines.push(`- **${id}**: ${run.action} (${elapsed})`);
92
+ }
93
+
94
+ lines.push("", `Total: ${state.activeRuns.size} active run(s)`);
95
+
96
+ return {
97
+ content: [{ type: "text", text: lines.join("\n") }],
98
+ };
99
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * maestro-wait auxiliary tool.
3
+ *
4
+ * Blocks until background maestro runs finish.
5
+ * Supports waiting for a specific run, the first to finish, or all runs.
6
+ */
7
+
8
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
9
+
10
+ interface MaestroState {
11
+ activeRuns: Map<
12
+ string,
13
+ {
14
+ action: string;
15
+ startedAt: number;
16
+ correlationId: string;
17
+ }
18
+ >;
19
+ }
20
+
21
+ interface WaitParams {
22
+ id?: string;
23
+ all?: boolean;
24
+ timeoutMs?: number;
25
+ }
26
+
27
+ const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
28
+ const POLL_INTERVAL_MS = 1000;
29
+
30
+ /**
31
+ * Wait for active maestro runs to complete.
32
+ *
33
+ * Uses a poll-based approach to check run completion status.
34
+ * Resolves when the target run(s) finish or timeout is reached.
35
+ */
36
+ export async function executeMaestroWait(
37
+ params: WaitParams,
38
+ signal: AbortSignal,
39
+ state: MaestroState,
40
+ ): Promise<AgentToolResult> {
41
+ const timeout = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;
42
+ const startTime = Date.now();
43
+
44
+ // No active runs
45
+ if (state.activeRuns.size === 0) {
46
+ return {
47
+ content: [
48
+ {
49
+ type: "text",
50
+ text: "No active maestro runs to wait for.",
51
+ },
52
+ ],
53
+ };
54
+ }
55
+
56
+ // If waiting for specific ID
57
+ if (params.id) {
58
+ const run = state.activeRuns.get(params.id);
59
+ if (!run) {
60
+ return {
61
+ content: [
62
+ {
63
+ type: "text",
64
+ text: `Run "${params.id}" not found in active runs.`,
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ }
70
+
71
+ // Poll until completion or timeout
72
+ return new Promise<AgentToolResult>((resolve) => {
73
+ const checkCompletion = () => {
74
+ // Check abort
75
+ if (signal.aborted) {
76
+ resolve({
77
+ content: [
78
+ {
79
+ type: "text",
80
+ text: "Wait was aborted.",
81
+ },
82
+ ],
83
+ });
84
+ return;
85
+ }
86
+
87
+ // Check timeout
88
+ if (Date.now() - startTime >= timeout) {
89
+ const remaining = Array.from(state.activeRuns.entries())
90
+ .map(([id, r]) => ` - ${id}: ${r.action} (running ${Math.floor((Date.now() - r.startedAt) / 1000)}s)`)
91
+ .join("\n");
92
+
93
+ resolve({
94
+ content: [
95
+ {
96
+ type: "text",
97
+ text: `Wait timed out after ${timeout}ms. Active runs:\n${remaining}`,
98
+ },
99
+ ],
100
+ });
101
+ return;
102
+ }
103
+
104
+ // Check if target run(s) finished
105
+ if (params.id) {
106
+ if (!state.activeRuns.has(params.id)) {
107
+ resolve({
108
+ content: [
109
+ {
110
+ type: "text",
111
+ text: `Run "${params.id}" completed.`,
112
+ },
113
+ ],
114
+ });
115
+ return;
116
+ }
117
+ } else if (params.all) {
118
+ if (state.activeRuns.size === 0) {
119
+ resolve({
120
+ content: [
121
+ {
122
+ type: "text",
123
+ text: "All maestro runs completed.",
124
+ },
125
+ ],
126
+ });
127
+ return;
128
+ }
129
+ } else {
130
+ // Wait for first — check if any have completed since we started
131
+ // (This simple implementation checks if the count decreased)
132
+ // A full implementation would use event subscriptions
133
+ if (state.activeRuns.size === 0) {
134
+ resolve({
135
+ content: [
136
+ {
137
+ type: "text",
138
+ text: "A maestro run completed.",
139
+ },
140
+ ],
141
+ });
142
+ return;
143
+ }
144
+ }
145
+
146
+ // Keep polling
147
+ setTimeout(checkCompletion, POLL_INTERVAL_MS);
148
+ };
149
+
150
+ // Start polling
151
+ checkCompletion();
152
+
153
+ // Handle abort signal
154
+ signal.addEventListener(
155
+ "abort",
156
+ () => {
157
+ resolve({
158
+ content: [
159
+ { type: "text", text: "Wait was aborted." },
160
+ ],
161
+ });
162
+ },
163
+ { once: true },
164
+ );
165
+ });
166
+ }