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,222 @@
1
+ /**
2
+ * Maestro Agent Extension Entry Point
3
+ *
4
+ * Registers three tools:
5
+ * - maestro: Main tool with action-based dispatch (explore, delegate, moa)
6
+ * - maestro-wait: Block until background maestro runs finish
7
+ * - maestro-status: Inspect active/completed runs
8
+ *
9
+ * Also registers dynamic LLM providers from cli-tools.json at startup.
10
+ */
11
+
12
+ import type {
13
+ ExtensionAPI,
14
+ ExtensionContext,
15
+ ToolDefinition,
16
+ } from "@earendil-works/pi-coding-agent";
17
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
18
+ import { Text } from "@earendil-works/pi-tui";
19
+ import {
20
+ MaestroParams,
21
+ MaestroWaitParams,
22
+ MaestroStatusParams,
23
+ } from "./schemas.ts";
24
+ import { executeExplore, type ExploreParams } from "../tools/explore.ts";
25
+ import { executeDelegate, type DelegateParams } from "../tools/delegate.ts";
26
+ import { executeMoa, type MoaParams } from "../tools/moa.ts";
27
+ import { executeMaestroWait } from "../tools/wait.ts";
28
+ import { executeMaestroStatus } from "../tools/status.ts";
29
+ import { registerMaestroProviders } from "../providers/provider-registry.ts";
30
+
31
+ interface MaestroState {
32
+ baseCwd: string;
33
+ activeRuns: Map<
34
+ string,
35
+ {
36
+ action: string;
37
+ startedAt: number;
38
+ correlationId: string;
39
+ }
40
+ >;
41
+ }
42
+
43
+ export default function registerMaestroExtension(pi: ExtensionAPI): void {
44
+ const state: MaestroState = {
45
+ baseCwd: "",
46
+ activeRuns: new Map(),
47
+ };
48
+
49
+ // Register dynamic providers from cli-tools.json
50
+ try {
51
+ registerMaestroProviders(pi);
52
+ } catch (error) {
53
+ // Provider registration failures should not block extension load
54
+ console.error(
55
+ `[maestro] Provider registration warning: ${error instanceof Error ? error.message : String(error)}`,
56
+ );
57
+ }
58
+
59
+ // === Main Tool: maestro ===
60
+ const maestroTool: ToolDefinition<typeof MaestroParams> = {
61
+ name: "maestro",
62
+ label: "Maestro",
63
+ description: `Maestro flow command tool with three actions:
64
+
65
+ - **explore**: Parallel code search via teammate agents. Each prompt spawns an independent search agent.
66
+ { action: "explore", prompts: ["FIND: auth middleware\\nSCOPE: src/"], model: "..." }
67
+
68
+ - **delegate**: Delegate a task to a specific model/provider for analysis or implementation.
69
+ { action: "delegate", prompt: "Analyze the auth flow", tool: "gemini", mode: "analysis" }
70
+
71
+ - **moa**: Mixture-of-Agents — parallel reference analysis across models, then aggregator synthesis.
72
+ { action: "moa", prompts: ["Compare auth strategies"], preset: "deep" }`,
73
+
74
+ parameters: MaestroParams,
75
+
76
+ async execute(
77
+ id: string,
78
+ params: Record<string, unknown>,
79
+ signal: AbortSignal,
80
+ onUpdate:
81
+ | ((result: AgentToolResult) => void)
82
+ | undefined,
83
+ ctx: ExtensionContext,
84
+ ): Promise<AgentToolResult> {
85
+ const action = params.action as string;
86
+
87
+ // Track run
88
+ state.activeRuns.set(id, {
89
+ action,
90
+ startedAt: Date.now(),
91
+ correlationId: id,
92
+ });
93
+
94
+ try {
95
+ switch (action) {
96
+ case "explore":
97
+ return await executeExplore(
98
+ params as unknown as ExploreParams,
99
+ signal,
100
+ ctx,
101
+ );
102
+
103
+ case "delegate":
104
+ return await executeDelegate(
105
+ params as unknown as DelegateParams,
106
+ signal,
107
+ ctx,
108
+ );
109
+
110
+ case "moa":
111
+ return await executeMoa(
112
+ params as unknown as MoaParams,
113
+ signal,
114
+ ctx,
115
+ );
116
+
117
+ default:
118
+ return {
119
+ content: [
120
+ {
121
+ type: "text",
122
+ text: `Unknown action "${action}". Valid actions: explore, delegate, moa`,
123
+ },
124
+ ],
125
+ isError: true,
126
+ };
127
+ }
128
+ } finally {
129
+ state.activeRuns.delete(id);
130
+ }
131
+ },
132
+
133
+ renderCall(args, theme) {
134
+ const action = (args.action as string) ?? "?";
135
+ const asyncLabel =
136
+ args.async === true ? theme.fg("warning", " [async]") : "";
137
+
138
+ let detail = "";
139
+ if (action === "explore") {
140
+ const prompts = args.prompts as string[] | undefined;
141
+ detail = prompts
142
+ ? ` (${prompts.length} prompt${prompts.length !== 1 ? "s" : ""})`
143
+ : "";
144
+ } else if (action === "delegate") {
145
+ const tool = (args.tool as string) ?? "";
146
+ detail = tool ? ` ${theme.fg("accent", tool)}` : "";
147
+ } else if (action === "moa") {
148
+ detail = "";
149
+ }
150
+
151
+ return new Text(
152
+ `${theme.fg("toolTitle", theme.bold("maestro "))}${action}${detail}${asyncLabel}`,
153
+ 0,
154
+ 0,
155
+ );
156
+ },
157
+ };
158
+
159
+ pi.registerTool(maestroTool);
160
+
161
+ // === Auxiliary Tool: maestro-wait ===
162
+ const waitTool: ToolDefinition<typeof MaestroWaitParams> = {
163
+ name: "maestro-wait",
164
+ label: "Maestro Wait",
165
+ description: `Block until background (async) maestro runs finish.
166
+
167
+ - { } — wait for first active run to finish (default)
168
+ - { all: true } — wait for all active runs to finish
169
+ - { id: "..." } — wait for a specific run
170
+ - { timeoutMs: 600000 } — timeout after N ms (runs continue regardless)`,
171
+
172
+ parameters: MaestroWaitParams,
173
+
174
+ async execute(
175
+ _id: string,
176
+ params: Record<string, unknown>,
177
+ signal: AbortSignal,
178
+ ): Promise<AgentToolResult> {
179
+ return executeMaestroWait(
180
+ params as { id?: string; all?: boolean; timeoutMs?: number },
181
+ signal,
182
+ state,
183
+ );
184
+ },
185
+ };
186
+
187
+ pi.registerTool(waitTool);
188
+
189
+ // === Auxiliary Tool: maestro-status ===
190
+ const statusTool: ToolDefinition<typeof MaestroStatusParams> = {
191
+ name: "maestro-status",
192
+ label: "Maestro Status",
193
+ description: `Inspect maestro run status.
194
+
195
+ - { } — fleet overview of all active runs
196
+ - { id: "..." } — details for a specific run
197
+ - { view: "transcript" } — tail the latest run transcript`,
198
+
199
+ parameters: MaestroStatusParams,
200
+
201
+ async execute(
202
+ _id: string,
203
+ params: Record<string, unknown>,
204
+ ): Promise<AgentToolResult> {
205
+ return executeMaestroStatus(
206
+ params as { id?: string; view?: "fleet" | "transcript" },
207
+ state,
208
+ );
209
+ },
210
+ };
211
+
212
+ pi.registerTool(statusTool);
213
+
214
+ // Session lifecycle
215
+ pi.on("session_start", (_event, ctx) => {
216
+ state.baseCwd = ctx.cwd;
217
+ });
218
+
219
+ pi.on("session_shutdown", () => {
220
+ state.activeRuns.clear();
221
+ });
222
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * TypeBox schemas for the maestro tool with action-based dispatch.
3
+ *
4
+ * Main tool: maestro (action: explore | delegate | moa)
5
+ * Auxiliary tools: maestro-wait, maestro-status
6
+ */
7
+
8
+ import { Type } from "typebox";
9
+
10
+ function StringEnum<T extends string[]>(values: [...T]) {
11
+ return Type.Unsafe<T[number]>({
12
+ type: "string",
13
+ enum: values,
14
+ });
15
+ }
16
+
17
+ export const MaestroParams = Type.Object({
18
+ // === Action Dispatch ===
19
+ action: StringEnum(["explore", "delegate", "moa"]),
20
+
21
+ // === Explore Action Fields ===
22
+ prompts: Type.Optional(
23
+ Type.Array(Type.String(), {
24
+ description:
25
+ "Search prompts for explore action (each prompt = one parallel agent)",
26
+ }),
27
+ ),
28
+ endpoint: Type.Optional(
29
+ Type.String({
30
+ description: "Specific model/endpoint for explore agents",
31
+ }),
32
+ ),
33
+ all: Type.Optional(
34
+ Type.Boolean({
35
+ description: "Fan out each prompt to all registered endpoints",
36
+ }),
37
+ ),
38
+ maxTurns: Type.Optional(
39
+ Type.Integer({
40
+ minimum: 1,
41
+ description: "Maximum agent turns per exploration job",
42
+ }),
43
+ ),
44
+ concurrency: Type.Optional(
45
+ Type.Integer({
46
+ minimum: 1,
47
+ description: "Maximum concurrent explore agents (default: 4)",
48
+ }),
49
+ ),
50
+
51
+ // === Delegate Action Fields ===
52
+ prompt: Type.Optional(
53
+ Type.String({
54
+ description: "Task prompt for delegate action",
55
+ }),
56
+ ),
57
+ tool: Type.Optional(
58
+ Type.String({
59
+ description:
60
+ "Target tool/provider for delegate (e.g., 'gemini', 'claude', 'codex')",
61
+ }),
62
+ ),
63
+ mode: Type.Optional(
64
+ StringEnum(["analysis", "write"]),
65
+ ),
66
+ model: Type.Optional(
67
+ Type.String({
68
+ description: "Model override for delegate or explore",
69
+ }),
70
+ ),
71
+ rule: Type.Optional(
72
+ Type.String({
73
+ description: "Protocol + prompt template for delegate",
74
+ }),
75
+ ),
76
+
77
+ // === MOA Action Fields ===
78
+ preset: Type.Optional(
79
+ Type.String({
80
+ description: "MOA preset configuration name",
81
+ }),
82
+ ),
83
+
84
+ // === Common Fields ===
85
+ cwd: Type.Optional(
86
+ Type.String({
87
+ description: "Working directory for the operation",
88
+ }),
89
+ ),
90
+ async: Type.Optional(
91
+ Type.Boolean({
92
+ description: "Run in background (default: false)",
93
+ }),
94
+ ),
95
+ timeoutMs: Type.Optional(
96
+ Type.Integer({
97
+ minimum: 1,
98
+ description: "Timeout in milliseconds",
99
+ }),
100
+ ),
101
+ });
102
+
103
+ export const MaestroWaitParams = Type.Object({
104
+ id: Type.Optional(
105
+ Type.String({
106
+ description: "Specific run ID to wait for (omit to wait for any)",
107
+ }),
108
+ ),
109
+ all: Type.Optional(
110
+ Type.Boolean({
111
+ description: "Wait for all active runs (default: wait for first)",
112
+ }),
113
+ ),
114
+ timeoutMs: Type.Optional(
115
+ Type.Integer({
116
+ minimum: 1,
117
+ description: "Timeout in milliseconds (default: 1800000 / 30 min)",
118
+ }),
119
+ ),
120
+ });
121
+
122
+ export const MaestroStatusParams = Type.Object({
123
+ id: Type.Optional(
124
+ Type.String({
125
+ description: "Specific run ID to inspect",
126
+ }),
127
+ ),
128
+ view: Type.Optional(
129
+ StringEnum(["fleet", "transcript"]),
130
+ ),
131
+ });
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Load and parse ~/.maestro/cli-tools.json configuration.
3
+ *
4
+ * Maps each enabled CLI tool to its provider configuration for
5
+ * dynamic pi provider registration.
6
+ */
7
+
8
+ import * as fs from "node:fs";
9
+ import * as os from "node:os";
10
+ import * as path from "node:path";
11
+
12
+ export interface CliToolConfig {
13
+ enabled: boolean;
14
+ primaryModel: string;
15
+ tags: string[];
16
+ type: string;
17
+ settingsFile?: string;
18
+ reasoningEffort?: string;
19
+ }
20
+
21
+ export interface CliToolsConfig {
22
+ version: string;
23
+ tools: Record<string, CliToolConfig>;
24
+ roles: Record<string, unknown>;
25
+ proxy?: {
26
+ enabled: boolean;
27
+ httpProxy?: string;
28
+ noProxy?: string;
29
+ };
30
+ }
31
+
32
+ const CLI_TOOLS_PATH = path.join(os.homedir(), ".maestro", "cli-tools.json");
33
+
34
+ /**
35
+ * Load cli-tools.json from the default location.
36
+ * Returns null if the file doesn't exist or can't be parsed.
37
+ */
38
+ export function loadCliToolsConfig(
39
+ configPath?: string,
40
+ ): CliToolsConfig | null {
41
+ const filePath = configPath ?? CLI_TOOLS_PATH;
42
+
43
+ try {
44
+ const content = fs.readFileSync(filePath, "utf-8");
45
+ const parsed = JSON.parse(content) as unknown;
46
+
47
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
48
+ return null;
49
+ }
50
+
51
+ const config = parsed as Record<string, unknown>;
52
+
53
+ // Validate basic structure
54
+ if (!config.tools || typeof config.tools !== "object") {
55
+ return null;
56
+ }
57
+
58
+ return parsed as CliToolsConfig;
59
+ } catch {
60
+ // File missing or invalid — not an error, providers are optional
61
+ return null;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Get all enabled tools from the config.
67
+ */
68
+ export function getEnabledTools(
69
+ config: CliToolsConfig,
70
+ ): Array<{ name: string; config: CliToolConfig }> {
71
+ return Object.entries(config.tools)
72
+ .filter(([_, toolConfig]) => toolConfig.enabled)
73
+ .map(([name, toolConfig]) => ({ name, config: toolConfig }));
74
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Dynamic LLM provider registration from cli-tools.json.
3
+ *
4
+ * At extension startup, reads cli-tools.json and registers each enabled
5
+ * tool as a pi provider via pi.registerProvider(). Uses pi CredentialStore
6
+ * for authentication ($API_KEY env var pattern).
7
+ */
8
+
9
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
10
+ import {
11
+ loadCliToolsConfig,
12
+ getEnabledTools,
13
+ type CliToolConfig,
14
+ } from "./cli-tools-loader.ts";
15
+
16
+ /**
17
+ * Map a CLI tool type to its pi provider configuration.
18
+ */
19
+ function mapToolToProviderConfig(
20
+ name: string,
21
+ config: CliToolConfig,
22
+ ): {
23
+ apiType: string;
24
+ envVar: string;
25
+ baseUrl?: string;
26
+ } {
27
+ // Map tool names/types to API configurations
28
+ switch (name) {
29
+ case "claude":
30
+ return {
31
+ apiType: "anthropic-messages",
32
+ envVar: "ANTHROPIC_API_KEY",
33
+ };
34
+
35
+ case "gemini":
36
+ return {
37
+ apiType: "google-genai",
38
+ envVar: "GOOGLE_API_KEY",
39
+ };
40
+
41
+ case "codex":
42
+ return {
43
+ apiType: "openai-completions",
44
+ envVar: "OPENAI_API_KEY",
45
+ };
46
+
47
+ case "opencode":
48
+ return {
49
+ apiType: "openai-compatible",
50
+ envVar: "OPENAI_API_KEY",
51
+ baseUrl: "https://api.openai.com/v1",
52
+ };
53
+
54
+ case "agy":
55
+ return {
56
+ apiType: "openai-compatible",
57
+ envVar: "AGY_API_KEY",
58
+ };
59
+
60
+ case "api-explore":
61
+ return {
62
+ apiType: "openai-compatible",
63
+ envVar: "API_EXPLORE_KEY",
64
+ };
65
+
66
+ default:
67
+ // Generic OpenAI-compatible fallback
68
+ return {
69
+ apiType: "openai-compatible",
70
+ envVar: `${name.toUpperCase()}_API_KEY`,
71
+ };
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Register all enabled CLI tools as pi providers.
77
+ *
78
+ * Reads cli-tools.json, maps each enabled tool to a provider config,
79
+ * and calls pi.registerProvider() for each.
80
+ */
81
+ export function registerMaestroProviders(pi: ExtensionAPI): void {
82
+ const cliConfig = loadCliToolsConfig();
83
+ if (!cliConfig) {
84
+ // No cli-tools.json — silently skip provider registration
85
+ return;
86
+ }
87
+
88
+ const enabledTools = getEnabledTools(cliConfig);
89
+
90
+ for (const { name, config: toolConfig } of enabledTools) {
91
+ if (!toolConfig.primaryModel) {
92
+ // Skip tools without a primary model (e.g., opencode with empty model)
93
+ continue;
94
+ }
95
+
96
+ const providerMapping = mapToolToProviderConfig(name, toolConfig);
97
+
98
+ try {
99
+ // Register the provider with pi
100
+ // Note: pi.registerProvider() is the ExtensionAPI method for adding
101
+ // LLM providers dynamically. The exact signature depends on the
102
+ // pi-agent-core version.
103
+ if (typeof pi.registerProvider === "function") {
104
+ pi.registerProvider(name, {
105
+ apiType: providerMapping.apiType,
106
+ apiKey: `$${providerMapping.envVar}`,
107
+ ...(providerMapping.baseUrl
108
+ ? { baseUrl: providerMapping.baseUrl }
109
+ : {}),
110
+ models: [
111
+ {
112
+ id: toolConfig.primaryModel,
113
+ name: toolConfig.primaryModel,
114
+ reasoning: false,
115
+ input: ["text"],
116
+ },
117
+ ],
118
+ ...(cliConfig.proxy?.enabled && cliConfig.proxy.httpProxy
119
+ ? { proxy: cliConfig.proxy.httpProxy }
120
+ : {}),
121
+ });
122
+ }
123
+ } catch (error) {
124
+ // Individual provider registration failures should not block others
125
+ console.error(
126
+ `[maestro] Failed to register provider "${name}": ${error instanceof Error ? error.message : String(error)}`,
127
+ );
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Delegate action — task delegation to a specific model/provider.
3
+ *
4
+ * Spawns a single teammate agent (delegate profile) with the specified
5
+ * model override from registered providers, passing the prompt as task
6
+ * with analysis or write mode.
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
+
13
+ export interface DelegateParams {
14
+ prompt?: string;
15
+ tool?: string;
16
+ mode?: "analysis" | "write";
17
+ model?: string;
18
+ rule?: string;
19
+ cwd?: string;
20
+ }
21
+
22
+ /**
23
+ * Execute delegate action: spawn a teammate with specified model override.
24
+ */
25
+ export async function executeDelegate(
26
+ params: DelegateParams,
27
+ signal: AbortSignal,
28
+ ctx: ExtensionContext,
29
+ ): Promise<AgentToolResult> {
30
+ if (!params.prompt) {
31
+ return {
32
+ content: [
33
+ { type: "text", text: "No prompt provided for delegate action." },
34
+ ],
35
+ isError: true,
36
+ };
37
+ }
38
+
39
+ // Build task with mode context
40
+ let task = params.prompt;
41
+ if (params.mode) {
42
+ task = `MODE: ${params.mode}\n\n${task}`;
43
+ }
44
+ if (params.rule) {
45
+ task = `RULE: ${params.rule}\n\n${task}`;
46
+ }
47
+
48
+ // Resolve model from tool name or explicit model
49
+ const model = params.model ?? params.tool;
50
+
51
+ try {
52
+ const result = await runTeammate(
53
+ {
54
+ agent: "delegate",
55
+ task,
56
+ model,
57
+ cwd: params.cwd,
58
+ reply_to: "caller",
59
+ lifecycle: "ephemeral",
60
+ },
61
+ {
62
+ baseCwd: ctx.cwd,
63
+ signal,
64
+ },
65
+ );
66
+
67
+ const lastMessage =
68
+ result.messages[result.messages.length - 1]?.content ?? "(no output)";
69
+
70
+ return {
71
+ content: [{ type: "text", text: lastMessage }],
72
+ isError: result.exitCode !== 0,
73
+ };
74
+ } catch (error) {
75
+ return {
76
+ content: [
77
+ {
78
+ type: "text",
79
+ text: `Delegate failed: ${error instanceof Error ? error.message : String(error)}`,
80
+ },
81
+ ],
82
+ isError: true,
83
+ };
84
+ }
85
+ }