@generacy-ai/workflow-engine 0.0.0-preview-20260304013206

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 (347) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +265 -0
  3. package/dist/actions/base-action.d.ts +63 -0
  4. package/dist/actions/base-action.d.ts.map +1 -0
  5. package/dist/actions/base-action.js +130 -0
  6. package/dist/actions/base-action.js.map +1 -0
  7. package/dist/actions/builtin/agent-invoke.d.ts +16 -0
  8. package/dist/actions/builtin/agent-invoke.d.ts.map +1 -0
  9. package/dist/actions/builtin/agent-invoke.js +108 -0
  10. package/dist/actions/builtin/agent-invoke.js.map +1 -0
  11. package/dist/actions/builtin/humancy-api-handler.d.ts +191 -0
  12. package/dist/actions/builtin/humancy-api-handler.d.ts.map +1 -0
  13. package/dist/actions/builtin/humancy-api-handler.js +521 -0
  14. package/dist/actions/builtin/humancy-api-handler.js.map +1 -0
  15. package/dist/actions/builtin/humancy-review.d.ts +83 -0
  16. package/dist/actions/builtin/humancy-review.d.ts.map +1 -0
  17. package/dist/actions/builtin/humancy-review.js +250 -0
  18. package/dist/actions/builtin/humancy-review.js.map +1 -0
  19. package/dist/actions/builtin/pr-create.d.ts +16 -0
  20. package/dist/actions/builtin/pr-create.d.ts.map +1 -0
  21. package/dist/actions/builtin/pr-create.js +110 -0
  22. package/dist/actions/builtin/pr-create.js.map +1 -0
  23. package/dist/actions/builtin/shell.d.ts +16 -0
  24. package/dist/actions/builtin/shell.d.ts.map +1 -0
  25. package/dist/actions/builtin/shell.js +80 -0
  26. package/dist/actions/builtin/shell.js.map +1 -0
  27. package/dist/actions/builtin/speckit/index.d.ts +47 -0
  28. package/dist/actions/builtin/speckit/index.d.ts.map +1 -0
  29. package/dist/actions/builtin/speckit/index.js +347 -0
  30. package/dist/actions/builtin/speckit/index.js.map +1 -0
  31. package/dist/actions/builtin/speckit/lib/feature.d.ts +17 -0
  32. package/dist/actions/builtin/speckit/lib/feature.d.ts.map +1 -0
  33. package/dist/actions/builtin/speckit/lib/feature.js +430 -0
  34. package/dist/actions/builtin/speckit/lib/feature.js.map +1 -0
  35. package/dist/actions/builtin/speckit/lib/fs.d.ts +62 -0
  36. package/dist/actions/builtin/speckit/lib/fs.d.ts.map +1 -0
  37. package/dist/actions/builtin/speckit/lib/fs.js +181 -0
  38. package/dist/actions/builtin/speckit/lib/fs.js.map +1 -0
  39. package/dist/actions/builtin/speckit/lib/paths.d.ts +7 -0
  40. package/dist/actions/builtin/speckit/lib/paths.d.ts.map +1 -0
  41. package/dist/actions/builtin/speckit/lib/paths.js +161 -0
  42. package/dist/actions/builtin/speckit/lib/paths.js.map +1 -0
  43. package/dist/actions/builtin/speckit/lib/prereqs.d.ts +8 -0
  44. package/dist/actions/builtin/speckit/lib/prereqs.d.ts.map +1 -0
  45. package/dist/actions/builtin/speckit/lib/prereqs.js +194 -0
  46. package/dist/actions/builtin/speckit/lib/prereqs.js.map +1 -0
  47. package/dist/actions/builtin/speckit/lib/stream-batcher.d.ts +14 -0
  48. package/dist/actions/builtin/speckit/lib/stream-batcher.d.ts.map +1 -0
  49. package/dist/actions/builtin/speckit/lib/stream-batcher.js +31 -0
  50. package/dist/actions/builtin/speckit/lib/stream-batcher.js.map +1 -0
  51. package/dist/actions/builtin/speckit/lib/templates.d.ts +15 -0
  52. package/dist/actions/builtin/speckit/lib/templates.d.ts.map +1 -0
  53. package/dist/actions/builtin/speckit/lib/templates.js +176 -0
  54. package/dist/actions/builtin/speckit/lib/templates.js.map +1 -0
  55. package/dist/actions/builtin/speckit/operations/check-prereqs.d.ts +11 -0
  56. package/dist/actions/builtin/speckit/operations/check-prereqs.d.ts.map +1 -0
  57. package/dist/actions/builtin/speckit/operations/check-prereqs.js +22 -0
  58. package/dist/actions/builtin/speckit/operations/check-prereqs.js.map +1 -0
  59. package/dist/actions/builtin/speckit/operations/clarify.d.ts +7 -0
  60. package/dist/actions/builtin/speckit/operations/clarify.d.ts.map +1 -0
  61. package/dist/actions/builtin/speckit/operations/clarify.js +302 -0
  62. package/dist/actions/builtin/speckit/operations/clarify.js.map +1 -0
  63. package/dist/actions/builtin/speckit/operations/copy-template.d.ts +11 -0
  64. package/dist/actions/builtin/speckit/operations/copy-template.d.ts.map +1 -0
  65. package/dist/actions/builtin/speckit/operations/copy-template.js +23 -0
  66. package/dist/actions/builtin/speckit/operations/copy-template.js.map +1 -0
  67. package/dist/actions/builtin/speckit/operations/create-feature.d.ts +11 -0
  68. package/dist/actions/builtin/speckit/operations/create-feature.d.ts.map +1 -0
  69. package/dist/actions/builtin/speckit/operations/create-feature.js +25 -0
  70. package/dist/actions/builtin/speckit/operations/create-feature.js.map +1 -0
  71. package/dist/actions/builtin/speckit/operations/get-paths.d.ts +11 -0
  72. package/dist/actions/builtin/speckit/operations/get-paths.d.ts.map +1 -0
  73. package/dist/actions/builtin/speckit/operations/get-paths.js +17 -0
  74. package/dist/actions/builtin/speckit/operations/get-paths.js.map +1 -0
  75. package/dist/actions/builtin/speckit/operations/implement.d.ts +7 -0
  76. package/dist/actions/builtin/speckit/operations/implement.d.ts.map +1 -0
  77. package/dist/actions/builtin/speckit/operations/implement.js +246 -0
  78. package/dist/actions/builtin/speckit/operations/implement.js.map +1 -0
  79. package/dist/actions/builtin/speckit/operations/plan.d.ts +7 -0
  80. package/dist/actions/builtin/speckit/operations/plan.d.ts.map +1 -0
  81. package/dist/actions/builtin/speckit/operations/plan.js +253 -0
  82. package/dist/actions/builtin/speckit/operations/plan.js.map +1 -0
  83. package/dist/actions/builtin/speckit/operations/specify.d.ts +7 -0
  84. package/dist/actions/builtin/speckit/operations/specify.d.ts.map +1 -0
  85. package/dist/actions/builtin/speckit/operations/specify.js +178 -0
  86. package/dist/actions/builtin/speckit/operations/specify.js.map +1 -0
  87. package/dist/actions/builtin/speckit/operations/tasks-to-issues.d.ts +42 -0
  88. package/dist/actions/builtin/speckit/operations/tasks-to-issues.d.ts.map +1 -0
  89. package/dist/actions/builtin/speckit/operations/tasks-to-issues.js +386 -0
  90. package/dist/actions/builtin/speckit/operations/tasks-to-issues.js.map +1 -0
  91. package/dist/actions/builtin/speckit/operations/tasks.d.ts +7 -0
  92. package/dist/actions/builtin/speckit/operations/tasks.d.ts.map +1 -0
  93. package/dist/actions/builtin/speckit/operations/tasks.js +234 -0
  94. package/dist/actions/builtin/speckit/operations/tasks.js.map +1 -0
  95. package/dist/actions/builtin/speckit/types.d.ts +268 -0
  96. package/dist/actions/builtin/speckit/types.d.ts.map +1 -0
  97. package/dist/actions/builtin/speckit/types.js +11 -0
  98. package/dist/actions/builtin/speckit/types.js.map +1 -0
  99. package/dist/actions/builtin/verification-check.d.ts +16 -0
  100. package/dist/actions/builtin/verification-check.d.ts.map +1 -0
  101. package/dist/actions/builtin/verification-check.js +99 -0
  102. package/dist/actions/builtin/verification-check.js.map +1 -0
  103. package/dist/actions/builtin/workspace-prepare.d.ts +16 -0
  104. package/dist/actions/builtin/workspace-prepare.d.ts.map +1 -0
  105. package/dist/actions/builtin/workspace-prepare.js +108 -0
  106. package/dist/actions/builtin/workspace-prepare.js.map +1 -0
  107. package/dist/actions/cli-utils.d.ts +76 -0
  108. package/dist/actions/cli-utils.d.ts.map +1 -0
  109. package/dist/actions/cli-utils.js +278 -0
  110. package/dist/actions/cli-utils.js.map +1 -0
  111. package/dist/actions/epic/check-completion.d.ts +14 -0
  112. package/dist/actions/epic/check-completion.d.ts.map +1 -0
  113. package/dist/actions/epic/check-completion.js +57 -0
  114. package/dist/actions/epic/check-completion.js.map +1 -0
  115. package/dist/actions/epic/close.d.ts +18 -0
  116. package/dist/actions/epic/close.d.ts.map +1 -0
  117. package/dist/actions/epic/close.js +76 -0
  118. package/dist/actions/epic/close.js.map +1 -0
  119. package/dist/actions/epic/create-pr.d.ts +22 -0
  120. package/dist/actions/epic/create-pr.d.ts.map +1 -0
  121. package/dist/actions/epic/create-pr.js +140 -0
  122. package/dist/actions/epic/create-pr.js.map +1 -0
  123. package/dist/actions/epic/dispatch-children.d.ts +16 -0
  124. package/dist/actions/epic/dispatch-children.d.ts.map +1 -0
  125. package/dist/actions/epic/dispatch-children.js +95 -0
  126. package/dist/actions/epic/dispatch-children.js.map +1 -0
  127. package/dist/actions/epic/find-children.d.ts +16 -0
  128. package/dist/actions/epic/find-children.d.ts.map +1 -0
  129. package/dist/actions/epic/find-children.js +92 -0
  130. package/dist/actions/epic/find-children.js.map +1 -0
  131. package/dist/actions/epic/index.d.ts +20 -0
  132. package/dist/actions/epic/index.d.ts.map +1 -0
  133. package/dist/actions/epic/index.js +35 -0
  134. package/dist/actions/epic/index.js.map +1 -0
  135. package/dist/actions/epic/post-tasks-summary.d.ts +18 -0
  136. package/dist/actions/epic/post-tasks-summary.d.ts.map +1 -0
  137. package/dist/actions/epic/post-tasks-summary.js +123 -0
  138. package/dist/actions/epic/post-tasks-summary.js.map +1 -0
  139. package/dist/actions/epic/update-status.d.ts +22 -0
  140. package/dist/actions/epic/update-status.d.ts.map +1 -0
  141. package/dist/actions/epic/update-status.js +105 -0
  142. package/dist/actions/epic/update-status.js.map +1 -0
  143. package/dist/actions/github/add-comment.d.ts +14 -0
  144. package/dist/actions/github/add-comment.d.ts.map +1 -0
  145. package/dist/actions/github/add-comment.js +44 -0
  146. package/dist/actions/github/add-comment.js.map +1 -0
  147. package/dist/actions/github/client/gh-cli.d.ts +68 -0
  148. package/dist/actions/github/client/gh-cli.d.ts.map +1 -0
  149. package/dist/actions/github/client/gh-cli.js +781 -0
  150. package/dist/actions/github/client/gh-cli.js.map +1 -0
  151. package/dist/actions/github/client/index.d.ts +12 -0
  152. package/dist/actions/github/client/index.d.ts.map +1 -0
  153. package/dist/actions/github/client/index.js +10 -0
  154. package/dist/actions/github/client/index.js.map +1 -0
  155. package/dist/actions/github/client/interface.d.ts +270 -0
  156. package/dist/actions/github/client/interface.d.ts.map +1 -0
  157. package/dist/actions/github/client/interface.js +2 -0
  158. package/dist/actions/github/client/interface.js.map +1 -0
  159. package/dist/actions/github/commit-and-push.d.ts +19 -0
  160. package/dist/actions/github/commit-and-push.d.ts.map +1 -0
  161. package/dist/actions/github/commit-and-push.js +86 -0
  162. package/dist/actions/github/commit-and-push.js.map +1 -0
  163. package/dist/actions/github/create-draft-pr.d.ts +18 -0
  164. package/dist/actions/github/create-draft-pr.d.ts.map +1 -0
  165. package/dist/actions/github/create-draft-pr.js +76 -0
  166. package/dist/actions/github/create-draft-pr.js.map +1 -0
  167. package/dist/actions/github/get-context.d.ts +23 -0
  168. package/dist/actions/github/get-context.d.ts.map +1 -0
  169. package/dist/actions/github/get-context.js +109 -0
  170. package/dist/actions/github/get-context.js.map +1 -0
  171. package/dist/actions/github/index.d.ts +32 -0
  172. package/dist/actions/github/index.d.ts.map +1 -0
  173. package/dist/actions/github/index.js +67 -0
  174. package/dist/actions/github/index.js.map +1 -0
  175. package/dist/actions/github/label-definitions.d.ts +19 -0
  176. package/dist/actions/github/label-definitions.d.ts.map +1 -0
  177. package/dist/actions/github/label-definitions.js +71 -0
  178. package/dist/actions/github/label-definitions.js.map +1 -0
  179. package/dist/actions/github/mark-pr-ready.d.ts +14 -0
  180. package/dist/actions/github/mark-pr-ready.d.ts.map +1 -0
  181. package/dist/actions/github/mark-pr-ready.js +43 -0
  182. package/dist/actions/github/mark-pr-ready.js.map +1 -0
  183. package/dist/actions/github/merge-from-base.d.ts +15 -0
  184. package/dist/actions/github/merge-from-base.d.ts.map +1 -0
  185. package/dist/actions/github/merge-from-base.js +120 -0
  186. package/dist/actions/github/merge-from-base.js.map +1 -0
  187. package/dist/actions/github/preflight.d.ts +36 -0
  188. package/dist/actions/github/preflight.d.ts.map +1 -0
  189. package/dist/actions/github/preflight.js +292 -0
  190. package/dist/actions/github/preflight.js.map +1 -0
  191. package/dist/actions/github/read-pr-feedback.d.ts +14 -0
  192. package/dist/actions/github/read-pr-feedback.d.ts.map +1 -0
  193. package/dist/actions/github/read-pr-feedback.js +53 -0
  194. package/dist/actions/github/read-pr-feedback.js.map +1 -0
  195. package/dist/actions/github/respond-pr-feedback.d.ts +14 -0
  196. package/dist/actions/github/respond-pr-feedback.d.ts.map +1 -0
  197. package/dist/actions/github/respond-pr-feedback.js +58 -0
  198. package/dist/actions/github/respond-pr-feedback.js.map +1 -0
  199. package/dist/actions/github/review-changes.d.ts +19 -0
  200. package/dist/actions/github/review-changes.d.ts.map +1 -0
  201. package/dist/actions/github/review-changes.js +95 -0
  202. package/dist/actions/github/review-changes.js.map +1 -0
  203. package/dist/actions/github/sync-labels.d.ts +14 -0
  204. package/dist/actions/github/sync-labels.d.ts.map +1 -0
  205. package/dist/actions/github/sync-labels.js +81 -0
  206. package/dist/actions/github/sync-labels.js.map +1 -0
  207. package/dist/actions/github/update-pr.d.ts +14 -0
  208. package/dist/actions/github/update-pr.d.ts.map +1 -0
  209. package/dist/actions/github/update-pr.js +63 -0
  210. package/dist/actions/github/update-pr.js.map +1 -0
  211. package/dist/actions/index.d.ts +109 -0
  212. package/dist/actions/index.d.ts.map +1 -0
  213. package/dist/actions/index.js +233 -0
  214. package/dist/actions/index.js.map +1 -0
  215. package/dist/actions/workflow/check-gate.d.ts +15 -0
  216. package/dist/actions/workflow/check-gate.d.ts.map +1 -0
  217. package/dist/actions/workflow/check-gate.js +75 -0
  218. package/dist/actions/workflow/check-gate.js.map +1 -0
  219. package/dist/actions/workflow/index.d.ts +21 -0
  220. package/dist/actions/workflow/index.d.ts.map +1 -0
  221. package/dist/actions/workflow/index.js +31 -0
  222. package/dist/actions/workflow/index.js.map +1 -0
  223. package/dist/actions/workflow/update-phase.d.ts +15 -0
  224. package/dist/actions/workflow/update-phase.d.ts.map +1 -0
  225. package/dist/actions/workflow/update-phase.js +143 -0
  226. package/dist/actions/workflow/update-phase.js.map +1 -0
  227. package/dist/actions/workflow/update-stage.d.ts +19 -0
  228. package/dist/actions/workflow/update-stage.d.ts.map +1 -0
  229. package/dist/actions/workflow/update-stage.js +137 -0
  230. package/dist/actions/workflow/update-stage.js.map +1 -0
  231. package/dist/errors/base-workflow-not-found.d.ts +10 -0
  232. package/dist/errors/base-workflow-not-found.d.ts.map +1 -0
  233. package/dist/errors/base-workflow-not-found.js +18 -0
  234. package/dist/errors/base-workflow-not-found.js.map +1 -0
  235. package/dist/errors/circular-extends.d.ts +9 -0
  236. package/dist/errors/circular-extends.d.ts.map +1 -0
  237. package/dist/errors/circular-extends.js +13 -0
  238. package/dist/errors/circular-extends.js.map +1 -0
  239. package/dist/errors/correlation-timeout.d.ts +9 -0
  240. package/dist/errors/correlation-timeout.d.ts.map +1 -0
  241. package/dist/errors/correlation-timeout.js +13 -0
  242. package/dist/errors/correlation-timeout.js.map +1 -0
  243. package/dist/errors/workflow-override.d.ts +9 -0
  244. package/dist/errors/workflow-override.d.ts.map +1 -0
  245. package/dist/errors/workflow-override.js +12 -0
  246. package/dist/errors/workflow-override.js.map +1 -0
  247. package/dist/executor/events.d.ts +55 -0
  248. package/dist/executor/events.d.ts.map +1 -0
  249. package/dist/executor/events.js +72 -0
  250. package/dist/executor/events.js.map +1 -0
  251. package/dist/executor/gate-handler.d.ts +55 -0
  252. package/dist/executor/gate-handler.d.ts.map +1 -0
  253. package/dist/executor/gate-handler.js +139 -0
  254. package/dist/executor/gate-handler.js.map +1 -0
  255. package/dist/executor/index.d.ts +140 -0
  256. package/dist/executor/index.d.ts.map +1 -0
  257. package/dist/executor/index.js +681 -0
  258. package/dist/executor/index.js.map +1 -0
  259. package/dist/index.d.ts +24 -0
  260. package/dist/index.d.ts.map +1 -0
  261. package/dist/index.js +34 -0
  262. package/dist/index.js.map +1 -0
  263. package/dist/interpolation/context.d.ts +117 -0
  264. package/dist/interpolation/context.d.ts.map +1 -0
  265. package/dist/interpolation/context.js +256 -0
  266. package/dist/interpolation/context.js.map +1 -0
  267. package/dist/interpolation/index.d.ts +76 -0
  268. package/dist/interpolation/index.d.ts.map +1 -0
  269. package/dist/interpolation/index.js +264 -0
  270. package/dist/interpolation/index.js.map +1 -0
  271. package/dist/loader/extends.d.ts +48 -0
  272. package/dist/loader/extends.d.ts.map +1 -0
  273. package/dist/loader/extends.js +140 -0
  274. package/dist/loader/extends.js.map +1 -0
  275. package/dist/loader/index.d.ts +50 -0
  276. package/dist/loader/index.d.ts.map +1 -0
  277. package/dist/loader/index.js +190 -0
  278. package/dist/loader/index.js.map +1 -0
  279. package/dist/loader/schema.d.ts +506 -0
  280. package/dist/loader/schema.d.ts.map +1 -0
  281. package/dist/loader/schema.js +63 -0
  282. package/dist/loader/schema.js.map +1 -0
  283. package/dist/loader/validator.d.ts +33 -0
  284. package/dist/loader/validator.d.ts.map +1 -0
  285. package/dist/loader/validator.js +135 -0
  286. package/dist/loader/validator.js.map +1 -0
  287. package/dist/registry/index.d.ts +42 -0
  288. package/dist/registry/index.d.ts.map +1 -0
  289. package/dist/registry/index.js +77 -0
  290. package/dist/registry/index.js.map +1 -0
  291. package/dist/retry/index.d.ts +37 -0
  292. package/dist/retry/index.d.ts.map +1 -0
  293. package/dist/retry/index.js +225 -0
  294. package/dist/retry/index.js.map +1 -0
  295. package/dist/retry/strategies.d.ts +74 -0
  296. package/dist/retry/strategies.d.ts.map +1 -0
  297. package/dist/retry/strategies.js +136 -0
  298. package/dist/retry/strategies.js.map +1 -0
  299. package/dist/store/filesystem-store.d.ts +52 -0
  300. package/dist/store/filesystem-store.d.ts.map +1 -0
  301. package/dist/store/filesystem-store.js +230 -0
  302. package/dist/store/filesystem-store.js.map +1 -0
  303. package/dist/store/index.d.ts +7 -0
  304. package/dist/store/index.d.ts.map +1 -0
  305. package/dist/store/index.js +6 -0
  306. package/dist/store/index.js.map +1 -0
  307. package/dist/types/action.d.ts +358 -0
  308. package/dist/types/action.d.ts.map +1 -0
  309. package/dist/types/action.js +96 -0
  310. package/dist/types/action.js.map +1 -0
  311. package/dist/types/events.d.ts +25 -0
  312. package/dist/types/events.d.ts.map +1 -0
  313. package/dist/types/events.js +6 -0
  314. package/dist/types/events.js.map +1 -0
  315. package/dist/types/execution.d.ts +145 -0
  316. package/dist/types/execution.d.ts.map +1 -0
  317. package/dist/types/execution.js +2 -0
  318. package/dist/types/execution.js.map +1 -0
  319. package/dist/types/gate.d.ts +98 -0
  320. package/dist/types/gate.d.ts.map +1 -0
  321. package/dist/types/gate.js +41 -0
  322. package/dist/types/gate.js.map +1 -0
  323. package/dist/types/github.d.ts +706 -0
  324. package/dist/types/github.d.ts.map +1 -0
  325. package/dist/types/github.js +6 -0
  326. package/dist/types/github.js.map +1 -0
  327. package/dist/types/index.d.ts +17 -0
  328. package/dist/types/index.d.ts.map +1 -0
  329. package/dist/types/index.js +8 -0
  330. package/dist/types/index.js.map +1 -0
  331. package/dist/types/logger.d.ts +60 -0
  332. package/dist/types/logger.d.ts.map +1 -0
  333. package/dist/types/logger.js +66 -0
  334. package/dist/types/logger.js.map +1 -0
  335. package/dist/types/retry.d.ts +36 -0
  336. package/dist/types/retry.d.ts.map +1 -0
  337. package/dist/types/retry.js +6 -0
  338. package/dist/types/retry.js.map +1 -0
  339. package/dist/types/store.d.ts +88 -0
  340. package/dist/types/store.d.ts.map +1 -0
  341. package/dist/types/store.js +6 -0
  342. package/dist/types/store.js.map +1 -0
  343. package/dist/types/workflow.d.ts +105 -0
  344. package/dist/types/workflow.d.ts.map +1 -0
  345. package/dist/types/workflow.js +6 -0
  346. package/dist/types/workflow.js.map +1 -0
  347. package/package.json +69 -0
@@ -0,0 +1,681 @@
1
+ import { createLogger } from '../types/logger.js';
2
+ import { getActionHandler, registerBuiltinActions, } from '../actions/index.js';
3
+ import { ExecutionContext, interpolate, interpolateValue } from '../interpolation/index.js';
4
+ import { RetryManager, withTimeout } from '../retry/index.js';
5
+ import { ExecutionEventEmitter } from './events.js';
6
+ import { FilesystemWorkflowStore } from '../store/filesystem-store.js';
7
+ import { simpleGit } from 'simple-git';
8
+ import { getDefaultBranch } from '../actions/builtin/speckit/lib/feature.js';
9
+ // Ensure built-in actions are registered
10
+ let actionsRegistered = false;
11
+ function ensureActionsRegistered() {
12
+ if (!actionsRegistered) {
13
+ registerBuiltinActions();
14
+ actionsRegistered = true;
15
+ }
16
+ }
17
+ /**
18
+ * Reset the actions registration state (for testing)
19
+ */
20
+ export function resetActionsRegistration() {
21
+ actionsRegistered = false;
22
+ }
23
+ /**
24
+ * Workflow executor class
25
+ */
26
+ export class WorkflowExecutor {
27
+ currentExecution;
28
+ eventEmitter = new ExecutionEventEmitter();
29
+ executionContext;
30
+ abortController;
31
+ logger;
32
+ store;
33
+ constructor(options = {}) {
34
+ ensureActionsRegistered();
35
+ this.logger = options.logger ?? createLogger('WorkflowExecutor');
36
+ this.store = options.store ?? new FilesystemWorkflowStore();
37
+ }
38
+ /**
39
+ * Get current execution status
40
+ */
41
+ getStatus() {
42
+ return this.currentExecution?.status ?? 'idle';
43
+ }
44
+ /**
45
+ * Check if currently executing
46
+ */
47
+ isRunning() {
48
+ return this.currentExecution?.status === 'running';
49
+ }
50
+ /**
51
+ * Get current execution result
52
+ */
53
+ getCurrentExecution() {
54
+ return this.currentExecution;
55
+ }
56
+ /**
57
+ * Get the execution context (for accessing step outputs)
58
+ */
59
+ getExecutionContext() {
60
+ return this.executionContext;
61
+ }
62
+ /**
63
+ * Add event listener
64
+ */
65
+ addEventListener(listener) {
66
+ return this.eventEmitter.addEventListener(listener);
67
+ }
68
+ /**
69
+ * Execute a workflow
70
+ */
71
+ async execute(workflow, options = { mode: 'normal' }, inputs) {
72
+ // Check if already running
73
+ if (this.isRunning()) {
74
+ throw new Error('A workflow is already running. Cancel it first.');
75
+ }
76
+ // Initialize execution context for variable interpolation
77
+ this.executionContext = new ExecutionContext(inputs, { ...workflow.env, ...options.env });
78
+ // Initialize execution result
79
+ this.currentExecution = {
80
+ workflowName: workflow.name,
81
+ status: 'running',
82
+ mode: options.mode,
83
+ startTime: Date.now(),
84
+ phaseResults: [],
85
+ env: { ...workflow.env, ...options.env },
86
+ };
87
+ // Create abort controller
88
+ this.abortController = new AbortController();
89
+ // Emit start event
90
+ this.eventEmitter.emitEvent('execution:start', workflow.name, {
91
+ message: `Starting workflow: ${workflow.name}`,
92
+ });
93
+ // Log start
94
+ this.logger.info(`Starting workflow: ${workflow.name} (mode: ${options.mode})`);
95
+ if (options.mode === 'dry-run') {
96
+ this.logger.info('DRY-RUN MODE: No actions will be executed');
97
+ }
98
+ try {
99
+ // Find starting phase
100
+ let startPhaseIndex = 0;
101
+ if (options.startPhase) {
102
+ const index = workflow.phases.findIndex(p => p.name === options.startPhase);
103
+ if (index >= 0) {
104
+ startPhaseIndex = index;
105
+ }
106
+ }
107
+ // Execute phases
108
+ for (let i = startPhaseIndex; i < workflow.phases.length; i++) {
109
+ if (this.abortController.signal.aborted) {
110
+ this.currentExecution.status = 'cancelled';
111
+ break;
112
+ }
113
+ const phase = workflow.phases[i];
114
+ if (!phase) {
115
+ continue;
116
+ }
117
+ const phaseResult = await this.executePhase(workflow, phase, i, workflow.phases.length, options, i === startPhaseIndex ? options.startStep : undefined);
118
+ this.currentExecution.phaseResults.push(phaseResult);
119
+ // After setup phase, validate we're on a feature branch (not default)
120
+ if (phase.name === 'setup' && phaseResult.status === 'completed' && options.cwd) {
121
+ await this.validateBranchState(options.cwd);
122
+ }
123
+ // Stop if phase failed and not continuing on error
124
+ if (phaseResult.status === 'failed') {
125
+ this.currentExecution.status = 'failed';
126
+ break;
127
+ }
128
+ }
129
+ // Set final status if not already set
130
+ if (this.currentExecution.status === 'running') {
131
+ this.currentExecution.status = 'completed';
132
+ }
133
+ }
134
+ catch (error) {
135
+ this.currentExecution.status = 'failed';
136
+ this.eventEmitter.emitEvent('execution:error', workflow.name, {
137
+ message: error instanceof Error ? error.message : String(error),
138
+ });
139
+ }
140
+ // Finalize execution
141
+ this.currentExecution.endTime = Date.now();
142
+ this.currentExecution.duration = this.currentExecution.endTime - this.currentExecution.startTime;
143
+ // Log completion
144
+ this.logger.info(`Workflow ${this.currentExecution.status}: ${workflow.name} (${this.currentExecution.duration}ms)`);
145
+ // Emit completion event
146
+ this.eventEmitter.emitEvent(this.currentExecution.status === 'completed' ? 'execution:complete' : 'execution:error', workflow.name, {
147
+ message: `Workflow ${this.currentExecution.status}: ${workflow.name}`,
148
+ data: this.currentExecution,
149
+ });
150
+ // Cleanup
151
+ this.abortController = undefined;
152
+ return this.currentExecution;
153
+ }
154
+ /**
155
+ * Execute a single phase
156
+ */
157
+ async executePhase(workflow, phase, phaseIndex, totalPhases, options, startStep) {
158
+ const result = {
159
+ phaseName: phase.name,
160
+ status: 'running',
161
+ startTime: Date.now(),
162
+ stepResults: [],
163
+ };
164
+ // Log phase start
165
+ this.logger.info(`Phase [${phaseIndex + 1}/${totalPhases}]: ${phase.name}`);
166
+ // Emit phase start event
167
+ this.eventEmitter.emitEvent('phase:start', workflow.name, {
168
+ phaseName: phase.name,
169
+ message: `Starting phase: ${phase.name}`,
170
+ });
171
+ // Check phase condition
172
+ if (phase.condition && options.mode !== 'dry-run') {
173
+ const shouldRun = await this.evaluateCondition(phase.condition, options);
174
+ if (!shouldRun) {
175
+ result.status = 'skipped';
176
+ result.endTime = Date.now();
177
+ result.duration = 0;
178
+ this.logger.info(`Phase "${phase.name}" skipped: condition not met`);
179
+ return result;
180
+ }
181
+ }
182
+ try {
183
+ // Find starting step
184
+ let startStepIndex = 0;
185
+ if (startStep) {
186
+ const index = phase.steps.findIndex(s => s.name === startStep);
187
+ if (index >= 0) {
188
+ startStepIndex = index;
189
+ }
190
+ }
191
+ // Execute steps
192
+ for (let i = startStepIndex; i < phase.steps.length; i++) {
193
+ if (this.abortController?.signal.aborted) {
194
+ result.status = 'failed';
195
+ break;
196
+ }
197
+ const step = phase.steps[i];
198
+ if (!step) {
199
+ continue;
200
+ }
201
+ const stepResult = await this.executeStep(workflow, phase, step, i, phase.steps.length, options);
202
+ result.stepResults.push(stepResult);
203
+ // Stop if step failed and not continuing on error
204
+ if (stepResult.status === 'failed' && !step.continueOnError) {
205
+ result.status = 'failed';
206
+ break;
207
+ }
208
+ }
209
+ // Set final status if not already set
210
+ if (result.status === 'running') {
211
+ result.status = 'completed';
212
+ }
213
+ }
214
+ catch (error) {
215
+ result.status = 'failed';
216
+ this.eventEmitter.emitEvent('phase:error', workflow.name, {
217
+ phaseName: phase.name,
218
+ message: error instanceof Error ? error.message : String(error),
219
+ });
220
+ }
221
+ // Finalize phase
222
+ result.endTime = Date.now();
223
+ result.duration = result.endTime - result.startTime;
224
+ // Log phase completion
225
+ this.logger.info(`Phase ${result.status}: ${phase.name} (${result.duration}ms)`);
226
+ // Emit phase completion event
227
+ this.eventEmitter.emitEvent(result.status === 'completed' ? 'phase:complete' : 'phase:error', workflow.name, {
228
+ phaseName: phase.name,
229
+ message: `Phase ${result.status}: ${phase.name}`,
230
+ data: result,
231
+ });
232
+ return result;
233
+ }
234
+ /**
235
+ * Execute a single step using action handlers
236
+ */
237
+ async executeStep(workflow, phase, step, stepIndex, totalSteps, options) {
238
+ const result = {
239
+ stepName: step.name,
240
+ phaseName: phase.name,
241
+ status: 'running',
242
+ startTime: Date.now(),
243
+ };
244
+ // Log step start
245
+ this.logger.info(` Step [${stepIndex + 1}/${totalSteps}]: ${step.name}`);
246
+ // Emit step start event
247
+ this.eventEmitter.emitEvent('step:start', workflow.name, {
248
+ phaseName: phase.name,
249
+ stepName: step.name,
250
+ message: `Starting step: ${step.name}`,
251
+ });
252
+ // Check step condition
253
+ if (step.condition && options.mode !== 'dry-run') {
254
+ const shouldRun = await this.evaluateCondition(step.condition, options);
255
+ if (!shouldRun) {
256
+ result.status = 'skipped';
257
+ result.endTime = Date.now();
258
+ result.duration = 0;
259
+ this.logger.info(` Step "${step.name}" skipped: condition not met`);
260
+ return result;
261
+ }
262
+ }
263
+ // For dry-run mode, don't actually execute
264
+ if (options.mode === 'dry-run') {
265
+ result.status = 'completed';
266
+ result.output = `[DRY-RUN] Would execute step: ${step.name}`;
267
+ result.endTime = Date.now();
268
+ result.duration = result.endTime - result.startTime;
269
+ this.logger.info(` [DRY-RUN] Would execute: ${step.name}`);
270
+ return result;
271
+ }
272
+ try {
273
+ // Interpolate step configuration
274
+ const interpolatedStep = this.interpolateStep(step);
275
+ // Get action handler for this step
276
+ const handler = getActionHandler(interpolatedStep);
277
+ if (handler) {
278
+ // Execute using action handler
279
+ const actionResult = await this.executeWithActionHandler(workflow, phase, interpolatedStep, handler, options);
280
+ result.output = typeof actionResult.output === 'string'
281
+ ? actionResult.output
282
+ : JSON.stringify(actionResult.output);
283
+ result.exitCode = actionResult.exitCode;
284
+ result.error = actionResult.error;
285
+ result.status = actionResult.success ? 'completed' : 'failed';
286
+ // Store step output for interpolation
287
+ this.storeStepOutput(step.name, actionResult);
288
+ // Emit output if available
289
+ if (actionResult.stdout) {
290
+ this.eventEmitter.emitEvent('step:output', workflow.name, {
291
+ phaseName: phase.name,
292
+ stepName: step.name,
293
+ message: actionResult.stdout,
294
+ });
295
+ }
296
+ }
297
+ else {
298
+ // No handler found - fail
299
+ result.status = 'failed';
300
+ result.error = `No action handler found for step "${step.name}"`;
301
+ this.logger.error(` No action handler found for step: ${step.name}`);
302
+ }
303
+ }
304
+ catch (error) {
305
+ result.status = 'failed';
306
+ result.error = error instanceof Error ? error.message : String(error);
307
+ }
308
+ // Finalize step
309
+ result.endTime = Date.now();
310
+ result.duration = result.endTime - result.startTime;
311
+ // Log step completion
312
+ this.logger.info(` Step ${result.status}: ${step.name} (${result.duration}ms)`);
313
+ // Emit step completion event
314
+ this.eventEmitter.emitEvent(result.status === 'completed' ? 'step:complete' : 'step:error', workflow.name, {
315
+ phaseName: phase.name,
316
+ stepName: step.name,
317
+ message: `Step ${result.status}: ${step.name}`,
318
+ data: result,
319
+ });
320
+ return result;
321
+ }
322
+ /**
323
+ * Execute step using action handler with retry logic
324
+ */
325
+ async executeWithActionHandler(workflow, phase, step, handler, options) {
326
+ if (!handler) {
327
+ return {
328
+ success: false,
329
+ output: null,
330
+ error: 'No action handler found',
331
+ duration: 0,
332
+ };
333
+ }
334
+ // Create a step-level AbortController so step timeouts can cancel the operation.
335
+ // Links to the parent (workflow-level) abort controller.
336
+ const stepAbort = new AbortController();
337
+ const parentSignal = this.abortController?.signal;
338
+ if (parentSignal?.aborted) {
339
+ stepAbort.abort();
340
+ }
341
+ else {
342
+ parentSignal?.addEventListener('abort', () => stepAbort.abort(), { once: true });
343
+ }
344
+ // Create action context with step-level signal
345
+ const context = this.createActionContext(workflow, phase, step, options);
346
+ context.signal = stepAbort.signal;
347
+ // Emit action start event
348
+ this.eventEmitter.emitEvent('action:start', workflow.name, {
349
+ phaseName: phase.name,
350
+ stepName: step.name,
351
+ message: `Starting action [${handler.type}]`,
352
+ });
353
+ // Create retry manager with event callback
354
+ const retryManager = RetryManager.fromStep(step, (state, error) => {
355
+ this.eventEmitter.emitEvent('action:retry', workflow.name, {
356
+ phaseName: phase.name,
357
+ stepName: step.name,
358
+ message: `Retrying action (attempt ${state.attempt + 1}): ${error.message}`,
359
+ data: state,
360
+ });
361
+ });
362
+ // Default timeout: 5 minutes (300000ms)
363
+ const DEFAULT_STEP_TIMEOUT = 300000;
364
+ const stepTimeout = step.timeout ?? DEFAULT_STEP_TIMEOUT;
365
+ // Execute with retry and timeout wrapper.
366
+ // On timeout, abort the step so spawned processes are killed.
367
+ const retryResult = await withTimeout(retryManager.executeWithRetry(handler, step, context), stepTimeout, stepAbort.signal, () => stepAbort.abort());
368
+ // Emit action completion event
369
+ this.eventEmitter.emitEvent(retryResult.result.success ? 'action:complete' : 'action:error', workflow.name, {
370
+ phaseName: phase.name,
371
+ stepName: step.name,
372
+ message: retryResult.result.success
373
+ ? `Action [${handler.type}] completed`
374
+ : `Action [${handler.type}] failed: ${retryResult.result.error}`,
375
+ data: {
376
+ attempts: retryResult.attempts,
377
+ totalDuration: retryResult.totalDuration,
378
+ },
379
+ });
380
+ return retryResult.result;
381
+ }
382
+ /**
383
+ * Create action context for step execution
384
+ */
385
+ createActionContext(workflow, phase, step, options) {
386
+ // Create logger for actions
387
+ const actionLogger = {
388
+ info: (msg) => this.logger.info(` ${msg}`),
389
+ warn: (msg) => this.logger.warn(` ${msg}`),
390
+ error: (msg) => this.logger.error(` ${msg}`),
391
+ debug: (msg) => this.logger.debug(` ${msg}`),
392
+ };
393
+ return {
394
+ workflow,
395
+ phase,
396
+ step,
397
+ inputs: this.executionContext?.getInputs() ?? {},
398
+ stepOutputs: this.executionContext?.getAllStepOutputs() ?? new Map(),
399
+ env: { ...workflow.env, ...options.env, ...step.env },
400
+ workdir: options.cwd ?? process.cwd(),
401
+ signal: this.abortController?.signal ?? new AbortController().signal,
402
+ logger: actionLogger,
403
+ emitEvent: (event) => {
404
+ this.eventEmitter.emitEvent(event.type, workflow.name, {
405
+ phaseName: phase.name,
406
+ stepName: step.name,
407
+ data: event.data,
408
+ });
409
+ },
410
+ };
411
+ }
412
+ /**
413
+ * Interpolate variables in step configuration
414
+ */
415
+ interpolateStep(step) {
416
+ if (!this.executionContext) {
417
+ return step;
418
+ }
419
+ const context = this.executionContext.getInterpolationContext();
420
+ // Deep clone and interpolate
421
+ const interpolated = { ...step };
422
+ if (interpolated.command) {
423
+ interpolated.command = interpolate(interpolated.command, context);
424
+ }
425
+ if (interpolated.script) {
426
+ interpolated.script = interpolate(interpolated.script, context);
427
+ }
428
+ if (interpolated.with) {
429
+ interpolated.with = interpolateValue(interpolated.with, context);
430
+ }
431
+ if (interpolated.env) {
432
+ interpolated.env = interpolateValue(interpolated.env, context);
433
+ }
434
+ return interpolated;
435
+ }
436
+ /**
437
+ * Store step output in execution context
438
+ */
439
+ storeStepOutput(stepId, result) {
440
+ if (!this.executionContext) {
441
+ return;
442
+ }
443
+ const raw = result.stdout ?? (typeof result.output === 'string' ? result.output : JSON.stringify(result.output));
444
+ // Try to parse output as JSON
445
+ let parsed = null;
446
+ if (typeof result.output === 'object') {
447
+ parsed = result.output;
448
+ }
449
+ else if (typeof result.output === 'string') {
450
+ try {
451
+ parsed = JSON.parse(result.output);
452
+ }
453
+ catch {
454
+ parsed = null;
455
+ }
456
+ }
457
+ const stepOutput = {
458
+ raw,
459
+ parsed,
460
+ exitCode: result.exitCode ?? (result.success ? 0 : 1),
461
+ completedAt: new Date(),
462
+ };
463
+ this.executionContext.setStepOutput(stepId, stepOutput);
464
+ }
465
+ /**
466
+ * Cancel the current execution
467
+ */
468
+ cancel() {
469
+ if (this.isRunning()) {
470
+ this.abortController?.abort();
471
+ if (this.currentExecution) {
472
+ this.currentExecution.status = 'cancelled';
473
+ }
474
+ this.eventEmitter.emitEvent('execution:cancel', this.currentExecution?.workflowName ?? 'unknown', {
475
+ message: 'Execution cancelled by user',
476
+ });
477
+ }
478
+ }
479
+ /**
480
+ * Validate a workflow without executing (dry-run)
481
+ */
482
+ async validate(workflow, env) {
483
+ return this.execute(workflow, { mode: 'dry-run', env });
484
+ }
485
+ /**
486
+ * Check if there's a pending workflow state that can be resumed
487
+ */
488
+ async hasPendingState(workflowId) {
489
+ const state = await this.store.load(workflowId);
490
+ return state !== null && state.pendingReview !== undefined;
491
+ }
492
+ /**
493
+ * Get all pending workflow states
494
+ */
495
+ async listPendingWorkflows() {
496
+ return this.store.listPending();
497
+ }
498
+ /**
499
+ * Load a saved workflow state
500
+ */
501
+ async loadWorkflowState(workflowId) {
502
+ return this.store.load(workflowId);
503
+ }
504
+ /**
505
+ * Resume a paused workflow from saved state.
506
+ * Call this after receiving a human review decision.
507
+ *
508
+ * @param workflow The workflow definition (must match the saved state)
509
+ * @param workflowId The workflow ID to resume
510
+ * @param resumeOptions Options containing the human decision
511
+ * @param executionOptions Standard execution options
512
+ * @returns The execution result
513
+ */
514
+ async resume(workflow, workflowId, resumeOptions, executionOptions = { mode: 'normal' }) {
515
+ // Load saved state
516
+ const savedState = await this.store.load(workflowId);
517
+ if (!savedState) {
518
+ throw new Error(`No saved workflow state found for ID: ${workflowId}`);
519
+ }
520
+ if (!savedState.pendingReview) {
521
+ throw new Error(`Workflow ${workflowId} does not have a pending review`);
522
+ }
523
+ this.logger.info(`Resuming workflow ${workflowId} from phase: ${savedState.currentPhase}, step: ${savedState.currentStep}`);
524
+ // Initialize execution context with saved state
525
+ this.executionContext = new ExecutionContext(savedState.inputs, { ...workflow.env, ...executionOptions.env });
526
+ // Restore step outputs from saved state
527
+ for (const [stepId, outputData] of Object.entries(savedState.stepOutputs)) {
528
+ const stepOutput = {
529
+ raw: outputData.raw,
530
+ parsed: outputData.parsed,
531
+ exitCode: outputData.exitCode,
532
+ completedAt: new Date(outputData.completedAt),
533
+ };
534
+ this.executionContext.setStepOutput(stepId, stepOutput);
535
+ }
536
+ // If we have a decision, inject it into the context for the review step
537
+ if (resumeOptions.decision) {
538
+ const reviewOutput = {
539
+ raw: JSON.stringify(resumeOptions.decision),
540
+ parsed: {
541
+ approved: resumeOptions.decision.approved,
542
+ comments: resumeOptions.decision.comments,
543
+ respondedBy: resumeOptions.decision.respondedBy,
544
+ respondedAt: resumeOptions.decision.respondedAt,
545
+ reviewId: savedState.pendingReview.reviewId,
546
+ },
547
+ exitCode: 0,
548
+ completedAt: new Date(),
549
+ };
550
+ this.executionContext.setStepOutput(savedState.currentStep, reviewOutput);
551
+ }
552
+ // Clear the pending state
553
+ await this.store.delete(workflowId);
554
+ // Find the next step after the review step
555
+ let nextPhaseIndex = -1;
556
+ let nextStepIndex = -1;
557
+ let foundCurrentStep = false;
558
+ for (let pi = 0; pi < workflow.phases.length; pi++) {
559
+ const phase = workflow.phases[pi];
560
+ if (!phase)
561
+ continue;
562
+ const isCurrentPhase = phase.name === savedState.currentPhase;
563
+ for (let si = 0; si < phase.steps.length; si++) {
564
+ const step = phase.steps[si];
565
+ if (!step)
566
+ continue;
567
+ const isCurrentStep = step.name === savedState.currentStep;
568
+ if (isCurrentPhase && isCurrentStep) {
569
+ foundCurrentStep = true;
570
+ // The next step is after this one
571
+ if (si + 1 < phase.steps.length) {
572
+ nextPhaseIndex = pi;
573
+ nextStepIndex = si + 1;
574
+ }
575
+ else if (pi + 1 < workflow.phases.length) {
576
+ nextPhaseIndex = pi + 1;
577
+ nextStepIndex = 0;
578
+ }
579
+ break;
580
+ }
581
+ }
582
+ if (foundCurrentStep)
583
+ break;
584
+ }
585
+ if (!foundCurrentStep) {
586
+ throw new Error(`Could not find current step ${savedState.currentStep} in workflow`);
587
+ }
588
+ // If there's no next step, workflow is complete
589
+ if (nextPhaseIndex < 0) {
590
+ this.logger.info('Workflow completed (no more steps after review)');
591
+ return {
592
+ workflowName: workflow.name,
593
+ status: 'completed',
594
+ mode: executionOptions.mode,
595
+ startTime: Date.now(),
596
+ endTime: Date.now(),
597
+ duration: 0,
598
+ phaseResults: [],
599
+ env: { ...workflow.env, ...executionOptions.env },
600
+ };
601
+ }
602
+ // Resume execution from the next step
603
+ const nextPhase = workflow.phases[nextPhaseIndex];
604
+ const nextStep = nextPhase?.steps[nextStepIndex];
605
+ this.logger.info(`Continuing from phase: ${nextPhase?.name}, step: ${nextStep?.name}`);
606
+ // Execute from the resume point
607
+ return this.execute(workflow, {
608
+ ...executionOptions,
609
+ startPhase: nextPhase?.name,
610
+ startStep: nextStep?.name,
611
+ }, savedState.inputs);
612
+ }
613
+ /**
614
+ * Dispose resources
615
+ */
616
+ dispose() {
617
+ this.cancel();
618
+ this.eventEmitter.removeAllListeners();
619
+ this.currentExecution = undefined;
620
+ this.executionContext = undefined;
621
+ }
622
+ /**
623
+ * Validate that the working directory is not on the default branch.
624
+ * Called after the setup phase to catch failed branch creation early.
625
+ */
626
+ async validateBranchState(cwd) {
627
+ const git = simpleGit(cwd);
628
+ const currentBranch = (await git.revparse(['--abbrev-ref', 'HEAD'])).trim();
629
+ const defaultBranch = await getDefaultBranch(git);
630
+ if (currentBranch === defaultBranch) {
631
+ throw new Error(`Branch validation failed: still on default branch "${currentBranch}" after setup phase. ` +
632
+ `The create-feature step likely failed to create/checkout the feature branch.`);
633
+ }
634
+ }
635
+ /**
636
+ * Evaluate a condition expression using interpolation context
637
+ */
638
+ async evaluateCondition(condition, options) {
639
+ try {
640
+ let evaluated = condition;
641
+ // Use interpolation context if available
642
+ if (this.executionContext) {
643
+ const context = this.executionContext.getInterpolationContext();
644
+ evaluated = interpolate(condition, context);
645
+ }
646
+ else {
647
+ // Fall back to simple environment variable replacement
648
+ for (const [key, value] of Object.entries(options.env ?? {})) {
649
+ evaluated = evaluated.replace(new RegExp(`\\$\\{?${key}\\}?`, 'g'), value);
650
+ }
651
+ }
652
+ // Handle function calls
653
+ if (evaluated.includes('success()')) {
654
+ const result = this.executionContext?.getInterpolationContext().functions.success() ?? true;
655
+ evaluated = evaluated.replace(/success\(\)/g, String(result));
656
+ }
657
+ if (evaluated.includes('failure()')) {
658
+ const result = this.executionContext?.getInterpolationContext().functions.failure() ?? false;
659
+ evaluated = evaluated.replace(/failure\(\)/g, String(result));
660
+ }
661
+ if (evaluated.includes('always()')) {
662
+ evaluated = evaluated.replace(/always\(\)/g, 'true');
663
+ }
664
+ // Simple truthy evaluation
665
+ if (evaluated === 'true' || evaluated === '1') {
666
+ return true;
667
+ }
668
+ if (evaluated === 'false' || evaluated === '0' || evaluated === '') {
669
+ return false;
670
+ }
671
+ // Default to true for non-empty strings
672
+ return evaluated.trim().length > 0;
673
+ }
674
+ catch {
675
+ return true; // Default to running on evaluation error
676
+ }
677
+ }
678
+ }
679
+ // Re-export events
680
+ export { ExecutionEventEmitter, createExecutionEvent } from './events.js';
681
+ //# sourceMappingURL=index.js.map