@hypercli/gen 0.1.1

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 (306) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +24 -0
  3. package/dist/actions/communication.d.ts +201 -0
  4. package/dist/actions/communication.d.ts.map +1 -0
  5. package/dist/actions/communication.js +515 -0
  6. package/dist/actions/communication.js.map +1 -0
  7. package/dist/actions/decorator.d.ts +22 -0
  8. package/dist/actions/decorator.d.ts.map +1 -0
  9. package/dist/actions/decorator.js +110 -0
  10. package/dist/actions/decorator.js.map +1 -0
  11. package/dist/actions/executor.d.ts +85 -0
  12. package/dist/actions/executor.d.ts.map +1 -0
  13. package/dist/actions/executor.js +289 -0
  14. package/dist/actions/executor.js.map +1 -0
  15. package/dist/actions/index.d.ts +14 -0
  16. package/dist/actions/index.d.ts.map +1 -0
  17. package/dist/actions/index.js +15 -0
  18. package/dist/actions/index.js.map +1 -0
  19. package/dist/actions/parameter-resolver.d.ts +54 -0
  20. package/dist/actions/parameter-resolver.d.ts.map +1 -0
  21. package/dist/actions/parameter-resolver.js +300 -0
  22. package/dist/actions/parameter-resolver.js.map +1 -0
  23. package/dist/actions/registry.d.ts +78 -0
  24. package/dist/actions/registry.d.ts.map +1 -0
  25. package/dist/actions/registry.js +221 -0
  26. package/dist/actions/registry.js.map +1 -0
  27. package/dist/actions/types.d.ts +109 -0
  28. package/dist/actions/types.d.ts.map +1 -0
  29. package/dist/actions/types.js +31 -0
  30. package/dist/actions/types.js.map +1 -0
  31. package/dist/actions/utils.d.ts +42 -0
  32. package/dist/actions/utils.d.ts.map +1 -0
  33. package/dist/actions/utils.js +144 -0
  34. package/dist/actions/utils.js.map +1 -0
  35. package/dist/ai/ai-collector.d.ts +52 -0
  36. package/dist/ai/ai-collector.d.ts.map +1 -0
  37. package/dist/ai/ai-collector.js +64 -0
  38. package/dist/ai/ai-collector.js.map +1 -0
  39. package/dist/ai/ai-config.d.ts +230 -0
  40. package/dist/ai/ai-config.d.ts.map +1 -0
  41. package/dist/ai/ai-config.js +8 -0
  42. package/dist/ai/ai-config.js.map +1 -0
  43. package/dist/ai/ai-service.d.ts +66 -0
  44. package/dist/ai/ai-service.d.ts.map +1 -0
  45. package/dist/ai/ai-service.js +198 -0
  46. package/dist/ai/ai-service.js.map +1 -0
  47. package/dist/ai/ai-variable-resolver.d.ts +59 -0
  48. package/dist/ai/ai-variable-resolver.d.ts.map +1 -0
  49. package/dist/ai/ai-variable-resolver.js +219 -0
  50. package/dist/ai/ai-variable-resolver.js.map +1 -0
  51. package/dist/ai/context-collector.d.ts +30 -0
  52. package/dist/ai/context-collector.d.ts.map +1 -0
  53. package/dist/ai/context-collector.js +158 -0
  54. package/dist/ai/context-collector.js.map +1 -0
  55. package/dist/ai/cost-tracker.d.ts +41 -0
  56. package/dist/ai/cost-tracker.d.ts.map +1 -0
  57. package/dist/ai/cost-tracker.js +131 -0
  58. package/dist/ai/cost-tracker.js.map +1 -0
  59. package/dist/ai/env.d.ts +36 -0
  60. package/dist/ai/env.d.ts.map +1 -0
  61. package/dist/ai/env.js +100 -0
  62. package/dist/ai/env.js.map +1 -0
  63. package/dist/ai/index.d.ts +17 -0
  64. package/dist/ai/index.d.ts.map +1 -0
  65. package/dist/ai/index.js +25 -0
  66. package/dist/ai/index.js.map +1 -0
  67. package/dist/ai/model-router.d.ts +32 -0
  68. package/dist/ai/model-router.d.ts.map +1 -0
  69. package/dist/ai/model-router.js +113 -0
  70. package/dist/ai/model-router.js.map +1 -0
  71. package/dist/ai/output-validator.d.ts +24 -0
  72. package/dist/ai/output-validator.d.ts.map +1 -0
  73. package/dist/ai/output-validator.js +279 -0
  74. package/dist/ai/output-validator.js.map +1 -0
  75. package/dist/ai/prompt-assembler.d.ts +30 -0
  76. package/dist/ai/prompt-assembler.d.ts.map +1 -0
  77. package/dist/ai/prompt-assembler.js +93 -0
  78. package/dist/ai/prompt-assembler.js.map +1 -0
  79. package/dist/ai/prompt-pipeline.d.ts +63 -0
  80. package/dist/ai/prompt-pipeline.d.ts.map +1 -0
  81. package/dist/ai/prompt-pipeline.js +119 -0
  82. package/dist/ai/prompt-pipeline.js.map +1 -0
  83. package/dist/ai/prompt-template.jig +88 -0
  84. package/dist/ai/transports/api-transport.d.ts +12 -0
  85. package/dist/ai/transports/api-transport.d.ts.map +1 -0
  86. package/dist/ai/transports/api-transport.js +86 -0
  87. package/dist/ai/transports/api-transport.js.map +1 -0
  88. package/dist/ai/transports/command-transport.d.ts +20 -0
  89. package/dist/ai/transports/command-transport.d.ts.map +1 -0
  90. package/dist/ai/transports/command-transport.js +203 -0
  91. package/dist/ai/transports/command-transport.js.map +1 -0
  92. package/dist/ai/transports/index.d.ts +11 -0
  93. package/dist/ai/transports/index.d.ts.map +1 -0
  94. package/dist/ai/transports/index.js +10 -0
  95. package/dist/ai/transports/index.js.map +1 -0
  96. package/dist/ai/transports/resolve-transport.d.ts +15 -0
  97. package/dist/ai/transports/resolve-transport.d.ts.map +1 -0
  98. package/dist/ai/transports/resolve-transport.js +96 -0
  99. package/dist/ai/transports/resolve-transport.js.map +1 -0
  100. package/dist/ai/transports/stdout-transport.d.ts +14 -0
  101. package/dist/ai/transports/stdout-transport.d.ts.map +1 -0
  102. package/dist/ai/transports/stdout-transport.js +27 -0
  103. package/dist/ai/transports/stdout-transport.js.map +1 -0
  104. package/dist/ai/transports/types.d.ts +77 -0
  105. package/dist/ai/transports/types.d.ts.map +1 -0
  106. package/dist/ai/transports/types.js +8 -0
  107. package/dist/ai/transports/types.js.map +1 -0
  108. package/dist/commands/cookbook/info.d.ts +22 -0
  109. package/dist/commands/cookbook/info.d.ts.map +1 -0
  110. package/dist/commands/cookbook/info.js +217 -0
  111. package/dist/commands/cookbook/info.js.map +1 -0
  112. package/dist/commands/cookbook/list.d.ts +20 -0
  113. package/dist/commands/cookbook/list.d.ts.map +1 -0
  114. package/dist/commands/cookbook/list.js +133 -0
  115. package/dist/commands/cookbook/list.js.map +1 -0
  116. package/dist/commands/gen.d.ts +65 -0
  117. package/dist/commands/gen.d.ts.map +1 -0
  118. package/dist/commands/gen.js +478 -0
  119. package/dist/commands/gen.js.map +1 -0
  120. package/dist/commands/recipe/info.d.ts +18 -0
  121. package/dist/commands/recipe/info.d.ts.map +1 -0
  122. package/dist/commands/recipe/info.js +89 -0
  123. package/dist/commands/recipe/info.js.map +1 -0
  124. package/dist/commands/recipe/list.d.ts +29 -0
  125. package/dist/commands/recipe/list.d.ts.map +1 -0
  126. package/dist/commands/recipe/list.js +215 -0
  127. package/dist/commands/recipe/list.js.map +1 -0
  128. package/dist/commands/recipe/run.d.ts +44 -0
  129. package/dist/commands/recipe/run.d.ts.map +1 -0
  130. package/dist/commands/recipe/run.js +239 -0
  131. package/dist/commands/recipe/run.js.map +1 -0
  132. package/dist/commands/recipe/validate.d.ts +19 -0
  133. package/dist/commands/recipe/validate.d.ts.map +1 -0
  134. package/dist/commands/recipe/validate.js +66 -0
  135. package/dist/commands/recipe/validate.js.map +1 -0
  136. package/dist/discovery/generator-discovery.d.ts +130 -0
  137. package/dist/discovery/generator-discovery.d.ts.map +1 -0
  138. package/dist/discovery/generator-discovery.js +674 -0
  139. package/dist/discovery/generator-discovery.js.map +1 -0
  140. package/dist/discovery/index.d.ts +8 -0
  141. package/dist/discovery/index.d.ts.map +1 -0
  142. package/dist/discovery/index.js +7 -0
  143. package/dist/discovery/index.js.map +1 -0
  144. package/dist/hooks/command-not-found.d.ts +18 -0
  145. package/dist/hooks/command-not-found.d.ts.map +1 -0
  146. package/dist/hooks/command-not-found.js +182 -0
  147. package/dist/hooks/command-not-found.js.map +1 -0
  148. package/dist/hooks/suggest.d.ts +13 -0
  149. package/dist/hooks/suggest.d.ts.map +1 -0
  150. package/dist/hooks/suggest.js +28 -0
  151. package/dist/hooks/suggest.js.map +1 -0
  152. package/dist/index.d.ts +2 -0
  153. package/dist/index.d.ts.map +1 -0
  154. package/dist/index.js +3 -0
  155. package/dist/index.js.map +1 -0
  156. package/dist/lib/base-command.d.ts +26 -0
  157. package/dist/lib/base-command.d.ts.map +1 -0
  158. package/dist/lib/base-command.js +24 -0
  159. package/dist/lib/base-command.js.map +1 -0
  160. package/dist/lib/flags.d.ts +33 -0
  161. package/dist/lib/flags.d.ts.map +1 -0
  162. package/dist/lib/flags.js +64 -0
  163. package/dist/lib/flags.js.map +1 -0
  164. package/dist/ops/add.d.ts +4 -0
  165. package/dist/ops/add.d.ts.map +1 -0
  166. package/dist/ops/add.js +85 -0
  167. package/dist/ops/add.js.map +1 -0
  168. package/dist/ops/inject.d.ts +4 -0
  169. package/dist/ops/inject.d.ts.map +1 -0
  170. package/dist/ops/inject.js +28 -0
  171. package/dist/ops/inject.js.map +1 -0
  172. package/dist/ops/injector.d.ts +4 -0
  173. package/dist/ops/injector.d.ts.map +1 -0
  174. package/dist/ops/injector.js +68 -0
  175. package/dist/ops/injector.js.map +1 -0
  176. package/dist/ops/result.d.ts +3 -0
  177. package/dist/ops/result.d.ts.map +1 -0
  178. package/dist/ops/result.js +8 -0
  179. package/dist/ops/result.js.map +1 -0
  180. package/dist/prompts/interactive-prompts.d.ts +152 -0
  181. package/dist/prompts/interactive-prompts.d.ts.map +1 -0
  182. package/dist/prompts/interactive-prompts.js +574 -0
  183. package/dist/prompts/interactive-prompts.js.map +1 -0
  184. package/dist/recipe-engine/group-executor.d.ts +97 -0
  185. package/dist/recipe-engine/group-executor.d.ts.map +1 -0
  186. package/dist/recipe-engine/group-executor.js +293 -0
  187. package/dist/recipe-engine/group-executor.js.map +1 -0
  188. package/dist/recipe-engine/index.d.ts +112 -0
  189. package/dist/recipe-engine/index.d.ts.map +1 -0
  190. package/dist/recipe-engine/index.js +223 -0
  191. package/dist/recipe-engine/index.js.map +1 -0
  192. package/dist/recipe-engine/output-evaluator.d.ts +28 -0
  193. package/dist/recipe-engine/output-evaluator.d.ts.map +1 -0
  194. package/dist/recipe-engine/output-evaluator.js +78 -0
  195. package/dist/recipe-engine/output-evaluator.js.map +1 -0
  196. package/dist/recipe-engine/recipe-engine.d.ts +227 -0
  197. package/dist/recipe-engine/recipe-engine.d.ts.map +1 -0
  198. package/dist/recipe-engine/recipe-engine.js +1036 -0
  199. package/dist/recipe-engine/recipe-engine.js.map +1 -0
  200. package/dist/recipe-engine/step-executor.d.ts +172 -0
  201. package/dist/recipe-engine/step-executor.d.ts.map +1 -0
  202. package/dist/recipe-engine/step-executor.js +802 -0
  203. package/dist/recipe-engine/step-executor.js.map +1 -0
  204. package/dist/recipe-engine/tools/action-tool.d.ts +103 -0
  205. package/dist/recipe-engine/tools/action-tool.d.ts.map +1 -0
  206. package/dist/recipe-engine/tools/action-tool.js +473 -0
  207. package/dist/recipe-engine/tools/action-tool.js.map +1 -0
  208. package/dist/recipe-engine/tools/ai-tool.d.ts +26 -0
  209. package/dist/recipe-engine/tools/ai-tool.d.ts.map +1 -0
  210. package/dist/recipe-engine/tools/ai-tool.js +233 -0
  211. package/dist/recipe-engine/tools/ai-tool.js.map +1 -0
  212. package/dist/recipe-engine/tools/base.d.ts +214 -0
  213. package/dist/recipe-engine/tools/base.d.ts.map +1 -0
  214. package/dist/recipe-engine/tools/base.js +397 -0
  215. package/dist/recipe-engine/tools/base.js.map +1 -0
  216. package/dist/recipe-engine/tools/codemod-tool.d.ts +130 -0
  217. package/dist/recipe-engine/tools/codemod-tool.d.ts.map +1 -0
  218. package/dist/recipe-engine/tools/codemod-tool.js +786 -0
  219. package/dist/recipe-engine/tools/codemod-tool.js.map +1 -0
  220. package/dist/recipe-engine/tools/ensure-dirs-tool.d.ts +21 -0
  221. package/dist/recipe-engine/tools/ensure-dirs-tool.d.ts.map +1 -0
  222. package/dist/recipe-engine/tools/ensure-dirs-tool.js +130 -0
  223. package/dist/recipe-engine/tools/ensure-dirs-tool.js.map +1 -0
  224. package/dist/recipe-engine/tools/index.d.ts +126 -0
  225. package/dist/recipe-engine/tools/index.d.ts.map +1 -0
  226. package/dist/recipe-engine/tools/index.js +290 -0
  227. package/dist/recipe-engine/tools/index.js.map +1 -0
  228. package/dist/recipe-engine/tools/install-tool.d.ts +20 -0
  229. package/dist/recipe-engine/tools/install-tool.d.ts.map +1 -0
  230. package/dist/recipe-engine/tools/install-tool.js +194 -0
  231. package/dist/recipe-engine/tools/install-tool.js.map +1 -0
  232. package/dist/recipe-engine/tools/parallel-tool.d.ts +21 -0
  233. package/dist/recipe-engine/tools/parallel-tool.d.ts.map +1 -0
  234. package/dist/recipe-engine/tools/parallel-tool.js +134 -0
  235. package/dist/recipe-engine/tools/parallel-tool.js.map +1 -0
  236. package/dist/recipe-engine/tools/patch-tool.d.ts +21 -0
  237. package/dist/recipe-engine/tools/patch-tool.d.ts.map +1 -0
  238. package/dist/recipe-engine/tools/patch-tool.js +248 -0
  239. package/dist/recipe-engine/tools/patch-tool.js.map +1 -0
  240. package/dist/recipe-engine/tools/prompt-tool.d.ts +25 -0
  241. package/dist/recipe-engine/tools/prompt-tool.d.ts.map +1 -0
  242. package/dist/recipe-engine/tools/prompt-tool.js +162 -0
  243. package/dist/recipe-engine/tools/prompt-tool.js.map +1 -0
  244. package/dist/recipe-engine/tools/query-tool.d.ts +21 -0
  245. package/dist/recipe-engine/tools/query-tool.d.ts.map +1 -0
  246. package/dist/recipe-engine/tools/query-tool.js +249 -0
  247. package/dist/recipe-engine/tools/query-tool.js.map +1 -0
  248. package/dist/recipe-engine/tools/recipe-tool.d.ts +103 -0
  249. package/dist/recipe-engine/tools/recipe-tool.d.ts.map +1 -0
  250. package/dist/recipe-engine/tools/recipe-tool.js +617 -0
  251. package/dist/recipe-engine/tools/recipe-tool.js.map +1 -0
  252. package/dist/recipe-engine/tools/registry.d.ts +151 -0
  253. package/dist/recipe-engine/tools/registry.d.ts.map +1 -0
  254. package/dist/recipe-engine/tools/registry.js +244 -0
  255. package/dist/recipe-engine/tools/registry.js.map +1 -0
  256. package/dist/recipe-engine/tools/sequence-tool.d.ts +22 -0
  257. package/dist/recipe-engine/tools/sequence-tool.d.ts.map +1 -0
  258. package/dist/recipe-engine/tools/sequence-tool.js +122 -0
  259. package/dist/recipe-engine/tools/sequence-tool.js.map +1 -0
  260. package/dist/recipe-engine/tools/shell-tool.d.ts +25 -0
  261. package/dist/recipe-engine/tools/shell-tool.d.ts.map +1 -0
  262. package/dist/recipe-engine/tools/shell-tool.js +149 -0
  263. package/dist/recipe-engine/tools/shell-tool.js.map +1 -0
  264. package/dist/recipe-engine/tools/template-tool.d.ts +88 -0
  265. package/dist/recipe-engine/tools/template-tool.d.ts.map +1 -0
  266. package/dist/recipe-engine/tools/template-tool.js +613 -0
  267. package/dist/recipe-engine/tools/template-tool.js.map +1 -0
  268. package/dist/recipe-engine/types.d.ts +963 -0
  269. package/dist/recipe-engine/types.d.ts.map +1 -0
  270. package/dist/recipe-engine/types.js +94 -0
  271. package/dist/recipe-engine/types.js.map +1 -0
  272. package/dist/template-engines/ai-tags.d.ts +26 -0
  273. package/dist/template-engines/ai-tags.d.ts.map +1 -0
  274. package/dist/template-engines/ai-tags.js +233 -0
  275. package/dist/template-engines/ai-tags.js.map +1 -0
  276. package/dist/template-engines/index.d.ts +8 -0
  277. package/dist/template-engines/index.d.ts.map +1 -0
  278. package/dist/template-engines/index.js +8 -0
  279. package/dist/template-engines/index.js.map +1 -0
  280. package/dist/template-engines/jig-engine.d.ts +47 -0
  281. package/dist/template-engines/jig-engine.d.ts.map +1 -0
  282. package/dist/template-engines/jig-engine.js +173 -0
  283. package/dist/template-engines/jig-engine.js.map +1 -0
  284. package/dist/utils/coerce-value.d.ts +7 -0
  285. package/dist/utils/coerce-value.d.ts.map +1 -0
  286. package/dist/utils/coerce-value.js +18 -0
  287. package/dist/utils/coerce-value.js.map +1 -0
  288. package/dist/utils/diff.d.ts +8 -0
  289. package/dist/utils/diff.d.ts.map +1 -0
  290. package/dist/utils/diff.js +10 -0
  291. package/dist/utils/diff.js.map +1 -0
  292. package/dist/utils/global-packages.d.ts +11 -0
  293. package/dist/utils/global-packages.d.ts.map +1 -0
  294. package/dist/utils/global-packages.js +88 -0
  295. package/dist/utils/global-packages.js.map +1 -0
  296. package/dist/utils/pager.d.ts +6 -0
  297. package/dist/utils/pager.d.ts.map +1 -0
  298. package/dist/utils/pager.js +41 -0
  299. package/dist/utils/pager.js.map +1 -0
  300. package/help/cookbook/info.md +35 -0
  301. package/help/cookbook/list.md +37 -0
  302. package/help/gen.md +26 -0
  303. package/help/recipe/run.md +52 -0
  304. package/help/recipe/validate.md +51 -0
  305. package/oclif.manifest.json +580 -0
  306. package/package.json +120 -0
@@ -0,0 +1,802 @@
1
+ /**
2
+ * Step Executor for Recipe Step System
3
+ *
4
+ * Orchestrates the execution of recipe steps with proper dependency management,
5
+ * conditional logic, and parallel execution capabilities. This is the core
6
+ * coordination layer that brings together all recipe tools.
7
+ */
8
+ import { EventEmitter } from "node:events";
9
+ import { ErrorCode, ErrorHandler, HypergenError } from "@hypercli/core";
10
+ import createDebug from "debug";
11
+ import { evaluateStepOutputs } from "./output-evaluator.js";
12
+ import { getToolRegistry } from "./tools/registry.js";
13
+ import { CircularDependencyError, StepExecutionError, isAIStep, isActionStep, isCodeModStep, isEnsureDirsStep, isInstallStep, isParallelStep, isPatchStep, isPromptStep, isQueryStep, isRecipeStep, isSequenceStep, isShellStep, isTemplateStep, } from "./types.js";
14
+ /**
15
+ * Default step executor configuration
16
+ */
17
+ const DEFAULT_CONFIG = {
18
+ maxConcurrency: 10,
19
+ defaultTimeout: 30000, // 30 seconds
20
+ defaultRetries: 3,
21
+ continueOnError: false,
22
+ enableParallelExecution: true,
23
+ collectMetrics: true,
24
+ enableProgressTracking: true,
25
+ memoryWarningThreshold: 1024, // 1GB
26
+ timeoutSafetyFactor: 1.2,
27
+ };
28
+ /**
29
+ * Step Executor for orchestrating recipe step execution
30
+ *
31
+ * The StepExecutor is responsible for:
32
+ * - Managing step dependencies and execution order
33
+ * - Coordinating parallel step execution
34
+ * - Evaluating conditional expressions
35
+ * - Routing steps to appropriate tools
36
+ * - Handling errors and retries
37
+ * - Tracking execution progress and metrics
38
+ */
39
+ export class StepExecutor extends EventEmitter {
40
+ toolRegistry;
41
+ debug;
42
+ config;
43
+ // Execution state
44
+ activeExecutions = new Map();
45
+ runningSteps = new Map();
46
+ executionCounter = 0;
47
+ // Metrics and progress tracking
48
+ metrics;
49
+ progress;
50
+ constructor(toolRegistry, config = {}) {
51
+ super();
52
+ this.toolRegistry = toolRegistry || getToolRegistry();
53
+ this.debug = createDebug("hyper:recipe:step-executor");
54
+ this.config = { ...DEFAULT_CONFIG, ...config };
55
+ this.debug("Step executor initialized with config: %o", this.config);
56
+ }
57
+ /**
58
+ * Execute a list of recipe steps with dependency management and parallel execution
59
+ */
60
+ async executeSteps(steps, context, options = {}) {
61
+ const executionId = this.generateExecutionId();
62
+ const startTime = Date.now();
63
+ this.debug("Starting step execution [%s] with %d steps", executionId, steps.length);
64
+ this.emit("execution:started", { executionId, steps: steps.length });
65
+ try {
66
+ // Initialize metrics and progress tracking
67
+ if (this.config.collectMetrics) {
68
+ this.initializeMetrics();
69
+ }
70
+ if (this.config.enableProgressTracking) {
71
+ this.initializeProgress(steps.length);
72
+ }
73
+ // Validate steps and context
74
+ this.validateSteps(steps);
75
+ this.validateContext(context);
76
+ // Create execution plan with dependency resolution
77
+ const executionPlan = await this.createExecutionPlan(steps, context);
78
+ this.debug("Execution plan created: %d phases", executionPlan.phases.length);
79
+ this.emit("execution:plan-created", { executionId, plan: executionPlan });
80
+ // Execute phases sequentially, steps within phases potentially in parallel
81
+ const results = await this.executeExecutionPlan(executionPlan, context, options, executionId);
82
+ // Finalize metrics
83
+ if (this.config.collectMetrics && this.metrics) {
84
+ this.metrics.totalExecutionTime = Date.now() - startTime;
85
+ this.finalizeMetrics();
86
+ }
87
+ this.debug("Step execution completed [%s] in %dms", executionId, Date.now() - startTime);
88
+ this.emit("execution:completed", {
89
+ executionId,
90
+ results,
91
+ duration: Date.now() - startTime,
92
+ metrics: this.metrics,
93
+ });
94
+ return results;
95
+ }
96
+ catch (error) {
97
+ this.debug("Step execution failed [%s]: %s", executionId, error instanceof Error ? error.message : String(error));
98
+ this.emit("execution:failed", {
99
+ executionId,
100
+ error,
101
+ duration: Date.now() - startTime,
102
+ });
103
+ if (error instanceof HypergenError) {
104
+ throw error;
105
+ }
106
+ throw ErrorHandler.createError(ErrorCode.INTERNAL_ERROR, `Step execution failed: ${error instanceof Error ? error.message : String(error)}`, { executionId, steps: steps.length });
107
+ }
108
+ finally {
109
+ this.activeExecutions.delete(executionId);
110
+ }
111
+ }
112
+ /**
113
+ * Execute a single step with proper tool routing
114
+ */
115
+ async executeStep(step, context, options = {}) {
116
+ const stepStartTime = Date.now();
117
+ const stepResult = {
118
+ status: "pending",
119
+ stepName: step.name,
120
+ toolType: step.tool,
121
+ startTime: new Date(),
122
+ retryCount: 0,
123
+ dependenciesSatisfied: true, // Single step execution assumes deps are satisfied
124
+ conditionResult: true,
125
+ };
126
+ this.debug("Executing single step: %s (%s)", step.name, step.tool);
127
+ this.emit("step:started", { step: step.name, toolType: step.tool });
128
+ try {
129
+ stepResult.status = "running";
130
+ // Evaluate condition if present
131
+ if (step.when) {
132
+ stepResult.conditionResult = context.evaluateCondition(step.when, context.variables);
133
+ if (!stepResult.conditionResult) {
134
+ this.debug("Step condition not met, skipping: %s", step.name);
135
+ stepResult.status = "skipped";
136
+ stepResult.endTime = new Date();
137
+ stepResult.duration = Date.now() - stepStartTime;
138
+ this.emit("step:skipped", { step: step.name, condition: step.when });
139
+ return stepResult;
140
+ }
141
+ }
142
+ // In collect mode (pass 1), only template steps need to run
143
+ // Sequence/parallel are structural containers that must execute so their
144
+ // nested template steps can run. Other tools (shell, action, prompt) are skipped.
145
+ if (context.collectMode &&
146
+ step.tool !== "template" &&
147
+ step.tool !== "sequence" &&
148
+ step.tool !== "parallel") {
149
+ this.debug("Skipping non-template step in collect mode: %s (%s)", step.name, step.tool);
150
+ stepResult.status = "skipped";
151
+ stepResult.endTime = new Date();
152
+ stepResult.duration = Date.now() - stepStartTime;
153
+ this.emit("step:skipped", { step: step.name, reason: "collect-mode" });
154
+ return stepResult;
155
+ }
156
+ // Execute step with retries
157
+ const maxRetries = options.retries ?? step.retries ?? this.config.defaultRetries;
158
+ let lastError;
159
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
160
+ try {
161
+ stepResult.retryCount = attempt;
162
+ if (attempt > 0) {
163
+ this.debug("Retrying step: %s (attempt %d/%d)", step.name, attempt + 1, maxRetries + 1);
164
+ await this.delay(this.calculateRetryDelay(attempt));
165
+ }
166
+ // Route to appropriate tool and execute
167
+ const toolResult = await this.routeAndExecuteStep(step, context, options);
168
+ stepResult.toolResult = toolResult;
169
+ // Respect failure status returned by the tool
170
+ if (toolResult.status === "failed") {
171
+ stepResult.status = "failed";
172
+ stepResult.error = toolResult.error;
173
+ stepResult.endTime = new Date();
174
+ stepResult.duration = Date.now() - stepStartTime;
175
+ throw new Error(toolResult.error?.message || `Step '${step.name}' failed`);
176
+ }
177
+ stepResult.status = "completed";
178
+ stepResult.endTime = new Date();
179
+ stepResult.duration = Date.now() - stepStartTime;
180
+ // Extract file changes from tool result
181
+ this.extractFileChanges(stepResult, toolResult);
182
+ // Evaluate export expressions and inject into context.variables
183
+ if (step.exports && Object.keys(step.exports).length > 0) {
184
+ const outputs = await evaluateStepOutputs(step.exports, toolResult, context);
185
+ stepResult.output = { ...stepResult.output, ...outputs };
186
+ Object.assign(context.variables, outputs);
187
+ }
188
+ this.debug("Step completed successfully: %s in %dms", step.name, stepResult.duration);
189
+ this.emit("step:completed", {
190
+ step: step.name,
191
+ toolType: step.tool,
192
+ duration: stepResult.duration,
193
+ retries: attempt,
194
+ });
195
+ return stepResult;
196
+ }
197
+ catch (error) {
198
+ lastError = error instanceof Error ? error : new Error(String(error));
199
+ if (attempt < maxRetries) {
200
+ this.debug("Step failed, will retry: %s - %s", step.name, lastError.message);
201
+ this.emit("step:retry", {
202
+ step: step.name,
203
+ attempt: attempt + 1,
204
+ error: lastError,
205
+ });
206
+ }
207
+ }
208
+ }
209
+ // All retries exhausted
210
+ stepResult.status = "failed";
211
+ stepResult.endTime = new Date();
212
+ stepResult.duration = Date.now() - stepStartTime;
213
+ stepResult.error = {
214
+ message: lastError?.message || "Step execution failed",
215
+ stack: lastError?.stack,
216
+ cause: lastError,
217
+ };
218
+ this.debug("Step failed permanently: %s after %d retries", step.name, maxRetries);
219
+ this.emit("step:failed", {
220
+ step: step.name,
221
+ toolType: step.tool,
222
+ error: lastError,
223
+ retries: maxRetries,
224
+ });
225
+ throw new StepExecutionError(`Step '${step.name}' failed after ${maxRetries} retries: ${lastError?.message}`, step.name, step.tool, lastError);
226
+ }
227
+ catch (error) {
228
+ if (stepResult.status !== "failed") {
229
+ stepResult.status = "failed";
230
+ stepResult.endTime = new Date();
231
+ stepResult.duration = Date.now() - stepStartTime;
232
+ stepResult.error = {
233
+ message: error instanceof Error ? error.message : String(error),
234
+ stack: error instanceof Error ? error.stack : undefined,
235
+ cause: error,
236
+ };
237
+ }
238
+ throw error;
239
+ }
240
+ }
241
+ /**
242
+ * Get current execution metrics
243
+ */
244
+ getMetrics() {
245
+ return this.metrics;
246
+ }
247
+ /**
248
+ * Get current execution progress
249
+ */
250
+ getProgress() {
251
+ return this.progress;
252
+ }
253
+ /**
254
+ * Cancel all running executions
255
+ */
256
+ async cancelAllExecutions() {
257
+ this.debug("Cancelling all running executions");
258
+ const promises = [];
259
+ for (const [executionId] of this.activeExecutions) {
260
+ promises.push(this.cancelExecution(executionId));
261
+ }
262
+ await Promise.allSettled(promises);
263
+ }
264
+ /**
265
+ * Cancel a specific execution
266
+ */
267
+ async cancelExecution(executionId) {
268
+ this.debug("Cancelling execution: %s", executionId);
269
+ // Cancel all running steps for this execution
270
+ for (const [stepName, stepInfo] of this.runningSteps) {
271
+ if (stepName.startsWith(executionId)) {
272
+ try {
273
+ await stepInfo.tool.cleanup();
274
+ }
275
+ catch (error) {
276
+ this.debug("Error cleaning up step during cancellation: %s - %s", stepName, error instanceof Error ? error.message : String(error));
277
+ }
278
+ }
279
+ }
280
+ this.emit("execution:cancelled", { executionId });
281
+ }
282
+ // Private implementation methods
283
+ async createExecutionPlan(steps, context) {
284
+ const startTime = Date.now();
285
+ // Build dependency graph
286
+ const dependencyGraph = this.buildDependencyGraph(steps);
287
+ // Detect circular dependencies
288
+ this.detectCircularDependencies(dependencyGraph);
289
+ // Create execution phases using topological sort
290
+ const phases = this.createExecutionPhases(steps, dependencyGraph);
291
+ const plan = {
292
+ recipe: {
293
+ name: context.recipe.name,
294
+ description: "Recipe execution plan",
295
+ version: context.recipe.version,
296
+ author: undefined,
297
+ category: undefined,
298
+ tags: [],
299
+ variables: context.recipeVariables,
300
+ steps,
301
+ examples: [],
302
+ dependencies: [],
303
+ outputs: [],
304
+ },
305
+ phases,
306
+ dependencyGraph,
307
+ estimatedDuration: this.estimateExecutionDuration(steps),
308
+ };
309
+ if (this.config.collectMetrics && this.metrics) {
310
+ this.metrics.dependencies.resolutionTime = Date.now() - startTime;
311
+ }
312
+ return plan;
313
+ }
314
+ buildDependencyGraph(steps) {
315
+ const graph = new Map();
316
+ // If ANY step declares explicit dependsOn, assume the recipe author manages
317
+ // dependencies manually — don't add implicit sequential deps.
318
+ // Otherwise, add implicit sequential deps so YAML order = execution order.
319
+ // This ensures that exports from earlier steps are available when later
320
+ // step conditions evaluate.
321
+ const hasExplicitDeps = steps.some((s) => s.dependsOn && s.dependsOn.length > 0);
322
+ for (let i = 0; i < steps.length; i++) {
323
+ const step = steps[i];
324
+ let dependencies;
325
+ if (step.dependsOn && step.dependsOn.length > 0) {
326
+ // Explicit dependencies — use as-is
327
+ dependencies = [...step.dependsOn];
328
+ }
329
+ else if (!hasExplicitDeps && i > 0) {
330
+ // No explicit deps in the recipe — implicitly depend on previous step
331
+ dependencies = [steps[i - 1].name];
332
+ }
333
+ else {
334
+ dependencies = [];
335
+ }
336
+ graph.set(step.name, {
337
+ stepName: step.name,
338
+ dependencies,
339
+ dependents: [],
340
+ priority: 0,
341
+ parallelizable: step.parallel ?? true,
342
+ });
343
+ }
344
+ // Build dependent relationships
345
+ for (const [stepName, node] of graph) {
346
+ for (const depName of node.dependencies) {
347
+ const depNode = graph.get(depName);
348
+ if (depNode) {
349
+ depNode.dependents.push(stepName);
350
+ }
351
+ }
352
+ }
353
+ // Calculate priority based on dependency depth
354
+ this.calculatePriorities(graph);
355
+ return graph;
356
+ }
357
+ calculatePriorities(graph) {
358
+ const visited = new Set();
359
+ const calculatePriority = (stepName) => {
360
+ if (visited.has(stepName)) {
361
+ return graph.get(stepName)?.priority ?? 0;
362
+ }
363
+ visited.add(stepName);
364
+ const node = graph.get(stepName);
365
+ if (node.dependencies.length === 0) {
366
+ node.priority = 0;
367
+ return 0;
368
+ }
369
+ let maxDepPriority = -1;
370
+ for (const depName of node.dependencies) {
371
+ const depPriority = calculatePriority(depName);
372
+ maxDepPriority = Math.max(maxDepPriority, depPriority);
373
+ }
374
+ node.priority = maxDepPriority + 1;
375
+ return node.priority;
376
+ };
377
+ for (const stepName of graph.keys()) {
378
+ calculatePriority(stepName);
379
+ }
380
+ }
381
+ detectCircularDependencies(graph) {
382
+ const visited = new Set();
383
+ const visiting = new Set();
384
+ const visit = (stepName, path = []) => {
385
+ if (visiting.has(stepName)) {
386
+ const cycle = [...path, stepName];
387
+ throw new CircularDependencyError(`Circular dependency detected: ${cycle.join(" -> ")}`, cycle);
388
+ }
389
+ if (visited.has(stepName)) {
390
+ return;
391
+ }
392
+ visiting.add(stepName);
393
+ const node = graph.get(stepName);
394
+ for (const depName of node.dependencies) {
395
+ visit(depName, [...path, stepName]);
396
+ }
397
+ visiting.delete(stepName);
398
+ visited.add(stepName);
399
+ };
400
+ for (const stepName of graph.keys()) {
401
+ visit(stepName);
402
+ }
403
+ }
404
+ createExecutionPhases(steps, graph) {
405
+ const phases = [];
406
+ const assigned = new Set();
407
+ const stepMap = new Map(steps.map((step) => [step.name, step]));
408
+ while (assigned.size < steps.length) {
409
+ const readySteps = [];
410
+ // Find steps that can run in this phase
411
+ for (const [stepName, node] of graph) {
412
+ if (assigned.has(stepName))
413
+ continue;
414
+ // Check if all dependencies are satisfied
415
+ const dependenciesSatisfied = node.dependencies.every((dep) => assigned.has(dep));
416
+ if (dependenciesSatisfied) {
417
+ readySteps.push(stepName);
418
+ }
419
+ }
420
+ if (readySteps.length === 0) {
421
+ throw ErrorHandler.createError(ErrorCode.INTERNAL_ERROR, "No ready steps found - possible circular dependency", { assigned: Array.from(assigned), total: steps.length });
422
+ }
423
+ // Determine if this phase can run in parallel
424
+ const canRunInParallel = this.config.enableParallelExecution &&
425
+ readySteps.length > 1 &&
426
+ readySteps.every((stepName) => {
427
+ const step = stepMap.get(stepName);
428
+ return step.parallel !== false;
429
+ });
430
+ phases.push({
431
+ phase: phases.length,
432
+ steps: readySteps,
433
+ parallel: canRunInParallel,
434
+ });
435
+ // Mark steps as assigned
436
+ readySteps.forEach((stepName) => assigned.add(stepName));
437
+ }
438
+ return phases;
439
+ }
440
+ async executeExecutionPlan(plan, context, options, executionId) {
441
+ const results = [];
442
+ const stepMap = new Map(plan.recipe.steps.map((step) => [step.name, step]));
443
+ if (this.config.enableProgressTracking && this.progress) {
444
+ this.progress.totalPhases = plan.phases.length;
445
+ }
446
+ for (let phaseIndex = 0; phaseIndex < plan.phases.length; phaseIndex++) {
447
+ const phase = plan.phases[phaseIndex];
448
+ this.debug("Executing phase %d with %d steps (parallel: %s)", phase.phase, phase.steps.length, phase.parallel);
449
+ if (this.config.enableProgressTracking && this.progress) {
450
+ this.progress.currentPhase = phaseIndex + 1;
451
+ this.progress.phaseDescription = `Phase ${phase.phase + 1}: ${phase.steps.join(", ")}`;
452
+ }
453
+ this.emit("phase:started", {
454
+ phase: phase.phase,
455
+ steps: phase.steps,
456
+ parallel: phase.parallel,
457
+ });
458
+ const phaseResults = await this.executePhase(phase.steps.map((stepName) => stepMap.get(stepName)), context, options, phase.parallel, executionId);
459
+ results.push(...phaseResults);
460
+ // Update step results in context for next phase
461
+ phaseResults.forEach((result) => {
462
+ context.stepResults.set(result.stepName, result);
463
+ });
464
+ this.emit("phase:completed", {
465
+ phase: phase.phase,
466
+ results: phaseResults,
467
+ });
468
+ // Check if we should continue execution
469
+ const failed = phaseResults.filter((r) => r.status === "failed");
470
+ if (failed.length > 0 && !options.continueOnError && !this.config.continueOnError) {
471
+ throw new StepExecutionError(`Phase ${phase.phase} had failures: ${failed.map((r) => r.stepName).join(", ")}`, failed[0].stepName, failed[0].toolType);
472
+ }
473
+ }
474
+ return results;
475
+ }
476
+ async executePhase(steps, context, options, parallel, executionId) {
477
+ if (!parallel) {
478
+ // Execute steps sequentially
479
+ const results = [];
480
+ for (const step of steps) {
481
+ const result = await this.executeStep(step, context, options);
482
+ results.push(result);
483
+ // Update progress
484
+ if (this.config.enableProgressTracking && this.progress) {
485
+ if (result.status === "completed") {
486
+ this.progress.completedSteps.push(step.name);
487
+ }
488
+ else if (result.status === "failed") {
489
+ this.progress.failedSteps.push(step.name);
490
+ }
491
+ else if (result.status === "skipped") {
492
+ this.progress.skippedSteps.push(step.name);
493
+ }
494
+ this.updateProgressPercentage();
495
+ }
496
+ }
497
+ return results;
498
+ }
499
+ // Execute steps in parallel with concurrency limit
500
+ const concurrency = Math.min(steps.length, this.config.maxConcurrency);
501
+ const results = [];
502
+ const executeWithConcurrency = async (stepList) => {
503
+ const promises = [];
504
+ let index = 0;
505
+ const executeNext = async () => {
506
+ if (index >= stepList.length)
507
+ return;
508
+ const step = stepList[index++];
509
+ const stepKey = `${executionId}:${step.name}`;
510
+ try {
511
+ // Track running step
512
+ const tool = await this.toolRegistry.resolve(step.tool, this.getToolName(step));
513
+ this.runningSteps.set(stepKey, { step, startTime: new Date(), tool });
514
+ if (this.config.enableProgressTracking && this.progress) {
515
+ this.progress.runningSteps.push(step.name);
516
+ }
517
+ const result = await this.executeStep(step, context, options);
518
+ results.push(result);
519
+ // Update progress
520
+ if (this.config.enableProgressTracking && this.progress) {
521
+ const runningIndex = this.progress.runningSteps.indexOf(step.name);
522
+ if (runningIndex !== -1) {
523
+ this.progress.runningSteps.splice(runningIndex, 1);
524
+ }
525
+ if (result.status === "completed") {
526
+ this.progress.completedSteps.push(step.name);
527
+ }
528
+ else if (result.status === "failed") {
529
+ this.progress.failedSteps.push(step.name);
530
+ }
531
+ else if (result.status === "skipped") {
532
+ this.progress.skippedSteps.push(step.name);
533
+ }
534
+ this.updateProgressPercentage();
535
+ }
536
+ }
537
+ finally {
538
+ // Release tool before deleting from tracking map
539
+ const runningStep = this.runningSteps.get(stepKey);
540
+ if (runningStep) {
541
+ this.toolRegistry.release(step.tool, this.getToolName(step), runningStep.tool);
542
+ }
543
+ this.runningSteps.delete(stepKey);
544
+ }
545
+ // Execute next step
546
+ return executeNext();
547
+ };
548
+ // Start concurrent executions
549
+ for (let i = 0; i < concurrency && i < stepList.length; i++) {
550
+ promises.push(executeNext());
551
+ }
552
+ await Promise.all(promises);
553
+ };
554
+ await executeWithConcurrency(steps);
555
+ // Update parallelization metrics
556
+ if (this.config.collectMetrics && this.metrics) {
557
+ this.metrics.parallelization.maxConcurrentSteps = Math.max(this.metrics.parallelization.maxConcurrentSteps, Math.min(steps.length, concurrency));
558
+ this.metrics.parallelization.parallelPhases++;
559
+ }
560
+ return results;
561
+ }
562
+ async routeAndExecuteStep(step, context, options) {
563
+ const toolName = this.getToolName(step);
564
+ const tool = await this.toolRegistry.resolve(step.tool, toolName);
565
+ this.debug("Routing step to tool: %s -> %s:%s", step.name, step.tool, toolName);
566
+ try {
567
+ // Initialize tool if needed
568
+ if (!tool.isInitialized()) {
569
+ await tool.initialize();
570
+ }
571
+ // Validate step configuration
572
+ const validation = await tool.validate(step, context);
573
+ if (!validation.isValid) {
574
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, `Step validation failed: ${validation.errors.join(", ")}`, { step: step.name, tool: step.tool, errors: validation.errors });
575
+ }
576
+ // Execute step through tool
577
+ const result = await tool.execute(step, context);
578
+ this.debug("Tool execution completed: %s", step.name);
579
+ return result;
580
+ }
581
+ finally {
582
+ // Release tool back to registry
583
+ this.toolRegistry.release(step.tool, toolName, tool);
584
+ }
585
+ }
586
+ getToolName(step) {
587
+ // Route to appropriate tool based on step type
588
+ // Most tools use 'default' as the tool name, but action and codemod use specific names
589
+ if (isTemplateStep(step)) {
590
+ return "default";
591
+ }
592
+ if (isActionStep(step)) {
593
+ return step.action;
594
+ }
595
+ if (isCodeModStep(step)) {
596
+ return step.codemod;
597
+ }
598
+ if (isRecipeStep(step)) {
599
+ return "default";
600
+ }
601
+ if (isShellStep(step)) {
602
+ return "default";
603
+ }
604
+ if (isPromptStep(step)) {
605
+ return "default";
606
+ }
607
+ if (isSequenceStep(step)) {
608
+ return "default";
609
+ }
610
+ if (isParallelStep(step)) {
611
+ return "default";
612
+ }
613
+ if (isAIStep(step)) {
614
+ return "default";
615
+ }
616
+ if (isInstallStep(step)) {
617
+ return "default";
618
+ }
619
+ if (isQueryStep(step)) {
620
+ return "default";
621
+ }
622
+ if (isPatchStep(step)) {
623
+ return "default";
624
+ }
625
+ if (isEnsureDirsStep(step)) {
626
+ return "default";
627
+ }
628
+ // Fallback: check if the tool type is registered
629
+ const toolType = step.tool;
630
+ if (typeof toolType === "string" && this.toolRegistry.isRegistered(toolType, "default")) {
631
+ return "default";
632
+ }
633
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, `Unknown step type: ${step.tool}`, { step: step.name });
634
+ }
635
+ extractFileChanges(result, toolResult) {
636
+ // Extract file changes from tool result based on result type
637
+ if (toolResult && typeof toolResult === "object") {
638
+ if (toolResult.filesGenerated) {
639
+ result.filesCreated = toolResult.filesGenerated;
640
+ }
641
+ if (toolResult.filesProcessed) {
642
+ result.filesModified = toolResult.filesProcessed;
643
+ }
644
+ if (toolResult.filesCreated) {
645
+ result.filesCreated = toolResult.filesCreated;
646
+ }
647
+ if (toolResult.filesModified) {
648
+ result.filesModified = toolResult.filesModified;
649
+ }
650
+ if (toolResult.filesDeleted) {
651
+ result.filesDeleted = toolResult.filesDeleted;
652
+ }
653
+ }
654
+ }
655
+ validateSteps(steps) {
656
+ if (!Array.isArray(steps)) {
657
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, "Steps must be an array");
658
+ }
659
+ if (steps.length === 0) {
660
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, "At least one step is required");
661
+ }
662
+ // Validate step names are unique
663
+ const stepNames = new Set();
664
+ for (const step of steps) {
665
+ if (!step.name) {
666
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, "Step name is required");
667
+ }
668
+ if (stepNames.has(step.name)) {
669
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, `Duplicate step name: ${step.name}`);
670
+ }
671
+ stepNames.add(step.name);
672
+ if (!step.tool) {
673
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, `Step ${step.name} must specify a tool`);
674
+ }
675
+ }
676
+ // Validate dependencies reference existing steps
677
+ for (const step of steps) {
678
+ if (step.dependsOn) {
679
+ for (const depName of step.dependsOn) {
680
+ if (!stepNames.has(depName)) {
681
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, `Step ${step.name} depends on unknown step: ${depName}`);
682
+ }
683
+ }
684
+ }
685
+ }
686
+ }
687
+ validateContext(context) {
688
+ if (!context) {
689
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, "Step context is required");
690
+ }
691
+ if (!context.evaluateCondition) {
692
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, "Context must provide evaluateCondition function");
693
+ }
694
+ if (!context.recipe) {
695
+ throw ErrorHandler.createError(ErrorCode.VALIDATION_ERROR, "Context must include recipe information");
696
+ }
697
+ }
698
+ calculateRetryDelay(attempt) {
699
+ // Exponential backoff with jitter
700
+ const baseDelay = 1000; // 1 second
701
+ const maxDelay = 30000; // 30 seconds
702
+ let delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
703
+ // Add jitter (±25%)
704
+ const jitter = delay * 0.25 * (Math.random() - 0.5);
705
+ delay += jitter;
706
+ return Math.max(delay, 100); // Minimum 100ms delay
707
+ }
708
+ estimateExecutionDuration(steps) {
709
+ // Simple estimation based on step types and historical data
710
+ const estimations = {
711
+ template: 5000, // 5 seconds average
712
+ action: 3000, // 3 seconds average
713
+ codemod: 10000, // 10 seconds average
714
+ recipe: 15000, // 15 seconds average (sub-recipes)
715
+ shell: 2000, // 2 seconds average
716
+ prompt: 30000, // 30 seconds average (interactive)
717
+ sequence: 0, // Sequence tool itself is instant
718
+ parallel: 0, // Parallel tool itself is instant
719
+ ai: 20000, // 20 seconds average (AI generation)
720
+ install: 15000, // 15 seconds average (package install)
721
+ query: 100, // 100ms average (file read + parse)
722
+ patch: 200, // 200ms average (read + merge + write)
723
+ "ensure-dirs": 50, // 50ms average (mkdir -p)
724
+ };
725
+ let totalEstimate = 0;
726
+ for (const step of steps) {
727
+ totalEstimate += estimations[step.tool] || 5000;
728
+ }
729
+ return totalEstimate;
730
+ }
731
+ delay(ms) {
732
+ return new Promise((resolve) => setTimeout(resolve, ms));
733
+ }
734
+ generateExecutionId() {
735
+ return `exec_${Date.now()}_${++this.executionCounter}`;
736
+ }
737
+ initializeMetrics() {
738
+ this.metrics = {
739
+ totalExecutionTime: 0,
740
+ stepExecutionTimes: new Map(),
741
+ memoryUsage: {
742
+ peak: 0,
743
+ average: 0,
744
+ start: process.memoryUsage().heapUsed,
745
+ end: 0,
746
+ },
747
+ parallelization: {
748
+ maxConcurrentSteps: 0,
749
+ averageConcurrentSteps: 0,
750
+ parallelPhases: 0,
751
+ },
752
+ errors: {
753
+ totalFailures: 0,
754
+ totalRetries: 0,
755
+ permanentFailures: [],
756
+ recoveredAfterRetries: [],
757
+ },
758
+ dependencies: {
759
+ resolutionTime: 0,
760
+ cyclesDetected: 0,
761
+ maxDepth: 0,
762
+ },
763
+ };
764
+ }
765
+ initializeProgress(totalSteps) {
766
+ this.progress = {
767
+ currentPhase: 0,
768
+ totalPhases: 0,
769
+ runningSteps: [],
770
+ completedSteps: [],
771
+ failedSteps: [],
772
+ skippedSteps: [],
773
+ progressPercentage: 0,
774
+ phaseDescription: "Initializing...",
775
+ };
776
+ }
777
+ updateProgressPercentage() {
778
+ if (!this.progress)
779
+ return;
780
+ const totalSteps = this.progress.completedSteps.length +
781
+ this.progress.failedSteps.length +
782
+ this.progress.skippedSteps.length +
783
+ this.progress.runningSteps.length;
784
+ if (totalSteps === 0)
785
+ return;
786
+ const completed = this.progress.completedSteps.length +
787
+ this.progress.failedSteps.length +
788
+ this.progress.skippedSteps.length;
789
+ this.progress.progressPercentage = Math.round((completed / totalSteps) * 100);
790
+ }
791
+ finalizeMetrics() {
792
+ if (!this.metrics)
793
+ return;
794
+ this.metrics.memoryUsage.end = process.memoryUsage().heapUsed;
795
+ // Calculate average memory usage (simplified)
796
+ this.metrics.memoryUsage.average =
797
+ (this.metrics.memoryUsage.start + this.metrics.memoryUsage.end) / 2;
798
+ // Set peak to end for now (would need continuous monitoring for accuracy)
799
+ this.metrics.memoryUsage.peak = Math.max(this.metrics.memoryUsage.start, this.metrics.memoryUsage.end);
800
+ }
801
+ }
802
+ //# sourceMappingURL=step-executor.js.map