@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,781 @@
1
+ import { executeCommand, parseJSONSafe } from '../../cli-utils.js';
2
+ /**
3
+ * GitHubClient implementation using gh CLI and git commands
4
+ */
5
+ export class GhCliGitHubClient {
6
+ workdir;
7
+ constructor(workdir) {
8
+ this.workdir = workdir ?? process.cwd();
9
+ }
10
+ // ==========================================================================
11
+ // Repository Info
12
+ // ==========================================================================
13
+ async getRepoInfo() {
14
+ const result = await executeCommand('gh', [
15
+ 'repo', 'view',
16
+ '--json', 'owner,name,defaultBranchRef',
17
+ ], { cwd: this.workdir });
18
+ if (result.exitCode !== 0) {
19
+ throw new Error(`Failed to get repo info: ${result.stderr}`);
20
+ }
21
+ const data = parseJSONSafe(result.stdout);
22
+ if (!data) {
23
+ throw new Error('Failed to parse repo info');
24
+ }
25
+ return {
26
+ owner: data.owner.login,
27
+ repo: data.name,
28
+ default_branch: data.defaultBranchRef.name,
29
+ };
30
+ }
31
+ // ==========================================================================
32
+ // Issue Operations
33
+ // ==========================================================================
34
+ async getIssue(owner, repo, number) {
35
+ const result = await executeCommand('gh', [
36
+ 'issue', 'view', String(number),
37
+ '-R', `${owner}/${repo}`,
38
+ '--json', 'number,title,body,state,labels,assignees,milestone,createdAt,updatedAt',
39
+ ], { cwd: this.workdir });
40
+ if (result.exitCode !== 0) {
41
+ throw new Error(`Failed to get issue #${number}: ${result.stderr}`);
42
+ }
43
+ const data = parseJSONSafe(result.stdout);
44
+ if (!data) {
45
+ throw new Error('Failed to parse issue data');
46
+ }
47
+ return {
48
+ number: data['number'],
49
+ title: data['title'],
50
+ body: data['body'] ?? '',
51
+ state: data['state'].toLowerCase(),
52
+ labels: (data['labels'] ?? []).map(l => ({
53
+ name: l.name,
54
+ color: l.color,
55
+ description: l.description,
56
+ })),
57
+ assignees: (data['assignees'] ?? []).map(a => a.login),
58
+ milestone: data['milestone'] ? {
59
+ number: data['milestone'].number,
60
+ title: data['milestone'].title,
61
+ state: 'open',
62
+ } : undefined,
63
+ created_at: data['createdAt'],
64
+ updated_at: data['updatedAt'],
65
+ };
66
+ }
67
+ async listIssuesWithLabel(owner, repo, label) {
68
+ const result = await executeCommand('gh', [
69
+ 'issue', 'list',
70
+ '-R', `${owner}/${repo}`,
71
+ '--label', label,
72
+ '--state', 'open',
73
+ '--json', 'number,title,body,state,labels,assignees,milestone,createdAt,updatedAt',
74
+ '--limit', '100',
75
+ ], { cwd: this.workdir });
76
+ if (result.exitCode !== 0) {
77
+ throw new Error(`Failed to list issues with label "${label}": ${result.stderr}`);
78
+ }
79
+ const data = parseJSONSafe(result.stdout);
80
+ if (!data) {
81
+ return [];
82
+ }
83
+ return data.map(item => ({
84
+ number: item['number'],
85
+ title: item['title'],
86
+ body: item['body'] ?? '',
87
+ state: (item['state'] ?? 'open').toLowerCase(),
88
+ labels: (item['labels'] ?? []).map(l => ({
89
+ name: l.name,
90
+ color: l.color,
91
+ description: l.description,
92
+ })),
93
+ assignees: (item['assignees'] ?? []).map(a => a.login),
94
+ milestone: item['milestone'] ? {
95
+ number: item['milestone'].number,
96
+ title: item['milestone'].title,
97
+ state: 'open',
98
+ } : undefined,
99
+ created_at: item['createdAt'],
100
+ updated_at: item['updatedAt'],
101
+ }));
102
+ }
103
+ async updateIssue(owner, repo, number, data) {
104
+ const args = ['issue', 'edit', String(number), '-R', `${owner}/${repo}`];
105
+ if (data.title) {
106
+ args.push('--title', data.title);
107
+ }
108
+ if (data.body !== undefined) {
109
+ args.push('--body', data.body);
110
+ }
111
+ if (data.labels) {
112
+ // Clear existing labels and add new ones
113
+ args.push('--remove-label', '*');
114
+ for (const label of data.labels) {
115
+ args.push('--add-label', label);
116
+ }
117
+ }
118
+ if (data.assignees) {
119
+ for (const assignee of data.assignees) {
120
+ args.push('--add-assignee', assignee);
121
+ }
122
+ }
123
+ const result = await executeCommand('gh', args, { cwd: this.workdir });
124
+ if (result.exitCode !== 0) {
125
+ throw new Error(`Failed to update issue #${number}: ${result.stderr}`);
126
+ }
127
+ }
128
+ async addIssueComment(owner, repo, number, body) {
129
+ const result = await executeCommand('gh', [
130
+ 'issue', 'comment', String(number),
131
+ '-R', `${owner}/${repo}`,
132
+ '--body', body,
133
+ ], { cwd: this.workdir });
134
+ if (result.exitCode !== 0) {
135
+ throw new Error(`Failed to add comment to issue #${number}: ${result.stderr}`);
136
+ }
137
+ // gh doesn't return the comment details, so we need to fetch the latest comment
138
+ const comments = await this.getIssueComments(owner, repo, number);
139
+ const latest = comments[comments.length - 1];
140
+ if (!latest) {
141
+ throw new Error('Failed to get created comment');
142
+ }
143
+ return latest;
144
+ }
145
+ async getIssueComments(owner, repo, number) {
146
+ const result = await executeCommand('gh', [
147
+ 'issue', 'view', String(number),
148
+ '-R', `${owner}/${repo}`,
149
+ '--json', 'comments',
150
+ ], { cwd: this.workdir });
151
+ if (result.exitCode !== 0) {
152
+ throw new Error(`Failed to get comments for issue #${number}: ${result.stderr}`);
153
+ }
154
+ const data = parseJSONSafe(result.stdout);
155
+ if (!data) {
156
+ return [];
157
+ }
158
+ return data.comments.map(c => ({
159
+ id: parseInt(c.id.split('/').pop() ?? '0', 10),
160
+ body: c.body,
161
+ author: c.author.login,
162
+ created_at: c.createdAt,
163
+ updated_at: c.updatedAt,
164
+ }));
165
+ }
166
+ async updateComment(owner, repo, commentId, body) {
167
+ // gh CLI doesn't have a direct command to edit comments, use API
168
+ const result = await executeCommand('gh', [
169
+ 'api',
170
+ '-X', 'PATCH',
171
+ `/repos/${owner}/${repo}/issues/comments/${commentId}`,
172
+ '-f', `body=${body}`,
173
+ ], { cwd: this.workdir });
174
+ if (result.exitCode !== 0) {
175
+ throw new Error(`Failed to update comment ${commentId}: ${result.stderr}`);
176
+ }
177
+ }
178
+ // ==========================================================================
179
+ // PR Operations
180
+ // ==========================================================================
181
+ async createPullRequest(owner, repo, data) {
182
+ const args = [
183
+ 'pr', 'create',
184
+ '-R', `${owner}/${repo}`,
185
+ '--title', data.title,
186
+ '--body', data.body ?? '',
187
+ '--head', data.head,
188
+ '--base', data.base,
189
+ ];
190
+ if (data.draft) {
191
+ args.push('--draft');
192
+ }
193
+ // Add JSON output for details
194
+ args.push('--json', 'number,url,state,headRefName,baseRefName,isDraft,title,body,createdAt,updatedAt');
195
+ const result = await executeCommand('gh', args, { cwd: this.workdir });
196
+ if (result.exitCode !== 0) {
197
+ throw new Error(`Failed to create PR: ${result.stderr}`);
198
+ }
199
+ const parsed = parseJSONSafe(result.stdout);
200
+ if (!parsed) {
201
+ // Try to extract URL from output
202
+ const urlMatch = result.stdout.match(/https:\/\/github\.com\/[^\s]+\/pull\/(\d+)/);
203
+ if (urlMatch) {
204
+ return {
205
+ number: parseInt(urlMatch[1], 10),
206
+ title: data.title,
207
+ body: data.body ?? '',
208
+ state: data.draft ? 'open' : 'open',
209
+ draft: data.draft ?? false,
210
+ head: { ref: data.head, sha: '', repo: `${owner}/${repo}` },
211
+ base: { ref: data.base, sha: '', repo: `${owner}/${repo}` },
212
+ labels: [],
213
+ created_at: new Date().toISOString(),
214
+ updated_at: new Date().toISOString(),
215
+ };
216
+ }
217
+ throw new Error('Failed to parse PR creation response');
218
+ }
219
+ return {
220
+ number: parsed['number'],
221
+ title: parsed['title'],
222
+ body: parsed['body'] ?? '',
223
+ state: 'open',
224
+ draft: parsed['isDraft'] ?? false,
225
+ head: { ref: parsed['headRefName'], sha: '', repo: `${owner}/${repo}` },
226
+ base: { ref: parsed['baseRefName'], sha: '', repo: `${owner}/${repo}` },
227
+ labels: [],
228
+ created_at: parsed['createdAt'],
229
+ updated_at: parsed['updatedAt'],
230
+ };
231
+ }
232
+ async getPullRequest(owner, repo, number) {
233
+ const result = await executeCommand('gh', [
234
+ 'pr', 'view', String(number),
235
+ '-R', `${owner}/${repo}`,
236
+ '--json', 'number,title,body,state,isDraft,headRefName,baseRefName,labels,mergeable,createdAt,updatedAt',
237
+ ], { cwd: this.workdir });
238
+ if (result.exitCode !== 0) {
239
+ throw new Error(`Failed to get PR #${number}: ${result.stderr}`);
240
+ }
241
+ const data = parseJSONSafe(result.stdout);
242
+ if (!data) {
243
+ throw new Error('Failed to parse PR data');
244
+ }
245
+ const state = data['state'].toLowerCase();
246
+ return {
247
+ number: data['number'],
248
+ title: data['title'],
249
+ body: data['body'] ?? '',
250
+ state: state === 'merged' ? 'merged' : state === 'closed' ? 'closed' : 'open',
251
+ draft: data['isDraft'] ?? false,
252
+ head: { ref: data['headRefName'], sha: '', repo: `${owner}/${repo}` },
253
+ base: { ref: data['baseRefName'], sha: '', repo: `${owner}/${repo}` },
254
+ labels: (data['labels'] ?? []).map(l => ({
255
+ name: l.name,
256
+ color: l.color,
257
+ })),
258
+ mergeable: data['mergeable'],
259
+ created_at: data['createdAt'],
260
+ updated_at: data['updatedAt'],
261
+ };
262
+ }
263
+ async updatePullRequest(owner, repo, number, data) {
264
+ const args = ['pr', 'edit', String(number), '-R', `${owner}/${repo}`];
265
+ if (data.title) {
266
+ args.push('--title', data.title);
267
+ }
268
+ if (data.body !== undefined) {
269
+ args.push('--body', data.body);
270
+ }
271
+ const result = await executeCommand('gh', args, { cwd: this.workdir });
272
+ if (result.exitCode !== 0) {
273
+ throw new Error(`Failed to update PR #${number}: ${result.stderr}`);
274
+ }
275
+ // Handle state change separately
276
+ if (data.state === 'closed') {
277
+ const closeResult = await executeCommand('gh', [
278
+ 'pr', 'close', String(number),
279
+ '-R', `${owner}/${repo}`,
280
+ ], { cwd: this.workdir });
281
+ if (closeResult.exitCode !== 0) {
282
+ throw new Error(`Failed to close PR #${number}: ${closeResult.stderr}`);
283
+ }
284
+ }
285
+ }
286
+ async markPRReady(owner, repo, number) {
287
+ const result = await executeCommand('gh', [
288
+ 'pr', 'ready', String(number),
289
+ '-R', `${owner}/${repo}`,
290
+ ], { cwd: this.workdir });
291
+ if (result.exitCode !== 0) {
292
+ throw new Error(`Failed to mark PR #${number} as ready: ${result.stderr}`);
293
+ }
294
+ }
295
+ async getPRComments(owner, repo, number) {
296
+ // Get review comments using API
297
+ const result = await executeCommand('gh', [
298
+ 'api',
299
+ `/repos/${owner}/${repo}/pulls/${number}/comments`,
300
+ '--jq', '.[] | {id: .id, body: .body, author: .user.login, path: .path, line: .line, in_reply_to_id: .in_reply_to_id, created_at: .created_at, updated_at: .updated_at}',
301
+ ], { cwd: this.workdir });
302
+ if (result.exitCode !== 0) {
303
+ // No comments is not an error
304
+ return [];
305
+ }
306
+ const lines = result.stdout.trim().split('\n').filter(l => l);
307
+ const comments = [];
308
+ for (const line of lines) {
309
+ const data = parseJSONSafe(line);
310
+ if (!data)
311
+ continue;
312
+ comments.push({
313
+ id: data['id'],
314
+ body: data['body'],
315
+ author: data['author'],
316
+ path: data['path'],
317
+ line: data['line'],
318
+ in_reply_to_id: data['in_reply_to_id'],
319
+ created_at: data['created_at'],
320
+ updated_at: data['updated_at'],
321
+ });
322
+ }
323
+ return comments;
324
+ }
325
+ async replyToPRComment(owner, repo, number, commentId, body) {
326
+ const result = await executeCommand('gh', [
327
+ 'api',
328
+ '-X', 'POST',
329
+ `/repos/${owner}/${repo}/pulls/${number}/comments/${commentId}/replies`,
330
+ '-f', `body=${body}`,
331
+ ], { cwd: this.workdir });
332
+ if (result.exitCode !== 0) {
333
+ throw new Error(`Failed to reply to comment ${commentId}: ${result.stderr}`);
334
+ }
335
+ const data = parseJSONSafe(result.stdout);
336
+ if (!data) {
337
+ throw new Error('Failed to parse reply response');
338
+ }
339
+ return {
340
+ id: data['id'],
341
+ body: data['body'],
342
+ author: data['user'].login,
343
+ created_at: data['created_at'],
344
+ updated_at: data['updated_at'],
345
+ };
346
+ }
347
+ async listOpenPullRequests(owner, repo) {
348
+ const result = await executeCommand('gh', [
349
+ 'pr', 'list',
350
+ '-R', `${owner}/${repo}`,
351
+ '--state', 'open',
352
+ '--json', 'number,title,body,state,isDraft,headRefName,baseRefName,labels,createdAt,updatedAt',
353
+ '--limit', '100',
354
+ ], { cwd: this.workdir });
355
+ if (result.exitCode !== 0) {
356
+ throw new Error(`Failed to list open PRs for ${owner}/${repo}: ${result.stderr}`);
357
+ }
358
+ const data = parseJSONSafe(result.stdout);
359
+ if (!data) {
360
+ return [];
361
+ }
362
+ return data.map(pr => ({
363
+ number: pr['number'],
364
+ title: pr['title'],
365
+ body: pr['body'] ?? '',
366
+ state: 'open',
367
+ draft: pr['isDraft'] ?? false,
368
+ head: { ref: pr['headRefName'], sha: '', repo: `${owner}/${repo}` },
369
+ base: { ref: pr['baseRefName'], sha: '', repo: `${owner}/${repo}` },
370
+ labels: (pr['labels'] ?? []).map(l => ({
371
+ name: l.name,
372
+ color: l.color,
373
+ })),
374
+ created_at: pr['createdAt'],
375
+ updated_at: pr['updatedAt'],
376
+ }));
377
+ }
378
+ async findPRForBranch(owner, repo, branch) {
379
+ const result = await executeCommand('gh', [
380
+ 'pr', 'list',
381
+ '-R', `${owner}/${repo}`,
382
+ '--head', branch,
383
+ '--json', 'number,title,body,state,isDraft,headRefName,baseRefName,labels,createdAt,updatedAt',
384
+ '--limit', '1',
385
+ ], { cwd: this.workdir });
386
+ if (result.exitCode !== 0) {
387
+ return null;
388
+ }
389
+ const data = parseJSONSafe(result.stdout);
390
+ if (!data || data.length === 0) {
391
+ return null;
392
+ }
393
+ const pr = data[0];
394
+ return {
395
+ number: pr['number'],
396
+ title: pr['title'],
397
+ body: pr['body'] ?? '',
398
+ state: pr['state'].toLowerCase() === 'merged' ? 'merged' : 'open',
399
+ draft: pr['isDraft'] ?? false,
400
+ head: { ref: pr['headRefName'], sha: '', repo: `${owner}/${repo}` },
401
+ base: { ref: pr['baseRefName'], sha: '', repo: `${owner}/${repo}` },
402
+ labels: (pr['labels'] ?? []).map(l => ({
403
+ name: l.name,
404
+ color: l.color,
405
+ })),
406
+ created_at: pr['createdAt'],
407
+ updated_at: pr['updatedAt'],
408
+ };
409
+ }
410
+ // ==========================================================================
411
+ // Label Operations
412
+ // ==========================================================================
413
+ async addLabels(owner, repo, number, labels) {
414
+ if (labels.length === 0)
415
+ return;
416
+ const args = ['issue', 'edit', String(number), '-R', `${owner}/${repo}`];
417
+ for (const label of labels) {
418
+ args.push('--add-label', label);
419
+ }
420
+ const result = await executeCommand('gh', args, { cwd: this.workdir });
421
+ if (result.exitCode !== 0) {
422
+ throw new Error(`Failed to add labels: ${result.stderr}`);
423
+ }
424
+ }
425
+ async removeLabels(owner, repo, number, labels) {
426
+ if (labels.length === 0)
427
+ return;
428
+ const args = ['issue', 'edit', String(number), '-R', `${owner}/${repo}`];
429
+ for (const label of labels) {
430
+ args.push('--remove-label', label);
431
+ }
432
+ const result = await executeCommand('gh', args, { cwd: this.workdir });
433
+ if (result.exitCode !== 0) {
434
+ // Label might not exist, don't fail
435
+ if (!result.stderr.includes('not found')) {
436
+ throw new Error(`Failed to remove labels: ${result.stderr}`);
437
+ }
438
+ }
439
+ }
440
+ async getRepoLabels(owner, repo) {
441
+ const result = await executeCommand('gh', [
442
+ 'label', 'list',
443
+ '-R', `${owner}/${repo}`,
444
+ '--json', 'name,color,description',
445
+ '--limit', '1000',
446
+ ], { cwd: this.workdir });
447
+ if (result.exitCode !== 0) {
448
+ throw new Error(`Failed to get labels: ${result.stderr}`);
449
+ }
450
+ const data = parseJSONSafe(result.stdout);
451
+ return data ?? [];
452
+ }
453
+ async createOrUpdateLabel(owner, repo, label) {
454
+ // Check if label exists
455
+ const existing = await this.getRepoLabels(owner, repo);
456
+ const exists = existing.some(l => l.name === label.name);
457
+ if (exists) {
458
+ // Update
459
+ const args = [
460
+ 'label', 'edit', label.name,
461
+ '-R', `${owner}/${repo}`,
462
+ '--color', label.color.replace('#', ''),
463
+ ];
464
+ if (label.description) {
465
+ args.push('--description', label.description);
466
+ }
467
+ const result = await executeCommand('gh', args, { cwd: this.workdir });
468
+ if (result.exitCode !== 0) {
469
+ throw new Error(`Failed to update label ${label.name}: ${result.stderr}`);
470
+ }
471
+ return { created: false };
472
+ }
473
+ else {
474
+ // Create
475
+ const args = [
476
+ 'label', 'create', label.name,
477
+ '-R', `${owner}/${repo}`,
478
+ '--color', label.color.replace('#', ''),
479
+ ];
480
+ if (label.description) {
481
+ args.push('--description', label.description);
482
+ }
483
+ const result = await executeCommand('gh', args, { cwd: this.workdir });
484
+ if (result.exitCode !== 0) {
485
+ throw new Error(`Failed to create label ${label.name}: ${result.stderr}`);
486
+ }
487
+ return { created: true };
488
+ }
489
+ }
490
+ // ==========================================================================
491
+ // Git Operations (Local)
492
+ // ==========================================================================
493
+ async getStatus() {
494
+ const branchResult = await executeCommand('git', ['branch', '--show-current'], { cwd: this.workdir });
495
+ const branch = branchResult.stdout.trim();
496
+ const statusResult = await executeCommand('git', ['status', '--porcelain'], { cwd: this.workdir });
497
+ const lines = statusResult.stdout.split('\n').filter(l => l);
498
+ const staged = [];
499
+ const unstaged = [];
500
+ const untracked = [];
501
+ for (const line of lines) {
502
+ const indexStatus = line[0];
503
+ const workingStatus = line[1];
504
+ const file = line.substring(3);
505
+ if (indexStatus === '?' && workingStatus === '?') {
506
+ untracked.push(file);
507
+ }
508
+ else {
509
+ if (indexStatus && indexStatus !== ' ' && indexStatus !== '?') {
510
+ staged.push(file);
511
+ }
512
+ if (workingStatus && workingStatus !== ' ' && workingStatus !== '?') {
513
+ unstaged.push(file);
514
+ }
515
+ }
516
+ }
517
+ return {
518
+ branch,
519
+ has_changes: lines.length > 0,
520
+ staged,
521
+ unstaged,
522
+ untracked,
523
+ };
524
+ }
525
+ async getCurrentBranch() {
526
+ const result = await executeCommand('git', ['branch', '--show-current'], { cwd: this.workdir });
527
+ if (result.exitCode !== 0) {
528
+ throw new Error(`Failed to get current branch: ${result.stderr}`);
529
+ }
530
+ return result.stdout.trim();
531
+ }
532
+ async branchExists(branch, remote = false) {
533
+ if (remote) {
534
+ const result = await executeCommand('git', ['ls-remote', '--heads', 'origin', branch], { cwd: this.workdir });
535
+ return result.stdout.includes(branch);
536
+ }
537
+ else {
538
+ const result = await executeCommand('git', ['branch', '--list', branch], { cwd: this.workdir });
539
+ return result.stdout.trim().length > 0;
540
+ }
541
+ }
542
+ async createBranch(name, startPoint) {
543
+ const args = ['checkout', '-b', name];
544
+ if (startPoint) {
545
+ args.push(startPoint);
546
+ }
547
+ const result = await executeCommand('git', args, { cwd: this.workdir });
548
+ if (result.exitCode !== 0) {
549
+ throw new Error(`Failed to create branch ${name}: ${result.stderr}`);
550
+ }
551
+ }
552
+ async checkout(branch) {
553
+ const result = await executeCommand('git', ['checkout', branch], { cwd: this.workdir });
554
+ if (result.exitCode !== 0) {
555
+ throw new Error(`Failed to checkout ${branch}: ${result.stderr}`);
556
+ }
557
+ }
558
+ async stageFiles(files) {
559
+ if (files.length === 0)
560
+ return;
561
+ const result = await executeCommand('git', ['add', ...files], { cwd: this.workdir });
562
+ if (result.exitCode !== 0) {
563
+ throw new Error(`Failed to stage files: ${result.stderr}`);
564
+ }
565
+ }
566
+ async stageAll() {
567
+ const result = await executeCommand('git', ['add', '-A'], { cwd: this.workdir });
568
+ if (result.exitCode !== 0) {
569
+ throw new Error(`Failed to stage all: ${result.stderr}`);
570
+ }
571
+ }
572
+ async commit(message) {
573
+ const result = await executeCommand('git', ['commit', '-m', message], { cwd: this.workdir });
574
+ if (result.exitCode !== 0) {
575
+ throw new Error(`Failed to commit: ${result.stderr}`);
576
+ }
577
+ // Get the commit SHA
578
+ const shaResult = await executeCommand('git', ['rev-parse', 'HEAD'], { cwd: this.workdir });
579
+ const sha = shaResult.stdout.trim();
580
+ // Get committed files
581
+ const diffResult = await executeCommand('git', ['diff-tree', '--no-commit-id', '--name-only', '-r', 'HEAD'], { cwd: this.workdir });
582
+ const files = diffResult.stdout.split('\n').filter(f => f);
583
+ return {
584
+ sha,
585
+ files_committed: files,
586
+ };
587
+ }
588
+ async push(remote = 'origin', branch, setUpstream = false) {
589
+ const currentBranch = branch ?? await this.getCurrentBranch();
590
+ const args = ['push', remote, currentBranch];
591
+ if (setUpstream) {
592
+ args.splice(1, 0, '-u');
593
+ }
594
+ const result = await executeCommand('git', args, { cwd: this.workdir });
595
+ if (result.exitCode !== 0) {
596
+ throw new Error(`Failed to push: ${result.stderr}`);
597
+ }
598
+ return {
599
+ success: true,
600
+ ref: currentBranch,
601
+ remote,
602
+ };
603
+ }
604
+ async fetch(remote = 'origin', prune = true) {
605
+ const args = ['fetch', remote];
606
+ if (prune) {
607
+ args.push('--prune');
608
+ }
609
+ const result = await executeCommand('git', args, { cwd: this.workdir });
610
+ if (result.exitCode !== 0) {
611
+ throw new Error(`Failed to fetch: ${result.stderr}`);
612
+ }
613
+ }
614
+ async merge(branch, noCommit = false) {
615
+ const args = ['merge', branch];
616
+ if (noCommit) {
617
+ args.push('--no-commit');
618
+ }
619
+ const result = await executeCommand('git', args, { cwd: this.workdir });
620
+ // Check for conflicts
621
+ if (result.exitCode !== 0 && result.stdout.includes('CONFLICT')) {
622
+ const conflicted = await this.getConflictedFiles();
623
+ const conflicts = conflicted.map(path => ({
624
+ path,
625
+ ours: '',
626
+ theirs: '',
627
+ resolved: false,
628
+ }));
629
+ return {
630
+ success: false,
631
+ commits_merged: 0,
632
+ already_up_to_date: false,
633
+ conflicts,
634
+ summary: `Merge conflict in ${conflicts.length} file(s)`,
635
+ };
636
+ }
637
+ if (result.exitCode !== 0) {
638
+ throw new Error(`Failed to merge ${branch}: ${result.stderr}`);
639
+ }
640
+ // Check if already up to date
641
+ if (result.stdout.includes('Already up to date')) {
642
+ return {
643
+ success: true,
644
+ commits_merged: 0,
645
+ already_up_to_date: true,
646
+ conflicts: [],
647
+ summary: 'Already up to date',
648
+ };
649
+ }
650
+ // Count commits merged (rough estimate from output)
651
+ const commitMatch = result.stdout.match(/(\d+) files? changed/);
652
+ const filesChanged = commitMatch ? parseInt(commitMatch[1], 10) : 0;
653
+ return {
654
+ success: true,
655
+ commits_merged: filesChanged > 0 ? 1 : 0,
656
+ already_up_to_date: false,
657
+ conflicts: [],
658
+ summary: result.stdout.trim().split('\n')[0] ?? 'Merge completed',
659
+ };
660
+ }
661
+ async mergeAbort() {
662
+ const result = await executeCommand('git', ['merge', '--abort'], { cwd: this.workdir });
663
+ if (result.exitCode !== 0) {
664
+ throw new Error(`Failed to abort merge: ${result.stderr}`);
665
+ }
666
+ }
667
+ async stash(message) {
668
+ const args = ['stash', 'push'];
669
+ if (message) {
670
+ args.push('-m', message);
671
+ }
672
+ const result = await executeCommand('git', args, { cwd: this.workdir });
673
+ // Returns true if something was stashed
674
+ return !result.stdout.includes('No local changes to save');
675
+ }
676
+ async stashPop() {
677
+ const result = await executeCommand('git', ['stash', 'pop'], { cwd: this.workdir });
678
+ if (result.exitCode !== 0) {
679
+ if (result.stderr.includes('CONFLICT')) {
680
+ return { success: false, conflicts: true };
681
+ }
682
+ throw new Error(`Failed to pop stash: ${result.stderr}`);
683
+ }
684
+ return { success: true, conflicts: false };
685
+ }
686
+ async getConflictedFiles() {
687
+ const result = await executeCommand('git', ['diff', '--name-only', '--diff-filter=U'], { cwd: this.workdir });
688
+ return result.stdout.split('\n').filter(f => f);
689
+ }
690
+ async getDefaultBranch() {
691
+ // Try to get from remote
692
+ const result = await executeCommand('git', ['symbolic-ref', 'refs/remotes/origin/HEAD'], { cwd: this.workdir });
693
+ if (result.exitCode === 0) {
694
+ return result.stdout.trim().replace('refs/remotes/origin/', '');
695
+ }
696
+ // Fallback to checking common names
697
+ for (const branch of ['develop', 'main', 'master']) {
698
+ const exists = await this.branchExists(branch, true);
699
+ if (exists)
700
+ return branch;
701
+ }
702
+ return 'main';
703
+ }
704
+ async getCommitsBetween(base, head) {
705
+ const result = await executeCommand('git', [
706
+ 'log', `${base}..${head}`,
707
+ '--format=%H|%s',
708
+ ], { cwd: this.workdir });
709
+ if (result.exitCode !== 0) {
710
+ return [];
711
+ }
712
+ return result.stdout.split('\n').filter(l => l).map(line => {
713
+ const [sha, ...messageParts] = line.split('|');
714
+ return {
715
+ sha: sha ?? '',
716
+ message: messageParts.join('|'),
717
+ };
718
+ });
719
+ }
720
+ // ==========================================================================
721
+ // Alias Methods (convenience wrappers)
722
+ // ==========================================================================
723
+ async listBranches(owner, repo) {
724
+ // List remote branches using gh api
725
+ const result = await executeCommand('gh', [
726
+ 'api',
727
+ `/repos/${owner}/${repo}/branches`,
728
+ '--jq', '.[].name',
729
+ ], { cwd: this.workdir });
730
+ if (result.exitCode !== 0) {
731
+ // Fallback to git
732
+ const gitResult = await executeCommand('git', [
733
+ 'branch', '-r', '--format=%(refname:short)',
734
+ ], { cwd: this.workdir });
735
+ return gitResult.stdout.split('\n')
736
+ .filter(b => b)
737
+ .map(b => b.replace('origin/', ''));
738
+ }
739
+ return result.stdout.split('\n').filter(b => b);
740
+ }
741
+ async createPR(owner, repo, data) {
742
+ return this.createPullRequest(owner, repo, data);
743
+ }
744
+ async updatePR(owner, repo, number, data) {
745
+ return this.updatePullRequest(owner, repo, number, data);
746
+ }
747
+ async getPRForBranch(owner, repo, branch) {
748
+ return this.findPRForBranch(owner, repo, branch);
749
+ }
750
+ async listLabels(owner, repo) {
751
+ return this.getRepoLabels(owner, repo);
752
+ }
753
+ async createLabel(owner, repo, name, color, description) {
754
+ const args = [
755
+ 'label', 'create', name,
756
+ '-R', `${owner}/${repo}`,
757
+ '--color', color.replace('#', ''),
758
+ ];
759
+ if (description) {
760
+ args.push('--description', description);
761
+ }
762
+ const result = await executeCommand('gh', args, { cwd: this.workdir });
763
+ if (result.exitCode !== 0) {
764
+ throw new Error(`Failed to create label ${name}: ${result.stderr}`);
765
+ }
766
+ }
767
+ async updateLabel(owner, repo, name, data) {
768
+ const args = ['label', 'edit', name, '-R', `${owner}/${repo}`];
769
+ if (data.color) {
770
+ args.push('--color', data.color.replace('#', ''));
771
+ }
772
+ if (data.description !== undefined) {
773
+ args.push('--description', data.description);
774
+ }
775
+ const result = await executeCommand('gh', args, { cwd: this.workdir });
776
+ if (result.exitCode !== 0) {
777
+ throw new Error(`Failed to update label ${name}: ${result.stderr}`);
778
+ }
779
+ }
780
+ }
781
+ //# sourceMappingURL=gh-cli.js.map